• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Нужна помощь Light Sleep Mode - стал срабатывать wdt ...

Slacky

Member
Контролирую внешнее питание по пину. Если пропало напряжение, вызываем вот такую функцию

Код:
void ICACHE_FLASH_ATTR sleepOnNow() {
    apModeNow=staModeNow=false;
    wifi_station_disconnect();
    wifi_set_opmode_current(NULL_MODE);
    wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); //light sleep mode
    gpio_pin_wakeup_enable(GPIO_ID_PIN(pin_num[HOT_PIN]),
            GPIO_PIN_INTR_LOLEVEL); /* Set the interrupt to look for LOW pulses on HOT_PIN  */
    gpio_pin_wakeup_enable(GPIO_ID_PIN(pin_num[COLD_PIN]),
            GPIO_PIN_INTR_LOLEVEL); /* Set the interrupt to look for LOW pulses on COLD_PIN */
    wifi_fpm_open();
    os_delay_us(50000);
    wifi_fpm_set_wakeup_cb(wakeupFromMotion); //wakeup callback
    wifi_fpm_do_sleep(0xFFFFFFF);
    os_delay_us(50000);
}
Пока не оброс кодом, все работало правильно. А сейчас, после вызова этой функции модуль перегружается по wdt ...

Есть идеи, куда копать?
 

Slacky

Member
1) уменьшите задержки раз в 100 вместо os_delay_us(50000); os_delay_us(500);
Действительно, совсем забыл. Этот код я перетащил из Ардуины, там было что-то типа
Код:
delay(100);
И тут было изначально
Код:
os_delay_us(100);
А потом я поменял на большее значение, так как это микросекунды. Но у меня отложилось в памяти, что вроде и так работало.

Еще раз спасибо. Буду пробовать ...
 

Slacky

Member
Не помогает.

Код:
void ICACHE_FLASH_ATTR sleepOnNow() {
    apModeNow(false);
    staModeNow(false);
    system_soft_wdt_stop();
    wifi_station_disconnect();
    wifi_set_opmode_current(NULL_MODE);
    wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); //light sleep mode
    gpio_pin_wakeup_enable(GPIO_ID_PIN(pin_num[HOT_PIN]),
            GPIO_PIN_INTR_LOLEVEL); /* Set the interrupt to look for LOW pulses on HOT_PIN  */
    gpio_pin_wakeup_enable(GPIO_ID_PIN(pin_num[COLD_PIN]),
            GPIO_PIN_INTR_LOLEVEL); /* Set the interrupt to look for LOW pulses on COLD_PIN */
    wifi_fpm_open();
    wifi_fpm_set_wakeup_cb(wakeupFromMotion); //wakeup callback
    wifi_fpm_do_sleep(0xFFFFFFF);
}
На выходе

Код:
Power low. Light sleep now ...
force slp enable,type: 1
pm close 7
fpm open,type:1 0
fpm 758

ets Jan  8 2013,rst cause:4, boot mode:(1,6)

wdt reset
 

Slacky

Member
Я попробую, но уже после праздников, уезжаю на выходные.

А вот облегченный код дал интересные результаты. Если убрать таймер и процедуру опроса двух пинов (buttonTimer и проч), то все работает. А вот так, если позамыкать пины на массу, заставив счетчик считать, то вот и срабатывает wdt, а если из кода убрать опрос пинов - то все нормально ...

Код:
#include <ets_sys.h>
#include <osapi.h>
#include <user_interface.h>
#include <os_type.h>
#include "driver/uart.h"
#include "driver/gpio16.h"
#include "user_config.h"
#include "wemos.h"

#define DELAY 1000 /* milliseconds */
#define HOT_PIN D1                                /* Pin of hot water                     */
#define COLD_PIN D2                               /* Pin of cold water                    */
#define EXT_POWER_PIN D0                          /* Pin of monitoring external power     */


LOCAL os_timer_t mainTimer, buttonTimer;

_waterCounter waterCounter;

LOCAL uint8_t ICACHE_FLASH_ATTR debounce(uint8 sample) {

  static uint8 state = 0xFF, cnt0, cnt1;
  uint8 delta;

  delta = sample ^ state;
  cnt1 = (cnt1 ^ cnt0) & delta;
  cnt0 = ~cnt0 & delta;
  state ^= (cnt0 & cnt1);

  return state;
}

