Немного о программировании ESP8266 на C под FreeRTOS

в 19:50, , рубрики: C, DIY, diy или сделай сам, программирование микроконтроллеров

Тут должна быть КДПВ, но на нее не хватило бюджета.

Замотивировавшись ответом от Tarson на мой комментарий к статье https://geektimes.ru/post/292801/ решил написать про основы программирования ESP8266 на C под FreeRTOS. Подробности под катом.

Шаг 0 — устройство
Для начала надо обзавестись устройством c ESP8266, желательно, чтобы там был разведен USB to UART, чтобы не пришлось городить программатор. Я свои бесчеловечные опыты провожу на NodeMCU.

Итак, шаг 1 — собираем тулчейн.
Для начала надо обзавестись компьютером с установленным на нем дистрибутивом Linux (у меня OpenSUSE Leap). Идем на гитхаб по ссылке тыц, читаем инструкцию по сборке, устанавливаем необходимые зависимости, клонируем репозиторий, и собираем. Я клонировал в /opt/ESP и перед сборкой правил Makefile, выставив переменные:

STANDALONE = n
VENDOR_SDK = 2.1.0

Далее можно в ~/.bashrc добавить в PATH путь к бинарникам тулчейна:

export PATH=/opt/ESP/esp-open-sdk/xtensa-lx106-elf/bin:$PATH

Шаг 2 — получаем SDK
Идем на гитхаб (тыньк), читаем инструкции, клонируем (например в /opt/ESP). Далее задаем любимым способом (например через ~/.bashrc) переменную окружения ESP8266_SDK_PATH:

export ESP8266_SDK_PATH=/opt/ESP/esp-open-rtos

Шаг 3 — создаем проект
Заходим в директорию examples в директории с SDK и копируем любой понравившийся пример. Импортируем/открываем проект в любимой среде разработки, мазохисты могут использовать текстовый редактор. Я предпочитаю NetBeans — у него неплохая поддержка C/C++ проектов, в том числе на основе Makefile. Собирается проект с помощью make, прошивается с помощью make flash. В файле local.mk можно настроить параметры для прошивки своего устройства (размер и режим обращения к флеш памяти, например).

Шаг 4 — программируем
Проводим анализ требований, предметной области, составляем ТЗ согласно ГОСТ 34.602-89, после чего можно начинать писать код. :) Светодиодами мигать не буду, т. к. их у меня нет, поэтому в качестве HelloWorld будет чтение данных с датчика AM2302 (он же DHT22) и отправка их по протоколу MQTT на сервер.

Для того, чтобы использовать дополнительные модули, например MQTT или DHT, их необходимо добавить в Makefile:

PROGRAM=fffmeteo
EXTRA_COMPONENTS = extras/paho_mqtt_c extras/dht
include $(ESP8266_SDK_PATH)/common.mk
main.h
#ifndef MAIN_H
#define MAIN_H

#include <stdio.h>
#include <stdint.h>
#include <limits.h>

#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
#include <semphr.h>

#define DEBUG

#ifdef DEBUG
#define debug(args...) printf("--- "); printf(args)
#define SNTP_DEBUG_ENABLED true
#else
#define debug(args...)
#define SNTP_DEBUG_ENABLED false
#endif

#define WIFI_SSID "kosmonaFFFt"
#define WIFI_PASS "mysupermegapassword"

#define MQTT_HOST "m11.cloudmqtt.com"
#define MQTT_PORT 16464
#define MQTT_USER "kosmonaFFFt"
#define MQTT_PASS "mysupermegapassword"
#define MQTT_TOPIC "/meteo"

#define NTP_SERVER "pool.ntp.org"

#define UART0_BAUD 9600

#define STACK_SIZE 512
#define INIT_TASK_PRIORITY (configTIMER_TASK_PRIORITY + 1)
#define MEASUREMENT_TASK_PRIORITY (INIT_TASK_PRIORITY + 1)
#define SENDING_DATA_TASK_PRIORITY (MEASUREMENT_TASK_PRIORITY + 1)

