#include "microOneWire.h" #include "LittleFS.h" // LittleFS is declared #ifdef USE_TINYUSB #include "Adafruit_TinyUSB.h" #endif #include #include //#define Adafruit_USBD_CDC HardwareSerial extern "C" void Debugprintf(const char * format, ...); int OWPIN = 15; // Pin for DS1904 RTC int PWRPIN = 12; // Clipper LTE Power Pin int RESETPIN = 13; // Clipper LTE Reset Pin int useDS1904 = 0; int useClipper = 0; int RTC = 0; // RTC Equates #define RTCNone 0 #define RTCDS1904 1 #define RTCLTE 2 char APN[64] = "data.uk"; char LTEUser[64] = "user"; char LTEPassword[64] = "one2one"; char LTESerialPort[32] = "Serial6:115200"; HardwareSerial * LTESerial = nullptr; char SMSDEST[16] = "07760401072"; char ConnectOut1[] = "skig.g8bpq.net:8015"; char ConnectOut2[] = "Skig.g8bpq.net:8015"; int SMSEvents = 0; // Equates for SMSEvent bits #define SMSonRestart 1 #define SMSonCallFailed 2 #ifdef USE_TINYUSB Adafruit_USBD_CDC USBSer1; Adafruit_USBD_CDC USBSer2; #endif // Serial Port Support // Serial1 (0,1) SerialPIO (2,3 4,5 6,7) Serial2 (8,9) SerialPIO (10,11) SerialPIO * Serial3 = nullptr; SerialPIO * Serial4 = nullptr; SerialPIO * Serial5 = nullptr; SerialPIO * Serial6 = nullptr; extern "C" void BPQTimerLoop(); extern "C" void BPQInit(); extern "C" void BPQFastPoll(); extern "C" char * strlop(char * buf, char delim); extern "C" void ConTermInput(int n, char *Msg); extern "C" char * stristr (char *ch1, char *ch2); extern "C" char NODECALLLOPPED[10]; File cfg; const char dummyConfig[] = "SIMPLE\r\n" "NODECALL=DUMMY\r\n" "NODEALIAS=RP2040\r\n" "PASSWORD=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n" "" "PORT\r\n" "TYPE=INTERNAL\r\n" "ENDPORT\r\n" "" "PORT\r\n" "TYPE=ASYNC\r\n" "ID=KISS Serial1\r\n" "COMPORT=Serial1\r\n" "SPEED=57600\r\n" "QUALITY=0\r\n" "ENDPORT\r\n"; const char boardConfig[] = "{\n" " \"NodeCall\": \"GE8BPQ\",\n" " \"ConTerms\": [\"Serial\", \"Serial1\", \"Serial6\"],\n" " \"Dial\": {\n" " \"SerialPort\": \"Serial6:115200\",\n" " \"APN\": \"data.uk\",\n" " \"User\": \"user\",\n" " \"Password\": \"one2one\",\n" " \"SMSDEST\": \"07760401072\",\n" " \"SMSEvents\": [\"Restart\", \"CallFailed\"],\n" " \"Callback1\": \"nottm.g8bpq.net:8015\",\n" " \"Type\": {\"Board\": \"Clipper\", \"PWRPIN\": 12, \"RESETPIN\": 13}\n" " },\n" " \"RTC\": {\"Chip\": \"DS1904\", \"Pins\": [15]}\n" "}\n"; HardwareSerial * CONSOLE; HardwareSerial * CONTERM[4]; HardwareSerial * DEBUG; char Consolenames[3][32]; int PWRKEY = 12; // Clipper LTE Power Pin int RESET = 13; // Clipper LTE Reset Pin //char apn[] = "iot.1nce.net"; /* For data / GPRS: Name: 1pMobile APN: data.uk User: user Password: one2one */ void getBoardConfig() { File cfg = LittleFS.open("xboard.cfg", "r"); JSONVar myObject = nullptr; if (cfg) { char * Config = (char *)malloc(8192); int n = cfg.read((unsigned char *)Config, 8190); Config[n] = 0; cfg.close(); if (n) myObject = JSON.parse(Config); if (myObject == nullptr) Serial.println("Couldn't parse board.cfg"); } else Serial.println("Couldn't read board.cfg - using default config"); if (myObject == nullptr) myObject = JSON.parse(boardConfig); if (myObject == nullptr) { Serial.println("Couldn't parse default config"); return; } for (int x = 0; x < myObject.keys().length(); x++) { Serial.print(x); Serial.println(myObject.keys()[x]); } // Serial.println(myObject()[0]); // Serial.println(myObject()[1]); // Serial.println(myObject()[2]); if (myObject.hasOwnProperty("NodeCall")) Serial.println((const char*) myObject["NodeCall"]); /* if (myObject.hasOwnProperty("DebugPort")) { char Port[64]; strlcpy(Port, myObject["DebugPort"], 60); DEBUG = CreateSerial(Port); if (DEBUG == nullptr) { Serial.println("Invalid DEBUG port"); DEBUG = &Serial; } } */ if (myObject.hasOwnProperty("Dial")) { JSONVar dialObject = myObject["Dial"]; for (int x = 0; x < dialObject.keys().length(); x++) { Serial.print(x); Serial.println(dialObject.keys()[x]); } if (dialObject.hasOwnProperty("APN")) strlcpy(APN, dialObject["APN"], 63); if (dialObject.hasOwnProperty("User")) strlcpy(LTEUser, dialObject["User"], 63); if (dialObject.hasOwnProperty("Password")) strlcpy(LTEPassword, dialObject["Password"], 63); if (dialObject.hasOwnProperty("SMSDEST")) strlcpy(SMSDEST, dialObject["SMSDEST"], 63); if (dialObject.hasOwnProperty("Callback1")) strlcpy(ConnectOut1, dialObject["Callback1"], 63); if (dialObject.hasOwnProperty("Callback2")) strlcpy(ConnectOut2, dialObject["Callback2"], 63); if (dialObject.hasOwnProperty("SMSEvents")) { JSONVar myArray = dialObject["SMSEvents"]; // \"SMSEvents\": [\"Restart\", \"CallFailed\"],\n" } if (dialObject.hasOwnProperty("Type")) { JSONVar typeObject = dialObject["Type"]; if (typeObject.hasOwnProperty("Board")) { char Board[64]; strlcpy(Board, typeObject["Board"], 60); if (strcmp(Board, "Clipper") == 0) { useClipper = 1; if (typeObject.hasOwnProperty("PWRPIN")) PWRPIN = typeObject["PWRPIN"]; if (typeObject.hasOwnProperty("RESETPIN")) RESETPIN = typeObject["RESETPIN"]; Debugprintf("Board is %s POWERPIN %d RESETPIN %d", Board, PWRPIN, RESETPIN); } } } } if (myObject.hasOwnProperty("ConsoleTerms")) { JSONVar consObject = myObject["ConsoleTerms"]; int c = consObject.length(); if (c > 3) c = 3; for (int n = 0; n < c; n++) { strlcpy(Consolenames[n], consObject[n], 30); } } if (myObject.hasOwnProperty("RTC")) { Serial.printf("Has RTC\n"); JSONVar rtcObject = myObject["RTC"]; for (int x = 0; x < rtcObject.keys().length(); x++) { Serial.print(x); Serial.println(rtcObject.keys()[x]); } if (rtcObject.hasOwnProperty("Chip")) { char Chip[64]; strlcpy(Chip, rtcObject["Chip"], 60); Serial.printf("RTC Chip %s", Chip); if (strcmp(Chip, "DS1904") == 0) { useDS1904 = 1; RTC = RTCDS1904; } } else if (rtcObject.hasOwnProperty("LTE")) RTC = RTCLTE; } Serial.printf("%d %d", RTC, useDS1904); // " \"RTC\": {\"Chip\": \"DS1904\", \"Pins\": [15]}\n" } HardwareSerial * CreateSerial(char * NameVal) { HardwareSerial * Device = nullptr; // If name includes a : then speed is specified char Name[32]; int Speed; strcpy(Name, NameVal); Speed = atoi(strlop(Name, ':')); // Serial, Serial1 and Serial 2 are always defined. Serial is normally a USB device if (strcmp(Name, "Serial") == 0) Device = (HardwareSerial *)&Serial; else if (strcmp(Name, "Serial1") == 0) Device = &Serial1; else if (strcmp(Name, "Serial2") == 0) Device = &Serial2; else if (strcmp(Name, "Serial3") == 0) { if (Serial3 == nullptr) Serial3 = new SerialPIO(2, 3, 256); Device = Serial3; } else if (strcmp(Name, "Serial4") == 0) { if (Serial4 == nullptr) Serial4 = new SerialPIO(4, 5, 256); Device = Serial4; } else if (strcmp(Name, "Serial5") == 0) { if (Serial5 == nullptr) Serial5 = new SerialPIO(6, 7, 256); Device = Serial5; } else if (strcmp(Name, "Serial6") == 0) { if (Serial6 == nullptr) Serial6 = new SerialPIO(10, 11, 256); Device = Serial6; } #ifdef USE_TINYUSB else if (strcmp(Name, "USBSer2") == 0) Device = (HardwareSerial *)&USBSer2; else if (strcmp(Name, "USBSer1") == 0) Device = (HardwareSerial *)&USBSer1; #endif else { DEBUG->printf("Unknown port name %s\n", Name); return 0; } // Device->setTimeout(0); if (Speed) Device->begin(Speed); return Device; } extern "C" void initClipper() { int ret = 0; int n = 10; char Line[512]; char * ptr; if (Serial6 == nullptr) Serial6 = new SerialPIO(10, 11, 256); LTESerial = Serial6; /* if (LTESerialPort[0]) { LTESerial = CreateSerial(LTESerialPort); if (LTESerial == nullptr) { Debugprintf("No Serial Port for LTE Modem"); useClipper = 0; return; } } */ LTESerial->setTimeout(1); LTESerial->begin(115200); pinMode(PWRKEY, OUTPUT); pinMode(RESET, OUTPUT); digitalWrite(RESET, HIGH); digitalWrite(PWRKEY, HIGH); delay(1); digitalWrite(PWRKEY, LOW); delay(1); digitalWrite(PWRKEY, HIGH); CONTERM[2] = LTESerial; // LTE Modem //AT+CGAUTH=1,2,"one2one","user" //AT+CGDCONT=1,"IP","data.uk" LTESerial->println("ATE0\r\n"); // echo off int Len = LTEReadAll(Line, 511, 500); // ATE0 Response if (Len == 0) { Debugprintf("Clipper not reponding - disabling"); useClipper = 0; return; } LTESerial->println("AT+CIPMODE=1"); // TCP Transparent mode Debugprintf("Waiting 1"); LTEReadAll(Line, 511, 1000); // CIP Response LTESerial->println("AT+NETOPEN"); // Need to wait for NETOPEN response Debugprintf("Waiting for NETOPEN Response"); Debugprintf("Waiting 1"); LTEReadAll(Line, 511, 5000); // Get OK from NETOPEN Debugprintf("Waiting 2"); Debugprintf("Checking NETOPEN Response"); if (strstr(Line, "+NETOPEN:")) Debugprintf("NETOPEN Ok"); LTESerial->println("AT+IPADDR"); LTESerial->println("AT+CCLK?"); LTESerial->println("AT+CMGF=1"); // SMS Text Mode LTEReadAll(Line, 511, 5000); if (RTC == RTCLTE) { if (ptr = strstr(Line, "+CCLK:")) { // "24/11/13,17:22:51 time_t tSet; struct tm tm; struct timeval now; ptr += 8; tm.tm_year = 10 * (ptr[0] - '0') + ptr[1] - '0' + 100; tm.tm_mon = 10 * (ptr[3] - '0') + ptr[4] - '0' - 1; tm.tm_mday = 10 * (ptr[6] - '0') + ptr[7] - '0'; tm.tm_hour = 10 * (ptr[9] - '0') + ptr[10] - '0'; tm.tm_min = 10 * (ptr[12] - '0') + ptr[13] - '0'; tm.tm_sec = 10 * (ptr[15] - '0') + ptr[16] - '0'; now.tv_sec = mktime(&tm); now.tv_usec=0; settimeofday(&now, NULL); } } if (ptr = strstr(Line, "+IPADDR:")) { strlop(ptr, 13); Debugprintf("IP Address %s", ptr + 9); } } extern "C" void sendSMS(char * Number, char *text) { char Line[256]; LTESerial->printf("AT+CMGS=\"%s\"\r\n", Number); delay(3000); Debugprintf("%d", LTESerial->read()); delay(100); Debugprintf("%d", LTESerial->read()); delay(100); Debugprintf("%d", LTESerial->read()); delay(100); Debugprintf("%d", LTESerial->read()); // int ret = waitforResponse(Line, 255, 1000); //if (ret) { // Debugprintf("Got %d %s", ret, Line); // if (memcmp(Line, ">\r\n", 3) == 0) LTESerial->printf("%s\r\n%c", text, 26); // ctrl/z on end } } uint32_t DS1904getClock() { uint32_t Time; oneWire_reset(OWPIN); oneWire_write(0xcc, OWPIN); // Skip addr oneWire_write(0x66, OWPIN); // read RTC Time = oneWire_read(OWPIN); // read and discard control byte Time = oneWire_read(OWPIN); Time |= oneWire_read(OWPIN) << 8; Time |= oneWire_read(OWPIN) << 16; Time |= oneWire_read(OWPIN) << 24; return Time; } extern "C" void DS1904setClock(uint32_t Time) { oneWire_reset(OWPIN); oneWire_write(0xcc, OWPIN); // Skip addr oneWire_write(0x99, OWPIN); // write RTC - this is the write code oneWire_write(0xAC, OWPIN); //This is the control byte. AC in hex = 10101100 oneWire_write(Time & 0xff, OWPIN); oneWire_write((Time >> 8) & 0xff, OWPIN); oneWire_write((Time >> 16) & 0xff, OWPIN); oneWire_write((Time >> 24) & 0xff, OWPIN); oneWire_reset(OWPIN); } extern "C" void picocreateDefaultcfg() { cfg = LittleFS.open("bpq32.cfg", "w"); if (cfg) { cfg.write(dummyConfig, strlen(dummyConfig)); cfg.close(); } } extern "C" const char *month[]; extern "C" const char *dat[]; void setup() { // put your setup code here, to run once: LittleFS.begin(); Serial.setTimeout(1); Serial.begin(115200); Serial1.setTimeout(1); Serial1.begin(115200); Serial2.setTimeout(1); Serial2.begin(115200); int n = 10; #ifdef USE_TINYUSB USBSer1.begin(115200); USBSer2.begin(115200); #endif while (!Serial && n--) delay(1000); Serial.println("Starting"); // check to see if multiple CDCs are enabled /* if (CFG_TUD_CDC < 2 ) { Serial.printf("To use multiple USB Serial ports CFG_TUD_CDC must be at least 2, current value is %u\n", CFG_TUD_CDC); Serial.println("Config file is located in arduino15/packages/rp2040/hardware/rp2040/4.2.0/libraries/Adafruit_TinyUSB_Arduino/src/arduino/ports/rp2040/tusb_config_rp2040.h"); Serial.println("Continuing with one usb serial device"); } */ // These may be changed from board.cfg but sensible to set here so Debugprintf works in config processing CONSOLE = (HardwareSerial *)&Serial1; DEBUG = (HardwareSerial *)&Serial1; getBoardConfig(); if (useDS1904) { digitalWrite(OWPIN, LOW); pinMode(OWPIN, INPUT); } if (Consolenames[0][0]) CONTERM[0] = CreateSerial(Consolenames[0]); if (Consolenames[1][0]) CONTERM[1] = CreateSerial(Consolenames[1]); if (Consolenames[2][0]) CONTERM[2] = CreateSerial(Consolenames[2]); // CONTERM[0]->begin(115200); // CONTERM[1]->begin(19200); // CONTERM[2]->begin(115200); rp2040.wdt_begin(8300); struct timeval now; int rc; if (RTC == RTCDS1904) { now.tv_sec=DS1904getClock(); now.tv_usec=0; rc=settimeofday(&now, NULL); time_t szClock = time(NULL); struct tm * TM = gmtime(&szClock); CONSOLE->printf("Time from DS1904 %s, %02d %s %3d %02d:%02d:%02d GMT\n", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon], TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec); } // CONSOLE->printf("Starting. Last reset caused by %d", rp2040. //UNKNOWN_RESET, PWRON_RESET, RUN_PIN_RESET, SOFT_RESET, WDT_RESET, DEBUG_RESET, GLITCH_RESET, BROWNOUT_RESET}; if (useClipper) initClipper(); BPQInit(); CONSOLE->print("Init Complete RTC Time "); CONSOLE->println(DS1904getClock()); // sendSMS("07760401072", "picoNode restarted"); } extern "C" int sendtoDialSession(char * Line, int rxed); int LTEReadAll(char * Line, int len, int mS) { // reads characters until they stop or for mS int start = millis(); int end = millis() + mS; int ret = 0, c; rp2040.wdt_begin(8300); // wait for first while (end > millis()) { if (LTESerial->available()) { // Got first c = LTESerial->read(); Line[ret++] = c; // now read until no more available while(1) { delayMicroseconds(200); rp2040.wdt_begin(8300); BPQFastPoll(); if (LTESerial->available() == 0 || ret >= len) { // no more Line[ret] = 0; return (ret); } c = LTESerial->read(); Line[ret++] = c; } } BPQFastPoll(); } Line[ret] = 0; return 0; } int tcpConnected = 0; int tcpConnecting = 0; extern "C" void tcpCall(char * host, int port) { // AT+CIPOPEN=0,"TCP","NOTTM.g8bpq.net", 8015 LTESerial->printf("AT+CIPOPEN=0,\"TCP\",\"%s\", %d\r\n", host, port); tcpConnecting = 1; } void pollLTE() { // See if any input from LTE modem. if (useClipper == 0) return; char Line[512]; int Len, ret; // if notification of a text message, get message and action // if attached to a terminal (dial command) send to user // if attached to a ConTerm, send to Node. // Otherwise write to debug log if (LTESerial->available()) { ret = LTESerial->readBytesUntil('\n', Line, 511); Line[ret] = 0; if (ret) { // Check for Text Message (+CMT: "+447760401072","","24/11/11,16:38:25+0") if (memcmp(Line, "+CMT: ", 6) == 0) { int ret = LTEReadAll(Line, 511, 500); char host [256]; Debugprintf("SMS %d, %s", ret, Line); // Get host to call if (stristr(Line, (char *)"Callback2")) strcpy(host, ConnectOut2); else strcpy(host, ConnectOut1); Debugprintf("Calling %s", host); char * port = strlop(host, ':'); tcpCall(host, atoi(port)); return; } if (tcpConnecting) { // look for CONNECT 115200 or CONNECT FAIL or ERROR Debugprintf(" look for CONNECT 115200 or CONNECT FAIL"); if (strstr(Line, "CONNECT FAIL")) tcpConnecting = 0; else if (strstr(Line, "CONNECT 115200")) { tcpConnecting = 0; tcpConnected = 1; Debugprintf(NODECALLLOPPED); LTESerial->printf("%s\r", NODECALLLOPPED); } else if (strstr(Line, "ERROR")) { // try reinitialising tcpConnecting = 0; tcpConnected = 0; initClipper(); } } if (tcpConnected) { // send to node command handler ConTermInput(2, Line); return; } if (sendtoDialSession(Line, ret)) return; // Default Debugprintf("%s",Line); } } } uint32_t nextTimerRun = 0; void loop() { rp2040.wdt_begin(8300); delay(1); BPQFastPoll(); // Run every mS to reduce chance of serial port overrun pollLTE(); if (millis() > nextTimerRun) // Runtimer every 100 mS { nextTimerRun = millis() + 100; BPQTimerLoop(); } } extern "C" void OutputDebugString(char * Mess) { DEBUG->print(Mess); } extern "C" int WritetoConsoleLocal(char * buff) { return CONSOLE->printf("%s", buff); } extern "C" void Debugprintf(const char * format, ...) { char Mess[10000]; va_list(arglist); va_start(arglist, format); vsprintf(Mess, format, arglist); strcat(Mess, "\r\n"); OutputDebugString(Mess); return; } extern "C" void Contermprint(int n, char * Msg) { CONTERM[n]->print(Msg); return; } extern "C" uint64_t GetTickCount() { return millis(); } extern "C" void Sleep(int mS) { delay(mS); } char * xxConfig; int n; char * rest; extern "C" char * strlop(char * buf, char delim); extern "C" int cfgOpen(char * FN, char * Mode) { CONSOLE->printf("Open cfg %s\r\n", FN); cfg = LittleFS.open(FN, Mode); if (!cfg) { CONSOLE->println("file open failed"); return 0; } xxConfig = (char *)malloc(8192); int n = cfg.read((unsigned char *)xxConfig, 8190); xxConfig[n] = 0; rest = xxConfig; cfg.close(); return 1; } extern "C" int cfgClose() { free(xxConfig); return 0; } extern "C" int cfgRead(unsigned char * Buffer, int Len) { // return a line at a time if (rest == 0 || rest[0] == 0) return 0; char * p = strlop(rest, '\r'); strcpy((char *)Buffer, rest); int n = strlen(p); rest = p; if (rest && rest[0] == '\n') rest++; return n; } extern "C" int ConTermKbhit(int n) { return CONTERM[n]->read(); } extern "C" int getFreeHeap() { return rp2040.getFreeHeap(); } // Serial Port Support // Serial1 (0,1) SerialPIO (2,3 4,5 6,7) Serial2 (8,9) SerialPIO (10,11) #define maxHandles 8 HardwareSerial * Handles[maxHandles] = {0}; extern "C" int picoOpenSerial(char * Name, int speed) { // Find a free handle int i; for (i = 0; i < maxHandles; i++) { if (Handles[i] == 0) break; } if (i == 8) { DEBUG->println("No Serial Handles free"); return 0; } if (strcmp(Name, "Serial1") == 0) { Serial1.setFIFOSize(256); Handles[i] = &Serial1; } else if (strcmp(Name, "Serial2") == 0) { Serial2.setFIFOSize(256); Handles[i] = &Serial2; } else if (strcmp(Name, "Serial3") == 0) { if (Serial3 == nullptr) Serial3 = new SerialPIO(2, 3, 256); Handles[i] = Serial3; } else if (strcmp(Name, "Serial4") == 0) { if (Serial4 == nullptr) Serial4 = new SerialPIO(4, 5, 256); Handles[i] = Serial4; } else if (strcmp(Name, "Serial5") == 0) { if (Serial5 == nullptr) Serial5 = new SerialPIO(6, 7, 256); Handles[i] = Serial5; } else if (strcmp(Name, "Serial6") == 0) { if (Serial6 == nullptr) Serial6 = new SerialPIO(10, 11, 256); Handles[i] = Serial6; } // else if (strcmp(Name, "USBSer2") == 0) // Handles[i] = &USBSer2; // else if (strcmp(Name, "USBSer3") == 0) // Handles[i] = &USBSer3; else { Debugprintf("Unknown port name %s\n", Name); return 0; } Handles[i]->setTimeout(1); Handles[i]->begin(speed); return i + 1; } extern "C" int picoWriteDial(char * Buffer, int Len) { // Need to check available. Looks like it only returns 1/0 not actual space so have to check for each byte for (int n = 0; n < Len; n++) { while(LTESerial->availableForWrite() == 0) { BPQFastPoll(); } LTESerial->write(Buffer[n]); } return n; } extern "C" int picoCloseSerial(int Chan) { return 0; } extern "C" int picoWriteSerial(int Chan, char * Buffer, int Len) { if (Chan == 0) return 0; Chan--; // Need to check available. Looks like it only returns 1/0 not actual space so have to check for each byte for (int n = 0; n < Len; n++) { while(Handles[Chan]->availableForWrite() == 0) { BPQFastPoll(); } Handles[Chan]->write(Buffer[n]); } return Len; } extern "C" int picoReadSerial(int Chan, char * Buffer, int Len) { int ptr = 0; int ret; if (Chan == 0) return 0; Chan--; if (Handles[Chan]->available() && ptr < Len) { ret = Handles[Chan]->read(); Buffer[ptr++] = ret; } return ptr; } extern "C" int picoSerialGetLine(int Chan, char * Line, int maxline) { int ret = 0; if (Chan == 0) return 0; Chan--; if (Handles[Chan]->available()) { ret = Handles[Chan]->readBytesUntil('\n', Line, maxline); Line[ret] = 0; } return ret; } extern "C" int DoRoutes(); extern "C" int DoNodes(); extern "C" int SaveNodes () { cfg = LittleFS.open("BPQNODES.dat", "w"); if (!cfg) { CONSOLE->println("BPQNODES create failed"); return 0; } DoRoutes(); DoNodes(); cfg.close(); return (0); } extern "C" void writeNodesLine(char * line) { cfg.write(line, strlen(line)); } File root; File file; extern "C" void OpenDirectory() { root = LittleFS.open("/", "r"); file = root.openNextFile(); } extern "C" void GetNextDirEntry(char * line) { if (file) { if (file.isDirectory()) sprintf(line, " DIR : %s", file.name()); else sprintf(line, (" FILE: %s\tSIZE: %d"), file.name(), file.size()); file = root.openNextFile(); return; } line[0] = 0; file.close(); root.close(); return; } extern "C" int picoDelete(char * from) { return LittleFS.remove(from); } extern "C" int picoRename(char * from, char * to) { return LittleFS.rename(from, to); } extern "C" File * picoOpenFile(char * FN) { if(!LittleFS.exists(FN)) return 0; File * file = 0; file = new(File); *file = LittleFS.open(FN, "r"); return file; } extern "C" int picoGetLine(File * file, char * Line, int maxline) { int n = file->readBytesUntil('\n', Line, maxline); Line[n] = 0; return n; } extern "C" void picoCloseFile(File * file) { file->close(); delete file; } extern "C" int GetNextFileBlock(File * file, char * line) { int n = file->read((unsigned char *)line, 128); line[n] = 0; return n; } extern "C" File * picoCreateFile(char * FN) { File * file = 0; file = new(File); *file = LittleFS.open(FN, "w"); return file; } extern "C" void picofprintf(File * file, const char * format, ...) { char Mess[1000]; va_list(arglist); va_start(arglist, format); int n = vsprintf(Mess, format, arglist); file->write(Mess, n); } extern"C" void picoWriteLine(File * file, char * Data, int len) { file->write(Data, len); } extern "C" int createandwritefile(char * FN, char * Data, int len) { File file = LittleFS.open(FN, "w"); if (file) { file.write(Data, len); file.close(); return 1; } return 0; // open failed }