diff --git a/boards/promicro_nrf52840.json b/boards/promicro_nrf52840.json new file mode 100644 index 0000000..346f0f2 --- /dev/null +++ b/boards/promicro_nrf52840.json @@ -0,0 +1,78 @@ +{ + "build": { + "arduino":{ + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + [ + "0x239A", + "0x00B3" + ], + [ + "0x239A", + "0x8029" + ], + [ + "0x239A", + "0x0029" + ], + [ + "0x239A", + "0x002A" + ], + [ + "0x239A", + "0x802A" + ] + ], + "usb_product": "ProMicro NRF52840", + "mcu": "nrf52840", + "variant": "promicro_nrf52840", + "variants_dir": "variants", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "6.1.1", + "sd_fwid": "0x00B6" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": [ + "bluetooth" + ], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd" + }, + "frameworks": [ + "arduino", + "zephyr" + ], + "name": "ProMicro NRF52840", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": [ + "jlink", + "nrfjprog", + "nrfutil", + "stlink" + ], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://www.nologo.tech/en/product/otherboard/NRF52840.html", + "vendor": "Nologo" + } \ No newline at end of file diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 8ffb2c1..b454d93 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -4,6 +4,10 @@ #define AUTO_OFF_MILLIS 15000 // 15 seconds +#ifndef USER_BTN_PRESSED +#define USER_BTN_PRESSED LOW +#endif + // 'meshcore', 128x13px static const uint8_t meshcore_logo [] PROGMEM = { 0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, @@ -22,13 +26,21 @@ static const uint8_t meshcore_logo [] PROGMEM = { }; void UITask::begin(const char* node_name, const char* build_date, uint32_t pin_code) { - _prevBtnState = HIGH; _auto_off = millis() + AUTO_OFF_MILLIS; clearMsgPreview(); _node_name = node_name; _build_date = build_date; _pin_code = pin_code; - _display->turnOn(); + if (_display != NULL) { + _display->turnOn(); + } +} + +void UITask::msgRead(int msgcount) { + _msgcount = msgcount; + if (msgcount == 0) { + clearMsgPreview(); + } } void UITask::clearMsgPreview() { @@ -36,7 +48,9 @@ void UITask::clearMsgPreview() { _msg[0] = 0; } -void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char* text) { +void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) { + _msgcount = msgcount; + if (path_len == 0xFF) { sprintf(_origin, "(F) %s", from_name); } else { @@ -44,11 +58,15 @@ void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char* } StrHelper::strncpy(_msg, text, sizeof(_msg)); - if (!_display->isOn()) _display->turnOn(); - _auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer + if (_display != NULL) { + if (!_display->isOn()) _display->turnOn(); + _auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer + } } void UITask::renderCurrScreen() { + if (_display == NULL) return; // assert() ?? + char tmp[80]; if (_origin[0] && _msg[0]) { // render message preview @@ -61,9 +79,10 @@ void UITask::renderCurrScreen() { _display->setCursor(0, 24); _display->print(_msg); - //_display->setCursor(100, 9); TODO - //_display->setTextSize(2); - //_display->printf("%d", msgs); + _display->setCursor(100, 9); + _display->setTextSize(2); + sprintf(tmp, "%d", _msgcount); + _display->print(tmp); } else { // render 'home' screen _display->drawXbm(0, 0, meshcore_logo, 128, 13); @@ -87,24 +106,72 @@ void UITask::renderCurrScreen() { } } -void UITask::loop() { - if (millis() >= _next_read) { - int btnState = digitalRead(PIN_USER_BTN); - if (btnState != _prevBtnState) { - if (btnState == LOW) { // pressed? - if (_display->isOn()) { - clearMsgPreview(); - } else { - _display->turnOn(); - } - _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer +void UITask::userLedHandler() { +#ifdef PIN_STATUS_LED + static int state = 0; + static int next_change = 0; + int cur_time = millis(); + if (cur_time > next_change) { + if (state == 0) { + state = 1; // led on, short = unread msg + if (_msgcount > 0) { + next_change = cur_time + 500; + } else { + next_change = cur_time + 2000; + } + } else { + state = 0; + if (_board->getBattMilliVolts() > 3800) { + next_change = cur_time + 2000; + } else { + next_change = cur_time + 4000; // 4s blank if bat level low } - _prevBtnState = btnState; } - _next_read = millis() + 100; // 10 reads per second + digitalWrite(PIN_STATUS_LED, state); } +#endif +} - if (_display->isOn()) { +void UITask::buttonHandler() { +#ifdef PIN_USER_BTN + static int prev_btn_state = !USER_BTN_PRESSED; + static unsigned long btn_state_change_time = 0; + static unsigned long next_read = 0; + int cur_time = millis(); + if (cur_time >= next_read) { + int btn_state = digitalRead(PIN_USER_BTN); + if (btn_state != prev_btn_state) { + if (btn_state == USER_BTN_PRESSED) { // pressed? + if (_display != NULL) { + if (_display->isOn()) { + clearMsgPreview(); + } else { + _display->turnOn(); + } + _auto_off = cur_time + AUTO_OFF_MILLIS; // extend auto-off timer + } + } else { // unpressed ? check pressed time ... + if ((cur_time - btn_state_change_time) > 5000) { + #ifdef PIN_STATUS_LED + digitalWrite(PIN_STATUS_LED, LOW); + delay(10); + #endif + _board->powerOff(); + } + } + btn_state_change_time = millis(); + prev_btn_state = btn_state; + } + next_read = millis() + 100; // 10 reads per second + } +#endif +} + +void UITask::loop() { + buttonHandler(); + userLedHandler(); + + if (_display != NULL && _display->isOn()) { if (millis() >= _next_refresh) { _display->startFrame(); renderCurrScreen(); diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index f1a6322..4cea0b8 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -1,25 +1,35 @@ #pragma once +#include #include class UITask { DisplayDriver* _display; - unsigned long _next_read, _next_refresh, _auto_off; - int _prevBtnState; + mesh::MainBoard* _board; + unsigned long _next_refresh, _auto_off; bool _connected; uint32_t _pin_code; const char* _node_name; const char* _build_date; char _origin[62]; char _msg[80]; + int _msgcount; void renderCurrScreen(); + void buttonHandler(); + void userLedHandler(); + public: - UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; _connected = false; } + + UITask(mesh::MainBoard* board, DisplayDriver* display) : _board(board), _display(display){ + _next_refresh = 0; + _connected = false; + } void begin(const char* node_name, const char* build_date, uint32_t pin_code); void setHasConnection(bool connected) { _connected = connected; } void clearMsgPreview(); - void showMsgPreview(uint8_t path_len, const char* from_name, const char* text); + void msgRead(int msgcount); + void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount); void loop(); -}; \ No newline at end of file +}; diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index ebde045..85a2c52 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -96,21 +96,32 @@ #include #include static TechoBoard board; + #elif defined(LILYGO_TBEAM) #include #include static TBeamBoard board; + +#elif defined(FAKETEC) + #include + #include + static faketecBoard board; + #else #error "need to provide a 'board' object" #endif #ifdef DISPLAY_CLASS + #include "UITask.h" #include static DISPLAY_CLASS display; - + static UITask ui_task(&board, &display); + #define HAS_UI +#elif defined(HAS_UI) #include "UITask.h" - static UITask ui_task(display); + + static UITask ui_task(&board, NULL); #endif // Believe it or not, this std C function is busted on some platforms! @@ -167,6 +178,9 @@ static uint32_t _atoi(const char* sp) { #define CMD_GET_CONTACT_BY_KEY 30 #define CMD_GET_CHANNEL 31 #define CMD_SET_CHANNEL 32 +#define CMD_SIGN_START 33 +#define CMD_SIGN_DATA 34 +#define CMD_SIGN_FINISH 35 #define RESP_CODE_OK 0 #define RESP_CODE_ERR 1 @@ -186,6 +200,8 @@ static uint32_t _atoi(const char* sp) { #define RESP_CODE_DISABLED 15 // ... _V3 stuff in here #define RESP_CODE_CHANNEL_INFO 18 // a reply to CMD_GET_CHANNEL +#define RESP_CODE_SIGN_START 19 +#define RESP_CODE_SIGNATURE 20 // these are _pushed_ to client app at any time #define PUSH_CODE_ADVERT 0x80 @@ -200,6 +216,8 @@ static uint32_t _atoi(const char* sp) { /* -------------------------------------------------------------------------------------- */ +#define MAX_SIGN_DATA_LEN (8*1024) // 8K + struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; @@ -232,6 +250,8 @@ class MyMesh : public BaseChatMesh { uint32_t _active_ble_pin; bool _iter_started; uint8_t app_target_ver; + uint8_t* sign_data; + uint32_t sign_data_len; uint8_t cmd_frame[MAX_FRAME_SIZE+1]; uint8_t out_frame[MAX_FRAME_SIZE+1]; @@ -571,8 +591,8 @@ protected: } else { soundBuzzer(); } - #ifdef DISPLAY_CLASS - ui_task.showMsgPreview(path_len, from.name, text); + #ifdef HAS_UI + ui_task.newMsg(path_len, from.name, text, offline_queue_len); #endif } @@ -613,8 +633,8 @@ protected: } else { soundBuzzer(); } - #ifdef DISPLAY_CLASS - ui_task.showMsgPreview(in_path_len < 0 ? 0xFF : in_path_len, "Public", text); + #ifdef HAS_UI + ui_task.newMsg(in_path_len < 0 ? 0xFF : in_path_len, "Public", text, offline_queue_len); #endif } @@ -692,6 +712,7 @@ public: app_target_ver = 0; _identity_store = NULL; pending_login = pending_status = 0; + sign_data = NULL; // defaults memset(&_prefs, 0, sizeof(_prefs)); @@ -1078,6 +1099,9 @@ public: int out_len; if ((out_len = getFromOfflineQueue(out_frame)) > 0) { _serial->writeFrame(out_frame, out_len); + #ifdef HAS_UI + ui_task.msgRead(offline_queue_len); + #endif } else { out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; _serial->writeFrame(out_frame, 1); @@ -1252,6 +1276,38 @@ public: } else { writeErrFrame(); } + } else if (cmd_frame[0] == CMD_SIGN_START) { + out_frame[0] = RESP_CODE_SIGN_START; + out_frame[1] = 0; // reserved + uint32_t len = MAX_SIGN_DATA_LEN; + memcpy(&out_frame[2], &len, 4); + _serial->writeFrame(out_frame, 6); + + if (sign_data) { + free(sign_data); + } + sign_data = (uint8_t *) malloc(MAX_SIGN_DATA_LEN); + sign_data_len = 0; + } else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) { + if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) { + writeErrFrame(); // error: too long + } else { + memcpy(&sign_data[sign_data_len], &cmd_frame[1], len - 1); + sign_data_len += (len - 1); + writeOKFrame(); + } + } else if (cmd_frame[0] == CMD_SIGN_FINISH) { + if (sign_data) { + self_id.sign(&out_frame[1], sign_data, sign_data_len); + + free(sign_data); // don't need sign_data now + sign_data = NULL; + + out_frame[0] = RESP_CODE_SIGNATURE; + _serial->writeFrame(out_frame, 1 + SIGNATURE_SIZE); + } else { + writeErrFrame(); + } } else { writeErrFrame(); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); @@ -1285,7 +1341,7 @@ public: checkConnections(); } - #ifdef DISPLAY_CLASS + #ifdef HAS_UI ui_task.setHasConnection(_serial->isConnected()); ui_task.loop(); #endif @@ -1357,6 +1413,13 @@ void setup() { spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); #endif int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); +#if defined(FAKETEC) + if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) { + #define SX126X_DIO3_TCXO_VOLTAGE (0.0f); + tcxo = SX126X_DIO3_TCXO_VOLTAGE; + status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + } +#endif if (status != RADIOLIB_ERR_NONE) { Serial.print("ERROR: radio init failed: "); Serial.println(status); @@ -1388,7 +1451,6 @@ void setup() { sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName()); serial_interface.begin(dev_name, the_mesh.getBLEPin()); #else - pinMode(WB_IO2, OUTPUT); serial_interface.begin(Serial); #endif the_mesh.startInterface(serial_interface); @@ -1413,6 +1475,8 @@ void setup() { #ifdef DISPLAY_CLASS display.begin(); +#endif +#ifdef HAS_UI ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE, the_mesh.getBLEPin()); #endif } diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 0ddf4fd..1bb42bd 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -96,6 +96,10 @@ #include #include static TechoBoard board; +#elif defined(FAKETEC) + #include + #include + static faketecBoard board; #else #error "need to provide a 'board' object" #endif @@ -685,6 +689,13 @@ void setup() { spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); #endif int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); +#if defined(FAKETEC) + if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) { + #define SX126X_DIO3_TCXO_VOLTAGE (0.0f); + tcxo = SX126X_DIO3_TCXO_VOLTAGE; + status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + } +#endif if (status != RADIOLIB_ERR_NONE) { delay(5000); Serial.print("ERROR: radio init failed: "); diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 94d1e4f..eb69bf5 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -76,7 +76,7 @@ #include #include static XiaoC3Board board; -#elif defined(SEEED_XIAO_S3) +#elif defined(SEEED_XIAO_S3) || defined(LILYGO_T3S3) #include #include static ESP32Board board; @@ -100,6 +100,10 @@ #include #include static TechoBoard board; +#elif defined(FAKETEC) + #include + #include + static faketecBoard board; #else #error "need to provide a 'board' object" #endif @@ -747,6 +751,13 @@ void setup() { spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); #endif int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); +#if defined(FAKETEC) + if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) { + #define SX126X_DIO3_TCXO_VOLTAGE (0.0f); + tcxo = SX126X_DIO3_TCXO_VOLTAGE; + status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo); + } +#endif if (status != RADIOLIB_ERR_NONE) { delay(5000); Serial.print("ERROR: radio init failed: "); diff --git a/examples/simple_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index 3a376e5..248353a 100644 --- a/examples/simple_secure_chat/main.cpp +++ b/examples/simple_secure_chat/main.cpp @@ -78,6 +78,10 @@ #include #include static T1000eBoard board; +#elif defined(FAKETEC) + #include + #include + static faketecBoard board; #else #error "need to provide a 'board' object" #endif diff --git a/platformio.ini b/platformio.ini index af7cef4..01d5775 100644 --- a/platformio.ini +++ b/platformio.ini @@ -130,7 +130,7 @@ build_flags = build_src_filter = ${esp32_base.build_src_filter} lib_deps = ${esp32_base.lib_deps} - adafruit/Adafruit SSD1306 @ ^2.5.13 + adafruit/Adafruit SSD1306 @ ^2.5.13 [env:Heltec_v3_repeater] extends = Heltec_lora32_v3 @@ -149,9 +149,6 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} [env:Heltec_v3_room_server] extends = Heltec_lora32_v3 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} - + - +<../examples/simple_room_server> build_flags = ${Heltec_lora32_v3.build_flags} -D DISPLAY_CLASS=SSD1306Display @@ -162,6 +159,9 @@ build_flags = -D ROOM_PASSWORD='"hello"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/simple_room_server> [env:Heltec_v3_terminal_chat] extends = Heltec_lora32_v3 @@ -522,7 +522,8 @@ build_flags = [LilyGo_T3S3_sx1262] extends = esp32_base board = t3_s3_v1_x -build_flags = ${esp32_base.build_flags} +build_flags = + ${esp32_base.build_flags} -D LILYGO_T3S3 -D P_LORA_DIO_1=33 -D P_LORA_NSS=7 @@ -533,6 +534,11 @@ build_flags = ${esp32_base.build_flags} -D P_LORA_MOSI=6 -D P_LORA_TX_LED=37 -D PIN_VBAT_READ=1 + -D PIN_USER_BTN=0 + -D PIN_BOARD_SDA=18 + -D PIN_BOARD_SCL=17 + -D P_LORA_TX_LED=37 + -D PIN_OLED_RESET=21 -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=130 @@ -540,20 +546,26 @@ build_flags = ${esp32_base.build_flags} -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 -D SX126X_RX_BOOSTED_GAIN=1 +build_src_filter = ${esp32_base.build_src_filter} +lib_deps = + ${esp32_base.lib_deps} + adafruit/Adafruit SSD1306 @ ^2.5.13 ; === LilyGo T3S3 with SX1262 environments === [env:LilyGo_T3S3_sx1262_Repeater] extends = LilyGo_T3S3_sx1262 -build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} - +<../examples/simple_repeater/main.cpp> build_flags = ${LilyGo_T3S3_sx1262.build_flags} + -D DISPLAY_CLASS=SSD1306Display -D ADVERT_NAME='"T3S3-1262 Repeater"' -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 -D ADMIN_PASSWORD='"password"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + + + +<../examples/simple_repeater> [env:LilyGo_T3S3_sx1262_terminal_chat] extends = LilyGo_T3S3_sx1262 @@ -569,10 +581,27 @@ lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:LilyGo_T3S3_sx1262_room_server] +extends = LilyGo_T3S3_sx1262 +build_flags = + ${LilyGo_T3S3_sx1262.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"T3S3-1262 Room"' + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + + + +<../examples/simple_room_server> + [env:LilyGo_T3S3_sx1262_companion_radio_usb] extends = LilyGo_T3S3_sx1262 build_flags = ${LilyGo_T3S3_sx1262.build_flags} + -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 @@ -580,7 +609,8 @@ build_flags = ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} - +<../examples/companion_radio/main.cpp> + + + +<../examples/companion_radio> lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -589,6 +619,7 @@ lib_deps = extends = LilyGo_T3S3_sx1262 build_flags = ${LilyGo_T3S3_sx1262.build_flags} + -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -599,7 +630,8 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + - +<../examples/companion_radio/main.cpp> + + + +<../examples/companion_radio> lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -820,8 +852,10 @@ board = tracker-t1000-e board_build.ldscript = boards/nrf52840_s140_v7.ld build_flags = ${nrf52840_t1000e.build_flags} -Ivariants/t1000-e - -DT1000_E + -D T1000_E -D PIN_USER_BTN=6 + -D USER_BTN_PRESSED=HIGH + -D PIN_STATUS_LED=24 -D RADIO_CLASS=CustomLR1110 -D WRAPPER_CLASS=CustomLR1110Wrapper -D MAX_LORA_TX_POWER=22 @@ -841,6 +875,7 @@ build_flags = ${t1000-e.build_flags} -D BLE_DEBUG_LOGGING=1 -D MESH_PACKET_LOGGING=1 -D MESH_DEBUG=1 + -D HAS_UI build_src_filter = ${t1000-e.build_src_filter} + +<../examples/companion_radio/*.cpp> @@ -996,3 +1031,93 @@ build_src_filter = ${LilyGo_Techo.build_src_filter} lib_deps = ${LilyGo_Techo.lib_deps} densaugeo/base64 @ ~1.4.0 + + +[faketec] +extends = nrf52840_base +board = promicro_nrf52840 +build_src_filter = ${nrf52840_base.build_src_filter} + +build_flags = ${nrf52840_base.build_flags} + -D FAKETEC + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_CURRENT_LIMIT=130 + -D SX126X_RX_BOOSTED_GAIN=1 + +[env:Faketec_Repeater] +extends = faketec +build_src_filter = ${faketec.build_src_filter} +<../examples/simple_repeater/main.cpp> +build_flags = + ${faketec.build_flags} + -D ADVERT_NAME="\"Faketec Repeater\"" + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD="\"password\"" +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${faketec.lib_deps} + adafruit/RTClib @ ^2.1.3 + +[env:Faketec_room_server] +extends = faketec +build_src_filter = ${faketec.build_src_filter} +<../examples/simple_room_server/main.cpp> +build_flags = + ${faketec.build_flags} + -D ADVERT_NAME="\"Test Room\"" + -D ADVERT_LAT=-37.0 + -D ADVERT_LON=145.0 + -D ADMIN_PASSWORD="\"password\"" + -D ROOM_PASSWORD="\"hello\"" +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${faketec.lib_deps} + adafruit/RTClib @ ^2.1.3 + +[env:Faketec_terminal_chat] +extends = faketec +build_flags = + ${faketec.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${faketec.build_src_filter} +<../examples/simple_secure_chat/main.cpp> +lib_deps = + ${faketec.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/RTClib @ ^2.1.3 + +[env:Faketec_companion_radio_usb] +extends = faketec +build_flags = + ${faketec.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 +; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 +build_src_filter = ${faketec.build_src_filter} +<../examples/companion_radio/main.cpp> +lib_deps = + ${faketec.lib_deps} + adafruit/RTClib @ ^2.1.3 + densaugeo/base64 @ ~1.4.0 + +[env:Faketec_companion_radio_ble] +extends = faketec +build_flags = + ${faketec.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D ENABLE_PRIVATE_KEY_EXPORT=1 + -D ENABLE_PRIVATE_KEY_IMPORT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${faketec.build_src_filter} + +<../examples/companion_radio/main.cpp> +lib_deps = + ${faketec.lib_deps} + adafruit/RTClib @ ^2.1.3 + densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/src/MeshCore.h b/src/MeshCore.h index 6bb1198..9593877 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -40,6 +40,7 @@ public: virtual void onBeforeTransmit() { } virtual void onAfterTransmit() { } virtual void reboot() = 0; + virtual void powerOff() { /* no op */ } virtual uint8_t getStartupReason() const = 0; virtual bool startOTAUpdate() { return false; } // not supported }; diff --git a/src/helpers/ArduinoSerialInterface.h b/src/helpers/ArduinoSerialInterface.h index 43613af..e84f6d3 100644 --- a/src/helpers/ArduinoSerialInterface.h +++ b/src/helpers/ArduinoSerialInterface.h @@ -23,7 +23,12 @@ public: #ifdef LILYGO_T3S3 void begin(HWCDC& serial) { _serial = &serial; } #elif defined(NRF52_PLATFORM) - void begin(Adafruit_USBD_CDC& serial) { _serial = &serial; } + void begin(Adafruit_USBD_CDC& serial) { + _serial = &serial; + #ifdef RAK_4631 + pinMode(WB_IO2, OUTPUT); + #endif + } #else void begin(HardwareSerial& serial) { _serial = &serial; } #endif diff --git a/src/helpers/HeltecV3Board.h b/src/helpers/HeltecV3Board.h index 927d537..605bfe2 100644 --- a/src/helpers/HeltecV3Board.h +++ b/src/helpers/HeltecV3Board.h @@ -24,11 +24,20 @@ #include class HeltecV3Board : public ESP32Board { +private: + bool adc_active_state; + public: void begin() { ESP32Board::begin(); + // Auto-detect correct ADC_CTRL pin polarity (different for boards >3.2) + pinMode(PIN_ADC_CTRL, INPUT); + adc_active_state = !digitalRead(PIN_ADC_CTRL); + pinMode(PIN_ADC_CTRL, OUTPUT); + digitalWrite(PIN_ADC_CTRL, !adc_active_state); // Initially inactive + pinMode(PIN_VEXT_EN, OUTPUT); digitalWrite(PIN_VEXT_EN, LOW); // for V3.2 boards @@ -67,9 +76,14 @@ public: esp_deep_sleep_start(); // CPU halts here and never returns! } + void powerOff() override { + // TODO: re-enable this when there is a definite wake-up source pin: + // enterDeepSleep(0); + } + uint16_t getBattMilliVolts() override { analogReadResolution(10); - digitalWrite(PIN_ADC_CTRL, PIN_ADC_CTRL_ACTIVE); + digitalWrite(PIN_ADC_CTRL, adc_active_state); uint32_t raw = 0; for (int i = 0; i < 8; i++) { @@ -77,7 +91,7 @@ public: } raw = raw / 8; - digitalWrite(PIN_ADC_CTRL, PIN_ADC_CTRL_INACTIVE); + digitalWrite(PIN_ADC_CTRL, !adc_active_state); return (5.2 * (3.3 / 1024.0) * raw) * 1000; } diff --git a/src/helpers/nrf52/T1000eBoard.h b/src/helpers/nrf52/T1000eBoard.h index 3c0f30b..ce2206e 100644 --- a/src/helpers/nrf52/T1000eBoard.h +++ b/src/helpers/nrf52/T1000eBoard.h @@ -3,8 +3,6 @@ #include #include -#define HAS_T1000e_POWEROFF - // LoRa and SPI pins #define P_LORA_DIO_1 (32 + 1) // P1.1 #define P_LORA_NSS (0 + 12) // P0.12 @@ -61,7 +59,7 @@ public: return 0; } - void powerOff() { + void powerOff() override { #ifdef HAS_GPS digitalWrite(GPS_VRTC_EN, LOW); digitalWrite(GPS_RESET, LOW); diff --git a/src/helpers/nrf52/faketecBoard.cpp b/src/helpers/nrf52/faketecBoard.cpp new file mode 100644 index 0000000..6ef294b --- /dev/null +++ b/src/helpers/nrf52/faketecBoard.cpp @@ -0,0 +1,63 @@ +#include +#include "faketecBoard.h" + +#include +#include + +static BLEDfu bledfu; + +static void connect_callback(uint16_t conn_handle) +{ + (void)conn_handle; + MESH_DEBUG_PRINTLN("BLE client connected"); +} + +static void disconnect_callback(uint16_t conn_handle, uint8_t reason) +{ + (void)conn_handle; + (void)reason; + + MESH_DEBUG_PRINTLN("BLE client disconnected"); +} + +bool faketecBoard::startOTAUpdate() { + // Config the peripheral connection with maximum bandwidth + // more SRAM required by SoftDevice + // Note: All config***() function must be called before begin() + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); + + Bluefruit.begin(1, 0); + // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4 + Bluefruit.setTxPower(4); + // Set the BLE device name + Bluefruit.setName("Faketec_OTA"); + + Bluefruit.Periph.setConnectCallback(connect_callback); + Bluefruit.Periph.setDisconnectCallback(disconnect_callback); + + // To be consistent OTA DFU should be added first if it exists + bledfu.begin(); + + // Set up and start advertising + // Advertising packet + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + Bluefruit.Advertising.addName(); + + /* Start Advertising + - Enable auto advertising if disconnected + - Interval: fast mode = 20 ms, slow mode = 152.5 ms + - Timeout for fast mode is 30 seconds + - Start(timeout) with timeout = 0 will advertise forever (until connected) + + For recommended advertising interval + https://developer.apple.com/library/content/qa/qa1931/_index.html + */ + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms + Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode + Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds + + return true; +} diff --git a/src/helpers/nrf52/faketecBoard.h b/src/helpers/nrf52/faketecBoard.h new file mode 100644 index 0000000..b5cebb7 --- /dev/null +++ b/src/helpers/nrf52/faketecBoard.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include + +#define P_LORA_NSS 13 //P1.13 45 +#define P_LORA_DIO_1 11 //P0.10 10 +#define P_LORA_RESET 10 //P0.09 9 +#define P_LORA_BUSY 16 //P0.29 29 +#define P_LORA_MISO 15 //P0.02 2 +#define P_LORA_SCLK 12 //P1.11 43 +#define P_LORA_MOSI 14 //P1.15 47 +#define SX126X_POWER_EN 21 //P0.13 13 +#define SX126X_RXEN 2 //P0.17 +#define SX126X_TXEN RADIOLIB_NC +#define SX126X_DIO2_AS_RF_SWITCH true +#define SX126X_DIO3_TCXO_VOLTAGE (1.8f) + +#define PIN_VBAT_READ 17 +#define ADC_MULTIPLIER (1.815f) // dependent on voltage divider resistors. TODO: more accurate battery tracking + +class faketecBoard : public mesh::MainBoard { +protected: + uint8_t startup_reason; + +public: + void begin() { + // for future use, sub-classes SHOULD call this from their begin() + startup_reason = BD_STARTUP_NORMAL; + + pinMode(PIN_VBAT_READ, INPUT); + + #if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL); + #else + Wire.begin(); + #endif + + pinMode(SX126X_POWER_EN, OUTPUT); + digitalWrite(SX126X_POWER_EN, HIGH); + delay(10); // give sx1262 some time to power up + } + + uint8_t getStartupReason() const override { return startup_reason; } + + #define BATTERY_SAMPLES 8 + + uint16_t getBattMilliVolts() override { + analogReadResolution(12); + + uint32_t raw = 0; + for (int i = 0; i < BATTERY_SAMPLES; i++) { + raw += analogRead(PIN_VBAT_READ); + } + raw = raw / BATTERY_SAMPLES; + return (ADC_MULTIPLIER * raw); + } + + const char* getManufacturerName() const override { + return "Faketec DIY"; + } + + void reboot() override { + NVIC_SystemReset(); + } + + bool startOTAUpdate() override; +}; diff --git a/variants/promicro_nrf52840/variant.cpp b/variants/promicro_nrf52840/variant.cpp new file mode 100644 index 0000000..0a4c3aa --- /dev/null +++ b/variants/promicro_nrf52840/variant.cpp @@ -0,0 +1,15 @@ +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + 8, 6, 17, 20, 22, 24, 32, 11, 36, 38, + 9, 10, 43, 45, 47, 2, 29, 31, + 33, 34, 37, + 13, 15 +}; + +void initVariant() +{ +} + diff --git a/variants/promicro_nrf52840/variant.h b/variants/promicro_nrf52840/variant.h new file mode 100644 index 0000000..98489da --- /dev/null +++ b/variants/promicro_nrf52840/variant.h @@ -0,0 +1,82 @@ +/* + * variant.h + * Copyright (C) 2023 Seeed K.K. + * MIT License + */ + + #pragma once + + #include "WVariant.h" + + //////////////////////////////////////////////////////////////////////////////// + // Low frequency clock source + +#define VARIANT_MCK (64000000ul) + +//#define USE_LFXO // 32.768 kHz crystal oscillator +#define USE_LFRC // 32.768 kHz RC oscillator + +//////////////////////////////////////////////////////////////////////////////// +// Power + +#define PIN_EXT_VCC (21) +#define EXT_VCC (PIN_EXT_VCC) + +#define BATTERY_PIN (17) +#define ADC_RESOLUTION 12 + +//////////////////////////////////////////////////////////////////////////////// +// Number of pins + +#define PINS_COUNT (23) +#define NUM_DIGITAL_PINS (23) +#define NUM_ANALOG_INPUTS (3) +#define NUM_ANALOG_OUTPUTS (0) + +//////////////////////////////////////////////////////////////////////////////// +// UART pin definition + +#define PIN_SERIAL1_TX (1) +#define PIN_SERIAL1_RX (0) + +//////////////////////////////////////////////////////////////////////////////// +// I2C pin definition + +#define WIRE_INTERFACES_COUNT 2 + +#define PIN_WIRE_SDA (6) +#define PIN_WIRE_SCL (7) +#define PIN_WIRE1_SDA (13) +#define PIN_WIRE1_SCL (14) + +//////////////////////////////////////////////////////////////////////////////// +// SPI pin definition + +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_SCK (2) +#define PIN_SPI_MISO (3) +#define PIN_SPI_MOSI (4) + +#define PIN_SPI_NSS (5) + +#define PIN_SPI1_SCK (18) +#define PIN_SPI1_MISO (19) +#define PIN_SPI1_MOSI (20) + +//////////////////////////////////////////////////////////////////////////////// +// Builtin LEDs + +#define PIN_LED (22) +#define LED_PIN PIN_LED +#define LED_BLUE PIN_LED +#define LED_BUILTIN PIN_LED +#define LED_STATE_ON 1 + +//////////////////////////////////////////////////////////////////////////////// +// Builtin buttons + +#define PIN_BUTTON1 (6) +#define BUTTON_PIN PIN_BUTTON1 + +