#define MEASUREMENTS_PERIOD_S 59
#define MAX_MEASUREMENTS_COUNT 16

#define SEND_PERIOD_S 120
#define RUN_SNTP_SYNC_PERIOD 5

#define MS(x) (x / portTICK_PERIOD_MS)

#define AM2302_PIN 5

#ifdef __cplusplus
extern "C"
{
#endif

#ifdef __cplusplus
}
#endif

#endif /* MAIN_H */

main.c

#include "main.h"
#include "sntp.h"

#include <esp/uart.h>

#include <espressif/esp_common.h>

#include <paho_mqtt_c/MQTTESP8266.h>
#include <paho_mqtt_c/MQTTClient.h>

#include <dht/dht.h>

//-----------------------------------------------------------------------------+
//                           Measurements task section.                        |
//-----------------------------------------------------------------------------+

struct measurement_results
{
    time_t timestamp;
    int am2302_humidity;
    int am2302_temperature;
};

static QueueHandle_t measurements_queue;

void measurement_task(void *arg)
{
    int16_t humidity;
    int16_t temperature;

    struct measurement_results measurements;

    while (true)
    {
        debug("MEASUREMENTS: Start measurements...n");
        measurements.timestamp = time(NULL);

        bool success = dht_read_data(DHT_TYPE_DHT22, AM2302_PIN, &humidity, &temperature);
        if (success && temperature >= -500 && temperature <= 1500 && humidity >= 0 && humidity <= 1000)
        {
            measurements.am2302_humidity = humidity;
            measurements.am2302_temperature = temperature;
        }
        else
        {
            debug("MEASUREMENT: Error! Cannot read data from AM2302!!!n");
            measurements.am2302_humidity = INT_MIN;
            measurements.am2302_temperature = INT_MIN;
        }

        debug("MEASUREMENTS: Measurements finished...n");

        xQueueSendToBack(measurements_queue, &measurements, MS(250));
        vTaskDelay(MS(MEASUREMENTS_PERIOD_S * 1000));
    }

    vTaskDelete(NULL);
}

//-----------------------------------------------------------------------------+
//                           Sending data task section.                        |
//-----------------------------------------------------------------------------+

static uint8_t mqtt_buf[512];
static uint8_t mqtt_readbuf[128];

void sending_data_task(void *arg)
{
    mqtt_network_t network;
    mqtt_network_new(&network);

    mqtt_client_t client = mqtt_client_default;
    mqtt_packet_connect_data_t data = mqtt_packet_connect_data_initializer;

    uint8_t sntp_sync_counter = 0;
    while (true)
    {
        debug("MQTT: ConnectNetwork...n");
        int err = mqtt_network_connect(&network, MQTT_HOST, MQTT_PORT);
        if (err)
        {
            debug("MQTT: Error!!! ConnectNetwork ERROR!n");
            vTaskDelay(MS(5 * 1000));
            continue;
        }
        else
        {
            debug("MQTT: ConnectNetwork success...n");
        }

        // TODO: add check for errors!!!
        // TODO: replace magic constants!!!
        mqtt_client_new(&client, &network, 5000, mqtt_buf, 100, mqtt_readbuf, 100);

        data.willFlag = 0;
        data.MQTTVersion = 3;
        data.clientID.cstring = "fff";
        data.username.cstring = MQTT_USER;
        data.password.cstring = MQTT_PASS;
        data.keepAliveInterval = 10;
        data.cleansession = 0;

        err = mqtt_connect(&client, &data);
        if (err)
        {
            debug("MQTT: Error!!! MQTTConnect ERROR!n");
            vTaskDelay(MS(5 * 1000));
            continue;
        }
        else
        {
            debug("MQTT: MQTTConnect success...n");
        }

        struct measurement_results msg;
        while (xQueueReceive(measurements_queue, &msg, 0) == pdTRUE)
        {
            if (msg.am2302_humidity == INT_MIN || msg.am2302_temperature == INT_MIN)
            {
                debug("MQTT: Got invalid message, no publishing!!!n");
                continue;
            }

            debug("MQTT: Got message to publish...n");
            debug("      timestamp: %ldn", msg.timestamp);
            debug("      am2302_humidity: %.1fn", msg.am2302_humidity / 10.0);
            debug("      am2302_temperature: %.1fn", msg.am2302_temperature / 10.0);

            msg.timestamp = htonl(msg.timestamp);
            msg.am2302_humidity = htonl(msg.am2302_humidity);
            msg.am2302_temperature = htonl(msg.am2302_temperature);

            mqtt_message_t message;
            message.payload = &msg;
            message.payloadlen = sizeof (msg);
            message.dup = 0;
            message.qos = MQTT_QOS1;
            message.retained = 0;

            err = mqtt_publish(&client, MQTT_TOPIC, &message);
            if (err)
            {
                debug("MQTT: Error!!! Error while publishing message!n");
            }
            else
            {
                debug("MQTT: Successfully publish message...n");
            }
        }

        mqtt_disconnect(&client);
        mqtt_network_disconnect(&network);

        ++sntp_sync_counter;
        if (sntp_sync_counter == RUN_SNTP_SYNC_PERIOD)
        {
            sntp_sync(NTP_SERVER, NULL, arg);
            sntp_sync_counter = 0;
        }

        vTaskDelay(MS(SEND_PERIOD_S * 1000));
    }

    vTaskDelete(NULL);
}

