diff --git a/boards/nano-g2-ultra.json b/boards/nano-g2-ultra.json index 11e7ebaa..9fa22d7b 100644 --- a/boards/nano-g2-ultra.json +++ b/boards/nano-g2-ultra.json @@ -46,7 +46,8 @@ ], "debug": { "jlink_device": "nRF52840_xxAA", - "svd_path": "nrf52840.svd" + "svd_path": "nrf52840.svd", + "openocd_target": "nrf52.cfg" }, "frameworks": [ "arduino" @@ -69,4 +70,4 @@ }, "url": "https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra", "vendor": "BQ Consulting" -} \ No newline at end of file +} diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index deeacdad..01906f90 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -5,7 +5,7 @@ #include "MyMesh.h" #define AUTO_OFF_MILLIS 15000 // 15 seconds -#define BOOT_SCREEN_MILLIS 4000 // 4 seconds +#define BOOT_SCREEN_MILLIS 3000 // 3 seconds #ifdef PIN_STATUS_LED #define LED_ON_MILLIS 20 @@ -163,9 +163,9 @@ void UITask::renderCurrScreen() { char tmp[80]; if (_alert[0]) { + _display->setTextSize(1.4); uint16_t textWidth = _display->getTextWidth(_alert); _display->setCursor((_display->width() - textWidth) / 2, 22); - _display->setTextSize(1.4); _display->setColor(DisplayDriver::GREEN); _display->print(_alert); _alert[0] = 0; @@ -191,7 +191,7 @@ void UITask::renderCurrScreen() { sprintf(tmp, "%d", _msgcount); _display->print(tmp); _display->setColor(DisplayDriver::YELLOW); // last color will be kept on T114 - } else if (millis() < BOOT_SCREEN_MILLIS) { // boot screen + } else if ((millis() - ui_started_at) < BOOT_SCREEN_MILLIS) { // boot screen // meshcore logo _display->setColor(DisplayDriver::BLUE); int logoWidth = 128; @@ -302,7 +302,7 @@ void UITask::loop() { if (_display != NULL && _display->isOn()) { static bool _firstBoot = true; - if(_firstBoot && millis() >= BOOT_SCREEN_MILLIS) { + if(_firstBoot && (millis() - ui_started_at) >= BOOT_SCREEN_MILLIS) { _need_refresh = true; _firstBoot = false; } @@ -344,6 +344,8 @@ void UITask::handleButtonShortPress() { // Otherwise, refresh the display _need_refresh = true; } + } else { + _need_refresh = true; // display just turned on, so we need to refresh } // Note: Display turn-on and auto-off timer extension are handled by handleButtonAnyPress } @@ -372,10 +374,12 @@ void UITask::handleButtonTriplePress() { if (buzzer.isQuiet()) { buzzer.quiet(false); soundBuzzer(UIEventType::ack); + sprintf(_alert, "Buzzer: ON"); } else { - soundBuzzer(UIEventType::ack); buzzer.quiet(true); + sprintf(_alert, "Buzzer: OFF"); } + _need_refresh = true; #endif } diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index effc177a..c33cadda 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -337,6 +337,9 @@ protected: int getInterferenceThreshold() const override { return _prefs.interference_threshold; } + int getAGCResetInterval() const override { + return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds + } void onAnonDataRecv(mesh::Packet* packet, uint8_t type, const mesh::Identity& sender, uint8_t* data, size_t len) override { if (type == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin client (unknown at this stage) @@ -575,7 +578,7 @@ public: _prefs.advert_interval = 1; // default to 2 minutes for NEW installs _prefs.flood_advert_interval = 3; // 3 hours _prefs.flood_max = 64; - _prefs.interference_threshold = 14; // DB + _prefs.interference_threshold = 0; // disabled } CommonCLI* getCLI() { return &_cli; } diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 400b31dd..740d70e5 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -420,6 +420,9 @@ protected: int getInterferenceThreshold() const override { return _prefs.interference_threshold; } + int getAGCResetInterval() const override { + return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds + } bool allowPacketForward(const mesh::Packet* packet) override { if (_prefs.disable_fwd) return false; @@ -725,7 +728,7 @@ public: _prefs.advert_interval = 1; // default to 2 minutes for NEW installs _prefs.flood_advert_interval = 3; // 3 hours _prefs.flood_max = 64; - _prefs.interference_threshold = 14; // DB + _prefs.interference_threshold = 0; // disabled #ifdef ROOM_PASSWORD StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); #endif diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 7ac5cbe3..7f39dc49 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -87,6 +87,14 @@ void Dispatcher::loop() { } else { return; // can't do any more radio activity until send is complete or timed out } + + // going back into receive mode now... + next_agc_reset_time = futureMillis(getAGCResetInterval()); + } + + if (getAGCResetInterval() > 0 && millisHasNowPassed(next_agc_reset_time)) { + _radio->resetAGC(); + next_agc_reset_time = futureMillis(getAGCResetInterval()); } // check inbound (delayed) queue diff --git a/src/Dispatcher.h b/src/Dispatcher.h index bce13b6b..2200f81b 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -65,6 +65,8 @@ public: virtual void triggerNoiseFloorCalibrate(int threshold) { } + virtual void resetAGC() { } + virtual bool isInRecvMode() const = 0; /** @@ -116,7 +118,7 @@ class Dispatcher { unsigned long next_tx_time; unsigned long cad_busy_start; unsigned long radio_nonrx_start; - unsigned long next_floor_calib_time; + unsigned long next_floor_calib_time, next_agc_reset_time; bool prev_isrecv_mode; uint32_t n_sent_flood, n_sent_direct; uint32_t n_recv_flood, n_recv_direct; @@ -134,7 +136,7 @@ protected: { outbound = NULL; total_air_time = 0; next_tx_time = 0; cad_busy_start = 0; - next_floor_calib_time = 0; + next_floor_calib_time = next_agc_reset_time = 0; _err_flags = 0; radio_nonrx_start = 0; prev_isrecv_mode = true; @@ -154,6 +156,7 @@ protected: virtual uint32_t getCADFailRetryDelay() const; virtual uint32_t getCADFailMaxDuration() const; virtual int getInterferenceThreshold() const { return 0; } // disabled by default + virtual int getAGCResetInterval() const { return 0; } // disabled by default public: void begin(); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index baad8f40..63e2740c 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -53,7 +53,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 file.read((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115 file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 - file.read(pad, 4); // 120 + file.read((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 + file.read(pad, 3); // 121 file.read((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124 file.read((uint8_t *) &_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125 file.read((uint8_t *) &_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126 @@ -107,7 +108,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 file.write((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115 file.write((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 - file.write(pad, 4); // 120 + file.write((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 + file.write(pad, 3); // 121 file.write((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124 file.write((uint8_t *) &_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125 file.write((uint8_t *) &_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126 @@ -180,6 +182,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor)); } else if (memcmp(config, "int.thresh", 10) == 0) { sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold); + } else if (memcmp(config, "agc.reset.interval", 18) == 0) { + sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4); } else if (memcmp(config, "allow.read.only", 15) == 0) { sprintf(reply, "> %s", _prefs->allow_read_only ? "on" : "off"); } else if (memcmp(config, "flood.advert.interval", 21) == 0) { @@ -231,6 +235,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _prefs->interference_threshold = atoi(&config[11]); savePrefs(); strcpy(reply, "OK"); + } else if (memcmp(config, "agc.reset.interval ", 19) == 0) { + _prefs->agc_reset_interval = atoi(&config[19]) / 4; + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "allow.read.only ", 16) == 0) { _prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0; savePrefs(); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 37402c09..1778c715 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -25,6 +25,7 @@ struct NodePrefs { // persisted to file float bw; uint8_t flood_max; uint8_t interference_threshold; + uint8_t agc_reset_interval; // secs / 4 }; class CommonCLICallbacks { diff --git a/src/helpers/RadioLibWrappers.cpp b/src/helpers/RadioLibWrappers.cpp index 1415a864..0593451a 100644 --- a/src/helpers/RadioLibWrappers.cpp +++ b/src/helpers/RadioLibWrappers.cpp @@ -52,6 +52,15 @@ void RadioLibWrapper::triggerNoiseFloorCalibrate(int threshold) { } } +void RadioLibWrapper::resetAGC() { + // make sure we're not mid-receive of packet! + if ((state & STATE_INT_READY) != 0 || isReceivingPacket()) return; + + // NOTE: according to higher powers, just issuing RadioLib's startReceive() will reset the AGC. + // revisit this if a better impl is discovered. + state = STATE_IDLE; // trigger a startReceive() +} + void RadioLibWrapper::loop() { if (state == STATE_RX && _num_floor_samples < NUM_NOISE_FLOOR_SAMPLES) { if (!isReceivingPacket()) { diff --git a/src/helpers/RadioLibWrappers.h b/src/helpers/RadioLibWrappers.h index bb308071..25cc5358 100644 --- a/src/helpers/RadioLibWrappers.h +++ b/src/helpers/RadioLibWrappers.h @@ -39,6 +39,7 @@ public: int getNoiseFloor() const override { return _noise_floor; } void triggerNoiseFloorCalibrate(int threshold) override; + void resetAGC() override; void loop() override; diff --git a/src/helpers/nrf52/ThinkNodeM1Board.cpp b/src/helpers/nrf52/ThinkNodeM1Board.cpp index ef1cf111..63768eea 100644 --- a/src/helpers/nrf52/ThinkNodeM1Board.cpp +++ b/src/helpers/nrf52/ThinkNodeM1Board.cpp @@ -26,6 +26,11 @@ void ThinkNodeM1Board::begin() { Wire.begin(); + #ifdef P_LORA_TX_LED + pinMode(P_LORA_TX_LED, OUTPUT); + digitalWrite(P_LORA_TX_LED, LOW); + #endif + pinMode(SX126X_POWER_EN, OUTPUT); digitalWrite(SX126X_POWER_EN, HIGH); delay(10); // give sx1262 some time to power up diff --git a/src/helpers/nrf52/ThinkNodeM1Board.h b/src/helpers/nrf52/ThinkNodeM1Board.h index cc87c96d..97334bd3 100644 --- a/src/helpers/nrf52/ThinkNodeM1Board.h +++ b/src/helpers/nrf52/ThinkNodeM1Board.h @@ -39,6 +39,15 @@ public: return startup_reason; } + #if defined(P_LORA_TX_LED) + void onBeforeTransmit() override { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + } + void onAfterTransmit() override { + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + } + #endif + const char* getManufacturerName() const override { return "Elecrow ThinkNode-M1"; } diff --git a/variants/heltec_ct62/HT-CT62Board.h b/variants/heltec_ct62/HT-CT62Board.h new file mode 100644 index 00000000..e5a627b8 --- /dev/null +++ b/variants/heltec_ct62/HT-CT62Board.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +#if defined(ESP_PLATFORM) + +#include + +class Heltec_CT62_Board : public ESP32Board { +public: + +uint16_t getBattMilliVolts() override { + #ifdef PIN_VBAT_READ + analogReadResolution(12); // ESP32-C3 ADC is 12-bit - 3.3/4096 (ref voltage/max counts) + uint32_t raw = 0; + for (int i = 0; i < 8; i++) { + raw += analogRead(PIN_VBAT_READ); + } + raw = raw / 8; + + return ((6.52 * raw) / 1024.0) * 1000; + #else + return 0; // not supported + #endif + } + + const char* getManufacturerName() const override { + return "Heltec CT62"; + } +}; + +#endif diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index 6a0ba376..ba23a5a6 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -12,12 +12,13 @@ build_flags = -D P_LORA_TX_LED=18 -D PIN_BOARD_SDA=0 -D PIN_BOARD_SCL=1 - -D PIN_USER_BTN=9 + ;-D PIN_USER_BTN=9 -D PIN_VBAT_READ=2 -D P_LORA_DIO_1=3 -D P_LORA_NSS=8 -D P_LORA_RESET=5 - -D P_LORA_RESET=RADIOLIB_NC + -D P_LORA_DIO_0=RADIOLIB_NC + -D P_LORA_DIO_2=RADIOLIB_NC -D P_LORA_BUSY=4 -D P_LORA_SCLK=10 -D P_LORA_MISO=6 @@ -33,6 +34,8 @@ build_src_filter = ${esp32_base.build_src_filter} extends = Heltec_ct62 build_flags = ${Heltec_ct62.build_flags} + ;-D ARDUINO_USB_MODE=1 + ;-D ARDUINO_USB_CDC_ON_BOOT=1 -D ADVERT_NAME='"HT-CT62 Repeater"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 @@ -45,3 +48,41 @@ build_src_filter = ${Heltec_ct62.build_src_filter} lib_deps = ${Heltec_ct62.lib_deps} ${esp32_ota.lib_deps} + +[env:Heltec_ct62_companion_radio_usb] +extends = Heltec_ct62 +build_flags = + ${Heltec_ct62.build_flags} +; -D ARDUINO_USB_MODE=1 +; -D ARDUINO_USB_CDC_ON_BOOT=1 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_ct62.build_src_filter} + +<../examples/companion_radio> +lib_deps = + ${Heltec_ct62.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Heltec_ct62_companion_radio_ble] +extends = Heltec_ct62 +build_flags = + ${Heltec_ct62.build_flags} +; -D ARDUINO_USB_MODE=1 +; -D ARDUINO_USB_CDC_ON_BOOT=1 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D OFFLINE_QUEUE_SIZE=256 + -D BLE_PIN_CODE=123456 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_ct62.build_src_filter} + +<../examples/companion_radio> + + +lib_deps = + ${Heltec_ct62.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/heltec_ct62/target.cpp b/variants/heltec_ct62/target.cpp index df43bc45..eae43bd1 100644 --- a/variants/heltec_ct62/target.cpp +++ b/variants/heltec_ct62/target.cpp @@ -1,14 +1,9 @@ #include #include "target.h" -ESP32Board board; - -#if defined(P_LORA_SCLK) - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); -#endif +Heltec_CT62_Board board; +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); WRAPPER_CLASS radio_driver(radio, board); ESP32RTCClock fallback_clock; @@ -72,4 +67,4 @@ void radio_set_tx_power(uint8_t dbm) { mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity -} +} \ No newline at end of file diff --git a/variants/heltec_ct62/target.h b/variants/heltec_ct62/target.h index eef923ab..ff114e6e 100644 --- a/variants/heltec_ct62/target.h +++ b/variants/heltec_ct62/target.h @@ -3,12 +3,12 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include "HT-CT62Board.h" #include #include #include -extern ESP32Board board; +extern Heltec_CT62_Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; @@ -17,4 +17,4 @@ bool radio_init(); uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); void radio_set_tx_power(uint8_t dbm); -mesh::LocalIdentity radio_new_identity(); +mesh::LocalIdentity radio_new_identity(); \ No newline at end of file diff --git a/variants/thinknode_m1/platformio.ini b/variants/thinknode_m1/platformio.ini index 1a4837d8..2104a080 100644 --- a/variants/thinknode_m1/platformio.ini +++ b/variants/thinknode_m1/platformio.ini @@ -19,6 +19,7 @@ build_flags = ${nrf52840_thinknode_m1.build_flags} -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 + -D P_LORA_TX_LED=13 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 build_src_filter = ${nrf52840_thinknode_m1.build_src_filter}