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

Зависание и сброс по WDT при срабатывании прерывания от GPIO

x8973

Member
Доброго времени суток всем. Продолжаю ковыряться с esp8266, и вот понадобилось мне получить прерывание от двух пинов GPIO.
Делаю следующим образом:
C:
#define user_procTaskPrio 0
#define user_procTaskQueueLen 1
os_event_t user_procTaskQueue[user_procTaskQueueLen];

volatile int what;

static void ICACHE_FLASH_ATTR loop(os_event_t *event)
{
    static int level;
    os_printf("What: %d\n", what);
    level = !level;
    GPIO_OUTPUT_SET(GPIO_ID_PIN(2), ((level)? 1 : 0));

    os_delay_us(100000);

    system_os_post(user_procTaskPrio, 0, 0);
}

static void gpio_intr_handler(int *dummy)
{
    // clear gpio status. Say ESP8266EX SDK Programming Guide in  5.1.6. GPIO interrupt handler
    uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);

    // if the interrupt was by GPIO
    if (gpio_status & BIT(4))
    {
        // disable interrupt for GPIO
        gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_DISABLE);

        // Do something
        (*dummy)++;

        //clear interrupt status for GPIO
        GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(4));

        // Reactivate interrupts for GPIO
        gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_NEGEDGE);
    }
    if (gpio_status & BIT(5))
    {
        gpio_pin_intr_state_set(GPIO_ID_PIN(5), GPIO_PIN_INTR_DISABLE);

        // Do something
        (*dummy)--;

        GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(5));
        gpio_pin_intr_state_set(GPIO_ID_PIN(5), GPIO_PIN_INTR_NEGEDGE);
    }
}


void ICACHE_FLASH_ATTR user_init(void)
{
    system_timer_reinit();

    uart_init(115200, 115200);

    gpio_init();

    // Configure as GPIO
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);    // LED
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);    // Button
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);    // Wire

    // Set logic 1 for output
    GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 1);

    // Disable output
    GPIO_DIS_OUTPUT(GPIO_ID_PIN(4));
    GPIO_DIS_OUTPUT(GPIO_ID_PIN(5));

    // Enable pullup for inputs
    PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO4_U);
    PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO5_U);

    // Disable interrupts by GPIO
    ETS_GPIO_INTR_DISABLE();

    // Attach interrupt handle to gpio interrupts.
    // You can set a void pointer that will be passed to interrupt handler each interrupt
    ETS_GPIO_INTR_ATTACH(gpio_intr_handler, &what);

    // From include file
    //   Set the specified GPIO register to the specified value.
    //   This is a very general and powerful interface that is not
    //   expected to be used during normal operation.  It is intended
    //   mainly for debug, or for unusual requirements.
    //
    // All people repeat this mantra but I don't know what it means
    //
    gpio_register_set(GPIO_PIN_ADDR(4),
                      GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE)  |
                      GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) |
                      GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE));
    // clear gpio status. Say ESP8266EX SDK Programming Guide in  5.1.6. GPIO interrupt handler
    GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(4));
    // enable interrupt for his GPIO
    //     GPIO_PIN_INTR_... defined in gpio.h
    gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_NEGEDGE);

    gpio_register_set(GPIO_PIN_ADDR(5),
                      GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE)  |
                      GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) |
                      GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE));
    GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(5));
    gpio_pin_intr_state_set(GPIO_ID_PIN(5), GPIO_PIN_INTR_NEGEDGE);

    ETS_GPIO_INTR_ENABLE();

    system_os_task(loop, user_procTaskPrio, user_procTaskQueue, user_procTaskQueueLen);
    system_os_post(user_procTaskPrio, 0, 0);
}

В результате получаю непрерывный вывод в консоль числа 0, однако при попытке замкнуть указанные GPIO на землю контроллер зависает, а потом перезагружается по ватчдогу. Иногда успевает проскочить 1 или -1.
Читал, что нужно обработчик прерывания декларировать как ICACHE_RAM_ATTR, однако в Non-OS SDK такого дефайна нет, код не компилируется. К тому же, если я правильно помню, функции, не описанные как ICACHE_FLASH_ATTR, и так попадают в RAM.
Скажите, что я делаю не так? Может быть, имеет смысл вообще забить на прерывания, и просто опрашивать ноги в таймере?
 

nikolz

