From 8d6a5c1a806cf75285a575ab803880c4a7bd3aae Mon Sep 17 00:00:00 2001 From: eadmaster <925171+eadmaster@users.noreply.github.com> Date: Sat, 7 Sep 2024 07:03:57 +0200 Subject: [PATCH 1/3] added functions in the js interpreter, hash and encryption commands, ir+rf raw mode --- platformio.ini | 39 +- src/core/globals.h | 2 + src/core/menu_items/RFMenu.cpp | 2 +- src/core/mykeyboard.cpp | 27 + src/core/mykeyboard.h | 2 + src/core/passwords.cpp | 58 +-- src/core/passwords.h | 6 +- src/core/sd_functions.cpp | 186 ++++--- src/core/sd_functions.h | 6 +- src/core/serialcmds.cpp | 452 ++++++++++++++--- src/core/wifi_common.cpp | 6 +- src/main.cpp | 5 +- src/modules/bjs_interpreter/interpreter.cpp | 524 +++++++++++++++++++- src/modules/bjs_interpreter/interpreter.h | 6 +- src/modules/ir/ir_read.cpp | 141 +++++- src/modules/ir/ir_read.h | 20 +- src/modules/others/bad_usb.cpp | 16 + src/modules/others/bad_usb.h | 1 + src/modules/others/webInterface.cpp | 34 +- src/modules/others/webInterface.h | 28 +- src/modules/rf/rf.cpp | 69 ++- src/modules/rf/rf.h | 2 +- 22 files changed, 1353 insertions(+), 279 deletions(-) diff --git a/platformio.ini b/platformio.ini index debe443b..6202c7e2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -35,6 +35,7 @@ build_flags = -Wl,--print-memory-usage -Wl,--gc-sections -DGIT_COMMIT_HASH='"Homebrew"' + -DSAFE_STACK_BUFFER_SIZE=4096 ; rtl_433_ESP flags https://github.com/NorthernMan54/rtl_433_ESP/blob/main/example/OOK_Receiver/platformio.ini ;-DOOK_MODULATION=true ; False is FSK, True is OOK ;-DRF_CC1101="CC1101" @@ -66,7 +67,10 @@ lib_deps = SmartRC-CC1101-Driver-Lib=https://github.com/bmorcelli/SmartRC-CC1101-Driver-Lib/archive/refs/heads/Bruce.zip ducktape=https://github.com/bmorcelli/duktape/releases/download/2.7.0-lite/duktape-2.7.0.zip ;https://github.com/eadmaster/rtl_433_ESP - + ;jackjansen/esp32_idf5_https_server_compat + tobozo/ESP32-PSRamFS + ;chegewara/EspTinyUSB + [env:m5stack-cplus2] platform = https://github.com/bmorcelli/platform-espressif32/releases/download/0.0.4/platform-espressif32.zip board = m5stick-c @@ -425,6 +429,7 @@ build_flags = -DGROVE_SDA=2 -DGROVE_SCL=1 + ;Default SPI port -DSPI_SCK_PIN=40 -DSPI_MOSI_PIN=14 -DSPI_MISO_PIN=39 @@ -819,6 +824,7 @@ lib_deps = platform = https://github.com/bmorcelli/platform-espressif32/releases/download/0.0.4/platform-espressif32.zip board = esp32-s3-devkitc-1 framework = arduino +board_build.arduino.memory_type = qio_opi ; NEEDED FOR 8MB PSRAM (N16R8) ;board_build.partitions = custom_16Mb.csv ;board_upload.flash_size = 16MB ;monitor_speed = 115200 @@ -832,7 +838,8 @@ build_flags = -DUSB_as_HID=1 ; needed for serial -DARDUINO_USB_CDC_ON_BOOT=1 - + -DBOARD_HAS_PSRAM + ; grove pins ; defaults from https://github.com/espressif/arduino-esp32/blob/master/variants/esp32s3/pins_arduino.h -DGROVE_SDA=8 ; default RF TX pin @@ -895,26 +902,22 @@ build_flags = ;CC1101 SPI connection pins ; best connection pins for higher speed https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/spi_master.html#gpio-matrix-and-io-mux - -DUSE_CC1101_VIA_SPI - -DCC1101_GDO0_PIN=9 - -DCC1101_SS_PIN=SPI_SS_PIN - -DCC1101_MOSI_PIN=SPI_MOSI_PIN - -DCC1101_SCK_PIN=SPI_SCK_PIN - -DCC1101_MISO_PIN=SPI_MISO_PIN + -DUSE_CC1101_VIA_SPI + -DCC1101_GDO0_PIN=9 + -DCC1101_SS_PIN=10 + -DCC1101_MOSI_PIN=11 + -DCC1101_SCK_PIN=12 + -DCC1101_MISO_PIN=13 ;-DCC1101_GDO2_PIN=14 ; optional ; connections are the same as CC1101 - -DUSE_NRF24_VIA_SPI - -DNRF24_CE_PIN=6 - -DNRF24_SS_PIN=7 - -DNRF24_MOSI_PIN=SPI_MOSI_PIN - -DNRF24_SCK_PIN=SPI_SCK_PIN - -DNRF24_MISO_PIN=SPI_MISO_PIN + ;-DUSE_NRF24_VIA_SPI + ;-DNRF24_CE_PIN=6 + ;-DNRF24_SS_PIN=7 ; chip select + ;-DNRF24_MOSI_PIN=11 + ;-DNRF24_SCK_PIN=12 + ;-DNRF24_MISO_PIN=13 - -DSPI_SCK_PIN=12 - -DSPI_MOSI_PIN=11 - -DSPI_MISO_PIN=13 - -DSPI_SS_PIN=10 lib_deps = ${common.lib_deps} diff --git a/src/core/globals.h b/src/core/globals.h index 282b4c47..6df73541 100644 --- a/src/core/globals.h +++ b/src/core/globals.h @@ -79,6 +79,8 @@ extern bool sdcardMounted; // inform if SD Cardis active or not extern bool wifiConnected; // inform if wifi is active or not +extern String wifiIP; + extern bool BLEConnected; // inform if BLE is active or not extern bool gpsConnected; // inform if GPS is active or not diff --git a/src/core/menu_items/RFMenu.cpp b/src/core/menu_items/RFMenu.cpp index 0cf141bb..a58f6209 100644 --- a/src/core/menu_items/RFMenu.cpp +++ b/src/core/menu_items/RFMenu.cpp @@ -5,7 +5,7 @@ void RFMenu::optionsMenu() { options = { - {"Scan/copy", [=]() { RCSwitch_Read_Raw(); }}, + {"Scan/copy", [=]() { RCSwitch_Read(); }}, {"Custom SubGhz", [=]() { otherRFcodes(); }}, {"Spectrum", [=]() { rf_spectrum(); }}, //@IncursioHack {"Jammer Itmt", [=]() { rf_jammerIntermittent(); }}, //@IncursioHack diff --git a/src/core/mykeyboard.cpp b/src/core/mykeyboard.cpp index 25f5fe31..c50a4edb 100644 --- a/src/core/mykeyboard.cpp +++ b/src/core/mykeyboard.cpp @@ -215,6 +215,33 @@ bool checkAnyKeyPress() { } #ifdef CARDPUTER + +bool checkNextPagePress(){ + Keyboard.update(); + if(Keyboard.isKeyPressed('/')) // right arrow + { + if(wakeUpScreen()){ + delay(200); + return false; + } + return true; + } + return false; +} + +bool checkPrevPagePress() { + Keyboard.update(); + if(Keyboard.isKeyPressed(',')) // left arrow + { + if(wakeUpScreen()){ + delay(200); + return false; + } + return true; + } + return false; +} + void checkShortcutPress(){ // shortctus to quickly starts apps Keyboard.update(); diff --git a/src/core/mykeyboard.h b/src/core/mykeyboard.h index 909bc61d..dc8fb8d0 100644 --- a/src/core/mykeyboard.h +++ b/src/core/mykeyboard.h @@ -16,6 +16,8 @@ bool checkEscPress(); void checkShortcutPress(); int checkNumberShortcutPress(); char checkLetterShortcutPress(); +bool checkNextPagePress(); +bool checkPrevPagePress(); #endif bool checkAnyKeyPress(); diff --git a/src/core/passwords.cpp b/src/core/passwords.cpp index 6a95570a..a641436f 100644 --- a/src/core/passwords.cpp +++ b/src/core/passwords.cpp @@ -1,38 +1,32 @@ -#include "mbedtls/aes.h" -#include "mbedtls/md.h" -#include "mbedtls/pkcs5.h" -#include - - -String aes_decrypt(uint8_t* inputData, size_t fileSize, const String& password_str) { - // generate key, iv, salt - const char *password = password_str.c_str(); - unsigned char key[32]; - unsigned char iv[16]; - unsigned char salt[8] = { /* The salt used by OpenSSL during encryption */ }; - - mbedtls_md_context_t md_ctx; - mbedtls_md_init(&md_ctx); - mbedtls_md_setup(&md_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); - // Derive key and IV using PBKDF2 - mbedtls_pkcs5_pbkdf2_hmac(&md_ctx, (unsigned char*)password, strlen(password), salt, 8, 10000, 32, key); - mbedtls_pkcs5_pbkdf2_hmac(&md_ctx, (unsigned char*)password, strlen(password), salt, 8, 10000, 16, iv); +#include +#include + + +String xorEncryptDecrypt(const String &input, const String &password) { + uint8_t md5Hash[16]; + + MD5Builder md5; + md5.begin(); + md5.add(password); + md5.calculate(); + md5.getBytes(md5Hash); // Store MD5 hash in the output array + + String output = input; // Copy input to output for modification + for (size_t i = 0; i < input.length(); i++) { + output[i] = input[i] ^ md5Hash[i % 16]; // XOR each byte with the MD5 hash + } + + return output; +} - mbedtls_md_free(&md_ctx); - - unsigned char *outputData = new unsigned char[fileSize]; - mbedtls_aes_context aes; - mbedtls_aes_init(&aes); - mbedtls_aes_setkey_dec(&aes, key, 256); - mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, fileSize, iv, inputData, outputData); - mbedtls_aes_free(&aes); - - // Convert the decrypted data to a string - String decryptedText = String(outputData, fileSize); - free(outputData); +String encryptString(String& plaintext, const String& password_str) { + // TODO: add "XOR" header + return xorEncryptDecrypt(plaintext, password_str); +} - return decryptedText; +String decryptString(String& cypertext, const String& password_str) { + return xorEncryptDecrypt(cypertext, password_str); } diff --git a/src/core/passwords.h b/src/core/passwords.h index b7240a0c..b704f938 100644 --- a/src/core/passwords.h +++ b/src/core/passwords.h @@ -1,2 +1,6 @@ -String aes_decrypt(uint8_t* inputData, size_t fileSize, const String& password_str); \ No newline at end of file +#include + +String encryptString(String& plaintext, const String& password_str); + +String decryptString(String& cypertext, const String& password_str); diff --git a/src/core/sd_functions.cpp b/src/core/sd_functions.cpp index a345eb6c..b4a730d2 100644 --- a/src/core/sd_functions.cpp +++ b/src/core/sd_functions.cpp @@ -1,7 +1,7 @@ #include #include "globals.h" #include "sd_functions.h" -#include "mykeyboard.h" // usinf keyboard when calling rename +#include "mykeyboard.h" // using keyboard when calling rename #include "display.h" // using displayRedStripe as error msg #include "passwords.h" #include "modules/others/audio.h" @@ -10,6 +10,10 @@ #include "modules/wifi/wigle.h" #include "modules/others/bad_usb.h" #include "modules/others/qrcode_menu.h" +#include "modules/bjs_interpreter/interpreter.h" + +#include +#include // for CRC32 struct FilePage { int pageIndex; @@ -299,12 +303,12 @@ String readSmallFile(FS &fs, String filepath) { if (!file) return ""; size_t fileSize = file.size(); - if(fileSize > 1024*3) { // 3K is the max + if(fileSize > SAFE_STACK_BUFFER_SIZE) { displayError("File is too big"); Serial.println("File is too big"); return ""; } - + fileContent = file.readString(); file.close(); @@ -335,63 +339,63 @@ size_t getFileSize(FS &fs, String filepath) { return fileSize; } -bool is_valid_ascii(const String &text) { - for (int i = 0; i < text.length(); i++) { - char c = text[i]; - // Check if the character is within the printable ASCII range or is a newline/carriage return - if (!(c >= 32 && c <= 126) && c != 10 && c != 13) { - return false; // Invalid character found - } - } - return true; // All characters are valid -} - -String readDecryptedAesFile(FS &fs, String filepath) { - File file = fs.open(filepath, FILE_READ); - if (!file) return ""; - - size_t fileSize = file.size(); - if(fileSize > 1024*3) { // 3K is the max - displayError("File is too big"); - return ""; +bool isValidAscii(const String &text) { + for (int i = 0; i < text.length(); i++) { + char c = text[i]; + // Check if the character is within the printable ASCII range or is a newline/carriage return + if (!(c >= 32 && c <= 126) && c != 10 && c != 13) { + return false; // Invalid character found + } } + return true; // All characters are valid +} - char buff[fileSize]; - size_t bytesRead = file.readBytes(buff, fileSize); - //Serial.print("fileSize:"); - //Serial.println(fileSize); - //Serial.println(bytesRead); - - /* - // read the whole file with a single call - char buff[fileSize + 1]; - size_t bytesRead = file.readBytes(buff, fileSize); - buff[bytesRead] = '\0'; // Null-terminate the string - return String(buff); - */ +String md5File(FS &fs, String filepath) { + if(!fs.exists(filepath)) return ""; + String txt = readSmallFile(fs, filepath); + MD5Builder md5; + md5.begin(); + md5.add(txt); + md5.calculate(); + return(md5.toString()); +} - if (bytesRead==0) { - Serial.println("empty cypherText"); - return ""; - } - // else +String crc32File(FS &fs, String filepath) { + if(!fs.exists(filepath)) return ""; + String txt = readSmallFile(fs, filepath); + // derived from https://techoverflow.net/2022/08/05/how-to-compute-crc32-with-ethernet-polynomial-0x04c11db7-on-esp32-crc-h/ + uint32_t romCRC = (~crc32_le((uint32_t)~(0xffffffff), (const uint8_t*)txt.c_str(), txt.length()))^0xffffffff; + char s[18] = {0}; + char crcBytes[4] = {0}; + memcpy(crcBytes, &romCRC, sizeof(uint32_t)); + snprintf(s, sizeof(s), "%02X%02X%02X%02X\n", crcBytes[3], crcBytes[2], crcBytes[1], crcBytes[0]); + return(String(s)); +} +String readDecryptedFile(FS &fs, String filepath) { + String cyphertext = readSmallFile(fs, filepath); + if(cyphertext.length() == 0) return ""; + if(cachedPassword.length()==0) { cachedPassword = keyboard("", 32, "password"); if(cachedPassword.length()==0) return ""; // cancelled } - + + //Serial.println(cyphertext); + //Serial.println(cachedPassword); + // else try to decrypt - String plaintext = aes_decrypt((uint8_t*)buff, bytesRead, cachedPassword); - + String plaintext = decryptString(cyphertext, cachedPassword); + // check if really plaintext - if(!is_valid_ascii(plaintext)) { + if(!isValidAscii(plaintext)) { // invalidate cached password -> will ask again on the next try cachedPassword = ""; Serial.println("invalid password"); - Serial.println(plaintext); + //Serial.println(plaintext); return ""; } + // else return plaintext; } @@ -649,43 +653,45 @@ String loopSD(FS &fs, bool filePicker, String allowed_ext) { Wigle wigle; wigle.upload(&fs, filepath); }}); + if(filepath.endsWith(".bjs") || filepath.endsWith(".js")) options.insert(options.begin(), {"JS Script Run", [&]() { + delay(200); + run_bjs_script_headless(fs, filepath); + }}); #if defined(USB_as_HID) if(filepath.endsWith(".txt")) { - options.push_back({"BadUSB Run", [&]() { - Kb.begin(); USB.begin(); - // TODO: set default keyboard layout + options.push_back({"BadUSB Run", [&]() { + Kb.begin(); USB.begin(); key_input(fs, filepath); + // TODO: reinit serial port + }}); + options.push_back({"USB HID Type", [&]() { + String t = readSmallFile(fs, filepath); + displayInfo("Typing"); + key_input_from_string(t); }}); - /* - options.push_back({"USB HID Type", [&]() { - Kb.begin(); USB.begin(); - Kb.print(readSmallFile(fs, filepath).c_str()); // buggy? - //String t = readSmallFile(fs, filepath).c_str(); - //Kb.write((const uint8_t*) t.c_str(), t.length()); - }});*/ } - #endif - /* WIP - if(filepath.endsWith(".aes") || filepath.endsWith(".enc")) { // aes encrypted files - options.insert(options.begin(), {"Decrypt+Type", [&]() { - String plaintext = readDecryptedAesFile(fs, filepath); - if(plaintext.length()==0) return displayError("invalid password");; // file is too big or cannot read, or cancelled + if(filepath.endsWith(".enc")) { // encrypted files + options.insert(options.begin(), {"Decrypt+Type", [&]() { + String plaintext = readDecryptedFile(fs, filepath); + if(plaintext.length()==0) // file is too big or cannot read, or cancelled // else - Kb.begin(); USB.begin(); - Kb.print(plaintext.c_str()); + key_input_from_string(plaintext); }}); } #endif - if(filepath.endsWith(".aes") || filepath.endsWith(".enc")) { // aes encrypted files + if(filepath.endsWith(".enc")) { // encrypted files options.insert(options.begin(), {"Decrypt+Show", [&]() { - String plaintext = readDecryptedAesFile(fs, filepath); - if(plaintext.length()==0) return; // file is too big or cannot read, or cancelled + String plaintext = readDecryptedFile(fs, filepath); + delay(200); + if(plaintext.length()==0) return displayError("Decryption failed"); + + //if(plaintext.length()<..) + displaySuccess(plaintext); + while(!checkAnyKeyPress()) delay(100); // else - displaySuccess(plaintext); - delay(2000); - // TODO: loop and wait for user input? + // TODO: show in the text viewer }}); - }*/ + } #if defined(HAS_NS4168_SPKR) if(isAudioFile(filepath)) options.insert(options.begin(), {"Play Audio", [&]() { delay(200); @@ -696,10 +702,22 @@ String loopSD(FS &fs, bool filePicker, String allowed_ext) { // generate qr codes from small files (<3K) size_t filesize = getFileSize(fs, filepath); //Serial.println(filesize); - if(filesize < 3*1024 && filesize>0) options.push_back({"QR code", [&]() { + if(filesize < SAFE_STACK_BUFFER_SIZE && filesize>0) { + options.push_back({"QR code", [&]() { delay(200); qrcode_display(readSmallFile(fs, filepath)); }}); + options.push_back({"CRC32", [&]() { + delay(200); + displaySuccess(crc32File(fs, filepath)); + while(!checkAnyKeyPress()) delay(100); + }}); + options.push_back({"MD5", [&]() { + delay(200); + displaySuccess(md5File(fs, filepath)); + while(!checkAnyKeyPress()) delay(100); + }}); + } options.push_back({"Main Menu", [&]() { exit = true; }}); delay(200); @@ -722,7 +740,9 @@ String loopSD(FS &fs, bool filePicker, String allowed_ext) { } #ifdef CARDPUTER - /* + if(checkEscPress()) break; // quit + + /* TODO: go back 1 level instead of quitting if(Keyboard.isKeyPressed(KEY_BACKSPACE)) { // go back 1 level if(Folder == "/") break; @@ -731,7 +751,23 @@ String loopSD(FS &fs, bool filePicker, String allowed_ext) { redraw=true; continue; }*/ - if(checkEscPress()) break; + + const short PAGE_JUMP_SIZE = 5; + if(checkNextPagePress()) { + index += PAGE_JUMP_SIZE; + if(index>maxFiles) index=maxFiles-1; // check bounds + redraw = true; + continue; + } + if(checkPrevPagePress()) { + index -= PAGE_JUMP_SIZE; + if(index<0) index = 0; // check bounds + redraw = true; + continue; + } + + // check letter shortcuts + char pressed_letter = checkLetterShortcutPress(); if(pressed_letter>0) { //Serial.println(pressed_letter); @@ -823,6 +859,8 @@ void viewFile(FS fs, String filepath) { if (!file) return; // TODO: detect binary file, switch to hex view + // String header=file.read(100); file.rewind(); + // if(isValidAscii(header)) ... while (file.available()) { fileContent = file.readString(); @@ -948,4 +986,4 @@ void fileInfo(FS fs, String filepath) { } return; -} \ No newline at end of file +} diff --git a/src/core/sd_functions.h b/src/core/sd_functions.h index 5af3973a..9e54c889 100644 --- a/src/core/sd_functions.h +++ b/src/core/sd_functions.h @@ -29,7 +29,11 @@ String readLineFromFile(File myFile); String readSmallFile(FS &fs, String filepath); -String readDecryptedAesFile(FS &fs, String filepath); +String md5File(FS &fs, String filepath); + +String crc32File(FS &fs, String filepath); + +String readDecryptedFile(FS &fs, String filepath); void readFs(FS fs, String folder, String result[][3], String allowed_ext = "*"); diff --git a/src/core/serialcmds.cpp b/src/core/serialcmds.cpp index c5b2d875..e561da61 100644 --- a/src/core/serialcmds.cpp +++ b/src/core/serialcmds.cpp @@ -6,21 +6,87 @@ #include // for PRIu64 #include +#include +#include + #include "sd_functions.h" #include "settings.h" #include "display.h" #include "powerSave.h" +#include "wifi_common.h" +#include "passwords.h" #include "modules/rf/rf.h" //#include "modules/rf/rtl433.h" //#include "modules/rf/radiolib_test.h" #include "modules/ir/TV-B-Gone.h" #include "modules/ir/ir_read.h" #include "modules/others/bad_usb.h" +#include "modules/others/webInterface.h" +#include "modules/bjs_interpreter/interpreter.h" // for JavaScript interpreter #if defined(HAS_NS4168_SPKR) || defined(BUZZ_PIN) #include "modules/others/audio.h" #endif + +#include + +bool setupPsramFs() { + // https://github.com/tobozo/ESP32-PsRamFS/blob/main/examples/PSRamFS_Test/PSRamFS_Test.ino + static bool psRamFSMounted = false; + if(psRamFSMounted) return true; // avoid reinit + + #ifdef BOARD_HAS_PSRAM + PSRamFS.setPartitionSize( ESP.getFreePsram()/2 ); // use half of psram + #else + PSRamFS.setPartitionSize( SAFE_STACK_BUFFER_SIZE ); + #endif + + if(!PSRamFS.begin()){ + Serial.println("PSRamFS Mount Failed"); + psRamFSMounted = false; + return false; + } + // else + psRamFSMounted = true; + return true; +} + + +String readSmallFileFromSerial() { + String buf = ""; + String curr_line = ""; + Serial.flush(); + while (true) { + if (!Serial.available()) { + delay(500); + Serial.flush(); + continue; + } + curr_line = Serial.readStringUntil('\n'); + if(curr_line.startsWith("EOF")) break; + buf += curr_line + "\n"; + if(buf.length()>SAFE_STACK_BUFFER_SIZE) break; // trim? + } + return buf; +} +/* +String readSmallFileFromSerialAlt() { + String buf = ""; + Serial.flush(); + while (true) { + if (!Serial.available()) { + delay(500); + Serial.flush(); + continue; + } + buf = Serial.readStringUntil(4); // ascii code 4 = EOT (End of Transmit) + return buf; + } +} +* */ + + /* task to handle serial commands, currently used in headless mode only */ #include #include @@ -42,7 +108,7 @@ void startSerialCommandsHandlerTask() { "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. - 1, // Priority of the task + 2, // Priority of the task &serialcmdsTaskHandle, // Task handle (optional, can be NULL). 1 // 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. ); @@ -86,31 +152,18 @@ void handleSerialCommands() { String cmd_str; - /* - if (Serial.available() >= MIN_CMD_LEN ) { - size_t len = Serial.available(); - char sbuf[len] = {0}; - Serial.readBytes(sbuf, len); - Serial.print("received:"); - Serial.println(sbuf); - //log_d(sbuf); - cmd_str = String(sbuf); - } else { - //Serial.println("nothing received"); - //log_d("nothing received"); + if (Serial.available() >= 1) { + cmd_str = Serial.readStringUntil('\n'); + } else { + // try again on next iteration return; - }*/ - - if (Serial.available() >= 1) { - cmd_str = Serial.readStringUntil('\n'); - } else { - // try again on next iteration - return; - } + } bool r = processSerialCommand(cmd_str); if(r) setup_gpio(); // temp fix for menu inf. loop else Serial.println("failed: " + cmd_str); + + returnToMenu = true; // forced menu redrawn } bool processSerialCommand(String cmd_str) { @@ -124,19 +177,44 @@ bool processSerialCommand(String cmd_str) { return true; } + // check cmd aliases + if(cmd_str == "ls") cmd_str.replace("ls", "storage list /"); + if(cmd_str.startsWith("ls ")) cmd_str.replace("ls ", "storage list "); + if(cmd_str.startsWith("dir ")) cmd_str.replace("dir ", "storage list "); + if(cmd_str.startsWith("rm ")) cmd_str.replace("rm ", "storage remove "); + if(cmd_str.startsWith("del ")) cmd_str.replace("del ", "storage remove "); + if(cmd_str.startsWith("md ")) cmd_str.replace("md ", "storage mkdir "); + if(cmd_str.startsWith("cat ")) cmd_str.replace("cat ", "storage read "); + if(cmd_str.startsWith("type ")) cmd_str.replace("type ", "storage read "); + if(cmd_str.startsWith("md5 ")) cmd_str.replace("md5 ", "storage md5 "); + if(cmd_str.startsWith("crc32 ")) cmd_str.replace("crc32 ", "storage crc32 "); + if(cmd_str.startsWith("play ")) cmd_str.replace("play ", "music_player "); + if(cmd_str.startsWith("rf ")) cmd_str.replace("rf ", "subghz "); + if(cmd_str.startsWith("bu ")) cmd_str.replace("bu ", "badusb "); + if(cmd_str.startsWith("set ")) cmd_str.replace("set ", "settings "); + if(cmd_str.startsWith("decrypt ")) cmd_str.replace("decrypt ", "crypto decrypt_from_file "); + if(cmd_str.startsWith("run ")) cmd_str.replace("run ", "js "); + // case-insensitive matching only in some cases -- TODO: better solution for this - if(cmd_str.indexOf("from_file ") == -1 && cmd_str.indexOf("storage ") == -1 && cmd_str.indexOf("settings ")) + if(cmd_str.indexOf("from_file ") == -1 && cmd_str.indexOf("storage ") == -1 && cmd_str.indexOf("settings ") && cmd_str.indexOf("js ")) cmd_str.toLowerCase(); // switch on cmd_str if(cmd_str.startsWith("ir") ) { - if(cmd_str == "ir rx") { // "ir rx raw" - IrRead i = IrRead(true); // true == headless mode - return(i.loop_headless(10)); // wait for 10 seconds + if(cmd_str.startsWith("ir rx")) { + IrRead* i = NULL; // avoid calling the constructor here + if(cmd_str == "ir rx") i = new IrRead(true); // true -> headless mode + else if(cmd_str == "ir rx raw") i = new IrRead(true, true); // true -> headless mode, true = raw mode + else return false; + String r = i->loop_headless(10); // 10s timeout + if(r.length()==0) return false; + // else + Serial.println(r); + delete i; + return true; } - //TODO: if(cmd_str == "ir rx" { - + if(cmd_str.startsWith("ir tx")) { // make sure it is initted gsetIrTxPin(false); @@ -169,10 +247,10 @@ bool processSerialCommand(String cmd_str) { } //if(cmd_str.startsWith("ir tx sirc")){ //if(cmd_str.startsWith("ir tx samsung")){ - //if(cmd_str.startsWith("ir tx raw")){ if(cmd_str.startsWith("ir tx_from_file ")){ + // example: ir tx_from_file LG_AKB72915206_power.ir String filepath = cmd_str.substring(strlen("ir tx_from_file ")); filepath.trim(); if(filepath.indexOf(".ir") == -1) return false; // invalid filename @@ -182,7 +260,20 @@ bool processSerialCommand(String cmd_str) { // else file not found return false; } - + + if(cmd_str.startsWith("ir tx_from_buffer")){ + if(!(setupPsramFs())) return false; + String txt = readSmallFileFromSerial(); + String tmpfilepath = "/tmpramfile"; // TODO: random name? + File f = PSRamFS.open(tmpfilepath, FILE_WRITE); + if(!f) return false; + f.write((const uint8_t*) txt.c_str(), txt.length()); + f.close(); + bool r = txIrFile(&PSRamFS, tmpfilepath); + PSRamFS.remove(tmpfilepath); // TODO: keep cached? + return r; + } + if(cmd_str.startsWith("irsend")) { // tasmota json command https://tasmota.github.io/docs/Tasmota-IR/#sending-ir-commands // e.g. IRSend {"Protocol":"NEC","Bits":32,"Data":"0x20DF10EF"} @@ -240,16 +331,34 @@ bool processSerialCommand(String cmd_str) { } // end of ir commands if(cmd_str.startsWith("rf") || cmd_str.startsWith("subghz" )) { - - if(cmd_str.startsWith("subghz rx")) { + + if(cmd_str.startsWith("subghz rx")) { + /* const char* args = cmd_str.c_str() + strlen("subghz rx"); float frequency=RfFreq; // global default if(strlen(args)>1) sscanf(args, " %f", &frequency); + * */ + String args = cmd_str.substring(cmd_str.indexOf(" ", strlen("subghz rx"))); + float frequency=RfFreq; // global default + if(args.length()>1) { + sscanf(args.c_str(), " %f", &frequency); + frequency /= 1000000; // passed as a long int (e.g. 433920000) + } //Serial.print("frequency:"); - //Serial.println((int) frequency); - return RCSwitch_Read_Raw(frequency, 10); + //Serial.println(frequency); + String r = ""; + if(cmd_str.startsWith("subghz rx_raw")) + r = RCSwitch_Read(frequency, 10, true); // true -> raw mode + else + r = RCSwitch_Read(frequency, 10, false); // false -> decoded mode + if(r.length()==0) return false; + // else + Serial.println(r); + return true; + } if(cmd_str.startsWith("subghz tx_from_file")) { + // example: subghz tx_from_file plug1_on.sub String filepath = cmd_str.substring(strlen("subghz tx_from_file ")); filepath.trim(); if(filepath.indexOf(".sub") == -1) return false; // invalid filename @@ -259,6 +368,19 @@ bool processSerialCommand(String cmd_str) { // else file not found return false; } + if(cmd_str == "subghz tx_from_buffer") { + if(!(setupPsramFs())) return false; + String txt = readSmallFileFromSerial(); + String tmpfilepath = "/tmpramfile"; // TODO: random name? + File f = PSRamFS.open(tmpfilepath, FILE_WRITE); + if(!f) return false; + f.write((const uint8_t*) txt.c_str(), txt.length()); + f.close(); + //if(PSRamFS.exists(filepath)) + bool r = txSubFile(&PSRamFS, tmpfilepath); + PSRamFS.remove(tmpfilepath); // TODO: keep cached? + return r; + } if(cmd_str.startsWith("subghz tx")) { // flipperzero-like cmd https://docs.flipper.net/development/cli/#wLVht @@ -329,8 +451,9 @@ bool processSerialCommand(String cmd_str) { #if defined(USB_as_HID) // badusb available - if(cmd_str.startsWith("badusb tx_from_file ")) { - String filepath = cmd_str.substring(strlen("badusb tx_from_file ")); + if(cmd_str.startsWith("badusb run_from_file ")) { + // badusb run_from_file HelloWorld.txt + String filepath = cmd_str.substring(strlen("badusb run_from_file ")); filepath.trim(); if(filepath.indexOf(".txt") == -1) return false; // invalid filename if(!filepath.startsWith("/")) filepath = "/" + filepath; // add "/" if missing @@ -341,10 +464,28 @@ bool processSerialCommand(String cmd_str) { Kb.begin(); USB.begin(); key_input(*fs, filepath); + //TODO: need to reinit serial when finished + //Kb.end(); + //USB.~ESPUSB(); // Explicit call to destructor + //Serial.begin(115200); + return true; + } + if(cmd_str == "badusb run_from_buffer") { + if(!(setupPsramFs())) return false; + String txt = readSmallFileFromSerial(); + String tmpfilepath = "/tmpramfile"; // TODO: random name? + File f = PSRamFS.open(tmpfilepath, FILE_WRITE); + if(!f) return false; + f.write((const uint8_t*) txt.c_str(), txt.length()); + f.close(); + Kb.begin(); + USB.begin(); + key_input(PSRamFS, tmpfilepath); + PSRamFS.remove(tmpfilepath); // TODO: keep cached? return true; } #endif - + #if defined(HAS_NS4168_SPKR) || defined(BUZZ_PIN) if(cmd_str.startsWith("tone") || cmd_str.startsWith("beep")) { // || cmd_str.startsWith("music_player beep" ) const char* args = cmd_str.c_str() + 4; @@ -456,7 +597,67 @@ bool processSerialCommand(String cmd_str) { //esp_timer_stop(screensaver_timer); return true; } + + if(cmd_str == "webui" ) { + // start the webui + if(!wifiConnected) { + Serial.println("wifiConnect"); + wifiConnect("",0,true); // TODO: read mode from settings file + } + Serial.println("startWebUi"); + startWebUi(true); // MEMO: will quit when checkEscPress + return true; + } + +/* + // WIP https://github.com/pr3y/Bruce/issues/162#issuecomment-2308788115 + + if(cmd_str.startsWith("serial2 write")) { + setupBruceDaughterboard(); + String args = cmd_str.substring(strlen("serial2 write")); + Serial2.println(args); // wakeup + Serial2.flush(); + return true; + } + if(cmd_str.startsWith("serial2 read")) { + setupBruceDaughterboard(); + String curr_line = ""; + while (Serial2.available()) { + curr_line = Serial.readStringUntil('\n'); + Serial2.println(curr_line); + } + return true; + } + + if(cmd_str == "power sleep_and_wakeup_from_uart" ) { + #ifdef HAS_SCREEN + turnOffDisplay(); + #endif + // https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/sleep_modes.html#uart-wakeup-light-sleep-only + // 2FIX: not waking up + // https://github.com/espressif/arduino-esp32/issues/5107 + // https://github.com/espressif/arduino-esp32/issues/6976 + //Serial.println(TX); + //Serial.println(RX); + //Serial.println((gpio_num_t)RX); + Serial.println("going to sleep..."); + //uart_set_pin(UART_NUM_0, TX, RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + gpio_sleep_set_direction((gpio_num_t)RX, GPIO_MODE_INPUT); + gpio_sleep_set_pull_mode((gpio_num_t)RX, GPIO_PULLUP_ONLY); + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); + uart_set_wakeup_threshold(UART_NUM_0, 3); // 3 edges on U0RXD to wakeup + delay(100); + esp_sleep_enable_uart_wakeup(UART_NUM_0); + //uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + delay(100); + esp_light_sleep_start(); + Serial.begin(115200); // needs reinit? + Serial.println("woken"); + return true; + } +*/ + // gpio cmds https://docs.flipper.net/development/cli/#aqA4b if(cmd_str.startsWith("gpio mode ")) { const char* args = cmd_str.c_str() + strlen("gpio mode "); @@ -564,6 +765,9 @@ bool processSerialCommand(String cmd_str) { Serial.print("Bruce v"); Serial.println(BRUCE_VERSION); Serial.println(GIT_COMMIT_HASH); + Serial.printf("SDK: %s\n", ESP.getSdkVersion()); + // TODO: read mac addresses https://lastminuteengineers.com/esp32-mac-address-tutorial/ + // https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/ChipID/GetChipID/GetChipID.ino //Serial.printf("Chip is %s (revision v%d)\n", ESP.getChipModel(), ESP.getChipRevision()); //Serial.printf("Detected flash size: %d\n", ESP.getFlashChipSize()); @@ -572,6 +776,13 @@ bool processSerialCommand(String cmd_str) { // Features: WiFi, BLE, Embedded Flash 8MB (GD) // Crystal is 40MHz // MAC: 24:58:7c:5b:24:5c + + if (wifiConnected) { + Serial.println("wifi: connected"); + Serial.println("ip: " + wifiIP); // read global var + } else { + Serial.println("wifi: not connected"); + } return true; } @@ -632,21 +843,33 @@ bool processSerialCommand(String cmd_str) { } // "storage" cmd to manage files https://docs.flipper.net/development/cli/#Xgais - if(cmd_str.startsWith("storage read ")) { - String txt = ""; - String filepath = cmd_str.substring(strlen("storage read ")); + + if(cmd_str.startsWith("storage read ") || cmd_str.startsWith("storage md5 ") || cmd_str.startsWith("storage crc32 ")) { + //String filepath = cmd_str.substring(strlen("storage read ")); + String filepath = cmd_str.substring(cmd_str.indexOf(" ", strlen("storage md5"))); filepath.trim(); if(filepath.length()==0) return false; // missing arg if(!filepath.startsWith("/")) filepath = "/" + filepath; // add "/" if missing - if(SD.exists(filepath)) txt = readSmallFile(SD, filepath); - if(LittleFS.exists(filepath)) txt = readSmallFile(LittleFS, filepath); - if(txt.length()!=0) { - Serial.println(txt); + FS* fs = NULL; + if(SD.exists(filepath)) fs = &SD; + if(LittleFS.exists(filepath)) fs = &LittleFS; + if(!fs) return false; + // else + if(cmd_str.startsWith("storage read ")) { + Serial.println( readSmallFile(*fs, filepath) ); + return true; + } + else if(cmd_str.startsWith("storage md5 ")) { + Serial.println( md5File(*fs, filepath) ); return true; - } else return false; + } + else if(cmd_str.startsWith("storage crc32 ")) { + Serial.println( crc32File(*fs, filepath) ); + return true; + } } - if(cmd_str.startsWith("storage stat ")) { + if(cmd_str.startsWith("storage stat ")) { // TODO: storage timestamp // storage stat /ir/_Flipper-IRDB-main.zip String filepath = cmd_str.substring(strlen("storage stat ")); filepath.trim(); @@ -716,9 +939,9 @@ bool processSerialCommand(String cmd_str) { while (file2) { Serial.print(file2.name()); if (file2.isDirectory()) { - Serial.println("\t\t"); + Serial.println("\t"); } else { - Serial.print("\t\t"); + Serial.print("\t"); Serial.println(file2.size()); } //Serial.println(file2.path()); @@ -749,6 +972,25 @@ bool processSerialCommand(String cmd_str) { // else return false; } + if(cmd_str.startsWith("storage write ")) { + String filepath = cmd_str.substring(strlen("storage write ")); + filepath.trim(); + if(filepath.length()==0) return false; // missing arg + if(!filepath.startsWith("/")) filepath = "/" + filepath; // add "/" if missing + FS* fs = &LittleFS; // default fallback + if(SD.exists(filepath)) fs = &SD; + if(LittleFS.exists(filepath)) fs = &LittleFS; + if(!fs && sdcardMounted) fs = &SD; + String txt = readSmallFileFromSerial(); + if(txt.length()==0) return false; + File f = fs->open(filepath, FILE_APPEND, true); // create if it does not exist, append otherwise + if(!f) return false; + f.write((const uint8_t*) txt.c_str(), txt.length()); + f.close(); + Serial.println("file written: " + filepath); + return true; + } + if(cmd_str.startsWith("storage rename ")) { // storage rename HelloWorld.txt HelloWorld2.txt String args = cmd_str.substring(strlen("storage rename ")); @@ -788,14 +1030,48 @@ bool processSerialCommand(String cmd_str) { fileToCopy=""; return false; } - - // TODO: storage write - if(cmd_str.startsWith("crypto decrypt_from_file")) { - // crypto decrypt_from_file passwords/github.com_pkcs5_pbkdf2.enc password - // crypto decrypt_from_file passwords/github.com_md5.enc 1234 - // crypto decrypt_from_file passwords/github.com2.enc 1234 - String args = cmd_str.substring(strlen("crypto decrypt_from_file ")); + if(cmd_str.startsWith("js run_from_buffer")){ + if(!(setupPsramFs())) return false; + String txt = readSmallFileFromSerial(); + String tmpfilepath = "/tmpramfile"; + File f = PSRamFS.open(tmpfilepath, FILE_WRITE); + if(!f) return false; + f.write((const uint8_t*) txt.c_str(), txt.length()); + f.close(); + bool r = run_bjs_script_headless(PSRamFS, tmpfilepath); + PSRamFS.remove(tmpfilepath); + return r; + } + + if(cmd_str.startsWith("js ")) { + String filepath = cmd_str.substring(strlen("js ")); + filepath.trim(); + if(filepath.length()==0) return false; // missing arg + //if(!filepath.startsWith("/")) filepath = "/" + filepath; // add "/" if missing + FS* fs = NULL; + if(SD.exists(filepath)) fs = &SD; + if(LittleFS.exists(filepath)) fs = &LittleFS; + if(!fs) { // dir not found + // assume filepath is an inline script + Serial.println(filepath); + run_bjs_script_headless(filepath); + return true; + } + // else + run_bjs_script_headless(*fs, filepath); + // else + return true; + } + + if(cmd_str.startsWith("crypto ")) { + // crypto decrypt_from_file passwords/test.txt.enc 123 + // crypto encrypt_to_file passwords/test.txt.enc 123 + String args = ""; + if(cmd_str.startsWith("crypto decrypt_from_file ")) + args = cmd_str.substring(strlen("crypto decrypt_from_file ")); + else if(cmd_str.startsWith("crypto encrypt_to_file ")) + args = cmd_str.substring(strlen("crypto encrypt_to_file ")); String filepath = args.substring(0, args.indexOf(" ")); filepath.trim(); if(filepath.length()==0) return false; // missing arg @@ -803,17 +1079,34 @@ bool processSerialCommand(String cmd_str) { String password = args.substring(args.indexOf(" ")+1, args.length()); password.trim(); if(password.length()==0) return false; // missing arg + cachedPassword = password; // avoid interactive prompt //Serial.println(filepath); //Serial.println(password); - FS* fs = NULL; - if(SD.exists(filepath)) fs = &SD; - if(LittleFS.exists(filepath)) fs = &LittleFS; - if(!fs) return false; // file not found - cachedPassword = password; // avoid interactive prompt - String plaintext = readDecryptedAesFile(*fs, filepath); - if(plaintext=="") return false; - Serial.println(plaintext); - return true; + + if(cmd_str.startsWith("crypto decrypt_from_file")) { + FS* fs = NULL; + if(SD.exists(filepath)) fs = &SD; + if(LittleFS.exists(filepath)) fs = &LittleFS; + if(!fs) return false; // file not found + String plaintext = readDecryptedFile(*fs, filepath); + if(plaintext=="") return false; + Serial.println(plaintext); + return true; + } + else if(cmd_str.startsWith("crypto encrypt_to_file")) { + String txt = readSmallFileFromSerial(); + if(txt.length()==0) return false; + FS* fs = &SD; + if(LittleFS.exists(filepath)) fs = &LittleFS; + File f = fs->open(filepath, FILE_WRITE); + if(!f) return false; + String cyphertxt = encryptString(txt, cachedPassword); + if(cyphertxt=="") return false; + f.write((const uint8_t*) cyphertxt.c_str(), cyphertxt.length()); + f.close(); + Serial.println("file written: " + filepath); + return true; + } } if(cmd_str == "uptime") { @@ -829,24 +1122,41 @@ bool processSerialCommand(String cmd_str) { } if(cmd_str == "date") { - Serial.print("Current time: "); - Serial.println(timeStr); - // TODO: full date - return true; + if (clock_set) { + Serial.print("Current time: "); + #if !defined(HAS_RTC) + Serial.println(rtc.getDateTime()); + //Serial.println(rtc.getTime("%A, %B %d %Y %H:%M:%S")); + #else + RTC_TimeTypeDef _time; + RTC_DateTypeDef _date; + cplus_RTC _rtc; + _rtc.begin(); + _rtc.GetTime(&_time); + _rtc.GetDate(&_date); + char stimeStr[100] = {0}; + snprintf(stimeStr, sizeof(stimeStr), "%02d %02d %04d %02d:%02d:%02d", _date.Month, _date.Date, _date.Year, _time.Hours, _time.Minutes, _time.Seconds); + Serial.println(stimeStr); + #endif + return true; + } else { + Serial.println("clock not set"); + return false; + } } /* WIP if(cmd_str.startsWith("rtl433")) { + // https://github.com/pr3y/Bruce/issues/192 rtl433_setup(); return rtl433_loop(10000*3); } if(cmd_str.startsWith("mass_storage")) { - // derived from https://github.com/espressif/arduino-esp32/blob/master/libraries/SD_MMC/examples/SD2USBMSC/SD2USBMSC.ino - + // WIP https://github.com/pr3y/Bruce/issues/210 + // https://github.com/espressif/arduino-esp32/blob/master/libraries/SD_MMC/examples/SD2USBMSC/SD2USBMSC.ino } - */ - /* + if(cmd_str.startsWith("radiolib rx")) { // https://github.com/jgromes/RadioLib/discussions/973 setup_cc1101(); diff --git a/src/core/wifi_common.cpp b/src/core/wifi_common.cpp index cbce4a0f..6d58d1f2 100644 --- a/src/core/wifi_common.cpp +++ b/src/core/wifi_common.cpp @@ -90,6 +90,7 @@ bool wifiConnect(String ssid, int encryptation, bool isAP) { if(WiFi.status() == WL_CONNECTED) { wifiConnected=true; + wifiIP = WiFi.localIP().toString(); // update global var timeClient.begin(); timeClient.update(); if(tmz==0) timeClient.setTimeOffset(-3 * 3600); @@ -130,8 +131,9 @@ bool wifiConnect(String ssid, int encryptation, bool isAP) { IPAddress AP_GATEWAY(172, 0, 0, 1); WiFi.mode(WIFI_AP); WiFi.softAPConfig(AP_GATEWAY, AP_GATEWAY, IPAddress(255, 255, 255, 0)); - WiFi.softAP("BruceNet", "brucenet", 6,0,4,false); - Serial.print("IP: "); Serial.println(WiFi.softAPIP()); + WiFi.softAP("BruceNet", "brucenet", 6,0,4,false); // TODO: customize options via bruce.conf + wifiIP = WiFi.softAPIP().toString(); // update global var + Serial.print("IP: "); Serial.println(wifiIP); wifiConnected=true; return true; } diff --git a/src/main.cpp b/src/main.cpp index 0e32e03e..16998699 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,6 +36,7 @@ bool interpreter_start = false; bool sdcardMounted = false; bool gpsConnected = false; bool wifiConnected = false; +String wifiIP = ""; bool BLEConnected = false; bool returnToMenu; bool isSleeping = false; @@ -125,6 +126,7 @@ void setup_gpio() { #if defined(BACKLIGHT) pinMode(BACKLIGHT, OUTPUT); #endif + //if(RfModule==1) initCC1101once(&sdcardSPI); // Sets GPIO in the CC1101 lib } @@ -307,6 +309,7 @@ void startup_sound() { ** Where the devices are started and variables set *********************************************************************/ void setup() { + Serial.setRxBufferSize(SAFE_STACK_BUFFER_SIZE); // Must be invoked before Serial.begin(). Default is 256 chars Serial.begin(115200); log_d("Total heap: %d", ESP.getHeapSize()); @@ -363,7 +366,7 @@ void loop() { interpreter_start=false; interpreter(); previousMillis = millis(); // ensure that will not dim screen when get back to menu - goto END; + //goto END; } #endif tft.fillRect(0,0,WIDTH,HEIGHT,BGCOLOR); diff --git a/src/modules/bjs_interpreter/interpreter.cpp b/src/modules/bjs_interpreter/interpreter.cpp index 101ae5d4..6789a41b 100644 --- a/src/modules/bjs_interpreter/interpreter.cpp +++ b/src/modules/bjs_interpreter/interpreter.cpp @@ -2,7 +2,14 @@ #include "core/sd_functions.h" #include "core/wifi_common.h" #include "core/mykeyboard.h" +#include "core/serialcmds.h" +#include "core/display.h" +#include "modules/rf/rf.h" +#include "modules/ir/ir_read.h" +#include "modules/others/bad_usb.h" +//#include // used for badusbPressSpecial +//USBHIDConsumerControl cc; String headers[20]; String script = "drawString('Something wrong.', 4, 4);"; @@ -19,7 +26,7 @@ static duk_ret_t native_load(duk_context *ctx) { } static duk_ret_t native_print(duk_context *ctx) { - Serial.print(duk_to_string(ctx, 0)); + Serial.println(duk_to_string(ctx, 0)); return 0; } @@ -53,6 +60,15 @@ static duk_ret_t native_getBattery(duk_context *ctx) { duk_push_int(ctx, bat); return 1; } + +// terminate the script +static duk_ret_t native_exit(duk_context *ctx) { + duk_error(ctx, DUK_ERR_ERROR, "Script exited"); + interpreter_start=false; + return 0; +} + + static duk_ret_t native_getBoard(duk_context *ctx) { String board = "Undefined"; #if defined(STICK_C_PLUS) @@ -191,11 +207,30 @@ static duk_ret_t native_drawFillRect(duk_context *ctx) { return 0; } +static duk_ret_t native_drawLine(duk_context *ctx) { + // usage: drawLine(int16_t x, int16_t y, int16_t x2, int16_t y2, uint16_t color) + tft.drawLine(duk_to_int(ctx, 0),duk_to_int(ctx, 1),duk_to_int(ctx, 2),duk_to_int(ctx, 3),duk_to_int(ctx, 4)); + return 0; +} + +static duk_ret_t native_drawPixel(duk_context *ctx) { + // usage: drawPixel(int16_t x, int16_t y, uint16_t color) + tft.drawPixel(duk_to_int(ctx, 0),duk_to_int(ctx, 1),duk_to_int(ctx, 2)); + return 0; +} + static duk_ret_t native_drawString(duk_context *ctx) { + // drawString(const char *string, int32_t x, int32_t y) tft.drawString(duk_to_string(ctx, 0),duk_to_int(ctx, 1),duk_to_int(ctx, 2)); return 0; } +static duk_ret_t native_fillScreen(duk_context *ctx) { + // fill the screen with the passed color + tft.fillScreen(duk_to_int(ctx, 0)); + return 0; +} + static duk_ret_t native_width(duk_context *ctx) { int width = tft.width(); duk_push_int(ctx, width); @@ -284,6 +319,369 @@ static duk_ret_t native_getKeysPressed(duk_context *ctx) { return 1; } + +// Serial functions + +static duk_ret_t native_serialReadln(duk_context *ctx) { + // usage: serialReadln(); // default to 10s timeout + // usage: serialReadln(timeout_in_ms : number); + String line ; + int maxloops = 1000*10; + if(duk_is_number(ctx, 0)) + maxloops = duk_to_number(ctx, 0); + Serial.flush(); + while (maxloops) { + if (!Serial.available()) { + maxloops -= 1; + delay(1); + continue; + } + // data is ready to read + line = Serial.readStringUntil('\n'); + } + duk_push_string(ctx, line.c_str()); + return 1; +} + +static duk_ret_t native_serialCmd(duk_context *ctx) { + bool r = processSerialCommand(String(duk_to_string(ctx, 0))); + duk_push_boolean(ctx, r); + return 1; +} + +static duk_ret_t native_playAudioFile(duk_context *ctx) { + // usage: playAudioFile(filename : string); + // returns: bool==true on success, false on any error + // MEMO: no need to check for board support (done in processSerialCommand) + bool r = processSerialCommand("music_player " + String(duk_to_string(ctx, 0))); + duk_push_boolean(ctx, r); + return 1; +} + +static duk_ret_t native_tone(duk_context *ctx) { + // usage: tone(frequency : number); + // usage: tone(frequency : number, duration : number); + // returns: bool==true on success, false on any error + // MEMO: no need to check for board support (done in processSerialCommand) + bool r = false; + if(duk_is_string(ctx, 1)) + r = processSerialCommand("tone " + String(duk_to_string(ctx, 0)) + " " + String(duk_to_string(ctx, 1))); + else + r = processSerialCommand("tone " + String(duk_to_string(ctx, 0))); + duk_push_boolean(ctx, r); + return 1; +} + +static duk_ret_t native_irTransmitFile(duk_context *ctx) { + // usage: irTransmitFile(filename : string); + // returns: bool==true on success, false on any error + bool r = processSerialCommand("ir tx_from_file " + String(duk_to_string(ctx, 0))); + duk_push_boolean(ctx, r); + return 1; +} + +static duk_ret_t native_subghzTransmitFile(duk_context *ctx) { + // usage: subghzTransmitFile(filename : string); + // returns: bool==true on success, false on any error + bool r = processSerialCommand("subghz tx_from_file " + String(duk_to_string(ctx, 0))); + duk_push_boolean(ctx, r); + return 1; +} + +static duk_ret_t native_badusbRunFile(duk_context *ctx) { + // usage: badusbRunFile(filename : string); + // returns: bool==true on success, false on any error + // MEMO: no need to check for board support (done in processSerialCommand) + bool r = processSerialCommand("badusb tx_from_file " + String(duk_to_string(ctx, 0))); + duk_push_boolean(ctx, r); + return 1; +} + +// badusb functions + +static duk_ret_t native_badusbSetup(duk_context *ctx) { + // usage: badusbSetup(); + // returns: bool==true on success, false on any error + #if defined(USB_as_HID) + Kb.begin(); + //cc.begin(); + USB.begin(); + duk_push_boolean(ctx, true); + #else + duk_push_boolean(ctx, false); + #endif + return 1; +} + +/* +static duk_ret_t native_badusbQuit(duk_context *ctx) { + // usage: badusbQuit(); + // returns: quit keyboard mode, reinit serial port + #if defined(USB_as_HID) + Kb.end(); + //cc.begin(); + USB.~ESPUSB(); // Explicit call to destructor + Serial.begin(115200); // need to reinit serial when finished + duk_push_boolean(ctx, true); + #else + duk_push_boolean(ctx, false); + #endif + return 1; +} +* */ + +static duk_ret_t native_badusbPrint(duk_context *ctx) { + // usage: badusbPrint(msg : string); + #if defined(USB_as_HID) + Kb.print(duk_to_string(ctx, 0)); + #endif + return 0; +} + +static duk_ret_t native_badusbPrintln(duk_context *ctx) { + // usage: badusbPrintln(msg : string); + #if defined(USB_as_HID) + Kb.println(duk_to_string(ctx, 0)); + #endif + return 0; +} + +static duk_ret_t native_badusbPress(duk_context *ctx) { + // usage: badusbPress(keycode_number); + // keycodes list: https://github.com/espressif/arduino-esp32/blob/master/libraries/USB/src/USBHIDKeyboard.h + #if defined(USB_as_HID) + Kb.press(duk_to_number(ctx, 0)); + delay(1); + Kb.release(duk_to_number(ctx, 0)); + #endif + return 0; +} + +static duk_ret_t native_badusbHold(duk_context *ctx) { + // usage: badusbHold(keycode : number); + #if defined(USB_as_HID) + Kb.press(duk_to_number(ctx, 0)); + #endif + return 0; +} + +static duk_ret_t native_badusbRelease(duk_context *ctx) { + // usage: badusbHold(keycode : number); + #if defined(USB_as_HID) + Kb.release(duk_to_number(ctx, 0)); + #endif + return 0; +} + +static duk_ret_t native_badusbReleaseAll(duk_context *ctx) { + #if defined(USB_as_HID) + Kb.releaseAll(); + #endif + return 0; +} + +static duk_ret_t native_badusbPressRaw(duk_context *ctx) { + // usage: badusbPressRaw(keycode_number); + // keycodes list: TinyUSB's HID_KEY_* macros https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h + #if defined(USB_as_HID) + Kb.pressRaw(duk_to_number(ctx, 0)); + delay(1); + Kb.releaseRaw(duk_to_number(ctx, 0)); + #endif + return 0; +} + +/* +static duk_ret_t native_badusbPressSpecial(duk_context *ctx) { + // usage: badusbPressSpecial(keycode_number); + // keycodes list: https://github.com/espressif/arduino-esp32/blob/master/libraries/USB/src/USBHIDConsumerControl.h + #if defined(USB_as_HID) + cc.press(duk_to_number(ctx, 0)); + delay(10); + cc.release(); + //cc.end(); + #endif + return 0; +} +*/ + +// IR functions + +static duk_ret_t native_irRead(duk_context *ctx) { + // usage: irRead(); + // usage: irRead(timeout_in_seconds : number); + // returns a string of the generated ir file, empty string on timeout or other errors + IrRead i = IrRead(true); // true == headless mode + String r = ""; + if(duk_is_number(ctx, 0)) + r = i.loop_headless(duk_to_number(ctx, 0)); // custom timeout + else + r = i.loop_headless(10); // 10s timeout + duk_push_string(ctx, r.c_str()); + return 1; +} + +static duk_ret_t native_irReadRaw(duk_context *ctx) { + // usage: irReadRaw(); + // usage: irRead(timeout_in_seconds : number); + // returns a string of the generated ir file, empty string on timeout or other errors + IrRead i = IrRead(true, true); // true == headless mode, true==raw mode + String r = ""; + if(duk_is_number(ctx, 0)) + r = i.loop_headless(duk_to_number(ctx, 0)); // custom timeout + else + r = i.loop_headless(10); // 10s timeout + duk_push_string(ctx, r.c_str()); + return 1; +} + +// Subghz functions + +static duk_ret_t native_subghzRead(duk_context *ctx) { + // usage: subghzRead(); + // usage: subghzRead(timeout_in_seconds : number); + // returns a string of the generated sub file, empty string on timeout or other errors (decoding failed) + String r = ""; + if(duk_is_number(ctx, 0)) + r = RCSwitch_Read(RfFreq, duk_to_number(ctx, 0)); // custom timeout + else + r = RCSwitch_Read(RfFreq, 10); + duk_push_string(ctx, r.c_str()); + return 1; +} + +static duk_ret_t native_subghzReadRaw(duk_context *ctx) { + String r = ""; + if(duk_is_number(ctx, 0)) + r = RCSwitch_Read(RfFreq, duk_to_number(ctx, 0), true); // custom timeout + else + r = RCSwitch_Read(RfFreq, 10, true); + duk_push_string(ctx, r.c_str()); + return 1; +} + + +static duk_ret_t native_subghzSetFrequency(duk_context *ctx) { + // usage: subghzSetFrequency(freq_as_float); + if(duk_is_number(ctx, 0)) + RfFreq = duk_to_number(ctx, 0); // global var + return 0; +} + +// Dialog functions + +static duk_ret_t native_dialogMessage(duk_context *ctx) { + // usage: dialogMessage(msg : string) + displayInfo(String(duk_to_string(ctx, 0))); + while(!checkAnyKeyPress()) delay(100); + return 0; +} + +static duk_ret_t native_dialogError(duk_context *ctx) { + // usage: dialogError(msg : string) + displayError(String(duk_to_string(ctx, 0))); + while(!checkAnyKeyPress()) delay(100); + return 0; +} + +static duk_ret_t native_dialogPickFile(duk_context *ctx) { + // usage: dialogPickFile() : string + // usage: dialogPickFile(path : string) : string + // returns: selected file , empty string if cancelled + String r = ""; + String filepath = "/"; + if(duk_is_string(ctx, 0)) { + filepath = String(duk_to_string(ctx, 0)); + if(!filepath.startsWith("/")) filepath = "/" + filepath; // add "/" if missing + } + FS* fs = NULL; + if(SD.exists(filepath)) fs = &SD; + if(LittleFS.exists(filepath)) fs = &LittleFS; + if(fs) { + r = loopSD(*fs, true); + } + duk_push_string(ctx, r.c_str()); + return 1; +} + +static duk_ret_t native_dialogViewFile(duk_context *ctx) { + // usage: dialogViewFile(path : string) + // returns: nothing + if(duk_is_string(ctx, 0)) { + String filepath = String(duk_to_string(ctx, 0)); + if(!filepath.startsWith("/")) filepath = "/" + filepath; // add "/" if missing + FS* fs = NULL; + if(SD.exists(filepath)) fs = &SD; + if(LittleFS.exists(filepath)) fs = &LittleFS; + if(fs) { + viewFile(*fs, filepath); + } + } + return 0; +} + +static duk_ret_t native_keyboard(duk_context *ctx) { + // usage: keyboard() : string + // usage: keyboard(title : string) : string + // usage: keyboard(title : string, maxlen : number) : string + // usage: keyboard(title : string, maxlen : number, initval : string) : string + // returns: text typed by the user + String r = ""; + if(!duk_is_string(ctx, 0)) + r = keyboard(""); + else if(!duk_is_number(ctx, 1)) + r = keyboard(String(duk_to_string(ctx, 0))); + else if(!duk_is_string(ctx, 2)) + r = keyboard(String(duk_to_string(ctx, 0)), duk_to_number(ctx, 1)); + else + r = keyboard(String(duk_to_string(ctx, 0)), duk_to_number(ctx, 1), String(duk_to_string(ctx, 2))); + duk_push_string(ctx, r.c_str()); + return 1; +} + + +// Storage functions + +static duk_ret_t native_storageRead(duk_context *ctx) { + // usage: storageRead(filename : string) + // returns: file contents as a string. Empty string on any error. + String r = ""; + if(duk_is_string(ctx, 0)) { + String filepath = String(duk_to_string(ctx, 0)); + if(!filepath.startsWith("/")) filepath = "/" + filepath; // add "/" if missing + if(SD.exists(filepath)) r = readSmallFile(SD, filepath); + if(LittleFS.exists(filepath)) r = readSmallFile(LittleFS, filepath); + } + duk_push_string(ctx, r.c_str()); + return 1; +} + +static duk_ret_t native_storageWrite(duk_context *ctx) { + // usage: storageWrite(filename : string, data : string) + // The write function writes a string to a file, returning true if successful. Overwrites existing file. + // The first parameter is the path of the file. + // The second parameter is the contents to write + bool r = false; + if(duk_is_string(ctx, 0) && duk_is_string(ctx, 1)) { + String filepath = String(duk_to_string(ctx, 0)); + String data = String(duk_to_string(ctx, 1)); + if(!filepath.startsWith("/")) filepath = "/" + filepath; // add "/" if missing + FS* fs = &LittleFS; // default fallback + if(SD.exists(filepath)) fs = &SD; + if(LittleFS.exists(filepath)) fs = &LittleFS; + if(!fs && sdcardMounted) fs = &SD; + File f = fs->open(filepath, FILE_APPEND, true); // create if it does not exist, append otherwise + if(f) { + f.write((const uint8_t*) data.c_str(), data.length()); + f.close(); + r = true; // success + } + } + duk_push_boolean(ctx, r); + return 1; +} + + // Read script file String readScriptFile(FS fs, String filename) { String fileError = "drawString('No boot.js file.', 4, 4);"; @@ -304,8 +702,13 @@ String readScriptFile(FS fs, String filename) { return s; } // Code interpreter, must be called in the loop() function to work -void interpreter() { +bool interpreter() { + /* if(!checkPrevPress() && !checkNextPress()) interpreter_start=false; + #if defined (CARDPUTER) + if(checkEscPress()) interpreter_start=false; + #endif + * */ tft.fillRect(0,0,WIDTH,HEIGHT,TFT_BLACK); tft.setRotation(rotation); tft.setTextSize(FM); @@ -326,6 +729,8 @@ void interpreter() { duk_put_global_string(ctx, "digitalWrite"); duk_push_c_function(ctx, native_pinMode, 2); duk_put_global_string(ctx, "pinMode"); + duk_push_c_function(ctx, native_exit, 0); + duk_put_global_string(ctx, "exit"); // Get Informations from the board duk_push_c_function(ctx, native_getBattery, 0); @@ -340,7 +745,8 @@ void interpreter() { duk_push_c_function(ctx, native_wifiDisconnect, 0); duk_put_global_string(ctx, "wifiDisconnect"); duk_push_c_function(ctx, native_get, 2); - duk_put_global_string(ctx, "httpGet"); + duk_put_global_string(ctx, "httpGet"); + // TODO: list wifi stations, get mac addresses // Graphics duk_push_c_function(ctx, native_color, 3); @@ -353,8 +759,16 @@ void interpreter() { duk_put_global_string(ctx, "drawRect"); duk_push_c_function(ctx, native_drawFillRect, 5); duk_put_global_string(ctx, "drawFillRect"); + duk_push_c_function(ctx, native_drawLine, 5); + duk_put_global_string(ctx, "drawLine"); duk_push_c_function(ctx, native_drawString, 3); duk_put_global_string(ctx, "drawString"); + duk_push_c_function(ctx, native_drawPixel, 3); + duk_put_global_string(ctx, "drawPixel"); + // TODO: drawBitmap(filename:string, x, y) + duk_push_c_function(ctx, native_fillScreen, 1); + duk_put_global_string(ctx, "fillScreen"); + duk_push_c_function(ctx, native_width, 0); duk_put_global_string(ctx, "width"); duk_push_c_function(ctx, native_height, 0); @@ -368,20 +782,102 @@ void interpreter() { duk_push_c_function(ctx, native_getSelPress, 0); // checkSelPress duk_put_global_string(ctx, "getSelPress"); duk_push_c_function(ctx, native_getNextPress, 0); // checkNextPress - duk_put_global_string(ctx, "getNextPress"); - + duk_put_global_string(ctx, "getNextPress"); + + // Serial + wrappers + duk_push_c_function(ctx, native_serialReadln, 0); + duk_put_global_string(ctx, "serialReadln"); + duk_push_c_function(ctx, native_serialCmd, 1); + duk_put_global_string(ctx, "serialCmd"); + duk_push_c_function(ctx, native_playAudioFile, 1); + duk_put_global_string(ctx, "playAudioFile"); + duk_push_c_function(ctx, native_tone, 2); + duk_put_global_string(ctx, "tone"); + duk_push_c_function(ctx, native_irTransmitFile, 1); + duk_put_global_string(ctx, "irTransmitFile"); + duk_push_c_function(ctx, native_subghzTransmitFile, 1); + duk_put_global_string(ctx, "subghzTransmitFile"); + duk_push_c_function(ctx, native_badusbRunFile, 1); + duk_put_global_string(ctx, "badusbRunFile"); + + // badusb functions + duk_push_c_function(ctx, native_badusbSetup, 0); + duk_put_global_string(ctx, "badusbSetup"); + duk_push_c_function(ctx, native_badusbPrint, 1); + duk_put_global_string(ctx, "badusbPrint"); + duk_push_c_function(ctx, native_badusbPrintln, 1); + duk_put_global_string(ctx, "badusbPrintln"); + duk_push_c_function(ctx, native_badusbPress, 1); + duk_put_global_string(ctx, "badusbPress"); + duk_push_c_function(ctx, native_badusbHold, 1); + duk_put_global_string(ctx, "badusbHold"); + duk_push_c_function(ctx, native_badusbRelease, 1); + duk_put_global_string(ctx, "badusbRelease"); + duk_push_c_function(ctx, native_badusbReleaseAll, 0); + duk_put_global_string(ctx, "badusbReleaseAll"); + duk_push_c_function(ctx, native_badusbPressRaw, 1); + duk_put_global_string(ctx, "badusbPressRaw"); + //duk_push_c_function(ctx, native_badusbPressSpecial, 1); + //duk_put_global_string(ctx, "badusbPressSpecial"); + + // IR functions + duk_push_c_function(ctx, native_irRead, 0); + duk_put_global_string(ctx, "irRead"); + duk_push_c_function(ctx, native_irReadRaw, 0); + duk_put_global_string(ctx, "irReadRaw"); + //TODO: native_irTransmit(string) + + // subghz functions + duk_push_c_function(ctx, native_subghzRead, 0); + duk_put_global_string(ctx, "subghzRead"); + duk_push_c_function(ctx, native_subghzReadRaw, 0); + duk_put_global_string(ctx, "subghzReadRaw"); + duk_push_c_function(ctx, native_subghzSetFrequency, 1); + duk_put_global_string(ctx, "subghzSetFrequency"); + //duk_put_global_string(ctx, "subghzSetIdle"); + // TODO: subghzTransmit(string) + + // Dialog functions + duk_push_c_function(ctx, native_dialogMessage, 1); + duk_put_global_string(ctx, "dialogMessage"); + duk_push_c_function(ctx, native_dialogError, 1); + duk_put_global_string(ctx, "dialogError"); + duk_push_c_function(ctx, native_dialogPickFile, 1); + duk_put_global_string(ctx, "dialogPickFile"); + // TODO: dialogChoice(array) + duk_push_c_function(ctx, native_dialogViewFile, 1); + duk_put_global_string(ctx, "dialogViewFile"); + duk_push_c_function(ctx, native_keyboard, 3); + duk_put_global_string(ctx, "keyboard"); + + // Storage functions + duk_push_c_function(ctx, native_storageRead, 1); + duk_put_global_string(ctx, "storageRead"); + duk_push_c_function(ctx, native_storageWrite, 2); + duk_put_global_string(ctx, "storageWrite"); + // TODO: wrap more serial storage cmd: mkdir, remove, ... + + // TODO: match flipper syntax https://github.com/jamisonderek/flipper-zero-tutorials/wiki/JavaScript + // https://github.com/jamisonderek/flipper-zero-tutorials/wiki/JavaScript + // MEMO: API https://github.com/joeqread/arduino-duktape/blob/main/src/duktape.h + + bool r; + duk_push_string(ctx, script.c_str()); if (duk_peval(ctx) != 0) { printf("eval failed: %s\n", duk_safe_to_string(ctx, -1)); + r = false; } else { printf("result is: %s\n", duk_safe_to_string(ctx, -1)); + r = true; } duk_pop(ctx); // Clean up. duk_destroy_heap(ctx); - delay(1000); + //delay(1000); + return r; } // function to start the JS Interpreterm choosinng the file, processing and start @@ -407,3 +903,19 @@ void run_bjs_script() { // To stop the script, press Prev and Next together for a few seconds } +bool run_bjs_script_headless(String code) { + script = code; + returnToMenu=true; + interpreter_start=true; + //while(interpreter_start) { + interpreter(); + // delay(1); + //} + interpreter_start=false; + return true; +} + +bool run_bjs_script_headless(FS fs, String filename) { + String code = readScriptFile(fs, filename); + return run_bjs_script_headless(code); +} diff --git a/src/modules/bjs_interpreter/interpreter.h b/src/modules/bjs_interpreter/interpreter.h index eb3cf14b..f9816047 100644 --- a/src/modules/bjs_interpreter/interpreter.h +++ b/src/modules/bjs_interpreter/interpreter.h @@ -13,4 +13,8 @@ void run_bjs_script(); -void interpreter(); \ No newline at end of file +bool interpreter(); + +bool run_bjs_script_headless(String code); +bool run_bjs_script_headless(FS fs, String filename); + diff --git a/src/modules/ir/ir_read.cpp b/src/modules/ir/ir_read.cpp index b7b833a2..3ca4d181 100644 --- a/src/modules/ir/ir_read.cpp +++ b/src/modules/ir/ir_read.cpp @@ -8,6 +8,7 @@ */ #include +//#include #include "ir_read.h" #include "core/globals.h" #include "core/mykeyboard.h" @@ -21,8 +22,20 @@ #define DUTY_CYCLE 0.330000 -IrRead::IrRead(bool headless_mode) { +String uint32ToString(uint32_t value) { + char buffer[12] = {0}; // 8 hex digits + 3 spaces + 1 null terminator + snprintf(buffer, sizeof(buffer), "%02X %02X %02X %02X", + value & 0xFF, + (value >> 8) & 0xFF, + (value >> 16) & 0xFF, + (value >> 24) & 0xFF); + return String(buffer); +} + + +IrRead::IrRead(bool headless_mode, bool raw_mode) { headless = headless_mode; + raw = raw_mode; setup(); } @@ -39,6 +52,7 @@ void IrRead::setup() { pinMode(IrRx, INPUT); if(headless) return; + // else begin(); return loop(); } @@ -99,18 +113,22 @@ void IrRead::display_btn_options() { padprintln("Press [ESC] to exit"); } -void IrRead::dump_signal_details() { - padprint("HEX: 0x"); - tft.println(results.value, HEX); -} - void IrRead::read_signal() { if (_read_signal || !irrecv.decode(&results)) return; _read_signal = true; + + // switch to raw mode if decoding failed + if(results.decode_type == decode_type_t::UNKNOWN ) raw = true; + // TODO: show a dialog/warning? + // { bool raw = yesNoDialog("decoding failed, save as RAW?") } display_banner(); - dump_signal_details(); + + // dump signal details + padprint("HEX: 0x"); + tft.println(results.value, HEX); + display_btn_options(); delay(500); @@ -128,7 +146,7 @@ void IrRead::save_signal() { String btn_name = keyboard("Btn"+String(signals_read), 30, "Btn name:"); - append_to_file_str(btn_name, parse_signal()); + append_to_file_str(btn_name); signals_read++; @@ -136,7 +154,7 @@ void IrRead::save_signal() { delay(100); } -String IrRead::parse_signal() { +String IrRead::parse_raw_signal() { rawcode = new uint16_t[MAX_RAWBUF_SIZE]; memset(rawcode, 0, MAX_RAWBUF_SIZE * sizeof(uint16_t)); raw_data_len = results.rawlen; @@ -154,13 +172,77 @@ String IrRead::parse_signal() { return signal_code; } -void IrRead::append_to_file_str(String btn_name, String signal_code) { - strDeviceContent += "#\n"; +void IrRead::append_to_file_str(String btn_name) { strDeviceContent += "name: " + btn_name + "\n"; - strDeviceContent += "type: raw\n"; - strDeviceContent += "frequency: " + String(IR_FREQUENCY) + "\n"; - strDeviceContent += "duty_cycle: " + String(DUTY_CYCLE) + "\n"; - strDeviceContent += "data: " + String(signal_code) + "\n"; + + if(raw) { + strDeviceContent += "type: raw\n"; + strDeviceContent += "frequency: " + String(IR_FREQUENCY) + "\n"; + strDeviceContent += "duty_cycle: " + String(DUTY_CYCLE) + "\n"; + strDeviceContent += "data: " + parse_raw_signal() + "\n"; + } else { + // parsed signal https://github.com/jamisonderek/flipper-zero-tutorials/wiki/Infrared + strDeviceContent += "type: parsed\n"; + switch (results.decode_type) { + case decode_type_t::RC5: + { + if(results.command > 0x3F ) + strDeviceContent += "protocol: RC5X\n"; + else + strDeviceContent += "protocol: RC5\n"; + break; + } + case decode_type_t::RC6: + { + strDeviceContent += "protocol: RC6\n"; + break; + } + case decode_type_t::SAMSUNG: + { + strDeviceContent += "protocol: Samsung32\n"; + break; + } + case decode_type_t::SONY: + { + // check address and command ranges to find the exact protocol + if(results.address>0xFF) + strDeviceContent += "protocol: SIRC20\n"; + else if(results.address>0x1F) + strDeviceContent += "protocol: SIRC15\n"; + else + strDeviceContent += "protocol: SIRC\n"; + break; + } + case decode_type_t::NEC: + { + // check address and command ranges to find the exact protocol + if(results.address>0xFFFF) + strDeviceContent += "protocol: NEC42ext\n"; + else if(results.address>0xFF1F) + strDeviceContent += "protocol: NECext\n"; + else if(results.address>0xFF) + strDeviceContent += "protocol: NEC42\n"; + else + strDeviceContent += "protocol: NEC\n"; + break; + } + // TODO: more protocols? + default: + { + Serial.println("unsupported protocol, try raw mode"); + return; + } + } + // + strDeviceContent += "address: " + uint32ToString(results.address) + "\n"; + strDeviceContent += "command: " + uint32ToString(results.command) + "\n"; + + //Serial.println("bits:"); + //Serial.println(results.bits); + //Serial.println("value:"); + //serialPrintUint64(results.value, HEX); + } + strDeviceContent += "#\n"; } void IrRead::save_device() { @@ -204,26 +286,35 @@ void IrRead::save_device() { } -bool IrRead::loop_headless(int max_loops) { +String IrRead::loop_headless(int max_loops) { while (!irrecv.decode(&results)) { max_loops -= 1; if(max_loops <= 0) { Serial.println("timeout"); - return false; // nothing received + return ""; // nothing received } delay(1000); } - - append_to_file_str("??", parse_signal()); - - Serial.println("Filetype: Bruce IR File"); - Serial.println("Version 1"); - Serial.println("#"); - Serial.print(strDeviceContent); irrecv.disableIRIn(); - return true; + + if(!raw && results.decode_type == decode_type_t::UNKNOWN ) + { + Serial.println("# decoding failed, try raw mode"); + return ""; + } + + String r = "Filetype: Bruce IR File\n"; + r += "Version 1\n"; + r += "#\n"; + r += "#\n"; + + strDeviceContent = ""; + append_to_file_str("??"); // writes on strDeviceContent + r += strDeviceContent; + + return r; } bool IrRead::write_file(String filename, FS* fs) { diff --git a/src/modules/ir/ir_read.h b/src/modules/ir/ir_read.h index d1f741ba..de008c7c 100644 --- a/src/modules/ir/ir_read.h +++ b/src/modules/ir/ir_read.h @@ -18,7 +18,7 @@ class IrRead { ///////////////////////////////////////////////////////////////////////////////////// // Constructor ///////////////////////////////////////////////////////////////////////////////////// - IrRead(bool headless_mode=false); + IrRead(bool headless_mode=false, bool raw_mode=false); /////////////////////////////////////////////////////////////////////////////////// // Arduino Life Cycle @@ -26,7 +26,7 @@ class IrRead { void setup(); void loop(); - bool loop_headless(int max_loops); + String loop_headless(int max_loops); private: bool _read_signal = false; @@ -36,24 +36,24 @@ class IrRead { int signals_read = 0; String strDeviceContent = ""; bool headless = false; + bool raw = false; ///////////////////////////////////////////////////////////////////////////////////// // Display functions ///////////////////////////////////////////////////////////////////////////////////// - void cls(); - void display_banner(); - void display_btn_options(); - void dump_signal_details(); + void cls(); + void display_banner(); + void display_btn_options(); ///////////////////////////////////////////////////////////////////////////////////// // Operations ///////////////////////////////////////////////////////////////////////////////////// - void begin(); + void begin(); void read_signal(); void save_device(); void save_signal(); void discard_signal(); - void append_to_file_str(String btn_name, String signal_code); - bool write_file(String filename, FS* fs); - String parse_signal(); + void append_to_file_str(String btn_name); + bool write_file(String filename, FS* fs); + String parse_raw_signal(); }; diff --git a/src/modules/others/bad_usb.cpp b/src/modules/others/bad_usb.cpp index f8e6792c..31a4653b 100644 --- a/src/modules/others/bad_usb.cpp +++ b/src/modules/others/bad_usb.cpp @@ -270,7 +270,23 @@ void usb_setup() { } +//#include // https://github.com/chegewara/EspTinyUSB 1.3.4 +void key_input_from_string(String text) { + + Kb.begin(); + USB.begin(); + + Kb.print(text.c_str()); // buggy with some special chars + + //Kb.end(); + + /* + HIDcomposite KeyboardMouse; + KeyboardMouse.begin(); + KeyboardMouse.sendString(text+"\n"); + * */ +} #if defined(CARDPUTER) //Now cardputer works as a USB Keyboard! diff --git a/src/modules/others/bad_usb.h b/src/modules/others/bad_usb.h index 038e4a71..755fe5d8 100644 --- a/src/modules/others/bad_usb.h +++ b/src/modules/others/bad_usb.h @@ -11,6 +11,7 @@ extern USBHIDKeyboard Kb; void key_input(FS fs, String bad_script = "/badpayload.txt"); +void key_input_from_string(String text); void usb_setup(); diff --git a/src/modules/others/webInterface.cpp b/src/modules/others/webInterface.cpp index c90345f8..15049b67 100644 --- a/src/modules/others/webInterface.cpp +++ b/src/modules/others/webInterface.cpp @@ -4,6 +4,7 @@ #include "core/mykeyboard.h" // using keyboard when calling rename #include "core/display.h" // using displayRedStripe as error msg #include "core/serialcmds.h" +#include "core/passwords.h" #include "webInterface.h" @@ -133,8 +134,10 @@ String listFiles(FS fs, bool ishtml, String folder, bool isLittleFS) { //if (String(foundfile.name()).substring(String(foundfile.name()).lastIndexOf('.') + 1).equalsIgnoreCase("bin")) returnText+= "  \n"; if (String(foundfile.name()).substring(String(foundfile.name()).lastIndexOf('.') + 1).equalsIgnoreCase("sub")) returnText+= "  \n"; if (String(foundfile.name()).substring(String(foundfile.name()).lastIndexOf('.') + 1).equalsIgnoreCase("ir")) returnText+= "  \n"; + if (String(foundfile.name()).substring(String(foundfile.name()).lastIndexOf('.') + 1).equalsIgnoreCase("js")) returnText+= "  \n"; + if (String(foundfile.name()).substring(String(foundfile.name()).lastIndexOf('.') + 1).equalsIgnoreCase("bjs")) returnText+= "  \n"; #if defined(USB_as_HID) - if (String(foundfile.name()).substring(String(foundfile.name()).lastIndexOf('.') + 1).equalsIgnoreCase("txt")) returnText+= "  \n"; + if (String(foundfile.name()).substring(String(foundfile.name()).lastIndexOf('.') + 1).equalsIgnoreCase("txt")) returnText+= "  \n"; #endif returnText += "  \n"; returnText += "\n\n"; @@ -197,21 +200,40 @@ bool checkUserWebAuth() { void handleFileUpload(FS fs) { HTTPUpload& upload = server->upload(); String filename = upload.filename; + if (server->hasArg("password")) filename = filename + ".enc"; if (upload.status == UPLOAD_FILE_START) { if (!filename.startsWith("/")) filename = "/" + filename; if (uploadFolder != "/") filename = uploadFolder + filename; fs.remove(filename); uploadFile = fs.open(filename, "w"); Serial.println("Upload Start: " + filename); - } else if (upload.status == UPLOAD_FILE_WRITE) { - if (uploadFile) uploadFile.write(upload.buf, upload.currentSize); - } else if (upload.status == UPLOAD_FILE_END) { - if (uploadFile) { + } else if (upload.status == UPLOAD_FILE_WRITE && uploadFile) { + if (server->hasArg("password")) { + // encryption requested + static int chunck_no = 0; + if(chunck_no != 0) { + // TODO: handle multiple chunks + server->send(404, "text/html", "file is too big"); + return; + } else chunck_no += 1; + String enc_password = server->arg("password"); + // upload to ram, encrypt, then write cypertext + //Serial.println(enc_password); + String plaintext = String((char*)upload.buf).substring(0, upload.currentSize); + //Serial.println(plaintext); + //Serial.println(upload.currentSize); + String cyphertxt = encryptString(plaintext, enc_password); + if(cyphertxt=="") return; + uploadFile.write((const uint8_t*) cyphertxt.c_str(), cyphertxt.length()); + } else { + // write directly + uploadFile.write(upload.buf, upload.currentSize); + } + } else if (upload.status == UPLOAD_FILE_END && uploadFile) { uploadFile.close(); Serial.println("Upload End: " + filename); server->sendHeader("Location", "/"); // Redireciona para a raiz server->send(303); - } } } diff --git a/src/modules/others/webInterface.h b/src/modules/others/webInterface.h index 8c668b18..557ba072 100644 --- a/src/modules/others/webInterface.h +++ b/src/modules/others/webInterface.h @@ -491,7 +491,21 @@ function sendSubFile(filePath) { listFilesButton(actualFolder, fs, true); } -function sendBadusbFile(filePath) { +function runJsFile(filePath) { + if(!confirm("Confirm executing the selected JS script?")) return; + var actualFolder = document.getElementById("actualFolder").value; + var fs = document.getElementById("actualFS").value; + const ajax5 = new XMLHttpRequest(); + const formdata5 = new FormData(); + formdata5.append("cmnd", "js " + filePath); + ajax5.open("POST", "/cm", false); + ajax5.send(formdata5); + document.getElementById("status").innerHTML = ajax5.responseText; + var fs = document.getElementById("actualFS").value; + listFilesButton(actualFolder, fs, true); +} + +function runBadusbFile(filePath) { if(!confirm("Confirm executing the selected DuckyScript on the machine connected via USB?")) return; var actualFolder = document.getElementById("actualFolder").value; var fs = document.getElementById("actualFS").value; @@ -556,6 +570,7 @@ function showUploadButtonFancy(folders) { "

