Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions variants/heltec_v3/DISPLAY_LED_CONTROL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Display and LED Control for Heltec V3

The Heltec V3 variant now supports user-configurable settings to enable/disable the screen and LED lights through CLI commands.

## Available Settings

### 1. Screen Control (Repeaters/Clients with Display)
- **Setting name**: `screen`
- **Values**: `0` (disabled) or `1` (enabled)
- **Default**: Enabled
- **Purpose**: Control the OLED display power

### 2. LED Control (All devices)
- **Setting name**: `led`
- **Values**: `0` (disabled) or `1` (enabled)
- **Default**: Enabled
- **Purpose**: Control the TX indicator LED (pin 35)

## CLI Commands

### View Current Settings
```bash
sensor list
```
This will show all available settings and their current values.

### Get Specific Setting
```bash
sensor get screen
sensor get led
```

### Change Settings
```bash
# Disable screen
sensor set screen 0

# Enable screen
sensor set screen 1

# Disable LED
sensor set led 0

# Enable LED
sensor set led 1
```

## Using in Application Code

### Check if Screen is Enabled
```cpp
#include "target.h"

void setup() {
board.begin();

#ifdef DISPLAY_CLASS
if (board.screen_enabled && display.begin()) {
// Display is enabled and initialized
display.startFrame();
display.print("Screen is on!");
display.endFrame();
}
#endif
}
```

### Control LED with Settings
```cpp
void onTransmit() {
// LED will only turn on if led_enabled is true
board.setLED(true);
delay(100);
board.setLED(false);
}
```

### Disable Display at Runtime
```cpp
void loop() {
#ifdef DISPLAY_CLASS
if (!board.screen_enabled && display.isOn()) {
display.turnOff(); // Turn off if setting was changed
} else if (board.screen_enabled && !display.isOn()) {
display.turnOn(); // Turn on if setting was changed
}
#endif
}
```

## Power Saving Benefits

Disabling the screen and LED can significantly reduce power consumption:
- **Screen**: ~15-20mA when active
- **LED**: ~5-10mA when lit
- **Combined savings**: Up to 30mA when both disabled

This is especially useful for:
- Solar-powered nodes
- Battery-operated devices
- Stealth deployments where visual indicators are not desired

## Hardware Details

- **Screen Power Pin**: GPIO 36 (`PIN_VEXT_EN`)
- **LED Pin**: GPIO 35 (`P_LORA_TX_LED`)
- **Display Type**: SSD1306 OLED (128x64)
- **I2C Address**: 0x3C

## Notes

1. Settings are managed through the board's `getNumSettings()`, `getSettingName()`, `getSettingValue()`, and `setSettingValue()` methods
2. The display power is controlled via the `RefCountedDigitalPin periph_power` to safely share power control with other peripherals
3. LED control uses direct GPIO manipulation for minimal overhead
4. Settings can be persisted by saving node preferences after modification
194 changes: 193 additions & 1 deletion variants/heltec_v3/HeltecV3Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
#include <helpers/RefCountedDigitalPin.h>
#include <helpers/ESP32Board.h>

#if defined(ESP32)
#include "nvs_flash.h"
#include "nvs.h"
#endif

// built-ins
#ifndef PIN_VBAT_READ // set in platformio.ini for boards like Heltec Wireless Paper (20)
#define PIN_VBAT_READ 1
Expand All @@ -19,15 +24,93 @@
class HeltecV3Board : public ESP32Board {
private:
bool adc_active_state;
bool initNvs() {
#if defined(ESP32)
static bool nvs_ready = false;
if (nvs_ready) return true;

esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
nvs_flash_erase();
err = nvs_flash_init();
}
nvs_ready = (err == ESP_OK);
return nvs_ready;
#else
return false;
#endif
}

bool readNvsU8(const char* key, uint8_t& value) {
#if defined(ESP32)
if (!initNvs()) return false;
nvs_handle_t handle;
if (nvs_open("heltec_v3", NVS_READWRITE, &handle) != ESP_OK) return false;
esp_err_t err = nvs_get_u8(handle, key, &value);
nvs_close(handle);
return err == ESP_OK;
#else
(void)key;
(void)value;
return false;
#endif
}

void writeNvsU8(const char* key, uint8_t value) {
#if defined(ESP32)
if (!initNvs()) return;
nvs_handle_t handle;
if (nvs_open("heltec_v3", NVS_READWRITE, &handle) != ESP_OK) return;
nvs_set_u8(handle, key, value);
nvs_commit(handle);
nvs_close(handle);
#else
(void)key;
(void)value;
#endif
}

public:
RefCountedDigitalPin periph_power;
bool screen_enabled;
bool led_enabled;
uint8_t screen_brightness;
mutable char brightness_str[4];

