Files
MeshCore-Evo/variants/lilygo_tbeam_supreme_SX1262/target.cpp
cod3doomy fd37810022 t-beam supreme: display fix, BME add, user btn fix
-Fixed build issues after display refactor
-Added BME280 support and updated SensorManager to include this data
-Fixed user button and verified it turns the display on
2025-05-19 12:55:56 -07:00

478 lines
14 KiB
C++

#include <Arduino.h>
#include "target.h"
#include <helpers/sensors/MicroNMEALocationProvider.h>
#include <Adafruit_BME280.h>
TBeamS3SupremeBoard board;
#ifdef DISPLAY_CLASS
DISPLAY_CLASS display;
#endif
bool pmuIntFlag;
//#define SEALEVELPRESSURE_HPA (1013.25)
#ifndef LORA_CR
#define LORA_CR 5
#endif
#if defined(P_LORA_SCLK)
static SPIClass spi;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
#else
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
#endif
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
AutoDiscoverRTCClock rtc_clock(fallback_clock);
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1);
TbeamSupSensorManager sensors = TbeamSupSensorManager(nmea);
Adafruit_BME280 bme;
static void setPMUIntFlag(){
pmuIntFlag = true;
}
#ifdef MESH_DEBUG
uint32_t deviceOnline = 0x00;
void scanDevices(TwoWire *w)
{
uint8_t err, addr;
int nDevices = 0;
uint32_t start = 0;
Serial.println("Scanning I2C for Devices");
for (addr = 1; addr < 127; addr++) {
start = millis();
w->beginTransmission(addr); delay(2);
err = w->endTransmission();
if (err == 0) {
nDevices++;
switch (addr) {
case 0x77:
case 0x76:
Serial.println("\tFound BME280 Sensor");
deviceOnline |= BME280_ONLINE;
break;
case 0x34:
Serial.println("\tFound AXP192/AXP2101 PMU");
deviceOnline |= POWERMANAGE_ONLINE;
break;
case 0x3C:
Serial.println("\tFound SSD1306/SH1106 dispaly");
deviceOnline |= DISPLAY_ONLINE;
break;
case 0x51:
Serial.println("\tFound PCF8563 RTC");
deviceOnline |= PCF8563_ONLINE;
break;
case 0x1C:
Serial.println("\tFound QMC6310 MAG Sensor");
deviceOnline |= QMC6310_ONLINE;
break;
default:
Serial.print("\tI2C device found at address 0x");
if (addr < 16) {
Serial.print("0");
}
Serial.print(addr, HEX);
Serial.println(" !");
break;
}
} else if (err == 4) {
Serial.print("Unknow error at address 0x");
if (addr < 16) {
Serial.print("0");
}
Serial.println(addr, HEX);
}
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
Serial.println("Scan for devices is complete.");
Serial.println("\n");
}
void TBeamS3SupremeBoard::printPMU()
{
Serial.print("isCharging:"); Serial.println(PMU.isCharging() ? "YES" : "NO");
Serial.print("isDischarge:"); Serial.println(PMU.isDischarge() ? "YES" : "NO");
Serial.print("isVbusIn:"); Serial.println(PMU.isVbusIn() ? "YES" : "NO");
Serial.print("getBattVoltage:"); Serial.print(PMU.getBattVoltage()); Serial.println("mV");
Serial.print("getVbusVoltage:"); Serial.print(PMU.getVbusVoltage()); Serial.println("mV");
Serial.print("getSystemVoltage:"); Serial.print(PMU.getSystemVoltage()); Serial.println("mV");
// The battery percentage may be inaccurate at first use, the PMU will automatically
// learn the battery curve and will automatically calibrate the battery percentage
// after a charge and discharge cycle
if (PMU.isBatteryConnect()) {
Serial.print("getBatteryPercent:"); Serial.print(PMU.getBatteryPercent()); Serial.println("%");
}
Serial.println();
}
void printBMEValues() {
Serial.print("Temperature = ");
Serial.print(bme.readTemperature());
Serial.println(" *C");
Serial.print("Pressure = ");
Serial.print(bme.readPressure() / 100.0F);
Serial.println(" hPa");
Serial.print("Approx. Altitude = ");
Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(" m");
Serial.print("Humidity = ");
Serial.print(bme.readHumidity());
Serial.println(" %");
Serial.println();
}
#endif
bool TBeamS3SupremeBoard::power_init()
{
bool result = PMU.begin(PMU_WIRE_PORT, I2C_PMU_ADD, PIN_BOARD_SDA1, PIN_BOARD_SCL1);
if (result == false) {
MESH_DEBUG_PRINTLN("power is not online..."); while (1)delay(50);
}
MESH_DEBUG_PRINTLN("Setting charge led");
PMU.setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG);
// Set up PMU interrupts
// MESH_DEBUG_PRINTLN("Setting up PMU interrupts");
// pinMode(PIN_PMU_IRQ, INPUT_PULLUP);
// attachInterrupt(PIN_PMU_IRQ, setPMUIntFlag, FALLING);
// GPS
MESH_DEBUG_PRINTLN("Setting and enabling a-ldo4 for GPS");
PMU.setALDO4Voltage(3300);
PMU.enableALDO4(); // disable to save power
// Lora
MESH_DEBUG_PRINTLN("Setting and enabling a-ldo3 for LoRa");
PMU.setALDO3Voltage(3300);
PMU.enableALDO3();
// To avoid SPI bus issues during power up, reset OLED, sensor, and SD card supplies
// MESH_DEBUG_PRINTLN("Reset a-ldo1&2 and b-ldo1");
// if (ESP_SLEEP_WAKEUP_UNDEFINED == esp_sleep_get_wakeup_cause())
// {
// PMU.disableALDO1();
// PMU.disableALDO2();
// PMU.disableBLDO1();
// delay(250);
// }
// m.2 interface
MESH_DEBUG_PRINTLN("Setting and enabling dcdc3 for m.2 interface");
PMU.setDC3Voltage(3300); // doesn't go anywhere in the schematic??
PMU.enableDC3();
// QMC6310U
MESH_DEBUG_PRINTLN("Setting and enabling a-ldo2 for QMC");
PMU.setALDO2Voltage(3300);
PMU.enableALDO2(); // disable to save power
// BME280 and OLED
MESH_DEBUG_PRINTLN("Setting and enabling a-ldo1 for oled");
PMU.setALDO1Voltage(3300);
PMU.enableALDO1();
// SD card
MESH_DEBUG_PRINTLN("Setting and enabling b-ldo2 for SD card");
PMU.setBLDO1Voltage(3300);
PMU.enableBLDO1();
// Out to header pins
// MESH_DEBUG_PRINTLN("Setting and enabling b-ldo2 for output to header");
// PMU.setBLDO2Voltage(3300);
// PMU.enableBLDO2();
// MESH_DEBUG_PRINTLN("Setting and enabling dcdc4 for output to header");
// PMU.setDC4Voltage(XPOWERS_AXP2101_DCDC4_VOL2_MAX); // 1.8V
// PMU.enableDC4();
// MESH_DEBUG_PRINTLN("Setting and enabling dcdc5 for output to header");
// PMU.setDC5Voltage(3300);
// PMU.enableDC5();
// Unused power rails
MESH_DEBUG_PRINTLN("Disabling unused supplies dcdc2, dldo1 and dldo2");
PMU.disableDC2();
PMU.disableDC5();
PMU.disableDLDO1();
PMU.disableDLDO2();
PMU.disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
// Set charge current to 500mA
MESH_DEBUG_PRINTLN("Setting battery charge current limit and voltage");
PMU.setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA);
PMU.setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2);
PMU.clearIrqStatus();
PMU.disableTSPinMeasure();
// enable battery voltage measurement
MESH_DEBUG_PRINTLN("Enabling battery measurement");
PMU.enableBattVoltageMeasure();
PMU.enableVbusVoltageMeasure();
// Reset and re-enable PMU interrupts
// MESH_DEBUG_PRINTLN("Re-enable interrupts");
// PMU.disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
// PMU.clearIrqStatus();
// PMU.enableIRQ(
// XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | // Battery interrupts
// XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS interrupts
// XPOWERS_AXP2101_PKEY_SHORT_IRQ | XPOWERS_AXP2101_PKEY_LONG_IRQ | // Power Key interrupts
// XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | XPOWERS_AXP2101_BAT_CHG_START_IRQ // Charging interrupts
// );
#ifdef MESH_DEBUG
// scanDevices(&Wire);
// scanDevices(&Wire1);
printPMU();
#endif
// Set the power key off press time
PMU.setPowerKeyPressOffTime(XPOWERS_POWEROFF_4S);
return true;
}
static bool readStringUntil(Stream& s, char dest[], size_t max_len, char term, unsigned int timeout_millis) {
unsigned long timeout = millis() + timeout_millis;
char *dp = dest;
while (millis() < timeout && dp - dest < max_len - 1) {
if (s.available()) {
char c = s.read();
if (c == term) break;
*dp++ = c; // append to dest[]
} else {
delay(1);
}
}
*dp = 0; // null terminator
return millis() < timeout; // false, if timed out
}
static bool l76kProbe()
{
bool result = false;
uint32_t startTimeout ;
Serial1.write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(5);
// Get version information
startTimeout = millis() + 3000;
MESH_DEBUG_PRINTLN("Trying to init L76K GPS");
// Serial1.flush();
while (Serial1.available()) {
int c = Serial1.read();
// Serial.write(c);
// Serial.print(".");
// Serial.flush();
// Serial1.flush();
if (millis() > startTimeout) {
MESH_DEBUG_PRINTLN("L76K NMEA timeout!");
return false;
}
};
Serial1.flush();
delay(200);
Serial1.write("$PCAS06,0*1B\r\n");
char ver[100];
if (!readStringUntil(Serial1, ver, sizeof(ver), '\n', 500)) {
MESH_DEBUG_PRINTLN("Get L76K timeout!");
return false;
}
if (memcmp(ver, "$GPTXT,01,01,02", 15) == 0) {
MESH_DEBUG_PRINTLN("L76K GNSS init succeeded, using L76K GNSS Module\n");
result = true;
}
delay(500);
// Initialize the L76K Chip, use GPS + GLONASS
Serial1.write("$PCAS04,5*1C\r\n");
delay(250);
// only ask for RMC and GGA
Serial1.write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n");
delay(250);
// Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
Serial1.write("$PCAS11,3*1E\r\n");
return result;
}
bool radio_init() {
fallback_clock.begin();
rtc_clock.begin(Wire1);
// #ifdef MESH_DEBUG
// printBMEValues();
// #endif
#ifdef SX126X_DIO3_TCXO_VOLTAGE
float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
#else
float tcxo = 1.6f;
#endif
#if defined(P_LORA_SCLK)
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
return false; // fail
}
radio.setCRC(1);
return true; // success
}
uint32_t radio_get_rng_seed() {
return radio.random(0x7FFFFFFF);
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
radio.setFrequency(freq);
radio.setSpreadingFactor(sf);
radio.setBandwidth(bw);
radio.setCodingRate(cr);
}
void radio_set_tx_power(uint8_t dbm) {
radio.setOutputPower(dbm);
}
void TbeamSupSensorManager::start_gps()
{
gps_active = true;
pinMode(P_GPS_WAKE, OUTPUT);
digitalWrite(P_GPS_WAKE, HIGH);
}
void TbeamSupSensorManager::sleep_gps() {
gps_active = false;
pinMode(P_GPS_WAKE, OUTPUT);
digitalWrite(P_GPS_WAKE, LOW);
}
bool TbeamSupSensorManager::begin() {
//init BME280
if (! bme.begin(0x77, &Wire)) {
MESH_DEBUG_PRINTLN("Could not find a valid BME280 sensor, check wiring!");
}
else
MESH_DEBUG_PRINTLN("BME280 found and init!");
// init GPS port
Serial1.begin(GPS_BAUD_RATE, SERIAL_8N1, P_GPS_RX, P_GPS_TX);
bool result = false;
for ( int i = 0; i < 3; ++i) {
result = l76kProbe();
if (result) {
gps_active = true;
return result;
}
}
return result;
}
bool TbeamSupSensorManager::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);
}
if (requester_permissions & TELEM_PERM_ENVIRONMENT) { // does requester have permission?
telemetry.addTemperature(TELEM_CHANNEL_SELF, node_temp);
telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, node_hum);
telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, node_pres);
//telemetry.addAltitude(TELEM_CHANNEL_SELF, node_alt);
}
return true;
}
void TbeamSupSensorManager::loop() {
static long next_update = 0;
_nmea->loop();
if (millis() > next_update) {
if (_nmea->isValid()) {
node_lat = ((double)_nmea->getLatitude())/1000000.;
node_lon = ((double)_nmea->getLongitude())/1000000.;
node_altitude = ((double)_nmea->getAltitude()) / 1000.0;
//Serial.printf("lat %f lon %f\r\n", _lat, _lon);
}
//read BME280 values
//node_alt = bme.readAltitude(SEALEVELPRESSURE_HPA);
node_temp = bme.readTemperature();
node_hum = bme.readHumidity();
node_pres = (bme.readPressure() / 100.0F);
#ifdef MESH_DEBUG
Serial.print("Temperature = ");
Serial.print(node_temp);
Serial.println(" *C");
Serial.print("Humidity = ");
Serial.print(node_hum);
Serial.println(" %");
Serial.print("Pressure = ");
Serial.print(node_pres);
Serial.println(" hPa");
// Serial.print("Approx. Altitude = ");
// Serial.print(node_alt);
// Serial.println(" m");
#endif
next_update = millis() + 1000;
}
}
int TbeamSupSensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch)
const char* TbeamSupSensorManager::getSettingName(int i) const {
return i == 0 ? "gps" : NULL;
}
const char* TbeamSupSensorManager::getSettingValue(int i) const {
if (i == 0) {
return gps_active ? "1" : "0";
}
return NULL;
}
bool TbeamSupSensorManager::setSettingValue(const char* name, const char* value) {
if (strcmp(name, "gps") == 0) {
if (strcmp(value, "0") == 0) {
sleep_gps();
} else {
start_gps();
}
return true;
}
return false; // not supported
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}