Compare commits

...

116 Commits

Author SHA1 Message Date
Scott Powell
88b88cbc90 * ver bump to 1.4.1 2025-03-25 22:02:37 +11:00
Scott Powell
753e6a69dc * flood_advert_interval" now defaults to 3 hours for upgrading nodes (not just fresh install)
* companion: "node_prefs" -> "new_prefs" migration
2025-03-25 20:20:03 +11:00
Scott Powell
f33e1b22b3 * build fixes 2025-03-24 13:11:08 +11:00
ripplebiz
16039eebfb Merge pull request #128 from Noki/xiao-s3-wio-room-server
xiao s3 wio: add room server
2025-03-24 11:59:05 +11:00
ripplebiz
0d9201b560 Merge pull request #127 from jameshall9327/main
Added screen and button support for the lilygo tbeam
2025-03-24 11:55:22 +11:00
ripplebiz
02edc645bb Merge pull request #135 from recrof/dev
LilyGo T-ECHO: report correct battery voltage
2025-03-24 11:48:52 +11:00
recrof
ac71bac4cf cleanup 2025-03-23 18:31:39 +01:00
Rastislav Vysoky
1677d4db65 Merge branch 'ripplebiz:dev' into dev 2025-03-23 18:27:02 +01:00
recrof
0d114ecfbd LilyGo T-ECHO: report correct battery voltage 2025-03-23 15:57:33 +01:00
Scott Powell
edb201ccbe * repeater & room server: "node_prefs" -> "com_prefs" migration 2025-03-24 00:50:28 +11:00
Scott Powell
d07abc39b6 * room server: new config "allow.read.only" 2025-03-24 00:25:36 +11:00
Scott Powell
39a4476194 * repeater & room server: new config "flood.advert.interval" 2025-03-23 23:23:02 +11:00
Scott Powell
7010123619 * companion: CMD_SEND_TXT_MSG fix for attempt number 2025-03-23 20:25:03 +11:00
Scott Powell
f861b68a09 * refactor: ESP32 OTA lib-deps now selectively added 2025-03-23 17:36:31 +11:00
Scott Powell
a4bb3782a4 * OTA update for ESP32 targets 2025-03-22 23:51:44 +11:00
Scott Powell
30c6a0bc76 * workaround for > 3 retry attempts 2025-03-22 22:21:54 +11:00
ripplebiz
2b8b6aacbb Merge pull request #131 from fdlamotte/t1000e_range_fix
Some fixes for range issue
2025-03-22 11:54:10 +11:00
Florent de Lamotte
1ff3033372 Some fixes for range issue 2025-03-21 15:08:13 +01:00
Scott Powell
0163c4034b * faketec/promicro fix 2025-03-21 21:53:53 +11:00
Scott Powell
965e40eb9e * fix for Faketec/Promicro 2025-03-21 21:53:23 +11:00
Scott Powell
bfb4b1c496 * BIG refactor: 'board' and 'radio' objects now defined in 'target.h/.cpp'
* mesh::RTCClock class moved to MeshCore.h
2025-03-21 18:57:12 +11:00
Tobias Schwarz
e1d8179f72 xiao s3 wio: add room server 2025-03-20 16:49:24 +00:00
James Hall
1299b6f813 added button support 2025-03-20 12:26:52 -04:00
James Hall
8b4662a40a removed repeater code not working properly 2025-03-20 11:48:35 -04:00
James Hall
ae08ecf8fd Screen support for tbeam 2025-03-20 11:44:12 -04:00
ripplebiz
0a5a115875 Merge pull request #124 from recrof/dev
added battery voltage support for Heltec T114
2025-03-20 22:22:33 +11:00
recrof
41e01a0e25 added battery voltage support for Heltec T114 2025-03-20 11:08:13 +01:00
ripplebiz
390e8407c0 Merge pull request #123 from oltaco/faketec-support
Faketec display and button support.
2025-03-20 15:27:25 +11:00
Scott Powell
182c6d479d * minor refactor, SNR from packet 2025-03-20 15:26:23 +11:00
taco
de67ee29eb Fix builds for repeater, room server and companion usb. 2025-03-20 12:50:35 +11:00
taco
1c14482bd4 Remove PIN_OLED_RESET from board header, will leave in build target for now. 2025-03-20 10:05:01 +11:00
taco
61f7f15dc1 Changed BLE pin back to 123456 now - dynamic display sensing happening. 2025-03-20 09:46:40 +11:00
taco
bd0ce731f1 Add faketec changes back after dev merge 2025-03-20 09:27:11 +11:00
taco
74ec702136 Merge branch 'dev' into faketec-support 2025-03-20 09:26:23 +11:00
Scott Powell
a9dde51a9b * build.sh: .uf2 step also for Faketec/ProMicro targets 2025-03-19 20:22:46 +11:00
Scott Powell
6a093743d3 * ver bump to v1.4.0 2025-03-19 19:32:07 +11:00
Scott Powell
fd55837eec * companion: CMD_SET_DEVICE_PIN now uint32 param, RESP_CODE_DEVICE_INFO now includes ble_pin 2025-03-19 18:21:35 +11:00
Scott Powell
4aeafbd034 * companion: added CMD_SET_DEVICE_PIN 2025-03-19 17:54:55 +11:00
Scott Powell
089ac96f2b * verify() fix moved to Identity class 2025-03-19 17:30:35 +11:00
Scott Powell
4943b388c0 * workaround for Identity::verify() bug. 2025-03-19 14:31:48 +11:00
Scott Powell
69a6d76b87 * experimental workaround for sig verify() bug 2025-03-19 13:54:43 +11:00
Scott Powell
38667dabb1 * refactored platformio.ini, breaking up into variants/*.ini files 2025-03-18 23:57:04 +11:00
Scott Powell
d21274db41 * TBeam: minor refactor. 2025-03-18 21:34:32 +11:00
ripplebiz
c7f791963e Merge pull request #100 from jameshall9327/main
Added Support for the LilyGo TBeam v1.1 and v1.2 with the SX1276
2025-03-18 21:29:38 +11:00
Scott Powell
50b62c9252 * room server: now handles REQ_TYPE_GET_STATUS, replies with ServerStats (same as RepeaterStats, with extra fields: n_posted, n_post_push) 2025-03-18 16:49:40 +11:00
Scott Powell
f08a30cf24 * companion: RESP_CODE_ERR now has err_code byte 2025-03-18 15:27:55 +11:00
ripplebiz
d15630660b Merge pull request #114 from lincomatic/pr-llcc68
Add ProMicroLLCC68 builds
2025-03-18 13:45:03 +11:00
Scott Powell
13679f9ff1 * CMD_SEND_TRACE_PATH, now replies with RESP_CODE_SENT 2025-03-18 00:14:57 +11:00
Scott Powell
691c135c22 * workaround for TRACE packets so they can follow a return path 2025-03-17 19:35:08 +11:00
Scott Powell
807b9bdc19 * companion support for TRACE packets. (CMD_SEND_TRACE_PATH, PUSH_CODE_TRACE_DATA) 2025-03-17 18:15:38 +11:00
Scott Powell
fe4fdeb236 * redesigned TRACE packet structure 2025-03-17 17:56:12 +11:00
taco
6a78cfd00d match changes to other nrf52 boards, add pin definitions to platformio.ini 2025-03-17 13:51:26 +11:00
taco
f671b753da revert variant.h pins 2025-03-17 13:50:05 +11:00
taco
88dfa42e47 Merge branch 'faketec-testing' into dev 2025-03-17 13:15:54 +11:00
taco
86ece04fe2 move DISPLAY_CLASS definition 2025-03-17 13:09:26 +11:00
taco
d59b1d235e define PIN_OLED_RESET -1 2025-03-17 13:09:02 +11:00
Scott Powell
156741e563 Merge branch 'dev' into trace
# Conflicts:
#	examples/companion_radio/main.cpp
2025-03-17 12:18:27 +11:00
taco
a67bb8e1e3 tidy up 2025-03-17 12:17:36 +11:00
lincomatic
bdeec374ef add ProMicroLLCC68 for Faketec w/ LLCC68 2025-03-16 17:48:52 -07:00
lincomatic
0eed29b99e fix compilation error 2025-03-16 16:49:55 -07:00
Scott Powell
25a68f5ca4 * build fix 2025-03-16 22:11:27 +11:00
ripplebiz
6c434328b5 Merge pull request #111 from recrof/dev
added display support for RAK4631
2025-03-16 21:36:19 +11:00
recrof
882377e4d6 fixed: added back pin inits when SDA and SCL are set. 2025-03-16 10:17:04 +01:00
recrof
d525680b71 added display support for RAK4631, removed Wire.begin(int, int) from nrf52 devices, cosmetic changes 2025-03-16 09:07:21 +01:00
ripplebiz
f700fd5a36 Merge pull request #107 from recrof/dev
heltec v2: added display, room server role
2025-03-16 19:04:38 +11:00
Rastislav Vysoky
32436b953e Merge branch 'ripplebiz:dev' into dev 2025-03-16 07:11:53 +01:00
Scott Powell
81bf4f0a08 * companion: SSD1306 display now dynamically detected at runtime. 2025-03-16 13:42:36 +11:00
taco
8b2f783d04 OLED and button support, some refactoring. 2025-03-16 13:24:43 +11:00
James Hall
395349d208 Merge branch 'dev' into main 2025-03-15 15:10:17 -04:00
recrof
c691cbae8e heltec v2: added display, room server role 2025-03-15 20:10:07 +01:00
James Hall
aeda218402 Update TBeamBoard.h 2025-03-15 13:09:15 -04:00
James Hall
b6990c73bb Merge branch 'ripplebiz:main' into main 2025-03-15 13:03:49 -04:00
ripplebiz
4113b20b4c Merge pull request #105 from recrof/dev
added display support for t3s3
2025-03-15 23:44:09 +11:00
recrof
7ee81f4f07 added display support for t3s3 2025-03-15 13:07:18 +01:00
Scott Powell
31f8576640 * CMD_SIGN_DATA null check 2025-03-15 22:06:57 +11:00
Scott Powell
6056c303c1 * companion: can now sign arbitrary data (up to 8K) 2025-03-15 21:41:44 +11:00
Scott Powell
9aa2edf9ba Merge branch 'dev' into trace
# Conflicts:
#	examples/companion_radio/main.cpp
#	src/Dispatcher.cpp
2025-03-15 15:12:50 +11:00
Scott Powell
27aa7a7bb0 * minor refactor for RAK_4631 SerialInterface 2025-03-15 14:55:52 +11:00
ripplebiz
06c666843e Merge pull request #90 from oltaco/faketec-support
Faketec support
2025-03-15 14:47:18 +11:00
ripplebiz
f844f9e02a Merge pull request #65 from awolden/awolden/h3-pin-fix
Fix heltec v3.2 pin polarity reversal
2025-03-15 12:09:47 +11:00
James Hall
bc6e4930d9 Cleaned up code 2025-03-14 19:54:46 -04:00
taco
6931887424 remove old unused board.json 2025-03-15 10:54:37 +11:00
James Hall
c2f92534df removed unnecessary defines 2025-03-14 14:27:10 -04:00
James Hall
aca0bcc919 removed unnecessary constants 2025-03-14 14:22:13 -04:00
Scott Powell
6a4b7463ef * some fixes around the powerOff(). Tidy ups 2025-03-14 19:08:41 +11:00
ripplebiz
99b376c512 Merge pull request #87 from fdlamotte/t1000e_ui
enhancement on the UI and support for power off and status_led
2025-03-14 18:52:00 +11:00
taco
fbb879600d MAX_GROUP_CHANNELS=8 for faketec 2025-03-14 15:24:58 +11:00
taco
5632e2771e added conditional to room server radio bringup for supporting RA01sh radio on faketec 2025-03-14 15:22:42 +11:00
James Hall
b41d1a56f6 Delete .vscode/settings.json 2025-03-13 20:42:36 -04:00
James Hall
6d8aa5817b removed pmu flag 2025-03-13 20:41:32 -04:00
James Hall
aca3486f88 Update settings.json 2025-03-13 20:35:44 -04:00
ripplebiz
0fe6f79f3c Merge pull request #94 from LitBomb/patch-5
Update faq.md for new EU and UK frequency, additional steps to setup repeater frequency
2025-03-14 11:29:34 +11:00
ripplebiz
87462b20d5 Update README.md 2025-03-14 09:55:48 +11:00
James Hall
2edbd92b3b Added Support for the LilyGo TBeam v1.2 SX1276 2025-03-13 14:07:54 -04:00
fdlamotte
c62f09d92e Merge branch 'ripplebiz:main' into t1000e_ui 2025-03-13 09:44:10 +01:00
taco
9c3b4dd520 added SX126X_RX_BOOSTED_GAIN=1 to faketec target 2025-03-13 17:46:58 +11:00
oltaco
c8104563a0 Merge branch 'ripplebiz:main' into faketec-support 2025-03-13 16:22:02 +11:00
uncle lit
6b6534ed79 Update faq.md
update UK and EU frequency to 869.525MHz per Andy and Recrof
add steps to set frequency on repeater after flash and before disconnecting USB cable
2025-03-12 10:45:07 -07:00
oltaco
883f591ccd Merge branch 'ripplebiz:main' into faketec-support 2025-03-12 18:31:37 +11:00
fdlamotte
bb826a2634 Merge branch 'ripplebiz:main' into t1000e_ui 2025-03-11 20:38:52 +01:00
Florent de Lamotte
dc4a139f88 resolve conflict 2025-03-11 09:18:50 +01:00
taco
1a21a08845 updated to MIT licensed board variant 2025-03-11 15:38:37 +11:00
taco
92bb6a844f added conditional to radio bringup for RA01-SH support on faketec 2025-03-11 09:34:52 +11:00
taco
c1faaf5e82 build fix to support RTC detection code 2025-03-11 09:34:52 +11:00
taco
027b7d83cb Added logic to companion_radio firmware to hopefully fix the radio bringup for Faketec with RA01-SH. 2025-03-11 09:34:52 +11:00
taco
a57053207f Added defines for SX126X_RXEN, SX126X_TXEN.
Updated build target to enable privkey import/export.
2025-03-11 09:34:52 +11:00
taco
8e793dc55e Faketec board support, including build targets 2025-03-11 09:34:52 +11:00
Florent
4cfdb0ef7c better handling of BTN_USER_PRESSED definition 2025-03-10 22:42:52 +01:00
Florent de Lamotte
7bb16cd7f3 enhancement on the UI and support for power off and status_led for t1000e 2025-03-10 18:11:15 +01:00
Alex Wolden
3dea712d37 Fix heltec v3.2 pin polarity reversal 2025-03-08 11:21:19 -08:00
Scott Powell
790898dbc5 * companion: better incoming ACK processing (match prev attempts) 2025-03-07 15:09:29 +11:00
Scott Powell
e534e1b529 * companion: FIRMWARE_VER_CODE now bumped to 3.
* companion: new RESP_CODE_CONTACT_MSG_RECV_V3, and _CHANNEL_MSG_RECV_V3. (now with packet SNR included)
2025-03-07 14:17:37 +11:00
Scott Powell
544adec2b6 * refactor Packet::_snr to single byte 2025-03-07 12:32:23 +11:00
Scott Powell
b2fcc692ea * TRACE packet logging fix 2025-03-07 12:24:18 +11:00
Scott Powell
b03aac18c0 Merge branch 'dev' into trace
# Conflicts:
#	src/Dispatcher.cpp
#	src/Mesh.cpp
#	src/helpers/BaseChatMesh.cpp
2025-03-07 12:14:26 +11:00
Scott Powell
0db15db625 * new Packet type: PAYLOAD_TYPE_TRACE 2025-02-17 19:22:31 +11:00
89 changed files with 5479 additions and 1680 deletions

View File

@@ -55,6 +55,11 @@ MeshCore is designed for use with:
## 📜 License
MeshCore is open-source software released under the MIT License. You are free to use, modify, and distribute it for personal and commercial projects.
## Contributing
Please submit PR's using 'dev' as the base branch!
For minor changes just submit your PR and I'll try to review it, but for anything more 'impactful' please open an Issue first and start a discussion. Is better to sound out what it is you want to achieve first, and try to come to a consensus on what the best approach is, especially when it impacts the structure or architecture of this codebase.
## 📞 Get Support
Check out the GitHub Issues page to report bugs or request features.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Ayush Sharma
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,2 @@
AsyncElegantOTA KEYWORD1
begin KEYWORD2

View File

@@ -0,0 +1,9 @@
name=AsyncElegantOTA
version=2.2.8
author=Ayush Sharma
category=Communication
maintainer=Ayush Sharma <asrocks5@gmail.com>
sentence=Perform OTAs for ESP8266 & ESP32 Asynchronously.
paragraph=A User Interface Library which provides interactive elements for your Over the Air Updates on ESP8266/ESP32.
url=https://github.com/ayushsharma82/AsyncElegantOTA
architectures=esp8266,esp32

View File

@@ -0,0 +1,129 @@
#include <AsyncElegantOTA.h>
AsyncElegantOtaClass AsyncElegantOTA;
void AsyncElegantOtaClass::setID(const char* id){
_id = id;
}
void AsyncElegantOtaClass::begin(AsyncWebServer *server, const char* username, const char* password){
_server = server;
if(strlen(username) > 0){
_authRequired = true;
_username = username;
_password = password;
}else{
_authRequired = false;
_username = "";
_password = "";
}
_server->on("/update/identity", HTTP_GET, [&](AsyncWebServerRequest *request){
if(_authRequired){
if(!request->authenticate(_username.c_str(), _password.c_str())){
return request->requestAuthentication();
}
}
#if defined(ESP8266)
request->send(200, "application/json", "{\"id\": \""+_id+"\", \"hardware\": \"ESP8266\"}");
#elif defined(ESP32)
request->send(200, "application/json", "{\"id\": \""+_id+"\", \"hardware\": \"ESP32\"}");
#endif
});
_server->on("/update", HTTP_GET, [&](AsyncWebServerRequest *request){
if(_authRequired){
if(!request->authenticate(_username.c_str(), _password.c_str())){
return request->requestAuthentication();
}
}
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", ELEGANT_HTML, ELEGANT_HTML_SIZE);
response->addHeader("Content-Encoding", "gzip");
request->send(response);
});
_server->on("/update", HTTP_POST, [&](AsyncWebServerRequest *request) {
if(_authRequired){
if(!request->authenticate(_username.c_str(), _password.c_str())){
return request->requestAuthentication();
}
}
// the request handler is triggered after the upload has finished...
// create the response, add header, and send response
AsyncWebServerResponse *response = request->beginResponse((Update.hasError())?500:200, "text/plain", (Update.hasError())?"FAIL":"OK");
response->addHeader("Connection", "close");
response->addHeader("Access-Control-Allow-Origin", "*");
request->send(response);
restart();
}, [&](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
//Upload handler chunks in data
if(_authRequired){
if(!request->authenticate(_username.c_str(), _password.c_str())){
return request->requestAuthentication();
}
}
if (!index) {
if(!request->hasParam("MD5", true)) {
return request->send(400, "text/plain", "MD5 parameter missing");
}
if(!Update.setMD5(request->getParam("MD5", true)->value().c_str())) {
return request->send(400, "text/plain", "MD5 parameter invalid");
}
#if defined(ESP8266)
int cmd = (filename == "filesystem") ? U_FS : U_FLASH;
Update.runAsync(true);
size_t fsSize = ((size_t) &_FS_end - (size_t) &_FS_start);
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if (!Update.begin((cmd == U_FS)?fsSize:maxSketchSpace, cmd)){ // Start with max available size
#elif defined(ESP32)
int cmd = (filename == "filesystem") ? U_SPIFFS : U_FLASH;
if (!Update.begin(UPDATE_SIZE_UNKNOWN, cmd)) { // Start with max available size
#endif
Update.printError(Serial);
return request->send(400, "text/plain", "OTA could not begin");
}
}
// Write chunked data to the free sketch space
if(len){
if (Update.write(data, len) != len) {
return request->send(400, "text/plain", "OTA could not begin");
}
}
if (final) { // if the final flag is set then this is the last frame of data
if (!Update.end(true)) { //true to set the size to the current progress
Update.printError(Serial);
return request->send(400, "text/plain", "Could not end OTA");
}
}else{
return;
}
});
}
// deprecated, keeping for backward compatibility
void AsyncElegantOtaClass::loop() {
}
void AsyncElegantOtaClass::restart() {
yield();
delay(1000);
yield();
ESP.restart();
}
String AsyncElegantOtaClass::getID(){
String id = "";
#if defined(ESP8266)
id = String(ESP.getChipId());
#elif defined(ESP32)
id = String((uint32_t)ESP.getEfuseMac(), HEX);
#endif
id.toUpperCase();
return id;
}

View File

@@ -0,0 +1,52 @@
#ifndef AsyncElegantOTA_h
#define AsyncElegantOTA_h
#warning AsyncElegantOTA library is deprecated, Please consider moving to newer ElegantOTA library which now comes with an Async Mode. Learn More: https://docs.elegantota.pro/async-mode/
#include "Arduino.h"
#include "stdlib_noniso.h"
#if defined(ESP8266)
#include "ESP8266WiFi.h"
#include "ESPAsyncTCP.h"
#include "flash_hal.h"
#include "FS.h"
#elif defined(ESP32)
#include "WiFi.h"
#include "AsyncTCP.h"
#include "Update.h"
#include "esp_int_wdt.h"
#include "esp_task_wdt.h"
#endif
#include "Hash.h"
#include "ESPAsyncWebServer.h"
#include "FS.h"
#include "elegantWebpage.h"
class AsyncElegantOtaClass{
public:
void
setID(const char* id),
begin(AsyncWebServer *server, const char* username = "", const char* password = ""),
loop(),
restart();
private:
AsyncWebServer *_server;
String getID();
String _id = getID();
String _username = "";
String _password = "";
bool _authRequired = false;
};
extern AsyncElegantOtaClass AsyncElegantOTA;
#endif

View File

@@ -0,0 +1,38 @@
/**
* @file Hash.h
* @date 20.05.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the esp8266 core for Arduino environment.
*
* 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 HASH_H_
#define HASH_H_
//#define DEBUG_SHA1
void sha1(const uint8_t* data, uint32_t size, uint8_t hash[20]);
void sha1(const char* data, uint32_t size, uint8_t hash[20]);
void sha1(const String& data, uint8_t hash[20]);
String sha1(const uint8_t* data, uint32_t size);
String sha1(const char* data, uint32_t size);
String sha1(const String& data);
#endif /* HASH_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
{
"build": {
"arduino":{
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
[
"0x239A",
"0x00B3"
],
[
"0x239A",
"0x8029"
],
[
"0x239A",
"0x0029"
],
[
"0x239A",
"0x002A"
],
[
"0x239A",
"0x802A"
]
],
"usb_product": "ProMicro NRF52840",
"mcu": "nrf52840",
"variant": "promicro_nrf52840",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": [
"bluetooth"
],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd"
},
"frameworks": [
"arduino",
"zephyr"
],
"name": "ProMicro NRF52840",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://www.nologo.tech/en/product/otherboard/NRF52840.html",
"vendor": "Nologo"
}

View File

@@ -58,7 +58,7 @@ build_firmware() {
fi
# build .uf2 for nrf52 boards
if [[ $1 == *"RAK_4631"* || $1 == *"t1000e"* || $1 == *"t114"* || $1 == *"T-Echo"* ]]; then
if [[ $1 == *"RAK_4631"* || $1 == *"t1000e"* || $1 == *"t114"* || $1 == *"T-Echo"* || $1 == *"Faketec"* || $1 == *"ProMicro"* ]]; then
python bin/uf2conv/uf2conv.py .pio/build/$1/firmware.hex -c -o .pio/build/$1/firmware.uf2 -f 0xADA52840
fi

View File

@@ -83,6 +83,12 @@ If you have two supported devices, and there are not many MeshCore users near yo
If you have two supported devices, and there are other MeshcCore users near by, you can flash one of your devices with BLE Companion firmware, and flash another supported device to repeater firmware. Place the repeater high above ground o extend your MeshCore network's reach.
After you flashed the latest firmware onto your repeater device, keep the device connected to your computer via USB serial, use the console feature on the web flasher and set the frequency for your region or country, so your client can remote administer the rpeater or room server over RF:
`set freq {frequency}`
The repeater and room server CLI reference is here: https://github.com/ripplebiz/MeshCore/wiki/Repeater-&-Room-Server-CLI-Reference
If you have more supported devices, you can use your additional deivces with the room server firmware.
### Q: Does MeshCore cost any money?
@@ -96,10 +102,10 @@ The T-Deck firmware is free to download and most features are available without
### Q: What frequencies are supported by MeshCore?
**A:** It supports the 868MHz range in the UK/EU and the 915MHz range in New Zealand, Australia, and the USA. Countries and regions in these two frequency ranges are also supported. The firmware and client allow users to set their preferred frequency.
- Australia and New Zealand are using **915.8MHz**
- UK and EU are gravitating toward **867.5MHz**
- There are discussions on discord for UK to move to 869.525MHz (https://discord.com/channels/826570251612323860/1330643963501351004/1342554454498742374)
- Canada and USA is on **910.525MHz**
- Australia and New Zealand are on **915.8MHz**
- UK and EU are on **869.525MHz**
- Canada and USA are on **910.525MHz**
- For other regions and countries, please check your local LoRa frequency
the rest of the radio settings are the same for all frequencies:
- Spread Factor (SF): 10
@@ -231,6 +237,13 @@ Lowering the spreading factor makes it more difficult for the gateway to receive
So it's balancing act between speed of the transmission and resistance to noise.
things network is mainly focused on LoRaWAN, but the LoRa low-level stuff still checks out for any LoRa project
### Q: What happens when a node learns a route via a mobile repeater, and that repeater is gone?
**A:** If you used to reach a node through a repeater and the repeater is no longer reachable, the client will send the message using the existing (but now broken) known path, the message will fail after 3 retries, and the app will reset the path and send the message as flood on the last retry by default. This can be turned off in settings. If the destination is reachable directly or through another repeater, the new path will be used going forward. Or you can set the path manually if you know a specific repeater to use to reach that destination.
In the case if users are moving around frequently, and the paths are breaking, they just see the phone client retries and revert to flood to attempt to reestablish a path.
### Q: Is MeshCore open source?
**A:** Most of the firmware is freely available. Everything is open source except the T-Deck firmware and Liam's native mobile apps.
- Firmware repo: <https://github.com/ripplebiz/MeshCore>
@@ -360,4 +373,3 @@ You can update repeater and room server firmware with a bluetooth connection bet
---

View File

@@ -4,6 +4,10 @@
#define AUTO_OFF_MILLIS 15000 // 15 seconds
#ifndef USER_BTN_PRESSED
#define USER_BTN_PRESSED LOW
#endif
// 'meshcore', 128x13px
static const uint8_t meshcore_logo [] PROGMEM = {
0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe,
@@ -21,14 +25,23 @@ static const uint8_t meshcore_logo [] PROGMEM = {
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8,
};
void UITask::begin(const char* node_name, const char* build_date, uint32_t pin_code) {
_prevBtnState = HIGH;
void UITask::begin(DisplayDriver* display, const char* node_name, const char* build_date, uint32_t pin_code) {
_display = display;
_auto_off = millis() + AUTO_OFF_MILLIS;
clearMsgPreview();
_node_name = node_name;
_build_date = build_date;
_pin_code = pin_code;
_display->turnOn();
if (_display != NULL) {
_display->turnOn();
}
}
void UITask::msgRead(int msgcount) {
_msgcount = msgcount;
if (msgcount == 0) {
clearMsgPreview();
}
}
void UITask::clearMsgPreview() {
@@ -36,7 +49,9 @@ void UITask::clearMsgPreview() {
_msg[0] = 0;
}
void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char* text) {
void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) {
_msgcount = msgcount;
if (path_len == 0xFF) {
sprintf(_origin, "(F) %s", from_name);
} else {
@@ -44,11 +59,15 @@ void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char*
}
StrHelper::strncpy(_msg, text, sizeof(_msg));
if (!_display->isOn()) _display->turnOn();
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
if (_display != NULL) {
if (!_display->isOn()) _display->turnOn();
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
}
}
void UITask::renderCurrScreen() {
if (_display == NULL) return; // assert() ??
char tmp[80];
if (_origin[0] && _msg[0]) {
// render message preview
@@ -61,9 +80,10 @@ void UITask::renderCurrScreen() {
_display->setCursor(0, 24);
_display->print(_msg);
//_display->setCursor(100, 9); TODO
//_display->setTextSize(2);
//_display->printf("%d", msgs);
_display->setCursor(100, 9);
_display->setTextSize(2);
sprintf(tmp, "%d", _msgcount);
_display->print(tmp);
} else {
// render 'home' screen
_display->drawXbm(0, 0, meshcore_logo, 128, 13);
@@ -87,24 +107,72 @@ void UITask::renderCurrScreen() {
}
}
void UITask::loop() {
if (millis() >= _next_read) {
int btnState = digitalRead(PIN_USER_BTN);
if (btnState != _prevBtnState) {
if (btnState == LOW) { // pressed?
if (_display->isOn()) {
clearMsgPreview();
} else {
_display->turnOn();
}
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
void UITask::userLedHandler() {
#ifdef PIN_STATUS_LED
static int state = 0;
static int next_change = 0;
int cur_time = millis();
if (cur_time > next_change) {
if (state == 0) {
state = 1; // led on, short = unread msg
if (_msgcount > 0) {
next_change = cur_time + 500;
} else {
next_change = cur_time + 2000;
}
} else {
state = 0;
if (_board->getBattMilliVolts() > 3800) {
next_change = cur_time + 2000;
} else {
next_change = cur_time + 4000; // 4s blank if bat level low
}
_prevBtnState = btnState;
}
_next_read = millis() + 100; // 10 reads per second
digitalWrite(PIN_STATUS_LED, state);
}
#endif
}
if (_display->isOn()) {
void UITask::buttonHandler() {
#ifdef PIN_USER_BTN
static int prev_btn_state = !USER_BTN_PRESSED;
static unsigned long btn_state_change_time = 0;
static unsigned long next_read = 0;
int cur_time = millis();
if (cur_time >= next_read) {
int btn_state = digitalRead(PIN_USER_BTN);
if (btn_state != prev_btn_state) {
if (btn_state == USER_BTN_PRESSED) { // pressed?
if (_display != NULL) {
if (_display->isOn()) {
clearMsgPreview();
} else {
_display->turnOn();
}
_auto_off = cur_time + AUTO_OFF_MILLIS; // extend auto-off timer
}
} else { // unpressed ? check pressed time ...
if ((cur_time - btn_state_change_time) > 5000) {
#ifdef PIN_STATUS_LED
digitalWrite(PIN_STATUS_LED, LOW);
delay(10);
#endif
_board->powerOff();
}
}
btn_state_change_time = millis();
prev_btn_state = btn_state;
}
next_read = millis() + 100; // 10 reads per second
}
#endif
}
void UITask::loop() {
buttonHandler();
userLedHandler();
if (_display != NULL && _display->isOn()) {
if (millis() >= _next_refresh) {
_display->startFrame();
renderCurrScreen();

View File

@@ -1,25 +1,37 @@
#pragma once
#include <MeshCore.h>
#include <helpers/ui/DisplayDriver.h>
#include <stddef.h>
class UITask {
DisplayDriver* _display;
unsigned long _next_read, _next_refresh, _auto_off;
int _prevBtnState;
mesh::MainBoard* _board;
unsigned long _next_refresh, _auto_off;
bool _connected;
uint32_t _pin_code;
const char* _node_name;
const char* _build_date;
char _origin[62];
char _msg[80];
int _msgcount;
void renderCurrScreen();
void buttonHandler();
void userLedHandler();
public:
UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; _connected = false; }
void begin(const char* node_name, const char* build_date, uint32_t pin_code);
UITask(mesh::MainBoard* board) : _board(board), _display(NULL) {
_next_refresh = 0;
_connected = false;
}
void begin(DisplayDriver* display, const char* node_name, const char* build_date, uint32_t pin_code);
void setHasConnection(bool connected) { _connected = connected; }
bool hasDisplay() const { return _display != NULL; }
void clearMsgPreview();
void showMsgPreview(uint8_t path_len, const char* from_name, const char* text);
void msgRead(int msgcount);
void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount);
void loop();
};
};

View File

@@ -16,6 +16,7 @@
#include <helpers/IdentityStore.h>
#include <helpers/BaseSerialInterface.h>
#include <RTClib.h>
#include <target.h>
/* ---------------------------------- CONFIGURATION ------------------------------------- */
@@ -59,54 +60,18 @@
#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg=="
#if defined(HELTEC_LORA_V3)
#include <helpers/HeltecV3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static HeltecV3Board board;
#elif defined(HELTEC_LORA_V2)
#include <helpers/HeltecV2Board.h>
#include <helpers/CustomSX1276Wrapper.h>
static HeltecV2Board board;
#elif defined(ARDUINO_XIAO_ESP32C3)
#include <helpers/XiaoC3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomSX1268Wrapper.h>
static XiaoC3Board board;
#elif defined(SEEED_XIAO_S3) || defined(LILYGO_T3S3)
#include <helpers/ESP32Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static ESP32Board board;
#elif defined(LILYGO_TLORA)
#include <helpers/LilyGoTLoraBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
static LilyGoTLoraBoard board;
#elif defined(RAK_4631)
#include <helpers/nrf52/RAK4631Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static RAK4631Board board;
#elif defined(T1000_E)
#include <helpers/nrf52/T1000eBoard.h>
#include <helpers/CustomLR1110Wrapper.h>
static T1000eBoard board;
#elif defined(HELTEC_T114)
#include <helpers/nrf52/T114Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static T114Board board;
#elif defined(LILYGO_TECHO)
#include <helpers/nrf52/TechoBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
static TechoBoard board;
#else
#error "need to provide a 'board' object"
#endif
#ifdef DISPLAY_CLASS
#include "UITask.h"
#include <helpers/ui/SSD1306Display.h>
static DISPLAY_CLASS display;
#define HAS_UI
#endif
#if defined(HAS_UI)
#include "UITask.h"
static UITask ui_task(display);
static UITask ui_task(&board);
#endif
// Believe it or not, this std C function is busted on some platforms!
@@ -121,14 +86,14 @@ static uint32_t _atoi(const char* sp) {
/*------------ Frame Protocol --------------*/
#define FIRMWARE_VER_CODE 2
#define FIRMWARE_VER_CODE 3
#ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "13 Mar 2025"
#define FIRMWARE_BUILD_DATE "25 Mar 2025"
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.3.0"
#define FIRMWARE_VERSION "v1.4.1"
#endif
#define CMD_APP_START 1
@@ -163,6 +128,11 @@ static uint32_t _atoi(const char* sp) {
#define CMD_GET_CONTACT_BY_KEY 30
#define CMD_GET_CHANNEL 31
#define CMD_SET_CHANNEL 32
#define CMD_SIGN_START 33
#define CMD_SIGN_DATA 34
#define CMD_SIGN_FINISH 35
#define CMD_SEND_TRACE_PATH 36
#define CMD_SET_DEVICE_PIN 37
#define RESP_CODE_OK 0
#define RESP_CODE_ERR 1
@@ -171,8 +141,8 @@ static uint32_t _atoi(const char* sp) {
#define RESP_CODE_END_OF_CONTACTS 4 // last reply to CMD_GET_CONTACTS
#define RESP_CODE_SELF_INFO 5 // reply to CMD_APP_START
#define RESP_CODE_SENT 6 // reply to CMD_SEND_TXT_MSG
#define RESP_CODE_CONTACT_MSG_RECV 7 // a reply to CMD_SYNC_NEXT_MESSAGE
#define RESP_CODE_CHANNEL_MSG_RECV 8 // a reply to CMD_SYNC_NEXT_MESSAGE
#define RESP_CODE_CONTACT_MSG_RECV 7 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3)
#define RESP_CODE_CHANNEL_MSG_RECV 8 // a reply to CMD_SYNC_NEXT_MESSAGE (ver < 3)
#define RESP_CODE_CURR_TIME 9 // a reply to CMD_GET_DEVICE_TIME
#define RESP_CODE_NO_MORE_MESSAGES 10 // a reply to CMD_SYNC_NEXT_MESSAGE
#define RESP_CODE_EXPORT_CONTACT 11
@@ -180,8 +150,11 @@ static uint32_t _atoi(const char* sp) {
#define RESP_CODE_DEVICE_INFO 13 // a reply to CMD_DEVICE_QEURY
#define RESP_CODE_PRIVATE_KEY 14 // a reply to CMD_EXPORT_PRIVATE_KEY
#define RESP_CODE_DISABLED 15
// ... _V3 stuff in here
#define RESP_CODE_CONTACT_MSG_RECV_V3 16 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3)
#define RESP_CODE_CHANNEL_MSG_RECV_V3 17 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3)
#define RESP_CODE_CHANNEL_INFO 18 // a reply to CMD_GET_CHANNEL
#define RESP_CODE_SIGN_START 19
#define RESP_CODE_SIGNATURE 20
// these are _pushed_ to client app at any time
#define PUSH_CODE_ADVERT 0x80
@@ -193,9 +166,19 @@ static uint32_t _atoi(const char* sp) {
#define PUSH_CODE_LOGIN_FAIL 0x86
#define PUSH_CODE_STATUS_RESPONSE 0x87
#define PUSH_CODE_LOG_RX_DATA 0x88
#define PUSH_CODE_TRACE_DATA 0x89
#define ERR_CODE_UNSUPPORTED_CMD 1
#define ERR_CODE_NOT_FOUND 2
#define ERR_CODE_TABLE_FULL 3
#define ERR_CODE_BAD_STATE 4
#define ERR_CODE_FILE_IO_ERROR 5
#define ERR_CODE_ILLEGAL_ARG 6
/* -------------------------------------------------------------------------------------- */
#define MAX_SIGN_DATA_LEN (8*1024) // 8K
struct NodePrefs { // persisted to file
float airtime_factor;
char node_name[32];
@@ -217,17 +200,17 @@ class MyMesh : public BaseChatMesh {
RADIO_CLASS* _phy;
IdentityStore* _identity_store;
NodePrefs _prefs;
uint32_t expected_ack_crc; // TODO: keep table of expected ACKs
uint32_t pending_login;
uint32_t pending_status;
BaseSerialInterface* _serial;
unsigned long last_msg_sent;
ContactsIterator _iter;
uint32_t _iter_filter_since;
uint32_t _most_recent_lastmod;
uint32_t _active_ble_pin;
bool _iter_started;
uint8_t app_target_ver;
uint8_t* sign_data;
uint32_t sign_data_len;
uint8_t cmd_frame[MAX_FRAME_SIZE+1];
uint8_t out_frame[MAX_FRAME_SIZE+1];
@@ -238,6 +221,14 @@ class MyMesh : public BaseChatMesh {
int offline_queue_len;
Frame offline_queue[OFFLINE_QUEUE_SIZE];
struct AckTableEntry {
unsigned long msg_sent;
uint32_t ack;
};
#define EXPECTED_ACK_TABLE_SIZE 8
AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table
int next_ack_idx;
void loadMainIdentity(mesh::RNG& trng) {
if (!_identity_store->load("_main", self_id)) {
self_id = mesh::LocalIdentity(&trng); // create new random identity
@@ -414,10 +405,11 @@ class MyMesh : public BaseChatMesh {
buf[0] = RESP_CODE_OK;
_serial->writeFrame(buf, 1);
}
void writeErrFrame() {
uint8_t buf[1];
void writeErrFrame(uint8_t err_code) {
uint8_t buf[2];
buf[0] = RESP_CODE_ERR;
_serial->writeFrame(buf, 1);
buf[1] = err_code;
_serial->writeFrame(buf, 2);
}
void writeDisabledFrame() {
@@ -528,26 +520,35 @@ protected:
}
bool processAck(const uint8_t *data) override {
// TODO: see if matches any in a table
if (memcmp(data, &expected_ack_crc, 4) == 0) { // got an ACK from recipient
out_frame[0] = PUSH_CODE_SEND_CONFIRMED;
memcpy(&out_frame[1], data, 4);
uint32_t trip_time = _ms->getMillis() - last_msg_sent;
memcpy(&out_frame[5], &trip_time, 4);
_serial->writeFrame(out_frame, 9);
// see if matches any in a table
for (int i = 0; i < EXPECTED_ACK_TABLE_SIZE; i++) {
if (memcmp(data, &expected_ack_table[i].ack, 4) == 0) { // got an ACK from recipient
out_frame[0] = PUSH_CODE_SEND_CONFIRMED;
memcpy(&out_frame[1], data, 4);
uint32_t trip_time = _ms->getMillis() - expected_ack_table[i].msg_sent;
memcpy(&out_frame[5], &trip_time, 4);
_serial->writeFrame(out_frame, 9);
// NOTE: the same ACK can be received multiple times!
expected_ack_crc = 0; // reset our expected hash, now that we have received ACK
return true;
// NOTE: the same ACK can be received multiple times!
expected_ack_table[i].ack = 0; // clear expected hash, now that we have received ACK
return true;
}
}
return checkConnectionsAck(data);
}
void queueMessage(const ContactInfo& from, uint8_t txt_type, uint8_t path_len, uint32_t sender_timestamp, const uint8_t* extra, int extra_len, const char *text) {
void queueMessage(const ContactInfo& from, uint8_t txt_type, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t* extra, int extra_len, const char *text) {
int i = 0;
out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV;
if (app_target_ver >= 3) {
out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV_V3;
out_frame[i++] = (int8_t)(pkt->getSNR() * 4);
out_frame[i++] = 0; // reserved1
out_frame[i++] = 0; // reserved2
} else {
out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV;
}
memcpy(&out_frame[i], from.id.pub_key, 6); i += 6; // just 6-byte prefix
out_frame[i++] = path_len;
uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF;
out_frame[i++] = txt_type;
memcpy(&out_frame[i], &sender_timestamp, 4); i += 4;
if (extra_len > 0) {
@@ -567,32 +568,41 @@ protected:
} else {
soundBuzzer();
}
#ifdef DISPLAY_CLASS
ui_task.showMsgPreview(path_len, from.name, text);
#ifdef HAS_UI
ui_task.newMsg(path_len, from.name, text, offline_queue_len);
#endif
}
void onMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override {
void onMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override {
markConnectionActive(from); // in case this is from a server, and we have a connection
queueMessage(from, TXT_TYPE_PLAIN, path_len, sender_timestamp, NULL, 0, text);
queueMessage(from, TXT_TYPE_PLAIN, pkt, sender_timestamp, NULL, 0, text);
}
void onCommandDataRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override {
void onCommandDataRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override {
markConnectionActive(from); // in case this is from a server, and we have a connection
queueMessage(from, TXT_TYPE_CLI_DATA, path_len, sender_timestamp, NULL, 0, text);
queueMessage(from, TXT_TYPE_CLI_DATA, pkt, sender_timestamp, NULL, 0, text);
}
void onSignedMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override {
void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override {
markConnectionActive(from);
saveContacts(); // from.sync_since change needs to be persisted
queueMessage(from, TXT_TYPE_SIGNED_PLAIN, path_len, sender_timestamp, sender_prefix, 4, text);
queueMessage(from, TXT_TYPE_SIGNED_PLAIN, pkt, sender_timestamp, sender_prefix, 4, text);
}
void onChannelMessageRecv(const mesh::GroupChannel& channel, int in_path_len, uint32_t timestamp, const char *text) override {
void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) override {
int i = 0;
out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV;
if (app_target_ver >= 3) {
out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV_V3;
out_frame[i++] = (int8_t)(pkt->getSNR() * 4);
out_frame[i++] = 0; // reserved1
out_frame[i++] = 0; // reserved2
} else {
out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV;
}
out_frame[i++] = findChannelIdx(channel);
out_frame[i++] = in_path_len < 0 ? 0xFF : in_path_len;
uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF;
out_frame[i++] = TXT_TYPE_PLAIN;
memcpy(&out_frame[i], &timestamp, 4); i += 4;
int tlen = strlen(text); // TODO: UTF-8 ??
@@ -609,8 +619,8 @@ protected:
} else {
soundBuzzer();
}
#ifdef DISPLAY_CLASS
ui_task.showMsgPreview(in_path_len < 0 ? 0xFF : in_path_len, "Public", text);
#ifdef HAS_UI
ui_task.newMsg(path_len, "Public", text, offline_queue_len);
#endif
}
@@ -667,6 +677,25 @@ protected:
}
}
void onTraceRecv(mesh::Packet* packet, uint32_t tag, uint32_t auth_code, uint8_t flags, const uint8_t* path_snrs, const uint8_t* path_hashes, uint8_t path_len) override {
int i = 0;
out_frame[i++] = PUSH_CODE_TRACE_DATA;
out_frame[i++] = 0; // reserved
out_frame[i++] = path_len;
out_frame[i++] = flags;
memcpy(&out_frame[i], &tag, 4); i += 4;
memcpy(&out_frame[i], &auth_code, 4); i += 4;
memcpy(&out_frame[i], path_hashes, path_len); i += path_len;
memcpy(&out_frame[i], path_snrs, path_len); i += path_len;
out_frame[i++] = (int8_t)(packet->getSNR() * 4); // extra/final SNR (to this node)
if (_serial->isConnected()) {
_serial->writeFrame(out_frame, i);
} else {
MESH_DEBUG_PRINTLN("onTraceRecv(), data received while app offline");
}
}
uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override {
return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis);
}
@@ -688,6 +717,8 @@ public:
app_target_ver = 0;
_identity_store = NULL;
pending_login = pending_status = 0;
next_ack_idx = 0;
sign_data = NULL;
// defaults
memset(&_prefs, 0, sizeof(_prefs));
@@ -701,7 +732,42 @@ public:
//_prefs.rx_delay_base = 10.0f; enable once new algo fixed
}
void begin(FILESYSTEM& fs, mesh::RNG& trng) {
void loadPrefsInt(const char* filename) {
File file = _fs->open(filename);
if (file) {
uint8_t pad[8];
file.read((uint8_t *) &_prefs.airtime_factor, sizeof(float)); // 0
file.read((uint8_t *) _prefs.node_name, sizeof(_prefs.node_name)); // 4
file.read(pad, 4); // 36
file.read((uint8_t *) &_prefs.node_lat, sizeof(_prefs.node_lat)); // 40
file.read((uint8_t *) &_prefs.node_lon, sizeof(_prefs.node_lon)); // 48
file.read((uint8_t *) &_prefs.freq, sizeof(_prefs.freq)); // 56
file.read((uint8_t *) &_prefs.sf, sizeof(_prefs.sf)); // 60
file.read((uint8_t *) &_prefs.cr, sizeof(_prefs.cr)); // 61
file.read((uint8_t *) &_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
file.read((uint8_t *) &_prefs.reserved2, sizeof(_prefs.reserved2)); // 63
file.read((uint8_t *) &_prefs.bw, sizeof(_prefs.bw)); // 64
file.read((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
file.read((uint8_t *) _prefs.unused, sizeof(_prefs.unused)); // 69
file.read((uint8_t *) &_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
file.read(pad, 4); // 76
file.read((uint8_t *) &_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
// 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.bw = constrain(_prefs.bw, 62.5f, 500.0f);
_prefs.sf = constrain(_prefs.sf, 7, 12);
_prefs.cr = constrain(_prefs.cr, 5, 8);
_prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, 1, MAX_LORA_TX_POWER);
file.close();
}
}
void begin(FILESYSTEM& fs, mesh::RNG& trng, bool has_display) {
_fs = &fs;
BaseChatMesh::begin();
@@ -715,45 +781,22 @@ public:
loadMainIdentity(trng);
// load persisted prefs
if (_fs->exists("/node_prefs")) {
File file = _fs->open("/node_prefs");
if (file) {
uint8_t pad[8];
file.read((uint8_t *) &_prefs.airtime_factor, sizeof(float)); // 0
file.read((uint8_t *) _prefs.node_name, sizeof(_prefs.node_name)); // 4
file.read(pad, 4); // 36
file.read((uint8_t *) &_prefs.node_lat, sizeof(_prefs.node_lat)); // 40
file.read((uint8_t *) &_prefs.node_lon, sizeof(_prefs.node_lon)); // 48
file.read((uint8_t *) &_prefs.freq, sizeof(_prefs.freq)); // 56
file.read((uint8_t *) &_prefs.sf, sizeof(_prefs.sf)); // 60
file.read((uint8_t *) &_prefs.cr, sizeof(_prefs.cr)); // 61
file.read((uint8_t *) &_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
file.read((uint8_t *) &_prefs.reserved2, sizeof(_prefs.reserved2)); // 63
file.read((uint8_t *) &_prefs.bw, sizeof(_prefs.bw)); // 64
file.read((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
file.read((uint8_t *) _prefs.unused, sizeof(_prefs.unused)); // 69
file.read((uint8_t *) &_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
file.read(pad, 4); // 76
file.read((uint8_t *) &_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
// 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.bw = constrain(_prefs.bw, 62.5f, 500.0f);
_prefs.sf = constrain(_prefs.sf, 7, 12);
_prefs.cr = constrain(_prefs.cr, 5, 8);
_prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, 1, MAX_LORA_TX_POWER);
file.close();
}
if (_fs->exists("/new_prefs")) {
loadPrefsInt("/new_prefs"); // new filename
} else if (_fs->exists("/node_prefs")) {
loadPrefsInt("/node_prefs");
savePrefs(); // save to new filename
_fs->remove("/node_prefs"); // remove old
}
#ifdef BLE_PIN_CODE
if (_prefs.ble_pin == 0) {
#ifdef DISPLAY_CLASS
_active_ble_pin = trng.nextInt(100000, 999999); // random pin each session
#ifdef HAS_UI
if (has_display) {
_active_ble_pin = trng.nextInt(100000, 999999); // random pin each session
} else {
_active_ble_pin = BLE_PIN_CODE; // otherwise static pin
}
#else
_active_ble_pin = BLE_PIN_CODE; // otherwise static pin
#endif
@@ -788,10 +831,10 @@ public:
void savePrefs() {
#if defined(NRF52_PLATFORM)
File file = _fs->open("/node_prefs", FILE_O_WRITE);
File file = _fs->open("/new_prefs", FILE_O_WRITE);
if (file) { file.seek(0); file.truncate(); }
#else
File file = _fs->open("/node_prefs", "w", true);
File file = _fs->open("/new_prefs", "w", true);
#endif
if (file) {
uint8_t pad[8];
@@ -827,7 +870,7 @@ public:
out_frame[i++] = FIRMWARE_VER_CODE;
out_frame[i++] = MAX_CONTACTS / 2; // v3+
out_frame[i++] = MAX_GROUP_CHANNELS; // v3+
memset(&out_frame[i], 0, 4); i += 4; // reserved
memcpy(&out_frame[i], &_prefs.ble_pin, 4); i += 4;
memset(&out_frame[i], 0, 12);
strcpy((char *) &out_frame[i], FIRMWARE_BUILD_DATE); i += 12;
StrHelper::strzcpy((char *) &out_frame[i], board.getManufacturerName(), 40); i += 40;
@@ -872,32 +915,37 @@ public:
memcpy(&msg_timestamp, &cmd_frame[i], 4); i += 4;
uint8_t* pub_key_prefix = &cmd_frame[i]; i += 6;
ContactInfo* recipient = lookupContactByPubKey(pub_key_prefix, 6);
if (recipient && attempt < 4 && (txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_CLI_DATA)) {
if (recipient && (txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_CLI_DATA)) {
char *text = (char *) &cmd_frame[i];
int tlen = len - i;
uint32_t est_timeout;
text[tlen] = 0; // ensure null
int result;
uint32_t expected_ack;
if (txt_type == TXT_TYPE_CLI_DATA) {
result = sendCommandData(*recipient, msg_timestamp, attempt, text, est_timeout);
expected_ack_crc = 0; // no Ack expected
expected_ack = 0; // no Ack expected
} else {
result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack_crc, est_timeout);
result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack, est_timeout);
}
// TODO: add expected ACK to table
if (result == MSG_SEND_FAILED) {
writeErrFrame();
writeErrFrame(ERR_CODE_TABLE_FULL);
} else {
last_msg_sent = _ms->getMillis();
if (expected_ack) {
expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis(); // add to circular table
expected_ack_table[next_ack_idx].ack = expected_ack;
next_ack_idx = (next_ack_idx + 1) % EXPECTED_ACK_TABLE_SIZE;
}
out_frame[0] = RESP_CODE_SENT;
out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
memcpy(&out_frame[2], &expected_ack_crc, 4);
memcpy(&out_frame[2], &expected_ack, 4);
memcpy(&out_frame[6], &est_timeout, 4);
_serial->writeFrame(out_frame, 10);
}
} else {
writeErrFrame(); // unknown recipient, or unsuported TXT_TYPE_*
writeErrFrame(recipient == NULL ? 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
int i = 1;
@@ -907,16 +955,20 @@ public:
memcpy(&msg_timestamp, &cmd_frame[i], 4); i += 4;
const char *text = (char *) &cmd_frame[i];
ChannelDetails channel;
bool success = getChannel(channel_idx, channel);
if (success && txt_type == TXT_TYPE_PLAIN && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) {
writeOKFrame();
if (txt_type != TXT_TYPE_PLAIN) {
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
} else {
writeErrFrame();
ChannelDetails channel;
bool success = getChannel(channel_idx, channel);
if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) {
writeOKFrame();
} else {
writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx
}
}
} else if (cmd_frame[0] == CMD_GET_CONTACTS) { // get Contact list
if (_iter_started) {
writeErrFrame(); // iterator is currently busy
writeErrFrame(ERR_CODE_BAD_STATE); // iterator is currently busy
} else {
if (len >= 5) { // has optional 'since' param
memcpy(&_iter_filter_since, &cmd_frame[1], 4);
@@ -955,7 +1007,7 @@ public:
savePrefs();
writeOKFrame();
} else {
writeErrFrame(); // invalid geo coordinate
writeErrFrame(ERR_CODE_ILLEGAL_ARG); // invalid geo coordinate
}
} else if (cmd_frame[0] == CMD_GET_DEVICE_TIME) {
uint8_t reply[5];
@@ -971,7 +1023,7 @@ public:
getRTCClock()->setCurrentTime(secs);
writeOKFrame();
} else {
writeErrFrame();
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
}
} else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) {
auto pkt = createSelfAdvert(_prefs.node_name, _prefs.node_lat, _prefs.node_lon);
@@ -983,7 +1035,7 @@ public:
}
writeOKFrame();
} else {
writeErrFrame();
writeErrFrame(ERR_CODE_TABLE_FULL);
}
} else if (cmd_frame[0] == CMD_RESET_PATH && len >= 1+32) {
uint8_t* pub_key = &cmd_frame[1];
@@ -994,7 +1046,7 @@ public:
saveContacts();
writeOKFrame();
} else {
writeErrFrame(); // unknown contact
writeErrFrame(ERR_CODE_NOT_FOUND); // unknown contact
}
} else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1+32+2+1) {
uint8_t* pub_key = &cmd_frame[1];
@@ -1013,7 +1065,7 @@ public:
saveContacts();
writeOKFrame();
} else {
writeErrFrame(); // table is full!
writeErrFrame(ERR_CODE_TABLE_FULL);
}
}
} else if (cmd_frame[0] == CMD_REMOVE_CONTACT) {
@@ -1023,15 +1075,19 @@ public:
saveContacts();
writeOKFrame();
} else {
writeErrFrame(); // not found, or unable to remove
writeErrFrame(ERR_CODE_NOT_FOUND); // not found, or unable to remove
}
} else if (cmd_frame[0] == CMD_SHARE_CONTACT) {
uint8_t* pub_key = &cmd_frame[1];
ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
if (recipient && shareContactZeroHop(*recipient)) {
writeOKFrame();
if (recipient) {
if (shareContactZeroHop(*recipient)) {
writeOKFrame();
} else {
writeErrFrame(ERR_CODE_TABLE_FULL); // unable to send
}
} else {
writeErrFrame(); // not found, or unable to send
writeErrFrame(ERR_CODE_NOT_FOUND);
}
} else if (cmd_frame[0] == CMD_GET_CONTACT_BY_KEY) {
uint8_t* pub_key = &cmd_frame[1];
@@ -1039,7 +1095,7 @@ public:
if (contact) {
writeContactRespFrame(RESP_CODE_CONTACT, *contact);
} else {
writeErrFrame(); // not found
writeErrFrame(ERR_CODE_NOT_FOUND); // not found
}
} else if (cmd_frame[0] == CMD_EXPORT_CONTACT) {
if (len < 1 + PUB_KEY_SIZE) {
@@ -1051,7 +1107,7 @@ public:
releasePacket(pkt); // undo the obtainNewPacket()
_serial->writeFrame(out_frame, out_len + 1);
} else {
writeErrFrame(); // Error
writeErrFrame(ERR_CODE_TABLE_FULL); // Error
}
} else {
uint8_t* pub_key = &cmd_frame[1];
@@ -1061,19 +1117,22 @@ public:
out_frame[0] = RESP_CODE_EXPORT_CONTACT;
_serial->writeFrame(out_frame, out_len + 1);
} else {
writeErrFrame(); // not found
writeErrFrame(ERR_CODE_NOT_FOUND); // not found
}
}
} else if (cmd_frame[0] == CMD_IMPORT_CONTACT && len > 2+32+64) {
if (importContact(&cmd_frame[1], len - 1)) {
writeOKFrame();
} else {
writeErrFrame();
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
}
} else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) {
int out_len;
if ((out_len = getFromOfflineQueue(out_frame)) > 0) {
_serial->writeFrame(out_frame, out_len);
#ifdef HAS_UI
ui_task.msgRead(offline_queue_len);
#endif
} else {
out_frame[0] = RESP_CODE_NO_MORE_MESSAGES;
_serial->writeFrame(out_frame, 1);
@@ -1103,11 +1162,11 @@ public:
writeOKFrame();
} else {
MESH_DEBUG_PRINTLN("Error: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr);
writeErrFrame();
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
}
} else if (cmd_frame[0] == CMD_SET_RADIO_TX_POWER) {
if (cmd_frame[1] > MAX_LORA_TX_POWER) {
writeErrFrame();
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
} else {
_prefs.tx_power_dbm = cmd_frame[1];
savePrefs();
@@ -1148,7 +1207,7 @@ public:
self_id = identity;
writeOKFrame();
} else {
writeErrFrame();
writeErrFrame(ERR_CODE_FILE_IO_ERROR);
}
#else
writeDisabledFrame();
@@ -1163,10 +1222,10 @@ public:
sendDirect(pkt, path, path_len);
writeOKFrame();
} else {
writeErrFrame();
writeErrFrame(ERR_CODE_TABLE_FULL);
}
} else {
writeErrFrame(); // flood, not supported (yet)
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // flood, not supported (yet)
}
} else if (cmd_frame[0] == CMD_SEND_LOGIN && len >= 1+PUB_KEY_SIZE) {
uint8_t* pub_key = &cmd_frame[1];
@@ -1177,7 +1236,7 @@ public:
uint32_t est_timeout;
int result = sendLogin(*recipient, password, est_timeout);
if (result == MSG_SEND_FAILED) {
writeErrFrame();
writeErrFrame(ERR_CODE_TABLE_FULL);
} else {
pending_status = 0;
memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse()
@@ -1188,7 +1247,7 @@ public:
_serial->writeFrame(out_frame, 10);
}
} else {
writeErrFrame(); // contact not found
writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
}
} else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1+PUB_KEY_SIZE) {
uint8_t* pub_key = &cmd_frame[1];
@@ -1197,7 +1256,7 @@ public:
uint32_t est_timeout;
int result = sendStatusRequest(*recipient, est_timeout);
if (result == MSG_SEND_FAILED) {
writeErrFrame();
writeErrFrame(ERR_CODE_TABLE_FULL);
} else {
pending_login = 0;
memcpy(&pending_status, recipient->id.pub_key, 4); // match this to onContactResponse()
@@ -1208,14 +1267,14 @@ public:
_serial->writeFrame(out_frame, 10);
}
} else {
writeErrFrame(); // contact not found
writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
}
} else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1+PUB_KEY_SIZE) {
uint8_t* pub_key = &cmd_frame[1];
if (hasConnectionTo(pub_key)) {
writeOKFrame();
} else {
writeErrFrame();
writeErrFrame(ERR_CODE_NOT_FOUND);
}
} else if (cmd_frame[0] == CMD_LOGOUT && len >= 1+PUB_KEY_SIZE) {
uint8_t* pub_key = &cmd_frame[1];
@@ -1232,10 +1291,10 @@ public:
memcpy(&out_frame[i], channel.channel.secret, 16); i += 16; // NOTE: only 128-bit supported
_serial->writeFrame(out_frame, i);
} else {
writeErrFrame();
writeErrFrame(ERR_CODE_NOT_FOUND);
}
} else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2+32+32) {
writeErrFrame(); // not supported (yet)
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); // not supported (yet)
} else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2+32+16) {
uint8_t channel_idx = cmd_frame[1];
ChannelDetails channel;
@@ -1246,10 +1305,66 @@ public:
saveChannels();
writeOKFrame();
} else {
writeErrFrame();
writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx
}
} else if (cmd_frame[0] == CMD_SIGN_START) {
out_frame[0] = RESP_CODE_SIGN_START;
out_frame[1] = 0; // reserved
uint32_t len = MAX_SIGN_DATA_LEN;
memcpy(&out_frame[2], &len, 4);
_serial->writeFrame(out_frame, 6);
if (sign_data) {
free(sign_data);
}
sign_data = (uint8_t *) malloc(MAX_SIGN_DATA_LEN);
sign_data_len = 0;
} else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) {
if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) {
writeErrFrame(sign_data == NULL ? ERR_CODE_BAD_STATE : ERR_CODE_TABLE_FULL); // error: too long
} else {
memcpy(&sign_data[sign_data_len], &cmd_frame[1], len - 1);
sign_data_len += (len - 1);
writeOKFrame();
}
} else if (cmd_frame[0] == CMD_SIGN_FINISH) {
if (sign_data) {
self_id.sign(&out_frame[1], sign_data, sign_data_len);
free(sign_data); // don't need sign_data now
sign_data = NULL;
out_frame[0] = RESP_CODE_SIGNATURE;
_serial->writeFrame(out_frame, 1 + SIGNATURE_SIZE);
} else {
writeErrFrame(ERR_CODE_BAD_STATE);
}
} else if (cmd_frame[0] == CMD_SEND_TRACE_PATH && len > 10 && len - 10 < MAX_PATH_SIZE) {
uint32_t tag, auth;
memcpy(&tag, &cmd_frame[1], 4);
memcpy(&auth, &cmd_frame[5], 4);
auto pkt = createTrace(tag, auth, cmd_frame[9]);
if (pkt) {
uint8_t path_len = len - 10;
sendDirect(pkt, &cmd_frame[10], path_len);
uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
uint32_t est_timeout = calcDirectTimeoutMillisFor(t, path_len);
out_frame[0] = RESP_CODE_SENT;
out_frame[1] = 0;
memcpy(&out_frame[2], &tag, 4);
memcpy(&out_frame[6], &est_timeout, 4);
_serial->writeFrame(out_frame, 10);
} else {
writeErrFrame(ERR_CODE_TABLE_FULL);
}
} else if (cmd_frame[0] == CMD_SET_DEVICE_PIN && len >= 5) {
memcpy(&_prefs.ble_pin, &cmd_frame[1], 4);
savePrefs();
writeOKFrame();
} else {
writeErrFrame();
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]);
}
}
@@ -1281,7 +1396,7 @@ public:
checkConnections();
}
#ifdef DISPLAY_CLASS
#ifdef HAS_UI
ui_task.setHasConnection(_serial->isConnected());
ui_task.loop();
#endif
@@ -1314,17 +1429,6 @@ public:
#error "need to define a serial interface"
#endif
#if defined(NRF52_PLATFORM)
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
#elif defined(LILYGO_TLORA)
SPIClass spi;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
#elif defined(P_LORA_SCLK)
SPIClass spi;
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
StdRNG fast_rng;
SimpleMeshTables tables;
MyMesh the_mesh(radio, *new WRAPPER_CLASS(radio, board), fast_rng, *new VolatileRTCClock(), tables);
@@ -1337,57 +1441,49 @@ void setup() {
Serial.begin(115200);
board.begin();
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
#if defined(NRF52_PLATFORM)
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
#elif defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
halt();
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
if (!radio_init()) { halt(); }
fast_rng.begin(radio.random(0x7FFFFFFF));
RadioNoiseListener trng(radio);
#ifdef HAS_UI
DisplayDriver* disp = NULL;
#ifdef DISPLAY_CLASS
if (display.begin()) {
disp = &display;
}
#endif
#endif
#if defined(NRF52_PLATFORM)
InternalFS.begin();
the_mesh.begin(InternalFS, trng);
the_mesh.begin(InternalFS, trng,
#ifdef HAS_UI
disp != NULL
#else
false
#endif
);
#ifdef BLE_PIN_CODE
char dev_name[32+16];
sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName());
serial_interface.begin(dev_name, the_mesh.getBLEPin());
#else
pinMode(WB_IO2, OUTPUT);
serial_interface.begin(Serial);
#endif
the_mesh.startInterface(serial_interface);
#elif defined(ESP32)
SPIFFS.begin(true);
the_mesh.begin(SPIFFS, trng);
the_mesh.begin(SPIFFS, trng,
#ifdef HAS_UI
disp != NULL
#else
false
#endif
);
#ifdef WIFI_SSID
WiFi.begin(WIFI_SSID, WIFI_PWD);
@@ -1404,9 +1500,8 @@ void setup() {
#error "need to define filesystem"
#endif
#ifdef DISPLAY_CLASS
display.begin();
ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE, the_mesh.getBLEPin());
#ifdef HAS_UI
ui_task.begin(disp, the_mesh.getNodeName(), FIRMWARE_BUILD_DATE, the_mesh.getBLEPin());
#endif
}

