Compare commits

...

34 Commits

Author SHA1 Message Date
Matthias Wientapper
58decb74b8 Fix fetching same PR twice under wrong name 2026-01-26 11:09:03 +01:00
Matthias Wientapper
e6cab77670 Add scripts to help automate the fw build process 2026-01-21 09:54:56 +01:00
ripplebiz
56ab59ded2 Merge pull request #1387 from chrisdavis2110/rak3401
Add Variant rak3401 (for new 1W booster kit)
2026-01-19 16:11:07 +11:00
ripplebiz
bf0777845a Merge pull request #1408 from oltaco/improved-contact-mgmt
Contact management tweaks
2026-01-19 12:29:52 +11:00
chrisdavis2110
ed5d2909fc updated variant rak3401 2026-01-17 22:54:20 -08:00
Chris Davis
5e4b33a1a0 Merge pull request #4 from chrisdavis2110/var-rak3401
Var rak3401
2026-01-17 22:47:25 -08:00
taco
b919119faf only write contacts when changed 2026-01-16 13:15:35 +11:00
taco
c61fde9328 always send PUSH_CODE_NEW_ADVERT when advert was not added to contacts[] 2026-01-16 13:15:35 +11:00
Liam Cottle
7d1f52252b Merge pull request #1402 from recrof/v3-usb-contact-fix
fix: bump max contacts for heltec v3 companion usb
2026-01-16 09:50:06 +13:00
Rastislav Vysoky
11565673c3 fix: bump max contacts for v3 companion usb 2026-01-15 15:39:44 +01:00
Liam Cottle
23f1f2a3fa Merge pull request #1399 from mannkind/patch-1
Fix Ikoka Stick builds
2026-01-15 23:32:34 +13:00
ripplebiz
d41a968d1d Merge pull request #1379 from oltaco/improved-contact-mgmt
Companion: Improved Contact Management
2026-01-15 20:36:28 +11:00
taco
df6687034a bootstrap RTC from contact.lastmod and improve slot overwrite logic
slot overwrite logic can now safely use contact.lastmod to find oldest contact for overwrite
2026-01-15 18:01:20 +11:00
taco
741564dd48 refactor: add populateContactFromAdvert() 2026-01-15 18:01:20 +11:00
taco
403ce1db08 contacts: granular autoadd and overwrite-oldest 2026-01-15 18:01:20 +11:00
Dustin Brewer
31f98bdd43 Fix Ikoka Stick builds 2026-01-14 17:53:42 -08:00
Liam Cottle
56eb5b0499 Merge pull request #1373 from liquidraver/buildwithoutdebug
DISABLE_DEBUG=1 env variable to build.sh
2026-01-15 06:36:00 +13:00
chrisdavis2110
06c4ca19ab added variant rak3401 2026-01-13 10:06:50 -08:00
liquidraver
a48b185189 DISABLE_DEBUG=1 env variable to build.sh 2026-01-13 12:48:53 +01:00
Liam Cottle
4643f4d3a3 Merge pull request #1378 from recrof/ikoka-cleanup
cleanup ikoka variants and add all supported sensors
2026-01-13 17:21:24 +13:00
Liam Cottle
77257a376b Merge pull request #1377 from recrof/t3s3-sx1276-fix
FIX: remove serial debug logging from t3s3 sx1276 companion usb
2026-01-13 10:34:45 +13:00
Rastislav Vysoky
324eab9394 cleanup ikoka variants and add all supported sensors 2026-01-12 19:29:32 +01:00
Rastislav Vysoky
266e4893fd remove serial debug logging from t3s3 sx1276 companion usb 2026-01-12 19:19:23 +01:00
Scott Powell
bafbfaf2b5 Merge branch 'regions-request' into dev 2026-01-12 17:48:19 +11:00
Scott Powell
69a71d0e25 * repeater login response, FIRMWARE_VER_LEVEL now bumped to 2 2026-01-12 17:47:51 +11:00
Scott Powell
b6110eee38 * new req/resp (after login): REQ_TYPE_GET_OWNER_INFO (includes firmware-ver)
* ANON_REQ_TYPE_OWNER, firmware-ver removed (security exploit)
* ANON_REQ_TYPE_BASIC, formware-ver removed, just remote clock + some 'feature' bits
* CTL_TYPE_NODE_DISCOVER_REQ now ingored if 'repeat off' has been set
2026-01-12 16:58:35 +11:00
Scott Powell
4e4f6d92a0 * ANON_REQ_TYPE_VER_OWNER now delimited by newline chars 2026-01-09 16:32:08 +11:00
Scott Powell
65796c8f20 * CommonCLI: added "set name ..." validation
* ANON_REQ_TYPE_VER_OWNER, now removes commas from node_name
2026-01-09 16:28:08 +11:00
Scott Powell
fd69acb421 * new ANON_REQ_TYPE_VER (for just simple clock + ver info) 2026-01-09 13:54:18 +11:00
Scott Powell
2a035ad816 * ANON_REQ_TYPE_VER_OWNER, now includes node_name 2026-01-09 13:20:20 +11:00
Scott Powell
5475043083 * new ANON_REQ_TYPE_VER_OWNER
* CommonCLI: new "get/set owner.info ..."
2026-01-09 11:07:31 +11:00
Scott Powell
5cc44dd802 * ANON_REQ_TYPE_REGIONS now direct only, with reply_path encoded in request 2026-01-08 13:20:52 +11:00
Scott Powell
8d51126956 Merge branch 'dev' into regions-request 2026-01-08 00:21:08 +11:00
Scott Powell
3af25495bb * Repeater: new anon request sub-type: ANON_REQ_TYPE_REGIONS (rate limited to max 4 every 3 mins)
* Companion: new CMD_SEND_ANON_REQ command (reply with existing RESP_CODE_SENT frame)
2026-01-03 12:02:15 +11:00
30 changed files with 1265 additions and 213 deletions

72
boards/rak3401.json Normal file
View File

@@ -0,0 +1,72 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
[
"0x239A",
"0x8029"
],
[
"0x239A",
"0x0029"
],
[
"0x239A",
"0x002A"
],
[
"0x239A",
"0x802A"
]
],
"usb_product": "WisCore RAK3401 Board",
"mcu": "nrf52840",
"variant": "WisCore_RAK3401_Board",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": [
"bluetooth"
],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd"
},
"frameworks": [
"arduino"
],
"name": "WisCore RAK3401 Board",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://www.rakwireless.com",
"vendor": "RAKwireless"
}

View File

