diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a806d902f1..3a1c2f0548 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ "name": "MeshCore", "image": "mcr.microsoft.com/devcontainers/python:3-bookworm", "features": { - "ghcr.io/devcontainers-extra/features/bun:1": {}, + "ghcr.io/devcontainers/features/node:1": {}, "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": { "packages": [ "sudo" diff --git a/.gitignore b/.gitignore index 50631d890c..a0ad5f6ea9 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ cmake-* compile_commands.json .venv/ venv/ +platformio.local.ini diff --git a/boards/heltec_t096.json b/boards/heltec_t096.json new file mode 100644 index 0000000000..9d4f3037e7 --- /dev/null +++ b/boards/heltec_t096.json @@ -0,0 +1,61 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + ["0x239A","0x8029"], + ["0x239A","0x0029"], + ["0x239A","0x002A"], + ["0x239A","0x802A"] + ], + "usb_product": "HT-n5262G", + "mcu": "nrf52840", + "variant": "Heltec_T096_Board", + "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", + "openocd_target": "nrf52.cfg" + }, + "frameworks": [ + "arduino" + ], + "name": "Heltec T096 Board", + "upload": { + "maximum_ram_size": 235520, + "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://heltec.org/", + "vendor": "Heltec" +} \ No newline at end of file diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 8ae95443ad..aac545383a 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -106,6 +106,13 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +### Discover zero hop neighbors + +**Usage:** +- `discover.neighbors` + +--- + ## Statistics ### Clear Stats @@ -238,6 +245,22 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Note:** Requires reboot to apply **Serial Only:** `set freq ` +--- + +#### View or change this node's rx boosted gain mode (SX12xx only, v1.14.1+) +**Usage:** +- `get radio.rxgain` +- `set radio.rxgain ` + +**Parameters:** + - `state`: `on`|`off` + +**Default:** `on` + +**Temporary Note:** If you upgraded from an older version to 1.14.1 without erasing flash, this setting is `off` because of [#2118](https://github.com/meshcore-dev/MeshCore/issues/2118) + +--- + ### System #### View or change this node's name @@ -477,7 +500,29 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change the duty cycle limit +**Usage:** +- `get dutycycle` +- `set dutycycle ` + +**Parameters:** +- `value`: Duty cycle percentage (1-100) + +**Default:** `50%` (equivalent to airtime factor 1.0) + +**Examples:** +- `set dutycycle 100` — no duty cycle limit +- `set dutycycle 50` — 50% duty cycle (default) +- `set dutycycle 10` — 10% duty cycle +- `set dutycycle 1` — 1% duty cycle (strictest EU requirement) + +> **Note:** Added in firmware v1.15.0 + +--- + #### View or change the airtime factor (duty cycle limit) +> **Deprecated** as of firmware v1.15.0. Use [`get/set dutycycle`](#view-or-change-the-duty-cycle-limit) instead. + **Usage:** - `get af` - `set af ` @@ -487,8 +532,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `af = 1` → ~50% duty - `af = 2` → ~33% duty - `af = 3` → ~25% duty - - `af = 9` → ~10% duty - Yyou are responsible for choosing a value that is appropriate for your jurisdiction and channel plan (for example EU 868 Mhz 10% duty cycle regulation). + - `af = 9` → ~10% duty + You are responsible for choosing a value that is appropriate for your jurisdiction and channel plan (for example EU 868 Mhz 10% duty cycle regulation). **Default:** `1.0` @@ -512,7 +557,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `set agc.reset.interval ` **Parameters:** -- `value`: Interval in seconds rounded down to a multiple of 4 (17 becomes 16) +- `value`: Interval in seconds rounded down to a multiple of 4 (17 becomes 16). 0 to disable. **Default:** `0.0` @@ -811,7 +856,9 @@ region save **Default:** `off` -**Note:** Output format: `{status}, {fix}, {sat count}` (when enabled) +**Note:** Output format: +- `off` when the GPS hardware is disabled +- `on, {active|deactivated}, {fix|no fix}, {sat count} sats` when the GPS hardware is enabled --- diff --git a/docs/companion_protocol.md b/docs/companion_protocol.md index 11ba0ab24c..bbad1e40f0 100644 --- a/docs/companion_protocol.md +++ b/docs/companion_protocol.md @@ -281,6 +281,33 @@ Bytes 7+: Message Text (UTF-8, variable length) --- +### 6. Send Channel Data Datagram + +**Purpose**: Send binary datagram data to a channel. + +**Command Format**: +``` +Byte 0: 0x3E +Bytes 1-2: Data Type (`data_type`, 16-bit little-endian) +Byte 3: Channel Index (0-7) +Bytes 4+: Binary payload bytes (variable length) +``` + +**Data Type / Transport Mapping**: +- `0x0000` is invalid for this command. +- `0xFFFF` (`DATA_TYPE_DEV`) is the developer namespace for experimenting and developing apps. +- Other non-zero values can be used as assigned application/community namespaces. + +**Note**: Applications that need a timestamp should encode it inside the binary payload. + +**Limits**: +- Maximum payload length is `163` bytes. +- Larger payloads are rejected with `PACKET_ERROR`. + +**Response**: `PACKET_OK` (0x00) on success + +--- + ### 6. Get Message **Purpose**: Request the next queued message from the device. diff --git a/docs/faq.md b/docs/faq.md index 530f97013b..560e3f629b 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -386,7 +386,7 @@ https://github.com/meshcore-dev/MeshCore/blob/main/src/Packet.h#L19 #define PAYLOAD_TYPE_TXT_MSG 0x02 // a plain text message (prefixed with dest/src hashes, MAC) (enc data: timestamp, text) #define PAYLOAD_TYPE_ACK 0x03 // a simple ack #define PAYLOAD_TYPE_ADVERT 0x04 // a node advertising its Identity #define PAYLOAD_TYPE_GRP_TXT 0x05 // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg") - #define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, blob) + #define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: data_type, data_len, blob) #define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...) #define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra) diff --git a/docs/nrf52_power_management.md b/docs/nrf52_power_management.md index 9c7416b3d6..58818edd50 100644 --- a/docs/nrf52_power_management.md +++ b/docs/nrf52_power_management.md @@ -33,11 +33,13 @@ Shutdown reason codes (stored in GPREGRET2): ## Supported Boards + | Board | Implemented | LPCOMP wake | VBUS wake | |-------|-------------|-------------|-----------| | Seeed Studio XIAO nRF52840 (`xiao_nrf52`) | Yes | Yes | Yes | | RAK4631 (`rak4631`) | Yes | Yes | Yes | | Heltec T114 (`heltec_t114`) | Yes | Yes | Yes | +| GAT562 Mesh Watch13 | Yes | Yes | Yes | | Promicro nRF52840 | No | No | No | | RAK WisMesh Tag | No | No | No | | Heltec Mesh Solar | No | No | No | diff --git a/docs/number_allocations.md b/docs/number_allocations.md new file mode 100644 index 0000000000..94ad1efda1 --- /dev/null +++ b/docs/number_allocations.md @@ -0,0 +1,20 @@ +# Number Allocations + +This document lists unique numbers/identifiers used in various MeshCore protcol payloads. + +# Group Data Types + +The `PAYLOAD_TYPE_GRP_DATA` payloads have a 16-bit data-type field, which identifies which application the packet is for. + +To make sure multiple applications can function without interfering with each other, the table below is for reserving various ranges of data-type values. Just modify this table, adding a row, then submit a PR to have it authorised/merged. + +NOTE: the range FF00 - FFFF is for use while you're developing, doing POC, and for these you don't need to request to use/allocate. + +Once you have a working app/project, you need to be able to demonstrate it exists/works, and THEN request type IDs. So, just use the testing/dev range while developing, then request IDs before you transition to publishing your project. + +| Data-Type range | App name | Contact | +|-----------------|-----------------------------|------------------------------------------------------| +| 0000 - 00FF | -reserved for internal use- | | +| FF00 - FFFF | -reserved for testing/dev- | | + +(add rows, inside the range 0100 - FEFF for custom apps) diff --git a/docs/payloads.md b/docs/payloads.md index 15fec7578a..7745ac61ba 100644 --- a/docs/payloads.md +++ b/docs/payloads.md @@ -226,7 +226,7 @@ txt_type | reply path | (variable) | reply path | -# Group text message / datagram +# Group text message | Field | Size (bytes) | Description | |--------------|-----------------|--------------------------------------------| @@ -236,6 +236,22 @@ txt_type The plaintext contained in the ciphertext matches the format described in [plain text message](#plain-text-message). Specifically, it consists of a four byte timestamp, a flags byte, and the message. The flags byte will generally be `0x00` because it is a "plain text message". The message will be of the form `: ` (eg., `user123: I'm on my way`). +# Group datagram + +| Field | Size (bytes) | Description | +|--------------|-----------------|--------------------------------------------| +| channel hash | 1 | first byte of SHA256 of channel's shared key | +| cipher MAC | 2 | MAC for encrypted data in next field | +| ciphertext | rest of payload | encrypted data, see below for details | + +The data contained in the ciphertext uses the format below: + +| Field | Size (bytes) | Description | +|--------------|-----------------|--------------------------------------------| +| data type | 2 | Identifier for type of data. (See number_allocations.md) | +| data len | 1 | byte length of data | +| data | rest of payload | (depends on data type) | + # Control data diff --git a/docs/terminal_chat_cli.md b/docs/terminal_chat_cli.md index f053e64d81..c889da9672 100644 --- a/docs/terminal_chat_cli.md +++ b/docs/terminal_chat_cli.md @@ -27,10 +27,15 @@ set lon {longitude} ``` Sets your advertisement map longitude. (decimal degrees) +``` +set dutycycle {percent} +``` +Sets the transmit duty cycle limit (1-100%). Example: `set dutycycle 10` for 10%. + ``` set af {air-time-factor} ``` -Sets the transmit air-time-factor. +Sets the transmit air-time-factor. Deprecated — use `set dutycycle` instead. ``` diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 32e362607f..60a5a75fec 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -58,6 +58,7 @@ #define CMD_GET_AUTOADD_CONFIG 59 #define CMD_GET_ALLOWED_REPEAT_FREQ 60 #define CMD_SET_PATH_HASH_MODE 61 +#define CMD_SEND_CHANNEL_DATA 62 // Stats sub-types for CMD_GET_STATS #define STATS_TYPE_CORE 0 @@ -91,6 +92,9 @@ #define RESP_CODE_STATS 24 // v8+, second byte is stats type #define RESP_CODE_AUTOADD_CONFIG 25 #define RESP_ALLOWED_REPEAT_FREQ 26 +#define RESP_CODE_CHANNEL_DATA_RECV 27 + +#define MAX_CHANNEL_DATA_LENGTH (MAX_FRAME_SIZE - 9) #define SEND_TIMEOUT_BASE_MILLIS 500 #define FLOOD_SEND_TIMEOUT_FACTOR 16.0f @@ -204,7 +208,8 @@ void MyMesh::updateContactFromFrame(ContactInfo &contact, uint32_t& last_mod, co } bool MyMesh::Frame::isChannelMsg() const { - return buf[0] == RESP_CODE_CHANNEL_MSG_RECV || buf[0] == RESP_CODE_CHANNEL_MSG_RECV_V3; + return buf[0] == RESP_CODE_CHANNEL_MSG_RECV || buf[0] == RESP_CODE_CHANNEL_MSG_RECV_V3 || + buf[0] == RESP_CODE_CHANNEL_DATA_RECV; } void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) { @@ -292,7 +297,7 @@ bool MyMesh::shouldAutoAddContactType(uint8_t contact_type) const { if ((_prefs.manual_add_contacts & 1) == 0) { return true; } - + uint8_t type_bit = 0; switch (contact_type) { case ADV_TYPE_CHAT: @@ -310,7 +315,7 @@ bool MyMesh::shouldAutoAddContactType(uint8_t contact_type) const { default: return false; // Unknown type, don't auto-add } - + return (_prefs.autoadd_config & type_bit) != 0; } @@ -564,6 +569,41 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe #endif } +void MyMesh::onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint16_t data_type, + const uint8_t *data, size_t data_len) { + if (data_len > MAX_CHANNEL_DATA_LENGTH) { + MESH_DEBUG_PRINTLN("onChannelDataRecv: dropping payload_len=%d exceeds frame limit=%d", + (uint32_t)data_len, (uint32_t)MAX_CHANNEL_DATA_LENGTH); + return; + } + + int i = 0; + out_frame[i++] = RESP_CODE_CHANNEL_DATA_RECV; + out_frame[i++] = (int8_t)(pkt->getSNR() * 4); + out_frame[i++] = 0; // reserved1 + out_frame[i++] = 0; // reserved2 + + uint8_t channel_idx = findChannelIdx(channel); + out_frame[i++] = channel_idx; + out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF; + out_frame[i++] = (uint8_t)(data_type & 0xFF); + out_frame[i++] = (uint8_t)(data_type >> 8); + out_frame[i++] = (uint8_t)data_len; + + int copy_len = (int)data_len; + if (copy_len > 0) { + memcpy(&out_frame[i], data, copy_len); + i += copy_len; + } + addToOfflineQueue(out_frame, i); + + if (_serial->isConnected()) { + uint8_t frame[1]; + frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' + _serial->writeFrame(frame, 1); + } +} + uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, uint8_t len, uint8_t *reply) { if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) { @@ -859,7 +899,7 @@ void MyMesh::begin(bool has_display) { // sanitise bad pref values _prefs.rx_delay_base = constrain(_prefs.rx_delay_base, 0, 20.0f); _prefs.airtime_factor = constrain(_prefs.airtime_factor, 0, 9.0f); - _prefs.freq = constrain(_prefs.freq, 400.0f, 2500.0f); + _prefs.freq = constrain(_prefs.freq, 150.0f, 2500.0f); _prefs.bw = constrain(_prefs.bw, 7.8f, 500.0f); _prefs.sf = constrain(_prefs.sf, 5, 12); _prefs.cr = constrain(_prefs.cr, 5, 8); @@ -1041,7 +1081,7 @@ void MyMesh::handleCmdFrame(size_t len) { ? ERR_CODE_NOT_FOUND : ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsuported TXT_TYPE_* } - } else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel msg + } else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel text msg int i = 1; uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN uint8_t channel_idx = cmd_frame[i++]; @@ -1061,6 +1101,46 @@ void MyMesh::handleCmdFrame(size_t len) { writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx } } + } else if (cmd_frame[0] == CMD_SEND_CHANNEL_DATA) { // send GroupChannel datagram + if (len < 4) { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + return; + } + int i = 1; + uint8_t channel_idx = cmd_frame[i++]; + uint8_t path_len = cmd_frame[i++]; + + // validate path len, allowing 0xFF for flood + if (!mesh::Packet::isValidPathLen(path_len) && path_len != OUT_PATH_UNKNOWN) { + MESH_DEBUG_PRINTLN("CMD_SEND_CHANNEL_DATA invalid path size: %d", path_len); + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + return; + } + + // parse provided path if not flood + uint8_t path[MAX_PATH_SIZE]; + if (path_len != OUT_PATH_UNKNOWN) { + i += mesh::Packet::writePath(path, &cmd_frame[i], path_len); + } + + uint16_t data_type = ((uint16_t)cmd_frame[i]) | (((uint16_t)cmd_frame[i + 1]) << 8); + i += 2; + const uint8_t *payload = &cmd_frame[i]; + int payload_len = (len > (size_t)i) ? (int)(len - i) : 0; + + ChannelDetails channel; + if (!getChannel(channel_idx, channel)) { + writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx + } else if (data_type == DATA_TYPE_RESERVED) { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } else if (payload_len > MAX_CHANNEL_DATA_LENGTH) { + MESH_DEBUG_PRINTLN("CMD_SEND_CHANNEL_DATA payload too long: %d > %d", payload_len, MAX_CHANNEL_DATA_LENGTH); + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } else if (sendGroupData(channel.channel, path, path_len, data_type, payload, payload_len)) { + writeOKFrame(); + } else { + writeErrFrame(ERR_CODE_TABLE_FULL); + } } else if (cmd_frame[0] == CMD_GET_CONTACTS) { // get Contact list if (_iter_started) { writeErrFrame(ERR_CODE_BAD_STATE); // iterator is currently busy @@ -1264,7 +1344,7 @@ void MyMesh::handleCmdFrame(size_t len) { if (repeat && !isValidClientRepeatFreq(freq)) { writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } else if (freq >= 300000 && freq <= 2500000 && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7000 && + } else if (freq >= 150000 && freq <= 2500000 && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7000 && bw <= 500000) { _prefs.sf = sf; _prefs.cr = cr; @@ -1620,7 +1700,7 @@ void MyMesh::handleCmdFrame(size_t len) { } else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PACKET_PAYLOAD-5) { uint8_t path_len = len - 10; uint8_t flags = cmd_frame[9]; - uint8_t path_sz = flags & 0x03; // NEW v1.11+ + uint8_t path_sz = flags & 0x03; // NEW v1.11+ if ((path_len >> path_sz) > MAX_PATH_SIZE || (path_len % (1 << path_sz)) != 0) { // make sure is multiple of path_sz writeErrFrame(ERR_CODE_ILLEGAL_ARG); } else { @@ -1927,7 +2007,7 @@ void MyMesh::checkCLIRescueCmd() { // get path from command e.g: "cat /contacts3" const char *path = &cli_command[4]; - + bool is_fs2 = false; if (memcmp(path, "UserData/", 9) == 0) { path += 8; // skip "UserData" diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 4271729b0c..3b02f5f69d 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -137,6 +137,8 @@ class MyMesh : public BaseChatMesh, public DataStoreHost { const uint8_t *sender_prefix, const char *text) override; void onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, const char *text) override; + void onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint16_t data_type, + const uint8_t *data, size_t data_len) override; uint8_t onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, uint8_t len, uint8_t *reply) override; @@ -163,6 +165,17 @@ class MyMesh : public BaseChatMesh, public DataStoreHost { public: void savePrefs() { _store->savePrefs(_prefs, sensors.node_lat, sensors.node_lon); } +#if ENV_INCLUDE_GPS == 1 + void applyGpsPrefs() { + sensors.setSettingValue("gps", _prefs.gps_enabled ? "1" : "0"); + if (_prefs.gps_interval > 0) { + char interval_str[12]; // Max: 24 hours = 86400 seconds (5 digits + null) + sprintf(interval_str, "%u", _prefs.gps_interval); + sensors.setSettingValue("gps_interval", interval_str); + } + } +#endif + private: void writeOKFrame(); void writeErrFrame(uint8_t err_code); diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index eff9efca47..876dc9c33c 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -213,6 +213,10 @@ void setup() { sensors.begin(); +#if ENV_INCLUDE_GPS == 1 + the_mesh.applyGpsPrefs(); +#endif + #ifdef DISPLAY_CLASS ui_task.begin(disp, &sensors, the_mesh.getNodePrefs()); // still want to pass this in as dependency, as prefs might be moved #endif diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 265532be0b..94a8ee3efa 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -560,18 +560,6 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no _node_prefs = node_prefs; -#if ENV_INCLUDE_GPS == 1 - // Apply GPS preferences from stored prefs - if (_sensors != NULL && _node_prefs != NULL) { - _sensors->setSettingValue("gps", _node_prefs->gps_enabled ? "1" : "0"); - if (_node_prefs->gps_interval > 0) { - char interval_str[12]; // Max: 24 hours = 86400 seconds (5 digits + null) - sprintf(interval_str, "%u", _node_prefs->gps_interval); - _sensors->setSettingValue("gps_interval", interval_str); - } - } -#endif - if (_display != NULL) { _display->turnOn(); } diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 24e8894927..d61468789c 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -856,9 +856,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc // defaults memset(&_prefs, 0, sizeof(_prefs)); _prefs.airtime_factor = 1.0; - _prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0; - _prefs.tx_delay_factor = 0.5f; // was 0.25f - _prefs.direct_tx_delay_factor = 0.3f; // was 0.2 + _prefs.rx_delay_base = 3.0f; // off by default, was 10.0, was 0.0 + _prefs.tx_delay_factor = 1.6f; // was 0.25f, was 0.5f + _prefs.direct_tx_delay_factor = 1.0f; // was zero, was 0.3 StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 929584484f..88729ea729 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -119,7 +119,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { #endif void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); - void sendNodeDiscoverReq(); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); uint8_t handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); @@ -177,7 +176,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables); void begin(FILESYSTEM* fs); - + void sendNodeDiscoverReq(); const char* getFirmwareVer() override { return FIRMWARE_VERSION; } const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; } const char* getRole() override { return FIRMWARE_ROLE; } diff --git a/examples/simple_repeater/UITask.cpp b/examples/simple_repeater/UITask.cpp index d096d14b22..acb4632581 100644 --- a/examples/simple_repeater/UITask.cpp +++ b/examples/simple_repeater/UITask.cpp @@ -2,6 +2,10 @@ #include #include +#ifndef USER_BTN_PRESSED +#define USER_BTN_PRESSED LOW +#endif + #define AUTO_OFF_MILLIS 20000 // 20 seconds #define BOOT_SCREEN_MILLIS 4000 // 4 seconds @@ -85,7 +89,7 @@ void UITask::loop() { if (millis() >= _next_read) { int btnState = digitalRead(PIN_USER_BTN); if (btnState != _prevBtnState) { - if (btnState == LOW) { // pressed? + if (btnState == USER_BTN_PRESSED) { // pressed? if (_display->isOn()) { // TODO: any action ? } else { diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 7b94377387..0ae7de0a69 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -606,9 +606,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc // defaults memset(&_prefs, 0, sizeof(_prefs)); _prefs.airtime_factor = 1.0; - _prefs.rx_delay_base = 0.0f; // off by default, was 10.0 - _prefs.tx_delay_factor = 0.5f; // was 0.25f; - _prefs.direct_tx_delay_factor = 0.2f; // was zero + _prefs.rx_delay_base = 3.0f; // off by default, was 10.0, was 0.0 + _prefs.tx_delay_factor = 1.6f; // was 0.25f, was 0.5f + _prefs.direct_tx_delay_factor = 1.0f; // was zero, was 0.3 StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; diff --git a/examples/simple_room_server/UITask.cpp b/examples/simple_room_server/UITask.cpp index 46311c5eb9..42bc14d4a5 100644 --- a/examples/simple_room_server/UITask.cpp +++ b/examples/simple_room_server/UITask.cpp @@ -2,6 +2,10 @@ #include #include +#ifndef USER_BTN_PRESSED +#define USER_BTN_PRESSED LOW +#endif + #define AUTO_OFF_MILLIS 20000 // 20 seconds #define BOOT_SCREEN_MILLIS 4000 // 4 seconds @@ -85,7 +89,7 @@ void UITask::loop() { if (millis() >= _next_read) { int btnState = digitalRead(PIN_USER_BTN); if (btnState != _prevBtnState) { - if (btnState == LOW) { // pressed? + if (btnState == USER_BTN_PRESSED) { // pressed? if (_display->isOn()) { // TODO: any action ? } else { diff --git a/examples/simple_sensor/UITask.cpp b/examples/simple_sensor/UITask.cpp index 0694bc3c1a..0e78fee005 100644 --- a/examples/simple_sensor/UITask.cpp +++ b/examples/simple_sensor/UITask.cpp @@ -2,6 +2,10 @@ #include #include +#ifndef USER_BTN_PRESSED +#define USER_BTN_PRESSED LOW +#endif + #define AUTO_OFF_MILLIS 20000 // 20 seconds #define BOOT_SCREEN_MILLIS 4000 // 4 seconds @@ -85,7 +89,7 @@ void UITask::loop() { if (millis() >= _next_read) { int btnState = digitalRead(PIN_USER_BTN); if (btnState != _prevBtnState) { - if (btnState == LOW) { // pressed? + if (btnState == USER_BTN_PRESSED) { // pressed? if (_display->isOn()) { // TODO: any action ? } else { diff --git a/library.json b/library.json index aa37cb6ed0..8504793c23 100644 --- a/library.json +++ b/library.json @@ -4,7 +4,7 @@ "dependencies": { "SPI": "*", "Wire": "*", - "jgromes/RadioLib": "^7.3.0", + "jgromes/RadioLib": "^7.6.0", "rweather/Crypto": "^0.4.0", "adafruit/RTClib": "^2.1.3", "melopero/Melopero RV3028": "^1.1.0", diff --git a/platformio.ini b/platformio.ini index ba601c26cd..5f722e8923 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,6 +11,7 @@ [platformio] extra_configs = variants/*/platformio.ini + platformio.local.ini [arduino_base] framework = arduino @@ -18,7 +19,7 @@ monitor_speed = 115200 lib_deps = SPI Wire - jgromes/RadioLib @ ^7.3.0 + jgromes/RadioLib @ ^7.6.0 rweather/Crypto @ ^0.4.0 adafruit/RTClib @ ^2.1.3 melopero/Melopero RV3028 @ ^1.1.0 diff --git a/src/MeshCore.h b/src/MeshCore.h index 70cd0f0672..2db1d4c3ec 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -17,6 +17,7 @@ #define PATH_HASH_SIZE 1 #define MAX_PACKET_PAYLOAD 184 +#define MAX_GROUP_DATA_LENGTH (MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE - 3) #define MAX_PATH_SIZE 64 #define MAX_TRANS_UNIT 255 @@ -100,4 +101,4 @@ class RTCClock { } }; -} \ No newline at end of file +} diff --git a/src/Packet.h b/src/Packet.h index 7861954618..0886a06c4e 100644 --- a/src/Packet.h +++ b/src/Packet.h @@ -22,7 +22,7 @@ namespace mesh { #define PAYLOAD_TYPE_ACK 0x03 // a simple ack #define PAYLOAD_TYPE_ADVERT 0x04 // a node advertising its Identity #define PAYLOAD_TYPE_GRP_TXT 0x05 // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg") -#define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, blob) +#define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: data_type(uint16), data_len, blob) #define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...) #define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra) #define PAYLOAD_TYPE_TRACE 0x09 // trace a path, collecting SNI for each hop diff --git a/src/helpers/AutoDiscoverRTCClock.cpp b/src/helpers/AutoDiscoverRTCClock.cpp index 5c3a4f1cc0..49a72893d9 100644 --- a/src/helpers/AutoDiscoverRTCClock.cpp +++ b/src/helpers/AutoDiscoverRTCClock.cpp @@ -1,6 +1,7 @@ #include "AutoDiscoverRTCClock.h" #include "RTClib.h" #include +#include "RTC_RX8130CE.h" static RTC_DS3231 rtc_3231; static bool ds3231_success = false; @@ -11,9 +12,13 @@ static bool rv3028_success = false; static RTC_PCF8563 rtc_8563; static bool rtc_8563_success = false; +static RTC_RX8130CE rtc_8130; +static bool rtc_8130_success = false; + #define DS3231_ADDRESS 0x68 #define RV3028_ADDRESS 0x52 #define PCF8563_ADDRESS 0x51 +#define RX8130CE_ADDRESS 0x32 bool AutoDiscoverRTCClock::i2c_probe(TwoWire& wire, uint8_t addr) { wire.beginTransmission(addr); @@ -25,22 +30,32 @@ void AutoDiscoverRTCClock::begin(TwoWire& wire) { if (i2c_probe(wire, DS3231_ADDRESS)) { ds3231_success = rtc_3231.begin(&wire); } + if (i2c_probe(wire, RV3028_ADDRESS)) { rtc_rv3028.initI2C(wire); - rtc_rv3028.writeToRegister(0x35, 0x00); - rtc_rv3028.writeToRegister(0x37, 0xB4); // Direct Switching Mode (DSM): when VDD < VBACKUP, switchover occurs from VDD to VBACKUP - rtc_rv3028.set24HourMode(); // Set the device to use the 24hour format (default) instead of the 12 hour format + rtc_rv3028.writeToRegister(0x35, 0x00); + rtc_rv3028.writeToRegister(0x37, 0xB4); // Direct Switching Mode (DSM): when VDD < VBACKUP, switchover occurs from VDD to VBACKUP + rtc_rv3028.set24HourMode(); // Set the device to use the 24hour format (default) instead of the 12 hour format rv3028_success = true; } - if(i2c_probe(wire,PCF8563_ADDRESS)){ + + if (i2c_probe(wire, PCF8563_ADDRESS)) { rtc_8563_success = rtc_8563.begin(&wire); } + + if (i2c_probe(wire, RX8130CE_ADDRESS)) { + MESH_DEBUG_PRINTLN("RX8130CE: Found"); + rtc_8130.begin(&wire); + rtc_8130_success = true; + MESH_DEBUG_PRINTLN("RX8130CE: Initialized"); + } } uint32_t AutoDiscoverRTCClock::getCurrentTime() { if (ds3231_success) { return rtc_3231.now().unixtime(); } + if (rv3028_success) { return DateTime( rtc_rv3028.getYear(), @@ -51,9 +66,16 @@ uint32_t AutoDiscoverRTCClock::getCurrentTime() { rtc_rv3028.getSecond() ).unixtime(); } - if(rtc_8563_success){ + + if (rtc_8563_success) { return rtc_8563.now().unixtime(); } + + if (rtc_8130_success) { + MESH_DEBUG_PRINTLN("RX8130CE: Reading time"); + return rtc_8130.now().unixtime(); + } + return _fallback->getCurrentTime(); } @@ -66,6 +88,9 @@ void AutoDiscoverRTCClock::setCurrentTime(uint32_t time) { rtc_rv3028.setTime(dt.year(), dt.month(), weekday, dt.day(), dt.hour(), dt.minute(), dt.second()); } else if (rtc_8563_success) { rtc_8563.adjust(DateTime(time)); + } else if (rtc_8130_success) { + MESH_DEBUG_PRINTLN("RX8130CE: Setting time"); + rtc_8130.adjust(DateTime(time)); } else { _fallback->setCurrentTime(time); } diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 33d7edbee4..7ddc461d29 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -353,8 +353,18 @@ int BaseChatMesh::searchChannelsByHash(const uint8_t* hash, mesh::GroupChannel d #endif void BaseChatMesh::onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mesh::GroupChannel& channel, uint8_t* data, size_t len) { - uint8_t txt_type = data[4]; - if (type == PAYLOAD_TYPE_GRP_TXT && len > 5 && (txt_type >> 2) == 0) { // 0 = plain text msg + if (type == PAYLOAD_TYPE_GRP_TXT) { + if (len < 5) { + MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping short group text payload len=%d", (uint32_t)len); + return; + } + + uint8_t txt_type = data[4]; + if ((txt_type >> 2) != 0) { + MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping unsupported group text type=%d", (uint32_t)txt_type); + return; + } + uint32_t timestamp; memcpy(×tamp, data, 4); @@ -363,6 +373,23 @@ void BaseChatMesh::onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mes // notify UI of this new message onChannelMessageRecv(channel, packet, timestamp, (const char *) &data[5]); // let UI know + } else if (type == PAYLOAD_TYPE_GRP_DATA) { + if (len < 3) { + MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping short group data payload len=%d", (uint32_t)len); + return; + } + + uint16_t data_type = ((uint16_t)data[0]) | (((uint16_t)data[1]) << 8); + uint8_t data_len = data[2]; + size_t available_len = len - 3; + + if (data_len > available_len) { + MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping malformed group data type=%d len=%d available=%d", + (uint32_t)data_type, (uint32_t)data_len, (uint32_t)available_len); + return; + } + + onChannelDataRecv(channel, packet, data_type, &data[3], data_len); } } @@ -454,6 +481,37 @@ bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& chan return false; } +bool BaseChatMesh::sendGroupData(mesh::GroupChannel& channel, uint8_t* path, uint8_t path_len, uint16_t data_type, const uint8_t* data, int data_len) { + if (data_len < 0) { + MESH_DEBUG_PRINTLN("sendGroupData: invalid negative data_len=%d", data_len); + return false; + } + if (data_len > MAX_GROUP_DATA_LENGTH) { + MESH_DEBUG_PRINTLN("sendGroupData: data_len=%d exceeds max=%d", data_len, MAX_GROUP_DATA_LENGTH); + return false; + } + + uint8_t temp[3 + MAX_GROUP_DATA_LENGTH]; + temp[0] = (uint8_t)(data_type & 0xFF); + temp[1] = (uint8_t)(data_type >> 8); + temp[2] = (uint8_t)data_len; + if (data_len > 0) memcpy(&temp[3], data, data_len); + + auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_DATA, channel, temp, 3 + data_len); + if (pkt == NULL) { + MESH_DEBUG_PRINTLN("sendGroupData: unable to create group datagram, data_len=%d", data_len); + return false; + } + + if (path_len == OUT_PATH_UNKNOWN) { + sendFloodScoped(channel, pkt); + } else { + sendDirect(pkt, path, path_len); + } + + return true; +} + bool BaseChatMesh::shareContactZeroHop(const ContactInfo& contact) { int plen = getBlobByKey(contact.id.pub_key, PUB_KEY_SIZE, temp_buf); // retrieve last raw advert packet if (plen == 0) return false; // not found diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index ab90d581be..b39e736388 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -111,6 +111,8 @@ class BaseChatMesh : public mesh::Mesh { virtual uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const = 0; virtual void onSendTimeout() = 0; virtual void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) = 0; + virtual void onChannelDataRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint16_t data_type, + const uint8_t* data, size_t data_len) {} virtual uint8_t onContactRequest(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t* data, uint8_t len, uint8_t* reply) = 0; virtual void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) = 0; virtual void handleReturnPathRetry(const ContactInfo& contact, const uint8_t* path, uint8_t path_len); @@ -148,6 +150,7 @@ class BaseChatMesh : public mesh::Mesh { int sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout); int sendCommandData(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& est_timeout); bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len); + bool sendGroupData(mesh::GroupChannel& channel, uint8_t* path, uint8_t path_len, uint16_t data_type, const uint8_t* data, int data_len); int sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout); int sendAnonReq(const ContactInfo& recipient, const uint8_t* data, uint8_t len, uint32_t& tag, uint32_t& est_timeout); int sendRequest(const ContactInfo& recipient, uint8_t req_type, uint32_t& tag, uint32_t& est_timeout); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 2f7a0fffcb..47a2592bc4 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -55,7 +55,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->tx_power_dbm, sizeof(_prefs->tx_power_dbm)); // 76 file.read((uint8_t *)&_prefs->disable_fwd, sizeof(_prefs->disable_fwd)); // 77 file.read((uint8_t *)&_prefs->advert_interval, sizeof(_prefs->advert_interval)); // 78 - file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 79 + file.read(pad, 1); // 79 : 1 byte unused (was rx_boosted_gain in v1.14.1, moved to end for upgrade compat) file.read((uint8_t *)&_prefs->rx_delay_base, sizeof(_prefs->rx_delay_base)); // 80 file.read((uint8_t *)&_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84 file.read((uint8_t *)&_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88 @@ -87,14 +87,15 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 - // next: 290 + file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 + // next: 291 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); _prefs->tx_delay_factor = constrain(_prefs->tx_delay_factor, 0, 2.0f); _prefs->direct_tx_delay_factor = constrain(_prefs->direct_tx_delay_factor, 0, 2.0f); _prefs->airtime_factor = constrain(_prefs->airtime_factor, 0, 9.0f); - _prefs->freq = constrain(_prefs->freq, 400.0f, 2500.0f); + _prefs->freq = constrain(_prefs->freq, 150.0f, 2500.0f); _prefs->bw = constrain(_prefs->bw, 7.8f, 500.0f); _prefs->sf = constrain(_prefs->sf, 5, 12); _prefs->cr = constrain(_prefs->cr, 5, 8); @@ -145,7 +146,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->tx_power_dbm, sizeof(_prefs->tx_power_dbm)); // 76 file.write((uint8_t *)&_prefs->disable_fwd, sizeof(_prefs->disable_fwd)); // 77 file.write((uint8_t *)&_prefs->advert_interval, sizeof(_prefs->advert_interval)); // 78 - file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 79 + file.write(pad, 1); // 79 : 1 byte unused (rx_boosted_gain moved to end) file.write((uint8_t *)&_prefs->rx_delay_base, sizeof(_prefs->rx_delay_base)); // 80 file.write((uint8_t *)&_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84 file.write((uint8_t *)&_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88 @@ -177,7 +178,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 - // next: 290 + file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 + // next: 291 file.close(); } @@ -273,7 +275,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch uint8_t sf = num > 2 ? atoi(parts[2]) : 0; uint8_t cr = num > 3 ? atoi(parts[3]) : 0; int temp_timeout_mins = num > 4 ? atoi(parts[4]) : 0; - if (freq >= 300.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f && temp_timeout_mins > 0) { + if (freq >= 150.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f && temp_timeout_mins > 0) { _callbacks->applyTempRadioParams(freq, bw, sf, cr, temp_timeout_mins); sprintf(reply, "OK - temp params for %d mins", temp_timeout_mins); } else { @@ -292,7 +294,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch */ } else if (memcmp(command, "get ", 4) == 0) { const char* config = &command[4]; - if (memcmp(config, "af", 2) == 0) { + if (memcmp(config, "dutycycle", 9) == 0) { + float dc = 100.0f / (_prefs->airtime_factor + 1.0f); + int dc_int = (int)dc; + int dc_frac = (int)((dc - dc_int) * 10.0f + 0.5f); + sprintf(reply, "> %d.%d%%", dc_int, dc_frac); + } else if (memcmp(config, "af", 2) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor)); } else if (memcmp(config, "int.thresh", 10) == 0) { sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold); @@ -449,7 +456,19 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch */ } else if (memcmp(command, "set ", 4) == 0) { const char* config = &command[4]; - if (memcmp(config, "af ", 3) == 0) { + if (memcmp(config, "dutycycle ", 10) == 0) { + float dc = atof(&config[10]); + if (dc < 1 || dc > 100) { + strcpy(reply, "ERROR: dutycycle must be 1-100"); + } else { + _prefs->airtime_factor = (100.0f / dc) - 1.0f; + savePrefs(); + float actual = 100.0f / (_prefs->airtime_factor + 1.0f); + int a_int = (int)actual; + int a_frac = (int)((actual - a_int) * 10.0f + 0.5f); + sprintf(reply, "OK - %d.%d%%", a_int, a_frac); + } + } else if (memcmp(config, "af ", 3) == 0) { _prefs->airtime_factor = atof(&config[3]); savePrefs(); strcpy(reply, "OK"); @@ -533,7 +552,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch float bw = num > 1 ? strtof(parts[1], nullptr) : 0.0f; uint8_t sf = num > 2 ? atoi(parts[2]) : 0; uint8_t cr = num > 3 ? atoi(parts[3]) : 0; - if (freq >= 300.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f) { + if (freq >= 150.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f) { _prefs->sf = sf; _prefs->cr = cr; _prefs->freq = freq; @@ -718,7 +737,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } } else if (memcmp(command, "sensor set ", 11) == 0) { strcpy(tmp, &command[11]); - const char *parts[2]; + const char *parts[2]; int num = mesh::Utils::parseTextParts(tmp, parts, 2, ' '); const char *key = (num > 0) ? parts[0] : ""; const char *value = (num > 1) ? parts[1] : "null"; @@ -741,7 +760,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch dp = strchr(dp, 0); int i; for (i = start; i < end && (dp-reply < 134); i++) { - sprintf(dp, "%s=%s\n", + sprintf(dp, "%s=%s\n", _sensors->getSettingName(i), _sensors->getSettingValue(i)); dp = strchr(dp, 0); @@ -821,8 +840,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch bool active = !strcmp(_sensors->getSettingByKey("gps"), "1"); if (enabled) { sprintf(reply, "on, %s, %s, %d sats", - active?"active":"deactivated", - fix?"fix":"no fix", + active?"active":"deactivated", + fix?"fix":"no fix", sats); } else { strcpy(reply, "off"); diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index bade3e8980..c2d78ae08f 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -3,6 +3,10 @@ #include #include +#ifndef USER_BTN_PRESSED +#define USER_BTN_PRESSED LOW +#endif + #if defined(ESP_PLATFORM) #include diff --git a/src/helpers/RTC_RX8130CE.cpp b/src/helpers/RTC_RX8130CE.cpp new file mode 100644 index 0000000000..8aa2b1ff3a --- /dev/null +++ b/src/helpers/RTC_RX8130CE.cpp @@ -0,0 +1,197 @@ +#include "RTC_RX8130CE.h" +#include "RTClib.h" + + +bool RTC_RX8130CE::stop(bool stop) { + write_register(0x1E, stop ? 0x040 : 0x00); + return true; +} + +bool RTC_RX8130CE::begin(TwoWire *wire) { + if (i2c_dev) { + delete i2c_dev; + } + + i2c_dev = new Adafruit_I2CDevice(this->_addr, wire); + if (!i2c_dev->begin()) { + return false; + } + + /* + * Digital offset register: + * [7] DET: 0 -> disabled + * [6:0] L7-L1: 0 -> no offset + */ + write_register(0x30, 0x00); + + /* + * Extension Register register: + * [7:6] FSEL: 0 -> 0 + * [5] USEL: 0 -> 0 + * [4] TE: 0 -> + * [3] WADA: 0 -> 0 + * [2-0] TSEL: 0 -> 0 + */ + write_register(0x1C, 0x00); + + /* + * Flag Register register: + * [7] VBLF: 0 -> 0 + * [6] 0: 0 -> + * [5] UF: 0 -> + * [4] TF: 0 -> + * [3] AF: 0 -> 0 + * [2] RSF: 0 -> 0 + * [1] VLF: 0 -> 0 + * [0] VBFF: 0 -> 0 + */ + write_register(0x1D, 0x00); + + /* + * Control Register0 register: + * [7] TEST: 0 -> 0 + * [6] STOP: 0 -> + * [5] UIE: 0 -> + * [4] TIE: 0 -> + * [3] AIE: 0 -> 0 + * [2] TSTP: 0 -> 0 + * [1] TBKON: 0 -> 0 + * [0] TBKE: 0 -> 0 + */ + write_register(0x1E, 0x00); + + /* + * Control Register1 register: + * [7-6] SMPTSEL: 0 -> 0 + * [5] CHGEN: 0 -> + * [4] INIEN: 0 -> + * [3] 0: 0 -> + * [2] RSVSEL: 0 -> 0 + * [1-0] BFVSEL: 0 -> 0 + */ + write_register(0x1F, 0x00); + + this->stop(false); // clear STOP bit + + /* + * Function register: + * [7] 100TH: 0 -> disabled + * [6:5] Periodic interrupt: 0 -> no periodic interrupt + * [4] RTCM: 0 -> real-time clock mode + * [3] STOPM: 0 -> RTC stop is controlled by STOP bit only + * [2:0] Clock output frequency: 000 (Default value) + */ + write_register(0x28, 0x00); + + // Battery switch register + write_register(0x26, 0x00); // enable battery switch feature + + return true; +} + +bool RTC_RX8130CE::setTime(struct tm *t) { + uint8_t buf[8]; + buf[0] = 0x10; + buf[1] = bin2bcd(t->tm_sec) & 0x7F; + buf[2] = bin2bcd(t->tm_min) & 0x7F; + buf[3] = bin2bcd(t->tm_hour) & 0x3F; + buf[4] = bin2bcd(t->tm_wday) & 0x07; + buf[5] = bin2bcd(t->tm_mday) & 0x3F; + buf[6] = bin2bcd(t->tm_mon + 1) & 0x1F; + buf[7] = bin2bcd((t->tm_year - 100)); + + this->stop(true); + i2c_dev->write(buf, sizeof(buf)); + this->stop(false); + + return true; +} + +void RTC_RX8130CE::adjust(DateTime dt) { + struct tm *atv; + time_t utime; + + utime = (time_t)dt.unixtime(); + atv = gmtime(&utime); + + this->setTime(atv); +} + +DateTime RTC_RX8130CE::now() { + struct tm atv; + this->getTime(&atv); + + return DateTime((uint32_t)mktime(&atv)); +} + +uint32_t RTC_RX8130CE::unixtime() { + struct tm atv; + this->getTime(&atv); + + return (uint32_t)mktime(&atv); +} + +bool RTC_RX8130CE::getTime(struct tm *t) { + uint8_t buff[7]; + + buff[0] = 0x10; + + i2c_dev->write_then_read(buff, 1, buff, 7); + + t->tm_sec = bcd2bin(buff[0] & 0x7F); + t->tm_min = bcd2bin(buff[1] & 0x7F); + t->tm_hour = bcd2bin(buff[2] & 0x3F); + t->tm_wday = bcd2bin(buff[3] & 0x07); + t->tm_mday = bcd2bin(buff[4] & 0x3F); + t->tm_mon = bcd2bin(buff[5] & 0x1F) - 1; + t->tm_year = bcd2bin(buff[6]) + 100; + + return true; +} + +bool RTC_RX8130CE::writeRAM(uint8_t address, uint8_t value) { + return this->writeRAM(address, &value, 1); +} + +size_t RTC_RX8130CE::writeRAM(uint8_t address, uint8_t *value, size_t len) { + uint8_t buf[len + 1]; + + if (address > 3) { + return 0; + } + + if ((address + len) > 3) { + len = 3 - address; + } + + buf[0] = 0x20 + address; + + for (int i = 1; i <= len + 1; i++) { + buf[i] = value[i - 1]; + } + + i2c_dev->write(buf, len + 1); + + return len; +} + +bool RTC_RX8130CE::readRAM(uint8_t address, uint8_t *value, size_t len) { + uint8_t real_address = 0x20 + address; + + if (address > 3) { // Oversize of 64-bytes RAM + return false; + } + + if ((address + len) > 3) { // Data size over RAM size + len = 3 - address; + } + + i2c_dev->write_then_read(&real_address, 1, value, len); + return true; +} + +uint8_t RTC_RX8130CE::readRAM(uint8_t address) { + uint8_t value = 0xFF; + this->readRAM(address, &value, 1); + return value; +} diff --git a/src/helpers/RTC_RX8130CE.h b/src/helpers/RTC_RX8130CE.h new file mode 100644 index 0000000000..1cf148a9fe --- /dev/null +++ b/src/helpers/RTC_RX8130CE.h @@ -0,0 +1,33 @@ +#ifndef __RTC_RX8130CE_H__ +#define __RTC_RX8130CE_H__ + +#include +#include +#include +#include "RTClib.h" + +class RTC_RX8130CE : RTC_I2C { + private: + const uint8_t _addr = 0x32; + + bool stop(bool stop); + + protected: + + public: + bool begin(TwoWire *wire); + bool setTime(struct tm *t); + bool getTime(struct tm *t); + void adjust(DateTime t); + + DateTime now(); + uint32_t unixtime(); + + bool writeRAM(uint8_t address, uint8_t value); + size_t writeRAM(uint8_t address, uint8_t *value, size_t len); + bool readRAM(uint8_t address, uint8_t *value, size_t len); + uint8_t readRAM(uint8_t address); + +}; + +#endif diff --git a/src/helpers/TxtDataHelpers.h b/src/helpers/TxtDataHelpers.h index 6ab84d3975..ece494f291 100644 --- a/src/helpers/TxtDataHelpers.h +++ b/src/helpers/TxtDataHelpers.h @@ -3,9 +3,11 @@ #include #include -#define TXT_TYPE_PLAIN 0 // a plain text message -#define TXT_TYPE_CLI_DATA 1 // a CLI command -#define TXT_TYPE_SIGNED_PLAIN 2 // plain text, signed by sender +#define TXT_TYPE_PLAIN 0 // a plain text message +#define TXT_TYPE_CLI_DATA 1 // a CLI command +#define TXT_TYPE_SIGNED_PLAIN 2 // plain text, signed by sender +#define DATA_TYPE_RESERVED 0x0000 // reserved for future use +#define DATA_TYPE_DEV 0xFFFF // developer namespace for experimenting with group/channel datagrams and building apps class StrHelper { public: diff --git a/src/helpers/esp32/ESPNOWRadio.h b/src/helpers/esp32/ESPNOWRadio.h index c696da3a87..43772e5e24 100644 --- a/src/helpers/esp32/ESPNOWRadio.h +++ b/src/helpers/esp32/ESPNOWRadio.h @@ -4,10 +4,10 @@ class ESPNOWRadio : public mesh::Radio { protected: - uint32_t n_recv, n_sent; + uint32_t n_recv, n_sent, n_recv_errors; public: - ESPNOWRadio() { n_recv = n_sent = 0; } + ESPNOWRadio() { n_recv = n_sent = n_recv_errors = 0; } void init(); int recvRaw(uint8_t* bytes, int sz) override; @@ -19,12 +19,21 @@ class ESPNOWRadio : public mesh::Radio { uint32_t getPacketsRecv() const { return n_recv; } uint32_t getPacketsSent() const { return n_sent; } - void resetStats() { n_recv = n_sent = 0; } + uint32_t getPacketsRecvErrors() const { return n_recv_errors; } + void resetStats() { n_recv = n_sent = n_recv_errors = 0; } virtual float getLastRSSI() const override; virtual float getLastSNR() const override; float packetScore(float snr, int packet_len) override { return 0; } + + /** + * These two functions do nothing for ESP-NOW, but are needed for the + * Radio interface. + */ + virtual void setRxBoostedGainMode(bool) { } + virtual bool getRxBoostedGainMode() const { return false; } + uint32_t intID(); void setTxPower(uint8_t dbm); }; diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 07807011db..19472406d8 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -12,7 +12,7 @@ #endif #define TELEM_BME680_SEALEVELPRESSURE_HPA (1013.25) #include -static Adafruit_BME680 BME680; +static Adafruit_BME680 BME680(TELEM_WIRE); #endif #ifdef ENV_INCLUDE_BMP085 @@ -62,9 +62,15 @@ LPS22HBClass LPS22HB(*TELEM_WIRE); #endif #if ENV_INCLUDE_INA3221 +#ifndef TELEM_INA3221_ADDRESS #define TELEM_INA3221_ADDRESS 0x42 // INA3221 3 channel current sensor I2C address +#endif +#ifndef TELEM_INA3221_SHUNT_VALUE #define TELEM_INA3221_SHUNT_VALUE 0.100 // most variants will have a 0.1 ohm shunts +#endif +#ifndef TELEM_INA3221_NUM_CHANNELS #define TELEM_INA3221_NUM_CHANNELS 3 +#endif #include static Adafruit_INA3221 INA3221; #endif @@ -101,6 +107,12 @@ static Adafruit_MLX90614 MLX90614; static Adafruit_VL53L0X VL53L0X; #endif +#if ENV_INCLUDE_RAK12035 +#define TELEM_RAK12035_ADDRESS 0x20 // RAK12035 Soil Moisture sensor I2C address +#include "RAK12035_SoilMoisture.h" +static RAK12035_SoilMoisture RAK12035; +#endif + #if ENV_INCLUDE_GPS && defined(RAK_BOARD) && !defined(RAK_WISMESH_TAG) #define RAK_WISBLOCK_GPS #endif @@ -180,7 +192,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_BME680 - if (BME680.begin(TELEM_BME680_ADDRESS, TELEM_WIRE)) { + if (BME680.begin(TELEM_BME680_ADDRESS)) { MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESS); BME680_initialized = true; } else { @@ -331,6 +343,17 @@ bool EnvironmentSensorManager::begin() { } #endif + #if ENV_INCLUDE_RAK12035 + RAK12035.setup(*TELEM_WIRE); + if (RAK12035.begin(TELEM_RAK12035_ADDRESS)) { + MESH_DEBUG_PRINTLN("Found sensor RAK12035 at address: %02X", TELEM_RAK12035_ADDRESS); + RAK12035_initialized = true; + } else { + RAK12035_initialized = false; + MESH_DEBUG_PRINTLN("RAK12035 was not found at I2C address %02X", TELEM_RAK12035_ADDRESS); + } + #endif + return true; } @@ -483,8 +506,36 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } #endif - } + #if ENV_INCLUDE_RAK12035 + if (RAK12035_initialized) { + // RAK12035 Telemetry is Channel 2 + telemetry.addTemperature(2, RAK12035.get_sensor_temperature()); + telemetry.addPercentage(2, RAK12035.get_sensor_moisture()); + + // RAK12035 CALIBRATION Telemetry is Channel 3, if enabled + + #ifdef ENABLE_RAK12035_CALIBRATION + // Calibration Data Screen is Channel 3 + float cap = RAK12035.get_sensor_capacitance(); + float _wet = RAK12035.get_humidity_full(); + float _dry = RAK12035.get_humidity_zero(); + + telemetry.addFrequency(3, cap); + telemetry.addTemperature(3, _wet); + telemetry.addPower(3, _dry); + + if(cap > _dry){ + RAK12035.set_humidity_zero(cap); + } + + if(cap < _wet){ + RAK12035.set_humidity_full(cap); + } + #endif + } + #endif + } return true; } @@ -665,7 +716,7 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ gps_detected = true; return true; } - + pinMode(ioPin, INPUT); MESH_DEBUG_PRINTLN("GPS did not init with this IO pin... try the next"); return false; diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index f176a33f5f..32413ebc03 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -22,6 +22,7 @@ class EnvironmentSensorManager : public SensorManager { bool SHT4X_initialized = false; bool BME680_initialized = false; bool BMP085_initialized = false; + bool RAK12035_initialized = false; bool gps_detected = false; bool gps_active = false; diff --git a/src/helpers/sensors/RAK12035_SoilMoisture.cpp b/src/helpers/sensors/RAK12035_SoilMoisture.cpp new file mode 100644 index 0000000000..4daeaadeaf --- /dev/null +++ b/src/helpers/sensors/RAK12035_SoilMoisture.cpp @@ -0,0 +1,554 @@ +/*----------------------------------------------------------------------* + * RAK12035_SoilMoistureSensor.cpp - Arduino library for the Sensor * + * version of I2C Soil Moisture Sensor version from Chrirp * + * (https://github.com/Miceuz/i2c-moisture-sensor). * + * * + * Ingo Fischer 11Nov2015 * + * https://github.com/Apollon77/I2CSoilMoistureSensor * + * * + * Ken Privitt 8Feb2026 * + * Adapted for MeshCore Firmware Stack * + * * + * MIT license * + * * + * This file contains a collection of routines to access the * + * RAK12035 Soil Moisture Sensor via I2C. The sensor provides * + * Soil Temperature and capacitance-based Soil Moisture Readings. * + * * + *----------------------------------------------------------------------*/ + +#include "RAK12035_SoilMoisture.h" +#include "MeshCore.h" +#include + +/*----------------------------------------------------------------------* + * Constructor. * + *----------------------------------------------------------------------*/ + // RAK12035_SoilMoisture(uint8_t addr) + // + // Accepts the I2C Address to look for the RAK12035 + // Initializes the I2C to null (will be setup later in Wire.Begin() + // + // No hardware is touched in the constructor. + // I2C communication is deferred until begin() is called. + //------------------------------------------------------------------------------ + +RAK12035_SoilMoisture::RAK12035_SoilMoisture(uint8_t addr) +{ + _addr = addr; // Save the sensor's I2C address + _i2c = nullptr; // Bus not assigned yet; must be set in begin() +} + +//------------------------------------------------------------------------------ +// setup() +//------------------------------------------------------------------------------ +// setup(TwoWire &i2c) +// +// Assigns the I2C bus that this driver instance will use. This allows the +// application to choose between Wire, Wire1, or any other TwoWire instance +// supported by the platform. +// +// No I2C communication occurs here; setup() simply stores the pointer so that +// begin() and all register‑level operations know which bus to use. +//------------------------------------------------------------------------------ +void RAK12035_SoilMoisture::setup(TwoWire &i2c) + +{ + _i2c = &i2c; // assigns the bus pointer + _i2c->begin(); // Initialize the bus to Wire or Wire1 +} + +//------------------------------------------------------------------------------ +// RAK12035 Soil Moisture begin() +//------------------------------------------------------------------------------ +// +// Performs initialization of the RAK12035 soil‑moisture sensor. This +// routine assumes that the application has already selected the I2C bus via +// setup() and that the bus has been initialized externally (Wire.begin()). +// It uses the passed in I2C Address (default 0x20) +// +// *** This code does not supprt three sensors *** +// The RAK12023 has three connectors, but each of the sensors attached must +// all have a different I2C addresses. +// This code has a function to set the I2C adress of a sensor +// and currently only supports one address 0x20 (the default). +// To support three sensors, EnvironmentSensorManager would need to be modified +// to support multiple instances of the RAK12035_SoilMoisture class, +// each with a different address. (0x20, 0x21, 0x22) +// The begin() function would need to be modified to loop through the three addresses +// +// DEBUG STATEMENTS: Can be enabled by uncommenting or adding: +// File: varients/rak4631 platformio.ini +// Section example: [env:RAK_4631_companion_radio_ble] +// Enable Debug statements: -D MESH_DEBUG=1 +// +//------------------------------------------------------------------------------ +bool RAK12035_SoilMoisture::begin(uint8_t addr) +{ +// MESH_DEBUG_PRINTLN("begin() - Start of RAK12035 initialization"); +// MESH_DEBUG_PRINTLN("begin() - RAK12035 passed in Address %02X", addr); + +// 1. Ensure setup() was called + if (_i2c == nullptr) { + MESH_DEBUG_PRINTLN("RAK12035 ERROR: I2C bus not set!"); + return false; +} + + uint16_t _dry_cal = 200; + uint16_t _wet_cal = 600; + uint8_t _version = 0; + uint8_t _addr; // The I2C address to be used (passed in parameter) + +/*------------------------------------------------------------------------------------------ + * Set Calibration values - This is done with custom a firmware version + * + * USE the Build Flag: -D ENABLE_RAK12035_CALIBRATION = 1 + * OR + * Change the value to 1 in the RAK12035_SoilMoisture.h file + * + * Calibration Procedure: + * 1) Flash the the Calibration version of the firmware. + * 2) Leave the sensor dry, power up the device. + * 3) After detecting the RAK12035 this firmware will display calibration data on Channel 3 + * + * Frequency = Current Capacitance Value + * Temperature = Current Wet calibration value + * Power = Current Dry calibration value + * + * 4) Click refresh several times. This will take a capacitance reading and if it is + * greater than the current Dry value it will store it in the sensor + * The value will bounce a little as you click refresh, but it eventually settles down (a few clicks) + * the stored value will stabalize at it's Maximum value. + * + * 5) Put the sensor in water. + * + * 6) Click refresh several times. This will take a capacitance reading and if it is + * less than the current Wet value it will store it in the sensor + * The value will bounce a little as you click refresh, but it eventually settles down (a few clicks) + * the stored value will stabalize at it's Minimum value. + * + * 7) The Sensor is now calibrated, turn off the device. + * + * 8) Reflash the device with the non-Calibration Firmware, Data will be shown on Channel 2 + * + *------------------------------------------------------------------------------------------ +*/ + +#if ENABLE_RAK12035_CALIBRATION + uint16_t _wet = 2000; // A high value the should be out of the normal Wet range + set_humidity_full(_wet); + + uint16_t _dry = 50; // A low value the should be out of the normal Dry range + set_humidity_zero(_dry); +#endif + + /*-------------------------------------------------------------------------------- + * + * Check if a sensor is present and return true if found, false if not present + * + *-------------------------------------------------------------------------------- + */ + if (query_sensor()) { + MESH_DEBUG_PRINTLN("begin() - Sensor responded with valid version"); + return true; + } + else { + MESH_DEBUG_PRINTLN("begin() - Sensor version FAIL"); + return false; + } +} + +/*--------------------------------------------------------------------------------- + * + * Below are all the routines to execute the various I2C commands supported + * by the RAK12035 sensor + * + *--------------------------------------------------------------------------------*/ + +uint16_t RAK12035_SoilMoisture::get_sensor_capacitance() //Command 01 - (r) 2 byte +{ + uint8_t buf[2] = {0}; + if (!read_rak12035(SOILMOISTURESENSOR_GET_CAPACITANCE, buf, 2)) { + MESH_DEBUG_PRINTLN("Function 1: get_capacitance() FAIL: Bad data returned = %02X %02X", buf[0], buf[1]); + return (buf[0] << 8) | buf[1]; // return raw for debugging + } + uint16_t cap = (buf[0] << 8) | buf[1]; + MESH_DEBUG_PRINTLN("Function 1: get_capacitance() SUCCESS: Capacitance = %d", cap); + return cap; +} + + +uint8_t RAK12035_SoilMoisture::get_I2C_address() //Command 02 - (r) 1 byte +{ + uint8_t addr = 0; + if (!read_rak12035(SOILMOISTURESENSOR_GET_I2C_ADDR, &addr, 1)) { + MESH_DEBUG_PRINTLN("Function 2: get_I2C_address() FAIL: Bad data returned = %02X", addr); + return addr; // return raw for debugging + } + MESH_DEBUG_PRINTLN("Function 2: get_I2C_address() SUCCESS: I2C Address = %02X", addr); + return addr; +} + + +bool RAK12035_SoilMoisture::set_sensor_addr(uint8_t addr) //Command 03 - (w) 1 byte +{ + if (!write_rak12035(SOILMOISTURESENSOR_SET_I2C_ADDR, &addr, 1)) { + MESH_DEBUG_PRINTLN("Function 3: set_I2C_address() FAIL: Could not set new address %02X", addr); + return false; + } + MESH_DEBUG_PRINTLN("Function 3: set_I2C_address() SUCCESS: New address = %02X", addr); + return true; +} + + +uint8_t RAK12035_SoilMoisture::get_sensor_version() // Command 04 - 1 byte +{ + uint8_t v = 0; + + read_rak12035(SOILMOISTURESENSOR_GET_VERSION, &v, 1); + if (!read_rak12035(SOILMOISTURESENSOR_GET_VERSION, &v, 1)) { + MESH_DEBUG_PRINTLN("Function 4: get_sensor_version() FAIL: Bad data returned = %02X", v); + return v; + } + MESH_DEBUG_PRINTLN("Function 4: get_sensor_version() SUCCESS: Version = %02X", v); + return v; +} + + +float RAK12035_SoilMoisture::get_sensor_temperature() //Command 05 - (r) 2 bytes +{ + uint8_t buf[2] = {0}; + if (!read_rak12035(SOILMOISTURESENSOR_GET_TEMPERATURE, buf, 2)) { + MESH_DEBUG_PRINTLN("Function 5: get_temperature() FAIL: Bad data returned = %02X %02X", buf[0], buf[1]); + return (buf[0] << 8) | buf[1]; // raw data returned for debugging 0XFFFF is error + } + // Sensor returns a 16-bit signed integer (°C * 10) + int16_t raw = (buf[0] << 8) | buf[1]; + float tempC = raw / 10.0f; + MESH_DEBUG_PRINTLN("Function 5: get_temperature() SUCCESS: Raw=%04X Temp=%.1f C", raw, tempC); + return tempC; +} + + +bool RAK12035_SoilMoisture::sensor_sleep() //Command 06 - (w) 1 byte +{ + uint8_t tmp = 0; + if (!write_rak12035(SOILMOISTURESENSOR_SET_SLEEP, &tmp, 1)) { + MESH_DEBUG_PRINTLN("Function 6: sensor_sleep() FAIL: Could not send sleep command"); + return false; + } + MESH_DEBUG_PRINTLN("Function 6: sensor_sleep() SUCCESS: Sensor acknowledged sleep command"); + +// Optional: turn off sensor power AFTER successful sleep command + +// This has been commented out due to a pin name conflict with the Heltec v3 +// This will need to be resolved if this funstion is to be utilized in the future +/* + digitalWrite(WB_IO2, LOW); +*/ + return true; +} + + +bool RAK12035_SoilMoisture::set_humidity_full(uint16_t full) //Command 07 - (w) 2 bytes +{ + uint8_t buf[2]; + buf[0] = (full >> 8) & 0xFF; // High byte + buf[1] = full & 0xFF; // Low byte + + if (!write_rak12035(SOILMOISTURESENSOR_SET_WET_CAL, buf, 2)) { + MESH_DEBUG_PRINTLN("Function 7: set_humidity_full() FAIL: Could not set wet calibration value" + ); + return false; + } + MESH_DEBUG_PRINTLN("Function 7: set_humidity_full() SUCCESS: New Full = %04X", full); + return true; +} + + +bool RAK12035_SoilMoisture::set_humidity_zero(uint16_t zero) //Command 08 - (w) 2 bytes +{ + uint8_t buf[2]; + buf[0] = (zero >> 8) & 0xFF; // High byte + buf[1] = zero & 0xFF; // Low byte + + if (!write_rak12035(SOILMOISTURESENSOR_SET_DRY_CAL, buf, 2)) { + MESH_DEBUG_PRINTLN("Function 8: set_humidity_zero() FAIL: Could not set dry calibration value"); + return false; + } + MESH_DEBUG_PRINTLN("Function 8: set_humidity_zero() SUCCESS: New Zero = %04X", zero); + return true; +} + + +uint8_t RAK12035_SoilMoisture::get_sensor_moisture() //Command 09 - (r) 1 byte +{ +// Load calibration values from sensor + _wet_cal = get_humidity_full(); + _dry_cal = get_humidity_zero(); + + MESH_DEBUG_PRINTLN("Function 9: get_moisture() - Read from sensor or calculate from capacitance"); + + // Read sensor version + uint8_t v = get_sensor_version(); + + // If version > 2, read moisture directly from the sensor + if (v > 2) { + MESH_DEBUG_PRINTLN("Version > 02 - Reading moisture directly from sensor"); + uint8_t moisture = get_sensor_humid(); + MESH_DEBUG_PRINTLN("get_moisture() Direct Read = %d%%", moisture); + return moisture; + } + // Otherwise calculate moisture from capacitance + MESH_DEBUG_PRINTLN("Calculating moisture from capacitance"); + + uint16_t cap = get_sensor_capacitance(); + + // Clamp capacitance between calibration points + if (_dry_cal < _wet_cal) { + if (cap <= _dry_cal) cap = _dry_cal; + if (cap >= _wet_cal) cap = _wet_cal; + + float pct = (_wet_cal - cap) * 100.0f / (_wet_cal - _dry_cal); + if (pct > 100.0f) pct = 100.0f; + + MESH_DEBUG_PRINTLN("get_moisture Case 1() Calculated = %d%%", (uint8_t)pct); + return (uint8_t)pct; + } else { + if (cap >= _dry_cal) cap = _dry_cal; + if (cap <= _wet_cal) cap = _wet_cal; + + float pct = (_dry_cal - cap) * 100.0f / (_dry_cal - _wet_cal); + if (pct > 100.0f) pct = 100.0f; + + MESH_DEBUG_PRINTLN("get_moisture Case 2() Calculated = %d%%", (uint8_t)pct); + return (uint8_t)pct; + } +} + + +uint8_t RAK12035_SoilMoisture::get_sensor_humid() //Command 09 - (r) 1 byte +{ + uint8_t moisture = 0; + + if (!read_rak12035(SOILMOISTURESENSOR_GET_MOISTURE, &moisture, 1)) { + MESH_DEBUG_PRINTLN("Function 9: get_sensor_humid() FAIL: Bad data returned = %02X", moisture); + return moisture; // raw fallback + } + MESH_DEBUG_PRINTLN("Function 9: get_sensor_humid() SUCCESS: Moisture = %d%%",moisture); + return moisture; +} + + +uint16_t RAK12035_SoilMoisture::get_humidity_full() //Command 0A - (r) 2 bytes +{ + uint8_t buf[2] = {0}; + + if (!read_rak12035(SOILMOISTURESENSOR_GET_WET_CAL, buf, 2)) { + MESH_DEBUG_PRINTLN("Function A: get_humidity_full() FAIL: Bad data returned = %02X%02X", buf[0], buf[1]); + return 0xFFFF; // error indicator + } + + uint16_t full = (buf[0] << 8) | buf[1]; + + MESH_DEBUG_PRINTLN("Function A: get_humidity_full() SUCCESS: Full = %04X = %d", full, full); + return full; +} + + +uint16_t RAK12035_SoilMoisture::get_humidity_zero() //Command 0B - 2 bytes +{ + uint8_t buf[2] = {0}; + + if (!read_rak12035(SOILMOISTURESENSOR_GET_DRY_CAL, buf, 2)) { + MESH_DEBUG_PRINTLN("Function B: get_humidity_zero() FAIL: Bad data returned = %02X%02X", buf[0], buf[1]); + return 0xFFFF; // error indicator + } + + uint16_t zero = (buf[0] << 8) | buf[1]; + + MESH_DEBUG_PRINTLN("Function B: get_humidity_zero() SUCCESS: Zero = %04X = %d", zero, zero); + return zero; +} + + +/*------------------------------------------------------------------------------------------* + * getEvent() - High-level function to read both moisture and temperature in one call. * + *------------------------------------------------------------------------------------------* + * This function reads the moisture percentage and temperature from the sensor and returns * + * them via output parameters. This may be used for the telemerty delivery in the MeshCore * + * firmware, with a single function to get all sensor data. * + * * + * The function returns true if both readings were successfully obtained, or false if any * + * error occurred during I2C communication. * + * * + * This function is currently not used * + *------------------------------------------------------------------------------------------*/ +bool RAK12035_SoilMoisture::getEvent(uint8_t *humidity, uint16_t *temp) +{ + // Read moisture (0-100%) + uint8_t moist = get_sensor_moisture(); + if (moist == 0xFF) //error indicator + return false; + MESH_DEBUG_PRINTLN("getEvent() - Humidity = %d", moist); + *humidity = moist; + + //Read temperature (degrees C) + uint16_t t = get_sensor_temperature(); + if (t == 0XFFFF) // error indicator + return false; + + *temp = t; + MESH_DEBUG_PRINTLN("getEvent() - Temperature = %d", t); + return true; +} + +/*------------------------------------------------------------------------------------------* + * Sensor Power Management and Reset Routines + * + * These routines manage the power and reset state of the sensor. The sensor_on() routine is + * designed to power on the sensor and wait for it to become responsive, while the reset() + * routine toggles the reset pin and waits for the sensor to respond with a valid version. + * + * They are for a future sensor power management function. + *------------------------------------------------------------------------------------------*/ + +bool RAK12035_SoilMoisture::sensor_on() +{ + uint8_t data; + // This has been commented out due to a pin name conflict with the Heltec v3 + // This will need to be resolved if this funstion is to be utilized in the future + +/* + pinMode(WB_IO2, OUTPUT); + digitalWrite(WB_IO2, HIGH); //Turn on Sensor Power + + pinMode(WB_IO4, OUTPUT); //Set IO4 Pin to Output (connected to *reset on sensor) + digitalWrite(WB_IO4, LOW); //*reset - Reset the Sensor + delay(1); //Wait for the minimum *reset, 1mS is longer than required minimum + digitalWrite(WB_IO4, HIGH); //Deassert Reset + + delay(10); // Wait for the sensor code to complete initialization +*/ + uint8_t v = 0; + time_t timeout = millis(); + while ((!query_sensor())) //Wait for sensor to respond to I2C commands, + { //indicating it is ready + if ((millis() - timeout) > 50){ //0.5 second timeout for sensor to respond + MESH_DEBUG_PRINTLN("reset() - Timeout, no response from I2C commands"); + return false; + } + else { + delay(10); //delay 10mS + } + } +} + +bool RAK12035_SoilMoisture::reset() +{ +// This function is for a future Sensor Power Management function. +// When power is reapplied this will reset the sensor and wait for it to respond +// with a valid version. +// +// The Atmel 8495 Microcoltroller: Reset input. A low level on this pin for longer than +// the minimum pulse length will generate a reset, even if the clock is not +// running and provided the reset pin has not been disabled. The minimum pulse length is +// given in Table 25-5 on page 240. 2000ns = .002mS +// Shorter pulses are not guaranteed to generate a reset. +// +// Power is never removed so the Sensor reset was removed and is not needed, +// But might be needed if power is ever switched off. Here is tested code. + +// This has been commented out due to a pin name conflict with the Heltec v3 +// This will need to be resolved if this funstion is to be utilized in the future + +/* + pinMode(WB_IO4, OUTPUT); //Set IO4 Pin to Output (connected to *reset on sensor) + MESH_DEBUG_PRINTLN("Assert *reset (Low) for 1 mS"); + digitalWrite(WB_IO4, LOW); //Reset the Sensor + delay(1); //Wait for the minimum *reset, 1mS is longer than required minimum + MESH_DEBUG_PRINTLN("reset() - De-assert *reset (High)"); + digitalWrite(WB_IO4, HIGH); // Deassert Reset +*/ + + MESH_DEBUG_PRINTLN("reset() - Begin poling in 100mS intervals for a non-zero version"); + uint32_t start_time = millis(); + MESH_DEBUG_PRINTLN("reset() - Timeout, Start Time: %d milliseconds", start_time); + + const uint32_t timeout_ms = 500; // Wait for 0.5 seconds + uint32_t start = millis(); + + while (true) { + if (query_sensor()) { + MESH_DEBUG_PRINTLN("reset() - First Pass, Sensor responded with valid version"); + uint32_t stop_time = millis(); + MESH_DEBUG_PRINTLN("reset() - Timeout, Stop Time: %d mS", stop_time); + MESH_DEBUG_PRINTLN("reset() - Timeout, Duration: %d mS", (stop_time - start_time)); + + return true; + } + if (millis() - start > timeout_ms) { + MESH_DEBUG_PRINTLN("reset() - Timeout waiting for valid sensor version"); + uint32_t stop_time = millis(); + MESH_DEBUG_PRINTLN("reset() - Timeout, Stop Time: %d mS", stop_time); + MESH_DEBUG_PRINTLN("reset() - Timeout, Duration: %d mS", (stop_time - start_time)); + return false; + } + delay(100); + } +} + + bool RAK12035_SoilMoisture::query_sensor() +{ + uint8_t v = 0; + v = get_sensor_version(); + + // Treat 0x00 and 0xFF as invalid / bootloader / garbage + if (v == 0x00 || v == 0xFF) { + MESH_DEBUG_PRINTLN("query_sensor() FAIL: Version value invalid: %02X", v); + return false; + } + MESH_DEBUG_PRINTLN("query_sensor() SUCCESS: Sensor Present, Version = %02X", v); + return true; +} + + +/*------------------------------------------------------------------------------------------* + * Below are the low-level I2C read and write functions. These handle the actual + * communication with the sensor registers. The higher-level functions call these + * to perform specific tasks. + *------------------------------------------------------------------------------------------*/ + +bool RAK12035_SoilMoisture::read_rak12035(uint8_t cmd, uint8_t *data, uint8_t length) +{ + _i2c->beginTransmission(_addr); + _i2c->write(cmd); // <-- COMMAND, not register index + if (_i2c->endTransmission() != 0) + return false; + + delay(20); + + int received = _i2c->requestFrom(_addr, length); + if (received != length) + return false; + + for (int i = 0; i < length; i++) + data[i] = _i2c->read(); + + return true; +} + +bool RAK12035_SoilMoisture::write_rak12035(uint8_t cmd, uint8_t *data, uint8_t length) +{ + _i2c->beginTransmission(_addr); + _i2c->write(cmd); // <-- COMMAND, not register index + + for (uint8_t i = 0; i < length; i++) + _i2c->write(data[i]); + + if (_i2c->endTransmission() != 0) + return false; + + delay(20); + return true; +} diff --git a/src/helpers/sensors/RAK12035_SoilMoisture.h b/src/helpers/sensors/RAK12035_SoilMoisture.h new file mode 100644 index 0000000000..f5dc1ce599 --- /dev/null +++ b/src/helpers/sensors/RAK12035_SoilMoisture.h @@ -0,0 +1,88 @@ +/** + * @file RAK12035_SoilMoisture.h + * @author Bernd Giesecke (bernd.giesecke@rakwireless.com) + * @brief Header file for Class RAK12035 + * @version 0.1 + * @date 2021-11-20 + * + * Updates for MeshCore integration + * Ken Privitt + * 2/26/2026 + * + * @copyright Copyright (c) 2021 + * + */ +#ifndef RAK12035_SOILMOISTURE_H +#define RAK12035_SOILMOISTURE_H +#endif + +#ifndef ENABLE_RAK12025_CALIBRATION +#define ENABLE_RAK12025_CALIBRATION = 0 // Used to generate Calibration Version of Firmware + +#include +#include + +#define RAK12035_I2C_ADDR_DEFAULT 0x20 +#define RAK12035_0_ADDR 0x20 +#define RAK12035_1_ADDR 0x21 +#define RAK12035_2_ADDR 0x22 + +// Command codes used by the RAK12035 firmware +#define SOILMOISTURESENSOR_GET_CAPACITANCE 0x01 // (r) 2 bytes +#define SOILMOISTURESENSOR_GET_I2C_ADDR 0x02 // (r) 1 bytes +#define SOILMOISTURESENSOR_SET_I2C_ADDR 0x03 // (w) 1 bytes +#define SOILMOISTURESENSOR_GET_VERSION 0x04 // (r) 1 bytes +#define SOILMOISTURESENSOR_GET_TEMPERATURE 0x05 // (r) 2 bytes +#define SOILMOISTURESENSOR_SET_SLEEP 0x06 // (w) 1 bytes +#define SOILMOISTURESENSOR_SET_WET_CAL 0x07 // (w) 2 bytes +#define SOILMOISTURESENSOR_SET_DRY_CAL 0x08 // (w) 2 bytes +#define SOILMOISTURESENSOR_GET_MOISTURE 0x09 // (r) 1 bytes +#define SOILMOISTURESENSOR_GET_WET_CAL 0x0A // (r) 2 bytes +#define SOILMOISTURESENSOR_GET_DRY_CAL 0x0B // (r) 2 bytes + +class RAK12035_SoilMoisture +{ +public: + RAK12035_SoilMoisture(uint8_t addr = RAK12035_I2C_ADDR_DEFAULT); + + void setup(TwoWire& i2c); + bool begin(uint8_t addr); + bool getEvent(uint8_t *humidity, uint16_t *temperature); + + uint16_t get_sensor_capacitance(); //Command 01 - (r) 2 byte + uint8_t get_I2C_address(); //Command 02 - (r) 1 byte + bool set_sensor_addr(uint8_t addr); //Command 03 - (w) 1 byte + uint8_t get_sensor_version(); //Command 04 - (r) 1 byte + float get_sensor_temperature(); //Command 05 - (r) 2 bytes + bool sensor_sleep(); //Command 06 - (w) 1 byte + bool set_humidity_full(uint16_t hundred_val); //Command 07 - (w) 2 bytes + bool set_humidity_zero(uint16_t zero_val); //Command 08 - (w) 2 bytes + uint8_t get_sensor_moisture(); //Command 09 - (r) 1 byte + uint8_t get_sensor_humid(); //Command 09 - (r) 1 byte + uint16_t get_humidity_full(); //Command 0A - (r) 2 bytes + uint16_t get_humidity_zero(); //Command 0B - (r) 2 bytes + + bool read_rak12035(uint8_t cmd, uint8_t *data, uint8_t length); + bool write_rak12035(uint8_t cmd, uint8_t *data, uint8_t length); + + bool query_sensor(); + bool sensor_on(); + bool reset(); + + uint16_t _dry_cal; + uint16_t _wet_cal; + +private: + bool read_reg(uint8_t reg, uint8_t *data, uint8_t len); + bool write_reg(uint8_t reg, uint8_t *data, uint8_t len); + + TwoWire *_i2c = &Wire; + uint8_t _addr; + + uint16_t default_dry_cal = 2000; + uint16_t default_wet_cal = 50; + uint8_t _capacitance = 0; + uint16_t _temperature = 0; + uint8_t _moisture = 0; +}; +#endif diff --git a/src/helpers/ui/ST7735Display.cpp b/src/helpers/ui/ST7735Display.cpp index 0a28077c06..3eb4521cca 100644 --- a/src/helpers/ui/ST7735Display.cpp +++ b/src/helpers/ui/ST7735Display.cpp @@ -21,10 +21,14 @@ bool ST7735Display::begin() { if (_peripher_power) _peripher_power->claim(); pinMode(PIN_TFT_LEDA_CTL, OUTPUT); - digitalWrite(PIN_TFT_LEDA_CTL, HIGH); +#if defined(PIN_TFT_LEDA_CTL_ACTIVE) + digitalWrite(PIN_TFT_LEDA_CTL, PIN_TFT_LEDA_CTL_ACTIVE); +#else + digitalWrite(PIN_TFT_LEDA_CTL, HIGH); +#endif digitalWrite(PIN_TFT_RST, HIGH); -#if defined(HELTEC_TRACKER_V2) +#if defined(HELTEC_TRACKER_V2) || defined(HELTEC_T096) display.initR(INITR_MINI160x80); display.setRotation(DISPLAY_ROTATION); uint8_t madctl = ST77XX_MADCTL_MY | ST77XX_MADCTL_MV |ST7735_MADCTL_BGR;//Adjust color to BGR @@ -50,9 +54,12 @@ void ST7735Display::turnOn() { void ST7735Display::turnOff() { if (_isOn) { - digitalWrite(PIN_TFT_LEDA_CTL, HIGH); digitalWrite(PIN_TFT_RST, LOW); - digitalWrite(PIN_TFT_LEDA_CTL, LOW); +#if defined(PIN_TFT_LEDA_CTL_ACTIVE) + digitalWrite(PIN_TFT_LEDA_CTL, !PIN_TFT_LEDA_CTL_ACTIVE); +#else + digitalWrite(PIN_TFT_LEDA_CTL, LOW); +#endif _isOn = false; if (_peripher_power) _peripher_power->release(); diff --git a/tream b/tream new file mode 100644 index 0000000000..74570f6639 --- /dev/null +++ b/tream @@ -0,0 +1,324 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + ESC-j * Forward one file line (or _N file lines). + ESC-k * Backward one file line (or _N file lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + ESC-b * Backward one window, but don't stop at beginning-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ^O^N ^On * Search forward for (_N-th) OSC8 hyperlink. + ^O^P ^Op * Search backward for (_N-th) OSC8 hyperlink. + ^O^L ^Ol Jump to the currently selected OSC8 hyperlink. + ESC-u Undo (toggle) search highlighting. + ESC-U Clear search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines. + --------------------------------------------------- + Search is case-sensitive unless changed with -i or -I. + A search pattern may begin with one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + ^S _n Search for match in _n-th parenthesized subpattern. + ^W WRAP search if no match found. + ^L Enter next character literally into pattern. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>. + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-m_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + ^O^O Open the currently selected OSC8 hyperlink. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + #_c_o_m_m_a_n_d Execute the shell command, expanded like a prompt. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c ........ --clear-screen + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D xx_c_o_l_o_r . --color=xx_c_o_l_o_r + Set screen colors. + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k _f_i_l_e ... --lesskey-file=_f_i_l_e + Use a compiled lesskey file. + -K ........ --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n ......... --line-numbers + Suppress line numbers in prompts and messages. + -N ......... --LINE-NUMBERS + Display line number at start of each line. + -o [_f_i_l_e] .. --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] .. --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p _p_a_t_t_e_r_n . --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t _t_a_g .... --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces, tabs and carriage returns. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Set horizontal scroll amount (0 = one half screen width). + + --exit-follow-on-close + Exit F command on a pipe when writer closes pipe. + --file-size + Automatically determine the size of the input file. + --follow-name + The F command changes files if the input file is renamed. + --form-feed + Stop scrolling when a form feed character is reached. + --header=[_L[,_C[,_N]]] + Use _L lines (starting at line _N) and _C columns as headers. + --incsearch + Search file as each pattern character is typed in. + --intr=[_C] + Use _C instead of ^X to interrupt a read. + --lesskey-context=_t_e_x_t + Use lesskey source file contents. + --lesskey-src=_f_i_l_e + Use a lesskey source file. + --line-num-width=[_N] + Set the width of the -N line number field to _N characters. + --match-shift=[_N] + Show at least _N characters to the left of a search match. + --modelines=[_N] + Read _N lines from the input file and look for vim modelines. + --mouse + Enable mouse input. + --no-edit-warn + Don't warn when using v command on a file opened via LESSOPEN. + --no-keypad + Don't send termcap keypad init/deinit strings. + --no-histdups + Remove duplicates from command history. + --no-number-headers + Don't give line numbers to header lines. + --no-paste + Ignore pasted input. + --no-search-header-lines + Searches do not include header lines. + --no-search-header-columns + Searches do not include header columns. + --no-search-headers + Searches do not include header lines or columns. + --no-vbell + Disable the terminal's visual bell. + --redraw-on-quit + Redraw final screen when quitting. + --rscroll=[_C] + Set the character used to mark truncated lines. + --save-marks + Retain marks across invocations of less. + --search-options=[EFKNRW-] + Set default options for every search. + --show-preproc-errors + Display a message if preprocessor exits with an error status. + --proc-backspace + Process backspaces for bold/underline. + --PROC-BACKSPACE + Treat backspaces as control characters. + --proc-return + Delete carriage returns before newline. + --PROC-RETURN + Treat carriage returns as control characters. + --proc-tab + Expand tabs to spaces. + --PROC-TAB + Treat tabs as control characters. + --status-col-width=[_N] + Set the width of the -J status column to _N characters. + --status-line + Highlight or color the entire line containing a mark. + --use-backslash + Subsequent options use backslash as escape char. + --use-color + Enables colored text. + --wheel-lines=[_N] + Each click of the mouse wheel moves _N lines. + --wordwrap + Wrap lines at spaces. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all. diff --git a/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.cpp b/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.cpp new file mode 100644 index 0000000000..ef4af8126b --- /dev/null +++ b/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include "GAT562EVBProBoard.h" + + +#ifdef NRF52_POWER_MANAGEMENT +// Static configuration for power management +// Values set in variant.h defines +const PowerMgtConfig power_config = { + .lpcomp_ain_channel = PWRMGT_LPCOMP_AIN, + .lpcomp_refsel = PWRMGT_LPCOMP_REFSEL, + .voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK +}; + + +void GAT562EVBProBoard::initiateShutdown(uint8_t reason) { + // Disable LoRa module power before shutdown + digitalWrite(SX126X_POWER_EN, LOW); + + if (reason == SHUTDOWN_REASON_LOW_VOLTAGE || + reason == SHUTDOWN_REASON_BOOT_PROTECT) { + configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel); + } + + enterSystemOff(reason); +} +#endif // NRF52_POWER_MANAGEMENT + + +void GAT562EVBProBoard::begin() { + NRF52BoardDCDC::begin(); + pinMode(PIN_VBAT_READ, INPUT); + + // Set all button pins to INPUT_PULLUP + pinMode(PIN_BUTTON1, INPUT_PULLUP); + +#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL); +#endif + + Wire.begin(); + + pinMode(SX126X_POWER_EN, OUTPUT); +#ifdef NRF52_POWER_MANAGEMENT + // Boot voltage protection check (may not return if voltage too low) + // We need to call this after we configure SX126X_POWER_EN as output but before we pull high + checkBootVoltage(&power_config); +#endif + digitalWrite(SX126X_POWER_EN, HIGH); + delay(10); // give sx1268 some time to power up +} \ No newline at end of file diff --git a/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h b/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h new file mode 100644 index 0000000000..33c3d05fc5 --- /dev/null +++ b/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + + +class GAT562EVBProBoard : public NRF52BoardDCDC { +protected: +#ifdef NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; +#endif + +public: + GAT562EVBProBoard() : NRF52Board("GAT562_OTA") {} + void begin(); + + #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) / 4096; + } + + const char* getManufacturerName() const override { + return "GAT562 EVB Pro"; + } + +#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 + + void powerOff() override { + uint32_t button_pin = PIN_BUTTON1; + nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); + nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); + sd_power_system_off(); + } + +}; diff --git a/variants/gat562_mesh_evb_pro/platformio.ini b/variants/gat562_mesh_evb_pro/platformio.ini new file mode 100644 index 0000000000..cede9c97c0 --- /dev/null +++ b/variants/gat562_mesh_evb_pro/platformio.ini @@ -0,0 +1,52 @@ +[GAT562_Mesh_EVB_Pro] +extends = nrf52_base +board = rak4631 +board_check = true +build_flags = ${nrf52_base.build_flags} + ${sensor_base.build_flags} + -I variants/gat562_mesh_evb_pro + -D NRF52_POWER_MANAGEMENT + -D PIN_BOARD_SCL=14 + -D PIN_BOARD_SDA=13 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 +build_src_filter = ${nrf52_base.build_src_filter} + +<../variants/gat562_mesh_evb_pro> + + + + +lib_deps = + ${nrf52_base.lib_deps} + ${sensor_base.lib_deps} + sparkfun/SparkFun u-blox GNSS Arduino Library@^2.2.27 + +[env:GAT562_Mesh_EVB_Pro_repeater] +extends = GAT562_Mesh_EVB_Pro +build_flags = + ${GAT562_Mesh_EVB_Pro.build_flags} + -D ADVERT_NAME='"GAT562 EVB Pro Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${GAT562_Mesh_EVB_Pro.build_src_filter} + +<../examples/simple_repeater> + + +[env:GAT562_Mesh_EVB_Pro_room_server] +extends = GAT562_Mesh_EVB_Pro +build_flags = + ${GAT562_Mesh_EVB_Pro.build_flags} + -D ADVERT_NAME='"GAT562 EVB Pro Room Server"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${GAT562_Mesh_EVB_Pro.build_src_filter} + +<../examples/simple_room_server> diff --git a/variants/gat562_mesh_evb_pro/target.cpp b/variants/gat562_mesh_evb_pro/target.cpp new file mode 100644 index 0000000000..368b5fa19d --- /dev/null +++ b/variants/gat562_mesh_evb_pro/target.cpp @@ -0,0 +1,58 @@ +#include +#include "target.h" +#include + +GAT562EVBProBoard board; + +#ifndef PIN_USER_BTN + #define PIN_USER_BTN (-1) +#endif + + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true, false, false); + + MomentaryButton back_btn(PIN_BACK_BTN, 1000, true, false, true); +#endif + + +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); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif + +bool radio_init() { + rtc_clock.begin(Wire); + return radio.std_init(&SPI); +} + +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(int8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/gat562_mesh_evb_pro/target.h b/variants/gat562_mesh_evb_pro/target.h new file mode 100644 index 0000000000..2a3c6f87c9 --- /dev/null +++ b/variants/gat562_mesh_evb_pro/target.h @@ -0,0 +1,29 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include + +#ifdef DISPLAY_CLASS + #include + extern DISPLAY_CLASS display; + #include + extern MomentaryButton user_btn; + + extern MomentaryButton back_btn; +#endif + +extern GAT562EVBProBoard 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(int8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/gat562_mesh_evb_pro/variant.cpp b/variants/gat562_mesh_evb_pro/variant.cpp new file mode 100644 index 0000000000..4bbfd78f05 --- /dev/null +++ b/variants/gat562_mesh_evb_pro/variant.cpp @@ -0,0 +1,49 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" +#include "nrf.h" + +const uint32_t g_ADigitalPinMap[] = +{ + // P0 + 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , + 8 , 9 , 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47 +}; + + +void initVariant() +{ + // LED1 & LED2 + pinMode(PIN_LED1, OUTPUT); + ledOff(PIN_LED1); + + // pinMode(PIN_LED2, OUTPUT); + // ledOff(PIN_LED2);; +} + diff --git a/variants/gat562_mesh_evb_pro/variant.h b/variants/gat562_mesh_evb_pro/variant.h new file mode 100644 index 0000000000..b0e54a7941 --- /dev/null +++ b/variants/gat562_mesh_evb_pro/variant.h @@ -0,0 +1,216 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_RAK4630_ +#define _VARIANT_RAK4630_ + +#define RAK4630 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +// define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + /* + * WisBlock Base GPIO definitions + */ + static const uint8_t WB_IO1 = 17; // SLOT_A SLOT_B + static const uint8_t WB_IO2 = 34; // SLOT_A SLOT_B + static const uint8_t WB_IO3 = 21; // SLOT_C + static const uint8_t WB_IO4 = 4; // SLOT_C + static const uint8_t WB_IO5 = 9; // SLOT_D + static const uint8_t WB_IO6 = 10; // SLOT_D + static const uint8_t WB_SW1 = 33; // IO_SLOT + static const uint8_t WB_A0 = 5; // IO_SLOT + static const uint8_t WB_A1 = 31; // IO_SLOT + static const uint8_t WB_I2C1_SDA = 13; // SENSOR_SLOT IO_SLOT + static const uint8_t WB_I2C1_SCL = 14; // SENSOR_SLOT IO_SLOT + static const uint8_t WB_I2C2_SDA = 24; // IO_SLOT + static const uint8_t WB_I2C2_SCL = 25; // IO_SLOT + static const uint8_t WB_SPI_CS = 26; // IO_SLOT + static const uint8_t WB_SPI_CLK = 3; // IO_SLOT + static const uint8_t WB_SPI_MISO = 29; // IO_SLOT + static const uint8_t WB_SPI_MOSI = 30; // IO_SLOT + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED1 (35) +#define PIN_LED2 (36) + +#define LED_BUILTIN PIN_LED1 +#define LED_CONN PIN_LED2 + +#define LED_GREEN PIN_LED1 +#define LED_BLUE PIN_LED2 + +#define LED_STATE_ON 1 // State when LED is litted + +// #define P_LORA_TX_LED LED_GREEN + + +/* + * Buttons + */ +#define PIN_BUTTON1 (9) // Menu / User Button +#define PIN_BACK_BTN PIN_BUTTON1 +#define PIN_USER_BTN PIN_BUTTON1 + + +// Analog pins +#define PIN_VBAT_READ (5) +#define ADC_MULTIPLIER (3 * 1.75 * 1.187 * 1000) + + +/* + * Analog pins + */ +#define PIN_A0 (5) //(3) +#define PIN_A1 (31) //(4) +#define PIN_A2 (28) +#define PIN_A3 (29) +#define PIN_A4 (30) +#define PIN_A5 (31) +#define PIN_A6 (0xff) +#define PIN_A7 (0xff) + + static const uint8_t A0 = PIN_A0; + static const uint8_t A1 = PIN_A1; + static const uint8_t A2 = PIN_A2; + static const uint8_t A3 = PIN_A3; + static const uint8_t A4 = PIN_A4; + static const uint8_t A5 = PIN_A5; + static const uint8_t A6 = PIN_A6; + static const uint8_t A7 = PIN_A7; +#define ADC_RESOLUTION 14 + +// Power management boot protection threshold (millivolts) +// Set to 0 to disable boot protection +#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV) +// LPCOMP wake configuration (voltage recovery from SYSTEMOFF) +// AIN3 = P0.05 = PIN_A0 / PIN_VBAT_READ +#define PWRMGT_LPCOMP_AIN 3 +#define PWRMGT_LPCOMP_REFSEL 4 // 5/8 VDD (~3.13-3.44V) + +// Other pins +#define PIN_AREF (2) +#define PIN_NFC1 (9) +#define PIN_NFC2 (10) + + static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +// TXD1 RXD1 on Base Board +#define PIN_SERIAL1_RX (15) +#define PIN_SERIAL1_TX (16) + +// TXD0 RXD0 on Base Board +#define PIN_SERIAL2_RX (19) +#define PIN_SERIAL2_TX (20) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 1 + +#define PIN_SPI_MISO (29) +#define PIN_SPI_MOSI (30) +#define PIN_SPI_SCK (3) + + static const uint8_t SS = 26; + static const uint8_t MOSI = PIN_SPI_MOSI; + static const uint8_t MISO = PIN_SPI_MISO; + static const uint8_t SCK = PIN_SPI_SCK; + +// LoRa radio module pins for RAK4631 + +#define SX126X_POWER_EN (37) +#define P_LORA_RESET (38) +#define P_LORA_NSS (42) +#define P_LORA_SCLK (43) +#define P_LORA_MOSI (44) +#define P_LORA_MISO (45) +#define P_LORA_BUSY (46) +#define P_LORA_DIO_1 (47) + +#define SX126X_DIO2_AS_RF_SWITCH true +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 2 + +#define PIN_WIRE_SDA (13) +#define PIN_WIRE_SCL (14) + +#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 + +// On-board QSPI Flash +// No onboard flash +#define EXTERNAL_FLASH_DEVICES IS25LP080D +#define EXTERNAL_FLASH_USE_QSPI + +#define GPS_ADDRESS 0x42 //i2c address for GPS + + +// GPS L76KB +#define GPS_BAUD_RATE 9600 +#define GPS_THREAD_INTERVAL 50 +#define PIN_GPS_TX PIN_SERIAL1_RX +#define PIN_GPS_RX PIN_SERIAL1_TX +#define PIN_GPS_EN (33) +#define PIN_GPS_PPS (17) + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif diff --git a/variants/gat562_mesh_tracker_pro/platformio.ini b/variants/gat562_mesh_tracker_pro/platformio.ini index 0aa315612c..8a947bce74 100644 --- a/variants/gat562_mesh_tracker_pro/platformio.ini +++ b/variants/gat562_mesh_tracker_pro/platformio.ini @@ -5,8 +5,6 @@ board_check = true build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_mesh_tracker_pro - -D RAK_4631 - -D RAK_BOARD -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 diff --git a/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.cpp b/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.cpp new file mode 100644 index 0000000000..5a24b541de --- /dev/null +++ b/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include "GAT56MeshWatch13Board.h" + + +#ifdef NRF52_POWER_MANAGEMENT +// Static configuration for power management +// Values set in variant.h defines +const PowerMgtConfig power_config = { + .lpcomp_ain_channel = PWRMGT_LPCOMP_AIN, + .lpcomp_refsel = PWRMGT_LPCOMP_REFSEL, + .voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK +}; + + +void GAT56MeshWatch13Board::initiateShutdown(uint8_t reason) { + if (reason == SHUTDOWN_REASON_LOW_VOLTAGE || + reason == SHUTDOWN_REASON_BOOT_PROTECT) { + configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel); + } + enterSystemOff(reason); +} +#endif // NRF52_POWER_MANAGEMENT + + +void GAT56MeshWatch13Board::begin() { + NRF52BoardDCDC::begin(); + pinMode(PIN_VBAT_READ, INPUT); + + +#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL); +#endif + + Wire.begin(); + + pinMode(SX126X_POWER_EN, OUTPUT); +#ifdef NRF52_POWER_MANAGEMENT + // Boot voltage protection check (may not return if voltage too low) + // We need to call this after we configure SX126X_POWER_EN as output but before we pull high + checkBootVoltage(&power_config); +#endif + digitalWrite(SX126X_POWER_EN, HIGH); + delay(10); // give sx1262 some time to power up +} \ No newline at end of file diff --git a/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h b/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h new file mode 100644 index 0000000000..da792b78b1 --- /dev/null +++ b/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + + +class GAT56MeshWatch13Board : public NRF52BoardDCDC { +protected: +#ifdef NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; +#endif + +public: + GAT56MeshWatch13Board() : NRF52Board("GAT562_OTA") {} + void begin(); + + #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) / 4096; + } + + const char* getManufacturerName() const override { + return "GAT562 Mesh Watch 13"; + } + + + void powerOff() override { + uint32_t button_pin = PIN_BUTTON1; + nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); + nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); + sd_power_system_off(); + } + +}; diff --git a/variants/gat562_mesh_watch13/platformio.ini b/variants/gat562_mesh_watch13/platformio.ini new file mode 100644 index 0000000000..ef30829d5c --- /dev/null +++ b/variants/gat562_mesh_watch13/platformio.ini @@ -0,0 +1,89 @@ +[GAT562_Mesh_Watch13] +extends = nrf52_base +board = rak4631 +board_check = true +build_flags = ${nrf52_base.build_flags} + ${sensor_base.build_flags} + -UENV_INCLUDE_GPS + -I variants/gat562_mesh_watch13 + -D RAK_4631 + -D RAK_BOARD + -D NRF52_POWER_MANAGEMENT + -D PIN_BOARD_SCL=14 + -D PIN_BOARD_SDA=13 + -D PIN_OLED_RESET=-1 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=19 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D QSPIFLASH=1 +build_src_filter = ${nrf52_base.build_src_filter} + +<../variants/gat562_mesh_watch13> + + + + + + +lib_deps = + ${nrf52_base.lib_deps} + ${sensor_base.lib_deps} + adafruit/Adafruit SSD1306 @ ^2.5.13 + + +;[env:GAT562_Mesh_Watch13_repeater] +;extends = GAT562_Mesh_Watch13 +;build_flags = +; ${GAT562_Mesh_Watch13.build_flags} +; -D DISPLAY_CLASS=SSD1306Display +; -D ADVERT_NAME='"GAT562 Repeater"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D MAX_NEIGHBOURS=50 +;; -D MESH_PACKET_LOGGING=1 +;; -D MESH_DEBUG=1 +;build_src_filter = ${GAT562_Mesh_Watch13.build_src_filter} +; + +; +<../examples/simple_repeater> + +;[env:GAT562_Mesh_Watch13_room_server] +;extends = GAT562_Mesh_Watch13 +;build_flags = +; ${GAT562_Mesh_Watch13.build_flags} +; -D DISPLAY_CLASS=SSD1306Display +; -D ADVERT_NAME='"GAT562 Room"' +; -D ADVERT_LAT=0.0 +; -D ADVERT_LON=0.0 +; -D ADMIN_PASSWORD='"password"' +; -D ROOM_PASSWORD='"hello"' +;; -D MESH_PACKET_LOGGING=1 +;; -D MESH_DEBUG=1 +;build_src_filter = ${GAT562_Mesh_Watch13.build_src_filter} +; + +; +<../examples/simple_room_server> + +[env:GAT562_Mesh_Watch13_companion_radio_ble] +extends = GAT562_Mesh_Watch13 +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${GAT562_Mesh_Watch13.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D PIN_VIBRATION=36 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${GAT562_Mesh_Watch13.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${GAT562_Mesh_Watch13.lib_deps} + densaugeo/base64 @ ~1.4.0 + diff --git a/variants/gat562_mesh_watch13/target.cpp b/variants/gat562_mesh_watch13/target.cpp new file mode 100644 index 0000000000..87f65dd98c --- /dev/null +++ b/variants/gat562_mesh_watch13/target.cpp @@ -0,0 +1,50 @@ +#include +#include "target.h" +#include + +GAT56MeshWatch13Board board; + +#ifndef PIN_USER_BTN + #define PIN_USER_BTN (-1) +#endif + + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true, false, true); +#endif + + +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); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +EnvironmentSensorManager sensors; + + +bool radio_init() { + rtc_clock.begin(Wire); + return radio.std_init(&SPI); +} + +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(int8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/gat562_mesh_watch13/target.h b/variants/gat562_mesh_watch13/target.h new file mode 100644 index 0000000000..a9a71ce4c4 --- /dev/null +++ b/variants/gat562_mesh_watch13/target.h @@ -0,0 +1,31 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include + +#ifdef DISPLAY_CLASS + #include + extern DISPLAY_CLASS display; + #include + extern MomentaryButton user_btn; +#endif + +#ifdef PIN_VIBRATION + #include +#endif + +extern GAT56MeshWatch13Board 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(int8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/gat562_mesh_watch13/variant.cpp b/variants/gat562_mesh_watch13/variant.cpp new file mode 100644 index 0000000000..9f9c19d8ba --- /dev/null +++ b/variants/gat562_mesh_watch13/variant.cpp @@ -0,0 +1,44 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" +#include "nrf.h" + +const uint32_t g_ADigitalPinMap[] = +{ + // P0 + 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , + 8 , 9 , 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47 +}; + + +void initVariant() +{ + +} + diff --git a/variants/gat562_mesh_watch13/variant.h b/variants/gat562_mesh_watch13/variant.h new file mode 100644 index 0000000000..f79c9970a5 --- /dev/null +++ b/variants/gat562_mesh_watch13/variant.h @@ -0,0 +1,201 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_RAK4630_ +#define _VARIANT_RAK4630_ + +#define RAK4630 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +// define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + /* + * WisBlock Base GPIO definitions + */ + static const uint8_t WB_IO1 = 17; // SLOT_A SLOT_B + static const uint8_t WB_IO2 = 34; // SLOT_A SLOT_B + static const uint8_t WB_IO3 = 21; // SLOT_C + static const uint8_t WB_IO4 = 4; // SLOT_C + static const uint8_t WB_IO5 = 9; // SLOT_D + static const uint8_t WB_IO6 = 10; // SLOT_D + static const uint8_t WB_SW1 = 33; // IO_SLOT + static const uint8_t WB_A0 = 5; // IO_SLOT + static const uint8_t WB_A1 = 31; // IO_SLOT + static const uint8_t WB_I2C1_SDA = 13; // SENSOR_SLOT IO_SLOT + static const uint8_t WB_I2C1_SCL = 14; // SENSOR_SLOT IO_SLOT + static const uint8_t WB_I2C2_SDA = 24; // IO_SLOT + static const uint8_t WB_I2C2_SCL = 25; // IO_SLOT + static const uint8_t WB_SPI_CS = 26; // IO_SLOT + static const uint8_t WB_SPI_CLK = 3; // IO_SLOT + static const uint8_t WB_SPI_MISO = 29; // IO_SLOT + static const uint8_t WB_SPI_MOSI = 30; // IO_SLOT + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED (-1) +#define LED_BUILTIN PIN_LED +#define LED_CONN PIN_LED +#define LED_GREEN PIN_LED +#define LED_BLUE PIN_LED +#define LED_STATE_ON 1 // State when LED is litted + + +/* + * Buttons + */ +#define PIN_BUTTON1 (9) +#define PIN_BUTTON2 (10) +#define PIN_USER_BTN PIN_BUTTON1 +#define PIN_BACK_BTN PIN_BUTTON2 + + +// Analog pins +#define PIN_VBAT_READ (5) +#define ADC_MULTIPLIER (3 * 1.75 * 1.187 * 1000) + + +/* + * Analog pins + */ +#define PIN_A0 (5) //(3) +#define PIN_A1 (31) //(4) +#define PIN_A2 (28) +#define PIN_A3 (29) +#define PIN_A4 (30) +#define PIN_A5 (31) +#define PIN_A6 (0xff) +#define PIN_A7 (0xff) + + static const uint8_t A0 = PIN_A0; + static const uint8_t A1 = PIN_A1; + static const uint8_t A2 = PIN_A2; + static const uint8_t A3 = PIN_A3; + static const uint8_t A4 = PIN_A4; + static const uint8_t A5 = PIN_A5; + static const uint8_t A6 = PIN_A6; + static const uint8_t A7 = PIN_A7; +#define ADC_RESOLUTION 14 + +// Power management boot protection threshold (millivolts) +// Set to 0 to disable boot protection +#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV) +// LPCOMP wake configuration (voltage recovery from SYSTEMOFF) +// AIN3 = P0.05 = PIN_A0 / PIN_VBAT_READ +#define PWRMGT_LPCOMP_AIN 3 +#define PWRMGT_LPCOMP_REFSEL 4 // 5/8 VDD (~3.13-3.44V) + +// Other pins +#define PIN_AREF (2) +#define PIN_NFC1 (9) +#define PIN_NFC2 (10) + + static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +// TXD1 RXD1 on Base Board +#define PIN_SERIAL1_RX (15) +#define PIN_SERIAL1_TX (16) + +// TXD0 RXD0 on Base Board +#define PIN_SERIAL2_RX (19) +#define PIN_SERIAL2_TX (20) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 1 + +#define PIN_SPI_MISO (29) +#define PIN_SPI_MOSI (30) +#define PIN_SPI_SCK (3) + + static const uint8_t SS = 26; + static const uint8_t MOSI = PIN_SPI_MOSI; + static const uint8_t MISO = PIN_SPI_MISO; + static const uint8_t SCK = PIN_SPI_SCK; + +// LoRa radio module pins for RAK4631 + +#define SX126X_POWER_EN (37) +#define P_LORA_RESET (38) +#define P_LORA_NSS (42) +#define P_LORA_SCLK (43) +#define P_LORA_MOSI (44) +#define P_LORA_MISO (45) +#define P_LORA_BUSY (46) +#define P_LORA_DIO_1 (47) + +#define SX126X_DIO2_AS_RF_SWITCH true +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 2 + +#define PIN_WIRE_SDA (13) +#define PIN_WIRE_SCL (14) + +#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 + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES W25Q16JV_IQ +#define EXTERNAL_FLASH_USE_QSPI + + + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif diff --git a/variants/generic_espnow/platformio.ini b/variants/generic_espnow/platformio.ini index cdeed076a4..928d5315d1 100644 --- a/variants/generic_espnow/platformio.ini +++ b/variants/generic_espnow/platformio.ini @@ -69,7 +69,7 @@ lib_deps = extends = Generic_ESPNOW build_flags = ${Generic_ESPNOW.build_flags} - -D ADVERT_NAME='"Heltec Room"' + -D ADVERT_NAME='"Generic ESPNow Room"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' diff --git a/variants/heltec_t096/LoRaFEMControl.cpp b/variants/heltec_t096/LoRaFEMControl.cpp new file mode 100644 index 0000000000..9aeb83852b --- /dev/null +++ b/variants/heltec_t096/LoRaFEMControl.cpp @@ -0,0 +1,51 @@ +#include "LoRaFEMControl.h" +#include + +void LoRaFEMControl::init(void) +{ + pinMode(P_LORA_PA_POWER, OUTPUT); + digitalWrite(P_LORA_PA_POWER, HIGH); + delay(1); + pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT); + digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); + pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT); + digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH); + setLnaCanControl(true); +} + +void LoRaFEMControl::setSleepModeEnable(void) +{ + // shutdown the PA + digitalWrite(P_LORA_KCT8103L_PA_CSD, LOW); +} + +void LoRaFEMControl::setTxModeEnable(void) +{ + digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); + digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH); +} + +void LoRaFEMControl::setRxModeEnable(void) +{ + digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); + if (lna_enabled) { + digitalWrite(P_LORA_KCT8103L_PA_CTX, LOW); + } else { + digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH); + } +} + +void LoRaFEMControl::setRxModeEnableWhenMCUSleep(void) +{ + digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); + if (lna_enabled) { + digitalWrite(P_LORA_KCT8103L_PA_CTX, LOW); + } else { + digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH); + } +} + +void LoRaFEMControl::setLNAEnable(bool enabled) +{ + lna_enabled = enabled; +} diff --git a/variants/heltec_t096/LoRaFEMControl.h b/variants/heltec_t096/LoRaFEMControl.h new file mode 100644 index 0000000000..2c50b74289 --- /dev/null +++ b/variants/heltec_t096/LoRaFEMControl.h @@ -0,0 +1,21 @@ +#pragma once +#include + +class LoRaFEMControl +{ + public: + LoRaFEMControl() {} + virtual ~LoRaFEMControl() {} + void init(void); + void setSleepModeEnable(void); + void setTxModeEnable(void); + void setRxModeEnable(void); + void setRxModeEnableWhenMCUSleep(void); + void setLNAEnable(bool enabled); + bool isLnaCanControl(void) { return lna_can_control; } + void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + + private: + bool lna_enabled = false; + bool lna_can_control = false; +}; diff --git a/variants/heltec_t096/T096Board.cpp b/variants/heltec_t096/T096Board.cpp new file mode 100644 index 0000000000..550131571f --- /dev/null +++ b/variants/heltec_t096/T096Board.cpp @@ -0,0 +1,126 @@ +#include "T096Board.h" + +#include +#include + +#ifdef NRF52_POWER_MANAGEMENT +// Static configuration for power management +// Values come from variant.h defines +const PowerMgtConfig power_config = { + .lpcomp_ain_channel = PWRMGT_LPCOMP_AIN, + .lpcomp_refsel = PWRMGT_LPCOMP_REFSEL, + .voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK +}; + +void T096Board::initiateShutdown(uint8_t reason) { +#if ENV_INCLUDE_GPS == 1 + pinMode(PIN_GPS_EN, OUTPUT); + digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE); +#endif + variant_shutdown(); + + bool enable_lpcomp = (reason == SHUTDOWN_REASON_LOW_VOLTAGE || + reason == SHUTDOWN_REASON_BOOT_PROTECT); + pinMode(PIN_BAT_CTL, OUTPUT); + digitalWrite(PIN_BAT_CTL, enable_lpcomp ? HIGH : LOW); + + if (enable_lpcomp) { + configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel); + } + + enterSystemOff(reason); +} +#endif // NRF52_POWER_MANAGEMENT + +void T096Board::begin() { + NRF52Board::begin(); + +#ifdef NRF52_POWER_MANAGEMENT + // Boot voltage protection check (may not return if voltage too low) + checkBootVoltage(&power_config); +#endif + +#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL); +#endif + + Wire.begin(); + + pinMode(P_LORA_TX_LED, OUTPUT); + digitalWrite(P_LORA_TX_LED, LOW); + + periph_power.begin(); + loRaFEMControl.init(); + delay(1); +} + +void T096Board::onBeforeTransmit() { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + loRaFEMControl.setTxModeEnable(); +} + +void T096Board::onAfterTransmit() { + digitalWrite(P_LORA_TX_LED, LOW); //turn TX LED off + loRaFEMControl.setRxModeEnable(); +} + +uint16_t T096Board::getBattMilliVolts() { + int adcvalue = 0; + analogReadResolution(12); + analogReference(AR_INTERNAL_3_0); + pinMode(PIN_VBAT_READ, INPUT); + pinMode(PIN_BAT_CTL, OUTPUT); + digitalWrite(PIN_BAT_CTL, 1); + + delay(10); + adcvalue = analogRead(PIN_VBAT_READ); + digitalWrite(PIN_BAT_CTL, 0); + + return (uint16_t)((float)adcvalue * MV_LSB * 4.9); +} +void T096Board::variant_shutdown() { + nrf_gpio_cfg_default(PIN_VEXT_EN); + nrf_gpio_cfg_default(PIN_TFT_CS); + nrf_gpio_cfg_default(PIN_TFT_DC); + nrf_gpio_cfg_default(PIN_TFT_SDA); + nrf_gpio_cfg_default(PIN_TFT_SCL); + nrf_gpio_cfg_default(PIN_TFT_RST); + nrf_gpio_cfg_default(PIN_TFT_LEDA_CTL); + + nrf_gpio_cfg_default(PIN_LED); + + nrf_gpio_cfg_default(P_LORA_KCT8103L_PA_CSD); + nrf_gpio_cfg_default(P_LORA_KCT8103L_PA_CTX); + pinMode(P_LORA_PA_POWER, OUTPUT); + digitalWrite(P_LORA_PA_POWER, LOW); + + digitalWrite(PIN_BAT_CTL, LOW); + nrf_gpio_cfg_default(LORA_CS); + nrf_gpio_cfg_default(SX126X_DIO1); + nrf_gpio_cfg_default(SX126X_BUSY); + nrf_gpio_cfg_default(SX126X_RESET); + + nrf_gpio_cfg_default(PIN_SPI_MISO); + nrf_gpio_cfg_default(PIN_SPI_MOSI); + nrf_gpio_cfg_default(PIN_SPI_SCK); + + // nrf_gpio_cfg_default(PIN_GPS_PPS); + nrf_gpio_cfg_default(PIN_GPS_RESET); + nrf_gpio_cfg_default(PIN_GPS_EN); + nrf_gpio_cfg_default(PIN_GPS_RX); + nrf_gpio_cfg_default(PIN_GPS_TX); +} + +void T096Board::powerOff() { +#if ENV_INCLUDE_GPS == 1 + pinMode(PIN_GPS_EN, OUTPUT); + digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE); +#endif + loRaFEMControl.setSleepModeEnable(); + variant_shutdown(); + sd_power_system_off(); +} + +const char* T096Board::getManufacturerName() const { + return "Heltec T096"; +} \ No newline at end of file diff --git a/variants/heltec_t096/T096Board.h b/variants/heltec_t096/T096Board.h new file mode 100644 index 0000000000..d1e3bdfdee --- /dev/null +++ b/variants/heltec_t096/T096Board.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include +#include "LoRaFEMControl.h" + +class T096Board : public NRF52BoardDCDC { +protected: +#ifdef NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; +#endif + void variant_shutdown(); + +public: + RefCountedDigitalPin periph_power; + LoRaFEMControl loRaFEMControl; + + T096Board() :periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE), NRF52Board("T096_OTA") {} + void begin(); + + void onBeforeTransmit(void) override; + void onAfterTransmit(void) override; + uint16_t getBattMilliVolts() override; + const char* getManufacturerName() const override ; + void powerOff() override; +}; diff --git a/variants/heltec_t096/platformio.ini b/variants/heltec_t096/platformio.ini new file mode 100644 index 0000000000..19b05f3ce4 --- /dev/null +++ b/variants/heltec_t096/platformio.ini @@ -0,0 +1,148 @@ +[Heltec_t096] +extends = nrf52_base +board = heltec_t096 +board_build.ldscript = boards/nrf52840_s140_v6.ld +build_flags = ${nrf52_base.build_flags} + ${sensor_base.build_flags} + -I lib/nrf52/s140_nrf52_6.1.1_API/include + -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52 + -I variants/heltec_t096 + -I src/helpers/ui + -D HELTEC_T096 + -D NRF52_POWER_MANAGEMENT + -D P_LORA_DIO_1=21 + -D P_LORA_NSS=5 + -D P_LORA_RESET=16 + -D P_LORA_BUSY=19 + -D P_LORA_SCLK=40 + -D P_LORA_MISO=14 + -D P_LORA_MOSI=11 + -D P_LORA_TX_LED=28 + -D P_LORA_PA_POWER=30 ;VFEM_Ctrl -LDO power enable + -D P_LORA_KCT8103L_PA_CSD=12 + -D P_LORA_KCT8103L_PA_CTX=41 + -D LORA_TX_POWER=9 ; 9dBm + ~13dB KCT8103L gain = ~22dBm output + -D MAX_LORA_TX_POWER=22 ; Max SX1262 output -> ~28dBm at antenna + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D PIN_VEXT_EN=26 ; Vext is connected to VDD which is also connected to TFT & GPS + -D PIN_VEXT_EN_ACTIVE=HIGH + -D PIN_GPS_RX=25 + -D PIN_GPS_TX=23 + -D PIN_GPS_EN=GPS_EN + -D PIN_GPS_EN_ACTIVE=LOW + -D PIN_GPS_RESET=GPS_RESET + -D PIN_GPS_RESET_ACTIVE=LOW + -D GPS_BAUD_RATE=115200 + -D PIN_VBAT_READ=BATTERY_PIN + -D PIN_BAT_CTL=47 + -D DISPLAY_CLASS=ST7735Display + -D DISPLAY_ROTATION=1 +build_src_filter = ${nrf52_base.build_src_filter} + + + + + +<../variants/heltec_t096> + + + + +lib_deps = + ${nrf52_base.lib_deps} + ${sensor_base.lib_deps} + adafruit/Adafruit ST7735 and ST7789 Library @ ^1.11.0 +debug_tool = jlink +upload_protocol = nrfutil + +[env:Heltec_t096_repeater] +extends = Heltec_t096 +build_src_filter = ${Heltec_t096.build_src_filter} + +<../examples/simple_repeater> + +build_flags = + ${Heltec_t096.build_flags} + -D ADVERT_NAME='"Heltec_t096 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + +[env:Heltec_t096_repeater_bridge_rs232] +extends = Heltec_t096 +build_flags = + ${Heltec_t096.build_flags} + -D ADVERT_NAME='"RS232 Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 + -D WITH_RS232_BRIDGE=Serial2 + -D WITH_RS232_BRIDGE_RX=9 + -D WITH_RS232_BRIDGE_TX=10 +; -D BRIDGE_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_t096.build_src_filter} + + + +<../examples/simple_repeater> + +[env:Heltec_t096_room_server] +extends = Heltec_t096 +build_src_filter = ${Heltec_t096.build_src_filter} + +<../examples/simple_room_server> +build_flags = + ${Heltec_t096.build_flags} + -D ADVERT_NAME='"Heltec_t096 Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + +[env:Heltec_t096_companion_radio_ble] +extends = Heltec_t096 +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${Heltec_t096.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D ENV_INCLUDE_GPS=1 ; enable the GPS page in UI +; -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_t096.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_t096.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Heltec_t096_companion_radio_usb] +extends = Heltec_t096 +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${Heltec_t096.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 +; -D BLE_PIN_CODE=123456 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_t096.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_t096.lib_deps} + densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/variants/heltec_t096/target.cpp b/variants/heltec_t096/target.cpp new file mode 100644 index 0000000000..09609b878f --- /dev/null +++ b/variants/heltec_t096/target.cpp @@ -0,0 +1,64 @@ +#include "target.h" + +#include +#include + +#ifdef ENV_INCLUDE_GPS +#include +#endif + +T096Board board; + +#if defined(P_LORA_SCLK) +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); +#else +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if ENV_INCLUDE_GPS +#include +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power); +EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else +EnvironmentSensorManager sensors; +#endif + +#ifdef DISPLAY_CLASS +DISPLAY_CLASS display(&board.periph_power); +MomentaryButton user_btn(PIN_USER_BTN, 1000, true); +#endif + +bool radio_init() { + rtc_clock.begin(Wire); + +#if defined(P_LORA_SCLK) + return radio.std_init(&SPI); +#else + return radio.std_init(); +#endif +} + +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(int8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/heltec_t096/target.h b/variants/heltec_t096/target.h new file mode 100644 index 0000000000..8c704b23ac --- /dev/null +++ b/variants/heltec_t096/target.h @@ -0,0 +1,33 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include +#include + +#ifdef DISPLAY_CLASS +#include +#include +#else +#include "helpers/ui/NullDisplayDriver.h" +#endif + +extern T096Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +#ifdef DISPLAY_CLASS +extern DISPLAY_CLASS display; +extern MomentaryButton user_btn; +#endif + +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(int8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/heltec_t096/variant.cpp b/variants/heltec_t096/variant.cpp new file mode 100644 index 0000000000..2bca56a1f9 --- /dev/null +++ b/variants/heltec_t096/variant.cpp @@ -0,0 +1,15 @@ +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + 0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47 +}; + +void initVariant() +{ + pinMode(PIN_USER_BTN, INPUT); +} diff --git a/variants/heltec_t096/variant.h b/variants/heltec_t096/variant.h new file mode 100644 index 0000000000..c240c1f27b --- /dev/null +++ b/variants/heltec_t096/variant.h @@ -0,0 +1,132 @@ +/* + * variant.h + * Copyright (C) 2023 Seeed K.K. + * MIT License + */ + +#pragma once + +#include "WVariant.h" + +//////////////////////////////////////////////////////////////////////////////// +// Low frequency clock source + +#define USE_LFXO // 32.768 kHz crystal oscillator +#define VARIANT_MCK (64000000ul) + +#define WIRE_INTERFACES_COUNT (2) + +//////////////////////////////////////////////////////////////////////////////// +// Power + +#define NRF_APM +#define PIN_3V3_EN (38) + +#define BATTERY_PIN (3) +#define ADC_MULTIPLIER (4.90F) + +#define ADC_RESOLUTION (14) +#define BATTERY_SENSE_RES (12) + +#define AREF_VOLTAGE (3.0) +#define MV_LSB (3000.0F / 4096.0F) // 12-bit ADC with 3.0V input range + +// Power management boot protection threshold (millivolts) +// Set to 0 to disable boot protection +#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV) +// LPCOMP wake configuration (voltage recovery from SYSTEMOFF) +// AIN1 = P0.03 = BATTERY_PIN / PIN_VBAT_READ +#define PWRMGT_LPCOMP_AIN 1 +#define PWRMGT_LPCOMP_REFSEL 1 // 2/8 VDD (~3.68-4.04V) + +//////////////////////////////////////////////////////////////////////////////// +// Number of pins + +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (1) +#define NUM_ANALOG_OUTPUTS (0) + + +// I2C pin definition + +#define PIN_WIRE_SDA (0 + 7) +#define PIN_WIRE_SCL (0 + 8) + +// I2C bus 1 +// Available on header pins, for general use +#define PIN_WIRE1_SDA (0 + 4) +#define PIN_WIRE1_SCL (0 + 27) + +//////////////////////////////////////////////////////////////////////////////// +// Builtin LEDs + +#define LED_BUILTIN (28) +#define PIN_LED LED_BUILTIN +#define LED_RED LED_BUILTIN +#define LED_BLUE (-1) // No blue led, prevents Bluefruit flashing the green LED during advertising +#define LED_PIN LED_BUILTIN + +#define LED_STATE_ON 1 + +// #define PIN_NEOPIXEL (-1) +// #define NEOPIXEL_NUM (2) + +//////////////////////////////////////////////////////////////////////////////// +// Builtin buttons + +#define PIN_BUTTON1 (32 + 10) +#define BUTTON_PIN PIN_BUTTON1 + +// #define PIN_BUTTON2 (11) +// #define BUTTON_PIN2 PIN_BUTTON2 + +#define PIN_USER_BTN BUTTON_PIN + +//////////////////////////////////////////////////////////////////////////////// +// Lora + +#define USE_SX1262 +#define LORA_CS (0 + 5) +#define SX126X_DIO1 (0 + 21) +#define SX126X_BUSY (0 + 19) +#define SX126X_RESET (0 + 16) +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +//////////////////////////////////////////////////////////////////////////////// +// SPI pin definition + +#define SPI_INTERFACES_COUNT (2) + +#define PIN_SPI_MISO (0 + 14) +#define PIN_SPI_MOSI (0 + 11) +#define PIN_SPI_SCK (32 + 8) +#define PIN_SPI_NSS LORA_CS + +#define PIN_SPI1_MISO (-1) +#define PIN_SPI1_MOSI (0+17) +#define PIN_SPI1_SCK (0+20) + +//////////////////////////////////////////////////////////////////////////////// +// GPS + +#define GPS_EN (0 + 6) +#define GPS_RESET (32 + 14) + +#define PIN_SERIAL1_RX (0 + 23) +#define PIN_SERIAL1_TX (0 + 25) + +#define PIN_SERIAL2_RX (0 + 9) +#define PIN_SERIAL2_TX (0 + 10) + +//////////////////////////////////////////////////////////////////////////////// +// TFT +#define PIN_TFT_SCL (0 + 20) +#define PIN_TFT_SDA (0 + 17) +#define PIN_TFT_RST (0 + 13) +// #define PIN_TFT_VDD_CTL (0 + 26) +#define PIN_TFT_LEDA_CTL (32 + 12) +#define PIN_TFT_LEDA_CTL_ACTIVE LOW +#define PIN_TFT_CS (0 + 22) +#define PIN_TFT_DC (0 + 15) diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index f8cc936083..99f6f7e13c 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -178,7 +178,7 @@ build_flags = ${Heltec_lora32_v2.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display - -D MAX_CONTACTS=160 + -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' diff --git a/variants/heltec_v3/HeltecV3Board.h b/variants/heltec_v3/HeltecV3Board.h index afdaf6398a..ba22a7f2b9 100644 --- a/variants/heltec_v3/HeltecV3Board.h +++ b/variants/heltec_v3/HeltecV3Board.h @@ -11,6 +11,9 @@ #ifndef PIN_ADC_CTRL // set in platformio.ini for Heltec Wireless Tracker (2) #define PIN_ADC_CTRL 37 #endif +#ifndef ADC_MULTIPLIER //default ADC multiplier + #define ADC_MULTIPLIER 5.42 +#endif #define PIN_ADC_CTRL_ACTIVE LOW #define PIN_ADC_CTRL_INACTIVE HIGH @@ -88,7 +91,7 @@ class HeltecV3Board : public ESP32Board { digitalWrite(PIN_ADC_CTRL, !adc_active_state); - return (5.42 * (3.3 / 1024.0) * raw) * 1000; + return (ADC_MULTIPLIER * (3.3 / 1024.0) * raw) * 1000; } const char* getManufacturerName() const override { diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 8186f2d4b2..49580d2ecf 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -7,31 +7,15 @@ void HeltecV4Board::begin() { pinMode(PIN_ADC_CTRL, OUTPUT); digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive - // Set up digital GPIO registers before releasing RTC hold. The hold latches - // the pad state including function select, so register writes accumulate - // without affecting the pad. On hold release, all changes apply atomically - // (IO MUX switches to digital GPIO with output already HIGH — no glitch). - pinMode(P_LORA_PA_POWER, OUTPUT); - digitalWrite(P_LORA_PA_POWER,HIGH); - rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_POWER); - - pinMode(P_LORA_PA_EN, OUTPUT); - digitalWrite(P_LORA_PA_EN,HIGH); - rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_EN); - pinMode(P_LORA_PA_TX_EN, OUTPUT); - digitalWrite(P_LORA_PA_TX_EN,LOW); - - esp_reset_reason_t reason = esp_reset_reason(); - if (reason != ESP_RST_DEEPSLEEP) { - delay(1); // GC1109 startup time after cold power-on - } + loRaFEMControl.init(); periph_power.begin(); + esp_reset_reason_t reason = esp_reset_reason(); if (reason == ESP_RST_DEEPSLEEP) { long wakeup_source = esp_sleep_get_ext1_wakeup_status(); if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep) startup_reason = BD_STARTUP_RX_PACKET; - } + } rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); @@ -40,12 +24,12 @@ void HeltecV4Board::begin() { void HeltecV4Board::onBeforeTransmit(void) { digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on - digitalWrite(P_LORA_PA_TX_EN,HIGH); + loRaFEMControl.setTxModeEnable(); } void HeltecV4Board::onAfterTransmit(void) { digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off - digitalWrite(P_LORA_PA_TX_EN,LOW); + loRaFEMControl.setRxModeEnable(); } void HeltecV4Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { @@ -57,9 +41,7 @@ void HeltecV4Board::begin() { rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - // Hold GC1109 FEM pins during sleep to keep LNA active for RX wake - rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER); - rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_EN); + loRaFEMControl.setRxModeEnableWhenMCUSleep();//It also needs to be enabled in receive mode if (pin_wake_btn < 0) { esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet @@ -95,9 +77,9 @@ void HeltecV4Board::begin() { } const char* HeltecV4Board::getManufacturerName() const { - #ifdef HELTEC_LORA_V4_TFT - return "Heltec V4 TFT"; - #else - return "Heltec V4 OLED"; - #endif +#ifdef HELTEC_LORA_V4_TFT + return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 TFT" : "Heltec V4 TFT"; +#else + return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 OLED" : "Heltec V4 OLED"; +#endif } diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index 745e8d8f3d..4d5ee46155 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -4,12 +4,12 @@ #include #include #include - +#include "LoRaFEMControl.h" class HeltecV4Board : public ESP32Board { public: RefCountedDigitalPin periph_power; - + LoRaFEMControl loRaFEMControl; HeltecV4Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { } void begin(); diff --git a/variants/heltec_v4/LoRaFEMControl.cpp b/variants/heltec_v4/LoRaFEMControl.cpp new file mode 100644 index 0000000000..168185c31f --- /dev/null +++ b/variants/heltec_v4/LoRaFEMControl.cpp @@ -0,0 +1,108 @@ +#include "LoRaFEMControl.h" +#include +#include +#include + +void LoRaFEMControl::init(void) +{ + // Power on FEM LDO — set registers before releasing RTC hold for + // atomic transition (no glitch on deep sleep wake). + pinMode(P_LORA_PA_POWER, OUTPUT); + digitalWrite(P_LORA_PA_POWER, HIGH); + rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_POWER); + + esp_reset_reason_t reason = esp_reset_reason(); + if (reason != ESP_RST_DEEPSLEEP) { + delay(1); // FEM startup time after cold power-on + } + + // Auto-detect FEM type via shared GPIO2 default pull level. + // GC1109 CSD: internal pull-down → reads LOW + // KCT8103L CSD: internal pull-up → reads HIGH + rtc_gpio_hold_dis((gpio_num_t)P_LORA_KCT8103L_PA_CSD); + pinMode(P_LORA_KCT8103L_PA_CSD, INPUT); + delay(1); + if(digitalRead(P_LORA_KCT8103L_PA_CSD)==HIGH) { + // FEM is KCT8103L (V4.3) + fem_type= KCT8103L_PA; + pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT); + digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); + rtc_gpio_hold_dis((gpio_num_t)P_LORA_KCT8103L_PA_CTX); + pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT); + digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH); + setLnaCanControl(true); + } else { + // FEM is GC1109 (V4.2) + fem_type= GC1109_PA; + pinMode(P_LORA_GC1109_PA_EN, OUTPUT); + digitalWrite(P_LORA_GC1109_PA_EN, HIGH); + pinMode(P_LORA_GC1109_PA_TX_EN, OUTPUT); + digitalWrite(P_LORA_GC1109_PA_TX_EN, LOW); + } +} + +void LoRaFEMControl::setSleepModeEnable(void) +{ + if(fem_type==GC1109_PA) { + /* + * Do not switch the power on and off frequently. + * After turning off P_LORA_PA_EN, the power consumption has dropped to the uA level. + */ + digitalWrite(P_LORA_GC1109_PA_EN, LOW); + digitalWrite(P_LORA_GC1109_PA_TX_EN, LOW); + } else if(fem_type==KCT8103L_PA) { + // shutdown the PA + digitalWrite(P_LORA_KCT8103L_PA_CSD, LOW); + } +} + +void LoRaFEMControl::setTxModeEnable(void) +{ + if(fem_type==GC1109_PA) { + digitalWrite(P_LORA_GC1109_PA_EN, HIGH); // CSD=1: Chip enabled + digitalWrite(P_LORA_GC1109_PA_TX_EN, HIGH); // CPS: 1=full PA, 0=bypass (for RX, CPS is don't care) + } else if(fem_type==KCT8103L_PA) { + digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); + digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH); + } +} + +void LoRaFEMControl::setRxModeEnable(void) +{ + if(fem_type==GC1109_PA) { + digitalWrite(P_LORA_GC1109_PA_EN, HIGH); // CSD=1: Chip enabled + digitalWrite(P_LORA_GC1109_PA_TX_EN, LOW); + } else if(fem_type==KCT8103L_PA) { + digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); + if(lna_enabled) { + digitalWrite(P_LORA_KCT8103L_PA_CTX, LOW); // LNA on + } else { + digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH); // LNA bypass + } + } +} + +void LoRaFEMControl::setRxModeEnableWhenMCUSleep(void) +{ + digitalWrite(P_LORA_PA_POWER, HIGH); + rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER); + if(fem_type==GC1109_PA) { + digitalWrite(P_LORA_GC1109_PA_EN, HIGH); + rtc_gpio_hold_en((gpio_num_t)P_LORA_GC1109_PA_EN); + gpio_pulldown_en((gpio_num_t)P_LORA_GC1109_PA_TX_EN); + } else if(fem_type==KCT8103L_PA) { + digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); + rtc_gpio_hold_en((gpio_num_t)P_LORA_KCT8103L_PA_CSD); + if(lna_enabled) { + digitalWrite(P_LORA_KCT8103L_PA_CTX, LOW); // LNA on + } else { + digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH); // LNA bypass + } + rtc_gpio_hold_en((gpio_num_t)P_LORA_KCT8103L_PA_CTX); + } +} + +void LoRaFEMControl::setLNAEnable(bool enabled) +{ + lna_enabled = enabled; +} diff --git a/variants/heltec_v4/LoRaFEMControl.h b/variants/heltec_v4/LoRaFEMControl.h new file mode 100644 index 0000000000..7545296503 --- /dev/null +++ b/variants/heltec_v4/LoRaFEMControl.h @@ -0,0 +1,29 @@ +#pragma once +#include + +typedef enum { + GC1109_PA, + KCT8103L_PA, + OTHER_FEM_TYPES +} LoRaFEMType; + +class LoRaFEMControl +{ + public: + LoRaFEMControl(){ } + virtual ~LoRaFEMControl(){ } + void init(void); + void setSleepModeEnable(void); + void setTxModeEnable(void); + void setRxModeEnable(void); + void setRxModeEnableWhenMCUSleep(void); + void setLNAEnable(bool enabled); + bool isLnaCanControl(void) { return lna_can_control; } + void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + LoRaFEMType getFEMType(void) const { return fem_type; } + private: + LoRaFEMType fem_type=OTHER_FEM_TYPES; + bool lna_enabled=true; + bool lna_can_control=false; +}; + diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index 6a1ad9fce5..6f6bf2b538 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -18,9 +18,11 @@ build_flags = -D P_LORA_SCLK=9 -D P_LORA_MISO=11 -D P_LORA_MOSI=10 - -D P_LORA_PA_POWER=7 ; VFEM_Ctrl - Power on GC1109 - -D P_LORA_PA_EN=2 ; PA CSD - Enable GC1109 - -D P_LORA_PA_TX_EN=46 ; PA CPS - GC1109 TX PA full(High) / bypass(Low) + -D P_LORA_PA_POWER=7 ; // VFEM_Ctrl -LDO power enable + -D P_LORA_GC1109_PA_EN=2 ; // CSD - GC1109 chip enable (HIGH=on) + -D P_LORA_GC1109_PA_TX_EN=46 ;// CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass) + -D P_LORA_KCT8103L_PA_CSD=2 + -D P_LORA_KCT8103L_PA_CTX=5 -D PIN_USER_BTN=0 -D PIN_VEXT_EN=36 -D PIN_VEXT_EN_ACTIVE=HIGH @@ -205,6 +207,7 @@ build_flags = -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SSD1306Display -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' @@ -368,6 +371,7 @@ build_flags = -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=ST7789LCDDisplay -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index ff554ab8aa..d6f43d75f8 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -22,6 +22,7 @@ build_flags = ;-D PIN_BOARD_SCL=18 ; same GPIO as P_LORA_TX_LED -D PIN_USER_BTN=0 -D PIN_VEXT_EN=45 + -D ADC_MULTIPLIER=8.4 -D PIN_VBAT_READ=20 -D PIN_ADC_CTRL=19 -D SX126X_DIO2_AS_RF_SWITCH=true diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini index cf17ae8bd0..7c8453077f 100644 --- a/variants/lilygo_tbeam_1w/platformio.ini +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -154,6 +154,7 @@ build_flags = -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' -D WIFI_PWD='"mypwd"' diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 7e1330e6cc..3641f12705 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -131,7 +131,7 @@ extends = LilyGo_TLora_V2_1_1_6 build_flags = ${LilyGo_TLora_V2_1_1_6.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=160 + -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D WIFI_SSID='"ssid"' -D WIFI_PWD='"password"' diff --git a/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h b/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h index 6858a1062f..4fa5cd41ea 100644 --- a/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h +++ b/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h @@ -63,7 +63,7 @@ class MinewsemiME25LS01Board : public NRF52BoardDCDC { digitalWrite(LED_PIN, LOW); #endif #ifdef BUTTON_PIN - nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_HIGH); + nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); #endif sd_power_system_off(); } diff --git a/variants/minewsemi_me25ls01/platformio.ini b/variants/minewsemi_me25ls01/platformio.ini index fd9c3819fe..dacd8d34e5 100644 --- a/variants/minewsemi_me25ls01/platformio.ini +++ b/variants/minewsemi_me25ls01/platformio.ini @@ -21,7 +21,6 @@ build_flags = ${nrf52840_me25ls01.build_flags} -I variants/minewsemi_me25ls01 -D me25ls01 -D PIN_USER_BTN=27 - -D USER_BTN_PRESSED=HIGH -D PIN_STATUS_LED=39 -D P_LORA_TX_LED=22 -D RADIO_CLASS=CustomLR1110 diff --git a/variants/muziworks_r1_neo/R1NeoBoard.cpp b/variants/muziworks_r1_neo/R1NeoBoard.cpp new file mode 100644 index 0000000000..616d255a8c --- /dev/null +++ b/variants/muziworks_r1_neo/R1NeoBoard.cpp @@ -0,0 +1,66 @@ +#include +#include + +#include "R1NeoBoard.h" + +#ifdef NRF52_POWER_MANAGEMENT +// Static configuration for power management +// Values set in variant.h defines +const PowerMgtConfig power_config = { + .lpcomp_ain_channel = PWRMGT_LPCOMP_AIN, + .lpcomp_refsel = PWRMGT_LPCOMP_REFSEL, + .voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK +}; + +void R1NeoBoard::initiateShutdown(uint8_t reason) { + // Disable LoRa module power before shutdown + MESH_DEBUG_PRINTLN("R1Neo: shutting down"); + digitalWrite(SX126X_POWER_EN, LOW); + + if (reason == SHUTDOWN_REASON_LOW_VOLTAGE || + reason == SHUTDOWN_REASON_BOOT_PROTECT) { + configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel); + } + + enterSystemOff(reason); +} +#endif // NRF52_POWER_MANAGEMENT + +void R1NeoBoard::begin() { + // R1 Neo peculiarity: tell DCDC converter to stay powered. + // Must be done as soon as practical during boot. + + pinMode(PIN_DCDC_EN_MCU_HOLD, OUTPUT); + digitalWrite(PIN_DCDC_EN_MCU_HOLD, HIGH); + + // R1 Neo peculiarity: Tell I/O Controller device is on + // Enables passthrough of buttons and LEDs + + pinMode(PIN_SOFT_SHUTDOWN, OUTPUT); + digitalWrite(PIN_SOFT_SHUTDOWN, HIGH); + + NRF52BoardDCDC::begin(); + + // button is active high and passed through from I/O controller + pinMode(PIN_USER_BTN, INPUT); + + pinMode(PIN_BUZZER, OUTPUT); + digitalWrite(PIN_BUZZER, LOW); + + // battery pins + pinMode(PIN_BAT_CHG, INPUT); + pinMode(PIN_VBAT_READ, INPUT); + + Wire.setPins(PIN_WIRE_SDA, PIN_WIRE_SCL); + + Wire.begin(); + + pinMode(SX126X_POWER_EN, OUTPUT); +#ifdef NRF52_POWER_MANAGEMENT + // Boot voltage protection check (may not return if voltage too low) + // We need to call this after we configure SX126X_POWER_EN as output but before we pull high + checkBootVoltage(&power_config); +#endif + digitalWrite(SX126X_POWER_EN, HIGH); + delay(10); // give sx1262 some time to power up +} diff --git a/variants/muziworks_r1_neo/R1NeoBoard.h b/variants/muziworks_r1_neo/R1NeoBoard.h new file mode 100644 index 0000000000..c27ea23212 --- /dev/null +++ b/variants/muziworks_r1_neo/R1NeoBoard.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include "NullDisplayDriver.h" +#include "MomentaryButton.h" + +#define DISPLAY_CLASS NullDisplayDriver + +class R1NeoBoard : public NRF52BoardDCDC { +protected: +#ifdef NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; +#endif + +public: + R1NeoBoard() : NRF52Board("R1NEO_OTA") {} + void begin(); + +#if defined(P_LORA_TX_LED) + void onBeforeTransmit() override { + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + #if defined(LED_BLUE) + // turn off that annoying blue LED before transmitting + digitalWrite(LED_BLUE, LOW); + #endif + } + void onAfterTransmit() override { + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + #if defined(LED_BLUE) + // do it after transmitting too, just in case + digitalWrite(LED_BLUE, LOW); + #endif + } +#endif + + #define BATTERY_SAMPLES 8 + + uint16_t getBattMilliVolts() override { + MESH_DEBUG_PRINTLN("R1Neo: Sampling battery"); + 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) / 4096; + } + + const char* getManufacturerName() const override { + return "muzi works R1 Neo"; + } +}; diff --git a/variants/muziworks_r1_neo/platformio.ini b/variants/muziworks_r1_neo/platformio.ini new file mode 100644 index 0000000000..39ef8728fd --- /dev/null +++ b/variants/muziworks_r1_neo/platformio.ini @@ -0,0 +1,132 @@ +[R1Neo] +extends = nrf52_base +board = rak4631 +board_check = true +build_flags = ${nrf52_base.build_flags} + ${sensor_base.build_flags} + -I variants/muziworks_r1_neo + -I src/helpers/ui + -D R1Neo + -D NRF52_POWER_MANAGEMENT + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D PIN_BUZZER=3 + -D PIN_USER_BTN=26 + -D USER_BTN_PRESSED=HIGH + -D PIN_GPS_TX=25 + -D PIN_GPS_RX=24 + -D PIN_GPS_EN=33 +build_src_filter = ${nrf52_base.build_src_filter} + +<../variants/muziworks_r1_neo> + + + + + + +lib_deps = + ${nrf52_base.lib_deps} + ${sensor_base.lib_deps} + sparkfun/SparkFun u-blox GNSS Arduino Library@^2.2.27 + +[env:R1Neo_repeater] +extends = R1Neo +build_flags = + ${R1Neo.build_flags} + -D ADVERT_NAME='"R1 Neo Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${R1Neo.build_src_filter} + +<../examples/simple_repeater> + +[env:R1Neo_room_server] +extends = R1Neo +build_flags = + ${R1Neo.build_flags} + -D ADVERT_NAME='"R1 Neo Test Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${R1Neo.build_src_filter} + +<../examples/simple_room_server> + +[env:R1Neo_companion_radio_usb] +extends = R1Neo +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${R1Neo.build_flags} + -I examples/companion_radio/ui-orig + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 +; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 +; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 +build_src_filter = ${R1Neo.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-orig/*.cpp> +lib_deps = + ${R1Neo.lib_deps} + densaugeo/base64 @ ~1.4.0 + end2endzone/NonBlockingRTTTL@^1.3.0 + +[env:R1Neo_companion_radio_ble] +extends = R1Neo +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${R1Neo.build_flags} + -I examples/companion_radio/ui-orig + -D ENV_INCLUDE_GPS=1 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${R1Neo.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-orig/*.cpp> +lib_deps = + ${R1Neo.lib_deps} + ${rak4631.lib_deps} + densaugeo/base64 @ ~1.4.0 + end2endzone/NonBlockingRTTTL@^1.3.0 + +[env:R1Neo_terminal_chat] +extends = R1Neo +build_flags = + ${R1Neo.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${R1Neo.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> +lib_deps = + ${R1Neo.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:R1Neo_sensor] +extends = R1Neo +build_flags = + ${R1Neo.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"R1 Neo Sensor"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' +; -D MESH_PACKET_LOGGING=1 + -D MESH_DEBUG=1 +build_src_filter = ${R1Neo.build_src_filter} + +<../examples/simple_sensor> diff --git a/variants/muziworks_r1_neo/target.cpp b/variants/muziworks_r1_neo/target.cpp new file mode 100644 index 0000000000..de889d4355 --- /dev/null +++ b/variants/muziworks_r1_neo/target.cpp @@ -0,0 +1,47 @@ +#include +#include "target.h" +#include + +R1NeoBoard board; + +DISPLAY_CLASS display; + +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); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif + +bool radio_init() { + rtc_clock.begin(Wire); + return radio.std_init(&SPI); +} + +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(int8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/muziworks_r1_neo/target.h b/variants/muziworks_r1_neo/target.h new file mode 100644 index 0000000000..53d7e03bec --- /dev/null +++ b/variants/muziworks_r1_neo/target.h @@ -0,0 +1,22 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include + +extern R1NeoBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; +extern DISPLAY_CLASS display; +extern MomentaryButton user_btn; + +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(int8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/muziworks_r1_neo/variant.cpp b/variants/muziworks_r1_neo/variant.cpp new file mode 100644 index 0000000000..bb3d1fdd47 --- /dev/null +++ b/variants/muziworks_r1_neo/variant.cpp @@ -0,0 +1,92 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" +#include "nrf.h" + +const uint32_t g_ADigitalPinMap[] = +{ + // P0 + 0, // P0.00 (NC) (XTAL) + 1, // P0.01 (NC) (XTAL) + 2, // P0.02 (30) GPS_PPS + 3, // P0.03 (29) BUZZER_DRIVE + 4, // P0.04 (41) NC + 5, // P0.05 (40) NC + 6, // P0.06 (NC) NOT_PRESENT + 7, // P0.07 (NC) (TRACECLK) + 8, // P0.08 (NC) NOT_PRESENT + 9, // P0.09 (13) NC + 10, // P0.10 (12) NC + 11, // P0.11 (NC) NOT_PRESENT + 12, // P0.12 (NC) NOT_PRESENT + 13, // P0.13 (04) DCDC_EN_MCU_HOLD + 14, // P0.14 (05) NC + 15, // P0.15 (06) NC + 16, // P0.16 (07) NC + 17, // P0.17 (08) NC + 18, // P0.18 (17) !RESET + 19, // P0.19 (09) RTC_SDA + 20, // P0.20 (10) RTC_SCL + 21, // P0.21 (11) NC + 22, // P0.22 (NC) NOT_PRESENT + 23, // P0.23 (NC) NOT_PRESENT + 24, // P0.24 (23) UART_GPS_RX + 25, // P0.25 (24) UART_GPS_TX + 26, // P0.26 (26) BTN_OK/USR_BTN_PROCESSED + 27, // P0.27 (NC) NOT_PRESENT + 28, // P0.28 (31) BLU_LED_RAK + 29, // P0.29 (32) SOFT_SHUTDOWN_SIGNAL + 30, // P0.30 (33) MCU_SIGNAL + 31, // P0.31 (39) ADC_VBAT + + // P1 + 32, // P1.00 (NC) NOT_PRESENT + 33, // P1.01 (25) GPS_EN + 34, // P1.02 (26) BAT_CHG_STATUS + 35, // P1.03 (27) NC + 36, // P1.04 (28) GRN_LED_RAK + 37, // P1.05 (SX) SX126X_POWER_EN + 38, // P1.06 (SX) P_LORA_RESET + 39, // P1.07 (NC) NOT_PRESENT + 40, // P1.08 (NC) NOT_PRESENT + 41, // P1.09 (NC) NOT_PRESENT + 42, // P1.10 (SX) P_LORA_NSS + 43, // P1.11 (SX) P_LORA_SCLK + 44, // P1.12 (SX) P_LORA_MOSI + 45, // P1.13 (SX) P_LORA_MISO + 46, // P1.14 (SX) P_LORA_BUSY + 47 // P1.15 (SX) P_LORA_DIO_1 +}; + + +void initVariant() +{ + // Red & Green LEDs - enable & turn off + pinMode(LED_GREEN, OUTPUT); + ledOff(LED_GREEN); + + pinMode(LED_BLUE, OUTPUT); + ledOff(LED_BLUE); + + pinMode(PIN_GPS_EN, OUTPUT); +} diff --git a/variants/muziworks_r1_neo/variant.h b/variants/muziworks_r1_neo/variant.h new file mode 100644 index 0000000000..92046d61af --- /dev/null +++ b/variants/muziworks_r1_neo/variant.h @@ -0,0 +1,183 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_R1NEO_ +#define _VARIANT_R1NEO_ + +#define RAK4630 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +// define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +/* Number of pins defined in PinDescription array */ +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (8) +#define NUM_ANALOG_OUTPUTS (0) + +/* R1Neo peculiarities */ +#define PIN_DCDC_EN_MCU_HOLD (13) // P0.13 (04) DCDC_EN_MCU_HOLD +#define PIN_SOFT_SHUTDOWN (29) // P0.29 (32) SOFT_SHUTDOWN_SIGNAL +#define PIN_MCU_SIGNAL (30) // P0.30 (33) MCU_SIGNAL + +/* R1Neo LoRa Radio */ +// RAK4630/4631 pins + +#define P_LORA_DIO_1 (47) // P1.15 (SX) +#define P_LORA_NSS (42) // P1.10 (SX) +#define P_LORA_RESET RADIOLIB_NC // P1.06 (SX) -- 38 +#define P_LORA_BUSY (46) // P1.14 (SX) +#define P_LORA_SCLK (43) // P1.11 (SX) +#define P_LORA_MISO (45) // P1.13 (SX) +#define P_LORA_MOSI (44) // P1.12 (SX) +#define SX126X_POWER_EN (37) // P1.05 (SX) + +#define SX126X_DIO2_AS_RF_SWITCH true +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +/* R1Neo peripherals */ +/* GPS */ +#define GPS_RX (24) // P0.24 (23) UART_GPS_RX +#define GPS_TX (25) // P0.25 (24) UART_GPS_TX +#define GPS_EN (33) // P1.01 (25) GPS_EN +#define GPS_PPS (2) // P0.02 (30) GPS_PPS + +#define PIN_GPS_1PPS GPS_PPS +#define GPS_BAUD_RATE 9600 + +/* RTC */ +#define RTC_SDA (19) // P0.19 (9) RTC_SDA +#define RTC_SCL (20) // P0.20 (10) RTC_SCL + +/* LEDs */ +#define LED_GREEN (36) // P1.04 (28) GRN_LED_RAK +#define LED_BLUE (28) // P0.28 (31) BLU_LED_RAK + +#define LED_BUILTIN (0xFF) + +#ifndef P_LORA_TX_LED + #define P_LORA_TX_LED LED_GREEN +#endif + +#define LED_STATE_ON 1 // State when LED is lit + +/* Buttons */ +#define PIN_USER_BTN (26) + +/* Buzzer */ +#define PIN_BUZZER (3) + +/* Analog pins */ +// Arduino makes me angry +#define PIN_A0 (0xFF) // NOT_PRESENT +#define PIN_A1 (0xFF) // NOT_PRESENT +#define PIN_A2 (4) // P0.04 (41) NC +#define PIN_A3 (5) // P0.05 (40) NC +#define PIN_A4 (0xFF) // NOT_PRESENT +#define PIN_A5 (0xFF) // NOT_PRESENT +#define PIN_A6 (0xFF) // NOT_PRESENT +#define PIN_A7 (31) // P0.31 (39) ADC_VBAT + + static const uint8_t A0 = PIN_A0; + static const uint8_t A1 = PIN_A1; + static const uint8_t A2 = PIN_A2; + static const uint8_t A3 = PIN_A3; + static const uint8_t A4 = PIN_A4; + static const uint8_t A5 = PIN_A5; + static const uint8_t A6 = PIN_A6; + static const uint8_t A7 = PIN_A7; +#define ADC_RESOLUTION 14 + +// Other pins +#define PIN_AREF (0xFF) // No analog reference + + static const uint8_t AREF = PIN_AREF; + +/* Serial interfaces */ +#define PIN_GPS_TX (GPS_TX) +#define PIN_GPS_RX (GPS_RX) +#define PIN_GPS_EN (GPS_EN) + +#define PIN_SERIAL1_TX (PIN_GPS_TX) +#define PIN_SERIAL1_RX (PIN_GPS_RX) + +/* SPI Interfaces */ +// unused pins - define anyways +#define SPI_INTERFACES_COUNT 1 +#define PIN_SPI_MOSI (9) // P0.09 (13) NC +#define PIN_SPI_MISO (10) // P0.10 (12) NC +#define PIN_SPI_SCK (21) // P0.21 (11) NC + +/* I2C Interfaces */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (RTC_SDA) +#define PIN_WIRE_SCL (RTC_SCL) + +/* QSPI Pins */ +// interface occupied by peripherals, define anyways +#define PIN_QSPI_SCK (3) // P0.03 (29) BUZZER +#define PIN_QSPI_CS (26) // P0.26 (34) USER_BUTTON +#define PIN_QSPI_IO0 (30) // P0.30 (33) MCU_SIGNAL +#define PIN_QSPI_IO1 (29) // P0.29 (32) SOFT_SHUTDOWN +#define PIN_QSPI_IO2 (28) // P0.28 (31) BLU_LED_RAK +#define PIN_QSPI_IO3 (2) // P0.02 (30) GPS_PPS + +/* On-board QSPI Flash */ +// No QSPI (define anyways) +#define EXTERNAL_FLASH_DEVICES IS25LP080D +#define EXTERNAL_FLASH_USE_QSPI + +/* Battery */ +#define PIN_VBAT_READ (31) // P0.31 (39) ADC_VBAT +#define PIN_BAT_CHG (34) // P1.02 (26) BAT_CHG_STATUS + +#define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000) + +// Power management boot protection threshold (millivolts) +// Set to 0 to disable boot protection +// disabled for now until I can figure this out +#define PWRMGT_VOLTAGE_BOOTLOCK 0 // Won't boot below this voltage (mV) +// LPCOMP wake configuration (voltage recovery from SYSTEMOFF) +// AIN3 = P0.05 = PIN_A0 / PIN_VBAT_READ +#define PWRMGT_LPCOMP_AIN 5 +#define PWRMGT_LPCOMP_REFSEL 4 // 5/8 VDD (~3.13-3.44V) + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 842a1ad1c4..ea7e49c355 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -20,6 +20,7 @@ build_flags = ${nrf52_base.build_flags} -D LORA_TX_POWER=22 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 + -D ENV_INCLUDE_RAK12035=1 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> + diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index b8fc8786a3..87e77152b8 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -28,7 +28,7 @@ build_flags = -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 -; -D SX126X_RX_BOOSTED_GAIN=1 - DO NOT ENABLE THIS! + -D SX126X_RX_BOOSTED_GAIN=0 ; Default value when 'radio.rxgain' has not been set. Must be OFF for the Station G2, see: ; https://wiki.uniteng.com/en/meshtastic/station-g2#impact-of-lora-node-dense-areashigh-noise-environments-on-rf-performance -D DISPLAY_CLASS=SH1106Display build_src_filter = ${esp32_base.build_src_filter} diff --git a/variants/t1000-e/T1000eBoard.h b/variants/t1000-e/T1000eBoard.h index 4922360779..e7653fb2f4 100644 --- a/variants/t1000-e/T1000eBoard.h +++ b/variants/t1000-e/T1000eBoard.h @@ -43,7 +43,7 @@ class T1000eBoard : public NRF52BoardDCDC { uint8_t v = digitalRead(BUTTON_PIN); if (v != btn_prev_state) { btn_prev_state = v; - return (v == LOW) ? 1 : -1; + return (v == USER_BTN_PRESSED) ? 1 : -1; } #endif return 0; diff --git a/variants/t1000-e/platformio.ini b/variants/t1000-e/platformio.ini index ac92930861..429390c486 100644 --- a/variants/t1000-e/platformio.ini +++ b/variants/t1000-e/platformio.ini @@ -22,7 +22,7 @@ build_flags = ${nrf52_base.build_flags} -D P_LORA_NSS=12 ; P0.12 -D P_LORA_DIO_1=33 ; P1.1 -D P_LORA_MISO=40 ; P1.8 - -D P_LORA_MOSI=41 ; P0.9 + -D P_LORA_MOSI=41 ; P1.9 -D P_LORA_RESET=42 ; P1.10 -D LR11X0_DIO_AS_RF_SWITCH=true -D LR11X0_DIO3_TCXO_VOLTAGE=1.6 diff --git a/variants/t1000-e/variant.cpp b/variants/t1000-e/variant.cpp index a598e3cad1..ed21fd68cf 100644 --- a/variants/t1000-e/variant.cpp +++ b/variants/t1000-e/variant.cpp @@ -55,7 +55,7 @@ const uint32_t g_ADigitalPinMap[PINS_COUNT + 1] = 42, // P1.10, LORA_RESET 43, // P1.11, GPS_EN 44, // P1.12, GPS_SLEEP_INT - 45, // P1.13 + 45, // P1.13, FLASH_ENABLE 46, // P1.14, GPS_RESETB 47, // P1.15, PIN_GPS_RESET 255, // NRFX_SPIM_PIN_NOT_USED diff --git a/variants/t1000-e/variant.h b/variants/t1000-e/variant.h index 458f12bccb..b95e1e4d86 100644 --- a/variants/t1000-e/variant.h +++ b/variants/t1000-e/variant.h @@ -97,7 +97,7 @@ #define LORA_BUSY (7) // P0.7 #define LORA_SCLK (PIN_SPI_SCK) // P0.11 #define LORA_MISO (PIN_SPI_MISO) // P1.8 -#define LORA_MOSI (PIN_SPI_MOSI) // P0.9 +#define LORA_MOSI (PIN_SPI_MOSI) // P1.9 #define LR11X0_DIO_AS_RF_SWITCH true #define LR11X0_DIO3_TCXO_VOLTAGE 1.6 @@ -133,4 +133,4 @@ // Buzzer #define BUZZER_EN (37) // P1.5 -#define BUZZER_PIN (25) // P0.25 \ No newline at end of file +#define BUZZER_PIN (25) // P0.25 diff --git a/variants/thinknode_m3/platformio.ini b/variants/thinknode_m3/platformio.ini index 8ef2ba54ad..88fd487aa6 100644 --- a/variants/thinknode_m3/platformio.ini +++ b/variants/thinknode_m3/platformio.ini @@ -10,7 +10,6 @@ build_flags = ${nrf52_base.build_flags} -I src/helpers/ui -D THINKNODE_M3 -D PIN_USER_BTN=12 - -D USER_BTN_PRESSED=LOW -D PIN_STATUS_LED=35 -D RADIO_CLASS=CustomLR1110 -D WRAPPER_CLASS=CustomLR1110Wrapper diff --git a/variants/thinknode_m5/platformio.ini b/variants/thinknode_m5/platformio.ini index 0c64bcef86..16df472a22 100644 --- a/variants/thinknode_m5/platformio.ini +++ b/variants/thinknode_m5/platformio.ini @@ -58,7 +58,7 @@ build_src_filter = ${ThinkNode_M5.build_src_filter} +<../examples/simple_repeater/*.cpp> build_flags = ${ThinkNode_M5.build_flags} - -D ADVERT_NAME='"Thinknode M2 Repeater"' + -D ADVERT_NAME='"Thinknode M5 Repeater"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' @@ -111,12 +111,12 @@ lib_deps = ${esp32_ota.lib_deps} [env:ThinkNode_M5_room_server] -extends = ThinkNonde_M5 +extends = ThinkNode_M5 build_src_filter = ${ThinkNode_M5.build_src_filter} +<../examples/simple_room_server> build_flags = ${ThinkNode_M5.build_flags} - -D ADVERT_NAME='"Thinknode M2 Room Server"' + -D ADVERT_NAME='"Thinknode M5 Room Server"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' @@ -193,8 +193,8 @@ build_flags = -D MAX_GROUP_CHANNELS=40 -D OFFLINE_QUEUE_SIZE=256 -D WIFI_DEBUG_LOGGING=1 - -D WIFI_SSID='"Livebox-633C"' - -D WIFI_PWD='"vvQUHGSxsWd7fKMYSr"' + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' build_src_filter = ${ThinkNode_M5.build_src_filter} + + diff --git a/variants/waveshare_rp2040_lora/platformio.ini b/variants/waveshare_rp2040_lora/platformio.ini index 1f7fb02f1b..880f238f2f 100644 --- a/variants/waveshare_rp2040_lora/platformio.ini +++ b/variants/waveshare_rp2040_lora/platformio.ini @@ -20,7 +20,8 @@ build_flags = ${rp2040_base.build_flags} -D P_LORA_MOSI=15 -D P_LORA_TX_LED=25 -D SX126X_DIO2_AS_RF_SWITCH=true - -D SX126X_DIO3_TCXO_VOLTAGE=0 + -D SX126X_RXEN=17 + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_RX_BOOSTED_GAIN=1 -D LORA_TX_POWER=22 ; Debug options diff --git a/variants/wio-e5-mini/platformio.ini b/variants/wio-e5-mini/platformio.ini index 8378444376..f589ea0323 100644 --- a/variants/wio-e5-mini/platformio.ini +++ b/variants/wio-e5-mini/platformio.ini @@ -9,7 +9,6 @@ build_flags = ${stm32_base.build_flags} -D RX_BOOSTED_GAIN=true -D P_LORA_TX_LED=LED_RED -D PIN_USER_BTN=USER_BTN - -D USER_BTN_PRESSED=LOW -I variants/wio-e5-mini build_src_filter = ${stm32_base.build_src_filter} +<../variants/wio-e5-mini> diff --git a/variants/xiao_nrf52/XiaoNrf52Board.h b/variants/xiao_nrf52/XiaoNrf52Board.h index bd0fd9b12f..2790dbad90 100644 --- a/variants/xiao_nrf52/XiaoNrf52Board.h +++ b/variants/xiao_nrf52/XiaoNrf52Board.h @@ -4,6 +4,10 @@ #include #include +#ifndef USER_BTN_PRESSED +#define USER_BTN_PRESSED LOW +#endif + #ifdef XIAO_NRF52 class XiaoNrf52Board : public NRF52BoardDCDC { @@ -35,7 +39,7 @@ class XiaoNrf52Board : public NRF52BoardDCDC { // set led on and wait for button release before poweroff digitalWrite(PIN_LED, LOW); #ifdef PIN_USER_BTN - while(digitalRead(PIN_USER_BTN) == LOW); + while(digitalRead(PIN_USER_BTN) == USER_BTN_PRESSED); #endif digitalWrite(LED_GREEN, HIGH); digitalWrite(LED_BLUE, HIGH); diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index b9a860aaf7..a085433688 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -72,11 +72,11 @@ build_flags = -I examples/companion_radio/ui-orig -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 -D QSPIFLASH=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_nrf52.build_src_filter} - + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-orig/*.cpp> lib_deps = @@ -117,4 +117,4 @@ build_flags = build_src_filter = ${Xiao_nrf52.build_src_filter} +<../examples/kiss_modem/*.cpp> lib_deps = - ${Xiao_nrf52.lib_deps} \ No newline at end of file + ${Xiao_nrf52.lib_deps}