diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index 71634ec..b29c2b0 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -21,6 +21,7 @@ #include "geteph.h" #include "rs92gps.h" #include "mqtt.h" +#include "esp_heap_caps.h" int e; @@ -188,9 +189,8 @@ void setupChannelList() { type = STYPE_M10; } else if (space[1] == '2') { - type = STYPE_M10; + type = STYPE_M20; } - else continue; int active = space[3] == '+' ? 1 : 0; if (space[4] == ' ') { @@ -218,6 +218,7 @@ const char *createQRGForm() { "stypes.set('6', 'DFM6 (old)');" "stypes.set('D', 'DFM');" "stypes.set('M', 'M10');" + "stypes.set('2', 'M20');" "function prep() {" " var stlist=document.querySelectorAll(\"input.stype\");" " for(txt of stlist){" @@ -291,7 +292,6 @@ const char *handleQRGPost(AsyncWebServerRequest *request) { const char *tstr = tstring.c_str(); const char *sstr = sstring.c_str(); Serial.printf("Processing a=%s, f=%s, t=%s, site=%s\n", active ? "YES" : "NO", fstr, tstr, sstr); - //char typech = (tstr[2] == '4' ? '4' : tstr[2] == '9' ? 'R' : tstr[0] == 'M' ? 'M' : tstr[3] == ' ' ? 'D' : tstr[3]); // a bit ugly char typech = tstr[0]; file.printf("%3.3f %c %c %s\n", atof(fstr), typech, active ? '+' : '-', sstr); } @@ -1418,6 +1418,18 @@ int scanI2Cdevice(void) extern int initlevels[40]; + +#ifdef ESP_MEM_DEBUG +typedef void (*esp_alloc_failed_hook_t) (size_t size, uint32_t caps, const char * function_name); +extern esp_err_t heap_caps_register_failed_alloc_callback(esp_alloc_failed_hook_t callback); + +void heap_caps_alloc_failed_hook(size_t requested_size, uint32_t caps, const char *function_name) +{ + printf("%s was called but failed to allocate %d bytes with 0x%X capabilities. \n",function_name, requested_size, caps); +} +#endif + + void setup() { char buf[12]; @@ -1428,6 +1440,9 @@ void setup() Serial.printf("%d:%d ", i, v); } Serial.println(""); +#ifdef ESP_MEM_DEBUG + esp_err_t error = heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook); +#endif axpSemaphore = xSemaphoreCreateBinary(); xSemaphoreGive(axpSemaphore); @@ -2410,6 +2425,7 @@ void execOTA() { + void loop() { Serial.printf("\nRunning main loop in state %d [currentDisp:%d, lastDiso:%d]. free heap: %d;\n", mainState, currentDisplay, lastDisplay, ESP.getFreeHeap()); diff --git a/RX_FSK/data/index.html b/RX_FSK/data/index.html index 748c958..24d4c08 100644 --- a/RX_FSK/data/index.html +++ b/RX_FSK/data/index.html @@ -58,9 +58,9 @@

About

%VERSION_NAME%
- Copyright © 2019 by Hansi Reiser, DL9RDZ
+ Copyright © 2019-2020 by Hansi Reiser, DL9RDZ
(version %VERSION_ID%)