//-----------------------------------------------------------------------------+
//                           Init task section.                                |
//-----------------------------------------------------------------------------+

/**
 * This semaphore is taken during sntp sync and released after it finished.
 */
static SemaphoreHandle_t init_task_sem;

/**
 * Set time and free init task semaphore.
 * @param error unused
 * @param arg unused
 */
void init_sntp_callback(int8_t error, void* arg)
{
    time_t ts = time(NULL);
    debug("TIME: %s", ctime(&ts));
    xSemaphoreGive(init_task_sem);
}

/**
 * Connection parameters.
 */
static struct sdk_station_config STATION_CONFIG = {
    .ssid = WIFI_SSID,
    .password = WIFI_PASS,
};

void init_task(void* arg)
{
    debug("INIT: setting pins...n");
    gpio_set_pullup(AM2302_PIN, false, false);

    debug("INIT: Set station parameters...n");
    sdk_wifi_station_set_auto_connect(false);
    sdk_wifi_station_set_config(&STATION_CONFIG);
    debug("Station parameters has been set.n");

    debug("INIT: Connecting to AP...n");
    sdk_wifi_station_connect();
    while (sdk_wifi_station_get_connect_status() != STATION_GOT_IP)
    {
        vTaskDelay(MS(1000));
    }
    debug("INIT: Connection to AP has been estabilished...n");

    debug("INIT: Start SNTP synchronization...n");

    init_task_sem = xSemaphoreCreateMutex();
    if (!init_task_sem)
    {
        debug("INIT: Cannot create init task semaphore!!!");
        return;
    }

    xSemaphoreTake(init_task_sem, 0);

    sntp_init();
    sntp_sync(NTP_SERVER, init_sntp_callback, arg);

    BaseType_t result = pdFALSE;
    while (true)
    {
        debug("INIT: Trying to take init task semaphore...n");
        result = xSemaphoreTake(init_task_sem, MS(5 * 1000));
        if (result == pdTRUE)
        {
            debug("INIT: Init task semaphore is taken...n");
            break;
        }
    }

    measurements_queue = xQueueCreate(MAX_MEASUREMENTS_COUNT, sizeof (struct measurement_results));
    if (!measurements_queue)
    {
        debug("INIT: ERROR!!! Cannot create queue for measurements!n");
        goto fail;
    }

    result = xTaskCreate(measurement_task, "measurement_task", STACK_SIZE, NULL, MEASUREMENT_TASK_PRIORITY, NULL);
    if (result == pdFAIL)
    {
        debug("INIT: Measurement task creation failed!!!n");
        goto fail;
    }
    debug("INIT: Measurement task created...n");

    result = xTaskCreate(sending_data_task, "send_data_task", STACK_SIZE, NULL, SENDING_DATA_TASK_PRIORITY, NULL);
    if (result == pdFAIL)
    {
        debug("INIT: Send task creation failed!!!n");
        goto fail;
    }
    debug("INIT: Send data task created...n");

fail:
    vSemaphoreDelete(init_task_sem);
    vTaskDelete(NULL);
}

