diff --git a/ESPixelStick/src/FileMgr.cpp b/ESPixelStick/src/FileMgr.cpp index 10eeb1730..4794e9163 100644 --- a/ESPixelStick/src/FileMgr.cpp +++ b/ESPixelStick/src/FileMgr.cpp @@ -1705,7 +1705,7 @@ void c_FileMgr::BuildFseqList() // open output file, erase old data ESP_SD.chdir(); // Set to sd root - FileMgr.DeleteSdFile(String(FSEQFILELIST)); + DeleteSdFile(String(FSEQFILELIST)); FsFile OutputFile; if(!OutputFile.open (String(F(FSEQFILELIST)).c_str(), O_WRITE | O_CREAT)) @@ -1810,7 +1810,7 @@ void c_FileMgr::BuildFseqList() OutputFile.print(String("], \"usedBytes\" : ") + int64String(usedBytes)); // LOG_PORT.print(String("], \"usedBytes\" : ") + int64String(usedBytes)); OutputFile.print(String(", \"numFiles\" : ") + String(numFiles)); - // LOG_PORT.print(String(", \"numFiles\" : ") + String(numFiles)); + OutputFile.print(String(", \"SdCardPresent\" : true")); // close the data section OutputFile.print("}"); diff --git a/ESPixelStick/src/output/OutputCommon.hpp b/ESPixelStick/src/output/OutputCommon.hpp index 4bb8c804e..2ef354000 100644 --- a/ESPixelStick/src/output/OutputCommon.hpp +++ b/ESPixelStick/src/output/OutputCommon.hpp @@ -75,7 +75,7 @@ class c_OutputCommon uint32_t OutputBufferSize = 0; uint32_t FrameCount = 0; - void ReportNewFrame (); + virtual void ReportNewFrame (); inline bool canRefresh () { diff --git a/ESPixelStick/src/output/OutputGrinch.cpp b/ESPixelStick/src/output/OutputGrinch.cpp new file mode 100644 index 000000000..8d71eb457 --- /dev/null +++ b/ESPixelStick/src/output/OutputGrinch.cpp @@ -0,0 +1,165 @@ +/* +* OutputGRINCH.cpp - GRINCH driver code for ESPixelStick UART +* +* Project: ESPixelStick - An ESP8266 / ESP32 and E1.31 based pixel driver +* Copyright (c) 2015, 2024 Shelby Merrick +* http://www.forkineye.com +* +* This program is provided free for you to use in any way that you wish, +* subject to the laws and regulations where you are using it. Due diligence +* is strongly suggested before using this code. Please give credit where due. +* +* The Author makes no warranty of any kind, express or implied, with regard +* to this program or the documentation contained in this document. The +* Author shall not be liable in any event for incidental or consequential +* damages in connection with, or arising out of, the furnishing, performance +* or use of these programs. +* +*/ + +#include "../ESPixelStick.h" +#ifdef SUPPORT_OutputType_GRINCH + +#include "OutputGrinch.hpp" + +//---------------------------------------------------------------------------- +c_OutputGrinch::c_OutputGrinch (c_OutputMgr::e_OutputChannelIds OutputChannelId, + gpio_num_t outputGpio, + uart_port_t uart, + c_OutputMgr::e_OutputType outputType) : + c_OutputCommon (OutputChannelId, outputGpio, uart, outputType) +{ + // DEBUG_START; + + // InterFrameGapInMicroSec = GRINCH_MIN_IDLE_TIME_US; + + memset(dataBuffer, 0x00, sizeof(dataBuffer)); + // DEBUG_END; +} // c_OutputGrinch + +//---------------------------------------------------------------------------- +c_OutputGrinch::~c_OutputGrinch () +{ + // DEBUG_START; + + // DEBUG_END; +} // ~c_OutputGrinch + +//---------------------------------------------------------------------------- +void c_OutputGrinch::GetConfig (ArduinoJson::JsonObject& jsonConfig) +{ + // DEBUG_START; + + c_OutputCommon::GetConfig (jsonConfig); + jsonConfig[CN_cs_pin] = uint8_t (DataStrobe); + jsonConfig[CN_count] = NumberOfGrinchControllers; + + // DEBUG_END; +} // GetConfig + +//---------------------------------------------------------------------------- +void c_OutputGrinch::GetStatus (ArduinoJson::JsonObject& jsonStatus) +{ + c_OutputCommon::GetStatus (jsonStatus); + +} // GetStatus + +//---------------------------------------------------------------------------- +void c_OutputGrinch::SetOutputBufferSize (uint32_t NumChannelsAvailable) +{ + // DEBUG_START; + + c_OutputCommon::SetOutputBufferSize (NumChannelsAvailable); + + // Calculate our refresh time + // SetFrameDurration (((1.0 / float (GRINCH_BIT_RATE)) * MicroSecondsInASecond), BlockSize, BlockDelay); + + // DEBUG_END; + +} // SetBufferSize + +//---------------------------------------------------------------------------- +/* Process the config +* +* needs +* reference to string to process +* returns +* true - config has been accepted +* false - Config rejected. Using defaults for invalid settings +*/ +bool c_OutputGrinch::SetConfig (ArduinoJson::JsonObject& jsonConfig) +{ + // DEBUG_START; + + bool response = c_OutputCommon::SetConfig (jsonConfig); + response |= setFromJSON (DataStrobe, jsonConfig, CN_cs_pin); + response |= setFromJSON (NumberOfGrinchControllers, jsonConfig, CN_count); + + NumberOfGrinchChannels = NumberOfGrinchControllers * DATA_CHANNELS_PER_GRINCH; + NumberOfGrinchDataBytes = NumberOfGrinchChannels / (sizeof(uint8_t) * 8); + + // DEBUG_V(String("NumberOfGrinchControllers: ") + String(NumberOfGrinchControllers)); + // DEBUG_V(String(" NumberOfGrinchChannels: ") + String(NumberOfGrinchChannels)); + // DEBUG_V(String(" NumberOfGrinchDataBytes: ") + String(NumberOfGrinchDataBytes)); + SetOutputBufferSize(NumberOfGrinchChannels); + + // Calculate our refresh time + // SetFrameDurration (((1.0 / float (GRINCH_BIT_RATE)) * MicroSecondsInASecond), BlockSize, BlockDelay); + + // DEBUG_END; + return response; + +} // SetConfig + +//---------------------------------------------------------------------------- +bool IRAM_ATTR c_OutputGrinch::ISR_GetNextIntensityToSend (uint32_t &DataToSend) +{ + if(ISR_MoreDataToSend()) + { + // DEBUG_V(String("DataToSend: ") + String(DataToSend)); + DataToSend = dataBuffer[0].Data[SpiOutputDataByteIndex-1]; + SpiOutputDataByteIndex--; + } + + return ISR_MoreDataToSend(); +} // ISR_GetNextIntensityToSend + +//---------------------------------------------------------------------------- +void c_OutputGrinch::StartNewFrame() +{ + // DEBUG_START; + + // build the data frame + uint32_t NumChannelsToProcess = GetNumOutputBufferBytesNeeded(); + uint8_t * pInputData = GetBufferAddress(); + uint8_t OutputData = 0; + uint8_t OutputDataByteIndex = 0; + uint8_t bitCounter = 0; + + while(NumChannelsToProcess) + { + if(bitCounter >= (sizeof(OutputData) * 8)) + { + // write the output data + OutputDataByteIndex ++; + + // move to the next data byte + bitCounter = 0; + // clear the starting data + OutputData = 0; + } + + // set the output bit to ON (zero) if the output is greater than 50% intensity + OutputData = (OutputData << 1) | (*pInputData < 128); + dataBuffer[0].Data[OutputDataByteIndex] = OutputData; + pInputData ++; + bitCounter ++; + NumChannelsToProcess--; + } + + SpiOutputDataByteIndex = NumberOfGrinchDataBytes; + + // DEBUG_END; +} // StartNewFrame + +#endif // def SUPPORT_OutputType_GRINCH diff --git a/ESPixelStick/src/output/OutputGrinch.hpp b/ESPixelStick/src/output/OutputGrinch.hpp new file mode 100644 index 000000000..3896587bb --- /dev/null +++ b/ESPixelStick/src/output/OutputGrinch.hpp @@ -0,0 +1,77 @@ +#pragma once +/* +* OutputGrinch.h - Grinch driver code for ESPixelStick +* +* Project: ESPixelStick - An ESP8266 / ESP32 and E1.31 based pixel driver +* Copyright (c) 2015, 2024 Shelby Merrick +* http://www.forkineye.com +* +* This program is provided free for you to use in any way that you wish, +* subject to the laws and regulations where you are using it. Due diligence +* is strongly suggested before using this code. Please give credit where due. +* +* The Author makes no warranty of any kind, express or implied, with regard +* to this program or the documentation contained in this document. The +* Author shall not be liable in any event for incidental or consequential +* damages in connection with, or arising out of, the furnishing, performance +* or use of these programs. +* +* This is a derived class that converts data in the output buffer into +* pixel intensities and then transmits them through the configured serial +* interface. +* +*/ +#include "OutputCommon.hpp" +#ifdef SUPPORT_OutputType_GRINCH + +class c_OutputGrinch : public c_OutputCommon +{ +public: + // These functions are inherited from c_OutputCommon + c_OutputGrinch (c_OutputMgr::e_OutputChannelIds OutputChannelId, + gpio_num_t outputGpio, + uart_port_t uart, + c_OutputMgr::e_OutputType outputType); + virtual ~c_OutputGrinch (); + + // functions to be provided by the derived class + virtual bool SetConfig (ArduinoJson::JsonObject & jsonConfig); ///< Set a new config in the driver + virtual void GetConfig (ArduinoJson::JsonObject & jsonConfig); ///< Get the current config used by the driver + void GetDriverName (String & sDriverName) { sDriverName = F("Grinch"); } + virtual void GetStatus (ArduinoJson::JsonObject & jsonStatus); + virtual void SetOutputBufferSize (uint32_t NumChannelsAvailable); + uint32_t GetNumOutputBufferBytesNeeded () { return (NumberOfGrinchChannels); } + uint32_t GetNumOutputBufferChannelsServiced () { return (NumberOfGrinchChannels); } + + void StartNewFrame(); + inline bool IRAM_ATTR ISR_MoreDataToSend () {return SpiOutputDataByteIndex > 0;} + bool IRAM_ATTR ISR_GetNextIntensityToSend (uint32_t &DataToSend); + +protected: + +#define MAX_NUM_SUPPORTED_GRINCHES 4 +#define DATA_CHANNELS_PER_GRINCH 64 + gpio_num_t DataStrobe = DEFAULT_SPI_CS_GPIO; + +private: + uint8_t NumberOfGrinchControllers = 1; + uint8_t NumberOfGrinchChannels = NumberOfGrinchControllers * DATA_CHANNELS_PER_GRINCH; + uint8_t NumberOfGrinchDataBytes = NumberOfGrinchChannels / 8; + uint8_t SpiOutputDataByteIndex = 0; + + union GrinchData_s + { + struct GrinchLatchData + { + uint8_t LatchChan_01_08 = 0; + uint8_t LatchChan_09_16 = 0; + uint8_t LatchChan_17_24 = 0; + uint8_t LatchChan_25_32 = 0; + }; + uint8_t Data[4]; + }; + GrinchData_s dataBuffer[MAX_NUM_SUPPORTED_GRINCHES]; + + +}; // c_OutputGrinch +#endif // def SUPPORT_OutputType_GRINCH diff --git a/ESPixelStick/src/output/OutputGrinchSpi.cpp b/ESPixelStick/src/output/OutputGrinchSpi.cpp new file mode 100644 index 000000000..69e5e83f9 --- /dev/null +++ b/ESPixelStick/src/output/OutputGrinchSpi.cpp @@ -0,0 +1,108 @@ +/* +* OutputGrinchSpi.cpp - GRINCH driver code for ESPixelStick SPI Channel +* +* Project: ESPixelStick - An ESP8266 / ESP32 and E1.31 based pixel driver +* Copyright (c) 2015, 2024 Shelby Merrick +* http://www.forkineye.com +* +* This program is provided free for you to use in any way that you wish, +* subject to the laws and regulations where you are using it. Due diligence +* is strongly suggested before using this code. Please give credit where due. +* +* The Author makes no warranty of any kind, express or implied, with regard +* to this program or the documentation contained in this document. The +* Author shall not be liable in any event for incidental or consequential +* damages in connection with, or arising out of, the furnishing, performance +* or use of these programs. +* +*/ + +#include "../ESPixelStick.h" +#if defined (SUPPORT_OutputType_GRINCH) && defined (SUPPORT_SPI_OUTPUT) + +#include "OutputGrinchSpi.hpp" + +//---------------------------------------------------------------------------- +c_OutputGrinchSpi::c_OutputGrinchSpi (c_OutputMgr::e_OutputChannelIds OutputChannelId, + gpio_num_t outputGpio, + uart_port_t uart, + c_OutputMgr::e_OutputType outputType) : + c_OutputGrinch (OutputChannelId, outputGpio, uart, outputType) +{ + // DEBUG_START; + + // update frame calculation + + // DEBUG_END; +} // c_OutputGrinchSpi + +//---------------------------------------------------------------------------- +c_OutputGrinchSpi::~c_OutputGrinchSpi () +{ + // DEBUG_START; + + // DEBUG_END; + +} // ~c_OutputGrinchSpi + +//---------------------------------------------------------------------------- +/* Use the current config to set up the output port +*/ +void c_OutputGrinchSpi::Begin () +{ + // DEBUG_START; + Spi.Begin (this); + Spi.SetCsPin(DataStrobe); + HasBeenInitialized = true; + + // DEBUG_END; + +} // init + +//---------------------------------------------------------------------------- +void c_OutputGrinchSpi::GetConfig (ArduinoJson::JsonObject& jsonConfig) +{ + // DEBUG_START; + + c_OutputGrinch::GetConfig (jsonConfig); + + // DEBUG_END; +} // GetConfig + +//---------------------------------------------------------------------------- +bool c_OutputGrinchSpi::SetConfig (ArduinoJson::JsonObject& jsonConfig) +{ + // DEBUG_START; + + bool response = c_OutputGrinch::SetConfig (jsonConfig); + + // DEBUG_END; + return response; + +} // GetStatus + +//---------------------------------------------------------------------------- +uint32_t c_OutputGrinchSpi::Poll () +{ + // DEBUG_START; + + uint32_t FrameLen = ActualFrameDurationMicroSec; + + if (canRefresh ()) + { + if (Spi.Poll ()) + { + ReportNewFrame (); + } + } + else + { + FrameLen = 0; + } + + // DEBUG_END; + return FrameLen; + +} // render + +#endif // defined (SUPPORT_OutputType_GRINCH) && defined (SUPPORT_SPI_OUTPUT) diff --git a/ESPixelStick/src/output/OutputGrinchSpi.hpp b/ESPixelStick/src/output/OutputGrinchSpi.hpp new file mode 100644 index 000000000..1ae4f4b48 --- /dev/null +++ b/ESPixelStick/src/output/OutputGrinchSpi.hpp @@ -0,0 +1,53 @@ +#pragma once +/* +* OutputGrinchSpi.h - GRINCH driver code for ESPixelStick Spi Channel +* +* Project: ESPixelStick - An ESP8266 / ESP32 and E1.31 based pixel driver +* Copyright (c) 2015, 2024 Shelby Merrick +* http://www.forkineye.com +* +* This program is provided free for you to use in any way that you wish, +* subject to the laws and regulations where you are using it. Due diligence +* is strongly suggested before using this code. Please give credit where due. +* +* The Author makes no warranty of any kind, express or implied, with regard +* to this program or the documentation contained in this document. The +* Author shall not be liable in any event for incidental or consequential +* damages in connection with, or arising out of, the furnishing, performance +* or use of these programs. +* +* This is a derived class that converts data in the output buffer into +* pixel intensities and then transmits them through the configured serial +* interface. +* +*/ +#include "../ESPixelStick.h" +#if defined (SUPPORT_OutputType_GRINCH) && defined (SUPPORT_SPI_OUTPUT) + +#include "OutputGrinch.hpp" +#include "OutputSpi.hpp" + +class c_OutputGrinchSpi : public c_OutputGrinch +{ +public: + // These functions are inherited from c_OutputCommon + c_OutputGrinchSpi (c_OutputMgr::e_OutputChannelIds OutputChannelId, + gpio_num_t outputGpio, + uart_port_t uart, + c_OutputMgr::e_OutputType outputType); + virtual ~c_OutputGrinchSpi (); + + // functions to be provided by the derived class + void Begin (); + void GetConfig (ArduinoJson::JsonObject& jsonConfig); + bool SetConfig (ArduinoJson::JsonObject& jsonConfig); ///< Set a new config in the driver + uint32_t Poll (); ///< Call from loop(), renders output data + void PauseOutput () {}; + +private: + + c_OutputSpi Spi; + +}; // c_OutputGrinchSpi + +#endif // defined (SUPPORT_OutputType_GRINCH) && defined (SUPPORT_SPI_OUTPUT) diff --git a/ESPixelStick/src/output/OutputMgr.cpp b/ESPixelStick/src/output/OutputMgr.cpp index 6d7f8b615..23019bbc4 100644 --- a/ESPixelStick/src/output/OutputMgr.cpp +++ b/ESPixelStick/src/output/OutputMgr.cpp @@ -2,7 +2,7 @@ * OutputMgr.cpp - Output Management class * * Project: ESPixelStick - An ESP8266 / ESP32 and E1.31 based pixel driver -* Copyright (c) 2021, 2022 Shelby Merrick +* Copyright (c) 2021, 2024 Shelby Merrick * http://www.forkineye.com * * This program is provided free for you to use in any way that you wish, @@ -32,6 +32,7 @@ #include "OutputAPA102Spi.hpp" #include "OutputGECEUart.hpp" #include "OutputGECERmt.hpp" +#include "OutputGrinchSpi.hpp" #include "OutputRelay.hpp" #include "OutputSerialUart.hpp" #include "OutputSerialRmt.hpp" @@ -89,6 +90,10 @@ static const OutputTypeXlateMap_t OutputTypeXlateMap[c_OutputMgr::e_OutputType:: {c_OutputMgr::e_OutputType::OutputType_GECE, "GECE"}, #endif // def SUPPORT_OutputType_GECE +#ifdef SUPPORT_OutputType_GRINCH + {c_OutputMgr::e_OutputType::OutputType_GRINCH, "Grinch"}, +#endif // def SUPPORT_OutputType_GRINCH + #ifdef SUPPORT_OutputType_GS8208 {c_OutputMgr::e_OutputType::OutputType_GS8208, "GS8208"}, #endif // def SUPPORT_OutputType_GS8208 @@ -845,6 +850,28 @@ void c_OutputMgr::InstantiateNewOutputChannel(DriverInfo_t & CurrentOutputChanne } #endif // def SUPPORT_OutputType_TM1814 +#ifdef SUPPORT_OutputType_GRINCH + case e_OutputType::OutputType_GRINCH: + { + if (CurrentOutputChannelDriver.PortType == OM_PortType_t::Spi) + { + // logcon (CN_stars + String ((" Starting GRINCH SPI for channel '")) + CurrentOutputChannelDriver.DriverId + "'. " + CN_stars); + CurrentOutputChannelDriver.pOutputChannelDriver = new c_OutputGrinchSpi(CurrentOutputChannelDriver.DriverId, CurrentOutputChannelDriver.GpioPin, CurrentOutputChannelDriver.PortId, OutputType_GRINCH); + // DEBUG_V (); + break; + } + + if (!BuildingNewConfig) + { + logcon(CN_stars + String(MN_07) + F("Grinch") + MN_08 + CurrentOutputChannelDriver.DriverId + "'. " + CN_stars); + } + CurrentOutputChannelDriver.pOutputChannelDriver = new c_OutputDisabled(CurrentOutputChannelDriver.DriverId, CurrentOutputChannelDriver.GpioPin, CurrentOutputChannelDriver.PortId, OutputType_Disabled); + // DEBUG_V (); + + break; + } +#endif // def SUPPORT_OutputType_GRINCH + #ifdef SUPPORT_OutputType_WS2801 case e_OutputType::OutputType_WS2801: { diff --git a/ESPixelStick/src/output/OutputMgr.hpp b/ESPixelStick/src/output/OutputMgr.hpp index cdfd95836..7bd4a0b08 100644 --- a/ESPixelStick/src/output/OutputMgr.hpp +++ b/ESPixelStick/src/output/OutputMgr.hpp @@ -183,6 +183,10 @@ class c_OutputMgr OutputType_TLS3001 = 14, #endif // def SUPPORT_OutputType_TLS3001 + #ifdef SUPPORT_OutputType_GRINCH + OutputType_GRINCH = 15, + #endif // def SUPPORT_OutputType_GRINCH + // Add new types here OutputType_End, // must be last OutputType_Start = OutputType_Disabled, diff --git a/ESPixelStick/src/output/OutputSpi.cpp b/ESPixelStick/src/output/OutputSpi.cpp index df23c829a..0d22f1e18 100644 --- a/ESPixelStick/src/output/OutputSpi.cpp +++ b/ESPixelStick/src/output/OutputSpi.cpp @@ -87,16 +87,21 @@ c_OutputSpi::~c_OutputSpi () if(HasBeenInitialized) { spi_transfer_callback_enabled = false; - if (OutputPixel) - { - logcon(CN_stars + String(F(" SPI Interface Shutdown requires a reboot ")) + CN_stars); - RequestReboot(100000); - } + logcon(CN_stars + String(F(" SPI Interface Shutdown requires a reboot ")) + CN_stars); + RequestReboot(100000); } // DEBUG_END; } // ~c_OutputSpi +#if defined(SUPPORT_OutputType_GRINCH) +void c_OutputSpi::Begin (c_OutputGrinch* _OutputGrinch) +{ + OutputGrinch = _OutputGrinch; + Begin ((c_OutputPixel*)nullptr); +} +#endif // defined(SUPPORT_OutputType_GRINCH) + //---------------------------------------------------------------------------- void c_OutputSpi::Begin (c_OutputPixel* _OutputPixel) { @@ -135,7 +140,7 @@ void c_OutputSpi::Begin (c_OutputPixel* _OutputPixel) SpiDeviceConfiguration.clock_speed_hz = SPI_SPI_MASTER_FREQ_1M; SpiDeviceConfiguration.mode = 0; // SPI mode 0 SpiDeviceConfiguration.spics_io_num = -1; // we will NOT use CS pin - SpiDeviceConfiguration.queue_size = 10 * SPI_NUM_TRANSACTIONS; // We want to be able to queue 2 transactions at a time + SpiDeviceConfiguration.queue_size = 1; // We want to be able to queue 2 transactions at a time // SpiDeviceConfiguration.pre_cb = nullptr; // Specify pre-transfer callback to handle D/C line SpiDeviceConfiguration.post_cb = spi_transfer_callback; // Specify post-transfer callback to handle D/C line // SpiDeviceConfiguration.flags = 0; @@ -152,13 +157,49 @@ void c_OutputSpi::Begin (c_OutputPixel* _OutputPixel) } // Begin +//---------------------------------------------------------------------------- +bool c_OutputSpi::ISR_MoreDataToSend() +{ + bool response = false; + if(OutputPixel) + { + response = OutputPixel->ISR_MoreDataToSend (); + } +#if defined(SUPPORT_OutputType_GRINCH) + else if(OutputGrinch) + { + response = OutputGrinch->ISR_MoreDataToSend (); + } +#endif // defined(SUPPORT_OutputType_GRINCH) + return response; +} + +//---------------------------------------------------------------------------- +bool c_OutputSpi::ISR_GetNextIntensityToSend(uint32_t& Data) +{ + bool response = false; + + if(OutputPixel) + { + response = OutputPixel->ISR_GetNextIntensityToSend (Data); + } +#if defined(SUPPORT_OutputType_GRINCH) + else if(OutputGrinch) + { + response = OutputGrinch->ISR_GetNextIntensityToSend (Data); + } +#endif // defined(SUPPORT_OutputType_GRINCH) + + return response; +} // ISR_GetNextIntensityToSend + //---------------------------------------------------------------------------- void c_OutputSpi::SendIntensityData () { // DEBUG_START; SendIntensityDataCounter++; - if (OutputPixel->ISR_MoreDataToSend ()) + if (ISR_MoreDataToSend ()) { spi_transaction_t & TransactionToFill = Transactions[NextTransactionToFill]; memset ( (void*)&Transactions[NextTransactionToFill], 0x00, sizeof (spi_transaction_t)); @@ -169,31 +210,67 @@ void c_OutputSpi::SendIntensityData () uint32_t NumEmptyIntensitySlots = SPI_NUM_INTENSITY_PER_TRANSACTION; uint32_t IntensityData = 0; - while ( (NumEmptyIntensitySlots) && (OutputPixel->ISR_MoreDataToSend ())) + while ( (NumEmptyIntensitySlots) && (ISR_MoreDataToSend ())) { - OutputPixel->ISR_GetNextIntensityToSend (IntensityData); + ISR_GetNextIntensityToSend (IntensityData); *pMem++ = byte(IntensityData); --NumEmptyIntensitySlots; } // end while there is space in the buffer TransactionToFill.length = SPI_BITS_PER_INTENSITY * (SPI_NUM_INTENSITY_PER_TRANSACTION - NumEmptyIntensitySlots); - if (!OutputPixel->ISR_MoreDataToSend ()) + if (!ISR_MoreDataToSend ()) { TransactionToFill.length++; } + if(gpio_num_t(-1) != cs_pin) + { + // turn on the output strobe (latch data) + digitalWrite(cs_pin, LOW); + } + ESP_ERROR_CHECK (spi_device_queue_trans (spi_device_handle, &Transactions[NextTransactionToFill], portMAX_DELAY)); if (++NextTransactionToFill >= SPI_NUM_TRANSACTIONS) { NextTransactionToFill = 0; } + + if(gpio_num_t(-1) != cs_pin) + { + if (!ISR_MoreDataToSend ()) + { + spi_transaction_t * pspi_transaction = &TransactionToFill; + spi_device_get_trans_result(spi_device_handle, &pspi_transaction, 100); + + // turn on the output strobe (latch data) + digitalWrite(cs_pin, HIGH); + } + } } // DEBUG_END; } // SendIntensityData +//---------------------------------------------------------------------------- +void c_OutputSpi::StartNewFrame() +{ + bool response = false; + + if(OutputPixel) + { + OutputPixel->StartNewFrame (); + } +#if defined(SUPPORT_OutputType_GRINCH) + else if(OutputGrinch) + { + OutputGrinch->StartNewFrame (); + } +#endif // defined(SUPPORT_OutputType_GRINCH) + +} // StartNewFrame + //---------------------------------------------------------------------------- bool c_OutputSpi::Poll () { @@ -201,7 +278,13 @@ bool c_OutputSpi::Poll () // DEBUG_START; - OutputPixel->StartNewFrame (); + StartNewFrame (); + + if(gpio_num_t(-1) != cs_pin) + { + // turn on the output strobe (latch data) + pinMode(cs_pin, OUTPUT); + } // fill all the available buffers NextTransactionToFill = 0; @@ -212,6 +295,7 @@ bool c_OutputSpi::Poll () vTaskResume (SendIntensityDataTaskHandle); } } + spi_transfer_callback_enabled = true; Response = true; diff --git a/ESPixelStick/src/output/OutputSpi.hpp b/ESPixelStick/src/output/OutputSpi.hpp index dbce629ec..d4ccd4af2 100644 --- a/ESPixelStick/src/output/OutputSpi.hpp +++ b/ESPixelStick/src/output/OutputSpi.hpp @@ -3,7 +3,7 @@ * OutputSpi.h - SPI driver code for ESPixelStick Spi Channel * * Project: ESPixelStick - An ESP8266 / ESP32 and E1.31 based pixel driver -* Copyright (c) 2015, 2022 Shelby Merrick +* Copyright (c) 2015, 2024 Shelby Merrick * http://www.forkineye.com * * This program is provided free for you to use in any way that you wish, @@ -28,6 +28,9 @@ #include "OutputPixel.hpp" #include #include +#if defined(SUPPORT_OutputType_GRINCH) + #include "OutputGrinch.hpp" +#endif // defined(SUPPORT_OutputType_GRINCH) class c_OutputSpi { @@ -38,11 +41,15 @@ class c_OutputSpi // functions to be provided by the derived class void Begin (c_OutputPixel* _OutputPixel); +#if defined(SUPPORT_OutputType_GRINCH) + void Begin (c_OutputGrinch* _OutputSerial); +#endif // defined(SUPPORT_OutputType_GRINCH) bool Poll (); ///< Call from loop (), renders output data TaskHandle_t GetTaskHandle () { return SendIntensityDataTaskHandle; } void GetDriverName (String& Name) { Name = CN_OutputSpi; } void DataOutputTask (void* pvParameters); void SendIntensityData (); + void SetCsPin(gpio_num_t _cs_pin) {cs_pin = _cs_pin;} uint32_t DataTaskcounter = 0; uint32_t DataCbCounter = 0; @@ -56,9 +63,14 @@ class c_OutputSpi #define SPI_SPI_HOST DEFAULT_SPI_DEVICE #define SPI_SPI_DMA_CHANNEL 2 + bool ISR_MoreDataToSend(); + bool ISR_GetNextIntensityToSend(uint32_t& Data); + void StartNewFrame(); + uint8_t NumIntensityValuesPerInterrupt = 0; uint8_t NumIntensityBitsPerInterrupt = 0; spi_device_handle_t spi_device_handle = 0; + gpio_num_t cs_pin = gpio_num_t(-1); // uint32_t FrameStartCounter = 0; uint32_t SendIntensityDataCounter = 0; @@ -74,6 +86,9 @@ class c_OutputSpi gpio_num_t ClockPin = DEFAULT_SPI_CLOCK_GPIO; c_OutputPixel* OutputPixel = nullptr; +#if defined(SUPPORT_OutputType_GRINCH) + c_OutputGrinch * OutputGrinch = nullptr; +#endif // defined(SUPPORT_OutputType_GRINCH) #ifndef HasBeenInitialized bool HasBeenInitialized = false; diff --git a/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_BreakDanceV2.hpp b/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_BreakDanceV2.hpp index f23cfc1a0..28234ea0e 100644 --- a/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_BreakDanceV2.hpp +++ b/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_BreakDanceV2.hpp @@ -28,6 +28,7 @@ #define SUPPORT_SPI_OUTPUT #define DEFAULT_SPI_DATA_GPIO gpio_num_t::GPIO_NUM_27 #define DEFAULT_SPI_CLOCK_GPIO gpio_num_t::GPIO_NUM_32 +#define DEFAULT_SPI_CS_GPIO gpio_num_t::GPIO_NUM_0 #define DEFAULT_SPI_DEVICE VSPI_HOST #define DEFAULT_I2C_SDA gpio_num_t::GPIO_NUM_3 @@ -47,6 +48,7 @@ #define SUPPORT_OutputType_APA102 // SPI #define SUPPORT_OutputType_DMX // UART / RMT #define SUPPORT_OutputType_GECE // UART / RMT +#define SUPPORT_OutputType_GRINCH // SPI #define SUPPORT_OutputType_GS8208 // UART / RMT #define SUPPORT_OutputType_Renard // UART / RMT #define SUPPORT_OutputType_Serial // UART / RMT diff --git a/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_DevkitC.hpp b/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_DevkitC.hpp index 34d3ae273..7ee5b32b3 100644 --- a/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_DevkitC.hpp +++ b/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_DevkitC.hpp @@ -31,6 +31,7 @@ #define SUPPORT_SPI_OUTPUT #define DEFAULT_SPI_DATA_GPIO gpio_num_t::GPIO_NUM_16 #define DEFAULT_SPI_CLOCK_GPIO gpio_num_t::GPIO_NUM_17 +#define DEFAULT_SPI_CS_GPIO gpio_num_t::GPIO_NUM_0 #define DEFAULT_SPI_DEVICE VSPI_HOST #define DEFAULT_I2C_SDA gpio_num_t::GPIO_NUM_3 @@ -50,6 +51,7 @@ #define SUPPORT_OutputType_APA102 // SPI #define SUPPORT_OutputType_DMX // UART / RMT #define SUPPORT_OutputType_GECE // UART +#define SUPPORT_OutputType_GRINCH // SPI #define SUPPORT_OutputType_GS8208 // UART / RMT #define SUPPORT_OutputType_Renard // UART / RMT #define SUPPORT_OutputType_Serial // UART / RMT diff --git a/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_LoLin_D32_PRO.hpp b/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_LoLin_D32_PRO.hpp index 88b380b13..8147da650 100644 --- a/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_LoLin_D32_PRO.hpp +++ b/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_LoLin_D32_PRO.hpp @@ -28,6 +28,7 @@ #define SUPPORT_SPI_OUTPUT #define DEFAULT_SPI_DATA_GPIO gpio_num_t::GPIO_NUM_27 #define DEFAULT_SPI_CLOCK_GPIO gpio_num_t::GPIO_NUM_32 +#define DEFAULT_SPI_CS_GPIO gpio_num_t::GPIO_NUM_0 #define DEFAULT_SPI_DEVICE VSPI_HOST #define DEFAULT_I2C_SDA gpio_num_t::GPIO_NUM_3 @@ -47,6 +48,7 @@ #define SUPPORT_OutputType_APA102 // SPI #define SUPPORT_OutputType_DMX // UART / RMT #define SUPPORT_OutputType_GECE // UART / RMT +#define SUPPORT_OutputType_GRINCH // SPI #define SUPPORT_OutputType_GS8208 // UART / RMT #define SUPPORT_OutputType_Renard // UART / RMT #define SUPPORT_OutputType_Serial // UART / RMT diff --git a/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_MH_ET_LIVE_MiniKit.hpp b/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_MH_ET_LIVE_MiniKit.hpp index 6812e4ce6..629da7cce 100644 --- a/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_MH_ET_LIVE_MiniKit.hpp +++ b/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_MH_ET_LIVE_MiniKit.hpp @@ -33,6 +33,7 @@ #define SUPPORT_SPI_OUTPUT #define DEFAULT_SPI_DATA_GPIO gpio_num_t::GPIO_NUM_16 #define DEFAULT_SPI_CLOCK_GPIO gpio_num_t::GPIO_NUM_17 +#define DEFAULT_SPI_CS_GPIO gpio_num_t::GPIO_NUM_0 #define DEFAULT_SPI_DEVICE VSPI_HOST #define DEFAULT_I2C_SDA gpio_num_t::GPIO_NUM_21 @@ -51,6 +52,7 @@ #define SUPPORT_OutputType_DMX // UART #define SUPPORT_OutputType_GECE // UART #define SUPPORT_OutputType_GS8208 // UART / RMT +#define SUPPORT_OutputType_GRINCH // SPI #define SUPPORT_OutputType_Renard // UART #define SUPPORT_OutputType_Serial // UART #define SUPPORT_OutputType_TM1814 // UART / RMT diff --git a/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_Tetra2go.hpp b/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_Tetra2go.hpp index d42a5cc9e..61613f533 100644 --- a/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_Tetra2go.hpp +++ b/ESPixelStick/src/platformDefinitions/GPIO_Defs_ESP32_Tetra2go.hpp @@ -28,6 +28,7 @@ #define SUPPORT_SPI_OUTPUT #define DEFAULT_SPI_DATA_GPIO gpio_num_t::GPIO_NUM_15 #define DEFAULT_SPI_CLOCK_GPIO gpio_num_t::GPIO_NUM_25 +#define DEFAULT_SPI_CS_GPIO gpio_num_t::GPIO_NUM_0 #define DEFAULT_SPI_DEVICE VSPI_HOST #define DEFAULT_I2C_SDA gpio_num_t::GPIO_NUM_21 @@ -46,6 +47,7 @@ #define SUPPORT_OutputType_DMX // UART #define SUPPORT_OutputType_GECE // UART #define SUPPORT_OutputType_GS8208 // UART / RMT +#define SUPPORT_OutputType_GRINCH // SPI #define SUPPORT_OutputType_Renard // UART #define SUPPORT_OutputType_Serial // UART #define SUPPORT_OutputType_TM1814 // UART / RMT diff --git a/html/grinch.html b/html/grinch.html new file mode 100644 index 000000000..a639e88d4 --- /dev/null +++ b/html/grinch.html @@ -0,0 +1,14 @@ +
+ Grinch Configuration +
+ +
+ +
+
+ + +
+ +
+
diff --git a/html/script.js b/html/script.js index 9abae4194..de4edbced 100644 --- a/html/script.js +++ b/html/script.js @@ -324,7 +324,7 @@ $(function () { SetServerTime(); }); -function SetServerTime() +function SetServerTime() { // console.info("SetServerTime"); let CurrentDate = Math.floor((new Date()).getTime() / 1000); @@ -368,7 +368,7 @@ function MergeConfig(SourceData, TargetData, FileName, SectionName) } // MergeConfig -function JsonObjectAccess(obj, Path, value, Action) +function JsonObjectAccess(obj, Path, value, Action) { try { @@ -630,11 +630,11 @@ async function RequestConfigFile(FileName) } // RequestConfigFile -function RequestStatusUpdate() +function RequestStatusUpdate() { // console.log("RequestStatusUpdate Start: "); // is the timer running? - if (null === StatusRequestTimer) + if (null === StatusRequestTimer) { // timer runs forever StatusRequestTimer = setTimeout(function () @@ -647,7 +647,7 @@ function RequestStatusUpdate() }, 1000); } // end timer was not running - if ($('#home').is(':visible')) + if ($('#home').is(':visible')) { // ask for a status update from the server let FileName = "HTTP://" + target + "/XJ"; @@ -666,7 +666,7 @@ function RequestStatusUpdate() } // RequestStatusUpdate -async function RequestListOfFiles() +async function RequestListOfFiles() { // console.info("ask for a file list from the server, starting at " + StartingFileIndex); @@ -1139,6 +1139,16 @@ function ProcessModeConfigurationDataRelay(RelayConfig) { } // ProcessModeConfigurationDataRelay +function ProcessModeConfigurationDataGrinch(GrinchConfig) +{ + // console.log("ProcessModeConfigurationDataGrinch"); + // console.info("GrinchConfig: " + JSON.stringify(GrinchConfig)); + + $('#grinch #controller_count' ).val(GrinchConfig.count); + $('#grinch #cs_pin' ).val(GrinchConfig.cs_pin); + +} // ProcessModeConfigurationDataGrinch + function ProcessModeConfigurationDataServoPCA9685(ServoConfig) { // console.log("Servochannelconfigurationtable.rows.length = " + $('#servo_pca9685channelconfigurationtable tr').length); @@ -1213,7 +1223,9 @@ function ProcessInputConfig() { function ProcessModeConfigurationData(channelId, ChannelType, JsonConfig) { // console.info("ProcessModeConfigurationData: Start"); - + // console.info("channelId: " + channelId); + // console.info("ChannelType: " + ChannelType); + // console.info("JsonConfig: " + JSON.stringify(JsonConfig)); // determine the type of in/output that has been selected and populate the form let TypeOfChannelId = parseInt($('#' + ChannelType + channelId + " option:selected").val(), 10); let channelConfigSet = JsonConfig.channels[channelId]; @@ -1279,6 +1291,11 @@ function ProcessModeConfigurationData(channelId, ChannelType, JsonConfig) { ProcessModeConfigurationDataServoPCA9685(channelConfig); } + else if ("grinch" === ChannelTypeName) { + // console.info("ProcessModeConfigurationData: grinch"); + ProcessModeConfigurationDataGrinch(channelConfig); + } + UpdateAdvancedOptionsMode(); UpdateChannelCounts(); @@ -1698,6 +1715,11 @@ function ExtractChannelConfigFromHtmlPage(JsonConfig, SectionName) { ChannelConfig.MarqueeGroups = MarqueeGroups; } + else if(ChannelConfig.type === "Grinch") + { + ChannelConfig.count = $('#grinch #controller_count' ).val(); + ChannelConfig.cs_pin = $('#grinch #cs_pin' ).val(); + } else { ExtractConfigFromHtmlPages(elementids, modeControlName, ChannelConfig); } @@ -1785,7 +1807,7 @@ function int2ip(num) { } // int2ip // Ping every 4sec -function MonitorServerConnection() +function MonitorServerConnection() { // console.info("MonitorServerConnection"); let MonitorTransactionRequestInProgress = false; diff --git a/platformio.ini b/platformio.ini index 109b01d98..408d2363a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -24,12 +24,12 @@ lib_compat_mode = strict lib_deps = adafruit/Adafruit PWM Servo Driver Library @ 2.4.0 https://github.com/bblanchon/ArduinoJson @ 7.1.0 - djgrrr/Int64String @ 1.1.1 + https://github.com/djGrrr/Int64String @ 1.1.1 https://github.com/esphome/ESPAsyncWebServer#4fd0a1fdf421664214a27373c0eb0247f94b7a79 https://github.com/forkineye/ESPAsyncE131 ottowinter/AsyncMqttClient-esphome @ 0.8.6 https://github.com/MartinMueller2003/Artnet - https://github.com/MartinMueller2003/Espalexa ; pull latest + https://github.com/MartinMueller2003/Espalexa https://github.com/PaulStoffregen/Time https://github.com/greiman/SdFat @ 2.2.3 https://github.com/MartinMueller2003/SimpleFTPServer