@@ -29,6 +29,20 @@ $ sh build.sh build-repeater-firmwares
Build all chat room server firmwares Build all chat room server firmwares
$ sh build.sh build-room-server-firmwares $ sh build.sh build-room-server-firmwares
Environment Variables:
DISABLE_DEBUG=1: Disables all debug logging flags (MESH_DEBUG, MESH_PACKET_LOGGING, etc.)
If not set, debug flags from variant platformio.ini files are used.
Examples:
Build without debug logging:
$ export FIRMWARE_VERSION=v1.0.0
$ export DISABLE_DEBUG=1
$ sh build.sh build-firmware RAK_4631_repeater
Build with debug logging (default, uses flags from variant files):
$ export FIRMWARE_VERSION=v1.0.0
$ sh build.sh build-firmware RAK_4631_repeater
EOF EOF
} }
@@ -68,6 +82,13 @@ get_pio_envs_ending_with_string() {
done done
} }
# disable all debug logging flags if DISABLE_DEBUG=1 is set
disable_debug_flags() {
if [ "$DISABLE_DEBUG" == "1" ]; then
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_DEBUG -UBLE_DEBUG_LOGGING -UWIFI_DEBUG_LOGGING -UBRIDGE_DEBUG -UGPS_NMEA_DEBUG -UCORE_DEBUG_LEVEL -UESPNOW_DEBUG_LOGGING -UDEBUG_RP2040_WIRE -UDEBUG_RP2040_SPI -UDEBUG_RP2040_CORE -UDEBUG_RP2040_PORT -URADIOLIB_DEBUG_SPI -UCFG_DEBUG -URADIOLIB_DEBUG_BASIC -URADIOLIB_DEBUG_PROTOCOL"
fi
}
# build firmware for the provided pio env in $1 # build firmware for the provided pio env in $1
build_firmware() { build_firmware() {
@@ -94,6 +115,9 @@ build_firmware() {
# add firmware version info to end of existing platformio build flags in environment vars # add firmware version info to end of existing platformio build flags in environment vars
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DFIRMWARE_BUILD_DATE='\"${FIRMWARE_BUILD_DATE}\"' -DFIRMWARE_VERSION='\"${FIRMWARE_VERSION_STRING}\"'" export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DFIRMWARE_BUILD_DATE='\"${FIRMWARE_BUILD_DATE}\"' -DFIRMWARE_VERSION='\"${FIRMWARE_VERSION_STRING}\"'"
# disable debug flags if requested
disable_debug_flags
# build firmware target # build firmware target
pio run -e $1 pio run -e $1

View File

@@ -103,7 +103,9 @@ Request type
| `0x02` | keepalive | (deprecated) | | `0x02` | keepalive | (deprecated) |
| `0x03` | get telemetry data | TODO | | `0x03` | get telemetry data | TODO |
| `0x04` | get min,max,avg data | sensor nodes - get min, max, average for given time span | | `0x04` | get min,max,avg data | sensor nodes - get min, max, average for given time span |
| `0x05` | get access list | get node's approved access list | | `0x05` | get access list | get node's approved access list |
| `0x06` | get neighbors | get repeater node's neighbors |
| `0x07` | get owner info | get repeater firmware-ver/name/owner info |
### Get stats ### Get stats
@@ -132,6 +134,27 @@ Gets information about the node, possibly including the following:
Request data about sensors on the node, including battery level. Request data about sensors on the node, including battery level.
### Get Telemetry
TODO
### Get Min/Max/Ave (Sensor nodes)
TODO
### Get Access List
TODO
### Get Neighors
TODO
### Get Owner Info
TODO
## Response ## Response
| Field | Size (bytes) | Description | | Field | Size (bytes) | Description |
@@ -179,6 +202,34 @@ txt_type
| timestamp | 4 | sender time (unix timestamp) | | timestamp | 4 | sender time (unix timestamp) |
| password | rest of message | password for repeater/sensor | | password | rest of message | password for repeater/sensor |
## Repeater - Regions request
| Field | Size (bytes) | Description |
|----------------|-----------------|-------------------------------------------------------------------------------|
| timestamp | 4 | sender time (unix timestamp) |
| req type | 1 | 0x01 (request sub type) |
| reply path len | 1 | path len for reply |
| reply path | (variable) | reply path |
## Repeater - Owner info request
| Field | Size (bytes) | Description |
|----------------|-----------------|-------------------------------------------------------------------------------|
| timestamp | 4 | sender time (unix timestamp) |
| req type | 1 | 0x02 (request sub type) |
| reply path len | 1 | path len for reply |
| reply path | (variable) | reply path |
## Repeater - Clock and status request
| Field | Size (bytes) | Description |
|----------------|-----------------|-------------------------------------------------------------------------------|
| timestamp | 4 | sender time (unix timestamp) |
| req type | 1 | 0x03 (request sub type) |
| reply path len | 1 | path len for reply |
| reply path | (variable) | reply path |
# Group text message / datagram # Group text message / datagram
| Field | Size (bytes) | Description | | Field | Size (bytes) | Description |

View File

@@ -227,6 +227,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no
file.read((uint8_t *)&_prefs.buzzer_quiet, sizeof(_prefs.buzzer_quiet)); // 84 file.read((uint8_t *)&_prefs.buzzer_quiet, sizeof(_prefs.buzzer_quiet)); // 84
file.read((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85 file.read((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85
file.read((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86 file.read((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86
file.read((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87
file.close(); file.close();
} }
@@ -261,6 +262,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_
file.write((uint8_t *)&_prefs.buzzer_quiet, sizeof(_prefs.buzzer_quiet)); // 84 file.write((uint8_t *)&_prefs.buzzer_quiet, sizeof(_prefs.buzzer_quiet)); // 84
file.write((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85 file.write((uint8_t *)&_prefs.gps_enabled, sizeof(_prefs.gps_enabled)); // 85
file.write((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86 file.write((uint8_t *)&_prefs.gps_interval, sizeof(_prefs.gps_interval)); // 86
file.write((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87
file.close(); file.close();
} }

View File

@@ -53,6 +53,9 @@
#define CMD_SET_FLOOD_SCOPE 54 // v8+ #define CMD_SET_FLOOD_SCOPE 54 // v8+
#define CMD_SEND_CONTROL_DATA 55 // v8+ #define CMD_SEND_CONTROL_DATA 55 // v8+
#define CMD_GET_STATS 56 // v8+, second byte is stats type #define CMD_GET_STATS 56 // v8+, second byte is stats type
#define CMD_SEND_ANON_REQ 57
#define CMD_SET_AUTOADD_CONFIG 58
#define CMD_GET_AUTOADD_CONFIG 59
// Stats sub-types for CMD_GET_STATS // Stats sub-types for CMD_GET_STATS
#define STATS_TYPE_CORE 0 #define STATS_TYPE_CORE 0
@@ -84,6 +87,7 @@
#define RESP_CODE_ADVERT_PATH 22 #define RESP_CODE_ADVERT_PATH 22
#define RESP_CODE_TUNING_PARAMS 23 #define RESP_CODE_TUNING_PARAMS 23
#define RESP_CODE_STATS 24 // v8+, second byte is stats type #define RESP_CODE_STATS 24 // v8+, second byte is stats type
#define RESP_CODE_AUTOADD_CONFIG 25
#define SEND_TIMEOUT_BASE_MILLIS 500 #define SEND_TIMEOUT_BASE_MILLIS 500
#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f #define FLOOD_SEND_TIMEOUT_FACTOR 16.0f
@@ -109,6 +113,8 @@
#define PUSH_CODE_BINARY_RESPONSE 0x8C #define PUSH_CODE_BINARY_RESPONSE 0x8C
#define PUSH_CODE_PATH_DISCOVERY_RESPONSE 0x8D #define PUSH_CODE_PATH_DISCOVERY_RESPONSE 0x8D
#define PUSH_CODE_CONTROL_DATA 0x8E // v8+ #define PUSH_CODE_CONTROL_DATA 0x8E // v8+
#define PUSH_CODE_CONTACT_DELETED 0x8F // used to notify client app of deleted contact when overwriting oldest
#define PUSH_CODE_CONTACTS_FULL 0x90 // used to notify client app that contacts storage is full
#define ERR_CODE_UNSUPPORTED_CMD 1 #define ERR_CODE_UNSUPPORTED_CMD 1
#define ERR_CODE_NOT_FOUND 2 #define ERR_CODE_NOT_FOUND 2
@@ -119,6 +125,15 @@
#define MAX_SIGN_DATA_LEN (8 * 1024) // 8K #define MAX_SIGN_DATA_LEN (8 * 1024) // 8K
// Auto-add config bitmask
// Bit 0: If set, overwrite oldest non-favourite contact when contacts file is full
// Bits 1-4: these indicate which contact types to auto-add when manual_contact_mode = 0x01
#define AUTO_ADD_OVERWRITE_OLDEST (1 << 0) // 0x01 - overwrite oldest non-favourite when full
#define AUTO_ADD_CHAT (1 << 1) // 0x02 - auto-add Chat (Companion) (ADV_TYPE_CHAT)
#define AUTO_ADD_REPEATER (1 << 2) // 0x04 - auto-add Repeater (ADV_TYPE_REPEATER)
#define AUTO_ADD_ROOM_SERVER (1 << 3) // 0x08 - auto-add Room Server (ADV_TYPE_ROOM)
#define AUTO_ADD_SENSOR (1 << 4) // 0x10 - auto-add Sensor (ADV_TYPE_SENSOR)
void MyMesh::writeOKFrame() { void MyMesh::writeOKFrame() {
uint8_t buf[1]; uint8_t buf[1];
buf[0] = RESP_CODE_OK; buf[0] = RESP_CODE_OK;
@@ -261,9 +276,54 @@ bool MyMesh::isAutoAddEnabled() const {
return (_prefs.manual_add_contacts & 1) == 0; return (_prefs.manual_add_contacts & 1) == 0;
} }
bool MyMesh::shouldAutoAddContactType(uint8_t contact_type) const {
if ((_prefs.manual_add_contacts & 1) == 0) {
return true;
}
uint8_t type_bit = 0;
switch (contact_type) {
case ADV_TYPE_CHAT:
type_bit = AUTO_ADD_CHAT;
break;
case ADV_TYPE_REPEATER:
type_bit = AUTO_ADD_REPEATER;
break;
case ADV_TYPE_ROOM:
type_bit = AUTO_ADD_ROOM_SERVER;
break;
case ADV_TYPE_SENSOR:
type_bit = AUTO_ADD_SENSOR;
break;
default:
return false; // Unknown type, don't auto-add
}
return (_prefs.autoadd_config & type_bit) != 0;
}
bool MyMesh::shouldOverwriteWhenFull() const {
return (_prefs.autoadd_config & AUTO_ADD_OVERWRITE_OLDEST) != 0;
}
void MyMesh::onContactOverwrite(const uint8_t* pub_key) {
if (_serial->isConnected()) {
out_frame[0] = PUSH_CODE_CONTACT_DELETED;
memcpy(&out_frame[1], pub_key, PUB_KEY_SIZE);
_serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE);
}
}
void MyMesh::onContactsFull() {
if (_serial->isConnected()) {
out_frame[0] = PUSH_CODE_CONTACTS_FULL;
_serial->writeFrame(out_frame, 1);
}
}
void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) { void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) {
if (_serial->isConnected()) { if (_serial->isConnected()) {
if (!isAutoAddEnabled() && is_new) { if (is_new) {
writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact); writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact);
} else { } else {
out_frame[0] = PUSH_CODE_ADVERT; out_frame[0] = PUSH_CODE_ADVERT;
@@ -298,7 +358,7 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
memcpy(p->path, path, p->path_len); memcpy(p->path, path, p->path_len);
} }
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); if (!is_new) dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); // only schedule lazy write for contacts that are in contacts[]
} }
static int sort_by_recent(const void *a, const void *b) { static int sort_by_recent(const void *a, const void *b) {
@@ -802,6 +862,7 @@ void MyMesh::begin(bool has_display) {
resetContacts(); resetContacts();
_store->loadContacts(this); _store->loadContacts(this);
bootstrapRTCfromContacts();
addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
_store->loadChannels(this); _store->loadChannels(this);
@@ -1286,6 +1347,27 @@ void MyMesh::handleCmdFrame(size_t len) {
} else { } else {
writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
} }
} else if (cmd_frame[0] == CMD_SEND_ANON_REQ && len > 1 + PUB_KEY_SIZE) {
uint8_t *pub_key = &cmd_frame[1];
ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
uint8_t *data = &cmd_frame[1 + PUB_KEY_SIZE];
if (recipient) {
uint32_t tag, est_timeout;
int result = sendAnonReq(*recipient, data, len - (1 + PUB_KEY_SIZE), tag, est_timeout);
if (result == MSG_SEND_FAILED) {
writeErrFrame(ERR_CODE_TABLE_FULL);
} else {
clearPendingReqs();
pending_req = tag; // match this to onContactResponse()
out_frame[0] = RESP_CODE_SENT;
out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
memcpy(&out_frame[2], &tag, 4);
memcpy(&out_frame[6], &est_timeout, 4);
_serial->writeFrame(out_frame, 10);
}
} else {
writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
}
} else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) { } else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) {
uint8_t *pub_key = &cmd_frame[1]; uint8_t *pub_key = &cmd_frame[1];
ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
@@ -1641,6 +1723,15 @@ void MyMesh::handleCmdFrame(size_t len) {
} else { } else {
writeErrFrame(ERR_CODE_TABLE_FULL); writeErrFrame(ERR_CODE_TABLE_FULL);
} }
} else if (cmd_frame[0] == CMD_SET_AUTOADD_CONFIG) {
_prefs.autoadd_config = cmd_frame[1];
savePrefs();
writeOKFrame();
} else if (cmd_frame[0] == CMD_GET_AUTOADD_CONFIG) {
int i = 0;
out_frame[i++] = RESP_CODE_AUTOADD_CONFIG;
out_frame[i++] = _prefs.autoadd_config;
_serial->writeFrame(out_frame, i);
} else { } else {
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]);

View File

@@ -114,6 +114,10 @@ protected:
void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override;
bool isAutoAddEnabled() const override; bool isAutoAddEnabled() const override;
bool shouldAutoAddContactType(uint8_t type) const override;
bool shouldOverwriteWhenFull() const override;
void onContactsFull() override;
void onContactOverwrite(const uint8_t* pub_key) override;
bool onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override; bool onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override;
void onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) override; void onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) override;
void onContactPathUpdated(const ContactInfo &contact) override; void onContactPathUpdated(const ContactInfo &contact) override;

View File

@@ -27,4 +27,5 @@ struct NodePrefs { // persisted to file
uint8_t buzzer_quiet; uint8_t buzzer_quiet;
uint8_t gps_enabled; // GPS enabled flag (0=disabled, 1=enabled) uint8_t gps_enabled; // GPS enabled flag (0=disabled, 1=enabled)
uint32_t gps_interval; // GPS read interval in seconds uint32_t gps_interval; // GPS read interval in seconds
uint8_t autoadd_config; // bitmask for auto-add contacts config
}; };

View File

@@ -41,16 +41,21 @@
#define TXT_ACK_DELAY 200 #define TXT_ACK_DELAY 200
#endif #endif
#define FIRMWARE_VER_LEVEL 1 #define FIRMWARE_VER_LEVEL 2
#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS #define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS
#define REQ_TYPE_KEEP_ALIVE 0x02 #define REQ_TYPE_KEEP_ALIVE 0x02
#define REQ_TYPE_GET_TELEMETRY_DATA 0x03 #define REQ_TYPE_GET_TELEMETRY_DATA 0x03
#define REQ_TYPE_GET_ACCESS_LIST 0x05 #define REQ_TYPE_GET_ACCESS_LIST 0x05
#define REQ_TYPE_GET_NEIGHBOURS 0x06 #define REQ_TYPE_GET_NEIGHBOURS 0x06
#define REQ_TYPE_GET_OWNER_INFO 0x07 // FIRMWARE_VER_LEVEL >= 2
#define RESP_SERVER_LOGIN_OK 0 // response to ANON_REQ #define RESP_SERVER_LOGIN_OK 0 // response to ANON_REQ
#define ANON_REQ_TYPE_REGIONS 0x01
#define ANON_REQ_TYPE_OWNER 0x02
#define ANON_REQ_TYPE_BASIC 0x03 // just remote clock
#define CLI_REPLY_DELAY_MILLIS 600 #define CLI_REPLY_DELAY_MILLIS 600
#define LAZY_CONTACTS_WRITE_DELAY 5000 #define LAZY_CONTACTS_WRITE_DELAY 5000
@@ -139,6 +144,64 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr
return 13; // reply length return 13; // reply length
} }
uint8_t MyMesh::handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data) {
if (anon_limiter.allow(rtc_clock.getCurrentTime())) {
// request data has: {reply-path-len}{reply-path}
reply_path_len = *data++ & 0x3F;
memcpy(reply_path, data, reply_path_len);
// data += reply_path_len;
memcpy(reply_data, &sender_timestamp, 4); // prefix with sender_timestamp, like a tag
uint32_t now = getRTCClock()->getCurrentTime();
memcpy(&reply_data[4], &now, 4); // include our clock (for easy clock sync, and packet hash uniqueness)
return 8 + region_map.exportNamesTo((char *) &reply_data[8], sizeof(reply_data) - 12, REGION_DENY_FLOOD); // reply length
}
return 0;
}
uint8_t MyMesh::handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data) {
if (anon_limiter.allow(rtc_clock.getCurrentTime())) {
// request data has: {reply-path-len}{reply-path}
reply_path_len = *data++ & 0x3F;
memcpy(reply_path, data, reply_path_len);
// data += reply_path_len;
memcpy(reply_data, &sender_timestamp, 4); // prefix with sender_timestamp, like a tag
uint32_t now = getRTCClock()->getCurrentTime();
memcpy(&reply_data[4], &now, 4); // include our clock (for easy clock sync, and packet hash uniqueness)
sprintf((char *) &reply_data[8], "%s\n%s", _prefs.node_name, _prefs.owner_info);
return 8 + strlen((char *) &reply_data[8]); // reply length
}
return 0;
}
uint8_t MyMesh::handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data) {
if (anon_limiter.allow(rtc_clock.getCurrentTime())) {
// request data has: {reply-path-len}{reply-path}
reply_path_len = *data++ & 0x3F;
memcpy(reply_path, data, reply_path_len);
// data += reply_path_len;
memcpy(reply_data, &sender_timestamp, 4); // prefix with sender_timestamp, like a tag
uint32_t now = getRTCClock()->getCurrentTime();
memcpy(&reply_data[4], &now, 4); // include our clock (for easy clock sync, and packet hash uniqueness)
reply_data[8] = 0; // features
#ifdef WITH_RS232_BRIDGE
reply_data[8] |= 0x01; // is bridge, type UART
#elif WITH_ESPNOW_BRIDGE
reply_data[8] |= 0x03; // is bridge, type ESP-NOW
#endif
if (_prefs.disable_fwd) { // is this repeater currently disabled
reply_data[8] |= 0x80; // is disabled
}
// TODO: add some kind of moving-window utilisation metric, so can query 'how busy' is this repeater
return 9; // reply length
}
return 0;
}
int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t *payload, size_t payload_len) { int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t *payload, size_t payload_len) {
// uint32_t now = getRTCClock()->getCurrentTimeUnique(); // uint32_t now = getRTCClock()->getCurrentTimeUnique();
// memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp // memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp
@@ -296,6 +359,9 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t
return reply_offset; return reply_offset;
} }
} else if (payload[0] == REQ_TYPE_GET_OWNER_INFO) {
sprintf((char *) &reply_data[4], "%s\n%s\n%s", FIRMWARE_VERSION, _prefs.node_name, _prefs.owner_info);
return 4 + strlen((char *) &reply_data[4]);
} }
return 0; // unknown command return 0; // unknown command
} }
@@ -448,12 +514,18 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
data[len] = 0; // ensure null terminator data[len] = 0; // ensure null terminator
uint8_t reply_len; uint8_t reply_len;
reply_path_len = -1;
if (data[4] == 0 || data[4] >= ' ') { // is password, ie. a login request if (data[4] == 0 || data[4] >= ' ') { // is password, ie. a login request
reply_len = handleLoginReq(sender, secret, timestamp, &data[4], packet->isRouteFlood()); reply_len = handleLoginReq(sender, secret, timestamp, &data[4], packet->isRouteFlood());
//} else if (data[4] == ANON_REQ_TYPE_*) { // future type codes } else if (data[4] == ANON_REQ_TYPE_REGIONS && packet->isRouteDirect()) {
// TODO reply_len = handleAnonRegionsReq(sender, timestamp, &data[5]);
} else if (data[4] == ANON_REQ_TYPE_OWNER && packet->isRouteDirect()) {
reply_len = handleAnonOwnerReq(sender, timestamp, &data[5]);
} else if (data[4] == ANON_REQ_TYPE_BASIC && packet->isRouteDirect()) {
reply_len = handleAnonClockReq(sender, timestamp, &data[5]);
} else { } else {
reply_len = 0; // unknown request type reply_len = 0; // unknown/invalid request type
} }
if (reply_len == 0) return; // invalid request if (reply_len == 0) return; // invalid request
@@ -463,9 +535,12 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len, mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len,
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
if (path) sendFlood(path, SERVER_RESPONSE_DELAY); if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
} else { } else if (reply_path_len < 0) {
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len); mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
if (reply) sendFlood(reply, SERVER_RESPONSE_DELAY); if (reply) sendFlood(reply, SERVER_RESPONSE_DELAY);
} else {
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
if (reply) sendDirect(reply, reply_path, reply_path_len, SERVER_RESPONSE_DELAY);
} }
} }
} }
@@ -637,7 +712,9 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t
void MyMesh::onControlDataRecv(mesh::Packet* packet) { void MyMesh::onControlDataRecv(mesh::Packet* packet) {
uint8_t type = packet->payload[0] & 0xF0; // just test upper 4 bits uint8_t type = packet->payload[0] & 0xF0; // just test upper 4 bits
if (type == CTL_TYPE_NODE_DISCOVER_REQ && packet->payload_len >= 6 && discover_limiter.allow(rtc_clock.getCurrentTime())) { if (type == CTL_TYPE_NODE_DISCOVER_REQ && packet->payload_len >= 6
&& !_prefs.disable_fwd && discover_limiter.allow(rtc_clock.getCurrentTime())
) {
int i = 1; int i = 1;
uint8_t filter = packet->payload[i++]; uint8_t filter = packet->payload[i++];
uint32_t tag; uint32_t tag;
@@ -668,7 +745,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
mesh::RTCClock &rtc, mesh::MeshTables &tables) mesh::RTCClock &rtc, mesh::MeshTables &tables)
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
_cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4), region_map(key_store), temp_map(key_store), _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4), region_map(key_store), temp_map(key_store),
discover_limiter(4, 120) // max 4 every 2 minutes discover_limiter(4, 120), // max 4 every 2 minutes
anon_limiter(4, 180) // max 4 every 3 minutes
#if defined(WITH_RS232_BRIDGE) #if defined(WITH_RS232_BRIDGE)
, bridge(&_prefs, WITH_RS232_BRIDGE, _mgr, &rtc) , bridge(&_prefs, WITH_RS232_BRIDGE, _mgr, &rtc)
#endif #endif