//-----------------------------------------------------------------------------+
//                           Application entry point.                          |
//-----------------------------------------------------------------------------+

void user_init(void)
{
    debug("USER_INIT: SDK version: %sn", sdk_system_get_sdk_version());
    debug("USER_INIT: sizeof (int): %dn", sizeof (int));
    debug("USER_INIT: sizeof (float): %dn", sizeof (float));
    debug("USER_INIT: sizeof (time_t): %dn", sizeof (time_t));
    uart_set_baud(0, UART0_BAUD);

    BaseType_t result = xTaskCreate(init_task, (const char * const) "init_task", STACK_SIZE, NULL, INIT_TASK_PRIORITY, NULL);
    if (!result)
    {
        debug("USER_INIT: Cannot create init task!!!");
        return;
    }
}

sntp.h

#ifndef SNTP_H
#define SNTP_H

#include <time.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C"
{
#endif

#define SNTP_ERR_OK 0
#define SNTP_ERR_CONTEXT -1
#define SNTP_ERR_DNS -2
#define SNTP_ERR_UDP_PCB_ALLOC -3
#define SNTP_ERR_PBUF_ALLOC -4
#define SNTP_ERR_SEND -5
#define SNTP_ERR_RECV_ADDR_PORT -6;
#define SNTP_ERR_RECV_SIZE -7
#define SNTP_ERR_RECV_MODE -8
#define SNTP_ERR_RECV_STRATUM -9

typedef void (*sntp_sync_callback)(int8_t error, void *arg);

void sntp_init();
void sntp_sync(char *server, sntp_sync_callback callback, void *callback_arg);

time_t sntp_get_rtc_time(int32_t *us);
void sntp_update_rtc(time_t t, uint32_t us);

#ifdef __cplusplus
}
#endif

#endif /* SNTP_H */

sntp.c

#include "main.h"
#include "sntp.h"

#include <time.h>
#include <string.h>

#include <lwip/ip_addr.h>
#include <lwip/err.h>
#include <lwip/dns.h>
#include <lwip/udp.h>

#include <esp/rtc_regs.h>

#include <espressif/esp_common.h>

#define TIMER_COUNT RTC.COUNTER

/**
 * Daylight settings.
 * Base calculated with value obtained from NTP server (64 bits).
 */
#define SNTP_BASE (*((uint64_t*) RTC.SCRATCH))

/**
 * Timer value when base was obtained.
 */
#define SNTP_TIME_REF (RTC.SCRATCH[2])

/**
 * Calibration value.
 */
#define SNTP_CALIBRATION (RTC.SCRATCH[3])

/**
 * SNTP modes.
 */
#define SNTP_MODE_CLIENT 0x03
#define SNTP_MODE_SERVER 0x04
#define SNTP_MODE_BROADCAST 0x05

/**
 * Kiss-of-death code.
 */
#define SNTP_STRATUM_KOD 0x00

#define SNTP_OFFSET_LI_VN_MODE 0
#define SNTP_OFFSET_STRATUM 1
#define SNTP_OFFSET_RECEIVE_TIME 32

#define DIFF_SEC_1900_1970 (2208988800UL)

struct sntp_message
{
    uint8_t li_vn_mode;
    uint8_t stratum;
    uint8_t poll;
    uint8_t precision;
    uint32_t root_delay;
    uint32_t root_dispersion;
    uint32_t reference_identifier;
    uint32_t reference_timestamp[2];
    uint32_t originate_timestamp[2];
    uint32_t receive_timestamp[2];
    uint32_t transmit_timestamp[2];
} __attribute__ ((packed));

