Merge 'dev' into 'feature/rp2040_picow_dev'.

This commit is contained in:
AeroXuk 2025-04-21 21:21:10 +01:00
commit b1c8963e1e
47 changed files with 1635 additions and 77 deletions

View file

@ -101,6 +101,12 @@ void Dispatcher::checkRecv() {
#endif
pkt->header = raw[i++];
if (pkt->hasTransportCodes()) {
memcpy(&pkt->transport_codes[0], &raw[i], 2); i += 2;
memcpy(&pkt->transport_codes[1], &raw[i], 2); i += 2;
} else {
pkt->transport_codes[0] = pkt->transport_codes[1] = 0;
}
pkt->path_len = raw[i++];
if (pkt->path_len > MAX_PATH_SIZE || i + pkt->path_len > len) {
@ -132,7 +138,7 @@ void Dispatcher::checkRecv() {
#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",
2 + pkt->path_len + pkt->payload_len, pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
pkt->getRawLength(), pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
(int)pkt->getSNR(), (int)_radio->getLastRSSI(), (int)(score*1000));
static uint8_t packet_hash[MAX_HASH_SIZE];
@ -147,7 +153,7 @@ void Dispatcher::checkRecv() {
Serial.printf("\n");
}
#endif
logRx(pkt, 2 + pkt->path_len + pkt->payload_len, score); // hook for custom logging
logRx(pkt, pkt->getRawLength(), score); // hook for custom logging
if (pkt->isRouteFlood()) {
n_recv_flood++;
@ -212,6 +218,10 @@ void Dispatcher::checkSend() {
raw[len++] = NODE_ID;
#endif
raw[len++] = outbound->header;
if (outbound->hasTransportCodes()) {
memcpy(&raw[len], &outbound->transport_codes[0], 2); len += 2;
memcpy(&raw[len], &outbound->transport_codes[1], 2); len += 2;
}
raw[len++] = outbound->path_len;
memcpy(&raw[len], outbound->path, outbound->path_len); len += outbound->path_len;

View file

@ -15,7 +15,7 @@ bool Mesh::allowPacketForward(const mesh::Packet* packet) {
return false; // by default, Transport NOT enabled
}
uint32_t Mesh::getRetransmitDelay(const mesh::Packet* packet) {
uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * 52 / 50) / 2;
uint32_t t = (_radio->getEstAirtimeFor(packet->getRawLength()) * 52 / 50) / 2;
return _rng->nextInt(0, 5)*t;
}

View file

@ -10,6 +10,10 @@ Packet::Packet() {
payload_len = 0;
}
int Packet::getRawLength() const {
return 2 + path_len + payload_len + (hasTransportCodes() ? 4 : 0);
}
void Packet::calculatePacketHash(uint8_t* hash) const {
SHA256 sha;
uint8_t t = getPayloadType();
@ -24,6 +28,10 @@ void Packet::calculatePacketHash(uint8_t* hash) const {
uint8_t Packet::writeTo(uint8_t dest[]) const {
uint8_t i = 0;
dest[i++] = header;
if (hasTransportCodes()) {
memcpy(&dest[i], &transport_codes[0], 2); i += 2;
memcpy(&dest[i], &transport_codes[1], 2); i += 2;
}
dest[i++] = path_len;
memcpy(&dest[i], path, path_len); i += path_len;
memcpy(&dest[i], payload, payload_len); i += payload_len;
@ -33,6 +41,12 @@ uint8_t Packet::writeTo(uint8_t dest[]) const {
bool Packet::readFrom(const uint8_t src[], uint8_t len) {
uint8_t i = 0;
header = src[i++];
if (hasTransportCodes()) {
memcpy(&transport_codes[0], &src[i], 2); i += 2;
memcpy(&transport_codes[1], &src[i], 2); i += 2;
} else {
transport_codes[0] = transport_codes[1] = 0;
}
path_len = src[i++];
if (path_len > sizeof(path)) return false; // bad encoding
memcpy(path, &src[i], path_len); i += path_len;

View file

@ -11,10 +11,10 @@ namespace mesh {
#define PH_VER_SHIFT 6
#define PH_VER_MASK 0x03 // 2-bits
#define ROUTE_TYPE_RESERVED1 0x00 // FUTURE
#define ROUTE_TYPE_FLOOD 0x01 // flood mode, needs 'path' to be built up (max 64 bytes)
#define ROUTE_TYPE_DIRECT 0x02 // direct route, 'path' is supplied
#define ROUTE_TYPE_RESERVED2 0x03 // FUTURE
#define ROUTE_TYPE_TRANSPORT_FLOOD 0x00 // flood mode + transport codes
#define ROUTE_TYPE_FLOOD 0x01 // flood mode, needs 'path' to be built up (max 64 bytes)
#define ROUTE_TYPE_DIRECT 0x02 // direct route, 'path' is supplied
#define ROUTE_TYPE_TRANSPORT_DIRECT 0x03 // direct route + transport codes
#define PAYLOAD_TYPE_REQ 0x00 // request (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
#define PAYLOAD_TYPE_RESPONSE 0x01 // response to REQ or ANON_REQ (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
@ -43,6 +43,7 @@ public:
uint8_t header;
uint16_t payload_len, path_len;
uint16_t transport_codes[2];
uint8_t path[MAX_PATH_SIZE];
uint8_t payload[MAX_PACKET_PAYLOAD];
int8_t _snr;
@ -58,8 +59,10 @@ public:
*/
uint8_t getRouteType() const { return header & PH_ROUTE_MASK; }
bool isRouteFlood() const { return getRouteType() == ROUTE_TYPE_FLOOD; }
bool isRouteDirect() const { return getRouteType() == ROUTE_TYPE_DIRECT; }
bool isRouteFlood() const { return getRouteType() == ROUTE_TYPE_FLOOD || getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD; }
bool isRouteDirect() const { return getRouteType() == ROUTE_TYPE_DIRECT || getRouteType() == ROUTE_TYPE_TRANSPORT_DIRECT; }
bool hasTransportCodes() const { return getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD || getRouteType() == ROUTE_TYPE_TRANSPORT_DIRECT; }
/**
* \returns one of PAYLOAD_TYPE_ values
@ -76,6 +79,11 @@ public:
float getSNR() const { return ((float)_snr) / 4.0f; }
/**
* \returns the encoded/wire format length of this packet
*/
int getRawLength() const;
/**
* \brief save entire packet as a blob
* \param dest (OUT) destination buffer (assumed to be MAX_MTU_SIZE)

View file

@ -8,8 +8,12 @@ static bool ds3231_success = false;
static Melopero_RV3028 rtc_rv3028;
static bool rv3028_success = false;
static RTC_PCF8563 rtc_8563;
static bool rtc_8563_success = false;
#define DS3231_ADDRESS 0x68
#define RV3028_ADDRESS 0x52
#define PCF8563_ADDRESS 0x51
bool AutoDiscoverRTCClock::i2c_probe(TwoWire& wire, uint8_t addr) {
wire.beginTransmission(addr);
@ -28,6 +32,9 @@ void AutoDiscoverRTCClock::begin(TwoWire& wire) {
rtc_rv3028.set24HourMode(); // Set the device to use the 24hour format (default) instead of the 12 hour format
rv3028_success = true;
}
if(i2c_probe(wire,PCF8563_ADDRESS)){
rtc_8563_success = rtc_8563.begin(&wire);
}
}
uint32_t AutoDiscoverRTCClock::getCurrentTime() {
@ -44,6 +51,9 @@ uint32_t AutoDiscoverRTCClock::getCurrentTime() {
rtc_rv3028.getSecond()
).unixtime();
}
if(rtc_8563_success){
return rtc_8563.now().unixtime();
}
return _fallback->getCurrentTime();
}
@ -52,9 +62,10 @@ void AutoDiscoverRTCClock::setCurrentTime(uint32_t time) {
rtc_3231.adjust(DateTime(time));
} else if (rv3028_success) {
auto dt = DateTime(time);
uint8_t weekday = (dt.day() + (uint16_t)((2.6 * dt.month()) - 0.2) - (2 * (dt.year() / 100)) + dt.year() + (uint16_t)(dt.year() / 4) + (uint16_t)(dt.year() / 400)) % 7;
uint8_t weekday = (dt.day() + (uint16_t)((2.6 * dt.month()) - 0.2) - (2 * (dt.year() / 100)) + dt.year() + (uint16_t)(dt.year() / 4) + (uint16_t)(dt.year() / 400)) % 7;
rtc_rv3028.setTime(dt.year(), dt.month(), weekday, dt.day(), dt.hour(), dt.minute(), dt.second());
} else if (rtc_8563_success) {
rtc_8563.adjust(DateTime(time));
} else {
_fallback->setCurrentTime(time);
}

View file

@ -31,8 +31,29 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id,
}
}
// save a copy of raw advert packet (to support "Share..." function)
int plen = packet->writeTo(temp_buf);
putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen);
bool is_new = false;
if (from == NULL) {
if (!isAutoAddEnabled()) {
ContactInfo ci;
memset(&ci, 0, sizeof(ci));
ci.id = id;
ci.out_path_len = -1; // initially out_path is unknown
StrHelper::strncpy(ci.name, parser.getName(), sizeof(ci.name));
ci.type = parser.getType();
if (parser.hasLatLon()) {
ci.gps_lat = parser.getIntLat();
ci.gps_lon = parser.getIntLon();
}
ci.last_advert_timestamp = timestamp;
ci.lastmod = getRTCClock()->getCurrentTime();
onDiscoveredContact(ci, true); // let UI know
return;
}
is_new = true;
if (num_contacts < MAX_CONTACTS) {
from = &contacts[num_contacts++];
@ -50,10 +71,6 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id,
}
}
// save a copy of raw advert packet (to support "Share..." function)
int plen = packet->writeTo(temp_buf);
putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen);
// update
StrHelper::strncpy(from->name, parser.getName(), sizeof(from->name));
from->type = parser.getType();
@ -252,7 +269,7 @@ int BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp,
mesh::Packet* pkt = composeMsgPacket(recipient, timestamp, attempt, text, expected_ack);
if (pkt == NULL) return MSG_SEND_FAILED;
uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
int rc;
if (recipient.out_path_len < 0) {
@ -279,7 +296,7 @@ int BaseChatMesh::sendCommandData(const ContactInfo& recipient, uint32_t timest
auto pkt = createDatagram(PAYLOAD_TYPE_TXT_MSG, recipient.id, recipient.shared_secret, temp, 5 + text_len);
if (pkt == NULL) return MSG_SEND_FAILED;
uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
int rc;
if (recipient.out_path_len < 0) {
sendFlood(pkt);
@ -362,7 +379,7 @@ int BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password,
auto pkt = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, recipient.id, recipient.shared_secret, temp, tlen);
if (pkt) {
uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
if (recipient.out_path_len < 0) {
sendFlood(pkt);
est_timeout = calcFloodTimeoutMillisFor(t);
@ -386,7 +403,7 @@ int BaseChatMesh::sendStatusRequest(const ContactInfo& recipient, uint32_t& est
auto pkt = createDatagram(PAYLOAD_TYPE_REQ, recipient.id, recipient.shared_secret, temp, sizeof(temp));
if (pkt) {
uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
if (recipient.out_path_len < 0) {
sendFlood(pkt);
est_timeout = calcFloodTimeoutMillisFor(t);

View file

@ -103,6 +103,7 @@ protected:
}
// 'UI' concepts, for sub-classes to implement
virtual bool isAutoAddEnabled() const { return true; }
virtual void onDiscoveredContact(ContactInfo& contact, bool is_new) = 0;
virtual bool processAck(const uint8_t *data) = 0;
virtual void onContactPathUpdated(const ContactInfo& contact) = 0;

View file

@ -27,4 +27,5 @@ public:
float getLastRSSI() const override { return ((CustomLR1110 *)_radio)->getRSSI(); }
float getLastSNR() const override { return ((CustomLR1110 *)_radio)->getSNR(); }
int16_t setRxBoostedGainMode(bool en) { return ((CustomLR1110 *)_radio)->setRxBoostedGainMode(en); };
};

View file

@ -0,0 +1,106 @@
#pragma once
#include "ESP32Board.h"
#include <driver/rtc_io.h>
#include <Wire.h>
#include <Arduino.h>
#include "XPowersLib.h"
// LoRa radio module pins for TBeam S3 Supreme
#define P_LORA_DIO_1 1 //SX1262 IRQ pin
#define P_LORA_NSS 10 //SX1262 SS pin
#define P_LORA_RESET 5 //SX1262 Rest pin
#define P_LORA_BUSY 4 //SX1262 Busy pin
#define P_LORA_SCLK 12 //SX1262 SCLK pin
#define P_LORA_MISO 13 //SX1262 MISO pin
#define P_LORA_MOSI 11 //SX1262 MOSI pin
#define PIN_BOARD_SDA 17 //SDA for OLED, BME280, and QMC6310U (0x1C)
#define PIN_BOARD_SCL 18 //SCL for OLED, BME280, and QMC6310U (0x1C)
#define PIN_BOARD_SDA1 42 //SDA for PMU and PFC8563 (RTC)
#define PIN_BOARD_SCL1 41 //SCL for PMU and PFC8563 (RTC)
#define PIN_PMU_IRQ 40 //IRQ pin for PMU
#define PIN_USER_BTN 0
#define P_BOARD_SPI_MOSI 35 //SPI for SD Card and QMI8653 (IMU)
#define P_BOARD_SPI_MISO 37 //SPI for SD Card and QMI8653 (IMU)
#define P_BOARD_SPI_SCK 36 //SPI for SD Card and QMI8653 (IMU)
#define P_BPARD_SPI_CS 47 //SPI for SD Card and QMI8653 (IMU)
#define P_BOARD_IMU_CS 34 //Pin for QMI8653 (IMU) CS
#define P_BOARD_IMU_INT 33 //IMU Int pin
#define P_BOARD_RTC_INT 14 //RTC Int pin
#define P_GPS_RX 9 //GPS RX pin
#define P_GPS_TX 8 //GPS TX pin
#define P_GPS_WAKE 7 //GPS Wakeup pin
#define P_GPS_1PPS 6 //GPS 1PPS pin
//I2C Wire addresses
#define I2C_BME280_ADD 0x76 //BME280 sensor I2C address on Wire
#define I2C_OLED_ADD 0x3C //SH1106 OLED I2C address on Wire
#define I2C_QMC6310U_ADD 0x1C //QMC6310U mag sensor I2C address on Wire
//I2C Wire1 addresses
#define I2C_RTC_ADD 0x51 //RTC I2C address on Wire1
#define I2C_PMU_ADD 0x34 //AXP2101 I2C address on Wire1
class TBeamS3SupremeBoard : public ESP32Board {
public:
void begin() {
bool power_init();
ESP32Board::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 enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) {
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!
}
uint16_t getBattMilliVolts() override {
return 0;
}
uint16_t getBattPercent();
const char* getManufacturerName() const override {
return "LilyGo T-Beam S3 Supreme SX1262";
}
};

View file

@ -29,9 +29,16 @@ public:
uint16_t getBattMilliVolts() override {
#ifdef BATTERY_PIN
#ifdef PIN_3V3_EN
digitalWrite(PIN_3V3_EN, HIGH);
#endif
analogReference(AR_INTERNAL_3_0);
analogReadResolution(12);
delay(10);
float volts = (analogRead(BATTERY_PIN) * ADC_MULTIPLIER * AREF_VOLTAGE) / 4096;
#ifdef PIN_3V3_EN
digitalWrite(PIN_3V3_EN, LOW);
#endif
analogReference(AR_DEFAULT); // put back to default
analogReadResolution(10);

View file

@ -0,0 +1,95 @@
#ifdef XIAO_NRF52
#include <Arduino.h>
#include "XiaoNrf52Board.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 XiaoNrf52Board::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);
#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 XiaoNrf52Board::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

View file

@ -0,0 +1,75 @@
#pragma once
#include <MeshCore.h>
#include <Arduino.h>
#ifdef XIAO_NRF52
// LoRa radio module pins for Seeed Xiao-nrf52
#ifdef SX1262_XIAO_S3_VARIANT
#define P_LORA_DIO_1 D0
#define P_LORA_BUSY D1
#define P_LORA_RESET D2
#define P_LORA_NSS D3
#define LORA_TX_BOOST_PIN D4
#else
#define P_LORA_DIO_1 D1
#define P_LORA_BUSY D3
#define P_LORA_RESET D2
#define P_LORA_NSS D4
#define LORA_TX_BOOST_PIN D5
#endif
#define P_LORA_SCLK PIN_SPI_SCK
#define P_LORA_MISO PIN_SPI_MISO
#define P_LORA_MOSI PIN_SPI_MOSI
//#define SX126X_POWER_EN 37
#define SX126X_DIO2_AS_RF_SWITCH true
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
class XiaoNrf52Board : 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 "Seeed Xiao-nrf52";
}
void reboot() override {
NVIC_SystemReset();
}
bool startOTAUpdate(const char* id, char reply[]) override;
};
#endif

View file

@ -7,7 +7,7 @@ class DisplayDriver {
protected:
DisplayDriver(int w, int h) { _w = w; _h = h; }
public:
enum Color { DARK, LIGHT };
enum Color { DARK=0, LIGHT, RED, GREEN, BLUE, YELLOW, ORANGE }; // on b/w screen, colors will be !=0 synonym of light
int width() const { return _w; }
int height() const { return _h; }

View file

@ -0,0 +1,99 @@
#include "GxEPDDisplay.h"
bool GxEPDDisplay::begin() {
display.epd2.selectSPI(SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
SPI1.begin();
display.init(115200, true, 2, false);
display.setRotation(3);
#ifdef TECHO_ZOOM
display.setFont(&FreeMono9pt7b);
#endif
display.setPartialWindow(0, 0, display.width(), display.height());
display.fillScreen(GxEPD_WHITE);
display.display(true);
#if DISP_BACKLIGHT
pinMode(DISP_BACKLIGHT, OUTPUT);
#endif
_init = true;
return true;
}
void GxEPDDisplay::turnOn() {
if (!_init) begin();
#if DISP_BACKLIGHT
digitalWrite(DISP_BACKLIGHT, HIGH);
_isOn = true;
#endif
}
void GxEPDDisplay::turnOff() {
#if DISP_BACKLIGHT
digitalWrite(DISP_BACKLIGHT, LOW);
#endif
_isOn = false;
}
void GxEPDDisplay::clear() {
display.fillScreen(GxEPD_WHITE);
display.setTextColor(GxEPD_BLACK);
}
void GxEPDDisplay::startFrame(Color bkg) {
display.fillScreen(GxEPD_WHITE);
}
void GxEPDDisplay::setTextSize(int sz) {
display.setTextSize(sz);
}
void GxEPDDisplay::setColor(Color c) {
display.setTextColor(GxEPD_BLACK);
}
void GxEPDDisplay::setCursor(int x, int y) {
#ifdef TECHO_ZOOM
x = x + (x >> 1);
y = y + (y >> 1);
#endif
display.setCursor(x, (y+10));
}
void GxEPDDisplay::print(const char* str) {
display.print(str);
}
void GxEPDDisplay::fillRect(int x, int y, int w, int h) {
#ifdef TECHO_ZOOM
x = x + (x >> 1);
y = y + (y >> 1);
w = w + (w >> 1);
h = h + (h >> 1);
#endif
display.fillRect(x, y, w, h, GxEPD_BLACK);
}
void GxEPDDisplay::drawRect(int x, int y, int w, int h) {
#ifdef TECHO_ZOOM
x = x + (x >> 1);
y = y + (y >> 1);
w = w + (w >> 1);
h = h + (h >> 1);
#endif
display.drawRect(x, y, w, h, GxEPD_BLACK);
}
void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
#ifdef TECHO_ZOOM
x = x + (x >> 1);
y = y + (y >> 1);
w = w + (w >> 1);
h = h + (h >> 1);
#endif
display.drawBitmap(x*1.5, (y*1.5) + 10, bits, w, h, GxEPD_BLACK);
}
void GxEPDDisplay::endFrame() {
display.display(true);
}

View file

@ -0,0 +1,51 @@
#pragma once
#include <SPI.h>
#include <Wire.h>
#define ENABLE_GxEPD2_GFX 0
#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <GxEPD2_4C.h>
#include <GxEPD2_7C.h>
#include <Fonts/FreeMono9pt7b.h>
#define GxEPD2_DISPLAY_CLASS GxEPD2_BW
#define GxEPD2_DRIVER_CLASS GxEPD2_150_BN // DEPG0150BN 200x200, SSD1681, (FPC8101), TTGO T5 V2.4.1
#include <epd/GxEPD2_150_BN.h> // 1.54" b/w
#include "DisplayDriver.h"
//GxEPD2_BW<GxEPD2_150_BN, 200> display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)); // DEPG0150BN 200x200, SSD1681, TTGO T5 V2.4.1
class GxEPDDisplay : public DisplayDriver {
GxEPD2_BW<GxEPD2_150_BN, 200> display;
bool _init = false;
bool _isOn = false;
public:
// there is a margin in y...
GxEPDDisplay() : DisplayDriver(200, 200-10), display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)) {
}
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;
void endFrame() override;
};

View file

@ -38,7 +38,7 @@ void SSD1306Display::setTextSize(int sz) {
}
void SSD1306Display::setColor(Color c) {
_color = (c == LIGHT) ? SSD1306_WHITE : SSD1306_BLACK;
_color = (c != 0) ? SSD1306_WHITE : SSD1306_BLACK;
display.setTextColor(_color);
}

View file

@ -0,0 +1,117 @@
#ifdef ST7789
#include "ST7789Display.h"
bool ST7789Display::i2c_probe(TwoWire& wire, uint8_t addr) {
return true;
/*
wire.beginTransmission(addr);
uint8_t error = wire.endTransmission();
return (error == 0);
*/
}
bool ST7789Display::begin() {
if(!_isOn) {
pinMode(PIN_TFT_VDD_CTL, OUTPUT);
pinMode(PIN_TFT_LEDA_CTL, OUTPUT);
digitalWrite(PIN_TFT_VDD_CTL, LOW);
digitalWrite(PIN_TFT_LEDA_CTL, LOW);
digitalWrite(PIN_TFT_RST, HIGH);
display.init(135, 240);
display.setRotation(2);
display.setSPISpeed(40000000);
display.fillScreen(ST77XX_BLACK);
display.setTextColor(ST77XX_WHITE);
display.setTextSize(2);
display.cp437(true); // Use full 256 char 'Code Page 437' font
_isOn = true;
}
return true;
}
void ST7789Display::turnOn() {
ST7789Display::begin();
}
void ST7789Display::turnOff() {
digitalWrite(PIN_TFT_VDD_CTL, HIGH);
digitalWrite(PIN_TFT_LEDA_CTL, HIGH);
digitalWrite(PIN_TFT_RST, LOW);
// digitalWrite(PIN_TFT_VDD_CTL, LOW);
// digitalWrite(PIN_TFT_LEDA_CTL, LOW);
_isOn = false;
}
void ST7789Display::clear() {
display.fillScreen(ST77XX_BLACK);
}
void ST7789Display::startFrame(Color bkg) {
display.fillScreen(0x00);
display.setTextColor(ST77XX_WHITE);
display.setTextSize(2);
display.cp437(true); // Use full 256 char 'Code Page 437' font
}
void ST7789Display::setTextSize(int sz) {
display.setTextSize(sz);
}
void ST7789Display::setColor(Color c) {
switch (c) {
case DisplayDriver::DARK :
_color = ST77XX_BLACK;
break;
case DisplayDriver::LIGHT :
_color = ST77XX_WHITE;
break;
case DisplayDriver::RED :
_color = ST77XX_RED;
break;
case DisplayDriver::GREEN :
_color = ST77XX_GREEN;
break;
case DisplayDriver::BLUE :
_color = ST77XX_BLUE;
break;
case DisplayDriver::YELLOW :
_color = ST77XX_YELLOW;
break;
case DisplayDriver::ORANGE :
_color = ST77XX_ORANGE;
break;
default:
_color = ST77XX_WHITE;
break;
}
display.setTextColor(_color);
}
void ST7789Display::setCursor(int x, int y) {
display.setCursor(x, y);
}
void ST7789Display::print(const char* str) {
display.print(str);
}
void ST7789Display::fillRect(int x, int y, int w, int h) {
display.fillRect(x, y, w, h, _color);
}
void ST7789Display::drawRect(int x, int y, int w, int h) {
display.drawRect(x, y, w, h, _color);
}
void ST7789Display::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
display.drawBitmap(x, y, bits, w, h, _color);
}
void ST7789Display::endFrame() {
// display.display();
}
#endif

View file

@ -0,0 +1,33 @@
#pragma once
#include "DisplayDriver.h"
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
class ST7789Display : public DisplayDriver {
Adafruit_ST7789 display;
bool _isOn;
uint16_t _color;
bool i2c_probe(TwoWire& wire, uint8_t addr);
public:
ST7789Display() : DisplayDriver(135, 240), display(&SPI1, PIN_TFT_CS, PIN_TFT_DC, PIN_TFT_RST) { _isOn = false; }
// ST7789Display() : DisplayDriver(135, 240), display(PIN_TFT_CS, PIN_TFT_DC, PIN_TFT_SDA, PIN_TFT_SCL, PIN_TFT_RST) { _isOn = false; }
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;
void endFrame() override;
};