From 545fc1bd00d830719eee6493201c7fcdfcf666e8 Mon Sep 17 00:00:00 2001 From: Matt Emerick-Law Date: Sat, 28 Feb 2026 02:08:25 +0000 Subject: [PATCH 01/73] Add read() method to SerialDevice and BLESerialService --- include/SerialDevice.h | 1 + src/core/USBSerial/USBSerial.h | 1 + .../ble_api/services/BLESerialService.cpp | 17 +++++++++++++++++ src/modules/ble_api/services/BLESerialService.h | 1 + 4 files changed, 20 insertions(+) diff --git a/include/SerialDevice.h b/include/SerialDevice.h index 0735abd14..12f1ae24e 100644 --- a/include/SerialDevice.h +++ b/include/SerialDevice.h @@ -13,6 +13,7 @@ class SerialDevice { virtual size_t print(int n, int format = DEC) = 0; virtual size_t print(const String &s) = 0; virtual void vprintf(const char *fmt, va_list args) = 0; + virtual int read() = 0; virtual size_t write(uint8_t *str, size_t size) = 0; void printf(const char *fmt, ...) { va_list args; diff --git a/src/core/USBSerial/USBSerial.h b/src/core/USBSerial/USBSerial.h index 9b127a01a..81083aa33 100644 --- a/src/core/USBSerial/USBSerial.h +++ b/src/core/USBSerial/USBSerial.h @@ -18,6 +18,7 @@ class USBSerial : public SerialDevice { void flush() override { out->flush(); } int available() override { return out->available(); } size_t write(uint8_t *str, size_t size) override { return out->write(str, size); } + int read() override { return out->read(); } void setSerialOutput(Stream *in) { out = in; } Stream *getSerialOutput() { return out; } USBSerial(Stream *in = &Serial) { out = in; } diff --git a/src/modules/ble_api/services/BLESerialService.cpp b/src/modules/ble_api/services/BLESerialService.cpp index bb6812c4e..bb4b4277e 100644 --- a/src/modules/ble_api/services/BLESerialService.cpp +++ b/src/modules/ble_api/services/BLESerialService.cpp @@ -99,6 +99,23 @@ size_t BLESerialService::write(uint8_t *str, size_t size) { return size; } +int BLESerialService::read() { + if (!available()) return -1; + + std::string value = serial_char->getValue(); + if (value.empty()) return -1; + + char firstChar = value[0]; + // Remove the first character from the buffer + if (value.length() > 1) { + serial_char->setValue(value.substr(1)); + } else { + serial_char->setValue(""); + } + + return (int)firstChar; +} + void BLESerialService::setMTU(uint16_t mtu) { this->mtu = mtu; } #endif diff --git a/src/modules/ble_api/services/BLESerialService.h b/src/modules/ble_api/services/BLESerialService.h index 6ceb938cb..e1b87f50a 100644 --- a/src/modules/ble_api/services/BLESerialService.h +++ b/src/modules/ble_api/services/BLESerialService.h @@ -26,6 +26,7 @@ class BLESerialService : public BruceBLEService, public SerialDevice { void vprintf(const char *str, va_list args) override; size_t println(uint32_t n) override; size_t write(uint8_t *str, size_t size) override; + int read() override; void flush() override {} String readStringUntil(char terminator) override; int available() override; From e523f0bb5be9005488f9e161511d1d3507e7c7a9 Mon Sep 17 00:00:00 2001 From: Aleksandr Chumakov Date: Mon, 16 Mar 2026 19:10:30 +0300 Subject: [PATCH 02/73] feat(iButton): enhance UI/UX and add file save/load support --- src/modules/others/ibutton.cpp | 470 ++++++++++++++++++++++++--------- src/modules/others/ibutton.h | 12 +- 2 files changed, 361 insertions(+), 121 deletions(-) diff --git a/src/modules/others/ibutton.cpp b/src/modules/others/ibutton.cpp index 85b1ad58c..6ebb9420c 100644 --- a/src/modules/others/ibutton.cpp +++ b/src/modules/others/ibutton.cpp @@ -2,51 +2,60 @@ #include "ibutton.h" #include "core/display.h" #include "core/mykeyboard.h" +#include "core/sd_functions.h" -#define ONE_WIRE_BUS 0 +// --------------------------------------------------------------------------- +// State +// --------------------------------------------------------------------------- +static OneWire *oneWire = nullptr; +static byte keyBuffer[8]; +static bool keyLoaded = false; -OneWire *oneWire; -byte buffer[8]; +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- -void setup_ibutton() { -Reset: - oneWire = new OneWire(bruceConfigPins.iButton); - tft.fillScreen(TFT_BLACK); - setiButtonPinMenu(); - drawMainBorderWithTitle("iButton"); - tft.setCursor(10, 50); - padprintln("Waiting for signal"); - padprintln("press [Next] to setup"); - delay(100); +static String bufferToHexStr(const byte *buf, int len, const char *sep = ":") { + String s; + for (int i = 0; i < len; i++) { + if (buf[i] < 0x10) s += '0'; + s += String(buf[i], HEX); + if (i < len - 1) s += sep; + } + s.toUpperCase(); + return s; +} - for (;;) { - if (check(EscPress)) { - returnToMenu = true; - delete oneWire; - break; - } - if (check(NextPress)) { - setiButtonPinMenu(); - delete oneWire; - goto Reset; - } - // iButton is plugged - if (oneWire->reset() != 0) { - // Main Button is pressed - if (check(SelPress)) { - write_ibutton(); - } else { - read_ibutton(); - } - delay(500); +static bool bufferCrcValid() { return OneWire::crc8(keyBuffer, 7) == keyBuffer[7]; } + +static void displayStatus() { + drawMainBorderWithTitle("iButton"); + if (keyLoaded) { + padprintln("UID: " + bufferToHexStr(keyBuffer, 8)); + if (!bufferCrcValid()) { + tft.setTextColor(TFT_RED); + padprintln("CRC ERROR!"); + tft.setTextColor(bruceConfig.priColor); + } else { + tft.setTextColor(TFT_GREEN); + padprintln("CRC OK"); + tft.setTextColor(bruceConfig.priColor); } + } else { + padprintln("No key in buffer"); } + padprintln(""); + padprintln("Waiting for iButton..."); + padprintln("[NEXT] for options"); } -void write_byte_rw1990(byte data) { - int data_bit; +// --------------------------------------------------------------------------- +// OneWire RW1990 low-level +// --------------------------------------------------------------------------- + +static void writeByte_RW1990(byte data) { uint8_t pin = bruceConfigPins.iButton; - for (data_bit = 0; data_bit < 8; data_bit++) { + for (int bit = 0; bit < 8; bit++) { if (data & 1) { digitalWrite(pin, LOW); pinMode(pin, OUTPUT); @@ -60,56 +69,58 @@ void write_byte_rw1990(byte data) { digitalWrite(pin, HIGH); } delay(10); - data = data >> 1; + data >>= 1; } } -// Not working ((( -// void write_byte_rw1990(byte data) { -// for (int data_bit = 0; data_bit < 8; data_bit++) { -// delay(25); -// // oneWire->write_bit(~data); -// oneWire->write_bit(data & 0x01); -// data >>= 1; -// } -// } - -void write_ibutton() { - - // Dislay ID - tft.fillScreen(TFT_BLACK); - drawMainBorderWithTitle("iButton Write"); - tft.setCursor(11, 50); - tft.print("Current buffer:"); - tft.setCursor(40, 57); - for (byte i = 0; i < 8; i++) { - tft.print(buffer[i], HEX); - tft.print(":"); +// --------------------------------------------------------------------------- +// Core operations +// --------------------------------------------------------------------------- + +static bool readKey(int maxRetries = 20) { + for (int attempt = 0; attempt < maxRetries; attempt++) { + if (check(EscPress)) return false; + if (oneWire->reset() == 0) return false; + + oneWire->write(0x33); + oneWire->read_bytes(keyBuffer, 8); + + if (bufferCrcValid()) { + keyLoaded = true; + return true; + } + + tft.fillScreen(bruceConfig.bgColor); + drawMainBorderWithTitle("iButton"); + padprintln("Reading... attempt " + String(attempt + 2)); + padprintln("CRC mismatch, retrying..."); + padprintln(""); + padprintln("Keep key on the contact!"); + delay(100); } - delay(1000); - tft.setCursor(52, 102); - tft.print("Wait..."); - tft.setCursor(110, 102); - tft.print('-'); + // Retries exhausted — still store what we got + keyLoaded = true; + return false; +} + +static void writeKey() { + uint8_t pin = bruceConfigPins.iButton; + oneWire->skip(); oneWire->reset(); - oneWire->write(0x33); // Read ROM + oneWire->write(0x33); oneWire->skip(); oneWire->reset(); - oneWire->write(0x3C); // Set write mode for some models - tft.print('-'); + oneWire->write(0x3C); delay(50); oneWire->skip(); oneWire->reset(); - oneWire->write(0xD1); // Write command - tft.print('-'); + oneWire->write(0xD1); delay(50); - // Write don't work without this code - uint8_t pin = bruceConfigPins.iButton; digitalWrite(pin, LOW); pinMode(pin, OUTPUT); delayMicroseconds(60); @@ -119,81 +130,302 @@ void write_ibutton() { oneWire->skip(); oneWire->reset(); - oneWire->write(0xD5); // Enter write mode - tft.print('-'); + oneWire->write(0xD5); delay(50); - tft.print('>'); + for (byte i = 0; i < 8; i++) { - write_byte_rw1990(buffer[i]); // Write each byte - tft.print('*'); + writeByte_RW1990(keyBuffer[i]); delayMicroseconds(25); } - oneWire->reset(); // Reset bus - oneWire->skip(); - // Step 3 : Finalise - oneWire->write(0xD1); // End of write command + oneWire->reset(); + oneWire->skip(); + oneWire->write(0xD1); delayMicroseconds(16); - oneWire->reset(); // Reset bus + oneWire->reset(); +} - // Display end of copy - tft.fillScreen(TFT_BLACK); - tft.setCursor(90, 50); - tft.setTextSize(FM); - displayTextLine("COPIED"); - tft.setCursor(40, 80); - tft.print("Release button"); +// --------------------------------------------------------------------------- +// File I/O +// --------------------------------------------------------------------------- - delay(3000); +static const char *IBUTTON_DIR = "/BruceIButton"; - tft.fillScreen(TFT_BLACK); - drawMainBorderWithTitle("iButton"); - tft.setCursor(10, 60); - displayTextLine("Waiting iButton..."); +static IButtonResult saveKey() { + FS *fs; + if (!getFsStorage(fs)) return IBUTTON_FAILURE; + if (!fs->exists(IBUTTON_DIR)) fs->mkdir(IBUTTON_DIR); + + char fname[48]; + snprintf( + fname, + sizeof(fname), + "%s/%02X%02X%02X%02X%02X%02X.ibtn", + IBUTTON_DIR, + keyBuffer[0], + keyBuffer[1], + keyBuffer[2], + keyBuffer[3], + keyBuffer[4], + keyBuffer[5] + ); + + String path = fname; + if (fs->exists(path)) { + int n = 1; + String base = path.substring(0, path.lastIndexOf('.')); + do { path = base + "_" + String(n++) + ".ibtn"; } while (fs->exists(path)); + } + + File file = fs->open(path, FILE_WRITE); + if (!file) return IBUTTON_FAILURE; + + file.println("Filetype: Bruce iButton File"); + file.println("Version 1"); + file.println("Device type: DS1990A"); + file.print("UID: "); + file.println(bufferToHexStr(keyBuffer, 8, " ")); + file.print("CRC: "); + if (keyBuffer[7] < 0x10) file.print('0'); + file.println(keyBuffer[7], HEX); + + file.close(); + return IBUTTON_SUCCESS; } -void read_ibutton() { - oneWire->write(0x33); // Read ID command - oneWire->read_bytes(buffer, 8); // Read ID +static IButtonResult loadKey() { + FS *fs; + if (!getFsStorage(fs)) return IBUTTON_FAILURE; - // Display iButton - tft.fillScreen(TFT_BLACK); - drawMainBorderWithTitle("iButton ID"); + String filepath = loopSD(*fs, true, "ibtn", IBUTTON_DIR); + if (filepath.length() == 0) return IBUTTON_FAILURE; - // Dislay ID - tft.setTextSize(1.75); - tft.setCursor(12, 57); - for (byte i = 0; i < 8; i++) { - tft.print(buffer[i], HEX); - tft.print(":"); + File file = fs->open(filepath, FILE_READ); + if (!file) return IBUTTON_FAILURE; + + byte tmp[8]; + bool found = false; + + while (file.available()) { + String line = file.readStringUntil('\n'); + line.trim(); + if (!line.startsWith("UID:")) continue; + + String hex = line.substring(line.indexOf(':') + 1); + hex.trim(); + hex.replace(" ", ""); + if (hex.length() != 16) continue; + + for (int i = 0; i < 8; i++) tmp[i] = strtoul(hex.substring(i * 2, i * 2 + 2).c_str(), NULL, 16); + found = true; + break; + } + file.close(); + + if (!found) return IBUTTON_FAILURE; + + memcpy(keyBuffer, tmp, 8); + keyLoaded = true; + return bufferCrcValid() ? IBUTTON_SUCCESS : IBUTTON_CRC_ERROR; +} + +// --------------------------------------------------------------------------- +// Menu actions +// --------------------------------------------------------------------------- + +enum MenuAction { + ACTION_WRITE, + ACTION_SAVE, + ACTION_LOAD, + ACTION_RESET, + ACTION_SETUP_PIN, + ACTION_CLOSE, + ACTION_MAIN_MENU, +}; + +static bool returnFromMenu = false; +static bool restartNeeded = false; +static MenuAction selectedAction; + +static void setAction(MenuAction a) { selectedAction = a; } + +static void doWrite() { + if (!keyLoaded) { + displayError("No key in buffer", true); + delay(1500); + return; + } + + tft.fillScreen(bruceConfig.bgColor); + drawMainBorderWithTitle("Write iButton"); + padprintln("UID: " + bufferToHexStr(keyBuffer, 8)); + padprintln(""); + padprintln("Touch blank key to writer..."); + padprintln("[Esc] Cancel"); + + while (oneWire->reset() == 0) { + if (check(EscPress)) return; + delay(50); + } + + writeKey(); + displaySuccess("Key written!"); + delay(1500); +} + +static void doSave() { + if (!keyLoaded) { + displayError("No key in buffer", true); + delay(1500); + return; } + IButtonResult r = saveKey(); + if (r == IBUTTON_SUCCESS) displaySuccess("Saved"); + else displayError("Save failed", true); + delay(1500); +} - if (OneWire::crc8(buffer, 7) != buffer[7]) { - tft.setCursor(55, 85); - tft.setTextSize(FM); - tft.setTextColor(TFT_RED); - tft.println("CRC ERROR!"); +static void doLoad() { + IButtonResult r = loadKey(); + if (r == IBUTTON_SUCCESS) { + displaySuccess("Key loaded"); + delay(1000); + } else if (r == IBUTTON_CRC_ERROR) { + displayWarning("Loaded (CRC mismatch)", true); + delay(1500); } else { - // Display copy infos - tft.setCursor(55, 85); - tft.setTextSize(1.5); - tft.println("Hold OK to copy"); + displayError("Load failed", true); + delay(1500); + } +} + +static void doReset() { + memset(keyBuffer, 0, sizeof(keyBuffer)); + keyLoaded = false; + displaySuccess("Buffer cleared"); + delay(1000); +} + +static void selectMenuOption() { + options = {}; + + if (keyLoaded) { + options.emplace_back("Write to key", []() { setAction(ACTION_WRITE); }); + options.emplace_back("Save to file", []() { setAction(ACTION_SAVE); }); + } + + options.emplace_back("Load from file", []() { setAction(ACTION_LOAD); }); + + if (keyLoaded) { + options.emplace_back("Reset buffer", []() { setAction(ACTION_RESET); }); + } + + options.emplace_back("Setup pin", []() { setAction(ACTION_SETUP_PIN); }); + options.emplace_back("Close menu", []() { setAction(ACTION_CLOSE); }); + options.emplace_back("Main menu", []() { setAction(ACTION_MAIN_MENU); }); + + selectedAction = ACTION_CLOSE; + loopOptions(options); + options.clear(); + + switch (selectedAction) { + case ACTION_WRITE: doWrite(); break; + case ACTION_SAVE: doSave(); break; + case ACTION_LOAD: doLoad(); break; + case ACTION_RESET: doReset(); break; + case ACTION_SETUP_PIN: restartNeeded = true; return; + case ACTION_MAIN_MENU: returnFromMenu = true; return; + case ACTION_CLOSE: break; + } +} + +// --------------------------------------------------------------------------- +// Public entry point +// --------------------------------------------------------------------------- + +void setup_ibutton() { + returnFromMenu = false; + restartNeeded = false; + keyLoaded = false; + +Restart: + if (oneWire) delete oneWire; + oneWire = new OneWire(bruceConfigPins.iButton); + + tft.fillScreen(bruceConfig.bgColor); + displayStatus(); + + for (;;) { + if (check(EscPress) || returnFromMenu) { + returnToMenu = true; + break; + } + + if (check(NextPress)) { + selectMenuOption(); + if (returnFromMenu) { + returnToMenu = true; + break; + } + if (restartNeeded) { + restartNeeded = false; + setiButtonPinMenu(); + goto Restart; + } + tft.fillScreen(bruceConfig.bgColor); + displayStatus(); + } + + // Auto-read when iButton touches the probe + if (oneWire->reset() != 0) { + bool ok = readKey(); + tft.fillScreen(bruceConfig.bgColor); + + if (ok) { + displayStatus(); + padprintln(""); + tft.setTextColor(TFT_GREEN); + padprintln("Key read successfully!"); + tft.setTextColor(bruceConfig.priColor); + } else if (keyLoaded) { + // Read failed CRC but we have data + displayStatus(); + padprintln(""); + tft.setTextColor(TFT_YELLOW); + padprintln("Read with CRC errors"); + tft.setTextColor(bruceConfig.priColor); + } else { + displayStatus(); + } + + // Wait for key removal to avoid re-reading in a loop + while (oneWire->reset() != 0) { + if (check(EscPress)) break; + delay(100); + } + } + + delay(50); } + + delete oneWire; + oneWire = nullptr; } -/********************************************************************* -** Function: setiButtonPin -** Main Menu to manually iButton Pin -**********************************************************************/ +// --------------------------------------------------------------------------- +// Pin selection menu +// --------------------------------------------------------------------------- + void setiButtonPinMenu() { options = {}; gpio_num_t sel = GPIO_NUM_NC; for (int8_t i = -1; i <= GPIO_NUM_MAX; i++) { - String tmp = "GPIO " + String(i); - options.push_back({tmp.c_str(), [i, &sel]() { sel = (gpio_num_t)i; }}); + String label = "GPIO " + String(i); + options.push_back({label.c_str(), [i, &sel]() { sel = (gpio_num_t)i; }}); } loopOptions(options, bruceConfigPins.iButton + 1); options.clear(); bruceConfigPins.setiButtonPin(sel); } + #endif diff --git a/src/modules/others/ibutton.h b/src/modules/others/ibutton.h index 0b579280e..7dc21564e 100644 --- a/src/modules/others/ibutton.h +++ b/src/modules/others/ibutton.h @@ -1,8 +1,16 @@ #ifndef LITE_VERSION +#pragma once + #include +enum IButtonResult { + IBUTTON_SUCCESS = 0, + IBUTTON_FAILURE, + IBUTTON_NO_KEY, + IBUTTON_CRC_ERROR, +}; + void setup_ibutton(); -void write_ibutton(); -void read_ibutton(); void setiButtonPinMenu(); + #endif From 0b944145a7a9eeb639d622db6fbfbca2f89fc9af Mon Sep 17 00:00:00 2001 From: RockBase-Ronnie Date: Wed, 18 Mar 2026 11:33:43 +0800 Subject: [PATCH 03/73] Add new board nm-cyd-c5, tested with nm-rf-hat. --- boards/nm-cyd-c5/connections.md | 39 +++++++ boards/nm-cyd-c5/interface.cpp | 174 ++++++++++++++++++++++++++++++++ boards/nm-cyd-c5/nm-cyd-c5.ini | 58 +++++++++++ boards/nm-cyd-c5/pins_arduino.h | 146 +++++++++++++++++++++++++++ boards/pinouts/pins_arduino.h | 2 + platformio.ini | 7 +- src/core/config.h | 2 +- src/modules/gps/gps_tracker.cpp | 1 + src/modules/gps/wardriving.cpp | 1 + 9 files changed, 426 insertions(+), 4 deletions(-) create mode 100644 boards/nm-cyd-c5/connections.md create mode 100644 boards/nm-cyd-c5/interface.cpp create mode 100644 boards/nm-cyd-c5/nm-cyd-c5.ini create mode 100644 boards/nm-cyd-c5/pins_arduino.h diff --git a/boards/nm-cyd-c5/connections.md b/boards/nm-cyd-c5/connections.md new file mode 100644 index 000000000..300b4a8ab --- /dev/null +++ b/boards/nm-cyd-c5/connections.md @@ -0,0 +1,39 @@ +# Pinouts diagram to use Bruce + +## USING NM-CYD-C5, with SPI / CC1101 and NRF24 work with NM-RF-HAT + +| Device | SCK | MISO | MOSI | CS | GDO0/CE | TFT_DC | TFT_RST | TFT_BL | +| --- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | +| Display | 6 | 2 | 7 | 23 | --- | 24 | C5 RST | 25 | +| SD Card | 6 | 2 | 7 | 10 | --- | --- | --- | --- | +| CC1101 | 6 | 2 | 7 | 9* | 8* | --- | --- | --- | +| NRF24 | 6 | 2 | 7 | 9* | 8* | --- | --- | --- | + +(*) CC1101, NRF24, W5500 use the same pinouts, need to add a switch on CS and CE/GDO0 to choose which to use. + + +If using ST7789 with XPT2046 fo touchscreen, in this case you have 2 GPIO available (0 and 28) to use on CC1101/NRF24 +| Device | SCK | MISO | MOSI | CS | IRQ | +| --- | :---: | :---: | :---: | :---: | :---: | +| Display | 6 | 2 | 7 | 23 | --- | +| Touch | 6 | 2 | 7 | 1 | --- | + + +| Device | RX | TX | GPIO | +| --- | :---: | :---: | :---: | +| GPS | 4 | 5 | --- | +| IR RX | --- | --- | 9 | +| IR TX | --- | --- | 8 | +| LED | --- | --- | 27 | +| 433 RX | --- | --- | 9 | +| 433 TX | --- | --- | 8 | + +ESP32-C5 doesn't support USB-OTG, for BadUSB you need to use a CH9329 module + +FM Radio, PN532 on I2C, other I2C devices, CH9329, Temperature and humidity sensor: BME280. +I2C SDA: 9 +I2C SCL: 8 + +Serial interface to other devices (Flipper) - USB TypeC interface on NM-CYD-C5 +Serial Tx: 11 +Serial Rx: 12 diff --git a/boards/nm-cyd-c5/interface.cpp b/boards/nm-cyd-c5/interface.cpp new file mode 100644 index 000000000..46a6144fc --- /dev/null +++ b/boards/nm-cyd-c5/interface.cpp @@ -0,0 +1,174 @@ +#include "core/powerSave.h" +#include "core/utils.h" +#include + +/*************************************************************************************** +** Function name: _setup_gpio() +** Location: main.cpp +** Description: initial setup for the device +***************************************************************************************/ +void _setup_gpio() { + + pinMode(TFT_CS, OUTPUT); + digitalWrite(TFT_CS, HIGH); + pinMode(TFT_MOSI, OUTPUT); + digitalWrite(TFT_MOSI, HIGH); + pinMode(TFT_SCLK, OUTPUT); + + pinMode(TFT_BL, OUTPUT); + digitalWrite(TFT_BL, HIGH); + pinMode(TFT_RST, OUTPUT); + pinMode(TFT_DC, OUTPUT); + digitalWrite(TFT_DC, HIGH); + +#ifdef HAS_3_BUTTONS + pinMode(UP_BTN, INPUT_PULLUP); // Sets the power btn as an INPUT + pinMode(SEL_BTN, INPUT_PULLUP); + pinMode(DW_BTN, INPUT_PULLUP); +#endif + pinMode(NRF24_SS_PIN, OUTPUT); + pinMode(CC1101_SS_PIN, OUTPUT); + pinMode(SDCARD_CS, OUTPUT); + pinMode(W5500_SS_PIN, OUTPUT); + pinMode(TFT_CS, OUTPUT); + + digitalWrite(NRF24_SS_PIN, HIGH); + digitalWrite(CC1101_SS_PIN, HIGH); + digitalWrite(SDCARD_CS, HIGH); + digitalWrite(W5500_SS_PIN, HIGH); + digitalWrite(TFT_CS, HIGH); +#ifdef ST7789_DRIVER + bruceConfig.colorInverted = 0; +#endif +#ifdef ILI9341_DRIVER + bruceConfig.colorInverted = 0; +#endif +} +/*************************************************************************************** +** Function name: _post_setup_gpio() +** Location: main.cpp +** Description: second stage gpio setup to make a few functions work +***************************************************************************************/ +void _post_setup_gpio() { +#ifdef HAS_TOUCH + pinMode(TOUCH_CS, OUTPUT); + uint16_t calData[5] = {225, 3413, 403, 3334, 1}; + tft.setTouch(calData); +#endif + bruceConfigPins.gps_bus.rx = (gpio_num_t)GPS_SERIAL_RX; + bruceConfigPins.gps_bus.tx = (gpio_num_t)GPS_SERIAL_TX; + bruceConfigPins.gpsBaudrate = 9600; + bruceConfigPins.rfTx = 8; + bruceConfigPins.rfRx = 9; +} + +/*************************************************************************************** +** Function name: getBattery() +** location: display.cpp +** Description: Delivers the battery value from 1-100 +***************************************************************************************/ +int getBattery() { return 0; } + +/*************************************************************************************** +** Function name: isCharging() +** Description: Default implementation that returns false +***************************************************************************************/ +bool isCharging() { return false; } + +/********************************************************************* +** Function: setBrightness +** location: settings.cpp +** set brightness value +**********************************************************************/ +void _setBrightness(uint8_t brightval) { + if (brightval == 0) { + analogWrite(TFT_BL, brightval); + } else { + int bl = MINBRIGHT + round(((255 - MINBRIGHT) * brightval / 100)); + analogWrite(TFT_BL, bl); + } +} + +/********************************************************************* +** Function: InputHandler +** Handles the variables PrevPress, NextPress, SelPress, AnyKeyPress and EscPress +**********************************************************************/ +void InputHandler(void) { + static unsigned long tm = 0; + if (millis() - tm < 200 && !LongPress) return; +#ifdef HAS_TOUCH + TouchPoint t; + checkPowerSaveTime(); + bool _IH_touched = tft.getTouch(&t.x, &t.y); + if (_IH_touched) { + NextPress = false; + PrevPress = false; + UpPress = false; + DownPress = false; + SelPress = false; + EscPress = false; + AnyKeyPress = false; + NextPagePress = false; + PrevPagePress = false; + touchPoint.pressed = false; + _IH_touched = false; + Serial.printf("\nRAW: Touch Pressed on x=%d, y=%d", t.x, t.y); + if (bruceConfigPins.rotation == 3) { + t.y = (tftHeight + 20) - t.y; + t.x = tftWidth - t.x; + } + if (bruceConfigPins.rotation == 0) { + uint16_t tmp = t.x; + t.x = map((tftHeight + 20) - t.y, 0, 320, 0, 240); + t.y = map(tmp, 0, 240, 0, 320); + } + if (bruceConfigPins.rotation == 2) { + uint16_t tmp = t.x; + t.x = map(t.y, 0, 320, 0, 240); + t.y = map(tftWidth - tmp, 0, 240, 0, 320); + } + + Serial.printf("\nROT: Touch Pressed on x=%d, y=%d, rot=%d\n", t.x, t.y, bruceConfigPins.rotation); + + if (!wakeUpScreen()) AnyKeyPress = true; + else return; + + // Touch point global variable + touchPoint.x = t.x; + touchPoint.y = t.y; + touchPoint.pressed = true; + touchHeatMap(touchPoint); + tm = millis(); + } + +#endif +#ifdef HAS_3_BUTTONS + bool upPressed = (digitalRead(UP_BTN) == LOW); + bool selPressed = (digitalRead(SEL_BTN) == LOW); + bool dwPressed = (digitalRead(DW_BTN) == LOW); + + bool anyPressed = upPressed || selPressed || dwPressed; + if (anyPressed) tm = millis(); + if (anyPressed && wakeUpScreen()) return; + + AnyKeyPress = anyPressed; + PrevPress = upPressed; + EscPress = upPressed && dwPressed; + NextPress = dwPressed; + SelPress = selPressed; +#endif +} + +/********************************************************************* +** Function: powerOff +** location: mykeyboard.cpp +** Turns off the device (or try to) +**********************************************************************/ +void powerOff() {} + +/********************************************************************* +** Function: checkReboot +** location: mykeyboard.cpp +** Btn logic to turnoff the device (name is odd btw) +**********************************************************************/ +void checkReboot() {} diff --git a/boards/nm-cyd-c5/nm-cyd-c5.ini b/boards/nm-cyd-c5/nm-cyd-c5.ini new file mode 100644 index 000000000..7bdd0c80e --- /dev/null +++ b/boards/nm-cyd-c5/nm-cyd-c5.ini @@ -0,0 +1,58 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:nm-cyd-c5] +board = esp32-c5-devkitc-1 +board_build.partitions = custom_8Mb.csv +build_src_filter =${env.build_src_filter} +<../boards/nm-cyd-c5> +build_flags = + ${env.build_flags} + -Iboards/nm-cyd-c5 + -DDEVICE_NAME='"ESP32-C5"' + -DNM_CYD_ESP32C5=1 + -DBOARD_HAS_PSRAM=1 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MODE=1 + -DCORE_DEBUG_LEVEL=1 + -DHAS_TOUCH=1 + -DROTATION=3 + + -DGPS_SERIAL_TX=5 + -DGPS_SERIAL_RX=4 + ; grove pins + ; defaults from https://github.com/espressif/arduino-esp32/blob/master/variants/esp32s3/pins_arduino.h + -DGROVE_SDA=8 ; default RF TX pin + -DGROVE_SCL=9 ; default IR/RF RX pin + ;-DALLOW_ALL_GPIO_FOR_IR_RF=1 ; Set this option to make use of all GPIOs, from 1 to 44 to be chosen, except TFT and SD pins + + ; ir led pin + -DIR_TX_PINS='{{"Pin 9", 9}, {"Pin 8", 8}}' + -DIR_RX_PINS='{{"Pin 9", 9}, {"Pin 8", 8}}' + -DLED_ON=HIGH + -DLED_OFF=LOW + + ;Radio Frequency (one pin modules) pin setting + -DRF_TX_PINS='{{"Pin 9", 9}, {"Pin 8", 8}}' + -DRF_RX_PINS='{{"Pin 9", 9}, {"Pin 8", 8}}' + + ; text sizes + -DFP=1 + -DFM=2 + -DFG=3 + ; ui control buttons + ;-DSEL_BTN=1 + ;-DUP_BTN=2 ; also work as ESC + ;-DDW_BTN=3 ; also work as NEXT + -DBTN_ALIAS='"OK"' + + ;FM Radio + -DFM_SI4713=1 ;Uncomment to activate FM Radio using Adafruit Si4713 + + diff --git a/boards/nm-cyd-c5/pins_arduino.h b/boards/nm-cyd-c5/pins_arduino.h new file mode 100644 index 000000000..53ffb73a8 --- /dev/null +++ b/boards/nm-cyd-c5/pins_arduino.h @@ -0,0 +1,146 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include "soc/soc_caps.h" +#include + +#define PIN_RGB_LED 27 +// BUILTIN_LED can be used in new Arduino API digitalWrite() like in Blink.ino +static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + PIN_RGB_LED; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN +// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API rgbLedWrite() +#define RGB_BUILTIN LED_BUILTIN +#define RGB_BRIGHTNESS 64 + +static const uint8_t TX = 11; +static const uint8_t RX = 12; + +static const uint8_t USB_DM = 13; +static const uint8_t USB_DP = 14; + +static const uint8_t SDA = 4; +static const uint8_t SCL = 5; + +static const uint8_t SS = 10; +static const uint8_t MOSI = 7; +static const uint8_t MISO = 2; +static const uint8_t SCK = 6; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; + +// LP I2C Pins are fixed on ESP32-C5 +static const uint8_t LP_SDA = 4; +static const uint8_t LP_SCL = 5; +#define WIRE1_PIN_DEFINED +#define SDA1 LP_SDA +#define SCL1 LP_SCL + +// LP UART Pins are fixed on ESP32-C5 +static const uint8_t LP_RX = 12; +static const uint8_t LP_TX = 11; + +#define HAS_RGB_LED 1 +#define LED_ORDER GRB +#define LED_TYPE_IS_RGBW 1 +#define LED_COUNT 1 +#define LED_TYPE WS2812 +#define LED_COLOR_STEP 15 +#define RGB_LED 27 + +/* Communication Buses*/ +// UART +#define SERIAL_TX 11 +#define SERIAL_RX 12 +// I2C +#define GROVE_SDA 8 +#define GROVE_SCL 9 +// SPI +#define SPI_SCK_PIN 6 +#define SPI_MOSI_PIN 7 +#define SPI_MISO_PIN 2 +#define SPI_SS_PIN 9 + +/* TFT definitions */ +#define HAS_SCREEN 1 +#define MINBRIGHT (uint8_t)1 +#define USER_SETUP_LOADED 1 + +/* --------------------- */ +// Setup for ST7789 240x320 + +#define ST7789_DRIVER 1 +#define TFT_WIDTH 240 +#define TFT_HEIGHT 320 + +/* --------------------- */ +// Setup for ILI9341 320x240 (no touch) + +// #define ILI9341_DRIVER 1 +// #define TFT_HEIGHT 320 +// #define TFT_WIDTH 240 + +/* --------------------- */ +// Common TFT definitions +#define TFT_BACKLIGHT_ON 1 +#define TFT_BL 25 +#define TFT_RST -1 +#define TFT_DC 24 +#define TFT_MISO 2 // set to share SPI with other devices +#define TFT_MOSI 7 +#define TFT_SCLK 6 +#define TFT_CS 23 +#define TOUCH_CS 1 +#define SMOOTH_FONT 1 +#define SPI_FREQUENCY 20000000 +#define SPI_READ_FREQUENCY 20000000 +#define SPI_TOUCH_FREQUENCY 2500000 + +/* Peripheral settings */ +// Bad USB with CH9329 +#define BAD_RX 4 +#define BAD_TX 5 +// GPS Bus +#define GPS_SERIAL_RX 4 +#define GPS_SERIAL_TX 5 + +#define USE_TFT_eSPI_TOUCH 1 +#define HAS_TOUCH 1 +#define TOUCH_CS 1 +#define BTN_ACT LOW +#define DEEPSLEEP_WAKEUP_PIN 0 + +// InfraRed +#define RXLED 9 +#define TXLED 8 +#define LED_ON HIGH +#define LED_OFF LOW +// SDCard +#define SDCARD_CS 10 +#define SDCARD_SCK SPI_SCK_PIN +#define SDCARD_MISO SPI_MISO_PIN +#define SDCARD_MOSI SPI_MOSI_PIN +// CC1101 +#define CC1101_GDO0_PIN 8 +#define CC1101_SS_PIN 9 +#define CC1101_MOSI_PIN SPI_MOSI_PIN +#define CC1101_SCK_PIN SPI_SCK_PIN +#define CC1101_MISO_PIN SPI_MISO_PIN +// NRF24 +#define NRF24_CE_PIN 8 +#define NRF24_SS_PIN 9 +#define NRF24_MOSI_PIN SPI_MOSI_PIN +#define NRF24_SCK_PIN SPI_SCK_PIN +#define NRF24_MISO_PIN SPI_MISO_PIN +// Ethernet +#define W5500_INT_PIN 8 +#define W5500_SS_PIN 9 +#define W5500_MOSI_PIN SPI_MOSI_PIN +#define W5500_SCK_PIN SPI_SCK_PIN +#define W5500_MISO_PIN SPI_MISO_PIN +#endif /* Pins_Arduino_h */ diff --git a/boards/pinouts/pins_arduino.h b/boards/pinouts/pins_arduino.h index 8cc716273..865909049 100644 --- a/boards/pinouts/pins_arduino.h +++ b/boards/pinouts/pins_arduino.h @@ -46,4 +46,6 @@ #include "../ESP32-C5-tft/pins_arduino.h" #elif ESP32C5_DEVKITC_1 #include "../ESP32-C5/pins_arduino.h" +#elif NM_CYD_ESP32C5 +#include "../nm-cyd-c5/pins_arduino.h" #endif diff --git a/platformio.ini b/platformio.ini index 16dd3ec4f..217860b30 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,7 @@ default_envs = ;m5stack-cardputer ;m5stack-sticks3 - m5stack-cplus2 + ;m5stack-cplus2 ;m5stack-cplus1_1 ;LAUNCHER_m5stack-cplus1_1 ;m5stack-core2 @@ -66,6 +66,7 @@ default_envs = ;esp32-c5-tft ;esp32-c5 ;ES3C28P + nm-cyd-c5 ;uncomment to not use global dirs to avoid possible conflicts ;platforms_dir = .pio/platforms @@ -82,8 +83,8 @@ extra_configs = platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.36/platform-espressif32.zip ; Arduino 3.3.6 ;https://github.com/pioarduino/platform-espressif32/releases/download/55.03.34/platform-espressif32.zip ; Arduino 3.3.4 ;https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip ; Last Version -platform_packages = - framework-arduinoespressif32-libs @ https://github.com/bmorcelli/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/bruce_esp32-arduino-libs-20260123-153546.zip ; Arduino 3.3.6 +;platform_packages = + ;framework-arduinoespressif32-libs @ https://github.com/bmorcelli/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/bruce_esp32-arduino-libs-20260123-153546.zip ; Arduino 3.3.6 ;framework-arduinoespressif32-libs @ https://github.com/bmorcelli/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/bruce_esp32-arduino-libs-20251205-131242.zip ; Arduino 3.3.4 ;framework-arduinoespressif32-libs @ https://github.com/bmorcelli/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-20250919-170356.zip ; Arduino 3.3.1 diff --git a/src/core/config.h b/src/core/config.h index 1aeea72ce..9c3239853 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -36,7 +36,7 @@ class BruceConfig : public BruceTheme { const char *filepath = "/bruce.conf"; // Settings - int dimmerSet = 10; + int dimmerSet = 60; int bright = 100; bool automaticTimeUpdateViaNTP = true; float tmz = 0; diff --git a/src/modules/gps/gps_tracker.cpp b/src/modules/gps/gps_tracker.cpp index d441145fd..26ea9d522 100644 --- a/src/modules/gps/gps_tracker.cpp +++ b/src/modules/gps/gps_tracker.cpp @@ -40,6 +40,7 @@ void GPSTracker::setup() { bool GPSTracker::begin_gps() { releasePins(); + pinMode(bruceConfigPins.gps_bus.rx, INPUT); GPSserial.begin( bruceConfigPins.gpsBaudrate, SERIAL_8N1, bruceConfigPins.gps_bus.rx, bruceConfigPins.gps_bus.tx ); diff --git a/src/modules/gps/wardriving.cpp b/src/modules/gps/wardriving.cpp index 335c48a60..3bf602a08 100644 --- a/src/modules/gps/wardriving.cpp +++ b/src/modules/gps/wardriving.cpp @@ -80,6 +80,7 @@ void Wardriving::begin_wifi() { bool Wardriving::begin_gps() { releasePins(); + pinMode(bruceConfigPins.gps_bus.rx, INPUT); GPSserial.begin( bruceConfigPins.gpsBaudrate, SERIAL_8N1, bruceConfigPins.gps_bus.rx, bruceConfigPins.gps_bus.tx ); From 6c3c921dced279ce9743a5be642ea47d8f56504d Mon Sep 17 00:00:00 2001 From: ! FrannnDev <181183464+fastdexcorp@users.noreply.github.com> Date: Sun, 22 Mar 2026 18:50:59 -0300 Subject: [PATCH 04/73] Improvements --- src/core/display.cpp | 133 ++++++----- src/core/main_menu.cpp | 3 + src/core/main_menu.h | 4 + src/core/menu_items/AppStoreMenu.cpp | 328 +++++++++++++++++++++++++++ src/core/menu_items/AppStoreMenu.h | 19 ++ src/core/menu_items/ConfigMenu.cpp | 5 - src/core/wifi/wg.cpp | 53 ++--- src/modules/ir/ir_read.cpp | 167 ++++++++------ src/modules/ir/ir_read.h | 38 +--- 9 files changed, 571 insertions(+), 179 deletions(-) create mode 100644 src/core/menu_items/AppStoreMenu.cpp create mode 100644 src/core/menu_items/AppStoreMenu.h diff --git a/src/core/display.cpp b/src/core/display.cpp index 341234ee9..1921c40ba 100644 --- a/src/core/display.cpp +++ b/src/core/display.cpp @@ -774,38 +774,8 @@ void drawSubmenu(int index, std::vector