View File

@@ -18,15 +18,16 @@
#include <helpers/TxtDataHelpers.h>
#include <helpers/CommonCLI.h>
#include <RTClib.h>
#include <target.h>
/* ------------------------------ Config -------------------------------- */
#ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "13 Mar 2025"
#define FIRMWARE_BUILD_DATE "25 Mar 2025"
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.3.0"
#define FIRMWARE_VERSION "v1.4.1"
#endif
#ifndef LORA_FREQ
@@ -59,47 +60,6 @@
#define ADMIN_PASSWORD "password"
#endif
#if defined(HELTEC_LORA_V3)
#include <helpers/HeltecV3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static HeltecV3Board board;
#elif defined(HELTEC_LORA_V2)
#include <helpers/HeltecV2Board.h>
#include <helpers/CustomSX1276Wrapper.h>
static HeltecV2Board board;
#elif defined(ARDUINO_XIAO_ESP32C3)
#include <helpers/XiaoC3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomSX1268Wrapper.h>
static XiaoC3Board board;
#elif defined(SEEED_XIAO_S3) || defined(LILYGO_T3S3)
#include <helpers/ESP32Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static ESP32Board board;
#elif defined(LILYGO_TLORA)
#include <helpers/LilyGoTLoraBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
static LilyGoTLoraBoard board;
#elif defined(STATION_G2)
#include <helpers/StationG2Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static StationG2Board board;
#elif defined(RAK_4631)
#include <helpers/nrf52/RAK4631Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static RAK4631Board board;
#elif defined(HELTEC_T114)
#include <helpers/nrf52/T114Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static T114Board board;
#elif defined(LILYGO_TECHO)
#include <helpers/nrf52/TechoBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
static TechoBoard board;
#else
#error "need to provide a 'board' object"
#endif
#ifdef DISPLAY_CLASS
#include <helpers/ui/SSD1306Display.h>
@@ -152,7 +112,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
FILESYSTEM* _fs;
RADIO_CLASS* _phy;
mesh::MainBoard* _board;
unsigned long next_local_advert;
unsigned long next_local_advert, next_flood_advert;
bool _logging;
NodePrefs _prefs;
CommonCLI _cli;
@@ -520,7 +480,7 @@ public:
{
my_radio = &radio;
memset(known_clients, 0, sizeof(known_clients));
next_local_advert = 0;
next_local_advert = next_flood_advert = 0;
_logging = false;
// defaults
@@ -538,6 +498,7 @@ public:
_prefs.cr = LORA_CR;
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_advert_interval = 3; // 3 hours
_prefs.flood_max = 64;
}
@@ -556,6 +517,7 @@ public:
_phy->setOutputPower(_prefs.tx_power_dbm);
updateAdvertTimer();
updateFloodAdvertTimer();
}
const char* getFirmwareVer() override { return FIRMWARE_VERSION; }
@@ -588,11 +550,18 @@ public:
void updateAdvertTimer() override {
if (_prefs.advert_interval > 0) { // schedule local advert timer
next_local_advert = futureMillis((uint32_t)_prefs.advert_interval * 2 * 60 * 1000);
next_local_advert = futureMillis( ((uint32_t)_prefs.advert_interval) * 2 * 60 * 1000);
} else {
next_local_advert = 0; // stop the timer
}
}
void updateFloodAdvertTimer() override {
if (_prefs.flood_advert_interval > 0) { // schedule flood advert timer
next_flood_advert = futureMillis( ((uint32_t)_prefs.flood_advert_interval) * 60 * 60 * 1000);
} else {
next_flood_advert = 0; // stop the timer
}
}
void setLoggingOn(bool enable) override { _logging = enable; }
@@ -619,11 +588,15 @@ public:
void loop() {
mesh::Mesh::loop();
if (next_local_advert && millisHasNowPassed(next_local_advert)) {
if (next_flood_advert && millisHasNowPassed(next_flood_advert)) {
mesh::Packet* pkt = createSelfAdvert();
if (pkt) {
sendZeroHop(pkt);
}
if (pkt) sendFlood(pkt);
updateFloodAdvertTimer(); // schedule next flood advert
updateAdvertTimer(); // also schedule local advert (so they don't overlap)
} else if (next_local_advert && millisHasNowPassed(next_local_advert)) {
mesh::Packet* pkt = createSelfAdvert();
if (pkt) sendZeroHop(pkt);
updateAdvertTimer(); // schedule next local advert
}
@@ -633,17 +606,6 @@ public:
}
};
#if defined(NRF52_PLATFORM)
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
#elif defined(LILYGO_TLORA)
SPIClass spi;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
#elif defined(P_LORA_SCLK)
SPIClass spi;
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
StdRNG fast_rng;
SimpleMeshTables tables;
@@ -672,37 +634,7 @@ void setup() {
#endif
rtc_clock.begin(Wire);
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
#if defined(NRF52_PLATFORM)
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
#elif defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
delay(5000);
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
halt();
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
if (!radio_init()) { halt(); }
fast_rng.begin(radio.random(0x7FFFFFFF));
@@ -738,7 +670,7 @@ void setup() {
#endif
// send out initial Advertisement to the mesh
the_mesh.sendSelfAdvertisement(2000);
the_mesh.sendSelfAdvertisement(16000);
}
void loop() {

View File

@@ -18,15 +18,16 @@
#include <helpers/TxtDataHelpers.h>
#include <helpers/CommonCLI.h>
#include <RTClib.h>
#include <target.h>
/* ------------------------------ Config -------------------------------- */
#ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "13 Mar 2025"
#define FIRMWARE_BUILD_DATE "25 Mar 2025"
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.3.0"
#define FIRMWARE_VERSION "v1.4.1"
#endif
#ifndef LORA_FREQ
@@ -67,43 +68,6 @@
#define MAX_UNSYNCED_POSTS 16
#endif
#if defined(HELTEC_LORA_V3)
#include <helpers/HeltecV3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static HeltecV3Board board;
#elif defined(ARDUINO_XIAO_ESP32C3)
#include <helpers/XiaoC3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomSX1268Wrapper.h>
static XiaoC3Board board;
#elif defined(SEEED_XIAO_S3)
#include <helpers/ESP32Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static ESP32Board board;
#elif defined(LILYGO_TLORA)
#include <helpers/LilyGoTLoraBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
static LilyGoTLoraBoard board;
#elif defined(STATION_G2)
#include <helpers/StationG2Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static StationG2Board board;
#elif defined(RAK_4631)
#include <helpers/nrf52/RAK4631Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static RAK4631Board board;
#elif defined(HELTEC_T114)
#include <helpers/nrf52/T114Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static T114Board board;
#elif defined(LILYGO_TECHO)
#include <helpers/nrf52/TechoBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
static TechoBoard board;
#else
#error "need to provide a 'board' object"
#endif
#ifdef DISPLAY_CLASS
#include <helpers/ui/SSD1306Display.h>
@@ -115,6 +79,12 @@
/* ------------------------------ Code -------------------------------- */
enum RoomPermission {
ADMIN,
GUEST,
READ_ONLY
};
struct ClientInfo {
mesh::Identity id;
uint32_t last_timestamp; // by THEIR clock
@@ -123,7 +93,7 @@ struct ClientInfo {
uint32_t pending_ack;
uint32_t push_post_timestamp;
unsigned long ack_timeout;
bool is_admin;
RoomPermission permission;
uint8_t push_failures;
uint8_t secret[PUB_KEY_SIZE];
int out_path_len;
@@ -153,18 +123,36 @@ struct PostInfo {
#define RESP_SERVER_LOGIN_OK 0 // response to ANON_REQ
struct ServerStats {
uint16_t batt_milli_volts;
uint16_t curr_tx_queue_len;
uint16_t curr_free_queue_len;
int16_t last_rssi;
uint32_t n_packets_recv;
uint32_t n_packets_sent;
uint32_t total_air_time_secs;
uint32_t total_up_time_secs;
uint32_t n_sent_flood, n_sent_direct;
uint32_t n_recv_flood, n_recv_direct;
uint16_t n_full_events;
int16_t last_snr; // x 4
uint16_t n_direct_dups, n_flood_dups;
uint16_t n_posted, n_post_push;
};
class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
RadioLibWrapper* my_radio;
FILESYSTEM* _fs;
RADIO_CLASS* _phy;
mesh::MainBoard* _board;
unsigned long next_local_advert;
unsigned long next_local_advert, next_flood_advert;
NodePrefs _prefs;
CommonCLI _cli;
uint8_t reply_data[MAX_PACKET_PAYLOAD];
int num_clients;
ClientInfo known_clients[MAX_CLIENTS];
unsigned long next_push;
uint16_t _num_posted, _num_post_pushes;
int next_client_idx; // for round-robin polling
int next_post_idx;
PostInfo posts[MAX_UNSYNCED_POSTS]; // cyclic queue
@@ -211,6 +199,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
next_post_idx = (next_post_idx + 1) % MAX_UNSYNCED_POSTS;
next_push = futureMillis(PUSH_NOTIFY_DELAY_MILLIS);
_num_posted++; // stats
}
void pushPostToClient(ClientInfo* client, PostInfo& post) {
@@ -237,6 +226,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
sendDirect(reply, client->out_path, client->out_path_len);
client->ack_timeout = futureMillis(PUSH_TIMEOUT_BASE + PUSH_ACK_TIMEOUT_FACTOR * (client->out_path_len + 1));
}
_num_post_pushes++; // stats
} else {
client->pending_ack = 0;
MESH_DEBUG_PRINTLN("Unable to push post to client");
@@ -315,13 +305,16 @@ protected:
memcpy(&sender_timestamp, data, 4);
memcpy(&sender_sync_since, &data[4], 4); // sender's "sync messags SINCE x" timestamp
bool is_admin;
RoomPermission perm;
data[len] = 0; // ensure null terminator
if (strcmp((char *) &data[8], _prefs.password) == 0) { // check for valid admin password
is_admin = true;
perm = RoomPermission::ADMIN;
} else {
is_admin = false;
if (strcmp((char *) &data[8], _prefs.guest_password) != 0) { // check the room/public password
if (strcmp((char *) &data[8], _prefs.guest_password) == 0) { // check the room/public password
perm = RoomPermission::GUEST;
} else if (_prefs.allow_read_only) {
perm = RoomPermission::READ_ONLY;
} else {
MESH_DEBUG_PRINTLN("Incorrect room password");
return; // no response. Client will timeout
}
@@ -334,7 +327,7 @@ protected:
}
MESH_DEBUG_PRINTLN("Login success!");
client->is_admin = is_admin;
client->permission = perm;
client->last_timestamp = sender_timestamp;
client->sync_since = sender_sync_since;
client->pending_ack = 0;
@@ -348,7 +341,7 @@ protected:
// TODO: maybe reply with count of messages waiting to be synced for THIS client?
reply_data[4] = RESP_SERVER_LOGIN_OK;
reply_data[5] = (CLIENT_KEEP_ALIVE_SECS >> 4); // NEW: recommended keep-alive interval (secs / 16)
reply_data[6] = is_admin ? 1 : 0;
reply_data[6] = (perm == RoomPermission::ADMIN ? 1 : (perm == RoomPermission::GUEST ? 0 : 2));
reply_data[7] = 0; // FUTURE: reserved
memcpy(&reply_data[8], "OK", 2); // REVISIT: not really needed
@@ -425,7 +418,7 @@ protected:
uint8_t temp[166];
bool send_ack;
if (flags == TXT_TYPE_CLI_DATA) {
if (client->is_admin) {
if (client->permission == RoomPermission::ADMIN) {
if (is_retry) {
temp[5] = 0; // no reply
} else {
@@ -438,11 +431,16 @@ protected:
send_ack = false; // and no ACK... user shoudn't be sending these
}
} else { // TXT_TYPE_PLAIN
if (!is_retry) {
addPost(client, (const char *) &data[5]);
if (client->permission == RoomPermission::READ_ONLY) {
temp[5] = 0; // no reply
send_ack = false; // no ACK
} else {
if (!is_retry) {
addPost(client, (const char *) &data[5]);
}
temp[5] = 0; // no reply (ACK is enough)
send_ack = true;
}
temp[5] = 0; // no reply (ACK is enough)
send_ack = true;
}
uint32_t delay_millis;
@@ -486,33 +484,81 @@ protected:
} else if (type == PAYLOAD_TYPE_REQ && len >= 5) {
uint32_t sender_timestamp;
memcpy(&sender_timestamp, data, 4); // timestamp (by sender's RTC clock - which could be wrong)
if (data[4] == REQ_TYPE_KEEP_ALIVE && packet->isRouteDirect()) { // request type
uint32_t forceSince = 0;
if (len >= 9) { // optional - last post_timestamp client received
memcpy(&forceSince, &data[5], 4); // NOTE: this may be 0, if part of decrypted PADDING!
} else {
memcpy(&data[5], &forceSince, 4); // make sure there are zeroes in payload (for ack_hash calc below)
}
if (forceSince > 0) {
client->sync_since = forceSince; // force-update the 'sync since'
}
if (sender_timestamp < client->last_timestamp) { // prevent replay attacks
MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected");
} else {
client->last_timestamp = sender_timestamp;
uint32_t now = getRTCClock()->getCurrentTime();
client->last_activity = now; // <-- THIS will keep client connection alive
client->push_failures = 0; // reset so push can resume (if prev failed)
client->pending_ack = 0;
// TODO: Throttle KEEP_ALIVE requests!
// if client sends too quickly, evict()
if (data[4] == REQ_TYPE_KEEP_ALIVE && packet->isRouteDirect()) { // request type
uint32_t forceSince = 0;
if (len >= 9) { // optional - last post_timestamp client received
memcpy(&forceSince, &data[5], 4); // NOTE: this may be 0, if part of decrypted PADDING!
} else {
memcpy(&data[5], &forceSince, 4); // make sure there are zeroes in payload (for ack_hash calc below)
}
if (forceSince > 0) {
client->sync_since = forceSince; // force-update the 'sync since'
}
// RULE: only send keep_alive response DIRECT!
if (client->out_path_len >= 0) {
uint32_t ack_hash; // calc ACK to prove to sender that we got request
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 9, client->id.pub_key, PUB_KEY_SIZE);
client->pending_ack = 0;
auto reply = createAck(ack_hash);
if (reply) {
sendDirect(reply, client->out_path, client->out_path_len);
// TODO: Throttle KEEP_ALIVE requests!
// if client sends too quickly, evict()
// RULE: only send keep_alive response DIRECT!
if (client->out_path_len >= 0) {
uint32_t ack_hash; // calc ACK to prove to sender that we got request
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 9, client->id.pub_key, PUB_KEY_SIZE);
auto reply = createAck(ack_hash);
if (reply) {
sendDirect(reply, client->out_path, client->out_path_len);
}
}
} else if (data[4] == REQ_TYPE_GET_STATUS) {
ServerStats stats;
stats.batt_milli_volts = board.getBattMilliVolts();
stats.curr_tx_queue_len = _mgr->getOutboundCount();
stats.curr_free_queue_len = _mgr->getFreeCount();
stats.last_rssi = (int16_t) my_radio->getLastRSSI();
stats.n_packets_recv = my_radio->getPacketsRecv();
stats.n_packets_sent = my_radio->getPacketsSent();
stats.total_air_time_secs = getTotalAirTime() / 1000;
stats.total_up_time_secs = _ms->getMillis() / 1000;
stats.n_sent_flood = getNumSentFlood();
stats.n_sent_direct = getNumSentDirect();
stats.n_recv_flood = getNumRecvFlood();
stats.n_recv_direct = getNumRecvDirect();
stats.n_full_events = getNumFullEvents();
stats.last_snr = (int16_t)(my_radio->getLastSNR() * 4);
stats.n_direct_dups = ((SimpleMeshTables *)getTables())->getNumDirectDups();
stats.n_flood_dups = ((SimpleMeshTables *)getTables())->getNumFloodDups();
stats.n_posted = _num_posted;
stats.n_post_push = _num_post_pushes;
now = getRTCClock()->getCurrentTimeUnique();
memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp
memcpy(&reply_data[4], &stats, sizeof(stats));
uint8_t reply_len = 4 + sizeof(stats);
if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
mesh::Packet* path = createPathReturn(client->id, secret, packet->path, packet->path_len,
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
if (path) sendFlood(path);
} else {
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len);
if (reply) {
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
sendDirect(reply, client->out_path, client->out_path_len);
} else {
sendFlood(reply);
}
}
}
}
}
@@ -552,7 +598,7 @@ public:
_phy(&phy), _board(&board), _cli(board, this, &_prefs, this)
{
my_radio = &radio;
next_local_advert = 0;
next_local_advert = next_flood_advert = 0;
// defaults
memset(&_prefs, 0, sizeof(_prefs));
@@ -570,6 +616,7 @@ public:
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.disable_fwd = 1;
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_advert_interval = 3; // 3 hours
_prefs.flood_max = 64;
#ifdef ROOM_PASSWORD
StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
@@ -580,6 +627,7 @@ public:
next_client_idx = 0;
next_push = 0;
memset(posts, 0, sizeof(posts));
_num_posted = _num_post_pushes = 0;
}
CommonCLI* getCLI() { return &_cli; }
@@ -634,6 +682,13 @@ public:
next_local_advert = 0; // stop the timer
}
}
void updateFloodAdvertTimer() override {
if (_prefs.flood_advert_interval > 0) { // schedule flood advert timer
next_flood_advert = futureMillis( ((uint32_t)_prefs.flood_advert_interval) * 60 * 60 * 1000);
} else {
next_flood_advert = 0; // stop the timer
}
}
void setLoggingOn(bool enable) override { /* no-op */ }
void eraseLogFile() override { /* no-op */ }
@@ -678,11 +733,15 @@ public:
next_push = futureMillis(SYNC_PUSH_INTERVAL);
}
if (next_local_advert && millisHasNowPassed(next_local_advert)) {
if (next_flood_advert && millisHasNowPassed(next_flood_advert)) {
mesh::Packet* pkt = createSelfAdvert();
if (pkt) {
sendZeroHop(pkt);
}
if (pkt) sendFlood(pkt);
updateFloodAdvertTimer(); // schedule next flood advert
updateAdvertTimer(); // also schedule local advert (so they don't overlap)
} else if (next_local_advert && millisHasNowPassed(next_local_advert)) {
mesh::Packet* pkt = createSelfAdvert();
if (pkt) sendZeroHop(pkt);
updateAdvertTimer(); // schedule next local advert
}
@@ -695,17 +754,6 @@ public:
}
};
#if defined(NRF52_PLATFORM)
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
#elif defined(LILYGO_TLORA)
SPIClass spi;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
#elif defined(P_LORA_SCLK)
SPIClass spi;
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
StdRNG fast_rng;
SimpleMeshTables tables;
@@ -734,37 +782,7 @@ void setup() {
#endif
rtc_clock.begin(Wire);
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
#if defined(NRF52_PLATFORM)
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
#elif defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
delay(5000);
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
halt();
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
if (!radio_init()) { halt(); }
fast_rng.begin(radio.random(0x7FFFFFFF));
@@ -799,7 +817,7 @@ void setup() {
#endif
// send out initial Advertisement to the mesh
the_mesh.sendSelfAdvertisement(2000);
the_mesh.sendSelfAdvertisement(16000);
}
void loop() {

View File

@@ -15,6 +15,7 @@
#include <helpers/SimpleMeshTables.h>
#include <helpers/IdentityStore.h>
#include <RTClib.h>
#include <target.h>
/* ---------------------------------- CONFIGURATION ------------------------------------- */
@@ -49,39 +50,6 @@
#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg=="
#if defined(HELTEC_LORA_V3)
#include <helpers/HeltecV3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static HeltecV3Board board;
#elif defined(HELTEC_LORA_V2)
#include <helpers/HeltecV2Board.h>
#include <helpers/CustomSX1276Wrapper.h>
static HeltecV2Board board;
#elif defined(ARDUINO_XIAO_ESP32C3)
#include <helpers/XiaoC3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomSX1268Wrapper.h>
static XiaoC3Board board;
#elif defined(SEEED_XIAO_S3) || defined(LILYGO_T3S3)
#include <helpers/ESP32Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static ESP32Board board;
#elif defined(LILYGO_TLORA)
#include <helpers/LilyGoTLoraBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
static LilyGoTLoraBoard board;
#elif defined(RAK_4631)
#include <helpers/nrf52/RAK4631Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static RAK4631Board board;
#elif defined(T1000_E)
#include <helpers/nrf52/T1000eBoard.h>
#include <helpers/CustomLR1110Wrapper.h>
static T1000eBoard board;
#else
#error "need to provide a 'board' object"
#endif
// Believe it or not, this std C function is busted on some platforms!
static uint32_t _atoi(const char* sp) {
uint32_t n = 0;
@@ -254,8 +222,8 @@ protected:
return false;
}
void onMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override {
Serial.printf("(%s) MSG -> from %s\n", path_len == 0xFF ? "DIRECT" : "FLOOD", from.name);
void onMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override {
Serial.printf("(%s) MSG -> from %s\n", pkt->isRouteDirect() ? "DIRECT" : "FLOOD", from.name);
Serial.printf(" %s\n", text);
if (strcmp(text, "clock sync") == 0) { // special text command
@@ -263,16 +231,16 @@ protected:
}
}
void onCommandDataRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override {
void onCommandDataRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override {
}
void onSignedMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override {
void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override {
}
void onChannelMessageRecv(const mesh::GroupChannel& channel, int in_path_len, uint32_t timestamp, const char *text) override {
if (in_path_len < 0) {
void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) override {
if (pkt->isRouteDirect()) {
Serial.printf("PUBLIC CHANNEL MSG -> (Direct!)\n");
} else {
Serial.printf("PUBLIC CHANNEL MSG -> (Flood) hops %d\n", in_path_len);
Serial.printf("PUBLIC CHANNEL MSG -> (Flood) hops %d\n", pkt->path_len);
}
Serial.printf(" %s\n", text);
}
@@ -543,17 +511,6 @@ public:
}
};
#if defined(NRF52_PLATFORM)
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
#elif defined(LILYGO_TLORA)
SPIClass spi;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
#elif defined(P_LORA_SCLK)
SPIClass spi;
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
StdRNG fast_rng;
SimpleMeshTables tables;
MyMesh the_mesh(*new WRAPPER_CLASS(radio, board), fast_rng, *new VolatileRTCClock(), tables);
@@ -566,34 +523,8 @@ void setup() {
Serial.begin(115200);
board.begin();
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
#if defined(NRF52_PLATFORM)
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
#elif defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
halt();
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
if (!radio_init()) { halt(); }
fast_rng.begin(radio.random(0x7FFFFFFF));

View File

@@ -8,6 +8,10 @@
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
extra_configs =
variants/*/platformio.ini
[arduino_base]
framework = arduino
monitor_speed = 115200
@@ -26,6 +30,8 @@ build_src_filter =
+<*.cpp>
+<helpers/*.cpp>
; ----------------- ESP32 ---------------------
[esp32_base]
extends = arduino_base
platform = espressif32
@@ -35,624 +41,10 @@ build_flags = ${arduino_base.build_flags}
; -D ESP32_CPU_FREQ=80 ; change it to your need
build_src_filter = ${arduino_base.build_src_filter}
; ================
[Heltec_lora32_v2]
extends = esp32_base
board = heltec_wifi_lora_32_V2 ; heltec_wifi_lora_32_V2
build_flags =
${esp32_base.build_flags}
-D HELTEC_LORA_V2
-D RADIO_CLASS=CustomSX1276
-D WRAPPER_CLASS=CustomSX1276Wrapper
-D LORA_TX_POWER=20
-D P_LORA_TX_LED=25
build_src_filter = ${esp32_base.build_src_filter}
[env:Heltec_v2_repeater]
extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<../examples/simple_repeater/main.cpp>
[env:Heltec_v2_terminal_chat]
extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
[esp32_ota]
lib_deps =
${Heltec_lora32_v2.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_v2_companion_radio_usb]
extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<helpers/esp32/*.cpp>
+<../examples/companion_radio/main.cpp>
lib_deps =
${Heltec_lora32_v2.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_v2_companion_radio_ble]
extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<helpers/esp32/*.cpp>
+<../examples/companion_radio/main.cpp>
lib_deps =
${Heltec_lora32_v2.lib_deps}
densaugeo/base64 @ ~1.4.0
; ================
[Heltec_lora32_v3]
extends = esp32_base
board = esp32-s3-devkitc-1
build_flags =
${esp32_base.build_flags}
-D HELTEC_LORA_V3
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D P_LORA_TX_LED=35
-D PIN_BOARD_SDA=17
-D PIN_BOARD_SCL=18
-D PIN_USER_BTN=0
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=130.0f ; for best TX power!
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${esp32_base.build_src_filter}
lib_deps =
${esp32_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
[env:Heltec_v3_repeater]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_repeater>
[env:Heltec_v3_room_server]
extends = Heltec_lora32_v3
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_room_server>
build_flags =
${Heltec_lora32_v3.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Heltec Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:Heltec_v3_terminal_chat]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${Heltec_lora32_v3.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_v3_companion_radio_usb]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D DISPLAY_CLASS=SSD1306Display
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${Heltec_lora32_v3.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_v3_companion_radio_ble]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D DISPLAY_CLASS=SSD1306Display
-D BLE_PIN_CODE=0 ; dynamic, random PIN
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/*.cpp>
+<helpers/esp32/*.cpp>
+<../examples/companion_radio>
lib_deps =
${Heltec_lora32_v3.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_v3_companion_radio_wifi]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D DISPLAY_CLASS=SSD1306Display
-D WIFI_DEBUG_LOGGING=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/*.cpp>
+<helpers/esp32/*.cpp>
+<../examples/companion_radio>
lib_deps =
${Heltec_lora32_v3.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_WSL3_repeater]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<../examples/simple_repeater>
[env:Heltec_WSL3_room_server]
extends = Heltec_lora32_v3
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<../examples/simple_room_server>
build_flags =
${Heltec_lora32_v3.build_flags}
-D ADVERT_NAME='"Heltec Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:Heltec_WSL3_companion_radio_ble]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/esp32/*.cpp>
+<../examples/companion_radio>
lib_deps =
${Heltec_lora32_v3.lib_deps}
densaugeo/base64 @ ~1.4.0
; ================
[Xiao_esp32_C3]
extends = esp32_base
board = seeed_xiao_esp32c3
build_flags =
${esp32_base.build_flags}
-D LORA_TX_BOOST_PIN=D3
-D P_LORA_TX_LED=D5
-D PIN_VBAT_READ=D0
-D P_LORA_DIO_1=D2
-D P_LORA_NSS=D4
-D P_LORA_RESET=RADIOLIB_NC
-D P_LORA_BUSY=D1
-D PIN_BOARD_SDA=D6
-D PIN_BOARD_SCL=D7
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=130.0f ; for best TX power!
[env:Xiao_C3_Repeater_sx1262]
extends = Xiao_esp32_C3
build_src_filter = ${Xiao_esp32_C3.build_src_filter}
+<../examples/simple_repeater/main.cpp>
build_flags =
${Xiao_esp32_C3.build_flags}
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D SX126X_RX_BOOSTED_GAIN=1
-D LORA_TX_POWER=22
-D ADVERT_NAME='"Xiao Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:Xiao_C3_Repeater_sx1268]
extends = Xiao_esp32_C3
build_src_filter = ${Xiao_esp32_C3.build_src_filter}
+<../examples/simple_repeater/main.cpp>
build_flags =
${Xiao_esp32_C3.build_flags}
-D RADIO_CLASS=CustomSX1268
-D WRAPPER_CLASS=CustomSX1268Wrapper
-D LORA_TX_POWER=22
-D ADVERT_NAME='"Xiao Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
; =============
[Xiao_S3_WIO]
extends = esp32_base
board = seeed_xiao_esp32s3
board_check = true
board_build.mcu = esp32s3
build_flags = ${esp32_base.build_flags}
-D SEEED_XIAO_S3
-D P_LORA_DIO_1=39
-D P_LORA_NSS=41
-D P_LORA_RESET=42 ; RADIOLIB_NC
-D P_LORA_BUSY=40 ; DIO2 = 38
-D P_LORA_SCLK=7
-D P_LORA_MISO=8
-D P_LORA_MOSI=9
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=130
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_RX_BOOSTED_GAIN=1
[env:Xiao_S3_WIO_Repeater]
extends = Xiao_S3_WIO
build_src_filter = ${Xiao_S3_WIO.build_src_filter}
+<../examples/simple_repeater/main.cpp>
build_flags =
${Xiao_S3_WIO.build_flags}
-D ADVERT_NAME='"XiaoS3 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:Xiao_S3_WIO_terminal_chat]
extends = Xiao_S3_WIO
build_flags =
${Xiao_S3_WIO.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Xiao_S3_WIO.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${Xiao_S3_WIO.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Xiao_S3_WIO_companion_radio_ble]
extends = Xiao_S3_WIO
build_flags =
${Xiao_S3_WIO.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Xiao_S3_WIO.build_src_filter}
+<helpers/esp32/*.cpp>
+<../examples/companion_radio/main.cpp>
lib_deps =
${Xiao_S3_WIO.lib_deps}
densaugeo/base64 @ ~1.4.0
; =============
[LilyGo_TLora_V2_1_1_6]
extends = esp32_base
board = ttgo-lora32-v1 ; LILYGO T-LoRa V2.1-1.6 ESP32 with SX1276
build_unflags = -Os
build_type = release ; Set build type to release
board_build.partitions = min_spiffs.csv ; get around 4mb flash limit
build_flags =
${esp32_base.build_flags}
-Os -ffunction-sections -fdata-sections ; Optimize for size
-D LILYGO_TLORA ; LILYGO T-LoRa V2.1-1.6 ESP32 with SX1276
-D P_LORA_DIO_0=26 ; SX1276 DIO0 interrupt pin
-D P_LORA_DIO_1=33 ; SX1276 DIO1 interrupt pin
-D P_LORA_NSS=18 ; Chip select - SS pin
-D P_LORA_RESET=14 ; Reset pin
-D P_LORA_SCLK=5 ; SPI clock
-D P_LORA_MISO=19 ; SPI MISO
-D P_LORA_MOSI=27 ; SPI MOSI
-D P_LORA_TX_LED=2 ; LED pin for TX indication
-D PIN_VBAT_READ=35 ; Battery voltage reading (analog pin)
-D PIN_USER_BTN=0
-D RADIO_CLASS=CustomSX1276
-D ARDUINO_LOOP_STACK_SIZE=16384
-D DISPLAY_CLASS=SSD1306Display
-D WRAPPER_CLASS=CustomSX1276Wrapper
-D LORA_TX_POWER=20
lib_deps =
${esp32_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
; === LILYGO T-LoRa V2.1-1.6 with SX1276 environments ===
[env:LilyGo_TLora_V2_1_1_6_Repeater]
extends = LilyGo_TLora_V2_1_1_6
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_repeater>
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D ADVERT_NAME='"TLora-V2.1-1.6 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
; -D CORE_DEBUG_LEVEL=3
[env:LilyGo_TLora_V2_1_1_6_terminal_chat]
extends = LilyGo_TLora_V2_1_1_6
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_repeater>
lib_deps =
${LilyGo_TLora_V2_1_1_6.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_TLora_V2_1_1_6_companion_radio_usb]
extends = LilyGo_TLora_V2_1_1_6
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${LilyGo_TLora_V2_1_1_6.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_TLora_V2_1_1_6_companion_radio_ble]
extends = LilyGo_TLora_V2_1_1_6
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${LilyGo_TLora_V2_1_1_6.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_TLora_V2_1_1_6_room_server]
extends = LilyGo_TLora_V2_1_1_6
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_room_server>
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D ADVERT_NAME='"TLora-V2.1-1.6 Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
; =============
[LilyGo_T3S3_sx1262]
extends = esp32_base
board = t3_s3_v1_x
build_flags = ${esp32_base.build_flags}
-D LILYGO_T3S3
-D P_LORA_DIO_1=33
-D P_LORA_NSS=7
-D P_LORA_RESET=8 ; RADIOLIB_NC
-D P_LORA_BUSY=34
-D P_LORA_SCLK=5
-D P_LORA_MISO=3
-D P_LORA_MOSI=6
-D P_LORA_TX_LED=37
-D PIN_VBAT_READ=1
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=130
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_RX_BOOSTED_GAIN=1
; === LilyGo T3S3 with SX1262 environments ===
[env:LilyGo_T3S3_sx1262_Repeater]
extends = LilyGo_T3S3_sx1262
build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
+<../examples/simple_repeater/main.cpp>
build_flags =
${LilyGo_T3S3_sx1262.build_flags}
-D ADVERT_NAME='"T3S3-1262 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:LilyGo_T3S3_sx1262_terminal_chat]
extends = LilyGo_T3S3_sx1262
build_flags =
${LilyGo_T3S3_sx1262.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${LilyGo_T3S3_sx1262.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_T3S3_sx1262_companion_radio_usb]
extends = LilyGo_T3S3_sx1262
build_flags =
${LilyGo_T3S3_sx1262.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
+<../examples/companion_radio/main.cpp>
lib_deps =
${LilyGo_T3S3_sx1262.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_T3S3_sx1262_companion_radio_ble]
extends = LilyGo_T3S3_sx1262
build_flags =
${LilyGo_T3S3_sx1262.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
+<helpers/esp32/*.cpp>
+<../examples/companion_radio/main.cpp>
lib_deps =
${LilyGo_T3S3_sx1262.lib_deps}
densaugeo/base64 @ ~1.4.0
[Station_G2]
extends = esp32_base
board = station-g2
build_flags =
${esp32_base.build_flags}
-D STATION_G2
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=7
; -D P_LORA_TX_LED=35
; -D PIN_BOARD_SDA=5
; -D PIN_BOARD_SCL=6
-D PIN_USER_BTN=0
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=130.0f ; for best TX power!
; -D SX126X_RX_BOOSTED_GAIN=1 - DO NOT ENABLE THIS!
; https://wiki.uniteng.com/en/meshtastic/station-g2#impact-of-lora-node-dense-areashigh-noise-environments-on-rf-performance
build_src_filter = ${esp32_base.build_src_filter}
lib_deps =
${esp32_base.lib_deps}
[env:Station_G2_repeater]
extends = Station_G2
build_flags =
${Station_G2.build_flags}
-D ADVERT_NAME='"Station G2 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Station_G2.build_src_filter}
+<../examples/simple_repeater>
[env:Station_G2_room_server]
extends = Station_G2
build_src_filter = ${Station_G2.build_src_filter}
+<../examples/simple_room_server>
build_flags =
${Station_G2.build_flags}
-D ADVERT_NAME='"Station G2 Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
me-no-dev/ESPAsyncWebServer @ ^3.6.0
file://arch/esp32/AsyncElegantOTA
; ----------------- NRF52 ---------------------
[nrf52_base]
@@ -668,293 +60,3 @@ lib_deps =
${nrf52_base.lib_deps}
rweather/Crypto @ ^0.4.0
https://github.com/adafruit/Adafruit_nRF52_Arduino
[rak4631]
extends = nrf52840_base
platform = https://github.com/maxgerhardt/platform-nordicnrf52.git#rak
board = wiscore_rak4631
board_check = true
build_src_filter = ${nrf52840_base.build_src_filter}
+<helpers/nrf52/*.cpp>
build_flags = ${nrf52840_base.build_flags}
-D RAK_4631
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=130
-D SX126X_RX_BOOSTED_GAIN=1
[env:RAK_4631_Repeater]
extends = rak4631
build_src_filter = ${rak4631.build_src_filter}
+<../examples/simple_repeater/main.cpp>
build_flags =
${rak4631.build_flags}
-D ADVERT_NAME='"RAK4631 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:RAK_4631_room_server]
extends = rak4631
build_src_filter = ${rak4631.build_src_filter}
+<../examples/simple_room_server/main.cpp>
build_flags =
${rak4631.build_flags}
-D ADVERT_NAME='"Test Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:RAK_4631_terminal_chat]
extends = rak4631
build_flags =
${rak4631.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
-D MESH_PACKET_LOGGING=1
-D MESH_DEBUG=1
build_src_filter = ${rak4631.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${rak4631.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:RAK_4631_companion_radio_usb]
extends = rak4631
build_flags =
${rak4631.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${rak4631.build_src_filter}
+<../examples/companion_radio/main.cpp>
lib_deps =
${rak4631.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:RAK_4631_companion_radio_ble]
extends = rak4631
build_flags =
${rak4631.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${rak4631.build_src_filter}
+<helpers/nrf52/*.cpp>
+<../examples/companion_radio/main.cpp>
lib_deps =
${rak4631.lib_deps}
densaugeo/base64 @ ~1.4.0
; ----------------- NRF52 T1000e---------------------
[nrf52840_t1000e]
extends = nrf52_base
platform_packages = framework-arduinoadafruitnrf52
build_flags = ${nrf52_base.build_flags}
-I src/helpers/nrf52
-I lib/nrf52/s140_nrf52_7.3.0_API/include
-I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52
lib_ignore =
BluetoothOTA
lvgl
lib5b4
lib_deps =
${nrf52_base.lib_deps}
rweather/Crypto @ ^0.4.0
[t1000-e]
extends = nrf52840_t1000e
board = tracker-t1000-e
board_build.ldscript = boards/nrf52840_s140_v7.ld
build_flags = ${nrf52840_t1000e.build_flags}
-Ivariants/t1000-e
-DT1000_E
-D PIN_USER_BTN=6
-D RADIO_CLASS=CustomLR1110
-D WRAPPER_CLASS=CustomLR1110Wrapper
-D MAX_LORA_TX_POWER=22
build_src_filter = ${nrf52840_t1000e.build_src_filter}
+<helpers/*.cpp>
+<helpers/nrf52/T1000eBoard.cpp>
+<../variants/t1000-e>
debug_tool = jlink
upload_protocol = nrfutil
[env:t1000e_companion_radio_ble]
extends = t1000-e
build_flags = ${t1000-e.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
-D MESH_PACKET_LOGGING=1
-D MESH_DEBUG=1
build_src_filter = ${t1000-e.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp>
lib_deps = ${t1000-e.lib_deps}
densaugeo/base64 @ ~1.4.0
[nrf52840_t114]
extends = nrf52_base
platform_packages = framework-arduinoadafruitnrf52
build_flags = ${nrf52_base.build_flags}
-I src/helpers/nrf52
-I lib/nrf52/s140_nrf52_6.1.1_API/include
-I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
lib_deps =
${nrf52_base.lib_deps}
rweather/Crypto @ ^0.4.0
[Heltec_t114]
extends = nrf52840_t114
board = heltec_t114
board_build.ldscript = boards/nrf52840_s140_v6.ld
build_flags = ${nrf52840_t114.build_flags}
-Ivariants/t114
-DHELTEC_T114
-D P_LORA_TX_LED=35
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=130
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${nrf52840_t114.build_src_filter}
+<helpers/*.cpp>
+<helpers/nrf52/T114Board.cpp>
+<../variants/t114>
debug_tool = jlink
upload_protocol = nrfutil
[env:Heltec_t114_repeater]
extends = Heltec_t114
build_src_filter = ${Heltec_t114.build_src_filter}
+<../examples/simple_repeater/main.cpp>
build_flags =
${Heltec_t114.build_flags}
-D ADVERT_NAME='"Heltec_T114 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:Heltec_t114_room_server]
extends = Heltec_t114
build_src_filter = ${Heltec_t114.build_src_filter}
+<../examples/simple_room_server>
build_flags =
${Heltec_t114.build_flags}
-D ADVERT_NAME='"Heltec_T114 Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:Heltec_t114_companion_radio_ble]
extends = Heltec_t114
build_flags =
${Heltec_t114.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_t114.build_src_filter}
+<helpers/nrf52/*.cpp>
+<../examples/companion_radio/main.cpp>
lib_deps =
${Heltec_t114.lib_deps}
densaugeo/base64 @ ~1.4.0
[nrf52840_techo]
extends = nrf52_base
platform_packages = framework-arduinoadafruitnrf52
build_flags = ${nrf52_base.build_flags}
-I src/helpers/nrf52
-I lib/nrf52/s140_nrf52_6.1.1_API/include
-I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
lib_deps =
${nrf52_base.lib_deps}
rweather/Crypto @ ^0.4.0
[LilyGo_Techo]
extends = nrf52840_techo
board = t-echo
board_build.ldscript = boards/nrf52840_s140_v6.ld
build_flags = ${nrf52840_techo.build_flags}
-Ivariants/techo
-DLILYGO_TECHO
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=130
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${nrf52840_techo.build_src_filter}
+<helpers/*.cpp>
+<helpers/nrf52/TechoBoard.cpp>
+<../variants/techo>
debug_tool = jlink
upload_protocol = nrfutil
[env:LilyGo_T-Echo_repeater]
extends = LilyGo_Techo
build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_repeater/main.cpp>
build_flags =
${LilyGo_Techo.build_flags}
-D ADVERT_NAME='"T-Echo Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:LilyGo_T-Echo_room_server]
extends = LilyGo_Techo
build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_room_server/main.cpp>
build_flags =
${LilyGo_Techo.build_flags}
-D ADVERT_NAME='"T-Echo Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:LilyGo_T-Echo_companion_radio_ble]
extends = LilyGo_Techo
build_flags =
${LilyGo_Techo.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_Techo.build_src_filter}
+<helpers/nrf52/*.cpp>
+<../examples/companion_radio/main.cpp>
lib_deps =
${LilyGo_Techo.lib_deps}
densaugeo/base64 @ ~1.4.0

View File

@@ -115,6 +115,7 @@ void Dispatcher::checkRecv() {
} else {
memcpy(pkt->payload, &raw[i], pkt->payload_len);
pkt->_snr = _radio->getLastSNR() * 4.0f;
score = _radio->packetScore(_radio->getLastSNR(), len);
air_time = _radio->getEstAirtimeFor(len);
}
@@ -129,7 +130,7 @@ void Dispatcher::checkRecv() {
Serial.print(getLogDateTime());
Serial.printf(": RX, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d",
2 + pkt->path_len + pkt->payload_len, pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
(int)_radio->getLastSNR(), (int)_radio->getLastRSSI(), (int)(score*1000));
(int)pkt->getSNR(), (int)_radio->getLastRSSI(), (int)(score*1000));
static uint8_t packet_hash[MAX_HASH_SIZE];
pkt->calculatePacketHash(packet_hash);
@@ -233,6 +234,7 @@ Packet* Dispatcher::obtainNewPacket() {
n_full_events++;
} else {
pkt->payload_len = pkt->path_len = 0;
pkt->_snr = 0;
}
return pkt;
}

View File

@@ -2,6 +2,7 @@
#include <string.h>
#define ED25519_NO_SEED 1
#include <ed_25519.h>
#include <Ed25519.h>
namespace mesh {
@@ -14,7 +15,12 @@ Identity::Identity(const char* pub_hex) {
}
bool Identity::verify(const uint8_t* sig, const uint8_t* message, int msg_len) const {
#if 0
// NOTE: memory corruption bug was found in this function!!
return ed25519_verify(sig, message, msg_len, pub_key);
#else
return Ed25519::verify(sig, this->pub_key, message, msg_len);
#endif
}
bool Identity::readFrom(Stream& s) {

View File

@@ -41,6 +41,30 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
return ACTION_RELEASE;
}
if (pkt->isRouteDirect() && pkt->getPayloadType() == PAYLOAD_TYPE_TRACE) {
if (pkt->path_len < MAX_PATH_SIZE) {
uint8_t i = 0;
uint32_t trace_tag;
memcpy(&trace_tag, &pkt->payload[i], 4); i += 4;
uint32_t auth_code;
memcpy(&auth_code, &pkt->payload[i], 4); i += 4;
uint8_t flags = pkt->payload[i++];
uint8_t len = pkt->payload_len - i;
if (pkt->path_len >= len) { // TRACE has reached end of given path
onTraceRecv(pkt, trace_tag, auth_code, flags, pkt->path, &pkt->payload[i], len);
} else if (self_id.isHashMatch(&pkt->payload[i + pkt->path_len]) && allowPacketForward(pkt) && !_tables->hasSeen(pkt)) {
// append SNR (Not hash!)
pkt->path[pkt->path_len] = (int8_t) (pkt->getSNR()*4);
pkt->path_len += PATH_HASH_SIZE;
uint32_t d = getDirectRetransmitDelay(pkt);
return ACTION_RETRANSMIT_DELAYED(5, d); // schedule with priority 5 (for now), maybe make configurable?
}
}
return ACTION_RELEASE;
}
if (pkt->isRouteDirect() && pkt->path_len >= PATH_HASH_SIZE) {
if (self_id.isHashMatch(pkt->path) && allowPacketForward(pkt)) {
if (_tables->hasSeen(pkt)) return ACTION_RELEASE; // don't retransmit!
@@ -79,7 +103,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
uint8_t src_hash = pkt->payload[i++];
uint8_t* macAndData = &pkt->payload[i]; // MAC + encrypted data
if (i + 2 >= pkt->payload_len) {
if (i + CIPHER_MAC_SIZE >= pkt->payload_len) {
MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime());
} else if (!_tables->hasSeen(pkt)) {
// NOTE: this is a 'first packet wins' impl. When receiving from multiple paths, the first to arrive wins.
@@ -333,7 +357,7 @@ Packet* Mesh::createPathReturn(const uint8_t* dest_hash, const uint8_t* secret,
Packet* Mesh::createDatagram(uint8_t type, const Identity& dest, const uint8_t* secret, const uint8_t* data, size_t data_len) {
if (type == PAYLOAD_TYPE_TXT_MSG || type == PAYLOAD_TYPE_REQ || type == PAYLOAD_TYPE_RESPONSE) {
if (data_len + 2 + CIPHER_BLOCK_SIZE-1 > MAX_PACKET_PAYLOAD) return NULL;
if (data_len + CIPHER_MAC_SIZE + CIPHER_BLOCK_SIZE-1 > MAX_PACKET_PAYLOAD) return NULL;
} else {
return NULL; // invalid type
}
@@ -433,7 +457,28 @@ Packet* Mesh::createRawData(const uint8_t* data, size_t len) {
return packet;
}
Packet* Mesh::createTrace(uint32_t tag, uint32_t auth_code, uint8_t flags) {
Packet* packet = obtainNewPacket();
if (packet == NULL) {
MESH_DEBUG_PRINTLN("%s Mesh::createTrace(): error, packet pool empty", getLogDateTime());
return NULL;
}
packet->header = (PAYLOAD_TYPE_TRACE << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later
memcpy(packet->payload, &tag, 4);
memcpy(&packet->payload[4], &auth_code, 4);
packet->payload[8] = flags;
packet->payload_len = 9; // NOTE: path will be appended to payload[] later
return packet;
}
void Mesh::sendFlood(Packet* packet, uint32_t delay_millis) {
if (packet->getPayloadType() == PAYLOAD_TYPE_TRACE) {
MESH_DEBUG_PRINTLN("%s Mesh::sendFlood(): TRACE type not suspported", getLogDateTime());
return;
}
packet->header &= ~PH_ROUTE_MASK;
packet->header |= ROUTE_TYPE_FLOOD;
packet->path_len = 0;
@@ -455,11 +500,20 @@ void Mesh::sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uin
packet->header &= ~PH_ROUTE_MASK;
packet->header |= ROUTE_TYPE_DIRECT;
memcpy(packet->path, path, packet->path_len = path_len);
uint8_t pri;
if (packet->getPayloadType() == PAYLOAD_TYPE_TRACE) { // TRACE packets are different
// for TRACE packets, path is appended to end of PAYLOAD. (path is used for SNR's)
memcpy(&packet->payload[packet->payload_len], path, path_len);
packet->payload_len += path_len;
packet->path_len = 0;
pri = 5; // maybe make this configurable
} else {
memcpy(packet->path, path, packet->path_len = path_len);
pri = 0;
}
_tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us
sendPacket(packet, 0, delay_millis);
sendPacket(packet, pri, delay_millis);
}
void Mesh::sendZeroHop(Packet* packet, uint32_t delay_millis) {

View File

@@ -4,34 +4,6 @@
namespace mesh {
/**
* An abstraction of the device's Realtime Clock.
*/
class RTCClock {
uint32_t last_unique;
protected:
RTCClock() { last_unique = 0; }
public:
/**
* \returns the current time. in UNIX epoch seconds.
*/
virtual uint32_t getCurrentTime() = 0;
/**
* \param time current time in UNIX epoch seconds.
*/
virtual void setCurrentTime(uint32_t time) = 0;
uint32_t getCurrentTimeUnique() {
uint32_t t = getCurrentTime();
if (t <= last_unique) {
return ++last_unique;
}
return last_unique = t;
}
};
class GroupChannel {
public:
uint8_t hash[PATH_HASH_SIZE];
@@ -100,9 +72,22 @@ protected:
* \param type one of: PAYLOAD_TYPE_TXT_MSG, PAYLOAD_TYPE_REQ, PAYLOAD_TYPE_RESPONSE
* \param sender_idx index of peer, [0..n) where n is what searchPeersByHash() returned
* \param secret the pre-calculated shared-secret (handy for sending response packet)
* \param data decrypted data from payload
*/
virtual void onPeerDataRecv(Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) { }
/**
* \brief A TRACE packet has been received. (and has reached the end of its given path)
* NOTE: this may have been initiated by another node.
* \param tag a random (unique-ish) tag set by initiator
* \param auth_code a code to authenticate the packet
* \param flags zero for now
* \param path_snrs single byte SNR*4 for each hop in the path
* \param path_hashes hashes if each repeater in the path
* \param path_len length of the path_snrs[] and path_hashes[] arrays
*/
virtual void onTraceRecv(Packet* packet, uint32_t tag, uint32_t auth_code, uint8_t flags, const uint8_t* path_snrs, const uint8_t* path_hashes, uint8_t path_len) { }
/**
* \brief A path TO peer (sender_idx) has been received. (also with optional 'extra' data encoded)
* NOTE: these can be received multiple times (per sender), via differen routes
@@ -182,6 +167,7 @@ public:
Packet* createPathReturn(const uint8_t* dest_hash, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len);
Packet* createPathReturn(const Identity& dest, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len);
Packet* createRawData(const uint8_t* data, size_t len);
Packet* createTrace(uint32_t tag, uint32_t auth_code, uint8_t flags = 0);
/**
* \brief send a locally-generated Packet with flood routing
@@ -197,6 +183,7 @@ public:
* \brief send a locally-generated Packet to just neigbor nodes (zero hops)
*/
void sendZeroHop(Packet* packet, uint32_t delay_millis=0);
};
}

View File

@@ -40,8 +40,37 @@ public:
virtual void onBeforeTransmit() { }
virtual void onAfterTransmit() { }
virtual void reboot() = 0;
virtual void powerOff() { /* no op */ }
virtual uint8_t getStartupReason() const = 0;
virtual bool startOTAUpdate() { return false; } // not supported
virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported
};
/**
* An abstraction of the device's Realtime Clock.
*/
class RTCClock {
uint32_t last_unique;
protected:
RTCClock() { last_unique = 0; }
public:
/**
* \returns the current time. in UNIX epoch seconds.
*/
virtual uint32_t getCurrentTime() = 0;
/**
* \param time current time in UNIX epoch seconds.
*/
virtual void setCurrentTime(uint32_t time) = 0;
uint32_t getCurrentTimeUnique() {
uint32_t t = getCurrentTime();
if (t <= last_unique) {
return ++last_unique;
}
return last_unique = t;
}
};
}

View File

@@ -10,11 +10,13 @@ Packet::Packet() {
payload_len = 0;
}
void Packet::calculatePacketHash(uint8_t* hash) const {
SHA256 sha;
uint8_t t = getPayloadType();
sha.update(&t, 1);
if (t == PAYLOAD_TYPE_TRACE) {
sha.update(&path_len, sizeof(path_len)); // CAVEAT: TRACE packets can revisit same node on return path
}
sha.update(payload, payload_len);
sha.finalize(hash, MAX_HASH_SIZE);
}

View File

@@ -25,6 +25,7 @@ namespace mesh {
#define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, 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
//...
#define PAYLOAD_TYPE_RAW_CUSTOM 0x0F // custom packet as raw bytes, for applications with custom encryption, payloads, etc
@@ -44,6 +45,7 @@ public:
uint16_t payload_len, path_len;
uint8_t path[MAX_PATH_SIZE];
uint8_t payload[MAX_PACKET_PAYLOAD];
int8_t _snr;
/**
* \brief calculate the hash of payload + type
@@ -72,6 +74,8 @@ public:
void markDoNotRetransmit() { header = 0xFF; }
bool isMarkedDoNotRetransmit() const { return header == 0xFF; }
float getSNR() const { return ((float)_snr) / 4.0f; }
/**
* \brief save entire packet as a blob
* \param dest (OUT) destination buffer (assumed to be MAX_MTU_SIZE)

View File

@@ -23,7 +23,12 @@ public:
#ifdef LILYGO_T3S3
void begin(HWCDC& serial) { _serial = &serial; }
#elif defined(NRF52_PLATFORM)
void begin(Adafruit_USBD_CDC& serial) { _serial = &serial; }
void begin(Adafruit_USBD_CDC& serial) {
_serial = &serial;
#ifdef RAK_4631
pinMode(WB_IO2, OUTPUT);
#endif
}
#else
void begin(HardwareSerial& serial) { _serial = &serial; }
#endif

View File

@@ -105,7 +105,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
data[len] = 0; // need to make a C string again, with null terminator
if (flags == TXT_TYPE_PLAIN) {
onMessageRecv(from, packet->isRouteFlood() ? packet->path_len : 0xFF, timestamp, (const char *) &data[5]); // let UI know
onMessageRecv(from, packet, timestamp, (const char *) &data[5]); // let UI know
uint32_t ack_hash; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 5 + strlen((char *)&data[5]), from.id.pub_key, PUB_KEY_SIZE);
@@ -126,7 +126,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
}
}
} else if (flags == TXT_TYPE_CLI_DATA) {
onCommandDataRecv(from, packet->isRouteFlood() ? packet->path_len : 0xFF, timestamp, (const char *) &data[5]); // let UI know
onCommandDataRecv(from, packet, timestamp, (const char *) &data[5]); // let UI know
// NOTE: no ack expected for CLI_DATA replies
if (packet->isRouteFlood()) {
@@ -138,7 +138,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
if (timestamp > from.sync_since) { // make sure 'sync_since' is up-to-date
from.sync_since = timestamp;
}
onSignedMessageRecv(from, packet->isRouteFlood() ? packet->path_len : 0xFF, timestamp, &data[5], (const char *) &data[9]); // let UI know
onSignedMessageRecv(from, packet, timestamp, &data[5], (const char *) &data[9]); // let UI know
uint32_t ack_hash; // calc truncated hash of the message timestamp + text + OUR pub_key, to prove to sender that we got it
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 9 + strlen((char *)&data[9]), self_id.pub_key, PUB_KEY_SIZE);
@@ -222,13 +222,14 @@ void BaseChatMesh::onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mes
data[len] = 0; // need to make a C string again, with null terminator
// notify UI of this new message
onChannelMessageRecv(channel, packet->isRouteFlood() ? packet->path_len : -1, timestamp, (const char *) &data[5]); // let UI know
onChannelMessageRecv(channel, packet, timestamp, (const char *) &data[5]); // let UI know
}
}
mesh::Packet* BaseChatMesh::composeMsgPacket(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char *text, uint32_t& expected_ack) {
int text_len = strlen(text);
if (text_len > MAX_TEXT_LEN) return NULL;
if (attempt > 3 && text_len > MAX_TEXT_LEN-2) return NULL;
uint8_t temp[5+MAX_TEXT_LEN+1];
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique
@@ -238,7 +239,13 @@ mesh::Packet* BaseChatMesh::composeMsgPacket(const ContactInfo& recipient, uint3
// calc expected ACK reply
mesh::Utils::sha256((uint8_t *)&expected_ack, 4, temp, 5 + text_len, self_id.pub_key, PUB_KEY_SIZE);
return createDatagram(PAYLOAD_TYPE_TXT_MSG, recipient.id, recipient.shared_secret, temp, 5 + text_len);
int len = 5 + text_len;
if (attempt > 3) {
temp[len++] = 0; // null terminator
temp[len++] = attempt; // hide attempt number at tail end of payload
}
return createDatagram(PAYLOAD_TYPE_TXT_MSG, recipient.id, recipient.shared_secret, temp, len);
}
int BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout) {

View File

@@ -106,13 +106,13 @@ protected:
virtual void onDiscoveredContact(ContactInfo& contact, bool is_new) = 0;
virtual bool processAck(const uint8_t *data) = 0;
virtual void onContactPathUpdated(const ContactInfo& contact) = 0;
virtual void onMessageRecv(const ContactInfo& contact, uint8_t path_len, uint32_t sender_timestamp, const char *text) = 0;
virtual void onCommandDataRecv(const ContactInfo& contact, uint8_t path_len, uint32_t sender_timestamp, const char *text) = 0;
virtual void onSignedMessageRecv(const ContactInfo& contact, uint8_t path_len, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) = 0;
virtual void onMessageRecv(const ContactInfo& contact, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) = 0;
virtual void onCommandDataRecv(const ContactInfo& contact, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) = 0;
virtual void onSignedMessageRecv(const ContactInfo& contact, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) = 0;
virtual uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const = 0;
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, int in_path_len, uint32_t timestamp, const char *text) = 0;
virtual void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) = 0;
virtual void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) = 0;
// storage concepts, for sub-classes to override/implement

View File

@@ -14,57 +14,66 @@ static uint32_t _atoi(const char* sp) {
}
void CommonCLI::loadPrefs(FILESYSTEM* fs) {
if (fs->exists("/node_prefs")) {
File file = fs->open("/node_prefs");
if (file) {
uint8_t pad[8];
if (fs->exists("/com_prefs")) {
loadPrefsInt(fs, "/com_prefs"); // new filename
} else if (fs->exists("/node_prefs")) {
loadPrefsInt(fs, "/node_prefs");
savePrefs(fs); // save to new filename
fs->remove("/node_prefs"); // remove old
}
}
file.read((uint8_t *) &_prefs->airtime_factor, sizeof(_prefs->airtime_factor)); // 0
file.read((uint8_t *) &_prefs->node_name, sizeof(_prefs->node_name)); // 4
file.read(pad, 4); // 36
file.read((uint8_t *) &_prefs->node_lat, sizeof(_prefs->node_lat)); // 40
file.read((uint8_t *) &_prefs->node_lon, sizeof(_prefs->node_lon)); // 48
file.read((uint8_t *) &_prefs->password[0], sizeof(_prefs->password)); // 56
file.read((uint8_t *) &_prefs->freq, sizeof(_prefs->freq)); // 72
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->unused, sizeof(_prefs->unused)); // 79
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
file.read((uint8_t *) &_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104
file.read(pad, 4); // 108
file.read((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112
file.read((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113
file.read((uint8_t *) &_prefs->reserved1, sizeof(_prefs->reserved1)); // 114
file.read((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115
file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116
file.read(pad, 4); // 120
file.read((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124
void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
File file = fs->open(filename);
if (file) {
uint8_t pad[8];
// 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->bw = constrain(_prefs->bw, 62.5f, 500.0f);
_prefs->sf = constrain(_prefs->sf, 7, 12);
_prefs->cr = constrain(_prefs->cr, 5, 8);
_prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, 1, 30);
file.read((uint8_t *) &_prefs->airtime_factor, sizeof(_prefs->airtime_factor)); // 0
file.read((uint8_t *) &_prefs->node_name, sizeof(_prefs->node_name)); // 4
file.read(pad, 4); // 36
file.read((uint8_t *) &_prefs->node_lat, sizeof(_prefs->node_lat)); // 40
file.read((uint8_t *) &_prefs->node_lon, sizeof(_prefs->node_lon)); // 48
file.read((uint8_t *) &_prefs->password[0], sizeof(_prefs->password)); // 56
file.read((uint8_t *) &_prefs->freq, sizeof(_prefs->freq)); // 72
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 *) pad, 1); // 79 was 'unused'
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
file.read((uint8_t *) &_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104
file.read(pad, 4); // 108
file.read((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112
file.read((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113
file.read((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114
file.read((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115
file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116
file.read(pad, 4); // 120
file.read((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124
file.read((uint8_t *) &_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125
file.close();
}
// 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->bw = constrain(_prefs->bw, 62.5f, 500.0f);
_prefs->sf = constrain(_prefs->sf, 7, 12);
_prefs->cr = constrain(_prefs->cr, 5, 8);
_prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, 1, 30);
file.close();
}
}
void CommonCLI::savePrefs(FILESYSTEM* fs) {
#if defined(NRF52_PLATFORM)
File file = fs->open("/node_prefs", FILE_O_WRITE);
File file = fs->open("/com_prefs", FILE_O_WRITE);
if (file) { file.seek(0); file.truncate(); }
#else
File file = fs->open("/node_prefs", "w", true);
File file = fs->open("/com_prefs", "w", true);
#endif
if (file) {
uint8_t pad[8];
@@ -80,7 +89,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->unused, sizeof(_prefs->unused)); // 79
file.write((uint8_t *) pad, 1); // 79 was 'unused'
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
@@ -88,11 +97,12 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write(pad, 4); // 108
file.write((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112
file.write((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113
file.write((uint8_t *) &_prefs->reserved1, sizeof(_prefs->reserved1)); // 114
file.write((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114
file.write((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115
file.write((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116
file.write(pad, 4); // 120
file.write((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124
file.write((uint8_t *) &_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125
file.close();
}
@@ -129,9 +139,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
strcpy(reply, "ERR: clock cannot go backwards");
}
} else if (memcmp(command, "start ota", 9) == 0) {
if (_board->startOTAUpdate()) {
strcpy(reply, "OK");
} else {
if (!_board->startOTAUpdate(_prefs->node_name, reply)) {
strcpy(reply, "Error");
}
} else if (memcmp(command, "clock", 5) == 0) {
@@ -157,6 +165,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
const char* config = &command[4];
if (memcmp(config, "af", 2) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor));
} else if (memcmp(config, "allow.read.only", 15) == 0) {
sprintf(reply, "> %s", _prefs->allow_read_only ? "on" : "off");
} else if (memcmp(config, "flood.advert.interval", 21) == 0) {
sprintf(reply, "> %d", ((uint32_t) _prefs->flood_advert_interval));
} else if (memcmp(config, "advert.interval", 15) == 0) {
sprintf(reply, "> %d", ((uint32_t) _prefs->advert_interval) * 2);
} else if (memcmp(config, "guest.password", 14) == 0) {
@@ -195,6 +207,22 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
_prefs->airtime_factor = atof(&config[3]);
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "allow.read.only ", 16) == 0) {
_prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "flood.advert.interval ", 22) == 0) {
int hours = _atoi(&config[22]);
if (hours > 0 && hours < 3) {
sprintf(reply, "Error: min is 3 hours");
} else if (hours > 48) {
strcpy(reply, "Error: max is 48 hours");
} else {
_prefs->flood_advert_interval = (uint8_t)(hours);
_callbacks->updateFloodAdvertTimer();
savePrefs();
strcpy(reply, "OK");
}
} else if (memcmp(config, "advert.interval ", 16) == 0) {
int mins = _atoi(&config[16]);
if (mins > 0 && mins < MIN_LOCAL_ADVERT_INTERVAL) {

View File

@@ -11,8 +11,8 @@ struct NodePrefs { // persisted to file
float freq;
uint8_t tx_power_dbm;
uint8_t disable_fwd;
uint8_t advert_interval; // minutes
uint8_t unused;
uint8_t advert_interval; // minutes / 2
uint8_t flood_advert_interval; // hours
float rx_delay_base;
float tx_delay_factor;
char guest_password[16];
@@ -20,7 +20,7 @@ struct NodePrefs { // persisted to file
uint32_t guard;
uint8_t sf;
uint8_t cr;
uint8_t reserved1;
uint8_t allow_read_only;
uint8_t reserved2;
float bw;
uint8_t flood_max;
@@ -34,6 +34,7 @@ public:
virtual bool formatFileSystem() = 0;
virtual void sendSelfAdvertisement(int delay_millis) = 0;
virtual void updateAdvertTimer() = 0;
virtual void updateFloodAdvertTimer() = 0;
virtual void setLoggingOn(bool enable) = 0;
virtual void eraseLogFile() = 0;
virtual void dumpLogFile() = 0;
@@ -52,6 +53,8 @@ class CommonCLI {
void checkAdvertInterval();
void loadPrefsInt(FILESYSTEM* _fs, const char* filename);
public:
CommonCLI(mesh::MainBoard& board, mesh::Mesh* mesh, NodePrefs* prefs, CommonCLICallbacks* callbacks)
: _board(&board), _mesh(mesh), _prefs(prefs), _callbacks(callbacks) { }

View File

@@ -9,7 +9,7 @@ class CustomLLCC68 : public LLCC68 {
CustomLLCC68(Module *mod) : LLCC68(mod) { }
bool isReceiving() {
uint16_t irq = getIrqStatus();
uint16_t irq = getIrqFlags();
bool hasPreamble = (irq & SX126X_IRQ_HEADER_VALID);
return hasPreamble;
}

View File

@@ -0,0 +1,41 @@
#ifdef ESP_PLATFORM
#include "ESP32Board.h"
#if defined(ADMIN_PASSWORD) // Repeater or Room Server only
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>
bool ESP32Board::startOTAUpdate(const char* id, char reply[]) {
WiFi.softAP("MeshCore-OTA", NULL);
sprintf(reply, "Started: http://%s/update", WiFi.softAPIP().toString().c_str());
MESH_DEBUG_PRINTLN("startOTAUpdate: %s", reply);
static char id_buf[60];
sprintf(id_buf, "%s (%s)", id, getManufacturerName());
static char home_buf[90];
sprintf(home_buf, "<H2>Hi! I am a MeshCore Repeater. ID: %s</H2>", id);
AsyncWebServer* server = new AsyncWebServer(80);
server->on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "text/html", home_buf);
});
AsyncElegantOTA.setID(id_buf);
AsyncElegantOTA.begin(server); // Start ElegantOTA
server->begin();
return true;
}
#else
bool ESP32Board::startOTAUpdate(const char* id, char reply[]) {
return false; // not supported
}
#endif
#endif

View File

@@ -7,6 +7,7 @@
#include <rom/rtc.h>
#include <sys/time.h>
#include <Wire.h>
class ESP32Board : public mesh::MainBoard {
protected:
@@ -73,6 +74,8 @@ public:
void reboot() override {
esp_restart();
}
bool startOTAUpdate(const char* id, char reply[]) override;
};
class ESP32RTCClock : public mesh::RTCClock {

View File

@@ -24,11 +24,20 @@
#include <driver/rtc_io.h>
class HeltecV3Board : public ESP32Board {
private:
bool adc_active_state;
public:
void begin() {
ESP32Board::begin();
// Auto-detect correct ADC_CTRL pin polarity (different for boards >3.2)
pinMode(PIN_ADC_CTRL, INPUT);
adc_active_state = !digitalRead(PIN_ADC_CTRL);
pinMode(PIN_ADC_CTRL, OUTPUT);
digitalWrite(PIN_ADC_CTRL, !adc_active_state); // Initially inactive
pinMode(PIN_VEXT_EN, OUTPUT);
digitalWrite(PIN_VEXT_EN, LOW); // for V3.2 boards
@@ -67,9 +76,14 @@ public:
esp_deep_sleep_start(); // CPU halts here and never returns!
}
void powerOff() override {
// TODO: re-enable this when there is a definite wake-up source pin:
// enterDeepSleep(0);
}
uint16_t getBattMilliVolts() override {
analogReadResolution(10);
digitalWrite(PIN_ADC_CTRL, PIN_ADC_CTRL_ACTIVE);
digitalWrite(PIN_ADC_CTRL, adc_active_state);
uint32_t raw = 0;
for (int i = 0; i < 8; i++) {
@@ -77,7 +91,7 @@ public:
}
raw = raw / 8;
digitalWrite(PIN_ADC_CTRL, PIN_ADC_CTRL_INACTIVE);
digitalWrite(PIN_ADC_CTRL, !adc_active_state);
return (5.2 * (3.3 / 1024.0) * raw) * 1000;
}

84
src/helpers/TBeamBoard.h Normal file
View File

@@ -0,0 +1,84 @@
#pragma once
#include <Wire.h>
#include <Arduino.h>
#include "XPowersLib.h"
// Defined using AXP2102
#define XPOWERS_CHIP_AXP2101
// LoRa radio module pins for TBeam
#define P_LORA_DIO_0 26
#define P_LORA_DIO_2 32
#define P_LORA_DIO_1 33
#define P_LORA_NSS 18
#define P_LORA_RESET 14
#define P_LORA_BUSY RADIOLIB_NC
#define P_LORA_SCLK 5
#define P_LORA_MISO 19
#define P_LORA_MOSI 27
// built-ins
//#define PIN_VBAT_READ 37
//#define PIN_LED_BUILTIN 25
#include "ESP32Board.h"
#include <driver/rtc_io.h>
class TBeamBoard : public ESP32Board {
XPowersAXP2101 power;
public:
void begin() {
ESP32Board::begin();
power.setALDO2Voltage(3300);
power.enableALDO2();
pinMode(38,INPUT_PULLUP);
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);
}
}
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
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
} else {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000);
}
// Finally set ESP32 into sleep
esp_deep_sleep_start(); // CPU halts here and never returns!
}
uint16_t getBattMilliVolts() override {
return 0;
}
const char* getManufacturerName() const override {
return "LilyGo T-Beam";
}
};

View File

@@ -6,75 +6,76 @@
static BLEDfu bledfu;
static void connect_callback(uint16_t conn_handle)
{
(void)conn_handle;
MESH_DEBUG_PRINTLN("BLE client connected");
static void connect_callback(uint16_t conn_handle) {
(void)conn_handle;
MESH_DEBUG_PRINTLN("BLE client connected");
}
static void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void)conn_handle;
(void)reason;
static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
(void)conn_handle;
(void)reason;
MESH_DEBUG_PRINTLN("BLE client disconnected");
MESH_DEBUG_PRINTLN("BLE client disconnected");
}
void RAK4631Board::begin() {
// for future use, sub-classes SHOULD call this from their begin()
startup_reason = BD_STARTUP_NORMAL;
pinMode(PIN_VBAT_READ, INPUT);
#ifdef PIN_USER_BTN
pinMode(PIN_USER_BTN, INPUT_PULLUP);
#endif
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL);
#else
Wire.begin();
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL)
#endif
Wire.begin();
pinMode(SX126X_POWER_EN, OUTPUT);
digitalWrite(SX126X_POWER_EN, HIGH);
delay(10); // give sx1262 some time to power up
}
bool RAK4631Board::startOTAUpdate() {
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
bool RAK4631Board::startOTAUpdate(const char* id, char reply[]) {
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
Bluefruit.begin(1, 0);
// Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
Bluefruit.setTxPower(4);
// Set the BLE device name
Bluefruit.setName("RAK4631_OTA");
Bluefruit.begin(1, 0);
// Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
Bluefruit.setTxPower(4);
// Set the BLE device name
Bluefruit.setName("RAK4631_OTA");
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// Set up and start advertising
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addName();
// Set up and start advertising
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addName();
/* Start Advertising
- Enable auto advertising if disconnected
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
- Timeout for fast mode is 30 seconds
- Start(timeout) with timeout = 0 will advertise forever (until connected)
/* Start Advertising
- Enable auto advertising if disconnected
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
- Timeout for fast mode is 30 seconds
- Start(timeout) with timeout = 0 will advertise forever (until connected)
For recommended advertising interval
https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
For recommended advertising interval
https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
strcpy(reply, "OK - started");
return true;
}

View File

@@ -50,5 +50,5 @@ public:
NVIC_SystemReset();
}
bool startOTAUpdate() override;
bool startOTAUpdate(const char* id, char reply[]) override;
};

View File

@@ -4,12 +4,13 @@
#include <bluefruit.h>
void T1000eBoard::begin()
{
void T1000eBoard::begin() {
// for future use, sub-classes SHOULD call this from their begin()
startup_reason = BD_STARTUP_NORMAL;
btn_prev_state = HIGH;
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
#ifdef BUTTON_PIN
pinMode(BATTERY_PIN, INPUT);
pinMode(BUTTON_PIN, INPUT);
@@ -17,71 +18,70 @@ void T1000eBoard::begin()
#endif
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL);
#else
Wire.begin();
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL)
#endif
Wire.begin();
delay(10); // give sx1262 some time to power up
}
#if 0
static BLEDfu bledfu;
static void connect_callback(uint16_t conn_handle)
{
(void)conn_handle;
MESH_DEBUG_PRINTLN("BLE client connected");
static void connect_callback(uint16_t conn_handle) {
(void)conn_handle;
MESH_DEBUG_PRINTLN("BLE client connected");
}
static void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void)conn_handle;
(void)reason;
static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
(void)conn_handle;
(void)reason;
MESH_DEBUG_PRINTLN("BLE client disconnected");
MESH_DEBUG_PRINTLN("BLE client disconnected");
}
bool TrackerT1000eBoard::startOTAUpdate() {
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
bool TrackerT1000eBoard::startOTAUpdate(const char* id, char reply[]) {
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
Bluefruit.begin(1, 0);
// Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
Bluefruit.setTxPower(4);
// Set the BLE device name
Bluefruit.setName("T1000E_OTA");
Bluefruit.begin(1, 0);
// Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
Bluefruit.setTxPower(4);
// Set the BLE device name
Bluefruit.setName("T1000E_OTA");
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// Set up and start advertising
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addName();
// Set up and start advertising
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addName();
/* Start Advertising
- Enable auto advertising if disconnected
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
- Timeout for fast mode is 30 seconds
- Start(timeout) with timeout = 0 will advertise forever (until connected)
/* Start Advertising
- Enable auto advertising if disconnected
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
- Timeout for fast mode is 30 seconds
- Start(timeout) with timeout = 0 will advertise forever (until connected)
For recommended advertising interval
https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
For recommended advertising interval
https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
strcpy(reply, "OK - started");
return true;
}
#endif

View File

@@ -3,8 +3,6 @@
#include <MeshCore.h>
#include <Arduino.h>
#define HAS_T1000e_POWEROFF
// LoRa and SPI pins
#define P_LORA_DIO_1 (32 + 1) // P1.1
#define P_LORA_NSS (0 + 12) // P0.12
@@ -61,7 +59,7 @@ public:
return 0;
}
void powerOff() {
void powerOff() override {
#ifdef HAS_GPS
digitalWrite(GPS_VRTC_EN, LOW);
digitalWrite(GPS_RESET, LOW);
@@ -92,5 +90,5 @@ public:
NVIC_SystemReset();
}
// bool startOTAUpdate() override;
// bool startOTAUpdate(const char* id, char reply[]) override;
};

View File

@@ -27,11 +27,11 @@ void T114Board::begin() {
pinMode(PIN_VBAT_READ, INPUT);
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL);
#else
Wire.begin();
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL)
#endif
Wire.begin();
#ifdef P_LORA_TX_LED
pinMode(P_LORA_TX_LED, OUTPUT);
digitalWrite(P_LORA_TX_LED, HIGH);
@@ -42,7 +42,7 @@ void T114Board::begin() {
delay(10); // give sx1262 some time to power up
}
bool T114Board::startOTAUpdate() {
bool T114Board::startOTAUpdate(const char* id, char reply[]) {
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
@@ -81,5 +81,6 @@ bool T114Board::startOTAUpdate() {
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
strcpy(reply, "OK - started");
return true;
}

View File

@@ -18,7 +18,8 @@
// built-ins
#define PIN_VBAT_READ 4
#define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000)
#define PIN_BAT_CTL 6
#define MV_LSB (3000.0F / 4096.0F) // 12-bit ADC with 3.0V input range
class T114Board : public mesh::MainBoard {
protected:
@@ -37,17 +38,18 @@ public:
}
#endif
#define BATTERY_SAMPLES 8
uint16_t getBattMilliVolts() override {
int adcvalue = 0;
analogReadResolution(12);
uint32_t raw = 0;
for (int i = 0; i < BATTERY_SAMPLES; i++) {
raw += analogRead(PIN_VBAT_READ);
}
raw = raw / BATTERY_SAMPLES;
analogReference(AR_INTERNAL_3_0);
pinMode(PIN_BAT_CTL, OUTPUT); // battery adc can be read only ctrl pin 6 set to high
digitalWrite(PIN_BAT_CTL, 1);
return (ADC_MULTIPLIER * raw) / 4096;
delay(10);
adcvalue = analogRead(PIN_VBAT_READ);
digitalWrite(6, 0);
return (uint16_t)((float)adcvalue * MV_LSB * 4.9);
}
const char* getManufacturerName() const override {
@@ -58,5 +60,5 @@ public:
NVIC_SystemReset();
}
bool startOTAUpdate() override;
bool startOTAUpdate(const char* id, char reply[]) override;
};

View File

@@ -1,19 +1,19 @@
#include <Arduino.h>
#include "TechoBoard.h"
#ifdef LILYGO_TECHO
#include <bluefruit.h>
#include <Wire.h>
static BLEDfu bledfu;
static void connect_callback(uint16_t conn_handle)
{
static void connect_callback(uint16_t conn_handle) {
(void)conn_handle;
MESH_DEBUG_PRINTLN("BLE client connected");
}
static void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
(void)conn_handle;
(void)reason;
@@ -24,20 +24,41 @@ void TechoBoard::begin() {
// for future use, sub-classes SHOULD call this from their begin()
startup_reason = BD_STARTUP_NORMAL;
pinMode(PIN_VBAT_READ, INPUT);
delay(200);
pinMode(PIN_PWR_EN, OUTPUT);
digitalWrite(PIN_PWR_EN, HIGH);
pinMode(PIN_BUTTON1, INPUT_PULLUP);
pinMode(PIN_BUTTON2, INPUT_PULLUP);
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
delay(200);
pinMode(PIN_TXCO, OUTPUT);
digitalWrite(PIN_TXCO, HIGH);
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL);
#else
Wire.begin();
#endif
pinMode(SX126X_POWER_EN, OUTPUT);
digitalWrite(SX126X_POWER_EN, HIGH);
delay(10); // give sx1262 some time to power up
}
bool TechoBoard::startOTAUpdate() {
uint16_t TechoBoard::getBattMilliVolts() {
int adcvalue = 0;
analogReference(AR_INTERNAL_3_0);
analogReadResolution(12);
delay(10);
// ADC range is 0..3000mV and resolution is 12-bit (0..4095)
adcvalue = analogRead(PIN_VBAT_READ);
// Convert the raw value to compensated mv, taking the resistor-
// divider into account (providing the actual LIPO voltage)
return (uint16_t)((float)adcvalue * REAL_VBAT_MV_PER_LSB);
}
bool TechoBoard::startOTAUpdate(const char* id, char reply[]) {
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
@@ -76,5 +97,7 @@ bool TechoBoard::startOTAUpdate() {
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
strcpy(reply, "OK - started");
return true;
}
#endif

View File

@@ -17,28 +17,26 @@
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// built-ins
#define PIN_VBAT_READ 4
#define ADC_MULTIPLIER (2.0)
#define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096
#define VBAT_DIVIDER (0.5F) // 150K + 150K voltage divider on VBAT
#define VBAT_DIVIDER_COMP (2.0F) // Compensation factor for the VBAT divider
#define PIN_VBAT_READ (4)
#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)
class TechoBoard : public mesh::MainBoard {
protected:
uint8_t startup_reason;
public:
void begin();
uint8_t getStartupReason() const override { return startup_reason; }
uint16_t getBattMilliVolts() override;
bool startOTAUpdate(const char* id, char reply[]) override;
#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;
uint8_t getStartupReason() const override {
return startup_reason;
}
const char* getManufacturerName() const override {
@@ -48,6 +46,4 @@ public:
void reboot() override {
NVIC_SystemReset();
}
bool startOTAUpdate() override;
};

View File

@@ -0,0 +1,83 @@
#include <Arduino.h>
#include "faketecBoard.h"
#include <bluefruit.h>
#include <Wire.h>
static BLEDfu bledfu;
void FaketecBoard::begin() {
// for future use, sub-classes SHOULD call this from their begin()
startup_reason = BD_STARTUP_NORMAL;
btn_prev_state = HIGH;
pinMode(PIN_VBAT_READ, INPUT);
#ifdef BUTTON_PIN
pinMode(BUTTON_PIN, INPUT);
#endif
#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);
digitalWrite(SX126X_POWER_EN, HIGH);
delay(10); // give sx1262 some time to power up
}
static void connect_callback(uint16_t conn_handle) {
(void)conn_handle;
MESH_DEBUG_PRINTLN("BLE client connected");
}
static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
(void)conn_handle;
(void)reason;
MESH_DEBUG_PRINTLN("BLE client disconnected");
}
bool FaketecBoard::startOTAUpdate(const char* id, char reply[]) {
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
Bluefruit.begin(1, 0);
// Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
Bluefruit.setTxPower(4);
// Set the BLE device name
Bluefruit.setName("Faketec_OTA");
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// Set up and start advertising
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addName();
/* Start Advertising
- Enable auto advertising if disconnected
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
- Timeout for fast mode is 30 seconds
- Start(timeout) with timeout = 0 will advertise forever (until connected)
For recommended advertising interval
https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
strcpy(reply, "OK - started");
return true;
}

View File

@@ -0,0 +1,65 @@
#pragma once
#include <MeshCore.h>
#include <Arduino.h>
#define P_LORA_NSS 13 //P1.13 45
#define P_LORA_DIO_1 11 //P0.10 10
#define P_LORA_RESET 10 //P0.09 9
#define P_LORA_BUSY 16 //P0.29 29
#define P_LORA_MISO 15 //P0.02 2
#define P_LORA_SCLK 12 //P1.11 43
#define P_LORA_MOSI 14 //P1.15 47
#define SX126X_POWER_EN 21 //P0.13 13
#define SX126X_RXEN 2 //P0.17
#define SX126X_TXEN RADIOLIB_NC
#define SX126X_DIO2_AS_RF_SWITCH true
#define SX126X_DIO3_TCXO_VOLTAGE (1.8f)
#define PIN_VBAT_READ 17
#define ADC_MULTIPLIER (1.815f) // dependent on voltage divider resistors. TODO: more accurate battery tracking
class FaketecBoard : public mesh::MainBoard {
protected:
uint8_t startup_reason;
uint8_t btn_prev_state;
public:
void begin();
uint8_t getStartupReason() const override { return startup_reason; }
#define BATTERY_SAMPLES 8
uint16_t getBattMilliVolts() override {
analogReadResolution(12);
uint32_t raw = 0;
for (int i = 0; i < BATTERY_SAMPLES; i++) {
raw += analogRead(PIN_VBAT_READ);
}
raw = raw / BATTERY_SAMPLES;
return (ADC_MULTIPLIER * raw);
}
const char* getManufacturerName() const override {
return "Faketec DIY";
}
int buttonStateChanged() {
#ifdef BUTTON_PIN
uint8_t v = digitalRead(BUTTON_PIN);
if (v != btn_prev_state) {
btn_prev_state = v;
return (v == LOW) ? 1 : -1;
}
#endif
return 0;
}
void reboot() override {
NVIC_SystemReset();
}
bool startOTAUpdate(const char* id, char reply[]) override;
};

View File

@@ -1,7 +1,13 @@
#include "SSD1306Display.h"
bool SSD1306Display::i2c_probe(TwoWire& wire, uint8_t addr) {
wire.beginTransmission(addr);
uint8_t error = wire.endTransmission();
return (error == 0);
}
bool SSD1306Display::begin() {
return display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_ADDRESS, true, false);
return display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_ADDRESS, true, false) && i2c_probe(Wire, DISPLAY_ADDRESS);
}
void SSD1306Display::turnOn() {

View File

@@ -3,6 +3,7 @@
#include "DisplayDriver.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#define SSD1306_NO_SPLASH
#include <Adafruit_SSD1306.h>
#ifndef PIN_OLED_RESET
@@ -18,6 +19,7 @@ class SSD1306Display : public DisplayDriver {
bool _isOn;
uint8_t _color;
bool i2c_probe(TwoWire& wire, uint8_t addr);
public:
SSD1306Display() : DisplayDriver(128, 64), display(128, 64, &Wire, PIN_OLED_RESET) { _isOn = false; }
bool begin();

View File

@@ -0,0 +1,109 @@
[Heltec_lora32_v2]
extends = esp32_base
board = heltec_wifi_lora_32_V2 ; heltec_wifi_lora_32_V2
build_flags =
${esp32_base.build_flags}
-I variants/heltec_v2
-D HELTEC_LORA_V2
-D PIN_BOARD_SDA=4
-D PIN_BOARD_SCL=15
-D PIN_USER_BTN=0
-D PIN_OLED_RESET=16
-D RADIO_CLASS=CustomSX1276
-D WRAPPER_CLASS=CustomSX1276Wrapper
-D LORA_TX_POWER=20
-D P_LORA_TX_LED=25
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/heltec_v2>
lib_deps =
${esp32_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
[env:Heltec_v2_repeater]
extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<../examples/simple_repeater>
+<helpers/ui/*.cpp>
lib_deps =
${Heltec_lora32_v2.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_v2_room_server]
extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Heltec Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_room_server>
lib_deps =
${Heltec_lora32_v2.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_v2_terminal_chat]
extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${Heltec_lora32_v2.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_v2_companion_radio_usb]
extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${Heltec_lora32_v2.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_v2_companion_radio_ble]
extends = Heltec_lora32_v2
build_flags =
${Heltec_lora32_v2.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=0
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${Heltec_lora32_v2.lib_deps}
densaugeo/base64 @ ~1.4.0

View File

@@ -0,0 +1,31 @@
#include <Arduino.h>
#include "target.h"
HeltecV2Board board;
#if defined(P_LORA_SCLK)
static SPIClass spi;
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
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#if defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
return true; // success
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/HeltecV2Board.h>
#include <helpers/CustomSX1276Wrapper.h>
extern HeltecV2Board board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,187 @@
[Heltec_lora32_v3]
extends = esp32_base
board = esp32-s3-devkitc-1
build_flags =
${esp32_base.build_flags}
-I variants/heltec_v3
-D HELTEC_LORA_V3
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D P_LORA_TX_LED=35
-D PIN_BOARD_SDA=17
-D PIN_BOARD_SCL=18
-D PIN_USER_BTN=0
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=130.0f ; for best TX power!
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/heltec_v3>
lib_deps =
${esp32_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
[env:Heltec_v3_repeater]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_repeater>
lib_deps =
${Heltec_lora32_v3.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_v3_room_server]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Heltec Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_room_server>
lib_deps =
${Heltec_lora32_v3.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_v3_terminal_chat]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${Heltec_lora32_v3.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_v3_companion_radio_usb]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D DISPLAY_CLASS=SSD1306Display
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${Heltec_lora32_v3.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_v3_companion_radio_ble]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D DISPLAY_CLASS=SSD1306Display
-D BLE_PIN_CODE=0 ; dynamic, random PIN
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/*.cpp>
+<helpers/esp32/*.cpp>
+<../examples/companion_radio>
lib_deps =
${Heltec_lora32_v3.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_v3_companion_radio_wifi]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D DISPLAY_CLASS=SSD1306Display
-D WIFI_DEBUG_LOGGING=1
-D WIFI_SSID='"myssid"'
-D WIFI_PWD='"mypwd"'
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/ui/*.cpp>
+<helpers/esp32/*.cpp>
+<../examples/companion_radio>
lib_deps =
${Heltec_lora32_v3.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_WSL3_repeater]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D ADVERT_NAME='"Heltec Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<../examples/simple_repeater>
lib_deps =
${Heltec_lora32_v3.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_WSL3_room_server]
extends = Heltec_lora32_v3
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<../examples/simple_room_server>
build_flags =
${Heltec_lora32_v3.build_flags}
-D ADVERT_NAME='"Heltec Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${Heltec_lora32_v3.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_WSL3_companion_radio_ble]
extends = Heltec_lora32_v3
build_flags =
${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<helpers/esp32/*.cpp>
+<../examples/companion_radio>
lib_deps =
${Heltec_lora32_v3.lib_deps}
densaugeo/base64 @ ~1.4.0

View File

@@ -0,0 +1,47 @@
#include <Arduino.h>
#include "target.h"
HeltecV3Board board;
#if defined(P_LORA_SCLK)
static SPIClass spi;
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
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
#if defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
return true; // success
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/HeltecV3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
extern HeltecV3Board board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,124 @@
[LilyGo_T3S3_sx1262]
extends = esp32_base
board = t3_s3_v1_x
build_flags =
${esp32_base.build_flags}
-I variants/lilygo_t3s3
-D LILYGO_T3S3
-D P_LORA_DIO_1=33
-D P_LORA_NSS=7
-D P_LORA_RESET=8 ; RADIOLIB_NC
-D P_LORA_BUSY=34
-D P_LORA_SCLK=5
-D P_LORA_MISO=3
-D P_LORA_MOSI=6
-D P_LORA_TX_LED=37
-D PIN_VBAT_READ=1
-D PIN_USER_BTN=0
-D PIN_BOARD_SDA=18
-D PIN_BOARD_SCL=17
-D P_LORA_TX_LED=37
-D PIN_OLED_RESET=21
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=130
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/lilygo_t3s3>
lib_deps =
${esp32_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
; === LilyGo T3S3 with SX1262 environments ===
[env:LilyGo_T3S3_sx1262_Repeater]
extends = LilyGo_T3S3_sx1262
build_flags =
${LilyGo_T3S3_sx1262.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"T3S3-1262 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_repeater>
lib_deps =
${LilyGo_T3S3_sx1262.lib_deps}
${esp32_ota.lib_deps}
[env:LilyGo_T3S3_sx1262_terminal_chat]
extends = LilyGo_T3S3_sx1262
build_flags =
${LilyGo_T3S3_sx1262.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${LilyGo_T3S3_sx1262.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_T3S3_sx1262_room_server]
extends = LilyGo_T3S3_sx1262
build_flags =
${LilyGo_T3S3_sx1262.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"T3S3-1262 Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_room_server>
lib_deps =
${LilyGo_T3S3_sx1262.lib_deps}
${esp32_ota.lib_deps}
[env:LilyGo_T3S3_sx1262_companion_radio_usb]
extends = LilyGo_T3S3_sx1262
build_flags =
${LilyGo_T3S3_sx1262.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${LilyGo_T3S3_sx1262.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_T3S3_sx1262_companion_radio_ble]
extends = LilyGo_T3S3_sx1262
build_flags =
${LilyGo_T3S3_sx1262.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${LilyGo_T3S3_sx1262.lib_deps}
densaugeo/base64 @ ~1.4.0

View File

@@ -0,0 +1,47 @@
#include <Arduino.h>
#include "target.h"
ESP32Board board;
#if defined(P_LORA_SCLK)
static SPIClass spi;
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
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
#if defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
return true; // success
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/ESP32Board.h>
#include <helpers/CustomSX1262Wrapper.h>
extern ESP32Board board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,46 @@
[LilyGo_TBeam]
extends = esp32_base
board = ttgo-t-beam
build_flags =
${esp32_base.build_flags}
-I variants/lilygo_tbeam
-D LILYGO_TBEAM
-D RADIO_CLASS=CustomSX1276
-D WRAPPER_CLASS=CustomSX1276Wrapper
-D LORA_TX_POWER=20
-D P_LORA_TX_LED=4
-D PIN_BOARD_SDA=21
-D PIN_BOARD_SCL=22
-D PIN_USER_BTN=38
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/lilygo_tbeam>
board_build.partitions = min_spiffs.csv ; get around 4mb flash limit
lib_deps =
${esp32_base.lib_deps}
lewisxhe/XPowersLib@^0.2.7
adafruit/Adafruit SSD1306 @ ^2.5.13
[env:Tbeam_companion_radio_ble]
extends = LilyGo_TBeam
board_build.upload.maximum_ram_size=2000000
build_flags =
${LilyGo_TBeam.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
-D MESH_PACKET_LOGGING=1
-D MESH_DEBUG=1
-D RADIOLIB_DEBUG_BASIC=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TBeam.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${LilyGo_TBeam.lib_deps}
densaugeo/base64 @ ~1.4.0

View File

@@ -0,0 +1,31 @@
#include <Arduino.h>
#include "target.h"
TBeamBoard board;
#if defined(P_LORA_SCLK)
static SPIClass spi;
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
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#if defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
return true; // success
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/TBeamBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
extern TBeamBoard board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,120 @@
[LilyGo_TLora_V2_1_1_6]
extends = esp32_base
board = ttgo-lora32-v1 ; LILYGO T-LoRa V2.1-1.6 ESP32 with SX1276
build_unflags = -Os
build_type = release ; Set build type to release
board_build.partitions = min_spiffs.csv ; get around 4mb flash limit
build_flags =
${esp32_base.build_flags}
-I variants/lilygo_tlora_v2_1
-Os -ffunction-sections -fdata-sections ; Optimize for size
-D LILYGO_TLORA ; LILYGO T-LoRa V2.1-1.6 ESP32 with SX1276
-D P_LORA_DIO_0=26 ; SX1276 DIO0 interrupt pin
-D P_LORA_DIO_1=33 ; SX1276 DIO1 interrupt pin
-D P_LORA_NSS=18 ; Chip select - SS pin
-D P_LORA_RESET=14 ; Reset pin
-D P_LORA_SCLK=5 ; SPI clock
-D P_LORA_MISO=19 ; SPI MISO
-D P_LORA_MOSI=27 ; SPI MOSI
-D P_LORA_TX_LED=2 ; LED pin for TX indication
-D PIN_VBAT_READ=35 ; Battery voltage reading (analog pin)
-D PIN_USER_BTN=0
-D RADIO_CLASS=CustomSX1276
-D ARDUINO_LOOP_STACK_SIZE=16384
-D DISPLAY_CLASS=SSD1306Display
-D WRAPPER_CLASS=CustomSX1276Wrapper
-D LORA_TX_POWER=20
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/lilygo_tlora_v2_1>
lib_deps =
${esp32_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
; === LILYGO T-LoRa V2.1-1.6 with SX1276 environments ===
[env:LilyGo_TLora_V2_1_1_6_Repeater]
extends = LilyGo_TLora_V2_1_1_6
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_repeater>
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D ADVERT_NAME='"TLora-V2.1-1.6 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
; -D CORE_DEBUG_LEVEL=3
lib_deps =
${LilyGo_TLora_V2_1_1_6.lib_deps}
${esp32_ota.lib_deps}
[env:LilyGo_TLora_V2_1_1_6_terminal_chat]
extends = LilyGo_TLora_V2_1_1_6
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_repeater>
lib_deps =
${LilyGo_TLora_V2_1_1_6.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_TLora_V2_1_1_6_companion_radio_usb]
extends = LilyGo_TLora_V2_1_1_6
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${LilyGo_TLora_V2_1_1_6.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_TLora_V2_1_1_6_companion_radio_ble]
extends = LilyGo_TLora_V2_1_1_6
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/esp32/*.cpp>
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${LilyGo_TLora_V2_1_1_6.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:LilyGo_TLora_V2_1_1_6_room_server]
extends = LilyGo_TLora_V2_1_1_6
build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_room_server>
build_flags =
${LilyGo_TLora_V2_1_1_6.build_flags}
-D ADVERT_NAME='"TLora-V2.1-1.6 Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${LilyGo_TLora_V2_1_1_6.lib_deps}
${esp32_ota.lib_deps}

View File

@@ -0,0 +1,25 @@
#include <Arduino.h>
#include "target.h"
LilyGoTLoraBoard board;
static SPIClass spi;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
return true; // success
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/LilyGoTLoraBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
extern LilyGoTLoraBoard board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,187 @@
[faketec]
extends = nrf52840_base
board = promicro_nrf52840
build_flags = ${nrf52840_base.build_flags}
-I variants/promicro
-D FAKETEC
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=130
-D SX126X_RX_BOOSTED_GAIN=1
-D DISPLAY_CLASS=SSD1306Display
-D PIN_BOARD_SCL=7
-D PIN_BOARD_SDA=8
-D PIN_OLED_RESET=-1
-D PIN_USER_BTN=6
build_src_filter = ${nrf52840_base.build_src_filter}
+<helpers/nrf52/FaketecBoard.cpp>
+<../variants/promicro>
lib_deps=
${nrf52840_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
[env:Faketec_Repeater]
extends = faketec
build_src_filter = ${faketec.build_src_filter} +<../examples/simple_repeater> +<helpers/ui/*.cpp>
build_flags =
${faketec.build_flags}
-D ADVERT_NAME="\"Faketec Repeater\""
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD="\"password\""
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${faketec.lib_deps}
adafruit/RTClib @ ^2.1.3
[env:Faketec_room_server]
extends = faketec
build_src_filter = ${faketec.build_src_filter} +<../examples/simple_room_server> +<helpers/ui/*.cpp>
build_flags =
${faketec.build_flags}
-D ADVERT_NAME="\"Test Room\""
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD="\"password\""
-D ROOM_PASSWORD="\"hello\""
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${faketec.lib_deps}
adafruit/RTClib @ ^2.1.3
[env:Faketec_terminal_chat]
extends = faketec
build_flags =
${faketec.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${faketec.build_src_filter} +<../examples/simple_secure_chat/main.cpp>
lib_deps =
${faketec.lib_deps}
densaugeo/base64 @ ~1.4.0
adafruit/RTClib @ ^2.1.3
[env:Faketec_companion_radio_usb]
extends = faketec
build_flags =
${faketec.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${faketec.build_src_filter} +<../examples/companion_radio> +<../examples/companion_radio> +<helpers/ui/*.cpp>
lib_deps =
${faketec.lib_deps}
adafruit/RTClib @ ^2.1.3
densaugeo/base64 @ ~1.4.0
[env:Faketec_companion_radio_ble]
extends = faketec
build_flags =
${faketec.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
-D ENABLE_PRIVATE_KEY_EXPORT=1
-D ENABLE_PRIVATE_KEY_IMPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${faketec.build_src_filter} +<helpers/nrf52/SerialBLEInterface.cpp> +<../examples/companion_radio> +<helpers/ui/*.cpp>
lib_deps =
${faketec.lib_deps}
adafruit/RTClib @ ^2.1.3
densaugeo/base64 @ ~1.4.0
[ProMicroLLCC68]
extends = nrf52840_base
board = promicro_nrf52840
build_flags = ${nrf52840_base.build_flags}
-I variants/promicro
-D FAKETEC
-D RADIO_CLASS=CustomLLCC68
-D WRAPPER_CLASS=CustomLLCC68Wrapper
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=130
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${nrf52840_base.build_src_filter}
+<helpers/nrf52/FaketecBoard.cpp>
+<../variants/promicro>
[env:ProMicroLLCC68_Repeater]
extends = ProMicroLLCC68
build_src_filter = ${ProMicroLLCC68.build_src_filter} +<../examples/simple_repeater/main.cpp>
build_flags =
${ProMicroLLCC68.build_flags}
-D ADVERT_NAME="\"ProMicroLLCC68 Repeater\""
-D ADMIN_PASSWORD="\"password\""
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${ProMicroLLCC68.lib_deps}
adafruit/RTClib @ ^2.1.3
[env:ProMicroLLCC68_room_server]
extends = ProMicroLLCC68
build_src_filter = ${ProMicroLLCC68.build_src_filter} +<../examples/simple_room_server/main.cpp>
build_flags =
${ProMicroLLCC68.build_flags}
-D ADVERT_NAME="\"ProMicroLLCC68 Room\""
-D ADMIN_PASSWORD="\"password\""
-D ROOM_PASSWORD="\"hello\""
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${ProMicroLLCC68.lib_deps}
adafruit/RTClib @ ^2.1.3
[env:ProMicroLLCC68_terminal_chat]
extends = ProMicroLLCC68
build_flags =
${ProMicroLLCC68.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${ProMicroLLCC68.build_src_filter} +<../examples/simple_secure_chat/main.cpp>
lib_deps =
${ProMicroLLCC68.lib_deps}
densaugeo/base64 @ ~1.4.0
adafruit/RTClib @ ^2.1.3
[env:ProMicroLLCC68_companion_radio_usb]
extends = ProMicroLLCC68
build_flags =
${ProMicroLLCC68.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${ProMicroLLCC68.build_src_filter} +<../examples/companion_radio/main.cpp>
lib_deps =
${ProMicroLLCC68.lib_deps}
adafruit/RTClib @ ^2.1.3
densaugeo/base64 @ ~1.4.0
[env:ProMicroLLCC68_companion_radio_ble]
extends = ProMicroLLCC68
build_flags =
${ProMicroLLCC68.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
-D ENABLE_PRIVATE_KEY_EXPORT=1
-D ENABLE_PRIVATE_KEY_IMPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${ProMicroLLCC68.build_src_filter} +<helpers/nrf52/SerialBLEInterface.cpp> +<../examples/companion_radio/main.cpp>
lib_deps =
${ProMicroLLCC68.lib_deps}
adafruit/RTClib @ ^2.1.3
densaugeo/base64 @ ~1.4.0

View File

@@ -0,0 +1,46 @@
#include <Arduino.h>
#include "target.h"
FaketecBoard board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) {
#define SX126X_DIO3_TCXO_VOLTAGE (0.0f);
tcxo = SX126X_DIO3_TCXO_VOLTAGE;
status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
}
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
return true; // success
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include <helpers/nrf52/FaketecBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomLLCC68Wrapper.h>
extern FaketecBoard board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,15 @@
#include "variant.h"
#include "wiring_constants.h"
#include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = {
8, 6, 17, 20, 22, 24, 32, 11, 36, 38,
9, 10, 43, 45, 47, 2, 29, 31,
33, 34, 37,
13, 15
};
void initVariant()
{
}

View File

@@ -0,0 +1,82 @@
/*
* variant.h
* Copyright (C) 2023 Seeed K.K.
* MIT License
*/
#pragma once
#include "WVariant.h"
////////////////////////////////////////////////////////////////////////////////
// Low frequency clock source
#define VARIANT_MCK (64000000ul)
//#define USE_LFXO // 32.768 kHz crystal oscillator
#define USE_LFRC // 32.768 kHz RC oscillator
////////////////////////////////////////////////////////////////////////////////
// Power
#define PIN_EXT_VCC (21)
#define EXT_VCC (PIN_EXT_VCC)
#define BATTERY_PIN (17)
#define ADC_RESOLUTION 12
////////////////////////////////////////////////////////////////////////////////
// Number of pins
#define PINS_COUNT (23)
#define NUM_DIGITAL_PINS (23)
#define NUM_ANALOG_INPUTS (3)
#define NUM_ANALOG_OUTPUTS (0)
////////////////////////////////////////////////////////////////////////////////
// UART pin definition
#define PIN_SERIAL1_TX (1)
#define PIN_SERIAL1_RX (0)
////////////////////////////////////////////////////////////////////////////////
// I2C pin definition
#define WIRE_INTERFACES_COUNT 2
#define PIN_WIRE_SDA (6)
#define PIN_WIRE_SCL (7)
#define PIN_WIRE1_SDA (13)
#define PIN_WIRE1_SCL (14)
////////////////////////////////////////////////////////////////////////////////
// SPI pin definition
#define SPI_INTERFACES_COUNT 2
#define PIN_SPI_SCK (2)
#define PIN_SPI_MISO (3)
#define PIN_SPI_MOSI (4)
#define PIN_SPI_NSS (5)
#define PIN_SPI1_SCK (18)
#define PIN_SPI1_MISO (19)
#define PIN_SPI1_MOSI (20)
////////////////////////////////////////////////////////////////////////////////
// Builtin LEDs
#define PIN_LED (22)
#define LED_PIN PIN_LED
#define LED_BLUE PIN_LED
#define LED_BUILTIN PIN_LED
#define LED_STATE_ON 1
////////////////////////////////////////////////////////////////////////////////
// Builtin buttons
#define PIN_BUTTON1 (6)
#define BUTTON_PIN PIN_BUTTON1

View File

@@ -0,0 +1,104 @@
[rak4631]
extends = nrf52840_base
platform = https://github.com/maxgerhardt/platform-nordicnrf52.git#rak
board = wiscore_rak4631
board_check = true
build_flags = ${nrf52840_base.build_flags}
-I variants/rak4631
-D RAK_4631
-D PIN_USER_BTN=9
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=130
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${nrf52840_base.build_src_filter}
+<helpers/nrf52/*.cpp>
+<../variants/rak4631>
lib_deps =
${nrf52840_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
[env:RAK_4631_Repeater]
extends = rak4631
build_flags =
${rak4631.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"RAK4631 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${rak4631.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_repeater>
[env:RAK_4631_room_server]
extends = rak4631
build_flags =
${rak4631.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Test Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${rak4631.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_room_server>
[env:RAK_4631_companion_radio_usb]
extends = rak4631
build_flags =
${rak4631.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${rak4631.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${rak4631.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:RAK_4631_companion_radio_ble]
extends = rak4631
build_flags =
${rak4631.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${rak4631.build_src_filter}
+<helpers/ui/*.cpp>
+<helpers/nrf52/*.cpp>
+<../examples/companion_radio>
lib_deps =
${rak4631.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:RAK_4631_terminal_chat]
extends = rak4631
build_flags =
${rak4631.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
-D MESH_PACKET_LOGGING=1
-D MESH_DEBUG=1
build_src_filter = ${rak4631.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${rak4631.lib_deps}
densaugeo/base64 @ ~1.4.0

View File

@@ -0,0 +1,41 @@
#include <Arduino.h>
#include "target.h"
RAK4631Board board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
return true; // success
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/nrf52/RAK4631Board.h>
#include <helpers/CustomSX1262Wrapper.h>
extern RAK4631Board board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,56 @@
[Station_G2]
extends = esp32_base
board = station-g2
build_flags =
${esp32_base.build_flags}
-I variants/station_g2
-D STATION_G2
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=7
; -D P_LORA_TX_LED=35
; -D PIN_BOARD_SDA=5
; -D PIN_BOARD_SCL=6
-D PIN_USER_BTN=0
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=130.0f ; for best TX power!
; -D SX126X_RX_BOOSTED_GAIN=1 - DO NOT ENABLE THIS!
; https://wiki.uniteng.com/en/meshtastic/station-g2#impact-of-lora-node-dense-areashigh-noise-environments-on-rf-performance
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/station_g2>
lib_deps =
${esp32_base.lib_deps}
[env:Station_G2_repeater]
extends = Station_G2
build_flags =
${Station_G2.build_flags}
-D ADVERT_NAME='"Station G2 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Station_G2.build_src_filter}
+<../examples/simple_repeater>
lib_deps =
${Station_G2.lib_deps}
${esp32_ota.lib_deps}
[env:Station_G2_room_server]
extends = Station_G2
build_src_filter = ${Station_G2.build_src_filter}
+<../examples/simple_room_server>
build_flags =
${Station_G2.build_flags}
-D ADVERT_NAME='"Station G2 Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${Station_G2.lib_deps}
${esp32_ota.lib_deps}

View File

@@ -0,0 +1,47 @@
#include <Arduino.h>
#include "target.h"
StationG2Board board;
#if defined(P_LORA_SCLK)
static SPIClass spi;
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
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
#if defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
return true; // success
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/StationG2Board.h>
#include <helpers/CustomSX1262Wrapper.h>
extern StationG2Board board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,53 @@
; ----------------- NRF52 T1000e---------------------
[nrf52840_t1000e]
extends = nrf52_base
platform_packages = framework-arduinoadafruitnrf52
build_flags = ${nrf52_base.build_flags}
-I src/helpers/nrf52
-I lib/nrf52/s140_nrf52_7.3.0_API/include
-I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52
lib_ignore =
BluetoothOTA
lvgl
lib5b4
lib_deps =
${nrf52_base.lib_deps}
rweather/Crypto @ ^0.4.0
[t1000-e]
extends = nrf52840_t1000e
board = tracker-t1000-e
board_build.ldscript = boards/nrf52840_s140_v7.ld
build_flags = ${nrf52840_t1000e.build_flags}
-I variants/t1000-e
-D T1000_E
-D PIN_USER_BTN=6
-D USER_BTN_PRESSED=HIGH
-D PIN_STATUS_LED=24
-D RADIO_CLASS=CustomLR1110
-D WRAPPER_CLASS=CustomLR1110Wrapper
-D LORA_TX_POWER=22
build_src_filter = ${nrf52840_t1000e.build_src_filter}
+<helpers/*.cpp>
+<helpers/nrf52/T1000eBoard.cpp>
+<../variants/t1000-e>
debug_tool = jlink
upload_protocol = nrfutil
[env:t1000e_companion_radio_ble]
extends = t1000-e
build_flags = ${t1000-e.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
-D MESH_PACKET_LOGGING=1
-D MESH_DEBUG=1
-D RX_BOOSTED_GAIN=true
-D RF_SWITCH_TABLE
-D HAS_UI
build_src_filter = ${t1000-e.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp>
lib_deps = ${t1000-e.lib_deps}
densaugeo/base64 @ ~1.4.0

View File

@@ -0,0 +1,60 @@
#include <Arduino.h>
#include "target.h"
T1000eBoard board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
#ifndef LORA_CR
#define LORA_CR 5
#endif
#ifdef RF_SWITCH_TABLE
static const uint32_t rfswitch_dios[Module::RFSWITCH_MAX_PINS] = {
RADIOLIB_LR11X0_DIO5,
RADIOLIB_LR11X0_DIO6,
RADIOLIB_LR11X0_DIO7,
RADIOLIB_LR11X0_DIO8,
RADIOLIB_NC
};
static const Module::RfSwitchMode_t rfswitch_table[] = {
// mode DIO5 DIO6 DIO7 DIO8
{ LR11x0::MODE_STBY, {LOW, LOW, LOW, LOW }},
{ LR11x0::MODE_RX, {HIGH, LOW, LOW, HIGH }},
{ LR11x0::MODE_TX, {HIGH, HIGH, LOW, HIGH }},
{ LR11x0::MODE_TX_HP, {LOW, HIGH, LOW, HIGH }},
{ LR11x0::MODE_TX_HF, {LOW, LOW, LOW, LOW }},
{ LR11x0::MODE_GNSS, {LOW, LOW, HIGH, LOW }},
{ LR11x0::MODE_WIFI, {LOW, LOW, LOW, LOW }},
END_OF_MODE_TABLE,
};
#endif
bool radio_init() {
#ifdef LR11X0_DIO3_TCXO_VOLTAGE
float tcxo = LR11X0_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
#ifdef RF_SWITCH_TABLE
radio.setRfSwitchTable(rfswitch_dios, rfswitch_table);
#endif
#ifdef RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(RX_BOOSTED_GAIN);
#endif
return true; // success
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/nrf52/T1000eBoard.h>
#include <helpers/CustomLR1110Wrapper.h>
extern T1000eBoard board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,76 @@
[nrf52840_t114]
extends = nrf52_base
platform_packages = framework-arduinoadafruitnrf52
build_flags = ${nrf52_base.build_flags}
-I src/helpers/nrf52
-I lib/nrf52/s140_nrf52_6.1.1_API/include
-I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
lib_deps =
${nrf52_base.lib_deps}
rweather/Crypto @ ^0.4.0
[Heltec_t114]
extends = nrf52840_t114
board = heltec_t114
board_build.ldscript = boards/nrf52840_s140_v6.ld
build_flags = ${nrf52840_t114.build_flags}
-I variants/t114
-DHELTEC_T114
-D P_LORA_TX_LED=35
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=130
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${nrf52840_t114.build_src_filter}
+<helpers/*.cpp>
+<helpers/nrf52/T114Board.cpp>
+<../variants/t114>
debug_tool = jlink
upload_protocol = nrfutil
[env:Heltec_t114_repeater]
extends = Heltec_t114
build_src_filter = ${Heltec_t114.build_src_filter}
+<../examples/simple_repeater/main.cpp>
build_flags =
${Heltec_t114.build_flags}
-D ADVERT_NAME='"Heltec_T114 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:Heltec_t114_room_server]
extends = Heltec_t114
build_src_filter = ${Heltec_t114.build_src_filter}
+<../examples/simple_room_server>
build_flags =
${Heltec_t114.build_flags}
-D ADVERT_NAME='"Heltec_T114 Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:Heltec_t114_companion_radio_ble]
extends = Heltec_t114
build_flags =
${Heltec_t114.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_t114.build_src_filter}
+<helpers/nrf52/*.cpp>
+<../examples/companion_radio/main.cpp>
lib_deps =
${Heltec_t114.lib_deps}
densaugeo/base64 @ ~1.4.0

41
variants/t114/target.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include <Arduino.h>
#include "target.h"
T114Board board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
return true; // success
}

9
variants/t114/target.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/nrf52/T114Board.h>
#include <helpers/CustomSX1262Wrapper.h>
extern T114Board board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,72 @@
[nrf52840_techo]
extends = nrf52_base
platform_packages = framework-arduinoadafruitnrf52
build_flags = ${nrf52_base.build_flags}
-I src/helpers/nrf52
-I lib/nrf52/s140_nrf52_6.1.1_API/include
-I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
lib_deps =
${nrf52_base.lib_deps}
rweather/Crypto @ ^0.4.0
[LilyGo_Techo]
extends = nrf52840_techo
board = t-echo
board_build.ldscript = boards/nrf52840_s140_v6.ld
build_flags = ${nrf52840_techo.build_flags}
-I variants/techo
-DLILYGO_TECHO
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=130
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${nrf52840_techo.build_src_filter}
+<helpers/*.cpp>
+<helpers/nrf52/TechoBoard.cpp>
+<../variants/techo>
debug_tool = jlink
upload_protocol = nrfutil
[env:LilyGo_T-Echo_repeater]
extends = LilyGo_Techo
build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_repeater/main.cpp>
build_flags =
${LilyGo_Techo.build_flags}
-D ADVERT_NAME='"T-Echo Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:LilyGo_T-Echo_room_server]
extends = LilyGo_Techo
build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_room_server/main.cpp>
build_flags =
${LilyGo_Techo.build_flags}
-D ADVERT_NAME='"T-Echo Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
[env:LilyGo_T-Echo_companion_radio_ble]
extends = LilyGo_Techo
build_flags =
${LilyGo_Techo.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_Techo.build_src_filter}
+<helpers/nrf52/*.cpp>
+<../examples/companion_radio/main.cpp>
lib_deps =
${LilyGo_Techo.lib_deps}
densaugeo/base64 @ ~1.4.0

41
variants/techo/target.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include <Arduino.h>
#include "target.h"
TechoBoard board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
return true; // success
}

9
variants/techo/target.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/nrf52/TechoBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
extern TechoBoard board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -14,12 +14,12 @@
#define USE_LFXO // 32.768 kHz crystal oscillator
#define VARIANT_MCK (64000000ul)
#define WIRE_INTERFACES_COUNT (1)
#define WIRE_INTERFACES_COUNT (1)
#define PIN_TXCO (21)
////////////////////////////////////////////////////////////////////////////////
// Power
#define PIN_PWR_EN (6)
#define PIN_PWR_EN (12)
#define BATTERY_PIN (4)
#define ADC_MULTIPLIER (4.90F)
@@ -62,11 +62,11 @@
////////////////////////////////////////////////////////////////////////////////
// Builtin LEDs
#define LED_RED (13)
#define LED_RED (34)
#define LED_GREEN (33)
#define LED_BLUE (14)
#define LED_GREEN (15)
#define LED_BUILTIN (15)
#define LED_BUILTIN LED_GREEN
#define PIN_LED LED_BUILTIN
#define LED_PIN LED_BUILTIN
#define LED_STATE_ON LOW
@@ -80,7 +80,7 @@
#define PIN_BUTTON1 (42)
#define BUTTON_PIN PIN_BUTTON1
#define PIN_BUTTON2 (18)
#define PIN_BUTTON2 (11)
#define BUTTON_PIN2 PIN_BUTTON2
#define EXTERNAL_FLASH_DEVICES MX25R1635F

View File

@@ -0,0 +1,59 @@
[Xiao_esp32_C3]
extends = esp32_base
board = seeed_xiao_esp32c3
build_flags =
${esp32_base.build_flags}
-I variants/xiao_c3
-D LORA_TX_BOOST_PIN=D3
-D P_LORA_TX_LED=D5
-D PIN_VBAT_READ=D0
-D P_LORA_DIO_1=D2
-D P_LORA_NSS=D4
-D P_LORA_RESET=RADIOLIB_NC
-D P_LORA_BUSY=D1
-D PIN_BOARD_SDA=D6
-D PIN_BOARD_SCL=D7
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=130.0f ; for best TX power!
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/xiao_c3>
[env:Xiao_C3_Repeater_sx1262]
extends = Xiao_esp32_C3
build_src_filter = ${Xiao_esp32_C3.build_src_filter}
+<../examples/simple_repeater/main.cpp>
build_flags =
${Xiao_esp32_C3.build_flags}
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D SX126X_RX_BOOSTED_GAIN=1
-D LORA_TX_POWER=22
-D ADVERT_NAME='"Xiao Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${Xiao_esp32_C3.lib_deps}
${esp32_ota.lib_deps}
[env:Xiao_C3_Repeater_sx1268]
extends = Xiao_esp32_C3
build_src_filter = ${Xiao_esp32_C3.build_src_filter}
+<../examples/simple_repeater/main.cpp>
build_flags =
${Xiao_esp32_C3.build_flags}
-D RADIO_CLASS=CustomSX1268
-D WRAPPER_CLASS=CustomSX1268Wrapper
-D LORA_TX_POWER=22
-D ADVERT_NAME='"Xiao Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${Xiao_esp32_C3.lib_deps}
${esp32_ota.lib_deps}

View File

@@ -0,0 +1,47 @@
#include <Arduino.h>
#include "target.h"
XiaoC3Board board;
#if defined(P_LORA_SCLK)
static SPIClass spi;
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
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
#if defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
return true; // success
}

10
variants/xiao_c3/target.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include <helpers/XiaoC3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomSX1268Wrapper.h>
extern XiaoC3Board board;
extern RADIO_CLASS radio;
bool radio_init();

View File

@@ -0,0 +1,90 @@
[Xiao_S3_WIO]
extends = esp32_base
board = seeed_xiao_esp32s3
board_check = true
board_build.mcu = esp32s3
build_flags = ${esp32_base.build_flags}
-I variants/xiao_s3_wio
-D SEEED_XIAO_S3
-D P_LORA_DIO_1=39
-D P_LORA_NSS=41
-D P_LORA_RESET=42 ; RADIOLIB_NC
-D P_LORA_BUSY=40 ; DIO2 = 38
-D P_LORA_SCLK=7
-D P_LORA_MISO=8
-D P_LORA_MOSI=9
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=130
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/xiao_s3_wio>
[env:Xiao_S3_WIO_Repeater]
extends = Xiao_S3_WIO
build_src_filter = ${Xiao_S3_WIO.build_src_filter}
+<../examples/simple_repeater/main.cpp>
build_flags =
${Xiao_S3_WIO.build_flags}
-D ADVERT_NAME='"XiaoS3 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${Xiao_S3_WIO.lib_deps}
${esp32_ota.lib_deps}
[env:Xiao_S3_WIO_room_server]
extends = Xiao_S3_WIO
build_src_filter = ${Xiao_S3_WIO.build_src_filter}
+<../examples/simple_room_server>
build_flags =
${Xiao_S3_WIO.build_flags}
-D ADVERT_NAME='"XiaoS3 Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${Xiao_S3_WIO.lib_deps}
${esp32_ota.lib_deps}
[env:Xiao_S3_WIO_terminal_chat]
extends = Xiao_S3_WIO
build_flags =
${Xiao_S3_WIO.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Xiao_S3_WIO.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${Xiao_S3_WIO.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Xiao_S3_WIO_companion_radio_ble]
extends = Xiao_S3_WIO
build_flags =
${Xiao_S3_WIO.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
; -D BLE_DEBUG_LOGGING=1
; -D ENABLE_PRIVATE_KEY_IMPORT=1
; -D ENABLE_PRIVATE_KEY_EXPORT=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Xiao_S3_WIO.build_src_filter}
+<helpers/esp32/*.cpp>
+<../examples/companion_radio/main.cpp>
lib_deps =
${Xiao_S3_WIO.lib_deps}
densaugeo/base64 @ ~1.4.0

View File

@@ -0,0 +1,47 @@
#include <Arduino.h>
#include "target.h"
ESP32Board board;
#if defined(P_LORA_SCLK)
static SPIClass spi;
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
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
#if defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
return true; // success
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <helpers/ESP32Board.h>
#include <helpers/CustomSX1262Wrapper.h>
extern ESP32Board board;
extern RADIO_CLASS radio;
bool radio_init();