- with contributions by Meinhard Guenther, DL2MF, + with contributions by Vigor and Xavier (M20 support), Meinhard Guenther, DL2MF, Johannes, Robert Stefanowicz, Josema, and probably some more people I forgot to mention here. diff --git a/RX_FSK/version.h b/RX_FSK/version.h index c7814fd..5ca8ea6 100644 --- a/RX_FSK/version.h +++ b/RX_FSK/version.h @@ -1,4 +1,4 @@ const char *version_name = "rdzTTGOsonde"; -const char *version_id = "devel20201227"; +const char *version_id = "devel20201227m"; const int SPIFFS_MAJOR=2; const int SPIFFS_MINOR=6; diff --git a/libraries/SondeLib/M20.cpp b/libraries/SondeLib/M20.cpp new file mode 100644 index 0000000..03cf767 --- /dev/null +++ b/libraries/SondeLib/M20.cpp @@ -0,0 +1,534 @@ + +/* M20 decoder functions */ +#include "M20.h" +#include "SX1278FSK.h" +#include "rsc.h" +#include "Sonde.h" +#include + +// well... +//#include "rs92gps.h" + +#define M20_DEBUG 1 + +#if M20_DEBUG +#define M20_DBG(x) x +#else +#define M20_DBG(x) +#endif + +static byte data1[512]; +static byte *dataptr=data1; + +static uint8_t rxbitc; +static uint16_t rxbyte; +static int rxp=0; +static int haveNewFrame = 0; +static int lastFrame = 0; +static int headerDetected = 0; + +int M20::setup(float frequency) +{ +#if M20_DEBUG + Serial.println("Setup sx1278 for M20 sonde"); +#endif + //if(!initialized) { + //Gencrctab(); + //initrsc(); + // not here for now.... get_eph("/brdc.19n"); + // initialized = true; + //} + + if(sx1278.ON()!=0) { + M20_DBG(Serial.println("Setting SX1278 power on FAILED")); + return 1; + } + if(sx1278.setFSK()!=0) { + M20_DBG(Serial.println("Setting FSJ mode FAILED")); + return 1; + } + if(sx1278.setBitrate(9600)!=0) { + M20_DBG(Serial.println("Setting bitrate 9600bit/s FAILED")); + return 1; + } +#if M20_DEBUG + float br = sx1278.getBitrate(); + Serial.print("Exact bitrate is "); + Serial.println(br); +#endif + if(sx1278.setAFCBandwidth(sonde.config.rs92.rxbw)!=0) { + M20_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.rs92.rxbw)); + return 1; + } + if(sx1278.setRxBandwidth(sonde.config.rs92.rxbw)!=0) { + M20_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.rs92.rxbw)); + return 1; + } + + // Enable auto-AFC, auto-AGC, RX Trigger by preamble + if(sx1278.setRxConf(0x1E)!=0) { + M20_DBG(Serial.println("Setting RX Config FAILED")); + return 1; + } + // Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte + //char header[] = "0110.0101 0110.0110 1010.0101 1010.1010"; + + //const char *SYNC="\x10\xB6\xCA\x11\x22\x96\x12\xF8"; + //const char *SYNC="\x08\x6D\x53\x88\x44\x69\x48\x1F"; + // was 0x57 + //const char *SYNC="\x99\x9A"; +#if 1 + // version 1, working with continuous RX + //const char *SYNC="\x66\x65"; + const char *SYNC="\x99\x99\x4C\x99"; + if(sx1278.setSyncConf(0x70, 4, (const uint8_t *)SYNC)!=0) { + M20_DBG(Serial.println("Setting SYNC Config FAILED")); + return 1; + } + //if(sx1278.setPreambleDetect(0xA8)!=0) { + if(sx1278.setPreambleDetect(0x20)!=0) { + M20_DBG(Serial.println("Setting PreambleDetect FAILED")); + return 1; + } +#endif +#if 0 + // version 2, with per-packet rx start, untested + // header is 2a 10 65, i.e. with lsb first + // 0 0101 0100 1 0 0000 1000 1 0 1010 0110 1 + // 10 10011001 10011010 01 10 10101010 01101010 01 10 01100110 10010110 01 + // preamble 0x6A 0x66 0x6A + // i.e. preamble detector on (0x80), preamble detector size 1 (0x00), preample chip errors??? (0x0A) + // after 2a2a2a2a2a1065 + if(sx1278.setPreambleDetect(0xA8)!=0) { + M20_DBG(Serial.println("Setting PreambleDetect FAILED")); + return 1; + } + // sync config: ato restart (01), preamble polarity AA (0), sync on (1), resevered (0), syncsize 2+1 (010) => 0x52 + const char *SYNC="\x6A\x66\x69"; + if(sx1278.setSyncConf(0x52, 3, (const uint8_t *)SYNC)!=0) { + M20_DBG(Serial.println("Setting SYNC Config FAILED")); + return 1; + } + // payload length is ((240 - 7)*10 +6)/8 = 292 +#endif + + // Packet config 1: fixed len, no mancecer, no crc, no address filter + // Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0) + if(sx1278.setPacketConfig(0x08, 0x40)!=0) { + M20_DBG(Serial.println("Setting Packet config FAILED")); + return 1; + } + + Serial.print("M20: setting RX frequency to "); + Serial.println(frequency); + int res = sx1278.setFrequency(frequency); + // enable RX + sx1278.setPayloadLength(0); // infinite for now... + //sx1278.setPayloadLength(292); + sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE); + +#if M20_DEBUG + M20_DBG(Serial.println("Setting SX1278 config for M20 finished\n"); Serial.println()); +#endif + return res; +} + +#if 0 +int M20::setFrequency(float frequency) { + Serial.print("M20: setting RX frequency to "); + Serial.println(frequency); + int res = sx1278.setFrequency(frequency); + // enable RX + sx1278.setPayloadLength(0); // infinite for now... + + sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE); + return res; +} +#endif + +#if 0 +uint32_t M20::bits2val(const uint8_t *bits, int len) { + uint32_t val = 0; + for (int j = 0; j < len; j++) { + val |= (bits[j] << (len-1-j)); + } + return val; +} +#endif + +M20::M20() { +} + +#define M20_FRAMELEN 70 +#define M20_CRCPOS 68 + +void M20::printRaw(uint8_t *data, int len) +{ + char buf[3]; + int i; + for(i=0; i> 1) | ((b & 1) << 7); + b ^= (b >> 2) & 0xFF; + // A1 + t6 = ( c & 1) ^ ((c >> 2) & 1) ^ ((c >> 4) & 1); + t7 = ((c >> 1) & 1) ^ ((c >> 3) & 1) ^ ((c >> 5) & 1); + t = (c & 0x3F) | (t6 << 6) | (t7 << 7); + // A2 + s = (c >> 7) & 0xFF; + s ^= (s >> 2) & 0xFF; + c0 = b ^ t ^ s; + return ((c1 << 8) | c0) & 0xFFFF; +} +static bool checkM20crc(uint8_t *msg) { + int i, cs, cs1; + cs = 0; + for (i = 0; i < M20_CRCPOS; i++) { + cs = update_checkM20(cs, msg[i]); + } + cs = cs & 0xFFFF; + cs1 = (msg[M20_CRCPOS] << 8) | msg[M20_CRCPOS+1]; + return (cs1 == cs); +} + +typedef uint32_t SET256[8]; +static SET256 sondeudp_VARSET = {0x03BBBBF0UL,0x80600000UL,0x06A001A0UL, + 0x0000001CUL,0x00000000UL,0x00000000UL,0x00000000UL, + 0x00000000UL}; +// VARSET=SET256{4..9,11..13,15..17,19..21,23..25,53..54,63,69,71,72,85,87,89,90,98..100}; + +static uint8_t fixcnt[M20_FRAMELEN]; +static uint8_t fixbytes[M20_FRAMELEN]; + +static int32_t getint32(uint8_t *data) { + return (int32_t)( data[3]|(data[2]<<8)|(data[1]<<16)|(data[0]<<24) ); +} + +static int32_t getint24(uint8_t *data) { + return (int32_t)(data[2]|(data[1]<<8)|(data[0]<<16) ); +} + +static int16_t getint16(uint8_t *data) { + return (int16_t)(data[1]|((uint16_t)data[0]<<8)); +} + +static int16_t getint16_r(uint8_t *data) { + return (int16_t)(((uint16_t)data[1]<<8) |data[0]); +} + +static char dez(uint8_t nr) { + nr = nr%10; + return '0'+nr; +} +static char hex(uint8_t nr) { + nr = nr&0x0f; + if(nr<10) return '0'+nr; + else return 'A'+nr-10; +} +const static float DEGMUL = 1.0/0xB60B60; +#define VMUL 0.005 +#define VMUL_M20 0.01 +#ifndef PI +#define PI (3.1415926535897932384626433832795) +#endif +#define RAD (PI/180) + + +// ret: 1=frame ok; 2=frame with errors; 0=ignored frame (m20dop-alternativ) +int M20::decodeframeM20(uint8_t *data) { + int repairstep = 16; + int repl = 0; + bool crcok; + // error correction, inspired by oe5dxl's sondeudp + do { + crcok = checkM20crc(data); + if(crcok) break; + repl = 0; + for(int i=0; i=repairstep) ) { + repl++; + data[i] = fixbytes[i]; + } + } + repairstep >>= 1; + } while(repairstep>0); + if(crcok) { + for(int i=0; iser, ids, 10); + sonde.si()->validID = true; + //Serial.printf("ID is %s [%02x %02x %d]\n", ids, data[95], data[93], id); + // ID printed on sonde is ...-.-abbbb, with a=id>>13, bbbb=id&0x1fff in decimal + // position data + // 0x1C 4 byte + sonde.si()->lat = getint32(data+28) * 1e-6; + //0x20 4 byte + sonde.si()->lon = getint32(data+32) * 1e-6; + //0x08 3 byte + sonde.si()->alt = getint24(data+8) * VMUL_M20; + //0x0B 2 byte + //VMUL_M20 specific + float ve = getint16(data+11)*VMUL_M20; + //0x0D 2 byte + float vn = getint16(data+13)*VMUL_M20; + //0x18 2 byte + sonde.si()->vs = getint16(data+24) * VMUL_M20; + sonde.si()->hs = sqrt(ve*ve+vn*vn); + float dir = atan2(vn, ve)*(1.0/RAD); + if(dir<0) dir+=360; + sonde.si()->dir = dir; + sonde.si()->validPos = 0x3f; + + //0x0F 3 byte + uint32_t tow = getint24(data+15); + uint16_t week = getint16(data+26); + sonde.si()->time = (tow+week*604800+315964800)-18; + + sonde.si()->validTime = true; + } else { + Serial.printf("data is %02x %02x %02x\n", data[0], data[1], data[2]); + return 0; + } + return crcok?1:2; +} + +static uint32_t rxdata; +static bool rxsearching=true; + +// search for +// //101001100110011010011010011001100110100110101010100110101001 +// //1010011001100110100110100110 0110.0110 1001.1010 1010.1001 1010.1001 => 0x669AA9A9 +void M20::processM20data(uint8_t dt) +{ + for(int i=0; i<8; i++) { + uint8_t d = (dt&0x80)?1:0; + dt <<= 1; + rxdata = (rxdata<<1) | d; + //uint8_t value = ((rxdata>>1)^rxdata)&0x01; + //if((rxbitc&1)==1) { rxbyte = (rxbyte>>1) + ((value)<<8); } // mancester decoded data + //rxbyte = (rxbyte>>1) | (d<<8); + if( (rxbitc&1)==0 ) { + // "bit1" + rxbyte = (rxbyte<<1) | d; + } else { + // "bit2" ==> 01 or 10 => 1, otherweise => 0 + rxbyte = rxbyte ^ d; + } + // + if(rxsearching) { + if( rxdata == 0xcccca64c || rxdata == 0x333359b3 ) { + rxsearching = false; + rxbitc = 0; + rxp = 0; +#if 1 + int rssi=sx1278.getRSSI(); + int fei=sx1278.getFEI(); + int afc=sx1278.getAFC(); + Serial.print("Test: RSSI="); Serial.print(rssi); + Serial.print(" FEI="); Serial.print(fei); + Serial.print(" AFC="); Serial.println(afc); + sonde.si()->rssi = rssi; + sonde.si()->afc = afc; +#endif + } + } else { + rxbitc = (rxbitc+1)%16; // 16; + if(rxbitc == 0) { // got 8 data bit + //Serial.printf("%03x ",rxbyte); + dataptr[rxp++] = rxbyte&0xff; // (rxbyte>>1)&0xff; +#if 0 + if(rxp==7 && dataptr[6] != 0x65) { + Serial.printf("wrong start: %02x\n",dataptr[6]); + rxsearching = true; + } +#endif + if(rxp>=M20_FRAMELEN) { + rxsearching = true; + haveNewFrame = decodeframeM20(dataptr); + } + } + } + } +} + +int M20::receive() { + unsigned long t0 = millis(); + Serial.printf("M20::receive() start at %ld\n",t0); + while( millis() - t0 < 1000 ) { + uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2); + if ( bitRead(value, 7) ) { + Serial.println("FIFO full"); + } + if ( bitRead(value, 4) ) { + Serial.println("FIFO overflow"); + } + if ( bitRead(value, 2) == 1 ) { + Serial.println("FIFO: ready()"); + sx1278.clearIRQFlags(); + } + if(bitRead(value, 6) == 0) { // while FIFO not empty + byte data = sx1278.readRegister(REG_FIFO); + //Serial.printf("%02x",data); + processM20data(data); + value = sx1278.readRegister(REG_IRQ_FLAGS2); + } else { + if(headerDetected) { + t0 = millis(); // restart timer... don't time out if header detected... + headerDetected = 0; + } + if(haveNewFrame) { + Serial.printf("M20::receive(): new frame complete after %ldms\n", millis()-t0); + printRaw(dataptr, M20_FRAMELEN); + int retval = haveNewFrame==1 ? RX_OK: RX_ERROR; + haveNewFrame = 0; + return retval; + } + delay(2); + } + } + Serial.printf("M20::receive() timed out\n"); + return RX_TIMEOUT; // TODO RX_OK; +} + +#define M20MAXLEN (240) +int M20::waitRXcomplete() { + // called after complete... +#if 0 + Serial.printf("decoding frame %d\n", lastFrame); + print_frame(lastFrame==1?data1:data2, 240); + SondeInfo *si = sonde.sondeList+rxtask.receiveSonde; + si->lat = gpx.lat; + si->lon = gpx.lon; + si->alt = gpx.alt; + si->vs = gpx.vU; + si->hs = gpx.vH; + si->dir = gpx.vD; + si->validPos = 0x3f; + memcpy(si->id, gpx.id, 9); + si->validID = true; + + int res=0; + uint32_t t0 = millis(); + while( rxtask.receiveResult == 0xFFFF && millis()-t0 < 2000) { delay(20); } + + if( rxtask.receiveResult<0 || rxtask.receiveResult==RX_TIMEOUT) { + res = RX_TIMEOUT; + } else if ( rxtask.receiveResult==0) { + res = RX_OK; + } else { + res = RX_ERROR; + } + rxtask.receiveResult = 0xFFFF; + Serial.printf("M20::waitRXcomplete returning %d (%s)\n", res, RXstr[res]); + return res; +#endif + return 0; +} + + +#if 0 +int oldwaitRXcomplete() { + Serial.println("M20: receive frame...\n"); + sx1278receiveData = true; + delay(6000); // done in other task.... + //sx1278receiveData = false; +#if 0 + //sx1278.setPayloadLength(518-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header) + //sx1278.setPayloadLength(0); // infinite for now... + +////// test code for continuous reception + // sx1278.receive(); /// active FSK RX mode -- already done above... + uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2); + unsigned long previous = millis(); + + byte ready=0; + uint32_t wait = 8000; + // while not yet done or FIFO not yet empty + // bit 6: FIFO Empty + // bit 2 payload ready + int by=0; + while( (!ready || bitRead(value,6)==0) && (millis() - previous < wait) ) + { + if( bitRead(value, 7) ) { Serial.println("FIFO full"); } + if( bitRead(value, 4) ) { Serial.println("FIFO overflow"); } + if( bitRead(value,2)==1 ) ready=1; + if( bitRead(value, 6) == 0 ) { // FIFO not empty + byte data = sx1278.readRegister(REG_FIFO); + process8N1data(data); + by++; +#if 0 + if(di==1) { + int rssi=getRSSI(); + int fei=getFEI(); + int afc=getAFC(); + Serial.print("Test: RSSI="); Serial.println(rssi); + Serial.print("Test: FEI="); Serial.println(fei); + Serial.print("Test: AFC="); Serial.println(afc); + sonde.si()->rssi = rssi; + sonde.si()->afc = afc; + } + if(di>520) { + // TODO + Serial.println("TOO MUCH DATA"); + break; + } + previous = millis(); // reset timeout after receiving data +#endif + } + value = sx1278.readRegister(REG_IRQ_FLAGS2); + } + Serial.printf("processed %d bytes before end/timeout\n", by); +#endif + + + +///// +#if 0 + int e = sx1278.receivePacketTimeout(1000, data+8); + if(e) { Serial.println("TIMEOUT"); return RX_TIMEOUT; } //if timeout... return 1 + + printRaw(data, M20MAXLEN); + //for(int i=0; i +#include +#include +#ifndef inttypes_h + #include +#endif + +#if 0 +struct CONTEXTR9 { + char calibdata[512]; + uint32_t calibok; + char mesok; + char posok; + char framesent; + double lat; + double lon; + double heig; + double speed; + double dir; + double climb; + double lastlat; + double laslong; + double lastalt; + double lastspeed; + double lastdir; + double lastclb; + float hrmsc; + float vrmsc; + double hp; + double hyg; + double temp; + double ozontemp; + double ozon; + uint32_t goodsats; + uint32_t timems; + uint32_t framenum; +}; +#endif + +/* Main class */ +class M20 +{ +private: + void printRaw(uint8_t *data, int len); + void processM20data(uint8_t data); + int decodeframeM20(uint8_t *data); +#if 0 + void stobyte92(uint8_t byte); + void dogps(const uint8_t *sf, int sf_len, + struct CONTEXTR9 * cont, uint32_t * timems, + uint32_t * gpstime); + uint32_t bits2val(const uint8_t *bits, int len); + int bitsToBytes(uint8_t *bits, uint8_t *bytes, int len); + int decode92(byte *data, int maxlen); + + uint8_t hamming_conf[ 7*8]; // 7*8=56 + uint8_t hamming_dat1[13*8]; // 13*8=104 + uint8_t hamming_dat2[13*8]; + + uint8_t block_conf[ 7*4]; // 7*4=28 + uint8_t block_dat1[13*4]; // 13*4=52 + uint8_t block_dat2[13*4]; + + uint8_t H[4][8] = // extended Hamming(8,4) particy check matrix + {{ 0, 1, 1, 1, 1, 0, 0, 0}, + { 1, 0, 1, 1, 0, 1, 0, 0}, + { 1, 1, 0, 1, 0, 0, 1, 0}, + { 1, 1, 1, 0, 0, 0, 0, 1}}; + uint8_t He[8] = { 0x7, 0xB, 0xD, 0xE, 0x8, 0x4, 0x2, 0x1}; // Spalten von H: + // 1-bit-error-Syndrome + boolean initialized = false; +#endif + +public: + M20(); + int setup(float frequency); + int receive(); + int waitRXcomplete(); + + //int use_ecc = 1; +}; + +extern M20 m20; + +#endif diff --git a/libraries/SondeLib/Sonde.cpp b/libraries/SondeLib/Sonde.cpp index 0343b44..0225610 100644 --- a/libraries/SondeLib/Sonde.cpp +++ b/libraries/SondeLib/Sonde.cpp @@ -6,6 +6,7 @@ #include "RS92.h" #include "DFM.h" #include "M10.h" +#include "M20.h" #include "SX1278FSK.h" #include "Display.h" #include @@ -438,9 +439,11 @@ void Sonde::setup() { rs92.setup( sondeList[rxtask.currentSonde].freq * 1000000); break; case STYPE_M10: - case STYPE_M20: m10.setup( sondeList[rxtask.currentSonde].freq * 1000000); break; + case STYPE_M20: + m20.setup( sondeList[rxtask.currentSonde].freq * 1000000); + break; } // debug float afcbw = sx1278.getAFCBandwidth(); @@ -464,9 +467,11 @@ void Sonde::receive() { res = rs92.receive(); break; case STYPE_M10: - case STYPE_M20: res = m10.receive(); break; + case STYPE_M20: + res = m20.receive(); + break; case STYPE_DFM06_OLD: case STYPE_DFM09_OLD: case STYPE_DFM: @@ -556,9 +561,11 @@ rxloop: rs92.waitRXcomplete(); break; case STYPE_M10: - case STYPE_M20: m10.waitRXcomplete(); break; + case STYPE_M20: + m20.waitRXcomplete(); + break; case STYPE_DFM06_OLD: case STYPE_DFM09_OLD: case STYPE_DFM: diff --git a/platformio.ini b/platformio.ini index a1def48..9fdd1cc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,7 +27,8 @@ lib_deps_external = https://github.com/dx168b/async-mqtt-client [env:ttgo-lora32] -platform = espressif32 +;platform = espressif32 +platform = https://github.com/platformio/platform-espressif32.git board = ttgo-lora32-v1 framework = arduino monitor_speed = 115200