LOCAL void ICACHE_FLASH_ATTR buttonTimer_cb(void *arg) {

    uint8 bitHot, bitCold, bitStatus, counter = 0;

    bitHot = gpio_read(HOT_PIN);
    bitCold = gpio_read(COLD_PIN) << 1;

    counter |= bitHot;
    counter |= bitCold;

    waterCounter.status = debounce(counter);

    if (waterCounter.status & 0x01) {
        if (!(waterCounter.status_old & 0x01)) {
            waterCounter.status_old |= 0x01;
            waterCounter.counterHotWater++;
            os_printf("hotCounter  = %d\r\n", waterCounter.counterHotWater);
        }
    } else {
        if (waterCounter.status_old & 0x01) {
            waterCounter.status_old &= ~0x01;
        }
    }

    if (waterCounter.status & 0x02) {
        if (!(waterCounter.status_old & 0x02)) {
            waterCounter.status_old |= 0x02;
            waterCounter.counterColdWater++;
            os_printf("coldCounter  = %d\r\n", waterCounter.counterColdWater);
        }
    } else {
        if (waterCounter.status_old & 0x02) {
            waterCounter.status_old &= ~0x02;
        }
    }
}



LOCAL void ICACHE_FLASH_ATTR wakeupFromMotion(void) {
    wifi_fpm_close();
    wifi_set_opmode(STATION_MODE);
    os_printf("Wake up from sleep.\r\n");
}

void ICACHE_FLASH_ATTR sleepOnNow() {
    wifi_station_disconnect();
    wifi_set_opmode(NULL_MODE);
    wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); //light sleep mode
    gpio_pin_wakeup_enable(GPIO_ID_PIN(pin_num[HOT_PIN]),
            GPIO_PIN_INTR_LOLEVEL); /* Set the interrupt to look for LOW pulses on HOT_PIN  */
    gpio_pin_wakeup_enable(GPIO_ID_PIN(pin_num[COLD_PIN]),
            GPIO_PIN_INTR_LOLEVEL); /* Set the interrupt to look for LOW pulses on COLD_PIN */
    wifi_fpm_open();
    wifi_fpm_set_wakeup_cb(wakeupFromMotion); //wakeup callback
    wifi_fpm_do_sleep(0xFFFFFFF);
}


LOCAL void ICACHE_FLASH_ATTR mainTimer_cb(void *arg)
{
    uint8 bit;

    bit = gpio_read(EXT_POWER_PIN);

    if (!bit) {
        os_printf("Power low. Light sleep now ...\r\n");
        os_delay_us(50000);
        sleepOnNow();
    }
}

/******************************************************************************
* FunctionName : user_rf_cal_sector_set
* Description  : SDK just reversed 4 sectors, used for rf init data and paramters.
*                We add this function to force users to set rf cal sector, since
*                we don't know which sector is free in user's application.
*                sector map for last several sectors : ABBBCDDD
*                A : rf cal
*                B : at parameters
*                C : rf init data
*                D : sdk parameters
* Parameters   : none
* Returns      : rf cal sector
*******************************************************************************/
uint32 ICACHE_FLASH_ATTR user_rf_cal_sector_set(void)
{
    enum flash_size_map size_map = system_get_flash_size_map();
    uint32 rf_cal_sec = 0;

    switch (size_map) {
        case FLASH_SIZE_4M_MAP_256_256:
            rf_cal_sec = 128 - 8;
            break;

        case FLASH_SIZE_8M_MAP_512_512:
            rf_cal_sec = 256 - 5;
            break;

        case FLASH_SIZE_16M_MAP_512_512:
        case FLASH_SIZE_16M_MAP_1024_1024:
            rf_cal_sec = 512 - 5;
            break;

        case FLASH_SIZE_32M_MAP_512_512:
        case FLASH_SIZE_32M_MAP_1024_1024:
            rf_cal_sec = 1024 - 5;
            break;

        default:
            rf_cal_sec = 0;
            break;
    }

    return rf_cal_sec;
}

void ICACHE_FLASH_ATTR user_rf_pre_init(void)
{
}

void ICACHE_FLASH_ATTR user_init(void)
{

    // Configure the UART
//    uart_init(BIT_RATE_115200, BIT_RATE_115200);
    uart_div_modify(0, UART_CLK_FREQ / 115200);

    if (set_gpio_mode(HOT_PIN, GPIO_INPUT, GPIO_PULLUP)) {
        os_printf("GPIO%d set input mode\r\n", pin_num[HOT_PIN]);
    }

    if (set_gpio_mode(COLD_PIN, GPIO_INPUT, GPIO_PULLUP)) {
        os_printf("GPIO%d set input mode\r\n", pin_num[COLD_PIN]);
    }

    if (set_gpio_mode(EXT_POWER_PIN, GPIO_INPUT, GPIO_PULLDOWN)) {
        os_printf("GPIO%d set input mode\r\n", pin_num[EXT_POWER_PIN]);
    }

    wifi_set_opmode(STATION_MODE);

    os_timer_disarm(&buttonTimer);
    os_timer_setfn(&buttonTimer, (os_timer_func_t *)buttonTimer_cb, (void *)0);
    os_timer_arm(&buttonTimer, 50, true);

    os_timer_disarm(&mainTimer);
    os_timer_setfn(&mainTimer, (os_timer_func_t *)mainTimer_cb, (void *)0);
    os_timer_arm(&mainTimer, DELAY, 1);
}
 