struct sntp_sync_context
{
    ip_addr_t ip_address;
    sntp_sync_callback callback;
    void* callback_arg;
};

void sntp_init()
{
    SNTP_BASE = 0;
    SNTP_CALIBRATION = 1;
    SNTP_TIME_REF = TIMER_COUNT;
}

void on_dns_found(const char* name, ip_addr_t* ipaddr, void* arg);
void on_udp_recv(void* arg, struct udp_pcb* pcb, struct pbuf* p, ip_addr_t* addr, u16_t port);

void sntp_sync(char* server, sntp_sync_callback callback, void* callback_arg)
{
    int result = ERR_OK;

    debug("SNTP: Start SNTP synchronization, allocating memory for context...n");
    struct sntp_sync_context* context = malloc(sizeof (struct sntp_sync_context));
    if (!context)
    {
        debug("SNTP: Error!!! Cannot allocate memory for context!n");
        result = SNTP_ERR_CONTEXT;
        goto fail;
    }
    context->callback = callback;
    context->callback_arg = callback_arg;
    debug("SNTP: Context successfully allocated...n");

    debug("SNTP: Start DNS lookup...n");
    err_t err = dns_gethostbyname(server, &(context->ip_address), on_dns_found, context);
    if (!(err == ERR_OK || err == ERR_INPROGRESS))
    {
        debug("SNTP: Error!!! DNS lookup error!n");
        result = SNTP_ERR_DNS;
        goto fail;
    }
    return;

fail:
    if (context)
    {
        free(context);
    }

    if (callback)
    {
        callback(result, callback_arg);
    }
}

//
//==============================================================================================================================================================
//

void on_dns_found(const char* name, ip_addr_t* ipaddr, void* arg)
{
    debug("SNTP: Start DNS lookup successfully finished...n");

    int result = ERR_OK;
    struct sntp_sync_context* context = arg;
    sntp_sync_callback callback = context->callback;
    void* callback_arg = context->callback_arg;

    debug("SNTP: Creating upd_pcb...n");
    struct udp_pcb* sntp_pcb = udp_new();
    if (!sntp_pcb)
    {
        debug("SNTP: Error!!! Cannot allocate udp_pcb!n");
        result = SNTP_ERR_UDP_PCB_ALLOC;
        goto fail;
    }
    debug("SNTP: Successfully created upd_pcb...n");

    debug("SNTP: Allocating pbuf...n");
    struct pbuf* p = pbuf_alloc(PBUF_TRANSPORT, sizeof (struct sntp_message), PBUF_RAM);
    if (!p)
    {
        debug("SNTP: Error!!! DNS lookup error!n");
        result = SNTP_ERR_PBUF_ALLOC;
        goto fail;
    }
    struct sntp_message* message = p->payload;
    memset(message, 0, sizeof (struct sntp_message));
    message->li_vn_mode = 0b00100011; // li = 00, vn = 4, mode = 3
    debug("SNTP: Pbuf allocated successfully...n");

    debug("SNTP: Sending data to server...n");
    udp_recv(sntp_pcb, on_udp_recv, context);
    err_t err = udp_sendto(sntp_pcb, p, ipaddr, 123);
    pbuf_free(p);
    if (err != ERR_OK)
    {
        debug("SNTP: Error!!! data sending error!n");
        result = SNTP_ERR_SEND;
        goto fail;
    }
    debug("SNTP: Data sent...n");
    return;

fail:
    if (context)
    {
        free(context);
    }

    if (sntp_pcb)
    {
        udp_remove(sntp_pcb);
    }

    if (callback)
    {
        callback(result, callback_arg);
    }
}

