mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-03-30 23:35:45 +00:00
Merge branch 'dev'
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -9,3 +9,8 @@ out/
|
||||
.DS_Store
|
||||
.vscode/settings.json
|
||||
.vscode/extensions.json
|
||||
.idea
|
||||
cmake-*
|
||||
.cache
|
||||
.ccls
|
||||
compile_commands.json
|
||||
|
||||
61
boards/heltec_mesh_solar.json
Normal file
61
boards/heltec_mesh_solar.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"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": "HT-n5262",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "heltec_mesh_solar",
|
||||
"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",
|
||||
"openocd_target": "nrf52.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "Heltec Mesh Solar 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://heltec.org/",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
44
boards/heltec_vision_master_e213.json
Normal file
44
boards/heltec_vision_master_e213.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_16MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"psram_type": "opi",
|
||||
"hwids": [
|
||||
["0x303A", "0x1001"],
|
||||
["0x303A", "0x0002"]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "heltec_vision_master_e213"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "Heltec Vision Master E213",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 8388608,
|
||||
"maximum_size": 16777216,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://heltec.org/project/vision-master-e213/",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
44
boards/heltec_vision_master_e290.json
Normal file
44
boards/heltec_vision_master_e290.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_16MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"psram_type": "opi",
|
||||
"hwids": [
|
||||
["0x303A", "0x1001"],
|
||||
["0x303A", "0x0002"]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "heltec_vision_master_e290"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "Heltec Vision Master E290",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 8388608,
|
||||
"maximum_size": 16777216,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://heltec.org/project/vision-master-e290/",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
44
boards/heltec_vision_master_t190.json
Normal file
44
boards/heltec_vision_master_t190.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_16MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"psram_type": "opi",
|
||||
"hwids": [
|
||||
["0x303A", "0x1001"],
|
||||
["0x303A", "0x0002"]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "heltec_vision_master_t190"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "Heltec Vision Master T190",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 8388608,
|
||||
"maximum_size": 16777216,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://heltec.org/project/vision-master-t190/",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
@@ -40,8 +40,8 @@
|
||||
],
|
||||
"name": "Seeed Wio Tracker L1",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
"maximum_size": 815104,
|
||||
"maximum_ram_size": 237568,
|
||||
"maximum_size": 811008,
|
||||
"protocol": "nrfutil",
|
||||
"speed": 115200,
|
||||
"protocols": [
|
||||
|
||||
11
build.sh
11
build.sh
@@ -3,6 +3,7 @@
|
||||
# usage
|
||||
# sh build.sh build-firmware RAK_4631_Repeater
|
||||
# sh build.sh build-firmwares
|
||||
# sh build.sh build-matching-firmwares RAK_4631
|
||||
# sh build.sh build-companion-firmwares
|
||||
# sh build.sh build-repeater-firmwares
|
||||
# sh build.sh build-room-server-firmwares
|
||||
@@ -144,6 +145,16 @@ mkdir -p out
|
||||
if [[ $1 == "build-firmware" ]]; then
|
||||
if [ "$2" ]; then
|
||||
build_firmware $2
|
||||
else
|
||||
echo "usage: $0 build-firmware <target>"
|
||||
exit 1
|
||||
fi
|
||||
elif [[ $1 == "build-matching-firmwares" ]]; then
|
||||
if [ "$2" ]; then
|
||||
build_all_firmwares_matching $2
|
||||
else
|
||||
echo "usage: $0 build-matching-firmwares <build-match-spec>"
|
||||
exit 1
|
||||
fi
|
||||
elif [[ $1 == "build-firmwares" ]]; then
|
||||
build_firmwares
|
||||
|
||||
64
build_as_lib.py
Normal file
64
build_as_lib.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from os.path import realpath
|
||||
|
||||
Import("env") # type: ignore
|
||||
menv=env # type: ignore
|
||||
|
||||
src_filter = [
|
||||
'+<*.cpp>',
|
||||
'+<helpers/*.cpp>',
|
||||
'+<helpers/sensors>',
|
||||
'+<helpers/radiolib/*.cpp>',
|
||||
'+<helpers/ui/MomentaryButton.cpp>',
|
||||
'+<helpers/ui/buzzer.cpp>',
|
||||
]
|
||||
|
||||
# add build and include dirs according to CPPDEFINES
|
||||
for item in menv.get("CPPDEFINES", []):
|
||||
|
||||
# PLATFORM HANDLING
|
||||
if item == "STM32_PLATFORM":
|
||||
src_filter.append("+<helpers/stm32/*>")
|
||||
elif item == "ESP32":
|
||||
src_filter.append("+<helpers/esp32/*>")
|
||||
elif item == "NRF52_PLATFORM":
|
||||
src_filter.append("+<helpers/nrf52/*>")
|
||||
elif item == "RP2040_PLATFORM":
|
||||
src_filter.append("+<helpers/rp2040/*>")
|
||||
|
||||
# DISPLAY HANDLING
|
||||
elif isinstance(item, tuple) and item[0] == "DISPLAY_CLASS":
|
||||
display_class = item[1]
|
||||
src_filter.append(f"+<helpers/ui/{display_class}.cpp>")
|
||||
if (display_class == "ST7789Display") :
|
||||
src_filter.append(f"+<helpers/ui/OLEDDisplay.cpp>")
|
||||
src_filter.append(f"+<helpers/ui/OLEDDisplayFonts.cpp>")
|
||||
|
||||
# VARIANTS HANDLING
|
||||
elif isinstance(item, tuple) and item[0] == "MC_VARIANT":
|
||||
variant_name = item[1]
|
||||
src_filter.append(f"+<../variants/{variant_name}>")
|
||||
|
||||
# INCLUDE EXAMPLE CODE IN BUILD (to provide your own support files without touching the tree)
|
||||
elif isinstance(item, tuple) and item[0] == "BUILD_EXAMPLE":
|
||||
example_name = item[1]
|
||||
src_filter.append(f"+<../examples/{example_name}/*.cpp>")
|
||||
|
||||
# EXCLUDE A SOURCE FILE FROM AN EXAMPLE (must be placed after example name or boom)
|
||||
elif isinstance(item, tuple) and item[0] == "EXCLUDE_FROM_EXAMPLE":
|
||||
exclude_name = item[1]
|
||||
if example_name is None:
|
||||
print("***** PLEASE DEFINE EXAMPLE FIRST *****")
|
||||
break
|
||||
src_filter.append(f"-<../examples/{example_name}/{exclude_name}>")
|
||||
|
||||
# DEAL WITH UI VARIANT FOR AN EXAMPLE
|
||||
elif isinstance(item, tuple) and item[0] == "MC_UI_FLAVOR":
|
||||
ui_flavor = item[1]
|
||||
if example_name is None:
|
||||
print("***** PLEASE DEFINE EXAMPLE FIRST *****")
|
||||
break
|
||||
src_filter.append(f"+<../examples/{example_name}/{ui_flavor}/*.cpp>")
|
||||
|
||||
menv.Replace(SRC_FILTER=src_filter)
|
||||
|
||||
#print (menv.Dump())
|
||||
46
examples/companion_radio/AbstractUITask.h
Normal file
46
examples/companion_radio/AbstractUITask.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <MeshCore.h>
|
||||
#include <helpers/ui/DisplayDriver.h>
|
||||
#include <helpers/ui/UIScreen.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#include <helpers/BaseSerialInterface.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef PIN_BUZZER
|
||||
#include <helpers/ui/buzzer.h>
|
||||
#endif
|
||||
|
||||
#include "NodePrefs.h"
|
||||
|
||||
enum class UIEventType {
|
||||
none,
|
||||
contactMessage,
|
||||
channelMessage,
|
||||
roomMessage,
|
||||
newContactMessage,
|
||||
ack
|
||||
};
|
||||
|
||||
class AbstractUITask {
|
||||
protected:
|
||||
mesh::MainBoard* _board;
|
||||
BaseSerialInterface* _serial;
|
||||
bool _connected;
|
||||
|
||||
AbstractUITask(mesh::MainBoard* board, BaseSerialInterface* serial) : _board(board), _serial(serial) {
|
||||
_connected = false;
|
||||
}
|
||||
|
||||
public:
|
||||
void setHasConnection(bool connected) { _connected = connected; }
|
||||
bool hasConnection() const { return _connected; }
|
||||
uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); }
|
||||
bool isSerialEnabled() const { return _serial->isEnabled(); }
|
||||
void enableSerial() { _serial->enable(); }
|
||||
void disableSerial() { _serial->disable(); }
|
||||
virtual void msgRead(int msgcount) = 0;
|
||||
virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) = 0;
|
||||
virtual void soundBuzzer(UIEventType bet = UIEventType::none) = 0;
|
||||
virtual void loop() = 0;
|
||||
};
|
||||
@@ -41,7 +41,7 @@
|
||||
#define CMD_SEND_TRACE_PATH 36
|
||||
#define CMD_SET_DEVICE_PIN 37
|
||||
#define CMD_SET_OTHER_PARAMS 38
|
||||
#define CMD_SEND_TELEMETRY_REQ 39
|
||||
#define CMD_SEND_TELEMETRY_REQ 39 // can deprecate this
|
||||
#define CMD_GET_CUSTOM_VARS 40
|
||||
#define CMD_SET_CUSTOM_VAR 41
|
||||
#define CMD_GET_ADVERT_PATH 42
|
||||
@@ -49,6 +49,7 @@
|
||||
// NOTE: CMD range 44..49 parked, potentially for WiFi operations
|
||||
#define CMD_SEND_BINARY_REQ 50
|
||||
#define CMD_FACTORY_RESET 51
|
||||
#define CMD_SEND_PATH_DISCOVERY_REQ 52
|
||||
|
||||
#define RESP_CODE_OK 0
|
||||
#define RESP_CODE_ERR 1
|
||||
@@ -97,6 +98,7 @@
|
||||
#define PUSH_CODE_NEW_ADVERT 0x8A
|
||||
#define PUSH_CODE_TELEMETRY_RESPONSE 0x8B
|
||||
#define PUSH_CODE_BINARY_RESPONSE 0x8C
|
||||
#define PUSH_CODE_PATH_DISCOVERY_RESPONSE 0x8D
|
||||
|
||||
#define ERR_CODE_UNSUPPORTED_CMD 1
|
||||
#define ERR_CODE_NOT_FOUND 2
|
||||
@@ -107,10 +109,6 @@
|
||||
|
||||
#define MAX_SIGN_DATA_LEN (8 * 1024) // 8K
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include "UITask.h"
|
||||
#endif
|
||||
|
||||
void MyMesh::writeOKFrame() {
|
||||
uint8_t buf[1];
|
||||
buf[0] = RESP_CODE_OK;
|
||||
@@ -152,7 +150,7 @@ void MyMesh::writeContactRespFrame(uint8_t code, const ContactInfo &contact) {
|
||||
_serial->writeFrame(out_frame, i);
|
||||
}
|
||||
|
||||
void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame, int len) {
|
||||
void MyMesh::updateContactFromFrame(ContactInfo &contact, uint32_t& last_mod, const uint8_t *frame, int len) {
|
||||
int i = 0;
|
||||
uint8_t code = frame[i++]; // eg. CMD_ADD_UPDATE_CONTACT
|
||||
memcpy(contact.id.pub_key, &frame[i], PUB_KEY_SIZE);
|
||||
@@ -166,11 +164,14 @@ void MyMesh::updateContactFromFrame(ContactInfo &contact, const uint8_t *frame,
|
||||
i += 32;
|
||||
memcpy(&contact.last_advert_timestamp, &frame[i], 4);
|
||||
i += 4;
|
||||
if (i + 8 >= len) { // optional fields
|
||||
if (len >= i + 8) { // optional fields
|
||||
memcpy(&contact.gps_lat, &frame[i], 4);
|
||||
i += 4;
|
||||
memcpy(&contact.gps_lon, &frame[i], 4);
|
||||
i += 4;
|
||||
if (len >= i + 4) {
|
||||
memcpy(&last_mod, &frame[i], 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +243,7 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
|
||||
}
|
||||
} else {
|
||||
#ifdef DISPLAY_CLASS
|
||||
ui_task.soundBuzzer(UIEventType::newContactMessage);
|
||||
if (_ui) _ui->soundBuzzer(UIEventType::newContactMessage);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -262,6 +263,7 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
|
||||
}
|
||||
|
||||
memcpy(p->pubkey_prefix, contact.id.pub_key, sizeof(p->pubkey_prefix));
|
||||
strcpy(p->name, contact.name);
|
||||
p->recv_timestamp = getRTCClock()->getCurrentTime();
|
||||
p->path_len = path_len;
|
||||
memcpy(p->path, path, p->path_len);
|
||||
@@ -270,6 +272,20 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
|
||||
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
|
||||
}
|
||||
|
||||
static int sort_by_recent(const void *a, const void *b) {
|
||||
return ((AdvertPath *) b)->recv_timestamp - ((AdvertPath *) a)->recv_timestamp;
|
||||
}
|
||||
|
||||
int MyMesh::getRecentlyHeard(AdvertPath dest[], int max_num) {
|
||||
if (max_num > ADVERT_PATH_TABLE_SIZE) max_num = ADVERT_PATH_TABLE_SIZE;
|
||||
qsort(advert_paths, ADVERT_PATH_TABLE_SIZE, sizeof(advert_paths[0]), sort_by_recent);
|
||||
|
||||
for (int i = 0; i < max_num; i++) {
|
||||
dest[i] = advert_paths[i];
|
||||
}
|
||||
return max_num;
|
||||
}
|
||||
|
||||
void MyMesh::onContactPathUpdated(const ContactInfo &contact) {
|
||||
out_frame[0] = PUSH_CODE_PATH_UPDATED;
|
||||
memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE);
|
||||
@@ -334,10 +350,10 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe
|
||||
#ifdef DISPLAY_CLASS
|
||||
// we only want to show text messages on display, not cli data
|
||||
bool should_display = txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_SIGNED_PLAIN;
|
||||
if (should_display) {
|
||||
ui_task.newMsg(path_len, from.name, text, offline_queue_len);
|
||||
if (should_display && _ui) {
|
||||
_ui->newMsg(path_len, from.name, text, offline_queue_len);
|
||||
if (!_serial->isConnected()) {
|
||||
ui_task.soundBuzzer(UIEventType::contactMessage);
|
||||
_ui->soundBuzzer(UIEventType::contactMessage);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -396,7 +412,7 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe
|
||||
_serial->writeFrame(frame, 1);
|
||||
} else {
|
||||
#ifdef DISPLAY_CLASS
|
||||
ui_task.soundBuzzer(UIEventType::channelMessage);
|
||||
if (_ui) _ui->soundBuzzer(UIEventType::channelMessage);
|
||||
#endif
|
||||
}
|
||||
#ifdef DISPLAY_CLASS
|
||||
@@ -406,7 +422,7 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe
|
||||
if (getChannel(channel_idx, channel_details)) {
|
||||
channel_name = channel_details.name;
|
||||
}
|
||||
ui_task.newMsg(path_len, channel_name, text, offline_queue_len);
|
||||
if (_ui) _ui->newMsg(path_len, channel_name, text, offline_queue_len);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -434,6 +450,9 @@ uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_tim
|
||||
permissions |= cp & TELEM_PERM_ENVIRONMENT;
|
||||
}
|
||||
|
||||
uint8_t perm_mask = ~(data[1]); // NEW: first reserved byte (of 4), is now inverse mask to apply to permissions
|
||||
permissions &= perm_mask;
|
||||
|
||||
if (permissions & TELEM_PERM_BASE) { // only respond if base permission bit is set
|
||||
telemetry.reset();
|
||||
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);
|
||||
@@ -524,6 +543,39 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
|
||||
}
|
||||
}
|
||||
|
||||
bool MyMesh::onContactPathRecv(ContactInfo& contact, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) {
|
||||
if (extra_type == PAYLOAD_TYPE_RESPONSE && extra_len > 4) {
|
||||
uint32_t tag;
|
||||
memcpy(&tag, extra, 4);
|
||||
|
||||
if (tag == pending_discovery) { // check for matching response tag)
|
||||
pending_discovery = 0;
|
||||
|
||||
if (in_path_len > MAX_PATH_SIZE || out_path_len > MAX_PATH_SIZE) {
|
||||
MESH_DEBUG_PRINTLN("onContactPathRecv, invalid path sizes: %d, %d", in_path_len, out_path_len);
|
||||
} else {
|
||||
int i = 0;
|
||||
out_frame[i++] = PUSH_CODE_PATH_DISCOVERY_RESPONSE;
|
||||
out_frame[i++] = 0; // reserved
|
||||
memcpy(&out_frame[i], contact.id.pub_key, 6);
|
||||
i += 6; // pub_key_prefix
|
||||
out_frame[i++] = out_path_len;
|
||||
memcpy(&out_frame[i], out_path, out_path_len);
|
||||
i += out_path_len;
|
||||
out_frame[i++] = in_path_len;
|
||||
memcpy(&out_frame[i], in_path, in_path_len);
|
||||
i += in_path_len;
|
||||
// NOTE: telemetry data in 'extra' is discarded at present
|
||||
|
||||
_serial->writeFrame(out_frame, i);
|
||||
}
|
||||
return false; // DON'T send reciprocal path!
|
||||
}
|
||||
}
|
||||
// let base class handle received path and data
|
||||
return BaseChatMesh::onContactPathRecv(contact, in_path, in_path_len, out_path, out_path_len, extra_type, extra, extra_len);
|
||||
}
|
||||
|
||||
void MyMesh::onRawDataRecv(mesh::Packet *packet) {
|
||||
if (packet->payload_len + 4 > sizeof(out_frame)) {
|
||||
MESH_DEBUG_PRINTLN("onRawDataRecv(), payload_len too long: %d", packet->payload_len);
|
||||
@@ -579,14 +631,14 @@ uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t
|
||||
|
||||
void MyMesh::onSendTimeout() {}
|
||||
|
||||
MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store)
|
||||
MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store, AbstractUITask* ui)
|
||||
: BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables),
|
||||
_serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store) {
|
||||
_serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store), _ui(ui) {
|
||||
_iter_started = false;
|
||||
_cli_rescue = false;
|
||||
offline_queue_len = 0;
|
||||
app_target_ver = 0;
|
||||
pending_login = pending_status = pending_telemetry = pending_req = 0;
|
||||
clearPendingReqs();
|
||||
next_ack_idx = 0;
|
||||
sign_data = NULL;
|
||||
dirty_contacts_expiry = 0;
|
||||
@@ -658,6 +710,7 @@ void MyMesh::begin(bool has_display) {
|
||||
_active_ble_pin = 0;
|
||||
#endif
|
||||
|
||||
resetContacts();
|
||||
_store->loadContacts(this);
|
||||
addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
|
||||
_store->loadChannels(this);
|
||||
@@ -896,15 +949,16 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
} else if (cmd_frame[0] == CMD_ADD_UPDATE_CONTACT && len >= 1 + 32 + 2 + 1) {
|
||||
uint8_t *pub_key = &cmd_frame[1];
|
||||
ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
|
||||
uint32_t last_mod = getRTCClock()->getCurrentTime(); // fallback value if not present in cmd_frame
|
||||
if (recipient) {
|
||||
updateContactFromFrame(*recipient, cmd_frame, len);
|
||||
// recipient->lastmod = ?? shouldn't be needed, app already has this version of contact
|
||||
updateContactFromFrame(*recipient, last_mod, cmd_frame, len);
|
||||
recipient->lastmod = last_mod;
|
||||
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
|
||||
writeOKFrame();
|
||||
} else {
|
||||
ContactInfo contact;
|
||||
updateContactFromFrame(contact, cmd_frame, len);
|
||||
contact.lastmod = getRTCClock()->getCurrentTime();
|
||||
updateContactFromFrame(contact, last_mod, cmd_frame, len);
|
||||
contact.lastmod = last_mod;
|
||||
contact.sync_since = 0;
|
||||
if (addContact(contact)) {
|
||||
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
|
||||
@@ -983,7 +1037,7 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
if ((out_len = getFromOfflineQueue(out_frame)) > 0) {
|
||||
_serial->writeFrame(out_frame, out_len);
|
||||
#ifdef DISPLAY_CLASS
|
||||
ui_task.msgRead(offline_queue_len);
|
||||
if (_ui) _ui->msgRead(offline_queue_len);
|
||||
#endif
|
||||
} else {
|
||||
out_frame[0] = RESP_CODE_NO_MORE_MESSAGES;
|
||||
@@ -1093,6 +1147,9 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
if (_store->saveMainIdentity(identity)) {
|
||||
self_id = identity;
|
||||
writeOKFrame();
|
||||
// re-load contacts, to recalc shared secrets
|
||||
resetContacts();
|
||||
_store->loadContacts(this);
|
||||
} else {
|
||||
writeErrFrame(ERR_CODE_FILE_IO_ERROR);
|
||||
}
|
||||
@@ -1126,7 +1183,7 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
if (result == MSG_SEND_FAILED) {
|
||||
writeErrFrame(ERR_CODE_TABLE_FULL);
|
||||
} else {
|
||||
pending_req = pending_telemetry = pending_status = 0;
|
||||
clearPendingReqs();
|
||||
memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse()
|
||||
out_frame[0] = RESP_CODE_SENT;
|
||||
out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
|
||||
@@ -1146,7 +1203,7 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
if (result == MSG_SEND_FAILED) {
|
||||
writeErrFrame(ERR_CODE_TABLE_FULL);
|
||||
} else {
|
||||
pending_req = pending_telemetry = pending_login = 0;
|
||||
clearPendingReqs();
|
||||
// FUTURE: pending_status = tag; // match this in onContactResponse()
|
||||
memcpy(&pending_status, recipient->id.pub_key, 4); // legacy matching scheme
|
||||
out_frame[0] = RESP_CODE_SENT;
|
||||
@@ -1158,6 +1215,35 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
} else {
|
||||
writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
|
||||
}
|
||||
} else if (cmd_frame[0] == CMD_SEND_PATH_DISCOVERY_REQ && cmd_frame[1] == 0 && len >= 2 + PUB_KEY_SIZE) {
|
||||
uint8_t *pub_key = &cmd_frame[2];
|
||||
ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
|
||||
if (recipient) {
|
||||
uint32_t tag, est_timeout;
|
||||
// 'Path Discovery' is just a special case of flood + Telemetry req
|
||||
uint8_t req_data[9];
|
||||
req_data[0] = REQ_TYPE_GET_TELEMETRY_DATA;
|
||||
req_data[1] = ~(TELEM_PERM_BASE); // NEW: inverse permissions mask (ie. we only want BASE telemetry)
|
||||
memset(&req_data[2], 0, 3); // reserved
|
||||
getRNG()->random(&req_data[5], 4); // random blob to help make packet-hash unique
|
||||
auto save = recipient->out_path_len; // temporarily force sendRequest() to flood
|
||||
recipient->out_path_len = -1;
|
||||
int result = sendRequest(*recipient, req_data, sizeof(req_data), tag, est_timeout);
|
||||
recipient->out_path_len = save;
|
||||
if (result == MSG_SEND_FAILED) {
|
||||
writeErrFrame(ERR_CODE_TABLE_FULL);
|
||||
} else {
|
||||
clearPendingReqs();
|
||||
pending_discovery = tag; // match this in onContactResponse()
|
||||
out_frame[0] = RESP_CODE_SENT;
|
||||
out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
|
||||
memcpy(&out_frame[2], &tag, 4);
|
||||
memcpy(&out_frame[6], &est_timeout, 4);
|
||||
_serial->writeFrame(out_frame, 10);
|
||||
}
|
||||
} else {
|
||||
writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
|
||||
}
|
||||
} else if (cmd_frame[0] == CMD_SEND_TELEMETRY_REQ && len >= 4 + PUB_KEY_SIZE) { // can deprecate, in favour of CMD_SEND_BINARY_REQ
|
||||
uint8_t *pub_key = &cmd_frame[4];
|
||||
ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
|
||||
@@ -1167,7 +1253,7 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
if (result == MSG_SEND_FAILED) {
|
||||
writeErrFrame(ERR_CODE_TABLE_FULL);
|
||||
} else {
|
||||
pending_status = pending_login = pending_req = 0;
|
||||
clearPendingReqs();
|
||||
pending_telemetry = tag; // match this in onContactResponse()
|
||||
out_frame[0] = RESP_CODE_SENT;
|
||||
out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
|
||||
@@ -1203,7 +1289,7 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
if (result == MSG_SEND_FAILED) {
|
||||
writeErrFrame(ERR_CODE_TABLE_FULL);
|
||||
} else {
|
||||
pending_status = pending_login = pending_telemetry = 0;
|
||||
clearPendingReqs();
|
||||
pending_req = tag; // match this in onContactResponse()
|
||||
out_frame[0] = RESP_CODE_SENT;
|
||||
out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0;
|
||||
@@ -1553,7 +1639,7 @@ void MyMesh::loop() {
|
||||
}
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
ui_task.setHasConnection(_serial->isConnected());
|
||||
if (_ui) _ui->setHasConnection(_serial->isConnected());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Mesh.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include "UITask.h"
|
||||
#endif
|
||||
#include "AbstractUITask.h"
|
||||
|
||||
/*------------ Frame Protocol --------------*/
|
||||
#define FIRMWARE_VER_CODE 7
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "24 Jul 2025"
|
||||
#define FIRMWARE_BUILD_DATE "31 Aug 2025"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.7.4"
|
||||
#define FIRMWARE_VERSION "v1.8.0"
|
||||
#endif
|
||||
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
@@ -77,9 +75,17 @@
|
||||
#define REQ_TYPE_KEEP_ALIVE 0x02
|
||||
#define REQ_TYPE_GET_TELEMETRY_DATA 0x03
|
||||
|
||||
struct AdvertPath {
|
||||
uint8_t pubkey_prefix[7];
|
||||
uint8_t path_len;
|
||||
char name[32];
|
||||
uint32_t recv_timestamp;
|
||||
uint8_t path[MAX_PATH_SIZE];
|
||||
};
|
||||
|
||||
class MyMesh : public BaseChatMesh, public DataStoreHost {
|
||||
public:
|
||||
MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store);
|
||||
MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store, AbstractUITask* ui=NULL);
|
||||
|
||||
void begin(bool has_display);
|
||||
void startInterface(BaseSerialInterface &serial);
|
||||
@@ -93,6 +99,8 @@ public:
|
||||
bool advert();
|
||||
void enterCLIRescue();
|
||||
|
||||
int getRecentlyHeard(AdvertPath dest[], int max_num);
|
||||
|
||||
protected:
|
||||
float getAirtimeBudgetFactor() const override;
|
||||
int getInterferenceThreshold() const override;
|
||||
@@ -101,6 +109,7 @@ protected:
|
||||
|
||||
void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override;
|
||||
bool isAutoAddEnabled() const override;
|
||||
bool onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override;
|
||||
void onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) override;
|
||||
void onContactPathUpdated(const ContactInfo &contact) override;
|
||||
bool processAck(const uint8_t *data) override;
|
||||
@@ -133,12 +142,16 @@ protected:
|
||||
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); }
|
||||
|
||||
void clearPendingReqs() {
|
||||
pending_login = pending_status = pending_telemetry = pending_discovery = pending_req = 0;
|
||||
}
|
||||
|
||||
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 updateContactFromFrame(ContactInfo &contact, uint32_t& last_mod, 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 {
|
||||
@@ -161,9 +174,10 @@ private:
|
||||
NodePrefs _prefs;
|
||||
uint32_t pending_login;
|
||||
uint32_t pending_status;
|
||||
uint32_t pending_telemetry; // pending _TELEMETRY_REQ
|
||||
uint32_t pending_telemetry, pending_discovery; // pending _TELEMETRY_REQ
|
||||
uint32_t pending_req; // pending _BINARY_REQ
|
||||
BaseSerialInterface *_serial;
|
||||
AbstractUITask* _ui;
|
||||
|
||||
ContactsIterator _iter;
|
||||
uint32_t _iter_filter_since;
|
||||
@@ -196,17 +210,8 @@ private:
|
||||
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
|
||||
@@ -75,14 +75,19 @@ static uint32_t _atoi(const char* sp) {
|
||||
#endif
|
||||
|
||||
/* GLOBAL OBJECTS */
|
||||
StdRNG fast_rng;
|
||||
SimpleMeshTables tables;
|
||||
MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store);
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include "UITask.h"
|
||||
UITask ui_task(&board);
|
||||
UITask ui_task(&board, &serial_interface);
|
||||
#endif
|
||||
|
||||
StdRNG fast_rng;
|
||||
SimpleMeshTables tables;
|
||||
MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store
|
||||
#ifdef DISPLAY_CLASS
|
||||
, &ui_task
|
||||
#endif
|
||||
);
|
||||
|
||||
/* END GLOBAL OBJECTS */
|
||||
|
||||
void halt() {
|
||||
@@ -99,7 +104,10 @@ void setup() {
|
||||
if (display.begin()) {
|
||||
disp = &display;
|
||||
disp->startFrame();
|
||||
disp->print("Please wait...");
|
||||
#ifdef ST7789
|
||||
disp->setTextSize(2);
|
||||
#endif
|
||||
disp->drawTextCentered(disp->width() / 2, 28, "Loading...");
|
||||
disp->endFrame();
|
||||
}
|
||||
#endif
|
||||
|
||||
612
examples/companion_radio/ui-new/UITask.cpp
Normal file
612
examples/companion_radio/ui-new/UITask.cpp
Normal file
@@ -0,0 +1,612 @@
|
||||
#include "UITask.h"
|
||||
#include <helpers/TxtDataHelpers.h>
|
||||
#include "../MyMesh.h"
|
||||
#include "target.h"
|
||||
|
||||
#define AUTO_OFF_MILLIS 15000 // 15 seconds
|
||||
#define BOOT_SCREEN_MILLIS 3000 // 3 seconds
|
||||
|
||||
#ifdef PIN_STATUS_LED
|
||||
#define LED_ON_MILLIS 20
|
||||
#define LED_ON_MSG_MILLIS 200
|
||||
#define LED_CYCLE_MILLIS 4000
|
||||
#endif
|
||||
|
||||
#define LONG_PRESS_MILLIS 1200
|
||||
|
||||
#ifndef UI_RECENT_LIST_SIZE
|
||||
#define UI_RECENT_LIST_SIZE 4
|
||||
#endif
|
||||
|
||||
#define PRESS_LABEL "long press"
|
||||
|
||||
#include "icons.h"
|
||||
|
||||
class SplashScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
unsigned long dismiss_after;
|
||||
char _version_info[12];
|
||||
|
||||
public:
|
||||
SplashScreen(UITask* task) : _task(task) {
|
||||
// strip off dash and commit hash by changing dash to null terminator
|
||||
// e.g: v1.2.3-abcdef -> v1.2.3
|
||||
const char *ver = FIRMWARE_VERSION;
|
||||
const char *dash = strchr(ver, '-');
|
||||
|
||||
int len = dash ? dash - ver : strlen(ver);
|
||||
if (len >= sizeof(_version_info)) len = sizeof(_version_info) - 1;
|
||||
memcpy(_version_info, ver, len);
|
||||
_version_info[len] = 0;
|
||||
|
||||
dismiss_after = millis() + BOOT_SCREEN_MILLIS;
|
||||
}
|
||||
|
||||
int render(DisplayDriver& display) override {
|
||||
// meshcore logo
|
||||
display.setColor(DisplayDriver::BLUE);
|
||||
int logoWidth = 128;
|
||||
display.drawXbm((display.width() - logoWidth) / 2, 3, meshcore_logo, logoWidth, 13);
|
||||
|
||||
// version info
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.setTextSize(2);
|
||||
display.drawTextCentered(display.width()/2, 22, _version_info);
|
||||
|
||||
display.setTextSize(1);
|
||||
display.drawTextCentered(display.width()/2, 42, FIRMWARE_BUILD_DATE);
|
||||
|
||||
return 1000;
|
||||
}
|
||||
|
||||
void poll() override {
|
||||
if (millis() >= dismiss_after) {
|
||||
_task->gotoHomeScreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HomeScreen : public UIScreen {
|
||||
enum HomePage {
|
||||
FIRST,
|
||||
RECENT,
|
||||
RADIO,
|
||||
BLUETOOTH,
|
||||
ADVERT,
|
||||
SHUTDOWN,
|
||||
Count // keep as last
|
||||
};
|
||||
|
||||
UITask* _task;
|
||||
mesh::RTCClock* _rtc;
|
||||
SensorManager* _sensors;
|
||||
NodePrefs* _node_prefs;
|
||||
uint8_t _page;
|
||||
bool _shutdown_init;
|
||||
AdvertPath recent[UI_RECENT_LIST_SIZE];
|
||||
|
||||
void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) {
|
||||
// Convert millivolts to percentage
|
||||
const int minMilliVolts = 3000; // Minimum voltage (e.g., 3.0V)
|
||||
const int maxMilliVolts = 4200; // Maximum voltage (e.g., 4.2V)
|
||||
int batteryPercentage = ((batteryMilliVolts - minMilliVolts) * 100) / (maxMilliVolts - minMilliVolts);
|
||||
if (batteryPercentage < 0) batteryPercentage = 0; // Clamp to 0%
|
||||
if (batteryPercentage > 100) batteryPercentage = 100; // Clamp to 100%
|
||||
|
||||
// battery icon
|
||||
int iconWidth = 24;
|
||||
int iconHeight = 10;
|
||||
int iconX = display.width() - iconWidth - 5; // Position the icon near the top-right corner
|
||||
int iconY = 0;
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
|
||||
// battery outline
|
||||
display.drawRect(iconX, iconY, iconWidth, iconHeight);
|
||||
|
||||
// battery "cap"
|
||||
display.fillRect(iconX + iconWidth, iconY + (iconHeight / 4), 3, iconHeight / 2);
|
||||
|
||||
// fill the battery based on the percentage
|
||||
int fillWidth = (batteryPercentage * (iconWidth - 4)) / 100;
|
||||
display.fillRect(iconX + 2, iconY + 2, fillWidth, iconHeight - 4);
|
||||
}
|
||||
|
||||
public:
|
||||
HomeScreen(UITask* task, mesh::RTCClock* rtc, SensorManager* sensors, NodePrefs* node_prefs)
|
||||
: _task(task), _rtc(rtc), _sensors(sensors), _node_prefs(node_prefs), _page(0), _shutdown_init(false) { }
|
||||
|
||||
void poll() override {
|
||||
if (_shutdown_init && !_task->isButtonPressed()) { // must wait for USR button to be released
|
||||
_task->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
int render(DisplayDriver& display) override {
|
||||
char tmp[80];
|
||||
// node name
|
||||
display.setCursor(0, 0);
|
||||
display.setTextSize(1);
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
display.print(_node_prefs->node_name);
|
||||
|
||||
// battery voltage
|
||||
renderBatteryIndicator(display, _task->getBattMilliVolts());
|
||||
|
||||
// curr page indicator
|
||||
int y = 14;
|
||||
int x = display.width() / 2 - 25;
|
||||
for (uint8_t i = 0; i < HomePage::Count; i++, x += 10) {
|
||||
if (i == _page) {
|
||||
display.fillRect(x-1, y-1, 3, 3);
|
||||
} else {
|
||||
display.fillRect(x, y, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (_page == HomePage::FIRST) {
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
display.setTextSize(2);
|
||||
sprintf(tmp, "MSG: %d", _task->getMsgCount());
|
||||
display.drawTextCentered(display.width() / 2, 20, tmp);
|
||||
|
||||
if (_task->hasConnection()) {
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
display.setTextSize(1);
|
||||
display.drawTextCentered(display.width() / 2, 43, "< Connected >");
|
||||
} else if (the_mesh.getBLEPin() != 0) { // BT pin
|
||||
display.setColor(DisplayDriver::RED);
|
||||
display.setTextSize(2);
|
||||
sprintf(tmp, "Pin:%d", the_mesh.getBLEPin());
|
||||
display.drawTextCentered(display.width() / 2, 43, tmp);
|
||||
}
|
||||
} else if (_page == HomePage::RECENT) {
|
||||
the_mesh.getRecentlyHeard(recent, UI_RECENT_LIST_SIZE);
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
int y = 20;
|
||||
for (int i = 0; i < UI_RECENT_LIST_SIZE; i++, y += 11) {
|
||||
auto a = &recent[i];
|
||||
if (a->name[0] == 0) continue; // empty slot
|
||||
display.setCursor(0, y);
|
||||
display.print(a->name);
|
||||
int secs = _rtc->getCurrentTime() - a->recv_timestamp;
|
||||
if (secs < 60) {
|
||||
sprintf(tmp, "%ds", secs);
|
||||
} else if (secs < 60*60) {
|
||||
sprintf(tmp, "%dm", secs / 60);
|
||||
} else {
|
||||
sprintf(tmp, "%dh", secs / (60*60));
|
||||
}
|
||||
display.setCursor(display.width() - display.getTextWidth(tmp) - 1, y);
|
||||
display.print(tmp);
|
||||
}
|
||||
} else if (_page == HomePage::RADIO) {
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
display.setTextSize(1);
|
||||
// freq / sf
|
||||
display.setCursor(0, 20);
|
||||
sprintf(tmp, "FQ: %06.3f SF: %d", _node_prefs->freq, _node_prefs->sf);
|
||||
display.print(tmp);
|
||||
|
||||
display.setCursor(0, 31);
|
||||
sprintf(tmp, "BW: %03.2f CR: %d", _node_prefs->bw, _node_prefs->cr);
|
||||
display.print(tmp);
|
||||
|
||||
// tx power, noise floor
|
||||
display.setCursor(0, 42);
|
||||
sprintf(tmp, "TX: %ddBm", _node_prefs->tx_power_dbm);
|
||||
display.print(tmp);
|
||||
display.setCursor(0, 53);
|
||||
sprintf(tmp, "Noise floor: %d", radio_driver.getNoiseFloor());
|
||||
display.print(tmp);
|
||||
} else if (_page == HomePage::BLUETOOTH) {
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
display.drawXbm((display.width() - 32) / 2, 18,
|
||||
_task->isSerialEnabled() ? bluetooth_on : bluetooth_off,
|
||||
32, 32);
|
||||
display.setTextSize(1);
|
||||
display.drawTextCentered(display.width() / 2, 64 - 11, "toggle: " PRESS_LABEL);
|
||||
} else if (_page == HomePage::ADVERT) {
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
display.drawXbm((display.width() - 32) / 2, 18, advert_icon, 32, 32);
|
||||
display.drawTextCentered(display.width() / 2, 64 - 11, "advert: " PRESS_LABEL);
|
||||
} else if (_page == HomePage::SHUTDOWN) {
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
display.setTextSize(1);
|
||||
if (_shutdown_init) {
|
||||
display.drawTextCentered(display.width() / 2, 34, "hibernating...");
|
||||
} else {
|
||||
display.drawXbm((display.width() - 32) / 2, 18, power_icon, 32, 32);
|
||||
display.drawTextCentered(display.width() / 2, 64 - 11, "hibernate: " PRESS_LABEL);
|
||||
}
|
||||
}
|
||||
return 5000; // next render after 5000 ms
|
||||
}
|
||||
|
||||
bool handleInput(char c) override {
|
||||
if (c == KEY_LEFT) {
|
||||
_page = (_page + HomePage::Count - 1) % HomePage::Count;
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_RIGHT || c == KEY_SELECT) {
|
||||
_page = (_page + 1) % HomePage::Count;
|
||||
if (_page == HomePage::RECENT) {
|
||||
_task->showAlert("Recent adverts", 800);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_ENTER && _page == HomePage::BLUETOOTH) {
|
||||
if (_task->isSerialEnabled()) { // toggle Bluetooth on/off
|
||||
_task->disableSerial();
|
||||
} else {
|
||||
_task->enableSerial();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_ENTER && _page == HomePage::ADVERT) {
|
||||
#ifdef PIN_BUZZER
|
||||
_task->soundBuzzer(UIEventType::ack);
|
||||
#endif
|
||||
if (the_mesh.advert()) {
|
||||
_task->showAlert("Advert sent!", 1000);
|
||||
} else {
|
||||
_task->showAlert("Advert failed..", 1000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_ENTER && _page == HomePage::SHUTDOWN) {
|
||||
_shutdown_init = true; // need to wait for button to be released
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class MsgPreviewScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
mesh::RTCClock* _rtc;
|
||||
|
||||
struct MsgEntry {
|
||||
uint32_t timestamp;
|
||||
char origin[62];
|
||||
char msg[78];
|
||||
};
|
||||
#define MAX_UNREAD_MSGS 32
|
||||
int num_unread;
|
||||
MsgEntry unread[MAX_UNREAD_MSGS];
|
||||
|
||||
public:
|
||||
MsgPreviewScreen(UITask* task, mesh::RTCClock* rtc) : _task(task), _rtc(rtc) { num_unread = 0; }
|
||||
|
||||
void addPreview(uint8_t path_len, const char* from_name, const char* msg) {
|
||||
if (num_unread >= MAX_UNREAD_MSGS) return; // full
|
||||
|
||||
auto p = &unread[num_unread++];
|
||||
p->timestamp = _rtc->getCurrentTime();
|
||||
if (path_len == 0xFF) {
|
||||
sprintf(p->origin, "(D) %s:", from_name);
|
||||
} else {
|
||||
sprintf(p->origin, "(%d) %s:", (uint32_t) path_len, from_name);
|
||||
}
|
||||
StrHelper::strncpy(p->msg, msg, sizeof(p->msg));
|
||||
}
|
||||
|
||||
int render(DisplayDriver& display) override {
|
||||
char tmp[16];
|
||||
display.setCursor(0, 0);
|
||||
display.setTextSize(1);
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
sprintf(tmp, "Unread: %d", num_unread);
|
||||
display.print(tmp);
|
||||
|
||||
auto p = &unread[0];
|
||||
|
||||
int secs = _rtc->getCurrentTime() - p->timestamp;
|
||||
if (secs < 60) {
|
||||
sprintf(tmp, "%ds", secs);
|
||||
} else if (secs < 60*60) {
|
||||
sprintf(tmp, "%dm", secs / 60);
|
||||
} else {
|
||||
sprintf(tmp, "%dh", secs / (60*60));
|
||||
}
|
||||
display.setCursor(display.width() - display.getTextWidth(tmp) - 2, 0);
|
||||
display.print(tmp);
|
||||
|
||||
display.drawRect(0, 11, display.width(), 1); // horiz line
|
||||
|
||||
display.setCursor(0, 14);
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
display.print(p->origin);
|
||||
|
||||
display.setCursor(0, 25);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.printWordWrap(p->msg, display.width());
|
||||
|
||||
return 1000; // next render after 1000 ms
|
||||
}
|
||||
|
||||
bool handleInput(char c) override {
|
||||
if (c == KEY_SELECT || c == KEY_RIGHT) {
|
||||
num_unread--;
|
||||
if (num_unread == 0) {
|
||||
_task->gotoHomeScreen();
|
||||
} else {
|
||||
// delete first/curr item from unread queue
|
||||
for (int i = 0; i < num_unread; i++) {
|
||||
unread[i] = unread[i + 1];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_ENTER) {
|
||||
num_unread = 0; // clear unread queue
|
||||
_task->gotoHomeScreen();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* node_prefs) {
|
||||
_display = display;
|
||||
_sensors = sensors;
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS;
|
||||
|
||||
#if defined(PIN_USER_BTN)
|
||||
user_btn.begin();
|
||||
#endif
|
||||
|
||||
_node_prefs = node_prefs;
|
||||
if (_display != NULL) {
|
||||
_display->turnOn();
|
||||
}
|
||||
|
||||
#ifdef PIN_BUZZER
|
||||
buzzer.begin();
|
||||
#endif
|
||||
|
||||
ui_started_at = millis();
|
||||
_alert_expiry = 0;
|
||||
|
||||
splash = new SplashScreen(this);
|
||||
home = new HomeScreen(this, &rtc_clock, sensors, node_prefs);
|
||||
msg_preview = new MsgPreviewScreen(this, &rtc_clock);
|
||||
setCurrScreen(splash);
|
||||
}
|
||||
|
||||
void UITask::showAlert(const char* text, int duration_millis) {
|
||||
strcpy(_alert, text);
|
||||
_alert_expiry = millis() + duration_millis;
|
||||
}
|
||||
|
||||
void UITask::soundBuzzer(UIEventType bet) {
|
||||
#if defined(PIN_BUZZER)
|
||||
switch(bet){
|
||||
case UIEventType::contactMessage:
|
||||
// gemini's pick
|
||||
buzzer.play("MsgRcv3:d=4,o=6,b=200:32e,32g,32b,16c7");
|
||||
break;
|
||||
case UIEventType::channelMessage:
|
||||
buzzer.play("kerplop:d=16,o=6,b=120:32g#,32c#");
|
||||
break;
|
||||
case UIEventType::ack:
|
||||
buzzer.play("ack:d=32,o=8,b=120:c");
|
||||
break;
|
||||
case UIEventType::roomMessage:
|
||||
case UIEventType::newContactMessage:
|
||||
case UIEventType::none:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UITask::msgRead(int msgcount) {
|
||||
_msgcount = msgcount;
|
||||
if (msgcount == 0) {
|
||||
gotoHomeScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) {
|
||||
_msgcount = msgcount;
|
||||
|
||||
((MsgPreviewScreen *) msg_preview)->addPreview(path_len, from_name, text);
|
||||
setCurrScreen(msg_preview);
|
||||
|
||||
if (_display != NULL) {
|
||||
if (!_display->isOn()) _display->turnOn();
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
|
||||
_next_refresh = 0; // trigger refresh
|
||||
}
|
||||
}
|
||||
|
||||
void UITask::userLedHandler() {
|
||||
#ifdef PIN_STATUS_LED
|
||||
static int state = 0;
|
||||
static int next_change = 0;
|
||||
static int last_increment = 0;
|
||||
|
||||
int cur_time = millis();
|
||||
if (cur_time > next_change) {
|
||||
if (state == 0) {
|
||||
state = 1;
|
||||
if (_msgcount > 0) {
|
||||
last_increment = LED_ON_MSG_MILLIS;
|
||||
} else {
|
||||
last_increment = LED_ON_MILLIS;
|
||||
}
|
||||
next_change = cur_time + last_increment;
|
||||
} else {
|
||||
state = 0;
|
||||
next_change = cur_time + LED_CYCLE_MILLIS - last_increment;
|
||||
}
|
||||
digitalWrite(PIN_STATUS_LED, state);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UITask::setCurrScreen(UIScreen* c) {
|
||||
curr = c;
|
||||
_next_refresh = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
hardware-agnostic pre-shutdown activity should be done here
|
||||
*/
|
||||
void UITask::shutdown(bool restart){
|
||||
|
||||
#ifdef PIN_BUZZER
|
||||
/* note: we have a choice here -
|
||||
we can do a blocking buzzer.loop() with non-deterministic consequences
|
||||
or we can set a flag and delay the shutdown for a couple of seconds
|
||||
while a non-blocking buzzer.loop() plays out in UITask::loop()
|
||||
*/
|
||||
buzzer.shutdown();
|
||||
uint32_t buzzer_timer = millis(); // fail-safe shutdown
|
||||
while (buzzer.isPlaying() && (millis() - 2500) < buzzer_timer)
|
||||
buzzer.loop();
|
||||
|
||||
#endif // PIN_BUZZER
|
||||
|
||||
if (restart) {
|
||||
_board->reboot();
|
||||
} else {
|
||||
_display->turnOff();
|
||||
_board->powerOff();
|
||||
}
|
||||
}
|
||||
|
||||
bool UITask::isButtonPressed() const {
|
||||
#ifdef PIN_USER_BTN
|
||||
return user_btn.isPressed();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UITask::loop() {
|
||||
char c = 0;
|
||||
#if defined(PIN_USER_BTN)
|
||||
int ev = user_btn.check();
|
||||
if (ev == BUTTON_EVENT_CLICK) {
|
||||
c = checkDisplayOn(KEY_SELECT);
|
||||
} else if (ev == BUTTON_EVENT_LONG_PRESS) {
|
||||
c = handleLongPress(KEY_ENTER);
|
||||
}
|
||||
#endif
|
||||
#if defined(WIO_TRACKER_L1)
|
||||
ev = joystick_left.check();
|
||||
if (ev == BUTTON_EVENT_CLICK) {
|
||||
c = checkDisplayOn(KEY_LEFT);
|
||||
} else if (ev == BUTTON_EVENT_LONG_PRESS) {
|
||||
c = handleLongPress(KEY_LEFT);
|
||||
}
|
||||
ev = joystick_right.check();
|
||||
if (ev == BUTTON_EVENT_CLICK) {
|
||||
c = checkDisplayOn(KEY_RIGHT);
|
||||
} else if (ev == BUTTON_EVENT_LONG_PRESS) {
|
||||
c = handleLongPress(KEY_RIGHT);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (c != 0 && curr) {
|
||||
curr->handleInput(c);
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
|
||||
_next_refresh = 0; // trigger refresh
|
||||
}
|
||||
|
||||
userLedHandler();
|
||||
|
||||
#ifdef PIN_BUZZER
|
||||
if (buzzer.isPlaying()) buzzer.loop();
|
||||
#endif
|
||||
|
||||
if (curr) curr->poll();
|
||||
|
||||
if (_display != NULL && _display->isOn()) {
|
||||
if (millis() >= _next_refresh && curr) {
|
||||
_display->startFrame();
|
||||
int delay_millis = curr->render(*_display);
|
||||
if (millis() < _alert_expiry) { // render alert popup
|
||||
_display->setTextSize(1);
|
||||
int y = _display->height() / 3;
|
||||
int p = _display->height() / 32;
|
||||
_display->setColor(DisplayDriver::DARK);
|
||||
_display->fillRect(p, y, _display->width() - p*2, y);
|
||||
_display->setColor(DisplayDriver::LIGHT); // draw box border
|
||||
_display->drawRect(p, y, _display->width() - p*2, y);
|
||||
_display->drawTextCentered(_display->width() / 2, y + p*3, _alert);
|
||||
_next_refresh = _alert_expiry; // will need refresh when alert is dismissed
|
||||
} else {
|
||||
_next_refresh = millis() + delay_millis;
|
||||
}
|
||||
_display->endFrame();
|
||||
}
|
||||
if (millis() > _auto_off) {
|
||||
_display->turnOff();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AUTO_SHUTDOWN_MILLIVOLTS
|
||||
if (millis() > next_batt_chck) {
|
||||
uint16_t milliVolts = getBattMilliVolts();
|
||||
if (milliVolts > 0 && milliVolts < AUTO_SHUTDOWN_MILLIVOLTS) {
|
||||
|
||||
// show low battery shutdown alert
|
||||
// we should only do this for eink displays, which will persist after power loss
|
||||
#ifdef THINKNODE_M1
|
||||
if (_display != NULL) {
|
||||
_display->startFrame();
|
||||
_display->setTextSize(2);
|
||||
_display->setColor(DisplayDriver::RED);
|
||||
_display->drawTextCentered(_display->width() / 2, 20, "Low Battery.");
|
||||
_display->drawTextCentered(_display->width() / 2, 40, "Shutting Down!");
|
||||
_display->endFrame();
|
||||
}
|
||||
#endif
|
||||
|
||||
shutdown();
|
||||
|
||||
}
|
||||
next_batt_chck = millis() + 8000;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
char UITask::checkDisplayOn(char c) {
|
||||
if (_display != NULL) {
|
||||
if (!_display->isOn()) {
|
||||
_display->turnOn(); // turn display on and consume event
|
||||
c = 0;
|
||||
}
|
||||
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
|
||||
_next_refresh = 0; // trigger refresh
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
char UITask::handleLongPress(char c) {
|
||||
if (millis() - ui_started_at < 8000) { // long press in first 8 seconds since startup -> CLI/rescue
|
||||
the_mesh.enterCLIRescue();
|
||||
c = 0; // consume event
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
void UITask::handleButtonTriplePress() {
|
||||
MESH_DEBUG_PRINTLN("UITask: triple press triggered");
|
||||
// Toggle buzzer quiet mode
|
||||
#ifdef PIN_BUZZER
|
||||
if (buzzer.isQuiet()) {
|
||||
buzzer.quiet(false);
|
||||
soundBuzzer(UIEventType::ack);
|
||||
showAlert("Buzzer: ON", 600);
|
||||
} else {
|
||||
buzzer.quiet(true);
|
||||
showAlert("Buzzer: OFF", 600);
|
||||
}
|
||||
_next_refresh = 0; // trigger refresh
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
65
examples/companion_radio/ui-new/UITask.h
Normal file
65
examples/companion_radio/ui-new/UITask.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <MeshCore.h>
|
||||
#include <helpers/ui/DisplayDriver.h>
|
||||
#include <helpers/ui/UIScreen.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#include <helpers/BaseSerialInterface.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef PIN_BUZZER
|
||||
#include <helpers/ui/buzzer.h>
|
||||
#endif
|
||||
|
||||
#include "../AbstractUITask.h"
|
||||
#include "../NodePrefs.h"
|
||||
|
||||
class UITask : public AbstractUITask {
|
||||
DisplayDriver* _display;
|
||||
SensorManager* _sensors;
|
||||
#ifdef PIN_BUZZER
|
||||
genericBuzzer buzzer;
|
||||
#endif
|
||||
unsigned long _next_refresh, _auto_off;
|
||||
NodePrefs* _node_prefs;
|
||||
char _alert[80];
|
||||
unsigned long _alert_expiry;
|
||||
int _msgcount;
|
||||
unsigned long ui_started_at, next_batt_chck;
|
||||
|
||||
UIScreen* splash;
|
||||
UIScreen* home;
|
||||
UIScreen* msg_preview;
|
||||
UIScreen* curr;
|
||||
|
||||
void userLedHandler();
|
||||
|
||||
// Button action handlers
|
||||
char checkDisplayOn(char c);
|
||||
char handleLongPress(char c);
|
||||
|
||||
void setCurrScreen(UIScreen* c);
|
||||
|
||||
public:
|
||||
|
||||
UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) {
|
||||
next_batt_chck = _next_refresh = 0;
|
||||
ui_started_at = 0;
|
||||
curr = NULL;
|
||||
}
|
||||
void begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* node_prefs);
|
||||
|
||||
void gotoHomeScreen() { setCurrScreen(home); }
|
||||
void showAlert(const char* text, int duration_millis);
|
||||
int getMsgCount() const { return _msgcount; }
|
||||
bool hasDisplay() const { return _display != NULL; }
|
||||
bool isButtonPressed() const;
|
||||
|
||||
// from AbstractUITask
|
||||
void msgRead(int msgcount) override;
|
||||
void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override;
|
||||
void soundBuzzer(UIEventType bet = UIEventType::none) override;
|
||||
void loop() override;
|
||||
|
||||
void shutdown(bool restart = false);
|
||||
};
|
||||
118
examples/companion_radio/ui-new/icons.h
Normal file
118
examples/companion_radio/ui-new/icons.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// 'meshcore', 128x13px
|
||||
static const uint8_t meshcore_logo [] = {
|
||||
0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe,
|
||||
0x3c, 0x03, 0xe3, 0xff, 0xc7, 0xff, 0x8e, 0x03, 0x8f, 0xfe, 0x3f, 0xfe, 0x1f, 0xff, 0x1f, 0xfe,
|
||||
0x3e, 0x03, 0xc3, 0xff, 0x8f, 0xff, 0x0e, 0x07, 0x8f, 0xfe, 0x7f, 0xfe, 0x1f, 0xff, 0x1f, 0xfc,
|
||||
0x3e, 0x07, 0xc7, 0x80, 0x0e, 0x00, 0x0e, 0x07, 0x9e, 0x00, 0x78, 0x0e, 0x3c, 0x0f, 0x1c, 0x00,
|
||||
0x3e, 0x0f, 0xc7, 0x80, 0x1e, 0x00, 0x0e, 0x07, 0x1e, 0x00, 0x70, 0x0e, 0x38, 0x0f, 0x3c, 0x00,
|
||||
0x7f, 0x0f, 0xc7, 0xfe, 0x1f, 0xfc, 0x1f, 0xff, 0x1c, 0x00, 0x70, 0x0e, 0x38, 0x0e, 0x3f, 0xf8,
|
||||
0x7f, 0x1f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x0e, 0x38, 0x0e, 0x3f, 0xf8,
|
||||
0x7f, 0x3f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x1e, 0x3f, 0xfe, 0x3f, 0xf0,
|
||||
0x77, 0x3b, 0x87, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xfc, 0x38, 0x00,
|
||||
0x77, 0xfb, 0x8f, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xf8, 0x38, 0x00,
|
||||
0x73, 0xf3, 0x8f, 0xff, 0x0f, 0xff, 0x1c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x78, 0x7f, 0xf8,
|
||||
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfe, 0x3c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x3c, 0x7f, 0xf8,
|
||||
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8,
|
||||
};
|
||||
|
||||
static const uint8_t bluetooth_on[] = {
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x30, 0x00, 0x00,
|
||||
0x00, 0x3C, 0x00, 0x00,
|
||||
0x00, 0x3E, 0x00, 0x00,
|
||||
0x00, 0x3F, 0x80, 0x00,
|
||||
0x00, 0x3F, 0xC0, 0x00,
|
||||
0x00, 0x3B, 0xE0, 0x00,
|
||||
0x30, 0x38, 0xF8, 0x00,
|
||||
0x3C, 0x38, 0x7C, 0x00,
|
||||
0x3E, 0x38, 0x7C, 0x00,
|
||||
0x1F, 0xB8, 0xF8, 0x70,
|
||||
0x07, 0xF9, 0xF0, 0x78,
|
||||
0x03, 0xFF, 0xC0, 0x78,
|
||||
0x00, 0xFF, 0x80, 0x3C,
|
||||
0x00, 0x7F, 0x07, 0x1C,
|
||||
0x00, 0x7E, 0x07, 0x1C,
|
||||
0x03, 0xFF, 0x82, 0x1C,
|
||||
0x03, 0xFF, 0xC0, 0x78,
|
||||
0x07, 0xFB, 0xE0, 0x78,
|
||||
0x0F, 0xB8, 0xF8, 0x70,
|
||||
0x3E, 0x38, 0x7C, 0x00,
|
||||
0x3C, 0x38, 0x7C, 0x00,
|
||||
0x38, 0x38, 0xF8, 0x00,
|
||||
0x00, 0x39, 0xF0, 0x00,
|
||||
0x00, 0x3F, 0xC0, 0x00,
|
||||
0x00, 0x3F, 0x80, 0x00,
|
||||
0x00, 0x3E, 0x00, 0x00,
|
||||
0x00, 0x3C, 0x00, 0x00,
|
||||
0x00, 0x38, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t bluetooth_off[] = {
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x03, 0x80, 0x00,
|
||||
0x00, 0x03, 0xC0, 0x00,
|
||||
0x00, 0x03, 0xE0, 0x00,
|
||||
0x38, 0x03, 0xF8, 0x00,
|
||||
0x3C, 0x03, 0xFC, 0x00,
|
||||
0x3E, 0x03, 0xBF, 0x00,
|
||||
0x0F, 0x83, 0x8F, 0x80,
|
||||
0x07, 0xC3, 0x87, 0xC0,
|
||||
0x03, 0xF0, 0x03, 0xC0,
|
||||
0x00, 0xF8, 0x0F, 0x80,
|
||||
0x00, 0x7C, 0x0F, 0x00,
|
||||
0x00, 0x1F, 0x0E, 0x00,
|
||||
0x00, 0x0F, 0x80, 0x00,
|
||||
0x00, 0x07, 0xE0, 0x00,
|
||||
0x00, 0x07, 0xF0, 0x00,
|
||||
0x00, 0x0F, 0xF8, 0x00,
|
||||
0x00, 0x3F, 0xBE, 0x00,
|
||||
0x00, 0x7F, 0x9F, 0x00,
|
||||
0x00, 0xFB, 0x8F, 0xC0,
|
||||
0x03, 0xE3, 0x83, 0xE0,
|
||||
0x03, 0xC3, 0x87, 0xF0,
|
||||
0x03, 0x83, 0x8F, 0xFC,
|
||||
0x00, 0x03, 0xBF, 0x3C,
|
||||
0x00, 0x03, 0xFC, 0x1C,
|
||||
0x00, 0x03, 0xF8, 0x00,
|
||||
0x00, 0x03, 0xE0, 0x00,
|
||||
0x00, 0x03, 0xC0, 0x00,
|
||||
0x00, 0x03, 0x80, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t power_icon[] = {
|
||||
0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00,
|
||||
0x00, 0x33, 0xCC, 0x00, 0x00, 0xF3, 0xCF, 0x00, 0x01, 0xF3, 0xCF, 0x80,
|
||||
0x03, 0xF3, 0xCF, 0xC0, 0x07, 0xF3, 0xCF, 0xE0, 0x0F, 0xE3, 0xC7, 0xF0,
|
||||
0x1F, 0xC3, 0xC3, 0xF8, 0x1F, 0x83, 0xC1, 0xF8, 0x3F, 0x03, 0xC0, 0xFC,
|
||||
0x3E, 0x03, 0xC0, 0x7C, 0x3E, 0x03, 0xC0, 0x7C, 0x7E, 0x01, 0x80, 0x7E,
|
||||
0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E,
|
||||
0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0x7C,
|
||||
0x3E, 0x00, 0x00, 0x7C, 0x3F, 0x00, 0x00, 0xFC, 0x1F, 0x80, 0x01, 0xF8,
|
||||
0x1F, 0xC0, 0x03, 0xF8, 0x0F, 0xE0, 0x07, 0xF0, 0x0F, 0xF8, 0x1F, 0xF0,
|
||||
0x07, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x00,
|
||||
0x00, 0x3F, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t advert_icon[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30,
|
||||
0x1C, 0x00, 0x00, 0x38, 0x18, 0x00, 0x00, 0x18, 0x30, 0x00, 0x00, 0x0C,
|
||||
0x30, 0x60, 0x06, 0x0C, 0x60, 0xE0, 0x07, 0x06, 0x61, 0xC0, 0x03, 0x86,
|
||||
0xE1, 0x81, 0x81, 0x87, 0xC3, 0x07, 0xE0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3,
|
||||
0xC3, 0x0F, 0xF0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3,
|
||||
0xC3, 0x07, 0xE0, 0xC3, 0xC1, 0x83, 0xC1, 0x83, 0x61, 0x80, 0x01, 0x86,
|
||||
0x60, 0xC0, 0x03, 0x06, 0x70, 0xE0, 0x07, 0x0E, 0x30, 0x40, 0x02, 0x0C,
|
||||
0x38, 0x00, 0x00, 0x1C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x30,
|
||||
0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "UITask.h"
|
||||
#include <Arduino.h>
|
||||
#include <helpers/TxtDataHelpers.h>
|
||||
#include "NodePrefs.h"
|
||||
#include "MyMesh.h"
|
||||
#include "../MyMesh.h"
|
||||
|
||||
#define AUTO_OFF_MILLIS 15000 // 15 seconds
|
||||
#define BOOT_SCREEN_MILLIS 3000 // 3 seconds
|
||||
@@ -9,28 +9,18 @@
|
||||
#include <helpers/ui/buzzer.h>
|
||||
#endif
|
||||
|
||||
#include "NodePrefs.h"
|
||||
#include "../AbstractUITask.h"
|
||||
#include "../NodePrefs.h"
|
||||
|
||||
#include "Button.h"
|
||||
|
||||
enum class UIEventType
|
||||
{
|
||||
none,
|
||||
contactMessage,
|
||||
channelMessage,
|
||||
roomMessage,
|
||||
newContactMessage,
|
||||
ack
|
||||
};
|
||||
|
||||
class UITask {
|
||||
class UITask : public AbstractUITask {
|
||||
DisplayDriver* _display;
|
||||
mesh::MainBoard* _board;
|
||||
SensorManager* _sensors;
|
||||
#ifdef PIN_BUZZER
|
||||
genericBuzzer buzzer;
|
||||
#endif
|
||||
unsigned long _next_refresh, _auto_off;
|
||||
bool _connected;
|
||||
NodePrefs* _node_prefs;
|
||||
char _version_info[32];
|
||||
char _origin[62];
|
||||
@@ -64,19 +54,20 @@ class UITask {
|
||||
|
||||
public:
|
||||
|
||||
UITask(mesh::MainBoard* board) : _board(board), _display(NULL), _sensors(NULL) {
|
||||
UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) {
|
||||
_next_refresh = 0;
|
||||
ui_started_at = 0;
|
||||
_connected = false;
|
||||
}
|
||||
void begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* node_prefs);
|
||||
|
||||
void setHasConnection(bool connected) { _connected = connected; }
|
||||
bool hasDisplay() const { return _display != NULL; }
|
||||
void clearMsgPreview();
|
||||
void msgRead(int msgcount);
|
||||
void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount);
|
||||
void soundBuzzer(UIEventType bet = UIEventType::none);
|
||||
|
||||
// from AbstractUITask
|
||||
void msgRead(int msgcount) override;
|
||||
void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override;
|
||||
void soundBuzzer(UIEventType bet = UIEventType::none) override;
|
||||
void loop() override;
|
||||
|
||||
void shutdown(bool restart = false);
|
||||
void loop();
|
||||
};
|
||||
@@ -22,11 +22,11 @@
|
||||
/* ------------------------------ Config -------------------------------- */
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "24 Jul 2025"
|
||||
#define FIRMWARE_BUILD_DATE "31 Aug 2025"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.7.4"
|
||||
#define FIRMWARE_VERSION "v1.8.0"
|
||||
#endif
|
||||
|
||||
#ifndef LORA_FREQ
|
||||
@@ -98,6 +98,7 @@ struct RepeaterStats {
|
||||
uint16_t err_events; // was 'n_full_events'
|
||||
int16_t last_snr; // x 4
|
||||
uint16_t n_direct_dups, n_flood_dups;
|
||||
uint32_t total_rx_air_time_secs;
|
||||
};
|
||||
|
||||
struct ClientInfo {
|
||||
@@ -158,7 +159,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
}
|
||||
|
||||
void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr) {
|
||||
#if MAX_NEIGHBOURS // check if neighbours enabled
|
||||
#if MAX_NEIGHBOURS // check if neighbours enabled
|
||||
// find existing neighbour, else use least recently updated
|
||||
uint32_t oldest_timestamp = 0xFFFFFFFF;
|
||||
NeighbourInfo* neighbour = &neighbours[0];
|
||||
@@ -208,16 +209,19 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
stats.last_snr = (int16_t)(radio_driver.getLastSNR() * 4);
|
||||
stats.n_direct_dups = ((SimpleMeshTables *)getTables())->getNumDirectDups();
|
||||
stats.n_flood_dups = ((SimpleMeshTables *)getTables())->getNumFloodDups();
|
||||
stats.total_rx_air_time_secs = getReceiveAirTime() / 1000;
|
||||
|
||||
memcpy(&reply_data[4], &stats, sizeof(stats));
|
||||
|
||||
return 4 + sizeof(stats); // reply_len
|
||||
}
|
||||
case REQ_TYPE_GET_TELEMETRY_DATA: {
|
||||
uint8_t perm_mask = ~(payload[1]); // NEW: first reserved byte (of 4), is now inverse mask to apply to permissions
|
||||
|
||||
telemetry.reset();
|
||||
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);
|
||||
// query other sensors -- target specific
|
||||
sensors.querySensors(sender->is_admin ? 0xFF : 0x00, telemetry);
|
||||
sensors.querySensors((sender->is_admin ? 0xFF : 0x00) & perm_mask, telemetry);
|
||||
|
||||
uint8_t tlen = telemetry.getSize();
|
||||
memcpy(&reply_data[4], telemetry.getBuffer(), tlen);
|
||||
@@ -585,7 +589,7 @@ public:
|
||||
_prefs.cr = LORA_CR;
|
||||
_prefs.tx_power_dbm = LORA_TX_POWER;
|
||||
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
|
||||
_prefs.flood_advert_interval = 3; // 3 hours
|
||||
_prefs.flood_advert_interval = 12; // 12 hours
|
||||
_prefs.flood_max = 64;
|
||||
_prefs.interference_threshold = 0; // disabled
|
||||
}
|
||||
@@ -607,8 +611,8 @@ public:
|
||||
const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; }
|
||||
const char* getRole() override { return FIRMWARE_ROLE; }
|
||||
const char* getNodeName() { return _prefs.node_name; }
|
||||
NodePrefs* getNodePrefs() {
|
||||
return &_prefs;
|
||||
NodePrefs* getNodePrefs() {
|
||||
return &_prefs;
|
||||
}
|
||||
|
||||
void savePrefs() override {
|
||||
@@ -715,7 +719,18 @@ public:
|
||||
*dp = 0; // null terminator
|
||||
}
|
||||
|
||||
const uint8_t* getSelfIdPubKey() override { return self_id.pub_key; }
|
||||
void removeNeighbor(const uint8_t* pubkey, int key_len) override {
|
||||
#if MAX_NEIGHBOURS
|
||||
for (int i = 0; i < MAX_NEIGHBOURS; i++) {
|
||||
NeighbourInfo* neighbour = &neighbours[i];
|
||||
if(memcmp(neighbour->id.pub_key, pubkey, key_len) == 0){
|
||||
neighbours[i] = NeighbourInfo(); // clear neighbour entry
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mesh::LocalIdentity& getSelfId() override { return self_id; }
|
||||
|
||||
void clearStats() override {
|
||||
radio_driver.resetStats();
|
||||
@@ -778,7 +793,7 @@ void halt() {
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
static char command[80];
|
||||
static char command[160];
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
/* ------------------------------ Config -------------------------------- */
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "24 Jul 2025"
|
||||
#define FIRMWARE_BUILD_DATE "31 Aug 2025"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.7.4"
|
||||
#define FIRMWARE_VERSION "v1.8.0"
|
||||
#endif
|
||||
|
||||
#ifndef LORA_FREQ
|
||||
@@ -298,7 +298,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
// uint32_t now = getRTCClock()->getCurrentTimeUnique();
|
||||
// memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp
|
||||
memcpy(reply_data, &sender_timestamp, 4); // reflect sender_timestamp back in response packet (kind of like a 'tag')
|
||||
|
||||
|
||||
switch (payload[0]) {
|
||||
case REQ_TYPE_GET_STATUS: {
|
||||
ServerStats stats;
|
||||
@@ -326,10 +326,12 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
}
|
||||
|
||||
case REQ_TYPE_GET_TELEMETRY_DATA: {
|
||||
uint8_t perm_mask = ~(payload[1]); // NEW: first reserved byte (of 4), is now inverse mask to apply to permissions
|
||||
|
||||
telemetry.reset();
|
||||
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);
|
||||
// query other sensors -- target specific
|
||||
sensors.querySensors(sender->permission == RoomPermission::ADMIN ? 0xFF : 0x00, telemetry);
|
||||
sensors.querySensors((sender->permission == RoomPermission::ADMIN ? 0xFF : 0x00) & perm_mask, telemetry);
|
||||
|
||||
uint8_t tlen = telemetry.getSize();
|
||||
memcpy(&reply_data[4], telemetry.getBuffer(), tlen);
|
||||
@@ -744,9 +746,9 @@ public:
|
||||
_prefs.tx_power_dbm = LORA_TX_POWER;
|
||||
_prefs.disable_fwd = 1;
|
||||
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
|
||||
_prefs.flood_advert_interval = 3; // 3 hours
|
||||
_prefs.flood_advert_interval = 12; // 12 hours
|
||||
_prefs.flood_max = 64;
|
||||
_prefs.interference_threshold = 0; // disabled
|
||||
_prefs.interference_threshold = 0; // disabled
|
||||
#ifdef ROOM_PASSWORD
|
||||
StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
|
||||
#endif
|
||||
@@ -776,8 +778,8 @@ public:
|
||||
const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; }
|
||||
const char* getRole() override { return FIRMWARE_ROLE; }
|
||||
const char* getNodeName() { return _prefs.node_name; }
|
||||
NodePrefs* getNodePrefs() {
|
||||
return &_prefs;
|
||||
NodePrefs* getNodePrefs() {
|
||||
return &_prefs;
|
||||
}
|
||||
|
||||
void savePrefs() override {
|
||||
@@ -861,7 +863,7 @@ public:
|
||||
strcpy(reply, "not supported");
|
||||
}
|
||||
|
||||
const uint8_t* getSelfIdPubKey() override { return self_id.pub_key; }
|
||||
mesh::LocalIdentity& getSelfId() override { return self_id; }
|
||||
|
||||
void clearStats() override {
|
||||
radio_driver.resetStats();
|
||||
|
||||
@@ -244,10 +244,12 @@ uint8_t SensorMesh::handleRequest(uint8_t perms, uint32_t sender_timestamp, uint
|
||||
memcpy(reply_data, &sender_timestamp, 4); // reflect sender_timestamp back in response packet (kind of like a 'tag')
|
||||
|
||||
if (req_type == REQ_TYPE_GET_TELEMETRY_DATA) { // allow all
|
||||
uint8_t perm_mask = ~(payload[0]); // NEW: first reserved byte (of 4), is now inverse mask to apply to permissions
|
||||
|
||||
telemetry.reset();
|
||||
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);
|
||||
// query other sensors -- target specific
|
||||
sensors.querySensors(0xFF, telemetry); // allow all telemetry permissions for admin or guest
|
||||
sensors.querySensors(0xFF & perm_mask, telemetry); // allow all telemetry permissions for admin or guest
|
||||
// TODO: let requester know permissions they have: telemetry.addPresence(TELEM_CHANNEL_SELF, perms);
|
||||
|
||||
uint8_t tlen = telemetry.getSize();
|
||||
@@ -545,7 +547,26 @@ void SensorMesh::handleCommand(uint32_t sender_timestamp, char* command, char* r
|
||||
Serial.printf("\n");
|
||||
}
|
||||
reply[0] = 0;
|
||||
} else {
|
||||
} else if (memcmp(command, "io ", 2) == 0) { // io {value}: write, io: read
|
||||
if (command[2] == ' ') { // it's a write
|
||||
uint32_t val;
|
||||
uint32_t g = board.getGpio();
|
||||
if (command[3] == 'r') { // reset bits
|
||||
sscanf(&command[4], "%x", &val);
|
||||
val = g & ~val;
|
||||
} else if (command[3] == 's') { // set bits
|
||||
sscanf(&command[4], "%x", &val);
|
||||
val |= g;
|
||||
} else if (command[3] == 't') { // toggle bits
|
||||
sscanf(&command[4], "%x", &val);
|
||||
val ^= g;
|
||||
} else { // set value
|
||||
sscanf(&command[3], "%x", &val);
|
||||
}
|
||||
board.setGpio(val);
|
||||
}
|
||||
sprintf(reply, "%x", board.getGpio());
|
||||
} else{
|
||||
_cli.handleCommand(sender_timestamp, command, reply); // common CLI commands
|
||||
}
|
||||
}
|
||||
@@ -592,6 +613,23 @@ void SensorMesh::getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) {
|
||||
}
|
||||
}
|
||||
|
||||
void SensorMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) {
|
||||
if (dest.out_path_len < 0) {
|
||||
mesh::Packet* ack = createAck(ack_hash);
|
||||
if (ack) sendFlood(ack, TXT_ACK_DELAY);
|
||||
} else {
|
||||
uint32_t d = TXT_ACK_DELAY;
|
||||
if (getExtraAckTransmitCount() > 0) {
|
||||
mesh::Packet* a1 = createMultiAck(ack_hash, 1);
|
||||
if (a1) sendDirect(a1, dest.out_path, dest.out_path_len, d);
|
||||
d += 300;
|
||||
}
|
||||
|
||||
mesh::Packet* a2 = createAck(ack_hash);
|
||||
if (a2) sendDirect(a2, dest.out_path, dest.out_path_len, d);
|
||||
}
|
||||
}
|
||||
|
||||
void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) {
|
||||
int i = matching_peer_indexes[sender_idx];
|
||||
if (i < 0 || i >= num_contacts) {
|
||||
@@ -635,38 +673,55 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i
|
||||
memcpy(&sender_timestamp, data, 4); // timestamp (by sender's RTC clock - which could be wrong)
|
||||
uint flags = (data[4] >> 2); // message attempt number, and other flags
|
||||
|
||||
if (!(flags == TXT_TYPE_CLI_DATA)) {
|
||||
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported text type received: flags=%02x", (uint32_t)flags);
|
||||
} else if (sender_timestamp > from.last_timestamp) { // prevent replay attacks
|
||||
from.last_timestamp = sender_timestamp;
|
||||
from.last_activity = getRTCClock()->getCurrentTime();
|
||||
if (sender_timestamp > from.last_timestamp) { // prevent replay attacks
|
||||
if (flags == TXT_TYPE_PLAIN) {
|
||||
bool handled = handleIncomingMsg(from, sender_timestamp, &data[5], flags, len - 5);
|
||||
if (handled) { // if msg was handled then send an ack
|
||||
uint32_t ack_hash; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it
|
||||
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 5 + strlen((char *)&data[5]), from.id.pub_key, PUB_KEY_SIZE);
|
||||
|
||||
// len can be > original length, but 'text' will be padded with zeroes
|
||||
data[len] = 0; // need to make a C string again, with null terminator
|
||||
|
||||
uint8_t temp[166];
|
||||
char *command = (char *) &data[5];
|
||||
char *reply = (char *) &temp[5];
|
||||
handleCommand(sender_timestamp, command, reply);
|
||||
|
||||
int text_len = strlen(reply);
|
||||
if (text_len > 0) {
|
||||
uint32_t timestamp = getRTCClock()->getCurrentTimeUnique();
|
||||
if (timestamp == sender_timestamp) {
|
||||
// WORKAROUND: the two timestamps need to be different, in the CLI view
|
||||
timestamp++;
|
||||
}
|
||||
memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique
|
||||
temp[4] = (TXT_TYPE_CLI_DATA << 2);
|
||||
|
||||
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, from.id, secret, temp, 5 + text_len);
|
||||
if (reply) {
|
||||
if (from.out_path_len < 0) {
|
||||
sendFlood(reply, CLI_REPLY_DELAY_MILLIS);
|
||||
if (packet->isRouteFlood()) {
|
||||
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK
|
||||
mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len,
|
||||
PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4);
|
||||
if (path) sendFlood(path, TXT_ACK_DELAY);
|
||||
} else {
|
||||
sendDirect(reply, from.out_path, from.out_path_len, CLI_REPLY_DELAY_MILLIS);
|
||||
sendAckTo(from, ack_hash);
|
||||
}
|
||||
}
|
||||
} else if (flags == TXT_TYPE_CLI_DATA) {
|
||||
from.last_timestamp = sender_timestamp;
|
||||
from.last_activity = getRTCClock()->getCurrentTime();
|
||||
|
||||
// len can be > original length, but 'text' will be padded with zeroes
|
||||
data[len] = 0; // need to make a C string again, with null terminator
|
||||
|
||||
uint8_t temp[166];
|
||||
char *command = (char *) &data[5];
|
||||
char *reply = (char *) &temp[5];
|
||||
handleCommand(sender_timestamp, command, reply);
|
||||
|
||||
int text_len = strlen(reply);
|
||||
if (text_len > 0) {
|
||||
uint32_t timestamp = getRTCClock()->getCurrentTimeUnique();
|
||||
if (timestamp == sender_timestamp) {
|
||||
// WORKAROUND: the two timestamps need to be different, in the CLI view
|
||||
timestamp++;
|
||||
}
|
||||
memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique
|
||||
temp[4] = (TXT_TYPE_CLI_DATA << 2);
|
||||
|
||||
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, from.id, secret, temp, 5 + text_len);
|
||||
if (reply) {
|
||||
if (from.out_path_len < 0) {
|
||||
sendFlood(reply, CLI_REPLY_DELAY_MILLIS);
|
||||
} else {
|
||||
sendDirect(reply, from.out_path, from.out_path_len, CLI_REPLY_DELAY_MILLIS);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported text type received: flags=%02x", (uint32_t)flags);
|
||||
}
|
||||
} else {
|
||||
MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected");
|
||||
@@ -674,6 +729,15 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i
|
||||
}
|
||||
}
|
||||
|
||||
bool SensorMesh::handleIncomingMsg(ContactInfo& from, uint32_t timestamp, uint8_t* data, uint flags, size_t len) {
|
||||
MESH_DEBUG_PRINT("handleIncomingMsg: unhandled msg from ");
|
||||
#ifdef MESH_DEBUG
|
||||
mesh::Utils::printHex(Serial, from.id.pub_key, PUB_KEY_SIZE);
|
||||
Serial.printf(": %s\n", data);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SensorMesh::onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) {
|
||||
int i = matching_peer_indexes[sender_idx];
|
||||
if (i < 0 || i >= num_contacts) {
|
||||
|
||||
@@ -49,11 +49,11 @@ struct ContactInfo {
|
||||
};
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "24 Jul 2025"
|
||||
#define FIRMWARE_BUILD_DATE "31 Aug 2025"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.7.4"
|
||||
#define FIRMWARE_VERSION "v1.8.0"
|
||||
#endif
|
||||
|
||||
#define FIRMWARE_ROLE "sensor"
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
void formatNeighborsReply(char *reply) override {
|
||||
strcpy(reply, "not supported");
|
||||
}
|
||||
const uint8_t* getSelfIdPubKey() override { return self_id.pub_key; }
|
||||
mesh::LocalIdentity& getSelfId() override { return self_id; }
|
||||
void clearStats() override { }
|
||||
void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override;
|
||||
|
||||
@@ -140,7 +140,8 @@ protected:
|
||||
void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override;
|
||||
bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override;
|
||||
void onAckRecv(mesh::Packet* packet, uint32_t ack_crc) override;
|
||||
|
||||
virtual bool handleIncomingMsg(ContactInfo& from, uint32_t timestamp, uint8_t* data, uint flags, size_t len);
|
||||
void sendAckTo(const ContactInfo& dest, uint32_t ack_hash);
|
||||
private:
|
||||
FILESYSTEM* _fs;
|
||||
unsigned long next_local_advert, next_flood_advert;
|
||||
|
||||
@@ -50,7 +50,7 @@ void halt() {
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
static char command[120];
|
||||
static char command[160];
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
16
library.json
Normal file
16
library.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "MeshCore",
|
||||
"version" : "1.7.4",
|
||||
"dependencies": {
|
||||
"SPI": "*",
|
||||
"Wire": "*",
|
||||
"jgromes/RadioLib": "^7.1.2",
|
||||
"rweather/Crypto": "^0.4.0",
|
||||
"adafruit/RTClib": "^2.1.3",
|
||||
"melopero/Melopero RV3028": "^1.1.0",
|
||||
"electroniccats/CayenneLPP": "1.4.0"
|
||||
},
|
||||
"build": {
|
||||
"extraScript": "build_as_lib.py"
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ build_src_filter =
|
||||
+<*.cpp>
|
||||
+<helpers/*.cpp>
|
||||
+<helpers/radiolib/*.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
|
||||
; ----------------- ESP32 ---------------------
|
||||
|
||||
@@ -67,7 +68,7 @@ lib_deps =
|
||||
; esp32c6 uses arduino framework 3.x
|
||||
[esp32c6_base]
|
||||
extends = esp32_base
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.12/platform-espressif32.zip
|
||||
|
||||
; ----------------- NRF52 ---------------------
|
||||
|
||||
@@ -102,3 +103,33 @@ build_src_filter = ${arduino_base.build_src_filter}
|
||||
+<helpers/stm32>
|
||||
lib_deps = ${arduino_base.lib_deps}
|
||||
file://arch/stm32/Adafruit_LittleFS_stm32
|
||||
|
||||
[sensor_base]
|
||||
build_flags =
|
||||
-D ENV_INCLUDE_GPS=1
|
||||
-D ENV_INCLUDE_AHTX0=1
|
||||
-D ENV_INCLUDE_BME280=1
|
||||
-D ENV_INCLUDE_BMP280=1
|
||||
-D ENV_INCLUDE_SHTC3=1
|
||||
-D ENV_INCLUDE_SHT4X=1
|
||||
-D ENV_INCLUDE_LPS22HB=1
|
||||
-D ENV_INCLUDE_INA3221=1
|
||||
-D ENV_INCLUDE_INA219=1
|
||||
-D ENV_INCLUDE_INA226=1
|
||||
-D ENV_INCLUDE_INA260=1
|
||||
-D ENV_INCLUDE_MLX90614=1
|
||||
-D ENV_INCLUDE_VL53L0X=1
|
||||
lib_deps =
|
||||
adafruit/Adafruit INA3221 Library @ ^1.0.1
|
||||
adafruit/Adafruit INA219 @ ^1.2.3
|
||||
robtillaart/INA226 @ ^0.6.4
|
||||
adafruit/Adafruit INA260 Library @ ^1.5.3
|
||||
adafruit/Adafruit AHTX0 @ ^2.0.5
|
||||
adafruit/Adafruit BME280 Library @ ^2.3.0
|
||||
adafruit/Adafruit BMP280 Library @ ^2.6.8
|
||||
adafruit/Adafruit SHTC3 Library @ ^1.0.1
|
||||
sensirion/Sensirion I2C SHT4x @ ^1.1.2
|
||||
arduino-libraries/Arduino_LPS22HB @ ^1.0.2
|
||||
adafruit/Adafruit MLX90614 Library @ ^2.1.5
|
||||
adafruit/Adafruit_VL53L0X @ ^1.2.4
|
||||
stevemarple/MicroNMEA @ ^2.0.6
|
||||
|
||||
@@ -159,6 +159,7 @@ void Dispatcher::checkRecv() {
|
||||
pkt->_snr = _radio->getLastSNR() * 4.0f;
|
||||
score = _radio->packetScore(_radio->getLastSNR(), len);
|
||||
air_time = _radio->getEstAirtimeFor(len);
|
||||
rx_air_time += air_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,9 +170,9 @@ void Dispatcher::checkRecv() {
|
||||
if (pkt) {
|
||||
#if MESH_PACKET_LOGGING
|
||||
Serial.print(getLogDateTime());
|
||||
Serial.printf(": RX, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d",
|
||||
Serial.printf(": RX, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d time=%d",
|
||||
pkt->getRawLength(), pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
|
||||
(int)pkt->getSNR(), (int)_radio->getLastRSSI(), (int)(score*1000));
|
||||
(int)pkt->getSNR(), (int)_radio->getLastRSSI(), (int)(score*1000), air_time);
|
||||
|
||||
static uint8_t packet_hash[MAX_HASH_SIZE];
|
||||
pkt->calculatePacketHash(packet_hash);
|
||||
|
||||
@@ -114,7 +114,7 @@ typedef uint32_t DispatcherAction;
|
||||
*/
|
||||
class Dispatcher {
|
||||
Packet* outbound; // current outbound packet
|
||||
unsigned long outbound_expiry, outbound_start, total_air_time;
|
||||
unsigned long outbound_expiry, outbound_start, total_air_time, rx_air_time;
|
||||
unsigned long next_tx_time;
|
||||
unsigned long cad_busy_start;
|
||||
unsigned long radio_nonrx_start;
|
||||
@@ -134,7 +134,9 @@ protected:
|
||||
Dispatcher(Radio& radio, MillisecondClock& ms, PacketManager& mgr)
|
||||
: _radio(&radio), _ms(&ms), _mgr(&mgr)
|
||||
{
|
||||
outbound = NULL; total_air_time = 0; next_tx_time = 0;
|
||||
outbound = NULL;
|
||||
total_air_time = rx_air_time = 0;
|
||||
next_tx_time = 0;
|
||||
cad_busy_start = 0;
|
||||
next_floor_calib_time = next_agc_reset_time = 0;
|
||||
_err_flags = 0;
|
||||
@@ -167,6 +169,7 @@ public:
|
||||
void sendPacket(Packet* packet, uint8_t priority, uint32_t delay_millis=0);
|
||||
|
||||
unsigned long getTotalAirTime() const { return total_air_time; } // in milliseconds
|
||||
unsigned long getReceiveAirTime() const {return rx_air_time; }
|
||||
uint32_t getNumSentFlood() const { return n_sent_flood; }
|
||||
uint32_t getNumSentDirect() const { return n_sent_direct; }
|
||||
uint32_t getNumRecvFlood() const { return n_recv_flood; }
|
||||
|
||||
@@ -41,6 +41,8 @@ public:
|
||||
virtual void onAfterTransmit() { }
|
||||
virtual void reboot() = 0;
|
||||
virtual void powerOff() { /* no op */ }
|
||||
virtual uint32_t getGpio() { return 0; }
|
||||
virtual void setGpio(uint32_t values) {}
|
||||
virtual uint8_t getStartupReason() const = 0;
|
||||
virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported
|
||||
};
|
||||
|
||||
@@ -235,9 +235,13 @@ bool BaseChatMesh::onPeerPathRecv(mesh::Packet* packet, int sender_idx, const ui
|
||||
|
||||
ContactInfo& from = contacts[i];
|
||||
|
||||
// NOTE: for this impl, we just replace the current 'out_path' regardless, whenever sender sends us a new out_path.
|
||||
return onContactPathRecv(from, packet->path, packet->path_len, path, path_len, extra_type, extra, extra_len);
|
||||
}
|
||||
|
||||
bool BaseChatMesh::onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) {
|
||||
// NOTE: default impl, we just replace the current 'out_path' regardless, whenever sender sends us a new out_path.
|
||||
// FUTURE: could store multiple out_paths per contact, and try to find which is the 'best'(?)
|
||||
memcpy(from.out_path, path, from.out_path_len = path_len); // store a copy of path, for sendDirect()
|
||||
memcpy(from.out_path, out_path, from.out_path_len = out_path_len); // store a copy of path, for sendDirect()
|
||||
from.lastmod = getRTCClock()->getCurrentTime();
|
||||
|
||||
onContactPathUpdated(from);
|
||||
|
||||
@@ -88,11 +88,14 @@ protected:
|
||||
memset(connections, 0, sizeof(connections));
|
||||
}
|
||||
|
||||
void resetContacts() { num_contacts = 0; }
|
||||
|
||||
// 'UI' concepts, for sub-classes to implement
|
||||
virtual bool isAutoAddEnabled() const { return true; }
|
||||
virtual void onDiscoveredContact(ContactInfo& contact, bool is_new, uint8_t path_len, const uint8_t* path) = 0;
|
||||
virtual bool processAck(const uint8_t *data) = 0;
|
||||
virtual void onContactPathUpdated(const ContactInfo& contact) = 0;
|
||||
virtual bool onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_t in_path_len, uint8_t* out_path, uint8_t out_path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len);
|
||||
virtual void onMessageRecv(const ContactInfo& contact, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) = 0;
|
||||
virtual void onCommandDataRecv(const ContactInfo& contact, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) = 0;
|
||||
virtual void onSignedMessageRecv(const ContactInfo& contact, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) = 0;
|
||||
|
||||
@@ -165,6 +165,17 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
||||
}
|
||||
} else if (memcmp(command, "neighbors", 9) == 0) {
|
||||
_callbacks->formatNeighborsReply(reply);
|
||||
} else if (memcmp(command, "neighbor.remove ", 16) == 0) {
|
||||
const char* hex = &command[16];
|
||||
uint8_t pubkey[PUB_KEY_SIZE];
|
||||
int hex_len = min((int)strlen(hex), PUB_KEY_SIZE*2);
|
||||
int pubkey_len = hex_len / 2;
|
||||
if (mesh::Utils::fromHex(pubkey, pubkey_len, hex)) {
|
||||
_callbacks->removeNeighbor(pubkey, pubkey_len);
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "ERR: bad pubkey");
|
||||
}
|
||||
} else if (memcmp(command, "tempradio ", 10) == 0) {
|
||||
strcpy(tmp, &command[10]);
|
||||
const char *parts[5];
|
||||
@@ -206,6 +217,11 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
||||
sprintf(reply, "> %d", ((uint32_t) _prefs->advert_interval) * 2);
|
||||
} else if (memcmp(config, "guest.password", 14) == 0) {
|
||||
sprintf(reply, "> %s", _prefs->guest_password);
|
||||
} else if (sender_timestamp == 0 && memcmp(config, "prv.key", 7) == 0) { // from serial command line only
|
||||
uint8_t prv_key[PRV_KEY_SIZE];
|
||||
int len = _callbacks->getSelfId().writeTo(prv_key, PRV_KEY_SIZE);
|
||||
mesh::Utils::toHex(tmp, prv_key, len);
|
||||
sprintf(reply, "> %s", tmp);
|
||||
} else if (memcmp(config, "name", 4) == 0) {
|
||||
sprintf(reply, "> %s", _prefs->node_name);
|
||||
} else if (memcmp(config, "repeat", 6) == 0) {
|
||||
@@ -233,7 +249,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
||||
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->freq));
|
||||
} else if (memcmp(config, "public.key", 10) == 0) {
|
||||
strcpy(reply, "> ");
|
||||
mesh::Utils::toHex(&reply[2], _callbacks->getSelfIdPubKey(), PUB_KEY_SIZE);
|
||||
mesh::Utils::toHex(&reply[2], _callbacks->getSelfId().pub_key, PUB_KEY_SIZE);
|
||||
} else if (memcmp(config, "role", 4) == 0) {
|
||||
sprintf(reply, "> %s", _callbacks->getRole());
|
||||
} else {
|
||||
@@ -252,7 +268,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
||||
} else if (memcmp(config, "agc.reset.interval ", 19) == 0) {
|
||||
_prefs->agc_reset_interval = atoi(&config[19]) / 4;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK");
|
||||
sprintf(reply, "OK - interval rounded to %d", ((uint32_t) _prefs->agc_reset_interval) * 4);
|
||||
} else if (memcmp(config, "multi.acks ", 11) == 0) {
|
||||
_prefs->multi_acks = atoi(&config[11]);
|
||||
savePrefs();
|
||||
@@ -285,6 +301,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
||||
StrHelper::strncpy(_prefs->guest_password, &config[15], sizeof(_prefs->guest_password));
|
||||
savePrefs();
|
||||
strcpy(reply, "OK");
|
||||
} else if (sender_timestamp == 0 && memcmp(config, "prv.key ", 8) == 0) { // from serial command line only
|
||||
uint8_t prv_key[PRV_KEY_SIZE];
|
||||
bool success = mesh::Utils::fromHex(prv_key, PRV_KEY_SIZE, &config[8]);
|
||||
if (success) {
|
||||
_callbacks->getSelfId().readFrom(prv_key, PRV_KEY_SIZE);
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Error, invalid key");
|
||||
}
|
||||
} else if (memcmp(config, "name ", 5) == 0) {
|
||||
StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name));
|
||||
savePrefs();
|
||||
|
||||
@@ -43,7 +43,10 @@ public:
|
||||
virtual void dumpLogFile() = 0;
|
||||
virtual void setTxPower(uint8_t power_dbm) = 0;
|
||||
virtual void formatNeighborsReply(char *reply) = 0;
|
||||
virtual const uint8_t* getSelfIdPubKey() = 0;
|
||||
virtual void removeNeighbor(const uint8_t* pubkey, int key_len) {
|
||||
// no op by default
|
||||
};
|
||||
virtual mesh::LocalIdentity& getSelfId() = 0;
|
||||
virtual void clearStats() = 0;
|
||||
virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0;
|
||||
};
|
||||
@@ -53,7 +56,7 @@ class CommonCLI {
|
||||
NodePrefs* _prefs;
|
||||
CommonCLICallbacks* _callbacks;
|
||||
mesh::MainBoard* _board;
|
||||
char tmp[80];
|
||||
char tmp[PRV_KEY_SIZE*2 + 4];
|
||||
|
||||
mesh::RTCClock* getRTCClock() { return _rtc; }
|
||||
void savePrefs();
|
||||
|
||||
@@ -5,25 +5,25 @@
|
||||
class RefCountedDigitalPin {
|
||||
uint8_t _pin;
|
||||
int8_t _claims = 0;
|
||||
|
||||
uint8_t _active = 0;
|
||||
public:
|
||||
RefCountedDigitalPin(uint8_t pin): _pin(pin) { }
|
||||
RefCountedDigitalPin(uint8_t pin,uint8_t active=HIGH): _pin(pin), _active(active) { }
|
||||
|
||||
void begin() {
|
||||
pinMode(_pin, OUTPUT);
|
||||
digitalWrite(_pin, LOW); // initial state
|
||||
digitalWrite(_pin, !_active); // initial state
|
||||
}
|
||||
|
||||
void claim() {
|
||||
_claims++;
|
||||
if (_claims > 0) {
|
||||
digitalWrite(_pin, HIGH);
|
||||
digitalWrite(_pin, _active);
|
||||
}
|
||||
}
|
||||
void release() {
|
||||
_claims--;
|
||||
if (_claims == 0) {
|
||||
digitalWrite(_pin, LOW);
|
||||
digitalWrite(_pin, !_active);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -83,6 +83,7 @@ void SerialBLEInterface::onConnect(BLEServer* pServer) {
|
||||
|
||||
void SerialBLEInterface::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) {
|
||||
BLE_DEBUG_PRINTLN("onConnect(), conn_id=%d, mtu=%d", param->connect.conn_id, pServer->getPeerMTU(param->connect.conn_id));
|
||||
last_conn_id = param->connect.conn_id;
|
||||
}
|
||||
|
||||
void SerialBLEInterface::onMtuChanged(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) {
|
||||
@@ -143,6 +144,7 @@ void SerialBLEInterface::disable() {
|
||||
BLE_DEBUG_PRINTLN("SerialBLEInterface::disable");
|
||||
|
||||
pServer->getAdvertising()->stop();
|
||||
pServer->disconnect(last_conn_id);
|
||||
pService->stop();
|
||||
oldDeviceConnected = deviceConnected = false;
|
||||
adv_restart_time = 0;
|
||||
|
||||
@@ -13,6 +13,7 @@ class SerialBLEInterface : public BaseSerialInterface, BLESecurityCallbacks, BLE
|
||||
bool deviceConnected;
|
||||
bool oldDeviceConnected;
|
||||
bool _isEnabled;
|
||||
uint16_t last_conn_id;
|
||||
uint32_t _pin_code;
|
||||
unsigned long _last_write;
|
||||
unsigned long adv_restart_time;
|
||||
@@ -56,6 +57,7 @@ public:
|
||||
adv_restart_time = 0;
|
||||
_isEnabled = false;
|
||||
_last_write = 0;
|
||||
last_conn_id = 0;
|
||||
send_queue_len = recv_queue_len = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,18 @@ bool SerialWifiInterface::isWriteBusy() const {
|
||||
}
|
||||
|
||||
size_t SerialWifiInterface::checkRecvFrame(uint8_t dest[]) {
|
||||
if (!client) client = server.available();
|
||||
// check if new client connected
|
||||
auto newClient = server.available();
|
||||
if (newClient) {
|
||||
|
||||
// disconnect existing client
|
||||
deviceConnected = false;
|
||||
client.stop();
|
||||
|
||||
// switch active connection to new client
|
||||
client = newClient;
|
||||
|
||||
}
|
||||
|
||||
if (client.connected()) {
|
||||
if (!deviceConnected) {
|
||||
|
||||
@@ -115,6 +115,20 @@ void SerialBLEInterface::enable() {
|
||||
void SerialBLEInterface::disable() {
|
||||
_isEnabled = false;
|
||||
BLE_DEBUG_PRINTLN("SerialBLEInterface::disable");
|
||||
|
||||
#ifdef RAK_BOARD
|
||||
Bluefruit.disconnect(Bluefruit.connHandle());
|
||||
#else
|
||||
uint16_t conn_id;
|
||||
if (Bluefruit.getConnectedHandles(&conn_id, 1) > 0) {
|
||||
Bluefruit.disconnect(conn_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
Bluefruit.Advertising.restartOnDisconnect(false);
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.Advertising.clearData();
|
||||
|
||||
stopAdv();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,45 @@ void T114Board::begin() {
|
||||
|
||||
pinMode(PIN_VBAT_READ, INPUT);
|
||||
|
||||
// Enable SoftDevice low-power mode
|
||||
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
|
||||
|
||||
// Enable DC/DC converter for better efficiency (REG1 stage)
|
||||
NRF_POWER->DCDCEN = 1;
|
||||
|
||||
// Power down unused communication peripherals
|
||||
// UART1 - Not used on T114
|
||||
NRF_UARTE1->ENABLE = 0;
|
||||
|
||||
// SPIM2/SPIS2 - Not used (SPI is on SPIM0)
|
||||
NRF_SPIM2->ENABLE = 0;
|
||||
NRF_SPIS2->ENABLE = 0;
|
||||
|
||||
// TWI1 (I2C1) - Not used (I2C is on TWI0)
|
||||
NRF_TWIM1->ENABLE = 0;
|
||||
NRF_TWIS1->ENABLE = 0;
|
||||
|
||||
// PWM modules - Not used for standard T114 functions
|
||||
NRF_PWM1->ENABLE = 0;
|
||||
NRF_PWM2->ENABLE = 0;
|
||||
NRF_PWM3->ENABLE = 0;
|
||||
|
||||
// PDM (Digital Microphone Interface) - Not used
|
||||
NRF_PDM->ENABLE = 0;
|
||||
|
||||
// I2S - Not used
|
||||
NRF_I2S->ENABLE = 0;
|
||||
|
||||
// QSPI - Not used (no external flash)
|
||||
NRF_QSPI->ENABLE = 0;
|
||||
|
||||
// Disable unused analog peripherals
|
||||
// SAADC channels - only keep what's needed for battery monitoring
|
||||
NRF_SAADC->ENABLE = 0; // Re-enable only when needed for measurements
|
||||
|
||||
// COMP - Comparator not used
|
||||
NRF_COMP->ENABLE = 0;
|
||||
|
||||
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
|
||||
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
|
||||
#endif
|
||||
|
||||
@@ -40,6 +40,9 @@ public:
|
||||
|
||||
uint16_t getBattMilliVolts() override {
|
||||
int adcvalue = 0;
|
||||
|
||||
NRF_SAADC->ENABLE = 1;
|
||||
|
||||
analogReadResolution(12);
|
||||
analogReference(AR_INTERNAL_3_0);
|
||||
pinMode(PIN_BAT_CTL, OUTPUT); // battery adc can be read only ctrl pin 6 set to high
|
||||
@@ -49,6 +52,8 @@ public:
|
||||
adcvalue = analogRead(PIN_VBAT_READ);
|
||||
digitalWrite(6, 0);
|
||||
|
||||
NRF_SAADC->ENABLE = 0;
|
||||
|
||||
return (uint16_t)((float)adcvalue * MV_LSB * 4.9);
|
||||
}
|
||||
|
||||
@@ -60,5 +65,9 @@ public:
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void powerOff() override {
|
||||
sd_power_system_off();
|
||||
}
|
||||
|
||||
bool startOTAUpdate(const char* id, char reply[]) override;
|
||||
};
|
||||
|
||||
@@ -43,6 +43,25 @@ public:
|
||||
return "LilyGo T-Echo";
|
||||
}
|
||||
|
||||
void powerOff() override {
|
||||
#ifdef LED_RED
|
||||
digitalWrite(LED_RED, LOW);
|
||||
#endif
|
||||
#ifdef LED_GREEN
|
||||
digitalWrite(LED_GREEN, LOW);
|
||||
#endif
|
||||
#ifdef LED_BLUE
|
||||
digitalWrite(LED_BLUE, LOW);
|
||||
#endif
|
||||
#ifdef DISP_BACKLIGHT
|
||||
digitalWrite(DISP_BACKLIGHT, LOW);
|
||||
#endif
|
||||
#ifdef PIN_PWR_EN
|
||||
digitalWrite(PIN_PWR_EN, LOW);
|
||||
#endif
|
||||
sd_power_system_off();
|
||||
}
|
||||
|
||||
void reboot() override {
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
@@ -55,4 +55,16 @@ public:
|
||||
void reboot() override {
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void powerOff() override {
|
||||
|
||||
// turn off all leds, sd_power_system_off will not do this for us
|
||||
#ifdef P_LORA_TX_LED
|
||||
digitalWrite(P_LORA_TX_LED, LOW);
|
||||
#endif
|
||||
|
||||
// power off board
|
||||
sd_power_system_off();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,6 +35,12 @@ static Adafruit_BMP280 BMP280;
|
||||
static Adafruit_SHTC3 SHTC3;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_SHT4X
|
||||
#define TELEM_SHT4X_ADDRESS 0x44 //0x44 - 0x46
|
||||
#include <SensirionI2cSht4x.h>
|
||||
static SensirionI2cSht4x SHT4X;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_LPS22HB
|
||||
#include <Arduino_LPS22HB.h>
|
||||
#endif
|
||||
@@ -53,6 +59,20 @@ static Adafruit_INA3221 INA3221;
|
||||
static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS);
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA260
|
||||
#define TELEM_INA260_ADDRESS 0x41 // INA260 single channel current sensor I2C address
|
||||
#include <Adafruit_INA260.h>
|
||||
static Adafruit_INA260 INA260;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA226
|
||||
#define TELEM_INA226_ADDRESS 0x44
|
||||
#define TELEM_INA226_SHUNT_VALUE 0.100
|
||||
#define TELEM_INA226_MAX_AMP 0.8
|
||||
#include <INA226.h>
|
||||
static INA226 INA226(TELEM_INA226_ADDRESS);
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_MLX90614
|
||||
#define TELEM_MLX90614_ADDRESS 0x5A // MLX90614 IR temperature sensor I2C address
|
||||
#include <Adafruit_MLX90614.h>
|
||||
@@ -130,6 +150,21 @@ bool EnvironmentSensorManager::begin() {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if ENV_INCLUDE_SHT4X
|
||||
SHT4X.begin(*TELEM_WIRE, TELEM_SHT4X_ADDRESS);
|
||||
uint32_t serialNumber = 0;
|
||||
int16_t sht4x_error;
|
||||
sht4x_error = SHT4X.serialNumber(serialNumber);
|
||||
if (sht4x_error == 0) {
|
||||
MESH_DEBUG_PRINTLN("Found SHT4X at address: %02X", TELEM_SHT4X_ADDRESS);
|
||||
SHT4X_initialized = true;
|
||||
} else {
|
||||
SHT4X_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("SHT4X was not found at I2C address %02X", TELEM_SHT4X_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_LPS22HB
|
||||
if (BARO.begin()) {
|
||||
MESH_DEBUG_PRINTLN("Found sensor: LPS22HB");
|
||||
@@ -165,6 +200,27 @@ bool EnvironmentSensorManager::begin() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA260
|
||||
if (INA260.begin(TELEM_INA260_ADDRESS, TELEM_WIRE)) {
|
||||
MESH_DEBUG_PRINTLN("Found INA260 at address: %02X", TELEM_INA260_ADDRESS);
|
||||
INA260_initialized = true;
|
||||
} else {
|
||||
INA260_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("INA260 was not found at I2C address %02X", TELEM_INA219_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA226
|
||||
if (INA226.begin()) {
|
||||
MESH_DEBUG_PRINTLN("Found INA226 at address: %02X", TELEM_INA226_ADDRESS);
|
||||
INA226.setMaxCurrentShunt(TELEM_INA226_MAX_AMP, TELEM_INA226_SHUNT_VALUE);
|
||||
INA226_initialized = true;
|
||||
} else {
|
||||
INA226_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("INA226 was not found at I2C address %02X", TELEM_INA226_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_MLX90614
|
||||
if (MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) {
|
||||
MESH_DEBUG_PRINTLN("Found MLX90614 at address: %02X", TELEM_MLX90614_ADDRESS);
|
||||
@@ -219,7 +275,7 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen
|
||||
if (BMP280_initialized) {
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, BMP280.readTemperature());
|
||||
telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BMP280.readPressure()/100);
|
||||
telemetry.addAltitude(TELEM_CHANNEL_SELF, BME280.readAltitude(TELEM_BME280_SEALEVELPRESSURE_HPA));
|
||||
telemetry.addAltitude(TELEM_CHANNEL_SELF, BMP280.readAltitude(TELEM_BMP280_SEALEVELPRESSURE_HPA));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -233,6 +289,18 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_SHT4X
|
||||
if (SHT4X_initialized) {
|
||||
float sht4x_humidity, sht4x_temperature;
|
||||
int16_t sht4x_error;
|
||||
sht4x_error = SHT4X.measureLowestPrecision(sht4x_temperature, sht4x_humidity);
|
||||
if (sht4x_error == 0) {
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, sht4x_temperature);
|
||||
telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, sht4x_humidity);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_LPS22HB
|
||||
if (LPS22HB_initialized) {
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, BARO.readTemperature());
|
||||
@@ -265,6 +333,24 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA260
|
||||
if (INA260_initialized) {
|
||||
telemetry.addVoltage(next_available_channel, INA260.readBusVoltage() / 1000);
|
||||
telemetry.addCurrent(next_available_channel, INA260.readCurrent() / 1000);
|
||||
telemetry.addPower(next_available_channel, INA260.readPower() / 1000);
|
||||
next_available_channel++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA226
|
||||
if (INA226_initialized) {
|
||||
telemetry.addVoltage(next_available_channel, INA226.getBusVoltage());
|
||||
telemetry.addCurrent(next_available_channel, INA226.getCurrent_mA() / 1000.0);
|
||||
telemetry.addPower(next_available_channel, INA226.getPower_mW() / 1000.0);
|
||||
next_available_channel++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_MLX90614
|
||||
if (MLX90614_initialized) {
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, MLX90614.readObjectTempC());
|
||||
|
||||
@@ -13,10 +13,13 @@ protected:
|
||||
bool BMP280_initialized = false;
|
||||
bool INA3221_initialized = false;
|
||||
bool INA219_initialized = false;
|
||||
bool INA260_initialized = false;
|
||||
bool INA226_initialized = false;
|
||||
bool SHTC3_initialized = false;
|
||||
bool LPS22HB_initialized = false;
|
||||
bool MLX90614_initialized = false;
|
||||
bool VL53L0X_initialized = false;
|
||||
bool SHT4X_initialized = false;
|
||||
|
||||
bool gps_detected = false;
|
||||
bool gps_active = false;
|
||||
|
||||
@@ -21,9 +21,15 @@ public:
|
||||
virtual void setColor(Color c) = 0;
|
||||
virtual void setCursor(int x, int y) = 0;
|
||||
virtual void print(const char* str) = 0;
|
||||
virtual void printWordWrap(const char* str, int max_width) { print(str); } // fallback to basic print() if no override
|
||||
virtual void fillRect(int x, int y, int w, int h) = 0;
|
||||
virtual void drawRect(int x, int y, int w, int h) = 0;
|
||||
virtual void drawXbm(int x, int y, const uint8_t* bits, int w, int h) = 0;
|
||||
virtual uint16_t getTextWidth(const char* str) = 0;
|
||||
virtual void drawTextCentered(int mid_x, int y, const char* str) { // helper method (override to optimise)
|
||||
int w = getTextWidth(str);
|
||||
setCursor(mid_x - w/2, y);
|
||||
print(str);
|
||||
}
|
||||
virtual void endFrame() = 0;
|
||||
};
|
||||
|
||||
@@ -2,20 +2,58 @@
|
||||
|
||||
#include "../../MeshCore.h"
|
||||
|
||||
BaseDisplay* E213Display::detectEInk()
|
||||
{
|
||||
// Test 1: Logic of BUSY pin
|
||||
|
||||
// Determines controller IC manufacturer
|
||||
// Fitipower: busy when LOW
|
||||
// Solomon Systech: busy when HIGH
|
||||
|
||||
// Force display BUSY by holding reset pin active
|
||||
pinMode(DISP_RST, OUTPUT);
|
||||
digitalWrite(DISP_RST, LOW);
|
||||
|
||||
delay(10);
|
||||
|
||||
// Read whether pin is HIGH or LOW while busy
|
||||
pinMode(DISP_BUSY, INPUT);
|
||||
bool busyLogic = digitalRead(DISP_BUSY);
|
||||
|
||||
// Test complete. Release pin
|
||||
pinMode(DISP_RST, INPUT);
|
||||
|
||||
if (busyLogic == LOW) {
|
||||
#ifdef VISION_MASTER_E213
|
||||
return new EInkDisplay_VisionMasterE213 ;
|
||||
#else
|
||||
return new EInkDisplay_WirelessPaperV1_1 ;
|
||||
#endif
|
||||
} else {// busy HIGH
|
||||
#ifdef VISION_MASTER_E213
|
||||
return new EInkDisplay_VisionMasterE213V1_1 ;
|
||||
#else
|
||||
return new EInkDisplay_WirelessPaperV1_1_1 ;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool E213Display::begin() {
|
||||
if (_init) return true;
|
||||
|
||||
powerOn();
|
||||
display.begin();
|
||||
|
||||
if(display==NULL) {
|
||||
display = detectEInk();
|
||||
}
|
||||
display->begin();
|
||||
// Set to landscape mode rotated 180 degrees
|
||||
display.setRotation(3);
|
||||
display->setRotation(3);
|
||||
|
||||
_init = true;
|
||||
_isOn = true;
|
||||
|
||||
clear();
|
||||
display.fastmodeOn(); // Enable fast mode for quicker (partial) updates
|
||||
display->fastmodeOn(); // Enable fast mode for quicker (partial) updates
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -23,15 +61,23 @@ bool E213Display::begin() {
|
||||
void E213Display::powerOn() {
|
||||
#ifdef PIN_VEXT_EN
|
||||
pinMode(PIN_VEXT_EN, OUTPUT);
|
||||
#ifdef PIN_VEXT_EN_ACTIVE
|
||||
digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE);
|
||||
#else
|
||||
digitalWrite(PIN_VEXT_EN, LOW); // Active low
|
||||
#endif
|
||||
delay(50); // Allow power to stabilize
|
||||
#endif
|
||||
}
|
||||
|
||||
void E213Display::powerOff() {
|
||||
#ifdef PIN_VEXT_EN
|
||||
#ifdef PIN_VEXT_EN_ACTIVE
|
||||
digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE);
|
||||
#else
|
||||
digitalWrite(PIN_VEXT_EN, HIGH); // Turn off power
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void E213Display::turnOn() {
|
||||
@@ -46,21 +92,23 @@ void E213Display::turnOff() {
|
||||
}
|
||||
|
||||
void E213Display::clear() {
|
||||
display.clear();
|
||||
display->clear();
|
||||
|
||||
}
|
||||
|
||||
void E213Display::startFrame(Color bkg) {
|
||||
// Fill screen with white first to ensure clean background
|
||||
display.fillRect(0, 0, width(), height(), WHITE);
|
||||
display->fillRect(0, 0, width(), height(), WHITE);
|
||||
|
||||
if (bkg == LIGHT) {
|
||||
// Fill with black if light background requested (inverted for e-ink)
|
||||
display.fillRect(0, 0, width(), height(), BLACK);
|
||||
display->fillRect(0, 0, width(), height(), BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
void E213Display::setTextSize(int sz) {
|
||||
// The library handles text size internally
|
||||
display.setTextSize(sz);
|
||||
display->setTextSize(sz);
|
||||
}
|
||||
|
||||
void E213Display::setColor(Color c) {
|
||||
@@ -68,19 +116,19 @@ void E213Display::setColor(Color c) {
|
||||
}
|
||||
|
||||
void E213Display::setCursor(int x, int y) {
|
||||
display.setCursor(x, y);
|
||||
display->setCursor(x, y);
|
||||
}
|
||||
|
||||
void E213Display::print(const char *str) {
|
||||
display.print(str);
|
||||
display->print(str);
|
||||
}
|
||||
|
||||
void E213Display::fillRect(int x, int y, int w, int h) {
|
||||
display.fillRect(x, y, w, h, BLACK);
|
||||
display->fillRect(x, y, w, h, BLACK);
|
||||
}
|
||||
|
||||
void E213Display::drawRect(int x, int y, int w, int h) {
|
||||
display.drawRect(x, y, w, h, BLACK);
|
||||
display->drawRect(x, y, w, h, BLACK);
|
||||
}
|
||||
|
||||
void E213Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h) {
|
||||
@@ -98,7 +146,7 @@ void E213Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h) {
|
||||
|
||||
// If the bit is set, draw the pixel
|
||||
if (bitSet) {
|
||||
display.drawPixel(x + bx, y + by, BLACK);
|
||||
display->drawPixel(x + bx, y + by, BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,10 +155,10 @@ void E213Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h) {
|
||||
uint16_t E213Display::getTextWidth(const char *str) {
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
|
||||
display->getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
|
||||
return w;
|
||||
}
|
||||
|
||||
void E213Display::endFrame() {
|
||||
display.update();
|
||||
display->update();
|
||||
}
|
||||
|
||||
@@ -8,13 +8,17 @@
|
||||
|
||||
// Display driver for E213 e-ink display
|
||||
class E213Display : public DisplayDriver {
|
||||
EInkDisplay_VisionMasterE213 display;
|
||||
BaseDisplay* display=NULL;
|
||||
bool _init = false;
|
||||
bool _isOn = false;
|
||||
|
||||
public:
|
||||
E213Display() : DisplayDriver(250, 122) {}
|
||||
|
||||
~E213Display(){
|
||||
if(display!=NULL) {
|
||||
delete display;
|
||||
}
|
||||
}
|
||||
bool begin();
|
||||
bool isOn() override { return _isOn; }
|
||||
void turnOn() override;
|
||||
@@ -32,6 +36,7 @@ public:
|
||||
void endFrame() override;
|
||||
|
||||
private:
|
||||
BaseDisplay* detectEInk();
|
||||
void powerOn();
|
||||
void powerOff();
|
||||
};
|
||||
116
src/helpers/ui/E290Display.cpp
Normal file
116
src/helpers/ui/E290Display.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "E290Display.h"
|
||||
|
||||
#include "../../MeshCore.h"
|
||||
|
||||
bool E290Display::begin() {
|
||||
if (_init) return true;
|
||||
|
||||
powerOn();
|
||||
display.begin();
|
||||
|
||||
// Set to landscape mode rotated 180 degrees
|
||||
display.setRotation(3);
|
||||
|
||||
_init = true;
|
||||
_isOn = true;
|
||||
|
||||
clear();
|
||||
display.fastmodeOn(); // Enable fast mode for quicker (partial) updates
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void E290Display::powerOn() {
|
||||
#ifdef PIN_VEXT_EN
|
||||
pinMode(PIN_VEXT_EN, OUTPUT);
|
||||
digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE);
|
||||
delay(50); // Allow power to stabilize
|
||||
#endif
|
||||
}
|
||||
|
||||
void E290Display::powerOff() {
|
||||
#ifdef PIN_VEXT_EN
|
||||
digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); // Turn off power
|
||||
#endif
|
||||
}
|
||||
|
||||
void E290Display::turnOn() {
|
||||
if (!_init) begin();
|
||||
powerOn();
|
||||
_isOn = true;
|
||||
}
|
||||
|
||||
void E290Display::turnOff() {
|
||||
powerOff();
|
||||
_isOn = false;
|
||||
}
|
||||
|
||||
void E290Display::clear() {
|
||||
display.clear();
|
||||
}
|
||||
|
||||
void E290Display::startFrame(Color bkg) {
|
||||
// Fill screen with white first to ensure clean background
|
||||
display.fillRect(0, 0, width(), height(), WHITE);
|
||||
if (bkg == LIGHT) {
|
||||
// Fill with black if light background requested (inverted for e-ink)
|
||||
display.fillRect(0, 0, width(), height(), BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
void E290Display::setTextSize(int sz) {
|
||||
// The library handles text size internally
|
||||
display.setTextSize(sz);
|
||||
}
|
||||
|
||||
void E290Display::setColor(Color c) {
|
||||
// implemented in individual display methods
|
||||
}
|
||||
|
||||
void E290Display::setCursor(int x, int y) {
|
||||
display.setCursor(x, y);
|
||||
}
|
||||
|
||||
void E290Display::print(const char *str) {
|
||||
display.print(str);
|
||||
}
|
||||
|
||||
void E290Display::fillRect(int x, int y, int w, int h) {
|
||||
display.fillRect(x, y, w, h, BLACK);
|
||||
}
|
||||
|
||||
void E290Display::drawRect(int x, int y, int w, int h) {
|
||||
display.drawRect(x, y, w, h, BLACK);
|
||||
}
|
||||
|
||||
void E290Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h) {
|
||||
// Width in bytes for bitmap processing
|
||||
uint16_t widthInBytes = (w + 7) / 8;
|
||||
|
||||
// Process the bitmap row by row
|
||||
for (int by = 0; by < h; by++) {
|
||||
// Scan across the row bit by bit
|
||||
for (int bx = 0; bx < w; bx++) {
|
||||
// Get the current bit using MSB ordering (like GxEPDDisplay)
|
||||
uint16_t byteOffset = (by * widthInBytes) + (bx / 8);
|
||||
uint8_t bitMask = 0x80 >> (bx & 7);
|
||||
bool bitSet = bits[byteOffset] & bitMask;
|
||||
|
||||
// If the bit is set, draw the pixel
|
||||
if (bitSet) {
|
||||
display.drawPixel(x + bx, y + by, BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t E290Display::getTextWidth(const char *str) {
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
|
||||
return w;
|
||||
}
|
||||
|
||||
void E290Display::endFrame() {
|
||||
display.update();
|
||||
}
|
||||
37
src/helpers/ui/E290Display.h
Normal file
37
src/helpers/ui/E290Display.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "DisplayDriver.h"
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Wire.h>
|
||||
#include <heltec-eink-modules.h>
|
||||
|
||||
// Display driver for E290 e-ink display
|
||||
class E290Display : public DisplayDriver {
|
||||
EInkDisplay_VisionMasterE290 display;
|
||||
bool _init = false;
|
||||
bool _isOn = false;
|
||||
|
||||
public:
|
||||
E290Display() : DisplayDriver(296, 128) {}
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
void powerOn();
|
||||
void powerOff();
|
||||
};
|
||||
@@ -29,8 +29,8 @@ void GxEPDDisplay::turnOn() {
|
||||
if (!_init) begin();
|
||||
#if DISP_BACKLIGHT
|
||||
digitalWrite(DISP_BACKLIGHT, HIGH);
|
||||
_isOn = true;
|
||||
#endif
|
||||
_isOn = true;
|
||||
}
|
||||
|
||||
void GxEPDDisplay::turnOff() {
|
||||
@@ -47,6 +47,7 @@ void GxEPDDisplay::clear() {
|
||||
|
||||
void GxEPDDisplay::startFrame(Color bkg) {
|
||||
display.fillScreen(GxEPD_WHITE);
|
||||
display.setTextColor(_curr_color = GxEPD_BLACK);
|
||||
}
|
||||
|
||||
void GxEPDDisplay::setTextSize(int sz) {
|
||||
@@ -67,7 +68,12 @@ void GxEPDDisplay::setTextSize(int sz) {
|
||||
}
|
||||
|
||||
void GxEPDDisplay::setColor(Color c) {
|
||||
display.setTextColor(GxEPD_BLACK);
|
||||
// colours need to be inverted for epaper displays
|
||||
if (c == DARK) {
|
||||
display.setTextColor(_curr_color = GxEPD_WHITE);
|
||||
} else {
|
||||
display.setTextColor(_curr_color = GxEPD_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
void GxEPDDisplay::setCursor(int x, int y) {
|
||||
@@ -79,11 +85,11 @@ void GxEPDDisplay::print(const char* str) {
|
||||
}
|
||||
|
||||
void GxEPDDisplay::fillRect(int x, int y, int w, int h) {
|
||||
display.fillRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, GxEPD_BLACK);
|
||||
display.fillRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, _curr_color);
|
||||
}
|
||||
|
||||
void GxEPDDisplay::drawRect(int x, int y, int w, int h) {
|
||||
display.drawRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, GxEPD_BLACK);
|
||||
display.drawRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, _curr_color);
|
||||
}
|
||||
|
||||
void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
|
||||
@@ -116,7 +122,7 @@ void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
|
||||
// If the bit is set, draw a block of pixels
|
||||
if (bitSet) {
|
||||
// Draw the block as a filled rectangle
|
||||
display.fillRect(x1, y1, block_w, block_h, GxEPD_BLACK);
|
||||
display.fillRect(x1, y1, block_w, block_h, _curr_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +132,7 @@ uint16_t GxEPDDisplay::getTextWidth(const char* str) {
|
||||
int16_t x1, y1;
|
||||
uint16_t w, h;
|
||||
display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
|
||||
return w / SCALE_X;
|
||||
return ceil((w + 1) / SCALE_X);
|
||||
}
|
||||
|
||||
void GxEPDDisplay::endFrame() {
|
||||
|
||||
@@ -28,6 +28,7 @@ class GxEPDDisplay : public DisplayDriver {
|
||||
GxEPD2_BW<GxEPD2_150_BN, 200> display;
|
||||
bool _init = false;
|
||||
bool _isOn = false;
|
||||
uint16_t _curr_color;
|
||||
|
||||
public:
|
||||
// there is a margin in y...
|
||||
|
||||
75
src/helpers/ui/MomentaryButton.cpp
Normal file
75
src/helpers/ui/MomentaryButton.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "MomentaryButton.h"
|
||||
|
||||
MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse, bool pulldownup) {
|
||||
_pin = pin;
|
||||
_reverse = reverse;
|
||||
_pull = pulldownup;
|
||||
down_at = 0;
|
||||
prev = _reverse ? HIGH : LOW;
|
||||
cancel = 0;
|
||||
_long_millis = long_press_millis;
|
||||
}
|
||||
|
||||
void MomentaryButton::begin() {
|
||||
if (_pin >= 0) {
|
||||
pinMode(_pin, _pull ? (_reverse ? INPUT_PULLUP : INPUT_PULLDOWN) : INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
bool MomentaryButton::isPressed() const {
|
||||
return isPressed(digitalRead(_pin));
|
||||
}
|
||||
|
||||
void MomentaryButton::cancelClick() {
|
||||
cancel = 1;
|
||||
}
|
||||
|
||||
bool MomentaryButton::isPressed(int level) const {
|
||||
if (_reverse) {
|
||||
return level == LOW;
|
||||
} else {
|
||||
return level != LOW;
|
||||
}
|
||||
}
|
||||
|
||||
int MomentaryButton::check(bool repeat_click) {
|
||||
if (_pin < 0) return BUTTON_EVENT_NONE;
|
||||
|
||||
int event = BUTTON_EVENT_NONE;
|
||||
int btn = digitalRead(_pin);
|
||||
if (btn != prev) {
|
||||
if (isPressed(btn)) {
|
||||
down_at = millis();
|
||||
} else {
|
||||
// button UP
|
||||
if (_long_millis > 0) {
|
||||
if (down_at > 0 && (unsigned long)(millis() - down_at) < _long_millis) { // only a CLICK if still within the long_press millis
|
||||
event = BUTTON_EVENT_CLICK;
|
||||
}
|
||||
} else {
|
||||
event = BUTTON_EVENT_CLICK; // any UP results in CLICK event when NOT using long_press feature
|
||||
}
|
||||
if (event == BUTTON_EVENT_CLICK && cancel) {
|
||||
event = BUTTON_EVENT_NONE;
|
||||
}
|
||||
down_at = 0;
|
||||
}
|
||||
prev = btn;
|
||||
}
|
||||
if (!isPressed(btn) && cancel) { // always clear the pending 'cancel' once button is back in UP state
|
||||
cancel = 0;
|
||||
}
|
||||
|
||||
if (_long_millis > 0 && down_at > 0 && (unsigned long)(millis() - down_at) >= _long_millis) {
|
||||
event = BUTTON_EVENT_LONG_PRESS;
|
||||
down_at = 0;
|
||||
}
|
||||
if (down_at > 0 && repeat_click) {
|
||||
unsigned long diff = (unsigned long)(millis() - down_at);
|
||||
if (diff >= 700) {
|
||||
event = BUTTON_EVENT_CLICK; // wait 700 millis before repeating the click events
|
||||
}
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
25
src/helpers/ui/MomentaryButton.h
Normal file
25
src/helpers/ui/MomentaryButton.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define BUTTON_EVENT_NONE 0
|
||||
#define BUTTON_EVENT_CLICK 1
|
||||
#define BUTTON_EVENT_LONG_PRESS 2
|
||||
|
||||
class MomentaryButton {
|
||||
int8_t _pin;
|
||||
int8_t prev, cancel;
|
||||
bool _reverse, _pull;
|
||||
int _long_millis;
|
||||
unsigned long down_at;
|
||||
|
||||
bool isPressed(int level) const;
|
||||
|
||||
public:
|
||||
MomentaryButton(int8_t pin, int long_press_mills=0, bool reverse=false, bool pulldownup=false);
|
||||
void begin();
|
||||
int check(bool repeat_click=false); // returns one of BUTTON_EVENT_*
|
||||
void cancelClick(); // suppress next BUTTON_EVENT_CLICK (if already in DOWN state)
|
||||
uint8_t getPin() { return _pin; }
|
||||
bool isPressed() const;
|
||||
};
|
||||
@@ -7,6 +7,9 @@ bool SSD1306Display::i2c_probe(TwoWire& wire, uint8_t addr) {
|
||||
}
|
||||
|
||||
bool SSD1306Display::begin() {
|
||||
#ifdef DISPLAY_ROTATION
|
||||
display.setRotation(DISPLAY_ROTATION);
|
||||
#endif
|
||||
return display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_ADDRESS, true, false) && i2c_probe(Wire, DISPLAY_ADDRESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,11 @@ bool ST7789Display::begin() {
|
||||
pinMode(PIN_TFT_VDD_CTL, OUTPUT);
|
||||
pinMode(PIN_TFT_LEDA_CTL, OUTPUT);
|
||||
digitalWrite(PIN_TFT_VDD_CTL, LOW);
|
||||
#ifdef PIN_TFT_LEDA_CTL_ACTIVE
|
||||
digitalWrite(PIN_TFT_LEDA_CTL, PIN_TFT_LEDA_CTL_ACTIVE);
|
||||
#else
|
||||
digitalWrite(PIN_TFT_LEDA_CTL, LOW);
|
||||
#endif
|
||||
digitalWrite(PIN_TFT_RST, HIGH);
|
||||
|
||||
display.init();
|
||||
@@ -43,15 +47,22 @@ void ST7789Display::turnOn() {
|
||||
delay(20);
|
||||
|
||||
// Now turn on the backlight
|
||||
#ifdef PIN_TFT_LEDA_CTL_ACTIVE
|
||||
digitalWrite(PIN_TFT_LEDA_CTL, PIN_TFT_LEDA_CTL_ACTIVE);
|
||||
#else
|
||||
digitalWrite(PIN_TFT_LEDA_CTL, LOW);
|
||||
|
||||
#endif
|
||||
_isOn = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ST7789Display::turnOff() {
|
||||
digitalWrite(PIN_TFT_VDD_CTL, HIGH);
|
||||
#ifdef PIN_TFT_LEDA_CTL_ACTIVE
|
||||
digitalWrite(PIN_TFT_LEDA_CTL, !PIN_TFT_LEDA_CTL_ACTIVE);
|
||||
#else
|
||||
digitalWrite(PIN_TFT_LEDA_CTL, HIGH);
|
||||
#endif
|
||||
digitalWrite(PIN_TFT_RST, LOW);
|
||||
_isOn = false;
|
||||
}
|
||||
@@ -62,6 +73,9 @@ void ST7789Display::clear() {
|
||||
|
||||
void ST7789Display::startFrame(Color bkg) {
|
||||
display.clear();
|
||||
_color = ST77XX_WHITE;
|
||||
display.setRGB(_color);
|
||||
display.setFont(ArialMT_Plain_16);
|
||||
}
|
||||
|
||||
void ST7789Display::setTextSize(int sz) {
|
||||
@@ -81,7 +95,9 @@ void ST7789Display::setColor(Color c) {
|
||||
switch (c) {
|
||||
case DisplayDriver::DARK :
|
||||
_color = ST77XX_BLACK;
|
||||
display.setColor(OLEDDISPLAY_COLOR::BLACK);
|
||||
break;
|
||||
#if 0
|
||||
case DisplayDriver::LIGHT :
|
||||
_color = ST77XX_WHITE;
|
||||
break;
|
||||
@@ -100,8 +116,10 @@ void ST7789Display::setColor(Color c) {
|
||||
case DisplayDriver::ORANGE :
|
||||
_color = ST77XX_ORANGE;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
_color = ST77XX_WHITE;
|
||||
display.setColor(OLEDDISPLAY_COLOR::WHITE);
|
||||
break;
|
||||
}
|
||||
display.setRGB(_color);
|
||||
@@ -116,6 +134,10 @@ void ST7789Display::print(const char* str) {
|
||||
display.drawString(_x, _y, str);
|
||||
}
|
||||
|
||||
void ST7789Display::printWordWrap(const char* str, int max_width) {
|
||||
display.drawStringMaxWidth(_x, _y, max_width*SCALE_X, str);
|
||||
}
|
||||
|
||||
void ST7789Display::fillRect(int x, int y, int w, int h) {
|
||||
display.fillRect(x*SCALE_X + X_OFFSET, y*SCALE_Y + Y_OFFSET, w*SCALE_X, h*SCALE_Y);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <Wire.h>
|
||||
#include <SPI.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <ST7789Spi.h>
|
||||
#include "ST7789Spi.h"
|
||||
|
||||
class ST7789Display : public DisplayDriver {
|
||||
ST7789Spi display;
|
||||
@@ -14,8 +14,11 @@ class ST7789Display : public DisplayDriver {
|
||||
|
||||
bool i2c_probe(TwoWire& wire, uint8_t addr);
|
||||
public:
|
||||
#ifdef HELTEC_VISION_MASTER_T190
|
||||
ST7789Display() : DisplayDriver(128, 64), display(&SPI, PIN_TFT_RST, PIN_TFT_DC, PIN_TFT_CS, GEOMETRY_RAWMODE, 320, 170,PIN_TFT_SDA,-1,PIN_TFT_SCL) {_isOn = false;}
|
||||
#else
|
||||
ST7789Display() : DisplayDriver(128, 64), display(&SPI1, PIN_TFT_RST, PIN_TFT_DC, PIN_TFT_CS, GEOMETRY_RAWMODE, 240, 135) {_isOn = false;}
|
||||
|
||||
#endif
|
||||
bool begin();
|
||||
|
||||
bool isOn() override { return _isOn; }
|
||||
@@ -27,6 +30,7 @@ public:
|
||||
void setColor(Color c) override;
|
||||
void setCursor(int x, int y) override;
|
||||
void print(const char* str) override;
|
||||
void printWordWrap(const char* str, int max_width) 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;
|
||||
|
||||
21
src/helpers/ui/UIScreen.h
Normal file
21
src/helpers/ui/UIScreen.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "DisplayDriver.h"
|
||||
|
||||
#define KEY_LEFT 0xB4
|
||||
#define KEY_UP 0xB5
|
||||
#define KEY_DOWN 0xB6
|
||||
#define KEY_RIGHT 0xB7
|
||||
#define KEY_SELECT 10
|
||||
#define KEY_ENTER 13
|
||||
#define KEY_BACK 27 // Esc
|
||||
|
||||
class UIScreen {
|
||||
protected:
|
||||
UIScreen() { }
|
||||
public:
|
||||
virtual int render(DisplayDriver& display) =0; // return value is number of millis until next render
|
||||
virtual bool handleInput(char c) { return false; }
|
||||
virtual void poll() { }
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ build_src_filter = ${esp32_base.build_src_filter}
|
||||
extends = Generic_ESPNOW
|
||||
build_flags =
|
||||
${Generic_ESPNOW.build_flags}
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_CONTACTS=300
|
||||
-D MAX_GROUP_CHANNELS=1
|
||||
build_src_filter = ${Generic_ESPNOW.build_src_filter}
|
||||
+<../examples/simple_secure_chat/main.cpp>
|
||||
@@ -54,13 +54,13 @@ lib_deps =
|
||||
extends = Generic_ESPNOW
|
||||
build_flags =
|
||||
${Generic_ESPNOW.build_flags}
|
||||
-D MAX_CONTACTS=100
|
||||
-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 = ${Generic_ESPNOW.build_src_filter}
|
||||
+<../examples/companion_radio>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
lib_deps =
|
||||
${Generic_ESPNOW.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
@@ -8,9 +8,35 @@
|
||||
#include <helpers/ESP32Board.h>
|
||||
|
||||
class Heltec_CT62_Board : public ESP32Board {
|
||||
public:
|
||||
uint32_t gpio_state = 0;
|
||||
|
||||
uint16_t getBattMilliVolts() override {
|
||||
public:
|
||||
void begin() {
|
||||
ESP32Board::begin();
|
||||
#if defined(PIN_BOARD_RELAY_CH1) && defined(PIN_BOARD_RELAY_CH2)
|
||||
pinMode(PIN_BOARD_RELAY_CH1, OUTPUT);
|
||||
pinMode(PIN_BOARD_RELAY_CH2, OUTPUT);
|
||||
#endif
|
||||
#if defined(PIN_BOARD_DIGITAL_IN)
|
||||
pinMode(PIN_BOARD_DIGITAL_IN, INPUT);
|
||||
#endif
|
||||
}
|
||||
uint32_t getGpio() override {
|
||||
#if defined(PIN_BOARD_DIGITAL_IN)
|
||||
return gpio_state | (digitalRead(PIN_BOARD_DIGITAL_IN) ? 1 : 0);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
void setGpio(uint32_t values) override {
|
||||
#if defined(PIN_BOARD_RELAY_CH1) && defined(PIN_BOARD_RELAY_CH2)
|
||||
gpio_state = values;
|
||||
digitalWrite(PIN_BOARD_RELAY_CH1, values & 2);
|
||||
digitalWrite(PIN_BOARD_RELAY_CH2, values & 4);
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -55,13 +55,13 @@ build_flags =
|
||||
${Heltec_ct62.build_flags}
|
||||
; -D ARDUINO_USB_MODE=1
|
||||
; -D ARDUINO_USB_CDC_ON_BOOT=1
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_CONTACTS=300
|
||||
-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>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_ct62.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
@@ -73,16 +73,37 @@ build_flags =
|
||||
${Heltec_ct62.build_flags}
|
||||
; -D ARDUINO_USB_MODE=1
|
||||
; -D ARDUINO_USB_CDC_ON_BOOT=1
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_CONTACTS=300
|
||||
-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>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<helpers/esp32/SerialBLEInterface.cpp>
|
||||
lib_deps =
|
||||
${Heltec_ct62.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:Heltec_ct62_sensor]
|
||||
extends = Heltec_ct62
|
||||
build_flags =
|
||||
${Heltec_ct62.build_flags}
|
||||
-D ADVERT_NAME='"HT-CT62 Sensor"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D PIN_BOARD_SDA=-1
|
||||
-D PIN_BOARD_SCL=-1
|
||||
-D PIN_BOARD_RELAY_CH1=0
|
||||
-D PIN_BOARD_RELAY_CH2=1
|
||||
-D PIN_BOARD_DIGITAL_IN=19
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Heltec_ct62.build_src_filter}
|
||||
+<../examples/simple_sensor>
|
||||
lib_deps =
|
||||
${Heltec_ct62.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
77
variants/heltec_mesh_solar/MeshSolarBoard.cpp
Normal file
77
variants/heltec_mesh_solar/MeshSolarBoard.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <Arduino.h>
|
||||
#include "MeshSolarBoard.h"
|
||||
|
||||
#include <bluefruit.h>
|
||||
#include <Wire.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 MeshSolarBoard::begin() {
|
||||
// for future use, sub-classes SHOULD call this from their begin()
|
||||
startup_reason = BD_STARTUP_NORMAL;
|
||||
|
||||
meshSolarStart();
|
||||
|
||||
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
|
||||
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
|
||||
#endif
|
||||
|
||||
Wire.begin();
|
||||
}
|
||||
|
||||
bool MeshSolarBoard::startOTAUpdate(const char* id, char reply[]) {
|
||||
// Config the peripheral connection with maximum bandwidth
|
||||
// more SRAM required by SoftDevice
|
||||
// Note: All config***() function must be called before begin()
|
||||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
|
||||
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
|
||||
|
||||
Bluefruit.begin(1, 0);
|
||||
// Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
|
||||
Bluefruit.setTxPower(4);
|
||||
// Set the BLE device name
|
||||
Bluefruit.setName("MESH_SOLAR_OTA");
|
||||
|
||||
Bluefruit.Periph.setConnectCallback(connect_callback);
|
||||
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
|
||||
|
||||
// To be consistent OTA DFU should be added first if it exists
|
||||
bledfu.begin();
|
||||
|
||||
// Set up and start advertising
|
||||
// Advertising packet
|
||||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
||||
Bluefruit.Advertising.addTxPower();
|
||||
Bluefruit.Advertising.addName();
|
||||
|
||||
/* Start Advertising
|
||||
- Enable auto advertising if disconnected
|
||||
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
|
||||
- Timeout for fast mode is 30 seconds
|
||||
- Start(timeout) with timeout = 0 will advertise forever (until connected)
|
||||
|
||||
For recommended advertising interval
|
||||
https://developer.apple.com/library/content/qa/qa1931/_index.html
|
||||
*/
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
|
||||
|
||||
strcpy(reply, "OK - started");
|
||||
return true;
|
||||
}
|
||||
44
variants/heltec_mesh_solar/MeshSolarBoard.h
Normal file
44
variants/heltec_mesh_solar/MeshSolarBoard.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <MeshCore.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef HELTEC_MESH_SOLAR
|
||||
#include "meshSolarApp.h"
|
||||
#endif
|
||||
|
||||
// LoRa radio module pins for Heltec T114
|
||||
#define P_LORA_DIO_1 20
|
||||
#define P_LORA_NSS 24
|
||||
#define P_LORA_RESET 25
|
||||
#define P_LORA_BUSY 17
|
||||
#define P_LORA_SCLK 19
|
||||
#define P_LORA_MISO 23
|
||||
#define P_LORA_MOSI 22
|
||||
|
||||
#define SX126X_DIO2_AS_RF_SWITCH true
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||
|
||||
|
||||
class MeshSolarBoard : public mesh::MainBoard {
|
||||
protected:
|
||||
uint8_t startup_reason;
|
||||
|
||||
public:
|
||||
void begin();
|
||||
uint8_t getStartupReason() const override { return startup_reason; }
|
||||
|
||||
uint16_t getBattMilliVolts() override {
|
||||
return meshSolarGetBattVoltage();
|
||||
}
|
||||
|
||||
const char* getManufacturerName() const override {
|
||||
return "Heltec Mesh Solar";
|
||||
}
|
||||
|
||||
void reboot() override {
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
bool startOTAUpdate(const char* id, char reply[]) override;
|
||||
};
|
||||
91
variants/heltec_mesh_solar/platformio.ini
Normal file
91
variants/heltec_mesh_solar/platformio.ini
Normal file
@@ -0,0 +1,91 @@
|
||||
[Heltec_mesh_solar]
|
||||
extends = nrf52_base
|
||||
board = heltec_mesh_solar
|
||||
platform_packages = framework-arduinoadafruitnrf52
|
||||
board_build.ldscript = boards/nrf52840_s140_v6.ld
|
||||
build_flags = ${nrf52_base.build_flags}
|
||||
-I src/helpers/nrf52
|
||||
-I lib/nrf52/s140_nrf52_6.1.1_API/include
|
||||
-I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
|
||||
-I variants/heltec_mesh_solar
|
||||
-D HELTEC_MESH_SOLAR
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D LORA_TX_POWER=22
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
build_src_filter = ${nrf52_base.build_src_filter}
|
||||
+<helpers/*.cpp>
|
||||
+<../variants/heltec_mesh_solar>
|
||||
lib_deps =
|
||||
${nrf52_base.lib_deps}
|
||||
rweather/Crypto @ ^0.4.0
|
||||
stevemarple/MicroNMEA @ ^2.0.6
|
||||
adafruit/Adafruit NeoPixel@^1.10.0
|
||||
https://github.com/NMIoT/meshsolar/archive/dfc5330dad443982e6cdd37a61d33fc7252f468b.zip
|
||||
debug_tool = jlink
|
||||
upload_protocol = nrfutil
|
||||
|
||||
[env:Heltec_mesh_solar_repeater]
|
||||
extends = Heltec_mesh_solar
|
||||
build_src_filter = ${Heltec_mesh_solar.build_src_filter}
|
||||
+<../examples/simple_repeater>
|
||||
|
||||
build_flags =
|
||||
${Heltec_mesh_solar.build_flags}
|
||||
-D ADVERT_NAME='"Heltec_Mesh_Solar 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
|
||||
|
||||
[env:Heltec_mesh_solar_room_server]
|
||||
extends = Heltec_mesh_solar
|
||||
build_src_filter = ${Heltec_mesh_solar.build_src_filter}
|
||||
+<../examples/simple_room_server>
|
||||
build_flags =
|
||||
${Heltec_mesh_solar.build_flags}
|
||||
-D ADVERT_NAME='"Heltec_Mesh_Solar 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
|
||||
|
||||
[env:Heltec_mesh_solar_companion_radio_ble]
|
||||
extends = Heltec_mesh_solar
|
||||
build_flags =
|
||||
${Heltec_mesh_solar.build_flags}
|
||||
-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 = ${Heltec_mesh_solar.build_src_filter}
|
||||
+<helpers/nrf52/SerialBLEInterface.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_mesh_solar.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:Heltec_mesh_solar_companion_radio_usb]
|
||||
extends = Heltec_mesh_solar
|
||||
build_flags =
|
||||
${Heltec_mesh_solar.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 = ${Heltec_mesh_solar.build_src_filter}
|
||||
+<helpers/nrf52/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_mesh_solar.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
123
variants/heltec_mesh_solar/target.cpp
Normal file
123
variants/heltec_mesh_solar/target.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include <Arduino.h>
|
||||
#include "target.h"
|
||||
#include <helpers/ArduinoHelpers.h>
|
||||
#include <helpers/sensors/MicroNMEALocationProvider.h>
|
||||
|
||||
MeshSolarBoard board;
|
||||
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
||||
|
||||
WRAPPER_CLASS radio_driver(radio, board);
|
||||
|
||||
VolatileRTCClock fallback_clock;
|
||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1);
|
||||
SolarSensorManager sensors = SolarSensorManager(nmea);
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
DISPLAY_CLASS display;
|
||||
#endif
|
||||
|
||||
bool radio_init() {
|
||||
rtc_clock.begin(Wire);
|
||||
return radio.std_init(&SPI);
|
||||
}
|
||||
|
||||
uint32_t radio_get_rng_seed() {
|
||||
return radio.random(0x7FFFFFFF);
|
||||
}
|
||||
|
||||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
|
||||
radio.setFrequency(freq);
|
||||
radio.setSpreadingFactor(sf);
|
||||
radio.setBandwidth(bw);
|
||||
radio.setCodingRate(cr);
|
||||
}
|
||||
|
||||
void radio_set_tx_power(uint8_t dbm) {
|
||||
radio.setOutputPower(dbm);
|
||||
}
|
||||
|
||||
mesh::LocalIdentity radio_new_identity() {
|
||||
RadioNoiseListener rng(radio);
|
||||
return mesh::LocalIdentity(&rng); // create new random identity
|
||||
}
|
||||
|
||||
void SolarSensorManager::start_gps() {
|
||||
if (!gps_active) {
|
||||
gps_active = true;
|
||||
_location->begin();
|
||||
}
|
||||
}
|
||||
|
||||
void SolarSensorManager::stop_gps() {
|
||||
if (gps_active) {
|
||||
gps_active = false;
|
||||
_location->stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool SolarSensorManager::begin() {
|
||||
Serial1.begin(9600);
|
||||
|
||||
// We'll consider GPS detected if we see any data on Serial1
|
||||
gps_detected = (Serial1.available() > 0);
|
||||
|
||||
if (gps_detected) {
|
||||
MESH_DEBUG_PRINTLN("GPS detected");
|
||||
} else {
|
||||
MESH_DEBUG_PRINTLN("No GPS detected");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SolarSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) {
|
||||
if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission?
|
||||
telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SolarSensorManager::loop() {
|
||||
static long next_gps_update = 0;
|
||||
|
||||
_location->loop();
|
||||
|
||||
if (millis() > next_gps_update) {
|
||||
if (_location->isValid()) {
|
||||
node_lat = ((double)_location->getLatitude())/1000000.;
|
||||
node_lon = ((double)_location->getLongitude())/1000000.;
|
||||
node_altitude = ((double)_location->getAltitude()) / 1000.0;
|
||||
MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon);
|
||||
}
|
||||
next_gps_update = millis() + 1000;
|
||||
}
|
||||
}
|
||||
|
||||
int SolarSensorManager::getNumSettings() const {
|
||||
return gps_detected ? 1 : 0; // only show GPS setting if GPS is detected
|
||||
}
|
||||
|
||||
const char* SolarSensorManager::getSettingName(int i) const {
|
||||
return (gps_detected && i == 0) ? "gps" : NULL;
|
||||
}
|
||||
|
||||
const char* SolarSensorManager::getSettingValue(int i) const {
|
||||
if (gps_detected && i == 0) {
|
||||
return gps_active ? "1" : "0";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool SolarSensorManager::setSettingValue(const char* name, const char* value) {
|
||||
if (gps_detected && strcmp(name, "gps") == 0) {
|
||||
if (strcmp(value, "0") == 0) {
|
||||
stop_gps();
|
||||
} else {
|
||||
start_gps();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false; // not supported
|
||||
}
|
||||
46
variants/heltec_mesh_solar/target.h
Normal file
46
variants/heltec_mesh_solar/target.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include <RadioLib.h>
|
||||
#include <helpers/radiolib/RadioLibWrappers.h>
|
||||
#include <MeshSolarBoard.h>
|
||||
#include <helpers/radiolib/CustomSX1262Wrapper.h>
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#include <helpers/sensors/LocationProvider.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/ST7789Display.h>
|
||||
#endif
|
||||
|
||||
class SolarSensorManager : public SensorManager {
|
||||
bool gps_active = false;
|
||||
bool gps_detected = false;
|
||||
LocationProvider* _location;
|
||||
|
||||
void start_gps();
|
||||
void stop_gps();
|
||||
public:
|
||||
SolarSensorManager(LocationProvider &location): _location(&location) { }
|
||||
bool begin() override;
|
||||
bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override;
|
||||
void loop() override;
|
||||
int getNumSettings() const override;
|
||||
const char* getSettingName(int i) const override;
|
||||
const char* getSettingValue(int i) const override;
|
||||
bool setSettingValue(const char* name, const char* value) override;
|
||||
};
|
||||
|
||||
extern MeshSolarBoard board;
|
||||
extern WRAPPER_CLASS radio_driver;
|
||||
extern AutoDiscoverRTCClock rtc_clock;
|
||||
extern SolarSensorManager 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();
|
||||
16
variants/heltec_mesh_solar/variant.cpp
Normal file
16
variants/heltec_mesh_solar/variant.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "variant.h"
|
||||
#include "wiring_constants.h"
|
||||
#include "wiring_digital.h"
|
||||
|
||||
const uint32_t g_ADigitalPinMap[] = {
|
||||
0xff, 0xff, 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
|
||||
};
|
||||
|
||||
void initVariant()
|
||||
{
|
||||
pinMode(PIN_USER_BTN, INPUT);
|
||||
pinMode(BQ4050_EMERGENCY_SHUTDOWN_PIN, INPUT);
|
||||
}
|
||||
127
variants/heltec_mesh_solar/variant.h
Normal file
127
variants/heltec_mesh_solar/variant.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* variant.h
|
||||
* Copyright (C) 2023 Seeed K.K.
|
||||
* MIT License
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "WVariant.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Low frequency clock source
|
||||
|
||||
#define USE_LFXO // 32.768 kHz crystal oscillator
|
||||
#define VARIANT_MCK (64000000ul)
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Power
|
||||
|
||||
#define NRF_APM
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Number of pins
|
||||
|
||||
#define PINS_COUNT (48)
|
||||
#define NUM_DIGITAL_PINS (48)
|
||||
#define NUM_ANALOG_INPUTS (1)
|
||||
#define NUM_ANALOG_OUTPUTS (0)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// UART pin definition
|
||||
|
||||
#define PIN_SERIAL1_RX (37)
|
||||
#define PIN_SERIAL1_TX (39)
|
||||
|
||||
#define PIN_SERIAL2_RX (9)
|
||||
#define PIN_SERIAL2_TX (10)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// I2C pin definition
|
||||
#define WIRE_INTERFACES_COUNT (2)
|
||||
|
||||
#define PIN_WIRE_SDA (6)
|
||||
#define PIN_WIRE_SCL (26)
|
||||
|
||||
#define PIN_WIRE1_SDA (30)
|
||||
#define PIN_WIRE1_SCL (5)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SPI pin definition
|
||||
|
||||
#define SPI_INTERFACES_COUNT (2)
|
||||
|
||||
#define PIN_SPI_MISO (23)
|
||||
#define PIN_SPI_MOSI (22)
|
||||
#define PIN_SPI_SCK (19)
|
||||
#define PIN_SPI_NSS (24)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Builtin LEDs
|
||||
|
||||
#define LED_BUILTIN (12)
|
||||
#define PIN_LED LED_BUILTIN
|
||||
#define LED_RED LED_BUILTIN
|
||||
#define LED_BLUE (-1) // No blue led, prevents Bluefruit flashing the green LED during advertising
|
||||
#define LED_PIN LED_BUILTIN
|
||||
|
||||
#define LED_STATE_ON LOW
|
||||
|
||||
#define PIN_NEOPIXEL (47)
|
||||
#define NEOPIXEL_NUM (1)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Builtin buttons
|
||||
|
||||
#define PIN_BUTTON1 (42)
|
||||
#define BUTTON_PIN PIN_BUTTON1
|
||||
|
||||
// #define PIN_BUTTON2 (11)
|
||||
// #define BUTTON_PIN2 PIN_BUTTON2
|
||||
|
||||
#define PIN_USER_BTN BUTTON_PIN
|
||||
|
||||
#define EXTERNAL_FLASH_DEVICES MX25R1635F
|
||||
#define EXTERNAL_FLASH_USE_QSPI
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Lora
|
||||
|
||||
#define USE_SX1262
|
||||
#define LORA_CS (24)
|
||||
#define SX126X_DIO1 (20)
|
||||
#define SX126X_BUSY (17)
|
||||
#define SX126X_RESET (25)
|
||||
#define SX126X_DIO2_AS_RF_SWITCH
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||
|
||||
#define PIN_SPI1_MISO (43)
|
||||
#define PIN_SPI1_MOSI (41)
|
||||
#define PIN_SPI1_SCK (40)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Buzzer
|
||||
|
||||
// #define PIN_BUZZER (46)
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GPS
|
||||
|
||||
#define GPS_RESET (38)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TFT
|
||||
// #define PIN_TFT_SCL (40)
|
||||
// #define PIN_TFT_SDA (41)
|
||||
// #define PIN_TFT_RST (2)
|
||||
// #define PIN_TFT_VDD_CTL (3)
|
||||
// #define PIN_TFT_LEDA_CTL (15)
|
||||
// #define PIN_TFT_CS (11)
|
||||
// #define PIN_TFT_DC (12)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define BQ4050_SDA_PIN (33) // I2C data line pin
|
||||
#define BQ4050_SCL_PIN (32) // I2C clock line pin
|
||||
#define BQ4050_EMERGENCY_SHUTDOWN_PIN (35) // Emergency shutdown pin
|
||||
@@ -39,10 +39,11 @@ extends = Heltec_tracker_base
|
||||
build_flags =
|
||||
${Heltec_tracker_base.build_flags}
|
||||
-I src/helpers/ui
|
||||
-I examples/companion_radio/ui-new
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1 ; need for Serial
|
||||
-D DISPLAY_ROTATION=1
|
||||
-D DISPLAY_CLASS=ST7735Display
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_CONTACTS=300
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D BLE_PIN_CODE=123456 ; HWT will use display for pin
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
@@ -51,7 +52,9 @@ build_flags =
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Heltec_tracker_base.build_src_filter}
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
+<helpers/ui/ST7735Display.cpp>
|
||||
lib_deps =
|
||||
${Heltec_tracker_base.lib_deps}
|
||||
|
||||
@@ -21,6 +21,7 @@ HWTSensorManager sensors = HWTSensorManager(nmea);
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
DISPLAY_CLASS display(&board.periph_power); // peripheral power pin is shared
|
||||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
|
||||
#endif
|
||||
|
||||
bool radio_init() {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <helpers/sensors/LocationProvider.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/ST7735Display.h>
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
class HWTSensorManager : public SensorManager {
|
||||
@@ -36,6 +37,7 @@ extern HWTSensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
extern DISPLAY_CLASS display;
|
||||
extern MomentaryButton user_btn;
|
||||
#endif
|
||||
|
||||
bool radio_init();
|
||||
|
||||
@@ -35,6 +35,7 @@ build_flags =
|
||||
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
|
||||
+<../examples/simple_repeater>
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
lib_deps =
|
||||
${Heltec_lora32_v2.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
@@ -53,6 +54,7 @@ build_flags =
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${Heltec_lora32_v2.lib_deps}
|
||||
@@ -76,6 +78,7 @@ lib_deps =
|
||||
extends = Heltec_lora32_v2
|
||||
build_flags =
|
||||
${Heltec_lora32_v2.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
@@ -84,7 +87,9 @@ build_flags =
|
||||
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/companion_radio>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_lora32_v2.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
@@ -93,6 +98,7 @@ lib_deps =
|
||||
extends = Heltec_lora32_v2
|
||||
build_flags =
|
||||
${Heltec_lora32_v2.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
@@ -104,7 +110,9 @@ build_flags =
|
||||
build_src_filter = ${Heltec_lora32_v2.build_src_filter}
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/companion_radio>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_lora32_v2.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
@@ -18,6 +18,7 @@ SensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
DISPLAY_CLASS display;
|
||||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
|
||||
#endif
|
||||
|
||||
bool radio_init() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <helpers/SensorManager.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/SSD1306Display.h>
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
extern HeltecV2Board board;
|
||||
@@ -18,6 +19,7 @@ extern SensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
extern DISPLAY_CLASS display;
|
||||
extern MomentaryButton user_btn;
|
||||
#endif
|
||||
|
||||
bool radio_init();
|
||||
|
||||
@@ -3,8 +3,10 @@ extends = esp32_base
|
||||
board = esp32-s3-devkitc-1
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
${sensor_base.build_flags}
|
||||
-I variants/heltec_v3
|
||||
-D HELTEC_LORA_V3
|
||||
-D ESP32_CPU_FREQ=80
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D LORA_TX_POWER=22
|
||||
@@ -15,14 +17,8 @@ build_flags =
|
||||
-D PIN_VEXT_EN=36
|
||||
-D SX126X_DIO2_AS_RF_SWITCH=true
|
||||
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_CURRENT_LIMIT=160
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
-D ENV_INCLUDE_AHTX0=1
|
||||
-D ENV_INCLUDE_BME280=1
|
||||
-D ENV_INCLUDE_BMP280=1
|
||||
-D ENV_INCLUDE_INA3221=1
|
||||
-D ENV_INCLUDE_INA219=1
|
||||
-D ENV_INCLUDE_GPS=1
|
||||
-D PIN_GPS_RX=47
|
||||
-D PIN_GPS_TX=48
|
||||
-D PIN_GPS_EN=26
|
||||
@@ -31,13 +27,7 @@ build_src_filter = ${esp32_base.build_src_filter}
|
||||
+<helpers/sensors>
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
adafruit/Adafruit SSD1306 @ ^2.5.13
|
||||
adafruit/Adafruit INA3221 Library @ ^1.0.1
|
||||
adafruit/Adafruit INA219 @ ^1.2.3
|
||||
adafruit/Adafruit AHTX0 @ ^2.0.5
|
||||
adafruit/Adafruit BME280 Library @ ^2.3.0
|
||||
adafruit/Adafruit BMP280 Library@^2.6.8
|
||||
stevemarple/MicroNMEA @ ^2.0.6
|
||||
${sensor_base.lib_deps}
|
||||
|
||||
[env:Heltec_v3_repeater]
|
||||
extends = Heltec_lora32_v3
|
||||
@@ -57,6 +47,7 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter}
|
||||
lib_deps =
|
||||
${Heltec_lora32_v3.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
bakercp/CRC32 @ ^2.0.0
|
||||
|
||||
[env:Heltec_v3_room_server]
|
||||
extends = Heltec_lora32_v3
|
||||
@@ -81,7 +72,7 @@ lib_deps =
|
||||
extends = Heltec_lora32_v3
|
||||
build_flags =
|
||||
${Heltec_lora32_v3.build_flags}
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_CONTACTS=300
|
||||
-D MAX_GROUP_CHANNELS=1
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
@@ -95,6 +86,7 @@ lib_deps =
|
||||
extends = Heltec_lora32_v3
|
||||
build_flags =
|
||||
${Heltec_lora32_v3.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
@@ -102,7 +94,9 @@ build_flags =
|
||||
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
|
||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/companion_radio>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_lora32_v3.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
@@ -111,18 +105,22 @@ lib_deps =
|
||||
extends = Heltec_lora32_v3
|
||||
build_flags =
|
||||
${Heltec_lora32_v3.build_flags}
|
||||
-D MAX_CONTACTS=100
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=160
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D BLE_PIN_CODE=123456 ; dynamic, random PIN
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3400
|
||||
-D BLE_DEBUG_LOGGING=1
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_lora32_v3.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
@@ -131,6 +129,7 @@ lib_deps =
|
||||
extends = Heltec_lora32_v3
|
||||
build_flags =
|
||||
${Heltec_lora32_v3.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
@@ -141,8 +140,10 @@ build_flags =
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_lora32_v3.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
@@ -157,8 +158,6 @@ build_flags =
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ENV_PIN_SDA=33
|
||||
-D ENV_PIN_SCL=34
|
||||
-D ENV_INCLUDE_MLX90614=1
|
||||
-D ENV_INCLUDE_VL53L0X=1
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
@@ -168,8 +167,6 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter}
|
||||
lib_deps =
|
||||
${Heltec_lora32_v3.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
adafruit/Adafruit MLX90614 Library @ ^2.1.5
|
||||
adafruit/Adafruit_VL53L0X @ ^1.2.4
|
||||
|
||||
[env:Heltec_WSL3_repeater]
|
||||
extends = Heltec_lora32_v3
|
||||
@@ -187,6 +184,7 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter}
|
||||
lib_deps =
|
||||
${Heltec_lora32_v3.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
bakercp/CRC32 @ ^2.0.0
|
||||
|
||||
[env:Heltec_WSL3_room_server]
|
||||
extends = Heltec_lora32_v3
|
||||
@@ -218,7 +216,21 @@ build_flags =
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_lora32_v3.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:Heltec_WSL3_companion_radio_usb]
|
||||
extends = Heltec_lora32_v3
|
||||
build_flags =
|
||||
${Heltec_lora32_v3.build_flags}
|
||||
-D MAX_CONTACTS=140
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
|
||||
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
|
||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter}
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_lora32_v3.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
@@ -25,6 +25,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
DISPLAY_CLASS display;
|
||||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
|
||||
#endif
|
||||
|
||||
bool radio_init() {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <helpers/sensors/EnvironmentSensorManager.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/SSD1306Display.h>
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
extern HeltecV3Board board;
|
||||
@@ -19,6 +20,7 @@ extern EnvironmentSensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
extern DISPLAY_CLASS display;
|
||||
extern MomentaryButton user_btn;
|
||||
#endif
|
||||
|
||||
bool radio_init();
|
||||
|
||||
69
variants/heltec_vision_master_e213/HeltecE213Board.cpp
Normal file
69
variants/heltec_vision_master_e213/HeltecE213Board.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "HeltecE213Board.h"
|
||||
|
||||
void HeltecE213Board::begin() {
|
||||
ESP32Board::begin();
|
||||
|
||||
pinMode(PIN_ADC_CTRL, OUTPUT);
|
||||
digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive
|
||||
|
||||
periph_power.begin();
|
||||
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
if (reason == ESP_RST_DEEPSLEEP) {
|
||||
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||
if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep)
|
||||
startup_reason = BD_STARTUP_RX_PACKET;
|
||||
}
|
||||
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
|
||||
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1);
|
||||
}
|
||||
}
|
||||
|
||||
void HeltecE213Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) {
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
||||
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
|
||||
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
|
||||
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
|
||||
|
||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
|
||||
|
||||
if (pin_wake_btn < 0) {
|
||||
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
|
||||
} else {
|
||||
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
|
||||
}
|
||||
|
||||
if (secs > 0) {
|
||||
esp_sleep_enable_timer_wakeup(secs * 1000000);
|
||||
}
|
||||
|
||||
// Finally set ESP32 into sleep
|
||||
esp_deep_sleep_start(); // CPU halts here and never returns!
|
||||
}
|
||||
|
||||
void HeltecE213Board::powerOff() {
|
||||
// TODO: re-enable this when there is a definite wake-up source pin:
|
||||
// enterDeepSleep(0);
|
||||
}
|
||||
|
||||
uint16_t HeltecE213Board::getBattMilliVolts() {
|
||||
analogReadResolution(10);
|
||||
digitalWrite(PIN_ADC_CTRL, HIGH);
|
||||
|
||||
uint32_t raw = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
raw += analogRead(PIN_VBAT_READ);
|
||||
}
|
||||
raw = raw / 8;
|
||||
|
||||
digitalWrite(PIN_ADC_CTRL, LOW);
|
||||
|
||||
return (5.42 * (3.3 / 1024.0) * raw) * 1000;
|
||||
}
|
||||
|
||||
const char* HeltecE213Board::getManufacturerName() const {
|
||||
return "Heltec E213";
|
||||
}
|
||||
|
||||
30
variants/heltec_vision_master_e213/HeltecE213Board.h
Normal file
30
variants/heltec_vision_master_e213/HeltecE213Board.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <helpers/RefCountedDigitalPin.h>
|
||||
#include <helpers/ESP32Board.h>
|
||||
#include <driver/rtc_io.h>
|
||||
|
||||
// LoRa radio module pins for heltec_vision_master_e213
|
||||
#define P_LORA_DIO_1 14
|
||||
#define P_LORA_NSS 8
|
||||
#define P_LORA_RESET 12
|
||||
#define P_LORA_BUSY 13
|
||||
#define P_LORA_SCLK 9
|
||||
#define P_LORA_MISO 11
|
||||
#define P_LORA_MOSI 10
|
||||
|
||||
class HeltecE213Board : public ESP32Board {
|
||||
|
||||
public:
|
||||
RefCountedDigitalPin periph_power;
|
||||
|
||||
HeltecE213Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { }
|
||||
|
||||
void begin();
|
||||
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
|
||||
void powerOff() override;
|
||||
uint16_t getBattMilliVolts() override;
|
||||
const char* getManufacturerName() const override ;
|
||||
|
||||
};
|
||||
61
variants/heltec_vision_master_e213/pins_arduino.h
Normal file
61
variants/heltec_vision_master_e213/pins_arduino.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t LED_BUILTIN = 45; // LED is not populated on earliest board variant
|
||||
#define BUILTIN_LED LED_BUILTIN // Backward compatibility
|
||||
#define LED_BUILTIN LED_BUILTIN
|
||||
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
static const uint8_t SDA = 39;
|
||||
static const uint8_t SCL = 38;
|
||||
|
||||
static const uint8_t SS = 8;
|
||||
static const uint8_t MOSI = 10;
|
||||
static const uint8_t MISO = 11;
|
||||
static const uint8_t SCK = 9;
|
||||
|
||||
static const uint8_t A0 = 1;
|
||||
static const uint8_t A1 = 2;
|
||||
static const uint8_t A2 = 3;
|
||||
static const uint8_t A3 = 4;
|
||||
static const uint8_t A4 = 5;
|
||||
static const uint8_t A5 = 6;
|
||||
static const uint8_t A6 = 7;
|
||||
static const uint8_t A7 = 8;
|
||||
static const uint8_t A8 = 9;
|
||||
static const uint8_t A9 = 10;
|
||||
static const uint8_t A10 = 11;
|
||||
static const uint8_t A11 = 12;
|
||||
static const uint8_t A12 = 13;
|
||||
static const uint8_t A13 = 14;
|
||||
static const uint8_t A14 = 15;
|
||||
static const uint8_t A15 = 16;
|
||||
static const uint8_t A16 = 17;
|
||||
static const uint8_t A17 = 18;
|
||||
static const uint8_t A18 = 19;
|
||||
static const uint8_t A19 = 20;
|
||||
|
||||
static const uint8_t T1 = 1;
|
||||
static const uint8_t T2 = 2;
|
||||
static const uint8_t T3 = 3;
|
||||
static const uint8_t T4 = 4;
|
||||
static const uint8_t T5 = 5;
|
||||
static const uint8_t T6 = 6;
|
||||
static const uint8_t T7 = 7;
|
||||
static const uint8_t T8 = 8;
|
||||
static const uint8_t T9 = 9;
|
||||
static const uint8_t T10 = 10;
|
||||
static const uint8_t T11 = 11;
|
||||
static const uint8_t T12 = 12;
|
||||
static const uint8_t T13 = 13;
|
||||
static const uint8_t T14 = 14;
|
||||
|
||||
static const uint8_t RST_LoRa = 12;
|
||||
static const uint8_t BUSY_LoRa = 13;
|
||||
static const uint8_t DIO1 = 14;
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
86
variants/heltec_vision_master_e213/platformio.ini
Normal file
86
variants/heltec_vision_master_e213/platformio.ini
Normal file
@@ -0,0 +1,86 @@
|
||||
[Heltec_Vision_Master_E213_base]
|
||||
extends = esp32_base
|
||||
board = heltec_vision_master_e213
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
-I variants/heltec_vision_master_e213
|
||||
-D HELTEC_VISION_MASTER_E213
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D LORA_TX_POWER=22
|
||||
-D P_LORA_TX_LED=45
|
||||
-D PIN_USER_BTN=0
|
||||
-D PIN_VEXT_EN=18
|
||||
-D PIN_VEXT_EN_ACTIVE=HIGH
|
||||
-D PIN_VBAT_READ=7
|
||||
-D PIN_ADC_CTRL=46
|
||||
-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
|
||||
-D PIN_BOARD_SDA=39
|
||||
-D PIN_BOARD_SCL=38
|
||||
-D DISP_CS=5
|
||||
-D DISP_BUSY=1
|
||||
-D DISP_DC=2
|
||||
-D DISP_RST=3
|
||||
-D DISP_SCLK=4
|
||||
-D DISP_MOSI=6
|
||||
-D Vision_Master_E213
|
||||
build_src_filter = ${esp32_base.build_src_filter}
|
||||
+<../variants/heltec_vision_master_e213>
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
https://github.com/Quency-D/heltec-eink-modules/archive/563dd41fd850a1bc3039b8723da4f3a20fe1c800.zip
|
||||
|
||||
[env:Heltec_Vision_Master_E213_radio_ble]
|
||||
extends = Heltec_Vision_Master_E213_base
|
||||
build_flags =
|
||||
${Heltec_Vision_Master_E213_base.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=300
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D DISPLAY_CLASS=E213Display
|
||||
-D BLE_PIN_CODE=123456 ; dynamic, random PIN
|
||||
-D BLE_DEBUG_LOGGING=1
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
build_src_filter = ${Heltec_Vision_Master_E213_base.build_src_filter}
|
||||
+<helpers/ui/E213Display.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_Vision_Master_E213_base.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:Heltec_Vision_Master_E213_repeater]
|
||||
extends = Heltec_Vision_Master_E213_base
|
||||
build_flags =
|
||||
${Heltec_Vision_Master_E213_base.build_flags}
|
||||
-D DISPLAY_CLASS=E213Display
|
||||
-D ADVERT_NAME='"Heltec E213 Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
build_src_filter = ${Heltec_Vision_Master_E213_base.build_src_filter}
|
||||
+<helpers/ui/E213Display.cpp>
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${Heltec_Vision_Master_E213_base.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:Heltec_Vision_Master_E213_room_server]
|
||||
extends = Heltec_Vision_Master_E213_base
|
||||
build_flags =
|
||||
${Heltec_Vision_Master_E213_base.build_flags}
|
||||
-D DISPLAY_CLASS=E213Display
|
||||
-D ADVERT_NAME='"Heltec E213 Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ROOM_PASSWORD='"hello"'
|
||||
build_src_filter = ${Heltec_Vision_Master_E213_base.build_src_filter}
|
||||
+<helpers/ui/E213Display.cpp>
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${Heltec_Vision_Master_E213_base.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
54
variants/heltec_vision_master_e213/target.cpp
Normal file
54
variants/heltec_vision_master_e213/target.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "target.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
HeltecE213Board board;
|
||||
|
||||
#if defined(P_LORA_SCLK)
|
||||
static SPIClass spi(FSPI);
|
||||
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);
|
||||
|
||||
#if defined(P_LORA_SCLK)
|
||||
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
|
||||
}
|
||||
29
variants/heltec_vision_master_e213/target.h
Normal file
29
variants/heltec_vision_master_e213/target.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include <RadioLib.h>
|
||||
#include <helpers/radiolib/RadioLibWrappers.h>
|
||||
#include <HeltecE213Board.h>
|
||||
#include <helpers/radiolib/CustomSX1262Wrapper.h>
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/E213Display.h>
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
extern HeltecE213Board 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();
|
||||
69
variants/heltec_vision_master_e290/HeltecE290Board.cpp
Normal file
69
variants/heltec_vision_master_e290/HeltecE290Board.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "HeltecE290Board.h"
|
||||
|
||||
void HeltecE290Board::begin() {
|
||||
ESP32Board::begin();
|
||||
|
||||
pinMode(PIN_ADC_CTRL, OUTPUT);
|
||||
digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive
|
||||
|
||||
periph_power.begin();
|
||||
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
if (reason == ESP_RST_DEEPSLEEP) {
|
||||
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||
if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep)
|
||||
startup_reason = BD_STARTUP_RX_PACKET;
|
||||
}
|
||||
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
|
||||
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1);
|
||||
}
|
||||
}
|
||||
|
||||
void HeltecE290Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) {
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
||||
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
|
||||
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
|
||||
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
|
||||
|
||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
|
||||
|
||||
if (pin_wake_btn < 0) {
|
||||
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
|
||||
} else {
|
||||
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
|
||||
}
|
||||
|
||||
if (secs > 0) {
|
||||
esp_sleep_enable_timer_wakeup(secs * 1000000);
|
||||
}
|
||||
|
||||
// Finally set ESP32 into sleep
|
||||
esp_deep_sleep_start(); // CPU halts here and never returns!
|
||||
}
|
||||
|
||||
void HeltecE290Board::powerOff() {
|
||||
// TODO: re-enable this when there is a definite wake-up source pin:
|
||||
// enterDeepSleep(0);
|
||||
}
|
||||
|
||||
uint16_t HeltecE290Board::getBattMilliVolts() {
|
||||
analogReadResolution(10);
|
||||
digitalWrite(PIN_ADC_CTRL, HIGH);
|
||||
|
||||
uint32_t raw = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
raw += analogRead(PIN_VBAT_READ);
|
||||
}
|
||||
raw = raw / 8;
|
||||
|
||||
digitalWrite(PIN_ADC_CTRL, LOW);
|
||||
|
||||
return (5.42 * (3.3 / 1024.0) * raw) * 1000;
|
||||
}
|
||||
|
||||
const char* HeltecE290Board::getManufacturerName() const {
|
||||
return "Heltec E290";
|
||||
}
|
||||
|
||||
30
variants/heltec_vision_master_e290/HeltecE290Board.h
Normal file
30
variants/heltec_vision_master_e290/HeltecE290Board.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <helpers/RefCountedDigitalPin.h>
|
||||
#include <helpers/ESP32Board.h>
|
||||
#include <driver/rtc_io.h>
|
||||
|
||||
// LoRa radio module pins for heltec_vision_master_e290
|
||||
#define P_LORA_DIO_1 14
|
||||
#define P_LORA_NSS 8
|
||||
#define P_LORA_RESET 12
|
||||
#define P_LORA_BUSY 13
|
||||
#define P_LORA_SCLK 9
|
||||
#define P_LORA_MISO 11
|
||||
#define P_LORA_MOSI 10
|
||||
|
||||
class HeltecE290Board : public ESP32Board {
|
||||
|
||||
public:
|
||||
RefCountedDigitalPin periph_power;
|
||||
|
||||
HeltecE290Board() : periph_power(PIN_VEXT_EN) { }
|
||||
|
||||
void begin();
|
||||
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
|
||||
void powerOff() override;
|
||||
uint16_t getBattMilliVolts() override;
|
||||
const char* getManufacturerName() const override ;
|
||||
|
||||
};
|
||||
61
variants/heltec_vision_master_e290/pins_arduino.h
Normal file
61
variants/heltec_vision_master_e290/pins_arduino.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t LED_BUILTIN = 45; // LED is not populated on earliest board variant
|
||||
#define BUILTIN_LED LED_BUILTIN // Backward compatibility
|
||||
#define LED_BUILTIN LED_BUILTIN
|
||||
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
static const uint8_t SDA = 39;
|
||||
static const uint8_t SCL = 38;
|
||||
|
||||
static const uint8_t SS = 8;
|
||||
static const uint8_t MOSI = 10;
|
||||
static const uint8_t MISO = 11;
|
||||
static const uint8_t SCK = 9;
|
||||
|
||||
static const uint8_t A0 = 1;
|
||||
static const uint8_t A1 = 2;
|
||||
static const uint8_t A2 = 3;
|
||||
static const uint8_t A3 = 4;
|
||||
static const uint8_t A4 = 5;
|
||||
static const uint8_t A5 = 6;
|
||||
static const uint8_t A6 = 7;
|
||||
static const uint8_t A7 = 8;
|
||||
static const uint8_t A8 = 9;
|
||||
static const uint8_t A9 = 10;
|
||||
static const uint8_t A10 = 11;
|
||||
static const uint8_t A11 = 12;
|
||||
static const uint8_t A12 = 13;
|
||||
static const uint8_t A13 = 14;
|
||||
static const uint8_t A14 = 15;
|
||||
static const uint8_t A15 = 16;
|
||||
static const uint8_t A16 = 17;
|
||||
static const uint8_t A17 = 18;
|
||||
static const uint8_t A18 = 19;
|
||||
static const uint8_t A19 = 20;
|
||||
|
||||
static const uint8_t T1 = 1;
|
||||
static const uint8_t T2 = 2;
|
||||
static const uint8_t T3 = 3;
|
||||
static const uint8_t T4 = 4;
|
||||
static const uint8_t T5 = 5;
|
||||
static const uint8_t T6 = 6;
|
||||
static const uint8_t T7 = 7;
|
||||
static const uint8_t T8 = 8;
|
||||
static const uint8_t T9 = 9;
|
||||
static const uint8_t T10 = 10;
|
||||
static const uint8_t T11 = 11;
|
||||
static const uint8_t T12 = 12;
|
||||
static const uint8_t T13 = 13;
|
||||
static const uint8_t T14 = 14;
|
||||
|
||||
static const uint8_t RST_LoRa = 12;
|
||||
static const uint8_t BUSY_LoRa = 13;
|
||||
static const uint8_t DIO1 = 14;
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
80
variants/heltec_vision_master_e290/platformio.ini
Normal file
80
variants/heltec_vision_master_e290/platformio.ini
Normal file
@@ -0,0 +1,80 @@
|
||||
[Heltec_Vision_Master_E290_base]
|
||||
extends = esp32_base
|
||||
board = heltec_vision_master_e290
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
-I variants/heltec_vision_master_e290
|
||||
-D HELTEC_VISION_MASTER_E290
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D LORA_TX_POWER=22
|
||||
-D P_LORA_TX_LED=45
|
||||
-D PIN_USER_BTN=0
|
||||
-D PIN_VEXT_EN=18
|
||||
-D PIN_VEXT_EN_ACTIVE=HIGH
|
||||
-D PIN_VBAT_READ=7
|
||||
-D PIN_ADC_CTRL=46
|
||||
-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
|
||||
-D PIN_BOARD_SDA=39
|
||||
-D PIN_BOARD_SCL=38
|
||||
-D Vision_Master_E290
|
||||
build_src_filter = ${esp32_base.build_src_filter}
|
||||
+<../variants/heltec_vision_master_e290>
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
https://github.com/Quency-D/heltec-eink-modules/archive/563dd41fd850a1bc3039b8723da4f3a20fe1c800.zip
|
||||
|
||||
[env:Heltec_Vision_Master_E290_radio_ble]
|
||||
extends = Heltec_Vision_Master_E290_base
|
||||
build_flags =
|
||||
${Heltec_Vision_Master_E290_base.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=300
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D DISPLAY_CLASS=E290Display
|
||||
-D BLE_PIN_CODE=123456 ; dynamic, random PIN
|
||||
-D BLE_DEBUG_LOGGING=1
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
build_src_filter = ${Heltec_Vision_Master_E290_base.build_src_filter}
|
||||
+<helpers/ui/E290Display.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_Vision_Master_E290_base.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:Heltec_Vision_Master_E290_repeater]
|
||||
extends = Heltec_Vision_Master_E290_base
|
||||
build_flags =
|
||||
${Heltec_Vision_Master_E290_base.build_flags}
|
||||
-D DISPLAY_CLASS=E290Display
|
||||
-D ADVERT_NAME='"Heltec E290 Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
build_src_filter = ${Heltec_Vision_Master_E290_base.build_src_filter}
|
||||
+<helpers/ui/E290Display.cpp>
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${Heltec_Vision_Master_E290_base.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:Heltec_Vision_Master_E290_room_server]
|
||||
extends = Heltec_Vision_Master_E290_base
|
||||
build_flags =
|
||||
${Heltec_Vision_Master_E290_base.build_flags}
|
||||
-D DISPLAY_CLASS=E290Display
|
||||
-D ADVERT_NAME='"Heltec E290 Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ROOM_PASSWORD='"hello"'
|
||||
build_src_filter = ${Heltec_Vision_Master_E290_base.build_src_filter}
|
||||
+<helpers/ui/E290Display.cpp>
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${Heltec_Vision_Master_E290_base.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
54
variants/heltec_vision_master_e290/target.cpp
Normal file
54
variants/heltec_vision_master_e290/target.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "target.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
HeltecE290Board board;
|
||||
|
||||
#if defined(P_LORA_SCLK)
|
||||
static SPIClass spi(FSPI);
|
||||
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);
|
||||
|
||||
#if defined(P_LORA_SCLK)
|
||||
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
|
||||
}
|
||||
29
variants/heltec_vision_master_e290/target.h
Normal file
29
variants/heltec_vision_master_e290/target.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include <RadioLib.h>
|
||||
#include <helpers/radiolib/RadioLibWrappers.h>
|
||||
#include <HeltecE290Board.h>
|
||||
#include <helpers/radiolib/CustomSX1262Wrapper.h>
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/E290Display.h>
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
extern HeltecE290Board 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();
|
||||
69
variants/heltec_vision_master_t190/HeltecT190Board.cpp
Normal file
69
variants/heltec_vision_master_t190/HeltecT190Board.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "HeltecT190Board.h"
|
||||
|
||||
void HeltecT190Board::begin() {
|
||||
ESP32Board::begin();
|
||||
|
||||
pinMode(PIN_ADC_CTRL, OUTPUT);
|
||||
digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive
|
||||
|
||||
periph_power.begin();
|
||||
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
if (reason == ESP_RST_DEEPSLEEP) {
|
||||
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||
if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep)
|
||||
startup_reason = BD_STARTUP_RX_PACKET;
|
||||
}
|
||||
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
|
||||
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1);
|
||||
}
|
||||
}
|
||||
|
||||
void HeltecT190Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) {
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
||||
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
|
||||
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
|
||||
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
|
||||
|
||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
|
||||
|
||||
if (pin_wake_btn < 0) {
|
||||
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
|
||||
} else {
|
||||
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
|
||||
}
|
||||
|
||||
if (secs > 0) {
|
||||
esp_sleep_enable_timer_wakeup(secs * 1000000);
|
||||
}
|
||||
|
||||
// Finally set ESP32 into sleep
|
||||
esp_deep_sleep_start(); // CPU halts here and never returns!
|
||||
}
|
||||
|
||||
void HeltecT190Board::powerOff() {
|
||||
// TODO: re-enable this when there is a definite wake-up source pin:
|
||||
// enterDeepSleep(0);
|
||||
}
|
||||
|
||||
uint16_t HeltecT190Board::getBattMilliVolts() {
|
||||
analogReadResolution(10);
|
||||
digitalWrite(PIN_ADC_CTRL, HIGH);
|
||||
delay(10);
|
||||
uint32_t raw = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
raw += analogRead(PIN_VBAT_READ);
|
||||
}
|
||||
raw = raw / 8;
|
||||
|
||||
digitalWrite(PIN_ADC_CTRL, LOW);
|
||||
|
||||
return (5.42 * (3.3 / 1024.0) * raw) * 1000;
|
||||
}
|
||||
|
||||
const char* HeltecT190Board::getManufacturerName() const {
|
||||
return "Heltec T190";
|
||||
}
|
||||
|
||||
30
variants/heltec_vision_master_t190/HeltecT190Board.h
Normal file
30
variants/heltec_vision_master_t190/HeltecT190Board.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <helpers/RefCountedDigitalPin.h>
|
||||
#include <helpers/ESP32Board.h>
|
||||
#include <driver/rtc_io.h>
|
||||
|
||||
// LoRa radio module pins for heltec_vision_master_e290
|
||||
#define P_LORA_DIO_1 14
|
||||
#define P_LORA_NSS 8
|
||||
#define P_LORA_RESET 12
|
||||
#define P_LORA_BUSY 13
|
||||
#define P_LORA_SCLK 9
|
||||
#define P_LORA_MISO 11
|
||||
#define P_LORA_MOSI 10
|
||||
|
||||
class HeltecT190Board : public ESP32Board {
|
||||
|
||||
public:
|
||||
RefCountedDigitalPin periph_power;
|
||||
|
||||
HeltecT190Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { }
|
||||
|
||||
void begin();
|
||||
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
|
||||
void powerOff() override;
|
||||
uint16_t getBattMilliVolts() override;
|
||||
const char* getManufacturerName() const override ;
|
||||
|
||||
};
|
||||
61
variants/heltec_vision_master_t190/pins_arduino.h
Normal file
61
variants/heltec_vision_master_t190/pins_arduino.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t LED_BUILTIN = 45; // LED is not populated on earliest board variant
|
||||
#define BUILTIN_LED LED_BUILTIN // Backward compatibility
|
||||
#define LED_BUILTIN LED_BUILTIN
|
||||
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
static const uint8_t SDA = 2;
|
||||
static const uint8_t SCL = 1;
|
||||
|
||||
static const uint8_t SS = 8;
|
||||
static const uint8_t MOSI = 10;
|
||||
static const uint8_t MISO = 11;
|
||||
static const uint8_t SCK = 9;
|
||||
|
||||
static const uint8_t A0 = 1;
|
||||
static const uint8_t A1 = 2;
|
||||
static const uint8_t A2 = 3;
|
||||
static const uint8_t A3 = 4;
|
||||
static const uint8_t A4 = 5;
|
||||
static const uint8_t A5 = 6;
|
||||
static const uint8_t A6 = 7;
|
||||
static const uint8_t A7 = 8;
|
||||
static const uint8_t A8 = 9;
|
||||
static const uint8_t A9 = 10;
|
||||
static const uint8_t A10 = 11;
|
||||
static const uint8_t A11 = 12;
|
||||
static const uint8_t A12 = 13;
|
||||
static const uint8_t A13 = 14;
|
||||
static const uint8_t A14 = 15;
|
||||
static const uint8_t A15 = 16;
|
||||
static const uint8_t A16 = 17;
|
||||
static const uint8_t A17 = 18;
|
||||
static const uint8_t A18 = 19;
|
||||
static const uint8_t A19 = 20;
|
||||
|
||||
static const uint8_t T1 = 1;
|
||||
static const uint8_t T2 = 2;
|
||||
static const uint8_t T3 = 3;
|
||||
static const uint8_t T4 = 4;
|
||||
static const uint8_t T5 = 5;
|
||||
static const uint8_t T6 = 6;
|
||||
static const uint8_t T7 = 7;
|
||||
static const uint8_t T8 = 8;
|
||||
static const uint8_t T9 = 9;
|
||||
static const uint8_t T10 = 10;
|
||||
static const uint8_t T11 = 11;
|
||||
static const uint8_t T12 = 12;
|
||||
static const uint8_t T13 = 13;
|
||||
static const uint8_t T14 = 14;
|
||||
|
||||
static const uint8_t RST_LoRa = 12;
|
||||
static const uint8_t BUSY_LoRa = 13;
|
||||
static const uint8_t DIO1 = 14;
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
87
variants/heltec_vision_master_t190/platformio.ini
Normal file
87
variants/heltec_vision_master_t190/platformio.ini
Normal file
@@ -0,0 +1,87 @@
|
||||
[Heltec_Vision_Master_T190_base]
|
||||
extends = esp32_base
|
||||
board = heltec_vision_master_t190
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
-I variants/heltec_vision_master_t190
|
||||
-I src/helpers/ui
|
||||
-D HELTEC_VISION_MASTER_T190
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D LORA_TX_POWER=22
|
||||
-D PIN_USER_BTN=0
|
||||
-D PIN_VEXT_EN=5
|
||||
-D PIN_VEXT_EN_ACTIVE=HIGH
|
||||
-D PIN_VBAT_READ=6
|
||||
-D PIN_ADC_CTRL=46
|
||||
-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
|
||||
-D PIN_BOARD_SDA=2
|
||||
-D PIN_BOARD_SCL=1
|
||||
-D PIN_TFT_SCL=38
|
||||
-D PIN_TFT_SDA=48
|
||||
-D PIN_TFT_RST=40
|
||||
-D PIN_TFT_VDD_CTL=7
|
||||
-D PIN_TFT_LEDA_CTL=17
|
||||
-D PIN_TFT_LEDA_CTL_ACTIVE=HIGH
|
||||
-D PIN_TFT_CS=39
|
||||
-D PIN_TFT_DC=47
|
||||
-D ST7789
|
||||
-D DISPLAY_CLASS=ST7789Display
|
||||
build_src_filter = ${esp32_base.build_src_filter}
|
||||
+<../variants/heltec_vision_master_t190>
|
||||
+<helpers/*.cpp>
|
||||
+<helpers/ui/ST7789Display.cpp>
|
||||
+<helpers/ui/OLEDDisplay.cpp>
|
||||
+<helpers/ui/OLEDDisplayFonts.cpp>
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
adafruit/Adafruit GFX Library @ ^1.12.1
|
||||
|
||||
[env:Heltec_Vision_Master_T190_radio_ble]
|
||||
extends = Heltec_Vision_Master_T190_base
|
||||
build_flags =
|
||||
${Heltec_Vision_Master_T190_base.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=300
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D BLE_PIN_CODE=123456 ; dynamic, random PIN
|
||||
-D BLE_DEBUG_LOGGING=1
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
build_src_filter = ${Heltec_Vision_Master_T190_base.build_src_filter}
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_Vision_Master_T190_base.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:Heltec_Vision_Master_T190_repeater]
|
||||
extends = Heltec_Vision_Master_T190_base
|
||||
build_flags =
|
||||
${Heltec_Vision_Master_T190_base.build_flags}
|
||||
-D ADVERT_NAME='"Heltec T190 Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
build_src_filter = ${Heltec_Vision_Master_T190_base.build_src_filter}
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${Heltec_Vision_Master_T190_base.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:Heltec_Vision_Master_T190_room_server]
|
||||
extends = Heltec_Vision_Master_T190_base
|
||||
build_flags =
|
||||
${Heltec_Vision_Master_T190_base.build_flags}
|
||||
-D ADVERT_NAME='"Heltec T190 Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ROOM_PASSWORD='"hello"'
|
||||
build_src_filter = ${Heltec_Vision_Master_T190_base.build_src_filter}
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${Heltec_Vision_Master_T190_base.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
54
variants/heltec_vision_master_t190/target.cpp
Normal file
54
variants/heltec_vision_master_t190/target.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "target.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
HeltecT190Board board;
|
||||
|
||||
#if defined(P_LORA_SCLK)
|
||||
static SPIClass spi(FSPI);
|
||||
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);
|
||||
|
||||
#if defined(P_LORA_SCLK)
|
||||
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
|
||||
}
|
||||
29
variants/heltec_vision_master_t190/target.h
Normal file
29
variants/heltec_vision_master_t190/target.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include <RadioLib.h>
|
||||
#include <helpers/radiolib/RadioLibWrappers.h>
|
||||
#include <HeltecT190Board.h>
|
||||
#include <helpers/radiolib/CustomSX1262Wrapper.h>
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/ST7789Display.h>
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
extern HeltecT190Board 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();
|
||||
@@ -28,16 +28,17 @@ build_flags =
|
||||
-D ARDUINO_heltec_wifi_lora_32_V3
|
||||
-D WIRELESS_PAPER
|
||||
build_src_filter = ${esp32_base.build_src_filter}
|
||||
+<../variants/heltec_wireless_paper>
|
||||
+<../variants/heltec_wireless_paper>
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
todd-herbert/heltec-eink-modules @ 4.5.0
|
||||
https://github.com/todd-herbert/heltec-eink-modules/archive/9207eb6ab2b96f66298e0488740218c17b006af7.zip
|
||||
|
||||
[env:Heltec_Wireless_Paper_companion_radio_ble]
|
||||
extends = Heltec_Wireless_Paper_base
|
||||
build_flags =
|
||||
${Heltec_Wireless_Paper_base.build_flags}
|
||||
-D MAX_CONTACTS=100
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=300
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D DISPLAY_CLASS=E213Display
|
||||
-D BLE_PIN_CODE=123456 ; dynamic, random PIN
|
||||
@@ -46,7 +47,8 @@ build_flags =
|
||||
build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter}
|
||||
+<helpers/ui/E213Display.cpp>
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Heltec_Wireless_Paper_base.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
@@ -19,6 +19,7 @@ SensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
DISPLAY_CLASS display;
|
||||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
|
||||
#endif
|
||||
|
||||
bool radio_init() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <helpers/SensorManager.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <helpers/ui/E213Display.h>
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
extern HeltecV3Board board;
|
||||
@@ -18,6 +19,7 @@ extern SensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
extern DISPLAY_CLASS display;
|
||||
extern MomentaryButton user_btn;
|
||||
#endif
|
||||
|
||||
bool radio_init();
|
||||
|
||||
99
variants/ikoka_stick_nrf/ikoka_stick_nrf_board.cpp
Normal file
99
variants/ikoka_stick_nrf/ikoka_stick_nrf_board.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#ifdef XIAO_NRF52
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "ikoka_stick_nrf_board.h"
|
||||
|
||||
#include <bluefruit.h>
|
||||
#include <Wire.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 ikoka_stick_nrf_board::begin() {
|
||||
// for future use, sub-classes SHOULD call this from their begin()
|
||||
startup_reason = BD_STARTUP_NORMAL;
|
||||
|
||||
pinMode(PIN_VBAT, INPUT);
|
||||
pinMode(VBAT_ENABLE, OUTPUT);
|
||||
digitalWrite(VBAT_ENABLE, HIGH);
|
||||
|
||||
#ifdef PIN_USER_BTN
|
||||
pinMode(PIN_USER_BTN, INPUT_PULLUP);
|
||||
#endif
|
||||
|
||||
#if defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL)
|
||||
Wire.setPins(PIN_WIRE_SDA, PIN_WIRE_SCL);
|
||||
#endif
|
||||
|
||||
Wire.begin();
|
||||
|
||||
#ifdef P_LORA_TX_LED
|
||||
pinMode(P_LORA_TX_LED, OUTPUT);
|
||||
digitalWrite(P_LORA_TX_LED, HIGH);
|
||||
#endif
|
||||
|
||||
// pinMode(SX126X_POWER_EN, OUTPUT);
|
||||
// digitalWrite(SX126X_POWER_EN, HIGH);
|
||||
delay(10); // give sx1262 some time to power up
|
||||
}
|
||||
|
||||
bool ikoka_stick_nrf_board::startOTAUpdate(const char* id, char reply[]) {
|
||||
// Config the peripheral connection with maximum bandwidth
|
||||
// more SRAM required by SoftDevice
|
||||
// Note: All config***() function must be called before begin()
|
||||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
|
||||
Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
|
||||
|
||||
Bluefruit.begin(1, 0);
|
||||
// Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
|
||||
Bluefruit.setTxPower(4);
|
||||
// Set the BLE device name
|
||||
Bluefruit.setName("XIAO_NRF52_OTA");
|
||||
|
||||
Bluefruit.Periph.setConnectCallback(connect_callback);
|
||||
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
|
||||
|
||||
// To be consistent OTA DFU should be added first if it exists
|
||||
bledfu.begin();
|
||||
|
||||
// Set up and start advertising
|
||||
// Advertising packet
|
||||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
||||
Bluefruit.Advertising.addTxPower();
|
||||
Bluefruit.Advertising.addName();
|
||||
|
||||
/* Start Advertising
|
||||
- Enable auto advertising if disconnected
|
||||
- Interval: fast mode = 20 ms, slow mode = 152.5 ms
|
||||
- Timeout for fast mode is 30 seconds
|
||||
- Start(timeout) with timeout = 0 will advertise forever (until connected)
|
||||
|
||||
For recommended advertising interval
|
||||
https://developer.apple.com/library/content/qa/qa1931/_index.html
|
||||
*/
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
|
||||
|
||||
strcpy(reply, "OK - started");
|
||||
return true;
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
52
variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h
Normal file
52
variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <MeshCore.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef XIAO_NRF52
|
||||
|
||||
class ikoka_stick_nrf_board : public mesh::MainBoard {
|
||||
protected:
|
||||
uint8_t startup_reason;
|
||||
|
||||
public:
|
||||
void begin();
|
||||
uint8_t getStartupReason() const override { return startup_reason; }
|
||||
|
||||
#if defined(P_LORA_TX_LED)
|
||||
void onBeforeTransmit() override {
|
||||
digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on
|
||||
}
|
||||
void onAfterTransmit() override {
|
||||
digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t getBattMilliVolts() override {
|
||||
// Please read befor going further ;)
|
||||
// https://wiki.seeedstudio.com/XIAO_BLE#q3-what-are-the-considerations-when-using-xiao-nrf52840-sense-for-battery-charging
|
||||
|
||||
// We can't drive VBAT_ENABLE to HIGH as long
|
||||
// as we don't know wether we are charging or not ...
|
||||
// this is a 3mA loss (4/1500)
|
||||
digitalWrite(VBAT_ENABLE, LOW);
|
||||
int adcvalue = 0;
|
||||
analogReadResolution(12);
|
||||
analogReference(AR_INTERNAL_3_0);
|
||||
delay(10);
|
||||
adcvalue = analogRead(PIN_VBAT);
|
||||
return (adcvalue * ADC_MULTIPLIER * AREF_VOLTAGE) / 4.096;
|
||||
}
|
||||
|
||||
const char* getManufacturerName() const override {
|
||||
return "Ikoka Stick (Xiao-nrf52)";
|
||||
}
|
||||
|
||||
void reboot() override {
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
bool startOTAUpdate(const char* id, char reply[]) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
307
variants/ikoka_stick_nrf/platformio.ini
Normal file
307
variants/ikoka_stick_nrf/platformio.ini
Normal file
@@ -0,0 +1,307 @@
|
||||
[nrf52840_xiao]
|
||||
extends = nrf52_base
|
||||
platform_packages =
|
||||
toolchain-gccarmnoneeabi@~1.100301.0
|
||||
framework-arduinoadafruitnrf52
|
||||
board = seeed-xiao-afruitnrf52-nrf52840
|
||||
board_build.ldscript = boards/nrf52840_s140_v7.ld
|
||||
build_flags = ${nrf52_base.build_flags}
|
||||
-D NRF52_PLATFORM -D XIAO_NRF52
|
||||
-I lib/nrf52/s140_nrf52_7.3.0_API/include
|
||||
-I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
lvgl
|
||||
lib5b4
|
||||
lib_deps =
|
||||
${nrf52_base.lib_deps}
|
||||
rweather/Crypto @ ^0.4.0
|
||||
adafruit/Adafruit INA3221 Library @ ^1.0.1
|
||||
adafruit/Adafruit INA219 @ ^1.2.3
|
||||
adafruit/Adafruit AHTX0 @ ^2.0.5
|
||||
adafruit/Adafruit BME280 Library @ ^2.3.0
|
||||
adafruit/Adafruit SSD1306 @ ^2.5.13
|
||||
|
||||
[ikoka_stick_nrf_baseboard]
|
||||
extends = nrf52840_xiao
|
||||
;board_build.ldscript = boards/nrf52840_s140_v7.ld
|
||||
build_flags = ${nrf52840_xiao.build_flags}
|
||||
-D P_LORA_TX_LED=11
|
||||
-I variants/ikoka_stick_nrf
|
||||
-I src/helpers/nrf52
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D DISPLAY_ROTATION=2
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D P_LORA_DIO_1=D1
|
||||
-D P_LORA_RESET=D2
|
||||
-D P_LORA_BUSY=D3
|
||||
-D P_LORA_NSS=D4
|
||||
-D SX126X_RXEN=D5
|
||||
-D SX126X_TXEN=RADIOLIB_NC
|
||||
-D SX126X_DIO2_AS_RF_SWITCH=1
|
||||
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
-D PIN_USER_BTN=0
|
||||
-D PIN_WIRE_SCL=7
|
||||
-D PIN_WIRE_SDA=6
|
||||
-D ENV_INCLUDE_AHTX0=1
|
||||
-D ENV_INCLUDE_BME280=1
|
||||
-D ENV_INCLUDE_INA3221=1
|
||||
-D ENV_INCLUDE_INA219=1
|
||||
debug_tool = jlink
|
||||
upload_protocol = nrfutil
|
||||
|
||||
|
||||
;;; abstracted hardware variants
|
||||
|
||||
[ikoka_stick_nrf_e22_22dbm]
|
||||
extends = ikoka_stick_nrf_baseboard
|
||||
; No PA in this model, full 22dBm
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_baseboard.build_flags}
|
||||
-D LORA_TX_POWER=22
|
||||
build_src_filter = ${nrf52840_xiao.build_src_filter}
|
||||
+<helpers/*.cpp>
|
||||
+<helpers/sensors>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../variants/ikoka_stick_nrf>
|
||||
|
||||
[ikoka_stick_nrf_e22_30dbm]
|
||||
extends = ikoka_stick_nrf_baseboard
|
||||
; limit txpower to 20dBm on E22-900M30S. Anything higher will
|
||||
; cause distortion in the PA output. 20dBm in -> 30dBm out
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_baseboard.build_flags}
|
||||
-D LORA_TX_POWER=20
|
||||
build_src_filter = ${nrf52840_xiao.build_src_filter}
|
||||
+<helpers/*.cpp>
|
||||
+<helpers/sensors>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../variants/ikoka_stick_nrf>
|
||||
|
||||
[ikoka_stick_nrf_e22_33dbm]
|
||||
extends = ikoka_stick_nrf_baseboard
|
||||
; limit txpower to 9dBm on E22-900M33S to avoid hardware damage
|
||||
; to the rf amplifier frontend. 9dBm in -> 33dBm out
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_baseboard.build_flags}
|
||||
-D LORA_TX_POWER=9
|
||||
build_src_filter = ${nrf52840_xiao.build_src_filter}
|
||||
+<helpers/*.cpp>
|
||||
+<helpers/sensors>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../variants/ikoka_stick_nrf>
|
||||
|
||||
;;; abstracted firmware roles
|
||||
|
||||
[ikoka_stick_nrf_companion_radio_ble]
|
||||
extends = ikoka_stick_nrf_baseboard
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_baseboard.build_flags}
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-I examples/companion_radio/ui-new
|
||||
; -D BLE_DEBUG_LOGGING=1
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter}
|
||||
+<helpers/nrf52/SerialBLEInterface.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${ikoka_stick_nrf_baseboard.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[ikoka_stick_nrf_companion_radio_usb]
|
||||
extends = ikoka_stick_nrf_baseboard
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_baseboard.build_flags}
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-I examples/companion_radio/ui-new
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter}
|
||||
+<helpers/nrf52/SerialBLEInterface.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${ikoka_stick_nrf_baseboard.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[ikoka_stick_nrf_repeater]
|
||||
extends = ikoka_stick_nrf_baseboard
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_baseboard.build_flags}
|
||||
-D ADVERT_NAME='"Ikoka Stick 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 = ${ikoka_stick_nrf_baseboard.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_repeater/*.cpp>
|
||||
|
||||
[ikoka_stick_nrf_room_server]
|
||||
extends = ikoka_stick_nrf_baseboard
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_baseboard.build_flags}
|
||||
-D ADVERT_NAME='"Ikoka Stick Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter}
|
||||
+<../examples/simple_room_server/*.cpp>
|
||||
|
||||
;;; hardware + firmware variants
|
||||
|
||||
;;; 22dBm EBYTE E22-900M22 variants
|
||||
|
||||
[env:ikoka_stick_nrf_22dbm_companion_radio_usb]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_22dbm
|
||||
ikoka_stick_nrf_companion_radio_usb
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_companion_radio_usb.build_flags}
|
||||
${ikoka_stick_nrf_e22_22dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_companion_radio_usb.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_22dbm.build_src_filter}
|
||||
|
||||
[env:ikoka_stick_nrf_22dbm_companion_radio_ble]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_22dbm
|
||||
ikoka_stick_nrf_companion_radio_ble
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_companion_radio_ble.build_flags}
|
||||
${ikoka_stick_nrf_e22_22dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_companion_radio_ble.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_22dbm.build_src_filter}
|
||||
|
||||
[env:ikoka_stick_nrf_22dbm_repeater]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_22dbm
|
||||
ikoka_stick_nrf_repeater
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_repeater.build_flags}
|
||||
${ikoka_stick_nrf_e22_22dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_repeater.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_22dbm.build_src_filter}
|
||||
|
||||
[env:ikoka_stick_nrf_22dbm_room_server]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_22dbm
|
||||
ikoka_stick_nrf_room_server
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_room_server.build_flags}
|
||||
${ikoka_stick_nrf_e22_22dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_room_server.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_22dbm.build_src_filter}
|
||||
|
||||
|
||||
;;; 30dBm EBYTE E22-900M30 variants
|
||||
|
||||
[env:ikoka_stick_nrf_30dbm_companion_radio_usb]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_30dbm
|
||||
ikoka_stick_nrf_companion_radio_usb
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_companion_radio_usb.build_flags}
|
||||
${ikoka_stick_nrf_e22_30dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_companion_radio_usb.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_30dbm.build_src_filter}
|
||||
|
||||
[env:ikoka_stick_nrf_30dbm_companion_radio_ble]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_30dbm
|
||||
ikoka_stick_nrf_companion_radio_ble
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_companion_radio_ble.build_flags}
|
||||
${ikoka_stick_nrf_e22_30dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_companion_radio_ble.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_30dbm.build_src_filter}
|
||||
|
||||
[env:ikoka_stick_nrf_30dbm_repeater]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_30dbm
|
||||
ikoka_stick_nrf_repeater
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_repeater.build_flags}
|
||||
${ikoka_stick_nrf_e22_30dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_repeater.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_30dbm.build_src_filter}
|
||||
|
||||
[env:ikoka_stick_nrf_30dbm_room_server]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_30dbm
|
||||
ikoka_stick_nrf_room_server
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_room_server.build_flags}
|
||||
${ikoka_stick_nrf_e22_30dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_room_server.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_30dbm.build_src_filter}
|
||||
|
||||
|
||||
;;; 33dBm EBYTE E22-900M33 variants
|
||||
|
||||
[env:ikoka_stick_nrf_33dbm_companion_radio_usb]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_33dbm
|
||||
ikoka_stick_nrf_companion_radio_usb
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_companion_radio_usb.build_flags}
|
||||
${ikoka_stick_nrf_e22_33dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_companion_radio_usb.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_33dbm.build_src_filter}
|
||||
|
||||
[env:ikoka_stick_nrf_33dbm_companion_radio_ble]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_33dbm
|
||||
ikoka_stick_nrf_companion_radio_ble
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_companion_radio_ble.build_flags}
|
||||
${ikoka_stick_nrf_e22_33dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_companion_radio_ble.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_33dbm.build_src_filter}
|
||||
|
||||
[env:ikoka_stick_nrf_33dbm_repeater]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_33dbm
|
||||
ikoka_stick_nrf_repeater
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_repeater.build_flags}
|
||||
${ikoka_stick_nrf_e22_33dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_repeater.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_33dbm.build_src_filter}
|
||||
|
||||
[env:ikoka_stick_nrf_33dbm_room_server]
|
||||
extends =
|
||||
ikoka_stick_nrf_e22_33dbm
|
||||
ikoka_stick_nrf_room_server
|
||||
build_flags =
|
||||
${ikoka_stick_nrf_room_server.build_flags}
|
||||
${ikoka_stick_nrf_e22_33dbm.build_flags}
|
||||
build_src_filter =
|
||||
${ikoka_stick_nrf_room_server.build_src_filter}
|
||||
${ikoka_stick_nrf_e22_33dbm.build_src_filter}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user