Detect OLED or the original stock TFT touch assembly in ordinary V4.2/V4.3 images. Add saved live display.type auto/oled/tft/off selection and report the active selection in board output. Keep sensors on primary I2C, preserve shared power, and isolate TFT FSPI from radio HSPI through sleep and changes. Consolidate future picker/build choices only with application capability proof. Retain exact legacy OTA identities and separate R8 hardware images. Document controls and physical qualification limits. Validation: five serialized Full builds with capability/size/RAM checks; focused host tests, existing R8/T-Deck/OLED regressions, build-profile suite, and picker/provider tests. V4.3 OLED live changes and off/auto reboot persistence passed on Mercerwood. Physical TFT qualification remains pending.
150 lines
4.5 KiB
C++
150 lines
4.5 KiB
C++
#include "HeltecV4Board.h"
|
|
#include <helpers/ESP32GpioHold.h>
|
|
#if defined(HELTEC_V4_DISPLAY_UNIFIED) && HELTEC_V4_DISPLAY_UNIFIED && defined(DISPLAY_CLASS)
|
|
#include <helpers/ui/HeltecV4DisplaySettings.h>
|
|
extern HeltecV4Display display;
|
|
#endif
|
|
|
|
void HeltecV4Board::begin() {
|
|
ESP32Board::begin();
|
|
|
|
|
|
pinMode(PIN_ADC_CTRL, OUTPUT);
|
|
digitalWrite(PIN_ADC_CTRL, LOW); // Initially inactive
|
|
|
|
loRaFEMControl.init();
|
|
|
|
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;
|
|
}
|
|
|
|
mesh::esp32::releaseRtcGpioIfSupported((gpio_num_t)P_LORA_NSS);
|
|
mesh::esp32::deinitRtcGpioIfSupported((gpio_num_t)P_LORA_DIO_1);
|
|
}
|
|
}
|
|
|
|
void HeltecV4Board::onBeforeTransmit(void) {
|
|
if (mesh::leds::enabled()) digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on
|
|
loRaFEMControl.setTxModeEnable();
|
|
}
|
|
|
|
void HeltecV4Board::onAfterTransmit(void) {
|
|
digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off
|
|
loRaFEMControl.setRxModeEnable();
|
|
}
|
|
|
|
void HeltecV4Board::shutdownPeripherals() {
|
|
ESP32Board::shutdownPeripherals();
|
|
|
|
// Turn off PA
|
|
mesh::esp32::holdOutputForDeepSleep((gpio_num_t)P_LORA_PA_POWER, LOW);
|
|
}
|
|
|
|
uint16_t HeltecV4Board::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 (adc_mult * (3.3 / 1024.0) * raw) * 1000;
|
|
}
|
|
|
|
const char* HeltecV4Board::getManufacturerName() const {
|
|
#if defined(HELTEC_V4_DISPLAY_UNIFIED) && HELTEC_V4_DISPLAY_UNIFIED && defined(DISPLAY_CLASS)
|
|
static char name[64];
|
|
snprintf(name, sizeof(name), "Heltec V4.%u %s",
|
|
loRaFEMControl.getFEMType() == KCT8103L_PA ? 3u : 2u,
|
|
display.status());
|
|
return name;
|
|
#elif defined(HELTEC_LORA_V4_TFT)
|
|
return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 TFT" : "Heltec V4 TFT";
|
|
#else
|
|
return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 OLED" : "Heltec V4 OLED";
|
|
#endif
|
|
}
|
|
|
|
bool HeltecV4Board::setLoRaFemLnaEnabled(bool enable) {
|
|
if (!loRaFEMControl.isLnaCanControl()) {
|
|
return false;
|
|
}
|
|
|
|
loRaFEMControl.setLNAEnable(enable);
|
|
loRaFEMControl.setRxModeEnable();
|
|
return true;
|
|
}
|
|
|
|
bool HeltecV4Board::canControlLoRaFemLna() const {
|
|
return loRaFEMControl.isLnaCanControl();
|
|
}
|
|
|
|
bool HeltecV4Board::isLoRaFemLnaEnabled() const {
|
|
return loRaFEMControl.isLNAEnabled();
|
|
}
|
|
|
|
void HeltecV4Board::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 HeltecV4Board::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
|
|
(void)sender_timestamp;
|
|
if (command == nullptr || reply == nullptr) {
|
|
return false;
|
|
}
|
|
#if defined(HELTEC_V4_DISPLAY_UNIFIED) && HELTEC_V4_DISPLAY_UNIFIED && defined(DISPLAY_CLASS)
|
|
if (mesh::ui::handleHeltecV4DisplayCommand(command, reply, 160, display)) return true;
|
|
#endif
|
|
|
|
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
|
|
}
|