Well-known member
GPIO_PIN_INTR_NEGEDGE- прерывание сработает как на замыкание, так и на размыкание.
если есть дребезг то будет многократное срабатывание.
При входе в функцию прерывания надо запретить прерывания а при выходе-разрешить
 

x8973

Member
GPIO_PIN_INTR_NEGEDGE- прерывание сработает как на замыкание, так и на размыкание.
Судя по имени, это "прерывание по спадающему фронту". Сейчас SDK под рукой нет, но для прерывания "по любому изменению" там вроде как есть отдельный дефайн.
Пины, если что, подтянуты снаружи к питанию резисторами на 4,7к, плюс в коде включена внутренняя подтяжка вверх.
При входе в функцию прерывания надо запретить прерывания а при выходе-разрешить
А это разве не оно?
C:
    // if the interrupt was by GPIO
    if (gpio_status & BIT(4))
    {
        // disable interrupt for GPIO
        gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_DISABLE);

        ...

        // Reactivate interrupts for GPIO
        gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_NEGEDGE);
    }
Или надо глобально прерывания гасить, через ETS_GPIO_INTR_DISABLE()?
 

nikolz

Well-known member
Судя по имени, это "прерывание по спадающему фронту". Сейчас SDK под рукой нет, но для прерывания "по любому изменению" там вроде как есть отдельный дефайн.
Пины, если что, подтянуты снаружи к питанию резисторами на 4,7к, плюс в коде включена внутренняя подтяжка вверх.

А это разве не оно?
C:
    // if the interrupt was by GPIO
    if (gpio_status & BIT(4))
    {
        // disable interrupt for GPIO
        gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_DISABLE);

        ...

        // Reactivate interrupts for GPIO
        gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_NEGEDGE);
    }
Или надо глобально прерывания гасить, через ETS_GPIO_INTR_DISABLE()?
пардон, да это отрицательный фронт.
--------------------
Вы запрещаете после обнаружение Вашего бита.
Надежнее запрещать прерывания первым оператором в функции колбека ,а разрешение перед return
Надо запрещать не конкретный пин а все прерывание:
ETS_GPIO_INTR_DISABLE()
ETS_GPIO_INTR_ENABLE()
-----------------
этот оператор: gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_NEGEDGE);
определяет вид сигнала и его используют один раз в функции init..
------------------
В колбек функции вы
запрещаете прерывание
читаете status
обрабатываете последовательно все свои пины
очищаете статус
разрешаете прерывания
выходите
-------------------
попробуйте сделать простой тест для нажатия кнопки и вывод значения в терминал .
 

nikolz

Well-known member
uint32 gpio_status;
gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); //читаем статус
status GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); //очищаем статус
//обрабатываем последовательно все свои пины по if...
 

nikolz

Well-known member
из своего опыта замечу, что прерывание по фронту
лучше делать через прерывания по уровню
При этом в колбек функции каждый раз переворачиваем уровень.
Т е в функции колбека устанавливаем, при очередном входе, вид сигнала инверсный текущему.
--------------------
Кроме того в функции колбека запоминаем системное время, а при очередном входе вычисляем длительность
между фронтами. Это значение используем для защиты от дребезга или для управление различной длительностью нажатия кнопки.
Например, короткие импульсы (дребезг, помехи) пропускаем.
 

pvvx

Активный участник сообщества
GPIO_PIN_INTR_NEGEDGE- прерывание сработает как на замыкание, так и на размыкание.
если есть дребезг то будет многократное срабатывание.
При входе в функцию прерывания надо запретить прерывания а при выходе-разрешить
Какая глупость. Походу вы впервые сталкиваетесь с ESP8266 и его прерываниями по GPIO.
В соседней теме есть всё...
 

pvvx

Активный участник сообщества
из своего опыта замечу, что прерывание по фронту
лучше делать через прерывания по уровню
Это по какому "опыту"? В LUA что-ли? Более от вас никто не видел никакого "опыту".
И не пудрите всем мозг неверными советами, раз не разбирались как работают прерывания от GPIO...
 

pvvx

Активный участник сообщества
Надежнее запрещать прерывания первым оператором в функции колбека ,а разрешение перед return
Надо запрещать не конкретный пин а все прерывание:
ETS_GPIO_INTR_DISABLE()
ETS_GPIO_INTR_ENABLE()
А кто по вашему запретит повторное прерывание пока CPU сохраняет регистры в процедуре старта прерывания (и она толще чем весь код прерывания)? Стек тогда всё равно переполнится.
А включены или нет повторные прерывания в контроллере ESP8266?
Как работает котроллер GPIO с режимами бит прерываний?
Ищите и обрящите литературу по данному поводу у вашего любимого Espressif. :) :p
 