void on_udp_recv(void* arg, struct udp_pcb* pcb, struct pbuf* p, ip_addr_t* addr, u16_t port)
{
    debug("SNTP: Response has successfully received...n");

    int result = ERR_OK;
    struct sntp_sync_context* context = arg;
    sntp_sync_callback callback = context->callback;
    void* callback_arg = context->callback_arg;

    debug("SNTP: Checking response size...n");
    if (p->tot_len < sizeof (struct sntp_message))
    {
        debug("SNTP: Error!!! Invalid response size!n");
        result = SNTP_ERR_RECV_SIZE;
        goto fail;
    }
    debug("SNTP: Response size is OK...n");

    debug("SNTP: Checking mode...n");
    u8_t mode = 0x0;
    pbuf_copy_partial(p, &mode, sizeof (mode), SNTP_OFFSET_LI_VN_MODE);
    mode &= 0b00000111;
    if (mode != SNTP_MODE_SERVER && mode != SNTP_MODE_BROADCAST)
    {
        debug("SNTP: Error!!! Invalid mode!n");
        result = SNTP_ERR_RECV_MODE;
        goto fail;
    }
    debug("SNTP: Mode is OK...n");

    debug("SNTP: Checking stratum...n");
    u8_t stratum = 0x0;
    pbuf_copy_partial(p, &stratum, sizeof (stratum), SNTP_OFFSET_STRATUM);
    if (stratum == SNTP_STRATUM_KOD)
    {
        debug("SNTP: Error!!! Kiss of death!n");
        result = SNTP_ERR_RECV_STRATUM;
        goto fail;
    }
    debug("SNTP: Stratum is OK...n");

    debug("SNTP: Updating system timer...n");
    uint32_t receive_time[2];
    pbuf_copy_partial(p, &receive_time, 2 * sizeof (uint32_t), SNTP_OFFSET_RECEIVE_TIME);
    time_t t = ntohl(receive_time[0]) - DIFF_SEC_1900_1970;
    uint32_t us = ntohl(receive_time[1]) / 4295;
    sntp_update_rtc(t, us);
    debug("SNTP: System timer updated...n");

fail:
    if (context)
    {
        free(context);
    }

    if (pcb)
    {
        udp_remove(pcb);
    }

    if (callback)
    {
        callback(result, callback_arg);
    }
}

/**
 * Check if a timer wrap has occurred. Compensate sntp_base reference
 * if affirmative.
 * TODO: think about multitasking and race conditions.
 */
inline void sntp_check_timer_wrap(uint32_t current_value)
{
    if (current_value < SNTP_TIME_REF)
    {
        // Timer wrap has occurred, compensate by subtracting 2^32 to ref.
        SNTP_BASE -= 1LLU << 32;
    }
}

/**
 * Return secs. If us is not a null pointer, fill it with usecs
 */
time_t sntp_get_rtc_time(int32_t *us)
{
    time_t secs;
    uint32_t tim;
    uint64_t base;

    tim = TIMER_COUNT;
    // Check for timer wrap.
    sntp_check_timer_wrap(tim);
    base = SNTP_BASE + tim - SNTP_TIME_REF;
    secs = base * SNTP_CALIBRATION / (1000000U << 12);
    if (us)
    {
        *us = base * SNTP_CALIBRATION % (1000000U << 12);
    }
    return secs;
}

/**
 * Update RTC timer. Called by SNTP module each time it receives an update.
 */
void sntp_update_rtc(time_t t, uint32_t us)
{
    // Apply daylight and timezone correction
    // DEBUG: Compute and print drift
    int64_t sntp_current = SNTP_BASE + TIMER_COUNT - SNTP_TIME_REF;
    int64_t sntp_correct = (((uint64_t) us + (uint64_t) t * 1000000U) << 12) / SNTP_CALIBRATION;
    debug("RTC Adjust: drift = %ld ticks, cal = %dn", (time_t) (sntp_correct - sntp_current), SNTP_CALIBRATION);

    SNTP_TIME_REF = TIMER_COUNT;
    SNTP_CALIBRATION = sdk_system_rtc_clock_cali_proc();
    SNTP_BASE = (((uint64_t) us + (uint64_t) t * 1000000U) << 12) / SNTP_CALIBRATION;
}

/**
 * Syscall implementation. doesn't seem to use tzp.
 */
