diff --git a/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp b/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp index 88c0e2ae59..ce52a44d53 100644 --- a/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp +++ b/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp @@ -111,6 +111,9 @@ @param cs Chip select pin # (OK to pass -1 if CS tied to GND). @param dc Data/Command pin # (required). @param rst Reset pin # (optional, pass -1 if unused). + @param model The display model to initiailize. + @param w Widht of the display in pixels. + @param h Height of the display in pixels. */ /**************************************************************************/ @@ -133,14 +136,20 @@ Adafruit_ILI9341::Adafruit_ILI9341(int8_t cs, int8_t dc, int8_t rst, uint8_t mod @param cs Chip select pin # (optional, pass -1 if unused and CS is tied to GND). @param rst Reset pin # (optional, pass -1 if unused). + @param model The display model to initiailize. + @param w Widht of the display in pixels. + @param h Height of the display in pixels. */ /**************************************************************************/ -// Adafruit_ILI9341::Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs, -// int8_t rst) -// : Adafruit_SPITFT(ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT, spiClass, cs, dc, -// rst) {} +Adafruit_ILI9341::Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs, + int8_t rst, uint8_t model, uint16_t w, uint16_t h) + : Adafruit_SPITFT(w, h, spiClass, cs, dc, rst) { + _model = model; + _w = w; + _h = h; +} #endif // end !ESP8266 /**************************************************************************/ diff --git a/lib/Adafruit_ILI9341/Adafruit_ILI9341.h b/lib/Adafruit_ILI9341/Adafruit_ILI9341.h index 3493237007..68735d225e 100644 --- a/lib/Adafruit_ILI9341/Adafruit_ILI9341.h +++ b/lib/Adafruit_ILI9341/Adafruit_ILI9341.h @@ -163,8 +163,8 @@ class Adafruit_ILI9341 : public Adafruit_SPITFT { #if !defined(ESP8266) - // Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs = -1, - // int8_t rst = -1); + Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs = -1, + int8_t rst = -1, uint8_t model = 0, uint16_t w = 0, uint16_t h = 0); #endif // end !ESP8266 // Adafruit_ILI9341(tftBusWidth busWidth, int8_t d0, int8_t wr, int8_t dc, // int8_t cs = -1, int8_t rst = -1, int8_t rd = -1); diff --git a/lib/Itho/CC1101.cpp b/lib/Itho/CC1101.cpp index 1fb2de787e..7a81ffbd18 100644 --- a/lib/Itho/CC1101.cpp +++ b/lib/Itho/CC1101.cpp @@ -5,7 +5,7 @@ #include "CC1101.h" // default constructor -CC1101::CC1101(int8_t CSpin, int8_t MISOpin) : _CSpin(CSpin), _MISOpin(MISOpin) +CC1101::CC1101(int8_t CSpin, int8_t MISOpin, SPIClass& spi) : _CSpin(CSpin), _MISOpin(MISOpin), _spi(spi) { // SPI.begin(); // Done already by ESPEasy pinMode(_CSpin, OUTPUT); @@ -51,7 +51,7 @@ void CC1101::reset() select(); spi_waitMiso(); - SPI.transfer(CC1101_SRES); + _spi.transfer(CC1101_SRES); delay(10); spi_waitMiso(); deselect(); @@ -63,7 +63,7 @@ uint8_t CC1101::writeCommand(uint8_t command) select(); spi_waitMiso(); - result = SPI.transfer(command); + result = _spi.transfer(command); deselect(); return result; @@ -73,8 +73,8 @@ void CC1101::writeRegister(uint8_t address, uint8_t data) { select(); spi_waitMiso(); - SPI.transfer(address); - SPI.transfer(data); + _spi.transfer(address); + _spi.transfer(data); deselect(); } @@ -84,8 +84,8 @@ uint8_t CC1101::readRegister(uint8_t address) select(); spi_waitMiso(); - SPI.transfer(address); - val = SPI.transfer(0); + _spi.transfer(address); + val = _spi.transfer(0); deselect(); return val; @@ -97,12 +97,12 @@ uint8_t CC1101::readRegisterMedian3(uint8_t address) select(); spi_waitMiso(); - SPI.transfer(address); - val1 = SPI.transfer(0); - SPI.transfer(address); - val2 = SPI.transfer(0); - SPI.transfer(address); - val3 = SPI.transfer(0); + _spi.transfer(address); + val1 = _spi.transfer(0); + _spi.transfer(address); + val2 = _spi.transfer(0); + _spi.transfer(address); + val3 = _spi.transfer(0); deselect(); // reverse sort (largest in val1) because this is te expected order for TX_BUFFER @@ -162,10 +162,10 @@ void CC1101::writeBurstRegister(uint8_t address, uint8_t *data, uint8_t length) select(); spi_waitMiso(); - SPI.transfer(address | CC1101_WRITE_BURST); + _spi.transfer(address | CC1101_WRITE_BURST); for (i = 0; i < length; i++) { - SPI.transfer(pgm_read_byte(&(data[i]))); + _spi.transfer(pgm_read_byte(&(data[i]))); } deselect(); } @@ -176,10 +176,10 @@ void CC1101::readBurstRegister(uint8_t *buffer, uint8_t address, uint8_t length) select(); spi_waitMiso(); - SPI.transfer(address | CC1101_READ_BURST); + _spi.transfer(address | CC1101_READ_BURST); for (i = 0; i < length; i++) { - buffer[i] = SPI.transfer(0x00); + buffer[i] = _spi.transfer(0x00); } deselect(); diff --git a/lib/Itho/CC1101.h b/lib/Itho/CC1101.h index 0fd489319e..8c01d31f3c 100644 --- a/lib/Itho/CC1101.h +++ b/lib/Itho/CC1101.h @@ -191,8 +191,9 @@ class CC1101 { public: - CC1101(int8_t CSpin = PIN_SPI_SS, - int8_t MISOpin = MISO); + CC1101(int8_t CSpin = PIN_SPI_SS, + int8_t MISOpin = MISO, + SPIClass& spi = SPI); virtual ~CC1101(); // spi @@ -230,6 +231,7 @@ class CC1101 { int8_t _CSpin = PIN_SPI_SS; int8_t _MISOpin; + SPIClass& _spi = SPI; protected: diff --git a/lib/Itho/IthoCC1101.cpp b/lib/Itho/IthoCC1101.cpp index e9a27dd7f9..dbd2270728 100644 --- a/lib/Itho/IthoCC1101.cpp +++ b/lib/Itho/IthoCC1101.cpp @@ -42,7 +42,7 @@ #define MDMCFG2 0x02 // 16bit sync word / 16bit specific // default constructor -IthoCC1101::IthoCC1101(int8_t CSpin, int8_t MISOpin, uint8_t counter, uint8_t sendTries) : CC1101(CSpin, MISOpin) +IthoCC1101::IthoCC1101(int8_t CSpin, int8_t MISOpin, SPIClass& spi, uint8_t counter, uint8_t sendTries) : CC1101(CSpin, MISOpin, spi) { this->outIthoPacket.counter = counter; this->sendTries = sendTries; diff --git a/lib/Itho/IthoCC1101.h b/lib/Itho/IthoCC1101.h index 399f279c2e..17c9a9fabb 100644 --- a/lib/Itho/IthoCC1101.h +++ b/lib/Itho/IthoCC1101.h @@ -78,10 +78,11 @@ class IthoCC1101 : protected CC1101 { public: - IthoCC1101(int8_t CSpin = PIN_SPI_SS, - int8_t MISOpin = MISO, - uint8_t counter = 0, - uint8_t sendTries = 3); // set initial counter value + IthoCC1101(int8_t CSpin = PIN_SPI_SS, + int8_t MISOpin = MISO, + SPIClass& spi = SPI, + uint8_t counter = 0, + uint8_t sendTries = 3); // set initial counter value ~IthoCC1101(); // init @@ -90,7 +91,8 @@ class IthoCC1101 : protected CC1101 { initReceive(); } // init,reset CC1101 - void initReceive(); + void initReceive(); + // uint8_t getLastCounter() const { // return outIthoPacket.counter; // } // counter is increased before sending a command @@ -104,7 +106,7 @@ class IthoCC1101 : protected CC1101 { } // receive - bool checkForNewPacket(); // check RX fifo for new data + bool checkForNewPacket(); // check RX fifo for new data // IthoPacket getLastPacket() const { // return inIthoPacket; // } // retrieve last received/parsed packet from remote @@ -120,14 +122,15 @@ class IthoCC1101 : protected CC1101 { // uint8_t ReadRSSI(); // bool checkID(const uint8_t *id) const; // int* getLastID(); - String getLastIDstr(bool ashex = true); + String getLastIDstr(bool ashex = true); + // String getLastMessagestr(bool ashex = true); // String LastMessageDecoded() const; // send - void sendCommand(IthoCommand command, - uint8_t srcId[3] = 0, - uint8_t destId[3] = 0); + void sendCommand(IthoCommand command, + uint8_t srcId[3] = 0, + uint8_t destId[3] = 0); void enableOrcon(bool state); diff --git a/lib/LOLIN_EPD/src/LOLIN_EPD.cpp b/lib/LOLIN_EPD/src/LOLIN_EPD.cpp index ce160c9111..4bfcb03356 100644 --- a/lib/LOLIN_EPD/src/LOLIN_EPD.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_EPD.cpp @@ -16,7 +16,7 @@ #include "Adafruit_GFX.h" #include "LOLIN_EPD.h" -LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t spi_mosi, int8_t spi_clock, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY):Adafruit_GFX(width, height) +LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t spi_mosi, int8_t spi_clock, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi):Adafruit_GFX(width, height) { cs = CS; rst = RST; @@ -26,9 +26,10 @@ LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t spi_mosi, int8_t spi_clock, i busy = BUSY; hwSPI = false; singleByteTxns = false; + _spi = spi; } -LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY):Adafruit_GFX(width, height) +LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi):Adafruit_GFX(width, height) { dc = DC; @@ -37,6 +38,7 @@ LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, in busy = BUSY; hwSPI = true; singleByteTxns = false; + _spi = spi; } /**************************************************************************/ @@ -90,9 +92,9 @@ void LOLIN_EPD::begin(bool reset) } else { - SPI.begin(); + // _spi.begin(); // Already done by ESPEasy #ifndef SPI_HAS_TRANSACTION - SPI.setClockDivider(4); + _spi.setClockDivider(4); #endif } @@ -194,12 +196,12 @@ uint8_t LOLIN_EPD::fastSPIwrite(uint8_t d) { uint8_t b; csLow(); - b = SPI.transfer(d); + b = _spi.transfer(d); csHigh(); return b; } else - return SPI.transfer(d); + return _spi.transfer(d); } else { @@ -234,7 +236,7 @@ uint8_t LOLIN_EPD::fastSPIwrite(uint8_t d) void LOLIN_EPD::csHigh() { #ifdef SPI_HAS_TRANSACTION - SPI.endTransaction(); + _spi.endTransaction(); #endif #ifdef HAVE_PORTREG *csport |= cspinmask; @@ -251,7 +253,7 @@ void LOLIN_EPD::csHigh() void LOLIN_EPD::csLow() { #ifdef SPI_HAS_TRANSACTION - SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); + _spi.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); #endif #ifdef HAVE_PORTREG *csport &= ~cspinmask; diff --git a/lib/LOLIN_EPD/src/LOLIN_EPD.h b/lib/LOLIN_EPD/src/LOLIN_EPD.h index dacd000716..677d82c94e 100644 --- a/lib/LOLIN_EPD/src/LOLIN_EPD.h +++ b/lib/LOLIN_EPD/src/LOLIN_EPD.h @@ -60,8 +60,8 @@ enum class LOLIN_EPD: public Adafruit_GFX { public: - LOLIN_EPD(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_EPD(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); virtual ~LOLIN_EPD(); virtual void begin(bool reset = true); @@ -111,6 +111,7 @@ class LOLIN_EPD: public Adafruit_GFX void dcLow(); private: + SPIClass& _spi = SPI; }; #include "LOLIN_IL3897.h" diff --git a/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp b/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp index 8cb34af916..764e6e3b0c 100644 --- a/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp @@ -184,7 +184,7 @@ const unsigned char LUT_DATA_part[] PROGMEM = { @param BUSY the busy pin to use */ /**************************************************************************/ -LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY) +LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) @@ -202,7 +202,7 @@ LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_ red_bufsize = bw_bufsize; } -LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, DC, RST, CS, BUSY) +LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) diff --git a/lib/LOLIN_EPD/src/LOLIN_IL3897.h b/lib/LOLIN_EPD/src/LOLIN_IL3897.h index a461f51e86..77847c7fbb 100644 --- a/lib/LOLIN_EPD/src/LOLIN_IL3897.h +++ b/lib/LOLIN_EPD/src/LOLIN_IL3897.h @@ -20,8 +20,8 @@ class LOLIN_IL3897 : public LOLIN_EPD { public: - LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); void begin(bool reset=true); diff --git a/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp b/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp index f51bdf3ee4..b9037a1dfd 100644 --- a/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp @@ -21,7 +21,7 @@ @param BUSY the busy pin to use */ /**************************************************************************/ -LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY) +LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) @@ -39,7 +39,7 @@ LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int red_bufsize = bw_bufsize; } -LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, DC, RST, CS, BUSY) +LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) diff --git a/lib/LOLIN_EPD/src/LOLIN_SSD1680.h b/lib/LOLIN_EPD/src/LOLIN_SSD1680.h index 9a7ebebcb2..47eeafcd17 100644 --- a/lib/LOLIN_EPD/src/LOLIN_SSD1680.h +++ b/lib/LOLIN_EPD/src/LOLIN_SSD1680.h @@ -19,8 +19,8 @@ class LOLIN_SSD1680 : public LOLIN_EPD { public: - LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); void begin(bool reset = true); diff --git a/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp b/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp index 737a22e434..cd7d48ea32 100644 --- a/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp @@ -21,7 +21,7 @@ @param BUSY the busy pin to use */ /**************************************************************************/ -LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY) +LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) @@ -39,7 +39,7 @@ LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int red_bufsize = bw_bufsize; } -LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, DC, RST, CS, BUSY) +LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) diff --git a/lib/LOLIN_EPD/src/LOLIN_UC8151D.h b/lib/LOLIN_EPD/src/LOLIN_UC8151D.h index f07603aa1d..a3d188d877 100644 --- a/lib/LOLIN_EPD/src/LOLIN_UC8151D.h +++ b/lib/LOLIN_EPD/src/LOLIN_UC8151D.h @@ -19,8 +19,8 @@ class LOLIN_UC8151D : public LOLIN_EPD { public: - LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); void begin(bool reset = true); diff --git a/lib/LOLIN_EPD/src/Waveshare_2in7.cpp b/lib/LOLIN_EPD/src/Waveshare_2in7.cpp index 12b73cee3d..2c16560613 100644 --- a/lib/LOLIN_EPD/src/Waveshare_2in7.cpp +++ b/lib/LOLIN_EPD/src/Waveshare_2in7.cpp @@ -76,8 +76,8 @@ static const unsigned char EPD_2in7_gray_lut_bb[] = { @param BUSY the busy pin to use */ -Waveshare_2in7::Waveshare_2in7(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) - : LOLIN_EPD(width, height, DC, RST, CS, BUSY) { +Waveshare_2in7::Waveshare_2in7(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) + : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) { _height_8bit = (height / 8 + 1) * 8; } else { @@ -247,7 +247,7 @@ void Waveshare_2in7::drawPixel(int16_t x, int16_t y, uint16_t color) { if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) { return; } - + // Corrections by @kretzp (Peter Kretz) 2022-03-27 // check rotation, move pixel around if necessary switch (getRotation()) { diff --git a/lib/LOLIN_EPD/src/Waveshare_2in7.h b/lib/LOLIN_EPD/src/Waveshare_2in7.h index d7f216dda1..7d3eda4b34 100644 --- a/lib/LOLIN_EPD/src/Waveshare_2in7.h +++ b/lib/LOLIN_EPD/src/Waveshare_2in7.h @@ -24,10 +24,10 @@ #define WS2IN7_DATA_STOP 0x11 #define WS2IN7_DISPLAY_REFRESH 0x12 #define WS2IN7_DATA_START_TRANSMISSION_2 0x13 -#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_1 0x14 -#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_2 0x15 +#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_1 0x14 +#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_2 0x15 #define WS2IN7_PARTIAL_DISPLAY_REFRESH 0x16 -#define WS2IN7_LUT_FOR_VCOM 0x20 +#define WS2IN7_LUT_FOR_VCOM 0x20 #define WS2IN7_LUT_WHITE_TO_WHITE 0x21 #define WS2IN7_LUT_BLACK_TO_WHITE 0x22 #define WS2IN7_LUT_WHITE_TO_BLACK 0x23 @@ -67,12 +67,13 @@ extern const unsigned char lut_wb[]; class Waveshare_2in7 : public LOLIN_EPD { public: - Waveshare_2in7(int width, - int height, - int8_t DC, - int8_t RST, - int8_t CS, - int8_t BUSY = -1); + Waveshare_2in7(int width, + int height, + int8_t DC, + int8_t RST, + int8_t CS, + int8_t BUSY = -1, + SPIClass& spi = SPI); void begin(bool reset = true); diff --git a/lib/MD_Parola/src/MD_Parola.h b/lib/MD_Parola/src/MD_Parola.h index f116b9e4ac..92e812bedf 100644 --- a/lib/MD_Parola/src/MD_Parola.h +++ b/lib/MD_Parola/src/MD_Parola.h @@ -1343,6 +1343,23 @@ class MD_Parola : public Print { _D(mod, csPin, numDevices), _numModules(numDevices) {} + /** + * Class constructor - SPI hardware interface. + * + * Instantiate a new instance of the class. The parameters passed are used to + * connect the software to the hardware using the MD_MAX72XX class. + * + * See documentation for the MD_MAX72XX library for detailed explanation of parameters. + * + * \param mod the hardware module type used in the application. One of the MD_MAX72XX::moduleType_t values. + * \param spi the SPI interface to use + * \param csPin output for selecting the device. + * \param numDevices number of devices connected. Default is 1 if not supplied. + */ + MD_Parola(MD_MAX72XX::moduleType_t mod, SPIClass &spi, uint8_t csPin, uint8_t numDevices = 1) : + _D(mod, spi, csPin, numDevices), _numModules(numDevices) + {} + /** * Initialize the object. * diff --git a/lib/MFRC522/MFRC522.cpp b/lib/MFRC522/MFRC522.cpp index 0d6d81669f..748b754d4d 100644 --- a/lib/MFRC522/MFRC522.cpp +++ b/lib/MFRC522/MFRC522.cpp @@ -29,11 +29,12 @@ MFRC522::MFRC522( uint8_t resetPowerDownPin ///< Arduino pin connected to MFRC52 * Prepares the output pins. */ MFRC522::MFRC522( uint8_t chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) - uint8_t resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low). If there is no connection from the CPU to NRSTPD, set this to UINT8_MAX. In this case, only soft reset will be used in PCD_Init(). - ) { - _chipSelectPin = chipSelectPin; - _resetPowerDownPin = resetPowerDownPin; -} // End constructor + uint8_t resetPowerDownPin, ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low). If there is no connection from the CPU to NRSTPD, set this to UINT8_MAX. In this case, only soft reset will be used in PCD_Init(). + SPIClass& spi) : + _chipSelectPin(chipSelectPin), + _resetPowerDownPin(resetPowerDownPin), + _spi(spi) +{} // End constructor ///////////////////////////////////////////////////////////////////////////////////// // Basic interface functions for communicating with the MFRC522 @@ -46,12 +47,12 @@ MFRC522::MFRC522( uint8_t chipSelectPin, ///< Arduino pin connected to MFRC522' void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to. One of the PCD_Register enums. uint8_t value ///< The value to write. ) { - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave - SPI.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. - SPI.transfer(value); + _spi.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + _spi.transfer(value); digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus } // End PCD_WriteRegister() /** @@ -62,14 +63,14 @@ void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to uint8_t count, ///< The number of bytes to write to the register uint8_t *values ///< The values to write. Byte array. ) { - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave - SPI.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + _spi.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. for (uint8_t index = 0; index < count; index++) { - SPI.transfer(values[index]); + _spi.transfer(values[index]); } digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus } // End PCD_WriteRegister() /** @@ -79,12 +80,12 @@ void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to uint8_t MFRC522::PCD_ReadRegister( PCD_Register reg ///< The register to read from. One of the PCD_Register enums. ) { uint8_t value; - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave - SPI.transfer(0x80 | reg); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. - value = SPI.transfer(0); // Read the value back. Send 0 to stop reading. + _spi.transfer(0x80 | reg); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. + value = _spi.transfer(0); // Read the value back. Send 0 to stop reading. digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus return value; } // End PCD_ReadRegister() @@ -103,26 +104,26 @@ void MFRC522::PCD_ReadRegister( PCD_Register reg, ///< The register to read from //Serial.print(F("Reading ")); Serial.print(count); Serial.println(F(" bytes from register.")); uint8_t address = 0x80 | reg; // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. uint8_t index = 0; // Index in values array. - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave count--; // One read is performed outside of the loop - SPI.transfer(address); // Tell MFRC522 which address we want to read + _spi.transfer(address); // Tell MFRC522 which address we want to read if (rxAlign) { // Only update bit positions rxAlign..7 in values[0] // Create bit mask for bit positions rxAlign..7 uint8_t mask = (0xFF << rxAlign) & 0xFF; // Read value and tell that we want to read the same address again. - uint8_t value = SPI.transfer(address); + uint8_t value = _spi.transfer(address); // Apply mask to both current value of values[0] and the new data in value. values[0] = (values[0] & ~mask) | (value & mask); index++; } while (index < count) { - values[index] = SPI.transfer(address); // Read value and tell that we want to read the same address again. + values[index] = _spi.transfer(address); // Read value and tell that we want to read the same address again. index++; } - values[index] = SPI.transfer(0); // Read the final byte. Send 0 to stop reading. + values[index] = _spi.transfer(0); // Read the final byte. Send 0 to stop reading. digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus } // End PCD_ReadRegister() /** diff --git a/lib/MFRC522/MFRC522.h b/lib/MFRC522/MFRC522.h index 0a7f0e4262..15479d540a 100644 --- a/lib/MFRC522/MFRC522.h +++ b/lib/MFRC522/MFRC522.h @@ -293,7 +293,7 @@ class MFRC522 { ///////////////////////////////////////////////////////////////////////////////////// MFRC522(); MFRC522(uint8_t resetPowerDownPin); - MFRC522(uint8_t chipSelectPin, uint8_t resetPowerDownPin); + MFRC522(uint8_t chipSelectPin, uint8_t resetPowerDownPin, SPIClass& spi = SPI); virtual ~MFRC522() {}; ///////////////////////////////////////////////////////////////////////////////////// @@ -389,6 +389,7 @@ class MFRC522 { protected: uint8_t _chipSelectPin; // Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) uint8_t _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + SPIClass& _spi = SPI; StatusCode MIFARE_TwoStepHelper(uint8_t command, uint8_t blockAddr, int32_t data); }; diff --git a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp index cdf22b23aa..4ebfe04150 100644 --- a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp +++ b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp @@ -22,7 +22,6 @@ #include "Arduino.h" #include "SparkFun_ADXL345.h" #include -#include #define ADXL345_DEVICE_DEFAULT (0x53) // Device Address for ADXL345 #define ADXL345_DEVICE (_i2c_addr) // Device Address for ADXL345 @@ -38,7 +37,7 @@ ADXL345::ADXL345(uint8_t i2c_addr = ADXL345_DEVICE_DEFAULT) : _i2c_addr(i2c_addr I2C = true; } -ADXL345::ADXL345(int CS) { +ADXL345::ADXL345(int CS, SPIClass& spi) { status = ADXL345_OK; error_code = ADXL345_NO_ERROR; @@ -47,9 +46,12 @@ ADXL345::ADXL345(int CS) { gains[2] = 0.00349265; _CS = CS; I2C = false; - // tonhuisman: disabled as SPI is already initialized in ESPEasy core. + + // 2021-12-13 tonhuisman: disabled as SPI is already initialized in ESPEasy core. // SPI.begin(); // SPI.setDataMode(SPI_MODE3); + // 2025-08-13 tonhuisman: provide external SPI bus + _spi = spi; pinMode(_CS, OUTPUT); digitalWrite(_CS, HIGH); } @@ -168,8 +170,8 @@ void ADXL345::readFromI2C(byte address, int num, byte _buff[]) { /* Point to Destination; Write Value; Turn Off */ void ADXL345::writeToSPI(byte __reg_address, byte __val) { digitalWrite(_CS, LOW); - SPI.transfer(__reg_address); - SPI.transfer(__val); + _spi.transfer(__reg_address); + _spi.transfer(__val); digitalWrite(_CS, HIGH); } @@ -185,10 +187,10 @@ void ADXL345::readFromSPI(byte __reg_address, int num, byte _buff[]) { } digitalWrite(_CS, LOW); - SPI.transfer(_address); // Transfer Starting Reg Address To Be Read + _spi.transfer(_address); // Transfer Starting Reg Address To Be Read for (int i = 0; i < num; i++) { - _buff[i] = SPI.transfer(0x00); + _buff[i] = _spi.transfer(0x00); } digitalWrite(_CS, HIGH); } diff --git a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h index c8cfb77501..0de0b52612 100644 --- a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h +++ b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h @@ -21,6 +21,8 @@ #ifndef ADXL345_h # define ADXL345_h +# include + /*************************** REGISTER MAP ***************************/ # define ADXL345_DEVID 0x00 // Device ID # define ADXL345_RESERVED1 0x01 // Reserved. Do Not Access. @@ -115,7 +117,8 @@ class ADXL345 { double gains[3]; // Counts to Gs ADXL345(uint8_t i2c_addr); - ADXL345(int CS); + ADXL345(int CS, + SPIClass& spi = SPI); void powerOn(); void powerOff(); int getDevID(); @@ -266,6 +269,7 @@ class ADXL345 { bool I2C = true; unsigned long SPIfreq = 5000000; uint8_t _i2c_addr; + SPIClass& _spi = SPI; }; void print_byte(byte val); #endif // ifndef ADXL345_h diff --git a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp index 75ba44ee5d..f884ffbfb2 100644 --- a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp +++ b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp @@ -32,7 +32,7 @@ void isrPin(void); bool XPT2046_Touchscreen::begin() { - SPI.begin(); + // _spi.begin(); // ESPEasy already does this pinMode(csPin, OUTPUT); digitalWrite(csPin, HIGH); if (255 != tirqPin) { @@ -105,25 +105,25 @@ void XPT2046_Touchscreen::update() uint32_t now = millis(); if (now - msraw < MSEC_THRESHOLD) return; - SPI.beginTransaction(SPI_SETTING); + _spi.beginTransaction(SPI_SETTING); digitalWrite(csPin, LOW); - SPI.transfer(0xB1 /* Z1 */); - int16_t z1 = SPI.transfer16(0xC1 /* Z2 */) >> 3; + _spi.transfer(0xB1 /* Z1 */); + int16_t z1 = _spi.transfer16(0xC1 /* Z2 */) >> 3; int z = z1 + 4095; - int16_t z2 = SPI.transfer16(0x91 /* X */) >> 3; + int16_t z2 = _spi.transfer16(0x91 /* X */) >> 3; z -= z2; if (z >= Z_THRESHOLD) { - SPI.transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy - data[0] = SPI.transfer16(0xD1 /* Y */) >> 3; - data[1] = SPI.transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements - data[2] = SPI.transfer16(0xD1 /* Y */) >> 3; - data[3] = SPI.transfer16(0x91 /* X */) >> 3; + _spi.transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy + data[0] = _spi.transfer16(0xD1 /* Y */) >> 3; + data[1] = _spi.transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements + data[2] = _spi.transfer16(0xD1 /* Y */) >> 3; + data[3] = _spi.transfer16(0x91 /* X */) >> 3; } else data[0] = data[1] = data[2] = data[3] = 0; // Compiler warns these values may be used unset on early exit. - data[4] = SPI.transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down - data[5] = SPI.transfer16(0) >> 3; + data[4] = _spi.transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down + data[5] = _spi.transfer16(0) >> 3; digitalWrite(csPin, HIGH); - SPI.endTransaction(); + _spi.endTransaction(); //Serial.printf("z=%d :: z1=%d, z2=%d ", z, z1, z2); if (z < 0) z = 0; if (z < Z_THRESHOLD) { // if ( !touched ) { diff --git a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h index d723ca1343..878b15d7bb 100644 --- a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h +++ b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h @@ -41,8 +41,8 @@ class TS_Point { class XPT2046_Touchscreen { public: - constexpr XPT2046_Touchscreen(uint8_t cspin, uint8_t tirq=255) - : csPin(cspin), tirqPin(tirq) { } + constexpr XPT2046_Touchscreen(uint8_t cspin, SPIClass& spi = SPI, uint8_t tirq=255) + : csPin(cspin), _spi(spi), tirqPin(tirq) { } bool begin(); TS_Point getPoint(); bool tirqTouched(); @@ -57,8 +57,13 @@ class XPT2046_Touchscreen { private: void update(); - uint8_t csPin, tirqPin, rotation = 1; - int16_t xraw = 0, yraw = 0, zraw = 0; + uint8_t csPin; + SPIClass& _spi = SPI; + uint8_t tirqPin; + uint8_t rotation = 1; + int16_t xraw = 0; + int16_t yraw = 0; + int16_t zraw = 0; uint32_t msraw = 0x80000000; bool flipped = false; }; diff --git a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp index f3ae64fe3a..07cf8700b0 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp @@ -3,10 +3,12 @@ #ifdef USES_NW004 # include "../../../src/Globals/Settings.h" +# include "../../../src/Globals/SPIe.h" # include "../../../src/Helpers/ESPEasy_time_calc.h" # include "../../../src/Helpers/Hardware_GPIO.h" # include "../../../src/Helpers/LongTermOnOffTimer.h" +# include "../../../src/Helpers/SPI_Helper.h" # include "../../../src/Helpers/StringConverter.h" # include "../../../src/WebServer/Markup.h" @@ -37,8 +39,9 @@ namespace eth { # define NW004_KEY_GW 8 # define NW004_KEY_SN 9 # define NW004_KEY_DNS 10 +# define NW004_KEY_SPI_BUS 11 -# define NW004_MAX_KEY 11 +# define NW004_MAX_KEY 12 const __FlashStringHelper * NW004_data_struct_ETH_SPI::getLabelString(uint32_t key, bool displayString, KVS_StorageType::Enum& storageType) { @@ -52,6 +55,7 @@ const __FlashStringHelper * NW004_data_struct_ETH_SPI::getLabelString(uint32_t k case NW004_KEY_ETH_PIN_CS: return displayString ? F("Ethernet CS pin") : F("CS"); case NW004_KEY_ETH_PIN_IRQ: return displayString ? F("Ethernet IRQ pin") : F("IRQ"); case NW004_KEY_ETH_PIN_RST: return displayString ? F("Ethernet RST pin") : F("RST"); + case NW004_KEY_SPI_BUS: return displayString ? F("SPI Bus") : F("ethspibus"); case NW004_KEY_IP: storageType = KVS_StorageType::Enum::string_type; return F("IP"); @@ -225,6 +229,15 @@ void NW004_data_struct_ETH_SPI::webform_load(EventStruct *event) addFormNote(F("I²C-address of Ethernet PHY")); } { + if ((getSPIBusCount() > 1) && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) { + const int key = NW004_KEY_SPI_BUS; + const uint8_t spiBus = _kvs->getValueAsInt(key); + KVS_StorageType::Enum storageType; + SPIInterfaceSelector(getLabelString(key, true, storageType), + getLabelString(key, false, storageType), + spiBus); + } + const int gpio_keys[] = { NW004_KEY_ETH_PIN_CS, NW004_KEY_ETH_PIN_IRQ, @@ -302,7 +315,7 @@ bool NW004_data_struct_ETH_SPI::write_Eth_port(KeyValueWriter *writer) int8_t spi_gpios[3]{}; - if (!Settings.getSPI_pins(spi_gpios)) { return false; } + if (!Settings.getSPI_pins(spi_gpios), (uint8_t)_kvs->getValueAsInt(NW004_KEY_SPI_BUS)) { return false; } const __FlashStringHelper*labels[] = { F("CLK"), F("MISO"), F("MOSI"), F("CS"), F("IRQ"), F("RST") }; const int pins[] = { @@ -367,7 +380,7 @@ void NW004_data_struct_ETH_SPI::ethPrintSettings() { if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log; - if (log.reserve(115)) { + if (log.reserve(125)) { const ESPEasy::net::EthPhyType_t phyType = static_cast(_kvs->getValueAsInt(NW004_KEY_ETH_PHY_TYPE)); @@ -375,7 +388,8 @@ void NW004_data_struct_ETH_SPI::ethPrintSettings() { log += toString(phyType); log += F(" PHY Addr: "); log += _kvs->getValueAsInt(NW004_KEY_ETH_PHY_ADDR); - log += strformat(F(" CS: %d IRQ: %d RST: %d"), + log += strformat(F(" SPI bus: %d CS: %d IRQ: %d RST: %d"), + _kvs->getValueAsInt(NW004_KEY_SPI_BUS), _kvs->getValueAsInt(NW004_KEY_ETH_PIN_CS), _kvs->getValueAsInt(NW004_KEY_ETH_PIN_IRQ), _kvs->getValueAsInt(NW004_KEY_ETH_PIN_RST)); @@ -421,13 +435,14 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { const int rstPin = _kvs->getValueAsInt(NW004_KEY_ETH_PIN_RST); const int csPin = _kvs->getValueAsInt(NW004_KEY_ETH_PIN_CS); const int irqPin = _kvs->getValueAsInt(NW004_KEY_ETH_PIN_IRQ); + const int spi_bus = _kvs->getValueAsInt(NW004_KEY_SPI_BUS); - spi_host_device_t SPI_host = Settings.getSPI_host(); + spi_host_device_t SPI_host = Settings.getSPI_host(spi_bus); if (SPI_host == spi_host_device_t::SPI_HOST_MAX) { addLog(LOG_LEVEL_ERROR, F("SPI not enabled")); - # ifdef ESP32C3 + # ifdef ESP32C3 // FIXME TD-er: Fallback for ETH01-EVO board SPI_host = spi_host_device_t::SPI2_HOST; @@ -435,7 +450,7 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { Settings.SPI_SCLK_pin = 7; Settings.SPI_MISO_pin = 3; Settings.SPI_MOSI_pin = 10; - # endif // ifdef ESP32C3 + # endif // ifdef ESP32C3 } // else @@ -447,7 +462,7 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { csPin, irqPin, rstPin, - SPI); + SPI_host); # else // if ETH_SPI_SUPPORTS_CUSTOM success = iface->begin( to_ESP_phy_type(phyType), diff --git a/src/_P039_Thermosensors.ino b/src/_P039_Thermosensors.ino index 641840db65..590e0c9a74 100644 --- a/src/_P039_Thermosensors.ino +++ b/src/_P039_Thermosensors.ino @@ -19,6 +19,8 @@ // Have fun ... Dominik /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus + * 2025-08-13 tonhuisman: Move most code to P039_PluginStruct * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery * 2025-01-03 tonhuisman: Small code size reductions, cleanup of DEBUG level logging * 2024-01-04 tonhuisman: Minor corrections, formatted source using Uncrustify @@ -77,180 +79,11 @@ // #endif -# define MAX31865_RD_ADDRESS(n) (MAX31865_READ_ADDR_BASE + (n)) -# define MAX31865_WR_ADDRESS(n) (MAX31865_WRITE_ADDR_BASE + (n)) - # define PLUGIN_039 # define PLUGIN_ID_039 39 # define PLUGIN_NAME_039 "Environment - Thermosensors" # define PLUGIN_VALUENAME1_039 "Temperature" -# define P039_SET true -# define P039_RESET false - -// typically 500ns of wating on positive/negative edge of CS should be enough ( -> datasheet); to make sure we cover a lot of devices we -// spend 1ms -// FIX 2021-05-05: review of all covered device datasheets showed 2µs is more than enough; review with every newly added device -# define P039_CS_Delay() delayMicroseconds(2u) - -# define P039_MAX_TYPE PCONFIG(0) -# define P039_TC_TYPE PCONFIG(1) -# define P039_FAM_TYPE PCONFIG(2) -# define P039_RTD_TYPE PCONFIG(3) -# define P039_CONFIG_4 PCONFIG(4) -# define P039_RTD_FILT_TYPE PCONFIG(5) -# define P039_RTD_LM_TYPE PCONFIG(6) -# define P039_RTD_LM_SHTDWN PCONFIG(7) -# define P039_RTD_RES PCONFIG_LONG(0) -# define P039_FLAGS PCONFIG_ULONG(3) -# define P039_TEMP_THRESHOLD_FLAG 0 -# define P039_RTD_OFFSET PCONFIG_FLOAT(0) -# define P039_TEMP_THRESHOLD PCONFIG_FLOAT(1) - -# define P039_TEMP_THRESHOLD_DEFAULT (-273.15f) // Default and minimum value -# define P039_TEMP_THRESHOLD_MIN P039_TEMP_THRESHOLD_DEFAULT -# define P039_TEMP_THRESHOLD_MAX (1000.0f) // Max value -# define P039_TC 0u -# define P039_RTD 1u - -# define P039_MAX6675 1 -# define P039_MAX31855 2 -# define P039_MAX31856 3 -# define P039_MAX31865 4 -# define P039_LM7x 5 - -// MAX 6675 related defines - -// bit masks to identify failures for MAX 6675 -# define MAX6675_TC_DEVID 0x0002u -# define MAX6675_TC_OC 0x0004u - -// MAX 31855 related defines - -// bit masks to identify failures for MAX 31855 -# define MAX31855_TC_OC 0x00000001u -# define MAX31855_TC_SC 0x00000002u -# define MAX31855_TC_SCVCC 0x00000004u -# define MAX31855_TC_GENFLT 0x00010000u - - -// MAX 31856 related defines - -// base address for read/write acces to MAX 31856 -# define MAX31856_READ_ADDR_BASE 0x00u -# define MAX31856_WRITE_ADDR_BASE 0x80u - -// register offset values for MAX 31856 -# define MAX31856_CR0 0u -# define MAX31856_CR1 1u -# define MAX31856_MASK 2u -# define MAX31856_CJHF 3u -# define MAX31856_CJLF 4u -# define MAX31856_LTHFTH 5u -# define MAX31856_LTHFTL 6u -# define MAX31856_LTLFTH 7u -# define MAX31856_LTLFTL 8u -# define MAX31856_CJTO 9u -# define MAX31856_CJTH 10u -# define MAX31856_CJTL 11u -# define MAX31856_LTCBH 12u -# define MAX31856_LTCBM 13u -# define MAX31856_LTCBL 14u -# define MAX31856_SR 15u - -# define MAX31856_NO_REG 16u - -// bit masks to identify failures for MAX 31856 -# define MAX31856_TC_OC 0x01u -# define MAX31856_TC_OVUV 0x02u -# define MAX31856_TC_TCLOW 0x04u -# define MAX31856_TC_TCLHIGH 0x08u -# define MAX31856_TC_CJLOW 0x10u -# define MAX31856_TC_CJHIGH 0x20u -# define MAX31856_TC_TCRANGE 0x40u -# define MAX31856_TC_CJRANGE 0x80u - -// bit masks for access of configuration bits -# define MAX31856_SET_50HZ 0x01u -# define MAX31856_CLEAR_FAULTS 0x02u -# define MAX31856_FLT_ISR_MODE 0x04u -# define MAX31856_CJ_SENS_DISABLE 0x08u -# define MAX31856_FAULT_CTRL_MASK 0x30u -# define MAX31856_SET_ONE_SHOT 0x40u -# define MAX31856_SET_CONV_AUTO 0x80u - - -// RTD related defines - -// MAX 31865 related defines - -// waiting time until "in sequence" conversion is ready (-> used in case device is set to shutdown in between call cycles) -// typically 70ms should be fine, according to datasheet maximum -> 66ms - give a little adder to "be sure" conversion is done -// alternatively ONE SHOT bit could be polled (system/SPI bus load !) -# define MAX31865_CONVERSION_TIME 70ul -# define MAX31865_BIAS_WAIT_TIME 10ul - -// MAX 31865 Main States -# define MAX31865_INIT_STATE 0u -# define MAX31865_BIAS_ON_STATE 1u -# define MAX31865_RD_STATE 2u -# define MAX31865_RDY_STATE 3u - -// sensor type -# define MAX31865_PT100 0u -# define MAX31865_PT1000 1u - -// base address for read/write acces to MAX 31865 -# define MAX31865_READ_ADDR_BASE 0x00u -# define MAX31865_WRITE_ADDR_BASE 0x80u - -// register offset values for MAX 31865 -# define MAX31865_CONFIG 0u -# define MAX31865_RTD_MSB 1u -# define MAX31865_RTD_LSB 2u -# define MAX31865_HFT_MSB 3u -# define MAX31865_HFT_LSB 4u -# define MAX31865_LFT_MSB 5u -# define MAX31865_LFT_LSB 6u -# define MAX31865_FAULT 7u - -// total number of registers in MAX 31865 -# define MAX31865_NO_REG 8u - -// bit masks to identify failures for MAX 31865 -# define MAX31865_FAULT_HIGHTHRESH 0x80u -# define MAX31865_FAULT_LOWTHRESH 0x40u -# define MAX31865_FAULT_REFINLOW 0x20u -# define MAX31865_FAULT_REFINHIGH 0x10u -# define MAX31865_FAULT_RTDINLOW 0x08u -# define MAX31865_FAULT_OVUV 0x04u - -// bit masks for access of configuration bits -# define MAX31865_SET_50HZ 0x01u -# define MAX31865_CLEAR_FAULTS 0x02u -# define MAX31865_FAULT_CTRL_MASK 0x0Cu -# define MAX31865_SET_3WIRE 0x10u -# define MAX31865_SET_ONE_SHOT 0x20u -# define MAX31865_SET_CONV_AUTO 0x40u -# define MAX31865_SET_VBIAS_ON 0x80u - -// LM7x related defines - -// LM7x subtype defines -# define LM7x_SD70 0x00u -# define LM7x_SD71 0x01u -# define LM7x_SD74 0x04u -# define LM7x_SD121 0x05u -# define LM7x_SD122 0x06u -# define LM7x_SD123 0x07u -# define LM7x_SD124 0x08u -# define LM7x_SD125 0x09u - -// bit masks for access of configuration bits -# define LM7x_CONV_RDY 0x02u - - -void P039_AddMainsFrequencyFilterSelection(struct EventStruct *event); boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) { @@ -269,6 +102,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) dev.SendDataOption = true; dev.TimerOption = true; dev.PluginStats = true; + dev.SpiBusSelect = true; break; } @@ -287,7 +121,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) # if FEATURE_MQTT_DISCOVER case PLUGIN_GET_DISCOVERY_VTYPES: { - success = getDiscoveryVType(event, Plugin_QueryVType_Temperature, 255, event->Par5);; + success = getDiscoveryVType(event, Plugin_QueryVType_Temperature, 255, event->Par5); break; } # endif // if FEATURE_MQTT_DISCOVER @@ -316,121 +150,12 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) } - initPluginTaskData(event->TaskIndex, new (std::nothrow) P039_data_struct()); + initPluginTaskData(event->TaskIndex, new (std::nothrow) P039_data_struct(event)); P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // set the slaveSelectPin as an output: - init_SPI_CS_Pin(CS_pin_no); - - // initialize SPI: - SPI.setHwCs(false); - SPI.begin(); - - // ensure MODE3 access to SPI device - SPI.setDataMode(SPI_MODE3); - - /* - if (P039_MAX_TYPE == P039_MAX6675) { - - // SPI.setBitOrder(MSBFIRST); - } - */ - if (P039_MAX_TYPE == P039_MAX31855) { - // SPI.setBitOrder(MSBFIRST); - - if (nullptr != P039_data) { - // FIXED: c.k.i. : moved static fault flag to instance data structure - P039_data->sensorFault = false; - } - } - - - if (P039_MAX_TYPE == P039_MAX31856) { - // init string - content accoring to inital implementation of P039 - MAX31856 read function - // write to Adress 0x80 - // activate 50Hz filter in CR0, choose averaging and TC type from configuration in CR1, activate OV/UV/OC faults, write defaults to - // CJHF, CJLF, LTHFTH, LTHFTL, LTLFTH, LTLFTL, CJTO - uint8_t sendBuffer[11] = - { 0x80, static_cast(P039_RTD_FILT_TYPE), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE), 0xFC, 0x7F, 0xC0, 0x7F, - 0xFF, 0x80, 0x00, 0x00 }; - - transfer_n_ByteSPI(CS_pin_no, 11, &sendBuffer[0]); - - if (nullptr != P039_data) { - // FIXED: c.k.i. : moved static fault flag to instance data structure - P039_data->sensorFault = false; - } - - // start on shot conversion for upcoming read cycle - change8BitRegister(CS_pin_no, - (MAX31856_READ_ADDR_BASE + MAX31856_CR0), - (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), - MAX31856_SET_ONE_SHOT, - P039_SET); - } - - - if (P039_MAX_TYPE == P039_MAX31865) { - // two step initialization buffer - uint8_t initSendBufferHFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; - uint8_t initSendBufferLFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; - - // write intially 0x00 to CONFIG register - write8BitRegister(CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), 0x00u); - - // activate 50Hz filter, clear all faults, no auto conversion, no conversion started - change8BitRegister(CS_pin_no, - MAX31865_RD_ADDRESS(MAX31865_CONFIG), - MAX31865_WR_ADDRESS(MAX31865_CONFIG), - MAX31865_SET_50HZ, - static_cast(P039_RTD_FILT_TYPE)); - - // configure 2/4-wire sensor connection as default - MAX31865_setConType(CS_pin_no, P039_CONFIG_4); - - // set HighFault Threshold - transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferHFTH[0]); - - // set LowFault Threshold - transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferLFTH[0]); - - // clear all faults - MAX31865_clearFaults(CS_pin_no); - - // activate BIAS short before read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_SET); - - if (nullptr != P039_data) { - // save current timer for next calculation - P039_data->timer = millis(); - - // start time to follow up on BIAS activation before starting the conversion - // and start conversion sequence via TIMER API - - Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); - } + if (nullptr != P039_data) { + success = P039_data->begin(event); } - - /* - if (P039_MAX_TYPE == P039_LM7x) - { - // TODO: c.k.i.: more detailed inits depending on the sub devices expected , e.g. TMP 122/124 - } - */ - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("P039 : %s : SPI Init - DONE"), getTaskDeviceName(event->TaskIndex).c_str())); - } - # endif // ifndef BUILD_NO_DEBUG - - success = true; break; } @@ -443,7 +168,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *Foptions[] = { F("Thermocouple"), F("RTD") }; const int FoptionValues[] = { P039_TC, P039_RTD }; constexpr size_t optionCount = NR_ELEMENTS(FoptionValues); - FormSelectorOptions selector( optionCount, Foptions, FoptionValues); + FormSelectorOptions selector(optionCount, Foptions, FoptionValues); selector.reloadonchange = true; selector.addFormSelector(F("Sensor Family Type"), F("famtype"), family); } @@ -457,7 +182,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options[] = { F("MAX 6675"), F("MAX 31855"), F("MAX 31856") }; const int optionValues[] = { P039_MAX6675, P039_MAX31855, P039_MAX31856 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector( optionCount, options, optionValues); + FormSelectorOptions selector(optionCount, options, optionValues); selector.reloadonchange = true; selector.addFormSelector(F("Adapter IC"), F("maxtype"), choice); } @@ -485,7 +210,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const int ToptionValues[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 12 }; constexpr size_t optionCount = NR_ELEMENTS(ToptionValues); - const FormSelectorOptions selector( optionCount, Toptions, ToptionValues); + const FormSelectorOptions selector(optionCount, Toptions, ToptionValues); selector.addFormSelector(F("Thermocouple type"), F("tctype"), P039_TC_TYPE); } { @@ -493,10 +218,10 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const int CoptionValues[] = { 0, 1, 2, 3, 4 }; constexpr size_t optionCount = NR_ELEMENTS(CoptionValues); const FormSelectorOptions selector(optionCount, Coptions, CoptionValues); - selector.addFormSelector(F("Averaging"), F("contype"), P039_CONFIG_4); + selector.addFormSelector(F("Averaging"), F("contype"), P039_CONFIG_4); addUnit(F("sample(s)")); } - P039_AddMainsFrequencyFilterSelection(event); + P039_data_struct::AddMainsFrequencyFilterSelection(event); } } else { @@ -521,17 +246,17 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const int PToptionValues[] = { MAX31865_PT100, MAX31865_PT1000 }; constexpr size_t optionCount = NR_ELEMENTS(PToptionValues); const FormSelectorOptions selector(optionCount, PToptions, PToptionValues); - selector.addFormSelector(F("Resistor Type"), F("rtdtype"), P039_RTD_TYPE); + selector.addFormSelector(F("Resistor Type"), F("rtdtype"), P039_RTD_TYPE); } { const __FlashStringHelper *Coptions[] = { F("2-/4"), F("3") }; constexpr size_t optionCount = NR_ELEMENTS(Coptions); - const FormSelectorOptions selector( optionCount, Coptions); + const FormSelectorOptions selector(optionCount, Coptions); selector.addFormSelector(F("Connection Type"), F("contype"), P039_CONFIG_4); addUnit(F("wire")); } - P039_AddMainsFrequencyFilterSelection(event); + P039_data_struct::AddMainsFrequencyFilterSelection(event); { addFormNumericBox(F("Reference Resistor"), F("res"), P039_RTD_RES, 0); @@ -610,64 +335,10 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_READ: { - // Get the MAX Type (6675 / 31855 / 31856) - uint8_t MaxType = P039_MAX_TYPE; - - float Plugin_039_Celsius = NAN; - - switch (MaxType) { - case P039_MAX6675: - Plugin_039_Celsius = readMax6675(event); - break; - case P039_MAX31855: - Plugin_039_Celsius = readMax31855(event); - break; - case P039_MAX31856: - Plugin_039_Celsius = readMax31856(event); - break; - case P039_MAX31865: - Plugin_039_Celsius = readMax31865(event); - break; - case P039_LM7x: - Plugin_039_Celsius = readLM7x(event); - break; - } - - if (isValidFloat(Plugin_039_Celsius)) - { - UserVar.setFloat(event->TaskIndex, 0, Plugin_039_Celsius); - -# ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = strformat(F("P039 : %s :"), getTaskDeviceName(event->TaskIndex).c_str()); - - const uint8_t valueCount = getValueCountForTask(event->TaskIndex); - - for (uint8_t i = 0; i < valueCount; ++i) - { - log += strformat( - F(" %s: %s"), - Cache.getTaskDeviceValueName(event->TaskIndex, i).c_str(), - formatUserVarNoCheck(event, i).c_str()); - } - addLogMove(LOG_LEVEL_INFO, log); - } -# endif // ifndef BUILD_NO_DEBUG - - if (definitelyGreaterThan(Plugin_039_Celsius, P039_TEMP_THRESHOLD)) { - success = true; - } - } - else - { - UserVar.setFloat(event->TaskIndex, 0, NAN); - UserVar.setFloat(event->TaskIndex, 1, NAN); + P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLog(LOG_LEVEL_ERROR, strformat(F("P039 : %s : No Sensor attached!"), getTaskDeviceName(event->TaskIndex).c_str())); - } - success = false; + if (nullptr != P039_data) { + success = P039_data->read(event); } break; @@ -677,1391 +348,14 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) { P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // Get the MAX Type (6675 / 31855 / 31856) - uint8_t MaxType = P039_MAX_TYPE; - - switch (MaxType) - { - case P039_MAX31865: - { - if ((nullptr != P039_data)) { - switch (event->Par1) - { - case MAX31865_BIAS_ON_STATE: - { - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat( - F("P039 : %s : current state: MAX31865_BIAS_ON_STATE; delta: %d ms"), - getTaskDeviceName(event->TaskIndex).c_str(), - timePassedSince(P039_data->timer))); // calc delta since last call - } - # endif // ifndef BUILD_NO_DEBUG - - // save current timer for next calculation - P039_data->timer = millis(); - - // activate one shot conversion - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_ONE_SHOT, - P039_SET); - - // set next state in sequence -> READ STATE - // start time to follow up on conversion and read the conversion result - P039_data->convReady = false; - Scheduler.setPluginTaskTimer(MAX31865_CONVERSION_TIME, event->TaskIndex, MAX31865_RD_STATE); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat( - F("P039 : %s : Next State: %d"), - getTaskDeviceName(event->TaskIndex).c_str(), - event->Par1)); - } - # endif // ifndef BUILD_NO_DEBUG - - break; - } - case MAX31865_RD_STATE: - { - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat( - F("P039 : %s : current state: MAX31865_RD_STATE; delta: %d ms"), - getTaskDeviceName(event->TaskIndex).c_str(), - timePassedSince(P039_data->timer))); // calc delta since last call - } - # endif // ifndef BUILD_NO_DEBUG - - // save current timer for next calculation - P039_data->timer = millis(); - - // read conversion result - P039_data->conversionResult = read16BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_RTD_MSB)); - - // deactivate BIAS short after read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_RESET); - - // read fault register to get a full picture - P039_data->deviceFaults = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_FAULT)); - - // mark conversion as ready - P039_data->convReady = true; - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, - strformat(F("P039 : %s : conversionResult: %s; deviceFaults: %s; Next State: %d"), - getTaskDeviceName(event->TaskIndex).c_str(), - formatToHex_decimal(P039_data->conversionResult).c_str(), - formatToHex_decimal(P039_data->deviceFaults).c_str(), - event->Par1)); - } - # endif // ifndef BUILD_NO_DEBUG - - - break; - } - case MAX31865_INIT_STATE: - default: - { - // clear all faults - MAX31865_clearFaults(CS_pin_no); - - // activate BIAS short before read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_SET); - - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : %s : current state: MAX31865_INIT_STATE, " - "default; next state: MAX31865_BIAS_ON_STATE"), - getTaskDeviceName(event->TaskIndex).c_str())); - - // FIXME: Shouldn't this be in active code? // save current timer for next calculation - P039_data->timer = millis(); - } - # endif // ifndef BUILD_NO_DEBUG - - // start time to follow up on BIAS activation before starting the conversion - // and start conversion sequence via TIMER API - // set next state in sequence -> BIAS ON STATE - - Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); - - - break; - } - } - } - break; - } - default: - { - break; - } + if (nullptr != P039_data) { + success = P039_data->plugin_tasktimer_in(event); } - success = true; break; } } return success; } -void P039_AddMainsFrequencyFilterSelection(struct EventStruct *event) -{ - const __FlashStringHelper *FToptions[] = { F("60"), F("50") }; - const int FToptionValues[] = { 0, 1 }; - - const FormSelectorOptions selector(NR_ELEMENTS(FToptions), FToptions, FToptionValues); - selector.addFormSelector(F("Supply Frequency Filter"), F("filttype"), P039_RTD_FILT_TYPE); - addUnit(F("Hz")); - # ifndef LIMIT_BUILD_SIZE - addFormNote(F("Filter power net frequency (50/60 Hz)")); - # else // ifndef LIMIT_BUILD_SIZE - addUnit(F("net frequency")); - # endif // ifndef LIMIT_BUILD_SIZE -} - -float readMax6675(struct EventStruct *event) -{ - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - uint8_t messageBuffer[2] = { 0 }; - uint16_t rawvalue = 0u; - - - // "transfer" 2 bytes to SPI to get 16 Bit return value - transfer_n_ByteSPI(CS_pin_no, 2, &messageBuffer[0]); - - // merge 16Bit return value from messageBuffer - rawvalue = ((messageBuffer[0] << 8) | messageBuffer[1]); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX6675 : RAW - BIN: %s HEX: %s DEC: %d MSB: %s LSB: %s"), - String(rawvalue, BIN).c_str(), - formatToHex(rawvalue).c_str(), - rawvalue, - formatToHex_decimal(messageBuffer[0]).c_str(), - formatToHex_decimal(messageBuffer[1]).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - // Open Thermocouple - // Bit D2 is normally low and goes high if the thermocouple input is open. In order to allow the operation of the - // open thermocouple detector, T- must be grounded. Make the ground connection as close to the GND pin - // as possible. - // 2021-05-11: FIXED: c.k.i.: OC Flag already checked; migrated to #define for improved maintenance - const bool Plugin_039_SensorAttached = !(rawvalue & MAX6675_TC_OC); - - if (Plugin_039_SensorAttached) - { - // shift RAW value 3 Bits to the right to get the data - rawvalue >>= 3; - - // calculate Celsius with device resolution 0.25 K/bit - return rawvalue * 0.25f; - } - else - { - return NAN; - } -} - -float readMax31855(struct EventStruct *event) -{ - P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - - uint8_t messageBuffer[4] = { 0 }; - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // "transfer" 0x0 and read the 32 Bit conversion register from the Chip - transfer_n_ByteSPI(CS_pin_no, 4, &messageBuffer[0]); - - // merge rawvalue from 4 bytes of messageBuffer - uint32_t rawvalue = - ((static_cast(messageBuffer[0]) << - 24) | - (static_cast(messageBuffer[1]) << - 16) | (static_cast(messageBuffer[2]) << 8) | static_cast(messageBuffer[3])); - - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - String log; - - if ((log.reserve(200u))) { // reserve value derived from example log file - log = strformat(F("P039 : MAX31855 : RAW - BIN: %s rawvalue,HEX: %s rawvalue,DEC: %d messageBuffer[],HEX:"), - String(rawvalue, BIN).c_str(), - formatToHex(rawvalue).c_str(), - rawvalue); - - for (size_t i = 0u; i < 4; ++i) - { - log += ' '; // 1 char - log += formatToHex_decimal(messageBuffer[i]); // 9 char - } - addLogMove(LOG_LEVEL_DEBUG, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - if (nullptr != P039_data) { - // FIXED: c.k.i. : moved static fault flag to instance data structure - - // check for fault flags in LSB of 32 Bit messageBuffer - if (P039_data->sensorFault != ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0)) { - // Fault code changed, log them - P039_data->sensorFault = ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - String log; - - if ((log.reserve(120u))) { // reserve value derived from example log file - log = F("P039 : MAX31855 : "); - - if ((P039_data->sensorFault)) { - log += F("Fault resolved"); - } else { - log += F("Fault code :"); - - if (rawvalue & MAX31855_TC_OC) { - log += F(" Open (no connection)"); - } - - if (rawvalue & MAX31855_TC_SC) { - log += F(" Short-circuit to GND"); - } - - if (rawvalue & MAX31855_TC_SCVCC) { - log += F(" Short-circuit to Vcc"); - } - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - # endif // ifndef BUILD_NO_DEBUG - } - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX31855 : rawvalue: %s P039_data->sensorFault: "), - formatToHex_decimal(rawvalue).c_str(), - formatToHex_decimal(P039_data->sensorFault).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - } - - // D16 - This bit reads at 1 when any of the SCV, SCG, or OC faults are active. Default value is 0. - // 2020-05-11: FIXED: c.k.i.: migrated plain flag mask to #defines to enhance maintainability; added all fault flags for safety reasons - const bool Plugin_039_SensorAttached = !(rawvalue & (MAX31855_TC_GENFLT | MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)); - - if (Plugin_039_SensorAttached) - { - // Data is D[31:18] - // Shift RAW value 18 Bits to the right to get the data - rawvalue >>= 18; - - // Check for negative Values - // +25.00 0000 0001 1001 00 - // 0.00 0000 0000 0000 00 - // -0.25 1111 1111 1111 11 - // -1.00 1111 1111 1111 00 - // -250.00 1111 0000 0110 00 - // We're left with (32 - 18 =) 14 bits - int temperature = Plugin_039_convert_two_complement(rawvalue, 14); - - // Calculate Celsius - return temperature * 0.25f; - } - else - { - // Fault state, thus output no value. - return NAN; - } -} - -float readMax31856(struct EventStruct *event) -{ - P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - - uint8_t registers[MAX31856_NO_REG] = { 0 }; - uint8_t messageBuffer[MAX31856_NO_REG + 1] = { 0 }; - - messageBuffer[0] = MAX31856_READ_ADDR_BASE; - - // "transfer" 0x0 starting at address 0x00 and read the all registers from the Chip - transfer_n_ByteSPI(CS_pin_no, (MAX31856_NO_REG + 1), &messageBuffer[0]); - - // transfer data from messageBuffer and get rid of initial address uint8_t - for (uint8_t i = 0u; i < MAX31856_NO_REG; ++i) { - registers[i] = messageBuffer[i + 1]; - } - - // configure device for next conversion - // activate frequency filter according to configuration - change8BitRegister(CS_pin_no, - (MAX31856_READ_ADDR_BASE + MAX31856_CR0), - (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), - MAX31856_SET_50HZ, - static_cast(P039_RTD_FILT_TYPE)); - - // set averaging and TC type - write8BitRegister(CS_pin_no, (MAX31856_WRITE_ADDR_BASE + MAX31856_CR1), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE)); - - - // start on shot conversion for next read cycle - change8BitRegister(CS_pin_no, - (MAX31856_READ_ADDR_BASE + MAX31856_CR0), - (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), - MAX31856_SET_ONE_SHOT, - P039_SET); - - - // now derive raw value from respective registers - uint32_t rawvalue = static_cast(registers[MAX31856_LTCBH]); - - rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBM]); - rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBL]); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - String log; - - if ((log.reserve(210u))) { // reserve value derived from example log file - log = F("P039 : MAX31856 :"); - - for (uint8_t i = 0; i < MAX31856_NO_REG; ++i) { - log += ' '; - log += formatToHex_decimal(registers[i]); - } - log += F(" rawvalue: "); - log += formatToHex_decimal(rawvalue); - addLogMove(LOG_LEVEL_DEBUG, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - - // ignore TC Range Bit in case Voltage Modes are used - // datasheet: - // Thermocouple Out-of-Range fault. - // 0 = The Thermocouple Hot Junction temperature is within the normal operating range (see Table 1). - // 1 = The Thermocouple Hot Junction temperature is outside of the normal operating range. - // Note: The TC Range bit should be ignored in voltage mode. - uint8_t sr = registers[MAX31856_SR]; - - if ((8u == P039_TC_TYPE) || (12u == P039_TC_TYPE)) { - sr &= ~MAX31856_TC_TCRANGE; - } - - - // FIXED: c.k.i. : moved static fault flag to instance data structure - if ((nullptr != P039_data)) { - // P039_data->sensorFault = false; - - P039_data->sensorFault = (sr != 0); // Set new state - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - // FIXME TD-er: Part of expression is always false (sr == 0) - const bool faultResolved = (P039_data->sensorFault) && (sr == 0); - - if ((P039_data->sensorFault) || faultResolved) { - String log; - - if ((log.reserve(140u))) { // reserve value derived from example log file - log = F("P039 : MAX31856 : "); - - if ((P039_data->sensorFault) == 0) { - log += F("Fault resolved"); - } else { - log += F("Fault :"); - - if (sr & MAX31856_TC_OC) { - log += F(" Open (no connection)"); - } - - if (sr & MAX31856_TC_OVUV) { - log += F(" Over/Under Voltage"); - } - - if (sr & MAX31856_TC_TCLOW) { - log += F(" TC Low"); - } - - if (sr & MAX31856_TC_TCLHIGH) { - log += F(" TC High"); - } - - if (sr & MAX31856_TC_CJLOW) { - log += F(" CJ Low"); - } - - if (sr & MAX31856_TC_CJHIGH) { - log += F(" CJ High"); - } - - if (sr & MAX31856_TC_TCRANGE) { - log += F(" TC Range"); - } - - if (sr & MAX31856_TC_CJRANGE) { - log += F(" CJ Range"); - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - } - } - # endif // ifndef BUILD_NO_DEBUG - } - - - const bool Plugin_039_SensorAttached = (sr == 0); - - if (Plugin_039_SensorAttached) - { - rawvalue >>= 5; // bottom 5 bits are unused - // We're left with (24 - 5 =) 19 bits - - { - float temperature = 0; - - switch (P039_TC_TYPE) - { - case 8: - { - temperature = rawvalue / 1677721.6f; // datasheet: rawvalue = 8 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (8 x 1.6 x 2^17) - break; - } - case 12: - { - temperature = rawvalue / 6710886.4f; // datasheet: rawvalue = 32 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (32 x 1.6 x 2^17) - break; - } - default: - { - temperature = Plugin_039_convert_two_complement(rawvalue, 19); - - // Calculate Celsius - temperature /= 128.0f; - break; - } - } - - return temperature; - } - } - else - { - // Fault state, thus output no value. - return NAN; - } -} - -float readMax31865(struct EventStruct *event) -{ - P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - - if (P039_data == nullptr) { - return NAN; - } - - uint8_t registers[MAX31865_NO_REG] = { 0 }; - uint16_t rawValue = 0u; - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - String log; - - if ((log.reserve(80u))) { // reserve value derived from example log file - log = F("P039 : MAX31865 :"); - log += F(" P039_data->convReady: "); - log += boolToString(P039_data->convReady); - - addLogMove(LOG_LEVEL_DEBUG, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - - // read conversion result and faults from plugin data structure - // if pointer exists and conversion has been finished - if (P039_data->convReady) { - rawValue = P039_data->conversionResult; - registers[MAX31865_FAULT] = P039_data->deviceFaults; - } - - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - String log; - - if ((log.reserve(160u))) { // reserve value derived from example log file - for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) - { - registers[i] = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + i)); - } - - log = F("P039 : MAX31865 :"); - - for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) - { - log += ' '; - log += formatToHex_decimal(registers[i]); - } - - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - // Prepare and start next conversion, before handling faults and rawValue - // clear all faults - MAX31865_clearFaults(CS_pin_no); - - // set frequency filter - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_50HZ, - static_cast(P039_RTD_FILT_TYPE)); - - - // configure read access with configuration from web interface - MAX31865_setConType(CS_pin_no, P039_CONFIG_4); - - // activate BIAS short before read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_SET); - - // start time to follow up on BIAS activation before starting the conversion - // and start conversion sequence via TIMER API - // save current timer for next calculation - P039_data->timer = millis(); - - // set next state to MAX31865_BIAS_ON_STATE - - Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - if (registers[MAX31865_FAULT]) - { - String log; - - if ((log.reserve(210u))) { // reserve value derived from example log file - log = F("P039 : MAX31865 : "); - - log += F("Fault : "); - log += formatToHex_decimal(registers[MAX31865_FAULT]); - log += F(" :"); - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_OVUV) - { - log += F(" Under/Over voltage"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_RTDINLOW) - { - log += F(" RTDIN- < 0.85 x Bias - FORCE- open"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINHIGH) - { - log += F(" REFIN- < 0.85 x Bias - FORCE- open"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINLOW) - { - log += F(" REFIN- > 0.85 x Bias"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_LOWTHRESH) - { - log += F(" RTD Low Threshold"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_HIGHTHRESH) - { - log += F(" RTD High Threshold"); - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - } - # endif // ifndef BUILD_NO_DEBUG - - - bool ValueValid = false; - - if (registers[MAX31865_FAULT] == 0x00u) { - ValueValid = true; - } - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : registers[MAX31865_FAULT]: %s ValueValid: %s"), - formatToHex_decimal(registers[MAX31865_FAULT]).c_str(), - FsP(boolToString(ValueValid)))); - } - - # endif // ifndef BUILD_NO_DEBUG - - if (ValueValid) - { - rawValue >>= 1; // bottom fault bits is unused - - float temperature = Plugin_039_convert_to_temperature(rawValue, getNomResistor(P039_RTD_TYPE), P039_RTD_RES); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : rawValue: %s temperature: %.3f P039_RTD_TYPE: %d P039_RTD_RES: %d"), - formatToHex_decimal(rawValue).c_str(), - temperature, - P039_RTD_TYPE, - P039_RTD_RES)); - } - - # endif // ifndef BUILD_NO_DEBUG - - // add offset handling from configuration webpage - temperature += P039_RTD_OFFSET; - - // Calculate Celsius - return temperature; - } - else - { - // Fault state, thus output no value. - return NAN; - } -} - -void MAX31865_clearFaults(int8_t l_CS_pin_no) -{ - uint8_t l_reg = 0u; - - // read in config register - l_reg = read8BitRegister(l_CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG)); - - - // clear all faults ( write "0" to D2, D3, D5; write "1" to D2) - l_reg &= ~(MAX31865_SET_ONE_SHOT | MAX31865_FAULT_CTRL_MASK); - l_reg |= MAX31865_CLEAR_FAULTS; - - // write configuration - write8BitRegister(l_CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), l_reg); -} - -void MAX31865_setConType(int8_t l_CS_pin_no, uint8_t l_conType) -{ - bool l_set_reset = false; - - // configure if 3 WIRE bit will be set/reset - switch (l_conType) - { - case 0: - l_set_reset = P039_RESET; - break; - case 1: - l_set_reset = P039_SET; - break; - default: - l_set_reset = P039_RESET; - break; - } - - // change to configuration register - change8BitRegister(l_CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_3WIRE, - l_set_reset); -} - -/**************************************************************************/ - -/*! - @brief Read the temperature in C from the RTD through calculation of the - resistance. Uses - http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf - technique - @param RTDnominal The 'nominal' resistance of the RTD sensor, usually 100 - or 1000 - @param refResistor The value of the matching reference resistor, usually - 430 or 4300 - @returns Temperature in C - */ - -/**************************************************************************/ -float Plugin_039_convert_to_temperature(uint32_t l_rawvalue, float RTDnominal, float refResistor) -{ - # define RTD_A 3.9083e-3f - # define RTD_B -5.775e-7f - - float Z1, Z2, Z3, Z4, Rt, temp; - - Rt = l_rawvalue; - Rt /= 32768u; - Rt *= refResistor; - - Z1 = -RTD_A; - Z2 = RTD_A * RTD_A - (4 * RTD_B); - Z3 = (4 * RTD_B) / RTDnominal; - Z4 = 2 * RTD_B; - - temp = Z2 + (Z3 * Rt); - temp = (sqrtf(temp) + Z1) / Z4; - - if (temp >= 0) { - return temp; - } - - Rt /= RTDnominal; - Rt *= 100; // normalize to 100 ohm - - float rpoly = Rt; - - temp = -242.02f; - temp += 2.2228f * rpoly; - rpoly *= Rt; // square - temp += 2.5859e-3f * rpoly; - rpoly *= Rt; // ^3 - temp -= 4.8260e-6f * rpoly; - rpoly *= Rt; // ^4 - temp -= 2.8183e-8f * rpoly; - rpoly *= Rt; // ^5 - temp += 1.5243e-10f * rpoly; - - return temp; -} - -uint16_t getNomResistor(uint8_t l_RType) -{ - uint16_t l_returnValue = 100u; - - switch (l_RType) - { - case MAX31865_PT100: - l_returnValue = 100u; - break; - case MAX31865_PT1000: - l_returnValue = 1000u; - break; - default: - l_returnValue = 100u; - break; - } - return l_returnValue; -} - -int Plugin_039_convert_two_complement(uint32_t value, int nr_bits) { - const bool negative = (value & (1 << (nr_bits - 1))) != 0; - int nativeInt; - - if (negative) { - // Add zeroes to the left to create the proper negative native-sized integer. - nativeInt = value | ~((1 << nr_bits) - 1); - } else { - nativeInt = value; - } - return nativeInt; -} - -float readLM7x(struct EventStruct *event) -{ - float temperature = 0.0f; - uint16_t device_id = 0u; - uint16_t rawValue = 0u; - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // operate LM7x devices in polling mode, assuming conversion is ready with every call of this read function ( >=210ms call cycle) - // this allows usage of multiples generations of LM7x devices, that doe not provde conversion ready information in temperature register - - rawValue = readLM7xRegisters(CS_pin_no, P039_RTD_LM_TYPE, P039_RTD_LM_SHTDWN, &device_id); - - temperature = convertLM7xTemp(rawValue, P039_RTD_LM_TYPE); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : LM7x : readLM7x : rawValue: %s device_id: %s temperature: %.3f"), - formatToHex_decimal(rawValue).c_str(), - formatToHex(device_id).c_str(), - temperature)); - } - # endif // ifndef BUILD_NO_DEBUG - - return temperature; -} - -float convertLM7xTemp(uint16_t l_rawValue, uint16_t l_LM7xsubtype) -{ - float l_returnValue = 0.0f; - float l_lsbvalue = 0.0f; - uint8_t l_noBits = 0u; - int l_intTemperature = 0; - - switch (l_LM7xsubtype) - { - case LM7x_SD70: - l_rawValue >>= 5; - l_lsbvalue = 0.25f; - l_noBits = 11u; - break; - case LM7x_SD71: - l_rawValue >>= 2; - l_lsbvalue = 0.03125f; - l_noBits = 14u; - break; - case LM7x_SD74: - l_rawValue >>= 3; - l_lsbvalue = 0.0625f; - l_noBits = 13u; - break; - case LM7x_SD121: - case LM7x_SD122: - case LM7x_SD123: - case LM7x_SD124: - l_rawValue >>= 4; - l_lsbvalue = 0.0625f; - l_noBits = 12u; - break; - case LM7x_SD125: - l_rawValue >>= 5; - l_lsbvalue = 0.25f; - l_noBits = 10u; - break; - default: // use lowest resolution as fallback if no device has been configured - l_rawValue >>= 5; - l_lsbvalue = 0.25f; - l_noBits = 11u; - break; - } - - l_intTemperature = Plugin_039_convert_two_complement(l_rawValue, l_noBits); - - l_returnValue = l_intTemperature * l_lsbvalue; - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, - strformat(F("P039 : LM7x : convertLM7xTemp : l_returnValue: %s l_LM7xsubtype: %s l_rawValue: %s l_noBits: %d l_lsbvalue: %.3f"), - formatToHex_decimal(l_returnValue).c_str(), - formatToHex_decimal(l_LM7xsubtype).c_str(), - formatToHex_decimal(l_rawValue).c_str(), - l_noBits, - l_lsbvalue)); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_returnValue; -} - -uint16_t readLM7xRegisters(int8_t l_CS_pin_no, uint8_t l_LM7xsubType, uint8_t l_runMode, uint16_t *l_device_id) -{ - uint16_t l_returnValue = 0u; - uint16_t l_mswaitTime = 0u; - - - switch (l_LM7xsubType) - { - case LM7x_SD70: - case LM7x_SD71: - case LM7x_SD74: - l_mswaitTime = 300; - break; - case LM7x_SD121: - case LM7x_SD122: - case LM7x_SD123: - case LM7x_SD124: - l_mswaitTime = 320; - break; - case LM7x_SD125: - l_mswaitTime = 100; - break; - default: - l_mswaitTime = 500; - break; - } - - // // activate communication -> CS low - // handle_SPI_CS_Pin(l_CS_pin_no, LOW); - - if (l_runMode) - { - // shutdown mode active -> conversion when called - uint8_t messageBuffer[12] = { 0xFF, 0xFF, 0xFF, 0X00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF }; - - // send inital 4 bytes to wake the device and start the conversion - transfer_n_ByteSPI(l_CS_pin_no, 4, &messageBuffer[0]); - - // wait specific ms for conversion to be ready (TI datasheet per devices) - delay(l_mswaitTime); - - // send remaining 8 bytes to read the device ID and shutdown the device - transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[4]); - - // read temperature value (16 Bit) - l_returnValue = ((messageBuffer[4] << 8) | messageBuffer[5]); - - // read Manufatures/Device ID (16 Bit) - *(l_device_id) = ((messageBuffer[8] << 8) | messageBuffer[9]); - - // // wakeup device and start conversion - // // initial read of conversion result is obsolete - // SPI.transfer16(0xFFFF); - - // // (wakeup device with "all zero2 message in the last 8 bits - // SPI.transfer16(0xFF00); - - // //wait specific ms for conversion to be ready (TI datasheet per devices) - // delay(l_mswaitTime); - - // //read temperature value (16 Bit) - // l_returnValue = SPI.transfer16(0x0000); - // // l_returnValue <<= 8; - // // l_returnValue = SPI.transfer(0x00); - - // // set device to shutdown with "all one" message in the last 8 bits - // SPI.transfer16(0xFFFF); - - // // read Manufatures/Device ID (16 Bit) - // *(l_device_id) = SPI.transfer16(0x0000); - // // *(l_device_id) <<= 8; - // // *(l_device_id) = SPI.transfer(0x00); - - // // set device to shutdown with "all one" message in the last 8 bits ( maybe redundant, check with test) - // SPI.transfer16(0xFFFF); - } - else - { - // shutdown mode inactive -> normal background conversion during call cycle - uint8_t messageBuffer[8] = { 0x00, 0x00, 0xFF, 0XFF, 0x00, 0x00, 0x00, 0x00 }; - - transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[0]); - - // read temperature value (16 Bit) - l_returnValue = ((messageBuffer[0] << 8) | messageBuffer[1]); - - // read Manufatures/Device ID (16 Bit) - *(l_device_id) = ((messageBuffer[4] << 8) | messageBuffer[5]); - - - // l_returnValue = SPI.transfer16(0x0000); //read temperature value (16 Bit) - // // l_returnValue <<= 8; - // // l_returnValue = SPI.transfer(0x00); - - // // set device to shutdown - // SPI.transfer16(0xFFFF); - - // // read Manufatures/Device ID (16 Bit) - // *(l_device_id) = SPI.transfer16(0x0000); - // // *(l_device_id) <<= 8; - // // *(l_device_id) = SPI.transfer(0x00); - - // // start conversion until next read (8 Bit sufficient) - // // 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F allowed - else device goes to test mode (not desirable here) - // SPI.transfer(0x00); - // // SPI.transfer16(0x0000); - } - - // // stop communication -> CS high - // handle_SPI_CS_Pin(l_CS_pin_no, HIGH); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : LM7x : readLM7xRegisters : l_returnValue: %s l_device_id: %s"), - formatToHex_decimal(l_returnValue).c_str(), - formatToHex(*(l_device_id)).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_returnValue; -} - -// POSSIBLE START OF GENERIC SPI HIGH LEVEL FUNCTIONS WITH POTENTIAL OF SYSTEM WIDE RE-USE - -/**************************************************************************/ - -/*! - @brief generic high level library to access SPI interface from plugins - with GPIO pin handled as CS - chri.kai.in 2021 - - Initial Revision - chri.kai.in 2021 - - TODO: c.k.i.: make it generic and carve out to generic _SPI_helper.c library - - - /**************************************************************************/ - - -/**************************************************************************/ - -/*! - - @brief Identifying the CS pin from the event basic data structure - @param event pointer to the event structure; default GPIO is chosen as GPIO 15 - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -int get_SPI_CS_Pin(struct EventStruct *event) { // If no Pin is in Config we use 15 as default -> Hardware Chip Select on ESP8266 - if (CONFIG_PIN1 != -1) { - return CONFIG_PIN1; - } - return 15; // D8 -} - -/**************************************************************************/ - -/*! - @brief Initializing GPIO as OUTPUT for CS for SPI communication - @param l_CS_pin_no the GPIO pin number used as CS - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void init_SPI_CS_Pin(int8_t l_CS_pin_no) { - // set the slaveSelectPin as an output: - pinMode(l_CS_pin_no, OUTPUT); -} - -/**************************************************************************/ - -/*! - @brief Handling GPIO as CS for SPI communication - @param l_CS_pin_no the GPIO pin number used as CS - @param l_state the state of the CS pin: "HIGH/LOW" reflecting the physical level - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void handle_SPI_CS_Pin(int8_t l_CS_pin_no, bool l_state) { - P039_CS_Delay(); // tCWH (min) >= x00ns - digitalWrite(l_CS_pin_no, l_state); - P039_CS_Delay(); // tCC (min) >= x00ns -} - -/**************************************************************************/ - -/*! - @brief write 8 bits to adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - @param value the unsigned 8 Bit message to be transferred - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void write8BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint8_t value) -{ - uint8_t l_messageBuffer[2] = { l_address, value }; - - transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write8BitRegister : l_address: %s value: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(value).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG -} - -/**************************************************************************/ - -/*! - @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - @param value the unsigned 16 Bit message to be transferred - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void write16BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint16_t value) -{ - uint8_t l_messageBuffer[3] = { l_address, static_cast((value >> 8) & 0xFF), static_cast(value & 0xFF) }; - - transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write16BitRegister : l_address: %s value: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(value).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG -} - -/**************************************************************************/ - -/*! - @brief read 8 bits from adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - - @returns the unsigned 8 Bit message read from l_address - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -uint8_t read8BitRegister(int8_t l_CS_pin_no, uint8_t l_address) -{ - uint8_t l_messageBuffer[2] = { l_address, 0x00 }; - - transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read8BitRegister : l_address: %s returnvalue: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(l_messageBuffer[1]).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_messageBuffer[1]; -} - -/**************************************************************************/ - -/*! - @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - - @returns the unsigned 16 Bit message read from l_address - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -uint16_t read16BitRegister(int8_t l_CS_pin_no, uint8_t l_address) -{ - uint8_t l_messageBuffer[3] = { l_address, 0x00, 0x00 }; - uint16_t l_returnValue; - - transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); - l_returnValue = ((l_messageBuffer[1] << 8) | l_messageBuffer[2]); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read16BitRegister : l_address: %s l_returnValue: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(l_returnValue).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_returnValue; -} - -/**************************************************************************/ - -/*! - @brief read from/write to dedicated number of bytes from/to SPI, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_noBytesToSend number of bytes to read/write from/to SPI - @param l_inoutMessageBuffer pointer to the messsage buffer to provide bytes to send - and provide read bytes from the SPI bus after the call - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void transfer_n_ByteSPI(int8_t l_CS_pin_no, uint8_t l_noBytesToSend, uint8_t *l_inoutMessageBuffer) -{ - // activate communication -> CS low - handle_SPI_CS_Pin(l_CS_pin_no, LOW); - - for (size_t i = 0u; i < l_noBytesToSend; i++) - { - l_inoutMessageBuffer[i] = SPI.transfer(l_inoutMessageBuffer[i]); - } - - // stop communication -> CS high - handle_SPI_CS_Pin(l_CS_pin_no, HIGH); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - String log; - - if ((log.reserve(120u))) { // reserve value derived from example log file - log = F("P039 : SPI : transfer_n_ByteSPI : "); // 34 char - - for (uint8_t i = 0; i < l_noBytesToSend; ++i) - { - log += ' '; // 1 char - log += formatToHex_decimal(l_inoutMessageBuffer[i]); // 9 char - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG -} - -/**************************************************************************/ - -/*! - @brief read a 16Bit register and change a flag, writing it back, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_readaddress SPI read address of the device register - @param l_writeaddress SPI write address of the device register - @param l_flagmask mask set to apply on the read register - @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) - - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void change16BitRegister(int8_t l_CS_pin_no, uint8_t l_readaddress, uint8_t l_writeaddress, uint16_t l_flagmask, bool l_set_reset) -{ - uint16_t l_reg = 0u; - - // read in config register - l_reg = read16BitRegister(l_CS_pin_no, l_readaddress); - - if (l_set_reset) { - l_reg |= l_flagmask; - } - else - { - l_reg &= ~(l_flagmask); - } - - // write to configuration register - write16BitRegister(l_CS_pin_no, l_writeaddress, l_reg); -} - -/**************************************************************************/ - -/*! - @brief read a 8 Bit register and change a flag, writing it back, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_readaddress SPI read address of the device register - @param l_writeaddress SPI write address of the device register - @param l_flagmask mask set to apply on the read register - @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) - - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void change8BitRegister(int8_t l_CS_pin_no, uint8_t l_readaddress, uint8_t l_writeaddress, uint8_t l_flagmask, bool l_set_reset) -{ - uint8_t l_reg = 0u; - - // read in config register - l_reg = read8BitRegister(l_CS_pin_no, l_readaddress); - - - // TODO: c.k.i.: analyze opportunity to use arduino bitSet/Clear macros instead - if (l_set_reset) { - l_reg |= l_flagmask; - } - else - { - l_reg &= ~(l_flagmask); - } - - // write to configuration register - write8BitRegister(l_CS_pin_no, l_writeaddress, l_reg); -} - #endif // USES_P039 diff --git a/src/_P095_ILI9341.ino b/src/_P095_ILI9341.ino index 9f7ae8a032..aaf50e95b7 100644 --- a/src/_P095_ILI9341.ino +++ b/src/_P095_ILI9341.ino @@ -10,11 +10,12 @@ # define PLUGIN_NAME_095 "Display - TFT ILI934x/ILI948x" # define PLUGIN_VALUENAME1_095 "CursorX" # define PLUGIN_VALUENAME2_095 "CursorY" -# define PLUGIN_095_MAX_DISPLAY 1 /** * Changelog: + * 2025-08-29 tonhuisman: Fix GPIO pin display on Devices page, no default GPIO pins for ESP32 + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2024-07-07 tonhuisman: Remove explicit support for ILI9486 as all ILI9486 displays tested so far work with the ILI9488 driver, * sometimes with Invert display setting enabled (or they are actually ILI9488 displays...) * 2024-06-23 tonhuisman: Add support for ILI9488 displays by implementing an adapted library (jaretburkett/ILI9488) @@ -153,6 +154,7 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) dev.ValueCount = 2; dev.TimerOption = true; dev.TimerOptional = true; + dev.SpiBusSelect = true; break; } @@ -179,20 +181,34 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_SET_DEFAULTS: + # ifndef LIMIT_BUILD_SIZE + case PLUGIN_WEBFORM_SHOW_GPIO_DESCR: { - # ifdef ESP32 + const char *separator = event->String1.c_str(); // contains the NewLine sequence + string = strformat( + F("%sCS: %s%sDC: %s%s RES: %s%sBtn: %s%sBckl: : %s"), + separator, + formatGpioLabel(PIN(0), false).c_str(), + separator, + formatGpioLabel(PIN(1), false).c_str(), + separator, + formatGpioLabel(PIN(2), false).c_str(), + separator, + formatGpioLabel(P095_CONFIG_BUTTON_PIN, false).c_str(), + separator, + formatGpioLabel(P095_CONFIG_BACKLIGHT_PIN, false).c_str()); + success = true; + break; + } + # endif // ifndef LIMIT_BUILD_SIZE - if (Settings.InitSPI == 2) { // When using ESP32 H(ardware-)SPI - PIN(0) = P095_TFT_CS_HSPI; - } else { - PIN(0) = P095_TFT_CS; - } - # else // ifdef ESP32 + case PLUGIN_SET_DEFAULTS: + { + # ifdef ESP8266 PIN(0) = P095_TFT_CS; - # endif // ifdef ESP32 - PIN(1) = P095_TFT_DC; - PIN(2) = P095_TFT_RST; + PIN(1) = P095_TFT_DC; + PIN(2) = P095_TFT_RST; + # endif // ifdef ESP8266 P095_CONFIG_BUTTON_PIN = -1; // No button connected P095_CONFIG_BACKLIGHT_PIN = P095_BACKLIGHT_PIN; P095_CONFIG_BACKLIGHT_PERCENT = 100; // Percentage backlight @@ -451,7 +467,9 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { # if P095_ENABLE_ILI948X if (10 == P095_CONFIG_FLAG_GET_TYPE) { // If ILI9486 was selected, reset to ILI9488 @@ -470,7 +488,8 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) P095_CONFIG_FLAG_GET_CMD_TRIGGER)), P095_CONFIG_GET_COLOR_FOREGROUND, P095_CONFIG_GET_COLOR_BACKGROUND, - bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_BACK_FILL) == 0 + bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_BACK_FILL) == 0, + spi_bus # if ADAGFX_FONTS_INCLUDED , P095_CONFIG_DEFAULT_FONT diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index c698c2793a..31deacad6a 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -13,6 +13,11 @@ // #define PLUGIN_096_MAX_DISPLAY 1 // Unused +/** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus + * 2025-08-13 tonhuisman: Start changelog + */ + /* README.MD @@ -184,6 +189,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) dev.TimerOption = true; dev.TimerOptional = true; # endif // if P096_USE_EXTENDED_SETTINGS + dev.SpiBusSelect = true; break; } @@ -278,8 +284,8 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) options4, optionValues4); selector.addFormSelector(F("eInk display model"), - F("_type"), - P096_CONFIG_FLAG_GET_DISPLAYTYPE); + F("_type"), + P096_CONFIG_FLAG_GET_DISPLAYTYPE); } addFormSubHeader(F("Layout")); @@ -292,7 +298,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options2[] = { F("Normal"), F("+90°"), F("+180°"), F("+270°") }; int optionValues2[] = { 0, 1, 2, 3 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues2); - const FormSelectorOptions selector( optionCount, options2, optionValues2); + const FormSelectorOptions selector(optionCount, options2, optionValues2); selector.addFormSelector(F("Rotation"), F("_rotate"), P096_CONFIG_ROTATION); } # endif // ifdef P096_USE_ADA_GRAPHICS @@ -341,8 +347,8 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) } constexpr size_t optionCount = NR_ELEMENTS(colorDepthOptions); const FormSelectorOptions selector(optionCount, colorDepths, colorDepthOptions); - selector.addFormSelector(F("Greyscale levels"),F("_colorDepth"), - P096_CONFIG_FLAG_GET_COLORDEPTH); + selector.addFormSelector(F("Greyscale levels"), F("_colorDepth"), + P096_CONFIG_FLAG_GET_COLORDEPTH); } AdaGFXFormTextPrintMode(F("_mode"), P096_CONFIG_FLAG_GET_MODE); @@ -379,7 +385,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) constexpr size_t optionCount = NR_ELEMENTS(commandTriggerOptions); const FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); selector.addFormSelector(F("Write Command trigger"), F("_commandtrigger"), - P096_CONFIG_FLAG_GET_CMD_TRIGGER); + P096_CONFIG_FLAG_GET_CMD_TRIGGER); addFormNote(F("Select the command that is used to handle commands for this display.")); } @@ -502,7 +508,9 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { initPluginTaskData(event->TaskIndex, # if P096_USE_EXTENDED_SETTINGS new (std::nothrow) P096_data_struct(static_cast(P096_CONFIG_FLAG_GET_DISPLAYTYPE), @@ -511,6 +519,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) static_cast(P096_CONFIG_FLAG_GET_MODE), P096_CommandTrigger_toString(static_cast( P096_CONFIG_FLAG_GET_CMD_TRIGGER)), + spi_bus, P096_CONFIG_GET_COLOR_FOREGROUND, P096_CONFIG_GET_COLOR_BACKGROUND, static_cast(P096_CONFIG_FLAG_GET_COLORDEPTH), @@ -522,7 +531,8 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) P096_CONFIG_ROTATION, P096_CONFIG_FLAG_GET_FONTSCALE, AdaGFXTextPrintMode::ContinueToNextLine, - F("epd")) + F("epd"), + spi_bus) # endif // if P096_USE_EXTENDED_SETTINGS ); P096_data_struct *P096_data = static_cast(getPluginTaskData(event->TaskIndex)); diff --git a/src/_P099_XPT2046Touch.ino b/src/_P099_XPT2046Touch.ino index 430c4afea5..06c2bfeabf 100644 --- a/src/_P099_XPT2046Touch.ino +++ b/src/_P099_XPT2046Touch.ino @@ -6,6 +6,7 @@ /** * Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Touch) * 2020-11-01 tonhuisman: Solved previous strange rotation settings to be compatible with TFT ILI9341 * 2020-11-01 tonhuisman: Add option to flip rotation by 180 deg, and command touch,flip,<0|1> @@ -52,10 +53,11 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_DEVICE_ADD: { auto& dev = Device[++deviceCount]; - dev.Number = PLUGIN_ID_099; - dev.Type = DEVICE_TYPE_SPI; - dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; - dev.ValueCount = 3; + dev.Number = PLUGIN_ID_099; + dev.Type = DEVICE_TYPE_SPI; + dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; + dev.ValueCount = 3; + dev.SpiBusSelect = true; break; } @@ -81,14 +83,14 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) break; } - # if FEATURE_MQTT_DISCOVER + #if FEATURE_MQTT_DISCOVER case PLUGIN_GET_DISCOVERY_VTYPES: { event->Par1 = static_cast(Sensor_VType::SENSOR_TYPE_NONE); // Not yet supported success = true; break; } - # endif // if FEATURE_MQTT_DISCOVER + #endif // if FEATURE_MQTT_DISCOVER case PLUGIN_SET_DEFAULTS: { @@ -362,6 +364,7 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); initPluginTaskData(event->TaskIndex, new (std::nothrow) P099_data_struct()); P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -374,7 +377,8 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z), bitRead(P099_CONFIG_FLAGS, P099_FLAGS_USE_CALIBRATION), P099_CONFIG_X_RES, - P099_CONFIG_Y_RES); + P099_CONFIG_Y_RES, + spi_bus); break; } diff --git a/src/_P104_max7219_Dotmatrix.ino b/src/_P104_max7219_Dotmatrix.ino index c00c18b4c1..3bc461e113 100644 --- a/src/_P104_max7219_Dotmatrix.ino +++ b/src/_P104_max7219_Dotmatrix.ino @@ -72,56 +72,59 @@ // Display of the selected zone is suspended until all provided dots are drawn. No quotes are needed around // the coordinate set. // -// History: -// 2025-04-03 tonhuisman: Set character spacing correctly when changing fonts -// 2023-10-15 tonhuisman: Code improvements, now using NR_ELEMENTS macro instead of multiple #ifdefs and increments -// Re-enable use of settings-version V3 after some more testing -// 2023-10-08 tonhuisman: Disable use of settings-version V3 for backward compatibility -// 2023-08-15 tonhuisman: Implement Extended CustomTaskSettings, and use that to significantly improve saving the settings on LittleFS by -// a) only storing the settings-version (V3) in regular CustomTaskSettings file, and the rest in the Extended -// CustomTaskSettings file, by using the offset as a starting location for the data elements -// b) Combine storing the size and the data-block in a single save action -// Apply toStringNoZero() converter to reduce the settings-data to be saved -// 2023-08-13 tonhuisman: Add Dot subcommand for pixel-drawing in a zone. Can be applied on any type of zone (so can be overwritten by the -// original content when that's updated...) -// Set default Hardware type to FC16, as that's the most used for modules found on Aliexpress -// 2023-03-07 tonhuisman: Parse text to display without trimming off leading and trailing spaces -// 2022-08-12 tonhuisman: Remove [DEVELOPMENT] tag -// 2021-10-03 tonhuisman: Add Inverted option per zone -// 2021-09 tonhuisman: Minor improvements, attempts to fix stack failures -// 2021-08-08 tonhuisman: Reworked loading & saving the settings from A huge fixed size pre-allocated block to dynamic allocation -// and saving/loading per zone. That should sove the numerous stack related crashes. -// 2021-08-07 tonhuisman: Review feedback: several small improvements and corrections -// 2021-07-18 tonhuisman: Small optimizations and improvements -// 2021-07-14 tonhuisman: Fix some bugs in font selection, add Text reverse content type to improve usability of Vertical font -// 2021-07-12 tonhuisman: Reduce number of reconfiguration during command handling, will be applied the next time content is displayed -// update/correct some documentation -// 2021-07-08 tonhuisman: Several bugfixes: settings defaults, fix brightness to enable 0 value, simplify storing the zone settings -// 2021-06-29-2021-07-03: Add Actions column to insert before/after zone or delete a zone, order the zones either in numeric order -// tonhuisman: or in display order ('upside-down'), fixed many bugs, refactored bar-graph method, other improvements -// All optional at compile-time by P104_USE_ZONE_ACTIONS and P104_USE_ZONE_ORDERING -// Disabled P104_DEBUG_DEV by default -// 2021-06-28 tonhuisman: Bugfixes during testing, re-enable subcommands for ESP8266 display build -// 2021-06-27 tonhuisman: Implement 'barType' option for Bar-graph, bugfixes, bugfixes, bugfixes -// 2021-06-26 tonhuisman: Implement 'direction' option for Bar-graph, bugfixes -// 2021-06-26 tonhuisman: Add update command for updating one or all zones, restart repeat timer if content is updated by command, bugfixes -// 2021-06-24 tonhuisman: Add min/max range with negative minimal value support and zero-point indication if possible -// 2021-06-23 tonhuisman: Add Bar-graph option and bar command (initial feature, options to implement) guarded with P104_USE_BAR_GRAPH -// 2021-06-22 tonhuisman: Add Bar-graph initial code-infrastructure -// 2021-06-21 tonhuisman: Add options for 'formatting' time & date, will be disabled on memory-tight configs guarded by -// P104_USE_DATETIME_OPTIONS -// Introduced guard P104_ADD_SETTINGS_NOTES to disable some addFormNotes() to further reduce code size -// 2021-06-19 tonhuisman: Implement repeat delay, add settxt command, add command reference (above), bug fixing, some source reorganization -// Webform_Load now works on current settings if the plugin is active, instead of last stored settings -// Disabled most commands a some fonts to make the build fit in the ESP8266 display configuration -// 2021-06-18 tonhuisman: Implement PLUGIN_WRITE commands -// Implement several fonts extracted from MD_Parola examples (Vertical, Extended ASCII, Full Double Height, Arabic, -// Greek, Katakana) (NB: Vertical isn't working as expected yet) Will be disabled when flash memory is tight -// 2021-06-16 tonhuisman: Implement Clock and Date (once per second) -// 2021-06-13 tonhuisman: First working version, many improvemnts to make, like command handling, repeat timer implementaation -// 2021-05 tonhuisman: Store and retrieve settings for max 8 (ESP82xx) or 16 (ESP32) zones -// 2021-04 tonhuisman: Pickup and rework to get it working with ESPEasy -// 2021-03 rixtyan : Initial plugin setup, partially based on P073 MAX7219 LED display + +/** History: + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus + * 2025-04-03 tonhuisman: Set character spacing correctly when changing fonts + * 2023-10-15 tonhuisman: Code improvements, now using NR_ELEMENTS macro instead of multiple #ifdefs and increments + * Re-enable use of settings-version V3 after some more testing + * 2023-10-08 tonhuisman: Disable use of settings-version V3 for backward compatibility + * 2023-08-15 tonhuisman: Implement Extended CustomTaskSettings, and use that to significantly improve saving the settings on LittleFS by + * a) only storing the settings-version (V3) in regular CustomTaskSettings file, and the rest in the Extended + * CustomTaskSettings file, by using the offset as a starting location for the data elements + * b) Combine storing the size and the data-block in a single save action + * Apply toStringNoZero() converter to reduce the settings-data to be saved + * 2023-08-13 tonhuisman: Add Dot subcommand for pixel-drawing in a zone. Can be applied on any type of zone (so can be overwritten by the + * original content when that's updated...) + * Set default Hardware type to FC16, as that's the most used for modules found on Aliexpress + * 2023-03-07 tonhuisman: Parse text to display without trimming off leading and trailing spaces + * 2022-08-12 tonhuisman: Remove [DEVELOPMENT] tag + * 2021-10-03 tonhuisman: Add Inverted option per zone + * 2021-09 tonhuisman: Minor improvements, attempts to fix stack failures + * 2021-08-08 tonhuisman: Reworked loading & saving the settings from A huge fixed size pre-allocated block to dynamic allocation + * and saving/loading per zone. That should sove the numerous stack related crashes. + * 2021-08-07 tonhuisman: Review feedback: several small improvements and corrections + * 2021-07-18 tonhuisman: Small optimizations and improvements + * 2021-07-14 tonhuisman: Fix some bugs in font selection, add Text reverse content type to improve usability of Vertical font + * 2021-07-12 tonhuisman: Reduce number of reconfiguration during command handling, will be applied the next time content is displayed + * update/correct some documentation + * 2021-07-08 tonhuisman: Several bugfixes: settings defaults, fix brightness to enable 0 value, simplify storing the zone settings + * 2021-06-29-2021-07-03: Add Actions column to insert before/after zone or delete a zone, order the zones either in numeric order + * tonhuisman: or in display order ('upside-down'), fixed many bugs, refactored bar-graph method, other improvements + * All optional at compile-time by P104_USE_ZONE_ACTIONS and P104_USE_ZONE_ORDERING + * Disabled P104_DEBUG_DEV by default + * 2021-06-28 tonhuisman: Bugfixes during testing, re-enable subcommands for ESP8266 display build + * 2021-06-27 tonhuisman: Implement 'barType' option for Bar-graph, bugfixes, bugfixes, bugfixes + * 2021-06-26 tonhuisman: Implement 'direction' option for Bar-graph, bugfixes + * 2021-06-26 tonhuisman: Add update command for updating one or all zones, restart repeat timer if content is updated by command, bugfixes + * 2021-06-24 tonhuisman: Add min/max range with negative minimal value support and zero-point indication if possible + * 2021-06-23 tonhuisman: Add Bar-graph option and bar command (initial feature, options to implement) guarded with P104_USE_BAR_GRAPH + * 2021-06-22 tonhuisman: Add Bar-graph initial code-infrastructure + * 2021-06-21 tonhuisman: Add options for 'formatting' time & date, will be disabled on memory-tight configs guarded by + * P104_USE_DATETIME_OPTIONS + * Introduced guard P104_ADD_SETTINGS_NOTES to disable some addFormNotes() to further reduce code size + * 2021-06-19 tonhuisman: Implement repeat delay, add settxt command, add command reference (above), bug fixing, some source reorganization + * Webform_Load now works on current settings if the plugin is active, instead of last stored settings + * Disabled most commands a some fonts to make the build fit in the ESP8266 display configuration + * 2021-06-18 tonhuisman: Implement PLUGIN_WRITE commands + * Implement several fonts extracted from MD_Parola examples (Vertical, Extended ASCII, Full Double Height, Arabic, + * Greek, Katakana) (NB: Vertical isn't working as expected yet) Will be disabled when flash memory is tight + * 2021-06-16 tonhuisman: Implement Clock and Date (once per second) + * 2021-06-13 tonhuisman: First working version, many improvemnts to make, like command handling, repeat timer implementaation + * 2021-05 tonhuisman: Store and retrieve settings for max 8 (ESP82xx) or 16 (ESP32) zones + * 2021-04 tonhuisman: Pickup and rework to get it working with ESPEasy + * 2021-03 rixtyan : Initial plugin setup, partially based on P073 MAX7219 LED display + */ # define PLUGIN_104 # define PLUGIN_ID_104 104 @@ -143,6 +146,7 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) dev.Type = DEVICE_TYPE_SPI; dev.VType = Sensor_VType::SENSOR_TYPE_NONE; dev.ExitTaskBeforeSave = false; + dev.SpiBusSelect = true; break; } diff --git a/src/_P111_RC522_RFID.ino b/src/_P111_RC522_RFID.ino index 20ee7c3209..844f7ece9d 100644 --- a/src/_P111_RC522_RFID.ino +++ b/src/_P111_RC522_RFID.ino @@ -8,6 +8,7 @@ /** Changelog: * 2025-09-12 TD-er: Add support for 7-byte UID * 2025-08-20 TD-er: Speed-up reading + send immediate event + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-06-14 tonhuisman: Add support for Custom Value Type per task value * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for RFID) * Update changelog @@ -52,6 +53,7 @@ boolean Plugin_111(uint8_t function, struct EventStruct *event, String& string) dev.HasFormatUserVar = true; dev.SendDataOption = true; dev.CustomVTypeVar = true; + dev.SpiBusSelect = true; break; } @@ -183,7 +185,8 @@ boolean Plugin_111(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - initPluginTaskData(event->TaskIndex, new (std::nothrow) P111_data_struct(P111_CS_PIN, P111_RST_PIN, P111_IRQ_PIN)); + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + initPluginTaskData(event->TaskIndex, new (std::nothrow) P111_data_struct(P111_CS_PIN, P111_RST_PIN, P111_IRQ_PIN, spi_bus)); P111_data_struct *P111_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr != P111_data) { diff --git a/src/_P116_ST77xx.ino b/src/_P116_ST77xx.ino index 28f192e598..3fa55761d0 100644 --- a/src/_P116_ST77xx.ino +++ b/src/_P116_ST77xx.ino @@ -7,35 +7,38 @@ // ####################################################################################################### -// History: -// 2025-06-11 tonhuisman: Add support for ST7789v3/ST7735 display with 170x320 resolution -// 2025-02-20 tonhuisman: Add support for ST7735 display with 172x320 resolution -// 2024-05-04 tonhuisman: Add Default font selection setting, if AdafruitGFX_Helper fonts are included -// 2024-03-17 tonhuisman: Add support for another alternative initialization for ST7735 displays, as the display controller -// used on the LilyGO TTGO T-Display (16 MB) seems to be a ST7735, despite being documented as ST7789 -// By default (also) only enabled on ESP32 builds -// Disabled the ST7789 alternatives for now, as that's not verified on any hardware -// 2024-03-09 tonhuisman: Add support for alternative initialization sequences for ST7789 displays, like used on -// some LilyGO models like the TTGO T-Display (16 MB Flash), and possibly the T-Display S3 -// By default only enabled on ESP32 builds -// 2023-02-27 tonhuisman: Implement support for getting config values, see AdafruitGFX_Helper.h changelog for details -// 2022-07-06 tonhuisman: Add support for ST7735sv M5Stack StickC (Inverted colors) -// 2021-11-16 tonhuisman: P116: Change state from Development to Testing -// 2021-11-08 tonhuisman: Add support for function PLUGIN_GET_DISPLAY_PARAMETERS for retrieving the display parameters -// as implemented by FT6206 touchscreen plugin. Added ST77xx_type_toResolution -// 2021-11-06 tonhuisman: P116: Add support for ST7796s 320x480 displays -// Changed name of plugin to 'Display - ST77xx TFT' (was 'Display - ST7735/ST7789 TFT') -// 2021-08-16 tonhuisman: P116: Add default color settings -// 2021-08-16 tonhuisman: P116: Reorder some device configuration options, add backlight command (triggerCmd option) -// 2021-08-15 tonhuisman: P116: Make CursorX/CursorY coordinates available as Values (no events are generated!) -// P116: Use more features of AdafruitGFX_helper -// AdafruitGFX: Apply 'Text Print Mode' options -// 2021-08 tonhuisman: Refactor into AdafruitGFX_helper -// 2021-08 tonhuisman: Continue development, added new features, font scaling, display limits, extra text lines -// update to current ESPEasy state/style of development, make multi-instance possible -// 2020-08 tonhuisman: Adaptations for multiple ST77xx chips, ST7735s, ST7789vw (shelved temporarily) -// Added several features like display button, rotation -// 2020-04 WDS (Wolfdieter): initial plugin for ST7735, based on P012 +/** History: + * 2025-08-29 tonhuisman: Fix GPIO pin display on Devices page, no default GPIO pins for ESP32 + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus + * 2025-06-11 tonhuisman: Add support for ST7789v3/ST7735 display with 170x320 resolution + * 2025-02-20 tonhuisman: Add support for ST7735 display with 172x320 resolution + * 2024-05-04 tonhuisman: Add Default font selection setting, if AdafruitGFX_Helper fonts are included + * 2024-03-17 tonhuisman: Add support for another alternative initialization for ST7735 displays, as the display controller + * used on the LilyGO TTGO T-Display (16 MB) seems to be a ST7735, despite being documented as ST7789 + * By default (also) only enabled on ESP32 builds + * Disabled the ST7789 alternatives for now, as that's not verified on any hardware + * 2024-03-09 tonhuisman: Add support for alternative initialization sequences for ST7789 displays, like used on + * some LilyGO models like the TTGO T-Display (16 MB Flash), and possibly the T-Display S3 + * By default only enabled on ESP32 builds + * 2023-02-27 tonhuisman: Implement support for getting config values, see AdafruitGFX_Helper.h changelog for details + * 2022-07-06 tonhuisman: Add support for ST7735sv M5Stack StickC (Inverted colors) + * 2021-11-16 tonhuisman: P116: Change state from Development to Testing + * 2021-11-08 tonhuisman: Add support for function PLUGIN_GET_DISPLAY_PARAMETERS for retrieving the display parameters + * as implemented by FT6206 touchscreen plugin. Added ST77xx_type_toResolution + * 2021-11-06 tonhuisman: P116: Add support for ST7796s 320x480 displays + * Changed name of plugin to 'Display - ST77xx TFT' (was 'Display - ST7735/ST7789 TFT') + * 2021-08-16 tonhuisman: P116: Add default color settings + * 2021-08-16 tonhuisman: P116: Reorder some device configuration options, add backlight command (triggerCmd option) + * 2021-08-15 tonhuisman: P116: Make CursorX/CursorY coordinates available as Values (no events are generated!) + * P116: Use more features of AdafruitGFX_helper + * AdafruitGFX: Apply 'Text Print Mode' options + * 2021-08 tonhuisman: Refactor into AdafruitGFX_helper + * 2021-08 tonhuisman: Continue development, added new features, font scaling, display limits, extra text lines + * update to current ESPEasy state/style of development, make multi-instance possible + * 2020-08 tonhuisman: Adaptations for multiple ST77xx chips, ST7735s, ST7789vw (shelved temporarily) + * Added several features like display button, rotation + * 2020-04 WDS (Wolfdieter): initial plugin for ST7735, based on P012 + */ # define PLUGIN_116 # define PLUGIN_ID_116 116 @@ -60,6 +63,7 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) dev.ValueCount = 2; dev.TimerOption = true; dev.TimerOptional = true; + dev.SpiBusSelect = true; break; } @@ -84,11 +88,13 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) break; } + # ifndef LIMIT_BUILD_SIZE case PLUGIN_WEBFORM_SHOW_GPIO_DESCR: { const char *separator = event->String1.c_str(); // contains the NewLine sequence string = strformat( - F("CS: %s%sDC: %s%s RES: %s%sBtn: %s%sBckl: : %s"), + F("%sCS: %s%sDC: %s%s RES: %s%sBtn: %s%sBckl: : %s"), + separator, formatGpioLabel(PIN(0), false).c_str(), separator, formatGpioLabel(PIN(1), false).c_str(), @@ -101,21 +107,15 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) success = true; break; } + # endif // ifndef LIMIT_BUILD_SIZE case PLUGIN_SET_DEFAULTS: { - # ifdef ESP32 - - if (Settings.InitSPI == 2) { // When using ESP32 H(ardware-)SPI - PIN(0) = P116_TFT_CS_HSPI; - } else { - PIN(0) = P116_TFT_CS; - } - # else // ifdef ESP32 + # ifdef ESP8266 PIN(0) = P116_TFT_CS; - # endif // ifdef ESP32 - PIN(1) = P116_TFT_DC; - PIN(2) = P116_TFT_RST; + PIN(1) = P116_TFT_DC; + PIN(2) = P116_TFT_RST; + # endif // ifdef ESP8266 P116_CONFIG_BUTTON_PIN = -1; // No button connected P116_CONFIG_BACKLIGHT_PIN = P116_BACKLIGHT_PIN; P116_CONFIG_BACKLIGHT_PERCENT = 100; // Percentage backlight @@ -342,7 +342,9 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { initPluginTaskData(event->TaskIndex, new (std::nothrow) P116_data_struct(static_cast(P116_CONFIG_FLAG_GET_TYPE), P116_CONFIG_FLAG_GET_ROTATION, @@ -355,7 +357,8 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) P116_CONFIG_FLAG_GET_CMD_TRIGGER)), P116_CONFIG_GET_COLOR_FOREGROUND, P116_CONFIG_GET_COLOR_BACKGROUND, - bitRead(P116_CONFIG_FLAGS, P116_CONFIG_FLAG_BACK_FILL) == 0 + bitRead(P116_CONFIG_FLAGS, P116_CONFIG_FLAG_BACK_FILL) == 0, + spi_bus # if ADAGFX_FONTS_INCLUDED , P116_CONFIG_DEFAULT_FONT diff --git a/src/_P118_Itho.ino b/src/_P118_Itho.ino index 1c83be4f8e..3cec889a89 100644 --- a/src/_P118_Itho.ino +++ b/src/_P118_Itho.ino @@ -17,6 +17,7 @@ // https://github.com/arjenhiemstra/IthoEcoFanRFT /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for ITHO) * Changelog is reverted and reformatted! * 05-03-2023 tonhuisman: Deprecate 'state' command, and add support for 'itho' as the main command @@ -135,6 +136,7 @@ boolean Plugin_118(uint8_t function, struct EventStruct *event, String& string) dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; dev.ValueCount = 3; dev.SendDataOption = true; + dev.SpiBusSelect = true; break; } @@ -198,10 +200,12 @@ boolean Plugin_118(uint8_t function, struct EventStruct *event, String& string) # endif // ifdef P118_DEBUG_LOG if (validGpio(P118_CSPIN) && (P118_IRQPIN != P118_CSPIN)) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); initPluginTaskData(event->TaskIndex, new (std::nothrow) P118_data_struct(P118_CSPIN, P118_IRQPIN, P118_CONFIG_LOG == 1, - P118_CONFIG_RF_LOG == 1)); + P118_CONFIG_RF_LOG == 1, + spi_bus)); P118_data_struct *P118_data = static_cast(getPluginTaskData(event->TaskIndex)); success = (nullptr != P118_data) && P118_data->plugin_init(event); diff --git a/src/_P125_ADXL345_SPI.ino b/src/_P125_ADXL345_SPI.ino index d90d8ed3fa..f2a9295d5e 100644 --- a/src/_P125_ADXL345_SPI.ino +++ b/src/_P125_ADXL345_SPI.ino @@ -11,6 +11,7 @@ */ /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported yet for Accelerometer) * Use all relevant code from P120 (I2C variant of the same sensor) * 2021-12-10 tonhuisman, Start SPI interface version of ADXL345 plugin, based on P120 ADXL345 I2C plugin @@ -51,6 +52,7 @@ boolean Plugin_125(uint8_t function, struct EventStruct *event, String& string) dev.TimerOptional = true; dev.PluginStats = true; dev.OutputDataType = Output_Data_type_t::Simple; + dev.SpiBusSelect = true; break; } diff --git a/src/_P141_PCD8544_Nokia5110.ino b/src/_P141_PCD8544_Nokia5110.ino index 7c79d281ad..2511a34b01 100644 --- a/src/_P141_PCD8544_Nokia5110.ino +++ b/src/_P141_PCD8544_Nokia5110.ino @@ -8,6 +8,7 @@ /** Changelog: + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2024-08-12 tonhuisman: Add Default font selection setting, if AdafruitGFX_Helper fonts are included * 2022-10-08 tonhuisman: Enable PLUGIN_GET_CONFIG_VALUE event to get runtime info from plugin * 2022-09-24 tonhuisman: Store inverted setting when changed via inv subcommand (not saved) @@ -53,6 +54,7 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) dev.SendDataOption = false; dev.TimerOption = true; dev.TimerOptional = true; + dev.SpiBusSelect = true; break; } @@ -248,7 +250,9 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { initPluginTaskData(event->TaskIndex, new (std::nothrow) P141_data_struct(P141_CONFIG_FLAG_GET_ROTATION, P141_CONFIG_FLAG_GET_FONTSCALE, @@ -262,7 +266,8 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) ADAGFX_WHITE, ADAGFX_BLACK, bitRead(P141_CONFIG_FLAGS, P141_CONFIG_FLAG_BACK_FILL) == 0, - bitRead(P141_CONFIG_FLAGS, P141_CONFIG_FLAG_INVERTED) == 1 + bitRead(P141_CONFIG_FLAGS, P141_CONFIG_FLAG_INVERTED) == 1, + spi_bus # if ADAGFX_FONTS_INCLUDED , P141_CONFIG_DEFAULT_FONT diff --git a/src/_P162_MCP42xxx.ino b/src/_P162_MCP42xxx.ino index 77d3454c69..7699492050 100644 --- a/src/_P162_MCP42xxx.ino +++ b/src/_P162_MCP42xxx.ino @@ -6,6 +6,7 @@ // ####################################################################################################### /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery * 2024-04-16 tonhuisman: Add Send values on change option, so Interval can be set to 0, and the data will be sent when changed * 2024-04-10 tonhuisman: Initial version. Support for Digipot MCP42xxx (dual channel) and MCP41xxx (single channel). @@ -40,6 +41,7 @@ boolean Plugin_162(uint8_t function, struct EventStruct *event, String& string) dev.TimerOptional = true; dev.FormulaOption = true; dev.PluginStats = true; + dev.SpiBusSelect = true; break; } @@ -123,7 +125,8 @@ boolean Plugin_162(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - initPluginTaskData(event->TaskIndex, new (std::nothrow) P162_data_struct(P162_CS_PIN, P162_RST_PIN, P162_SHD_PIN)); + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + initPluginTaskData(event->TaskIndex, new (std::nothrow) P162_data_struct(P162_CS_PIN, P162_RST_PIN, P162_SHD_PIN, spi_bus)); P162_data_struct *P162_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr != P162_data) { diff --git a/src/_P172_BMP3xx_SPI.ino b/src/_P172_BMP3xx_SPI.ino index 7864d72551..e179ad3075 100644 --- a/src/_P172_BMP3xx_SPI.ino +++ b/src/_P172_BMP3xx_SPI.ino @@ -6,6 +6,7 @@ // ####################################################################################################### /** Changelog: + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery * Use all relevant code from P154 (I2C variant of the same sensor) * 2024-06-30 tonhuisman: Start SPI plugin, based on P154, re-using most code (dependency checked in define_plugin_sets.h) @@ -36,6 +37,7 @@ boolean Plugin_172(uint8_t function, struct EventStruct *event, String& string) dev.SendDataOption = true; dev.TimerOption = true; dev.PluginStats = true; + dev.SpiBusSelect = true; break; } diff --git a/src/src/Commands/SDCARD.cpp b/src/src/Commands/SDCARD.cpp index 1a95297afa..dc37ae81bf 100644 --- a/src/src/Commands/SDCARD.cpp +++ b/src/src/Commands/SDCARD.cpp @@ -6,11 +6,12 @@ #if FEATURE_SD -#include "../ESPEasyCore/Serial.h" -#include "../Globals/Settings.h" -#include "../Helpers/StringConverter.h" +# include "../ESPEasyCore/Serial.h" +# include "../Globals/Settings.h" +# include "../Helpers/Hardware_SPI.h" +# include "../Helpers/StringConverter.h" -#include +# include void printDirectory(fs::File dir, int numTabs) @@ -40,22 +41,26 @@ void printDirectory(fs::File dir, int numTabs) } } - -const __FlashStringHelper * Command_SD_LS(struct EventStruct *event, const char* Line) +const __FlashStringHelper* Command_SD_LS(struct EventStruct *event, const char*Line) { - fs::File root = SD.open("/"); - root.rewindDirectory(); - printDirectory(root, 0); - root.close(); - return return_see_serial(event); + if (initSDcard()) { + fs::File root = SD.open("/"); + root.rewindDirectory(); + printDirectory(root, 0); + root.close(); + return return_see_serial(event); + } + return return_command_failed_flashstr(); } -String Command_SD_Remove(struct EventStruct *event, const char* Line) +String Command_SD_Remove(struct EventStruct *event, const char*Line) { // FIXME TD-er: This one is not using parseString* function String fname = Line; + fname = fname.substring(9); - SD.remove((char*)fname.c_str()); + SD.remove((char *)fname.c_str()); return return_result(event, concat(F("Removing:"), fname)); } -#endif + +#endif // if FEATURE_SD diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index a1aff7ab0a..b1582ef0cd 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -47,6 +47,11 @@ // #define I2C_PERIPHERAL_BUS_??? 9 // bit-offset for I2C bus used for the ??? #endif // if FEATURE_I2C_MULTIPLE +// Stored in Settings.I2C_SPI_bus_Flags !!! +#define SPI_FLAGS_TASK_BUS_NUMBER 0 // 2 bit, stores the configured bus for a task +// Stored in Settings.I2C_SPI_bus_Flags for Task 1 settings +// #define SPI_FLAGS_xxx_unused 2 // 2 bit, available +#define SPI_FLAGS_SDCARD_BUS_NUMBER 4 // 2 bit, stores the configured bus for the SDCard /*********************************************************************************************\ * DeviceStruct @@ -118,7 +123,7 @@ struct DeviceStruct uint32_t MqttStateClass : 1; // MQTT StateClass setting in DevicesPage uint32_t HideDerivedValues : 1; // Hide the options for derived values uint32_t NoDeviceSettings : 1; // Indicating the device is not referencing actual hardware, like the Dummy task - uint32_t Dummy25 : 1; // Dummy added to force alignment, can be re-used + uint32_t SpiBusSelect : 1; // Allow selection of the SPI bus, if multiple buses are configured uint32_t Dummy26 : 1; // Dummy added to force alignment, can be re-used uint32_t Dummy27 : 1; // Dummy added to force alignment, can be re-used uint32_t Dummy28 : 1; // Dummy added to force alignment, can be re-used diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index dc6ac78382..beac8867d7 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -366,21 +366,33 @@ class SettingsStruct_tmpl PinBootState getPinBootState(int8_t gpio_pin) const; void setPinBootState(int8_t gpio_pin, PinBootState state); - bool getSPI_pins(int8_t spi_gpios[3]) const; + bool getSPI_pins(int8_t spi_gpios[3], + uint8_t spi_bus = 0, + bool noCheck = false) const; + bool isSPI_enabled(uint8_t spi_bus) const; #ifdef ESP32 - spi_host_device_t getSPI_host() const; + spi_host_device_t getSPI_host(uint8_t spi_bus = 0) const; #endif // Return true when pin is one of the SPI pins and SPI is enabled - bool isSPI_pin(int8_t pin) const; + bool isSPI_pin(int8_t pin, + uint8_t spi_bus = 0xFF) const; // Return true when SPI enabled and opt. user defined pins valid. - bool isSPI_valid() const; + bool isSPI_valid(uint8_t spi_bus) const; // Return true when pin is one of the configured I2C pins. bool isI2C_pin(int8_t pin) const; + uint8_t getSPIBusForTask(taskIndex_t TaskIndex) const; + void setSPIBusForTask(taskIndex_t TaskIndex, uint8_t spi_bus); + + #if FEATURE_SD + uint8_t getSPIBusForSDCard() const; + void setSPIBusForSDCard(uint8_t spi_bus); + #endif // if FEATURE_SD + // Return true if I2C settings are correct bool isI2CEnabled(uint8_t i2cBus) const; @@ -565,7 +577,11 @@ class SettingsStruct_tmpl int8_t I2C3_Multiplexer_Type = I2C_MULTIPLEXER_NONE; int8_t I2C3_Multiplexer_Addr = -1; int8_t I2C3_Multiplexer_ResetPin = -1; - uint32_t OLD_TaskDeviceID[N_TASKS - 7] = {0}; //UNUSED: this can be reused + uint8_t InitSPI1 = 0; // 0 = disabled, 1= enabled but for ESP32 there is option 2= SPI2 9 = User defined, see src/src/WebServer/HardwarePage.h enum SPI_Options_e + int8_t SPI1_SCLK_pin = -1; + int8_t SPI1_MISO_pin = -1; + int8_t SPI1_MOSI_pin = -1; + unsigned int OLD_TaskDeviceID[N_TASKS - 8] = {0}; // UNUSED: this can be reused // FIXME TD-er: When used on ESP8266, this conversion union may not work // It might work as it is 32-bit in size. @@ -681,7 +697,7 @@ class SettingsStruct_tmpl int8_t I2C_Multiplexer_Type = I2C_MULTIPLEXER_NONE; int8_t I2C_Multiplexer_Addr = -1; int8_t I2C_Multiplexer_Channel[N_TASKS]{}; - uint8_t I2C_Flags[N_TASKS] = {0}; + uint8_t I2C_SPI_bus_Flags[N_TASKS] = {0}; uint32_t I2C_clockSpeed_Slow = 100000; int8_t I2C_Multiplexer_ResetPin = -1; diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 41f017435d..4a4afaef38 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -988,14 +988,20 @@ void SettingsStruct_tmpl::setPinBootState(int8_t gpio_pin, PinBootState } template -bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3]) const { +bool SettingsStruct_tmpl::isSPI_enabled(uint8_t spi_bus) const { + const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); + return SPI_Options_e::None != SPI_selection; +} + +template +bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3], uint8_t spi_bus, bool noCheck) const { spi_gpios[0] = -1; spi_gpios[1] = -1; spi_gpios[2] = -1; - if (isSPI_valid()) { + if (noCheck || isSPI_valid(spi_bus)) { # ifdef ESP32 - const SPI_Options_e SPI_selection = static_cast(InitSPI); + const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); switch (SPI_selection) { case SPI_Options_e::Vspi_Fspi: @@ -1016,9 +1022,21 @@ bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3]) const { #endif case SPI_Options_e::UserDefined: { - spi_gpios[0] = SPI_SCLK_pin; - spi_gpios[1] = SPI_MISO_pin; - spi_gpios[2] = SPI_MOSI_pin; + #ifdef ESP32 + if (0 == spi_bus) + #endif // ifdef ESP32 + { + spi_gpios[0] = SPI_SCLK_pin; + spi_gpios[1] = SPI_MISO_pin; + spi_gpios[2] = SPI_MOSI_pin; + } + #ifdef ESP32 + else if (1 == spi_bus) { + spi_gpios[0] = SPI1_SCLK_pin; + spi_gpios[1] = SPI1_MISO_pin; + spi_gpios[2] = SPI1_MOSI_pin; + } + #endif // ifdef ESP32 break; } case SPI_Options_e::None: @@ -1035,10 +1053,10 @@ bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3]) const { #ifdef ESP32 template -spi_host_device_t SettingsStruct_tmpl::getSPI_host() const +spi_host_device_t SettingsStruct_tmpl::getSPI_host(uint8_t spi_bus) const { - if (isSPI_valid()) { - const SPI_Options_e SPI_selection = static_cast(InitSPI); + if (isSPI_valid(spi_bus)) { + const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); switch (SPI_selection) { case SPI_Options_e::Vspi_Fspi: { @@ -1081,31 +1099,83 @@ spi_host_device_t SettingsStruct_tmpl::getSPI_host() const template -bool SettingsStruct_tmpl::isSPI_pin(int8_t pin) const { +bool SettingsStruct_tmpl::isSPI_pin(int8_t pin, uint8_t spi_bus) const { if (pin < 0) { return false; } int8_t spi_gpios[3]; - if (getSPI_pins(spi_gpios)) { + if (getSPI_pins(spi_gpios, 0u) && ((0xFF == spi_bus) || (0u == spi_bus))) { + for (uint8_t i = 0; i < 3; ++i) { + if (spi_gpios[i] == pin) { return true; } + } + } + #ifdef ESP32 + if ((getSPIBusCount() > 1u) && getSPI_pins(spi_gpios, 1u) && ((0xFF == spi_bus) || (1u == spi_bus))) { for (uint8_t i = 0; i < 3; ++i) { if (spi_gpios[i] == pin) { return true; } } } + #endif // ifdef ESP32 return false; } template -bool SettingsStruct_tmpl::isSPI_valid() const { - if (InitSPI == static_cast(SPI_Options_e::None)) { return false; } +bool SettingsStruct_tmpl::isSPI_valid(uint8_t spi_bus) const { + int8_t spi0_pins[3]; + bool result = false; + getSPI_pins(spi0_pins, 0u, true); - if (InitSPI == static_cast(SPI_Options_e::UserDefined)) { - return !((SPI_SCLK_pin == -1) || - (SPI_MISO_pin == -1) || - (SPI_MOSI_pin == -1) || - (SPI_SCLK_pin == SPI_MISO_pin) || - (SPI_MISO_pin == SPI_MOSI_pin) || - (SPI_MOSI_pin == SPI_SCLK_pin)); + #ifdef ESP32 + if (0 == spi_bus) + #endif // ifdef ESP32 + { + if (InitSPI == static_cast(SPI_Options_e::None)) { return false; } + + result = !((spi0_pins[0] == -1) || + (spi0_pins[1] == -1) || + (spi0_pins[2] == -1) || + (spi0_pins[0] == spi0_pins[1]) || + (spi0_pins[1] == spi0_pins[2]) || + (spi0_pins[2] == spi0_pins[0])); + #ifdef ESP32 + if (result && (getSPIBusCount() > 1)) { // Cross-check pins with other bus + int8_t spi1_pins[3]; + getSPI_pins(spi1_pins, 1u, true); + + for (uint8_t i = 0; (i < 3) && result; ++i) { + for (uint8_t j = 0; (j < 3) && result; ++j) { + if (spi0_pins[i] == spi1_pins[j]) { + result = false; + } + } + } + } + #endif // ifdef ESP32 + } + #ifdef ESP32 + else if ((1 == spi_bus) && (getSPIBusCount() > 1)) { + if (InitSPI1 == static_cast(SPI_Options_e::None)) { return false; } + int8_t spi1_pins[3]; + getSPI_pins(spi1_pins, 1u, true); + + result = !((spi1_pins[0] == -1) || + (spi1_pins[1] == -1) || + (spi1_pins[2] == -1) || + (spi1_pins[0] == spi1_pins[1]) || + (spi1_pins[1] == spi1_pins[2]) || + (spi1_pins[2] == spi1_pins[0])); + + if (result) { // Cross-check pins + for (uint8_t i = 0; (i < 3) && result; ++i) { + for (uint8_t j = 0; (j < 3) && result; ++j) { + if (spi0_pins[i] == spi1_pins[j]) { + result = false; + } + } + } + } } - return true; + #endif // ifdef ESP32 + return result; } template @@ -1132,9 +1202,33 @@ bool SettingsStruct_tmpl::isI2CEnabled(uint8_t i2cBus) const { (getI2CClockSpeedSlow(i2cBus) > 0); } +// stored in I2C_SPI_bus_Flags per Task +template +uint8_t SettingsStruct_tmpl::getSPIBusForTask(taskIndex_t TaskIndex) const { + return get2BitFromUL(I2C_SPI_bus_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER); +} + +template +void SettingsStruct_tmpl::setSPIBusForTask(taskIndex_t TaskIndex, uint8_t spi_bus) { + set2BitToUL(I2C_SPI_bus_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER, spi_bus); +} + +#if FEATURE_SD +// stored in I2C_SPI_bus_Flags for Task 1 (index 0) +template +uint8_t SettingsStruct_tmpl::getSPIBusForSDCard() const { + return get2BitFromUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER); +} + +template +void SettingsStruct_tmpl::setSPIBusForSDCard(uint8_t spi_bus) { + set2BitToUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER, spi_bus); +} +#endif // if FEATURE_SD + template uint8_t SettingsStruct_tmpl::getI2CInterface(taskIndex_t TaskIndex) const { - return get3BitFromUL(I2C_Flags[TaskIndex], I2C_FLAGS_BUS_NUMBER); + return get3BitFromUL(I2C_SPI_bus_Flags[TaskIndex], I2C_FLAGS_BUS_NUMBER); } template diff --git a/src/src/DataTypes/SPI_options.cpp b/src/src/DataTypes/SPI_options.cpp index 57bac16f27..d20419226b 100644 --- a/src/src/DataTypes/SPI_options.cpp +++ b/src/src/DataTypes/SPI_options.cpp @@ -84,7 +84,7 @@ const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option) { return F("Unknown"); } -const __FlashStringHelper* getSPI_optionToShortString(SPI_Options_e option) { +const String getSPI_optionToShortString(SPI_Options_e option, uint8_t spi_bus) { switch (option) { case SPI_Options_e::None: return F("Disabled"); @@ -95,7 +95,12 @@ const __FlashStringHelper* getSPI_optionToShortString(SPI_Options_e option) { return F("HSPI"); #endif case SPI_Options_e::UserDefined: + #ifdef ESP32 + return concat(F("User-defined SPI Bus "), spi_bus); + #endif // ifdef ESP32 + #ifdef ESP8266 return F("User-defined SPI"); + #endif // ifdef ESP8266 } return F("Unknown"); } diff --git a/src/src/DataTypes/SPI_options.h b/src/src/DataTypes/SPI_options.h index 3bb385a8f7..3da9ce257b 100644 --- a/src/src/DataTypes/SPI_options.h +++ b/src/src/DataTypes/SPI_options.h @@ -2,6 +2,7 @@ #define DATATYPES_SPI_OPTIONS_H #include "../../ESPEasy_common.h" +#include "../Helpers/StringConverter.h" // ESP32 classic has default pins for HSPI. @@ -78,7 +79,7 @@ enum class SPI_Options_e { #ifdef ESP32 const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option); -const __FlashStringHelper* getSPI_optionToShortString(SPI_Options_e option); +const String getSPI_optionToShortString(SPI_Options_e option, uint8_t spi_bus = 0); #endif // ifdef ESP32 #endif // ifndef DATATYPES_SPI_OPTIONS_H diff --git a/src/src/Globals/Plugins.cpp b/src/src/Globals/Plugins.cpp index 2337076f2f..74f5de41b5 100644 --- a/src/src/Globals/Plugins.cpp +++ b/src/src/Globals/Plugins.cpp @@ -229,7 +229,7 @@ bool prepare_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) const uint8_t i2cBus = 0; #endif // if FEATURE_I2C_MULTIPLE - if (bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)) { + if (bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)) { I2CSelectLowClockSpeed(i2cBus); // Set to slow, also switch the bus } else { I2CSelectHighClockSpeed(i2cBus); // Set to normal, also switch the bus diff --git a/src/src/Globals/SPIe.cpp b/src/src/Globals/SPIe.cpp new file mode 100644 index 0000000000..d17238b37c --- /dev/null +++ b/src/src/Globals/SPIe.cpp @@ -0,0 +1,5 @@ +#include "../Globals/SPIe.h" + +#ifdef ESP32 +SPIClass SPIe(HSPI); +#endif // ifdef ESP32 diff --git a/src/src/Globals/SPIe.h b/src/src/Globals/SPIe.h new file mode 100644 index 0000000000..567768d74e --- /dev/null +++ b/src/src/Globals/SPIe.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef ESP32 +# include + +extern SPIClass SPIe; +#endif // ifdef ESP32 diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index c75b157279..8a6f25c1fd 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -550,6 +550,17 @@ bool BuildFixes() } } + if (Settings.Build <= 21303) { // 2025-08-25 + // Add second SPI bus + if ((Settings.SPI1_SCLK_pin == 0) && + (Settings.SPI1_MISO_pin == 0) && + (Settings.SPI1_MOSI_pin == 0) ){ + Settings.SPI1_SCLK_pin = -1; + Settings.SPI1_MISO_pin = -1; + Settings.SPI1_MOSI_pin = -1; + } + } + // Starting 2022/08/18 // Use get_build_nr() value for settings transitions. // This value will also be shown when building using PlatformIO, when showing the Compile time defines diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index f7265d4871..0c267560b4 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -178,7 +178,7 @@ void run_compiletime_checks() { static_assert(198u == offsetof(SettingsStruct, TaskDeviceNumber), "NOTIFICATION_MAX has changed?"); // All settings related to N_TASKS - static_assert((228 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. + static_assert((232 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. static_assert((200 + (67 * TASKS_MAX)) == offsetof(SettingsStruct, ControllerEnabled), ""); // Used to compute true offset. diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index 6763dbb4d5..a828b19639 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -20,6 +20,7 @@ #include "../Helpers/Hardware_device_info.h" #include "../Helpers/Hardware_GPIO.h" #include "../Helpers/Hardware_I2C.h" +#include "../Helpers/Hardware_SPI.h" #include "../Helpers/I2C_access.h" #include "../Helpers/Misc.h" #include "../Helpers/PortStatus.h" @@ -35,8 +36,6 @@ # include #endif // if defined(ESP32) -// #include "../../ESPEasy-Globals.h" - #ifdef ESP32 # include # include @@ -44,45 +43,14 @@ # include # include - # if ESP_IDF_VERSION_MAJOR == 4 - # if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3 - # include - # include - # include - # elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2 - # include - # include - # include - # elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 - # include - # include - # elif CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 - # include - # include - # include - # else // if CONFIG_IDF_TARGET_ESP32S3 - # error Target CONFIG_IDF_TARGET is not supported - # endif // if CONFIG_IDF_TARGET_ESP32S3 - # else // ESP32 IDF 5.x and later - # include - # include - # include - # endif // if ESP_IDF_VERSION_MAJOR == 4 - -#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 - # if ESP_IDF_VERSION_MAJOR < 5 - # define HAS_HALL_EFFECT_SENSOR 1 - # else // if ESP_IDF_VERSION_MAJOR < 5 - -// Support for Hall Effect sensor was removed in ESP_IDF 5.x - # define HAS_HALL_EFFECT_SENSOR 0 - # endif // if ESP_IDF_VERSION_MAJOR < 5 -# else - # define HAS_HALL_EFFECT_SENSOR 0 -# endif - - -# if ESP_IDF_VERSION_MAJOR >= 5 + // ESP32 IDF 5.x and later + # include + # include + # include + + // Support for Hall Effect sensor was removed in ESP_IDF 5.x + # define HAS_HALL_EFFECT_SENSOR 0 + # include # include @@ -91,27 +59,11 @@ // #include -# endif // if ESP_IDF_VERSION_MAJOR >= 5 - # include "../Helpers/Hardware_ADC_cali.h" -#if FEATURE_ETHERNET -#include -#endif - #endif // ifdef ESP32 -#if FEATURE_SD -# include -#endif // if FEATURE_SD - - -#include - - -# define GPIO_PLUGIN_ID 1 - /********************************************************************************************\ * Initialize specific hardware settings (only global ones, others are set through devices) \*********************************************************************************************/ @@ -206,6 +158,7 @@ void hardwareInit() } } + // Initialize I2C buses initI2C(); #if FEATURE_PLUGIN_PRIORITY @@ -213,78 +166,10 @@ void hardwareInit() PluginCall(PLUGIN_PRIORITY_INIT_ALL, nullptr, dummy); #endif // if FEATURE_PLUGIN_PRIORITY - bool tryInitSPI = true; -#if FEATURE_ETHERNET -// FIXME TD-er: Is this still needed? - if ((Settings.NetworkMedium == ESPEasy::net::NetworkMedium_t::Ethernet) && - isValid(Settings.ETH_Phy_Type) && - ESPEasy::net::isSPI_EthernetType(Settings.ETH_Phy_Type)) - { -#if !ETH_SPI_SUPPORTS_CUSTOM - tryInitSPI = false; -#endif - } -#endif - - - // SPI Init - bool SPI_initialized = false; - if (tryInitSPI && Settings.isSPI_valid()) - { - SPI.setHwCs(false); - - // MFD: for ESP32 enable the SPI on HSPI as the default is VSPI - #ifdef ESP32 - - const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); - int8_t spi_gpios[3] = {}; - - if (Settings.getSPI_pins(spi_gpios)) { - if (SPI_selection == SPI_Options_e::Vspi_Fspi) { - SPI.begin(); // Default SPI bus - } else { - SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); - } - SPI_initialized = true; - } - #else // ifdef ESP32 - SPI.begin(); - SPI_initialized = true; - #endif // ifdef ESP32 - } - - if (SPI_initialized) - { -#ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_INFO, F("INIT : SPI Init (without CS)")); -#endif - #if FEATURE_SD - - if (Settings.Pin_sd_cs >= 0) - { - if (SD.begin(Settings.Pin_sd_cs)) - { -#ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_INFO, F("SD : Init OK")); -#endif - } - else - { - SD.end(); -#ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_ERROR, F("SD : Init failed")); -#endif - } - } -#endif // if FEATURE_SD -#ifndef BUILD_NO_DEBUG - } else { - addLog(LOG_LEVEL_INFO, F("INIT : SPI not enabled")); -#endif - } + // Initialize SPI buses, also initializes SD-card when available in build and configured + initializeSPIBuses(); } - void checkResetFactoryPin() { static uint8_t factoryResetCounter = 0; diff --git a/src/src/Helpers/Hardware.h b/src/src/Helpers/Hardware.h index dc0f21bb40..68d3f406ab 100644 --- a/src/src/Helpers/Hardware.h +++ b/src/src/Helpers/Hardware.h @@ -57,23 +57,8 @@ int espeasy_analogRead(int pin, int getCPU_MaxFreqMHz(); int getCPU_MinFreqMHz(); -# if ESP_IDF_VERSION_MAJOR < 5 -# if CONFIG_IDF_TARGET_ESP32 -# define ESP_PM_CONFIG_T esp_pm_config_esp32_t -# elif CONFIG_IDF_TARGET_ESP32S3 -# define ESP_PM_CONFIG_T esp_pm_config_esp32s3_t -# elif CONFIG_IDF_TARGET_ESP32S2 -# define ESP_PM_CONFIG_T esp_pm_config_esp32s2_t -# elif CONFIG_IDF_TARGET_ESP32C6 -# define ESP_PM_CONFIG_T esp_pm_config_esp32c3_t -# elif CONFIG_IDF_TARGET_ESP32C3 -# define ESP_PM_CONFIG_T esp_pm_config_esp32c3_t -# elif CONFIG_IDF_TARGET_ESP32C2 -# define ESP_PM_CONFIG_T esp_pm_config_esp32c2_t -# endif // if CONFIG_IDF_TARGET_ESP32 -# else // if ESP_IDF_VERSION_MAJOR < 5 -# define ESP_PM_CONFIG_T esp_pm_config_t -# endif // if ESP_IDF_VERSION_MAJOR < 5 +// if ESP_IDF_VERSION_MAJOR > 5 +# define ESP_PM_CONFIG_T esp_pm_config_t #endif // ifdef ESP32 diff --git a/src/src/Helpers/Hardware_I2C.cpp b/src/src/Helpers/Hardware_I2C.cpp index b4c210c6b6..7767958e66 100644 --- a/src/src/Helpers/Hardware_I2C.cpp +++ b/src/src/Helpers/Hardware_I2C.cpp @@ -263,7 +263,7 @@ void I2CMultiplexerSelectByTaskIndex(taskIndex_t taskIndex) { const uint8_t i2cBus = 0; # endif // if FEATURE_I2C_MULTIPLE - if (!bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { + if (!bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { uint8_t i = Settings.I2C_Multiplexer_Channel[taskIndex]; if (i > 7) { return; } @@ -322,8 +322,8 @@ bool I2CMultiplexerPortSelectedForTask(taskIndex_t taskIndex) { # endif // if FEATURE_I2C_MULTIPLE if (!isI2CMultiplexerEnabled(i2cBus)) { return false; } - return (!bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != -1) - || (bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != 0); + return (!bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != -1) + || (bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != 0); } #endif // if FEATURE_I2CMULTIPLEXER diff --git a/src/src/Helpers/Hardware_SPI.cpp b/src/src/Helpers/Hardware_SPI.cpp new file mode 100644 index 0000000000..56bc55f487 --- /dev/null +++ b/src/src/Helpers/Hardware_SPI.cpp @@ -0,0 +1,144 @@ + +#include "../ESPEasy/net/DataTypes/NetworkMedium.h" +#include "../Helpers/Hardware_SPI.h" +#include "../Globals/Settings.h" + +#if FEATURE_SD +# include +#endif // if FEATURE_SD + +#include + +#include "../Globals/SPIe.h" + +#if FEATURE_ETHERNET +# include +#endif // if FEATURE_ETHERNET + +void initializeSPIBuses() { + // SPI Init + uint8_t SPI_initialized = 0; + + #ifdef ESP32 + uint8_t skipInitSPI = 0; + #endif // ifdef ESP32 + + #if FEATURE_ETHERNET + + if ((Settings.NetworkMedium == ESPEasy::net::NetworkMedium_t::Ethernet) && + isValid(Settings.ETH_Phy_Type) && + isSPI_EthernetType(Settings.ETH_Phy_Type)) + { + # if !ETH_SPI_SUPPORTS_CUSTOM + skipInitSPI = Settings.getSPIBusForEth() + 1; // Increment by 1 + # endif // if !ETH_SPI_SUPPORTS_CUSTOM + } + #endif // if FEATURE_ETHERNET + + + if (Settings.isSPI_valid(0u) + #ifdef ESP32 + || Settings.isSPI_valid(1u) + #endif // ifdef ESP32 + ) + { + #ifdef ESP32 + + const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); + int8_t spi_gpios[3] = {}; + + if (skipInitSPI != 1) { + SPI.end(); // Disconnect current GPIO mapping + } + + if ((skipInitSPI != 1) && Settings.getSPI_pins(spi_gpios, 0u)) { + SPI.setHwCs(false); + SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); // Use explicit GPIO configuration + SPI_initialized |= 1; + } + + delay(1); + + // Init second SPI interface (SPIe) + const SPI_Options_e SPI1_selection = static_cast(Settings.InitSPI1); + + if (skipInitSPI != 2) { + SPIe.end(); // Disconnect current GPIO mapping + } + + if ((skipInitSPI != 2) && Settings.getSPI_pins(spi_gpios, 1u)) { + SPIe.setHwCs(false); + SPIe.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); + SPI_initialized |= 2; + } + + #else // ifdef ESP32 + SPI.setHwCs(false); + SPI.begin(); // Use default GPIO configuration + SPI_initialized = 1; + #endif // ifdef ESP32 + } + + if (SPI_initialized) + { + #ifdef ESP32 + + for (uint8_t i = 1; i < 3; ++i) { + if (SPI_initialized & i) { + addLog(LOG_LEVEL_INFO, strformat(F("INIT : SPI Bus %d Init (without CS)"), i - 1)); + } + } + #endif // ifdef ESP32 + #ifdef ESP8266 + addLog(LOG_LEVEL_INFO, F("INIT : SPI Init (without CS)")); + #endif // ifdef ESP8266 + + #if FEATURE_SD + initSDcard(); + #endif // if FEATURE_SD + } else { + addLog(LOG_LEVEL_INFO, F("INIT : SPI not enabled")); + } +} + +#if FEATURE_SD +bool initSDcard() { + bool result = false; + + if (Settings.Pin_sd_cs >= 0) + { + # ifdef ESP32 + const uint8_t sdspi = Settings.getSPIBusForSDCard(); + # else // ifdef ESP32 + constexpr uint8_t sdspi{}; + # endif // ifdef ESP32 + + if (Settings.isSPI_enabled(sdspi)) { + # ifdef ESP32 + + if (SD.begin(Settings.Pin_sd_cs, (0 == sdspi) ? SPI : SPIe)) + # endif // ifdef ESP32 + # ifdef ESP8266 + + if (SD.begin(Settings.Pin_sd_cs)) + # endif // ifdef ESP8266 + { + # ifdef ESP32 + addLog(LOG_LEVEL_INFO, strformat(F("SD : Init on SPI Bus %d OK"), sdspi)); + # endif // ifdef ESP32 + # ifdef ESP8266 + addLog(LOG_LEVEL_INFO, F("SD : Init OK")); + # endif // ifdef ESP8266 + result = true; + } + else + { + SD.end(); + addLog(LOG_LEVEL_ERROR, F("SD : Init failed")); + } + } + } + return result; +} + +#endif // if FEATURE_SD diff --git a/src/src/Helpers/Hardware_SPI.h b/src/src/Helpers/Hardware_SPI.h new file mode 100644 index 0000000000..c104c9f0de --- /dev/null +++ b/src/src/Helpers/Hardware_SPI.h @@ -0,0 +1,10 @@ +#pragma once + +#include "../../ESPEasy_common.h" + + +void initializeSPIBuses(); + +#if FEATURE_SD +bool initSDcard(); +#endif // if FEATURE_SD diff --git a/src/src/Helpers/Hardware_device_info.h b/src/src/Helpers/Hardware_device_info.h index 48548fc1b3..4c5484ae88 100644 --- a/src/src/Helpers/Hardware_device_info.h +++ b/src/src/Helpers/Hardware_device_info.h @@ -117,6 +117,18 @@ constexpr uint8_t getI2CBusCount() { #endif } +constexpr uint8_t getSPIBusCount() { +#if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4) + return 2u; +#elif defined(ESP32C2) || defined(ESP32C3) || defined(ESP32C5) || defined(ESP32C6) || defined(ESP32H2) + return 1u; +#elif defined(ESP8266) + return 1u; +#else // if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4) + static_assert(false, "Implement processor architecture"); +#endif // if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4) +} + /********************************************************************************************\ PSRAM support \*********************************************************************************************/ diff --git a/src/src/Helpers/Misc.h b/src/src/Helpers/Misc.h index 5b263a3742..098c4039e7 100644 --- a/src/src/Helpers/Misc.h +++ b/src/src/Helpers/Misc.h @@ -21,11 +21,13 @@ #define setNBitToUL(N, B, V, M) N=(((N) & ~((M) << (B))) | (static_cast((V) & (M)) << (B))) #define getNBitFromUL(number, bitnr, mask) (((number) >> (bitnr)) & (mask)) +#define set16BitToUL(N, B, V) setNBitToUL(N, B, V, 0xFFFFUL) #define set8BitToUL(N, B, V) setNBitToUL(N, B, V, 0xFFUL) #define set4BitToUL(N, B, V) setNBitToUL(N, B, V, 0x0FUL) #define set3BitToUL(N, B, V) setNBitToUL(N, B, V, 0x07UL) #define set2BitToUL(N, B, V) setNBitToUL(N, B, V, 0x03UL) +#define get16BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0xFFFFUL) #define get8BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0xFFUL) #define get4BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0x0FUL) #define get3BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0x07UL) diff --git a/src/src/Helpers/SPI_Helper.cpp b/src/src/Helpers/SPI_Helper.cpp new file mode 100644 index 0000000000..ce1c291bd1 --- /dev/null +++ b/src/src/Helpers/SPI_Helper.cpp @@ -0,0 +1,41 @@ +#include "../Helpers/SPI_Helper.h" + +void SPIInterfaceSelector(String label, + String id, + uint8_t choice, + bool disabled) { + const uint8_t spiCount = getSPIBusCount(); + const bool spi0valid = Settings.isSPI_valid(0); + const bool spi1valid = Settings.isSPI_valid(1); + const uint8_t spiBitmap = (spi0valid ? 1 : 0) | (spi1valid ? 2 : 0); + const uint8_t spiMaxBusCount = (spiCount > 1 + ? (spi1valid ? 1 : 0) + : 0) + (spi0valid ? 1 : 0); + + if ((spiMaxBusCount > 1) || spi1valid) { // Show selector if only bus 1 is enabled + static uint8_t spiBusCount{}; + static uint8_t spiBusBitmap{}; + static int spiBusNumbers[2]; + static String spiBusAttr[2]; + + if (spiBusBitmap != spiBitmap) { + spiBusCount = 0; + spiBusNumbers[spiBusCount] = 0; + spiBusAttr[0] = spi0valid ? EMPTY_STRING : F("disabled"); + spiBusBitmap = spi0valid ? 1 : 0; + ++spiBusCount; + + if ((spiCount > 1) && spi1valid) { + spiBusNumbers[spiBusCount] = 1; + spiBusBitmap |= 2; + ++spiBusCount; + } + } + FormSelectorOptions selector(spiBusCount, + spiBusNumbers, + spiBusAttr); + selector.default_index = 0; + selector.enabled = !disabled; + selector.addFormSelector(label, id, choice); + } +} diff --git a/src/src/Helpers/SPI_Helper.h b/src/src/Helpers/SPI_Helper.h new file mode 100644 index 0000000000..c0568e61be --- /dev/null +++ b/src/src/Helpers/SPI_Helper.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../DataStructs/SettingsStruct.h" +#include "../DataTypes/TaskIndex.h" +#include "../Globals/Plugins.h" +#include "../Globals/Settings.h" +#include "../Helpers/Hardware_device_info.h" + +#include "../WebServer/Markup_Forms.h" + +void SPIInterfaceSelector(String label, + String id, + uint8_t choice, + bool disabled = false); diff --git a/src/src/Helpers/StringGenerator_GPIO.cpp b/src/src/Helpers/StringGenerator_GPIO.cpp index 0b58bb23b0..31425a522f 100644 --- a/src/src/Helpers/StringGenerator_GPIO.cpp +++ b/src/src/Helpers/StringGenerator_GPIO.cpp @@ -319,12 +319,22 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose } } } -#ifdef ESP8266 + #ifdef ESP32 + if (includeSPI) { + if (Settings.isSPI_pin(gpio, 0u)) { + return F("SPI (bus 0)"); + } + else if (Settings.isSPI_pin(gpio, 1u)) { + return F("SPI (bus 1)"); + } + } + #endif // ifdef ESP32 + #ifdef ESP8266 if (includeSPI && Settings.isSPI_pin(gpio)) { return F("SPI"); } -#endif // ifdef ESP8266 + #endif // ifdef ESP8266 if (includeStatusLed && (Settings.Pin_status_led == gpio) && (-1 != gpio)) { return F("Wifi Status LED"); diff --git a/src/src/PluginStructs/P039_data_struct.cpp b/src/src/PluginStructs/P039_data_struct.cpp index 814af8073c..5ddee65f9f 100644 --- a/src/src/PluginStructs/P039_data_struct.cpp +++ b/src/src/PluginStructs/P039_data_struct.cpp @@ -2,29 +2,1497 @@ #ifdef USES_P039 -/* - P039_data_struct::P039_data_struct( - uint16_t l_conversionResult, - uint8_t l_devicefaults, - unsigned long l_timer, - bool l_sensorFault, - bool l_convReady) - : conversionResult(l_conversionResult), deviceFaults(l_devicefaults), timer(l_timer), sensorFault(l_sensorFault), convReady(l_convReady) - {} +P039_data_struct::P039_data_struct(struct EventStruct*event) { + # ifdef ESP32 + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + _spi = 0 == spi_bus ? SPI : SPIe; + # endif // ifdef ESP32 +} + +bool P039_data_struct::begin(struct EventStruct*event) { + bool success = false; + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // set the slaveSelectPin as an output: + init_SPI_CS_Pin(CS_pin_no); + + // ensure MODE3 access to SPI device + _spi.setDataMode(SPI_MODE3); + + if (P039_MAX_TYPE == P039_MAX31855) { + sensorFault = false; + } + + + if (P039_MAX_TYPE == P039_MAX31856) { + // init string - content accoring to inital implementation of P039 - MAX31856 read function + // write to Adress 0x80 + // activate 50Hz filter in CR0, choose averaging and TC type from configuration in CR1, activate OV/UV/OC faults, write defaults to + // CJHF, CJLF, LTHFTH, LTHFTL, LTLFTH, LTLFTL, CJTO + uint8_t sendBuffer[11] = + { 0x80, static_cast(P039_RTD_FILT_TYPE), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE), 0xFC, 0x7F, 0xC0, 0x7F, + 0xFF, 0x80, 0x00, 0x00 }; + + transfer_n_ByteSPI(CS_pin_no, 11, &sendBuffer[0]); + + sensorFault = false; + + // start on shot conversion for upcoming read cycle + change8BitRegister(CS_pin_no, + (MAX31856_READ_ADDR_BASE + MAX31856_CR0), + (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), + MAX31856_SET_ONE_SHOT, + P039_SET); + } + + + if (P039_MAX_TYPE == P039_MAX31865) { + // two step initialization buffer + uint8_t initSendBufferHFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; + uint8_t initSendBufferLFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; + + // write intially 0x00 to CONFIG register + write8BitRegister(CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), 0x00u); + + // activate 50Hz filter, clear all faults, no auto conversion, no conversion started + change8BitRegister(CS_pin_no, + MAX31865_RD_ADDRESS(MAX31865_CONFIG), + MAX31865_WR_ADDRESS(MAX31865_CONFIG), + MAX31865_SET_50HZ, + static_cast(P039_RTD_FILT_TYPE)); + + // configure 2/4-wire sensor connection as default + MAX31865_setConType(CS_pin_no, P039_CONFIG_4); + + // set HighFault Threshold + transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferHFTH[0]); + + // set LowFault Threshold + transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferLFTH[0]); + + // clear all faults + MAX31865_clearFaults(CS_pin_no); + + // activate BIAS short before read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_SET); + + // save current timer for next calculation + timer = millis(); + + // start time to follow up on BIAS activation before starting the conversion + // and start conversion sequence via TIMER API + + Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); + } + + /* + if (P039_MAX_TYPE == P039_LM7x) + { + // TODO: c.k.i.: more detailed inits depending on the sub devices expected , e.g. TMP 122/124 + } + */ + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("P039 : %s : SPI Init - DONE"), getTaskDeviceName(event->TaskIndex).c_str())); + } + # endif // ifndef BUILD_NO_DEBUG + + return success; +} + +bool P039_data_struct::read(EventStruct *event) { + bool success = false; + + // Get the MAX Type (6675 / 31855 / 31856) + uint8_t MaxType = P039_MAX_TYPE; + + float Plugin_039_Celsius = NAN; + + switch (MaxType) { + case P039_MAX6675: + Plugin_039_Celsius = readMax6675(event); + break; + case P039_MAX31855: + Plugin_039_Celsius = readMax31855(event); + break; + case P039_MAX31856: + Plugin_039_Celsius = readMax31856(event); + break; + case P039_MAX31865: + Plugin_039_Celsius = readMax31865(event); + break; + case P039_LM7x: + Plugin_039_Celsius = readLM7x(event); + break; + } + + if (isValidFloat(Plugin_039_Celsius)) + { + UserVar.setFloat(event->TaskIndex, 0, Plugin_039_Celsius); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + String log = strformat(F("P039 : %s :"), getTaskDeviceName(event->TaskIndex).c_str()); + + const uint8_t valueCount = getValueCountForTask(event->TaskIndex); + + for (uint8_t i = 0; i < valueCount; ++i) + { + log += strformat( + F(" %s: %s"), + Cache.getTaskDeviceValueName(event->TaskIndex, i).c_str(), + formatUserVarNoCheck(event, i).c_str()); + } + addLogMove(LOG_LEVEL_INFO, log); + } + # endif // ifndef BUILD_NO_DEBUG + + if (definitelyGreaterThan(Plugin_039_Celsius, P039_TEMP_THRESHOLD)) { + success = true; + } + } + else + { + UserVar.setFloat(event->TaskIndex, 0, NAN); + UserVar.setFloat(event->TaskIndex, 1, NAN); + + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat(F("P039 : %s : No Sensor attached!"), getTaskDeviceName(event->TaskIndex).c_str())); + } + success = false; + } + return success; +} + +bool P039_data_struct::plugin_tasktimer_in(EventStruct *event) { + bool success = false; + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // Get the MAX Type (6675 / 31855 / 31856) + uint8_t MaxType = P039_MAX_TYPE; + + switch (MaxType) + { + case P039_MAX31865: + { + switch (event->Par1) + { + case MAX31865_BIAS_ON_STATE: + { + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("P039 : %s : current state: MAX31865_BIAS_ON_STATE; delta: %d ms"), + getTaskDeviceName(event->TaskIndex).c_str(), + timePassedSince(timer))); // calc delta since last call + } + # endif // ifndef BUILD_NO_DEBUG + + // save current timer for next calculation + timer = millis(); + + // activate one shot conversion + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_ONE_SHOT, + P039_SET); + + // set next state in sequence -> READ STATE + // start time to follow up on conversion and read the conversion result + convReady = false; + Scheduler.setPluginTaskTimer(MAX31865_CONVERSION_TIME, event->TaskIndex, MAX31865_RD_STATE); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("P039 : %s : Next State: %d"), + getTaskDeviceName(event->TaskIndex).c_str(), + event->Par1)); + } + # endif // ifndef BUILD_NO_DEBUG + + break; + } + case MAX31865_RD_STATE: + { + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("P039 : %s : current state: MAX31865_RD_STATE; delta: %d ms"), + getTaskDeviceName(event->TaskIndex).c_str(), + timePassedSince(timer))); // calc delta since last call + } + # endif // ifndef BUILD_NO_DEBUG + + // save current timer for next calculation + timer = millis(); + + // read conversion result + conversionResult = read16BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_RTD_MSB)); + + // deactivate BIAS short after read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_RESET); + + // read fault register to get a full picture + deviceFaults = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_FAULT)); + + // mark conversion as ready + convReady = true; + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, + strformat(F("P039 : %s : conversionResult: %s; deviceFaults: %s; Next State: %d"), + getTaskDeviceName(event->TaskIndex).c_str(), + formatToHex_decimal(conversionResult).c_str(), + formatToHex_decimal(deviceFaults).c_str(), + event->Par1)); + } + # endif // ifndef BUILD_NO_DEBUG + + + break; + } + case MAX31865_INIT_STATE: + default: + { + // clear all faults + MAX31865_clearFaults(CS_pin_no); + + // activate BIAS short before read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_SET); + + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : %s : current state: MAX31865_INIT_STATE, " + "default; next state: MAX31865_BIAS_ON_STATE"), + getTaskDeviceName(event->TaskIndex).c_str())); + + // FIXME: Shouldn't this be in active code? // save current timer for next calculation + timer = millis(); + } + # endif // ifndef BUILD_NO_DEBUG + + // start time to follow up on BIAS activation before starting the conversion + // and start conversion sequence via TIMER API + // set next state in sequence -> BIAS ON STATE + + Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); + + + break; + } + } + break; + } + default: + { + break; + } + } + return success; +} + +void P039_data_struct::AddMainsFrequencyFilterSelection(struct EventStruct *event) +{ + const __FlashStringHelper *FToptions[] = { F("60"), F("50") }; + const int FToptionValues[] = { 0, 1 }; + + const FormSelectorOptions selector(NR_ELEMENTS(FToptions), FToptions, FToptionValues); + + selector.addFormSelector(F("Supply Frequency Filter"), F("filttype"), P039_RTD_FILT_TYPE); + addUnit(F("Hz")); + # ifndef LIMIT_BUILD_SIZE + addFormNote(F("Filter power net frequency (50/60 Hz)")); + # else // ifndef LIMIT_BUILD_SIZE + addUnit(F("net frequency")); + # endif // ifndef LIMIT_BUILD_SIZE +} + +float P039_data_struct::readMax6675(struct EventStruct *event) +{ + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + uint8_t messageBuffer[2] = { 0 }; + uint16_t rawvalue = 0u; + + + // "transfer" 2 bytes to SPI to get 16 Bit return value + transfer_n_ByteSPI(CS_pin_no, 2, &messageBuffer[0]); + + // merge 16Bit return value from messageBuffer + rawvalue = ((messageBuffer[0] << 8) | messageBuffer[1]); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX6675 : RAW - BIN: %s HEX: %s DEC: %d MSB: %s LSB: %s"), + String(rawvalue, BIN).c_str(), + formatToHex(rawvalue).c_str(), + rawvalue, + formatToHex_decimal(messageBuffer[0]).c_str(), + formatToHex_decimal(messageBuffer[1]).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + // Open Thermocouple + // Bit D2 is normally low and goes high if the thermocouple input is open. In order to allow the operation of the + // open thermocouple detector, T- must be grounded. Make the ground connection as close to the GND pin + // as possible. + // 2021-05-11: FIXED: c.k.i.: OC Flag already checked; migrated to #define for improved maintenance + const bool Plugin_039_SensorAttached = !(rawvalue & MAX6675_TC_OC); + + if (Plugin_039_SensorAttached) + { + // shift RAW value 3 Bits to the right to get the data + rawvalue >>= 3; + + // calculate Celsius with device resolution 0.25 K/bit + return rawvalue * 0.25f; + } + else + { + return NAN; + } +} + +float P039_data_struct::readMax31855(struct EventStruct *event) +{ + uint8_t messageBuffer[4] = { 0 }; + + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // "transfer" 0x0 and read the 32 Bit conversion register from the Chip + transfer_n_ByteSPI(CS_pin_no, 4, &messageBuffer[0]); + + // merge rawvalue from 4 bytes of messageBuffer + uint32_t rawvalue = + ((static_cast(messageBuffer[0]) << + 24) | + (static_cast(messageBuffer[1]) << + 16) | (static_cast(messageBuffer[2]) << 8) | static_cast(messageBuffer[3])); + + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + String log; + + if (log.reserve(200u)) { // reserve value derived from example log file + log = strformat(F("P039 : MAX31855 : RAW - BIN: %s rawvalue,HEX: %s rawvalue,DEC: %d messageBuffer[],HEX:"), + String(rawvalue, BIN).c_str(), + formatToHex(rawvalue).c_str(), + rawvalue); + + for (size_t i = 0u; i < 4; ++i) + { + log += ' '; // 1 char + log += formatToHex_decimal(messageBuffer[i]); // 9 char + } + addLogMove(LOG_LEVEL_DEBUG, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + // check for fault flags in LSB of 32 Bit messageBuffer + if (sensorFault != ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0)) { + // Fault code changed, log them + sensorFault = ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + String log; + + if (log.reserve(120u)) { // reserve value derived from example log file + log = F("P039 : MAX31855 : "); + + if (sensorFault) { + log += F("Fault resolved"); + } else { + log += F("Fault code :"); + + if (rawvalue & MAX31855_TC_OC) { + log += F(" Open (no connection)"); + } + + if (rawvalue & MAX31855_TC_SC) { + log += F(" Short-circuit to GND"); + } + + if (rawvalue & MAX31855_TC_SCVCC) { + log += F(" Short-circuit to Vcc"); + } + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + # endif // ifndef BUILD_NO_DEBUG + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX31855 : rawvalue: %s sensorFault: "), + formatToHex_decimal(rawvalue).c_str(), + formatToHex_decimal(sensorFault).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + } + + // D16 - This bit reads at 1 when any of the SCV, SCG, or OC faults are active. Default value is 0. + // 2020-05-11: FIXED: c.k.i.: migrated plain flag mask to #defines to enhance maintainability; added all fault flags for safety reasons + const bool Plugin_039_SensorAttached = !(rawvalue & (MAX31855_TC_GENFLT | MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)); + + if (Plugin_039_SensorAttached) + { + // Data is D[31:18] + // Shift RAW value 18 Bits to the right to get the data + rawvalue >>= 18; + + // Check for negative Values + // +25.00 0000 0001 1001 00 + // 0.00 0000 0000 0000 00 + // -0.25 1111 1111 1111 11 + // -1.00 1111 1111 1111 00 + // -250.00 1111 0000 0110 00 + // We're left with (32 - 18 =) 14 bits + int temperature = convert_two_complement(rawvalue, 14); + + // Calculate Celsius + return temperature * 0.25f; + } + else + { + // Fault state, thus output no value. + return NAN; + } +} + +float P039_data_struct::readMax31856(struct EventStruct *event) +{ + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + + uint8_t registers[MAX31856_NO_REG] = { 0 }; + uint8_t messageBuffer[MAX31856_NO_REG + 1] = { 0 }; + + messageBuffer[0] = MAX31856_READ_ADDR_BASE; + + // "transfer" 0x0 starting at address 0x00 and read the all registers from the Chip + transfer_n_ByteSPI(CS_pin_no, (MAX31856_NO_REG + 1), &messageBuffer[0]); + + // transfer data from messageBuffer and get rid of initial address uint8_t + for (uint8_t i = 0u; i < MAX31856_NO_REG; ++i) { + registers[i] = messageBuffer[i + 1]; + } + + // configure device for next conversion + // activate frequency filter according to configuration + change8BitRegister(CS_pin_no, + (MAX31856_READ_ADDR_BASE + MAX31856_CR0), + (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), + MAX31856_SET_50HZ, + static_cast(P039_RTD_FILT_TYPE)); + + // set averaging and TC type + write8BitRegister(CS_pin_no, (MAX31856_WRITE_ADDR_BASE + MAX31856_CR1), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE)); + + + // start on shot conversion for next read cycle + change8BitRegister(CS_pin_no, + (MAX31856_READ_ADDR_BASE + MAX31856_CR0), + (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), + MAX31856_SET_ONE_SHOT, + P039_SET); + + + // now derive raw value from respective registers + uint32_t rawvalue = static_cast(registers[MAX31856_LTCBH]); + + rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBM]); + rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBL]); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + String log; + + if (log.reserve(210u)) { // reserve value derived from example log file + log = F("P039 : MAX31856 :"); + + for (uint8_t i = 0; i < MAX31856_NO_REG; ++i) { + log += ' '; + log += formatToHex_decimal(registers[i]); + } + log += F(" rawvalue: "); + log += formatToHex_decimal(rawvalue); + addLogMove(LOG_LEVEL_DEBUG, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + + // ignore TC Range Bit in case Voltage Modes are used + // datasheet: + // Thermocouple Out-of-Range fault. + // 0 = The Thermocouple Hot Junction temperature is within the normal operating range (see Table 1). + // 1 = The Thermocouple Hot Junction temperature is outside of the normal operating range. + // Note: The TC Range bit should be ignored in voltage mode. + uint8_t sr = registers[MAX31856_SR]; + + if ((8u == P039_TC_TYPE) || (12u == P039_TC_TYPE)) { + sr &= ~MAX31856_TC_TCRANGE; + } + + + // FIXED: c.k.i. : moved static fault flag to instance data structure + + sensorFault = (sr != 0); // Set new state + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + // FIXME TD-er: Part of expression is always false (sr == 0) + const bool faultResolved = sensorFault && (sr == 0); + + if (sensorFault || faultResolved) { + String log; + + if (log.reserve(140u)) { // reserve value derived from example log file + log = F("P039 : MAX31856 : "); + + if (!sensorFault) { + log += F("Fault resolved"); + } else { + log += F("Fault :"); + + if (sr & MAX31856_TC_OC) { + log += F(" Open (no connection)"); + } + + if (sr & MAX31856_TC_OVUV) { + log += F(" Over/Under Voltage"); + } + + if (sr & MAX31856_TC_TCLOW) { + log += F(" TC Low"); + } + + if (sr & MAX31856_TC_TCLHIGH) { + log += F(" TC High"); + } + + if (sr & MAX31856_TC_CJLOW) { + log += F(" CJ Low"); + } + + if (sr & MAX31856_TC_CJHIGH) { + log += F(" CJ High"); + } + + if (sr & MAX31856_TC_TCRANGE) { + log += F(" TC Range"); + } + + if (sr & MAX31856_TC_CJRANGE) { + log += F(" CJ Range"); + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + } + } + # endif // ifndef BUILD_NO_DEBUG + + + const bool Plugin_039_SensorAttached = (sr == 0); + + if (Plugin_039_SensorAttached) + { + rawvalue >>= 5; // bottom 5 bits are unused + // We're left with (24 - 5 =) 19 bits + + { + float temperature = 0; + + switch (P039_TC_TYPE) + { + case 8: + { + temperature = rawvalue / 1677721.6f; // datasheet: rawvalue = 8 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (8 x 1.6 x 2^17) + break; + } + case 12: + { + temperature = rawvalue / 6710886.4f; // datasheet: rawvalue = 32 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (32 x 1.6 x 2^17) + break; + } + default: + { + temperature = convert_two_complement(rawvalue, 19); + + // Calculate Celsius + temperature /= 128.0f; + break; + } + } + + return temperature; + } + } + else + { + // Fault state, thus output no value. + return NAN; + } +} + +float P039_data_struct::readMax31865(struct EventStruct *event) +{ + uint8_t registers[MAX31865_NO_REG] = { 0 }; + uint16_t rawValue = 0u; + + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + String log; + + if (log.reserve(80u)) { // reserve value derived from example log file + log = F("P039 : MAX31865 :"); + log += F(" P039_data->convReady: "); + log += boolToString(convReady); + + addLogMove(LOG_LEVEL_DEBUG, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + + // read conversion result and faults from plugin data structure + // if pointer exists and conversion has been finished + if (convReady) { + rawValue = conversionResult; + registers[MAX31865_FAULT] = deviceFaults; + } + + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + String log; + + if (log.reserve(160u)) { // reserve value derived from example log file + for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) + { + registers[i] = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + i)); + } + + log = F("P039 : MAX31865 :"); + + for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) + { + log += ' '; + log += formatToHex_decimal(registers[i]); + } + + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + // Prepare and start next conversion, before handling faults and rawValue + // clear all faults + MAX31865_clearFaults(CS_pin_no); + + // set frequency filter + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_50HZ, + static_cast(P039_RTD_FILT_TYPE)); + + + // configure read access with configuration from web interface + MAX31865_setConType(CS_pin_no, P039_CONFIG_4); + + // activate BIAS short before read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_SET); + + // start time to follow up on BIAS activation before starting the conversion + // and start conversion sequence via TIMER API + // save current timer for next calculation + timer = millis(); + + // set next state to MAX31865_BIAS_ON_STATE + + Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + if (registers[MAX31865_FAULT]) + { + String log; + + if (log.reserve(210u)) { // reserve value derived from example log file + log = F("P039 : MAX31865 : "); + + log += F("Fault : "); + log += formatToHex_decimal(registers[MAX31865_FAULT]); + log += F(" :"); + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_OVUV) + { + log += F(" Under/Over voltage"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_RTDINLOW) + { + log += F(" RTDIN- < 0.85 x Bias - FORCE- open"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINHIGH) + { + log += F(" REFIN- < 0.85 x Bias - FORCE- open"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINLOW) + { + log += F(" REFIN- > 0.85 x Bias"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_LOWTHRESH) + { + log += F(" RTD Low Threshold"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_HIGHTHRESH) + { + log += F(" RTD High Threshold"); + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + } + # endif // ifndef BUILD_NO_DEBUG + + + bool ValueValid = false; + + if (registers[MAX31865_FAULT] == 0x00u) { + ValueValid = true; + } + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : registers[MAX31865_FAULT]: %s ValueValid: %s"), + formatToHex_decimal(registers[MAX31865_FAULT]).c_str(), + FsP(boolToString(ValueValid)))); + } + + # endif // ifndef BUILD_NO_DEBUG + + if (ValueValid) + { + rawValue >>= 1; // bottom fault bits is unused + + float temperature = convert_to_temperature(rawValue, getNomResistor(P039_RTD_TYPE), P039_RTD_RES); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : rawValue: %s temperature: %.3f P039_RTD_TYPE: %d P039_RTD_RES: %d"), + formatToHex_decimal(rawValue).c_str(), + temperature, + P039_RTD_TYPE, + P039_RTD_RES)); + } + + # endif // ifndef BUILD_NO_DEBUG + + // add offset handling from configuration webpage + temperature += P039_RTD_OFFSET; + + // Calculate Celsius + return temperature; + } + else + { + // Fault state, thus output no value. + return NAN; + } +} + +void P039_data_struct::MAX31865_clearFaults(int8_t l_CS_pin_no) +{ + uint8_t l_reg = 0u; + + // read in config register + l_reg = read8BitRegister(l_CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG)); + + + // clear all faults ( write "0" to D2, D3, D5; write "1" to D2) + l_reg &= ~(MAX31865_SET_ONE_SHOT | MAX31865_FAULT_CTRL_MASK); + l_reg |= MAX31865_CLEAR_FAULTS; + + // write configuration + write8BitRegister(l_CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), l_reg); +} + +void P039_data_struct::MAX31865_setConType(int8_t l_CS_pin_no, uint8_t l_conType) +{ + bool l_set_reset = false; + + // configure if 3 WIRE bit will be set/reset + switch (l_conType) + { + case 0: + l_set_reset = P039_RESET; + break; + case 1: + l_set_reset = P039_SET; + break; + default: + l_set_reset = P039_RESET; + break; + } + + // change to configuration register + change8BitRegister(l_CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_3WIRE, + l_set_reset); +} + +/**************************************************************************/ + +/*! + @brief Read the temperature in C from the RTD through calculation of the + resistance. Uses + http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf + technique + @param RTDnominal The 'nominal' resistance of the RTD sensor, usually 100 + or 1000 + @param refResistor The value of the matching reference resistor, usually + 430 or 4300 + @returns Temperature in C */ -bool P039_data_struct::begin() + +/**************************************************************************/ +float P039_data_struct::convert_to_temperature(uint32_t l_rawvalue, float RTDnominal, float refResistor) +{ + # define RTD_A 3.9083e-3f + # define RTD_B -5.775e-7f + + float Z1, Z2, Z3, Z4, Rt, temp; + + Rt = l_rawvalue; + Rt /= 32768u; + Rt *= refResistor; + + Z1 = -RTD_A; + Z2 = RTD_A * RTD_A - (4 * RTD_B); + Z3 = (4 * RTD_B) / RTDnominal; + Z4 = 2 * RTD_B; + + temp = Z2 + (Z3 * Rt); + temp = (sqrtf(temp) + Z1) / Z4; + + if (temp >= 0) { + return temp; + } + + Rt /= RTDnominal; + Rt *= 100; // normalize to 100 ohm + + float rpoly = Rt; + + temp = -242.02f; + temp += 2.2228f * rpoly; + rpoly *= Rt; // square + temp += 2.5859e-3f * rpoly; + rpoly *= Rt; // ^3 + temp -= 4.8260e-6f * rpoly; + rpoly *= Rt; // ^4 + temp -= 2.8183e-8f * rpoly; + rpoly *= Rt; // ^5 + temp += 1.5243e-10f * rpoly; + + return temp; +} + +uint16_t P039_data_struct::getNomResistor(uint8_t l_RType) +{ + uint16_t l_returnValue = 100u; + + switch (l_RType) + { + case MAX31865_PT1000: + l_returnValue = 1000u; + break; + case MAX31865_PT100: // Fall through + default: + break; + } + return l_returnValue; +} + +int P039_data_struct::convert_two_complement(uint32_t value, int nr_bits) { + const bool negative = (value & (1 << (nr_bits - 1))) != 0; + int nativeInt; + + if (negative) { + // Add zeroes to the left to create the proper negative native-sized integer. + nativeInt = value | ~((1 << nr_bits) - 1); + } else { + nativeInt = value; + } + return nativeInt; +} + +float P039_data_struct::readLM7x(struct EventStruct *event) { - return false; + float temperature = 0.0f; + uint16_t device_id = 0u; + uint16_t rawValue = 0u; + + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // operate LM7x devices in polling mode, assuming conversion is ready with every call of this read function ( >=210ms call cycle) + // this allows usage of multiples generations of LM7x devices, that doe not provde conversion ready information in temperature register + + rawValue = readLM7xRegisters(CS_pin_no, P039_RTD_LM_TYPE, P039_RTD_LM_SHTDWN, &device_id); + + temperature = convertLM7xTemp(rawValue, P039_RTD_LM_TYPE); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : LM7x : readLM7x : rawValue: %s device_id: %s temperature: %.3f"), + formatToHex_decimal(rawValue).c_str(), + formatToHex(device_id).c_str(), + temperature)); + } + # endif // ifndef BUILD_NO_DEBUG + + return temperature; } -bool P039_data_struct::read() +float P039_data_struct::convertLM7xTemp(uint16_t l_rawValue, uint16_t l_LM7xsubtype) { - return false; + float l_returnValue = 0.0f; + float l_lsbvalue = 0.0f; + uint8_t l_noBits = 0u; + int l_intTemperature = 0; + + switch (l_LM7xsubtype) + { + case LM7x_SD70: + l_rawValue >>= 5; + l_lsbvalue = 0.25f; + l_noBits = 11u; + break; + case LM7x_SD71: + l_rawValue >>= 2; + l_lsbvalue = 0.03125f; + l_noBits = 14u; + break; + case LM7x_SD74: + l_rawValue >>= 3; + l_lsbvalue = 0.0625f; + l_noBits = 13u; + break; + case LM7x_SD121: + case LM7x_SD122: + case LM7x_SD123: + case LM7x_SD124: + l_rawValue >>= 4; + l_lsbvalue = 0.0625f; + l_noBits = 12u; + break; + case LM7x_SD125: + l_rawValue >>= 5; + l_lsbvalue = 0.25f; + l_noBits = 10u; + break; + default: // use lowest resolution as fallback if no device has been configured + l_rawValue >>= 5; + l_lsbvalue = 0.25f; + l_noBits = 11u; + break; + } + + l_intTemperature = convert_two_complement(l_rawValue, l_noBits); + + l_returnValue = l_intTemperature * l_lsbvalue; + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, + strformat(F("P039 : LM7x : convertLM7xTemp : l_returnValue: %s l_LM7xsubtype: %s l_rawValue: %s l_noBits: %d l_lsbvalue: %.3f"), + formatToHex_decimal(l_returnValue).c_str(), + formatToHex_decimal(l_LM7xsubtype).c_str(), + formatToHex_decimal(l_rawValue).c_str(), + l_noBits, + l_lsbvalue)); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_returnValue; } -bool P039_data_struct::write() +uint16_t P039_data_struct::readLM7xRegisters(int8_t l_CS_pin_no, uint8_t l_LM7xsubType, uint8_t l_runMode, uint16_t *l_device_id) { - return false; + uint16_t l_returnValue = 0u; + uint16_t l_mswaitTime = 0u; + + + switch (l_LM7xsubType) + { + case LM7x_SD70: + case LM7x_SD71: + case LM7x_SD74: + l_mswaitTime = 300; + break; + case LM7x_SD121: + case LM7x_SD122: + case LM7x_SD123: + case LM7x_SD124: + l_mswaitTime = 320; + break; + case LM7x_SD125: + l_mswaitTime = 100; + break; + default: + l_mswaitTime = 500; + break; + } + + // // activate communication -> CS low + // handle_SPI_CS_Pin(l_CS_pin_no, LOW); + + if (l_runMode) + { + // shutdown mode active -> conversion when called + uint8_t messageBuffer[12] = { 0xFF, 0xFF, 0xFF, 0X00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF }; + + // send inital 4 bytes to wake the device and start the conversion + transfer_n_ByteSPI(l_CS_pin_no, 4, &messageBuffer[0]); + + // wait specific ms for conversion to be ready (TI datasheet per devices) + delay(l_mswaitTime); + + // send remaining 8 bytes to read the device ID and shutdown the device + transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[4]); + + // read temperature value (16 Bit) + l_returnValue = ((messageBuffer[4] << 8) | messageBuffer[5]); + + // read Manufatures/Device ID (16 Bit) + *(l_device_id) = ((messageBuffer[8] << 8) | messageBuffer[9]); + } + else + { + // shutdown mode inactive -> normal background conversion during call cycle + uint8_t messageBuffer[8] = { 0x00, 0x00, 0xFF, 0XFF, 0x00, 0x00, 0x00, 0x00 }; + + transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[0]); + + // read temperature value (16 Bit) + l_returnValue = ((messageBuffer[0] << 8) | messageBuffer[1]); + + // read Manufatures/Device ID (16 Bit) + *(l_device_id) = ((messageBuffer[4] << 8) | messageBuffer[5]); + } + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : LM7x : readLM7xRegisters : l_returnValue: %s l_device_id: %s"), + formatToHex_decimal(l_returnValue).c_str(), + formatToHex(*(l_device_id)).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_returnValue; +} + +// POSSIBLE START OF GENERIC SPI HIGH LEVEL FUNCTIONS WITH POTENTIAL OF SYSTEM WIDE RE-USE + +/**************************************************************************/ + +/*! + @brief generic high level library to access SPI interface from plugins + with GPIO pin handled as CS - chri.kai.in 2021 + + Initial Revision - chri.kai.in 2021 + + TODO: c.k.i.: make it generic and carve out to generic _SPI_helper.c library + + + **************************************************************************/ + + +/**************************************************************************/ + +/*! + + @brief Identifying the CS pin from the event basic data structure + @param event pointer to the event structure; default GPIO is chosen as GPIO 15 + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +int P039_data_struct::get_SPI_CS_Pin(struct EventStruct *event) { // If no Pin is in Config we use 15 as default -> Hardware Chip Select on + // ESP8266 + if (CONFIG_PIN1 != -1) { + return CONFIG_PIN1; + } + return 15; // D8 +} + +/**************************************************************************/ + +/*! + @brief Initializing GPIO as OUTPUT for CS for SPI communication + @param l_CS_pin_no the GPIO pin number used as CS + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::init_SPI_CS_Pin(int8_t l_CS_pin_no) { + // set the slaveSelectPin as an output: + pinMode(l_CS_pin_no, OUTPUT); +} + +/**************************************************************************/ + +/*! + @brief Handling GPIO as CS for SPI communication + @param l_CS_pin_no the GPIO pin number used as CS + @param l_state the state of the CS pin: "HIGH/LOW" reflecting the physical level + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::handle_SPI_CS_Pin(int8_t l_CS_pin_no, bool l_state) { + P039_CS_Delay(); // tCWH (min) >= x00ns + digitalWrite(l_CS_pin_no, l_state); + P039_CS_Delay(); // tCC (min) >= x00ns +} + +/**************************************************************************/ + +/*! + @brief write 8 bits to adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + @param value the unsigned 8 Bit message to be transferred + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::write8BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint8_t value) +{ + uint8_t l_messageBuffer[2] = { l_address, value }; + + transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write8BitRegister : l_address: %s value: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(value).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG +} + +/**************************************************************************/ + +/*! + @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + @param value the unsigned 16 Bit message to be transferred + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::write16BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint16_t value) +{ + uint8_t l_messageBuffer[3] = { l_address, static_cast((value >> 8) & 0xFF), static_cast(value & 0xFF) }; + + transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write16BitRegister : l_address: %s value: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(value).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG +} + +/**************************************************************************/ + +/*! + @brief read 8 bits from adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + + @returns the unsigned 8 Bit message read from l_address + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +uint8_t P039_data_struct::read8BitRegister(int8_t l_CS_pin_no, uint8_t l_address) +{ + uint8_t l_messageBuffer[2] = { l_address, 0x00 }; + + transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read8BitRegister : l_address: %s returnvalue: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(l_messageBuffer[1]).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_messageBuffer[1]; +} + +/**************************************************************************/ + +/*! + @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + + @returns the unsigned 16 Bit message read from l_address + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +uint16_t P039_data_struct::read16BitRegister(int8_t l_CS_pin_no, uint8_t l_address) +{ + uint8_t l_messageBuffer[3] = { l_address, 0x00, 0x00 }; + uint16_t l_returnValue; + + transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); + l_returnValue = ((l_messageBuffer[1] << 8) | l_messageBuffer[2]); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read16BitRegister : l_address: %s l_returnValue: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(l_returnValue).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_returnValue; +} + +/**************************************************************************/ + +/*! + @brief read from/write to dedicated number of bytes from/to SPI, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_noBytesToSend number of bytes to read/write from/to SPI + @param l_inoutMessageBuffer pointer to the messsage buffer to provide bytes to send + and provide read bytes from the SPI bus after the call + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::transfer_n_ByteSPI(int8_t l_CS_pin_no, uint8_t l_noBytesToSend, uint8_t *l_inoutMessageBuffer) +{ + // activate communication -> CS low + handle_SPI_CS_Pin(l_CS_pin_no, LOW); + + for (size_t i = 0u; i < l_noBytesToSend; i++) + { + l_inoutMessageBuffer[i] = _spi.transfer(l_inoutMessageBuffer[i]); + } + + // stop communication -> CS high + handle_SPI_CS_Pin(l_CS_pin_no, HIGH); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + String log; + + if (log.reserve(120u)) { // reserve value derived from example log file + log = F("P039 : SPI : transfer_n_ByteSPI : "); // 34 char + + for (uint8_t i = 0; i < l_noBytesToSend; ++i) + { + log += ' '; // 1 char + log += formatToHex_decimal(l_inoutMessageBuffer[i]); // 9 char + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG +} + +/**************************************************************************/ + +/*! + @brief read a 16Bit register and change a flag, writing it back, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_readaddress SPI read address of the device register + @param l_writeaddress SPI write address of the device register + @param l_flagmask mask set to apply on the read register + @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) + + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::change16BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint16_t l_flagmask, + bool l_set_reset) +{ + uint16_t l_reg = 0u; + + // read in config register + l_reg = read16BitRegister(l_CS_pin_no, l_readaddress); + + if (l_set_reset) { + l_reg |= l_flagmask; + } + else + { + l_reg &= ~(l_flagmask); + } + + // write to configuration register + write16BitRegister(l_CS_pin_no, l_writeaddress, l_reg); +} + +/**************************************************************************/ + +/*! + @brief read a 8 Bit register and change a flag, writing it back, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_readaddress SPI read address of the device register + @param l_writeaddress SPI write address of the device register + @param l_flagmask mask set to apply on the read register + @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) + + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::change8BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint8_t l_flagmask, + bool l_set_reset) +{ + uint8_t l_reg = 0u; + + // read in config register + l_reg = read8BitRegister(l_CS_pin_no, l_readaddress); + + + // TODO: c.k.i.: analyze opportunity to use arduino bitSet/Clear macros instead + if (l_set_reset) { + l_reg |= l_flagmask; + } + else + { + l_reg &= ~(l_flagmask); + } + + // write to configuration register + write8BitRegister(l_CS_pin_no, l_writeaddress, l_reg); } #endif // ifdef USES_P039 diff --git a/src/src/PluginStructs/P039_data_struct.h b/src/src/PluginStructs/P039_data_struct.h index 92305b0680..836cb7ba6d 100644 --- a/src/src/PluginStructs/P039_data_struct.h +++ b/src/src/PluginStructs/P039_data_struct.h @@ -5,36 +5,251 @@ #ifdef USES_P039 +# include + +# include "../Globals/SPIe.h" + +# define MAX31865_RD_ADDRESS(n) (MAX31865_READ_ADDR_BASE + (n)) +# define MAX31865_WR_ADDRESS(n) (MAX31865_WRITE_ADDR_BASE + (n)) + +# define P039_SET true +# define P039_RESET false + +// typically 500ns of wating on positive/negative edge of CS should be enough ( -> datasheet); to make sure we cover a lot of devices we +// spend 1ms +// FIX 2021-05-05: review of all covered device datasheets showed 2µs is more than enough; review with every newly added device +# define P039_CS_Delay() delayMicroseconds(2u) + +# define P039_MAX_TYPE PCONFIG(0) +# define P039_TC_TYPE PCONFIG(1) +# define P039_FAM_TYPE PCONFIG(2) +# define P039_RTD_TYPE PCONFIG(3) +# define P039_CONFIG_4 PCONFIG(4) +# define P039_RTD_FILT_TYPE PCONFIG(5) +# define P039_RTD_LM_TYPE PCONFIG(6) +# define P039_RTD_LM_SHTDWN PCONFIG(7) +# define P039_RTD_RES PCONFIG_LONG(0) +# define P039_FLAGS PCONFIG_ULONG(3) +# define P039_TEMP_THRESHOLD_FLAG 0 +# define P039_RTD_OFFSET PCONFIG_FLOAT(0) +# define P039_TEMP_THRESHOLD PCONFIG_FLOAT(1) + +# define P039_TEMP_THRESHOLD_DEFAULT (-273.15f) // Default and minimum value +# define P039_TEMP_THRESHOLD_MIN P039_TEMP_THRESHOLD_DEFAULT +# define P039_TEMP_THRESHOLD_MAX (1000.0f) // Max value +# define P039_TC 0u +# define P039_RTD 1u + +# define P039_MAX6675 1 +# define P039_MAX31855 2 +# define P039_MAX31856 3 +# define P039_MAX31865 4 +# define P039_LM7x 5 + +// MAX 6675 related defines + +// bit masks to identify failures for MAX 6675 +# define MAX6675_TC_DEVID 0x0002u +# define MAX6675_TC_OC 0x0004u + +// MAX 31855 related defines + +// bit masks to identify failures for MAX 31855 +# define MAX31855_TC_OC 0x00000001u +# define MAX31855_TC_SC 0x00000002u +# define MAX31855_TC_SCVCC 0x00000004u +# define MAX31855_TC_GENFLT 0x00010000u + + +// MAX 31856 related defines + +// base address for read/write acces to MAX 31856 +# define MAX31856_READ_ADDR_BASE 0x00u +# define MAX31856_WRITE_ADDR_BASE 0x80u + +// register offset values for MAX 31856 +# define MAX31856_CR0 0u +# define MAX31856_CR1 1u +# define MAX31856_MASK 2u +# define MAX31856_CJHF 3u +# define MAX31856_CJLF 4u +# define MAX31856_LTHFTH 5u +# define MAX31856_LTHFTL 6u +# define MAX31856_LTLFTH 7u +# define MAX31856_LTLFTL 8u +# define MAX31856_CJTO 9u +# define MAX31856_CJTH 10u +# define MAX31856_CJTL 11u +# define MAX31856_LTCBH 12u +# define MAX31856_LTCBM 13u +# define MAX31856_LTCBL 14u +# define MAX31856_SR 15u + +# define MAX31856_NO_REG 16u + +// bit masks to identify failures for MAX 31856 +# define MAX31856_TC_OC 0x01u +# define MAX31856_TC_OVUV 0x02u +# define MAX31856_TC_TCLOW 0x04u +# define MAX31856_TC_TCLHIGH 0x08u +# define MAX31856_TC_CJLOW 0x10u +# define MAX31856_TC_CJHIGH 0x20u +# define MAX31856_TC_TCRANGE 0x40u +# define MAX31856_TC_CJRANGE 0x80u + +// bit masks for access of configuration bits +# define MAX31856_SET_50HZ 0x01u +# define MAX31856_CLEAR_FAULTS 0x02u +# define MAX31856_FLT_ISR_MODE 0x04u +# define MAX31856_CJ_SENS_DISABLE 0x08u +# define MAX31856_FAULT_CTRL_MASK 0x30u +# define MAX31856_SET_ONE_SHOT 0x40u +# define MAX31856_SET_CONV_AUTO 0x80u + + +// RTD related defines + +// MAX 31865 related defines + +// waiting time until "in sequence" conversion is ready (-> used in case device is set to shutdown in between call cycles) +// typically 70ms should be fine, according to datasheet maximum -> 66ms - give a little adder to "be sure" conversion is done +// alternatively ONE SHOT bit could be polled (system/SPI bus load !) +# define MAX31865_CONVERSION_TIME 70ul +# define MAX31865_BIAS_WAIT_TIME 10ul + +// MAX 31865 Main States +# define MAX31865_INIT_STATE 0u +# define MAX31865_BIAS_ON_STATE 1u +# define MAX31865_RD_STATE 2u +# define MAX31865_RDY_STATE 3u + +// sensor type +# define MAX31865_PT100 0u +# define MAX31865_PT1000 1u + +// base address for read/write acces to MAX 31865 +# define MAX31865_READ_ADDR_BASE 0x00u +# define MAX31865_WRITE_ADDR_BASE 0x80u + +// register offset values for MAX 31865 +# define MAX31865_CONFIG 0u +# define MAX31865_RTD_MSB 1u +# define MAX31865_RTD_LSB 2u +# define MAX31865_HFT_MSB 3u +# define MAX31865_HFT_LSB 4u +# define MAX31865_LFT_MSB 5u +# define MAX31865_LFT_LSB 6u +# define MAX31865_FAULT 7u + +// total number of registers in MAX 31865 +# define MAX31865_NO_REG 8u + +// bit masks to identify failures for MAX 31865 +# define MAX31865_FAULT_HIGHTHRESH 0x80u +# define MAX31865_FAULT_LOWTHRESH 0x40u +# define MAX31865_FAULT_REFINLOW 0x20u +# define MAX31865_FAULT_REFINHIGH 0x10u +# define MAX31865_FAULT_RTDINLOW 0x08u +# define MAX31865_FAULT_OVUV 0x04u + +// bit masks for access of configuration bits +# define MAX31865_SET_50HZ 0x01u +# define MAX31865_CLEAR_FAULTS 0x02u +# define MAX31865_FAULT_CTRL_MASK 0x0Cu +# define MAX31865_SET_3WIRE 0x10u +# define MAX31865_SET_ONE_SHOT 0x20u +# define MAX31865_SET_CONV_AUTO 0x40u +# define MAX31865_SET_VBIAS_ON 0x80u + +// LM7x related defines + +// LM7x subtype defines +# define LM7x_SD70 0x00u +# define LM7x_SD71 0x01u +# define LM7x_SD74 0x04u +# define LM7x_SD121 0x05u +# define LM7x_SD122 0x06u +# define LM7x_SD123 0x07u +# define LM7x_SD124 0x08u +# define LM7x_SD125 0x09u + +// bit masks for access of configuration bits +# define LM7x_CONV_RDY 0x02u + struct P039_data_struct : public PluginTaskData_base { public: - /* - P039_data_struct(uint16_t conversionResult, - uint8_t deviceFaults, - unsigned long timer, - bool sensorFault, - bool convReady); - */ - - P039_data_struct() = default; + P039_data_struct() = delete; + P039_data_struct(struct EventStruct *event); virtual ~P039_data_struct() = default; - bool begin(); + bool begin(struct EventStruct*event); // Perform read and return true when an alert has been high - bool read(); + bool read(struct EventStruct *event); - // Perform write and return true when an alert has been high - bool write(); + bool plugin_tasktimer_in(struct EventStruct*event); + + static void AddMainsFrequencyFilterSelection(struct EventStruct *event); + +private: + + float readMax6675(struct EventStruct *event); + float readMax31855(struct EventStruct *event); + float readMax31856(struct EventStruct *event); + float readMax31865(struct EventStruct *event); + void MAX31865_clearFaults(int8_t l_CS_pin_no); + void MAX31865_setConType(int8_t l_CS_pin_no, + uint8_t l_conType); + float convert_to_temperature(uint32_t l_rawvalue, + float RTDnominal, + float refResistor); + uint16_t getNomResistor(uint8_t l_RType); + int convert_two_complement(uint32_t value, + int nr_bits); + float readLM7x(struct EventStruct *event); + float convertLM7xTemp(uint16_t l_rawValue, + uint16_t l_LM7xsubtype); + uint16_t readLM7xRegisters(int8_t l_CS_pin_no, + uint8_t l_LM7xsubType, + uint8_t l_runMode, + uint16_t *l_device_id); + int get_SPI_CS_Pin(struct EventStruct *event); + void init_SPI_CS_Pin(int8_t l_CS_pin_no); + void handle_SPI_CS_Pin(int8_t l_CS_pin_no, + bool l_state); + void write8BitRegister(int8_t l_CS_pin_no, + uint8_t l_address, + uint8_t value); + void write16BitRegister(int8_t l_CS_pin_no, + uint8_t l_address, + uint16_t value); + uint8_t read8BitRegister(int8_t l_CS_pin_no, + uint8_t l_address); + uint16_t read16BitRegister(int8_t l_CS_pin_no, + uint8_t l_address); + void transfer_n_ByteSPI(int8_t l_CS_pin_no, + uint8_t l_noBytesToSend, + uint8_t *l_inoutMessageBuffer); + void change16BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint16_t l_flagmask, + bool l_set_reset); + void change8BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint8_t l_flagmask, + bool l_set_reset); - // uint8_t mainState = 0x00u;; - // uint8_t command = 0x00u; uint16_t conversionResult = 0x0000u; uint8_t deviceFaults = 0x00u; unsigned long timer = 0; bool sensorFault = false; bool convReady = false; + + SPIClass& _spi = SPI; }; diff --git a/src/src/PluginStructs/P095_data_struct.cpp b/src/src/PluginStructs/P095_data_struct.cpp index 927b4e36cb..5e65da9bb9 100644 --- a/src/src/PluginStructs/P095_data_struct.cpp +++ b/src/src/PluginStructs/P095_data_struct.cpp @@ -99,7 +99,8 @@ P095_data_struct::P095_data_struct(ILI9xxx_type_e displayType, String commandTrigger, uint16_t fgcolor, uint16_t bgcolor, - bool textBackFill + bool textBackFill, + uint8_t spi_bus # if ADAGFX_FONTS_INCLUDED , const uint8_t defaultFontId @@ -108,7 +109,7 @@ P095_data_struct::P095_data_struct(ILI9xxx_type_e displayType, : _displayType(displayType), _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _backlightPin(backlightPin), _backlightPercentage(backlightPercentage), _displayTimer(displayTimer), _displayTimeout(displayTimer), _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), - _textBackFill(textBackFill) + _textBackFill(textBackFill), _spi_bus(spi_bus) # if ADAGFX_FONTS_INCLUDED , _defaultFontId(defaultFontId) # endif // if ADAGFX_FONTS_INCLUDED @@ -165,7 +166,18 @@ bool P095_data_struct::plugin_init(struct EventStruct *event) { } else # endif // if P095_ENABLE_ILI948X { + # ifdef ESP32 // PIN(0) and PIN(1) swapped! + tft = new (std::nothrow) Adafruit_ILI9341(0 == _spi_bus ? &SPI : &SPIe, + PIN(1), + PIN(0), + PIN(2), + static_cast(_displayType), + _xpix, + _ypix); + # endif // ifdef ESP32 + # ifdef ESP8266 tft = new (std::nothrow) Adafruit_ILI9341(PIN(0), PIN(1), PIN(2), static_cast(_displayType), _xpix, _ypix); + # endif // ifdef ESP8266 if (nullptr != tft) { tft->begin(); diff --git a/src/src/PluginStructs/P095_data_struct.h b/src/src/PluginStructs/P095_data_struct.h index dec258d0e4..23b575a61b 100644 --- a/src/src/PluginStructs/P095_data_struct.h +++ b/src/src/PluginStructs/P095_data_struct.h @@ -4,8 +4,10 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P095 -# include // include Adafruit graphics library -# include // include Adafruit ILI9341 TFT library +# include // include Adafruit graphics library +# include // include Adafruit ILI9341 TFT library + +# include "../Globals/SPIe.h" # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object # include "../CustomBuild/StorageLayout.h" @@ -137,10 +139,11 @@ struct P095_data_struct : public PluginTaskData_base { String commandTrigger, uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, - bool textBackFill = true + bool textBackFill = true, + uint8_t spi_bus = 0 # if ADAGFX_FONTS_INCLUDED , - const uint8_t defaultFontId = 0 + const uint8_t defaultFontId = 0 # endif // if ADAGFX_FONTS_INCLUDED ); P095_data_struct() = delete; @@ -208,6 +211,7 @@ struct P095_data_struct : public PluginTaskData_base { uint16_t _fgcolor = ADAGFX_WHITE; uint16_t _bgcolor = ADAGFX_BLACK; bool _textBackFill = false; + uint8_t _spi_bus; # if ADAGFX_FONTS_INCLUDED uint8_t _defaultFontId; # endif // if ADAGFX_FONTS_INCLUDED diff --git a/src/src/PluginStructs/P096_data_struct.cpp b/src/src/PluginStructs/P096_data_struct.cpp index 8d4b348cf3..1be9706ad9 100644 --- a/src/src/PluginStructs/P096_data_struct.cpp +++ b/src/src/PluginStructs/P096_data_struct.cpp @@ -75,6 +75,7 @@ P096_data_struct::P096_data_struct(EPD_type_e display, uint8_t fontscaling, AdaGFXTextPrintMode textmode, String commandTrigger, + uint8_t spi_bus, uint16_t fgcolor, uint16_t bgcolor, AdaGFXColorDepth colorDepth, @@ -84,7 +85,7 @@ P096_data_struct::P096_data_struct(EPD_type_e display, _xpix(width), _ypix(height), # endif // if !P096_USE_EXTENDED_SETTINGS _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _commandTrigger(commandTrigger), - _fgcolor(fgcolor), _bgcolor(bgcolor), _colorDepth(colorDepth), _textBackFill(textBackFill) + _spi_bus(spi_bus), _fgcolor(fgcolor), _bgcolor(bgcolor), _colorDepth(colorDepth), _textBackFill(textBackFill) { _commandTrigger.toLowerCase(); _commandTriggerCmd = _commandTrigger; @@ -124,17 +125,33 @@ bool P096_data_struct::plugin_init(struct EventStruct *event) { switch (_display) { case EPD_type_e::EPD_IL3897: - eInkScreen = new (std::nothrow) LOLIN_IL3897(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) LOLIN_IL3897(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); // HSPI break; case EPD_type_e::EPD_UC8151D: - eInkScreen = new (std::nothrow) LOLIN_UC8151D(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) LOLIN_UC8151D(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); // HSPI break; case EPD_type_e::EPD_SSD1680: - eInkScreen = new (std::nothrow) LOLIN_SSD1680(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) LOLIN_SSD1680(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); // HSPI break; # if P096_USE_WAVESHARE_2IN7 case EPD_type_e::EPD_WS2IN7: - eInkScreen = new (std::nothrow) Waveshare_2in7(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) Waveshare_2in7(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); // HSPI break; # endif // if P096_USE_WAVESHARE_2IN7 case EPD_type_e::EPD_MAX: diff --git a/src/src/PluginStructs/P096_data_struct.h b/src/src/PluginStructs/P096_data_struct.h index 9f6a796287..3150a3ae42 100644 --- a/src/src/PluginStructs/P096_data_struct.h +++ b/src/src/PluginStructs/P096_data_struct.h @@ -4,6 +4,8 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P096 +# include "../Globals/SPIe.h" + # include // include Adafruit graphics library # include // include Adafruit Lolin eInk/ePaper library @@ -110,11 +112,12 @@ struct P096_data_struct : public PluginTaskData_base { uint8_t fontscaling, AdaGFXTextPrintMode textmode, String commandTrigger, + uint8_t spi_bus, uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, AdaGFXColorDepth colorDepth = AdaGFXColorDepth::Monochrome, bool textBackFill = true); - P096_data_struct() = delete; + P096_data_struct() = delete; virtual ~P096_data_struct(); bool plugin_init(struct EventStruct *event); @@ -146,6 +149,7 @@ struct P096_data_struct : public PluginTaskData_base { uint8_t _fontscaling; AdaGFXTextPrintMode _textmode; String _commandTrigger; + uint8_t _spi_bus; uint16_t _fgcolor; uint16_t _bgcolor; AdaGFXColorDepth _colorDepth; diff --git a/src/src/PluginStructs/P099_data_struct.cpp b/src/src/PluginStructs/P099_data_struct.cpp index 03fbbaa5a5..7c1746426a 100644 --- a/src/src/PluginStructs/P099_data_struct.cpp +++ b/src/src/PluginStructs/P099_data_struct.cpp @@ -42,7 +42,8 @@ bool P099_data_struct::init(taskIndex_t taskIndex, bool send_z, bool useCalibration, uint16_t ts_x_res, - uint16_t ts_y_res) { + uint16_t ts_y_res, + uint8_t spi_bus) { reset(); _address_ts_cs = cs; @@ -54,8 +55,13 @@ bool P099_data_struct::init(taskIndex_t taskIndex, _useCalibration = useCalibration; _ts_x_res = ts_x_res; _ts_y_res = ts_y_res; + _spi_bus = spi_bus; - touchscreen = new (std::nothrow) XPT2046_Touchscreen(_address_ts_cs); + touchscreen = new (std::nothrow) XPT2046_Touchscreen(_address_ts_cs + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); if (touchscreen != nullptr) { touchscreen->setRotation(_rotation); @@ -328,7 +334,7 @@ bool P099_data_struct::plugin_write(struct EventStruct *event, const String& str subcommand = parseString(string, 2); if (equals(command, F("touch"))) { - int command_i = GetCommandCode(subcommand.c_str(), p099_subcommands); + int command_i = GetCommandCode(subcommand.c_str(), p099_subcommands); if (command_i == -1) { // No matching subcommand found diff --git a/src/src/PluginStructs/P099_data_struct.h b/src/src/PluginStructs/P099_data_struct.h index 89814a6c15..abb73cb8c5 100644 --- a/src/src/PluginStructs/P099_data_struct.h +++ b/src/src/PluginStructs/P099_data_struct.h @@ -8,64 +8,66 @@ # include +# include "../Globals/SPIe.h" + // #define PLUGIN_099_DEBUG // Additional debugging information // Define default values for both ESP32/lolin32 and D1 Mini # ifdef ESP32 # define P099_TS_CS 12 # else // ESP8266/ESP8285 - # define P099_TS_CS 0 // D3 + # define P099_TS_CS 0 // D3 # endif // ESP32 -# define P099_MaxObjectNameLength 15 // 14 character objectnames + terminating 0 -# define P099_MaxObjectCount 40 // This count of touchobjects should be enough, because of limited settings storage, 960 bytes + 8 - // bytes calibration coordinates - -# define P099_FLAGS_ON_OFF_BUTTON 0 // TouchObjects.flags On/Off Button function -# define P099_FLAGS_INVERT_BUTTON 1 // TouchObjects.flags Inverted On/Off Button function - -#define P099_FLAGS_SEND_XY 0 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_SEND_Z 1 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_SEND_OBJECTNAME 2 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_USE_CALIBRATION 3 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_LOG_CALIBRATION 4 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_ROTATION_FLIPPED 5 // Set in P099_CONFIG_FLAGS - -#define P099_CONFIG_STATE PCONFIG(0) -#define P099_CONFIG_CS_PIN PIN(0) -#define P099_CONFIG_TRESHOLD PCONFIG(1) -#define P099_CONFIG_ROTATION PCONFIG(2) -#define P099_CONFIG_X_RES PCONFIG(3) -#define P099_CONFIG_Y_RES PCONFIG(4) -#define P099_CONFIG_OBJECTCOUNT PCONFIG(5) -#define P099_CONFIG_DEBOUNCE_MS PCONFIG(6) -#define P099_CONFIG_FLAGS PCONFIG_LONG(0) // 0-31 flags - -#define P099_VALUE_X UserVar[event->BaseVarIndex + 0] -#define P099_VALUE_Y UserVar[event->BaseVarIndex + 1] -#define P099_VALUE_Z UserVar[event->BaseVarIndex + 2] - -#define P099_SET_VALUE_X(v) UserVar.setFloat(event->TaskIndex, 0, v) -#define P099_SET_VALUE_Y(v) UserVar.setFloat(event->TaskIndex, 1, v) -#define P099_SET_VALUE_Z(v) UserVar.setFloat(event->TaskIndex, 2, v) - -#define P099_TS_TRESHOLD 15 // Treshold before the value is registered as a proper touch -#define P099_TS_ROTATION 2 // Rotation 0-3 = 0/90/180/270 degrees, compatible with TFT ILI9341 -#define P099_TS_SEND_XY true // Enable X/Y events -#define P099_TS_SEND_Z false // Disable Z events -#define P099_TS_SEND_OBJECTNAME true // Enable objectname events -#define P099_TS_USE_CALIBRATION false // Disable calibration -#define P099_TS_LOG_CALIBRATION true // Enable calibration logging -#define P099_TS_ROTATION_FLIPPED false // Enable rotation flipped 180 deg. -#define P099_TS_X_RES 240 // Pixels, should match with the screen it is mounted on -#define P099_TS_Y_RES 320 -#define P099_INIT_OBJECTCOUNT 8 // Initial setting -#define P099_DEBOUNCE_MILLIS 150 // Debounce delay for On/Off button function - -#define P099_TOUCH_X_INVALID 4095 // When picking up spurious noise (or an open/not connected TS-CS pin), these are the values that - // turn up -#define P099_TOUCH_Y_INVALID 4095 -#define P099_TOUCH_Z_INVALID 255 +# define P099_MaxObjectNameLength 15 // 14 character objectnames + terminating 0 +# define P099_MaxObjectCount 40 // This count of touchobjects should be enough, because of limited settings storage, 960 bytes + 8 + // bytes calibration coordinates + +# define P099_FLAGS_ON_OFF_BUTTON 0 // TouchObjects.flags On/Off Button function +# define P099_FLAGS_INVERT_BUTTON 1 // TouchObjects.flags Inverted On/Off Button function + +# define P099_FLAGS_SEND_XY 0 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_SEND_Z 1 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_SEND_OBJECTNAME 2 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_USE_CALIBRATION 3 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_LOG_CALIBRATION 4 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_ROTATION_FLIPPED 5 // Set in P099_CONFIG_FLAGS + +# define P099_CONFIG_STATE PCONFIG(0) +# define P099_CONFIG_CS_PIN PIN(0) +# define P099_CONFIG_TRESHOLD PCONFIG(1) +# define P099_CONFIG_ROTATION PCONFIG(2) +# define P099_CONFIG_X_RES PCONFIG(3) +# define P099_CONFIG_Y_RES PCONFIG(4) +# define P099_CONFIG_OBJECTCOUNT PCONFIG(5) +# define P099_CONFIG_DEBOUNCE_MS PCONFIG(6) +# define P099_CONFIG_FLAGS PCONFIG_LONG(0) // 0-31 flags + +# define P099_VALUE_X UserVar[event->BaseVarIndex + 0] +# define P099_VALUE_Y UserVar[event->BaseVarIndex + 1] +# define P099_VALUE_Z UserVar[event->BaseVarIndex + 2] + +# define P099_SET_VALUE_X(v) UserVar.setFloat(event->TaskIndex, 0, v) +# define P099_SET_VALUE_Y(v) UserVar.setFloat(event->TaskIndex, 1, v) +# define P099_SET_VALUE_Z(v) UserVar.setFloat(event->TaskIndex, 2, v) + +# define P099_TS_TRESHOLD 15 // Treshold before the value is registered as a proper touch +# define P099_TS_ROTATION 2 // Rotation 0-3 = 0/90/180/270 degrees, compatible with TFT ILI9341 +# define P099_TS_SEND_XY true // Enable X/Y events +# define P099_TS_SEND_Z false // Disable Z events +# define P099_TS_SEND_OBJECTNAME true // Enable objectname events +# define P099_TS_USE_CALIBRATION false // Disable calibration +# define P099_TS_LOG_CALIBRATION true // Enable calibration logging +# define P099_TS_ROTATION_FLIPPED false // Enable rotation flipped 180 deg. +# define P099_TS_X_RES 240 // Pixels, should match with the screen it is mounted on +# define P099_TS_Y_RES 320 +# define P099_INIT_OBJECTCOUNT 8 // Initial setting +# define P099_DEBOUNCE_MILLIS 150 // Debounce delay for On/Off button function + +# define P099_TOUCH_X_INVALID 4095 // When picking up spurious noise (or an open/not connected TS-CS pin), these are the values that + // turn up +# define P099_TOUCH_Y_INVALID 4095 +# define P099_TOUCH_Z_INVALID 255 // Data structure @@ -84,7 +86,8 @@ struct P099_data_struct : public PluginTaskData_base bool send_z, bool useCalibration, uint16_t ts_x_res, - uint16_t ts_y_res); + uint16_t ts_y_res, + uint8_t spi_bus); bool isInitialized() const; void loadTouchObjects(taskIndex_t taskIndex); bool touched(); @@ -105,7 +108,8 @@ struct P099_data_struct : public PluginTaskData_base void scaleRawToCalibrated(uint16_t& x, uint16_t& y); - bool plugin_write(struct EventStruct *event,const String& string); + bool plugin_write(struct EventStruct *event, + const String & string); // This is initialized by calling init() XPT2046_Touchscreen *touchscreen = nullptr; @@ -118,6 +122,7 @@ struct P099_data_struct : public PluginTaskData_base bool _useCalibration = 0; uint16_t _ts_x_res = 0; uint16_t _ts_y_res = 0; + uint8_t _spi_bus = 0; // This is filled during checking of a touchobject uint32_t SurfaceAreas[P099_MaxObjectCount] = { 0 }; diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 3255bbdfde..c7ee59160a 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -33,8 +33,14 @@ P104_data_struct::P104_data_struct(MD_MAX72XX::moduleType_t _mod, uint8_t _modules, uint8_t _zonesCount) : mod(_mod), taskIndex(_taskIndex), cs_pin(_cs_pin), modules(_modules), expectedZones(_zonesCount) { - if (Settings.isSPI_valid()) { - P = new (std::nothrow) MD_Parola(mod, cs_pin, modules); + const uint8_t spi_bus = Settings.getSPIBusForTask(taskIndex); + + if (Settings.isSPI_valid(spi_bus)) { + P = new (std::nothrow) MD_Parola(mod, + # ifdef ESP32 + 0 == spi_bus ? SPI : SPIe, + # endif // ifdef ESP32 + cs_pin, modules); } else { addLog(LOG_LEVEL_ERROR, F("DOTMATRIX: Required SPI not enabled. Initialization aborted!")); } diff --git a/src/src/PluginStructs/P104_data_struct.h b/src/src/PluginStructs/P104_data_struct.h index f50975328f..c43b36e41a 100644 --- a/src/src/PluginStructs/P104_data_struct.h +++ b/src/src/PluginStructs/P104_data_struct.h @@ -16,6 +16,8 @@ # include "../Helpers/Misc.h" # include "../Helpers/StringParser.h" +#include "../Globals/SPIe.h" + # include // # if defined(PLUGIN_SET_MAX) || defined(PLUGIN_BUILD_CUSTOM) || ((defined(PLUGIN_DISPLAY_A_COLLECTION) || diff --git a/src/src/PluginStructs/P111_data_struct.cpp b/src/src/PluginStructs/P111_data_struct.cpp index 821b38605a..2db1671a4b 100644 --- a/src/src/PluginStructs/P111_data_struct.cpp +++ b/src/src/PluginStructs/P111_data_struct.cpp @@ -11,10 +11,11 @@ # include -P111_data_struct::P111_data_struct(int8_t csPin, - int8_t rstPin, - int8_t irqPin) - : mfrc522(nullptr), _csPin(csPin), _rstPin(rstPin), _irqPin(irqPin) +P111_data_struct::P111_data_struct(int8_t csPin, + int8_t rstPin, + int8_t irqPin, + uint8_t spi_bus) + : mfrc522(nullptr), _csPin(csPin), _rstPin(rstPin), _irqPin(irqPin), _spi_bus(spi_bus) {} P111_data_struct::~P111_data_struct() { @@ -29,7 +30,12 @@ P111_data_struct::~P111_data_struct() { void P111_data_struct::init() { delete mfrc522; - mfrc522 = new (std::nothrow) MFRC522(_csPin, _rstPin); // Instantiate a MFRC522 + // Instantiate a MFRC522 + mfrc522 = new (std::nothrow) MFRC522(_csPin, _rstPin + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); if (mfrc522 != nullptr) { mfrc522->PCD_Init(); // Initialize MFRC522 reader @@ -251,7 +257,10 @@ uint8_t P111_data_struct::readPassiveTargetID(uint8_t *uid, return P111_NO_ERROR; } -void P111_data_struct::mfrc522_interrupt(P111_data_struct *self) { self->_irq_pin_time_micros = getMicros64(); } +void P111_data_struct::mfrc522_interrupt(P111_data_struct *self) +{ + self->_irq_pin_time_micros = getMicros64(); +} /********************************************************************************************* * Handle regular read and reset processing diff --git a/src/src/PluginStructs/P111_data_struct.h b/src/src/PluginStructs/P111_data_struct.h index 72da647428..7180fc878d 100644 --- a/src/src/PluginStructs/P111_data_struct.h +++ b/src/src/PluginStructs/P111_data_struct.h @@ -6,6 +6,8 @@ # include +# include "../Globals/SPIe.h" + # define P111_CS_PIN PIN(0) # define P111_RST_PIN PIN(1) # define P111_IRQ_PIN PIN(2) @@ -28,18 +30,20 @@ enum class P111_initPhases : uint8_t { ResetDelay1 = 0x01, ResetDelay2 = 0x02, Undefined = 0xFF + }; struct P111_data_struct : public PluginTaskData_base { - P111_data_struct(int8_t csPin, - int8_t rstPin, - int8_t irqPin); + P111_data_struct(int8_t csPin, + int8_t rstPin, + int8_t irqPin, + uint8_t spi_bus); P111_data_struct() = delete; virtual ~P111_data_struct(); - void init(); - bool plugin_ten_per_second(struct EventStruct *event); - bool plugin_fifty_per_second(struct EventStruct *event); + void init(); + bool plugin_ten_per_second(struct EventStruct *event); + bool plugin_fifty_per_second(struct EventStruct *event); String PCD_getVersion(uint8_t& v); @@ -51,21 +55,22 @@ struct P111_data_struct : public PluginTaskData_base { uint8_t counter = 0; - String getCardName(); - uint8_t readCardStatus(uint64_t *key, - bool *removedTag); - bool reset(int8_t csPin, - int8_t resetPin); - uint8_t readPassiveTargetID(uint8_t *uid, - uint8_t *uidLength); + String getCardName(); + uint8_t readCardStatus(uint64_t *key, + bool *removedTag); + bool reset(int8_t csPin, + int8_t resetPin); + uint8_t readPassiveTargetID(uint8_t *uid, + uint8_t *uidLength); - static void mfrc522_interrupt(P111_data_struct * self); + static void mfrc522_interrupt(P111_data_struct *self); int32_t timeToWait = 0; - int8_t _csPin; - int8_t _rstPin; - int8_t _irqPin; + int8_t _csPin; + int8_t _rstPin; + int8_t _irqPin; + uint8_t _spi_bus; uint8_t errorCount = 0; bool removedState = true; // On startup, there will usually not be a tag nearby @@ -74,6 +79,7 @@ struct P111_data_struct : public PluginTaskData_base { int64_t _last_served_irq_pin_time_micros{}; ESPEASY_VOLATILE(int64_t) _irq_pin_time_micros = -1; + }; #endif // ifdef USES_P111 diff --git a/src/src/PluginStructs/P116_data_struct.cpp b/src/src/PluginStructs/P116_data_struct.cpp index ad65da83e5..94a75fabd5 100644 --- a/src/src/PluginStructs/P116_data_struct.cpp +++ b/src/src/PluginStructs/P116_data_struct.cpp @@ -128,7 +128,8 @@ P116_data_struct::P116_data_struct(ST77xx_type_e device, String commandTrigger, uint16_t fgcolor, uint16_t bgcolor, - bool textBackFill + bool textBackFill, + uint8_t spi_bus # if ADAGFX_FONTS_INCLUDED , const uint8_t defaultFontId @@ -136,7 +137,7 @@ P116_data_struct::P116_data_struct(ST77xx_type_e device, ) : _device(device), _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _backlightPin(backlightPin), _backlightPercentage(backlightPercentage), _displayTimer(displayTimer), _displayTimeout(displayTimer), - _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), _textBackFill(textBackFill) + _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), _textBackFill(textBackFill), _spi_bus(spi_bus) # if ADAGFX_FONTS_INCLUDED , _defaultFontId(defaultFontId) # endif // if ADAGFX_FONTS_INCLUDED @@ -197,14 +198,14 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { initRoptions = INITR_BLACKTAB135x240; // 135x240px } - // fall through + // fall through case ST77xx_type_e::ST7735s_172x320: if (initRoptions == 0xFF) { initRoptions = INITR_BLACKTAB172x320; // 172x320px } - // fall through + // fall through case ST77xx_type_e::ST77xxs_170x320: if (initRoptions == 0xFF) { @@ -233,7 +234,12 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { initRoptions = INITR_MINI160x80; // 80x160px } + # ifdef ESP32 + st7735 = new (std::nothrow) Adafruit_ST7735(0 == _spi_bus ? &SPI : &SPIe, PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP32 + # ifdef ESP8266 st7735 = new (std::nothrow) Adafruit_ST7735(PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP8166 if (nullptr != st7735) { st7735->initR(initRoptions); // initialize a ST7735s chip @@ -251,7 +257,12 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { case ST77xx_type_e::ST7789vw3_135x240: # endif // if P116_EXTRA_ST7789 { + # ifdef ESP32 + st7789 = new (std::nothrow) Adafruit_ST7789(0 == _spi_bus ? &SPI : &SPIe, PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP32 + # ifdef ESP8266 st7789 = new (std::nothrow) Adafruit_ST7789(PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP8266 if (nullptr != st7789) { uint8_t init_seq = 0; // Default/original initialisation @@ -273,7 +284,12 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { } case ST77xx_type_e::ST7796s_320x480: { + # ifdef ESP32 + st7796 = new (std::nothrow) Adafruit_ST7796S_kbv(0 == _spi_bus ? &SPI : &SPIe, PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP32 + # ifdef ESP8266 st7796 = new (std::nothrow) Adafruit_ST7796S_kbv(PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP8266 if (nullptr != st7796) { st7796->begin(); diff --git a/src/src/PluginStructs/P116_data_struct.h b/src/src/PluginStructs/P116_data_struct.h index b70d3a381f..5a27290a31 100644 --- a/src/src/PluginStructs/P116_data_struct.h +++ b/src/src/PluginStructs/P116_data_struct.h @@ -32,9 +32,11 @@ # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object # include "../CustomBuild/StorageLayout.h" -# define P116_Nlines 24 // The number of different lines which can be displayed +# include "../Globals/SPIe.h" + +# define P116_Nlines 24 // The number of different lines which can be displayed # define P116_Nchars 60 -# define P116_DebounceTreshold 5 // number of 20 msec (fifty per second) ticks before the button has settled +# define P116_DebounceTreshold 5 // number of 20 msec (fifty per second) ticks before the button has settled // # define P116_SHOW_SPLASH // Enable to show splash (text) @@ -137,10 +139,11 @@ struct P116_data_struct : public PluginTaskData_base { String commandTrigger, uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, - bool textBackFill = true + bool textBackFill = true, + uint8_t spi_bus = 0 # if ADAGFX_FONTS_INCLUDED , - const uint8_t defaultFontId = 0 + const uint8_t defaultFontId = 0 # endif // if ADAGFX_FONTS_INCLUDED ); P116_data_struct() = delete; @@ -197,6 +200,7 @@ struct P116_data_struct : public PluginTaskData_base { uint16_t _fgcolor; uint16_t _bgcolor; bool _textBackFill; + uint8_t _spi_bus; # if ADAGFX_FONTS_INCLUDED uint8_t _defaultFontId; # endif // if ADAGFX_FONTS_INCLUDED diff --git a/src/src/PluginStructs/P118_data_struct.cpp b/src/src/PluginStructs/P118_data_struct.cpp index 9b2db6e8ad..6c9a0b4e3b 100644 --- a/src/src/PluginStructs/P118_data_struct.cpp +++ b/src/src/PluginStructs/P118_data_struct.cpp @@ -5,11 +5,12 @@ // **************************************************************************/ // Constructor // **************************************************************************/ -P118_data_struct::P118_data_struct(int8_t csPin, - int8_t irqPin, - bool logData, - bool rfLog) - : _csPin(csPin), _irqPin(irqPin), _log(logData), _rfLog(rfLog) {} +P118_data_struct::P118_data_struct(int8_t csPin, + int8_t irqPin, + bool logData, + bool rfLog, + uint8_t spi_bus) + : _csPin(csPin), _irqPin(irqPin), _log(logData), _rfLog(rfLog), _spi_bus(spi_bus) {} // **************************************************************************/ // Destructor @@ -30,9 +31,13 @@ bool P118_data_struct::plugin_init(struct EventStruct *event) { int8_t spi_pins[3]; uint32_t startInit = 0; - if (Settings.getSPI_pins(spi_pins) && validGpio(spi_pins[1])) { + if (Settings.getSPI_pins(spi_pins, _spi_bus) && validGpio(spi_pins[1])) { startInit = millis(); - _rf = new (std::nothrow) IthoCC1101(_csPin, spi_pins[1]); // Pass CS and MISO + _rf = new (std::nothrow) IthoCC1101(_csPin, spi_pins[1] + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe // defaults and SPI bus for ESP32 only + # endif // ifdef ESP32 + ); // Pass CS and MISO } else { addLog(LOG_LEVEL_ERROR, F("ITHO: SPI configuration not correct!")); } diff --git a/src/src/PluginStructs/P118_data_struct.h b/src/src/PluginStructs/P118_data_struct.h index fe6bba7776..c06ca7003c 100644 --- a/src/src/PluginStructs/P118_data_struct.h +++ b/src/src/PluginStructs/P118_data_struct.h @@ -9,6 +9,8 @@ # include "IthoCC1101.h" # include "IthoPacket.h" +# include "../Globals/SPIe.h" + # define P118_DEBUG_LOG // Enable for some (extra) logging # ifndef P118_FEATURE_ORCON # define P118_FEATURE_ORCON 1 // Enable use of Orcon commands @@ -60,10 +62,11 @@ struct PLUGIN_118_ExtraSettingsStruct { struct P118_data_struct : public PluginTaskData_base { public: - P118_data_struct(int8_t csPin, - int8_t irqPin, - bool logData, - bool rfLog); + P118_data_struct(int8_t csPin, + int8_t irqPin, + bool logData, + bool rfLog, + uint8_t spi_bus); P118_data_struct() = delete; virtual ~P118_data_struct(); @@ -104,10 +107,11 @@ struct P118_data_struct : public PluginTaskData_base { int _OldLastIDindex = 0; bool _InitRunned = false; - int8_t _csPin = -1; - int8_t _irqPin = -1; - bool _log = false; - bool _rfLog = false; + int8_t _csPin = -1; + int8_t _irqPin = -1; + bool _log = false; + bool _rfLog = false; + uint8_t _spi_bus; PLUGIN_118_ExtraSettingsStruct _ExtraSettings; diff --git a/src/src/PluginStructs/P120_data_struct.cpp b/src/src/PluginStructs/P120_data_struct.cpp index ae63cc6848..ff8c79bcb0 100644 --- a/src/src/PluginStructs/P120_data_struct.cpp +++ b/src/src/PluginStructs/P120_data_struct.cpp @@ -4,6 +4,7 @@ # define P120_RAD_TO_DEG 57.295779f // 180.0/M_PI +# include "../Globals/SPIe.h" P120_data_struct::P120_data_struct(uint8_t aSize) : _aSize(aSize) @@ -235,7 +236,14 @@ bool P120_data_struct::init_sensor(struct EventStruct *event) { if (i2c_mode) { adxl345 = new (std::nothrow) ADXL345(_i2c_addr); // Init using I2C } else { - adxl345 = new (std::nothrow) ADXL345(_cs_pin); // Init using SPI + # ifdef ESP32 + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + # endif // ifdef ESP32 + adxl345 = new (std::nothrow) ADXL345(_cs_pin + # ifdef ESP32 + , 0 == spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); // Init using SPI } if (initialized()) { @@ -537,7 +545,7 @@ bool P120_data_struct::plugin_webform_load(struct EventStruct *event) { FormSelectorOptions selector(4, rangeOptions, rangeValues); selector.default_index = P120_RANGE_16G; selector.addFormSelector(F("Range"), F("range"), - get2BitFromUL(P120_CONFIG_FLAGS1, P120_FLAGS1_RANGE)); + get2BitFromUL(P120_CONFIG_FLAGS1, P120_FLAGS1_RANGE)); addUnit('g'); } @@ -631,7 +639,7 @@ bool P120_data_struct::plugin_webform_load(struct EventStruct *event) { F("10"), F("50") }; int frequencyValues[] = { P120_FREQUENCY_10, P120_FREQUENCY_50 }; - const FormSelectorOptions selector( 2, frequencyOptions, frequencyValues); + const FormSelectorOptions selector(2, frequencyOptions, frequencyValues); selector.addFormSelector(F("Measuring frequency"), F("frequency"), P120_FREQUENCY); addUnit(F("Hz")); addFormNote(F("Values X/Y/Z are updated 1x per second, Controller updates & Value-events are based on 'Interval' setting.")); diff --git a/src/src/PluginStructs/P141_data_struct.cpp b/src/src/PluginStructs/P141_data_struct.cpp index 74a23c95a5..71f1300b7a 100644 --- a/src/src/PluginStructs/P141_data_struct.cpp +++ b/src/src/PluginStructs/P141_data_struct.cpp @@ -27,7 +27,8 @@ P141_data_struct::P141_data_struct(uint8_t rotation, uint16_t fgcolor, uint16_t bgcolor, bool textBackFill, - bool displayInverted + bool displayInverted, + uint8_t spi_bus # if ADAGFX_FONTS_INCLUDED , const uint8_t defaultFontId @@ -36,7 +37,7 @@ P141_data_struct::P141_data_struct(uint8_t rotation, : _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _backlightPin(backlightPin), _backlightPercentage(backlightPercentage), _contrast(contrast), _displayTimer(displayTimer), _displayTimeout(displayTimer), _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), - _textBackFill(textBackFill), _displayInverted(displayInverted) + _textBackFill(textBackFill), _displayInverted(displayInverted), _spi_bus(spi_bus) # if ADAGFX_FONTS_INCLUDED , _defaultFontId(defaultFontId) # endif // if ADAGFX_FONTS_INCLUDED @@ -66,7 +67,11 @@ bool P141_data_struct::plugin_init(struct EventStruct *event) { if (nullptr == pcd8544) { addLog(LOG_LEVEL_INFO, F("PCD8544: Init start.")); - pcd8544 = new (std::nothrow) Adafruit_PCD8544(P141_DC_PIN, P141_CS_PIN, P141_RST_PIN); + pcd8544 = new (std::nothrow) Adafruit_PCD8544(P141_DC_PIN, P141_CS_PIN, P141_RST_PIN + # ifdef ESP32 + , 0 == _spi_bus ? &SPI : &SPIe + # endif // ifdef ESP32 + ); # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { diff --git a/src/src/PluginStructs/P141_data_struct.h b/src/src/PluginStructs/P141_data_struct.h index 649eb89d8f..abfbd6ff6c 100644 --- a/src/src/PluginStructs/P141_data_struct.h +++ b/src/src/PluginStructs/P141_data_struct.h @@ -4,8 +4,10 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P141 -# include // include Adafruit graphics library -# include // include Adafruit PCD8544 LCD library +# include // include Adafruit graphics library +# include // include Adafruit PCD8544 LCD library + +# include "../Globals/SPIe.h" # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object @@ -90,13 +92,14 @@ struct P141_data_struct : public PluginTaskData_base { uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, bool textBackFill = true, - bool displayInverted = false + bool displayInverted = false, + uint8_t spi_bus = 0 # if ADAGFX_FONTS_INCLUDED , - const uint8_t defaultFontId = 0 + const uint8_t defaultFontId = 0 # endif // if ADAGFX_FONTS_INCLUDED ); - P141_data_struct() = delete; + P141_data_struct() = delete; virtual ~P141_data_struct(); bool plugin_init(struct EventStruct *event); @@ -151,6 +154,7 @@ struct P141_data_struct : public PluginTaskData_base { uint16_t _bgcolor; bool _textBackFill; bool _displayInverted; + uint8_t _spi_bus; # if ADAGFX_FONTS_INCLUDED uint8_t _defaultFontId; # endif // if ADAGFX_FONTS_INCLUDED diff --git a/src/src/PluginStructs/P154_data_struct.cpp b/src/src/PluginStructs/P154_data_struct.cpp index 589dcc718a..c64871f481 100644 --- a/src/src/PluginStructs/P154_data_struct.cpp +++ b/src/src/PluginStructs/P154_data_struct.cpp @@ -10,7 +10,11 @@ P154_data_struct::P154_data_struct(struct EventStruct *event) : i2cAddress(P154_I2C_ADDR), elevation(P154_ALTITUDE), csPin(PIN(0)) -{} +{ + # ifdef ESP32 + _spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + # endif // ifdef ESP32 +} bool P154_data_struct::begin(bool _i2cMode) { @@ -20,7 +24,11 @@ bool P154_data_struct::begin(bool _i2cMode) return false; } - if (!i2cMode && !bmp.begin_SPI(csPin)) { + if (!i2cMode && !bmp.begin_SPI(csPin + # ifdef ESP32 + , 0 == _spi_bus ? &SPI : &SPIe + # endif // ifdef ESP32 + )) { return false; } diff --git a/src/src/PluginStructs/P154_data_struct.h b/src/src/PluginStructs/P154_data_struct.h index 4c507ff012..375372e3b3 100644 --- a/src/src/PluginStructs/P154_data_struct.h +++ b/src/src/PluginStructs/P154_data_struct.h @@ -7,6 +7,8 @@ # include # include +#include "../Globals/SPIe.h" + # define P154_I2C_ADDR PCONFIG(0) # define P154_ALTITUDE PCONFIG(1) @@ -36,6 +38,7 @@ struct P154_data_struct : public PluginTaskData_base { uint8_t i2cAddress; int16_t elevation{}; int16_t csPin{}; + uint8_t _spi_bus{}; bool initialized = false; bool i2cMode = true; diff --git a/src/src/PluginStructs/P162_data_struct.cpp b/src/src/PluginStructs/P162_data_struct.cpp index 809b020eb7..65dc962800 100644 --- a/src/src/PluginStructs/P162_data_struct.cpp +++ b/src/src/PluginStructs/P162_data_struct.cpp @@ -9,18 +9,23 @@ // Needed also here for PlatformIO's library finder as the .h file // is in a directory which is excluded in the src_filter -P162_data_struct::P162_data_struct(int8_t csPin, - int8_t rstPin, - int8_t shdPin) - : _csPin(csPin), _rstPin(rstPin), _shdPin(shdPin) -{} +P162_data_struct::P162_data_struct(int8_t csPin, + int8_t rstPin, + int8_t shdPin, + uint8_t spi_bus) + : _csPin(csPin), _rstPin(rstPin), _shdPin(shdPin), _spi_bus(spi_bus) +{ + # ifdef ESP32 + _spi = 0 == _spi_bus ? SPI : SPIe; + # endif // ifdef ESP32 +} P162_data_struct::~P162_data_struct() { // } bool P162_data_struct::plugin_init(struct EventStruct *event) { - if (validGpio(_csPin) && Settings.isSPI_valid()) { + if (validGpio(_csPin) && Settings.isSPI_valid(_spi_bus)) { pinMode(_csPin, OUTPUT); _initialized = true; } @@ -168,8 +173,8 @@ void P162_data_struct::write_pot(uint8_t cmd, digitalWrite(_csPin, LOW); // send the command and value via SPI: - SPI.transfer(cmd); - SPI.transfer(val); + _spi.transfer(cmd); + _spi.transfer(val); // Set the CS pin high to execute the command: digitalWrite(_csPin, HIGH); diff --git a/src/src/PluginStructs/P162_data_struct.h b/src/src/PluginStructs/P162_data_struct.h index b26916a3b5..b6fdadab63 100644 --- a/src/src/PluginStructs/P162_data_struct.h +++ b/src/src/PluginStructs/P162_data_struct.h @@ -4,6 +4,8 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P162 +# include "../Globals/SPIe.h" + # define P162_CS_PIN PIN(0) # define P162_RST_PIN PIN(1) # define P162_SHD_PIN PIN(2) @@ -33,9 +35,10 @@ const uint8_t P162_BOTH_POT_SHUTDOWN = 0x23; const uint8_t P162_RESET_VALUE = 0x80; // Pot setting on power-up/reset struct P162_data_struct : public PluginTaskData_base { - P162_data_struct(int8_t csPin, - int8_t rstPin, - int8_t shdPin); + P162_data_struct(int8_t csPin, + int8_t rstPin, + int8_t shdPin, + uint8_t spi_bus); P162_data_struct() = delete; virtual ~P162_data_struct(); @@ -45,6 +48,7 @@ struct P162_data_struct : public PluginTaskData_base { private: + SPIClass& _spi = SPI; bool hw_reset(); void write_pot(uint8_t cmd, uint8_t val); @@ -57,6 +61,7 @@ struct P162_data_struct : public PluginTaskData_base { int8_t _shdPin; uint8_t _shdState = HIGH; bool _initialized = false; + uint8_t _spi_bus; }; #endif // ifdef USES_P162 diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 5f1f5283ce..31f5c3a076 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -31,6 +31,7 @@ # include "../Helpers/_Plugin_Helper_serial.h" # include "../Helpers/ESPEasy_Storage.h" # include "../Helpers/I2C_Plugin_Helper.h" +# include "../Helpers/SPI_Helper.h" # include "../Helpers/StringConverter.h" # include "../Helpers/StringGenerator_GPIO.h" @@ -161,7 +162,7 @@ void handle_devices() { const DeviceStruct& device = Device[DeviceIndex]; if ((device.Type == DEVICE_TYPE_I2C) && device.I2CMax100kHz) { // 100 kHz-only I2C device? - bitWrite(Settings.I2C_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED, 1); // Then: Enable Force Slow I2C speed checkbox by default + bitWrite(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED, 1); // Then: Enable Force Slow I2C speed checkbox by default } } } @@ -281,6 +282,12 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task Settings.TaskDeviceNumber[taskIndex] = taskdevicenumber.value; + #ifdef ESP32 + if (device.isSPI()) { + Settings.setSPIBusForTask(taskIndex, getFormItemInt(F("pspibus"), 0)); + } + #endif // ifdef ESP32 + if (device.Type == DEVICE_TYPE_I2C) { uint8_t flags = 0; bitWrite(flags, I2C_FLAGS_SLOW_SPEED, isFormItemChecked(F("taskdeviceflags0"))); @@ -322,7 +329,7 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task # endif // if FEATURE_I2CMULTIPLEXER - Settings.I2C_Flags[taskIndex] = flags; + Settings.I2C_SPI_bus_Flags[taskIndex] = flags; } // Must load from file system to make sure all caches and checksums match. @@ -646,7 +653,7 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) if (device.Type == DEVICE_TYPE_I2C) { format_I2C_port_description(x); } else if (device.isSPI()) { - format_SPI_port_description(spi_gpios); + format_SPI_port_description(spi_gpios, Settings.getSPIBusForTask(x)); } else if (device.isSerial()) { # ifdef PLUGIN_USES_SERIAL addHtml(serialHelper_getSerialTypeLabel(&TempEvent)); @@ -999,7 +1006,7 @@ void format_I2C_port_description(taskIndex_t x) if (isI2CMultiplexerEnabled(i2cBus) && I2CMultiplexerPortSelectedForTask(x)) { String mux; - if (bitRead(Settings.I2C_Flags[x], I2C_FLAGS_MUX_MULTICHANNEL)) { // Multi-channel + if (bitRead(Settings.I2C_SPI_bus_Flags[x], I2C_FLAGS_MUX_MULTICHANNEL)) { // Multi-channel mux = F("
Multiplexer channel(s)"); uint8_t b = 0; // For adding lineBreaks @@ -1018,14 +1025,14 @@ void format_I2C_port_description(taskIndex_t x) # endif // if FEATURE_I2CMULTIPLEXER } -void format_SPI_port_description(int8_t spi_gpios[3]) +void format_SPI_port_description(int8_t spi_gpios[3], uint8_t spi_bus) { - if (!Settings.getSPI_pins(spi_gpios)) { + if (!Settings.getSPI_pins(spi_gpios, spi_bus)) { addHtml(F("SPI (Not enabled)")); return; } # ifdef ESP32 - addHtml(getSPI_optionToShortString(static_cast(Settings.InitSPI))); + addHtml(getSPI_optionToShortString(static_cast(0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1), spi_bus)); # endif // ifdef ESP32 # ifdef ESP8266 addHtml(F("SPI")); @@ -1049,7 +1056,8 @@ void format_I2C_pin_description(taskIndex_t x) void format_SPI_pin_description(int8_t spi_gpios[3], taskIndex_t x, bool showCSpin) { - if (Settings.InitSPI > static_cast(SPI_Options_e::None)) { + const uint8_t spi_bus = Settings.getSPIBusForTask(x); + if ((0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1) > static_cast(SPI_Options_e::None)) { const __FlashStringHelper*labels[] = { F("CLK"), F("MISO"), F("MOSI") }; for (size_t i = 0; i < NR_ELEMENTS(labels); ++i) { @@ -1321,9 +1329,8 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex addFormNote(F("Will go into effect on next input change.")); } - if (device.isSPI() - && (Settings.InitSPI == static_cast(SPI_Options_e::None))) { - addFormNote(F("SPI Interface is not configured yet (Hardware page).")); + if (device.isSPI()) { + devicePage_show_SPI_config(taskIndex, DeviceIndex); } if (device.connectedToGPIOpins()) { @@ -1399,6 +1406,22 @@ void devicePage_show_serial_config(taskIndex_t taskIndex) # endif // ifdef PLUGIN_USES_SERIAL +void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) +{ + if (Device[DeviceIndex].isSPI() + && !(Settings.isSPI_valid(0u) || (getSPIBusCount() > 1 && Settings.isSPI_valid(1u)))) { + addFormNote(F("SPI Bus not configured yet (Hardware page).")); + } + #ifdef ESP32 + if (Device[DeviceIndex].SpiBusSelect && getSPIBusCount() > 1 && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) { + uint8_t spiBus = Settings.getSPIBusForTask(taskIndex); + SPIInterfaceSelector(F("SPI Bus"), + F("pspibus"), + spiBus); + } + #endif // ifdef ESP32 +} + void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { struct EventStruct TempEvent(taskIndex); @@ -1419,7 +1442,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex String dummy; PluginCall(PLUGIN_WEBFORM_SHOW_I2C_PARAMS, &TempEvent, dummy); - addFormCheckBox(F("Force Slow I2C speed"), F("taskdeviceflags0"), bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)); + addFormCheckBox(F("Force Slow I2C speed"), F("taskdeviceflags0"), bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)); if (Device[DeviceIndex].I2CMax100kHz) { addFormNote(F("This device is specified for max. 100 kHz operation!")); @@ -1442,7 +1465,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex // Show selector for an I2C multiplexer port if a multiplexer is configured if (isI2CMultiplexerEnabled(i2cBus)) { - bool multipleMuxPorts = bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL); + bool multipleMuxPorts = bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL); { const __FlashStringHelper *i2c_mux_channels[] = { F("Single channel"), diff --git a/src/src/WebServer/DevicesPage.h b/src/src/WebServer/DevicesPage.h index 8b7bece77b..1b33d2ec8a 100644 --- a/src/src/WebServer/DevicesPage.h +++ b/src/src/WebServer/DevicesPage.h @@ -37,7 +37,7 @@ void format_originating_node(uint8_t remoteUnit); #endif void format_I2C_port_description(taskIndex_t x); -void format_SPI_port_description(int8_t spi_gpios[3]); +void format_SPI_port_description(int8_t spi_gpios[3], uint8_t spi_bus); void format_I2C_pin_description(taskIndex_t x); @@ -56,6 +56,8 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex void devicePage_show_serial_config(taskIndex_t taskIndex); #endif +void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); + void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 7370cf812b..5e511fdb47 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -17,6 +17,8 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware_GPIO.h" #include "../Helpers/Hardware_I2C.h" +#include "../Helpers/Hardware_SPI.h" +#include "../Helpers/SPI_Helper.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" @@ -108,18 +110,28 @@ void handle_hardware() { set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); #endif // if FEATURE_I2C_MULTIPLE #ifdef ESP32 - Settings.InitSPI = getFormItemInt(F("initspi"), static_cast(SPI_Options_e::None)); - if (Settings.InitSPI == static_cast(SPI_Options_e::UserDefined)) { // User-define SPI GPIO pins - Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk"), -1); - Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso"), -1); - Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi"), -1); - if (!Settings.isSPI_valid()) { // Checks - error += F("User-defined SPI pins not configured correctly!\n"); - } + Settings.InitSPI = getFormItemInt(F("initspi0"), static_cast(SPI_Options_e::None)); + // User-defined SPI bus 0 GPIO pins + Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1); + Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1); + Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1); + + Settings.InitSPI1 = getFormItemInt(F("initspi1"), static_cast(SPI_Options_e::None)); + // User-defined SPI bus 1 GPIO pins + Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1); + Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1); + Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1); + for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { + if (Settings.isSPI_enabled(spi_bus) && !Settings.isSPI_valid(spi_bus)) { // Checks + error += strformat(F("SPI bus %u pins not configured correctly!
"), spi_bus); } + } #else //for ESP8266 we keep the old UI - Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init + Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init #endif + #if defined(ESP32) && FEATURE_SD + Settings.setSPIBusForSDCard(getFormItemInt(F("sdspibus"), 0)); + #endif // if defined(ESP32) && FEATURE_SD Settings.Pin_sd_cs = getFormItemInt(F("sd")); int gpio = 0; @@ -140,6 +152,8 @@ void handle_hardware() { if (error.isEmpty()) { // Apply I2C settings. initI2C(); + // Apply SPI settings + initializeSPIBuses(); } } @@ -269,52 +283,67 @@ void handle_hardware() { } #endif // if FEATURE_I2C_MULTIPLE - // SPI Init - addFormSubHeader(F("SPI Interface")); #ifdef ESP32 - { - // Script to show GPIO pins for User-defined SPI GPIOs - // html_add_script(F("function spiOptionChanged(elem) {var spipinstyle = elem.value == 9 ? '' : 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"), - // Minified: - html_add_script(F("function spiOptionChanged(e){var i=9==e.value?'':'none';" - "document.getElementById('tr_spipinsclk').style.display=i," - "document.getElementById('tr_spipinmiso').style.display=i," - "document.getElementById('tr_spipinmosi').style.display=i}"), - false); - const __FlashStringHelper * spi_options[] = { - getSPI_optionToString(SPI_Options_e::None), - getSPI_optionToString(SPI_Options_e::Vspi_Fspi), - #ifdef ESP32_CLASSIC - getSPI_optionToString(SPI_Options_e::Hspi), - #endif - getSPI_optionToString(SPI_Options_e::UserDefined)}; - const int spi_index[] = { - static_cast(SPI_Options_e::None), - static_cast(SPI_Options_e::Vspi_Fspi), - #ifdef ESP32_CLASSIC - static_cast(SPI_Options_e::Hspi), - #endif - static_cast(SPI_Options_e::UserDefined) - }; - constexpr size_t nrOptions = NR_ELEMENTS(spi_index); - FormSelectorOptions selector(nrOptions, spi_options, spi_index); - selector.onChangeCall = F("spiOptionChanged(this)"); - selector.addFormSelector(F("Init SPI"), F("initspi"), Settings.InitSPI); - // User-defined pins - addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("CLK")), F("spipinsclk"), Settings.SPI_SCLK_pin); - addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), F("spipinmiso"), Settings.SPI_MISO_pin); - addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), F("spipinmosi"), Settings.SPI_MOSI_pin); - html_add_script(F("document.getElementById('initspi').onchange();"), false); // Initial trigger onchange script - addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!")); + for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { + // SPI Init + addFormSubHeader(concat(F("SPI Bus "), spi_bus)); + { + // Script to show GPIO pins for User-defined SPI GPIOs + // html_add_script(F("function spiOptionChanged(elem) {var spipinstyle = elem.value == 9 ? '' : 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"), + // Minified: + html_add_script(strformat(F("function spi%uOptionChanged(e){var i=9==e.value?'':'none';" + "document.getElementById('tr_spipinsclk%u').style.display=i," + "document.getElementById('tr_spipinmiso%u').style.display=i," + "document.getElementById('tr_spipinmosi%u').style.display=i" + "}"), spi_bus, spi_bus, spi_bus, spi_bus, spi_bus), + false); + const __FlashStringHelper * spi_options[] = { + getSPI_optionToString(SPI_Options_e::None), + getSPI_optionToString(SPI_Options_e::Vspi_Fspi), + #ifdef ESP32_CLASSIC + getSPI_optionToString(SPI_Options_e::Hspi), + #endif + getSPI_optionToString(SPI_Options_e::UserDefined)}; + const int spi_index[] = { + static_cast(SPI_Options_e::None), + static_cast(SPI_Options_e::Vspi_Fspi), + #ifdef ESP32_CLASSIC + static_cast(SPI_Options_e::Hspi), + #endif + static_cast(SPI_Options_e::UserDefined) + }; + constexpr size_t nrOptions = NR_ELEMENTS(spi_index); + FormSelectorOptions selector(nrOptions, spi_options, spi_index); + selector.onChangeCall = strformat(F("spi%uOptionChanged(this)"), spi_bus); + selector.addFormSelector(concat(F("Init SPI Bus "), spi_bus), concat(F("initspi"), spi_bus), 0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1); + int8_t spi_gpio[3]; + Settings.getSPI_pins(spi_gpio, spi_bus, true); // Load GPIO pins even if wrongly configured + // User-defined pins + addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("CLK")), concat(F("spipinsclk"), spi_bus), spi_gpio[0]); + addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), concat(F("spipinmiso"), spi_bus), spi_gpio[1]); + addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), concat(F("spipinmosi"), spi_bus), spi_gpio[2]); + html_add_script(strformat(F("document.getElementById('initspi%u').onchange();"), spi_bus), false); // Initial trigger onchange script + // addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!")); + addFormNote(F("Chip Select (CS) config must be done in the plugin")); + } } #else //for ESP8266 we keep the existing UI + addFormSubHeader(F("SPI Bus 0")); addFormCheckBox(F("Init SPI"), F("initspi"), Settings.InitSPI > static_cast(SPI_Options_e::None)); addFormNote(F("CLK=GPIO-14 (D5), MISO=GPIO-12 (D6), MOSI=GPIO-13 (D7)")); - #endif addFormNote(F("Chip Select (CS) config must be done in the plugin")); + #endif // ifdef ESP32 #if FEATURE_SD addFormSubHeader(F("SD Card")); + #ifdef ESP32 + if (getSPIBusCount() > 1 && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) { + uint8_t spiBus = Settings.getSPIBusForSDCard(); + SPIInterfaceSelector(F("SPI Bus"), + F("sdspibus"), + spiBus); + } + #endif // ifdef ESP32 addFormPinSelect(PinSelectPurpose::SD_Card, formatGpioName_output(F("SD Card CS")), F("sd"), Settings.Pin_sd_cs); #endif // if FEATURE_SD diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index fb98a9f5f3..41218a8c6e 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -632,7 +632,7 @@ void handle_json() # endif int8_t channel = Settings.I2C_Multiplexer_Channel[TaskIndex]; - if (bitRead(Settings.I2C_Flags[TaskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { + if (bitRead(Settings.I2C_SPI_bus_Flags[TaskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { KeyValueStruct kv(F("I2CBus")); diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 8580a8de0d..f5c1c3cc64 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -464,6 +464,11 @@ void addFormPinSelect(PinSelectPurpose purpose, const String& label, const __Fla addPinSelect(purpose, id, choice); } +void addFormPinSelect(PinSelectPurpose purpose, const String& label, const String& id, int choice) { + addRowLabel_tr_id(label, id); + addPinSelect(purpose, id, choice); +} + void addFormPinSelect(PinSelectPurpose purpose, const __FlashStringHelper * label, const __FlashStringHelper * id, int choice) { addRowLabel_tr_id(label, id); diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index 2251e76ddf..6f79d75d96 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -271,6 +271,7 @@ void addFormPinSelect(const __FlashStringHelper * label, int choice); */ void addFormPinSelect(PinSelectPurpose purpose, const String& label, const __FlashStringHelper * id, int choice); +void addFormPinSelect(PinSelectPurpose purpose, const String& label, const String& id, int choice); void addFormPinSelect(PinSelectPurpose purpose, const __FlashStringHelper * label, const __FlashStringHelper * id, int choice);