x8973

Member
Я выпилил из программы все лишнее, оставил необходимый минимум, чтобы контроллер вообще стартовал. Получилось вот так:
C:
#include "ets_sys.h"
#include "gpio.h"
#include "user_interface.h"

#define SPI_FLASH_SIZE_MAP 4
#define SYSTEM_PARTITION_OTA_SIZE                            0x6A000
#define SYSTEM_PARTITION_OTA_2_ADDR                            0x81000
#define SYSTEM_PARTITION_RF_CAL_ADDR                        0x3fb000
#define SYSTEM_PARTITION_PHY_DATA_ADDR                        0x3fc000
#define SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR                0x3fd000

static const partition_item_t at_partition_table[] = {
    { SYSTEM_PARTITION_BOOTLOADER,                         0x0,                                                 0x1000},
    { SYSTEM_PARTITION_OTA_1,                           0x1000,                                             SYSTEM_PARTITION_OTA_SIZE},
    { SYSTEM_PARTITION_OTA_2,                           SYSTEM_PARTITION_OTA_2_ADDR,                         SYSTEM_PARTITION_OTA_SIZE},
    { SYSTEM_PARTITION_RF_CAL,                          SYSTEM_PARTITION_RF_CAL_ADDR,                         0x1000},
    { SYSTEM_PARTITION_PHY_DATA,                         SYSTEM_PARTITION_PHY_DATA_ADDR,                     0x1000},
    { SYSTEM_PARTITION_SYSTEM_PARAMETER,                 SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR,             0x3000},
};

void ICACHE_FLASH_ATTR user_pre_init(void)
{
    if(!system_partition_table_regist(at_partition_table,
                                      sizeof(at_partition_table) / sizeof(at_partition_table[0]),
                                      SPI_FLASH_SIZE_MAP))
    {
        while(1);
    }
}

#define GPIO_OUT_W1TS (*(volatile uint32_t *)0x60000304)
#define GPIO_OUT_W1TC (*(volatile uint32_t *)0x60000308)

static void gpio_intr_handler(int *dummy)
{
    (void)dummy;

    uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
    GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);

    if (gpio_status & BIT(4))
    {
        GPIO_OUT_W1TS = (1 << 2);
    }
    if (gpio_status & BIT(5))
    {
        GPIO_OUT_W1TC = (1 << 2);
    }
}

void ICACHE_FLASH_ATTR user_init(void)
{
    gpio_init();
    // Configure as GPIO
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);    // LED
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);    // Button
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);    // Wire

    gpio_output_set(0, 0, (1 << 2), (1 << 4) | (1 << 5));

    // Disable interrupts by GPIO
    ETS_GPIO_INTR_DISABLE();

    gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_NEGEDGE);
    gpio_pin_intr_state_set(GPIO_ID_PIN(5), GPIO_PIN_INTR_NEGEDGE);

    ETS_GPIO_INTR_ATTACH(gpio_intr_handler, NULL);

    ETS_GPIO_INTR_ENABLE();
}

Остальное максимально привел к такому же как у вас виду, кроме разве что таймера. Макросы ETS_GPIO_... и GPIO_REG_... разыменовываются в те же конструкции, что и у вас.
Результат тот же. После запуска светодиод загорается, замыкание любого входа на землю приводит к зависанию и перезагрузке.
 

pvvx

Активный участник сообщества
1613604618118.png
C++:
#include "ets_sys.h"
#include "gpio.h"
#include "user_interface.h"

#define GPIO_OUT_W1TS (*(volatile uint32_t *)0x60000304)
#define GPIO_OUT_W1TC (*(volatile uint32_t *)0x60000308)

void gpio_intr_handler(int *dummy)
{
  (void)dummy;

  uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
  GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);

  if (gpio_status & BIT(4))
  {
    GPIO_OUT_W1TS = (1 << 2);
  }
  if (gpio_status & BIT(5))
  {
    GPIO_OUT_W1TC = (1 << 2);
  }
}