int _gettimeofday_r(struct _reent* r, struct timeval* tp, void* tzp)
{
    // Syscall defined by xtensa newlib defines tzp as void*
    // So it looks like it is not used. Also check tp is not NULL
    if (tzp || !tp)
    {
        return EINVAL;
    }

    tp->tv_sec = sntp_get_rtc_time((int32_t*) & tp->tv_usec);
    return 0;
}

Лирическое отступление по поводу наличия своего кода синхронизации времени по SNTP: в extensions из SDK уже есть такой модуль, но мне он почему-то не понравился (давно было, уже не помню почему), поэтому я тот код нагло скопипастил и доработал под себя.

Работает все просто: при старте контроллера запускается задача инициализации, которая подключается к точке доступа, синхронизирует время по SNTP, запускает задичи измерения температуры с влажностью и отправки данных на сервер, после чего самоубивается. Задачка измерения опрашивает датчик раз в 59 секунд и складывает результаты в очередь, задача отправки запускается раз в 2 минуты, читает данные из очереди и отправляет на MQTT сервер.

Теоретичесик, можно писать и на C++,

Шаг 5 — заключение, куда же без него.
Таким вот нехитрым образом, с помощью языка C и рук с небольшим радиусом кривизны можно запрограммировать контроллер ESP8266. Основное преимущество данного подхода перед скриптовыми решениями (например LUA или MicroPython) в полном контроле над составом и ресурсами прошивки, и возможность впихнуть больше функциональности при ограниченных ресурсах контроллера. Так же есть вариант использования RTOS SDK или NONOS SDK от Espressif, но с первым у меня не срослось, а второй не пробовал использовать. Если кому-то будет интересно, а так же когда сам разберусь, могу написать следующий туториал про OTA (обновление прошивки по воздуху).

Немного результатов работы данного кода:

Данные, полученные с сервера MQTT, и залитые в БД

Немного о программировании ESP8266 на C под FreeRTOS - 1
Отладочный выхлоп контроллера в UART

SDK version: 0.9.9                                                                                                               
--- USER_INIT: sizeof (int): 4                                                                                                                                                               
--- USER_INIT: sizeof (float): 4                                                                                                                                                             
--- USER_INIT: sizeof (time_t): 4                                                                                                                                                            
mode : sta(18:fe:34:d2:c5:a7)                                                                                                                                                                
add if0                                                                                                                                                                                      
--- INIT: setting pins...                                                                                                                                                                    
--- INIT: Set station parameters...                                                                                                                                                          
--- Station parameters has been set.                                                                                                                                                         
--- INIT: Connecting to AP...                                                                                                                                                                
scandone                                                                                                                                                                                     
add 0                                                                                                                                                                                        
aid 2                                                                                                                                                                                        
cnt                                                                                                                                                                                          