Send file to " + folders + "

"+ "
" + "" + + " Encrypted
" + "
" + "" + "

" + @@ -568,14 +583,21 @@ function _(el) { return document.getElementById(el); } - +var cachedPassword=""; function uploadFile(folder) { var fs = document.getElementById("actualFS").value; var folder = _("folder").value; var files = _("file1").files; // Extract files from input element - + var formdata = new FormData(); + + var encrypted = _("encryptCheckbox").checked; + if(encrypted) { + cachedPassword = prompt("Enter encryption password (do not lose it): ", cachedPassword); + formdata.append("password", cachedPassword); + } + for (var i = 0; i < files.length; i++) { formdata.append("files[]", files[i]); // Append each file to form data } diff --git a/src/modules/rf/rf.cpp b/src/modules/rf/rf.cpp index 7c0b55ca..453f3143 100644 --- a/src/modules/rf/rf.cpp +++ b/src/modules/rf/rf.cpp @@ -484,7 +484,7 @@ bool initRfModule(String mode, float frequency) { } -bool RCSwitch_Read_Raw(float frequency, int max_loops) { +String RCSwitch_Read(float frequency, int max_loops, bool raw) { RCSwitch rcswitch = RCSwitch(); RfCodes received; @@ -498,7 +498,7 @@ bool RCSwitch_Read_Raw(float frequency, int max_loops) { RestartRec: // init receive - if(!initRfModule("rx", frequency)) return false; + if(!initRfModule("rx", frequency)) return ""; if(RfModule == 1) { // CC1101 in use #ifdef USE_CC1101_VIA_SPI #ifdef CC1101_GDO2_PIN @@ -508,18 +508,18 @@ bool RCSwitch_Read_Raw(float frequency, int max_loops) { Serial.println("CC1101 enableReceive()"); #endif #else - return false; + return ""; #endif } else { rcswitch.enableReceive(RfRx); } while(!checkEscPress()) { if(rcswitch.available()) { - Serial.println("Available"); + //Serial.println("Available"); long value = rcswitch.getReceivedValue(); - Serial.println("getReceivedValue()"); + //Serial.println("getReceivedValue()"); if(value) { - Serial.println("has value"); + //Serial.println("has value"); unsigned int* raw = rcswitch.getReceivedRawdata(); received.frequency=long(frequency*1000000); received.key=rcswitch.getReceivedValue(); @@ -527,15 +527,20 @@ bool RCSwitch_Read_Raw(float frequency, int max_loops) { received.preset=rcswitch.getReceivedProtocol(); received.te=rcswitch.getReceivedDelay(); received.Bit=rcswitch.getReceivedBitlength(); - received.filepath="Last copied"; - Serial.println(received.te*2); + received.filepath="unsaved"; + //Serial.println(received.te*2); + // derived from https://github.com/sui77/rc-switch/tree/master/examples/ReceiveDemo_Advanced received.data=""; - for(int i=0; i0) received.data+=" "; - received.data+=raw[i]; + if(i % 2 == 0) sign = +1; + else sign = -1; + received.data += String(sign * (int)raw[i]); } - Serial.println(received.protocol); - Serial.println(received.data); + //Serial.println(received.protocol); + //Serial.println(received.data); const char* b = dec2binWzerofill(received.key, received.Bit); decimalToHexString(received.key,hexString); // need to remove the extra padding 0s? @@ -560,21 +565,33 @@ bool RCSwitch_Read_Raw(float frequency, int max_loops) { previousMillis = millis(); } if(received.key>0) { - String subfile_out = "Filetype: Bruce SubGhz RAW File\nVersion 1\n"; + #ifndef HAS_SCREEN + // switch to raw mode if decoding failed + if(received.preset == 0) raw = true; + // TODO: show a dialog/warning? + // { bool raw = yesNoDialog("decoding failed, save as RAW?") } + #endif + + String subfile_out = "Filetype: Bruce SubGhz File\nVersion 1\n"; subfile_out += "Frequency: " + String(int(frequency*1000000)) + "\n"; - if(received.preset=="1") received.preset="FuriHalSubGhzPresetOok270Async"; - else if (received.preset=="2") received.preset="FuriHalSubGhzPresetOok650Async"; - subfile_out += "Preset: " + String(received.preset) + "\n"; - subfile_out += "Protocol: RcSwitch\n"; - subfile_out += "Bit: " + String(received.Bit) + "\n"; - subfile_out += "Key: " + String(hexString) + "\n"; - // subfile_out += "RAW_Data: " + received.data; // not in flipper pattern - subfile_out += "TE: " + String(received.te) + "\n"; + if(!raw) { + subfile_out += "Preset: " + String(received.preset) + "\n"; + subfile_out += "Protocol: RcSwitch\n"; + subfile_out += "Bit: " + String(received.Bit) + "\n"; + subfile_out += "Key: " + String(hexString) + "\n"; + subfile_out += "TE: " + String(received.te) + "\n"; + } else { + // save as raw + if(received.preset=="1") received.preset="FuriHalSubGhzPresetOok270Async"; + else if (received.preset=="2") received.preset="FuriHalSubGhzPresetOok650Async"; + subfile_out += "Preset: " + String(received.preset) + "\n"; + subfile_out += "Protocol: RAW\n"; + subfile_out += "RAW_Data: " + received.data; + } #ifndef HAS_SCREEN - // headless mode, just print the file on serial and quit - Serial.println(subfile_out); - return true; + // headless mode + return subfile_out; #endif if(checkSelPress()) { @@ -631,7 +648,7 @@ bool RCSwitch_Read_Raw(float frequency, int max_loops) { delay(1000); if(max_loops==0) { Serial.println("timeout"); - return false; + return ""; } } //#endif @@ -641,7 +658,7 @@ bool RCSwitch_Read_Raw(float frequency, int max_loops) { deinitRfModule(); - return true; + return ""; } diff --git a/src/modules/rf/rf.h b/src/modules/rf/rf.h index 487e3e64..a143c57d 100644 --- a/src/modules/rf/rf.h +++ b/src/modules/rf/rf.h @@ -18,7 +18,7 @@ void rf_jammerIntermittent(); void rf_jammerFull(); void otherRFcodes(); bool txSubFile(FS *fs, String filepath); -bool RCSwitch_Read_Raw(float frequency=0, int max_loops=-1); +String RCSwitch_Read(float frequency=0, int max_loops=-1, bool raw=false); void RCSwitch_send(uint64_t data, unsigned int bits, int pulse=0, int protocol=1, int repeat=10); void addToRecentCodes(struct RfCodes rfcode); void sendRfCommand(struct RfCodes rfcode); From 5ae39fe72961862bcc47d53180e223e96807da0a Mon Sep 17 00:00:00 2001 From: eadmaster <925171+eadmaster@users.noreply.github.com> Date: Sat, 7 Sep 2024 09:34:15 +0200 Subject: [PATCH 2/3] fixed esp32-s3-devkitc-1 build --- platformio.ini | 6 +++++- src/core/VectorDisplay.h | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 6202c7e2..6b521043 100644 --- a/platformio.ini +++ b/platformio.ini @@ -918,7 +918,11 @@ build_flags = ;-DNRF24_SCK_PIN=12 ;-DNRF24_MISO_PIN=13 - + -DSPI_SCK_PIN=12 + -DSPI_MOSI_PIN=11 + -DSPI_MISO_PIN=13 + -DSPI_SS_PIN=10 + lib_deps = ${common.lib_deps} diff --git a/src/core/VectorDisplay.h b/src/core/VectorDisplay.h index fcd0d8b4..b5f88487 100755 --- a/src/core/VectorDisplay.h +++ b/src/core/VectorDisplay.h @@ -115,6 +115,23 @@ class Stream : public Print { #define MESSAGE_BUTTON 'B' #define MESSAGE_ACK 'A' +//These enumerate the text plotting alignment (reference datum point) +#define TL_DATUM 0 // Top left (default) +#define TC_DATUM 1 // Top centre +#define TR_DATUM 2 // Top right +#define ML_DATUM 3 // Middle left +#define CL_DATUM 3 // Centre left, same as above +#define MC_DATUM 4 // Middle centre +#define CC_DATUM 4 // Centre centre, same as above +#define MR_DATUM 5 // Middle right +#define CR_DATUM 5 // Centre right, same as above +#define BL_DATUM 6 // Bottom left +#define BC_DATUM 7 // Bottom centre +#define BR_DATUM 8 // Bottom right +#define L_BASELINE 9 // Left character baseline (Line the 'A' character would sit on) +#define C_BASELINE 10 // Centre character baseline +#define R_BASELINE 11 // Right character baseline + typedef uint32_t FixedPoint32; #define TO_FP32(f) ((uint32_t)((f)*65536. + 0.5)) @@ -865,7 +882,9 @@ class VectorDisplayClass : public Print { textsize = size; textSize((FixedPoint32)size * 8 * 65536); } - + + void setTextDatum(uint8_t d) { } // mockup + void setTextColor(uint16_t f, uint16_t b) { textBackColor565(b); textForeColor565(f); From b8cab3efeeb50cb6b8cb0df23391f4b53e1d0cd3 Mon Sep 17 00:00:00 2001 From: eadmaster <925171+eadmaster@users.noreply.github.com> Date: Sat, 7 Sep 2024 10:01:10 +0200 Subject: [PATCH 3/3] added wifiScan and wifiConnect js functions (#131) --- src/core/serialcmds.cpp | 2 +- src/modules/bjs_interpreter/interpreter.cpp | 87 +++++++++++++++++++-- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/core/serialcmds.cpp b/src/core/serialcmds.cpp index e561da61..477b13a1 100644 --- a/src/core/serialcmds.cpp +++ b/src/core/serialcmds.cpp @@ -347,7 +347,7 @@ bool processSerialCommand(String cmd_str) { //Serial.print("frequency:"); //Serial.println(frequency); String r = ""; - if(cmd_str.startsWith("subghz rx_raw")) + if(cmd_str.startsWith("subghz rx_raw") || cmd_str.startsWith("subghz rx raw")) r = RCSwitch_Read(frequency, 10, true); // true -> raw mode else r = RCSwitch_Read(frequency, 10, false); // false -> decoded mode diff --git a/src/modules/bjs_interpreter/interpreter.cpp b/src/modules/bjs_interpreter/interpreter.cpp index 6789a41b..f9ef14bc 100644 --- a/src/modules/bjs_interpreter/interpreter.cpp +++ b/src/modules/bjs_interpreter/interpreter.cpp @@ -94,14 +94,85 @@ static duk_ret_t native_getBoard(duk_context *ctx) { } // Wifi Functions -static duk_ret_t native_wifiConnect(duk_context *ctx) { +static duk_ret_t native_wifiConnectDialog(duk_context *ctx) { wifiConnectMenu(); return 0; } + +static duk_ret_t native_wifiConnect(duk_context *ctx) { + // usage: wifiConnect(ssid : string ) + // usage: wifiConnect(ssid : string, timeout_in_seconds : number) + // usage: wifiConnect(ssid : string, timeout_in_seconds : number, pwd : string) + String ssid = duk_to_string(ctx, 0); + int timeout_in_seconds = 10; + if(duk_is_number(ctx, 1)) timeout_in_seconds = duk_to_number(ctx, 1); + + bool r = false; + + Serial.println("Connecting to: " + ssid); + + if(duk_is_string(ctx, 2)) { + String pwd = duk_to_string(ctx, 2); + WiFi.begin(ssid, pwd); + } else { + WiFi.begin(ssid); + } + + int i=0; + do { + delay(1000); + i++; + if(i>timeout_in_seconds) { + Serial.println("timeout"); + break; + } + } while (WiFi.status() != WL_CONNECTED); + + if(WiFi.status() == WL_CONNECTED) { + r = true; + wifiIP = WiFi.localIP().toString(); // update global var + } + + duk_push_boolean(ctx, r); + return 1; +} + +static duk_ret_t native_wifiScan(duk_context *ctx) { + // Example usage: `print(wifiScan()[0].SSID)` + wifiDisconnect(); + WiFi.mode(WIFI_MODE_STA); + Serial.println("Scanning..."); + int nets = WiFi.scanNetworks(); + duk_push_array(ctx); + int arrayIndex = 0; + duk_idx_t obj_idx; + for(int i=0; i