void setup() {
  gpio_init();
  // Configure as GPIO
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);    // LED
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);    // Button
  PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);    // Wire

  gpio_output_set(0, 0, (1 << 2), (1 << 4) | (1 << 5));

  // Disable interrupts by GPIO
  ETS_GPIO_INTR_DISABLE();

  gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_NEGEDGE);
  gpio_pin_intr_state_set(GPIO_ID_PIN(5), GPIO_PIN_INTR_NEGEDGE);

  ETS_GPIO_INTR_ATTACH(gpio_intr_handler, NULL);

  ETS_GPIO_INTR_ENABLE();
}

void loop() {
  // put your main code here, to run repeatedly:

}
 

pvvx

Активный участник сообщества
Я выпилил из программы все лишнее, оставил необходимый минимум, чтобы контроллер вообще стартовал. Получилось вот так:
.......
Результат тот же. После запуска светодиод загорается, замыкание любого входа на землю приводит к зависанию и перезагрузке.
Значит дело не в бобине... а в вашем отсутствующем коде.
 

pvvx

Активный участник сообщества
И наблюдаем постоянные пропуски, в ничего не делающей программе:
1613606529917.png
Espressif ! :)
А что будет когда будет включен WiFi.... Лучше не смотреть... ESP8266 = Какашка
 

x8973

Member
Значит дело не в бобине... а в вашем отсутствующем коде.
Я привел полный текст файла main.c, даже из makefile выпилил все исходники, кроме него. Это все, что есть.
Если речь идет о функции user_pre_init(), то без нее программа не линкуется, а если ее оставить пустой, то контроллер уходит в циклический перезапуск при старте.
Может быть, у меня SDK устарел? Я использую 3.0.1. Вы, я так понял, работаете в Ардуино без использования ее библиотек. Не могли бы вы проверить мой код на чистом SDK? Подозреваю, результат будет как у меня.
 

nikolz

Well-known member
pvvx,
Найдите хотя бы два отличия того, что Вы написали от того что я сказал.
Сделайте как сказал и все будет работать.
Если Вы не заметили , то приведенные мною функции для СИ, а не для LUA.
Ах, да,Вы же луа не знаете... За пять лет могли бы выучить,
 

nikolz

Well-known member
Я выпилил из программы все лишнее, оставил необходимый минимум, чтобы контроллер вообще стартовал. Получилось вот так:
C:
#include "ets_sys.h"
#include "gpio.h"
#include "user_interface.h"

#define SPI_FLASH_SIZE_MAP 4
#define SYSTEM_PARTITION_OTA_SIZE                            0x6A000
#define SYSTEM_PARTITION_OTA_2_ADDR                            0x81000
#define SYSTEM_PARTITION_RF_CAL_ADDR                        0x3fb000
#define SYSTEM_PARTITION_PHY_DATA_ADDR                        0x3fc000
#define SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR                0x3fd000

static const partition_item_t at_partition_table[] = {
    { SYSTEM_PARTITION_BOOTLOADER,                         0x0,                                                 0x1000},
    { SYSTEM_PARTITION_OTA_1,                           0x1000,                                             SYSTEM_PARTITION_OTA_SIZE},
    { SYSTEM_PARTITION_OTA_2,                           SYSTEM_PARTITION_OTA_2_ADDR,                         SYSTEM_PARTITION_OTA_SIZE},
    { SYSTEM_PARTITION_RF_CAL,                          SYSTEM_PARTITION_RF_CAL_ADDR,                         0x1000},
    { SYSTEM_PARTITION_PHY_DATA,                         SYSTEM_PARTITION_PHY_DATA_ADDR,                     0x1000},
    { SYSTEM_PARTITION_SYSTEM_PARAMETER,                 SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR,             0x3000},
};

void ICACHE_FLASH_ATTR user_pre_init(void)
{
    if(!system_partition_table_regist(at_partition_table,
                                      sizeof(at_partition_table) / sizeof(at_partition_table[0]),
                                      SPI_FLASH_SIZE_MAP))
    {
        while(1);
    }
}

#define GPIO_OUT_W1TS (*(volatile uint32_t *)0x60000304)
#define GPIO_OUT_W1TC (*(volatile uint32_t *)0x60000308)

static void gpio_intr_handler(int *dummy)
{
    (void)dummy;

    uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
    GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);

    if (gpio_status & BIT(4))
    {
        GPIO_OUT_W1TS = (1 << 2);
    }
    if (gpio_status & BIT(5))
    {
        GPIO_OUT_W1TC = (1 << 2);
    }
}