connected with kosmonaFFFt, channel 1                                                                                                                                                        
dhcp client start...                                                                                                                                                                         
ip:192.168.1.21,mask:255.255.255.0,gw:192.168.1.1                                                                                                                                            
--- INIT: Connection to AP has been estabilished...                                                                                                                                          
--- INIT: Start SNTP synchronization...                                                                                                                                                      
--- SNTP: Start SNTP synchronization, allocating memory for context...                                                                                                                       
--- SNTP: Context successfully allocated...                                                                                                                                                  
--- SNTP: Start DNS lookup...                                                                                                                                                                
--- INIT: Trying to take init task semaphore...                                                                                                                                              
--- SNTP: Start DNS lookup successfully finished...                                                                                                                                          
--- SNTP: Creating upd_pcb...                                                                                                                                                                
--- SNTP: Successfully created upd_pcb...                                                                                                                                                    
--- SNTP: Allocating pbuf...                                                                                                                                                                 
--- SNTP: Pbuf allocated successfully...                                                                                                                                                     
--- SNTP: Sending data to server...                                                                                                                                                          
--- SNTP: Data sent...                                                                                                                                                                       
--- SNTP: Response has successfully received...                                                                                                                                              
--- SNTP: Checking response size...                                                                                                                                                          
--- SNTP: Response size is OK...                                                                                                                                                             
--- SNTP: Checking mode...                                                                                                                                                                   
--- SNTP: Mode is OK...                                                                                                                                                                      
--- SNTP: Checking stratum...                                                                                                                                                                
--- SNTP: Stratum is OK...                                                                                                                                                                   
--- SNTP: Updating system timer...                                                                                                                                                           
--- RTC Adjust: drift = 1220897578 ticks, cal = 1                                                                                                                                            
--- SNTP: System timer updated...                                                                                                                                                            
--- TIME: Thu Sep 21 19:20:36 2017                                                                                                                                                           
--- INIT: Init task semaphore is taken...                                                                                                                                                    
--- MEASUREMENTS: Start measurements...                                                                                                                                                      
--- MEASUREMENTS: Measurements finished...                                                                                                                                                   
--- INIT: Measurement task created...                                                                                                                                                        
--- MQTT: ConnectNetwork...                                                                                                                                                                  
--- INIT: Send data task created...                                                                                                                                                          
--- MQTT: ConnectNetwork success...                                                                                                                                                          
--- MQTT: MQTTConnect success...                                                                                                                                                             
--- MQTT: Got message to publish...                                                                                                                                                          
---       timestamp: 1506021636                                                                                                                                                              
---       am2302_humidity: 55.8                                                                                                                                                              
---       am2302_temperature: 23.4                                                                                                                                                           
--- MQTT: Successfully publish message...                                                                                                                                                    
--- MEASUREMENTS: Start measurements...                                                                                                                                                      
--- MEASUREMENTS: Measurements finished...                                                                                                                                                   
--- MEASUREMENTS: Start measurements...                                                                                                                                                      
--- MEASUREMENTS: Measurements finished...                                                                                                                                                   
--- MQTT: ConnectNetwork...                                                                                                                                                                  
--- MQTT: ConnectNetwork success...                                                                                                                                                          
--- MQTT: MQTTConnect success...
--- MQTT: Got message to publish...
---       timestamp: 1506021694
---       am2302_humidity: 55.2
---       am2302_temperature: 23.8
--- MQTT: Successfully publish message...
--- MQTT: Got message to publish...
---       timestamp: 1506021751
---       am2302_humidity: 56.5
---       am2302_temperature: 24.4
--- MQTT: Successfully publish message...
--- MEASUREMENTS: Start measurements...
--- MEASUREMENTS: Measurements finished...
--- MEASUREMENTS: Start measurements...
--- MEASUREMENTS: Measurements finished...
--- MQTT: ConnectNetwork...
--- MQTT: ConnectNetwork success...
--- MQTT: MQTTConnect success...
--- MQTT: Got message to publish...
---       timestamp: 1506021807
---       am2302_humidity: 53.0
---       am2302_temperature: 24.7
--- MQTT: Successfully publish message...
--- MQTT: Got message to publish...
---       timestamp: 1506021863
---       am2302_humidity: 52.3
---       am2302_temperature: 24.8
--- MQTT: Successfully publish message...
--- MEASUREMENTS: Start measurements...
--- MEASUREMENTS: Measurements finished...
--- MEASUREMENTS: Start measurements...
--- MEASUREMENTS: Measurements finished...
--- MQTT: ConnectNetwork...
--- MQTT: ConnectNetwork success...
--- MQTT: MQTTConnect success...
--- MQTT: Got message to publish...
---       timestamp: 1506021919
---       am2302_humidity: 52.0
---       am2302_temperature: 24.9
--- MQTT: Successfully publish message...
--- MQTT: Got message to publish...
---       timestamp: 1506021975
---       am2302_humidity: 53.3
---       am2302_temperature: 25.2
--- MQTT: Successfully publish message...

P.S. Для работы с UART на PC рекомендую использовать minicom (консоль), или cutecom (GUI).

Полезные ссылки:

Автор: Антон

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js