Slacky

Member
относительно колбека таймера поспешил.
Чтобы сказать определеннее надо поставить в колбеки принты и посмотреть результат.
Хм, ну я могу конечно поставить вывод некой инфы, но мне кажется это тут ни при чем ...
Не видно, где вы включили режим пинов на ввод.
В user_init
Код:
    if (set_gpio_mode(HOT_PIN, GPIO_INPUT, GPIO_PULLUP)) {
        os_printf("GPIO%d set input mode\r\n", pin_num[HOT_PIN]);
    }

    if (set_gpio_mode(COLD_PIN, GPIO_INPUT, GPIO_PULLUP)) {
        os_printf("GPIO%d set input mode\r\n", pin_num[COLD_PIN]);
    }
Ну и добавление в wakeupFromMotion(void)
Код:
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);
положительных результатов не дало ...
еще замечу что в этом режиме таймеры все останавливаются.
Не знаю логику вашей проги, но вы это учитываете.
Не уверен, что таймеры остаются считать, но можно проверить, наверно, с помощью статической переменной ...
 

Slacky

Member
и еще надо учитывать, что после выхода в активный режим время установления соединения WIFI составит порядка 3 секунд
а у вас таймер работает с интервалом 50 мс.
Так задумано? Не получится что ESP никогда не установит соединение?
Не, 50 мс - именно только на опрос пинов.

Заметил пока следующее - если сбросить ESP, не замыкать пины на землю, то после пропадания внешнего питания все прекрасно засыпает. В спящем режиме, если один (или оба) из пинов замкнуть на землю, то ESP просыпается, отрабатывает подсчет нажатия (замыкания пина на массу) и опять спокойно засыпает.

Но если после старта позамыкать пины, а потом отключить питание, тут и срабатывает wdt ...
 

Slacky

Member
Пока для анализа выяснил только следующее. Если срабатывает wdt, то в юарт успевает проскочить такая инфа
Код:
Power low. Light sleep now ...
force slp enable,type: 1
pm close 7
fpm open,type:1 0
fpm 758

ets Jan  8 2013,rst cause:4, boot mode:(1,7)

wdt reset
Если же засыпаем нормально, то инфа другая
Код:
Power low. Light sleep now ...
state: 5 -> 0 (0)
rm 0
del if0
usl
mode : null
force slp enable,type: 1
fpm open,type:1 0
 

Slacky

Member
А как Вы отключаете питание? И главное -зачем (т е нафига)?
У Вемоса есть battery шилд. Питание на него можно подать через микроUSB. Приходящие 5 вольт через резистор подаем на D0. Если питание по USB приходит, то считаем внешне питание есть. Если нет, значит нет.

Зачем - при пропадании внешнего питания, переходим на питание от батареи ну и засыпанием уменьшаем ток потребления, но при этом остается возможность контролировать замыкание пинов ...
 

Slacky

Member
ссылки не открываются.
Возможно я не понял Ваш алгоритм, либо Вы не выключаете питание.
Так как если выключить питание и потом включить то это будет состояние RST а не deep-sleep.
Странно, у меня даже с работы открылась ...
https://wiki.wemos.cc/_media/products:d1_mini_shields:sch_battery_v1.2.0.pdf
Ни про какой deep sleep речи не идет. Запитывается модуль через battery шилд, к которому подходит внешнее питание и АКБ. При пропадании внешнего питания с battery шилда, модуль продолжает запитываться от АКБ. И мы обсуждаем light sleep ...
 

Slacky

Member
теперь понял.
Вопросы:
1) зачем записывать этот сигнал.
О каком сигнале идет речь?
2) Возможно он вызывает прерывание.
3) Через D0 выводится сигнал на светодиод Вы это учитываете?
Ну дело в том, что та же логика, но написанная на Arduino - работает ...
 

Slacky

Member
Это вам так хочется думать.
Но если там работает а здесь нет, то там не то,что здесь.
------------------------------
Я вообще-то пишу на CИ и делал этот режим, проблем не было.
Но режим не очень хороший так как всегда включается при выходе WIFI и время соединения всегда большое.
по существу -----------------
1) Вы убрали задержки ?
os_delay_us(50000);
-----------------------------
Да
2) можете отключить WDT
Поставьте в user_init()
функцию:
ets_wdt_disable();
ее надо объявить
void ets_wdt_disable(void);
-----------------
Делал. Просто тупо все подвисает и на пины не реагирует. Секунд через 30 перегружается.
3) попробуйте убрать пока работу с D0 или переместите сигнал с него на другой пин
-----------------------------------
Убрал. Сделал засыпание по таймеру через 15 секунд. Все тоже самое.
4) поставьте запрет прерывания при обработке сигналов от пинов
Не помогло.
и сброс регистра статуса пинов после обработки и перед разрешением прерывания
А вот про сброс регистра можно поподробней?

