Add get leds and set leds on|off for Companion, repeater, room server and sensor builds. Preserve the setting across reboots and cover GPIO, RGB, Bluetooth, notification and expander indicators without changing radio amplifier controls or screen backlights. RAK Companions blink while advertising and keep the Bluetooth LED off once connected, including late timer callbacks. Adapted from meshcore-dev/MeshCore#3605 and #2694. Validated with host regressions and firmware builds for SenseCAP Solar, RAK4631, T096, Heltec V4, T-Beam, Xiao S3, Pico W and Wio E5 Mini. Co-authored-by: R0ck <develop@hectospark.co.uk> Co-authored-by: Nick Dunklee <nick@dunklee.org>
203 lines
5.6 KiB
C++
203 lines
5.6 KiB
C++
#include "T096Board.h"
|
|
|
|
#include <Arduino.h>
|
|
#include <Wire.h>
|
|
|
|
#ifdef NRF52_POWER_MANAGEMENT
|
|
// Static configuration for power management
|
|
// Values come from variant.h defines
|
|
const PowerMgtConfig power_config = {
|
|
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
|
|
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
|
|
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK
|
|
};
|
|
|
|
void T096Board::initiateShutdown(uint8_t reason) {
|
|
#if ENV_INCLUDE_GPS == 1
|
|
pinMode(PIN_GPS_EN, OUTPUT);
|
|
digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE);
|
|
#endif
|
|
variant_shutdown();
|
|
|
|
bool enable_lpcomp = (reason == SHUTDOWN_REASON_LOW_VOLTAGE ||
|
|
reason == SHUTDOWN_REASON_BOOT_PROTECT);
|
|
pinMode(PIN_BAT_CTL, OUTPUT);
|
|
digitalWrite(PIN_BAT_CTL, enable_lpcomp ? HIGH : LOW);
|
|
|
|
if (enable_lpcomp) {
|
|
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);
|
|
}
|
|
|
|
enterSystemOff(reason);
|
|
}
|
|
#endif // NRF52_POWER_MANAGEMENT
|
|
|
|
void T096Board::begin() {
|
|
// T096 populates the nRF52840 DC/DC network. Do not bypass the regulator
|
|
// initializer and leave application efficiency dependent on bootloader state.
|
|
NRF52BoardDCDC::begin();
|
|
|
|
#ifdef NRF52_POWER_MANAGEMENT
|
|
// Boot voltage protection check (may not return if voltage too low)
|
|
checkBootVoltage(&power_config);
|
|
#endif
|
|
|
|
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
|
|
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
|
|
#endif
|
|
|
|
Wire.begin();
|
|
|
|
pinMode(P_LORA_TX_LED, OUTPUT);
|
|
digitalWrite(P_LORA_TX_LED, LOW);
|
|
|
|
periph_power.begin();
|
|
loRaFEMControl.init();
|
|
delay(1);
|
|
}
|
|
|
|
void T096Board::applyLedPolicy() {
|
|
NRF52Board::applyLedPolicy();
|
|
if (!mesh::leds::enabled()) digitalWrite(P_LORA_TX_LED, LOW);
|
|
}
|
|
|
|
void T096Board::onBeforeTransmit() {
|
|
if (mesh::leds::enabled()) digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on
|
|
loRaFEMControl.setTxModeEnable();
|
|
}
|
|
|
|
void T096Board::onAfterTransmit() {
|
|
digitalWrite(P_LORA_TX_LED, LOW); //turn TX LED off
|
|
loRaFEMControl.setRxModeEnable();
|
|
}
|
|
|
|
uint16_t T096Board::getBattMilliVolts() {
|
|
int adcvalue = 0;
|
|
analogReadResolution(12);
|
|
analogReference(AR_INTERNAL_3_0);
|
|
pinMode(PIN_VBAT_READ, INPUT);
|
|
pinMode(PIN_BAT_CTL, OUTPUT);
|
|
digitalWrite(PIN_BAT_CTL, 1);
|
|
|
|
delay(10);
|
|
adcvalue = analogRead(PIN_VBAT_READ);
|
|
digitalWrite(PIN_BAT_CTL, 0);
|
|
|
|
return calibrateBatteryMillivolts((uint16_t)((float)adcvalue * MV_LSB * 4.9));
|
|
}
|
|
void T096Board::variant_shutdown() {
|
|
nrf_gpio_cfg_default(PIN_VEXT_EN);
|
|
nrf_gpio_cfg_default(PIN_TFT_CS);
|
|
nrf_gpio_cfg_default(PIN_TFT_DC);
|
|
nrf_gpio_cfg_default(PIN_TFT_SDA);
|
|
nrf_gpio_cfg_default(PIN_TFT_SCL);
|
|
nrf_gpio_cfg_default(PIN_TFT_RST);
|
|
nrf_gpio_cfg_default(PIN_TFT_LEDA_CTL);
|
|
|
|
nrf_gpio_cfg_default(PIN_LED);
|
|
|
|
nrf_gpio_cfg_default(P_LORA_KCT8103L_PA_CSD);
|
|
nrf_gpio_cfg_default(P_LORA_KCT8103L_PA_CTX);
|
|
pinMode(P_LORA_PA_POWER, OUTPUT);
|
|
digitalWrite(P_LORA_PA_POWER, LOW);
|
|
|
|
digitalWrite(PIN_BAT_CTL, LOW);
|
|
nrf_gpio_cfg_default(LORA_CS);
|
|
nrf_gpio_cfg_default(SX126X_DIO1);
|
|
nrf_gpio_cfg_default(SX126X_BUSY);
|
|
nrf_gpio_cfg_default(SX126X_RESET);
|
|
|
|
nrf_gpio_cfg_default(PIN_SPI_MISO);
|
|
nrf_gpio_cfg_default(PIN_SPI_MOSI);
|
|
nrf_gpio_cfg_default(PIN_SPI_SCK);
|
|
|
|
// nrf_gpio_cfg_default(PIN_GPS_PPS);
|
|
nrf_gpio_cfg_default(PIN_GPS_RESET);
|
|
nrf_gpio_cfg_default(PIN_GPS_EN);
|
|
nrf_gpio_cfg_default(PIN_GPS_RX);
|
|
nrf_gpio_cfg_default(PIN_GPS_TX);
|
|
}
|
|
|
|
void T096Board::shutdownPeripherals() {
|
|
NRF52Board::shutdownPeripherals();
|
|
|
|
loRaFEMControl.setSleepModeEnable();
|
|
nrf_gpio_cfg_default(PIN_GPS_EN); // 363uA down to 39uA
|
|
}
|
|
|
|
const char* T096Board::getManufacturerName() const {
|
|
return "Heltec T096";
|
|
}
|
|
|
|
bool T096Board::setLoRaFemLnaEnabled(bool enable) {
|
|
if (!loRaFEMControl.isLnaCanControl()) {
|
|
return false;
|
|
}
|
|
|
|
loRaFEMControl.setLNAEnable(enable);
|
|
loRaFEMControl.setRxModeEnable();
|
|
return true;
|
|
}
|
|
|
|
bool T096Board::canControlLoRaFemLna() const {
|
|
return loRaFEMControl.isLnaCanControl();
|
|
}
|
|
|
|
bool T096Board::isLoRaFemLnaEnabled() const {
|
|
return loRaFEMControl.isLNAEnabled();
|
|
}
|
|
|
|
void T096Board::attachDynamicPrefs(KeyValueStore* prefs) {
|
|
_prefs = prefs;
|
|
if (_prefs == nullptr) {
|
|
return;
|
|
}
|
|
|
|
char radio_fem_rxgain[8] = { 0 };
|
|
_prefs->getByKey("fem_rxgain", radio_fem_rxgain, 7); // get initial values
|
|
|
|
setLoRaFemLnaEnabled(strcmp(radio_fem_rxgain, "1") == 0);
|
|
}
|
|
|
|
bool T096Board::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
|
|
(void)sender_timestamp;
|
|
if (command == nullptr || reply == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
if (strcmp(command, "get radio.fem.rxgain") == 0) {
|
|
if (!loRaFEMControl.isLnaCanControl()) {
|
|
strcpy(reply, "Error: unsupported");
|
|
} else {
|
|
sprintf(reply, "> %s", isLoRaFemLnaEnabled() ? "on" : "off");
|
|
}
|
|
return true;
|
|
}
|
|
if (strncmp(command, "set radio.fem.rxgain ", 21) == 0) {
|
|
if (!loRaFEMControl.isLnaCanControl()) {
|
|
strcpy(reply, "Error: unsupported");
|
|
} else if (_prefs == nullptr) {
|
|
strcpy(reply, "Error: preferences unavailable");
|
|
} else if (strcmp(&command[21], "on") == 0) {
|
|
if (setLoRaFemLnaEnabled(true)) {
|
|
_prefs->setByKey("fem_rxgain", "1");
|
|
strcpy(reply, "OK - LoRa FEM RX gain on");
|
|
} else {
|
|
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
|
|
}
|
|
} else if (strcmp(&command[21], "off") == 0) {
|
|
if (setLoRaFemLnaEnabled(false)) {
|
|
_prefs->setByKey("fem_rxgain", "0");
|
|
strcpy(reply, "OK - LoRa FEM RX gain off");
|
|
} else {
|
|
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
|
|
}
|
|
} else {
|
|
strcpy(reply, "Error: state must be on or off");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false; // not handled
|
|
}
|