HeltecV3Board() : periph_power(PIN_VEXT_EN) { }
#ifdef PIN_VEXT_EN_ACTIVE
HeltecV3Board() : periph_power(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE), screen_enabled(true), led_enabled(true), screen_brightness(255) {
#else
HeltecV3Board() : periph_power(PIN_VEXT_EN), screen_enabled(true), led_enabled(true), screen_brightness(255) {
#endif
brightness_str[0] = '2';
brightness_str[1] = '5';
brightness_str[2] = '5';
brightness_str[3] = 0;
}

void begin() {
ESP32Board::begin();

// Load settings from NVS (ESP32)
uint8_t val;
if (readNvsU8("screen", val)) {
screen_enabled = (val != 0);
}
if (readNvsU8("led", val)) {
led_enabled = (val != 0);
}
if (readNvsU8("brightness", val)) {
screen_brightness = val;
} else {
screen_brightness = 255;
writeNvsU8("brightness", screen_brightness);
}
if (screen_brightness == 0) {
screen_brightness = 255;
writeNvsU8("brightness", screen_brightness);
}
Serial.printf("Loaded: screen=%d led=%d brightness=%u\n", screen_enabled, led_enabled, screen_brightness);

// Auto-detect correct ADC_CTRL pin polarity (different for boards >3.2)
pinMode(PIN_ADC_CTRL, INPUT);
adc_active_state = !digitalRead(PIN_ADC_CTRL);
Expand All @@ -37,6 +120,12 @@ class HeltecV3Board : public ESP32Board {

periph_power.begin();

// Initialize LED pin
#ifdef P_LORA_TX_LED
pinMode(P_LORA_TX_LED, OUTPUT);
digitalWrite(P_LORA_TX_LED, LOW); // Start with LED off
#endif

esp_reset_reason_t reason = esp_reset_reason();
if (reason == ESP_RST_DEEPSLEEP) {
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
Expand Down Expand Up @@ -94,4 +183,107 @@ class HeltecV3Board : public ESP32Board {
const char* getManufacturerName() const override {
return "Heltec V3";
}

// Settings support for screen and LED control
int getNumSettings() const {
#ifdef DISPLAY_CLASS
return 3; // screen + led + brightness for repeaters/clients with display
#else
return 1; // led only
#endif
}

const char* getSettingName(int i) const {
#ifdef DISPLAY_CLASS
if (i == 0) return "screen";
if (i == 1) return "led";
if (i == 2) return "brightness";
#else
if (i == 0) return "led";
#endif
return NULL;
}

const char* getSettingValue(int i) const {
#ifdef DISPLAY_CLASS
if (i == 0) return screen_enabled ? "1" : "0";
if (i == 1) return led_enabled ? "1" : "0";
if (i == 2) {
snprintf(brightness_str, sizeof(brightness_str), "%u", screen_brightness);
return brightness_str;
}
#else
if (i == 0) return led_enabled ? "1" : "0";
#endif
return NULL;
}

bool setSettingValue(const char* name, const char* value) {
bool enable = (strcmp(value, "1") == 0);
bool changed = false;
bool brightness_changed = false;

#ifdef DISPLAY_CLASS
if (strcmp(name, "screen") == 0) {
screen_enabled = enable;
changed = true;
}
#endif

if (strcmp(name, "led") == 0) {
led_enabled = enable;
#ifdef P_LORA_TX_LED
digitalWrite(P_LORA_TX_LED, enable ? LOW : LOW); // Keep off when disabled
#endif
changed = true;
}

#ifdef DISPLAY_CLASS
if (strcmp(name, "brightness") == 0) {
int val = atoi(value);
if (val < 0) val = 0;
if (val > 255) val = 255;
screen_brightness = (uint8_t)val;
brightness_changed = true;
changed = true;
}
#endif

if (changed) {
// Save to NVS (ESP32)
if (brightness_changed) {
writeNvsU8("brightness", screen_brightness);
Serial.printf("Saved: brightness=%u\n", screen_brightness);
} else {
writeNvsU8(name, enable ? 1 : 0);
Serial.printf("Saved: %s=%d\n", name, enable);
}
return true;
}

return false;
}

// Helper to control LED based on settings
void setLED(bool on) {
#ifdef P_LORA_TX_LED
if (led_enabled && on) {
digitalWrite(P_LORA_TX_LED, HIGH);
} else {
digitalWrite(P_LORA_TX_LED, LOW);
}
#endif
}

// Override transmit callbacks to respect LED setting
#ifdef P_LORA_TX_LED
void onBeforeTransmit() override {
if (led_enabled) {
digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on only if enabled
}
}
void onAfterTransmit() override {
digitalWrite(P_LORA_TX_LED, LOW); // always turn off
}
#endif
};
Loading