View File

@@ -88,12 +88,14 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
NodePrefs _prefs; NodePrefs _prefs;
CommonCLI _cli; CommonCLI _cli;
uint8_t reply_data[MAX_PACKET_PAYLOAD]; uint8_t reply_data[MAX_PACKET_PAYLOAD];
uint8_t reply_path[MAX_PATH_SIZE];
int8_t reply_path_len;
ClientACL acl; ClientACL acl;
TransportKeyStore key_store; TransportKeyStore key_store;
RegionMap region_map, temp_map; RegionMap region_map, temp_map;
RegionEntry* load_stack[8]; RegionEntry* load_stack[8];
RegionEntry* recv_pkt_region; RegionEntry* recv_pkt_region;
RateLimiter discover_limiter; RateLimiter discover_limiter, anon_limiter;
bool region_load_active; bool region_load_active;
unsigned long dirty_contacts_expiry; unsigned long dirty_contacts_expiry;
#if MAX_NEIGHBOURS #if MAX_NEIGHBOURS
@@ -114,6 +116,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr);
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data);
uint8_t handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data);
uint8_t handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data);
int handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len); int handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len);
mesh::Packet* createSelfAdvert(); mesh::Packet* createSelfAdvert();

10
fetch_prs.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
git branch -D pr-1199
git branch -D pr-1297
git branch -D pr-1338
# fetch PRs
git fetch upstream pull/1338/head:pr-1338
git fetch upstream pull/1297/head:pr-1297
git fetch upstream pull/1199/head:pr-1199

9
merge_prs.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/sh
git merge pr-1338 --no-edit -m "Integration of upstrem PR #1338"
git merge pr-1297 --no-edit -m "Integration of upstrem PR #1297"
git merge pr-1199 --no-edit -m "Integration of upstrem PR #1199"
git merge pio-ini-adjustments -m "platformio.ini: Adjust defaults for LoRa frequncies and advert interval limits"

View File

@@ -55,6 +55,54 @@ void BaseChatMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) {
} }
} }
void BaseChatMesh::bootstrapRTCfromContacts() {
uint32_t latest = 0;
for (int i = 0; i < num_contacts; i++) {
if (contacts[i].lastmod > latest) {
latest = contacts[i].lastmod;
}
}
if (latest != 0) {
getRTCClock()->setCurrentTime(latest + 1);
}
}
ContactInfo* BaseChatMesh::allocateContactSlot() {
if (num_contacts < MAX_CONTACTS) {
return &contacts[num_contacts++];
} else if (shouldOverwriteWhenFull()) {
// Find oldest non-favourite contact by oldest lastmod timestamp
int oldest_idx = -1;
uint32_t oldest_lastmod = 0xFFFFFFFF;
for (int i = 0; i < num_contacts; i++) {
bool is_favourite = (contacts[i].flags & 0x01) != 0;
if (!is_favourite && contacts[i].lastmod < oldest_lastmod) {
oldest_lastmod = contacts[i].lastmod;
oldest_idx = i;
}
}
if (oldest_idx >= 0) {
onContactOverwrite(contacts[oldest_idx].id.pub_key);
return &contacts[oldest_idx];
}
}
return NULL; // no space, no overwrite or all contacts are all favourites
}
void BaseChatMesh::populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp) {
memset(&ci, 0, sizeof(ci));
ci.id = id;
ci.out_path_len = -1; // initially out_path is unknown
StrHelper::strncpy(ci.name, parser.getName(), sizeof(ci.name));
ci.type = parser.getType();
if (parser.hasLatLon()) {
ci.gps_lat = parser.getIntLat();
ci.gps_lon = parser.getIntLon();
}
ci.last_advert_timestamp = timestamp;
ci.lastmod = getRTCClock()->getCurrentTime();
}
void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) { void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) {
AdvertDataParser parser(app_data, app_data_len); AdvertDataParser parser(app_data, app_data_len);
if (!(parser.isValid() && parser.hasName())) { if (!(parser.isValid() && parser.hasName())) {
@@ -85,50 +133,38 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id,
} }
putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen); putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen);
bool is_new = false; bool is_new = false; // true = not in contacts[], false = exists in contacts[]
if (from == NULL) { if (from == NULL) {
if (!isAutoAddEnabled()) { if (!shouldAutoAddContactType(parser.getType())) {
ContactInfo ci; ContactInfo ci;
memset(&ci, 0, sizeof(ci)); populateContactFromAdvert(ci, id, parser, timestamp);
ci.id = id;
ci.out_path_len = -1; // initially out_path is unknown
StrHelper::strncpy(ci.name, parser.getName(), sizeof(ci.name));
ci.type = parser.getType();
if (parser.hasLatLon()) {
ci.gps_lat = parser.getIntLat();
ci.gps_lon = parser.getIntLon();
}
ci.last_advert_timestamp = timestamp;
ci.lastmod = getRTCClock()->getCurrentTime();
onDiscoveredContact(ci, true, packet->path_len, packet->path); // let UI know onDiscoveredContact(ci, true, packet->path_len, packet->path); // let UI know
return; return;
} }
is_new = true; from = allocateContactSlot();
if (num_contacts < MAX_CONTACTS) { if (from == NULL) {
from = &contacts[num_contacts++]; ContactInfo ci;
from->id = id; populateContactFromAdvert(ci, id, parser, timestamp);
from->out_path_len = -1; // initially out_path is unknown onDiscoveredContact(ci, true, packet->path_len, packet->path);
from->gps_lat = 0; // initially unknown GPS loc onContactsFull();
from->gps_lon = 0; MESH_DEBUG_PRINTLN("onAdvertRecv: unable to allocate contact slot for new contact");
from->sync_since = 0;
from->shared_secret_valid = false; // ecdh shared_secret will be calculated later on demand
} else {
MESH_DEBUG_PRINTLN("onAdvertRecv: contacts table is full!");
return; return;
} }
populateContactFromAdvert(*from, id, parser, timestamp);
from->sync_since = 0;
from->shared_secret_valid = false;
} }
// update // update
StrHelper::strncpy(from->name, parser.getName(), sizeof(from->name)); StrHelper::strncpy(from->name, parser.getName(), sizeof(from->name));
from->type = parser.getType(); from->type = parser.getType();
if (parser.hasLatLon()) { if (parser.hasLatLon()) {
from->gps_lat = parser.getIntLat(); from->gps_lat = parser.getIntLat();
from->gps_lon = parser.getIntLon(); from->gps_lon = parser.getIntLon();
} }
from->last_advert_timestamp = timestamp; from->last_advert_timestamp = timestamp;
from->lastmod = getRTCClock()->getCurrentTime(); from->lastmod = getRTCClock()->getCurrentTime();
onDiscoveredContact(*from, is_new, packet->path_len, packet->path); // let UI know onDiscoveredContact(*from, is_new, packet->path_len, packet->path); // let UI know
} }
@@ -477,6 +513,31 @@ int BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password,
return MSG_SEND_FAILED; return MSG_SEND_FAILED;
} }
int BaseChatMesh::sendAnonReq(const ContactInfo& recipient, const uint8_t* data, uint8_t len, uint32_t& tag, uint32_t& est_timeout) {
mesh::Packet* pkt;
{
uint8_t temp[MAX_PACKET_PAYLOAD];
tag = getRTCClock()->getCurrentTimeUnique();
memcpy(temp, &tag, 4); // tag to match later (also extra blob to help make packet_hash unique)
memcpy(&temp[4], data, len);
pkt = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, recipient.id, recipient.getSharedSecret(self_id), temp, 4 + len);
}
if (pkt) {
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
if (recipient.out_path_len < 0) {
sendFloodScoped(recipient, pkt);
est_timeout = calcFloodTimeoutMillisFor(t);
return MSG_SEND_SENT_FLOOD;
} else {
sendDirect(pkt, recipient.out_path, recipient.out_path_len);
est_timeout = calcDirectTimeoutMillisFor(t, recipient.out_path_len);
return MSG_SEND_SENT_DIRECT;
}
}
return MSG_SEND_FAILED;
}
int BaseChatMesh::sendRequest(const ContactInfo& recipient, const uint8_t* req_data, uint8_t data_len, uint32_t& tag, uint32_t& est_timeout) { int BaseChatMesh::sendRequest(const ContactInfo& recipient, const uint8_t* req_data, uint8_t data_len, uint32_t& tag, uint32_t& est_timeout) {
if (data_len > MAX_PACKET_PAYLOAD - 16) return MSG_SEND_FAILED; if (data_len > MAX_PACKET_PAYLOAD - 16) return MSG_SEND_FAILED;
@@ -697,10 +758,9 @@ ContactInfo* BaseChatMesh::lookupContactByPubKey(const uint8_t* pub_key, int pre
} }
bool BaseChatMesh::addContact(const ContactInfo& contact) { bool BaseChatMesh::addContact(const ContactInfo& contact) {
if (num_contacts < MAX_CONTACTS) { ContactInfo* dest = allocateContactSlot();
auto dest = &contacts[num_contacts++]; if (dest) {
*dest = contact; *dest = contact;
dest->shared_secret_valid = false; // mark shared_secret as needing calculation dest->shared_secret_valid = false; // mark shared_secret as needing calculation
return true; // success return true; // success
} }

View File

@@ -88,10 +88,17 @@ protected:
memset(connections, 0, sizeof(connections)); memset(connections, 0, sizeof(connections));
} }
void bootstrapRTCfromContacts();
void resetContacts() { num_contacts = 0; } void resetContacts() { num_contacts = 0; }
void populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp);
ContactInfo* allocateContactSlot(); // helper to find slot for new contact
// 'UI' concepts, for sub-classes to implement // 'UI' concepts, for sub-classes to implement
virtual bool isAutoAddEnabled() const { return true; } virtual bool isAutoAddEnabled() const { return true; }
virtual bool shouldAutoAddContactType(uint8_t type) const { return true; }
virtual void onContactsFull() {};
virtual bool shouldOverwriteWhenFull() const { return false; }
virtual void onContactOverwrite(const uint8_t* pub_key) {};
virtual void onDiscoveredContact(ContactInfo& contact, bool is_new, uint8_t path_len, const uint8_t* path) = 0; virtual void onDiscoveredContact(ContactInfo& contact, bool is_new, uint8_t path_len, const uint8_t* path) = 0;
virtual ContactInfo* processAck(const uint8_t *data) = 0; virtual ContactInfo* processAck(const uint8_t *data) = 0;
virtual void onContactPathUpdated(const ContactInfo& contact) = 0; virtual void onContactPathUpdated(const ContactInfo& contact) = 0;
@@ -141,6 +148,7 @@ public:
int sendCommandData(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& est_timeout); int sendCommandData(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& est_timeout);
bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len); bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len);
int sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout); int sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout);
int sendAnonReq(const ContactInfo& recipient, const uint8_t* data, uint8_t len, uint32_t& tag, uint32_t& est_timeout);
int sendRequest(const ContactInfo& recipient, uint8_t req_type, uint32_t& tag, uint32_t& est_timeout); int sendRequest(const ContactInfo& recipient, uint8_t req_type, uint32_t& tag, uint32_t& est_timeout);
int sendRequest(const ContactInfo& recipient, const uint8_t* req_data, uint8_t data_len, uint32_t& tag, uint32_t& est_timeout); int sendRequest(const ContactInfo& recipient, const uint8_t* req_data, uint8_t data_len, uint32_t& tag, uint32_t& est_timeout);
bool shareContactZeroHop(const ContactInfo& contact); bool shareContactZeroHop(const ContactInfo& contact);

