A bit about programming ESP8266 in C under FreeRTOS

There should be a KDPV, but it did not have enough budget.



Motivated by the answer from Tarson to my comment on Programming and data exchange with ARDUINO via WI-FI via ESP8266 , I decided to write about the basics of programming ESP8266 in C under FreeRTOS. Details under the cut.



Step 0 - device



First you need to get a device c ESP8266, it is desirable that there was divorced USB to UART, so as not to have to fence the programmer. I spend my inhuman experiences on NodeMCU .



So, step 1 - we collect tulcheyn



First you need to get a computer with a Linux distribution installed on it (I have openSUSE Leap). Go to the github on the link tyts , read the assembly instructions, install the necessary dependencies, clone the repository, and collect. I cloned into / opt / ESP and before building the Makefile rules, setting variables:



STANDALONE = n VENDOR_SDK = 2.1.0
      
      





Then you can add the path to the binary code in PATH to ~ / .bashrc:



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





Step 2 - Get the SDK



We go to the githab ( tynk ), read the instructions, we clone (for example, in / opt / ESP). Next, in a favorite way (for example, through ~ / .bashrc), set the environment variable ESP8266_SDK_PATH:



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





Step 3 - create a project



Go to the examples directory in the directory with the SDK and copy any example you like. We import / open a project in our favorite development environment, masochists can use a text editor. I prefer NetBeans - it has good support for C / C ++ projects, including those based on a Makefile. A project is being built with make, and it is being flashed with make flash. In the local.mk file, you can configure the parameters for flashing your device (size and mode of accessing flash memory, for example).



Step 4 - we program



We analyze the requirements of the subject area, draw up the TZ according to GOST 34.602-89, after which you can start writing code :) I won’t blink with LEDs, because I don’t have them, therefore, HelloWorld will read data from AM2302 sensor ( DHT22) and send them via the MQTT protocol to the server.



In order to use additional modules, for example, MQTT or DHT, they must be added to the 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: %ld\n", msg.timestamp); debug(" am2302_humidity: %.1f\n", msg.am2302_humidity / 10.0); debug(" am2302_temperature: %.1f\n", 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: %s\n", sdk_system_get_sdk_version()); debug("USER_INIT: sizeof (int): %d\n", sizeof (int)); debug("USER_INIT: sizeof (float): %d\n", sizeof (float)); debug("USER_INIT: sizeof (time_t): %d\n", 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 = %d\n", (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; }
      
      







Lyrical digression about the presence of my time synchronization code via SNTP: there are already such a module in extensions from the SDK, but for some reason I didn’t like it (I’ve been in a long time, I don’t remember why), so I brazenly copied the code and modified it myself.



Everything works simply: when the controller starts, the initialization task starts, which connects to the access point, synchronizes the time via SNTP, starts the temperature measurement tasks with humidity and sends data to the server, and then suicides. The measurement task polls the sensor every 59 seconds and adds the results to the queue, the sending task starts every 2 minutes, reads the data from the queue and sends it to the MQTT server.



Theoretically, you can write in C ++.



Step 5 - Conclusion, where do without it



This simple way, with the help of the C language and hands with a small radius of curvature, you can program the ESP8266 controller. The main advantage of this approach over scripting solutions (for example, LUA or MicroPython) in full control over the composition and resources of the firmware, and the ability to cram more functionality with limited controller resources. There is also the option of using the RTOS SDK or NONOS SDK from Espressif, but with the first one I didn’t grow together, and I didn’t try to use the second one. If someone is interested, as well as when I figure it out myself, I can write the following tutorial about OTA (firmware update over the air).



Some results of this code:



Data received from the server MQTT, and filled in the database




Debug exhaust controller in 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...
      
      







PS To work with UART on a PC, I recommend using minicom (console), or cutecom (GUI).



Useful links:






All Articles