From a083e00d1af43905180d404b1fa80e50067dbffc Mon Sep 17 00:00:00 2001 From: eadmaster <925171+eadmaster@users.noreply.github.com> Date: Sun, 4 Aug 2024 13:51:46 +0200 Subject: [PATCH] refactored serialcmds for reuse in the webui, added ir/subghz tx_from_file cmds (#107) --- platformio.ini | 4 +- src/core/globals.h | 7 +- src/core/serialcmds.cpp | 130 ++++++++++---- src/core/serialcmds.h | 8 +- src/main.cpp | 13 +- src/modules/ir/TV-B-Gone.cpp | 264 ++++++++++------------------ src/modules/ir/TV-B-Gone.h | 2 +- src/modules/others/webInterface.cpp | 23 ++- src/modules/others/webInterface.h | 14 +- src/modules/rf/rf.cpp | 33 ++-- src/modules/rf/rf.h | 1 + 11 files changed, 244 insertions(+), 255 deletions(-) diff --git a/platformio.ini b/platformio.ini index 8bcc2127..885ce9a1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -325,8 +325,8 @@ build_flags = -DSDCARD_SCK=-1 -DSDCARD_MISO=-1 -DSDCARD_MOSI=-1 - ; grove pins - -DGROVE_SDA=-35 + ; grove pins (SDA=default TX pin, SCL=default RX pin) + -DGROVE_SDA=35 -DGROVE_SCL=36 ; tft vars -DROTATION=1 diff --git a/src/core/globals.h b/src/core/globals.h index 921ca253..bf89a31d 100644 --- a/src/core/globals.h +++ b/src/core/globals.h @@ -32,9 +32,10 @@ extern char16_t FGCOLOR; // Declaração dos objetos TFT #if defined(HAS_SCREEN) - extern TFT_eSPI tft; - extern TFT_eSprite sprite; - extern TFT_eSprite draw; + #include + extern TFT_eSPI tft; + extern TFT_eSprite sprite; + extern TFT_eSprite draw; #else #include "VectorDisplay.h" extern SerialDisplayClass tft; diff --git a/src/core/serialcmds.cpp b/src/core/serialcmds.cpp index 6a932ea1..cb2ac379 100644 --- a/src/core/serialcmds.cpp +++ b/src/core/serialcmds.cpp @@ -2,8 +2,7 @@ #include "serialcmds.h" #include "globals.h" #include -#include -#include "modules/ir/TV-B-Gone.h" +//#include #include "cJSON.h" #include // for PRIu64 @@ -17,6 +16,35 @@ #include "display.h" #include "powerSave.h" #include "modules/rf/rf.h" +#include "modules/ir/TV-B-Gone.h" + + +/* task to handle serial commands, currently used in headless mode only */ +#include +#include + +void serialcmds_loop(void* pvParameters) { + Serial.begin (115200); + while (1) { + handleSerialCommands(); + //delay (500); // wait for half a second + vTaskDelay(500); // sleep this task only + } +} + +void startSerialCommandsHandlerTask() { + TaskHandle_t serialcmdsTaskHandle; + + xTaskCreatePinnedToCore ( + serialcmds_loop, // Function to implement the task + "serialcmds", // Name of the task (any string) + 20000, // Stack size in bytes + NULL, // This is a pointer to the parameter that will be passed to the new task. We are not using it here and therefore it is set to NULL. + 0, // Priority of the task + &serialcmdsTaskHandle, // Task handle (optional, can be NULL). + 0 // Core where the task should run. By default, all your Arduino code runs on Core 1 and the Wi-Fi and RF functions (these are usually hidden from the Arduino environment) use the Core 0. + ); +} void SerialPrintHexString(uint64_t val) { @@ -27,7 +55,10 @@ void SerialPrintHexString(uint64_t val) { Serial.println(s); } + void handleSerialCommands() { + // read and process a single command + String cmd_str; /* @@ -54,12 +85,17 @@ void handleSerialCommands() { //log_d(cmd_str.c_str()); cmd_str.trim(); - cmd_str.toLowerCase(); // case-insensitive matching + //cmd_str.toLowerCase(); // case-insensitive matching -> issue with filenames + + processSerialCommand(cmd_str); +} - // TODO: more commands https://docs.flipper.net/development/cli#0Z9fs + +bool processSerialCommand(String cmd_str) { + // return true on success, false on error if(cmd_str == "" ) { // empty - return; + return false; } if(cmd_str.startsWith("ir") ) { @@ -73,22 +109,33 @@ void handleSerialCommands() { String address = cmd_str.substring(10, 10+8); String command = cmd_str.substring(19, 19+8); sendNECCommand(address, command); // TODO: add arg for displayRedStripe optional - return; + return true; } if(cmd_str.startsWith("ir tx rc5 ")){ String address = cmd_str.substring(10, 10+8); String command = cmd_str.substring(19, 19+8); sendRC5Command(address, command); - return; + return true; } if(cmd_str.startsWith("ir tx rc6 ")){ String address = cmd_str.substring(10, 10+8); String command = cmd_str.substring(19, 19+8); sendRC6Command(address, command); - return; + return true; } - // TODO: more protocols: Samsung32, SIRC - //if(cmd_str.startsWith("ir tx raw")){ + // TODO: more protocols: Samsung32, SIRC + + //if(cmd_str.startsWith("ir tx raw")){ + + if(cmd_str.startsWith("ir tx_from_file ")){ + String filepath = cmd_str.substring(strlen("ir tx_from_file "), cmd_str.length()); + if(filepath.indexOf(".ir") == -1) return false; // invalid filename + if(!filepath.startsWith("/")) filepath = "/" + filepath; // add "/" if missing + if (SD.begin()) if (SD.exists(filepath)) return txIrFile(&SD, filepath); + if (LittleFS.begin()) if (LittleFS.exists(filepath)) return txIrFile(&LittleFS, filepath); + // else file not found + return false; + } if(cmd_str.startsWith("irsend")) { // tasmota json command https://tasmota.github.io/docs/Tasmota-IR/#sending-ir-commands @@ -105,7 +152,7 @@ void handleSerialCommands() { cJSON *root = cJSON_Parse(cmd_str.c_str() + 6); if (root == NULL) { Serial.println("This is NOT json format"); - return; + return false; } uint16_t bits = 32; // defaults to 32 bits const char *dataStr = ""; @@ -121,7 +168,7 @@ void handleSerialCommands() { dataStr = dataItem->valuestring; } else { Serial.println("missing or invalid data to send"); - return; + return false; } //String dataStr = cmd_str.substring(36, 36+8); uint64_t data = strtoul(dataStr, nullptr, 16); @@ -135,14 +182,16 @@ void handleSerialCommands() { if(protocolStr == "nec"){ // sendNEC(uint64_t data, uint16_t nbits, uint16_t repeat) irsend.sendNEC(data, bits, 10); + return true; } // TODO: more protocols + return false; } // turn off the led digitalWrite(IrTx, LED_OFF); //backToMenu(); - return; + return false; } // end of ir commands if(cmd_str.startsWith("rf") || cmd_str.startsWith("subghz" )) { @@ -150,12 +199,22 @@ void handleSerialCommands() { pinMode(RfTx, OUTPUT); //Serial.println(RfTx); + if(cmd_str.startsWith("subghz tx_from_file")) { + String filepath = cmd_str.substring(strlen("subghz tx_from_file "), cmd_str.length()); + if(filepath.indexOf(".sub") == -1) return false; // invalid filename + if(!filepath.startsWith("/")) filepath = "/" + filepath; // add "/" if missing + if (SD.begin()) if (SD.exists(filepath)) return txSubFile(&SD, filepath); + if (LittleFS.begin()) if (LittleFS.exists(filepath)) return txSubFile(&LittleFS, filepath); + // else file not found + return false; + } /* WIP: if(cmd_str.startsWith("subghz tx")) { // flipperzero-like cmd https://docs.flipper.net/development/cli/#wLVht // e.g. subghz tx 0000000000200001 868250000 403 10 // https://forum.flipper.net/t/friedland-libra-48249sl-wireless-doorbell-request/4528/20 // {hex_key} {frequency} {te} {count} }*/ + if(cmd_str.startsWith("rfsend")) { // tasmota json command https://tasmota.github.io/docs/Tasmota-IR/#sending-ir-commands // e.g. RfSend {"Data":"0x447503","Bits":24,"Protocol":1,"Pulse":174,"Repeat":10} // on @@ -164,7 +223,7 @@ void handleSerialCommands() { cJSON *root = cJSON_Parse(cmd_str.c_str() + 6); if (root == NULL) { Serial.println("This is NOT json format"); - return; + return false; } unsigned int bits = 32; // defaults to 32 bits const char *dataStr = ""; @@ -187,7 +246,7 @@ void handleSerialCommands() { } else { Serial.println("missing or invalid data to send"); cJSON_Delete(root); - return; + return false; } //String dataStr = cmd_str.substring(36, 36+8); uint64_t data = strtoul(dataStr, nullptr, 16); @@ -198,7 +257,7 @@ void handleSerialCommands() { RCSwitch_send(data, bits, pulse, protocol, repeat); cJSON_Delete(root); - return; + return true; } } // endof rf @@ -231,7 +290,7 @@ void handleSerialCommands() { if(!source) { Serial.print("audio file not found: "); Serial.println(song); - return; + return false; } if(source){ // switch on extension @@ -274,7 +333,7 @@ void handleSerialCommands() { ESP8266SAM *sam = new ESP8266SAM; sam->Say(audioout, cmd_str.c_str() + strlen("tts ")); delete sam; - return; + return true; } if(generator && source && audioout) { @@ -284,15 +343,16 @@ void handleSerialCommands() { if (!generator->loop()) generator->stop(); } delete generator; delete source, delete audioout; - return; + return true; } } // end of music_player - #endif + #endif // HAS_NS4168_SPKR // WIP: record | mic // https://github.com/earlephilhower/ESP8266Audio/issues/70 // https://github.com/earlephilhower/ESP8266Audio/pull/118 +#if defined(HAS_SCREEN) // backlight brightness adjust (range 0-255) https://docs.flipper.net/development/cli/#XQQAI // e.g. "led br 127" if(cmd_str.startsWith("led br ")) { @@ -303,7 +363,7 @@ void handleSerialCommands() { if(value<=0) value=1; if(value>100) value=100; setBrightness(value, false); // false -> do not save - return; + return true; } else if(cmd_str.startsWith("led ")) { // change UI color @@ -312,18 +372,24 @@ void handleSerialCommands() { int r, g, b; if (sscanf(rgbString, "%d %d %d", &r, &g, &b) != 3) { Serial.println("invalid color: " + String(rgbString)); - return; + return false; } if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { Serial.println("invalid color: " + String(rgbString)); - return; + return false; } uint16_t hexColor = tft.color565(r, g, b); // Use the TFT_eSPI function to convert RGB to 16-bit color //Serial.print("converted color:"); //SerialPrintHexString(hexColor); FGCOLOR = hexColor; // change global var, dont save in settings - return; + return true; } + if(cmd_str == "clock" ) { + //esp_timer_stop(screensaver_timer); // disable screensaver while the clock is running + runClockLoop(); + return true; + } +#endif // HAS_SCREEN // power cmds: off, reboot, sleep if(cmd_str == "power off" ) { @@ -337,33 +403,27 @@ void handleSerialCommands() { //ESP.deepSleep(0); esp_deep_sleep_start(); // only wake up via hardware reset #endif - return; + return true; } if(cmd_str == "power reboot" ) { ESP.restart(); - return; + return true; } if(cmd_str == "power sleep" ) { // NOTE: cmd not supported on flipper0 setSleepMode(); //turnOffDisplay(); //esp_timer_stop(screensaver_timer); - return; - } - - if(cmd_str == "clock" ) { - //esp_timer_stop(screensaver_timer); // disable screensaver while the clock is running - runClockLoop(); - return; + return true; } // TODO: "storage" cmd to manage files https://docs.flipper.net/development/cli/#Xgais // TODO: "gpio" cmds https://docs.flipper.net/development/cli/#aqA4b + // TODO: more commands https://docs.flipper.net/development/cli#0Z9fs Serial.println("unsupported serial command: " + cmd_str); - - + return false; } diff --git a/src/core/serialcmds.h b/src/core/serialcmds.h index 8ffcac56..cb5a1481 100644 --- a/src/core/serialcmds.h +++ b/src/core/serialcmds.h @@ -1,3 +1,9 @@ +#include + +void handleSerialCommands(); + +bool processSerialCommand(String cmd_str); + +void startSerialCommandsHandlerTask(); -void handleSerialCommands(); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 40bcc770..ad4a5ff0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -259,7 +259,12 @@ void setup() { init_clock(); if(!LittleFS.begin(true)) { LittleFS.format(), LittleFS.begin();} - + + #if ! defined(HAS_SCREEN) + // start a task to handle serial commands while the webui is running + startSerialCommandsHandlerTask(); + #endif + delay(200); previousMillis = millis(); } @@ -342,20 +347,14 @@ void loop() { #include "modules/others/webInterface.h" void loop() { - Serial.println("alt loop()"); setupSdCard(); getConfigs(); - if(!wifiConnected) { Serial.println("wifiConnect"); wifiConnect("",0,true); // TODO: read mode from settings file } Serial.println("startWebUi"); startWebUi(true); // MEMO: will quit when checkEscPress - - // TODO: start a task for - // handleSerialCommands(); - } #endif diff --git a/src/modules/ir/TV-B-Gone.cpp b/src/modules/ir/TV-B-Gone.cpp index bd7b7956..efda583c 100644 --- a/src/modules/ir/TV-B-Gone.cpp +++ b/src/modules/ir/TV-B-Gone.cpp @@ -252,7 +252,10 @@ struct Codes selectRecentIrMenu() { return(selected_code); } -bool otherIRcodesHeadless(FS *fs, String filepath) { + +bool txIrFile(FS *fs, String filepath) { + // SPAM all codes of the file + int total_codes = 0; String line; @@ -269,8 +272,6 @@ bool otherIRcodesHeadless(FS *fs, String filepath) { } Serial.println("Opened database file."); - // SPAM all codes of the file - // TODO: interactive menu to select a single code bool endingEarly; int codes_sent=0; int frequency = 0; @@ -278,7 +279,9 @@ bool otherIRcodesHeadless(FS *fs, String filepath) { String protocol = ""; String address = ""; String command = ""; + databaseFile.seek(0); // comes back to first position + // count the number of codes to replay while (databaseFile.available()) { line = databaseFile.readStringUntil('\n'); @@ -340,7 +343,7 @@ bool otherIRcodesHeadless(FS *fs, String filepath) { command = line.substring(8); command.trim(); Serial.println("Command: "+command); - } else if (line.indexOf("#") != -1) { + } else if (line.indexOf("#") != -1) { // TODO: also detect EOF if (protocol.startsWith("NEC")) { sendNECCommand(address, command); } else if (protocol.startsWith("RC5")) { @@ -362,14 +365,35 @@ bool otherIRcodesHeadless(FS *fs, String filepath) { } } } - } // end while + // if user is pushing (holding down) TRIGGER button, stop transmission early + if (checkSelPress()) // Pause TV-B-Gone + { + while (checkSelPress()) yield(); + displayRedStripe("Paused", TFT_WHITE, BGCOLOR); + + while (!checkSelPress()){ // If Presses Select again, continues + if(checkEscPress()) { + endingEarly= true; + break; + } + } + while (checkSelPress()){ + yield(); + } + if (endingEarly) break; // Cancels custom IR Spam + displayRedStripe("Running, Wait", TFT_WHITE, FGCOLOR); + } + } // end while file has lines to process databaseFile.close(); + Serial.println("closed"); + Serial.println("EXTRA finished"); + + resetCodesArray(); digitalWrite(IrTx, LED_OFF); return true; } - void otherIRcodes() { resetCodesArray(); int total_codes = 0; @@ -377,6 +401,7 @@ void otherIRcodes() { File databaseFile; FS *fs = NULL; struct Codes selected_code; + options = { {"Recent", [&]() { selected_code = selectRecentIrMenu(); }}, {"LittleFS", [&]() { fs=&LittleFS; }}, @@ -400,11 +425,31 @@ void otherIRcodes() { // no need to proceed, go back } + // select a file to tx filepath = loopSD(*fs, true, "IR"); + if(filepath=="") return; // cancelled + // else + + // select mode + bool mode_cmd=true; + options = { + {"Choose cmd", [&]() { mode_cmd=true; }}, + {"Spam all", [&]() { mode_cmd=false; }}, + }; + delay(200); + loopOptions(options); + delay(200); + + if(mode_cmd == false) { + // Spam all selected + txIrFile(fs, filepath); + return; + } + + // else continue and try to parse the file + databaseFile = fs->open(filepath, FILE_READ); drawMainBorder(); - pinMode(IrTx, OUTPUT); - //digitalWrite(IrTx, LED_ON); if (!databaseFile) { Serial.println("Failed to open database file."); @@ -413,176 +458,49 @@ void otherIRcodes() { return; } Serial.println("Opened database file."); - bool mode_cmd=true; - options = { - {"Choose cmd", [&]() { mode_cmd=true; }}, - {"Spam all", [&]() { mode_cmd=false; }}, - }; - delay(200); - loopOptions(options); - delay(200); - + + pinMode(IrTx, OUTPUT); + //digitalWrite(IrTx, LED_ON); + // Mode to choose and send command by command limitted to 50 commands String line; - - // Mode to choose and send command by command limitted to 50 commands - if(mode_cmd) { - String txt; - while (databaseFile.available() && total_codes<50) { - line = databaseFile.readStringUntil('\n'); - txt=line.substring(line.indexOf(":") + 1); - txt.trim(); - if(line.startsWith("name:")) { codes[total_codes].name = txt; codes[total_codes].filepath = txt + " " + filepath.substring( 1 + filepath.lastIndexOf("/") ) ;} - if(line.startsWith("type:")) codes[total_codes].type = txt; - if(line.startsWith("protocol:")) codes[total_codes].protocol = txt; - if(line.startsWith("address:")) codes[total_codes].address = txt; - if(line.startsWith("frequency:")) codes[total_codes].frequency = txt.toInt(); - //if(line.startsWith("duty_cycle:")) codes[total_codes].duty_cycle = txt.toFloat(); - if(line.startsWith("command:")) { codes[total_codes].command = txt; total_codes++; } - if(line.startsWith("data:")) { codes[total_codes].data = txt; total_codes++; } - } - options = { }; - bool exit = false; - for(int i=0; i<=total_codes; i++) { - if(codes[i].type=="raw") options.push_back({ codes[i].name.c_str(), [=](){ sendRawCommand(codes[i].frequency, codes[i].data); addToRecentCodes(codes[i]); }}); - if(codes[i].protocol.startsWith("NEC")) options.push_back({ codes[i].name.c_str(), [=](){ sendNECCommand(codes[i].address, codes[i].command); addToRecentCodes(codes[i]); }}); - if(codes[i].protocol.startsWith("RC5")) options.push_back({ codes[i].name.c_str(), [=](){ sendRC5Command(codes[i].address, codes[i].command); addToRecentCodes(codes[i]); }}); - if(codes[i].protocol.startsWith("RC6")) options.push_back({ codes[i].name.c_str(), [=](){ sendRC6Command(codes[i].address, codes[i].command); addToRecentCodes(codes[i]); }}); - if(codes[i].protocol.startsWith("Samsung")) options.push_back({ codes[i].name.c_str(), [=](){ sendSamsungCommand(codes[i].address, codes[i].command); addToRecentCodes(codes[i]); }}); - if(codes[i].protocol=="SIRC") options.push_back({ codes[i].name.c_str(), [=](){ sendSonyCommand(codes[i].address, codes[i].command); addToRecentCodes(codes[i]); }}); - } - options.push_back({ "Main Menu" , [&](){ exit=true; }}); - databaseFile.close(); - - digitalWrite(IrTx, LED_OFF); - while (1) { - delay(200); - loopOptions(options); - if(checkEscPress() || exit) break; - delay(200); - } + String txt; + while (databaseFile.available() && total_codes<50) { + line = databaseFile.readStringUntil('\n'); + txt=line.substring(line.indexOf(":") + 1); + txt.trim(); + if(line.startsWith("name:")) { codes[total_codes].name = txt; codes[total_codes].filepath = txt + " " + filepath.substring( 1 + filepath.lastIndexOf("/") ) ;} + if(line.startsWith("type:")) codes[total_codes].type = txt; + if(line.startsWith("protocol:")) codes[total_codes].protocol = txt; + if(line.startsWith("address:")) codes[total_codes].address = txt; + if(line.startsWith("frequency:")) codes[total_codes].frequency = txt.toInt(); + //if(line.startsWith("duty_cycle:")) codes[total_codes].duty_cycle = txt.toFloat(); + if(line.startsWith("command:")) { codes[total_codes].command = txt; total_codes++; } + if(line.startsWith("data:")) { codes[total_codes].data = txt; total_codes++; } } - - - else { // SPAM all codes of the file - bool endingEarly; - int codes_sent=0; - int frequency = 0; - String rawData = ""; - String protocol = ""; - String address = ""; - String command = ""; - databaseFile.seek(0); // comes back to first position - // count the number of codes to replay - while (databaseFile.available()) { - line = databaseFile.readStringUntil('\n'); - if(line.startsWith("type:")) total_codes++; - } - - Serial.printf("\nStarted SPAM all codes with: %d codes", total_codes); - // comes back to first position, beggining of the file - databaseFile.seek(0); - while (databaseFile.available()) { - progressHandler(codes_sent,total_codes); - line = databaseFile.readStringUntil('\n'); - if (line.endsWith("\r")) line.remove(line.length() - 1); - - if (line.startsWith("type:")) { - codes_sent++; - String type = line.substring(5); - type.trim(); - Serial.println("Type: "+type); - if (type == "raw") { - Serial.println("RAW code"); - while (databaseFile.available()) { - line = databaseFile.readStringUntil('\n'); - if (line.endsWith("\r")) line.remove(line.length() - 1); - - if (line.startsWith("frequency:")) { - line = line.substring(10); - line.trim(); - frequency = line.toInt(); - Serial.println("Frequency: " + String(frequency)); - } else if (line.startsWith("data:")) { - rawData = line.substring(5); - rawData.trim(); - Serial.println("RawData: "+rawData); - } else if ((frequency != 0 && rawData != "") || line.startsWith("#")) { - sendRawCommand(frequency, rawData); - rawData = ""; - frequency = 0; - type = ""; - line = ""; - break; - } - } - } else if (type == "parsed") { - Serial.println("PARSED"); - while (databaseFile.available()) { - line = databaseFile.readStringUntil('\n'); - if (line.endsWith("\r")) line.remove(line.length() - 1); - - if (line.startsWith("protocol:")) { - protocol = line.substring(9); - protocol.trim(); - Serial.println("Protocol: "+protocol); - } else if (line.startsWith("address:")) { - address = line.substring(8); - address.trim(); - Serial.println("Address: "+address); - } else if (line.startsWith("command:")) { - command = line.substring(8); - command.trim(); - Serial.println("Command: "+command); - } else if (line.indexOf("#") != -1) { - if (protocol.startsWith("NEC")) { - sendNECCommand(address, command); - } else if (protocol.startsWith("RC5")) { - sendRC5Command(address, command); - } else if (protocol.startsWith("RC6")) { - sendRC6Command(address, command); - } else if (protocol.startsWith("Samsung")) { - sendSamsungCommand(address, command); - } else if (protocol.startsWith("SIRC")) { - sendSonyCommand(address, command); - } - protocol = ""; - address = ""; - command = ""; - type = ""; - line = ""; - break; - } - } - } - } - // if user is pushing (holding down) TRIGGER button, stop transmission early - if (checkSelPress()) // Pause TV-B-Gone - { - while (checkSelPress()) yield(); - displayRedStripe("Paused", TFT_WHITE, BGCOLOR); - - while (!checkSelPress()){ // If Presses Select again, continues - if(checkEscPress()) { - endingEarly= true; - break; - } - } - while (checkSelPress()){ - yield(); - } - if (endingEarly) break; // Cancels custom IR Spam - displayRedStripe("Running, Wait", TFT_WHITE, FGCOLOR); - } - - } - databaseFile.close(); - Serial.println("closed"); - Serial.println("EXTRA finished"); + options = { }; + bool exit = false; + for(int i=0; i<=total_codes; i++) { + if(codes[i].type=="raw") options.push_back({ codes[i].name.c_str(), [=](){ sendRawCommand(codes[i].frequency, codes[i].data); addToRecentCodes(codes[i]); }}); + if(codes[i].protocol.startsWith("NEC")) options.push_back({ codes[i].name.c_str(), [=](){ sendNECCommand(codes[i].address, codes[i].command); addToRecentCodes(codes[i]); }}); + if(codes[i].protocol.startsWith("RC5")) options.push_back({ codes[i].name.c_str(), [=](){ sendRC5Command(codes[i].address, codes[i].command); addToRecentCodes(codes[i]); }}); + if(codes[i].protocol.startsWith("RC6")) options.push_back({ codes[i].name.c_str(), [=](){ sendRC6Command(codes[i].address, codes[i].command); addToRecentCodes(codes[i]); }}); + if(codes[i].protocol.startsWith("Samsung")) options.push_back({ codes[i].name.c_str(), [=](){ sendSamsungCommand(codes[i].address, codes[i].command); addToRecentCodes(codes[i]); }}); + if(codes[i].protocol=="SIRC") options.push_back({ codes[i].name.c_str(), [=](){ sendSonyCommand(codes[i].address, codes[i].command); addToRecentCodes(codes[i]); }}); } - resetCodesArray(); + options.push_back({ "Main Menu" , [&](){ exit=true; }}); + databaseFile.close(); + digitalWrite(IrTx, LED_OFF); -} + + while (1) { + delay(200); + loopOptions(options); + if(checkEscPress() || exit) break; + delay(200); + } +} // end of otherIRcodes + //IR commands void sendNECCommand(String address, String command) { diff --git a/src/modules/ir/TV-B-Gone.h b/src/modules/ir/TV-B-Gone.h index 82df42d5..591a2932 100644 --- a/src/modules/ir/TV-B-Gone.h +++ b/src/modules/ir/TV-B-Gone.h @@ -72,4 +72,4 @@ void sendRC6Command(String address, String command); void sendSamsungCommand(String address, String command); void sendSonyCommand(String address, String command); void otherIRcodes(); -bool otherIRcodesHeadless(FS *fs, String filepath); +bool txIrFile(FS *fs, String filepath); diff --git a/src/modules/others/webInterface.cpp b/src/modules/others/webInterface.cpp index da7ba95e..d1cceb85 100644 --- a/src/modules/others/webInterface.cpp +++ b/src/modules/others/webInterface.cpp @@ -3,7 +3,7 @@ #include "core/wifi_common.h" // using common wifisetup #include "core/mykeyboard.h" // using keyboard when calling rename #include "core/display.h" // using displayRedStripe as error msg -#include "modules/ir/TV-B-Gone.h" +#include "core/serialcmds.h" #include "webInterface.h" @@ -282,22 +282,19 @@ void configureWebServer() { } }); - // Route to send an ir file - server->on("/ir", HTTP_POST, []() { - if (server->hasArg("filePath")) { - String fs = server->arg("fs"); - String filePath = server->arg("filePath").c_str(); - if(fs == "SD") { - if( otherIRcodesHeadless(&SD, filePath) ) // no error - server->send(200, "text/plain", "sent"); + // Route to send an generic command (Tasmota compatible API) https://tasmota.github.io/docs/Commands/#with-web-requests + server->on("/cm", HTTP_POST, []() { + if (server->hasArg("cmnd")) { + String cmnd = server->arg("cmnd"); + if( processSerialCommand( cmnd ) ) { + server->send(200, "text/plain", "OK"); } else { - if( otherIRcodesHeadless(&LittleFS, filePath) ) // no error - server->send(200, "text/plain", "sent"); + server->send(400, "text/plain", "ERROR, check the serial log on the device for details"); } } - server->send(400, "text/plain", "ERROR"); + server->send(400, "text/plain", "http request missing required arg: cmnd"); }); - + // Reinicia o ESP server->on("/reboot", HTTP_GET, []() { if (checkUserWebAuth()) { diff --git a/src/modules/others/webInterface.h b/src/modules/others/webInterface.h index 668a7b30..ee6deb28 100644 --- a/src/modules/others/webInterface.h +++ b/src/modules/others/webInterface.h @@ -6,6 +6,8 @@ #include #include +extern WebServer* server; // used to check if the webserver is running + // function defaults String humanReadableSize(uint64_t bytes); String listFiles(FS fs, bool ishtml, String folder, bool isLittleFS); @@ -419,13 +421,13 @@ function renameFile(filePath, oldName) { } function sendIrFile(filePath) { + if(!confirm("Confirm spamming all codes inside the file?")) return; var actualFolder = document.getElementById("actualFolder").value; var fs = document.getElementById("actualFS").value; const ajax5 = new XMLHttpRequest(); const formdata5 = new FormData(); - formdata5.append("fs", fs); - formdata5.append("filePath", filePath); - ajax5.open("POST", "/ir", false); + formdata5.append("cmnd", "ir tx_from_file " + filePath); + ajax5.open("POST", "/cm", false); ajax5.send(formdata5); document.getElementById("status").innerHTML = ajax5.responseText; var fs = document.getElementById("actualFS").value; @@ -433,13 +435,13 @@ function sendIrFile(filePath) { } function sendSubFile(filePath) { + if(!confirm("Confirm sending the codes inside the file?")) return; var actualFolder = document.getElementById("actualFolder").value; var fs = document.getElementById("actualFS").value; const ajax5 = new XMLHttpRequest(); const formdata5 = new FormData(); - formdata5.append("fs", fs); - formdata5.append("filePath", filePath); - ajax5.open("POST", "/rf", false); + formdata5.append("cmnd", "subghz tx_from_file " + filePath); + ajax5.open("POST", "/cm", false); ajax5.send(formdata5); document.getElementById("status").innerHTML = ajax5.responseText; var fs = document.getElementById("actualFS").value; diff --git a/src/modules/rf/rf.cpp b/src/modules/rf/rf.cpp index 927a6270..65a7288f 100644 --- a/src/modules/rf/rf.cpp +++ b/src/modules/rf/rf.cpp @@ -197,11 +197,13 @@ void RCSwitch_send(uint64_t data, unsigned int bits, int pulse, int protocol, in mySwitch.setRepeatTransmit(repeat); mySwitch.send(data, bits); + /* Serial.println(data,HEX); Serial.println(bits); Serial.println(pulse); Serial.println(protocol); Serial.println(repeat); + * */ mySwitch.disableTransmit(); } @@ -503,18 +505,10 @@ void RCSwitch_RAW_send(int nTransmitterPin, int * ptrtransmittimings, struct Pro delayMicroseconds( ptrtransmittimings[currenttiming] ); /* - uint8_t firstLogicLevel = (protocol.invertedSignal) ? LOW : HIGH; - uint8_t secondLogicLevel = (protocol.invertedSignal) ? HIGH : LOW; - - digitalWrite(nTransmitterPin, firstLogicLevel); - delayMicroseconds( protocol.pulseLength * pulses.high); - digitalWrite(nTransmitterPin, secondLogicLevel); - delayMicroseconds( protocol.pulseLength * pulses.low); - * */ - Serial.print(ptrtransmittimings[currenttiming]); Serial.print("="); Serial.println(currentlogiclevel); + */ currenttiming++; } @@ -670,7 +664,7 @@ struct RfCodes selectRecentRfMenu() { void otherRFcodes() { - File databaseFile; + // interactive menu part only FS *fs = NULL; String filepath = ""; struct RfCodes selected_code; @@ -691,14 +685,26 @@ void otherRFcodes() { } filepath = loopSD(*fs, true, "SUB"); + if(filepath=="") return; // cancelled + // else + txSubFile(fs, filepath); +} + + +bool txSubFile(FS *fs, String filepath) { + struct RfCodes selected_code; + File databaseFile; + + if(!fs) return false; + databaseFile = fs->open(filepath, FILE_READ); - drawMainBorder(); + //drawMainBorder(); if (!databaseFile) { Serial.println("Failed to open database file."); displayError("Fail to open file"); delay(2000); - return; + return false; } Serial.println("Opened sub file."); selected_code.filepath = filepath.substring( 1 + filepath.lastIndexOf("/") ); @@ -725,8 +731,7 @@ void otherRFcodes() { addToRecentCodes(selected_code); sendRfCommand(selected_code); - // TODO: menu to resend command/pick another file from the same dir? - digitalWrite(RfTx, LED_OFF); + return true; } diff --git a/src/modules/rf/rf.h b/src/modules/rf/rf.h index ff6364d7..5c7bccfd 100644 --- a/src/modules/rf/rf.h +++ b/src/modules/rf/rf.h @@ -17,6 +17,7 @@ void rf_spectrum(); void rf_jammerIntermittent(); void rf_jammerFull(); void otherRFcodes(); +bool txSubFile(FS *fs, String filepath); void RCSwitch_Read_Raw(); void RCSwitch_send(uint64_t data, unsigned int bits, int pulse=0, int protocol=1, int repeat=10); void addToRecentCodes(struct RfCodes rfcode);