mirror of https://github.com/meshcore-dev/MeshCore
96 changed files with 3051 additions and 433 deletions
@ -0,0 +1,39 @@ |
|||
{ |
|||
"build": { |
|||
"arduino": { |
|||
"ldscript": "esp32s3_out.ld" |
|||
}, |
|||
"core": "esp32", |
|||
"extra_flags": [ |
|||
"-D ARDUINO_USB_CDC_ON_BOOT=0", |
|||
"-D ARDUINO_USB_MSC_ON_BOOT=0", |
|||
"-D ARDUINO_USB_DFU_ON_BOOT=0", |
|||
"-D ARDUINO_USB_MODE=0", |
|||
"-D ARDUINO_RUNNING_CORE=1", |
|||
"-D ARDUINO_EVENT_RUNNING_CORE=1" |
|||
], |
|||
"f_cpu": "240000000L", |
|||
"f_flash": "80000000L", |
|||
"flash_mode": "qio", |
|||
"hwids": [["0x303A", "0x1001"]], |
|||
"mcu": "esp32s3", |
|||
"variant": "ESP32-S3-WROOM-1-N4" |
|||
}, |
|||
"connectivity": ["wifi", "bluetooth"], |
|||
"debug": { |
|||
"default_tool": "esp-builtin", |
|||
"onboard_tools": ["esp-builtin"], |
|||
"openocd_target": "esp32s3.cfg" |
|||
}, |
|||
"frameworks": ["arduino", "espidf"], |
|||
"name": "ESP32-S3-WROOM-1-N4 (4 MB Flash, No PSRAM)", |
|||
"upload": { |
|||
"flash_size": "4MB", |
|||
"maximum_ram_size": 524288, |
|||
"maximum_size": 4194304, |
|||
"require_upload_port": true, |
|||
"speed": 921600 |
|||
}, |
|||
"url": "https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf", |
|||
"vendor": "Espressif" |
|||
} |
|||
@ -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 RAK4631 Board", |
|||
"mcu": "nrf52840", |
|||
"variant": "WisCore_RAK4631_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 RAK4631 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" |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
#pragma once |
|||
|
|||
#include <stdint.h> |
|||
|
|||
class RateLimiter { |
|||
uint32_t _start_timestamp; |
|||
uint32_t _secs; |
|||
uint16_t _maximum, _count; |
|||
|
|||
public: |
|||
RateLimiter(uint16_t maximum, uint32_t secs): _maximum(maximum), _secs(secs), _start_timestamp(0), _count(0) { } |
|||
|
|||
bool allow(uint32_t now) { |
|||
if (now < _start_timestamp + _secs) { |
|||
_count++; |
|||
if (_count > _maximum) return false; // deny
|
|||
} else { // time window now expired
|
|||
_start_timestamp = now; |
|||
_count = 1; |
|||
} |
|||
return true; |
|||
} |
|||
}; |
|||
@ -0,0 +1,237 @@ |
|||
#include "RegionMap.h" |
|||
#include <helpers/TxtDataHelpers.h> |
|||
#include <SHA256.h> |
|||
|
|||
RegionMap::RegionMap(TransportKeyStore& store) : _store(&store) { |
|||
next_id = 1; num_regions = 0; home_id = 0; |
|||
wildcard.id = wildcard.parent = 0; |
|||
wildcard.flags = 0; // default behaviour, allow flood and direct
|
|||
strcpy(wildcard.name, "*"); |
|||
} |
|||
|
|||
bool RegionMap::is_name_char(char c) { |
|||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '_' || c == '#'; |
|||
} |
|||
|
|||
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 |
|||
} |
|||
|
|||
bool RegionMap::load(FILESYSTEM* _fs) { |
|||
if (_fs->exists("/regions2")) { |
|||
#if defined(RP2040_PLATFORM) |
|||
File file = _fs->open("/regions2", "r"); |
|||
#else |
|||
File file = _fs->open("/regions2"); |
|||
#endif |
|||
|
|||
if (file) { |
|||
uint8_t pad[128]; |
|||
|
|||
num_regions = 0; next_id = 1; home_id = 0; |
|||
|
|||
bool success = file.read(pad, 5) == 5; // reserved header
|
|||
success = success && file.read((uint8_t *) &home_id, sizeof(home_id)) == sizeof(home_id); |
|||
success = success && file.read((uint8_t *) &wildcard.flags, sizeof(wildcard.flags)) == sizeof(wildcard.flags); |
|||
success = success && file.read((uint8_t *) &next_id, sizeof(next_id)) == sizeof(next_id); |
|||
|
|||
if (success) { |
|||
while (num_regions < MAX_REGION_ENTRIES) { |
|||
auto r = ®ions[num_regions]; |
|||
|
|||
success = file.read((uint8_t *) &r->id, sizeof(r->id)) == sizeof(r->id); |
|||
success = success && file.read((uint8_t *) &r->parent, sizeof(r->parent)) == sizeof(r->parent); |
|||
success = success && file.read((uint8_t *) r->name, sizeof(r->name)) == sizeof(r->name); |
|||
success = success && file.read((uint8_t *) &r->flags, sizeof(r->flags)) == sizeof(r->flags); |
|||
success = success && file.read(pad, sizeof(pad)) == sizeof(pad); |
|||
|
|||
if (!success) break; // EOF
|
|||
|
|||
if (r->id >= next_id) { // make sure next_id is valid
|
|||
next_id = r->id + 1; |
|||
} |
|||
num_regions++; |
|||
} |
|||
} |
|||
file.close(); |
|||
return true; |
|||
} |
|||
} |
|||
return false; // failed
|
|||
} |
|||
|
|||
bool RegionMap::save(FILESYSTEM* _fs) { |
|||
File file = openWrite(_fs, "/regions2"); |
|||
if (file) { |
|||
uint8_t pad[128]; |
|||
memset(pad, 0, sizeof(pad)); |
|||
|
|||
bool success = file.write(pad, 5) == 5; // reserved header
|
|||
success = success && file.write((uint8_t *) &home_id, sizeof(home_id)) == sizeof(home_id); |
|||
success = success && file.write((uint8_t *) &wildcard.flags, sizeof(wildcard.flags)) == sizeof(wildcard.flags); |
|||
success = success && file.write((uint8_t *) &next_id, sizeof(next_id)) == sizeof(next_id); |
|||
|
|||
if (success) { |
|||
for (int i = 0; i < num_regions; i++) { |
|||
auto r = ®ions[i]; |
|||
|
|||
success = file.write((uint8_t *) &r->id, sizeof(r->id)) == sizeof(r->id); |
|||
success = success && file.write((uint8_t *) &r->parent, sizeof(r->parent)) == sizeof(r->parent); |
|||
success = success && file.write((uint8_t *) r->name, sizeof(r->name)) == sizeof(r->name); |
|||
success = success && file.write((uint8_t *) &r->flags, sizeof(r->flags)) == sizeof(r->flags); |
|||
success = success && file.write(pad, sizeof(pad)) == sizeof(pad); |
|||
if (!success) break; // write failed
|
|||
} |
|||
} |
|||
file.close(); |
|||
return true; |
|||
} |
|||
return false; // failed
|
|||
} |
|||
|
|||
RegionEntry* RegionMap::putRegion(const char* name, uint16_t parent_id, uint16_t id) { |
|||
const char* sp = name; // check for illegal name chars
|
|||
while (*sp) { |
|||
if (!is_name_char(*sp)) return NULL; // error
|
|||
sp++; |
|||
} |
|||
|
|||
auto region = findByName(name); |
|||
if (region) { |
|||
if (region->id == parent_id) return NULL; // ERROR: invalid parent!
|
|||
|
|||
region->parent = parent_id; // re-parent / move this region in the hierarchy
|
|||
} else { |
|||
if (id == 0 && num_regions >= MAX_REGION_ENTRIES) return NULL; // full!
|
|||
|
|||
region = ®ions[num_regions++]; // alloc new RegionEntry
|
|||
region->flags = REGION_DENY_FLOOD; // DENY by default
|
|||
region->id = id == 0 ? next_id++ : id; |
|||
StrHelper::strncpy(region->name, name, sizeof(region->name)); |
|||
region->parent = parent_id; |
|||
} |
|||
return region; |
|||
} |
|||
|
|||
RegionEntry* RegionMap::findMatch(mesh::Packet* packet, uint8_t mask) { |
|||
for (int i = 0; i < num_regions; i++) { |
|||
auto region = ®ions[i]; |
|||
if ((region->flags & mask) == 0) { // does region allow this? (per 'mask' param)
|
|||
TransportKey keys[4]; |
|||
int num; |
|||
if (region->name[0] == '#') { // auto hashtag region
|
|||
_store->getAutoKeyFor(region->id, region->name, keys[0]); |
|||
num = 1; |
|||
} else { |
|||
num = _store->loadKeysFor(region->id, keys, 4); |
|||
} |
|||
for (int j = 0; j < num; j++) { |
|||
uint16_t code = keys[j].calcTransportCode(packet); |
|||
if (packet->transport_codes[0] == code) { // a match!!
|
|||
return region; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return NULL; // no matches
|
|||
} |
|||
|
|||
RegionEntry* RegionMap::findByName(const char* name) { |
|||
if (strcmp(name, "*") == 0) return &wildcard; |
|||
|
|||
for (int i = 0; i < num_regions; i++) { |
|||
auto region = ®ions[i]; |
|||
if (strcmp(name, region->name) == 0) return region; |
|||
} |
|||
return NULL; // not found
|
|||
} |
|||
|
|||
RegionEntry* RegionMap::findByNamePrefix(const char* prefix) { |
|||
if (strcmp(prefix, "*") == 0) return &wildcard; |
|||
|
|||
RegionEntry* partial = NULL; |
|||
for (int i = 0; i < num_regions; i++) { |
|||
auto region = ®ions[i]; |
|||
if (strcmp(prefix, region->name) == 0) return region; // is a complete match, preference this one
|
|||
if (memcmp(prefix, region->name, strlen(prefix)) == 0) { |
|||
partial = region; |
|||
} |
|||
} |
|||
return partial; |
|||
} |
|||
|
|||
RegionEntry* RegionMap::findById(uint16_t id) { |
|||
if (id == 0) return &wildcard; // special root Region
|
|||
|
|||
for (int i = 0; i < num_regions; i++) { |
|||
auto region = ®ions[i]; |
|||
if (region->id == id) return region; |
|||
} |
|||
return NULL; // not found
|
|||
} |
|||
|
|||
RegionEntry* RegionMap::getHomeRegion() { |
|||
return findById(home_id); |
|||
} |
|||
|
|||
void RegionMap::setHomeRegion(const RegionEntry* home) { |
|||
home_id = home ? home->id : 0; |
|||
} |
|||
|
|||
bool RegionMap::removeRegion(const RegionEntry& region) { |
|||
if (region.id == 0) return false; // failed (cannot remove the wildcard Region)
|
|||
|
|||
int i; // first check region has no child regions
|
|||
for (i = 0; i < num_regions; i++) { |
|||
if (regions[i].parent == region.id) return false; // failed (must remove child Regions first)
|
|||
} |
|||
|
|||
i = 0; |
|||
while (i < num_regions) { |
|||
if (region.id == regions[i].id) break; |
|||
i++; |
|||
} |
|||
if (i >= num_regions) return false; // failed (not found)
|
|||
|
|||
num_regions--; // remove from regions array
|
|||
while (i < num_regions) { |
|||
regions[i] = regions[i + 1]; |
|||
i++; |
|||
} |
|||
return true; // success
|
|||
} |
|||
|
|||
bool RegionMap::clear() { |
|||
num_regions = 0; |
|||
return true; // success
|
|||
} |
|||
|
|||
void RegionMap::printChildRegions(int indent, const RegionEntry* parent, Stream& out) const { |
|||
for (int i = 0; i < indent; i++) { |
|||
out.print(' '); |
|||
} |
|||
|
|||
if (parent->flags & REGION_DENY_FLOOD) { |
|||
out.printf("%s%s\n", parent->name, parent->id == home_id ? "^" : ""); |
|||
} else { |
|||
out.printf("%s%s F\n", parent->name, parent->id == home_id ? "^" : ""); |
|||
} |
|||
|
|||
for (int i = 0; i < num_regions; i++) { |
|||
auto r = ®ions[i]; |
|||
if (r->parent == parent->id) { |
|||
printChildRegions(indent + 1, r, out); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void RegionMap::exportTo(Stream& out) const { |
|||
printChildRegions(0, &wildcard, out); // recursive
|
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> // needed for PlatformIO |
|||
#include <Packet.h> |
|||
#include "TransportKeyStore.h" |
|||
|
|||
#ifndef MAX_REGION_ENTRIES |
|||
#define MAX_REGION_ENTRIES 32 |
|||
#endif |
|||
|
|||
#define REGION_DENY_FLOOD 0x01 |
|||
#define REGION_DENY_DIRECT 0x02 // reserved for future
|
|||
|
|||
struct RegionEntry { |
|||
uint16_t id; |
|||
uint16_t parent; |
|||
uint8_t flags; |
|||
char name[31]; |
|||
}; |
|||
|
|||
class RegionMap { |
|||
TransportKeyStore* _store; |
|||
uint16_t next_id, home_id; |
|||
uint16_t num_regions; |
|||
RegionEntry regions[MAX_REGION_ENTRIES]; |
|||
RegionEntry wildcard; |
|||
|
|||
void printChildRegions(int indent, const RegionEntry* parent, Stream& out) const; |
|||
|
|||
public: |
|||
RegionMap(TransportKeyStore& store); |
|||
|
|||
static bool is_name_char(char c); |
|||
|
|||
bool load(FILESYSTEM* _fs); |
|||
bool save(FILESYSTEM* _fs); |
|||
|
|||
RegionEntry* putRegion(const char* name, uint16_t parent_id, uint16_t id = 0); |
|||
RegionEntry* findMatch(mesh::Packet* packet, uint8_t mask); |
|||
RegionEntry& getWildcard() { return wildcard; } |
|||
RegionEntry* findByName(const char* name); |
|||
RegionEntry* findByNamePrefix(const char* prefix); |
|||
RegionEntry* findById(uint16_t id); |
|||
RegionEntry* getHomeRegion(); // NOTE: can be NULL
|
|||
void setHomeRegion(const RegionEntry* home); |
|||
bool removeRegion(const RegionEntry& region); |
|||
bool clear(); |
|||
void resetFrom(const RegionMap& src) { num_regions = 0; next_id = src.next_id; } |
|||
int getCount() const { return num_regions; } |
|||
|
|||
void exportTo(Stream& out) const; |
|||
}; |
|||
@ -0,0 +1,54 @@ |
|||
#pragma once |
|||
|
|||
#include "Mesh.h" |
|||
|
|||
class StatsFormatHelper { |
|||
public: |
|||
static void formatCoreStats(char* reply, |
|||
mesh::MainBoard& board, |
|||
mesh::MillisecondClock& ms, |
|||
uint16_t err_flags, |
|||
mesh::PacketManager* mgr) { |
|||
sprintf(reply, |
|||
"{\"battery_mv\":%u,\"uptime_secs\":%u,\"errors\":%u,\"queue_len\":%u}", |
|||
board.getBattMilliVolts(), |
|||
ms.getMillis() / 1000, |
|||
err_flags, |
|||
mgr->getOutboundCount(0xFFFFFFFF) |
|||
); |
|||
} |
|||
|
|||
template<typename RadioDriverType> |
|||
static void formatRadioStats(char* reply, |
|||
mesh::Radio* radio, |
|||
RadioDriverType& driver, |
|||
uint32_t total_air_time_ms, |
|||
uint32_t total_rx_air_time_ms) { |
|||
sprintf(reply, |
|||
"{\"noise_floor\":%d,\"last_rssi\":%d,\"last_snr\":%.2f,\"tx_air_secs\":%u,\"rx_air_secs\":%u}", |
|||
(int16_t)radio->getNoiseFloor(), |
|||
(int16_t)driver.getLastRSSI(), |
|||
driver.getLastSNR(), |
|||
total_air_time_ms / 1000, |
|||
total_rx_air_time_ms / 1000 |
|||
); |
|||
} |
|||
|
|||
template<typename RadioDriverType> |
|||
static void formatPacketStats(char* reply, |
|||
RadioDriverType& driver, |
|||
uint32_t n_sent_flood, |
|||
uint32_t n_sent_direct, |
|||
uint32_t n_recv_flood, |
|||
uint32_t n_recv_direct) { |
|||
sprintf(reply, |
|||
"{\"recv\":%u,\"sent\":%u,\"flood_tx\":%u,\"direct_tx\":%u,\"flood_rx\":%u,\"direct_rx\":%u}", |
|||
driver.getPacketsRecv(), |
|||
driver.getPacketsSent(), |
|||
n_sent_flood, |
|||
n_sent_direct, |
|||
n_recv_flood, |
|||
n_recv_direct |
|||
); |
|||
} |
|||
}; |
|||
@ -0,0 +1,92 @@ |
|||
#include "TransportKeyStore.h" |
|||
#include <SHA256.h> |
|||
|
|||
uint16_t TransportKey::calcTransportCode(const mesh::Packet* packet) const { |
|||
uint16_t code; |
|||
SHA256 sha; |
|||
sha.resetHMAC(key, sizeof(key)); |
|||
uint8_t type = packet->getPayloadType(); |
|||
sha.update(&type, 1); |
|||
sha.update(packet->payload, packet->payload_len); |
|||
sha.finalizeHMAC(key, sizeof(key), &code, 2); |
|||
if (code == 0) { // reserve codes 0000 and FFFF
|
|||
code++; |
|||
} else if (code == 0xFFFF) { |
|||
code--; |
|||
} |
|||
return code; |
|||
} |
|||
|
|||
bool TransportKey::isNull() const { |
|||
for (int i = 0; i < sizeof(key); i++) { |
|||
if (key[i]) return false; |
|||
} |
|||
return true; // key is all zeroes
|
|||
} |
|||
|
|||
void TransportKeyStore::putCache(uint16_t id, const TransportKey& key) { |
|||
if (num_cache < MAX_TKS_ENTRIES) { |
|||
cache_ids[num_cache] = id; |
|||
cache_keys[num_cache] = key; |
|||
num_cache++; |
|||
} else { |
|||
// TODO: evict oldest cache entry
|
|||
} |
|||
} |
|||
|
|||
void TransportKeyStore::getAutoKeyFor(uint16_t id, const char* name, TransportKey& dest) { |
|||
for (int i = 0; i < num_cache; i++) { // first, check cache
|
|||
if (cache_ids[i] == id) { // cache hit!
|
|||
dest = cache_keys[i]; |
|||
return; |
|||
} |
|||
} |
|||
// calc key for publicly-known hashtag region name
|
|||
SHA256 sha; |
|||
sha.update(name, strlen(name)); |
|||
sha.finalize(&dest.key, sizeof(dest.key)); |
|||
|
|||
putCache(id, dest); |
|||
} |
|||
|
|||
int TransportKeyStore::loadKeysFor(uint16_t id, TransportKey keys[], int max_num) { |
|||
int n = 0; |
|||
for (int i = 0; i < num_cache && n < max_num; i++) { // first, check cache
|
|||
if (cache_ids[i] == id) { |
|||
keys[n++] = cache_keys[i]; |
|||
} |
|||
} |
|||
if (n > 0) return n; // cache hit!
|
|||
|
|||
// TODO: retrieve from difficult-to-copy keystore
|
|||
|
|||
// store in cache (if room)
|
|||
for (int i = 0; i < n; i++) { |
|||
putCache(id, keys[i]); |
|||
} |
|||
return n; |
|||
} |
|||
|
|||
bool TransportKeyStore::saveKeysFor(uint16_t id, const TransportKey keys[], int num) { |
|||
invalidateCache(); |
|||
|
|||
// TODO: update hardware keystore
|
|||
|
|||
return false; // failed
|
|||
} |
|||
|
|||
bool TransportKeyStore::removeKeys(uint16_t id) { |
|||
invalidateCache(); |
|||
|
|||
// TODO: remove from hardware keystore
|
|||
|
|||
return false; // failed
|
|||
} |
|||
|
|||
bool TransportKeyStore::clear() { |
|||
invalidateCache(); |
|||
|
|||
// TODO: clear hardware keystore
|
|||
|
|||
return false; // failed
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> // needed for PlatformIO |
|||
#include <Packet.h> |
|||
#include <helpers/IdentityStore.h> |
|||
|
|||
struct TransportKey { |
|||
uint8_t key[16]; |
|||
|
|||
uint16_t calcTransportCode(const mesh::Packet* packet) const; |
|||
bool isNull() const; |
|||
}; |
|||
|
|||
#define MAX_TKS_ENTRIES 16 |
|||
|
|||
class TransportKeyStore { |
|||
uint16_t cache_ids[MAX_TKS_ENTRIES]; |
|||
TransportKey cache_keys[MAX_TKS_ENTRIES]; |
|||
int num_cache; |
|||
|
|||
void putCache(uint16_t id, const TransportKey& key); |
|||
void invalidateCache() { num_cache = 0; } |
|||
|
|||
public: |
|||
TransportKeyStore() { num_cache = 0; } |
|||
void getAutoKeyFor(uint16_t id, const char* name, TransportKey& dest); |
|||
int loadKeysFor(uint16_t id, TransportKey keys[], int max_num); |
|||
bool saveKeysFor(uint16_t id, const TransportKey keys[], int num); |
|||
bool removeKeys(uint16_t id); |
|||
bool clear(); |
|||
}; |
|||
@ -0,0 +1,125 @@ |
|||
#include "LGFXDisplay.h" |
|||
|
|||
bool LGFXDisplay::begin() { |
|||
turnOn(); |
|||
display->init(); |
|||
display->setRotation(1); |
|||
display->setBrightness(64); |
|||
display->setColorDepth(8); |
|||
display->setTextColor(TFT_WHITE); |
|||
|
|||
buffer.setColorDepth(8); |
|||
buffer.setPsram(true); |
|||
buffer.createSprite(width(), height()); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void LGFXDisplay::turnOn() { |
|||
// display->wakeup();
|
|||
if (!_isOn) { |
|||
display->wakeup(); |
|||
} |
|||
_isOn = true; |
|||
} |
|||
|
|||
void LGFXDisplay::turnOff() { |
|||
if (_isOn) { |
|||
display->sleep(); |
|||
} |
|||
_isOn = false; |
|||
} |
|||
|
|||
void LGFXDisplay::clear() { |
|||
// display->clearDisplay();
|
|||
buffer.clearDisplay(); |
|||
} |
|||
|
|||
void LGFXDisplay::startFrame(Color bkg) { |
|||
// display->startWrite();
|
|||
// display->getScanLine();
|
|||
buffer.clearDisplay(); |
|||
buffer.setTextColor(TFT_WHITE); |
|||
} |
|||
|
|||
void LGFXDisplay::setTextSize(int sz) { |
|||
buffer.setTextSize(sz); |
|||
} |
|||
|
|||
void LGFXDisplay::setColor(Color c) { |
|||
// _color = (c != 0) ? ILI9342_WHITE : ILI9342_BLACK;
|
|||
switch (c) { |
|||
case DARK: |
|||
_color = TFT_BLACK; |
|||
break; |
|||
case LIGHT: |
|||
_color = TFT_WHITE; |
|||
break; |
|||
case RED: |
|||
_color = TFT_RED; |
|||
break; |
|||
case GREEN: |
|||
_color = TFT_GREEN; |
|||
break; |
|||
case BLUE: |
|||
_color = TFT_BLUE; |
|||
break; |
|||
case YELLOW: |
|||
_color = TFT_YELLOW; |
|||
break; |
|||
case ORANGE: |
|||
_color = TFT_ORANGE; |
|||
break; |
|||
default: |
|||
_color = TFT_WHITE; |
|||
} |
|||
buffer.setTextColor(_color); |
|||
} |
|||
|
|||
void LGFXDisplay::setCursor(int x, int y) { |
|||
buffer.setCursor(x, y); |
|||
} |
|||
|
|||
void LGFXDisplay::print(const char* str) { |
|||
buffer.println(str); |
|||
// Serial.println(str);
|
|||
} |
|||
|
|||
void LGFXDisplay::fillRect(int x, int y, int w, int h) { |
|||
buffer.fillRect(x, y, w, h, _color); |
|||
} |
|||
|
|||
void LGFXDisplay::drawRect(int x, int y, int w, int h) { |
|||
buffer.drawRect(x, y, w, h, _color); |
|||
} |
|||
|
|||
void LGFXDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { |
|||
buffer.drawBitmap(x, y, bits, w, h, _color); |
|||
} |
|||
|
|||
uint16_t LGFXDisplay::getTextWidth(const char* str) { |
|||
return buffer.textWidth(str); |
|||
} |
|||
|
|||
void LGFXDisplay::endFrame() { |
|||
display->startWrite(); |
|||
if (UI_ZOOM != 1) { |
|||
buffer.pushRotateZoom(display, display->width()/2, display->height()/2 , 0, UI_ZOOM, UI_ZOOM); |
|||
} else { |
|||
buffer.pushSprite(display, 0, 0); |
|||
} |
|||
display->endWrite(); |
|||
} |
|||
|
|||
bool LGFXDisplay::getTouch(int *x, int *y) { |
|||
lgfx::v1::touch_point_t point; |
|||
display->getTouch(&point); |
|||
if (UI_ZOOM != 1) { |
|||
*x = point.x / UI_ZOOM; |
|||
*y = point.y / UI_ZOOM; |
|||
} else { |
|||
*x = point.x; |
|||
*y = point.y; |
|||
} |
|||
return (*x >= 0) && (*y >= 0); |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
#pragma once |
|||
|
|||
#include <helpers/ui/DisplayDriver.h> |
|||
|
|||
#define LGFX_USE_V1 |
|||
#include <LovyanGFX.hpp> |
|||
|
|||
#ifndef UI_ZOOM |
|||
#define UI_ZOOM 1 |
|||
#endif |
|||
|
|||
class LGFXDisplay : public DisplayDriver { |
|||
protected: |
|||
LGFX_Device* display; |
|||
LGFX_Sprite buffer; |
|||
|
|||
bool _isOn = false; |
|||
int _color = TFT_WHITE; |
|||
|
|||
public: |
|||
LGFXDisplay(int w, int h, LGFX_Device &disp) |
|||
: DisplayDriver(w/UI_ZOOM, h/UI_ZOOM), display(&disp) {} |
|||
bool begin(); |
|||
bool isOn() override { return _isOn; } |
|||
void turnOn() override; |
|||
void turnOff() override; |
|||
void clear() override; |
|||
void startFrame(Color bkg = DARK) override; |
|||
void setTextSize(int sz) override; |
|||
void setColor(Color c) override; |
|||
void setCursor(int x, int y) override; |
|||
void print(const char* str) override; |
|||
void fillRect(int x, int y, int w, int h) override; |
|||
void drawRect(int x, int y, int w, int h) override; |
|||
void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override; |
|||
uint16_t getTextWidth(const char* str) override; |
|||
void endFrame() override; |
|||
virtual bool getTouch(int *x, int *y); |
|||
}; |
|||
@ -0,0 +1,49 @@ |
|||
/*
|
|||
Copyright (c) 2014-2015 Arduino LLC. All right reserved. |
|||
Copyright (c) 2016 Sandeep Mistry All right reserved. |
|||
Copyright (c) 2018, Adafruit Industries (adafruit.com) |
|||
|
|||
This library is free software; you can redistribute it and/or |
|||
modify it under the terms of the GNU Lesser General Public |
|||
License as published by the Free Software Foundation; either |
|||
version 2.1 of the License, or (at your option) any later version. |
|||
|
|||
This library is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|||
See the GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public |
|||
License along with this library; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
*/ |
|||
|
|||
#include "variant.h" |
|||
#include "wiring_constants.h" |
|||
#include "wiring_digital.h" |
|||
#include "nrf.h" |
|||
|
|||
const uint32_t g_ADigitalPinMap[] = |
|||
{ |
|||
// P0
|
|||
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , |
|||
8 , 9 , 10, 11, 12, 13, 14, 15, |
|||
16, 17, 18, 19, 20, 21, 22, 23, |
|||
24, 25, 26, 27, 28, 29, 30, 31, |
|||
|
|||
// P1
|
|||
32, 33, 34, 35, 36, 37, 38, 39, |
|||
40, 41, 42, 43, 44, 45, 46, 47 |
|||
}; |
|||
|
|||
|
|||
void initVariant() |
|||
{ |
|||
// LED1 & LED2
|
|||
pinMode(PIN_LED1, OUTPUT); |
|||
ledOff(PIN_LED1); |
|||
|
|||
pinMode(PIN_LED2, OUTPUT); |
|||
ledOff(PIN_LED2);; |
|||
} |
|||
|
|||
@ -0,0 +1,172 @@ |
|||
/*
|
|||
Copyright (c) 2014-2015 Arduino LLC. All right reserved. |
|||
Copyright (c) 2016 Sandeep Mistry All right reserved. |
|||
Copyright (c) 2018, Adafruit Industries (adafruit.com) |
|||
|
|||
This library is free software; you can redistribute it and/or |
|||
modify it under the terms of the GNU Lesser General Public |
|||
License as published by the Free Software Foundation; either |
|||
version 2.1 of the License, or (at your option) any later version. |
|||
This library is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|||
See the GNU Lesser General Public License for more details. |
|||
You should have received a copy of the GNU Lesser General Public |
|||
License along with this library; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
*/ |
|||
|
|||
#ifndef _VARIANT_RAK4630_ |
|||
#define _VARIANT_RAK4630_ |
|||
|
|||
#define RAK4630 |
|||
|
|||
/** Master clock frequency */ |
|||
#define VARIANT_MCK (64000000ul) |
|||
|
|||
#define USE_LFXO // Board uses 32khz crystal for LF
|
|||
// define USE_LFRC // Board uses RC for LF
|
|||
|
|||
/*----------------------------------------------------------------------------
|
|||
* Headers |
|||
*----------------------------------------------------------------------------*/ |
|||
|
|||
#include "WVariant.h" |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" |
|||
{ |
|||
#endif // __cplusplus
|
|||
|
|||
/*
|
|||
* WisBlock Base GPIO definitions |
|||
*/ |
|||
static const uint8_t WB_IO1 = 17; // SLOT_A SLOT_B
|
|||
static const uint8_t WB_IO2 = 34; // SLOT_A SLOT_B
|
|||
static const uint8_t WB_IO3 = 21; // SLOT_C
|
|||
static const uint8_t WB_IO4 = 4; // SLOT_C
|
|||
static const uint8_t WB_IO5 = 9; // SLOT_D
|
|||
static const uint8_t WB_IO6 = 10; // SLOT_D
|
|||
static const uint8_t WB_SW1 = 33; // IO_SLOT
|
|||
static const uint8_t WB_A0 = 5; // IO_SLOT
|
|||
static const uint8_t WB_A1 = 31; // IO_SLOT
|
|||
static const uint8_t WB_I2C1_SDA = 13; // SENSOR_SLOT IO_SLOT
|
|||
static const uint8_t WB_I2C1_SCL = 14; // SENSOR_SLOT IO_SLOT
|
|||
static const uint8_t WB_I2C2_SDA = 24; // IO_SLOT
|
|||
static const uint8_t WB_I2C2_SCL = 25; // IO_SLOT
|
|||
static const uint8_t WB_SPI_CS = 26; // IO_SLOT
|
|||
static const uint8_t WB_SPI_CLK = 3; // IO_SLOT
|
|||
static const uint8_t WB_SPI_MISO = 29; // IO_SLOT
|
|||
static const uint8_t WB_SPI_MOSI = 30; // IO_SLOT
|
|||
|
|||
// Number of pins defined in PinDescription array
|
|||
#define PINS_COUNT (48) |
|||
#define NUM_DIGITAL_PINS (48) |
|||
#define NUM_ANALOG_INPUTS (6) |
|||
#define NUM_ANALOG_OUTPUTS (0) |
|||
|
|||
// LEDs
|
|||
#define PIN_LED1 (35) |
|||
#define PIN_LED2 (36) |
|||
|
|||
#define LED_BUILTIN PIN_LED1 |
|||
#define LED_CONN PIN_LED2 |
|||
|
|||
#define LED_GREEN PIN_LED1 |
|||
#define LED_BLUE PIN_LED2 |
|||
|
|||
#define LED_STATE_ON 1 // State when LED is litted
|
|||
|
|||
/*
|
|||
* Buttons |
|||
*/ |
|||
// RAK4631 has no buttons
|
|||
|
|||
/*
|
|||
* 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 PIN_AREF (2) |
|||
#define PIN_NFC1 (9) |
|||
#define PIN_NFC2 (10) |
|||
|
|||
static const uint8_t AREF = PIN_AREF; |
|||
|
|||
/*
|
|||
* Serial interfaces |
|||
*/ |
|||
// TXD1 RXD1 on Base Board
|
|||
#define PIN_SERIAL1_RX (15) |
|||
#define PIN_SERIAL1_TX (16) |
|||
|
|||
// TXD0 RXD0 on Base Board
|
|||
#define PIN_SERIAL2_RX (19) |
|||
#define PIN_SERIAL2_TX (20) |
|||
|
|||
/*
|
|||
* SPI Interfaces |
|||
*/ |
|||
#define SPI_INTERFACES_COUNT 1 |
|||
|
|||
#define PIN_SPI_MISO (29) |
|||
#define PIN_SPI_MOSI (30) |
|||
#define PIN_SPI_SCK (3) |
|||
|
|||
static const uint8_t SS = 26; |
|||
static const uint8_t MOSI = PIN_SPI_MOSI; |
|||
static const uint8_t MISO = PIN_SPI_MISO; |
|||
static const uint8_t SCK = PIN_SPI_SCK; |
|||
|
|||
/*
|
|||
* Wire Interfaces |
|||
*/ |
|||
#define WIRE_INTERFACES_COUNT 2 |
|||
|
|||
#define PIN_WIRE_SDA (13) |
|||
#define PIN_WIRE_SCL (14) |
|||
|
|||
#define PIN_WIRE1_SDA (24) |
|||
#define PIN_WIRE1_SCL (25) |
|||
|
|||
// QSPI Pins
|
|||
// QSPI occupied by GPIO's
|
|||
#define PIN_QSPI_SCK 3 // 19
|
|||
#define PIN_QSPI_CS 26 // 17
|
|||
#define PIN_QSPI_IO0 30 // 20
|
|||
#define PIN_QSPI_IO1 29 // 21
|
|||
#define PIN_QSPI_IO2 28 // 22
|
|||
#define PIN_QSPI_IO3 2 // 23
|
|||
|
|||
// On-board QSPI Flash
|
|||
// No onboard flash
|
|||
#define EXTERNAL_FLASH_DEVICES IS25LP080D |
|||
#define EXTERNAL_FLASH_USE_QSPI |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
|||
|
|||
/*----------------------------------------------------------------------------
|
|||
* Arduino objects - C++ only |
|||
*----------------------------------------------------------------------------*/ |
|||
|
|||
#endif |
|||
@ -0,0 +1,128 @@ |
|||
#pragma once |
|||
|
|||
#include <helpers/ui/LGFXDisplay.h> |
|||
|
|||
#define LGFX_USE_V1 |
|||
#include <LovyanGFX.hpp> |
|||
|
|||
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp> |
|||
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp> |
|||
|
|||
class LGFX : public lgfx::LGFX_Device |
|||
{ |
|||
lgfx::Panel_ST7701 _panel_instance; |
|||
lgfx::Bus_RGB _bus_instance; |
|||
lgfx::Light_PWM _light_instance; |
|||
lgfx::Touch_FT5x06 _touch_instance; |
|||
|
|||
public: |
|||
const uint16_t screenWidth = 480; |
|||
const uint16_t screenHeight = 480; |
|||
|
|||
bool hasButton(void) { return true; } |
|||
|
|||
LGFX(void) |
|||
{ |
|||
{ |
|||
auto cfg = _panel_instance.config(); |
|||
cfg.memory_width = 480; |
|||
cfg.memory_height = 480; |
|||
cfg.panel_width = screenWidth; |
|||
cfg.panel_height = screenHeight; |
|||
cfg.offset_x = 0; |
|||
cfg.offset_y = 0; |
|||
cfg.offset_rotation = 1; |
|||
_panel_instance.config(cfg); |
|||
} |
|||
|
|||
{ |
|||
auto cfg = _panel_instance.config_detail(); |
|||
cfg.pin_cs = 4 | IO_EXPANDER; |
|||
cfg.pin_sclk = 41; |
|||
cfg.pin_mosi = 48; |
|||
cfg.use_psram = 1; |
|||
_panel_instance.config_detail(cfg); |
|||
} |
|||
|
|||
{ |
|||
auto cfg = _bus_instance.config(); |
|||
cfg.panel = &_panel_instance; |
|||
|
|||
cfg.freq_write = 8000000; |
|||
cfg.pin_henable = 18; |
|||
|
|||
cfg.pin_pclk = 21; |
|||
cfg.pclk_active_neg = 0; |
|||
cfg.pclk_idle_high = 0; |
|||
cfg.de_idle_high = 1; |
|||
|
|||
cfg.pin_hsync = 16; |
|||
cfg.hsync_polarity = 0; |
|||
cfg.hsync_front_porch = 10; |
|||
cfg.hsync_pulse_width = 8; |
|||
cfg.hsync_back_porch = 50; |
|||
|
|||
cfg.pin_vsync = 17; |
|||
cfg.vsync_polarity = 0; |
|||
cfg.vsync_front_porch = 10; |
|||
cfg.vsync_pulse_width = 8; |
|||
cfg.vsync_back_porch = 20; |
|||
|
|||
cfg.pin_d0 = 15; |
|||
cfg.pin_d1 = 14; |
|||
cfg.pin_d2 = 13; |
|||
cfg.pin_d3 = 12; |
|||
cfg.pin_d4 = 11; |
|||
cfg.pin_d5 = 10; |
|||
cfg.pin_d6 = 9; |
|||
cfg.pin_d7 = 8; |
|||
cfg.pin_d8 = 7; |
|||
cfg.pin_d9 = 6; |
|||
cfg.pin_d10 = 5; |
|||
cfg.pin_d11 = 4; |
|||
cfg.pin_d12 = 3; |
|||
cfg.pin_d13 = 2; |
|||
cfg.pin_d14 = 1; |
|||
cfg.pin_d15 = 0; |
|||
|
|||
_bus_instance.config(cfg); |
|||
} |
|||
_panel_instance.setBus(&_bus_instance); |
|||
|
|||
{ |
|||
auto cfg = _light_instance.config(); |
|||
cfg.pin_bl = 45; |
|||
_light_instance.config(cfg); |
|||
} |
|||
_panel_instance.light(&_light_instance); |
|||
|
|||
{ |
|||
auto cfg = _touch_instance.config(); |
|||
cfg.pin_cs = GPIO_NUM_NC; |
|||
cfg.x_min = 0; |
|||
cfg.x_max = 479; |
|||
cfg.y_min = 0; |
|||
cfg.y_max = 479; |
|||
cfg.pin_int = GPIO_NUM_NC; |
|||
cfg.pin_rst = GPIO_NUM_NC; |
|||
cfg.bus_shared = true; |
|||
cfg.offset_rotation = 0; |
|||
|
|||
cfg.i2c_port = 0; |
|||
cfg.i2c_addr = 0x48; |
|||
cfg.pin_sda = 39; |
|||
cfg.pin_scl = 40; |
|||
cfg.freq = 400000; |
|||
_touch_instance.config(cfg); |
|||
_panel_instance.setTouch(&_touch_instance); |
|||
} |
|||
|
|||
setPanel(&_panel_instance); |
|||
} |
|||
}; |
|||
|
|||
class SCIndicatorDisplay : public LGFXDisplay { |
|||
LGFX disp; |
|||
public: |
|||
SCIndicatorDisplay() : LGFXDisplay(480, 480, disp) {} |
|||
}; |
|||
@ -0,0 +1,50 @@ |
|||
[SenseCapIndicator-ESPNow] |
|||
extends = esp32_base |
|||
board = esp32-s3-devkitc-1 |
|||
board_build.arduino.memory_type = qio_opi |
|||
board_build.flash_mode = qio |
|||
board_build.psram_type = opi |
|||
board_upload.flash_size = 8MB |
|||
board_upload.maximum_size = 8388608 |
|||
board_build.partitions = default.csv |
|||
build_flags = |
|||
${esp32_base.build_flags} |
|||
-D PIN_BOARD_SDA=39 |
|||
-D PIN_BOARD_SCL=40 |
|||
-D DISPLAY_CLASS=SCIndicatorDisplay |
|||
-D DISPLAY_LINES=21 |
|||
-D LINE_LENGTH=53 |
|||
-D DISABLE_WIFI_OTA=1 |
|||
-D IO_EXPANDER=0x40 |
|||
-D IO_EXPANDER_IRQ=42 |
|||
-D UI_ZOOM=3.5 |
|||
-D UI_RECENT_LIST_SIZE=9 |
|||
-D UI_SENSORS_PAGE=1 |
|||
-D PIN_USER_BTN=38 |
|||
-D HAS_TOUCH |
|||
-I variants/sensecap_indicator-espnow |
|||
build_src_filter = ${esp32_base.build_src_filter} |
|||
+<../variants/sensecap_indicator-espnow/*.cpp> |
|||
+<helpers/esp32/ESPNOWRadio.cpp> |
|||
+<helpers/ui/LGFXDisplay.cpp> |
|||
+<helpers/sensors/*> |
|||
lib_deps=${esp32_base.lib_deps} |
|||
adafruit/Adafruit BusIO @ ^1.17.2 |
|||
lovyan03/LovyanGFX @ ^1.2.7 |
|||
|
|||
[env:SenseCapIndicator-ESPNow_comp_radio_usb] |
|||
extends =SenseCapIndicator-ESPNow |
|||
build_flags = |
|||
${SenseCapIndicator-ESPNow.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D MAX_CONTACTS=300 |
|||
-D MAX_GROUP_CHANNELS=8 |
|||
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 |
|||
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 |
|||
; NOTE: DO NOT ENABLE --> -D ESPNOW_DEBUG_LOGGING=1 |
|||
build_src_filter = ${SenseCapIndicator-ESPNow.build_src_filter} |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
lib_deps = |
|||
${SenseCapIndicator-ESPNow.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
@ -0,0 +1,56 @@ |
|||
#include <Arduino.h> |
|||
#include "target.h" |
|||
#include <helpers/ArduinoHelpers.h> |
|||
|
|||
ESP32Board board; |
|||
|
|||
ESPNOWRadio radio_driver; |
|||
|
|||
ESP32RTCClock rtc_clock; |
|||
#if defined(ENV_INCLUDE_GPS) |
|||
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, (mesh::RTCClock*)&rtc_clock); |
|||
EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); |
|||
#else |
|||
EnvironmentSensorManager sensors = EnvironmentSensorManager(); |
|||
#endif |
|||
|
|||
#ifdef DISPLAY_CLASS |
|||
DISPLAY_CLASS display; |
|||
#ifdef PIN_USER_BTN |
|||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true); |
|||
#endif |
|||
#endif |
|||
|
|||
bool radio_init() { |
|||
rtc_clock.begin(); |
|||
|
|||
radio_driver.init(); |
|||
|
|||
return true; // success
|
|||
} |
|||
|
|||
uint32_t radio_get_rng_seed() { |
|||
return millis() + radio_driver.intID(); // TODO: where to get some entropy?
|
|||
} |
|||
|
|||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { |
|||
// no-op
|
|||
} |
|||
|
|||
void radio_set_tx_power(uint8_t dbm) { |
|||
radio_driver.setTxPower(dbm); |
|||
} |
|||
|
|||
// NOTE: as we are using the WiFi radio, the ESP_IDF will have enabled hardware RNG:
|
|||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html
|
|||
class ESP_RNG : public mesh::RNG { |
|||
public: |
|||
void random(uint8_t* dest, size_t sz) override { |
|||
esp_fill_random(dest, sz); |
|||
} |
|||
}; |
|||
|
|||
mesh::LocalIdentity radio_new_identity() { |
|||
ESP_RNG rng; |
|||
return mesh::LocalIdentity(&rng); // create new random identity
|
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
#pragma once |
|||
|
|||
#include <helpers/ESP32Board.h> |
|||
#include <helpers/esp32/ESPNOWRadio.h> |
|||
#include <helpers/SensorManager.h> |
|||
#include <helpers/sensors/EnvironmentSensorManager.h> |
|||
#ifdef ENV_INCLUDE_GPS |
|||
#include <helpers/sensors/MicroNMEALocationProvider.h> |
|||
#endif |
|||
#ifdef DISPLAY_CLASS |
|||
#include "SCIndicatorDisplay.h" |
|||
#include <helpers/ui/MomentaryButton.h> |
|||
#endif |
|||
|
|||
extern ESP32Board board; |
|||
extern ESPNOWRadio radio_driver; |
|||
extern ESP32RTCClock rtc_clock; |
|||
extern EnvironmentSensorManager sensors; |
|||
|
|||
#ifdef DISPLAY_CLASS |
|||
extern DISPLAY_CLASS display; |
|||
extern MomentaryButton user_btn; |
|||
#endif |
|||
|
|||
bool radio_init(); |
|||
uint32_t radio_get_rng_seed(); |
|||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); |
|||
void radio_set_tx_power(uint8_t dbm); |
|||
mesh::LocalIdentity radio_new_identity(); |
|||
@ -1,22 +1,7 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> |
|||
|
|||
// LoRa radio module pins for Station G2
|
|||
#define P_LORA_DIO_1 48 |
|||
#define P_LORA_NSS 11 |
|||
#define P_LORA_RESET 21 |
|||
#define P_LORA_BUSY 47 |
|||
#define P_LORA_SCLK 12 |
|||
#define P_LORA_MISO 14 |
|||
#define P_LORA_MOSI 13 |
|||
|
|||
// built-ins
|
|||
//#define PIN_LED_BUILTIN 35
|
|||
//#define PIN_VEXT_EN 36
|
|||
|
|||
#include "ESP32Board.h" |
|||
|
|||
#include <helpers/ESP32Board.h> |
|||
#include <driver/rtc_io.h> |
|||
|
|||
class StationG2Board : public ESP32Board { |
|||
@ -0,0 +1,32 @@ |
|||
#include "ThinknodeM2Board.h" |
|||
|
|||
|
|||
|
|||
void ThinknodeM2Board::begin() { |
|||
ESP32Board::begin(); |
|||
pinMode(PIN_VEXT_EN, OUTPUT); // init display
|
|||
digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); // pin needs to be high
|
|||
delay(10); |
|||
digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); // need to do this twice. do not know why..
|
|||
pinMode(PIN_STATUS_LED, OUTPUT); // init power led
|
|||
} |
|||
|
|||
void ThinknodeM2Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { |
|||
esp_deep_sleep_start(); |
|||
} |
|||
|
|||
void ThinknodeM2Board::powerOff() { |
|||
enterDeepSleep(0); |
|||
} |
|||
|
|||
uint16_t ThinknodeM2Board::getBattMilliVolts() { |
|||
analogReadResolution(12); |
|||
delay(10); |
|||
float volts = (analogRead(PIN_VBAT_READ) * ADC_MULTIPLIER * AREF_VOLTAGE) / 4096; |
|||
analogReadResolution(10); |
|||
return volts * 1000; |
|||
} |
|||
|
|||
const char* ThinknodeM2Board::getManufacturerName() const { |
|||
return "Elecrow ThinkNode M2"; |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> |
|||
#include <helpers/RefCountedDigitalPin.h> |
|||
#include <helpers/ESP32Board.h> |
|||
#include <driver/rtc_io.h> |
|||
|
|||
class ThinknodeM2Board : public ESP32Board { |
|||
|
|||
public: |
|||
|
|||
void begin(); |
|||
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); |
|||
void powerOff() override; |
|||
uint16_t getBattMilliVolts() override; |
|||
const char* getManufacturerName() const override ; |
|||
|
|||
}; |
|||
@ -0,0 +1,28 @@ |
|||
// Need this file for ESP32-S3
|
|||
// No need to modify this file, changes to pins imported from variant.h
|
|||
// Most is similar to https://github.com/espressif/arduino-esp32/blob/master/variants/esp32s3/pins_arduino.h
|
|||
|
|||
#ifndef Pins_Arduino_h |
|||
#define Pins_Arduino_h |
|||
|
|||
#include <stdint.h> |
|||
#include <variant.h> |
|||
|
|||
#define USB_VID 0x303a |
|||
#define USB_PID 0x1001 |
|||
|
|||
// Serial
|
|||
static const uint8_t TX = GPS_TX; |
|||
static const uint8_t RX = GPS_RX; |
|||
|
|||
// Default SPI will be mapped to Radio
|
|||
static const uint8_t SS = P_LORA_NSS; |
|||
static const uint8_t SCK = P_LORA_SCLK; |
|||
static const uint8_t MOSI = P_LORA_MISO; |
|||
static const uint8_t MISO = P_LORA_MOSI; |
|||
|
|||
// The default Wire will be mapped to PMU and RTC
|
|||
static const uint8_t SCL = PIN_BOARD_SCL; |
|||
static const uint8_t SDA = PIN_BOARD_SDA; |
|||
|
|||
#endif /* Pins_Arduino_h */ |
|||
@ -0,0 +1,171 @@ |
|||
[ThinkNode_M2] |
|||
extends = esp32_base |
|||
board = ESP32-S3-WROOM-1-N4 |
|||
build_flags = ${esp32_base.build_flags} |
|||
-I variants/thinknode_m2 |
|||
-D THINKNODE_M2 |
|||
-D GPS_RX=44 |
|||
-D GPS_TX=43 |
|||
-D PIN_VEXT_EN=46 |
|||
-D PIN_BUZZER=5 |
|||
-D PIN_VEXT_EN_ACTIVE=HIGH |
|||
-D PIN_BOARD_SCL=15 |
|||
-D PIN_BOARD_SDA=16 |
|||
-D P_LORA_DIO_1=3 |
|||
-D P_LORA_NSS=10 |
|||
-D P_LORA_RESET=21 ; RADIOLIB_NC |
|||
-D P_LORA_BUSY=14 ; DIO2 = 38 |
|||
-D P_LORA_SCLK=12 |
|||
-D P_LORA_MISO=13 |
|||
-D P_LORA_MOSI=11 |
|||
-D PIN_USER_BTN=47 |
|||
-D PIN_STATUS_LED=6 |
|||
-D PIN_LED=6 |
|||
-D SX126X_DIO2_AS_RF_SWITCH=true |
|||
-D SX126X_DIO3_TCXO_VOLTAGE=3.3 |
|||
-D SX126X_CURRENT_LIMIT=140 |
|||
-D DISPLAY_CLASS=SH1106Display |
|||
-D RADIO_CLASS=CustomSX1262 |
|||
-D WRAPPER_CLASS=CustomSX1262Wrapper |
|||
-D LORA_TX_POWER=22 |
|||
-D SX126X_RX_BOOSTED_GAIN=1 |
|||
-D MESH_DEBUG=1 |
|||
build_src_filter = ${esp32_base.build_src_filter} |
|||
+<helpers/ui/SH1106Display.cpp> |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<helpers/ui/buzzer.cpp> |
|||
+<../variants/thinknode_m2> |
|||
lib_deps = ${esp32_base.lib_deps} |
|||
adafruit/Adafruit SH110X @ ~2.1.13 |
|||
adafruit/Adafruit GFX Library @ ^1.12.1 |
|||
|
|||
[env:ThinkNode_M2_Repeater] |
|||
extends = ThinkNode_M2 |
|||
build_src_filter = ${ThinkNode_M2.build_src_filter} |
|||
+<../examples/simple_repeater/*.cpp> |
|||
build_flags = |
|||
${ThinkNode_M2.build_flags} |
|||
-D ADVERT_NAME='"Thinknode M2 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 |
|||
lib_deps = |
|||
${ThinkNode_M2.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
; [env:ThinkNode_M2_Repeater_bridge_rs232] |
|||
; extends = ThinkNode_M2 |
|||
; build_src_filter = ${ThinkNode_M2.build_src_filter} |
|||
; +<helpers/bridges/RS232Bridge.cpp> |
|||
; +<../examples/simple_repeater/*.cpp> |
|||
; build_flags = |
|||
; ${ThinkNode_M2.build_flags} |
|||
; -D ADVERT_NAME='"RS232 Bridge"' |
|||
; -D ADVERT_LAT=0.0 |
|||
; -D ADVERT_LON=0.0 |
|||
; -D ADMIN_PASSWORD='"password"' |
|||
; -D MAX_NEIGHBOURS=8 |
|||
; -D WITH_RS232_BRIDGE=Serial2 |
|||
; -D WITH_RS232_BRIDGE_RX=5 |
|||
; -D WITH_RS232_BRIDGE_TX=6 |
|||
; ; -D MESH_PACKET_LOGGING=1 |
|||
; ; -D MESH_DEBUG=1 |
|||
; lib_deps = |
|||
; ${ThinkNode_M2.lib_deps} |
|||
; ${esp32_ota.lib_deps} |
|||
|
|||
[env:ThinkNode_M2_Repeater_bridge_espnow] |
|||
extends = ThinkNode_M2 |
|||
build_src_filter = ${ThinkNode_M2.build_src_filter} |
|||
+<helpers/bridges/ESPNowBridge.cpp> |
|||
+<../examples/simple_repeater/*.cpp> |
|||
build_flags = |
|||
${ThinkNode_M2.build_flags} |
|||
-D ADVERT_NAME='"ESPNow Bridge"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D MAX_NEIGHBOURS=8 |
|||
-D WITH_ESPNOW_BRIDGE=1 |
|||
-D WITH_ESPNOW_BRIDGE_SECRET='"shared-secret"' |
|||
; -D MESH_PACKET_LOGGING=1 |
|||
; -D MESH_DEBUG=1 |
|||
lib_deps = |
|||
${ThinkNode_M2.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:ThinkNode_M2_room_server] |
|||
extends = ThinkNode_M2 |
|||
build_src_filter = ${ThinkNode_M2.build_src_filter} |
|||
+<../examples/simple_room_server> |
|||
build_flags = |
|||
${ThinkNode_M2.build_flags} |
|||
-D ADVERT_NAME='"Thinknode M2 Room Server"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D ROOM_PASSWORD='"hello"' |
|||
; -D MESH_PACKET_LOGGING=1 |
|||
; -D MESH_DEBUG=1 |
|||
lib_deps = |
|||
${ThinkNode_M2.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:ThinkNode_M2_terminal_chat] |
|||
extends = ThinkNode_M2 |
|||
build_flags = |
|||
${ThinkNode_M2.build_flags} |
|||
-D MAX_CONTACTS=300 |
|||
-D MAX_GROUP_CHANNELS=8 |
|||
; -D MESH_PACKET_LOGGING=1 |
|||
; -D MESH_DEBUG=1 |
|||
build_src_filter = ${ThinkNode_M2.build_src_filter} |
|||
+<../examples/simple_secure_chat/main.cpp> |
|||
lib_deps = |
|||
${ThinkNode_M2.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:ThinkNode_M2_companion_radio_ble] |
|||
extends = ThinkNode_M2 |
|||
build_flags = |
|||
${ThinkNode_M2.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D MAX_CONTACTS=300 |
|||
-D MAX_GROUP_CHANNELS=8 |
|||
-D BLE_PIN_CODE=123456 |
|||
-D OFFLINE_QUEUE_SIZE=256 |
|||
; -D BLE_DEBUG_LOGGING=1 |
|||
; -D MESH_PACKET_LOGGING=1 |
|||
; -D MESH_DEBUG=1 |
|||
build_src_filter = ${ThinkNode_M2.build_src_filter} |
|||
+<helpers/esp32/*.cpp> |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
|
|||
lib_deps = |
|||
${ThinkNode_M2.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:ThinkNode_M2_companion_radio_serial] |
|||
extends = ThinkNode_M2 |
|||
build_flags = |
|||
${ThinkNode_M2.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D MAX_CONTACTS=300 |
|||
-D MAX_GROUP_CHANNELS=8 |
|||
-D SERIAL_TX=D6 |
|||
-D SERIAL_RX=D7 |
|||
; -D MESH_PACKET_LOGGING=1 |
|||
; -D MESH_DEBUG=1 |
|||
build_src_filter = ${ThinkNode_M2.build_src_filter} |
|||
+<helpers/esp32/*.cpp> |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${ThinkNode_M2.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
@ -0,0 +1,57 @@ |
|||
#include <Arduino.h> |
|||
#include "target.h" |
|||
|
|||
ThinknodeM2Board board; |
|||
|
|||
#if defined(P_LORA_SCLK) |
|||
static SPIClass spi; |
|||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); |
|||
#else |
|||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); |
|||
#endif |
|||
|
|||
WRAPPER_CLASS radio_driver(radio, board); |
|||
|
|||
ESP32RTCClock fallback_clock; |
|||
AutoDiscoverRTCClock rtc_clock(fallback_clock); |
|||
SensorManager sensors; |
|||
|
|||
#ifdef DISPLAY_CLASS |
|||
DISPLAY_CLASS display; |
|||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true); |
|||
#endif |
|||
|
|||
bool radio_init() { |
|||
fallback_clock.begin(); |
|||
rtc_clock.begin(Wire); |
|||
pinMode(21, INPUT); |
|||
pinMode(48, OUTPUT); |
|||
#if defined(P_LORA_SCLK) |
|||
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); |
|||
return radio.std_init(&spi); |
|||
#else |
|||
return radio.std_init(); |
|||
#endif |
|||
} |
|||
|
|||
uint32_t radio_get_rng_seed() { |
|||
return radio.random(0x7FFFFFFF); |
|||
} |
|||
|
|||
|
|||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { |
|||
radio.setFrequency(freq); |
|||
radio.setSpreadingFactor(sf); |
|||
radio.setBandwidth(bw); |
|||
radio.setCodingRate(cr); |
|||
} |
|||
|
|||
void radio_set_tx_power(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,32 @@ |
|||
#pragma once |
|||
|
|||
#define RADIOLIB_STATIC_ONLY 1 |
|||
#include <RadioLib.h> |
|||
#include <helpers/radiolib/RadioLibWrappers.h> |
|||
//#include <helpers/ESP32Board.h>
|
|||
#include <ThinknodeM2Board.h> |
|||
#include <helpers/radiolib/CustomSX1262Wrapper.h> |
|||
#include <helpers/AutoDiscoverRTCClock.h> |
|||
#include <helpers/SensorManager.h> |
|||
#ifdef DISPLAY_CLASS |
|||
#include <helpers/ui/SH1106Display.h> |
|||
#include <helpers/ui/MomentaryButton.h> |
|||
#endif |
|||
|
|||
extern ThinknodeM2Board board; |
|||
extern WRAPPER_CLASS radio_driver; |
|||
extern AutoDiscoverRTCClock rtc_clock; |
|||
extern SensorManager sensors; |
|||
|
|||
#ifdef DISPLAY_CLASS |
|||
extern DISPLAY_CLASS display; |
|||
extern MomentaryButton user_btn; |
|||
#endif |
|||
|
|||
bool radio_init(); |
|||
uint32_t radio_get_rng_seed(); |
|||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); |
|||
void radio_set_tx_power(uint8_t dbm); |
|||
mesh::LocalIdentity radio_new_identity(); |
|||
|
|||
|
|||
@ -0,0 +1,15 @@ |
|||
#define I2C_SCL 15 |
|||
#define I2C_SDA 16 |
|||
#define PIN_VBAT_READ 17 |
|||
#define AREF_VOLTAGE (3.0) |
|||
#define ADC_MULTIPLIER (1.548F) |
|||
#define PIN_BUZZER 5 |
|||
#define PIN_VEXT_EN_ACTIVE HIGH |
|||
#define PIN_VEXT_EN 46 |
|||
#define PIN_USER_BTN 47 |
|||
#define PIN_LED 6 |
|||
#define PIN_STATUS_LED 6 |
|||
#define PIN_PWRBTN 4 |
|||
|
|||
|
|||
|
|||
@ -0,0 +1,75 @@ |
|||
#ifdef WIO_WM1110 |
|||
|
|||
#include <Arduino.h> |
|||
#include <Wire.h> |
|||
#include <bluefruit.h> |
|||
|
|||
#include "WioWM1110Board.h" |
|||
|
|||
static BLEDfu bledfu; |
|||
|
|||
static void connect_callback(uint16_t conn_handle) { |
|||
(void)conn_handle; |
|||
MESH_DEBUG_PRINTLN("BLE client connected"); |
|||
} |
|||
|
|||
static void disconnect_callback(uint16_t conn_handle, uint8_t reason) { |
|||
(void)conn_handle; |
|||
(void)reason; |
|||
|
|||
MESH_DEBUG_PRINTLN("BLE client disconnected"); |
|||
} |
|||
|
|||
void WioWM1110Board::begin() { |
|||
startup_reason = BD_STARTUP_NORMAL; |
|||
|
|||
sd_power_mode_set(NRF_POWER_MODE_LOWPWR); |
|||
NRF_POWER->DCDCEN = 1; |
|||
|
|||
pinMode(BATTERY_PIN, INPUT); |
|||
pinMode(LED_GREEN, OUTPUT); |
|||
pinMode(LED_RED, OUTPUT); |
|||
pinMode(SENSOR_POWER_PIN, OUTPUT); |
|||
|
|||
digitalWrite(LED_GREEN, HIGH); |
|||
digitalWrite(LED_RED, LOW); |
|||
digitalWrite(SENSOR_POWER_PIN, LOW); |
|||
|
|||
Serial1.begin(115200); |
|||
|
|||
#if defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL) |
|||
Wire.setPins(PIN_WIRE_SDA, PIN_WIRE_SCL); |
|||
#endif |
|||
|
|||
Wire.begin(); |
|||
|
|||
delay(10); |
|||
} |
|||
|
|||
bool WioWM1110Board::startOTAUpdate(const char *id, char reply[]) { |
|||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); |
|||
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); |
|||
|
|||
Bluefruit.begin(1, 0); |
|||
Bluefruit.setTxPower(4); |
|||
Bluefruit.setName("WM1110_OTA"); |
|||
|
|||
Bluefruit.Periph.setConnectCallback(connect_callback); |
|||
Bluefruit.Periph.setDisconnectCallback(disconnect_callback); |
|||
|
|||
bledfu.begin(); |
|||
|
|||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); |
|||
Bluefruit.Advertising.addTxPower(); |
|||
Bluefruit.Advertising.addName(); |
|||
Bluefruit.Advertising.restartOnDisconnect(true); |
|||
Bluefruit.Advertising.setInterval(32, 244); |
|||
Bluefruit.Advertising.setFastTimeout(30); |
|||
Bluefruit.Advertising.start(0); |
|||
|
|||
strcpy(reply, "OK - started"); |
|||
return true; |
|||
} |
|||
|
|||
#endif |
|||
|
|||
@ -0,0 +1,58 @@ |
|||
#pragma once |
|||
|
|||
#include <MeshCore.h> |
|||
#include <Arduino.h> |
|||
|
|||
#ifdef WIO_WM1110 |
|||
|
|||
#ifdef Serial |
|||
#undef Serial |
|||
#endif |
|||
#define Serial Serial1 |
|||
|
|||
class WioWM1110Board : public mesh::MainBoard { |
|||
protected: |
|||
uint8_t startup_reason; |
|||
|
|||
public: |
|||
void begin(); |
|||
uint8_t getStartupReason() const override { return startup_reason; } |
|||
|
|||
#if defined(LED_GREEN) |
|||
void onBeforeTransmit() override { |
|||
digitalWrite(LED_RED, HIGH); |
|||
} |
|||
void onAfterTransmit() override { |
|||
digitalWrite(LED_RED, LOW); |
|||
} |
|||
#endif |
|||
|
|||
uint16_t getBattMilliVolts() override { |
|||
int adcvalue = 0; |
|||
analogReadResolution(12); |
|||
analogReference(AR_INTERNAL_3_0); |
|||
delay(10); |
|||
adcvalue = analogRead(BATTERY_PIN); |
|||
return (adcvalue * ADC_MULTIPLIER * AREF_VOLTAGE * 1000.0) / 4096.0; |
|||
} |
|||
|
|||
const char* getManufacturerName() const override { |
|||
return "Seeed Wio WM1110"; |
|||
} |
|||
|
|||
void reboot() override { |
|||
NVIC_SystemReset(); |
|||
} |
|||
|
|||
bool startOTAUpdate(const char* id, char reply[]) override; |
|||
|
|||
void enableSensorPower(bool enable) { |
|||
digitalWrite(SENSOR_POWER_PIN, enable ? HIGH : LOW); |
|||
if (enable) { |
|||
delay(100); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
#endif |
|||
|
|||
@ -0,0 +1,86 @@ |
|||
[wio_wm1110] |
|||
extends = nrf52_base |
|||
board = seeed-xiao-afruitnrf52-nrf52840 |
|||
board_build.ldscript = boards/nrf52840_s140_v7.ld |
|||
build_flags = ${nrf52_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/nrf52 |
|||
-I variants/wio_wm1110 |
|||
-D NRF52_PLATFORM |
|||
-D WIO_WM1110 |
|||
; -D MESH_DEBUG=1 |
|||
-D RADIO_CLASS=CustomLR1110 |
|||
-D WRAPPER_CLASS=CustomLR1110Wrapper |
|||
-D LORA_TX_POWER=22 |
|||
-D RX_BOOSTED_GAIN=true |
|||
-D P_LORA_DIO_1=40 |
|||
-D P_LORA_RESET=42 |
|||
-D P_LORA_BUSY=43 |
|||
-D P_LORA_NSS=44 |
|||
-D P_LORA_SCLK=45 |
|||
-D P_LORA_MOSI=46 |
|||
-D P_LORA_MISO=47 |
|||
-D LR11X0_DIO_AS_RF_SWITCH=true |
|||
-D LR11X0_DIO3_TCXO_VOLTAGE=1.8 |
|||
-D RF_SWITCH_TABLE |
|||
-D ENV_INCLUDE_GPS=0 |
|||
build_src_filter = ${nrf52_base.build_src_filter} |
|||
+<helpers/*.cpp> |
|||
+<helpers/sensors> |
|||
+<../variants/wio_wm1110> |
|||
debug_tool = jlink |
|||
upload_protocol = jlink |
|||
lib_deps = ${nrf52_base.lib_deps} |
|||
${sensor_base.lib_deps} |
|||
adafruit/Adafruit LIS3DH @ ^1.2.4 |
|||
adafruit/Adafruit SHT4x Library @ ^1.0.4 |
|||
|
|||
[env:wio_wm1110_repeater] |
|||
extends = wio_wm1110 |
|||
build_flags = |
|||
${wio_wm1110.build_flags} |
|||
-D ADVERT_NAME='"WM1110 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 = ${wio_wm1110.build_src_filter} |
|||
+<../examples/simple_repeater/*.cpp> |
|||
|
|||
[env:wio_wm1110_room_server] |
|||
extends = wio_wm1110 |
|||
build_flags = |
|||
${wio_wm1110.build_flags} |
|||
-D ADVERT_NAME='"WM1110 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 = ${wio_wm1110.build_src_filter} |
|||
+<../examples/simple_room_server/*.cpp> |
|||
|
|||
[env:wio_wm1110_companion_radio_ble] |
|||
extends = wio_wm1110 |
|||
board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld |
|||
board_upload.maximum_size = 708608 |
|||
build_flags = |
|||
${wio_wm1110.build_flags} |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D BLE_PIN_CODE=123456 |
|||
-D QSPIFLASH=1 |
|||
-D OFFLINE_QUEUE_SIZE=256 |
|||
; -D BLE_DEBUG_LOGGING=1 |
|||
; -D MESH_PACKET_LOGGING=1 |
|||
; -D MESH_DEBUG=1 |
|||
build_src_filter = ${wio_wm1110.build_src_filter} |
|||
+<helpers/nrf52/SerialBLEInterface.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
lib_deps = |
|||
${wio_wm1110.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
@ -0,0 +1,92 @@ |
|||
#include <Arduino.h> |
|||
#include "target.h" |
|||
#include <helpers/ArduinoHelpers.h> |
|||
|
|||
WioWM1110Board board; |
|||
|
|||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); |
|||
|
|||
WRAPPER_CLASS radio_driver(radio, board); |
|||
|
|||
VolatileRTCClock rtc_clock; |
|||
EnvironmentSensorManager sensors; |
|||
|
|||
#ifndef LORA_CR |
|||
#define LORA_CR 5 |
|||
#endif |
|||
|
|||
#ifdef RF_SWITCH_TABLE |
|||
static const uint32_t rfswitch_dios[Module::RFSWITCH_MAX_PINS] = { |
|||
RADIOLIB_LR11X0_DIO5, |
|||
RADIOLIB_LR11X0_DIO6, |
|||
RADIOLIB_LR11X0_DIO7, |
|||
RADIOLIB_LR11X0_DIO8, |
|||
RADIOLIB_NC |
|||
}; |
|||
|
|||
static const Module::RfSwitchMode_t rfswitch_table[] = { |
|||
// mode DIO5 DIO6 DIO7 DIO8
|
|||
{ LR11x0::MODE_STBY, {LOW, LOW, LOW, LOW }}, |
|||
{ LR11x0::MODE_RX, {HIGH, LOW, LOW, HIGH }}, |
|||
{ LR11x0::MODE_TX, {HIGH, HIGH, LOW, HIGH }}, |
|||
{ LR11x0::MODE_TX_HP, {LOW, HIGH, LOW, HIGH }}, |
|||
{ LR11x0::MODE_TX_HF, {LOW, LOW, LOW, LOW }}, |
|||
{ LR11x0::MODE_GNSS, {LOW, LOW, HIGH, LOW }}, |
|||
{ LR11x0::MODE_WIFI, {LOW, LOW, LOW, LOW }}, |
|||
END_OF_MODE_TABLE, |
|||
}; |
|||
#endif |
|||
|
|||
bool radio_init() { |
|||
board.enableSensorPower(true); |
|||
|
|||
#ifdef LR11X0_DIO3_TCXO_VOLTAGE |
|||
float tcxo = LR11X0_DIO3_TCXO_VOLTAGE; |
|||
#else |
|||
float tcxo = 1.8f; |
|||
#endif |
|||
|
|||
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); |
|||
SPI.begin(); |
|||
|
|||
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); |
|||
if (status != RADIOLIB_ERR_NONE) { |
|||
Serial.print("ERROR: radio init failed: "); |
|||
Serial.println(status); |
|||
return false; // fail
|
|||
} |
|||
|
|||
radio.setCRC(2); |
|||
radio.explicitHeader(); |
|||
|
|||
#ifdef RF_SWITCH_TABLE |
|||
radio.setRfSwitchTable(rfswitch_dios, rfswitch_table); |
|||
#endif |
|||
|
|||
#ifdef RX_BOOSTED_GAIN |
|||
radio.setRxBoostedGainMode(RX_BOOSTED_GAIN); |
|||
#endif |
|||
|
|||
return true; // success
|
|||
} |
|||
|
|||
uint32_t radio_get_rng_seed() { |
|||
return radio.random(0x7FFFFFFF); |
|||
} |
|||
|
|||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { |
|||
radio.setFrequency(freq); |
|||
radio.setSpreadingFactor(sf); |
|||
radio.setBandwidth(bw); |
|||
radio.setCodingRate(cr); |
|||
} |
|||
|
|||
void radio_set_tx_power(uint8_t dbm) { |
|||
radio.setOutputPower(dbm); |
|||
} |
|||
|
|||
mesh::LocalIdentity radio_new_identity() { |
|||
RadioNoiseListener rng(radio); |
|||
return mesh::LocalIdentity(&rng); // create new random identity
|
|||
} |
|||
|
|||
@ -0,0 +1,21 @@ |
|||
#pragma once |
|||
|
|||
#define RADIOLIB_STATIC_ONLY 1 |
|||
#include <RadioLib.h> |
|||
#include <helpers/radiolib/RadioLibWrappers.h> |
|||
#include "WioWM1110Board.h" |
|||
#include <helpers/radiolib/CustomLR1110Wrapper.h> |
|||
#include <helpers/ArduinoHelpers.h> |
|||
#include <helpers/sensors/EnvironmentSensorManager.h> |
|||
|
|||
extern WioWM1110Board board; |
|||
extern WRAPPER_CLASS radio_driver; |
|||
extern VolatileRTCClock 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(); |
|||
|
|||
@ -0,0 +1,92 @@ |
|||
/*
|
|||
* variant.cpp - Seeed Wio WM1110 Dev Board |
|||
* Pin mapping for nRF52840 |
|||
*/ |
|||
|
|||
#include "variant.h" |
|||
#include "wiring_constants.h" |
|||
#include "wiring_digital.h" |
|||
|
|||
const uint32_t g_ADigitalPinMap[PINS_COUNT + 1] = |
|||
{ |
|||
0, // P0.00
|
|||
1, // P0.01
|
|||
2, // P0.02, AIN0, SENSOR_AIN_0
|
|||
3, // P0.03, AIN1, SENSOR_AIN_1
|
|||
4, // P0.04, AIN2, SENSOR_AIN_2
|
|||
5, // P0.05, AIN3, SENSOR_AIN_3
|
|||
6, // P0.06, PIN_SERIAL2_RX, SENSOR_RXD
|
|||
7, // P0.07, SENSOR_POWER_PIN
|
|||
8, // P0.08, PIN_SERIAL2_TX, SENSOR_TXD
|
|||
9, // P0.09
|
|||
10, // P0.10
|
|||
11, // P0.11, LIS3DH_INT_PIN_1, SENSOR_INT_1
|
|||
12, // P0.12, LIS3DH_INT_PIN_2, SENSOR_INT_2
|
|||
13, // P0.13, LED_GREEN, USER_LED_G
|
|||
14, // P0.14, LED_RED, USER_LED_R
|
|||
15, // P0.15
|
|||
16, // P0.16
|
|||
17, // P0.17
|
|||
18, // P0.18
|
|||
19, // P0.19
|
|||
20, // P0.20
|
|||
21, // P0.21
|
|||
22, // P0.22, PIN_SERIAL1_RX, DEBUG_RX_PIN
|
|||
23, // P0.23
|
|||
24, // P0.24, PIN_SERIAL1_TX, DEBUG_TX_PIN
|
|||
25, // P0.25
|
|||
26, // P0.26, PIN_WIRE_SCL, SENSOR_SCL
|
|||
27, // P0.27, PIN_WIRE_SDA, SENSOR_SDA
|
|||
28, // P0.28, AIN4, SENSOR_AIN_4
|
|||
29, // P0.29, AIN5, SENSOR_AIN_5
|
|||
30, // P0.30, AIN6, SENSOR_AIN_6
|
|||
31, // P0.31, AIN7, SENSOR_AIN_7, BATTERY_PIN
|
|||
32, // P1.00
|
|||
33, // P1.01
|
|||
34, // P1.02
|
|||
35, // P1.03
|
|||
36, // P1.04
|
|||
37, // P1.05, LR1110_GNSS_ANT_PIN
|
|||
38, // P1.06
|
|||
39, // P1.07
|
|||
40, // P1.08, LORA_DIO_1, LR1110_IRQ_PIN
|
|||
41, // P1.09
|
|||
42, // P1.10, LORA_RESET, LR1110_NRESET_PIN
|
|||
43, // P1.11, LORA_BUSY, LR1110_BUSY_PIN
|
|||
44, // P1.12, PIN_SPI_NSS, LR1110_SPI_NSS_PIN
|
|||
45, // P1.13, PIN_SPI_SCK, LR1110_SPI_SCK_PIN
|
|||
46, // P1.14, PIN_SPI_MOSI, LR1110_SPI_MOSI_PIN
|
|||
47, // P1.15, PIN_SPI_MISO, LR1110_SPI_MISO_PIN
|
|||
255, // NRFX_SPIM_PIN_NOT_USED
|
|||
}; |
|||
|
|||
void initVariant() |
|||
{ |
|||
// All pins output HIGH by default.
|
|||
// https://github.com/Seeed-Studio/Adafruit_nRF52_Arduino/blob/fab7d30a997a1dfeef9d1d59bfb549adda73815a/cores/nRF5/wiring.c#L65-L69
|
|||
|
|||
// Set analog input pins
|
|||
pinMode(BATTERY_PIN, INPUT); |
|||
pinMode(SENSOR_AIN_0, INPUT); |
|||
pinMode(SENSOR_AIN_1, INPUT); |
|||
pinMode(SENSOR_AIN_2, INPUT); |
|||
pinMode(SENSOR_AIN_3, INPUT); |
|||
pinMode(SENSOR_AIN_4, INPUT); |
|||
pinMode(SENSOR_AIN_5, INPUT); |
|||
pinMode(SENSOR_AIN_6, INPUT); |
|||
|
|||
// Sensor interrupts as inputs
|
|||
pinMode(LIS3DH_INT_PIN_1, INPUT); |
|||
pinMode(LIS3DH_INT_PIN_2, INPUT); |
|||
|
|||
// Set output pins
|
|||
pinMode(LED_GREEN, OUTPUT); |
|||
pinMode(LED_RED, OUTPUT); |
|||
pinMode(SENSOR_POWER_PIN, OUTPUT); |
|||
|
|||
// Initialize outputs to safe states
|
|||
digitalWrite(LED_GREEN, HIGH); // Power indicator LED on
|
|||
digitalWrite(LED_RED, LOW); |
|||
digitalWrite(SENSOR_POWER_PIN, LOW); // Sensors powered off initially
|
|||
} |
|||
|
|||
@ -0,0 +1,145 @@ |
|||
/*
|
|||
* variant.h - Seeed Wio WM1110 Dev Board |
|||
* nRF52840 + LR1110 (LoRa + GNSS + WiFi Scanner) |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include "WVariant.h" |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// Low frequency clock source
|
|||
|
|||
#define USE_LFXO // 32.768 kHz crystal oscillator
|
|||
#define VARIANT_MCK (64000000ul) |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// Power
|
|||
|
|||
#define BATTERY_PIN (31) // AIN7
|
|||
#define BATTERY_IMMUTABLE |
|||
#define ADC_MULTIPLIER (2.0F) |
|||
|
|||
#define ADC_RESOLUTION (14) |
|||
#define BATTERY_SENSE_RES (12) |
|||
|
|||
#define AREF_VOLTAGE (3.0) |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// Number of pins
|
|||
|
|||
#define PINS_COUNT (48) |
|||
#define NUM_DIGITAL_PINS (48) |
|||
#define NUM_ANALOG_INPUTS (8) |
|||
#define NUM_ANALOG_OUTPUTS (0) |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// UART pin definition
|
|||
|
|||
#define PIN_SERIAL1_RX (22) |
|||
#define PIN_SERIAL1_TX (24) |
|||
|
|||
#define PIN_SERIAL2_RX (6) |
|||
#define PIN_SERIAL2_TX (8) |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// I2C pin definition
|
|||
|
|||
#define HAS_WIRE (1) |
|||
#define WIRE_INTERFACES_COUNT (1) |
|||
|
|||
#define PIN_WIRE_SDA (27) |
|||
#define PIN_WIRE_SCL (26) |
|||
#define I2C_NO_RESCAN |
|||
|
|||
#define SENSOR_POWER_PIN (7) |
|||
|
|||
#define HAS_LIS3DH (1) |
|||
#define LIS3DH_INT_PIN_1 (11) |
|||
#define LIS3DH_INT_PIN_2 (12) |
|||
|
|||
#define HAS_SHT41 (1) |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// SPI pin definition
|
|||
|
|||
#define SPI_INTERFACES_COUNT (1) |
|||
|
|||
#define PIN_SPI_MISO (47) |
|||
#define PIN_SPI_MOSI (46) |
|||
#define PIN_SPI_SCK (45) |
|||
#define PIN_SPI_NSS (44) |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// Builtin LEDs
|
|||
|
|||
#define LED_BUILTIN (13) |
|||
#define LED_GREEN (13) |
|||
#define LED_RED (14) |
|||
#define LED_BLUE LED_RED |
|||
#define LED_PIN LED_GREEN |
|||
|
|||
#define LED_STATE_ON HIGH |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// Builtin buttons
|
|||
|
|||
#define PIN_BUTTON1 (-1) |
|||
#define BUTTON_PIN PIN_BUTTON1 |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// LR1110 LoRa Radio + GNSS + WiFi
|
|||
|
|||
#define LORA_DIO_1 (40) // P1.8 - LR1110_IRQ_PIN
|
|||
#define LORA_NSS (PIN_SPI_NSS) // P1.12
|
|||
#define LORA_RESET (42) // P1.10 - LR1110_NRESET_PIN
|
|||
#define LORA_BUSY (43) // P1.11 - LR1110_BUSY_PIN
|
|||
#define LORA_SCLK (PIN_SPI_SCK) // P1.13
|
|||
#define LORA_MISO (PIN_SPI_MISO) // P1.15
|
|||
#define LORA_MOSI (PIN_SPI_MOSI) // P1.14
|
|||
#define LORA_CS PIN_SPI_NSS // P1.12
|
|||
|
|||
// LR1110 specific settings
|
|||
#define LR11X0_DIO_AS_RF_SWITCH true |
|||
#define LR11X0_DIO3_TCXO_VOLTAGE 1.8 |
|||
#define LR1110_GNSS_ANT_PIN (37) // P1.5
|
|||
|
|||
// Pin aliases for LR1110 driver compatibility
|
|||
#define LR1110_IRQ_PIN LORA_DIO_1 |
|||
#define LR1110_NRESET_PIN LORA_RESET |
|||
#define LR1110_BUSY_PIN LORA_BUSY |
|||
#define LR1110_SPI_NSS_PIN LORA_CS |
|||
#define LR1110_SPI_SCK_PIN LORA_SCLK |
|||
#define LR1110_SPI_MOSI_PIN LORA_MOSI |
|||
#define LR1110_SPI_MISO_PIN LORA_MISO |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// Analog Input Pins
|
|||
|
|||
#define SENSOR_AIN_0 (2) |
|||
#define SENSOR_AIN_1 (3) |
|||
#define SENSOR_AIN_2 (4) |
|||
#define SENSOR_AIN_3 (5) |
|||
#define SENSOR_AIN_4 (28) |
|||
#define SENSOR_AIN_5 (29) |
|||
#define SENSOR_AIN_6 (30) |
|||
#define SENSOR_AIN_7 (31) |
|||
|
|||
static const uint8_t A0 = SENSOR_AIN_0; |
|||
static const uint8_t A1 = SENSOR_AIN_1; |
|||
static const uint8_t A2 = SENSOR_AIN_2; |
|||
static const uint8_t A3 = SENSOR_AIN_3; |
|||
static const uint8_t A4 = SENSOR_AIN_4; |
|||
static const uint8_t A5 = SENSOR_AIN_5; |
|||
static const uint8_t A6 = SENSOR_AIN_6; |
|||
static const uint8_t A7 = SENSOR_AIN_7; |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////
|
|||
// GPS/GNSS
|
|||
|
|||
#define HAS_GPS 0 |
|||
#define PIN_GPS_TX (-1) |
|||
#define PIN_GPS_RX (-1) |
|||
#define GPS_EN (-1) |
|||
#define GPS_RESET (-1) |
|||
|
|||
@ -0,0 +1,13 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> |
|||
#include <helpers/ESP32Board.h> |
|||
|
|||
class XiaoS3WIOBoard : public ESP32Board { |
|||
public: |
|||
XiaoS3WIOBoard() { } |
|||
|
|||
const char* getManufacturerName() const override { |
|||
return "Xiao S3 WIO"; |
|||
} |
|||
}; |
|||
Loading…
Reference in new issue