From ca5e2c87251aa3c805319bcfd6ed24542936084a Mon Sep 17 00:00:00 2001 From: eadmaster <925171+eadmaster@users.noreply.github.com> Date: Sun, 1 Sep 2024 06:26:27 +0200 Subject: [PATCH] read decoded ir signals (#216), serial fixes --- scripts/bruce_run_from_buffer.sh | 36 +++++++ src/core/mykeyboard.cpp | 4 +- src/core/serialcmds.cpp | 91 +++++++++++------ src/main.cpp | 4 + src/modules/bjs_interpreter/interpreter.cpp | 103 ++++++++++++++++---- src/modules/bjs_interpreter/interpreter.h | 2 +- src/modules/ir/ir_read.cpp | 90 +++++++++++++++-- src/modules/ir/ir_read.h | 2 +- 8 files changed, 272 insertions(+), 60 deletions(-) create mode 100755 scripts/bruce_run_from_buffer.sh diff --git a/scripts/bruce_run_from_buffer.sh b/scripts/bruce_run_from_buffer.sh new file mode 100755 index 000000000..4a8ce7938 --- /dev/null +++ b/scripts/bruce_run_from_buffer.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# args checking +if [ -z "$1" ] || [ ! -f "$1" ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + echo "usage: $(basename $0) INPUT_FILE" + echo + exit 0 +fi + + +INPUTFILE="$1" +INPUTFILEEXT=$( echo -n "$INPUTFILE" | rev | cut -d'.' -f1 | rev ) +INPUTFILEEXT=$( echo -n $INPUTFILEEXT | tr '[A-Z]' '[a-z]' ) # force lowercase extension +SERIAL_CMD="" + +# try to detect the input file type according on its extension (useful if file is missing or buggy) +case $INPUTFILEEXT in + sub ) SERIAL_CMD="subghz tx_from_buffer" ;; + ir ) SERIAL_CMD="ir tx_from_buffer" ;; + txt ) SERIAL_CMD="badusb run_from_buffer" ;; + js|bjs ) SERIAL_CMD="js run_from_buffer" ;; +esac + +if [ -z "$SERIAL_CMD" ]; then + echo "$(basename $0) err: unsupported file format: $INPUTFILEEXT" + exit 1 +fi + +echo "$SERIAL_CMD" | busybox microcom -s 115200 /dev/ttyACM0 -t 1000 + +sleep 1 + +cat "$INPUTFILE" | busybox microcom -s 115200 /dev/ttyACM0 -t 1000 + +echo "EOF" | busybox microcom -s 115200 /dev/ttyACM0 +#echo -e '\x04' | busybox microcom -s 115200 /dev/ttyACM0 diff --git a/src/core/mykeyboard.cpp b/src/core/mykeyboard.cpp index 4e4ad4174..c50a4edb6 100644 --- a/src/core/mykeyboard.cpp +++ b/src/core/mykeyboard.cpp @@ -78,7 +78,7 @@ bool menuPress(int bot) { bool checkNextPress(){ #if defined (CARDPUTER) Keyboard.update(); - if(Keyboard.isKeyPressed('.')) + if(Keyboard.isKeyPressed('/') || Keyboard.isKeyPressed('.')) #elif defined(CORE2) || defined(CORE) M5.update(); if(M5.BtnC.isPressed()) @@ -108,7 +108,7 @@ bool checkPrevPress() { if(axp192.GetBtnPress()) #elif defined(CARDPUTER) Keyboard.update(); - if(Keyboard.isKeyPressed(';')) + if(Keyboard.isKeyPressed(',') || Keyboard.isKeyPressed(';')) #elif defined(CORE2) || defined(CORE) M5.update(); if(M5.BtnA.isPressed()) diff --git a/src/core/serialcmds.cpp b/src/core/serialcmds.cpp index de3748110..0f40e6205 100644 --- a/src/core/serialcmds.cpp +++ b/src/core/serialcmds.cpp @@ -52,12 +52,15 @@ bool setupPsramFs() { 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'); @@ -67,6 +70,21 @@ String readSmallFileFromSerial() { } 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 */ @@ -134,27 +152,12 @@ 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 @@ -199,16 +202,17 @@ bool processSerialCommand(String cmd_str) { // switch on cmd_str if(cmd_str.startsWith("ir") ) { - if(cmd_str == "ir rx") { // "ir rx raw" - IrRead i = IrRead(true); // true == headless mode - String r = i.loop_headless(10); // wait for 10 seconds + if(cmd_str.startsWith("ir rx")) { + IrRead i = IrRead(true); // true -> headless mode + String r = ""; + if(cmd_str == "ir rx") r = i.loop_headless(10, false); // wait for 10 seconds, false -> try to decode + if(cmd_str == "ir rx raw") r = i.loop_headless(10, true); // true -> raw mode if(r.length()==0) return false; // else Serial.println(r); return true; } - //TODO: if(cmd_str == "ir rx" { - + if(cmd_str.startsWith("ir tx")) { // make sure it is initted gsetIrTxPin(false); @@ -241,7 +245,6 @@ 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 ")){ @@ -255,7 +258,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"} @@ -421,8 +437,8 @@ 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 ")) { + 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 @@ -435,7 +451,7 @@ bool processSerialCommand(String cmd_str) { key_input(*fs, filepath); return true; } - if(cmd_str == "badusb tx_from_buffer") { + if(cmd_str == "badusb run_from_buffer") { if(!(setupPsramFs())) return false; String txt = readSmallFileFromSerial(); String tmpfilepath = "/tmpramfile"; // TODO: random name? @@ -1001,7 +1017,20 @@ bool processSerialCommand(String cmd_str) { fileToCopy=""; return false; } - + + 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(); diff --git a/src/main.cpp b/src/main.cpp index d3ff61821..e30bdd3d4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -304,6 +304,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()); @@ -381,7 +382,10 @@ void loop() { delay(200); } +#if !defined(LITE_VERSION) handleSerialCommands(); +#endif + #ifdef CARDPUTER checkShortcutPress(); // shortctus to quickly start apps without navigating the menus #endif diff --git a/src/modules/bjs_interpreter/interpreter.cpp b/src/modules/bjs_interpreter/interpreter.cpp index 5872adc2e..87832bfb6 100644 --- a/src/modules/bjs_interpreter/interpreter.cpp +++ b/src/modules/bjs_interpreter/interpreter.cpp @@ -8,6 +8,9 @@ #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);"; HTTPClient http; @@ -203,18 +206,29 @@ static duk_ret_t native_drawFillRect(duk_context *ctx) { return 0; } -/* static duk_ret_t native_drawLine(duk_context *ctx) { - // drawLine(int16_t x, int16_t y, int16_t x2, int16_t y2, uint16_t color) - //tft.fillRect(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)); + // 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_clearScreen(duk_context *ctx) { + tft.fillScreen(BGCOLOR); + return 0; +} + static duk_ret_t native_width(duk_context *ctx) { int width = tft.width(); duk_push_int(ctx, width); @@ -307,12 +321,13 @@ static duk_ret_t native_getKeysPressed(duk_context *ctx) { // Serial functions static duk_ret_t native_serialReadln(duk_context *ctx) { - // usage: serialReadln(); - // usage: serialReadln(timeout_in_ms : number); // default to 10s + // 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; @@ -387,6 +402,7 @@ static duk_ret_t native_badusbSetup(duk_context *ctx) { // 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 @@ -413,10 +429,11 @@ static duk_ret_t native_badusbPrintln(duk_context *ctx) { static duk_ret_t native_badusbPress(duk_context *ctx) { // usage: badusbPress(keycode_number); - // keycodes list + // 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)); - Kb.releaseAll(); + delay(1); + Kb.release(duk_to_number(ctx, 0)); #endif return 0; } @@ -437,6 +454,38 @@ static duk_ret_t native_badusbRelease(duk_context *ctx) { 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) { @@ -609,7 +658,7 @@ 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) @@ -665,13 +714,17 @@ 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_width, 0); - // TODO: drawLine(x1, y1, x2, y2, color) - // TODO: drawPixel(x, y, color) + duk_push_c_function(ctx, native_drawPixel, 3); + duk_put_global_string(ctx, "drawPixel"); // TODO: drawBitmap(filename:string, x, y) - // TODO: clearScreen() + duk_push_c_function(ctx, native_clearScreen, 0); + duk_put_global_string(ctx, "clearScreen"); + + duk_push_c_function(ctx, native_width, 0); duk_put_global_string(ctx, "width"); duk_push_c_function(ctx, native_height, 0); duk_put_global_string(ctx, "height"); @@ -715,7 +768,13 @@ void interpreter() { 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"); @@ -725,7 +784,7 @@ void interpreter() { duk_put_global_string(ctx, "subghzRead"); duk_push_c_function(ctx, native_subghzSetFrequency, 1); duk_put_global_string(ctx, "subghzSetFrequency"); - //duk_put_global_string(ctx, "subghzSetIde"); + //duk_put_global_string(ctx, "subghzSetIdle"); // Dialog functions duk_push_c_function(ctx, native_dialogMessage, 1); @@ -751,18 +810,23 @@ void interpreter() { // 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 @@ -792,10 +856,11 @@ bool run_bjs_script_headless(String code) { script = code; returnToMenu=true; interpreter_start=true; - while(interpreter_start) { + //while(interpreter_start) { interpreter(); - delay(1); - } + // delay(1); + //} + interpreter_start=false; return true; } diff --git a/src/modules/bjs_interpreter/interpreter.h b/src/modules/bjs_interpreter/interpreter.h index 1b328f721..f9816047b 100644 --- a/src/modules/bjs_interpreter/interpreter.h +++ b/src/modules/bjs_interpreter/interpreter.h @@ -13,7 +13,7 @@ void run_bjs_script(); -void interpreter(); +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 d62d18c36..5f12f399e 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,6 +22,17 @@ #define DUTY_CYCLE 0.330000 +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) { headless = headless_mode; setup(); @@ -155,12 +167,12 @@ String IrRead::parse_signal() { } void IrRead::append_to_file_str(String btn_name, String signal_code) { - strDeviceContent += "#\n"; 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"; + strDeviceContent += "#\n"; } void IrRead::save_device() { @@ -204,7 +216,7 @@ void IrRead::save_device() { } -String IrRead::loop_headless(int max_loops) { +String IrRead::loop_headless(int max_loops, bool raw) { while (!irrecv.decode(&results)) { max_loops -= 1; @@ -214,15 +226,81 @@ String IrRead::loop_headless(int max_loops) { } delay(1000); } - - append_to_file_str("??", parse_signal()); // writes on strDeviceContent + + irrecv.disableIRIn(); String r = "Filetype: Bruce IR File\n"; r += "Version 1\n"; r += "#\n"; - r += strDeviceContent + "\n"; - irrecv.disableIRIn(); + if(raw) { + append_to_file_str("??", parse_signal()); // writes on strDeviceContent + r += strDeviceContent + "\n"; + } else { + // parsed signal https://github.com/jamisonderek/flipper-zero-tutorials/wiki/Infrared + r += "name: ??\n"; // button name + r += "type: parsed\n"; + switch (results.decode_type) { + case decode_type_t::RC5: + { + if(results.command > 0x3F ) + r += "protocol: RC5X\n"; + else + r += "protocol: RC5\n"; + break; + } + case decode_type_t::RC6: + { + r += "protocol: RC6\n"; + break; + } + case decode_type_t::SAMSUNG: + { + r += "protocol: Samsung32\n"; + break; + } + case decode_type_t::SONY: + { + // check address and command ranges to find the exact protocol + if(results.address>0xFF) + r += "protocol: SIRC20\n"; + else if(results.address>0x1F) + r += "protocol: SIRC15\n"; + else + r += "protocol: SIRC\n"; + break; + } + case decode_type_t::NEC: + { + // check address and command ranges to find the exact protocol + if(results.address>0xFFFF) + r += "protocol: NEC42ext\n"; + else if(results.address>0xFF1F) + r += "protocol: NECext\n"; + else if(results.address>0xFF) + r += "protocol: NEC42\n"; + else + r += "protocol: NEC\n"; + break; + } + // TODO: more protocols? + default: + { + Serial.println("unsupported protocol, try raw mode"); + irrecv.disableIRIn(); + return ""; + } + } + // + r += "address: " + uint32ToString(results.address) + "\n"; + r += "command: " + uint32ToString(results.command) + "\n"; + + //Serial.println("bits:"); + //Serial.println(results.bits); + //Serial.println("value:"); + //serialPrintUint64(results.value, HEX); + } + return r; } diff --git a/src/modules/ir/ir_read.h b/src/modules/ir/ir_read.h index 37648bf3c..0e2e53d3c 100644 --- a/src/modules/ir/ir_read.h +++ b/src/modules/ir/ir_read.h @@ -26,7 +26,7 @@ class IrRead { void setup(); void loop(); - String loop_headless(int max_loops); + String loop_headless(int max_loops, bool raw=false); private: bool _read_signal = false;