From 4dcbbc9eb3f8bf83cab4fd085b5769fe04b6dba6 Mon Sep 17 00:00:00 2001 From: "Hansi, dl9rdz" Date: Sat, 14 Aug 2021 00:30:51 +0200 Subject: [PATCH] SPI locking and sx1278 configurability --- RX_FSK/RX_FSK.ino | 51 +++++++++++++++++--- RX_FSK/version.h | 2 +- libraries/SondeLib/Display.cpp | 35 +++++++++++++- libraries/SondeLib/SX1278FSK.cpp | 64 +++++++++++++++---------- libraries/SondeLib/SX1278FSK.h | 8 ++-- libraries/SondeLib/Sonde.cpp | 73 ++++++++++++++++++++++------- libraries/SondeLib/Sonde.h | 8 ++++ libraries/SondeLib/autodetect-infos | 10 ++++ 8 files changed, 196 insertions(+), 55 deletions(-) diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index 45ba170..c0654b4 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -663,6 +663,12 @@ struct st_configitems config_list[] = { {"led_pout", "LED output port", 0, &sonde.config.led_pout}, {"gps_rxd", "GPS RXD pin (-1 to disable)", 0, &sonde.config.gps_rxd}, {"gps_txd", "GPS TXD pin (not really needed)", 0, &sonde.config.gps_txd}, +#if 1 + {"sx1278_ss", "SX1278 SS", 0, &sonde.config.sx1278_ss}, + {"sx1278_miso", "SX1278 MISO", 0, &sonde.config.sx1278_miso}, + {"sx1278_mosi", "SX1278 MOSI", 0, &sonde.config.sx1278_mosi}, + {"sx1278_sck", "SX1278 SCK", 0, &sonde.config.sx1278_sck}, +#endif {"mdnsname", "mDNS name", 14, &sonde.config.mdnsname}, #if FEATURE_SONDEHUB @@ -1844,6 +1850,7 @@ int scanI2Cdevice(void) extern int initlevels[40]; +extern xSemaphoreHandle globalLock; #ifdef ESP_MEM_DEBUG typedef void (*esp_alloc_failed_hook_t) (size_t size, uint32_t caps, const char * function_name); @@ -1928,8 +1935,14 @@ void setup() Serial.println("AXP192 Begin FAIL"); } axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); - axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); - + if(sonde.config.type == TYPE_M5_CORE2) { + // Display backlight on M5 Core2 + axp.setPowerOutPut(AXP192_DCDC3, AXP202_ON); + axp.setDCDC3Voltage(3300); + } else { + // GPS on T-Beam, buzzer on M5 Core2 + axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); + } axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON); axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON); axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); @@ -2044,9 +2057,30 @@ void setup() } // == show initial values from config.txt ========================= // -#if 0 +#if 1 + + if(sonde.config.type == TYPE_M5_CORE2) { + // Core2 uses Pin 38 for MISO + SPI.begin(18, 38, 23, -1); + } else { + SPI.begin(); + } + //Set most significant bit first + SPI.setBitOrder(MSBFIRST); + //Divide the clock frequency + SPI.setClockDivider(SPI_CLOCK_DIV2); + //Set data mode + SPI.setDataMode(SPI_MODE0); + +sx1278.setup(globalLock); + +uint8_t state = 2; +int i=0; +while(++i<3) { + delay(500); // == check the radio chip by setting default frequency =========== // - if (rs41.setFrequency(402700000) == 0) { + sx1278.ON(); + if (sx1278.setFrequency(402700000) == 0) { Serial.println(F("Setting freq: SUCCESS ")); } else { Serial.println(F("Setting freq: ERROR ")); @@ -2055,6 +2089,7 @@ void setup() Serial.print("Frequency set to "); Serial.println(f); // == check the radio chip by setting default frequency =========== // +} #endif //sx1278.setLNAGain(-48); @@ -2128,9 +2163,12 @@ void enterMode(int mode) { // trigger activation of background task // currentSonde should be set before enterMode() rxtask.activate = ACT_SONDE(sonde.currentSonde); + // + Serial.println("clearing and updating display"); sonde.clearDisplay(); sonde.updateDisplay(); } + printf("enterMode ok\n"); } static char text[40]; @@ -2511,6 +2549,7 @@ void enableNetwork(bool enable) { MDNS.end(); connected = false; } + Serial.println("enableNetwork done"); } // Events used only for debug output right now @@ -3422,8 +3461,8 @@ void sondehub_send_next(WiFiClient * client, SondeInfo * s, struct st_sondehub * client->print("\r\n"); Serial.printf("%x\r\n", chunklen + 1); - Serial.write(first ? "[" : ",", 1); - Serial.write(chunk, chunklen); + Serial.write((const uint8_t *)(first ? "[" : ","), 1); + Serial.write((const uint8_t *)chunk, chunklen); Serial.print("\r\n"); } void sondehub_send_last(WiFiClient * client, SondeInfo * s, struct st_sondehub * conf) { diff --git a/RX_FSK/version.h b/RX_FSK/version.h index eaf4c51..cb09c6e 100644 --- a/RX_FSK/version.h +++ b/RX_FSK/version.h @@ -1,4 +1,4 @@ const char *version_name = "rdzTTGOsonde"; -const char *version_id = "devel20210812"; +const char *version_id = "devel20210813-M5"; const int SPIFFS_MAJOR=2; const int SPIFFS_MINOR=14; diff --git a/libraries/SondeLib/Display.cpp b/libraries/SondeLib/Display.cpp index 7d9e0a4..a73da52 100644 --- a/libraries/SondeLib/Display.cpp +++ b/libraries/SondeLib/Display.cpp @@ -1,3 +1,4 @@ +#include "../../RX_FSK/features.h" #include #include #include @@ -24,6 +25,13 @@ extern AXP20X_Class axp; extern bool axp192_found; extern SemaphoreHandle_t axpSemaphore; +extern xSemaphoreHandle globalLock; +#define SPI_MUTEX_LOCK() \ + do \ + { \ + } while (xSemaphoreTake(globalLock, portMAX_DELAY) != pdPASS) +#define SPI_MUTEX_UNLOCK() xSemaphoreGive(globalLock) + struct GpsPos gpsPos; //SPIClass spiDisp(HSPI); @@ -341,8 +349,15 @@ Arduino_DataBus *bus; void ILI9225Display::begin() { Serial.println("ILI9225/ILI9341 init"); - bus = new Arduino_ESP32SPI( sonde.config.tft_rs, sonde.config.tft_cs, - sonde.config.oled_scl, sonde.config.oled_sda, -1, HSPI); + // On the M5, the display and the Lora chip are on the same SPI interface (VSPI default pins), + // we must use the same SPI bus with correct locking + if(sonde.config.type == TYPE_M5_CORE2) { + bus = new Arduino_ESP32SPI( sonde.config.tft_rs, sonde.config.tft_cs, + sonde.config.oled_scl, sonde.config.oled_sda, 38, VSPI); + } else { + bus = new Arduino_ESP32SPI( sonde.config.tft_rs, sonde.config.tft_cs, + sonde.config.oled_scl, sonde.config.oled_sda, -1, HSPI); + } if(_type == 3) tft = new Arduino_ILI9341(bus, sonde.config.oled_rst); else if(_type == 4) @@ -354,10 +369,14 @@ void ILI9225Display::begin() { tft->fillScreen(BLACK); tft->setRotation(sonde.config.tft_orient); tft->setTextWrap(false); + if(sonde.config.type == TYPE_M5_CORE2) + tft->invertDisplay(true); } void ILI9225Display::clear() { + SPI_MUTEX_LOCK(); tft->fillScreen(BLACK); + SPI_MUTEX_UNLOCK(); } // for now, 0=small=FreeSans9pt7b, 1=large=FreeSans18pt7b @@ -412,6 +431,7 @@ void ILI9225Display::drawString(uint8_t x, uint8_t y, const char *s, int16_t wid } // Standard font if(findex<3) { + SPI_MUTEX_LOCK(); DebugPrintf(DEBUG_DISPLAY, "Simple Text %s at %d,%d [%d]\n", s, x, y, width); // for gpx fonts and new library, cursor is at baseline!! int h = 6; if(findex>1) h=12; @@ -445,9 +465,11 @@ void ILI9225Display::drawString(uint8_t x, uint8_t y, const char *s, int16_t wid // tft->fillRectangle(curx, y, x + width - 1, y + h - 1, bg); //} } + SPI_MUTEX_UNLOCK(); return; } // GFX font + SPI_MUTEX_LOCK(); int16_t x1, y1; if(1||width==WIDTH_AUTO || alignright) { tft->getTextBounds(s, x, y + gfxoffsets[findex-3].yofs, &x1, &y1, (uint16_t *)&w, (uint16_t *)&h); @@ -515,10 +537,12 @@ void ILI9225Display::drawString(uint8_t x, uint8_t y, const char *s, int16_t wid free(bitmap); #endif #endif + SPI_MUTEX_UNLOCK(); } void ILI9225Display::drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr) { int i,j; + SPI_MUTEX_LOCK(); tft->startWrite(); for(i=0; iendWrite(); + SPI_MUTEX_UNLOCK(); #if 0 int i,j; tft->startWrite(); @@ -545,18 +570,24 @@ void ILI9225Display::drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_p } void ILI9225Display::drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color, boolean fill) { + SPI_MUTEX_LOCK(); if(fill) tft->fillTriangle(x1, y1, x2, y2, x3, y3, color); else tft->drawTriangle(x1, y1, x2, y2, x3, y3, color); + SPI_MUTEX_UNLOCK(); } void ILI9225Display::drawBitmap(uint16_t x1, uint16_t y1, const uint16_t* bitmap, int16_t w, int16_t h) { + SPI_MUTEX_LOCK(); tft->draw16bitRGBBitmap(x1, y1, bitmap, w, h); + SPI_MUTEX_UNLOCK(); } void ILI9225Display::welcome() { + SPI_MUTEX_LOCK(); tft->fillScreen(0); + SPI_MUTEX_UNLOCK(); setFont(6); drawString(0, 0*22, version_name, WIDTH_AUTO, 0xff00); setFont(5); diff --git a/libraries/SondeLib/SX1278FSK.cpp b/libraries/SondeLib/SX1278FSK.cpp index ecb7fdc..ba56638 100644 --- a/libraries/SondeLib/SX1278FSK.cpp +++ b/libraries/SondeLib/SX1278FSK.cpp @@ -14,13 +14,38 @@ #include #include -SX1278FSK::SX1278FSK() + +#define SPI_MUTEX_LOCK() \ + do \ + { \ + } while (xSemaphoreTake(_lock, portMAX_DELAY) != pdPASS) +#define SPI_MUTEX_UNLOCK() xSemaphoreGive(_lock) + +SX1278FSK::SX1278FSK() {} + +void SX1278FSK::setup(xSemaphoreHandle lock) { - // Initialize class variables + _lock = lock; + Serial.println("Setup sx1278"); + if(_lock) SPI_MUTEX_LOCK(); + digitalWrite(sonde.config.sx1278_ss, HIGH); + pinMode(sonde.config.sx1278_ss, OUTPUT); + Serial.printf("Configuing SX1278FSK SPI with miso=%d, mosi=%d, sck=%d, ss=%d\n", sonde.config.sx1278_miso, + sonde.config.sx1278_mosi, sonde.config.sx1278_sck, sonde.config.sx1278_ss); + SPI.begin(sonde.config.sx1278_sck, sonde.config.sx1278_miso, sonde.config.sx1278_mosi, -1); // no hardware CS + // was: SPI.begin(); + + //Set most significant bit first + SPI.setBitOrder(MSBFIRST); + //Divide the clock frequency + SPI.setClockDivider(SPI_CLOCK_DIV2); + //Set data mode + SPI.setDataMode(SPI_MODE0); + if(_lock) SPI_MUTEX_UNLOCK(); }; -static SPISettings spiset = SPISettings(40000000L, MSBFIRST, SPI_MODE0); +static SPISettings spiset = SPISettings(10000000L, MSBFIRST, SPI_MODE0); /* Function: Turns the module ON. @@ -34,19 +59,6 @@ uint8_t SX1278FSK::ON() Serial.println(F("Starting 'ON'")); #endif - // Powering the module - pinMode(SX1278_SS, OUTPUT); - digitalWrite(SX1278_SS, HIGH); - - //Configure the MISO, MOSI, CS, SPCR. - SPI.begin(); - //Set most significant bit first - SPI.setBitOrder(MSBFIRST); - //Divide the clock frequency - SPI.setClockDivider(SPI_CLOCK_DIV2); - //Set data mode - SPI.setDataMode(SPI_MODE0); - // Set Maximum Over Current Protection state = setMaxCurrent(0x1B); if( state == 0 ) @@ -60,7 +72,6 @@ uint8_t SX1278FSK::ON() { return 1; } - // set FSK mode state = setFSK(); return state; @@ -77,10 +88,12 @@ void SX1278FSK::OFF() Serial.println(F("Starting 'OFF'")); #endif - SPI.end(); + //SPI.end(); +#if 0 // Powering the module pinMode(SX1278_SS,OUTPUT); digitalWrite(SX1278_SS,LOW); +#endif #if (SX1278FSK_debug_mode > 1) Serial.println(F("## Setting OFF ##")); @@ -98,15 +111,16 @@ byte SX1278FSK::readRegister(byte address) { byte value = 0x00; + if(_lock) SPI_MUTEX_LOCK(); + digitalWrite(sonde.config.sx1278_ss,LOW); SPI.beginTransaction(spiset); - digitalWrite(SX1278_SS,LOW); //delay(1); bitClear(address, 7); // Bit 7 cleared to write in registers SPI.transfer(address); value = SPI.transfer(0x00); - digitalWrite(SX1278_SS,HIGH); SPI.endTransaction(); + digitalWrite(sonde.config.sx1278_ss,HIGH); #if (SX1278FSK_debug_mode > 1) if(address!=0x3F) { @@ -118,7 +132,7 @@ byte SX1278FSK::readRegister(byte address) Serial.println(); } #endif - + if(_lock) SPI_MUTEX_UNLOCK(); return value; } @@ -131,15 +145,16 @@ Parameters: */ void SX1278FSK::writeRegister(byte address, byte data) { + if(_lock) SPI_MUTEX_LOCK(); + digitalWrite(sonde.config.sx1278_ss,LOW); SPI.beginTransaction(spiset); - digitalWrite(SX1278_SS,LOW); //delay(1); bitSet(address, 7); // Bit 7 set to read from registers SPI.transfer(address); SPI.transfer(data); - digitalWrite(SX1278_SS,HIGH); SPI.endTransaction(); + digitalWrite(sonde.config.sx1278_ss,HIGH); #if (SX1278FSK_debug_mode > 1) Serial.print(F("## Writing: ##\t")); @@ -150,7 +165,7 @@ void SX1278FSK::writeRegister(byte address, byte data) Serial.print(data, HEX); Serial.println(); #endif - + if(_lock) SPI_MUTEX_UNLOCK(); } /* @@ -867,4 +882,5 @@ void SX1278FSK::showRxRegisters() } #endif +xSemaphoreHandle globalLock =xSemaphoreCreateMutex(); SX1278FSK sx1278 = SX1278FSK(); diff --git a/libraries/SondeLib/SX1278FSK.h b/libraries/SondeLib/SX1278FSK.h index 6162be9..ea95b69 100644 --- a/libraries/SondeLib/SX1278FSK.h +++ b/libraries/SondeLib/SX1278FSK.h @@ -35,8 +35,6 @@ #define SX1278FSK_debug_mode 0 -#define SX1278_SS SS - //! MACROS // #define bitRead(value, bit) (((value) >> (bit)) & 0x01) // read a bit #define bitSet(value, bit) ((value) |= (1UL << (bit))) // set bit to '1' @@ -171,7 +169,9 @@ class SX1278FSK { public: // class constructor - SX1278FSK(); + SX1278FSK(); + + void setup(xSemaphoreHandle lock); // Turn on SX1278 module (return 0 on sucess, 1 otherwise) uint8_t ON(); @@ -256,7 +256,7 @@ public: // Receive a packet uint8_t receivePacketTimeout(uint32_t wait, byte *data); - + xSemaphoreHandle _lock = NULL; #if 0 //! It gets the internal temperature of the module. diff --git a/libraries/SondeLib/Sonde.cpp b/libraries/SondeLib/Sonde.cpp index d8d0df5..9f6f6c2 100644 --- a/libraries/SondeLib/Sonde.cpp +++ b/libraries/SondeLib/Sonde.cpp @@ -82,9 +82,14 @@ void Sonde::defaultConfig() { config.power_pout = -1; config.spectrum=10; // Try autodetecting board type + config.type = TYPE_TTGO; // Seems like on startup, GPIO4 is 1 on v1 boards, 0 on v2.1 boards? config.gps_rxd = -1; config.gps_txd = -1; + config.sx1278_ss = SS; // default SS pin, on all TTGOs + config.sx1278_miso = MISO; + config.sx1278_mosi = MOSI; + config.sx1278_sck = SCK; config.oled_rst = 16; config.disptype = 0; config.tft_orient = 1; @@ -103,32 +108,55 @@ void Sonde::defaultConfig() { } else { config.oled_sda = 21; config.oled_scl = 22; - if(initlevels[17]==0) { // T-Beam + if(initlevels[17]==0) { // T-Beam or M5Stack Core2? int tbeam=7; if(initlevels[12]==0) { tbeam = 10; - Serial.println("Autoconfig: looks like T-Beam 1.0 board"); + Serial.println("Autoconfig: looks like T-Beam 1.0 or M5Stack Core2 board"); } else if ( initlevels[4]==1 && initlevels[12]==1 ) { tbeam = 11; Serial.println("Autoconfig: looks like T-Beam 1.1 board"); } if(tbeam == 10 || tbeam == 11) { // T-Beam v1.0 or T-Beam v1.1 - config.button_pin = 38; - config.button2_pin = 15 + 128; //T4 + 128; // T4 = GPIO13 - // Maybe in future use as default only PWR as button2? - //config.button2_pin = 255; - config.button2_axp = 1; - config.gps_rxd = 34; - config.gps_txd = 12; - // Check for I2C-Display@21,22 -#define SSD1306_ADDRESS 0x3c Wire.begin(21, 22); - Wire.beginTransmission(SSD1306_ADDRESS); - byte err = Wire.endTransmission(); - delay(100); // otherwise its too fast?! - Wire.beginTransmission(SSD1306_ADDRESS); - err = Wire.endTransmission(); - if(err!=0 && fingerprint!=17) { // hmm. 17 after powerup with oled commected and no i2c answer!?!? +#define BM8563_ADDRESS 0x51 + Wire.beginTransmission(BM8563_ADDRESS); + byte err = Wire.endTransmission(); + if(err==0) { + Serial.println("M5stack Core2 board detected\n"); + config.type = TYPE_M5_CORE2; + config.button_pin = 255; + config.button2_pin = 255; + config.button2_axp = 1; + config.disptype = 4; // ILI9342 + config.oled_sda = 23; + config.oled_scl = 18; + config.oled_rst = -1; + config.tft_rs = 15; + config.tft_cs = 5; + config.screenfile = 4; + config.gps_rxd = 13; + config.gps_txd = -1; // 14 + config.sx1278_ss = 33; + config.sx1278_miso = 38; + config.sx1278_mosi = 23; //MOSI; + config.sx1278_sck = 18; // SCK; + } else { // some t-beam... + config.button_pin = 38; + config.button2_pin = 15 + 128; //T4 + 128; // T4 = GPIO13 + // Maybe in future use as default only PWR as button2? + //config.button2_pin = 255; + config.button2_axp = 1; + config.gps_rxd = 34; + config.gps_txd = 12; + // Check for I2C-Display@21,22 +#define SSD1306_ADDRESS 0x3c + Wire.beginTransmission(SSD1306_ADDRESS); + err = Wire.endTransmission(); + delay(100); // otherwise its too fast?! + Wire.beginTransmission(SSD1306_ADDRESS); + err = Wire.endTransmission(); + if(err!=0 && fingerprint!=17) { // hmm. 17 after powerup with oled commected and no i2c answer!?!? fingerprint |= 128; Serial.println("no I2C display found, assuming large TFT display\n"); // CS=0, RST=14, RS=2, SDA=4, CLK=13 @@ -141,10 +169,11 @@ void Sonde::defaultConfig() { config.tft_cs = 0; config.spectrum = -1; // no spectrum for now on large display config.screenfile = 2; - } else { + } else { // OLED display, pins 21,22 ok... config.disptype = 0; Serial.println("... with small OLED display\n"); + } } } else { Serial.println("Autoconfig: looks like T-Beam v0.7 board"); @@ -270,6 +299,14 @@ void Sonde::setConfig(const char *cfg) { config.gps_rxd = atoi(val); } else if(strcmp(cfg,"gps_txd")==0) { config.gps_txd = atoi(val); + } else if(strcmp(cfg,"sx1278_ss")==0) { + config.sx1278_ss = atoi(val); + } else if(strcmp(cfg,"sx1278_miso")==0) { + config.sx1278_miso = atoi(val); + } else if(strcmp(cfg,"sx1278_mosi")==0) { + config.sx1278_mosi = atoi(val); + } else if(strcmp(cfg,"sx1278_sck")==0) { + config.sx1278_sck = atoi(val); } else if(strcmp(cfg,"maxsonde")==0) { config.maxsonde = atoi(val); if(config.maxsonde>MAXSONDE) config.maxsonde=MAXSONDE; diff --git a/libraries/SondeLib/Sonde.h b/libraries/SondeLib/Sonde.h index 4e04e4e..42f2e6e 100644 --- a/libraries/SondeLib/Sonde.h +++ b/libraries/SondeLib/Sonde.h @@ -194,7 +194,11 @@ struct st_sondehub { char email[64]; }; +// to be extended +enum { TYPE_TTGO, TYPE_M5_CORE2 }; + typedef struct st_rdzconfig { + int type; // autodetected type, TTGO or M5_CORE2 // hardware configuration int button_pin; // PIN port number menu button (+128 for touch mode) int button2_pin; // PIN port number menu button (+128 for touch mode) @@ -212,6 +216,10 @@ typedef struct st_rdzconfig { int tft_spifreq; // SPI transfer speed (default 40M is out of spec for some TFT) int gps_rxd; // GPS module RXD pin. We expect 9600 baud NMEA data. int gps_txd; // GPS module TXD pin + int sx1278_ss; // SPI slave select for sx1278 + int sx1278_miso; // SPI MISO for sx1278 + int sx1278_mosi; // SPI MOSI for sx1278 + int sx1278_sck; // SPI SCK for sx1278 // software configuration int debug; // show port and config options after reboot int wifi; // connect to known WLAN 0=skip diff --git a/libraries/SondeLib/autodetect-infos b/libraries/SondeLib/autodetect-infos index fe23d05..d93da7f 100644 --- a/libraries/SondeLib/autodetect-infos +++ b/libraries/SondeLib/autodetect-infos @@ -34,6 +34,16 @@ TTGO T-Team 1.0 with IL9225 TFT => fingerprint 23 0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:1 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 0:1 1:1 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:1 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup) +M5Stack Core2 +0:1 1:0 2:0 3:1 4:1 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:1 14:1 15:1 16:1 17:0 18:1 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 +0:1 1:1 2:0 3:1 4:1 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:1 14:1 15:1 16:1 17:0 18:1 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup) +Board fingerprint is 87 (nach power on) +0:1 1:0 2:0 3:1 4:0 5:0 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:0 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:0 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 +0:1 1:1 2:0 3:1 4:0 5:0 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:0 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:0 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup) +Board fingerprint is 22 (nach reset) +Autoconfig: looks like T-Beam 1.0 board + + vs 0010111 T-Beam 1.1 seems to be different: 1110111 GPIO4 = 1 (additional pullup) => +64