• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Дикое потребление ESP32-C3 в BLE (Arduino)

pvvx

Активный участник сообщества
Модули ESP32-C3S от Ai-Thinker...
Потребовалось слепить экономичный приемник BLE рекламы с одного устройства...
В качестве испытуемого передатчика BLE рекламы взял Xiaomi LYWSD03MMC.
Код:
/*
 * Test Advertisements Scanning
 */
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEScan.h>
#include "BLEDevice.h"
#include "soc/rtc_cntl_reg.h"
#include "hal/gpio_ll.h" 
#define USE_LIGH_SLEEP 1 // 1 - on, 0 - off
#if USE_LIGH_SLEEP
#define eprintf // no printf
#else
#define eprintf printf
#endif
#define SCAN_TIME_DEF 21 // seconds
int scanTime = SCAN_TIME_DEF; // seconds
int stage = 0;
int delay_ms = 50;
uint32_t period_min_ms, period_max_ms;
BLEAddress inMacAddress = BLEAddress("a4:c1:38:0b:5e:ed"); // The remote data device MAC
uint32_t tik_scan, rx_all_count = 0, rx_err_count = 0;
boolean flag_delay = false;
BLEScan* pBLEScan;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      if (inMacAddress.equals(advertisedDevice.getAddress())) {
        uint32_t tt = millis();
        uint32_t delta = tt - tik_scan;
        tik_scan = tt;
        pBLEScan->stop();
        pBLEScan->clearResults();
        flag_delay = true;
        if(stage) {
          if (stage == 1) {
            period_min_ms = delta;
            period_max_ms = delta;
            delay_ms = delta - 20;
            scanTime = 1 + (period_max_ms + 250)/500; // sec 
            eprintf("Sheck period: %u ms, delay: %u, scanTime: %d\n", delta, delay_ms, scanTime);
            stage = 2;
          }          
          if(rx_all_count++) {
            if(delta > period_max_ms) {
              if(delta > period_max_ms + (period_max_ms>>2)) {
                rx_err_count++;
              } else 
                period_max_ms = delta;
            } else if (delta < period_min_ms) {
              period_min_ms = delta;
              delay_ms = period_min_ms - 20;
              scanTime = 1 + (period_max_ms + 250)/500; // sec
              eprintf("Correct min period: %u ms, delay: %u, scanTime: %d\n", delta, delay_ms, scanTime);
            }
          }
          eprintf("delta: %d ms (%u..%u), lost: %u, total: %u\n", delta, period_min_ms, period_max_ms, rx_err_count, rx_all_count);
        } else {
            eprintf("Start calk delta...\n");
            stage = 1;
        } 
      }
    }
};
void setup() {
  CLEAR_PERI_REG_MASK(USB_DEVICE_CONF0_REG, USB_DEVICE_USB_PAD_ENABLE);
#if (!USE_LIGH_SLEEP)
  pinMode(PIN_X, OUTPUT);
  digitalWrite(PIN_X, LOW); 
  Serial.begin(115200);
  Serial.println("Test Scanning");
#endif  
  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), true);
  //pBLEScan->setInterval(100); // default 100
  //pBLEScan->setWindow(100);  // default 100, less or equal setInterval value
  eprintf("Start scan (%u sec).\n", scanTime);
  tik_scan = millis();
}
void LSleep(uint32_t ms) {
#if (!USE_LIGH_SLEEP)
  Serial.flush( true ); // wait for all data to be sent out UART
#else 
  for (int gpio_num = GPIO_NUM_0; gpio_num < GPIO_NUM_MAX; gpio_num++) {
    if (GPIO_IS_VALID_GPIO(gpio_num)) {
      if (gpio_num <= GPIO_NUM_5) {
        REG_CLR_BIT(RTC_CNTL_PAD_HOLD_REG, BIT(gpio_num));
      } else {
        CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PAD_HOLD_REG, GPIO_HOLD_MASK[gpio_num]);
      }
    }
  }
#endif  
  esp_sleep_enable_timer_wakeup (ms * 1000); 
  esp_light_sleep_start();
#if (USE_LIGH_SLEEP)  
  CLEAR_PERI_REG_MASK(USB_DEVICE_CONF0_REG, USB_DEVICE_USB_PAD_ENABLE);
#endif
}
void loop() {
  pBLEScan->setActiveScan(false);
  BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
  if(flag_delay) {
    flag_delay = false;
#if (USE_LIGH_SLEEP)
    if(delay_ms > 5)
      LSleep(delay_ms);
    else
#endif
    if(delay_ms > 0)
      delay(delay_ms);
  } else {
     eprintf("Timeout scan! Last scan %u ms.\n", millis() - tik_scan);
     stage = 0;
     scanTime = SCAN_TIME_DEF; // sec
  }
}
В принципе работает - вычисляет длительность advertising interval у передатчика и далее пытается окном принимать рекламные пакеты, а в остальное время спит (если нет пропусков и не нужна новая подстройка интервала). Спит и окном - чтобы меньше жрал.
Но ничего хорошего, как всегда c ESP, не вышло.
1641838562082.png
Вот так оно входит в light_sleep - что-то тама в черном ящике заряжается и не успевает упасть до обещанных Espressif в PDF значений в режиме light_sleep, как надо уже новую рекламку принимать...
Окно приема кушает примерно так:
1641838919359.png
Снижение частоты CPU с 160 на 80МГц ничего существенного не дает - среднее потребление в 15 мА (при приеме рекламы с интервалом 2.5 сек, как на диаграммах) падает не более чем на 1 мА.
На плате ESP32-C3-32S_Kit всё ещё хуже - при включении любого sleep врубается что-то в SERIAL_USB_JTAG и выставляет на ногу GPIO19 "1" и светит светодиодом :)

Что надо сделать, чтобы ток упал до обещанных Espressif для ESP3-C3?
Иначе это какой-то кошмар, т.к. любой другой чип имеет потребление при включенном BLE приемнике 5..6 мА, а если ещё держать прием окном - выйдет менее 1 мА (т.е. не менее чем в 15 раз меньше чем у ESP32-C3).
 

pvvx

Активный участник сообщества
К примеру приемник всех BLE реклам на TLSR825x с одновременно включенным BLE device выдающем advertising c интервалом 1 сек, работающем CPU на 24MHz без всяких sleep, вечно включенном приемнике RF:
1642094732658.png
потребляет всего в среднем 5.87 мА.
Мелкие пички вниз на графике - это прием и обработка принятых BLE реклам, большие - передача рекламы устройства...

Что такого наворотили в ESP32-C3, что он жрет за 80 мА при тех-же действиях?
 
Сверху Снизу