mirror of https://github.com/meshcore-dev/MeshCore
80 changed files with 3739 additions and 1928 deletions
@ -0,0 +1,84 @@ |
|||||
|
# .clang-format |
||||
|
Language: Cpp |
||||
|
AccessModifierOffset: -2 |
||||
|
AlignAfterOpenBracket: Align |
||||
|
AlignConsecutiveAssignments: false |
||||
|
AlignConsecutiveDeclarations: false |
||||
|
AlignConsecutiveMacros: |
||||
|
Enabled: true |
||||
|
AcrossEmptyLines: true |
||||
|
AcrossComments: true |
||||
|
AlignOperands: true |
||||
|
AlignTrailingComments: true |
||||
|
AllowAllParametersOfDeclarationOnNextLine: false |
||||
|
AllowShortBlocksOnASingleLine: false |
||||
|
AllowShortCaseLabelsOnASingleLine: false |
||||
|
AllowShortFunctionsOnASingleLine: Inline |
||||
|
AllowShortIfStatementsOnASingleLine: true |
||||
|
AllowShortLoopsOnASingleLine: false |
||||
|
AlwaysBreakAfterDefinitionReturnType: None |
||||
|
AlwaysBreakAfterReturnType: None |
||||
|
AlwaysBreakBeforeMultilineStrings: false |
||||
|
AlwaysBreakTemplateDeclarations: No |
||||
|
BinPackArguments: true |
||||
|
BinPackParameters: true |
||||
|
BraceWrapping: |
||||
|
AfterClass: false |
||||
|
AfterControlStatement: false |
||||
|
AfterEnum: false |
||||
|
AfterFunction: false |
||||
|
AfterNamespace: false |
||||
|
AfterObjCDeclaration: false |
||||
|
AfterStruct: false |
||||
|
AfterUnion: false |
||||
|
BeforeCatch: true |
||||
|
BeforeElse: true |
||||
|
IndentBraces: false |
||||
|
BreakBeforeBinaryOperators: None |
||||
|
BreakBeforeBraces: Attach |
||||
|
BreakBeforeTernaryOperators: true |
||||
|
BreakConstructorInitializersBeforeComma: false |
||||
|
ColumnLimit: 110 |
||||
|
CommentPragmas: '^ IWYU pragma:' |
||||
|
CompactNamespaces: false |
||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false |
||||
|
Cpp11BracedListStyle: false |
||||
|
DerivePointerAlignment: false |
||||
|
DisableFormat: false |
||||
|
IncludeBlocks: Regroup |
||||
|
IndentCaseLabels: false |
||||
|
IndentPPDirectives: None |
||||
|
IndentWidth: 2 |
||||
|
IndentWrappedFunctionNames: false |
||||
|
KeepEmptyLinesAtTheStartOfBlocks: true |
||||
|
MacroBlockBegin: '' |
||||
|
MacroBlockEnd: '' |
||||
|
MaxEmptyLinesToKeep: 1 |
||||
|
NamespaceIndentation: None |
||||
|
ObjCBinPackProtocolList: Auto |
||||
|
PenaltyBreakBeforeFirstCallParameter: 19 |
||||
|
PenaltyBreakComment: 300 |
||||
|
PenaltyBreakFirstLessLess: 120 |
||||
|
PenaltyBreakString: 1000 |
||||
|
PenaltyExcessCharacter: 100000 |
||||
|
PenaltyReturnTypeOnItsOwnLine: 60 |
||||
|
PointerAlignment: Right |
||||
|
ReflowComments: true |
||||
|
SortIncludes: true |
||||
|
SpaceAfterCStyleCast: false |
||||
|
SpaceAfterTemplateKeyword: true |
||||
|
SpaceBeforeAssignmentOperators: true |
||||
|
SpaceBeforeCtorInitializerColon: true |
||||
|
SpaceBeforeInheritanceColon: true |
||||
|
SpaceBeforeParens: ControlStatements |
||||
|
SpaceInEmptyParentheses: false |
||||
|
SpacesBeforeTrailingComments: 1 |
||||
|
SpacesInAngles: false |
||||
|
SpacesInContainerLiterals: false |
||||
|
SpacesInCStyleCastParentheses: false |
||||
|
SpacesInParentheses: false |
||||
|
SpacesInSquareBrackets: false |
||||
|
Standard: Auto |
||||
|
TabWidth: 2 |
||||
|
UseTab: Never |
||||
|
AlignEscapedNewlines: LeftWithLastLine |
||||
@ -0,0 +1,176 @@ |
|||||
|
# Meshcore payloads |
||||
|
Inside of each [meshcore packet](./packet_structure.md) is a payload, identified by the payload type in the packet header. The types of payloads are: |
||||
|
|
||||
|
* Request (destination/source hashes + MAC). |
||||
|
* Response to REQ or ANON_REQ. |
||||
|
* Plain text message. |
||||
|
* Acknowledgment. |
||||
|
* Node advertisement. |
||||
|
* Group text message (unverified). |
||||
|
* Group datagram (unverified). |
||||
|
* Anonymous request. |
||||
|
* Returned path. |
||||
|
* Custom packet (raw bytes, custom encryption). |
||||
|
|
||||
|
This document defines the structure of each of these payload types |
||||
|
|
||||
|
# Node advertisement |
||||
|
This kind of payload notifies receivers that a node exists, and gives information about the node |
||||
|
|
||||
|
| Field | Size (bytes) | Description | |
||||
|
|---------------|-----------------|----------------------------------------------------------| |
||||
|
| public key | 32 | Ed25519 public key | |
||||
|
| timestamp | 4 | unix timestamp of advertisement | |
||||
|
| signature | 64 | Ed25519 signature of public key, timestamp, and app data | |
||||
|
| appdata | rest of payload | optional, see below | |
||||
|
|
||||
|
Appdata |
||||
|
|
||||
|
| Field | Size (bytes) | Description | |
||||
|
|---------------|-----------------|-------------------------------------------------------| |
||||
|
| flags | 1 | specifies which of the fields are present, see below | |
||||
|
| latitude | 4 | decimal latitude multiplied by 1000000, integer | |
||||
|
| longitude | 4 | decimal longitude multiplied by 1000000, integer | |
||||
|
| feature 1 | 2 | reserved for future use | |
||||
|
| feature 2 | 2 | reserved for future use | |
||||
|
| name | rest of appdata | name of the node | |
||||
|
|
||||
|
Appdata Flags |
||||
|
|
||||
|
| Value | Name | Description | |
||||
|
|--------|-----------|---------------------------------------| |
||||
|
| `0x10` | location | appdata contains lat/long information | |
||||
|
| `0x20` | feature 1 | Reserved for future use. | |
||||
|
| `0x40` | feature 2 | Reserved for future use. | |
||||
|
| `0x80` | name | appdata contains a node name | |
||||
|
|
||||
|
# Acknowledgement |
||||
|
| Field | Size (bytes) | Description | |
||||
|
|----------|--------------|------------------------------------------------------------| |
||||
|
| checksum | 4 | CRC checksum of message timestamp, text, and sender pubkey | |
||||
|
|
||||
|
|
||||
|
# Returned path, request, response, and plain text message |
||||
|
| Field | Size (bytes) | Description | |
||||
|
|------------------|-----------------|------------------------------------------------------| |
||||
|
| destination hash | 1 | first byte of destination node public key | |
||||
|
| source hash | 1 | first byte of source node public key | |
||||
|
| cipher MAC | 2 | MAC for encrypted data in next field | |
||||
|
| ciphertext | rest of payload | encrypted message, see subsections below for details | |
||||
|
|
||||
|
## Returned path |
||||
|
|
||||
|
| Field | Size (bytes) | Description | |
||||
|
|-------------|--------------|----------------------------------------------------------------------------------------------| |
||||
|
| path length | 1 | length of next field | |
||||
|
| path | see above | a list of node hashes (one byte each) describing the route from us to the packet author | |
||||
|
| extra type | 1 | extra, bundled payload type, eg., acknowledgement or response. See packet structure spec | |
||||
|
| extra | rest of data | extra, bundled payload content, follows same format as main content defined by this document | |
||||
|
|
||||
|
## Request |
||||
|
|
||||
|
| Field | Size (bytes) | Description | |
||||
|
|--------------|-----------------|----------------------------| |
||||
|
| timestamp | 4 | send time (unix timestamp) | |
||||
|
| request type | 1 | see below | |
||||
|
| request data | rest of payload | depends on request type | |
||||
|
|
||||
|
Request type |
||||
|
|
||||
|
| Value | Name | Description | |
||||
|
|--------|--------------------|---------------------------------------| |
||||
|
| `0x01` | get status | get status of repeater or room server | |
||||
|
| `0x02` | keepalive | TODO | |
||||
|
| `0x03` | get telemetry data | TODO | |
||||
|
|
||||
|
### Get status |
||||
|
|
||||
|
Gets information about the node, possibly including the following: |
||||
|
|
||||
|
* Battery level (millivolts) |
||||
|
* Current transmit queue length |
||||
|
* Current free queue length |
||||
|
* Last RSSI value |
||||
|
* Number of received packets |
||||
|
* Number of sent packets |
||||
|
* Total airtime (seconds) |
||||
|
* Total uptime (seconds) |
||||
|
* Number of packets sent as flood |
||||
|
* Number of packets sent directly |
||||
|
* Number of packets received as flood |
||||
|
* Number of packets received directly |
||||
|
* Error flags |
||||
|
* Last SNR value |
||||
|
* Number of direct route duplicates |
||||
|
* Number of flood route duplicates |
||||
|
* Number posted (?) |
||||
|
* Number of post pushes (?) |
||||
|
|
||||
|
### Keepalive |
||||
|
|
||||
|
No-op request. |
||||
|
|
||||
|
### Get telemetry data |
||||
|
|
||||
|
Request data about sensors on the node, including battery level. |
||||
|
|
||||
|
## Response |
||||
|
|
||||
|
| Field | Size (bytes) | Description | |
||||
|
|---------|-----------------|-------------| |
||||
|
| tag | 4 | TODO | |
||||
|
| content | rest of payload | TODO | |
||||
|
|
||||
|
## Plain text message |
||||
|
|
||||
|
| Field | Size (bytes) | Description | |
||||
|
|--------------|-----------------|--------------------------------------------------------------| |
||||
|
| timestamp | 4 | send time (unix timestamp) | |
||||
|
| flags + TODO | 1 | first six bits are flags (see below), last two bits are TODO | |
||||
|
| message | rest of payload | the message content, see next table | |
||||
|
|
||||
|
Flags |
||||
|
|
||||
|
| Value | Description | Message content | |
||||
|
|--------|---------------------------|------------------------------------------------------------| |
||||
|
| `0x00` | plain text message | the plain text of the message | |
||||
|
| `0x01` | CLI command | the command text of the message | |
||||
|
| `0x02` | signed plain text message | two bytes of sender prefix, followed by plain text message | |
||||
|
|
||||
|
# Anonymous request |
||||
|
|
||||
|
| Field | Size (bytes) | Description | |
||||
|
|------------------|-----------------|-------------------------------------------| |
||||
|
| destination hash | 1 | first byte of destination node public key | |
||||
|
| public key | 32 | sender's Ed25519 public key | |
||||
|
| cipher MAC | 2 | MAC for encrypted data in next field | |
||||
|
| ciphertext | rest of payload | encrypted message, see below for details | |
||||
|
|
||||
|
Plaintext message |
||||
|
|
||||
|
| Field | Size (bytes) | Description | |
||||
|
|----------------|-----------------|-------------------------------------------------------------------------------| |
||||
|
| timestamp | 4 | send time (unix timestamp) | |
||||
|
| sync timestamp | 4 | for room server, otherwise absent: sender's "sync messages SINCE x" timestamp | |
||||
|
| password | rest of message | password for repeater/room | |
||||
|
|
||||
|
# Group text message / datagram |
||||
|
|
||||
|
| Field | Size (bytes) | Description | |
||||
|
|--------------|-----------------|------------------------------------------| |
||||
|
| channel hash | 1 | TODO | |
||||
|
| cipher MAC | 2 | MAC for encrypted data in next field | |
||||
|
| ciphertext | rest of payload | encrypted message, see below for details | |
||||
|
|
||||
|
Plaintext for text message |
||||
|
|
||||
|
| Field | Size (bytes) | Description | |
||||
|
|-----------|-----------------|----------------------------------| |
||||
|
| timestamp | 4 | send time (unix timestamp) | |
||||
|
| content | rest of message | plain group text message content | |
||||
|
|
||||
|
TODO: describe what datagram looks like |
||||
|
|
||||
|
# Custom packet |
||||
|
|
||||
|
Custom packets have no defined format. |
||||
@ -0,0 +1,391 @@ |
|||||
|
#include <Arduino.h> |
||||
|
#include "DataStore.h" |
||||
|
|
||||
|
DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _clock(&clock), |
||||
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
||||
|
identity_store(fs, "") |
||||
|
#elif defined(RP2040_PLATFORM) |
||||
|
identity_store(fs, "/identity") |
||||
|
#else |
||||
|
identity_store(fs, "/identity") |
||||
|
#endif |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
static File openWrite(FILESYSTEM* _fs, const char* filename) { |
||||
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
||||
|
_fs->remove(filename); |
||||
|
return _fs->open(filename, FILE_O_WRITE); |
||||
|
#elif defined(RP2040_PLATFORM) |
||||
|
return _fs->open(filename, "w"); |
||||
|
#else |
||||
|
return _fs->open(filename, "w", true); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
void DataStore::begin() { |
||||
|
#if defined(RP2040_PLATFORM) |
||||
|
identity_store.begin(); |
||||
|
#endif |
||||
|
|
||||
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
||||
|
checkAdvBlobFile(); |
||||
|
#else |
||||
|
// init 'blob store' support
|
||||
|
_fs->mkdir("/bl"); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
#if defined(ESP32) |
||||
|
#include <SPIFFS.h> |
||||
|
#elif defined(RP2040_PLATFORM) |
||||
|
#include <LittleFS.h> |
||||
|
#endif |
||||
|
|
||||
|
File DataStore::openRead(const char* filename) { |
||||
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
||||
|
return _fs->open(filename, FILE_O_READ); |
||||
|
#elif defined(RP2040_PLATFORM) |
||||
|
return _fs->open(filename, "r"); |
||||
|
#else |
||||
|
return _fs->open(filename, "r", false); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
bool DataStore::removeFile(const char* filename) { |
||||
|
return _fs->remove(filename); |
||||
|
} |
||||
|
|
||||
|
bool DataStore::formatFileSystem() { |
||||
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
||||
|
return _fs->format(); |
||||
|
#elif defined(RP2040_PLATFORM) |
||||
|
return LittleFS.format(); |
||||
|
#elif defined(ESP32) |
||||
|
return ((fs::SPIFFSFS *)_fs)->format(); |
||||
|
#else |
||||
|
#error "need to implement format()" |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
bool DataStore::loadMainIdentity(mesh::LocalIdentity &identity) { |
||||
|
return identity_store.load("_main", identity); |
||||
|
} |
||||
|
|
||||
|
bool DataStore::saveMainIdentity(const mesh::LocalIdentity &identity) { |
||||
|
return identity_store.save("_main", identity); |
||||
|
} |
||||
|
|
||||
|
void DataStore::loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon) { |
||||
|
if (_fs->exists("/new_prefs")) { |
||||
|
loadPrefsInt("/new_prefs", prefs, node_lat, node_lon); // new filename
|
||||
|
} else if (_fs->exists("/node_prefs")) { |
||||
|
loadPrefsInt("/node_prefs", prefs, node_lat, node_lon); |
||||
|
savePrefs(prefs, node_lat, node_lon); // save to new filename
|
||||
|
_fs->remove("/node_prefs"); // remove old
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& node_lat, double& node_lon) { |
||||
|
#if defined(RP2040_PLATFORM) |
||||
|
File file = _fs->open(filename, "r"); |
||||
|
#else |
||||
|
File file = _fs->open(filename); |
||||
|
#endif |
||||
|
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 *)&node_lat, sizeof(node_lat)); // 40
|
||||
|
file.read((uint8_t *)&node_lon, sizeof(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.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 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.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69
|
||||
|
file.read((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70
|
||||
|
file.read((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71
|
||||
|
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
|
||||
|
|
||||
|
file.close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_lon) { |
||||
|
File file = openWrite(_fs, "/new_prefs"); |
||||
|
if (file) { |
||||
|
uint8_t pad[8]; |
||||
|
memset(pad, 0, sizeof(pad)); |
||||
|
|
||||
|
file.write((uint8_t *)&_prefs.airtime_factor, sizeof(float)); // 0
|
||||
|
file.write((uint8_t *)_prefs.node_name, sizeof(_prefs.node_name)); // 4
|
||||
|
file.write(pad, 4); // 36
|
||||
|
file.write((uint8_t *)&node_lat, sizeof(node_lat)); // 40
|
||||
|
file.write((uint8_t *)&node_lon, sizeof(node_lon)); // 48
|
||||
|
file.write((uint8_t *)&_prefs.freq, sizeof(_prefs.freq)); // 56
|
||||
|
file.write((uint8_t *)&_prefs.sf, sizeof(_prefs.sf)); // 60
|
||||
|
file.write((uint8_t *)&_prefs.cr, sizeof(_prefs.cr)); // 61
|
||||
|
file.write((uint8_t *)&_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
|
||||
|
file.write((uint8_t *)&_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63
|
||||
|
file.write((uint8_t *)&_prefs.bw, sizeof(_prefs.bw)); // 64
|
||||
|
file.write((uint8_t *)&_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
|
||||
|
file.write((uint8_t *)&_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69
|
||||
|
file.write((uint8_t *)&_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70
|
||||
|
file.write((uint8_t *)&_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71
|
||||
|
file.write((uint8_t *)&_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
|
||||
|
file.write(pad, 4); // 76
|
||||
|
file.write((uint8_t *)&_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
|
||||
|
|
||||
|
file.close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void DataStore::loadContacts(DataStoreHost* host) { |
||||
|
if (_fs->exists("/contacts3")) { |
||||
|
#if defined(RP2040_PLATFORM) |
||||
|
File file = _fs->open("/contacts3", "r"); |
||||
|
#else |
||||
|
File file = _fs->open("/contacts3"); |
||||
|
#endif |
||||
|
if (file) { |
||||
|
bool full = false; |
||||
|
while (!full) { |
||||
|
ContactInfo c; |
||||
|
uint8_t pub_key[32]; |
||||
|
uint8_t unused; |
||||
|
|
||||
|
bool success = (file.read(pub_key, 32) == 32); |
||||
|
success = success && (file.read((uint8_t *)&c.name, 32) == 32); |
||||
|
success = success && (file.read(&c.type, 1) == 1); |
||||
|
success = success && (file.read(&c.flags, 1) == 1); |
||||
|
success = success && (file.read(&unused, 1) == 1); |
||||
|
success = success && (file.read((uint8_t *)&c.sync_since, 4) == 4); // was 'reserved'
|
||||
|
success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1); |
||||
|
success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4); |
||||
|
success = success && (file.read(c.out_path, 64) == 64); |
||||
|
success = success && (file.read((uint8_t *)&c.lastmod, 4) == 4); |
||||
|
success = success && (file.read((uint8_t *)&c.gps_lat, 4) == 4); |
||||
|
success = success && (file.read((uint8_t *)&c.gps_lon, 4) == 4); |
||||
|
|
||||
|
if (!success) break; // EOF
|
||||
|
|
||||
|
c.id = mesh::Identity(pub_key); |
||||
|
if (!host->onContactLoaded(c)) full = true; |
||||
|
} |
||||
|
file.close(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void DataStore::saveContacts(DataStoreHost* host) { |
||||
|
File file = openWrite(_fs, "/contacts3"); |
||||
|
if (file) { |
||||
|
uint32_t idx = 0; |
||||
|
ContactInfo c; |
||||
|
uint8_t unused = 0; |
||||
|
|
||||
|
while (host->getContactForSave(idx, c)) { |
||||
|
bool success = (file.write(c.id.pub_key, 32) == 32); |
||||
|
success = success && (file.write((uint8_t *)&c.name, 32) == 32); |
||||
|
success = success && (file.write(&c.type, 1) == 1); |
||||
|
success = success && (file.write(&c.flags, 1) == 1); |
||||
|
success = success && (file.write(&unused, 1) == 1); |
||||
|
success = success && (file.write((uint8_t *)&c.sync_since, 4) == 4); |
||||
|
success = success && (file.write((uint8_t *)&c.out_path_len, 1) == 1); |
||||
|
success = success && (file.write((uint8_t *)&c.last_advert_timestamp, 4) == 4); |
||||
|
success = success && (file.write(c.out_path, 64) == 64); |
||||
|
success = success && (file.write((uint8_t *)&c.lastmod, 4) == 4); |
||||
|
success = success && (file.write((uint8_t *)&c.gps_lat, 4) == 4); |
||||
|
success = success && (file.write((uint8_t *)&c.gps_lon, 4) == 4); |
||||
|
|
||||
|
if (!success) break; // write failed
|
||||
|
|
||||
|
idx++; // advance to next contact
|
||||
|
} |
||||
|
file.close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void DataStore::loadChannels(DataStoreHost* host) { |
||||
|
if (_fs->exists("/channels2")) { |
||||
|
#if defined(RP2040_PLATFORM) |
||||
|
File file = _fs->open("/channels2", "r"); |
||||
|
#else |
||||
|
File file = _fs->open("/channels2"); |
||||
|
#endif |
||||
|
if (file) { |
||||
|
bool full = false; |
||||
|
uint8_t channel_idx = 0; |
||||
|
while (!full) { |
||||
|
ChannelDetails ch; |
||||
|
uint8_t unused[4]; |
||||
|
|
||||
|
bool success = (file.read(unused, 4) == 4); |
||||
|
success = success && (file.read((uint8_t *)ch.name, 32) == 32); |
||||
|
success = success && (file.read((uint8_t *)ch.channel.secret, 32) == 32); |
||||
|
|
||||
|
if (!success) break; // EOF
|
||||
|
|
||||
|
if (host->onChannelLoaded(channel_idx, ch)) { |
||||
|
channel_idx++; |
||||
|
} else { |
||||
|
full = true; |
||||
|
} |
||||
|
} |
||||
|
file.close(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void DataStore::saveChannels(DataStoreHost* host) { |
||||
|
File file = openWrite(_fs, "/channels2"); |
||||
|
if (file) { |
||||
|
uint8_t channel_idx = 0; |
||||
|
ChannelDetails ch; |
||||
|
uint8_t unused[4]; |
||||
|
memset(unused, 0, 4); |
||||
|
|
||||
|
while (host->getChannelForSave(channel_idx, ch)) { |
||||
|
bool success = (file.write(unused, 4) == 4); |
||||
|
success = success && (file.write((uint8_t *)ch.name, 32) == 32); |
||||
|
success = success && (file.write((uint8_t *)ch.channel.secret, 32) == 32); |
||||
|
|
||||
|
if (!success) break; // write failed
|
||||
|
channel_idx++; |
||||
|
} |
||||
|
file.close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
||||
|
|
||||
|
#define MAX_ADVERT_PKT_LEN (2 + 32 + PUB_KEY_SIZE + 4 + SIGNATURE_SIZE + MAX_ADVERT_DATA_SIZE) |
||||
|
|
||||
|
struct BlobRec { |
||||
|
uint32_t timestamp; |
||||
|
uint8_t key[7]; |
||||
|
uint8_t len; |
||||
|
uint8_t data[MAX_ADVERT_PKT_LEN]; |
||||
|
}; |
||||
|
|
||||
|
void DataStore::checkAdvBlobFile() { |
||||
|
if (!_fs->exists("/adv_blobs")) { |
||||
|
File file = openWrite(_fs, "/adv_blobs"); |
||||
|
if (file) { |
||||
|
BlobRec zeroes; |
||||
|
memset(&zeroes, 0, sizeof(zeroes)); |
||||
|
for (int i = 0; i < 20; i++) { // pre-allocate to fixed size
|
||||
|
file.write((uint8_t *) &zeroes, sizeof(zeroes)); |
||||
|
} |
||||
|
file.close(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { |
||||
|
File file = _fs->open("/adv_blobs"); |
||||
|
uint8_t len = 0; // 0 = not found
|
||||
|
|
||||
|
if (file) { |
||||
|
BlobRec tmp; |
||||
|
while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) { |
||||
|
if (memcmp(key, tmp.key, sizeof(tmp.key)) == 0) { // only match by 7 byte prefix
|
||||
|
len = tmp.len; |
||||
|
memcpy(dest_buf, tmp.data, len); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
file.close(); |
||||
|
} |
||||
|
return len; |
||||
|
} |
||||
|
|
||||
|
bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) { |
||||
|
if (len < PUB_KEY_SIZE+4+SIGNATURE_SIZE || len > MAX_ADVERT_PKT_LEN) return false; |
||||
|
|
||||
|
checkAdvBlobFile(); |
||||
|
|
||||
|
File file = _fs->open("/adv_blobs", FILE_O_WRITE); |
||||
|
if (file) { |
||||
|
uint32_t pos = 0, found_pos = 0; |
||||
|
uint32_t min_timestamp = 0xFFFFFFFF; |
||||
|
|
||||
|
// search for matching key OR evict by oldest timestmap
|
||||
|
BlobRec tmp; |
||||
|
file.seek(0); |
||||
|
while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) { |
||||
|
if (memcmp(key, tmp.key, sizeof(tmp.key)) == 0) { // only match by 7 byte prefix
|
||||
|
found_pos = pos; |
||||
|
break; |
||||
|
} |
||||
|
if (tmp.timestamp < min_timestamp) { |
||||
|
min_timestamp = tmp.timestamp; |
||||
|
found_pos = pos; |
||||
|
} |
||||
|
|
||||
|
pos += sizeof(tmp); |
||||
|
} |
||||
|
|
||||
|
memcpy(tmp.key, key, sizeof(tmp.key)); // just record 7 byte prefix of key
|
||||
|
memcpy(tmp.data, src_buf, len); |
||||
|
tmp.len = len; |
||||
|
tmp.timestamp = _clock->getCurrentTime(); |
||||
|
|
||||
|
file.seek(found_pos); |
||||
|
file.write((uint8_t *) &tmp, sizeof(tmp)); |
||||
|
|
||||
|
file.close(); |
||||
|
return true; |
||||
|
} |
||||
|
return false; // error
|
||||
|
} |
||||
|
#else |
||||
|
uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { |
||||
|
char path[64]; |
||||
|
char fname[18]; |
||||
|
|
||||
|
if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix)
|
||||
|
mesh::Utils::toHex(fname, key, key_len); |
||||
|
sprintf(path, "/bl/%s", fname); |
||||
|
|
||||
|
if (_fs->exists(path)) { |
||||
|
#if defined(RP2040_PLATFORM) |
||||
|
File f = _fs->open(path, "r"); |
||||
|
#else |
||||
|
File f = _fs->open(path); |
||||
|
#endif |
||||
|
if (f) { |
||||
|
int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!!
|
||||
|
f.close(); |
||||
|
return len; |
||||
|
} |
||||
|
} |
||||
|
return 0; // not found
|
||||
|
} |
||||
|
|
||||
|
bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len) { |
||||
|
char path[64]; |
||||
|
char fname[18]; |
||||
|
|
||||
|
if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix)
|
||||
|
mesh::Utils::toHex(fname, key, key_len); |
||||
|
sprintf(path, "/bl/%s", fname); |
||||
|
|
||||
|
File f = openWrite(_fs, path); |
||||
|
if (f) { |
||||
|
int n = f.write(src_buf, len); |
||||
|
f.close(); |
||||
|
if (n == len) return true; // success!
|
||||
|
|
||||
|
_fs->remove(path); // blob was only partially written!
|
||||
|
} |
||||
|
return false; // error
|
||||
|
} |
||||
|
#endif |
||||
@ -0,0 +1,42 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <helpers/IdentityStore.h> |
||||
|
#include <helpers/ContactInfo.h> |
||||
|
#include <helpers/ChannelDetails.h> |
||||
|
#include "NodePrefs.h" |
||||
|
|
||||
|
class DataStoreHost { |
||||
|
public: |
||||
|
virtual bool onContactLoaded(const ContactInfo& contact) =0; |
||||
|
virtual bool getContactForSave(uint32_t idx, ContactInfo& contact) =0; |
||||
|
virtual bool onChannelLoaded(uint8_t channel_idx, const ChannelDetails& ch) =0; |
||||
|
virtual bool getChannelForSave(uint8_t channel_idx, ChannelDetails& ch) =0; |
||||
|
}; |
||||
|
|
||||
|
class DataStore { |
||||
|
FILESYSTEM* _fs; |
||||
|
mesh::RTCClock* _clock; |
||||
|
IdentityStore identity_store; |
||||
|
|
||||
|
void loadPrefsInt(const char *filename, NodePrefs& prefs, double& node_lat, double& node_lon); |
||||
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
||||
|
void checkAdvBlobFile(); |
||||
|
#endif |
||||
|
|
||||
|
public: |
||||
|
DataStore(FILESYSTEM& fs, mesh::RTCClock& clock); |
||||
|
void begin(); |
||||
|
bool formatFileSystem(); |
||||
|
bool loadMainIdentity(mesh::LocalIdentity &identity); |
||||
|
bool saveMainIdentity(const mesh::LocalIdentity &identity); |
||||
|
void loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon); |
||||
|
void savePrefs(const NodePrefs& prefs, double node_lat, double node_lon); |
||||
|
void loadContacts(DataStoreHost* host); |
||||
|
void saveContacts(DataStoreHost* host); |
||||
|
void loadChannels(DataStoreHost* host); |
||||
|
void saveChannels(DataStoreHost* host); |
||||
|
uint8_t getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); |
||||
|
bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len); |
||||
|
File openRead(const char* filename); |
||||
|
bool removeFile(const char* filename); |
||||
|
}; |
||||
File diff suppressed because it is too large
@ -0,0 +1,210 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <Mesh.h> |
||||
|
#ifdef DISPLAY_CLASS |
||||
|
#include "UITask.h" |
||||
|
#endif |
||||
|
|
||||
|
/*------------ Frame Protocol --------------*/ |
||||
|
#define FIRMWARE_VER_CODE 5 |
||||
|
|
||||
|
#ifndef FIRMWARE_BUILD_DATE |
||||
|
#define FIRMWARE_BUILD_DATE "7 Jun 2025" |
||||
|
#endif |
||||
|
|
||||
|
#ifndef FIRMWARE_VERSION |
||||
|
#define FIRMWARE_VERSION "v1.7.0" |
||||
|
#endif |
||||
|
|
||||
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) |
||||
|
#include <InternalFileSystem.h> |
||||
|
#elif defined(RP2040_PLATFORM) |
||||
|
#include <LittleFS.h> |
||||
|
#elif defined(ESP32) |
||||
|
#include <SPIFFS.h> |
||||
|
#endif |
||||
|
|
||||
|
#include "DataStore.h" |
||||
|
#include "NodePrefs.h" |
||||
|
|
||||
|
#include <RTClib.h> |
||||
|
#include <helpers/ArduinoHelpers.h> |
||||
|
#include <helpers/BaseSerialInterface.h> |
||||
|
#include <helpers/IdentityStore.h> |
||||
|
#include <helpers/SimpleMeshTables.h> |
||||
|
#include <helpers/StaticPoolPacketManager.h> |
||||
|
#include <target.h> |
||||
|
|
||||
|
/* ---------------------------------- CONFIGURATION ------------------------------------- */ |
||||
|
|
||||
|
#ifndef LORA_FREQ |
||||
|
#define LORA_FREQ 915.0 |
||||
|
#endif |
||||
|
#ifndef LORA_BW |
||||
|
#define LORA_BW 250 |
||||
|
#endif |
||||
|
#ifndef LORA_SF |
||||
|
#define LORA_SF 10 |
||||
|
#endif |
||||
|
#ifndef LORA_CR |
||||
|
#define LORA_CR 5 |
||||
|
#endif |
||||
|
#ifndef LORA_TX_POWER |
||||
|
#define LORA_TX_POWER 20 |
||||
|
#endif |
||||
|
#ifndef MAX_LORA_TX_POWER |
||||
|
#define MAX_LORA_TX_POWER LORA_TX_POWER |
||||
|
#endif |
||||
|
|
||||
|
#ifndef MAX_CONTACTS |
||||
|
#define MAX_CONTACTS 100 |
||||
|
#endif |
||||
|
|
||||
|
#ifndef OFFLINE_QUEUE_SIZE |
||||
|
#define OFFLINE_QUEUE_SIZE 16 |
||||
|
#endif |
||||
|
|
||||
|
#ifndef BLE_NAME_PREFIX |
||||
|
#define BLE_NAME_PREFIX "MeshCore-" |
||||
|
#endif |
||||
|
|
||||
|
#include <helpers/BaseChatMesh.h> |
||||
|
|
||||
|
/* -------------------------------------------------------------------------------------- */ |
||||
|
|
||||
|
#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS
|
||||
|
#define REQ_TYPE_KEEP_ALIVE 0x02 |
||||
|
#define REQ_TYPE_GET_TELEMETRY_DATA 0x03 |
||||
|
|
||||
|
class MyMesh : public BaseChatMesh, public DataStoreHost { |
||||
|
public: |
||||
|
MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store); |
||||
|
|
||||
|
void begin(bool has_display); |
||||
|
void startInterface(BaseSerialInterface &serial); |
||||
|
|
||||
|
const char *getNodeName(); |
||||
|
NodePrefs *getNodePrefs(); |
||||
|
uint32_t getBLEPin(); |
||||
|
|
||||
|
void loop(); |
||||
|
void handleCmdFrame(size_t len); |
||||
|
bool advert(); |
||||
|
void enterCLIRescue(); |
||||
|
|
||||
|
protected: |
||||
|
float getAirtimeBudgetFactor() const override; |
||||
|
int getInterferenceThreshold() const override; |
||||
|
int calcRxDelay(float score, uint32_t air_time) const override; |
||||
|
|
||||
|
void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; |
||||
|
bool isAutoAddEnabled() const override; |
||||
|
void onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) override; |
||||
|
void onContactPathUpdated(const ContactInfo &contact) override; |
||||
|
bool processAck(const uint8_t *data) override; |
||||
|
void queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt, uint32_t sender_timestamp, |
||||
|
const uint8_t *extra, int extra_len, const char *text); |
||||
|
|
||||
|
void onMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, |
||||
|
const char *text) override; |
||||
|
void onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, |
||||
|
const char *text) override; |
||||
|
void onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp, |
||||
|
const uint8_t *sender_prefix, const char *text) override; |
||||
|
void onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, |
||||
|
const char *text) override; |
||||
|
|
||||
|
uint8_t onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data, |
||||
|
uint8_t len, uint8_t *reply) override; |
||||
|
void onContactResponse(const ContactInfo &contact, const uint8_t *data, uint8_t len) override; |
||||
|
void onRawDataRecv(mesh::Packet *packet) override; |
||||
|
void onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code, uint8_t flags, |
||||
|
const uint8_t *path_snrs, const uint8_t *path_hashes, uint8_t path_len) override; |
||||
|
|
||||
|
uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override; |
||||
|
uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override; |
||||
|
void onSendTimeout() override; |
||||
|
|
||||
|
// DataStoreHost methods
|
||||
|
bool onContactLoaded(const ContactInfo& contact) override { return addContact(contact); } |
||||
|
bool getContactForSave(uint32_t idx, ContactInfo& contact) override { return getContactByIdx(idx, contact); } |
||||
|
bool onChannelLoaded(uint8_t channel_idx, const ChannelDetails& ch) override { return setChannel(channel_idx, ch); } |
||||
|
bool getChannelForSave(uint8_t channel_idx, ChannelDetails& ch) override { return getChannel(channel_idx, ch); } |
||||
|
|
||||
|
private: |
||||
|
void writeOKFrame(); |
||||
|
void writeErrFrame(uint8_t err_code); |
||||
|
void writeDisabledFrame(); |
||||
|
void writeContactRespFrame(uint8_t code, const ContactInfo &contact); |
||||
|
void updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len); |
||||
|
void addToOfflineQueue(const uint8_t frame[], int len); |
||||
|
int getFromOfflineQueue(uint8_t frame[]); |
||||
|
int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override { |
||||
|
return _store->getBlobByKey(key, key_len, dest_buf); |
||||
|
} |
||||
|
bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override { |
||||
|
return _store->putBlobByKey(key, key_len, src_buf, len); |
||||
|
} |
||||
|
|
||||
|
void checkCLIRescueCmd(); |
||||
|
void checkSerialInterface(); |
||||
|
|
||||
|
// helpers, short-cuts
|
||||
|
void savePrefs() { _store->savePrefs(_prefs, sensors.node_lat, sensors.node_lon); } |
||||
|
void saveChannels() { _store->saveChannels(this); } |
||||
|
void saveContacts() { _store->saveContacts(this); } |
||||
|
|
||||
|
private: |
||||
|
DataStore* _store; |
||||
|
NodePrefs _prefs; |
||||
|
uint32_t pending_login; |
||||
|
uint32_t pending_status; |
||||
|
uint32_t pending_telemetry; |
||||
|
BaseSerialInterface *_serial; |
||||
|
|
||||
|
ContactsIterator _iter; |
||||
|
uint32_t _iter_filter_since; |
||||
|
uint32_t _most_recent_lastmod; |
||||
|
uint32_t _active_ble_pin; |
||||
|
bool _iter_started; |
||||
|
bool _cli_rescue; |
||||
|
char cli_command[80]; |
||||
|
uint8_t app_target_ver; |
||||
|
uint8_t *sign_data; |
||||
|
uint32_t sign_data_len; |
||||
|
unsigned long dirty_contacts_expiry; |
||||
|
|
||||
|
uint8_t cmd_frame[MAX_FRAME_SIZE + 1]; |
||||
|
uint8_t out_frame[MAX_FRAME_SIZE + 1]; |
||||
|
CayenneLPP telemetry; |
||||
|
|
||||
|
struct Frame { |
||||
|
uint8_t len; |
||||
|
uint8_t buf[MAX_FRAME_SIZE]; |
||||
|
}; |
||||
|
int offline_queue_len; |
||||
|
Frame offline_queue[OFFLINE_QUEUE_SIZE]; |
||||
|
|
||||
|
struct AckTableEntry { |
||||
|
unsigned long msg_sent; |
||||
|
uint32_t ack; |
||||
|
}; |
||||
|
#define EXPECTED_ACK_TABLE_SIZE 8 |
||||
|
AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table
|
||||
|
int next_ack_idx; |
||||
|
|
||||
|
struct AdvertPath { |
||||
|
uint8_t pubkey_prefix[7]; |
||||
|
uint8_t path_len; |
||||
|
uint32_t recv_timestamp; |
||||
|
uint8_t path[MAX_PATH_SIZE]; |
||||
|
}; |
||||
|
#define ADVERT_PATH_TABLE_SIZE 16 |
||||
|
AdvertPath advert_paths[ADVERT_PATH_TABLE_SIZE]; // circular table
|
||||
|
}; |
||||
|
|
||||
|
extern MyMesh the_mesh; |
||||
|
#ifdef DISPLAY_CLASS |
||||
|
extern UITask ui_task; |
||||
|
#endif |
||||
File diff suppressed because it is too large
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
@ -0,0 +1,9 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <Mesh.h> |
||||
|
|
||||
|
struct ChannelDetails { |
||||
|
mesh::GroupChannel channel; |
||||
|
char name[32]; |
||||
|
}; |
||||
@ -0,0 +1,18 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <Mesh.h> |
||||
|
|
||||
|
struct ContactInfo { |
||||
|
mesh::Identity id; |
||||
|
char name[32]; |
||||
|
uint8_t type; // on of ADV_TYPE_*
|
||||
|
uint8_t flags; |
||||
|
int8_t out_path_len; |
||||
|
uint8_t out_path[MAX_PATH_SIZE]; |
||||
|
uint32_t last_advert_timestamp; // by THEIR clock
|
||||
|
uint8_t shared_secret[PUB_KEY_SIZE]; |
||||
|
uint32_t lastmod; // by OUR clock
|
||||
|
int32_t gps_lat, gps_lon; // 6 dec places
|
||||
|
uint32_t sync_since; |
||||
|
}; |
||||
@ -0,0 +1,30 @@ |
|||||
|
#include "WaveshareBoard.h" |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <Wire.h> |
||||
|
|
||||
|
void WaveshareBoard::begin() { |
||||
|
// for future use, sub-classes SHOULD call this from their begin()
|
||||
|
startup_reason = BD_STARTUP_NORMAL; |
||||
|
|
||||
|
#ifdef P_LORA_TX_LED |
||||
|
pinMode(P_LORA_TX_LED, OUTPUT); |
||||
|
#endif |
||||
|
|
||||
|
#ifdef PIN_VBAT_READ |
||||
|
pinMode(PIN_VBAT_READ, INPUT); |
||||
|
#endif |
||||
|
|
||||
|
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) |
||||
|
Wire.setSDA(PIN_BOARD_SDA); |
||||
|
Wire.setSCL(PIN_BOARD_SCL); |
||||
|
#endif |
||||
|
|
||||
|
Wire.begin(); |
||||
|
|
||||
|
delay(10); // give sx1262 some time to power up
|
||||
|
} |
||||
|
|
||||
|
bool WaveshareBoard::startOTAUpdate(const char *id, char reply[]) { |
||||
|
return false; |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <MeshCore.h> |
||||
|
|
||||
|
// LoRa radio module pins for Waveshare RP2040-LoRa-HF/LF
|
||||
|
// https://files.waveshare.com/wiki/RP2040-LoRa/Rp2040-lora-sch.pdf
|
||||
|
|
||||
|
#define P_LORA_DIO_1 16 |
||||
|
#define P_LORA_NSS 13 // CS
|
||||
|
#define P_LORA_RESET 23 |
||||
|
#define P_LORA_BUSY 18 |
||||
|
#define P_LORA_SCLK 14 |
||||
|
#define P_LORA_MISO 24 |
||||
|
#define P_LORA_MOSI 15 |
||||
|
#define P_LORA_TX_LED 25 |
||||
|
|
||||
|
#define SX126X_DIO2_AS_RF_SWITCH true |
||||
|
#define SX126X_DIO3_TCXO_VOLTAGE 0 |
||||
|
|
||||
|
/*
|
||||
|
* This board has no built-in way to read battery voltage. |
||||
|
* Nevertheless it's very easy to make it work, you only require two 1% resistors. |
||||
|
* |
||||
|
* BAT+ -----+ |
||||
|
* | |
||||
|
* VSYS --+ -/\/\/\/\- --+ |
||||
|
* 200k | |
||||
|
* +-- GPIO28 |
||||
|
* | |
||||
|
* GND --+ -/\/\/\/\- --+ |
||||
|
* | 100k |
||||
|
* BAT- -----+ |
||||
|
*/ |
||||
|
#define PIN_VBAT_READ 28 |
||||
|
#define BATTERY_SAMPLES 8 |
||||
|
#define ADC_MULTIPLIER (3.0f * 3.3f * 1000) |
||||
|
|
||||
|
class WaveshareBoard : public mesh::MainBoard { |
||||
|
protected: |
||||
|
uint8_t startup_reason; |
||||
|
|
||||
|
public: |
||||
|
void begin(); |
||||
|
uint8_t getStartupReason() const override { return startup_reason; } |
||||
|
|
||||
|
#ifdef P_LORA_TX_LED |
||||
|
void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); } |
||||
|
void onAfterTransmit() override { digitalWrite(P_LORA_TX_LED, LOW); } |
||||
|
#endif |
||||
|
|
||||
|
uint16_t getBattMilliVolts() override { |
||||
|
#if defined(PIN_VBAT_READ) && defined(ADC_MULTIPLIER) |
||||
|
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; |
||||
|
#else |
||||
|
return 0; |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
const char *getManufacturerName() const override { return "Waveshare RP2040-LoRa"; } |
||||
|
|
||||
|
void reboot() override { rp2040.reboot(); } |
||||
|
|
||||
|
bool startOTAUpdate(const char *id, char reply[]) override; |
||||
|
}; |
||||
@ -0,0 +1,33 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <MeshCore.h> |
||||
|
#include <Arduino.h> |
||||
|
|
||||
|
#if defined(ESP_PLATFORM) |
||||
|
|
||||
|
#include <helpers/ESP32Board.h> |
||||
|
|
||||
|
class Heltec_CT62_Board : public ESP32Board { |
||||
|
public: |
||||
|
|
||||
|
uint16_t getBattMilliVolts() override { |
||||
|
#ifdef PIN_VBAT_READ |
||||
|
analogReadResolution(12); // ESP32-C3 ADC is 12-bit - 3.3/4096 (ref voltage/max counts)
|
||||
|
uint32_t raw = 0; |
||||
|
for (int i = 0; i < 8; i++) { |
||||
|
raw += analogRead(PIN_VBAT_READ); |
||||
|
} |
||||
|
raw = raw / 8; |
||||
|
|
||||
|
return ((6.52 * raw) / 1024.0) * 1000; |
||||
|
#else |
||||
|
return 0; // not supported
|
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
const char* getManufacturerName() const override { |
||||
|
return "Heltec CT62"; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
#endif |
||||
@ -0,0 +1,88 @@ |
|||||
|
[Heltec_ct62] |
||||
|
extends = esp32_base |
||||
|
board = esp32-c3-devkitm-1 |
||||
|
build_flags = |
||||
|
${esp32_base.build_flags} |
||||
|
-I variants/heltec_ct62 |
||||
|
-D HELTEC_HT_CT62=1 |
||||
|
-D RADIO_CLASS=CustomSX1262 |
||||
|
-D WRAPPER_CLASS=CustomSX1262Wrapper |
||||
|
-D ESP32_CPU_FREQ=80 |
||||
|
-D LORA_TX_POWER=22 |
||||
|
-D P_LORA_TX_LED=18 |
||||
|
-D PIN_BOARD_SDA=0 |
||||
|
-D PIN_BOARD_SCL=1 |
||||
|
;-D PIN_USER_BTN=9 |
||||
|
-D PIN_VBAT_READ=2 |
||||
|
-D P_LORA_DIO_1=3 |
||||
|
-D P_LORA_NSS=8 |
||||
|
-D P_LORA_RESET=5 |
||||
|
-D P_LORA_DIO_0=RADIOLIB_NC |
||||
|
-D P_LORA_DIO_2=RADIOLIB_NC |
||||
|
-D P_LORA_BUSY=4 |
||||
|
-D P_LORA_SCLK=10 |
||||
|
-D P_LORA_MISO=6 |
||||
|
-D P_LORA_MOSI=7 |
||||
|
-D SX126X_DIO2_AS_RF_SWITCH=true |
||||
|
-D SX126X_DIO3_TCXO_VOLTAGE=1.8 |
||||
|
-D SX126X_CURRENT_LIMIT=140 |
||||
|
-D SX126X_RX_BOOSTED_GAIN=1 |
||||
|
build_src_filter = ${esp32_base.build_src_filter} |
||||
|
+<../variants/heltec_ct62> |
||||
|
|
||||
|
[env:Heltec_ct62_repeater] |
||||
|
extends = Heltec_ct62 |
||||
|
build_flags = |
||||
|
${Heltec_ct62.build_flags} |
||||
|
;-D ARDUINO_USB_MODE=1 |
||||
|
;-D ARDUINO_USB_CDC_ON_BOOT=1 |
||||
|
-D ADVERT_NAME='"HT-CT62 Repeater"' |
||||
|
-D ADVERT_LAT=0.0 |
||||
|
-D ADVERT_LON=0.0 |
||||
|
-D ADMIN_PASSWORD='"password"' |
||||
|
-D MAX_NEIGHBOURS=8 |
||||
|
; -D MESH_PACKET_LOGGING=1 |
||||
|
; -D MESH_DEBUG=1 |
||||
|
build_src_filter = ${Heltec_ct62.build_src_filter} |
||||
|
+<../examples/simple_repeater> |
||||
|
lib_deps = |
||||
|
${Heltec_ct62.lib_deps} |
||||
|
${esp32_ota.lib_deps} |
||||
|
|
||||
|
[env:Heltec_ct62_companion_radio_usb] |
||||
|
extends = Heltec_ct62 |
||||
|
build_flags = |
||||
|
${Heltec_ct62.build_flags} |
||||
|
; -D ARDUINO_USB_MODE=1 |
||||
|
; -D ARDUINO_USB_CDC_ON_BOOT=1 |
||||
|
-D MAX_CONTACTS=100 |
||||
|
-D MAX_GROUP_CHANNELS=8 |
||||
|
-D OFFLINE_QUEUE_SIZE=256 |
||||
|
; -D MESH_PACKET_LOGGING=1 |
||||
|
; -D MESH_DEBUG=1 |
||||
|
build_src_filter = ${Heltec_ct62.build_src_filter} |
||||
|
+<../examples/companion_radio> |
||||
|
lib_deps = |
||||
|
${Heltec_ct62.lib_deps} |
||||
|
${esp32_ota.lib_deps} |
||||
|
densaugeo/base64 @ ~1.4.0 |
||||
|
|
||||
|
[env:Heltec_ct62_companion_radio_ble] |
||||
|
extends = Heltec_ct62 |
||||
|
build_flags = |
||||
|
${Heltec_ct62.build_flags} |
||||
|
; -D ARDUINO_USB_MODE=1 |
||||
|
; -D ARDUINO_USB_CDC_ON_BOOT=1 |
||||
|
-D MAX_CONTACTS=100 |
||||
|
-D MAX_GROUP_CHANNELS=8 |
||||
|
-D OFFLINE_QUEUE_SIZE=256 |
||||
|
-D BLE_PIN_CODE=123456 |
||||
|
; -D MESH_PACKET_LOGGING=1 |
||||
|
; -D MESH_DEBUG=1 |
||||
|
build_src_filter = ${Heltec_ct62.build_src_filter} |
||||
|
+<../examples/companion_radio> |
||||
|
+<helpers/esp32/SerialBLEInterface.cpp> |
||||
|
lib_deps = |
||||
|
${Heltec_ct62.lib_deps} |
||||
|
${esp32_ota.lib_deps} |
||||
|
densaugeo/base64 @ ~1.4.0 |
||||
@ -0,0 +1,70 @@ |
|||||
|
#include <Arduino.h> |
||||
|
#include "target.h" |
||||
|
|
||||
|
Heltec_CT62_Board board; |
||||
|
|
||||
|
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); |
||||
|
WRAPPER_CLASS radio_driver(radio, board); |
||||
|
|
||||
|
ESP32RTCClock fallback_clock; |
||||
|
AutoDiscoverRTCClock rtc_clock(fallback_clock); |
||||
|
SensorManager sensors; |
||||
|
|
||||
|
#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
|
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#define RADIOLIB_STATIC_ONLY 1 |
||||
|
#include <RadioLib.h> |
||||
|
#include <helpers/RadioLibWrappers.h> |
||||
|
#include "HT-CT62Board.h" |
||||
|
#include <helpers/CustomSX1262Wrapper.h> |
||||
|
#include <helpers/AutoDiscoverRTCClock.h> |
||||
|
#include <helpers/SensorManager.h> |
||||
|
|
||||
|
extern Heltec_CT62_Board board; |
||||
|
extern WRAPPER_CLASS radio_driver; |
||||
|
extern AutoDiscoverRTCClock rtc_clock; |
||||
|
extern SensorManager 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(); |
||||
@ -0,0 +1,119 @@ |
|||||
|
[LilyGo_T3S3_sx1276] |
||||
|
extends = esp32_base |
||||
|
board = t3_s3_v1_x |
||||
|
build_flags = |
||||
|
${esp32_base.build_flags} |
||||
|
-I variants/lilygo_t3s3_sx1276 |
||||
|
-D LILYGO_T3S3 |
||||
|
-D P_LORA_DIO_0=9 |
||||
|
-D P_LORA_DIO_1=33 |
||||
|
-D P_LORA_NSS=7 |
||||
|
-D P_LORA_RESET=8 |
||||
|
-D P_LORA_SCLK=5 |
||||
|
-D P_LORA_MISO=3 |
||||
|
-D P_LORA_MOSI=6 |
||||
|
-D P_LORA_TX_LED=37 |
||||
|
-D PIN_VBAT_READ=1 |
||||
|
-D PIN_USER_BTN=0 |
||||
|
-D PIN_BOARD_SDA=18 |
||||
|
-D PIN_BOARD_SCL=17 |
||||
|
-D PIN_OLED_RESET=21 |
||||
|
-D RADIO_CLASS=CustomSX1276 |
||||
|
-D WRAPPER_CLASS=CustomSX1276Wrapper |
||||
|
-D SX127X_CURRENT_LIMIT=120 |
||||
|
-D LORA_TX_POWER=20 |
||||
|
build_src_filter = ${esp32_base.build_src_filter} |
||||
|
+<../variants/lilygo_t3s3_sx1276> |
||||
|
lib_deps = |
||||
|
${esp32_base.lib_deps} |
||||
|
adafruit/Adafruit SSD1306 @ ^2.5.13 |
||||
|
|
||||
|
; === LilyGo T3S3 with SX1276 environments === |
||||
|
[env:LilyGo_T3S3_sx1276_Repeater] |
||||
|
extends = LilyGo_T3S3_sx1276 |
||||
|
build_flags = |
||||
|
${LilyGo_T3S3_sx1276.build_flags} |
||||
|
-D DISPLAY_CLASS=SSD1306Display |
||||
|
-D ADVERT_NAME='"T3S3-1276 Repeater"' |
||||
|
-D ADVERT_LAT=0.0 |
||||
|
-D ADVERT_LON=0.0 |
||||
|
-D ADMIN_PASSWORD='"password"' |
||||
|
-D MAX_NEIGHBOURS=8 |
||||
|
; -D MESH_PACKET_LOGGING=1 |
||||
|
; -D MESH_DEBUG=1 |
||||
|
build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} |
||||
|
+<helpers/ui/SSD1306Display.cpp> |
||||
|
+<../examples/simple_repeater> |
||||
|
lib_deps = |
||||
|
${LilyGo_T3S3_sx1276.lib_deps} |
||||
|
${esp32_ota.lib_deps} |
||||
|
|
||||
|
[env:LilyGo_T3S3_sx1276_terminal_chat] |
||||
|
extends = LilyGo_T3S3_sx1276 |
||||
|
build_flags = |
||||
|
${LilyGo_T3S3_sx1276.build_flags} |
||||
|
-D MAX_CONTACTS=100 |
||||
|
-D MAX_GROUP_CHANNELS=1 |
||||
|
; -D MESH_PACKET_LOGGING=1 |
||||
|
; -D MESH_DEBUG=1 |
||||
|
build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} |
||||
|
+<../examples/simple_secure_chat/main.cpp> |
||||
|
lib_deps = |
||||
|
${LilyGo_T3S3_sx1276.lib_deps} |
||||
|
densaugeo/base64 @ ~1.4.0 |
||||
|
|
||||
|
[env:LilyGo_T3S3_sx1276_room_server] |
||||
|
extends = LilyGo_T3S3_sx1276 |
||||
|
build_flags = |
||||
|
${LilyGo_T3S3_sx1276.build_flags} |
||||
|
-D DISPLAY_CLASS=SSD1306Display |
||||
|
-D ADVERT_NAME='"T3S3-1276 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 = ${LilyGo_T3S3_sx1276.build_src_filter} |
||||
|
+<helpers/ui/SSD1306Display.cpp> |
||||
|
+<../examples/simple_room_server> |
||||
|
lib_deps = |
||||
|
${LilyGo_T3S3_sx1276.lib_deps} |
||||
|
${esp32_ota.lib_deps} |
||||
|
|
||||
|
[env:LilyGo_T3S3_sx1276_companion_radio_usb] |
||||
|
extends = LilyGo_T3S3_sx1276 |
||||
|
upload_speed = 115200 |
||||
|
build_flags = |
||||
|
${LilyGo_T3S3_sx1276.build_flags} |
||||
|
-D DISPLAY_CLASS=SSD1306Display |
||||
|
-D MAX_CONTACTS=100 |
||||
|
-D MAX_GROUP_CHANNELS=8 |
||||
|
-D MESH_PACKET_LOGGING=1 |
||||
|
-D MESH_DEBUG=1 |
||||
|
build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} |
||||
|
+<helpers/ui/SSD1306Display.cpp> |
||||
|
+<../examples/companion_radio> |
||||
|
lib_deps = |
||||
|
${LilyGo_T3S3_sx1276.lib_deps} |
||||
|
densaugeo/base64 @ ~1.4.0 |
||||
|
|
||||
|
[env:LilyGo_T3S3_sx1276_companion_radio_ble] |
||||
|
extends = LilyGo_T3S3_sx1276 |
||||
|
build_flags = |
||||
|
${LilyGo_T3S3_sx1276.build_flags} |
||||
|
-D DISPLAY_CLASS=SSD1306Display |
||||
|
-D MAX_CONTACTS=100 |
||||
|
-D MAX_GROUP_CHANNELS=8 |
||||
|
-D BLE_PIN_CODE=123456 |
||||
|
-D BLE_DEBUG_LOGGING=1 |
||||
|
-D OFFLINE_QUEUE_SIZE=256 |
||||
|
; -D MESH_PACKET_LOGGING=1 |
||||
|
; -D MESH_DEBUG=1 |
||||
|
build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} |
||||
|
+<helpers/esp32/*.cpp> |
||||
|
+<helpers/ui/SSD1306Display.cpp> |
||||
|
+<../examples/companion_radio> |
||||
|
lib_deps = |
||||
|
${LilyGo_T3S3_sx1276.lib_deps} |
||||
|
densaugeo/base64 @ ~1.4.0 |
||||
@ -0,0 +1,70 @@ |
|||||
|
#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_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); |
||||
|
SensorManager sensors; |
||||
|
|
||||
|
#ifdef DISPLAY_CLASS |
||||
|
DISPLAY_CLASS display; |
||||
|
#endif |
||||
|
|
||||
|
#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
|
||||
|
} |
||||
|
|
||||
|
#ifdef SX127X_CURRENT_LIMIT |
||||
|
radio.setCurrentLimit(SX127X_CURRENT_LIMIT); |
||||
|
#endif |
||||
|
|
||||
|
radio.setCRC(1); |
||||
|
|
||||
|
radio.setRfSwitchPins(21, 10); |
||||
|
|
||||
|
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
|
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#define RADIOLIB_STATIC_ONLY 1 |
||||
|
#include <RadioLib.h> |
||||
|
#include <helpers/RadioLibWrappers.h> |
||||
|
#include <helpers/ESP32Board.h> |
||||
|
#include <helpers/CustomSX1276Wrapper.h> |
||||
|
#include <helpers/AutoDiscoverRTCClock.h> |
||||
|
#include <helpers/SensorManager.h> |
||||
|
#ifdef DISPLAY_CLASS |
||||
|
#include <helpers/ui/SSD1306Display.h> |
||||
|
#endif |
||||
|
|
||||
|
extern ESP32Board board; |
||||
|
extern WRAPPER_CLASS radio_driver; |
||||
|
extern AutoDiscoverRTCClock rtc_clock; |
||||
|
extern SensorManager sensors; |
||||
|
|
||||
|
#ifdef DISPLAY_CLASS |
||||
|
extern DISPLAY_CLASS display; |
||||
|
#endif |
||||
|
|
||||
|
bool radio_init(); |
||||
|
uint32_t radio_get_rng_seed(); |
||||
|
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); |
||||
|
void radio_set_tx_power(uint8_t dbm); |
||||
|
mesh::LocalIdentity radio_new_identity(); |
||||
@ -0,0 +1,119 @@ |
|||||
|
#include <Arduino.h> |
||||
|
#include "t1000e_sensors.h" |
||||
|
|
||||
|
#define HEATER_NTC_BX 4250 // thermistor coefficient B
|
||||
|
#define HEATER_NTC_RP 8250 // ohm, series resistance to thermistor
|
||||
|
#define HEATER_NTC_KA 273.15 // 25 Celsius at Kelvin
|
||||
|
#define NTC_REF_VCC 3000 // mV, output voltage of LDO
|
||||
|
#define LIGHT_REF_VCC 2400 //
|
||||
|
|
||||
|
static unsigned int ntc_res2[136]={ |
||||
|
113347,107565,102116,96978,92132,87559,83242,79166,75316,71677, |
||||
|
68237,64991,61919,59011,56258,53650,51178,48835,46613,44506, |
||||
|
42506,40600,38791,37073,35442,33892,32420,31020,29689,28423, |
||||
|
27219,26076,24988,23951,22963,22021,21123,20267,19450,18670, |
||||
|
17926,17214,16534,15886,15266,14674,14108,13566,13049,12554, |
||||
|
12081,11628,11195,10780,10382,10000,9634,9284,8947,8624, |
||||
|
8315,8018,7734,7461,7199,6948,6707,6475,6253,6039, |
||||
|
5834,5636,5445,5262,5086,4917,4754,4597,4446,4301, |
||||
|
4161,4026,3896,3771,3651,3535,3423,3315,3211,3111, |
||||
|
3014,2922,2834,2748,2666,2586,2509,2435,2364,2294, |
||||
|
2228,2163,2100,2040,1981,1925,1870,1817,1766,1716, |
||||
|
1669,1622,1578,1535,1493,1452,1413,1375,1338,1303, |
||||
|
1268,1234,1202,1170,1139,1110,1081,1053,1026,999, |
||||
|
974,949,925,902,880,858, |
||||
|
}; |
||||
|
|
||||
|
static char ntc_temp2[136]= |
||||
|
{ |
||||
|
-30,-29,-28,-27,-26,-25,-24,-23,-22,-21, |
||||
|
-20,-19,-18,-17,-16,-15,-14,-13,-12,-11, |
||||
|
-10,-9,-8,-7,-6,-5,-4,-3,-2,-1, |
||||
|
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,32,33,34,35,36,37,38,39, |
||||
|
40,41,42,43,44,45,46,47,48,49, |
||||
|
50,51,52,53,54,55,56,57,58,59, |
||||
|
60,61,62,63,64,65,66,67,68,69, |
||||
|
70,71,72,73,74,75,76,77,78,79, |
||||
|
80,81,82,83,84,85,86,87,88,89, |
||||
|
90,91,92,93,94,95,96,97,98,99, |
||||
|
100,101,102,103,104,105, |
||||
|
}; |
||||
|
|
||||
|
static float get_heater_temperature( unsigned int vcc_volt, unsigned int ntc_volt ) |
||||
|
{ |
||||
|
int i = 0; |
||||
|
float Vout = 0, Rt = 0, temp = 0; |
||||
|
Vout = ntc_volt; |
||||
|
|
||||
|
Rt = ( HEATER_NTC_RP * vcc_volt ) / Vout - HEATER_NTC_RP; |
||||
|
|
||||
|
for( i = 0; i < 136; i++ ) |
||||
|
{ |
||||
|
if( Rt >= ntc_res2[i] ) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
temp = ntc_temp2[i - 1] + 1 * ( ntc_res2[i - 1] - Rt ) / ( float )( ntc_res2[i - 1] - ntc_res2[i] ); |
||||
|
|
||||
|
temp = ( temp * 100 + 5 ) / 100; |
||||
|
return temp; |
||||
|
} |
||||
|
|
||||
|
static int get_light_lv( unsigned int light_volt ) |
||||
|
{ |
||||
|
float Vout = 0, Vin = 0, Rt = 0, temp = 0; |
||||
|
unsigned int light_level = 0; |
||||
|
|
||||
|
if( light_volt <= 80 ) |
||||
|
{ |
||||
|
light_level = 0; |
||||
|
return light_level; |
||||
|
} |
||||
|
else if( light_volt >= 2480 ) |
||||
|
{ |
||||
|
light_level = 100; |
||||
|
return light_level; |
||||
|
} |
||||
|
Vout = light_volt; |
||||
|
light_level = 100 * ( Vout - 80 ) / LIGHT_REF_VCC; |
||||
|
|
||||
|
return light_level; |
||||
|
} |
||||
|
|
||||
|
float t1000e_get_temperature( void ) |
||||
|
{ |
||||
|
unsigned int ntc_v, vcc_v; |
||||
|
|
||||
|
digitalWrite(PIN_3V3_EN, HIGH); |
||||
|
digitalWrite(SENSOR_EN, HIGH); |
||||
|
analogReference(AR_INTERNAL_3_0); |
||||
|
analogReadResolution(12); |
||||
|
delay(10); |
||||
|
vcc_v = (1000.0*(analogRead(BATTERY_PIN) * ADC_MULTIPLIER * AREF_VOLTAGE)) / 4096; |
||||
|
ntc_v = (1000.0 * AREF_VOLTAGE * analogRead(TEMP_SENSOR)) / 4096; |
||||
|
digitalWrite(PIN_3V3_EN, LOW); |
||||
|
digitalWrite(SENSOR_EN, LOW); |
||||
|
|
||||
|
return get_heater_temperature (vcc_v, ntc_v); |
||||
|
} |
||||
|
|
||||
|
uint32_t t1000e_get_light( void ) |
||||
|
{ |
||||
|
int lux = 0; |
||||
|
unsigned int lux_v = 0; |
||||
|
|
||||
|
digitalWrite(SENSOR_EN, HIGH); |
||||
|
analogReference(AR_INTERNAL_3_0); |
||||
|
analogReadResolution(12); |
||||
|
delay(10); |
||||
|
lux_v = 1000 * analogRead(LUX_SENSOR) * AREF_VOLTAGE / 4096; |
||||
|
lux = get_light_lv( lux_v ); |
||||
|
digitalWrite(SENSOR_EN, LOW); |
||||
|
|
||||
|
return lux; |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
// Light and temperature sensors are on ADC ports
|
||||
|
// functions adapted from Seeed examples to get values
|
||||
|
// see : https://github.com/Seeed-Studio/Seeed-Tracker-T1000-E-for-LoRaWAN-dev-board
|
||||
|
|
||||
|
extern uint32_t t1000e_get_light(); |
||||
|
extern float t1000e_get_temperature(); |
||||
@ -0,0 +1,107 @@ |
|||||
|
; Waveshare RP2040-LoRa-HF/LF |
||||
|
; https://files.waveshare.com/wiki/RP2040-LoRa/Rp2040-lora-sch.pdf |
||||
|
|
||||
|
[waveshare_rp2040_lora] |
||||
|
extends = rp2040_base |
||||
|
|
||||
|
board = pico |
||||
|
board_build.filesystem_size = 0.5m |
||||
|
|
||||
|
build_flags = ${rp2040_base.build_flags} |
||||
|
-I variants/waveshare_rp2040_lora |
||||
|
-D SX126X_CURRENT_LIMIT=140 |
||||
|
-D RADIO_CLASS=CustomSX1262 |
||||
|
-D WRAPPER_CLASS=CustomSX1262Wrapper |
||||
|
-D LORA_TX_POWER=22 |
||||
|
-D SX126X_RX_BOOSTED_GAIN=1 |
||||
|
; Debug options |
||||
|
; -D DEBUG_RP2040_WIRE=1 |
||||
|
; -D DEBUG_RP2040_SPI=1 |
||||
|
; -D DEBUG_RP2040_CORE=1 |
||||
|
; -D RADIOLIB_DEBUG_SPI=1 |
||||
|
; -D DEBUG_RP2040_PORT=Serial |
||||
|
|
||||
|
build_src_filter = ${rp2040_base.build_src_filter} |
||||
|
+<helpers/rp2040/WaveshareBoard.cpp> |
||||
|
+<../variants/waveshare_rp2040_lora> |
||||
|
|
||||
|
lib_deps = ${rp2040_base.lib_deps} |
||||
|
|
||||
|
[env:waveshare_rp2040_lora_Repeater] |
||||
|
extends = waveshare_rp2040_lora |
||||
|
build_flags = ${waveshare_rp2040_lora.build_flags} |
||||
|
-D ADVERT_NAME='"RP2040-LoRa Repeater"' |
||||
|
-D ADVERT_LAT=0.0 |
||||
|
-D ADVERT_LON=0.0 |
||||
|
-D ADMIN_PASSWORD='"password"' |
||||
|
-D MAX_NEIGHBOURS=8 |
||||
|
; -D MESH_PACKET_LOGGING=1 |
||||
|
; -D MESH_DEBUG=1 |
||||
|
build_src_filter = ${waveshare_rp2040_lora.build_src_filter} |
||||
|
+<../examples/simple_repeater> |
||||
|
|
||||
|
[env:waveshare_rp2040_lora_room_server] |
||||
|
extends = waveshare_rp2040_lora |
||||
|
build_flags = ${waveshare_rp2040_lora.build_flags} |
||||
|
-D ADVERT_NAME='"RP2040-LoRa 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 = ${waveshare_rp2040_lora.build_src_filter} |
||||
|
+<../examples/simple_room_server> |
||||
|
|
||||
|
[env:waveshare_rp2040_lora_companion_radio_usb] |
||||
|
extends = waveshare_rp2040_lora |
||||
|
build_flags = ${waveshare_rp2040_lora.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 = ${waveshare_rp2040_lora.build_src_filter} |
||||
|
+<../examples/companion_radio> |
||||
|
lib_deps = ${waveshare_rp2040_lora.lib_deps} |
||||
|
densaugeo/base64 @ ~1.4.0 |
||||
|
|
||||
|
; [env:waveshare_rp2040_lora_companion_radio_ble] |
||||
|
; extends = waveshare_rp2040_lora |
||||
|
; build_flags = ${waveshare_rp2040_lora.build_flags} |
||||
|
; -D MAX_CONTACTS=100 |
||||
|
; -D MAX_GROUP_CHANNELS=8 |
||||
|
; -D BLE_PIN_CODE=123456 |
||||
|
; -D BLE_DEBUG_LOGGING=1 |
||||
|
; ; -D MESH_PACKET_LOGGING=1 |
||||
|
; ; -D MESH_DEBUG=1 |
||||
|
; build_src_filter = ${waveshare_rp2040_lora.build_src_filter} |
||||
|
; +<../examples/companion_radio> |
||||
|
; lib_deps = ${waveshare_rp2040_lora.lib_deps} |
||||
|
; densaugeo/base64 @ ~1.4.0 |
||||
|
|
||||
|
; [env:waveshare_rp2040_lora_companion_radio_wifi] |
||||
|
; extends = waveshare_rp2040_lora |
||||
|
; build_flags = ${waveshare_rp2040_lora.build_flags} |
||||
|
; -D MAX_CONTACTS=100 |
||||
|
; -D MAX_GROUP_CHANNELS=8 |
||||
|
; -D WIFI_DEBUG_LOGGING=1 |
||||
|
; -D WIFI_SSID='"myssid"' |
||||
|
; -D WIFI_PWD='"mypwd"' |
||||
|
; ; -D MESH_PACKET_LOGGING=1 |
||||
|
; ; -D MESH_DEBUG=1 |
||||
|
; build_src_filter = ${waveshare_rp2040_lora.build_src_filter} |
||||
|
; +<../examples/companion_radio> |
||||
|
; lib_deps = ${waveshare_rp2040_lora.lib_deps} |
||||
|
; densaugeo/base64 @ ~1.4.0 |
||||
|
|
||||
|
[env:waveshare_rp2040_lora_terminal_chat] |
||||
|
extends = waveshare_rp2040_lora |
||||
|
build_flags = ${waveshare_rp2040_lora.build_flags} |
||||
|
-D MAX_CONTACTS=100 |
||||
|
-D MAX_GROUP_CHANNELS=1 |
||||
|
; -D MESH_PACKET_LOGGING=1 |
||||
|
; -D MESH_DEBUG=1 |
||||
|
build_src_filter = ${waveshare_rp2040_lora.build_src_filter} |
||||
|
+<../examples/simple_secure_chat/main.cpp> |
||||
|
lib_deps = ${waveshare_rp2040_lora.lib_deps} |
||||
|
densaugeo/base64 @ ~1.4.0 |
||||
@ -0,0 +1,81 @@ |
|||||
|
#include "target.h" |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <helpers/ArduinoHelpers.h> |
||||
|
|
||||
|
WaveshareBoard board; |
||||
|
|
||||
|
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI1); |
||||
|
WRAPPER_CLASS radio_driver(radio, board); |
||||
|
|
||||
|
VolatileRTCClock fallback_clock; |
||||
|
AutoDiscoverRTCClock rtc_clock(fallback_clock); |
||||
|
SensorManager sensors; |
||||
|
|
||||
|
#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 |
||||
|
|
||||
|
SPI1.setSCK(P_LORA_SCLK); |
||||
|
SPI1.setTX(P_LORA_MOSI); |
||||
|
SPI1.setRX(P_LORA_MISO); |
||||
|
|
||||
|
pinMode(P_LORA_NSS, OUTPUT); |
||||
|
digitalWrite(P_LORA_NSS, HIGH); |
||||
|
|
||||
|
SPI1.begin(false); |
||||
|
|
||||
|
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
|
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#define RADIOLIB_STATIC_ONLY 1 |
||||
|
|
||||
|
#include <RadioLib.h> |
||||
|
#include <helpers/AutoDiscoverRTCClock.h> |
||||
|
#include <helpers/CustomSX1262Wrapper.h> |
||||
|
#include <helpers/RadioLibWrappers.h> |
||||
|
#include <helpers/SensorManager.h> |
||||
|
#include <helpers/rp2040/WaveshareBoard.h> |
||||
|
|
||||
|
extern WaveshareBoard board; |
||||
|
extern WRAPPER_CLASS radio_driver; |
||||
|
extern AutoDiscoverRTCClock rtc_clock; |
||||
|
extern SensorManager 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(); |
||||
Loading…
Reference in new issue