ЗЫ. А может я куда-нибудь выложу этот пример, а Вы его у себя проверите? Там дел то на 5 минут :))
 

Slacky

Member
можете выложить здесь или в переписку,
но я сейчас экспериментирую с RTL и RDA не очень хотелось бы отвлекаться.
Поэтому обещать ничего не буду.
Ну вдруг найдете пару минут :))

Ну или кто-нибудь еще попробует ...

http://slacky.ru/upload/files/WemosD1miniSleep.rar

И про сброс регистров можно более подробно?

Спасибо.
 

Slacky

Member
Я посмотрел Вашу программу.
Могу сказать следующее:
1) Не надо два раза определять колбеки таймеров. Если Вы их останавливаете то для запуска снова загрузите интервал.
Я делаю иначе.
Я пускаю без автозагрузки и пускаю снова в конце колбека таймера либо там где это мне нужно.
таким образом всегда гарантируется исполнение кода в колбеке до момента очередного вызова этого колбека.
Ну я и так и так пробовал. Просто в многочисленных примерах происходит переопределение. Ну я на всякий случай сделал так же ...
2) У Вас логика не верная.
Поясняю. У Вас просыпание происходит по внешнему сигналу. верно? Значит и обработку этого сигнала Вы должны делать в колбеке просыпания.
На мой взгляд правильная. Просыпание происходит по низкому уровню. А опрос кнопок выполнен на срабатывание от низкого в высокому. Поэтому эти два события никак не связаны между собой.
кроме того, после просыпания происходит восстановление соединения.
Вы нигде не контролируете это событие.
Надо использовать колбек WIFI
wifi_handle_event_cb
Это ж заготовка. Просто разорвать соединение - восстановить. В реальной программе так и сделано ...
-------------------
3) WDT не надо рестартовать.
---------------------
Это осталось от проверки на выключение wdt, нужно просто удалить ...
Вот пока примерно все, что можно сказать про вашу прогу
Меня все-таки интересует реальный запуск на другом модуле, другими руками и головой :))
 

Slacky

Member
Что произойдет, если у Вас о на 1 сменится до Вашей проверки?
или вы удерживаете ноль скажем секунду?
WDT не засвистит?
Большие задержки при работе WIFI не разрешаются.
А в момент установления соединения Вы можете прозевать смену 0 на 1.
----------------------
Это достаточно медленный счетчик, т.е. переход от нуля к единице может продолжаться 1 секунду. Я проверял, ничего не теряется. К тому же, еще раз хочу напомнить, это всего-лишь заготовка для проверки засыпания и не более ...
 

Slacky

Member
уберите ets_intr_lock(); и unlock() и повторное назначение колбека таймера из buttonTimer
если это заготовка то сделайте одну кнопку и упростите ее проверку
----------------
ets_intr_lock и ets_intr_unlock, а также переопределение таймеров - это уже в процессе поиска срабатывания wdt, изначально там их не было ...
и еще у Вас нет установки пинов на ввод в user_init()
Вот это разве не оно?

Код:
    if (set_gpio_mode(HOT_PIN, GPIO_INPUT, GPIO_PULLUP)) {
        os_printf("GPIO%d set input mode\r\n", pin_num[HOT_PIN]);
    }

    if (set_gpio_mode(COLD_PIN, GPIO_INPUT, GPIO_PULLUP)) {
        os_printf("GPIO%d set input mode\r\n", pin_num[COLD_PIN]);
    }
 

Slacky

Member
Если решать Вашу задачу правильно и красиво, то надо делать так:
Когда esp просыпается по нулю, вы меняете сигнал просыпания на 1 и снова идете спать.
Когда проснетесь по единице меняете на ноль и справляете нужду с WIFI и идете спать.
Это уже частности, но дело в том, что до каллбека не доходит, все падает на моменте засыпания ...
 

Slacky

Member
----------------
это уберите.
У вас есть закоментировано PIN_FUNC_SELECT... для GPIO4 и GPIO5 перенесите их в user_init
и после них добавьте
GPIO_DIS_OUTPUT(GPIO_ID_PIN(4));
GPIO_DIS_OUTPUT(GPIO_ID_PIN(5));
А в чем разница? Честно, не копал, просто предполагаю, что в принципе это одно и то же ...
 
Сверху Снизу