void ICACHE_FLASH_ATTR user_init(void)
{
    gpio_init();
    // Configure as GPIO
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);    // LED
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);    // Button
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);    // Wire

    gpio_output_set(0, 0, (1 << 2), (1 << 4) | (1 << 5));

    // Disable interrupts by GPIO
    ETS_GPIO_INTR_DISABLE();

    gpio_pin_intr_state_set(GPIO_ID_PIN(4), GPIO_PIN_INTR_NEGEDGE);
    gpio_pin_intr_state_set(GPIO_ID_PIN(5), GPIO_PIN_INTR_NEGEDGE);

    ETS_GPIO_INTR_ATTACH(gpio_intr_handler, NULL);

    ETS_GPIO_INTR_ENABLE();
}

Остальное максимально привел к такому же как у вас виду, кроме разве что таймера. Макросы ETS_GPIO_... и GPIO_REG_... разыменовываются в те же конструкции, что и у вас.
Результат тот же. После запуска светодиод загорается, замыкание любого входа на землю приводит к зависанию и перезагрузке.
У Вас все виснет потому что в ней ошибка: нет бесконечного цикла.
т е Ваша программа работает лишь один раз по нажатию кнопки и потом кирдык до срабатывания WDT, который делает RESET.
 

x8973

Member
У Вас все виснет потому что в ней ошибка: нет бесконечного цикла или таймера.
т е Ваша программа работает лишь один раз по нажатию кнопки и потом кирдык до срабатывания WDT, который делает RESET.
В таком случае оно бы резетилось постоянно и без прерывания. Однако пока не ткнешь пинцетом в ногу, оно стоит себе спокойно, светит светодиодом. Перезагрузку я определяю по тому, что светодиод на короткое время гаснет.
Ватчдог в нормальном режиме работы сбрасывается где-то в глубинах приложения, пользователю не надо его постоянно дергать. По крайней мере, у меня прекрасно работают устройства, в которых нет ни одного бесконечного цикла, а таймеры имеют периоды по минуте (учитывая, что wdt timeout по умолчанию - 3 секунды).
 

nikolz

Well-known member
В таком случае оно бы резетилось постоянно и без прерывания. Однако пока не ткнешь пинцетом в ногу, оно стоит себе спокойно, светит светодиодом. Перезагрузку я определяю по тому, что светодиод на короткое время гаснет.
Ватчдог в нормальном режиме работы сбрасывается где-то в глубинах приложения, пользователю не надо его постоянно дергать. По крайней мере, у меня прекрасно работают устройства, в которых нет ни одного бесконечного цикла, а таймеры имеют периоды по минуте (учитывая, что wdt timeout по умолчанию - 3 секунды).
таймер и создает этот бесконечный цикл.
поставьте таймер в этом коде.
 

pvvx

Активный участник сообщества
pvvx,
Найдите хотя бы два отличия того, что Вы написали от того что я сказал.
Сделайте как сказал и все будет работать.
Если Вы не заметили , то приведенные мною функции для СИ, а не для LUA.
Ах, да,Вы же луа не знаете... За пять лет могли бы выучить,
Вы забыли - почаще гуляйте - говорят может помочь от деменции :) А счас брысь на рабБоту на папу Карло - иначе опоздаете :)
Исправленный мной вариант LUA для ESP8266 лежит в моем репозитории с 2015 года, когда ModeMCU "несмогла" слепить нормального... :p

Я привел полный текст файла main.c, даже из makefile выпилил все исходники, кроме него. Это все, что есть.
А я его скопировал и залил, но компилятором в Arduino.
Если речь идет о функции user_pre_init(), то без нее программа не линкуется, а если ее оставить пустой, то контроллер уходит в циклический перезапуск при старте.
Оно тут не причем.
Может быть, у меня SDK устарел? Я использую 3.0.1. Вы, я так понял, работаете в Ардуино без использования ее библиотек. Не могли бы вы проверить мой код на чистом SDK? Подозреваю, результат будет как у меня.
Я не работаю в Arduino, и вообще с ESP8266 уже несколько лет. Специально для вас достал это чудо и показал, что пример работает и на этом всё.
Вы просите ещё поставить какой-то давно ненужный мне SDK? Я думаю, что это будет платно для вас, но оплата не мне... Перечислите на счет форума что-нить - тогда подумаю. :)
 
Сверху Снизу