View File

@@ -14,6 +14,14 @@ static uint32_t _atoi(const char* sp) {
return n; return n;
} }
static bool isValidName(const char *n) {
while (*n) {
if (*n == '[' || *n == ']' || *n == '/' || *n == '\\' || *n == ':' || *n == ',' || *n == '?' || *n == '*') return false;
n++;
}
return true;
}
void CommonCLI::loadPrefs(FILESYSTEM* fs) { void CommonCLI::loadPrefs(FILESYSTEM* fs) {
if (fs->exists("/com_prefs")) { if (fs->exists("/com_prefs")) {
loadPrefsInt(fs, "/com_prefs"); // new filename loadPrefsInt(fs, "/com_prefs"); // new filename
@@ -72,7 +80,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy)); // 161 file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy)); // 161
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
// 170 file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
// 290
// sanitise bad pref values // sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
@@ -155,7 +164,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->advert_loc_policy, sizeof(_prefs->advert_loc_policy)); // 161 file.write((uint8_t *)&_prefs->advert_loc_policy, sizeof(_prefs->advert_loc_policy)); // 161
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
// 170 file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
// 290
file.close(); file.close();
} }
@@ -301,6 +311,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); sprintf(reply, "> %d", (uint32_t)_prefs->flood_max);
} else if (memcmp(config, "direct.txdelay", 14) == 0) { } else if (memcmp(config, "direct.txdelay", 14) == 0) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor));
} else if (memcmp(config, "owner.info", 10) == 0) {
*reply++ = '>';
*reply++ = ' ';
const char* sp = _prefs->owner_info;
while (*sp) {
*reply++ = (*sp == '\n') ? '|' : *sp; // translate newline back to orig '|'
sp++;
}
*reply = 0; // set null terminator
} else if (memcmp(config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) { } else if (memcmp(config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) {
sprintf(reply, "> %d", (uint32_t) _prefs->tx_power_dbm); sprintf(reply, "> %d", (uint32_t) _prefs->tx_power_dbm);
} else if (memcmp(config, "freq", 4) == 0) { } else if (memcmp(config, "freq", 4) == 0) {
@@ -410,9 +429,13 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
strcpy(reply, "Error, invalid key"); strcpy(reply, "Error, invalid key");
} }
} else if (memcmp(config, "name ", 5) == 0) { } else if (memcmp(config, "name ", 5) == 0) {
StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name)); if (isValidName(&config[5])) {
savePrefs(); StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name));
strcpy(reply, "OK"); savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, bad chars");
}
} else if (memcmp(config, "repeat ", 7) == 0) { } else if (memcmp(config, "repeat ", 7) == 0) {
_prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0; _prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0;
savePrefs(); savePrefs();
@@ -479,6 +502,16 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
} else { } else {
strcpy(reply, "Error, cannot be negative"); strcpy(reply, "Error, cannot be negative");
} }
} else if (memcmp(config, "owner.info ", 11) == 0) {
config += 11;
char *dp = _prefs->owner_info;
while (*config && dp - _prefs->owner_info < sizeof(_prefs->owner_info)-1) {
*dp++ = (*config == '|') ? '\n' : *config; // translate '|' to newline chars
config++;
}
*dp = 0;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "tx ", 3) == 0) { } else if (memcmp(config, "tx ", 3) == 0) {
_prefs->tx_power_dbm = atoi(&config[3]); _prefs->tx_power_dbm = atoi(&config[3]);
savePrefs(); savePrefs();

View File

@@ -50,6 +50,7 @@ struct NodePrefs { // persisted to file
uint8_t advert_loc_policy; uint8_t advert_loc_policy;
uint32_t discovery_mod_timestamp; uint32_t discovery_mod_timestamp;
float adc_multiplier; float adc_multiplier;
char owner_info[120];
}; };
class CommonCLICallbacks { class CommonCLICallbacks {

View File

@@ -9,8 +9,9 @@ RegionMap::RegionMap(TransportKeyStore& store) : _store(&store) {
strcpy(wildcard.name, "*"); strcpy(wildcard.name, "*");
} }
bool RegionMap::is_name_char(char c) { bool RegionMap::is_name_char(uint8_t c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '_' || c == '#'; // accept all alpha-num or accented characters, but exclude most punctuation chars
return c == '-' || c == '#' || (c >= '0' && c <= '9') || c >= 'A';
} }
static File openWrite(FILESYSTEM* _fs, const char* filename) { static File openWrite(FILESYSTEM* _fs, const char* filename) {
@@ -24,12 +25,12 @@ static File openWrite(FILESYSTEM* _fs, const char* filename) {
#endif #endif
} }
bool RegionMap::load(FILESYSTEM* _fs) { bool RegionMap::load(FILESYSTEM* _fs, const char* path) {
if (_fs->exists("/regions2")) { if (_fs->exists(path ? path : "/regions2")) {
#if defined(RP2040_PLATFORM) #if defined(RP2040_PLATFORM)
File file = _fs->open("/regions2", "r"); File file = _fs->open(path ? path : "/regions2", "r");
#else #else
File file = _fs->open("/regions2"); File file = _fs->open(path ? path : "/regions2");
#endif #endif
if (file) { if (file) {
@@ -67,8 +68,8 @@ bool RegionMap::load(FILESYSTEM* _fs) {
return false; // failed return false; // failed
} }
bool RegionMap::save(FILESYSTEM* _fs) { bool RegionMap::save(FILESYSTEM* _fs, const char* path) {
File file = openWrite(_fs, "/regions2"); File file = openWrite(_fs, path ? path : "/regions2");
if (file) { if (file) {
uint8_t pad[128]; uint8_t pad[128];
memset(pad, 0, sizeof(pad)); memset(pad, 0, sizeof(pad));
@@ -235,3 +236,27 @@ void RegionMap::printChildRegions(int indent, const RegionEntry* parent, Stream&
void RegionMap::exportTo(Stream& out) const { void RegionMap::exportTo(Stream& out) const {
printChildRegions(0, &wildcard, out); // recursive printChildRegions(0, &wildcard, out); // recursive
} }
int RegionMap::exportNamesTo(char *dest, int max_len, uint8_t mask) {
char *dp = dest;
if ((wildcard.flags & mask) == 0) {
*dp++ = '*';
*dp++ = ',';
}
for (int i = 0; i < num_regions; i++) {
auto region = &regions[i];
if ((region->flags & mask) == 0) { // region allowed? (per 'mask' param)
int len = strlen(region->name);
if ((dp - dest) + len + 2 < max_len) { // only append if name will fit
memcpy(dp, region->name, len);
dp += len;
*dp++ = ',';
}
}
}
if (dp > dest) { dp--; } // don't include trailing comma
*dp = 0; // set null terminator
return dp - dest; // return length
}

View File

@@ -30,10 +30,10 @@ class RegionMap {
public: public:
RegionMap(TransportKeyStore& store); RegionMap(TransportKeyStore& store);
static bool is_name_char(char c); static bool is_name_char(uint8_t c);
bool load(FILESYSTEM* _fs); bool load(FILESYSTEM* _fs, const char* path=NULL);
bool save(FILESYSTEM* _fs); bool save(FILESYSTEM* _fs, const char* path=NULL);
RegionEntry* putRegion(const char* name, uint16_t parent_id, uint16_t id = 0); RegionEntry* putRegion(const char* name, uint16_t parent_id, uint16_t id = 0);
RegionEntry* findMatch(mesh::Packet* packet, uint8_t mask); RegionEntry* findMatch(mesh::Packet* packet, uint8_t mask);
@@ -47,6 +47,9 @@ public:
bool clear(); bool clear();
void resetFrom(const RegionMap& src) { num_regions = 0; next_id = src.next_id; } void resetFrom(const RegionMap& src) { num_regions = 0; next_id = src.next_id; }
int getCount() const { return num_regions; } int getCount() const { return num_regions; }
const RegionEntry* getByIdx(int i) const { return &regions[i]; }
const RegionEntry* getRoot() const { return &wildcard; }
int exportNamesTo(char *dest, int max_len, uint8_t mask);
void exportTo(Stream& out) const; void exportTo(Stream& out) const;
}; };

View File

@@ -0,0 +1,64 @@
#!/bin/bash # Note: switched to bash for process substitution support
export PATH="$HOME/.platformio/penv/bin:$PATH"
LOGFILE="$PWD/meshcore-evo-fw.log"
FIRMWARE_VERSION="v1.11.0-evo_0.1.3"
FIRMWARE_BUILD_DATE=$(date '+%d-%b-%Y')
collect_bin_files(){
DEST_DIR="./firmwares"
mkdir -p "$DEST_DIR"
BUILD_DIR=".pio/build"
if [ ! -d "$BUILD_DIR" ]; then
echo "Error: $BUILD_DIR not found. Did you run the build process?"
exit 1
fi
echo "Copying firmware files to $DEST_DIR..."
for target_path in "$BUILD_DIR"/*/; do
echo $target_path
target_name=$(basename "$target_path")
# if ls "$target_path"*.bin >/dev/null 2>&1; then
for bin_file in "$target_path"*firmware*.{uf2,bin,zip}; do
filename=$(basename "$bin_file")
new_filename="${target_name}_${FIRMWARE_VERSION}_${FIRMWARE_BUILD_DATE}_${filename}"
cp "$bin_file" "$DEST_DIR/$new_filename"
echo "Done: $new_filename"
done
# fi
done
}
# Everything after this line goes to BOTH console and logfile
exec > >(tee -a "$LOGFILE") 2>&1
echo "-------------------- Build start ----------------"
date
echo "-------------------------------------------------"
# apply patches
# ./tools/maint/apply_patches.sh 1199 1338 1297
# build all repeater firmwares, the will be in .out
FIRMWARE_VERSION="v1.11_evo" ./build.sh build-repeater-firmwares
# build single firmwares
#FIRMWARE_VERSION=$FIRMWARE_VERSION FIRMWARE_BUILD_DATE=$FIRMWARE_BUILD_DATE ./build.sh build-firmware ProMicro_repeater
#FIRMWARE_VERSION=$FIRMWARE_VERSION FIRMWARE_BUILD_DATE=$FIRMWARE_BUILD_DATE ./build.sh build-firmware RAK_4631_repeater
#FIRMWARE_VERSION=$FIRMWARE_VERSION FIRMWARE_BUILD_DATE=$FIRMWARE_BUILD_DATE ./build.sh build-firmware heltec_v4_repeater
#FIRMWARE_VERSION=$FIRMWARE_VERSION FIRMWARE_BUILD_DATE=$FIRMWARE_BUILD_DATE ./build.sh build-firmware Heltec_v3_repeater
#FIRMWARE_VERSION=$FIRMWARE_VERSION FIRMWARE_BUILD_DATE=$FIRMWARE_BUILD_DATE ./build.sh build-firmware Xiao_nrf52_repeater
#FIRMWARE_VERSION=$FIRMWARE_VERSION FIRMWARE_BUILD_DATE=$FIRMWARE_BUILD_DATE ./build.sh build-firmware LilyGo_T3S3_sx1262_repeater
#FIRMWARE_VERSION=$FIRMWARE_VERSION FIRMWARE_BUILD_DATE=$FIRMWARE_BUILD_DATE ./build.sh build-firmware Heltec_t114_without_display_repeater
#FIRMWARE_VERSION=$FIRMWARE_VERSION FIRMWARE_BUILD_DATE=$FIRMWARE_BUILD_DATE ./build.sh build-firmware Heltec_t114_repeater
#collect_bin_files
echo "-------------------- Build end ------------------"
date
echo "-------------------------------------------------"
#grep -E " SUCCESS | FAILED " hansemesh_fw.log

View File

@@ -323,7 +323,7 @@ lib_deps =
extends = Heltec_lora32_v3 extends = Heltec_lora32_v3
build_flags = build_flags =
${Heltec_lora32_v3.build_flags} ${Heltec_lora32_v3.build_flags}
-D MAX_CONTACTS=140 -D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40 -D MAX_GROUP_CHANNELS=40
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1

View File

@@ -1,8 +1,5 @@
[ikoka_nrf52] [ikoka_handheld_nrf]
extends = Xiao_nrf52 extends = nrf52_base
lib_deps = ${nrf52_base.lib_deps}
${sensor_base.lib_deps}
densaugeo/base64 @ ~1.4.0
build_flags = ${nrf52_base.build_flags} build_flags = ${nrf52_base.build_flags}
${sensor_base.build_flags} ${sensor_base.build_flags}
-I lib/nrf52/s140_nrf52_7.3.0_API/include -I lib/nrf52/s140_nrf52_7.3.0_API/include
@@ -26,12 +23,15 @@ build_flags = ${nrf52_base.build_flags}
build_src_filter = ${nrf52_base.build_src_filter} build_src_filter = ${nrf52_base.build_src_filter}
+<../variants/ikoka_handheld_nrf> +<../variants/ikoka_handheld_nrf>
+<helpers/sensors> +<helpers/sensors>
lib_deps = ${nrf52_base.lib_deps}
${sensor_base.lib_deps}
densaugeo/base64 @ ~1.4.0
# larger screen has a different driver, this is for the 0.96 inch # larger screen has a different driver, this is for the 0.96 inch
[ikoka_nrf52_ssd1306_companion] [ikoka_handheld_nrf_ssd1306_companion]
lib_deps = ${ikoka_nrf52.lib_deps} lib_deps = ${ikoka_handheld_nrf.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13 adafruit/Adafruit SSD1306 @ ^2.5.13
build_flags = ${ikoka_nrf52.build_flags} build_flags = ${ikoka_handheld_nrf.build_flags}
-D DISPLAY_CLASS=SSD1306Display -D DISPLAY_CLASS=SSD1306Display
-D DISPLAY_ROTATION=0 -D DISPLAY_ROTATION=0
-D PIN_WIRE_SCL=D6 -D PIN_WIRE_SCL=D6
@@ -42,62 +42,62 @@ build_flags = ${ikoka_nrf52.build_flags}
-D OFFLINE_QUEUE_SIZE=256 -D OFFLINE_QUEUE_SIZE=256
-D QSPIFLASH=1 -D QSPIFLASH=1
-I examples/companion_radio/ui-new -I examples/companion_radio/ui-new
build_src_filter = ${ikoka_nrf52.build_src_filter} build_src_filter = ${ikoka_handheld_nrf.build_src_filter}
+<helpers/ui/SSD1306Display.cpp> +<helpers/ui/SSD1306Display.cpp>
+<../examples/companion_radio/ui-new/UITask.cpp> +<../examples/companion_radio/ui-new/UITask.cpp>
+<../examples/companion_radio/*.cpp> +<../examples/companion_radio/*.cpp>
[env:ikoka_handheld_nrf_e22_30dbm_096_companion_radio_ble] [env:ikoka_handheld_nrf_e22_30dbm_096_companion_radio_ble]
extends = ikoka_nrf52 extends = ikoka_nrf52
build_flags = ${ikoka_nrf52_ssd1306_companion.build_flags} build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags}
-D BLE_PIN_CODE=123456 -D BLE_PIN_CODE=123456
-D LORA_TX_POWER=20 -D LORA_TX_POWER=20
build_src_filter = ${ikoka_nrf52_ssd1306_companion.build_src_filter} build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp> +<helpers/nrf52/SerialBLEInterface.cpp>
[env:ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_ble] [env:ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_ble]
extends = ikoka_nrf52 extends = ikoka_nrf52
build_flags = ${ikoka_nrf52_ssd1306_companion.build_flags} build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags}
-D BLE_PIN_CODE=123456 -D BLE_PIN_CODE=123456
-D LORA_TX_POWER=20 -D LORA_TX_POWER=20
-D DISPLAY_ROTATION=2 -D DISPLAY_ROTATION=2
build_src_filter = ${ikoka_nrf52_ssd1306_companion.build_src_filter} build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp> +<helpers/nrf52/SerialBLEInterface.cpp>
[env:ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb] [env:ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb]
extends = ikoka_nrf52 extends = ikoka_nrf52
build_flags = ${ikoka_nrf52_ssd1306_companion.build_flags} build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags}
-D LORA_TX_POWER=20 -D LORA_TX_POWER=20
build_src_filter = ${ikoka_nrf52_ssd1306_companion.build_src_filter} build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter}
[env:ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb] [env:ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb]
extends = ikoka_nrf52 extends = ikoka_nrf52
build_flags = ${ikoka_nrf52_ssd1306_companion.build_flags} build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags}
-D LORA_TX_POWER=20 -D LORA_TX_POWER=20
-D DISPLAY_ROTATION=2 -D DISPLAY_ROTATION=2
build_src_filter = ${ikoka_nrf52_ssd1306_companion.build_src_filter} build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter}
[env:ikoka_handheld_nrf_e22_30dbm_repeater] [env:ikoka_handheld_nrf_e22_30dbm_repeater]
extends = ikoka_nrf52 extends = ikoka_nrf52
build_flags = build_flags =
${ikoka_nrf52.build_flags} ${ikoka_handheld_nrf.build_flags}
-D ADVERT_NAME='"ikoka_handheld Repeater"' -D ADVERT_NAME='"ikoka_handheld Repeater"'
-D ADVERT_LAT=0.0 -D ADVERT_LAT=0.0
-D ADVERT_LON=0.0 -D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"' -D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50 -D MAX_NEIGHBOURS=50
-D LORA_TX_POWER=20 -D LORA_TX_POWER=20
build_src_filter = ${ikoka_nrf52.build_src_filter} build_src_filter = ${ikoka_handheld_nrf.build_src_filter}
+<../examples/simple_repeater/*.cpp> +<../examples/simple_repeater/*.cpp>
[env:ikoka_handheld_nrf_e22_30dbm_room_server] [env:ikoka_handheld_nrf_e22_30dbm_room_server]
extends = ikoka_nrf52 extends = ikoka_nrf52
build_flags = build_flags =
${ikoka_nrf52.build_flags} ${ikoka_handheld_nrf.build_flags}
-D ADVERT_NAME='"ikoka_handheld Room"' -D ADVERT_NAME='"ikoka_handheld Room"'
-D ADVERT_LAT=0.0 -D ADVERT_LAT=0.0
-D ADVERT_LON=0.0 -D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"' -D ADMIN_PASSWORD='"password"'
-D LORA_TX_POWER=20 -D LORA_TX_POWER=20
build_src_filter = ${ikoka_nrf52.build_src_filter} build_src_filter = ${ikoka_handheld_nrf.build_src_filter}
+<../examples/simple_room_server/*.cpp> +<../examples/simple_room_server/*.cpp>

View File

@@ -1,151 +1,124 @@
[nrf52840_xiao] [ikoka_nano_nrf]
extends = nrf52_base extends = nrf52_base
platform_packages =
toolchain-gccarmnoneeabi@~1.100301.0
framework-arduinoadafruitnrf52
board = seeed-xiao-afruitnrf52-nrf52840 board = seeed-xiao-afruitnrf52-nrf52840
board_build.ldscript = boards/nrf52840_s140_v7.ld board_build.ldscript = boards/nrf52840_s140_v7.ld
build_flags = ${nrf52_base.build_flags} build_flags = ${nrf52_base.build_flags}
${sensor_base.build_flags}
-D NRF52_PLATFORM -D XIAO_NRF52 -D NRF52_PLATFORM -D XIAO_NRF52
-I lib/nrf52/s140_nrf52_7.3.0_API/include -I lib/nrf52/s140_nrf52_7.3.0_API/include
-I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52 -I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52
lib_ignore =
BluetoothOTA
lvgl
lib5b4
lib_deps =
${nrf52_base.lib_deps}
rweather/Crypto @ ^0.4.0
adafruit/Adafruit INA3221 Library @ ^1.0.1
adafruit/Adafruit INA219 @ ^1.2.3
adafruit/Adafruit AHTX0 @ ^2.0.5
adafruit/Adafruit BME280 Library @ ^2.3.0
adafruit/Adafruit SSD1306 @ ^2.5.13
[ikoka_nano_nrf_baseboard]
extends = nrf52840_xiao
;board_build.ldscript = boards/nrf52840_s140_v7.ld
build_flags = ${nrf52840_xiao.build_flags}
-D P_LORA_TX_LED=11
-I variants/ikoka_nano_nrf -I variants/ikoka_nano_nrf
-I src/helpers/nrf52 -I src/helpers/nrf52
-D P_LORA_TX_LED=11
-D DISPLAY_CLASS=NullDisplayDriver -D DISPLAY_CLASS=NullDisplayDriver
-D RADIO_CLASS=CustomSX1262 -D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper -D WRAPPER_CLASS=CustomSX1262Wrapper
-D P_LORA_DIO_1=D1 -D P_LORA_DIO_1=D1
; -D P_LORA_BUSY=D3 -D P_LORA_BUSY=D2
-D P_LORA_BUSY=D2 ; specific to ikoka nano variant. -D P_LORA_RESET=D3
; -D P_LORA_RESET=D2 -D P_LORA_NSS=D0
-D P_LORA_RESET=D3 ; specific to ikoka nano variant.
; -D P_LORA_NSS=D4
-D P_LORA_NSS=D0 ; specific to ikoka nano variant.
; -D SX126X_RXEN=D5
-D SX126X_RXEN=D7 -D SX126X_RXEN=D7
-D SX126X_TXEN=RADIOLIB_NC -D SX126X_TXEN=RADIOLIB_NC
-D SX126X_DIO2_AS_RF_SWITCH=1 -D SX126X_DIO2_AS_RF_SWITCH=1
-D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=140 -D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1 -D SX126X_RX_BOOSTED_GAIN=1
-D PIN_WIRE_SCL=5 ; specific to ikoka nano variant. -D PIN_WIRE_SCL=5
-D PIN_WIRE_SDA=4 ; specific to ikoka nano variant. -D PIN_WIRE_SDA=4
-D ENV_INCLUDE_AHTX0=1 -UENV_INCLUDE_GPS
-D ENV_INCLUDE_BME280=1
-D ENV_INCLUDE_INA3221=1
-D ENV_INCLUDE_INA219=1
debug_tool = jlink debug_tool = jlink
upload_protocol = nrfutil upload_protocol = nrfutil
lib_deps = ${nrf52_base.lib_deps}
${sensor_base.lib_deps}
;;; abstracted hardware variants
[ikoka_nano_nrf_e22_22dbm] [ikoka_nano_nrf_e22_22dbm]
extends = ikoka_nano_nrf_baseboard extends = ikoka_nano_nrf
; No PA in this model, full 22dBm ; No PA in this model, full 22dBm
build_flags = build_flags =
${ikoka_nano_nrf_baseboard.build_flags} ${ikoka_nano_nrf.build_flags}
-D MANUFACTURER_STRING='"Ikoka Nano-E22-22dBm (Xiao_nrf52)"' -D MANUFACTURER_STRING='"Ikoka Nano-E22-22dBm (Xiao_nrf52)"'
-D LORA_TX_POWER=22 -D LORA_TX_POWER=22
build_src_filter = ${nrf52840_xiao.build_src_filter} build_src_filter = ${ikoka_nano_nrf.build_src_filter}
+<helpers/*.cpp> +<helpers/*.cpp>
+<helpers/sensors> +<helpers/sensors>
+<helpers/ui/NullDisplayDriver.cpp> +<helpers/ui/NullDisplayDriver.cpp>
+<../variants/ikoka_nano_nrf> +<../variants/ikoka_nano_nrf>
[ikoka_nano_nrf_e22_30dbm] [ikoka_nano_nrf_e22_30dbm]
extends = ikoka_nano_nrf_baseboard extends = ikoka_nano_nrf
; limit txpower to 20dBm on E22-900M30S. Anything higher will ; limit txpower to 20dBm on E22-900M30S. Anything higher will
; cause distortion in the PA output. 20dBm in -> 30dBm out ; cause distortion in the PA output. 20dBm in -> 30dBm out
build_flags = build_flags =
${ikoka_nano_nrf_baseboard.build_flags} ${ikoka_nano_nrf.build_flags}
-D MANUFACTURER_STRING='"Ikoka Nano-E22-30dBm (Xiao_nrf52)"' -D MANUFACTURER_STRING='"Ikoka Nano-E22-30dBm (Xiao_nrf52)"'
-D LORA_TX_POWER=20 -D LORA_TX_POWER=20
build_src_filter = ${nrf52840_xiao.build_src_filter} build_src_filter = ${ikoka_nano_nrf.build_src_filter}
+<helpers/*.cpp> +<helpers/*.cpp>
+<helpers/sensors> +<helpers/sensors>
+<helpers/ui/NullDisplayDriver.cpp> +<helpers/ui/NullDisplayDriver.cpp>
+<../variants/ikoka_nano_nrf> +<../variants/ikoka_nano_nrf>
[ikoka_nano_nrf_e22_33dbm] [ikoka_nano_nrf_e22_33dbm]
extends = ikoka_nano_nrf_baseboard extends = ikoka_nano_nrf
; limit txpower to 9dBm on E22-900M33S to avoid hardware damage ; limit txpower to 9dBm on E22-900M33S to avoid hardware damage
; to the rf amplifier frontend. 9dBm in -> 33dBm out ; to the rf amplifier frontend. 9dBm in -> 33dBm out
build_flags = build_flags =
${ikoka_nano_nrf_baseboard.build_flags} ${ikoka_nano_nrf.build_flags}
-D MANUFACTURER_STRING='"Ikoka Nano-E22-33dBm (Xiao_nrf52)"' -D MANUFACTURER_STRING='"Ikoka Nano-E22-33dBm (Xiao_nrf52)"'
-D LORA_TX_POWER=9 -D LORA_TX_POWER=9
build_src_filter = ${nrf52840_xiao.build_src_filter} build_src_filter = ${ikoka_nano_nrf.build_src_filter}
+<helpers/*.cpp> +<helpers/*.cpp>
+<helpers/sensors> +<helpers/sensors>
+<helpers/ui/NullDisplayDriver.cpp> +<helpers/ui/NullDisplayDriver.cpp>
+<../variants/ikoka_nano_nrf> +<../variants/ikoka_nano_nrf>
;;; abstracted firmware roles
[ikoka_nano_nrf_companion_radio_ble] [ikoka_nano_nrf_companion_radio_ble]
extends = ikoka_nano_nrf_baseboard extends = ikoka_nano_nrf
board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld
board_upload.maximum_size = 708608 board_upload.maximum_size = 708608
build_flags = build_flags =
${ikoka_nano_nrf_baseboard.build_flags} ${ikoka_nano_nrf.build_flags}
-D MAX_CONTACTS=350 -D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40 -D MAX_GROUP_CHANNELS=40
-D BLE_PIN_CODE=123456 -D BLE_PIN_CODE=123456
-D OFFLINE_QUEUE_SIZE=256 -D OFFLINE_QUEUE_SIZE=256
-I examples/companion_radio/ui-new -I examples/companion_radio/ui-new
-D QSPIFLASH=1
; -D BLE_DEBUG_LOGGING=1 ; -D BLE_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1 ; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
build_src_filter = ${ikoka_nano_nrf_baseboard.build_src_filter} build_src_filter = ${ikoka_nano_nrf.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp> +<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp> +<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp> +<../examples/companion_radio/ui-new/*.cpp>
lib_deps = lib_deps =
${ikoka_nano_nrf_baseboard.lib_deps} ${ikoka_nano_nrf.lib_deps}
densaugeo/base64 @ ~1.4.0 densaugeo/base64 @ ~1.4.0
[ikoka_nano_nrf_companion_radio_usb] [ikoka_nano_nrf_companion_radio_usb]
extends = ikoka_nano_nrf_baseboard extends = ikoka_nano_nrf
board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld
board_upload.maximum_size = 708608 board_upload.maximum_size = 708608
build_flags = build_flags =
${ikoka_nano_nrf_baseboard.build_flags} ${ikoka_nano_nrf.build_flags}
-D MAX_CONTACTS=350 -D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40 -D MAX_GROUP_CHANNELS=40
-I examples/companion_radio/ui-new -I examples/companion_radio/ui-new
-D QSPIFLASH=1
; -D MESH_PACKET_LOGGING=1 ; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
build_src_filter = ${ikoka_nano_nrf_baseboard.build_src_filter} build_src_filter = ${ikoka_nano_nrf.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp> +<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp> +<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp> +<../examples/companion_radio/ui-new/*.cpp>
lib_deps = lib_deps =
${ikoka_nano_nrf_baseboard.lib_deps} ${ikoka_nano_nrf.lib_deps}
densaugeo/base64 @ ~1.4.0 densaugeo/base64 @ ~1.4.0
[ikoka_nano_nrf_repeater] [ikoka_nano_nrf_repeater]
extends = ikoka_nano_nrf_baseboard extends = ikoka_nano_nrf
build_flags = build_flags =
${ikoka_nano_nrf_baseboard.build_flags} ${ikoka_nano_nrf.build_flags}
-D ADVERT_NAME='"Ikoka Nano Repeater"' -D ADVERT_NAME='"Ikoka Nano Repeater"'
-D ADVERT_LAT=0.0 -D ADVERT_LAT=0.0
-D ADVERT_LON=0.0 -D ADVERT_LON=0.0
@@ -153,26 +126,23 @@ build_flags =
-D MAX_NEIGHBOURS=50 -D MAX_NEIGHBOURS=50
; -D MESH_PACKET_LOGGING=1 ; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
build_src_filter = ${ikoka_nano_nrf_baseboard.build_src_filter} build_src_filter = ${ikoka_nano_nrf.build_src_filter}
+<../examples/simple_repeater/*.cpp> +<../examples/simple_repeater/*.cpp>
[ikoka_nano_nrf_room_server] [ikoka_nano_nrf_room_server]
extends = ikoka_nano_nrf_baseboard extends = ikoka_nano_nrf
build_flags = build_flags =
${ikoka_nano_nrf_baseboard.build_flags} ${ikoka_nano_nrf.build_flags}
-D ADVERT_NAME='"Ikoka Nano Room"' -D ADVERT_NAME='"Ikoka Nano Room"'
-D ADVERT_LAT=0.0 -D ADVERT_LAT=0.0
-D ADVERT_LON=0.0 -D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"' -D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1 ; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
build_src_filter = ${ikoka_nano_nrf_baseboard.build_src_filter} build_src_filter = ${ikoka_nano_nrf.build_src_filter}
+<../examples/simple_room_server/*.cpp> +<../examples/simple_room_server/*.cpp>
;;; hardware + firmware variants
;;; 22dBm EBYTE E22-900M22 variants ;;; 22dBm EBYTE E22-900M22 variants
[env:ikoka_nano_nrf_22dbm_companion_radio_usb] [env:ikoka_nano_nrf_22dbm_companion_radio_usb]
extends = extends =
ikoka_nano_nrf_e22_22dbm ikoka_nano_nrf_e22_22dbm
@@ -219,7 +189,6 @@ build_src_filter =
;;; 30dBm EBYTE E22-900M30 variants ;;; 30dBm EBYTE E22-900M30 variants
[env:ikoka_nano_nrf_30dbm_companion_radio_usb] [env:ikoka_nano_nrf_30dbm_companion_radio_usb]
extends = extends =
ikoka_nano_nrf_e22_30dbm ikoka_nano_nrf_e22_30dbm
@@ -266,7 +235,6 @@ build_src_filter =
;;; 33dBm EBYTE E22-900M33 variants ;;; 33dBm EBYTE E22-900M33 variants
[env:ikoka_nano_nrf_33dbm_companion_radio_usb] [env:ikoka_nano_nrf_33dbm_companion_radio_usb]
extends = extends =
ikoka_nano_nrf_e22_33dbm ikoka_nano_nrf_e22_33dbm

View File

@@ -1,34 +1,15 @@
[nrf52840_xiao] [ikoka_stick_nrf]
extends = nrf52_base extends = nrf52_base
platform_packages =
toolchain-gccarmnoneeabi@~1.100301.0
framework-arduinoadafruitnrf52
board = seeed-xiao-afruitnrf52-nrf52840 board = seeed-xiao-afruitnrf52-nrf52840
board_build.ldscript = boards/nrf52840_s140_v7.ld board_build.ldscript = boards/nrf52840_s140_v7.ld
build_flags = ${nrf52_base.build_flags} build_flags = ${nrf52_base.build_flags}
${sensor_base.build_flags}
-D NRF52_PLATFORM -D XIAO_NRF52 -D NRF52_PLATFORM -D XIAO_NRF52
-I lib/nrf52/s140_nrf52_7.3.0_API/include -I lib/nrf52/s140_nrf52_7.3.0_API/include
-I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52 -I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52
lib_ignore =
BluetoothOTA
lvgl
lib5b4
lib_deps =
${nrf52_base.lib_deps}
rweather/Crypto @ ^0.4.0
adafruit/Adafruit INA3221 Library @ ^1.0.1
adafruit/Adafruit INA219 @ ^1.2.3
adafruit/Adafruit AHTX0 @ ^2.0.5
adafruit/Adafruit BME280 Library @ ^2.3.0
adafruit/Adafruit SSD1306 @ ^2.5.13
[ikoka_stick_nrf_baseboard]
extends = nrf52840_xiao
;board_build.ldscript = boards/nrf52840_s140_v7.ld
build_flags = ${nrf52840_xiao.build_flags}
-D P_LORA_TX_LED=11
-I variants/ikoka_stick_nrf -I variants/ikoka_stick_nrf
-I src/helpers/nrf52 -I src/helpers/nrf52
-D P_LORA_TX_LED=11
-D DISPLAY_CLASS=SSD1306Display -D DISPLAY_CLASS=SSD1306Display
-D DISPLAY_ROTATION=2 -D DISPLAY_ROTATION=2
-D RADIO_CLASS=CustomSX1262 -D RADIO_CLASS=CustomSX1262
@@ -46,24 +27,18 @@ build_flags = ${nrf52840_xiao.build_flags}
-D PIN_USER_BTN=0 -D PIN_USER_BTN=0
-D PIN_WIRE_SCL=7 -D PIN_WIRE_SCL=7
-D PIN_WIRE_SDA=6 -D PIN_WIRE_SDA=6
-D ENV_INCLUDE_AHTX0=1 -UENV_INCLUDE_GPS
-D ENV_INCLUDE_BME280=1 lib_deps = ${nrf52_base.lib_deps}
-D ENV_INCLUDE_INA3221=1 ${sensor_base.lib_deps}
-D ENV_INCLUDE_INA219=1
debug_tool = jlink
upload_protocol = nrfutil
;;; abstracted hardware variants
[ikoka_stick_nrf_e22_22dbm] [ikoka_stick_nrf_e22_22dbm]
extends = ikoka_stick_nrf_baseboard extends = ikoka_stick_nrf
; No PA in this model, full 22dBm ; No PA in this model, full 22dBm
build_flags = build_flags =
${ikoka_stick_nrf_baseboard.build_flags} ${ikoka_stick_nrf.build_flags}
-D MANUFACTURER_STRING='"Ikoka Stick-E22-22dBm (Xiao_nrf52)"' -D MANUFACTURER_STRING='"Ikoka Stick-E22-22dBm (Xiao_nrf52)"'
-D LORA_TX_POWER=22 -D LORA_TX_POWER=22
build_src_filter = ${nrf52840_xiao.build_src_filter} build_src_filter = ${ikoka_stick_nrf.build_src_filter}
+<helpers/*.cpp> +<helpers/*.cpp>
+<helpers/sensors> +<helpers/sensors>
+<helpers/ui/MomentaryButton.cpp> +<helpers/ui/MomentaryButton.cpp>
@@ -71,14 +46,14 @@ build_src_filter = ${nrf52840_xiao.build_src_filter}
+<../variants/ikoka_stick_nrf> +<../variants/ikoka_stick_nrf>
[ikoka_stick_nrf_e22_30dbm] [ikoka_stick_nrf_e22_30dbm]
extends = ikoka_stick_nrf_baseboard extends = ikoka_stick_nrf
; limit txpower to 20dBm on E22-900M30S. Anything higher will ; limit txpower to 20dBm on E22-900M30S. Anything higher will
; cause distortion in the PA output. 20dBm in -> 30dBm out ; cause distortion in the PA output. 20dBm in -> 30dBm out
build_flags = build_flags =
${ikoka_stick_nrf_baseboard.build_flags} ${ikoka_stick_nrf.build_flags}
-D MANUFACTURER_STRING='"Ikoka Stick-E22-30dBm (Xiao_nrf52)"' -D MANUFACTURER_STRING='"Ikoka Stick-E22-30dBm (Xiao_nrf52)"'
-D LORA_TX_POWER=20 -D LORA_TX_POWER=20
build_src_filter = ${nrf52840_xiao.build_src_filter} build_src_filter = ${ikoka_stick_nrf.build_src_filter}
+<helpers/*.cpp> +<helpers/*.cpp>
+<helpers/sensors> +<helpers/sensors>
+<helpers/ui/MomentaryButton.cpp> +<helpers/ui/MomentaryButton.cpp>
@@ -86,14 +61,14 @@ build_src_filter = ${nrf52840_xiao.build_src_filter}
+<../variants/ikoka_stick_nrf> +<../variants/ikoka_stick_nrf>
[ikoka_stick_nrf_e22_33dbm] [ikoka_stick_nrf_e22_33dbm]
extends = ikoka_stick_nrf_baseboard extends = ikoka_stick_nrf
; limit txpower to 9dBm on E22-900M33S to avoid hardware damage ; limit txpower to 9dBm on E22-900M33S to avoid hardware damage
; to the rf amplifier frontend. 9dBm in -> 33dBm out ; to the rf amplifier frontend. 9dBm in -> 33dBm out
build_flags = build_flags =
${ikoka_stick_nrf_baseboard.build_flags} ${ikoka_stick_nrf.build_flags}
-D MANUFACTURER_STRING='"Ikoka Stick-E22-33dBm (Xiao_nrf52)"' -D MANUFACTURER_STRING='"Ikoka Stick-E22-33dBm (Xiao_nrf52)"'
-D LORA_TX_POWER=9 -D LORA_TX_POWER=9
build_src_filter = ${nrf52840_xiao.build_src_filter} build_src_filter = ${ikoka_stick_nrf.build_src_filter}
+<helpers/*.cpp> +<helpers/*.cpp>
+<helpers/sensors> +<helpers/sensors>
+<helpers/ui/MomentaryButton.cpp> +<helpers/ui/MomentaryButton.cpp>
@@ -103,50 +78,52 @@ build_src_filter = ${nrf52840_xiao.build_src_filter}
;;; abstracted firmware roles ;;; abstracted firmware roles
[ikoka_stick_nrf_companion_radio_ble] [ikoka_stick_nrf_companion_radio_ble]
extends = ikoka_stick_nrf_baseboard extends = ikoka_stick_nrf
board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld
board_upload.maximum_size = 708608 board_upload.maximum_size = 708608
build_flags = build_flags =
${ikoka_stick_nrf_baseboard.build_flags} ${ikoka_stick_nrf.build_flags}
-D MAX_CONTACTS=350 -D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40 -D MAX_GROUP_CHANNELS=40
-D BLE_PIN_CODE=123456 -D BLE_PIN_CODE=123456
-D OFFLINE_QUEUE_SIZE=256 -D OFFLINE_QUEUE_SIZE=256
-I examples/companion_radio/ui-new -I examples/companion_radio/ui-new
-D QSPIFLASH=1
; -D BLE_DEBUG_LOGGING=1 ; -D BLE_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1 ; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter} build_src_filter = ${ikoka_stick_nrf.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp> +<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp> +<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp> +<../examples/companion_radio/ui-new/*.cpp>
lib_deps = lib_deps =
${ikoka_stick_nrf_baseboard.lib_deps} ${ikoka_stick_nrf.lib_deps}
densaugeo/base64 @ ~1.4.0 densaugeo/base64 @ ~1.4.0
[ikoka_stick_nrf_companion_radio_usb] [ikoka_stick_nrf_companion_radio_usb]
extends = ikoka_stick_nrf_baseboard extends = ikoka_stick_nrf
board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld
board_upload.maximum_size = 708608 board_upload.maximum_size = 708608
build_flags = build_flags =
${ikoka_stick_nrf_baseboard.build_flags} ${ikoka_stick_nrf.build_flags}
-D MAX_CONTACTS=350 -D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40 -D MAX_GROUP_CHANNELS=40
-I examples/companion_radio/ui-new -I examples/companion_radio/ui-new
-D QSPIFLASH=1
; -D MESH_PACKET_LOGGING=1 ; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter} build_src_filter = ${ikoka_stick_nrf.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp> +<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp> +<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp> +<../examples/companion_radio/ui-new/*.cpp>
lib_deps = lib_deps =
${ikoka_stick_nrf_baseboard.lib_deps} ${ikoka_stick_nrf.lib_deps}
densaugeo/base64 @ ~1.4.0 densaugeo/base64 @ ~1.4.0
[ikoka_stick_nrf_repeater] [ikoka_stick_nrf_repeater]
extends = ikoka_stick_nrf_baseboard extends = ikoka_stick_nrf
build_flags = build_flags =
${ikoka_stick_nrf_baseboard.build_flags} ${ikoka_stick_nrf.build_flags}
-D ADVERT_NAME='"Ikoka Stick Repeater"' -D ADVERT_NAME='"Ikoka Stick Repeater"'
-D ADVERT_LAT=0.0 -D ADVERT_LAT=0.0
-D ADVERT_LON=0.0 -D ADVERT_LON=0.0
@@ -154,21 +131,21 @@ build_flags =
-D MAX_NEIGHBOURS=50 -D MAX_NEIGHBOURS=50
; -D MESH_PACKET_LOGGING=1 ; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter} build_src_filter = ${ikoka_stick_nrf.build_src_filter}
+<helpers/ui/SSD1306Display.cpp> +<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater/*.cpp> +<../examples/simple_repeater/*.cpp>
[ikoka_stick_nrf_room_server] [ikoka_stick_nrf_room_server]
extends = ikoka_stick_nrf_baseboard extends = ikoka_stick_nrf
build_flags = build_flags =
${ikoka_stick_nrf_baseboard.build_flags} ${ikoka_stick_nrf.build_flags}
-D ADVERT_NAME='"Ikoka Stick Room"' -D ADVERT_NAME='"Ikoka Stick Room"'
-D ADVERT_LAT=0.0 -D ADVERT_LAT=0.0
-D ADVERT_LON=0.0 -D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"' -D ADMIN_PASSWORD='"password"'
; -D MESH_PACKET_LOGGING=1 ; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1 ; -D MESH_DEBUG=1
build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter} build_src_filter = ${ikoka_stick_nrf.build_src_filter}
+<../examples/simple_room_server/*.cpp> +<../examples/simple_room_server/*.cpp>
;;; hardware + firmware variants ;;; hardware + firmware variants

View File

@@ -138,8 +138,6 @@ build_flags =
-D DISPLAY_CLASS=SSD1306Display -D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=350 -D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40 -D MAX_GROUP_CHANNELS=40
-D MESH_PACKET_LOGGING=1
-D MESH_DEBUG=1
build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter}
+<helpers/ui/SSD1306Display.cpp> +<helpers/ui/SSD1306Display.cpp>
+<helpers/ui/MomentaryButton.cpp> +<helpers/ui/MomentaryButton.cpp>

View File

@@ -0,0 +1,32 @@
#include <Arduino.h>
#include <Wire.h>
#include "RAK3401Board.h"
void RAK3401Board::begin() {
NRF52BoardDCDC::begin();
pinMode(PIN_VBAT_READ, INPUT);
#ifdef PIN_USER_BTN
pinMode(PIN_USER_BTN, INPUT_PULLUP);
#endif
#ifdef PIN_USER_BTN_ANA
pinMode(PIN_USER_BTN_ANA, INPUT_PULLUP);
#endif
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
#endif
Wire.begin();
pinMode(PIN_3V3_EN, OUTPUT);
digitalWrite(PIN_3V3_EN, HIGH);
#ifdef P_LORA_PA_EN
// Initialize RAK13302 1W LoRa transceiver module PA control pin
pinMode(P_LORA_PA_EN, OUTPUT);
digitalWrite(P_LORA_PA_EN, LOW); // Start with PA disabled
delay(10); // Allow PA module to initialize
#endif
}

View File

@@ -0,0 +1,70 @@
#pragma once
#include <MeshCore.h>
#include <Arduino.h>
#include <helpers/NRF52Board.h>
// LoRa radio module pins for RAK13302
#define P_LORA_SCLK 3
#define P_LORA_MISO 29
#define P_LORA_MOSI 30
#define P_LORA_NSS 26
#define P_LORA_DIO_1 10
#define P_LORA_BUSY 9
#define P_LORA_RESET 4
#ifndef P_LORA_PA_EN
#define P_LORA_PA_EN 31
#endif
//#define PIN_GPS_SDA 13 //GPS SDA pin (output option)
//#define PIN_GPS_SCL 14 //GPS SCL pin (output option)
// #define PIN_GPS_TX 16 //GPS TX pin
// #define PIN_GPS_RX 15 //GPS RX pin
#define PIN_GPS_1PPS 17 //GPS PPS pin
#define GPS_BAUD_RATE 9600
#define GPS_ADDRESS 0x42 //i2c address for GPS
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// built-ins
#define PIN_VBAT_READ 5
#define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000)
#define PIN_3V3_EN (34)
#define WB_IO2 PIN_3V3_EN
class RAK3401Board : public NRF52BoardDCDC, public NRF52BoardOTA {
public:
RAK3401Board() : NRF52BoardOTA("RAK3401_OTA") {}
void begin();
#define BATTERY_SAMPLES 8
uint16_t getBattMilliVolts() override {
analogReadResolution(12);
uint32_t raw = 0;
for (int i = 0; i < BATTERY_SAMPLES; i++) {
raw += analogRead(PIN_VBAT_READ);
}
raw = raw / BATTERY_SAMPLES;
return (ADC_MULTIPLIER * raw) / 4096;
}
const char* getManufacturerName() const override {
return "RAK 3401";
}
#ifdef P_LORA_PA_EN
void onBeforeTransmit() override {
digitalWrite(P_LORA_PA_EN, HIGH); // Enable PA before transmission
}
void onAfterTransmit() override {
digitalWrite(P_LORA_PA_EN, LOW); // Disable PA after transmission to save power
}
#endif
};

View File

@@ -0,0 +1,127 @@
[rak3401]
extends = nrf52_base
board = rak3401
board_check = true
build_flags = ${nrf52_base.build_flags}
${sensor_base.build_flags}
-I variants/rak3401
-D RAK_3401
-D RAK13302
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${nrf52_base.build_src_filter}
+<../variants/rak3401>
+<helpers/sensors>
+<helpers/ui/SSD1306Display.cpp>
+<helpers/ui/MomentaryButton.cpp>
lib_deps =
${nrf52_base.lib_deps}
${sensor_base.lib_deps}
adafruit/Adafruit SSD1306 @ ^2.5.13
sparkfun/SparkFun u-blox GNSS Arduino Library@^2.2.27
[env:RAK_3401_repeater]
extends = rak3401
build_flags =
${rak3401.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"RAK3401 1W Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
;-D MESH_PACKET_LOGGING=1
;-D MESH_DEBUG=1
build_src_filter = ${rak3401.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_repeater>
[env:RAK_3401_room_server]
extends = rak3401
build_flags =
${rak3401.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"Test Room"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
;-D MESH_PACKET_LOGGING=1
;-D MESH_DEBUG=1
build_src_filter = ${rak3401.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_room_server>
[env:RAK_3401_companion_radio_usb]
extends = rak3401
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
board_upload.maximum_size = 712704
build_flags =
${rak3401.build_flags}
-I examples/companion_radio/ui-new
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
build_src_filter = ${rak3401.build_src_filter}
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
${rak3401.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:RAK_3401_companion_radio_ble]
extends = rak3401
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
board_upload.maximum_size = 712704
build_flags =
${rak3401.build_flags}
-I examples/companion_radio/ui-new
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D BLE_PIN_CODE=123456
-D BLE_DEBUG_LOGGING=1
-D OFFLINE_QUEUE_SIZE=256
;-D MESH_PACKET_LOGGING=1
;-D MESH_DEBUG=1
build_src_filter = ${rak3401.build_src_filter}
+<helpers/nrf52/SerialBLEInterface.cpp>
+<../examples/companion_radio/*.cpp>
+<../examples/companion_radio/ui-new/*.cpp>
lib_deps =
${rak3401.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:RAK_3401_terminal_chat]
extends = rak3401
build_flags =
${rak3401.build_flags}
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
;-D MESH_PACKET_LOGGING=1
;-D MESH_DEBUG=1
build_src_filter = ${rak3401.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${rak3401.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:RAK_3401_sensor]
extends = rak3401
build_flags =
${rak3401.build_flags}
-D DISPLAY_CLASS=SSD1306Display
-D ADVERT_NAME='"RAK3401 Sensor"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
;-D MESH_PACKET_LOGGING=1
;-D MESH_DEBUG=1
build_src_filter = ${rak3401.build_src_filter}
+<helpers/ui/SSD1306Display.cpp>
+<../examples/simple_sensor>

View File

@@ -0,0 +1,58 @@
#include <Arduino.h>
#include "target.h"
#include <helpers/ArduinoHelpers.h>
RAK3401Board board;
#ifndef PIN_USER_BTN
#define PIN_USER_BTN (-1)
#endif
#ifdef DISPLAY_CLASS
DISPLAY_CLASS display;
MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true);
#if defined(PIN_USER_BTN_ANA)
MomentaryButton analog_btn(PIN_USER_BTN_ANA, 1000, 20);
#endif
#endif
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
WRAPPER_CLASS radio_driver(radio, board);
VolatileRTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
#if ENV_INCLUDE_GPS
#include <helpers/sensors/MicroNMEALocationProvider.h>
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1);
EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea);
#else
EnvironmentSensorManager sensors;
#endif
bool radio_init() {
rtc_clock.begin(Wire);
return radio.std_init(&SPI);
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}

30
variants/rak3401/target.h Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/radiolib/RadioLibWrappers.h>
#include <RAK3401Board.h>
#include <helpers/radiolib/CustomSX1262Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
#include <helpers/sensors/EnvironmentSensorManager.h>
#ifdef DISPLAY_CLASS
#include <helpers/ui/SSD1306Display.h>
extern DISPLAY_CLASS display;
#include <helpers/ui/MomentaryButton.h>
extern MomentaryButton user_btn;
#if defined(PIN_USER_BTN_ANA)
extern MomentaryButton analog_btn;
#endif
#endif
extern RAK3401Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
extern EnvironmentSensorManager sensors;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

View File

@@ -0,0 +1,52 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "variant.h"
#include "wiring_constants.h"
#include "wiring_digital.h"
#include "nrf.h"
const uint32_t g_ADigitalPinMap[] =
{
// P0
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ,
8 , 9 , 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
// P1
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47
};
void initVariant()
{
// LED1 & LED2
pinMode(PIN_LED1, OUTPUT);
ledOff(PIN_LED1);
pinMode(PIN_LED2, OUTPUT);
ledOff(PIN_LED2);
// 3V3 Power Rail
pinMode(PIN_3V3_EN, OUTPUT);
digitalWrite(PIN_3V3_EN, HIGH);
}

199
variants/rak3401/variant.h Normal file
View File

@@ -0,0 +1,199 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _VARIANT_RAK3401_
#define _VARIANT_RAK3401_
#define RAK4630
/** Master clock frequency */
#define VARIANT_MCK (64000000ul)
#define USE_LFXO // Board uses 32khz crystal for LF
// define USE_LFRC // Board uses RC for LF
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "WVariant.h"
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
// Number of pins defined in PinDescription array
#define PINS_COUNT (48)
#define NUM_DIGITAL_PINS (48)
#define NUM_ANALOG_INPUTS (6)
#define NUM_ANALOG_OUTPUTS (0)
// LEDs
#define PIN_LED1 (35)
#define PIN_LED2 (36)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1
#define LED_BLUE PIN_LED2
#define LED_STATE_ON 1 // State when LED is litted
/*
* Analog pins
*/
#define PIN_A0 (5) //(3)
#define PIN_A1 (31) //(4)
#define PIN_A2 (28)
#define PIN_A3 (29)
#define PIN_A4 (30)
#define PIN_A5 (31)
#define PIN_A6 (0xff)
#define PIN_A7 (0xff)
static const uint8_t A0 = PIN_A0;
static const uint8_t A1 = PIN_A1;
static const uint8_t A2 = PIN_A2;
static const uint8_t A3 = PIN_A3;
static const uint8_t A4 = PIN_A4;
static const uint8_t A5 = PIN_A5;
static const uint8_t A6 = PIN_A6;
static const uint8_t A7 = PIN_A7;
#define ADC_RESOLUTION 14
// Other pins
#define WB_I2C1_SDA (13) // SENSOR_SLOT IO_SLOT
#define WB_I2C1_SCL (14) // SENSOR_SLOT IO_SLOT
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
/*
* Serial interfaces
*/
// TXD1 RXD1 on Base Board
#define PIN_SERIAL1_RX (15)
#define PIN_SERIAL1_TX (16)
// Connected to Jlink CDC
#define PIN_SERIAL2_RX (8)
#define PIN_SERIAL2_TX (6)
/*
* SPI Interfaces
*/
#define SPI_INTERFACES_COUNT 2
#define PIN_SPI_MISO (45)
#define PIN_SPI_MOSI (44)
#define PIN_SPI_SCK (43)
#define PIN_SPI1_MISO (29)
#define PIN_SPI1_MOSI (30)
#define PIN_SPI1_SCK (3)
static const uint8_t SS = 42;
static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK;
/*
* Wire Interfaces
*/
#define WIRE_INTERFACES_COUNT 1
#define PIN_WIRE_SDA (WB_I2C1_SDA)
#define PIN_WIRE_SCL (WB_I2C1_SCL)
// QSPI Pins
// QSPI occupied by GPIO's
#define PIN_QSPI_SCK 3
#define PIN_QSPI_CS 26
#define PIN_QSPI_IO0 30
#define PIN_QSPI_IO1 29
#define PIN_QSPI_IO2 28
#define PIN_QSPI_IO3 2
// On-board QSPI Flash
// No onboard flash
#define EXTERNAL_FLASH_DEVICES IS25LP080D
#define EXTERNAL_FLASH_USE_QSPI
#define P_LORA_SCK PIN_SPI1_SCK
#define P_LORA_MISO PIN_SPI1_MISO
#define P_LORA_MOSI PIN_SPI1_MOSI
#define P_LORA_CS 26
#define USE_SX1262
#define SX126X_CS (26)
#define SX126X_DIO1 (10)
#define SX126X_BUSY (9)
#define SX126X_RESET (4)
#define SX126X_POWER_EN (21)
// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// enables 3.3V periphery like GPS or IO Module
// Do not toggle this for GPS power savings
#define PIN_3V3_EN (34)
#define WB_IO2 PIN_3V3_EN
// RAK1910 GPS module
// If using the wisblock GPS module and pluged into Port A on WisBlock base
// IO1 is hooked to PPS (pin 12 on header) = gpio 17
// IO2 is hooked to GPS RESET = gpio 34, but it can not be used to this because IO2 is ALSO used to control 3V3_S power (1 is on).
// Therefore must be 1 to keep peripherals powered
// Power is on the controllable 3V3_S rail
#define PIN_GPS_PPS (17) // Pulse per second input from the GPS
#define PIN_GPS_RX PIN_SERIAL1_RX
#define PIN_GPS_TX PIN_SERIAL1_TX
// Battery
// The battery sense is hooked to pin A0 (5)
#define BATTERY_PIN PIN_A0
// and has 12 bit resolution
#define BATTERY_SENSE_RESOLUTION_BITS 12
#define BATTERY_SENSE_RESOLUTION 4096.0
#undef AREF_VOLTAGE
#define AREF_VOLTAGE 3.0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER 1.73
#define HAS_RTC 1
#define RAK_4631 1
#ifdef __cplusplus
}
#endif
/*----------------------------------------------------------------------------
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif