diff --git a/boards/t_beam_1w.json b/boards/t_beam_1w.json new file mode 100644 index 00000000..2f1159aa --- /dev/null +++ b/boards/t_beam_1w.json @@ -0,0 +1,50 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "memory_type": "qio_opi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DLILYGO_TBEAM_1W", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=0", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "psram_type": "opi", + "hwids": [ + [ + "0x303A", + "0x1001" + ] + ], + "mcu": "esp32s3", + "variant": "lilygo_tbeam_1w" + }, + "connectivity": [ + "wifi", + "bluetooth", + "lora" + ], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino" + ], + "name": "LilyGo TBeam-1W", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 921600 + }, + "url": "http://www.lilygo.cn/", + "vendor": "LilyGo" +} diff --git a/docs/stats_binary_frames.md b/docs/stats_binary_frames.md index 1b409912..f3b17da9 100644 --- a/docs/stats_binary_frames.md +++ b/docs/stats_binary_frames.md @@ -94,7 +94,7 @@ struct StatsRadio { ## RESP_CODE_STATS + STATS_TYPE_PACKETS (24, 2) -**Total Frame Size:** 26 bytes +**Total Frame Size:** 26 bytes (legacy) or 30 bytes (includes `recv_errors`) | Offset | Size | Type | Field Name | Description | Range/Notes | |--------|------|------|------------|-------------|-------------| @@ -106,12 +106,14 @@ struct StatsRadio { | 14 | 4 | uint32_t | direct_tx | Packets sent via direct routing | 0 - 4,294,967,295 | | 18 | 4 | uint32_t | flood_rx | Packets received via flood routing | 0 - 4,294,967,295 | | 22 | 4 | uint32_t | direct_rx | Packets received via direct routing | 0 - 4,294,967,295 | +| 26 | 4 | uint32_t | recv_errors | Receive/CRC errors (RadioLib); present only in 30-byte frame | 0 - 4,294,967,295 | ### Notes - Counters are cumulative from boot and may wrap. - `recv = flood_rx + direct_rx` - `sent = flood_tx + direct_tx` +- Clients should accept frame length ≥ 26; if length ≥ 30, parse `recv_errors` at offset 26. ### Example Structure (C/C++) @@ -125,6 +127,7 @@ struct StatsPackets { uint32_t direct_tx; uint32_t flood_rx; uint32_t direct_rx; + uint32_t recv_errors; // present when frame size is 30 } __attribute__((packed)); ``` @@ -183,11 +186,12 @@ def parse_stats_radio(frame): } def parse_stats_packets(frame): - """Parse RESP_CODE_STATS + STATS_TYPE_PACKETS frame (26 bytes)""" + """Parse RESP_CODE_STATS + STATS_TYPE_PACKETS frame (26 or 30 bytes)""" + assert len(frame) >= 26, "STATS_TYPE_PACKETS frame too short" response_code, stats_type, recv, sent, flood_tx, direct_tx, flood_rx, direct_rx = \ - struct.unpack('= 30: + (recv_errors,) = struct.unpack('= 30) { + result.recv_errors = view.getUint32(26, true); + } + return result; } ``` diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index f61f53ae..c0f2c021 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -560,14 +560,20 @@ bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src } return false; // error } +bool DataStore::deleteBlobByKey(const uint8_t key[], int key_len) { + return true; // this is just a stub on NRF52/STM32 platforms +} #else -uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { - char path[64]; +inline void makeBlobPath(const uint8_t key[], int key_len, char* path, size_t path_size) { char fname[18]; - if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) mesh::Utils::toHex(fname, key, key_len); sprintf(path, "/bl/%s", fname); +} + +uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { + char path[64]; + makeBlobPath(key, key_len, path, sizeof(path)); if (_fs->exists(path)) { File f = openRead(_fs, path); @@ -582,11 +588,7 @@ uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_b bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) { char path[64]; - char fname[18]; - - if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) - mesh::Utils::toHex(fname, key, key_len); - sprintf(path, "/bl/%s", fname); + makeBlobPath(key, key_len, path, sizeof(path)); File f = openWrite(_fs, path); if (f) { @@ -598,4 +600,13 @@ bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src } return false; // error } + +bool DataStore::deleteBlobByKey(const uint8_t key[], int key_len) { + char path[64]; + makeBlobPath(key, key_len, path, sizeof(path)); + + _fs->remove(path); + + return true; // return true even if file did not exist +} #endif diff --git a/examples/companion_radio/DataStore.h b/examples/companion_radio/DataStore.h index 62580942..58b4d5d2 100644 --- a/examples/companion_radio/DataStore.h +++ b/examples/companion_radio/DataStore.h @@ -42,6 +42,7 @@ public: void migrateToSecondaryFS(); uint8_t getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len); + bool deleteBlobByKey(const uint8_t key[], int key_len); File openRead(const char* filename); File openRead(FILESYSTEM* fs, const char* filename); bool removeFile(const char* filename); diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 2dad7866..9bb747e7 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -307,6 +307,7 @@ bool MyMesh::shouldOverwriteWhenFull() const { } void MyMesh::onContactOverwrite(const uint8_t* pub_key) { + _store->deleteBlobByKey(pub_key, PUB_KEY_SIZE); // delete from storage if (_serial->isConnected()) { out_frame[0] = PUSH_CODE_CONTACT_DELETED; memcpy(&out_frame[1], pub_key, PUB_KEY_SIZE); @@ -330,10 +331,11 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); } - } + } else { #ifdef DISPLAY_CLASS - if (_ui && !_prefs.buzzer_quiet) _ui->notify(UIEventType::newContactMessage); //buzz if enabled + if (_ui) _ui->notify(UIEventType::newContactMessage); #endif + } // add inbound-path to mem cache if (path && path_len <= sizeof(AdvertPath::path)) { // check path is valid @@ -440,7 +442,9 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe bool should_display = txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_SIGNED_PLAIN; if (should_display && _ui) { _ui->newMsg(path_len, from.name, text, offline_queue_len); - if (!_prefs.buzzer_quiet) _ui->notify(UIEventType::contactMessage); //buzz if enabled + if (!_serial->isConnected()) { + _ui->notify(UIEventType::contactMessage); + } } #endif } @@ -525,8 +529,11 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe uint8_t frame[1]; frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); + } else { +#ifdef DISPLAY_CLASS + if (_ui) _ui->notify(UIEventType::channelMessage); +#endif } - #ifdef DISPLAY_CLASS // Get the channel name from the channel index const char *channel_name = "Unknown"; @@ -534,10 +541,7 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe if (getChannel(channel_idx, channel_details)) { channel_name = channel_details.name; } - if (_ui) { - _ui->newMsg(path_len, channel_name, text, offline_queue_len); - if (!_prefs.buzzer_quiet) _ui->notify(UIEventType::channelMessage); //buzz if enabled - } + if (_ui) _ui->newMsg(path_len, channel_name, text, offline_queue_len); #endif } @@ -796,7 +800,6 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe _prefs.bw = LORA_BW; _prefs.cr = LORA_CR; _prefs.tx_power_dbm = LORA_TX_POWER; - _prefs.buzzer_quiet = 0; _prefs.gps_enabled = 0; // GPS disabled by default _prefs.gps_interval = 0; // No automatic GPS updates by default //_prefs.rx_delay_base = 10.0f; enable once new algo fixed @@ -836,7 +839,6 @@ void MyMesh::begin(bool has_display) { _prefs.sf = constrain(_prefs.sf, 5, 12); _prefs.cr = constrain(_prefs.cr, 5, 8); _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, 1, MAX_LORA_TX_POWER); - _prefs.buzzer_quiet = constrain(_prefs.buzzer_quiet, 0, 1); // Ensure boolean 0 or 1 _prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1 _prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours @@ -1123,6 +1125,7 @@ void MyMesh::handleCmdFrame(size_t len) { uint8_t *pub_key = &cmd_frame[1]; ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); if (recipient && removeContact(*recipient)) { + _store->deleteBlobByKey(pub_key, PUB_KEY_SIZE); dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); writeOKFrame(); } else { @@ -1688,12 +1691,14 @@ void MyMesh::handleCmdFrame(size_t len) { uint32_t n_sent_direct = getNumSentDirect(); uint32_t n_recv_flood = getNumRecvFlood(); uint32_t n_recv_direct = getNumRecvDirect(); + uint32_t n_recv_errors = radio_driver.getPacketsRecvErrors(); memcpy(&out_frame[i], &recv, 4); i += 4; memcpy(&out_frame[i], &sent, 4); i += 4; memcpy(&out_frame[i], &n_sent_flood, 4); i += 4; memcpy(&out_frame[i], &n_sent_direct, 4); i += 4; memcpy(&out_frame[i], &n_recv_flood, 4); i += 4; memcpy(&out_frame[i], &n_recv_direct, 4); i += 4; + memcpy(&out_frame[i], &n_recv_errors, 4); i += 4; _serial->writeFrame(out_frame, i); } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); // invalid stats sub-type diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index a2b0033f..95265a19 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -8,11 +8,11 @@ #define FIRMWARE_VER_CODE 8 #ifndef FIRMWARE_BUILD_DATE -#define FIRMWARE_BUILD_DATE "30 Nov 2025" +#define FIRMWARE_BUILD_DATE "29 Jan 2026" #endif #ifndef FIRMWARE_VERSION -#define FIRMWARE_VERSION "v1.11.0" +#define FIRMWARE_VERSION "v1.12.0" #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 8077627f..0690b45a 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -103,8 +103,14 @@ class HomeScreen : public UIScreen { void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) { // Convert millivolts to percentage - const int minMilliVolts = 3000; // Minimum voltage (e.g., 3.0V) - const int maxMilliVolts = 4200; // Maximum voltage (e.g., 4.2V) +#ifndef BATT_MIN_MILLIVOLTS + #define BATT_MIN_MILLIVOLTS 3000 +#endif +#ifndef BATT_MAX_MILLIVOLTS + #define BATT_MAX_MILLIVOLTS 4200 +#endif + const int minMilliVolts = BATT_MIN_MILLIVOLTS; + const int maxMilliVolts = BATT_MAX_MILLIVOLTS; int batteryPercentage = ((batteryMilliVolts - minMilliVolts) * 100) / (maxMilliVolts - minMilliVolts); if (batteryPercentage < 0) batteryPercentage = 0; // Clamp to 0% if (batteryPercentage > 100) batteryPercentage = 100; // Clamp to 100% diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp index 39cbf23a..3ad36fb0 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -149,8 +149,14 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i void UITask::renderBatteryIndicator(uint16_t batteryMilliVolts) { // Convert millivolts to percentage - const int minMilliVolts = 3000; // Minimum voltage (e.g., 3.0V) - const int maxMilliVolts = 4200; // Maximum voltage (e.g., 4.2V) +#ifndef BATT_MIN_MILLIVOLTS + #define BATT_MIN_MILLIVOLTS 3000 +#endif +#ifndef BATT_MAX_MILLIVOLTS + #define BATT_MAX_MILLIVOLTS 4200 +#endif + const int minMilliVolts = BATT_MIN_MILLIVOLTS; + const int maxMilliVolts = BATT_MAX_MILLIVOLTS; int batteryPercentage = ((batteryMilliVolts - minMilliVolts) * 100) / (maxMilliVolts - minMilliVolts); if (batteryPercentage < 0) batteryPercentage = 0; // Clamp to 0% if (batteryPercentage > 100) batteryPercentage = 100; // Clamp to 100% diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 60d22902..0d5cd28a 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -69,11 +69,11 @@ struct NeighbourInfo { }; #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "30 Nov 2025" + #define FIRMWARE_BUILD_DATE "29 Jan 2026" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.11.0" + #define FIRMWARE_VERSION "v1.12.0" #endif #define FIRMWARE_ROLE "repeater" diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 4f3ed0e4..f470e55e 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -26,11 +26,11 @@ /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "30 Nov 2025" + #define FIRMWARE_BUILD_DATE "29 Jan 2026" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.11.0" + #define FIRMWARE_VERSION "v1.12.0" #endif #ifndef LORA_FREQ diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index eb2d90c5..ed352345 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -33,11 +33,11 @@ #define PERM_RECV_ALERTS_HI (1 << 7) // high priority alerts #ifndef FIRMWARE_BUILD_DATE - #define FIRMWARE_BUILD_DATE "30 Nov 2025" + #define FIRMWARE_BUILD_DATE "29 Jan 2026" #endif #ifndef FIRMWARE_VERSION - #define FIRMWARE_VERSION "v1.11.0" + #define FIRMWARE_VERSION "v1.12.0" #endif #define FIRMWARE_ROLE "sensor" diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index aebfc1b6..6de7469d 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -131,7 +131,6 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, plen = packet->writeTo(temp_buf); packet->header = save; } - putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen); bool is_new = false; // true = not in contacts[], false = exists in contacts[] if (from == NULL) { @@ -157,6 +156,7 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, from->shared_secret_valid = false; } // update + putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen); StrHelper::strncpy(from->name, parser.getName(), sizeof(from->name)); from->type = parser.getType(); if (parser.hasLatLon()) { diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 42198b49..10ab8669 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -16,7 +16,7 @@ static uint32_t _atoi(const char* sp) { static bool isValidName(const char *n) { while (*n) { - if (*n == '[' || *n == ']' || *n == '/' || *n == '\\' || *n == ':' || *n == ',' || *n == '?' || *n == '*') return false; + if (*n == '[' || *n == ']' || *n == '\\' || *n == ':' || *n == ',' || *n == '?' || *n == '*') return false; n++; } return true; diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 8471d80d..a75d378c 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -284,7 +284,7 @@ bool EnvironmentSensorManager::begin() { INA260_initialized = true; } else { INA260_initialized = false; - MESH_DEBUG_PRINTLN("INA260 was not found at I2C address %02X", TELEM_INA219_ADDRESS); + MESH_DEBUG_PRINTLN("INA260 was not found at I2C address %02X", TELEM_INA260_ADDRESS); } #endif diff --git a/src/helpers/ui/ST7789Display.cpp b/src/helpers/ui/ST7789Display.cpp index 7ea35187..f7d20b8a 100644 --- a/src/helpers/ui/ST7789Display.cpp +++ b/src/helpers/ui/ST7789Display.cpp @@ -10,8 +10,13 @@ #define Y_OFFSET 1 // Vertical offset to prevent top row cutoff #endif -#define SCALE_X 1.875f // 240 / 128 -#define SCALE_Y 2.109375f // 135 / 64 +#ifdef HELTEC_VISION_MASTER_T190 + #define SCALE_X 2.5f // 320 / 128 + #define SCALE_Y 2.65625f // 170 / 64 +#else + #define SCALE_X 1.875f // 240 / 128 + #define SCALE_Y 2.109375f // 135 / 64 +#endif bool ST7789Display::begin() { if(!_isOn) { diff --git a/variants/heltec_t114/T114Board.cpp b/variants/heltec_t114/T114Board.cpp index 2a36bd90..c03d39af 100644 --- a/variants/heltec_t114/T114Board.cpp +++ b/variants/heltec_t114/T114Board.cpp @@ -34,7 +34,6 @@ void T114Board::initiateShutdown(uint8_t reason) { void T114Board::begin() { NRF52Board::begin(); - NRF_POWER->DCDCEN = 1; pinMode(PIN_VBAT_READ, INPUT); diff --git a/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp b/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp new file mode 100644 index 00000000..1719d733 --- /dev/null +++ b/variants/lilygo_tbeam_1w/TBeam1WBoard.cpp @@ -0,0 +1,71 @@ +#include "TBeam1WBoard.h" + +void TBeam1WBoard::begin() { + ESP32Board::begin(); + + // Power on radio module (must be done before radio init) + pinMode(SX126X_POWER_EN, OUTPUT); + digitalWrite(SX126X_POWER_EN, HIGH); + radio_powered = true; + delay(10); // Allow radio to power up + + // RF switch RXEN pin handled by RadioLib via setRfSwitchPins() + + // Initialize LED + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, LOW); + + // Initialize fan control (on by default - 1W PA can overheat) + pinMode(FAN_CTRL_PIN, OUTPUT); + digitalWrite(FAN_CTRL_PIN, HIGH); +} + +void TBeam1WBoard::onBeforeTransmit() { + // RF switching handled by RadioLib via SX126X_DIO2_AS_RF_SWITCH and setRfSwitchPins() + digitalWrite(LED_PIN, HIGH); // TX LED on +} + +void TBeam1WBoard::onAfterTransmit() { + digitalWrite(LED_PIN, LOW); // TX LED off +} + +uint16_t TBeam1WBoard::getBattMilliVolts() { + // T-Beam 1W uses 7.4V battery with voltage divider + // ADC reads through divider - adjust multiplier based on actual divider ratio + analogReadResolution(12); + uint32_t raw = 0; + for (int i = 0; i < 8; i++) { + raw += analogRead(BATTERY_PIN); + } + raw = raw / 8; + // Assuming voltage divider ratio from ADC_MULTIPLIER + // 3.3V reference, 12-bit ADC (4095 max) + return static_cast((raw * 3300 * ADC_MULTIPLIER) / 4095); +} + +const char* TBeam1WBoard::getManufacturerName() const { + return "LilyGo T-Beam 1W"; +} + +void TBeam1WBoard::powerOff() { + // Turn off radio LNA (CTRL pin must be LOW when not receiving) + digitalWrite(SX126X_RXEN, LOW); + + // Turn off radio power + digitalWrite(SX126X_POWER_EN, LOW); + radio_powered = false; + + // Turn off LED and fan + digitalWrite(LED_PIN, LOW); + digitalWrite(FAN_CTRL_PIN, LOW); + + ESP32Board::powerOff(); +} + +void TBeam1WBoard::setFanEnabled(bool enabled) { + digitalWrite(FAN_CTRL_PIN, enabled ? HIGH : LOW); +} + +bool TBeam1WBoard::isFanEnabled() const { + return digitalRead(FAN_CTRL_PIN) == HIGH; +} diff --git a/variants/lilygo_tbeam_1w/TBeam1WBoard.h b/variants/lilygo_tbeam_1w/TBeam1WBoard.h new file mode 100644 index 00000000..d999dfd4 --- /dev/null +++ b/variants/lilygo_tbeam_1w/TBeam1WBoard.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include "variant.h" + +// LilyGo T-Beam 1W with SX1262 + external PA (XY16P35 module) +// +// Power architecture (LDO is separate chip on T-Beam board, not inside XY16P35): +// +// VCC (+4.0~+8.0V) ──┬──────────────────► XY16P35 VCC pin 5 (PA direct) +// (USB or Battery) │ +// │ ┌───────────┐ +// └──►│ LDO Chip │──► +3.3V ──► XY16P35 (SX1262 + LNA) +// │ EN=GPIO40 │ +// └───────────┘ +// LDO_EN (GPIO 40): H @ +1.2V~VIN, active high, not floating +// +// Control signals: +// - LDO_EN (GPIO 40): HIGH enables LDO → powers SX1262 + LNA +// - TCXO_EN (DIO3): HIGH enables TCXO (set to 1.8V per Meshtastic) +// - CTL (GPIO 21): HIGH=RX (LNA on), LOW=TX (LNA off) +// - DIO2: AUTO via SX126X_DIO2_AS_RF_SWITCH (TX path) +// +// Power notes: +// - PA needs VCC 4.0-8.0V for full 32dBm output +// - USB-C (3.9-6V) marginal; 7.4V battery recommended +// - Battery must support 2A+ discharge for high-power TX + +class TBeam1WBoard : public ESP32Board { +private: + bool radio_powered = false; + +public: + void begin(); + void onBeforeTransmit() override; + void onAfterTransmit() override; + uint16_t getBattMilliVolts() override; + const char* getManufacturerName() const override; + void powerOff() override; + + // Fan control methods + void setFanEnabled(bool enabled); + bool isFanEnabled() const; +}; diff --git a/variants/lilygo_tbeam_1w/pins_arduino.h b/variants/lilygo_tbeam_1w/pins_arduino.h new file mode 100644 index 00000000..c6f596f4 --- /dev/null +++ b/variants/lilygo_tbeam_1w/pins_arduino.h @@ -0,0 +1,26 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +// Serial (USB CDC) +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +// I2C for OLED and sensors +static const uint8_t SDA = 8; +static const uint8_t SCL = 9; + +// Default SPI mapped to Radio/SD +static const uint8_t SS = 15; // LoRa CS +static const uint8_t MOSI = 11; +static const uint8_t MISO = 12; +static const uint8_t SCK = 13; + +// SD Card CS +#define SDCARD_CS 10 + +#endif /* Pins_Arduino_h */ diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini new file mode 100644 index 00000000..cf17ae8b --- /dev/null +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -0,0 +1,193 @@ +[LilyGo_TBeam_1W] +extends = esp32_base +board = t_beam_1w +build_flags = + ${esp32_base.build_flags} + -I variants/lilygo_tbeam_1w + -D TBEAM_1W + + ; Radio - SX1262 with high-power PA (32dBm max output) + ; Note: Set SX1262 output to 22dBm max, external PA provides additional gain + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D P_LORA_DIO_1=1 + -D P_LORA_NSS=15 + -D P_LORA_RESET=3 + -D P_LORA_BUSY=38 + -D P_LORA_SCLK=13 + -D P_LORA_MISO=12 + -D P_LORA_MOSI=11 + + ; RF switch configuration: + ; DIO2 controls TX path (PA enable) via SX126X_DIO2_AS_RF_SWITCH + ; GPIO21 controls RX path (LNA enable) via SX126X_RXEN + ; Truth table: DIO2=1,RXEN=0 → TX | DIO2=0,RXEN=1 → RX + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_RXEN=21 + -D SX126X_DIO3_TCXO_VOLTAGE=3.0 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + + ; TX power: 22dBm to SX1262, PA module adds ~10dB for 32dBm total + -D LORA_TX_POWER=22 + + ; Battery - 2S 7.4V LiPo (6.0V min, 8.4V max) + -D BATT_MIN_MILLIVOLTS=6000 + -D BATT_MAX_MILLIVOLTS=8400 + + ; Display - SH1106 OLED at 0x3C + -D DISPLAY_CLASS=SH1106Display + + ; I2C pins + -D PIN_BOARD_SDA=8 + -D PIN_BOARD_SCL=9 + + ; GPS - L76K module + ; GNSS_TXD (IO5) = GPS transmits → MCU RX + ; GNSS_RXD (IO6) = GPS receives → MCU TX + -D PIN_GPS_TX=5 + -D PIN_GPS_RX=6 + -D PIN_GPS_EN=16 + -D ENV_INCLUDE_GPS=1 + + ; User interface + -D PIN_USER_BTN=17 + +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/lilygo_tbeam_1w> + + + + + + + +lib_deps = + ${esp32_base.lib_deps} + adafruit/Adafruit SH110X @ ~2.1.13 + stevemarple/MicroNMEA @ ~2.0.6 + +; === LILYGO T-Beam 1W Repeater === +[env:LilyGo_TBeam_1W_repeater] +extends = LilyGo_TBeam_1W +build_flags = + ${LilyGo_TBeam_1W.build_flags} + -D ADVERT_NAME='"T-Beam 1W Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 + -D PERSISTANT_GPS=1 + -D ENV_SKIP_GPS_DETECT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_1W.build_src_filter} + +<../examples/simple_repeater> +lib_deps = + ${LilyGo_TBeam_1W.lib_deps} + ${esp32_ota.lib_deps} + +; === LILYGO T-Beam 1W Room Server === +[env:LilyGo_TBeam_1W_room_server] +extends = LilyGo_TBeam_1W +build_flags = + ${LilyGo_TBeam_1W.build_flags} + -D ADVERT_NAME='"T-Beam 1W Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' + -D PERSISTANT_GPS=1 + -D ENV_SKIP_GPS_DETECT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_1W.build_src_filter} + +<../examples/simple_room_server> +lib_deps = + ${LilyGo_TBeam_1W.lib_deps} + ${esp32_ota.lib_deps} + +; === LILYGO T-Beam 1W Companion Radio (USB) === +[env:LilyGo_TBeam_1W_companion_radio_usb] +extends = LilyGo_TBeam_1W +build_flags = + ${LilyGo_TBeam_1W.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D PERSISTANT_GPS=1 + -D ENV_SKIP_GPS_DETECT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_1W.build_src_filter} + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${LilyGo_TBeam_1W.lib_deps} + densaugeo/base64 @ ~1.4.0 + +; === LILYGO T-Beam 1W Companion Radio (BLE) === +[env:LilyGo_TBeam_1W_companion_radio_ble] +extends = LilyGo_TBeam_1W +build_flags = + ${LilyGo_TBeam_1W.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 + -D PERSISTANT_GPS=1 + -D ENV_SKIP_GPS_DETECT=1 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_1W.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${LilyGo_TBeam_1W.lib_deps} + densaugeo/base64 @ ~1.4.0 + +; === LILYGO T-Beam 1W Companion Radio (WiFi) === +[env:LilyGo_TBeam_1W_companion_radio_wifi] +extends = LilyGo_TBeam_1W +build_flags = + ${LilyGo_TBeam_1W.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D WIFI_DEBUG_LOGGING=1 + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' + -D PERSISTANT_GPS=1 + -D ENV_SKIP_GPS_DETECT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_1W.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${LilyGo_TBeam_1W.lib_deps} + densaugeo/base64 @ ~1.4.0 + +; === LILYGO T-Beam 1W Repeater with ESPNow Bridge === +[env:LilyGo_TBeam_1W_repeater_bridge_espnow] +extends = LilyGo_TBeam_1W +build_flags = + ${LilyGo_TBeam_1W.build_flags} + -D ADVERT_NAME='"T-Beam 1W ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 + -D WITH_ESPNOW_BRIDGE=1 + -D PERSISTANT_GPS=1 + -D ENV_SKIP_GPS_DETECT=1 +; -D BRIDGE_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_1W.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${LilyGo_TBeam_1W.lib_deps} + ${esp32_ota.lib_deps} diff --git a/variants/lilygo_tbeam_1w/target.cpp b/variants/lilygo_tbeam_1w/target.cpp new file mode 100644 index 00000000..fcdb42ed --- /dev/null +++ b/variants/lilygo_tbeam_1w/target.cpp @@ -0,0 +1,64 @@ +#include +#include "target.h" + +TBeam1WBoard board; + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); +#endif + +static SPIClass spi; + +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + + // Initialize SPI for radio + spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + + // GPS serial initialized by EnvironmentSensorManager::begin() + + bool success = radio.std_init(&spi); + if (success) { + // T-Beam 1W has external PA requiring longer ramp time (>800us recommended) + // RADIOLIB_SX126X_PA_RAMP_800U = 0x05 + radio.setTxParams(LORA_TX_POWER, RADIOLIB_SX126X_PA_RAMP_800U); + } + return success; +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); +} diff --git a/variants/lilygo_tbeam_1w/target.h b/variants/lilygo_tbeam_1w/target.h new file mode 100644 index 00000000..2c3e8970 --- /dev/null +++ b/variants/lilygo_tbeam_1w/target.h @@ -0,0 +1,27 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include "TBeam1WBoard.h" + +#ifdef DISPLAY_CLASS + #include + #include + extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; +#endif + +extern TBeam1WBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +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(); diff --git a/variants/lilygo_tbeam_1w/variant.h b/variants/lilygo_tbeam_1w/variant.h new file mode 100644 index 00000000..f6807e56 --- /dev/null +++ b/variants/lilygo_tbeam_1w/variant.h @@ -0,0 +1,96 @@ +// LilyGo T-Beam-1W variant.h +// Configuration based on Meshtastic PR #8967 and LilyGO documentation + +#pragma once + +// I2C for OLED display (SH1106 at 0x3C) +#define I2C_SDA 8 +#define I2C_SCL 9 + +// GPS - Quectel L76K +// GNSS_TXD (IO5) = GPS transmits → MCU RX (setPins rxPin) +// GNSS_RXD (IO6) = GPS receives → MCU TX (setPins txPin) +#define PIN_GPS_TX 5 // MCU receives from GPS TX +#define PIN_GPS_RX 6 // MCU transmits to GPS RX +#define PIN_GPS_PPS 7 // GPS PPS output +#define PIN_GPS_EN 16 // GPS wake-up/enable (GPS_EN_PIN in LilyGO code) +#define HAS_GPS 1 +#define GPS_BAUDRATE 9600 + +// Buttons +#define BUTTON_PIN 0 // BUTTON 1 (boot) +#define BUTTON_PIN_ALT 17 // BUTTON 2 + +// SPI (shared by LoRa and SD) +#define SPI_MOSI 11 +#define SPI_SCK 13 +#define SPI_MISO 12 +#define SPI_CS 10 + +// SD Card +#define HAS_SDCARD +#define SDCARD_USE_SPI1 +#define SDCARD_CS SPI_CS + +// LoRa Radio - SX1262 with 1W PA +#define USE_SX1262 + +#define LORA_SCK SPI_SCK +#define LORA_MISO SPI_MISO +#define LORA_MOSI SPI_MOSI +#define LORA_CS 15 +#define LORA_RESET 3 +#define LORA_DIO1 1 +#define LORA_BUSY 38 + +// CRITICAL: Radio power enable - MUST be HIGH before lora.begin()! +// GPIO 40 powers the SX1262 + PA module via LDO +#define SX126X_POWER_EN 40 + +#ifdef USE_SX1262 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_BUSY +#define SX126X_RESET LORA_RESET + +// RF switching configuration for 1W PA module +// DIO2 controls PA (via SX126X_DIO2_AS_RF_SWITCH) +// CTRL PIN (GPIO 21) controls LNA - must be HIGH during RX +// Truth table: DIO2=1,CTRL=0 -> TX (PA on, LNA off) +// DIO2=0,CTRL=1 -> RX (PA off, LNA on) +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_RXEN 21 // LNA enable - HIGH during RX + +// TCXO voltage - required for radio init +#define SX126X_DIO3_TCXO_VOLTAGE 3.0 + +#define SX126X_MAX_POWER 22 +#endif + +// LED +#define LED_PIN 18 +#define LED_STATE_ON 1 // HIGH = ON + +// Battery ADC +#define BATTERY_PIN 4 +#define ADC_CHANNEL ADC1_GPIO4_CHANNEL +#define BATTERY_SENSE_SAMPLES 30 +#define ADC_MULTIPLIER 3.0 + +// NTC temperature sensor +#define NTC_PIN 14 + +// Fan control +#define FAN_CTRL_PIN 41 + +// PA Ramp Time - T-Beam 1W requires >800us stabilization (default is 200us) +// Value 0x05 = RADIOLIB_SX126X_PA_RAMP_800U +#define SX126X_PA_RAMP_US 0x05 + +// Display - SH1106 OLED (128x64) +#define USE_SH1106 +#define OLED_WIDTH 128 +#define OLED_HEIGHT 64 + +// 32768 Hz crystal present +#define HAS_32768HZ 1 diff --git a/variants/rak3401/RAK3401Board.h b/variants/rak3401/RAK3401Board.h index 609393c3..20edf906 100644 --- a/variants/rak3401/RAK3401Board.h +++ b/variants/rak3401/RAK3401Board.h @@ -4,30 +4,6 @@ #include #include -// LoRa radio module pins for RAK13302 -#define P_LORA_SCLK 3 -#define P_LORA_MISO 29 -#define P_LORA_MOSI 30 -#define P_LORA_NSS 26 -#define P_LORA_DIO_1 10 -#define P_LORA_BUSY 9 -#define P_LORA_RESET 4 -#ifndef P_LORA_PA_EN - #define P_LORA_PA_EN 31 -#endif - -//#define PIN_GPS_SDA 13 //GPS SDA pin (output option) -//#define PIN_GPS_SCL 14 //GPS SCL pin (output option) -// #define PIN_GPS_TX 16 //GPS TX pin -// #define PIN_GPS_RX 15 //GPS RX pin -#define PIN_GPS_1PPS 17 //GPS PPS pin -#define GPS_BAUD_RATE 9600 -#define GPS_ADDRESS 0x42 //i2c address for GPS - -#define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - - // built-ins #define PIN_VBAT_READ 5 #define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000) @@ -35,9 +11,13 @@ #define PIN_3V3_EN (34) #define WB_IO2 PIN_3V3_EN -class RAK3401Board : public NRF52BoardDCDC, public NRF52BoardOTA { +class RAK3401Board : public NRF52BoardDCDC { +protected: +#ifdef NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; +#endif public: - RAK3401Board() : NRF52BoardOTA("RAK3401_OTA") {} + RAK3401Board() : NRF52Board("RAK3401_OTA") {} void begin(); #define BATTERY_SAMPLES 8 diff --git a/variants/rak3401/variant.h b/variants/rak3401/variant.h index 9c182247..56fe0816 100644 --- a/variants/rak3401/variant.h +++ b/variants/rak3401/variant.h @@ -141,11 +141,6 @@ static const uint8_t AREF = PIN_AREF; #define EXTERNAL_FLASH_DEVICES IS25LP080D #define EXTERNAL_FLASH_USE_QSPI -#define P_LORA_SCK PIN_SPI1_SCK -#define P_LORA_MISO PIN_SPI1_MISO -#define P_LORA_MOSI PIN_SPI1_MOSI -#define P_LORA_CS 26 - #define USE_SX1262 #define SX126X_CS (26) #define SX126X_DIO1 (10) @@ -157,6 +152,15 @@ static const uint8_t AREF = PIN_AREF; #define SX126X_DIO2_AS_RF_SWITCH #define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#define P_LORA_SCLK PIN_SPI1_SCK +#define P_LORA_MISO PIN_SPI1_MISO +#define P_LORA_MOSI PIN_SPI1_MOSI +#define P_LORA_NSS SX126X_CS +#define P_LORA_DIO_1 SX126X_DIO1 +#define P_LORA_BUSY SX126X_BUSY +#define P_LORA_RESET SX126X_RESET +#define P_LORA_PA_EN 31 + // enables 3.3V periphery like GPS or IO Module // Do not toggle this for GPS power savings #define PIN_3V3_EN (34) @@ -173,6 +177,10 @@ static const uint8_t AREF = PIN_AREF; #define PIN_GPS_RX PIN_SERIAL1_RX #define PIN_GPS_TX PIN_SERIAL1_TX +#define PIN_GPS_1PPS PIN_GPS_PPS +#define GPS_BAUD_RATE 9600 +#define GPS_ADDRESS 0x42 //i2c address for GPS + // Battery // The battery sense is hooked to pin A0 (5) #define BATTERY_PIN PIN_A0 diff --git a/variants/rak4631/RAK4631Board.h b/variants/rak4631/RAK4631Board.h index ff4a5b7d..7e67165b 100644 --- a/variants/rak4631/RAK4631Board.h +++ b/variants/rak4631/RAK4631Board.h @@ -4,27 +4,6 @@ #include #include -// LoRa radio module pins for RAK4631 -#define P_LORA_DIO_1 47 -#define P_LORA_NSS 42 -#define P_LORA_RESET RADIOLIB_NC // 38 -#define P_LORA_BUSY 46 -#define P_LORA_SCLK 43 -#define P_LORA_MISO 45 -#define P_LORA_MOSI 44 -#define SX126X_POWER_EN 37 - -//#define PIN_GPS_SDA 13 //GPS SDA pin (output option) -//#define PIN_GPS_SCL 14 //GPS SCL pin (output option) -//#define PIN_GPS_TX 16 //GPS TX pin -//#define PIN_GPS_RX 15 //GPS RX pin -#define PIN_GPS_1PPS 17 //GPS PPS pin -#define GPS_BAUD_RATE 9600 -#define GPS_ADDRESS 0x42 //i2c address for GPS - -#define SX126X_DIO2_AS_RF_SWITCH true -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - // built-ins #define PIN_VBAT_READ 5 #define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000) diff --git a/variants/rak4631/variant.h b/variants/rak4631/variant.h index b18335f8..142d93e9 100644 --- a/variants/rak4631/variant.h +++ b/variants/rak4631/variant.h @@ -144,6 +144,19 @@ extern "C" static const uint8_t MISO = PIN_SPI_MISO; static const uint8_t SCK = PIN_SPI_SCK; +// LoRa radio module pins for RAK4631 +#define P_LORA_DIO_1 (47) +#define P_LORA_NSS (42) +#define P_LORA_RESET (-1) +#define P_LORA_BUSY (46) +#define P_LORA_SCLK (43) +#define P_LORA_MISO (45) +#define P_LORA_MOSI (44) +#define SX126X_POWER_EN (37) + +#define SX126X_DIO2_AS_RF_SWITCH true +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + /* * Wire Interfaces */ @@ -155,19 +168,23 @@ extern "C" #define PIN_WIRE1_SDA (24) #define PIN_WIRE1_SCL (25) - // QSPI Pins - // QSPI occupied by GPIO's - #define PIN_QSPI_SCK 3 // 19 - #define PIN_QSPI_CS 26 // 17 - #define PIN_QSPI_IO0 30 // 20 - #define PIN_QSPI_IO1 29 // 21 - #define PIN_QSPI_IO2 28 // 22 - #define PIN_QSPI_IO3 2 // 23 +// QSPI Pins +// QSPI occupied by GPIO's +#define PIN_QSPI_SCK 3 // 19 +#define PIN_QSPI_CS 26 // 17 +#define PIN_QSPI_IO0 30 // 20 +#define PIN_QSPI_IO1 29 // 21 +#define PIN_QSPI_IO2 28 // 22 +#define PIN_QSPI_IO3 2 // 23 - // On-board QSPI Flash - // No onboard flash - #define EXTERNAL_FLASH_DEVICES IS25LP080D - #define EXTERNAL_FLASH_USE_QSPI +// On-board QSPI Flash +// No onboard flash +#define EXTERNAL_FLASH_DEVICES IS25LP080D +#define EXTERNAL_FLASH_USE_QSPI + +#define PIN_GPS_1PPS 17 //GPS PPS pin +#define GPS_BAUD_RATE 9600 +#define GPS_ADDRESS 0x42 //i2c address for GPS #ifdef __cplusplus } diff --git a/variants/thinknode_m3/ThinkNodeM3Board.cpp b/variants/thinknode_m3/ThinkNodeM3Board.cpp new file mode 100644 index 00000000..ac513ade --- /dev/null +++ b/variants/thinknode_m3/ThinkNodeM3Board.cpp @@ -0,0 +1,28 @@ +#include +#include "ThinkNodeM3Board.h" +#include + +#include + +void ThinkNodeM3Board::begin() { + NRF52Board::begin(); + btn_prev_state = HIGH; + + Wire.begin(); + + delay(10); // give sx1262 some time to power up +} + +uint16_t ThinkNodeM3Board::getBattMilliVolts() { + int adcvalue = 0; + + analogReference(AR_INTERNAL_2_4); + analogReadResolution(ADC_RESOLUTION); + delay(10); + + // ADC range is 0..2400mV and resolution is 12-bit (0..4095) + adcvalue = analogRead(PIN_VBAT_READ); + // Convert the raw value to compensated mv, taking the resistor- + // divider into account (providing the actual LIPO voltage) + return (uint16_t)((float)adcvalue * ADC_FACTOR); +} diff --git a/variants/thinknode_m3/ThinkNodeM3Board.h b/variants/thinknode_m3/ThinkNodeM3Board.h new file mode 100644 index 00000000..1435d31d --- /dev/null +++ b/variants/thinknode_m3/ThinkNodeM3Board.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include + +#define ADC_FACTOR ((1000.0*ADC_MULTIPLIER*AREF_VOLTAGE)/ADC_MAX) + +class ThinkNodeM3Board : public NRF52BoardDCDC { +protected: +#if NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; +#endif + uint8_t btn_prev_state; + +public: + ThinkNodeM3Board() : NRF52Board("THINKNODE_M3_OTA") {} + void begin(); + uint16_t getBattMilliVolts() override; + +#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 M3"; + } + + int buttonStateChanged() { + #ifdef BUTTON_PIN + uint8_t v = digitalRead(BUTTON_PIN); + if (v != btn_prev_state) { + btn_prev_state = v; + return (v == LOW) ? 1 : -1; + } + #endif + return 0; + } + + void powerOff() override { + // turn off all leds, sd_power_system_off will not do this for us + #ifdef P_LORA_TX_LED + digitalWrite(P_LORA_TX_LED, LOW); + #endif + + // power off board + sd_power_system_off(); + } +}; diff --git a/variants/thinknode_m3/ThinknodeM3Board.cpp b/variants/thinknode_m3/ThinknodeM3Board.cpp deleted file mode 100644 index d7ecd62d..00000000 --- a/variants/thinknode_m3/ThinknodeM3Board.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include "ThinknodeM3Board.h" -#include - -#include - -void ThinknodeM3Board::begin() { - Nrf52BoardDCDC::begin(); - btn_prev_state = HIGH; - - Wire.begin(); - - delay(10); // give sx1262 some time to power up -} \ No newline at end of file diff --git a/variants/thinknode_m3/ThinknodeM3Board.h b/variants/thinknode_m3/ThinknodeM3Board.h deleted file mode 100644 index 62694087..00000000 --- a/variants/thinknode_m3/ThinknodeM3Board.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include -#include -#include - -#define ADC_FACTOR ((1000.0*ADC_MULTIPLIER*AREF_VOLTAGE)/ADC_MAX) - -class ThinknodeM3Board : public Nrf52BoardDCDC { -protected: - uint8_t btn_prev_state; - -public: - void begin(); - - uint16_t getBattMilliVolts() override { - int adcvalue = 0; - - analogReference(AR_INTERNAL_2_4); - analogReadResolution(ADC_RESOLUTION); - delay(10); - - // ADC range is 0..2400mV and resolution is 12-bit (0..4095) - adcvalue = analogRead(PIN_VBAT_READ); - // Convert the raw value to compensated mv, taking the resistor- - // divider into account (providing the actual LIPO voltage) - return (uint16_t)((float)adcvalue * ADC_FACTOR); - } - -#if defined(P_LORA_TX_LED) -#if !defined(P_LORA_TX_LED_ON) -#define P_LORA_TX_LED_ON HIGH -#endif - void onBeforeTransmit() override { - digitalWrite(P_LORA_TX_LED, P_LORA_TX_LED_ON); // turn TX LED on - } - void onAfterTransmit() override { - digitalWrite(P_LORA_TX_LED, !P_LORA_TX_LED_ON); // turn TX LED off - } - #endif - - const char* getManufacturerName() const override { - return "Elecrow ThinkNode M3"; - } - - int buttonStateChanged() { - #ifdef BUTTON_PIN - uint8_t v = digitalRead(BUTTON_PIN); - if (v != btn_prev_state) { - btn_prev_state = v; - return (v == LOW) ? 1 : -1; - } - #endif - return 0; - } - - void powerOff() override { sd_power_system_off(); } -}; \ No newline at end of file diff --git a/variants/thinknode_m3/target.cpp b/variants/thinknode_m3/target.cpp index c6708e4d..91d186dc 100644 --- a/variants/thinknode_m3/target.cpp +++ b/variants/thinknode_m3/target.cpp @@ -2,7 +2,7 @@ #include "target.h" #include -ThinknodeM3Board board; +ThinkNodeM3Board board; RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); @@ -30,26 +30,26 @@ static const uint32_t rfswitch_dios[Module::RFSWITCH_MAX_PINS] = { RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, - RADIOLIB_NC, + RADIOLIB_NC, RADIOLIB_NC }; static const Module::RfSwitchMode_t rfswitch_table[] = { - // mode DIO5 DIO6 - { LR11x0::MODE_STBY, {LOW , LOW }}, + // mode DIO5 DIO6 + { LR11x0::MODE_STBY, {LOW , LOW }}, { LR11x0::MODE_RX, {HIGH, LOW }}, { LR11x0::MODE_TX, {HIGH, HIGH }}, { LR11x0::MODE_TX_HP, {LOW , HIGH }}, - { LR11x0::MODE_TX_HF, {LOW , LOW }}, + { LR11x0::MODE_TX_HF, {LOW , LOW }}, { LR11x0::MODE_GNSS, {LOW , LOW }}, - { LR11x0::MODE_WIFI, {LOW , LOW }}, + { LR11x0::MODE_WIFI, {LOW , LOW }}, END_OF_MODE_TABLE, }; #endif bool radio_init() { rtc_clock.begin(Wire); - + #ifdef LR11X0_DIO3_TCXO_VOLTAGE float tcxo = LR11X0_DIO3_TCXO_VOLTAGE; #else @@ -64,7 +64,7 @@ bool radio_init() { Serial.println(status); return false; // fail } - + radio.setCRC(2); radio.explicitHeader(); diff --git a/variants/thinknode_m3/target.h b/variants/thinknode_m3/target.h index f60a85b0..23e99581 100644 --- a/variants/thinknode_m3/target.h +++ b/variants/thinknode_m3/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include "ThinknodeM3Board.h" +#include "ThinkNodeM3Board.h" #include #include #include @@ -17,7 +17,7 @@ extern NullDisplayDriver display; #endif -extern ThinknodeM3Board board; +extern ThinkNodeM3Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/thinknode_m6/ThinkNodeM6Board.h b/variants/thinknode_m6/ThinkNodeM6Board.h index c03e1fbc..32baa2a0 100644 --- a/variants/thinknode_m6/ThinkNodeM6Board.h +++ b/variants/thinknode_m6/ThinkNodeM6Board.h @@ -12,9 +12,14 @@ #define PIN_VBAT_READ BATTERY_PIN #define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB) -class ThinkNodeM6Board : public Nrf52BoardOTA { +class ThinkNodeM6Board : public NRF52BoardDCDC { +protected: +#if NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; +#endif + public: - ThinkNodeM6Board() : NRF52BoardOTA("THINKNODE_M1_OTA") {} + ThinkNodeM6Board() : NRF52Board("THINKNODE_M6_OTA") {} void begin(); uint16_t getBattMilliVolts() override; @@ -25,10 +30,10 @@ public: void onAfterTransmit() override { digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } - #endif +#endif const char* getManufacturerName() const override { - return "Elecrow ThinkNode-M6"; + return "Elecrow ThinkNode M6"; } void powerOff() override {