Compare commits

...

98 Commits

Author SHA1 Message Date
Scott Powell
b2b755c0fb Merge branch 'dev' 2025-04-07 22:01:08 +10:00
Scott Powell
d7e6a361b5 * ver bump to v1.4.3 2025-04-07 21:59:53 +10:00
Scott Powell
396a7a24b1 * default SF now 11 2025-04-07 21:55:24 +10:00
Scott Powell
9498d2e824 * ID hash 0x00 and 0xFF now reserved for future. 2025-04-06 12:34:09 +10:00
ripplebiz
86a3f592b9 Merge pull request #172 from recrof/dev
set repeater/room server Lat & Lon to 0.0 as default
2025-04-04 19:26:30 +11:00
ripplebiz
4d2380d168 Merge pull request #173 from oltaco/promicro-rfswitch
FIX: Set RfSwitch pins properly.
2025-04-04 19:25:12 +11:00
taco
96faf423e3 FIX: Need to explicitly setRfSwitchPins to drive RXEN high when in receive mode. 2025-04-04 15:39:11 +11:00
recrof
04ad06b8be set repeater/room server Lat & Lon to 0.0 as default 2025-04-03 14:59:20 +02:00
ripplebiz
259f4ec169 Merge pull request #171 from LitBomb/patch-9
Update faq.md
2025-04-02 19:15:33 +11:00
uncle lit
49d24283f7 Update faq.md
Add table of content
Add http://config.meshcore.dev for repeater and room server setup
Add T-Deck GPS info screen sentences counter
Add explanation who 869.525MHz is the chosen frequency for EU and UK and why 867.5MHz isn't good comparing to 869.525
2025-04-01 22:49:09 -07:00
ripplebiz
2bec0f1418 Merge pull request #167 from fdlamotte/xiao_s3_serial_onpins
Allow the use companion radio on other serial interfaces
2025-04-02 00:02:46 +11:00
Florent de Lamotte
c762d88edd Merge branch 'dev' into xiao_s3_serial_onpins 2025-04-01 11:33:00 +02:00
Florent de Lamotte
87f476354f initial commit 2025-04-01 11:32:04 +02:00
ripplebiz
accd80db77 Merge pull request #165 from liamcottle/dev
Show please wait message on boot
2025-04-01 14:42:59 +11:00
liamcottle
4d4a0ae4e3 init display before radio 2025-04-01 16:10:12 +13:00
liamcottle
7e583d7f98 show please wait message on room server boot 2025-04-01 16:04:06 +13:00
liamcottle
f93a5156bb show please wait message on repeater boot 2025-04-01 16:00:09 +13:00
liamcottle
af070af554 show please wait message on companion boot 2025-04-01 15:50:24 +13:00
ripplebiz
ec320cb5a8 Merge pull request #164 from liamcottle/dev
Fix Companion Display Noise
2025-04-01 13:02:57 +11:00
liamcottle
87443ad43f clear display on init to avoid static 2025-04-01 14:46:48 +13:00
ripplebiz
0a62ab663f Merge pull request #155 from oltaco/promicro-refactor
ProMicro refactoring
2025-04-01 12:43:09 +11:00
ripplebiz
7d24c65c4e Merge pull request #163 from LitBomb/patch-7
Update faq.md
2025-04-01 12:37:08 +11:00
uncle lit
5e553b1fc8 Update faq.md
added the following to FAQ:

