Reduce sender power draw (RX windows + CPU/WiFi/ADC/pins)
- Add LoRa idle/sleep/receive-window helpers and use short RX windows for ACK/time sync - Schedule sender time-sync windows (fast/slow) and track RX vs sleep time in debug - Lower sender power (80 MHz CPU, WiFi/BT off, reduced ADC sampling, unused pins pulldown) - Make SERIAL_DEBUG_MODE a build flag, add prod envs with debug off, and document changes
This commit is contained in:
@@ -110,10 +110,24 @@ bool lora_receive(LoraPacket &pkt, uint32_t timeout_ms) {
|
||||
}
|
||||
}
|
||||
|
||||
void lora_idle() {
|
||||
LoRa.idle();
|
||||
}
|
||||
|
||||
void lora_sleep() {
|
||||
LoRa.sleep();
|
||||
}
|
||||
|
||||
bool lora_receive_window(LoraPacket &pkt, uint32_t timeout_ms) {
|
||||
if (timeout_ms == 0) {
|
||||
return false;
|
||||
}
|
||||
LoRa.receive();
|
||||
bool got = lora_receive(pkt, timeout_ms);
|
||||
LoRa.sleep();
|
||||
return got;
|
||||
}
|
||||
|
||||
uint32_t lora_airtime_ms(size_t packet_len) {
|
||||
if (packet_len == 0) {
|
||||
return 0;
|
||||
|
||||
88
src/main.cpp
88
src/main.cpp
@@ -88,6 +88,10 @@ static uint8_t g_inflight_count = 0;
|
||||
static uint16_t g_inflight_batch_id = 0;
|
||||
static bool g_inflight_active = false;
|
||||
static uint32_t g_last_debug_log_ms = 0;
|
||||
static uint32_t g_sender_last_timesync_check_ms = 0;
|
||||
static uint32_t g_sender_rx_window_ms = 0;
|
||||
static uint32_t g_sender_sleep_ms = 0;
|
||||
static uint32_t g_sender_power_log_ms = 0;
|
||||
|
||||
static void watchdog_kick();
|
||||
|
||||
@@ -144,6 +148,22 @@ static void update_battery_cache() {
|
||||
g_last_battery_ms = millis();
|
||||
}
|
||||
|
||||
static bool sender_timesync_window_due() {
|
||||
uint32_t interval_sec = SENDER_TIMESYNC_CHECK_SEC_FAST;
|
||||
if (time_is_synced() && time_rtc_present()) {
|
||||
interval_sec = SENDER_TIMESYNC_CHECK_SEC_SLOW;
|
||||
}
|
||||
if (g_sender_last_timesync_check_ms == 0) {
|
||||
g_sender_last_timesync_check_ms = millis() - interval_sec * 1000UL;
|
||||
}
|
||||
uint32_t now_ms = millis();
|
||||
if (now_ms - g_sender_last_timesync_check_ms >= interval_sec * 1000UL) {
|
||||
g_sender_last_timesync_check_ms = now_ms;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool batch_queue_drop_oldest() {
|
||||
if (g_batch_count == 0) {
|
||||
return false;
|
||||
@@ -620,6 +640,7 @@ void setup() {
|
||||
|
||||
if (g_role == DeviceRole::Sender) {
|
||||
power_sender_init();
|
||||
power_configure_unused_pins_sender();
|
||||
meter_init();
|
||||
g_last_sample_ms = millis() - METER_SAMPLE_INTERVAL_MS;
|
||||
g_last_send_ms = millis();
|
||||
@@ -712,37 +733,18 @@ static void sender_loop() {
|
||||
}
|
||||
|
||||
if (g_batch_ack_pending) {
|
||||
uint32_t end_ms = millis() + 400;
|
||||
while (millis() < end_ms) {
|
||||
LoraPacket ack_pkt = {};
|
||||
if (!lora_receive(ack_pkt, 0) || ack_pkt.protocol_version != PROTOCOL_VERSION) {
|
||||
delay(5);
|
||||
continue;
|
||||
}
|
||||
if (ack_pkt.payload_type == PayloadType::Ack && ack_pkt.payload_len >= 6 && ack_pkt.role == DeviceRole::Receiver) {
|
||||
uint16_t ack_id = read_u16_le(ack_pkt.payload);
|
||||
uint16_t ack_sender = read_u16_le(&ack_pkt.payload[2]);
|
||||
uint16_t ack_receiver = read_u16_le(&ack_pkt.payload[4]);
|
||||
if (ack_sender == g_short_id && ack_receiver == ack_pkt.device_id_short &&
|
||||
g_batch_ack_pending && ack_id == g_last_sent_batch_id) {
|
||||
g_last_acked_batch_id = ack_id;
|
||||
serial_debug_printf("ack: ok batch_id=%u", ack_id);
|
||||
finish_inflight_batch();
|
||||
break;
|
||||
}
|
||||
}
|
||||
LoraPacket ack_pkt = {};
|
||||
uint32_t rx_start = millis();
|
||||
bool got_ack = lora_receive_window(ack_pkt, 400);
|
||||
uint32_t rx_elapsed = millis() - rx_start;
|
||||
if (SERIAL_DEBUG_MODE) {
|
||||
g_sender_rx_window_ms += rx_elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
LoraPacket rx = {};
|
||||
if (lora_receive(rx, 0) && rx.protocol_version == PROTOCOL_VERSION) {
|
||||
if (rx.payload_type == PayloadType::TimeSync) {
|
||||
time_handle_timesync_payload(rx.payload, rx.payload_len);
|
||||
} else if (rx.payload_type == PayloadType::Ack && rx.payload_len >= 6 && rx.role == DeviceRole::Receiver) {
|
||||
uint16_t ack_id = read_u16_le(rx.payload);
|
||||
uint16_t ack_sender = read_u16_le(&rx.payload[2]);
|
||||
uint16_t ack_receiver = read_u16_le(&rx.payload[4]);
|
||||
if (ack_sender == g_short_id && ack_receiver == rx.device_id_short &&
|
||||
if (got_ack && ack_pkt.payload_type == PayloadType::Ack && ack_pkt.payload_len >= 6 && ack_pkt.role == DeviceRole::Receiver) {
|
||||
uint16_t ack_id = read_u16_le(ack_pkt.payload);
|
||||
uint16_t ack_sender = read_u16_le(&ack_pkt.payload[2]);
|
||||
uint16_t ack_receiver = read_u16_le(&ack_pkt.payload[4]);
|
||||
if (ack_sender == g_short_id && ack_receiver == ack_pkt.device_id_short &&
|
||||
g_batch_ack_pending && ack_id == g_last_sent_batch_id) {
|
||||
g_last_acked_batch_id = ack_id;
|
||||
serial_debug_printf("ack: ok batch_id=%u", ack_id);
|
||||
@@ -751,6 +753,23 @@ static void sender_loop() {
|
||||
}
|
||||
}
|
||||
|
||||
bool timesync_due = (!g_batch_ack_pending && sender_timesync_window_due());
|
||||
if (timesync_due) {
|
||||
LoraPacket rx = {};
|
||||
uint32_t rx_start = millis();
|
||||
bool got = lora_receive_window(rx, SENDER_TIMESYNC_WINDOW_MS);
|
||||
uint32_t rx_elapsed = millis() - rx_start;
|
||||
if (SERIAL_DEBUG_MODE) {
|
||||
g_sender_rx_window_ms += rx_elapsed;
|
||||
}
|
||||
if (got && rx.payload_type == PayloadType::TimeSync) {
|
||||
time_handle_timesync_payload(rx.payload, rx.payload_len);
|
||||
}
|
||||
}
|
||||
if (!g_batch_ack_pending) {
|
||||
lora_sleep();
|
||||
}
|
||||
|
||||
if (g_batch_ack_pending && (now_ms - g_last_batch_send_ms >= g_batch_ack_timeout_ms)) {
|
||||
if (g_batch_retry_count < BATCH_MAX_RETRIES) {
|
||||
g_batch_retry_count++;
|
||||
@@ -780,6 +799,15 @@ static void sender_loop() {
|
||||
uint32_t next_due = next_sample_due < next_send_due ? next_sample_due : next_send_due;
|
||||
if (!g_batch_ack_pending && next_due > now_ms) {
|
||||
watchdog_kick();
|
||||
if (SERIAL_DEBUG_MODE) {
|
||||
g_sender_sleep_ms += (next_due - now_ms);
|
||||
if (now_ms - g_sender_power_log_ms >= 10000) {
|
||||
g_sender_power_log_ms = now_ms;
|
||||
serial_debug_printf("power: rx_ms=%lu sleep_ms=%lu", static_cast<unsigned long>(g_sender_rx_window_ms),
|
||||
static_cast<unsigned long>(g_sender_sleep_ms));
|
||||
}
|
||||
}
|
||||
lora_sleep();
|
||||
light_sleep_ms(next_due - now_ms);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,10 @@ static constexpr float BATTERY_CAL = 1.0f;
|
||||
static constexpr float ADC_REF_V = 3.3f;
|
||||
|
||||
void power_sender_init() {
|
||||
setCpuFrequencyMhz(80);
|
||||
WiFi.mode(WIFI_OFF);
|
||||
esp_wifi_stop();
|
||||
esp_wifi_deinit();
|
||||
btStop();
|
||||
analogReadResolution(12);
|
||||
pinMode(PIN_BAT_ADC, INPUT);
|
||||
@@ -22,14 +25,19 @@ void power_receiver_init() {
|
||||
pinMode(PIN_BAT_ADC, INPUT);
|
||||
}
|
||||
|
||||
void read_battery(MeterData &data) {
|
||||
const int samples = 8;
|
||||
uint32_t sum = 0;
|
||||
for (int i = 0; i < samples; ++i) {
|
||||
sum += analogRead(PIN_BAT_ADC);
|
||||
delay(5);
|
||||
void power_configure_unused_pins_sender() {
|
||||
// Board-specific: only touch pins that are known unused and safe on TTGO LoRa32 v1.6.1
|
||||
const uint8_t pins[] = {32, 33};
|
||||
for (uint8_t pin : pins) {
|
||||
pinMode(pin, INPUT_PULLDOWN);
|
||||
}
|
||||
float avg = static_cast<float>(sum) / samples;
|
||||
}
|
||||
|
||||
void read_battery(MeterData &data) {
|
||||
uint32_t sum = 0;
|
||||
sum += analogRead(PIN_BAT_ADC);
|
||||
sum += analogRead(PIN_BAT_ADC);
|
||||
float avg = static_cast<float>(sum) / 2.0f;
|
||||
float v = (avg / 4095.0f) * ADC_REF_V * BATTERY_DIVIDER * BATTERY_CAL;
|
||||
|
||||
data.battery_voltage_v = v;
|
||||
|
||||
Reference in New Issue
Block a user