public/private flood and repeater set flood.max CLI
public key in hex and base64
How paths are created and reuse to minimize utilization
T-Deck Diag explanation
Update repeater with a Raspberry Pi
clients don't repeat explanation
other meshcore projects
fix discord link to build instruction
2025-03-31 14:51:52 -07:00
Scott Powell
3a6b8fda93 * room server: faster sync of unread posts, MAX_UNSYNCED_POSTS now 32 (was 16) 2025-03-31 14:01:49 +11:00
ripplebiz
6e109779f3 Merge pull request #149 from jameshall9327/main
TBeam: Added Repeater
2025-03-31 13:10:20 +11:00
Scott Powell
3a920986ca * Tbeam fix: GPIO assignments wrong 2025-03-31 13:03:33 +11:00
Scott Powell
8740528d37 Merge commit '5493dbc12096f3f090cc48e70547a4491f5a06b1' into dev 2025-03-31 00:15:38 +11:00
ripplebiz
5493dbc120 Merge pull request #157 from fdlamotte/t1000e_clock
Remove rtc auto-detect on t1000
2025-03-31 00:14:17 +11:00
Florent
2f6427ce21 Remove rtc auto-detect on t1000 2025-03-30 15:09:40 +02:00
Scott Powell
97b6a1874a Merge branch 'dev' 2025-03-30 19:18:35 +11:00
ripplebiz
05fa1ba50e Merge pull request #145 from recrof/main
change new default freq to 869.525
2025-03-30 19:01:55 +11:00
Scott Powell
fc6aa0ed0f * ver bump to v1.4.2 2025-03-30 18:56:21 +11:00
taco
b8f09531c4 fix: give ProMicroLLCC68 a different definition 2025-03-30 15:57:20 +11:00
taco
01e98caea7 Refactor: FaketecBoard to PromicroBoard 2025-03-30 15:52:25 +11:00
Scott Powell
75eabd5c66 * minor refactor 2025-03-30 14:47:52 +11:00
ripplebiz
2e5b4eb910 Merge pull request #154 from recrof/dev
added new CommonCLI get commands: `get role` and `get public.key`
2025-03-30 14:40:49 +11:00
recrof
b77701ced0 added new CommonCLI get commands: get role and get public.key 2025-03-29 19:40:35 +01:00
Scott Powell
0d5c17f7ed * ESP-Now: long range mode 2025-03-29 13:27:17 +11:00
Scott Powell
022b43ef3d * isReceiving() now includes either preamble detect or header detect 2025-03-28 22:20:42 +11:00
Scott Powell
89bd6c3416 * Dispatcher: now impose a max time for CAD fail (ie. channel busy), defined by getCADFailMaxDuration(), default 4 seconds.
(an sx126x bug _might_ be making radio->isReceiving() return true indefinitely)
2025-03-28 20:50:42 +11:00
Scott Powell
ba93867037 * companion: temp fix: logRxRaw() is blowing the MAX_FRAME_SIZE 2025-03-28 14:14:59 +11:00
James Hall
74e7af3f57 TBeam: Added Repeater 2025-03-27 09:21:29 -04:00
Scott Powell
c0cb57be5c * refactor: rtc_clock now defined by variants/*/target modules 2025-03-27 21:28:06 +11:00
Scott Powell
2224bddcb5 * new ESPNOWRadio driver
* refactored the examples/*/main.cpp modules, moving radio specifics to variants/*/target modules
* new Generic_ESPNOW_* target envs
2025-03-27 19:34:16 +11:00
Scott Powell
9c165add61 Merge branch 'dev' into esp-now 2025-03-27 17:24:56 +11:00
Scott Powell
d5cc28b0c5 * room server: now support "log start/stop..." 2025-03-27 13:00:40 +11:00
recrof
8a248bb47d change new default freq to 869.525 2025-03-26 18:06:20 +01:00
ripplebiz
32edc934fa Merge pull request #144 from adrian-immel/Fix-Typo
Fix build issue for FakeTec board
2025-03-26 16:22:01 +11:00
Adrian
7cab681697 Update FaketecBoard.cpp 2025-03-25 23:21:05 +01:00
Adrian
4d262e9e5d Rename faketecBoard.h to FaketecBoard.h 2025-03-25 23:16:08 +01:00
Adrian
ee230ab2ca Rename faketecBoard.cpp to FaketecBoard.cpp 2025-03-25 23:14:48 +01:00
Scott Powell
321a9425da * experiment: enabling forwarding in terminal chat 2025-03-26 03:57:36 +11:00
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
d32e641225 * fix for double radio.begin(). RNG seed from pressing ENTER 2025-03-25 17:40:36 +11:00
Scott Powell
1220c69aa9 * wifi power max 2025-03-25 15:13:07 +11:00
Scott Powell
8355543366 * more experiments 2025-03-25 01:26:46 +11:00
Scott Powell
7bd7bfb14a * ESP-NOW experiment with terminal-chat 2025-03-24 21:16:00 +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
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
taco
a67bb8e1e3 tidy up 2025-03-17 12:17:36 +11:00
taco
8b2f783d04 OLED and button support, some refactoring. 2025-03-16 13:24:43 +11:00
88 changed files with 4628 additions and 854 deletions

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

@@ -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

@@ -1,13 +1,75 @@
# MeshCore-FAQ
**MeshCore-FAQ**<!-- omit from toc -->
A list of frequently-asked questions and answers for MeshCore
The current version of this MeshCore FAQ is at https://github.com/ripplebiz/MeshCore/blob/main/docs/faq.md.
This MeshCore FAQ is also mirrored at https://github.com/LitBomb/MeshCore-FAQ and might have newer updates if pull requests on Scott's MeshCore repo are not approved yet.
author: https://github.com/LitBomb
author: https://github.com/LitBomb<!-- omit from toc -->
---
## Q: What is MeshCore?
- [1. Introduction](#1-introduction)
- [1.1. Q: What is MeshCore?](#11-q-what-is-meshcore)
- [1.2. Q: What do you need to start using MeshCore?](#12-q-what-do-you-need-to-start-using-meshcore)
- [1.2.1. Hardware](#121-hardware)
- [1.2.2. Firmware](#122-firmware)
- [1.2.3. Companion Radio Firmware](#123-companion-radio-firmware)
- [1.2.4. Repeater](#124-repeater)
- [1.2.5. Room Server](#125-room-server)
- [2. Initial Setup](#2-initial-setup)
- [2.1. Q: How many devices do I need to start using meshcore?](#21-q-how-many-devices-do-i-need-to-start-using-meshcore)
- [2.2. Q: Does MeshCore cost any money?](#22-q-does-meshcore-cost-any-money)
- [2.3. Q: What frequencies are supported by MeshCore?](#23-q-what-frequencies-are-supported-by-meshcore)
- [2.4. Q: What is an "advert" in MeshCore?](#24-q-what-is-an-advert-in-meshcore)
- [2.5. Q: Is there a hop limit?](#25-q-is-there-a-hop-limit)
- [3. Server Administration](#3-server-administration)
- [3.1. Q: How do you configure a repeater or a room server?](#31-q-how-do-you-configure-a-repeater-or-a-room-server)
- [3.2. Q: Do I need to set the location for a repeater?](#32-q-do-i-need-to-set-the-location-for-a-repeater)
- [3.3. Q: What is the password to administer a repeater or a room server?](#33-q-what-is-the-password-to-administer-a-repeater-or-a-room-server)
- [3.4. Q: What is the password to join a room server?](#34-q-what-is-the-password-to-join-a-room-server)
- [4. T-Deck Related](#4-t-deck-related)
- [4.1. Q: What are the steps to get a T-Deck into DFU (Device Firmware Update) mode?](#41-q-what-are-the-steps-to-get-a-t-deck-into-dfu-device-firmware-update-mode)
- [4.2. Q: Why is my T-Deck Plus not getting any satellite lock?](#42-q-why-is-my-t-deck-plus-not-getting-any-satellite-lock)
- [4.3. Q: Why is my OG (non-Plus) T-Deck not getting any satellite lock?](#43-q-why-is-my-og-non-plus-t-deck-not-getting-any-satellite-lock)
- [4.4. Q: What size of SD card does the T-Deck support?](#44-q-what-size-of-sd-card-does-the-t-deck-support)
- [4.5. Q: How do I get maps on T-Deck?](#45-q-how-do-i-get-maps-on-t-deck)
- [4.6. Q: Where do the map tiles go?](#46-q-where-do-the-map-tiles-go)
- [4.7. Q: How to unlock deeper map zoom and server management features on T-Deck?](#47-q-how-to-unlock-deeper-map-zoom-and-server-management-features-on-t-deck)
- [4.8. Q: How to decipher the diagnostics screen on T-Deck?](#48-q-how-to-decipher-the-diagnostics-screen-on-t-deck)
- [4.9. Q: The T-Deck sound is too loud?](#49-q-the-t-deck-sound-is-too-loud)
- [4.10. Q: Can you customize the sound?](#410-q-can-you-customize-the-sound)
- [4.11. Q: What is the 'Import from Clipboard' feature on the t-deck and is there a way to manually add nodes without having to receive adverts?](#411-q-what-is-the-import-from-clipboard-feature-on-the-t-deck-and-is-there-a-way-to-manually-add-nodes-without-having-to-receive-adverts)
- [5. General](#5-general)
- [5.1. Q: What are BW, SF, and CR?](#51-q-what-are-bw-sf-and-cr)
- [5.2. Q: Do MeshCore clients repeat?](#52-q-do-meshcore-clients-repeat)
- [5.3. Q: What happens when a node learns a route via a mobile repeater, and that repeater is gone?](#53-q-what-happens-when-a-node-learns-a-route-via-a-mobile-repeater-and-that-repeater-is-gone)
- [5.4. Q: How does a node discovery a path to its destination and then use it to send messages in the future, instead of flooding every message it sends like Meshtastic?](#54-q-how-does-a-node-discovery-a-path-to-its-destination-and-then-use-it-to-send-messages-in-the-future-instead-of-flooding-every-message-it-sends-like-meshtastic)
- [5.5. Q: Do public channels always flood? Do private channels always flood?](#55-q-do-public-channels-always-flood-do-private-channels-always-flood)
- [5.6. Q: what is the public key for the default public channel?](#56-q-what-is-the-public-key-for-the-default-public-channel)
- [5.7. Q: Is MeshCore open source?](#57-q-is-meshcore-open-source)
- [5.8. Q: How can I support MeshCore?](#58-q-how-can-i-support-meshcore)
- [5.9. Q: How do I build MeshCore firmware from source?](#59-q-how-do-i-build-meshcore-firmware-from-source)
- [5.10. Q: Are there other MeshCore related open source projects?](#510-q-are-there-other-meshcore-related-open-source-projects)
- [5.11. Q: Does MeshCore support ATAK](#511-q-does-meshcore-support-atak)
- [5.12. Q: How do I add a node to the MeshCore Map](#512-q-how-do-i-add-a-node-to-the-meshcore-map)
- [5.13. Q: Can I use a Raspberry Pi to update a MeshCore radio?](#513-q-can-i-use-a-raspberry-pi-to-update-a-meshcore-radio)
- [5.14. Q: Are there are projects built around MeshCore?](#514-q-are-there-are-projects-built-around-meshcore)
- [5.14.1. meshcoremqtt](#5141-meshcoremqtt)
- [5.14.2. MeshCore for Home Assistant](#5142-meshcore-for-home-assistant)
- [5.14.3. Python MeshCore](#5143-python-meshcore)
- [5.14.4. meshcore-cli](#5144-meshcore-cli)
- [5.14.5. meshcore.js](#5145-meshcorejs)
- [6. Troubleshooting](#6-troubleshooting)
- [6.1. Q: My client says another client or a repeater or a room server was last seen many, many days ago.](#61-q-my-client-says-another-client-or-a-repeater-or-a-room-server-was-last-seen-many-many-days-ago)
- [6.2. Q: A repeater or a client or a room server I expect to see on my discover list (on T-Deck) or contact list (on a smart device client) are not listed.](#62-q-a-repeater-or-a-client-or-a-room-server-i-expect-to-see-on-my-discover-list-on-t-deck-or-contact-list-on-a-smart-device-client-are-not-listed)
- [6.3. Q: How to connect to a repeater via BLE (bluetooth)?](#63-q-how-to-connect-to-a-repeater-via-ble-bluetooth)
- [6.4. Q: I can't connect via bluetooth, what is the bluetooth pairing code?](#64-q-i-cant-connect-via-bluetooth-what-is-the-bluetooth-pairing-code)
- [6.5. Q: My Heltec V3 keeps disconnecting from my smartphone. It can't hold a solid Bluetooth connection.](#65-q-my-heltec-v3-keeps-disconnecting-from-my-smartphone--it-cant-hold-a-solid-bluetooth-connection)
- [7. Other Questions:](#7-other-questions)
- [7.1. Q: How to Update repeater and room server firmware over the air?](#71-q-how-to--update-repeater-and-room-server-firmware-over-the-air)
## 1. Introduction
### 1.1. Q: What is MeshCore?
**A:** MeshCore is free and open source
* MeshCore is the routing and firmware etc, available on GitHub under MIT license
@@ -22,7 +84,7 @@ These features are completely optional and aren't needed for the core messaging
Anyone is able to build anything they like on top of MeshCore without paying anything.
## Q: What do you need to start using MeshCore?
### 1.2. Q: What do you need to start using MeshCore?
**A:** Everything you need for MeshCore is available at:
Main web site: [https://meshcore.co.uk/](https://meshcore.co.uk/)
Firmware Flasher: https://flasher.meshcore.co.uk/
@@ -34,15 +96,15 @@ Anyone is able to build anything they like on top of MeshCore without paying any
You need LoRa hardware devices to run MeshCore firmware as clients or server (repeater and room server).
### Hardware
#### 1.2.1. Hardware
To use MeshCore without using a phone as the client interface, you can run MeshCore on a T-Deck or T-Deck Plus. It is a complete off-grid secure communication solution.
MeshCore is also available on a variety of 868MHz and 915MHz LoRa devices. For example, RAK4631 devices (19003, 19007, 19026), Heltec V3, Xiao S3 WIO, Xiao C3, Heltec T114, Station G2, Seeed Studio T1000-E. More devices will be supported later.
### Firmware
#### 1.2.2. Firmware
MeshCore has four firmware types that are not available on other LoRa systems. MeshCore has the following:
#### Companion Radio Firmware
#### 1.2.3. Companion Radio Firmware
Companion radios are for connecting to the Android app or web app as a messenger client. There are two different companion radio firmware versions:
1. **BLE Companion**
@@ -54,12 +116,12 @@ Companion radios are for connecting to the Android app or web app as a messenger
<https://meshcore.liamcottle.net/#/>
<https://client.meshcore.co.uk/tabs/devices>
#### Repeater
#### 1.2.4. Repeater
Repeaters are used to extend the range of a MeshCore network. Repeater firmware runs on the same devices that run client firmware. A repeater's job is to forward MeshCore packets to the destination device. It does **not** forward or retransmit every packet it receives, unlike other LoRa mesh systems.
A repeater can be remotely administered using a T-Deck running the MeshCore firwmware with remote admistration features unlocked, or from a BLE Companion client connected to a smartphone running the MeshCore app.
#### Room Server
#### 1.2.5. Room Server
A room server is a simple BBS server for sharing posts. T-Deck devices running MeshCore firmware or a BLE Companion client connected to a smartphone running the MeshCore app can connect to a room server.
room servers store message history on them, and push the stored messages to users. Room servers allow roaming users to come back later and retrieve message history. Contrast to channels, messages are either received when it's sent, or not received and missed if the a room user is out of range. You can think of room servers like email servers where you can come back later and get your emails from your mail server
@@ -74,9 +136,9 @@ A room server can also take on the repeater role. To enable repeater role on a
---
## Initial Setup
## 2. Initial Setup
### Q: How many devices do I need to start using meshcore?
### 2.1. Q: How many devices do I need to start using meshcore?
**A:** If you have one supported device, flash the BLE Companion firmware and use your device as a client. You can connect to the device using the Android client via bluetooth (iOS client will be available later). You can start communiating with other MeshCore users near you.
If you have two supported devices, and there are not many MeshCore users near you, flash both of them to BLE Companion firmware so you can use your devices to communiate with your near-by friends and family.
@@ -91,7 +153,7 @@ The repeater and room server CLI reference is here: https://github.com/ripplebiz
If you have more supported devices, you can use your additional deivces with the room server firmware.
### Q: Does MeshCore cost any money?
### 2.2. Q: Does MeshCore cost any money?
**A:** All radio firmware versions (e.g. for Heltec V3, RAK, T-1000E, etc) are free and open source developed by Scott at Ripple Radios.
@@ -100,19 +162,23 @@ The native Android and iOS client uses the freemium model and is developed by Li
The T-Deck firmware is free to download and most features are available without cost. To support the firmware developer, you can pay for a registration key to unlock your T-Deck for deeper map zoom and remote server administration over RF using the T-Deck. You do not need to pay for the registration to use your T-Deck for direct messaging and connecting to repeaters and room servers.
### Q: What frequencies are supported by MeshCore?
### 2.3. 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 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
In UK and EU, 867.5MHz is not allowed to use 250kHz bandwidth and it only allows 2.5% duty cycle for clients. 869.525Mhz allows an airtime of 10%, 250KHz bandwidth, and a higher EIRP, therefore MeshCore nodes can send more often and with more power. That is why this frequency is chosen for UK and EU. This is also why Meshtastic also uses this frequency.
[Source]([https://](https://discord.com/channels/826570251612323860/1330643963501351004/1356540643853209641))
the rest of the radio settings are the same for all frequencies:
- Spread Factor (SF): 10
- Coding Rate (CR): 5
- Bandwidth (BW): 250.00
### Q: What is an "advert" in MeshCore?
### 2.4. Q: What is an "advert" in MeshCore?
**A:**
Advert means to advertise yourself on the network. In Reticulum terms it would be to announce. In Meshtastic terms it would be the node sending it's node info.
@@ -125,7 +191,7 @@ MeshCore clients only advertise themselves when the user initiates it. A repeate
`set advert.interval {minutes}`
### Q: Is there a hop limit?
### 2.5. Q: Is there a hop limit?
**A:** Internally the firmware has maximum limit of 64 hops. In real world settings it will be difficult to get close to the limit due to the environments and timing as packets travel further and further. We want to hear how far your MeshCore conversations go.
@@ -133,30 +199,43 @@ MeshCore clients only advertise themselves when the user initiates it. A repeate
---
## Server Administration
## 3. Server Administration
### Q: How do you configure a repeater or a room server?
**A:** One of these servers can be administered with one of the options below:
### 3.1. Q: How do you configure a repeater or a room server?
**A:** - When MeshCore is flashed onto a LoRa device is for the first time, it is necessary to set the server device's frequency to make it utilize the frequency that is legal in your country or region.
Repeater or room server can be administered with one of the options below:
- After a repeater or room server firmware is flashed on to a LoRa device, go to <https://config.meshcore.dev> and use the web user interface to connect to the LoRa device via USB serial. From there you can set the name of the server, its frequency and other related settings, location, passwords etc.
![image](https://github.com/user-attachments/assets/bec28ff3-a7d6-4a1e-8602-cb6b290dd150)
- Connect the server device using a USB cable to a computer running Chrome on https://flasher.meshcore.co.uk/, then use the `console` feature to connect to the device
- this is necessary to set the server device's frequency if it doesn't match the frequency for your local region or country
- MeshCore smart device clients have the ability to remotely administer servers.
- A T-Deck running unlocked/registered MeshCore firmware. Remote server administration is enabled through registering your T-Deck with Ripple Radios. It is one of the ways to support MeshCore development. You can register your T-Deck at:
<https://buymeacoffee.com/ripplebiz/e/249834>
### Q: Do I need to set the location for a repeater?
- Use a MeshCore smartphone clients to remotely administer servers via LoRa.
- A T-Deck running unlocked/registered MeshCore firmware. Remote server administration is enabled through registering your T-Deck with Ripple Radios. It is one of the ways to support MeshCore development. You can register your T-Deck at:
<https://buymeacoffee.com/ripplebiz/e/249834>
### 3.2. Q: Do I need to set the location for a repeater?
**A:** With location set for a repeater, it can show up on a MeshCore map in the future. Set location with the following commands:
`set lat <GPS Lat> set long <GPS Lon>`
You can get the latitude and longitude from Google Maps by right-clicking the location you are at on the map.
### Q: What is the password to administer a repeater or a room server?
### 3.3. Q: What is the password to administer a repeater or a room server?
**A:** The default admin password to a repeater and room server is `password`. Use the following command to change the admin password:
`password {new-password}`
### Q: What is the password to join a room server?
### 3.4. Q: What is the password to join a room server?
**A:** The default guest password to a room server is `hello`. Use the following command to change the guest password:
`set guest.password {guest-password}`
@@ -164,9 +243,9 @@ You can get the latitude and longitude from Google Maps by right-clicking the lo
---
## T-Deck Related
## 4. T-Deck Related
### Q: What are the steps to get a T-Deck into DFU (Device Firmware Update) mode?
### 4.1. Q: What are the steps to get a T-Deck into DFU (Device Firmware Update) mode?
**A:**
1. Device off
2. Connect USB cable to device
@@ -177,16 +256,20 @@ You can get the latitude and longitude from Google Maps by right-clicking the lo
7. T-Deck in DFU mode now
8. At this point you can begin flashing using <https://flasher.meshcore.co.uk/>
### Q: Why is my T-Deck Plus not getting any satellite lock?
### 4.2. Q: Why is my T-Deck Plus not getting any satellite lock?
**A:** For T-Deck Plus, the GPS baud rate should be set to **38400**. Also, a number of T-Deck Plus devices were found to have the GPS module installed upside down, with the GPS antenna facing down instead of up. If your T-Deck Plus still doesn't get any satellite lock after setting the baud rate to 38400, you might need to open up the device to check the GPS orientation.
### Q: Why is my OG (non-Plus) T-Deck not getting any satellite lock?
GPS on T-Deck is always enabled. You can skip the "GPS clock sync" and the T-Deck will continue to try to get a GPS lock. You can go to the `GPS Info` screen, you should see the `Sentences:` coutner increasing if the baud rate is correct.
[Source]([https://](https://discord.com/channels/826570251612323860/1330643963501351004/1356609240302616689))
### 4.3. Q: Why is my OG (non-Plus) T-Deck not getting any satellite lock?
**A:** The OG (non-Plus) T-Deck doesn't come with a GPS. If you added a GPS to your OG T-Deck, please refer to the manual of your GPS to see what baud rate it requires. Alternatively, you can try to set the baud rate from 9600, 19200, etc., and up to 115200 to see which one works.
### Q: What size of SD card does the T-Deck support?
### 4.4. Q: What size of SD card does the T-Deck support?
**A:** Users have had no issues using 16GB or 32GB SD cards. Format the SD card to **FAT32**.
### Q: How do I get maps on T-Deck?
### 4.5. Q: How do I get maps on T-Deck?
**A:** You need map tiles. You can get pre-downloaded map tiles here (a good way to support development):
- <https://buymeacoffee.com/ripplebiz/e/342543> (Europe)
- <https://buymeacoffee.com/ripplebiz/e/342542> (US)
@@ -200,28 +283,46 @@ There is also a modified script that adds additional error handling and parallel
UK map tiles are available separately from Andy Kirby on his discord server:
<https://discord.com/channels/826570251612323860/1330643963501351004/1331346597367386224>
### Q: Where do the map tiles go?
### 4.6. Q: Where do the map tiles go?
Once you have the tiles downloaded, copy the `\tiles` folder to the root of your T-Deck's SD card.
### Q: How to unlock deeper map zoom and server management features on T-Deck?
### 4.7. Q: How to unlock deeper map zoom and server management features on T-Deck?
**A:** You can download, install, and use the T-Deck firmware for free, but it has some features (map zoom, server administration) that are enabled if you purchase an unlock code for \$10 per T-Deck device.
Unlock page: <https://buymeacoffee.com/ripplebiz/e/249834>
### 4.8. Q: How to decipher the diagnostics screen on T-Deck?
### Q: The T-Deck sound is too loud?
### Q: Can you customize the sound?
**A: ** Space is tight on T-Deck's screen so the information is a bit cryptic. Format is :
`{hops} l:{packet-length}({payload-len}) t:{packet-type} snr:{n} rssi:{n}`
See here for packet-type: [https://github.com/ripplebiz/MeshCore/blob/main/src/Packet.h#L19](https://github.com/ripplebiz/MeshCore/blob/main/src/Packet.h#L19 "https://github.com/ripplebiz/MeshCore/blob/main/src/Packet.h#L19")
#define PAYLOAD_TYPE_REQ 0x00 // request (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
#define PAYLOAD_TYPE_RESPONSE 0x01 // response to REQ or ANON_REQ (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
#define PAYLOAD_TYPE_TXT_MSG 0x02 // a plain text message (prefixed with dest/src hashes, MAC) (enc data: timestamp, text)
#define PAYLOAD_TYPE_ACK 0x03 // a simple ack #define PAYLOAD_TYPE_ADVERT 0x04 // a node advertising its Identity
#define PAYLOAD_TYPE_GRP_TXT 0x05 // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg")
#define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, blob)
#define PAYLOAD_TYPE_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)
[Source](https://discord.com/channels/1343693475589263471/1343693475589263474/1350611321040932966)
### 4.9. Q: The T-Deck sound is too loud?
### 4.10. Q: Can you customize the sound?
**A:** You can customise the sounds on the T-Deck, just by placing `.mp3` files onto the `root` dir of the SD card. `startup.mp3`, `alert.mp3` and `new-advert.mp3`
### Q: What is the 'Import from Clipboard' feature on the t-deck and is there a way to manually add nodes without having to receive adverts?
### 4.11. Q: What is the 'Import from Clipboard' feature on the t-deck and is there a way to manually add nodes without having to receive adverts?
**A:** 'Import from Clipboard' is for importing a contact via a file named 'clipboard.txt' on the SD card. The opposite, is in the Identity screen, the 'Card to Clipboard' menu, which writes to 'clipboard.txt' so you can share yourself (call these 'biz cards', that start with "meshcore://...")
---
## General
## 5. General
### Q: What are BW, SF, and CR?
### 5.1. Q: What are BW, SF, and CR?
**A:**
@@ -237,74 +338,201 @@ 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?
### 5.2. Q: Do MeshCore clients repeat?
**A:** No, MeshCore clients do not repeat. This is the core of MeshCore's messaging-first design. This is to avoid devices flooding the air ware and create endless collisions so messages sent aren't received.
In MeshCore, only repeaters and room server with '`set repeat on` repeat.
### 5.3. 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.
### 5.4. Q: How does a node discovery a path to its destination and then use it to send messages in the future, instead of flooding every message it sends like Meshtastic?
### Q: Is MeshCore open source?
Routes are stored in sender's contact list. When you send a message the first time, the message first gets to your destination by flood routing, When your destination node gets the message, it sends back to the sender a delivery report with all repeaters that the original message went through. This delivery report is flood-routed back to you the sender and is a basis for future direct path. when you send the next message, the path will get embedded into the packet and be evaluated by repeaters. if the hop and address of the repeater matches, it will retransmit the message, otherwise it will not retransmit, hence minimizing utilization.
[Source](https://discord.com/channels/826570251612323860/1330643963501351004/1351279141630119996)
### 5.5. Q: Do public channels always flood? Do private channels always flood?
**A:** Yes, group channels are A to B, so there is no defined path. They have to flood. Repeaters can however deny flood traffic up to some hop limit, with the `set flood.max` CLI command. Admistrators of repeaters get to set the rules of their repeaters.
[Source](https://discord.com/channels/1343693475589263471/1343693475589263474/1350023009527664672)
### 5.6. Q: what is the public key for the default public channel?
**A:** The smartphone app key is in hex:
` 8b3387e9c5cdea6ac9e5edbaa115cd72`
T-Deck uses the same key but in base64
`izOH6cXN6mrJ5e26oRXNcg==`
The third character is the capital letter 'O', not zero `0`
[Source](https://discord.com/channels/826570251612323860/1330643963501351004/1354194409213792388)
### 5.7. 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>
### Q: How can I support MeshCore?
### 5.8. Q: How can I support MeshCore?
**A:** Provide your honest feedback on GitHub and on AndyKirby's Discord server <http://discord.com/invite/H62Re4DCeD>. Spread the word of MeshCore to your friends and communities; help them get started with MeshCore. Support Scott's MeshCore development at <https://buymeacoffee.com/ripplebiz>.
Support Liam Cottle's smartphone client development by unlocking the server administration wait gate with in-app purchase
Support Rastislav Vysoky (recrof)'s flasher web site and the map web site development through [PayPal](https://www.paypal.com/donate/?business=DREHF5HM265ES&no_recurring=0&item_name=If+you+enjoy+my+work%2C+you+can+support+me+here%3A&currency_code=EUR) or [Revolut](https://revolut.me/recrof)
### Q: How do I build MeshCore firmware from source?
### 5.9. Q: How do I build MeshCore firmware from source?
**A:** See instructions here:
<https://discord.com/channels/826570251612323860/1330643963501351004/1342120825251299388>
https://discord.com/channels/826570251612323860/1330643963501351004/1341826372120608769
Build instructions for MeshCore:
For Windows, first install WSL and Python+pip via: https://plainenglish.io/blog/setting-up-python-on-windows-subsystem-for-linux-wsl-26510f1b2d80
(Linux, Windows+WSL) In the terminal/shell:
```
sudo apt update
sudo apt install libpython3-dev
sudo apt install python3-venv
```
Mac: python3 should be already installed.
Then it should be the same for all platforms:
```
python3 -m venv meshcore
cd meshcore && source bin/activate
pip install -U platformio
git clone https://github.com/ripplebiz/MeshCore.git
cd MeshCore
```
open platformio.ini and in `[arduino_base]` edit the `LORA_FREQ=867.5`
save, then run:
```
pio run -e RAK_4631_Repeater
```
then you'll find `firmware.zip` in `.pio/build/RAK_4631_Repeater`
Andy also has a video on how to build using VS Code:
*How to build and flash Meshcore repeater firmware | Heltec V3*
<https://www.youtube.com/watch?v=WJvg6dt13hk> *(Link referenced in the Discord post)*
### Q: Are there other MeshCore related open source projects?
### 5.10. Q: Are there other MeshCore related open source projects?
**A:** [Liam Cottle](https://liamcottle.net)'s MeshCore web client and MeshCore Javascript libary are open source under MIT license.
Web client: https://github.com/liamcottle/meshcore-web
Javascript: https://github.com/liamcottle/meshcore.js
### Q: Does MeshCore support ATAK
### 5.11. Q: Does MeshCore support ATAK
**A:** ATAK is not currently on MeshCore's roadmap.
### Q: How do I add a node to the [MeshCore Map]([url](https://meshcore.co.uk/map.html))
Meshcore would not be best suited to ATAK because MeshCore:
clients do not repeat and therefore you would need a network of repeaters in place
will not have a stable path where all clients are constantly moving between repeaters
MeshCore clients would need to reset path constantly and flood traffic across the network which could lead to lots of collisions with something as chatty as ATAK.
This could change in the future if MeshCore develops a client firmware that repeats.
[Source](https://discord.com/channels/826570251612323860/1330643963501351004/1354780032140054659)
### 5.12. Q: How do I add a node to the [MeshCore Map]([url](https://meshcore.co.uk/map.html))
**A:** From the smartphone app, connect to a BLE Companion radio
- To add the BLE Companion radio your smartphone is connected to to the map, tap the `advert` icon, then tap `Advert (To Clipboard)`.
- To add a Repeater or Room Server to the map, tap the 3 dots next to the Repeater or Room Server you want to add to the map, then tap `Share (To Clipboard)`.
- Go to the [MeshCore Map web site]([url](https://meshcore.co.uk/map.html)), tap the plus sign on the lower right corner and paste in the meshcore://... blob, then tap `Add Node`
### 5.13. Q: Can I use a Raspberry Pi to update a MeshCore radio?
** A:** Yes.
You will need to install picocom on the pi.
`sudo apt install picocom`
Then run the following commands to setup the repeater.
```
picocom -b 115200 /dev/ttyUSB0 --imap lfcrlf
set name your_repeater_name
time epoch_time
password your_unique_password
set advert.interval 240
advert
```
Note: If using a RAK the path will most likely be /dev/ttyACM0
Epoch time comes from https://www.epochconverter.com/
You can also flash the repeater using esptool. You will need to install esptool with the following command...
`pip install esptool --break-system-packages`
Then to flash the firmware to Heltec, obtain the .bin file from https://flasher.meshcore.co.uk/ (download all firmware link)
For Heltec:
`esptool.py -p /dev/ttyUSB0 --chip esp32-s3 write_flash 0x00000 firmware.bin`
If flashing a visual studio code build bin file, flash with the following offset:
`esptool.py -p /dev/ttyUSB0 --chip esp32-s3 write_flash 0x10000 firmware.bin`
For Pi
Download the zip from the online flasher website and use the following command:
Note: Requires adafruit-nrfutil command which can be installed as follows.
`pip install adafruit-nrfutil --break-system-packages`
```
adafruit-nrfutil --verbose dfu serial --package t1000_e_bootloader-0.9.1-5-g488711a_s140_7.3.0.zip -p /dev/ttyACM0 -b 115200 --singlebank --touch 1200
```
[Source](https://discord.com/channels/826570251612323860/1330643963501351004/1342120825251299388)
### 5.14. Q: Are there are projects built around MeshCore?
**A:** Yes. See the following:
#### 5.14.1. meshcoremqtt
A python based script to send meshore debug and packet capture data to MQTT for analysis
https://github.com/Andrew-a-g/meshcoretomqtt
#### 5.14.2. MeshCore for Home Assistant
A custom Home Assistant integration for MeshCore mesh radio nodes. It allows you to monitor and control MeshCore nodes via USB, BLE, or TCP connections.
https://github.com/awolden/meshcore-ha
#### 5.14.3. Python MeshCore
Bindings to access your MeshCore companion radio nodes in python.
https://github.com/fdlamotte/meshcore_py
#### 5.14.4. meshcore-cli
CLI interface to MeshCore companion radio over BLE, TCP, or serial. Uses Pyton MeshCore above.
https://github.com/fdlamotte/meshcore-cli
#### 5.14.5. meshcore.js
A Javascript library for interacting with a MeshCore device running the companion radio firmware
https://github.com/liamcottle/meshcore.js
---
## Troubleshooting
## 6. Troubleshooting
### Q: My client says another client or a repeater or a room server was last seen many, many days ago.
### Q: A repeater or a client or a room server I expect to see on my discover list (on T-Deck) or contact list (on a smart device client) are not listed.
### 6.1. Q: My client says another client or a repeater or a room server was last seen many, many days ago.
### 6.2. Q: A repeater or a client or a room server I expect to see on my discover list (on T-Deck) or contact list (on a smart device client) are not listed.
**A:**
- If your client is a T-Deck, it may not have its time set (no GPS installed, no GPS lock, or wrong GPS baud rate).
- If you are using the Android or iOS client, the other client, repeater, or room server may have the wrong time.
You can get the epoch time on <https://www.epochconverter.com/> and use it to set your T-Deck clock. For a repeater and room server, the admin can use a T-Deck to remotely set their clock (clock sync), or use the `time` command in the USB serial console with the server device connected.
### Q: How to connect to a repeater via BLE (bluetooth)?
### 6.3. Q: How to connect to a repeater via BLE (bluetooth)?
**A:** You can't connect to a device running repeater firmware via bluetooth. Devices running the BLE companion firmware you can connect to it via bluetooth using the android app
### Q: I can't connect via bluetooth, what is the bluetooth pairing code?
### 6.4. Q: I can't connect via bluetooth, what is the bluetooth pairing code?
**A:** the default bluetooth pairing code is `123456`
### Q: My Heltec V3 keeps disconnecting from my smartphone. It can't hold a solid Bluetooth connection.
### 6.5. Q: My Heltec V3 keeps disconnecting from my smartphone. It can't hold a solid Bluetooth connection.
**A:** Heltec V3 has a very small coil antenna on its PCB for WiFi and Bluetooth connectivty. It has a very short range, only a few feet. It is possible to remove the coil antenna and replace it with a 31mm wire. The BT range is much improved with the modification.
---
## Other Questions:
### Q: How to Update repeater and room server firmware over the air?
## 7. Other Questions:
### 7.1. Q: How to Update repeater and room server firmware over the air?
**A:** Only nRF-based RAK4631 and Heltec T114 OTA firmware update are verified using nRF smartphone app. Lilygo T-Echo doesn't work currently.
You can update repeater and room server firmware with a bluetooth connection between your smartphone and your LoRa radio using the nRF app.
@@ -313,7 +541,8 @@ You can update repeater and room server firmware with a bluetooth connection bet
2. On the phone client, log on to the repeater as administrator (default password is `password`) to issue the `start ota`command to the repeater or room server to get the device into OTA DFU mode
![image](https://github.com/user-attachments/assets/889bb81b-7214-4a1c-955a-396b5a05d8ad)
1. `start ota` can be initiated from USB serial console on the web flasher page or a T-Deck
1. `start ota` can be initiated from USB serial console on the web flasher page or a T-Deck
4. On the smartphone, download and run the nRF app and scan for Bluetooth devices
5. Connect to the repeater/room server node you want to update
1. nRF app is available on both Android and iOS
@@ -322,7 +551,8 @@ You can update repeater and room server firmware with a bluetooth connection bet
**iOS continues here:**
5. Once connected successfully, a `DFU` icon ![Pasted image 20250309173039](https://github.com/user-attachments/assets/af7a9f78-8739-4946-b734-02bade9c8e71)
appears in the top right corner of the app![Pasted image 20250309171919](https://github.com/user-attachments/assets/08007ec8-4924-49c1-989f-ca2611e78793)
appears in the top right corner of the app
![Pasted image 20250309171919](https://github.com/user-attachments/assets/08007ec8-4924-49c1-989f-ca2611e78793)
6. Scroll down to change the `PRN(s)` number:

View File

@@ -7,15 +7,13 @@
#include <SPIFFS.h>
#endif
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/ArduinoHelpers.h>
#include <helpers/StaticPoolPacketManager.h>
#include <helpers/SimpleMeshTables.h>
#include <helpers/IdentityStore.h>
#include <helpers/BaseSerialInterface.h>
#include <RTClib.h>
#include <target.h>
/* ---------------------------------- CONFIGURATION ------------------------------------- */
@@ -59,59 +57,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;
#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;
#elif defined(LILYGO_TBEAM)
#include <helpers/TBeamBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
static TBeamBoard board;
#elif defined(FAKETEC)
#include <helpers/nrf52/faketecBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomLLCC68Wrapper.h>
static faketecBoard board;
#else
#error "need to provide a 'board' object"
#endif
#ifdef DISPLAY_CLASS
#include "UITask.h"
#include <helpers/ui/SSD1306Display.h>
@@ -141,11 +86,11 @@ static uint32_t _atoi(const char* sp) {
#define FIRMWARE_VER_CODE 3
#ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "19 Mar 2025"
#define FIRMWARE_BUILD_DATE "7 Apr 2025"
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.4.0"
#define FIRMWARE_VERSION "v1.4.3"
#endif
#define CMD_APP_START 1
@@ -249,7 +194,6 @@ struct NodePrefs { // persisted to file
class MyMesh : public BaseChatMesh {
FILESYSTEM* _fs;
RADIO_CLASS* _phy;
IdentityStore* _identity_store;
NodePrefs _prefs;
uint32_t pending_login;
@@ -281,9 +225,13 @@ class MyMesh : public BaseChatMesh {
AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table
int next_ack_idx;
void loadMainIdentity(mesh::RNG& trng) {
void loadMainIdentity() {
if (!_identity_store->load("_main", self_id)) {
self_id = mesh::LocalIdentity(&trng); // create new random identity
self_id = radio_new_identity(); // create new random identity
int count = 0;
while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes
self_id = radio_new_identity(); count++;
}
saveMainIdentity(self_id);
}
}
@@ -540,7 +488,7 @@ protected:
}
void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override {
if (_serial->isConnected()) {
if (_serial->isConnected() && len+3 <= MAX_FRAME_SIZE) {
int i = 0;
out_frame[i++] = PUSH_CODE_LOG_RX_DATA;
out_frame[i++] = (int8_t)(snr * 4);
@@ -739,7 +687,7 @@ protected:
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)(_radio->getLastSNR() * 4); // extra/final SNR (to this node)
out_frame[i++] = (int8_t)(packet->getSNR() * 4); // extra/final SNR (to this node)
if (_serial->isConnected()) {
_serial->writeFrame(out_frame, i);
@@ -761,8 +709,8 @@ protected:
public:
MyMesh(RADIO_CLASS& phy, RadioLibWrapper& rw, mesh::RNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables)
: BaseChatMesh(rw, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), _serial(NULL), _phy(&phy)
MyMesh(mesh::Radio& radio, mesh::RNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables)
: BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), _serial(NULL)
{
_iter_started = false;
offline_queue_len = 0;
@@ -784,7 +732,42 @@ public:
//_prefs.rx_delay_base = 10.0f; enable once new algo fixed
}
void begin(FILESYSTEM& fs, mesh::RNG& trng, bool has_display) {
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, bool has_display) {
_fs = &fs;
BaseChatMesh::begin();
@@ -795,49 +778,23 @@ public:
_identity_store = new IdentityStore(fs, "/identity");
#endif
loadMainIdentity(trng);
loadMainIdentity();
// 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 HAS_UI
if (has_display) {
_active_ble_pin = trng.nextInt(100000, 999999); // random pin each session
StdRNG rng;
_active_ble_pin = rng.nextInt(100000, 999999); // random pin each session
} else {
_active_ble_pin = BLE_PIN_CODE; // otherwise static pin
}
@@ -858,11 +815,8 @@ public:
addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
loadChannels();
_phy->setFrequency(_prefs.freq);
_phy->setSpreadingFactor(_prefs.sf);
_phy->setBandwidth(_prefs.bw);
_phy->setCodingRate(_prefs.cr);
_phy->setOutputPower(_prefs.tx_power_dbm);
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);
}
const char* getNodeName() { return _prefs.node_name; }
@@ -875,10 +829,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];
@@ -959,7 +913,7 @@ 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;
@@ -1197,10 +1151,7 @@ public:
_prefs.bw = (float)bw / 1000.0;
savePrefs();
_phy->setFrequency(_prefs.freq);
_phy->setSpreadingFactor(_prefs.sf);
_phy->setBandwidth(_prefs.bw);
_phy->setCodingRate(_prefs.cr);
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr);
writeOKFrame();
@@ -1214,7 +1165,7 @@ public:
} else {
_prefs.tx_power_dbm = cmd_frame[1];
savePrefs();
_phy->setOutputPower(_prefs.tx_power_dbm);
radio_set_tx_power(_prefs.tx_power_dbm);
writeOKFrame();
}
} else if (cmd_frame[0] == CMD_SET_TUNING_PARAMS) {
@@ -1457,6 +1408,10 @@ public:
#elif defined(BLE_PIN_CODE)
#include <helpers/esp32/SerialBLEInterface.h>
SerialBLEInterface serial_interface;
#elif defined(SERIAL_RX)
#include <helpers/ArduinoSerialInterface.h>
ArduinoSerialInterface serial_interface;
HardwareSerial companion_serial(1);
#else
#include <helpers/ArduinoSerialInterface.h>
ArduinoSerialInterface serial_interface;
@@ -1473,23 +1428,9 @@ 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(LILYGO_TBEAM)
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);
MyMesh the_mesh(radio_driver, fast_rng, *new VolatileRTCClock(), tables); // TODO: test with 'rtc_clock' in target.cpp
void halt() {
while (1) ;
@@ -1499,60 +1440,26 @@ 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 defined(FAKETEC)
if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) {
#define SX126X_DIO3_TCXO_VOLTAGE (0.0f);
tcxo = SX126X_DIO3_TCXO_VOLTAGE;
status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
}
#endif
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
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
fast_rng.begin(radio.random(0x7FFFFFFF));
RadioNoiseListener trng(radio);
#ifdef HAS_UI
DisplayDriver* disp = NULL;
#ifdef DISPLAY_CLASS
if (display.begin()) {
disp = &display;
disp->startFrame();
disp->print("Please wait...");
disp->endFrame();
}
#endif
#endif
if (!radio_init()) { halt(); }
fast_rng.begin(radio_get_rng_seed());
#if defined(NRF52_PLATFORM)
InternalFS.begin();
the_mesh.begin(InternalFS, trng,
the_mesh.begin(InternalFS,
#ifdef HAS_UI
disp != NULL
#else
@@ -1570,7 +1477,7 @@ void setup() {
the_mesh.startInterface(serial_interface);
#elif defined(ESP32)
SPIFFS.begin(true);
the_mesh.begin(SPIFFS, trng,
the_mesh.begin(SPIFFS,
#ifdef HAS_UI
disp != NULL
#else
@@ -1585,6 +1492,10 @@ void setup() {
char dev_name[32+16];
sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName());
serial_interface.begin(dev_name, the_mesh.getBLEPin());
#elif defined(SERIAL_RX)
companion_serial.setPins(SERIAL_RX, SERIAL_TX);
companion_serial.begin(115200);
serial_interface.begin(companion_serial);
#else
serial_interface.begin(Serial);
#endif

View File

@@ -7,26 +7,24 @@
#include <SPIFFS.h>
#endif
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/ArduinoHelpers.h>
#include <helpers/StaticPoolPacketManager.h>
#include <helpers/SimpleMeshTables.h>
#include <helpers/IdentityStore.h>
#include <helpers/AutoDiscoverRTCClock.h>
#include <helpers/AdvertDataHelpers.h>
#include <helpers/TxtDataHelpers.h>
#include <helpers/CommonCLI.h>
#include <RTClib.h>
#include <target.h>
/* ------------------------------ Config -------------------------------- */
#ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "19 Mar 2025"
#define FIRMWARE_BUILD_DATE "7 Apr 2025"
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.4.0"
#define FIRMWARE_VERSION "v1.4.3"
#endif
#ifndef LORA_FREQ
@@ -59,52 +57,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;
#elif defined(FAKETEC)
#include <helpers/nrf52/faketecBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomLLCC68Wrapper.h>
static faketecBoard board;
#else
#error "need to provide a 'board' object"
#endif
#ifdef DISPLAY_CLASS
#include <helpers/ui/SSD1306Display.h>
@@ -114,6 +66,8 @@
static UITask ui_task(display);
#endif
#define FIRMWARE_ROLE "repeater"
#define PACKET_LOG_FILE "/packet_log"
/* ------------------------------ Code -------------------------------- */
@@ -153,11 +107,8 @@ struct ClientInfo {
#define CLI_REPLY_DELAY_MILLIS 1500
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;
bool _logging;
NodePrefs _prefs;
CommonCLI _cli;
@@ -192,9 +143,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
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.last_rssi = (int16_t) radio_driver.getLastRSSI();
stats.n_packets_recv = radio_driver.getPacketsRecv();
stats.n_packets_sent = radio_driver.getPacketsSent();
stats.total_air_time_secs = getTotalAirTime() / 1000;
stats.total_up_time_secs = _ms->getMillis() / 1000;
stats.n_sent_flood = getNumSentFlood();
@@ -202,7 +153,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
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.last_snr = (int16_t)(radio_driver.getLastSNR() * 4);
stats.n_direct_dups = ((SimpleMeshTables *)getTables())->getNumDirectDups();
stats.n_flood_dups = ((SimpleMeshTables *)getTables())->getNumFloodDups();
@@ -519,13 +470,12 @@ protected:
}
public:
MyMesh(RADIO_CLASS& phy, mesh::MainBoard& board, RadioLibWrapper& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables)
MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables)
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
_phy(&phy), _board(&board), _cli(board, this, &_prefs, this)
_cli(board, this, &_prefs, this)
{
my_radio = &radio;
memset(known_clients, 0, sizeof(known_clients));
next_local_advert = 0;
next_local_advert = next_flood_advert = 0;
_logging = false;
// defaults
@@ -543,6 +493,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;
}
@@ -554,17 +505,16 @@ public:
// load persisted prefs
_cli.loadPrefs(_fs);
_phy->setFrequency(_prefs.freq);
_phy->setSpreadingFactor(_prefs.sf);
_phy->setBandwidth(_prefs.bw);
_phy->setCodingRate(_prefs.cr);
_phy->setOutputPower(_prefs.tx_power_dbm);
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);
updateAdvertTimer();
updateFloodAdvertTimer();
}
const char* getFirmwareVer() override { return FIRMWARE_VERSION; }
const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; }
const char* getRole() override { return FIRMWARE_ROLE; }
const char* getNodeName() { return _prefs.node_name; }
void savePrefs() override {
@@ -593,11 +543,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; }
@@ -618,17 +575,21 @@ public:
}
void setTxPower(uint8_t power_dbm) override {
_phy->setOutputPower(power_dbm);
radio_set_tx_power(power_dbm);
}
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
}
@@ -638,28 +599,10 @@ 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;
#ifdef ESP32
ESP32RTCClock fallback_clock;
#else
VolatileRTCClock fallback_clock;
#endif
AutoDiscoverRTCClock rtc_clock(fallback_clock);
MyMesh the_mesh(radio, board, *new WRAPPER_CLASS(radio, board), *new ArduinoMillis(), fast_rng, rtc_clock, tables);
MyMesh the_mesh(board, radio_driver, *new ArduinoMillis(), fast_rng, rtc_clock, tables);
void halt() {
while (1) ;
@@ -672,51 +615,18 @@ void setup() {
delay(1000);
board.begin();
#ifdef ESP32
fallback_clock.begin();
#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 defined(FAKETEC)
if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) {
#define SX126X_DIO3_TCXO_VOLTAGE (0.0f);
tcxo = SX126X_DIO3_TCXO_VOLTAGE;
status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
#ifdef DISPLAY_CLASS
if(display.begin()){
display.startFrame();
display.print("Please wait...");
display.endFrame();
}
#endif
if (status != RADIOLIB_ERR_NONE) {
delay(5000);
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
halt();
}
radio.setCRC(1);
if (!radio_init()) { halt(); }
#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
fast_rng.begin(radio.random(0x7FFFFFFF));
fast_rng.begin(radio_get_rng_seed());
FILESYSTEM* fs;
#if defined(NRF52_PLATFORM)
@@ -732,8 +642,11 @@ void setup() {
#endif
if (!store.load("_main", the_mesh.self_id)) {
MESH_DEBUG_PRINTLN("Generating new keypair");
RadioNoiseListener rng(radio);
the_mesh.self_id = mesh::LocalIdentity(&rng); // create new random identity
the_mesh.self_id = radio_new_identity(); // create new random identity
int count = 0;
while (count < 10 && (the_mesh.self_id.pub_key[0] == 0x00 || the_mesh.self_id.pub_key[0] == 0xFF)) { // reserved id hashes
the_mesh.self_id = radio_new_identity(); count++;
}
store.save("_main", the_mesh.self_id);
}
@@ -745,12 +658,11 @@ void setup() {
the_mesh.begin(fs);
#ifdef DISPLAY_CLASS
display.begin();
ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE);
#endif
// send out initial Advertisement to the mesh
the_mesh.sendSelfAdvertisement(2000);
the_mesh.sendSelfAdvertisement(16000);
}
void loop() {

View File

@@ -7,26 +7,24 @@
#include <SPIFFS.h>
#endif
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/ArduinoHelpers.h>
#include <helpers/StaticPoolPacketManager.h>
#include <helpers/SimpleMeshTables.h>
#include <helpers/IdentityStore.h>
#include <helpers/AutoDiscoverRTCClock.h>
#include <helpers/AdvertDataHelpers.h>
#include <helpers/TxtDataHelpers.h>
#include <helpers/CommonCLI.h>
#include <RTClib.h>
#include <target.h>
/* ------------------------------ Config -------------------------------- */
#ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "19 Mar 2025"
#define FIRMWARE_BUILD_DATE "7 Apr 2025"
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.4.0"
#define FIRMWARE_VERSION "v1.4.3"
#endif
#ifndef LORA_FREQ
@@ -64,53 +62,7 @@
#endif
#ifndef MAX_UNSYNCED_POSTS
#define MAX_UNSYNCED_POSTS 16
#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;
#elif defined(FAKETEC)
#include <helpers/nrf52/faketecBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomLLCC68Wrapper.h>
static faketecBoard board;
#else
#error "need to provide a 'board' object"
#define MAX_UNSYNCED_POSTS 32
#endif
#ifdef DISPLAY_CLASS
@@ -122,8 +74,18 @@
static UITask ui_task(display);
#endif
#define FIRMWARE_ROLE "room_server"
#define PACKET_LOG_FILE "/packet_log"
/* ------------------------------ Code -------------------------------- */
enum RoomPermission {
ADMIN,
GUEST,
READ_ONLY
};
struct ClientInfo {
mesh::Identity id;
uint32_t last_timestamp; // by THEIR clock
@@ -132,7 +94,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;
@@ -149,7 +111,7 @@ struct PostInfo {
#define REPLY_DELAY_MILLIS 1500
#define PUSH_NOTIFY_DELAY_MILLIS 2000
#define SYNC_PUSH_INTERVAL 2000
#define SYNC_PUSH_INTERVAL 1200
#define PUSH_ACK_TIMEOUT_FLOOD 12000
#define PUSH_TIMEOUT_BASE 4000
@@ -180,11 +142,9 @@ struct ServerStats {
};
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;
bool _logging;
NodePrefs _prefs;
CommonCLI _cli;
uint8_t reply_data[MAX_PACKET_PAYLOAD];
@@ -296,6 +256,14 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
return createAdvert(self_id, app_data, app_data_len);
}
File openAppend(const char* fname) {
#if defined(NRF52_PLATFORM)
return _fs->open(fname, FILE_O_WRITE);
#else
return _fs->open(fname, "a", true);
#endif
}
protected:
float getAirtimeBudgetFactor() const override {
return _prefs.airtime_factor;
@@ -310,6 +278,55 @@ protected:
#endif
}
void logRx(mesh::Packet* pkt, int len, float score) override {
if (_logging) {
File f = openAppend(PACKET_LOG_FILE);
if (f) {
f.print(getLogDateTime());
f.printf(": RX, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d",
len, pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
(int)_radio->getLastSNR(), (int)_radio->getLastRSSI(), (int)(score*1000));
if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH || pkt->getPayloadType() == PAYLOAD_TYPE_REQ
|| pkt->getPayloadType() == PAYLOAD_TYPE_RESPONSE || pkt->getPayloadType() == PAYLOAD_TYPE_TXT_MSG) {
f.printf(" [%02X -> %02X]\n", (uint32_t)pkt->payload[1], (uint32_t)pkt->payload[0]);
} else {
f.printf("\n");
}
f.close();
}
}
}
void logTx(mesh::Packet* pkt, int len) override {
if (_logging) {
File f = openAppend(PACKET_LOG_FILE);
if (f) {
f.print(getLogDateTime());
f.printf(": TX, len=%d (type=%d, route=%s, payload_len=%d)",
len, pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);
if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH || pkt->getPayloadType() == PAYLOAD_TYPE_REQ
|| pkt->getPayloadType() == PAYLOAD_TYPE_RESPONSE || pkt->getPayloadType() == PAYLOAD_TYPE_TXT_MSG) {
f.printf(" [%02X -> %02X]\n", (uint32_t)pkt->payload[1], (uint32_t)pkt->payload[0]);
} else {
f.printf("\n");
}
f.close();
}
}
}
void logTxFail(mesh::Packet* pkt, int len) override {
if (_logging) {
File f = openAppend(PACKET_LOG_FILE);
if (f) {
f.print(getLogDateTime());
f.printf(": TX FAIL!, len=%d (type=%d, route=%s, payload_len=%d)\n",
len, pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);
f.close();
}
}
}
int calcRxDelay(float score, uint32_t air_time) const override {
if (_prefs.rx_delay_base <= 0.0f) return 0;
return (int) ((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time);
@@ -344,13 +361,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
}
@@ -363,7 +383,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;
@@ -377,7 +397,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
@@ -454,7 +474,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 {
@@ -467,11 +487,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;
@@ -555,9 +580,9 @@ protected:
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.last_rssi = (int16_t) radio_driver.getLastRSSI();
stats.n_packets_recv = radio_driver.getPacketsRecv();
stats.n_packets_sent = radio_driver.getPacketsSent();
stats.total_air_time_secs = getTotalAirTime() / 1000;
stats.total_up_time_secs = _ms->getMillis() / 1000;
stats.n_sent_flood = getNumSentFlood();
@@ -565,7 +590,7 @@ protected:
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.last_snr = (int16_t)(radio_driver.getLastSNR() * 4);
stats.n_direct_dups = ((SimpleMeshTables *)getTables())->getNumDirectDups();
stats.n_flood_dups = ((SimpleMeshTables *)getTables())->getNumFloodDups();
stats.n_posted = _num_posted;
@@ -575,7 +600,7 @@ protected:
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,
@@ -624,12 +649,12 @@ protected:
}
public:
MyMesh(RADIO_CLASS& phy, mesh::MainBoard& board, RadioLibWrapper& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables)
MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables)
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
_phy(&phy), _board(&board), _cli(board, this, &_prefs, this)
_cli(board, this, &_prefs, this)
{
my_radio = &radio;
next_local_advert = 0;
next_local_advert = next_flood_advert = 0;
_logging = false;
// defaults
memset(&_prefs, 0, sizeof(_prefs));
@@ -647,6 +672,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));
@@ -668,17 +694,16 @@ public:
// load persisted prefs
_cli.loadPrefs(_fs);
_phy->setFrequency(_prefs.freq);
_phy->setSpreadingFactor(_prefs.sf);
_phy->setBandwidth(_prefs.bw);
_phy->setCodingRate(_prefs.cr);
_phy->setOutputPower(_prefs.tx_power_dbm);
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);
updateAdvertTimer();
updateFloodAdvertTimer();
}
const char* getFirmwareVer() override { return FIRMWARE_VERSION; }
const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; }
const char* getRole() override { return FIRMWARE_ROLE; }
const char* getNodeName() { return _prefs.node_name; }
void savePrefs() override {
@@ -712,13 +737,34 @@ 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 */ }
void dumpLogFile() override { /* no-op */ }
void setLoggingOn(bool enable) override { _logging = enable; }
void eraseLogFile() override {
_fs->remove(PACKET_LOG_FILE);
}
void dumpLogFile() override {
File f = _fs->open(PACKET_LOG_FILE);
if (f) {
while (f.available()) {
int c = f.read();
if (c < 0) break;
Serial.print((char)c);
}
f.close();
}
}
void setTxPower(uint8_t power_dbm) override {
_phy->setOutputPower(power_dbm);
radio_set_tx_power(power_dbm);
}
void loop() {
@@ -736,6 +782,7 @@ public:
}
// check next Round-Robin client, and sync next new post
auto client = &known_clients[next_client_idx];
bool did_push = false;
if (client->pending_ack == 0 && client->last_activity != 0 && client->push_failures < 3) { // not already waiting for ACK, AND not evicted, AND retries not max
MESH_DEBUG_PRINTLN("loop - checking for client %02X", (uint32_t) client->id.pub_key[0]);
for (int k = 0, idx = next_post_idx; k < MAX_UNSYNCED_POSTS; k++) {
@@ -743,6 +790,7 @@ public:
&& !posts[idx].author.matches(client->id)) { // don't push posts to the author
// push this post to Client, then wait for ACK
pushPostToClient(client, posts[idx]);
did_push = true;
MESH_DEBUG_PRINTLN("loop - pushed to client %02X: %s", (uint32_t) client->id.pub_key[0], posts[idx].text);
break;
}
@@ -753,14 +801,23 @@ public:
}
next_client_idx = (next_client_idx + 1) % num_clients; // round robin polling for each client
next_push = futureMillis(SYNC_PUSH_INTERVAL);
if (did_push) {
next_push = futureMillis(SYNC_PUSH_INTERVAL);
} else {
// were no unsynced posts for curr client, so proccess next client much quicker! (in next loop())
next_push = futureMillis(SYNC_PUSH_INTERVAL / 8);
}
}
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
}
@@ -773,28 +830,9 @@ 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;
#ifdef ESP32
ESP32RTCClock fallback_clock;
#else
VolatileRTCClock fallback_clock;
#endif
AutoDiscoverRTCClock rtc_clock(fallback_clock);
MyMesh the_mesh(radio, board, *new WRAPPER_CLASS(radio, board), *new ArduinoMillis(), fast_rng, rtc_clock, tables);
MyMesh the_mesh(board, radio_driver, *new ArduinoMillis(), fast_rng, rtc_clock, tables);
void halt() {
while (1) ;
@@ -807,51 +845,18 @@ void setup() {
delay(1000);
board.begin();
#ifdef ESP32
fallback_clock.begin();
#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 defined(FAKETEC)
if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) {
#define SX126X_DIO3_TCXO_VOLTAGE (0.0f);
tcxo = SX126X_DIO3_TCXO_VOLTAGE;
status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
#ifdef DISPLAY_CLASS
if(display.begin()){
display.startFrame();
display.print("Please wait...");
display.endFrame();
}
#endif
if (status != RADIOLIB_ERR_NONE) {
delay(5000);
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
halt();
}
radio.setCRC(1);
if (!radio_init()) { halt(); }
#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
fast_rng.begin(radio.random(0x7FFFFFFF));
fast_rng.begin(radio_get_rng_seed());
FILESYSTEM* fs;
#if defined(NRF52_PLATFORM)
@@ -866,8 +871,11 @@ void setup() {
#error "need to define filesystem"
#endif
if (!store.load("_main", the_mesh.self_id)) {
RadioNoiseListener rng(radio);
the_mesh.self_id = mesh::LocalIdentity(&rng); // create new random identity
the_mesh.self_id = radio_new_identity(); // create new random identity
int count = 0;
while (count < 10 && (the_mesh.self_id.pub_key[0] == 0x00 || the_mesh.self_id.pub_key[0] == 0xFF)) { // reserved id hashes
the_mesh.self_id = radio_new_identity(); count++;
}
store.save("_main", the_mesh.self_id);
}
@@ -879,12 +887,11 @@ void setup() {
the_mesh.begin(fs);
#ifdef DISPLAY_CLASS
display.begin();
ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE);
#endif
// send out initial Advertisement to the mesh
the_mesh.sendSelfAdvertisement(2000);
the_mesh.sendSelfAdvertisement(16000);
}
void loop() {

View File

@@ -7,14 +7,12 @@
#include <SPIFFS.h>
#endif
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/ArduinoHelpers.h>
#include <helpers/StaticPoolPacketManager.h>
#include <helpers/SimpleMeshTables.h>
#include <helpers/IdentityStore.h>
#include <RTClib.h>
#include <target.h>
/* ---------------------------------- CONFIGURATION ------------------------------------- */
@@ -49,44 +47,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;
#elif defined(FAKETEC)
#include <helpers/nrf52/faketecBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomLLCC68Wrapper.h>
static faketecBoard 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;
@@ -230,6 +190,10 @@ protected:
return 0; // disable rxdelay
}
bool allowPacketForward(const mesh::Packet* packet) override {
return true;
}
void onDiscoveredContact(ContactInfo& contact, bool is_new) override {
// TODO: if not in favs, prompt to add as fav(?)
@@ -299,8 +263,7 @@ protected:
}
public:
MyMesh(RadioLibWrapper& radio, mesh::RNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables)
MyMesh(mesh::Radio& radio, StdRNG& rng, mesh::RTCClock& rtc, SimpleMeshTables& tables)
: BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables)
{
// defaults
@@ -328,7 +291,19 @@ public:
IdentityStore store(fs, "/identity");
#endif
if (!store.load("_main", self_id, _prefs.node_name, sizeof(_prefs.node_name))) { // legacy: node_name was from identity file
// Need way to get some entropy to seed RNG
Serial.println("Press ENTER to generate key:");
char c = 0;
while (c != '\n') { // wait for ENTER to be pressed
if (Serial.available()) c = Serial.read();
}
((StdRNG *)getRNG())->begin(millis());
self_id = mesh::LocalIdentity(getRNG()); // create new random identity
int count = 0;
while (count < 10 && (self_id.pub_key[0] == 0x00 || self_id.pub_key[0] == 0xFF)) { // reserved id hashes
self_id = mesh::LocalIdentity(getRNG()); count++;
}
store.save("_main", self_id);
}
@@ -362,6 +337,8 @@ public:
Serial.println("===== MeshCore Chat Terminal =====");
Serial.println();
Serial.printf("WELCOME %s\n", _prefs.node_name);
mesh::Utils::printHex(Serial, self_id.pub_key, PUB_KEY_SIZE);
Serial.println();
Serial.println(" (enter 'help' for basic commands)");
Serial.println();
}
@@ -548,20 +525,9 @@ 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);
MyMesh the_mesh(radio_driver, fast_rng, *new VolatileRTCClock(), tables); // TODO: test with 'rtc_clock' in target.cpp
void halt() {
while (1) ;
@@ -571,36 +537,10 @@ 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();
}
if (!radio_init()) { 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
fast_rng.begin(radio.random(0x7FFFFFFF));
fast_rng.begin(radio_get_rng_seed());
#if defined(NRF52_PLATFORM)
InternalFS.begin();
@@ -612,12 +552,8 @@ void setup() {
#error "need to define filesystem"
#endif
if (LORA_FREQ != the_mesh.getFreqPref()) {
radio.setFrequency(the_mesh.getFreqPref());
}
if (LORA_TX_POWER != the_mesh.getTxPowerPref()) {
radio.setOutputPower(the_mesh.getTxPowerPref());
}
radio_set_params(the_mesh.getFreqPref(), LORA_BW, LORA_SF, LORA_CR);
radio_set_tx_power(the_mesh.getTxPowerPref());
the_mesh.showWelcome();

View File

@@ -23,9 +23,9 @@ lib_deps =
adafruit/RTClib @ ^2.1.3
melopero/Melopero RV3028 @ ^1.1.0
build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1
-D LORA_FREQ=867.5
-D LORA_FREQ=869.525
-D LORA_BW=250
-D LORA_SF=10
-D LORA_SF=11
build_src_filter =
+<*.cpp>
+<helpers/*.cpp>
@@ -41,6 +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}
[esp32_ota]
lib_deps =
me-no-dev/ESPAsyncWebServer @ ^3.6.0
file://arch/esp32/AsyncElegantOTA
; ----------------- NRF52 ---------------------
[nrf52_base]

View File

@@ -29,6 +29,9 @@ int Dispatcher::calcRxDelay(float score, uint32_t air_time) const {
uint32_t Dispatcher::getCADFailRetryDelay() const {
return 200;
}
uint32_t Dispatcher::getCADFailMaxDuration() const {
return 4000; // 4 seconds
}
void Dispatcher::loop() {
if (outbound) { // waiting for outbound send to be completed
@@ -185,9 +188,20 @@ void Dispatcher::checkSend() {
if (_mgr->getOutboundCount() == 0) return; // nothing waiting to send
if (!millisHasNowPassed(next_tx_time)) return; // still in 'radio silence' phase (from airtime budget setting)
if (_radio->isReceiving()) { // LBT - check if radio is currently mid-receive, or if channel activity
next_tx_time = futureMillis(getCADFailRetryDelay());
return;
if (cad_busy_start == 0) {
cad_busy_start = _ms->getMillis(); // record when CAD busy state started
}
if (_ms->getMillis() - cad_busy_start > getCADFailMaxDuration()) {
MESH_DEBUG_PRINTLN("%s Dispatcher::checkSend(): CAD busy max duration reached!", getLogDateTime());
// channel activity has gone on too long... (Radio might be in a bad state)
// force the pending transmit below...
} else {
next_tx_time = futureMillis(getCADFailRetryDelay());
return;
}
}
cad_busy_start = 0; // reset busy state
outbound = _mgr->getNextOutbound(_ms->getMillis());
if (outbound) {

View File

@@ -98,6 +98,7 @@ class Dispatcher {
Packet* outbound; // current outbound packet
unsigned long outbound_expiry, outbound_start, total_air_time;
unsigned long next_tx_time;
unsigned long cad_busy_start;
uint32_t n_sent_flood, n_sent_direct;
uint32_t n_recv_flood, n_recv_direct;
uint32_t n_full_events;
@@ -113,6 +114,7 @@ protected:
: _radio(&radio), _ms(&ms), _mgr(&mgr)
{
outbound = NULL; total_air_time = 0; next_tx_time = 0;
cad_busy_start = 0;
}
virtual DispatcherAction onRecvPacket(Packet* pkt) = 0;
@@ -127,6 +129,7 @@ protected:
virtual float getAirtimeBudgetFactor() const;
virtual int calcRxDelay(float score, uint32_t air_time) const;
virtual uint32_t getCADFailRetryDelay() const;
virtual uint32_t getCADFailMaxDuration() const;
public:
void begin();

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];

View File

@@ -42,7 +42,35 @@ public:
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

@@ -229,6 +229,7 @@ void BaseChatMesh::onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mes
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

@@ -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) {
@@ -186,6 +198,11 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
sprintf(reply, "> %d", (uint32_t) _prefs->tx_power_dbm);
} else if (memcmp(config, "freq", 4) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->freq));
} else if (memcmp(config, "public.key", 10) == 0) {
strcpy(reply, "> ");
mesh::Utils::toHex(&reply[2], _mesh->self_id.pub_key, PUB_KEY_SIZE);
} else if (memcmp(config, "role", 4) == 0) {
sprintf(reply, "> %s", _callbacks->getRole());
} else {
sprintf(reply, "??: %s", config);
}
@@ -195,6 +212,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;
@@ -31,9 +31,11 @@ public:
virtual void savePrefs() = 0;
virtual const char* getFirmwareVer() = 0;
virtual const char* getBuildDate() = 0;
virtual const char* getRole() = 0;
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,8 +54,10 @@ class CommonCLI {
void checkAdvertInterval();
void loadPrefsInt(FILESYSTEM* _fs, const char* filename);
public:
CommonCLI(mesh::MainBoard& board, mesh::Mesh* mesh, NodePrefs* prefs, CommonCLICallbacks* callbacks)
CommonCLI(mesh::MainBoard& board, mesh::Mesh* mesh, NodePrefs* prefs, CommonCLICallbacks* callbacks)
: _board(&board), _mesh(mesh), _prefs(prefs), _callbacks(callbacks) { }
void loadPrefs(FILESYSTEM* _fs);

View File

@@ -3,6 +3,7 @@
#include <RadioLib.h>
#define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received
#define SX126X_IRQ_PREAMBLE_DETECTED 0x04
class CustomLLCC68 : public LLCC68 {
public:
@@ -10,7 +11,7 @@ class CustomLLCC68 : public LLCC68 {
bool isReceiving() {
uint16_t irq = getIrqFlags();
bool hasPreamble = (irq & SX126X_IRQ_HEADER_VALID);
return hasPreamble;
bool detected = (irq & SX126X_IRQ_HEADER_VALID) || (irq & SX126X_IRQ_PREAMBLE_DETECTED);
return detected;
}
};

View File

@@ -11,7 +11,7 @@ class CustomLR1110 : public LR1110 {
bool isReceiving() {
uint16_t irq = getIrqStatus();
bool hasPreamble = ((irq & LR1110_IRQ_HEADER_VALID) && (irq & LR1110_IRQ_HAS_PREAMBLE));
return hasPreamble;
bool detected = ((irq & LR1110_IRQ_HEADER_VALID) || (irq & LR1110_IRQ_HAS_PREAMBLE));
return detected;
}
};

View File

@@ -3,6 +3,7 @@
#include <RadioLib.h>
#define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received
#define SX126X_IRQ_PREAMBLE_DETECTED 0x04
class CustomSX1262 : public SX1262 {
public:
@@ -10,7 +11,7 @@ class CustomSX1262 : public SX1262 {
bool isReceiving() {
uint16_t irq = getIrqFlags();
bool hasPreamble = (irq & SX126X_IRQ_HEADER_VALID);
return hasPreamble;
bool detected = (irq & SX126X_IRQ_HEADER_VALID) || (irq & SX126X_IRQ_PREAMBLE_DETECTED);
return detected;
}
};

View File

@@ -3,6 +3,7 @@
#include <RadioLib.h>
#define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received
#define SX126X_IRQ_PREAMBLE_DETECTED 0x04
class CustomSX1268 : public SX1268 {
public:
@@ -10,7 +11,7 @@ class CustomSX1268 : public SX1268 {
bool isReceiving() {
uint16_t irq = getIrqFlags();
bool hasPreamble = (irq & SX126X_IRQ_HEADER_VALID);
return hasPreamble;
bool detected = (irq & SX126X_IRQ_HEADER_VALID) || (irq & SX126X_IRQ_PREAMBLE_DETECTED);
return detected;
}
};

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:
@@ -33,7 +34,9 @@ public:
#endif
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
#if PIN_BOARD_SDA >= 0 && PIN_BOARD_SCL >= 0
Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL);
#endif
#else
Wire.begin();
#endif
@@ -73,6 +76,8 @@ public:
void reboot() override {
esp_restart();
}
bool startOTAUpdate(const char* id, char reply[]) override;
};
class ESP32RTCClock : public mesh::RTCClock {

View File

@@ -37,6 +37,8 @@ public:
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();

View File

@@ -0,0 +1,103 @@
#include "ESPNOWRadio.h"
#include <esp_now.h>
#include <WiFi.h>
#include <esp_wifi.h>
static uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
static esp_now_peer_info_t peerInfo;
static bool is_send_complete = false;
static esp_err_t last_send_result;
static uint8_t rx_buf[256];
static uint8_t last_rx_len = 0;
// callback when data is sent
static void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
is_send_complete = true;
ESPNOW_DEBUG_PRINTLN("Send Status: %d", (int)status);
}
static void OnDataRecv(const uint8_t *mac, const uint8_t *data, int len) {
ESPNOW_DEBUG_PRINTLN("Recv: len = %d", len);
memcpy(rx_buf, data, len);
last_rx_len = len;
}
void ESPNOWRadio::begin() {
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Long Range mode
esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR);
// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
ESPNOW_DEBUG_PRINTLN("Error initializing ESP-NOW");
return;
}
esp_wifi_set_max_tx_power(80); // should be 20dBm
esp_now_register_send_cb(OnDataSent);
esp_now_register_recv_cb(OnDataRecv);
// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
// Add peer
if (esp_now_add_peer(&peerInfo) == ESP_OK) {
ESPNOW_DEBUG_PRINTLN("init success");
} else {
// ESPNOW_DEBUG_PRINTLN("Failed to add peer");
}
}
void ESPNOWRadio::setTxPower(uint8_t dbm) {
esp_wifi_set_max_tx_power(dbm * 4);
}
uint32_t ESPNOWRadio::intID() {
uint8_t mac[8];
memset(mac, 0, sizeof(mac));
esp_efuse_mac_get_default(mac);
uint32_t n, m;
memcpy(&n, &mac[0], 4);
memcpy(&m, &mac[4], 4);
return n + m;
}
void ESPNOWRadio::startSendRaw(const uint8_t* bytes, int len) {
// Send message via ESP-NOW
is_send_complete = false;
esp_err_t result = esp_now_send(broadcastAddress, bytes, len);
if (result == ESP_OK) {
ESPNOW_DEBUG_PRINTLN("Send success");
} else {
last_send_result = result;
is_send_complete = true;
ESPNOW_DEBUG_PRINTLN("Send failed: %d", result);
}
}
bool ESPNOWRadio::isSendComplete() {
return is_send_complete;
}
void ESPNOWRadio::onSendFinished() {
}
float ESPNOWRadio::getLastRSSI() const { return 0; }
float ESPNOWRadio::getLastSNR() const { return 0; }
int ESPNOWRadio::recvRaw(uint8_t* bytes, int sz) {
int len = last_rx_len;
if (last_rx_len > 0) {
memcpy(bytes, rx_buf, last_rx_len);
last_rx_len = 0;
}
return len;
}
uint32_t ESPNOWRadio::getEstAirtimeFor(int len_bytes) {
return 4; // Fast AF
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <Mesh.h>
class ESPNOWRadio : public mesh::Radio {
protected:
uint32_t n_recv, n_sent;
public:
ESPNOWRadio() { n_recv = n_sent = 0; }
void begin() override;
int recvRaw(uint8_t* bytes, int sz) override;
uint32_t getEstAirtimeFor(int len_bytes) override;
void startSendRaw(const uint8_t* bytes, int len) override;
bool isSendComplete() override;
void onSendFinished() override;
uint32_t getPacketsRecv() const { return n_recv; }
uint32_t getPacketsSent() const { return n_sent; }
virtual float getLastRSSI() const override;
virtual float getLastSNR() const override;
float packetScore(float snr, int packet_len) override { return 0; }
uint32_t intID();
void setTxPower(uint8_t dbm);
};
#if ESPNOW_DEBUG_LOGGING && ARDUINO
#include <Arduino.h>
#define ESPNOW_DEBUG_PRINT(F, ...) Serial.printf("ESP-Now: " F, ##__VA_ARGS__)
#define ESPNOW_DEBUG_PRINTLN(F, ...) Serial.printf("ESP-Now: " F "\n", ##__VA_ARGS__)
#else
#define ESPNOW_DEBUG_PRINT(...) {}
#define ESPNOW_DEBUG_PRINTLN(...) {}
#endif

View File

@@ -1,24 +1,45 @@
#include <Arduino.h>
#include "faketecBoard.h"
#include "PromicroBoard.h"
#include <bluefruit.h>
#include <Wire.h>
static BLEDfu bledfu;
void PromicroBoard::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");
(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");
(void)conn_handle;
(void)reason;
MESH_DEBUG_PRINTLN("BLE client disconnected");
}
bool faketecBoard::startOTAUpdate() {
bool PromicroBoard::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()
@@ -29,7 +50,7 @@ bool faketecBoard::startOTAUpdate() {
// 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.setName("ProMicro_OTA");
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
@@ -57,5 +78,6 @@ bool faketecBoard::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

@@ -2,7 +2,6 @@
#include <MeshCore.h>
#include <Arduino.h>
#include <Wire.h>
#define P_LORA_NSS 13 //P1.13 45
#define P_LORA_DIO_1 11 //P0.10 10
@@ -20,27 +19,13 @@
#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 {
class PromicroBoard : public mesh::MainBoard {
protected:
uint8_t startup_reason;
uint8_t btn_prev_state;
public:
void begin() {
// for future use, sub-classes SHOULD call this from their begin()
startup_reason = BD_STARTUP_NORMAL;
pinMode(PIN_VBAT_READ, INPUT);
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
Wire.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
}
void begin();
uint8_t getStartupReason() const override { return startup_reason; }
@@ -58,12 +43,23 @@ public:
}
const char* getManufacturerName() const override {
return "Faketec DIY";
return "ProMicro 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() override;
bool startOTAUpdate(const char* id, char reply[]) override;
};

View File

@@ -37,7 +37,7 @@ void RAK4631Board::begin() {
delay(10); // give sx1262 some time to power up
}
bool RAK4631Board::startOTAUpdate() {
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()
@@ -76,5 +76,6 @@ bool RAK4631Board::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

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

View File

@@ -9,6 +9,8 @@ void T1000eBoard::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);
@@ -40,7 +42,7 @@ static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
}
bool TrackerT1000eBoard::startOTAUpdate() {
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()
@@ -79,6 +81,7 @@ bool TrackerT1000eBoard::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

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

View File

@@ -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,6 +1,8 @@
#include <Arduino.h>
#include "TechoBoard.h"
#ifdef LILYGO_TECHO
#include <bluefruit.h>
#include <Wire.h>
@@ -22,11 +24,18 @@ 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);
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL)
#endif
pinMode(PIN_TXCO, OUTPUT);
digitalWrite(PIN_TXCO, HIGH);
Wire.begin();
@@ -35,7 +44,21 @@ void TechoBoard::begin() {
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()
@@ -74,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

@@ -15,6 +15,7 @@ public:
virtual bool isOn() = 0;
virtual void turnOn() = 0;
virtual void turnOff() = 0;
virtual void clear() = 0;
virtual void startFrame(Color bkg = DARK) = 0;
virtual void setTextSize(int sz) = 0;
virtual void setColor(Color c) = 0;

View File

@@ -20,6 +20,11 @@ void SSD1306Display::turnOff() {
_isOn = false;
}
void SSD1306Display::clear() {
display.clearDisplay();
display.display();
}
void SSD1306Display::startFrame(Color bkg) {
display.clearDisplay(); // TODO: apply 'bkg'
_color = SSD1306_WHITE;

View File

@@ -27,6 +27,7 @@ public:
bool isOn() override { return _isOn; }
void turnOn() override;
void turnOff() override;
void clear() override;
void startFrame(Color bkg = DARK) override;
void setTextSize(int sz) override;
void setColor(Color c) override;

View File

@@ -0,0 +1,82 @@
; ----------- Generic ESP32-C3 ------------
[Generic_ESPNOW]
extends = esp32_base
board = esp32-c3-devkitm-1
;board = esp32-s3-devkitm-1
build_flags =
${esp32_base.build_flags}
-I variants/generic_espnow
; -D ESP32_CPU_FREQ=80
-D PIN_BOARD_SDA=-1
-D PIN_BOARD_SCL=-1
; -D P_LORA_TX_LED=8
-D PIN_USER_BTN=0
; -D ARDUINO_USB_MODE=1
; -D ARDUINO_USB_CDC_ON_BOOT=1
; -D ESPNOW_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${esp32_base.build_src_filter}
+<helpers/esp32/ESPNowRadio.cpp>
+<../variants/generic_espnow>
[env:Generic_ESPNOW_terminal_chat]
extends = Generic_ESPNOW
build_flags =
${Generic_ESPNOW.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
build_src_filter = ${Generic_ESPNOW.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${Generic_ESPNOW.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Generic_ESPNOW_repeatr]
extends = Generic_ESPNOW
build_flags =
${Generic_ESPNOW.build_flags}
-D ADVERT_NAME='"ESPNOW Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
build_src_filter = ${Generic_ESPNOW.build_src_filter}
+<../examples/simple_repeater/main.cpp>
lib_deps =
${Generic_ESPNOW.lib_deps}
${esp32_ota.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Generic_ESPNOW_comp_radio_usb]
extends = Generic_ESPNOW
build_flags =
${Generic_ESPNOW.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
; NOTE: DO NOT ENABLE --> -D ESPNOW_DEBUG_LOGGING=1
build_src_filter = ${Generic_ESPNOW.build_src_filter}
+<../examples/companion_radio/main.cpp>
lib_deps =
${Generic_ESPNOW.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Generic_ESPNOW_room_svr]
extends = Generic_ESPNOW
build_flags =
${Generic_ESPNOW.build_flags}
-D ADVERT_NAME='"Heltec Room"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
build_src_filter = ${Generic_ESPNOW.build_src_filter}
+<../examples/simple_room_server/main.cpp>
lib_deps =
${Generic_ESPNOW.lib_deps}
${esp32_ota.lib_deps}

View File

@@ -0,0 +1,33 @@
#include <Arduino.h>
#include "target.h"
#include <helpers/ArduinoHelpers.h>
ESP32Board board;
ESPNOWRadio radio_driver;
ESP32RTCClock rtc_clock;
bool radio_init() {
rtc_clock.begin();
// NOTE: radio_driver.begin() is called by Dispatcher::begin(), so not needed here
return true; // success
}
uint32_t radio_get_rng_seed() {
return millis() + radio_driver.intID(); // TODO: where to get some entropy?
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
// no-op
}
void radio_set_tx_power(uint8_t dbm) {
radio_driver.setTxPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
StdRNG rng; // TODO: need stronger True-RNG here
return mesh::LocalIdentity(&rng); // create new random identity
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <helpers/ESP32Board.h>
#include <helpers/esp32/ESPNOWRadio.h>
extern ESP32Board board;
extern ESPNOWRadio radio_driver;
extern ESP32RTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -3,6 +3,7 @@ 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
@@ -13,6 +14,7 @@ build_flags =
-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
@@ -23,14 +25,17 @@ 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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${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
@@ -38,8 +43,8 @@ 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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
@@ -47,6 +52,9 @@ build_flags =
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

View File

@@ -0,0 +1,59 @@
#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
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

View File

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/HeltecV2Board.h>
#include <helpers/CustomSX1276Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern HeltecV2Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -3,6 +3,7 @@ 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
@@ -16,6 +17,7 @@ build_flags =
-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
@@ -26,14 +28,17 @@ 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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${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
@@ -41,8 +46,8 @@ 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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
@@ -50,6 +55,9 @@ build_flags =
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
@@ -131,13 +139,16 @@ 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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${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
@@ -146,12 +157,15 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter}
build_flags =
${Heltec_lora32_v3.build_flags}
-D ADVERT_NAME='"Heltec Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${Heltec_lora32_v3.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_WSL3_companion_radio_ble]
extends = Heltec_lora32_v3

View File

@@ -0,0 +1,75 @@
#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
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

View File

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/HeltecV3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern HeltecV3Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -3,6 +3,7 @@ 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
@@ -26,6 +27,7 @@ build_flags =
-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
@@ -37,14 +39,17 @@ 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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${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
@@ -66,8 +71,8 @@ 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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
@@ -75,6 +80,9 @@ build_flags =
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

View File

@@ -0,0 +1,75 @@
#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
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

View File

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/ESP32Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern ESP32Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -3,19 +3,29 @@ 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
@@ -29,8 +39,26 @@ build_flags =
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TBeam.build_src_filter}
+<helpers/esp32/*.cpp>
+<../examples/companion_radio/main.cpp>
+<helpers/ui/*.cpp>
+<../examples/companion_radio>
lib_deps =
${LilyGo_TBeam.lib_deps}
densaugeo/base64 @ ~1.4.0
lewisxhe/XPowersLib@^0.2.7
[env:Tbeam_repeater]
extends = LilyGo_TBeam
build_flags =
${LilyGo_TBeam.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Tbeam Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${LilyGo_TBeam.build_src_filter}
+<helpers/ui/*.cpp>
+<../examples/simple_repeater>
lib_deps =
${LilyGo_TBeam.lib_deps}
${esp32_ota.lib_deps}

View File

@@ -0,0 +1,59 @@
#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_0, P_LORA_RESET, P_LORA_DIO_1, spi);
#else
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1);
#endif
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

View File

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/TBeamBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern TBeamBoard board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -6,6 +6,7 @@ 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
@@ -23,6 +24,8 @@ build_flags =
-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
@@ -36,12 +39,15 @@ build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
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 ADVERT_LAT=0.0
-D ADVERT_LON=0.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
@@ -103,9 +109,12 @@ build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter}
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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${LilyGo_TLora_V2_1_1_6.lib_deps}
${esp32_ota.lib_deps}

View File

@@ -0,0 +1,53 @@
#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);
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire);
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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

View File

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/LilyGoTLoraBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern LilyGoTLoraBoard board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -1,78 +1,89 @@
[faketec]
[Faketec]
extends = nrf52840_base
board = promicro_nrf52840
build_src_filter = ${nrf52840_base.build_src_filter} +<helpers/nrf52/faketecBoard.cpp>
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/PromicroBoard.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/main.cpp>
extends = Faketec
build_src_filter = ${Faketec.build_src_filter} +<../examples/simple_repeater> +<helpers/ui/*.cpp>
build_flags =
${faketec.build_flags}
${Faketec.build_flags}
-D ADVERT_NAME="\"Faketec Repeater\""
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD="\"password\""
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${faketec.lib_deps}
${Faketec.lib_deps}
adafruit/RTClib @ ^2.1.3
[env:Faketec_room_server]
extends = faketec
build_src_filter = ${faketec.build_src_filter} +<../examples/simple_room_server/main.cpp>
extends = Faketec
build_src_filter = ${Faketec.build_src_filter} +<../examples/simple_room_server> +<helpers/ui/*.cpp>
build_flags =
${faketec.build_flags}
${Faketec.build_flags}
-D ADVERT_NAME="\"Test Room\""
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD="\"password\""
-D ROOM_PASSWORD="\"hello\""
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${faketec.lib_deps}
${Faketec.lib_deps}
adafruit/RTClib @ ^2.1.3
[env:Faketec_terminal_chat]
extends = faketec
extends = Faketec
build_flags =
${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>
build_src_filter = ${Faketec.build_src_filter} +<../examples/simple_secure_chat/main.cpp>
lib_deps =
${faketec.lib_deps}
${Faketec.lib_deps}
densaugeo/base64 @ ~1.4.0
adafruit/RTClib @ ^2.1.3
[env:Faketec_companion_radio_usb]
extends = faketec
extends = Faketec
build_flags =
${faketec.build_flags}
${Faketec.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${faketec.build_src_filter} +<../examples/companion_radio/main.cpp>
build_src_filter = ${Faketec.build_src_filter} +<../examples/companion_radio> +<../examples/companion_radio> +<helpers/ui/*.cpp>
lib_deps =
${faketec.lib_deps}
${Faketec.lib_deps}
adafruit/RTClib @ ^2.1.3
densaugeo/base64 @ ~1.4.0
[env:Faketec_companion_radio_ble]
extends = faketec
extends = Faketec
build_flags =
${faketec.build_flags}
${Faketec.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D BLE_PIN_CODE=123456
@@ -81,23 +92,26 @@ build_flags =
-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/main.cpp>
build_src_filter = ${Faketec.build_src_filter} +<helpers/nrf52/SerialBLEInterface.cpp> +<../examples/companion_radio> +<helpers/ui/*.cpp>
lib_deps =
${faketec.lib_deps}
${Faketec.lib_deps}
adafruit/RTClib @ ^2.1.3
densaugeo/base64 @ ~1.4.0
[ProMicroLLCC68]
extends = nrf52840_base
board = promicro_nrf52840
build_src_filter = ${nrf52840_base.build_src_filter} +<helpers/nrf52/faketecBoard.cpp>
build_flags = ${nrf52840_base.build_flags}
-D FAKETEC
-D RADIO_CLASS=CustomLLCC68
-I variants/promicro
-D PROMICROLLCC68
-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/PromicroBoard.cpp>
+<../variants/promicro>
[env:ProMicroLLCC68_Repeater]
extends = ProMicroLLCC68

View File

@@ -0,0 +1,75 @@
#include <Arduino.h>
#include "target.h"
#include <helpers/ArduinoHelpers.h>
PromicroBoard board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
rtc_clock.begin(Wire);
#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();
radio.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

View File

@@ -0,0 +1,19 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/nrf52/PromicroBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomLLCC68Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern PromicroBoard board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -4,6 +4,7 @@ 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
@@ -13,6 +14,7 @@ build_flags = ${nrf52840_base.build_flags}
-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
@@ -23,8 +25,8 @@ 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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
@@ -38,8 +40,8 @@ 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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1

View File

@@ -0,0 +1,69 @@
#include <Arduino.h>
#include "target.h"
#include <helpers/ArduinoHelpers.h>
RAK4631Board board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

18
variants/rak4631/target.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/nrf52/RAK4631Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern RAK4631Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -3,6 +3,7 @@ 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
@@ -16,8 +17,8 @@ build_flags =
-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}
@@ -26,13 +27,16 @@ 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 ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${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
@@ -41,9 +45,12 @@ build_src_filter = ${Station_G2.build_src_filter}
build_flags =
${Station_G2.build_flags}
-D ADVERT_NAME='"Station G2 Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
lib_deps =
${Station_G2.lib_deps}
${esp32_ota.lib_deps}

View File

@@ -0,0 +1,75 @@
#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
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

View File

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/StationG2Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern StationG2Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -19,14 +19,14 @@ extends = nrf52840_t1000e
board = tracker-t1000-e
board_build.ldscript = boards/nrf52840_s140_v7.ld
build_flags = ${nrf52840_t1000e.build_flags}
-Ivariants/t1000-e
-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 MAX_LORA_TX_POWER=22
-D LORA_TX_POWER=22
build_src_filter = ${nrf52840_t1000e.build_src_filter}
+<helpers/*.cpp>
+<helpers/nrf52/T1000eBoard.cpp>
@@ -43,6 +43,8 @@ build_flags = ${t1000-e.build_flags}
-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>

View File

@@ -0,0 +1,86 @@
#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);
WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock rtc_clock;
#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() {
//rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

18
variants/t1000-e/target.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/nrf52/T1000eBoard.h>
#include <helpers/CustomLR1110Wrapper.h>
#include <helpers/ArduinoHelpers.h>
extern T1000eBoard board;
extern WRAPPER_CLASS radio_driver;
extern VolatileRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -14,7 +14,7 @@ extends = nrf52840_t114
board = heltec_t114
board_build.ldscript = boards/nrf52840_s140_v6.ld
build_flags = ${nrf52840_t114.build_flags}
-Ivariants/t114
-I variants/t114
-DHELTEC_T114
-D P_LORA_TX_LED=35
-D RADIO_CLASS=CustomSX1262
@@ -36,8 +36,8 @@ build_src_filter = ${Heltec_t114.build_src_filter}
build_flags =
${Heltec_t114.build_flags}
-D ADVERT_NAME='"Heltec_T114 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
@@ -49,8 +49,8 @@ build_src_filter = ${Heltec_t114.build_src_filter}
build_flags =
${Heltec_t114.build_flags}
-D ADVERT_NAME='"Heltec_T114 Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1

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

@@ -0,0 +1,69 @@
#include <Arduino.h>
#include "target.h"
#include <helpers/ArduinoHelpers.h>
T114Board board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

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

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/nrf52/T114Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern T114Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -14,7 +14,7 @@ extends = nrf52840_techo
board = t-echo
board_build.ldscript = boards/nrf52840_s140_v6.ld
build_flags = ${nrf52840_techo.build_flags}
-Ivariants/techo
-I variants/techo
-DLILYGO_TECHO
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
@@ -34,8 +34,8 @@ build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_repeate
build_flags =
${LilyGo_Techo.build_flags}
-D ADVERT_NAME='"T-Echo Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
@@ -46,8 +46,8 @@ build_src_filter = ${LilyGo_Techo.build_src_filter} +<../examples/simple_room_se
build_flags =
${LilyGo_Techo.build_flags}
-D ADVERT_NAME='"T-Echo Room"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1

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

@@ -0,0 +1,69 @@
#include <Arduino.h>
#include "target.h"
#include <helpers/ArduinoHelpers.h>
TechoBoard board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

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

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/nrf52/TechoBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern TechoBoard board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

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

@@ -3,6 +3,7 @@ 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
@@ -15,6 +16,8 @@ build_flags =
-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
@@ -27,11 +30,14 @@ build_flags =
-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 ADVERT_LAT=0.0
-D ADVERT_LON=0.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
@@ -43,8 +49,11 @@ build_flags =
-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 ADVERT_LAT=0.0
-D ADVERT_LON=0.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,75 @@
#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
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

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

@@ -0,0 +1,19 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/XiaoC3Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/CustomSX1268Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern XiaoC3Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -4,6 +4,7 @@ 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
@@ -19,6 +20,8 @@ build_flags = ${esp32_base.build_flags}
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/xiao_s3_wio>
[env:Xiao_S3_WIO_Repeater]
extends = Xiao_S3_WIO
@@ -27,11 +30,31 @@ build_src_filter = ${Xiao_S3_WIO.build_src_filter}
build_flags =
${Xiao_S3_WIO.build_flags}
-D ADVERT_NAME='"XiaoS3 Repeater"'
-D ADVERT_LAT=-37.0
-D ADVERT_LON=145.0
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.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=0.0
-D ADVERT_LON=0.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
@@ -65,3 +88,20 @@ build_src_filter = ${Xiao_S3_WIO.build_src_filter}
lib_deps =
${Xiao_S3_WIO.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Xiao_S3_WIO_companion_radio_serial]
extends = Xiao_S3_WIO
build_flags =
${Xiao_S3_WIO.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D SERIAL_TX=D6
-D SERIAL_RX=D7
; -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,75 @@
#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
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#ifndef LORA_CR
#define LORA_CR 5
#endif
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire);
#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
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

View File

@@ -0,0 +1,18 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/ESP32Board.h>
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
extern ESP32Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();