前置知识:
- ESP-IDF/ESP8266_RTOS_SDK(IDF-Style) 环境搭建
- ESP32/ESP8266 编译环境搭建
- MQTT 通信基础
- 基本Linux操作
- 阿里云IOT平台基本使用
准备材料:
- 阿里云C-SDK https://help.aliyun.com/document_detail/96623.html
- ESP32-IDF 或 ESP8266_RTOS_SDK(IDF-Style) https://github.com/espressif
- 编译器/Ubuntu系统/虚拟机...
- 阿里云基础版模型
设定好IDF_PATH后,进入c-sdk目录.
在tools/board目录下,新建ESP32的Profile,如果是ESP8266,则不用新建,因为默认就有一个.
taterli@taterli-VirtualBox:~/aliyun/c-sdk/tools/board$ cat config.alios.esp32 CONFIG_ENV_CFLAGS += -DBOARD_ESP32 -u call_user_start -fno-inline-functions -ffunction-sections -fdata-sections -mlongcalls -DESPOS_FOR_ESP32 -Wl,-static -DXT_USE_THREAD_SAFE_CLIB=0 CONFIG_ENV_CFLAGS += -Os -DCONFIG_HTTP_AUTH_TIMEOUT=500 -DCONFIG_MID_HTTP_TIMEOUT=500 -DCONFIG_GUIDER_AUTH_TIMEOUT=500 -DCONFIG_MQTT_TX_MAXLEN=640 -DCONFIG_MQTT_RX_MAXLEN=1200 CONFIG_external_libs/mbedtls := CONFIG_tests := CROSS_PREFIX := xtensa-esp32-elf-
如下图:
其他地方也不用修改了,回到c-sdk根目录,执行重配置,生成文件.
最后make生成库文件.
真正有用文件只有一个.
执行导出代码.
把导出的代码,复制到[ESP-IDF目录]/components/libiot_sdk/中,然后新建component.mk文件.
而component.mk主要指导如何make这个组件,由于我是一直用到,就不加Kconfig了.
taterli@taterli-VirtualBox:~/esp/esp-idf/components/libiot_sdk$ cat component.mk COMPONENT_ADD_INCLUDEDIRS += dev_model dev_sign infra mqtt wrappers COMPONENT_SRCDIRS := wrappers LIBS := iot_sdk COMPONENT_ADD_LDFLAGS += -lpthread -L$(COMPONENT_PATH)/lib $(addprefix -l,$(LIBS))
到这里成功了一半,另外的一半?就是wrapper.c实现,这就是代码分离的优势.
wrapper.c文件参考(ESP32/ESP8266都可以用这个文件,记得替换里面的Key.):
/** * NOTE: * * HAL_TCP_xxx API reference implementation: wrappers/os/ubuntu/HAL_TCP_linux.c * */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include <sys/time.h> #include <errno.h> #include <assert.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/ioctl.h> #include <sys/time.h> #include <time.h> #include <signal.h> #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_timer.h" #include "nvs.h" #include "esp_log.h" #include "esp_ota_ops.h" #include "sdkconfig.h" #include "infra_types.h" #include "infra_defs.h" #include "infra_compat.h" #include "wrappers_defs.h" #include "stdarg.h" #include "lwip/netdb.h" static const char* TAG = "in"; #define hal_emerg(...) ESP_LOGE(TAG, __VA_ARGS__) #define hal_crit(...) ESP_LOGE(TAG, __VA_ARGS__) #define hal_err(...) ESP_LOGE(TAG, __VA_ARGS__) #define hal_warning(...) ESP_LOGW(TAG, __VA_ARGS__) #define hal_info(...) ESP_LOGI(TAG, __VA_ARGS__) #define hal_debug(...) ESP_LOGD(TAG, __VA_ARGS__) #define HAL_TCP_CONNECT_TIMEOUT 10 * 1000000 char _product_key[IOTX_PRODUCT_KEY_LEN + 1] = "x"; char _product_secret[IOTX_PRODUCT_SECRET_LEN + 1] = "x"; char _device_name[IOTX_DEVICE_NAME_LEN + 1] = "x"; char _device_secret[IOTX_DEVICE_SECRET_LEN + 1] = "x"; static uint64_t _linux_get_time_ms(void) { struct timeval tv = { 0 }; uint64_t time_ms; gettimeofday(&tv, NULL); time_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000; return time_ms; } static uint64_t _linux_time_left(uint64_t t_end, uint64_t t_now) { uint64_t t_left; if (t_end > t_now) { t_left = t_end - t_now; } else { t_left = 0; } return t_left; } /** * @brief Deallocate memory block * * @param[in] ptr @n Pointer to a memory block previously allocated with platform_malloc. * @return None. * @see None. * @note None. */ void HAL_Free(void *ptr) { free(ptr); } /** * @brief Get device name from user's system persistent storage * * @param [ou] device_name: array to store device name, max length is IOTX_DEVICE_NAME_LEN * @return the actual length of device name */ int HAL_GetDeviceName(char device_name[IOTX_DEVICE_NAME_LEN + 1]) { int len = strlen(_device_name); memset(device_name, 0x0, IOTX_DEVICE_NAME_LEN); strncpy(device_name, _device_name, len); return strlen(device_name); } /** * @brief Get device secret from user's system persistent storage * * @param [ou] device_secret: array to store device secret, max length is IOTX_DEVICE_SECRET_LEN * @return the actual length of device secret */ int HAL_GetDeviceSecret(char device_secret[IOTX_DEVICE_SECRET_LEN + 1]) { int len = strlen(_device_secret); memset(device_secret, 0x0, IOTX_DEVICE_SECRET_LEN); strncpy(device_secret, _device_secret, len); return len; } /** * @brief Get firmware version * * @param [ou] version: array to store firmware version, max length is IOTX_FIRMWARE_VER_LEN * @return the actual length of firmware version */ int HAL_GetFirmwareVersion(char *version) { char *ver = "app-1.0.0-20180101.1000"; int len = strlen(ver); memset(version, 0x0, IOTX_FIRMWARE_VER_LEN); strncpy(version, ver, IOTX_FIRMWARE_VER_LEN); version[len] = ' '; return strlen(version); } /** * @brief Get product key from user's system persistent storage * * @param [ou] product_key: array to store product key, max length is IOTX_PRODUCT_KEY_LEN * @return the actual length of product key */ int HAL_GetProductKey(char product_key[IOTX_PRODUCT_KEY_LEN + 1]) { int len = strlen(_product_key); memset(product_key, 0x0, IOTX_PRODUCT_KEY_LEN); strncpy(product_key, _product_key, len); return len; } int HAL_GetProductSecret(char product_secret[IOTX_PRODUCT_SECRET_LEN + 1]) { int len = strlen(_product_secret); memset(product_secret, 0x0, IOTX_PRODUCT_SECRET_LEN); strncpy(product_secret, _product_secret, len); return len; } /** * @brief Allocates a block of size bytes of memory, returning a pointer to the beginning of the block. * * @param [in] size @n specify block size in bytes. * @return A pointer to the beginning of the block. * @see None. * @note Block value is indeterminate. */ void *HAL_Malloc(uint32_t size) { return malloc(size); } /** * @brief Create a mutex. * * @retval NULL : Initialize mutex failed. * @retval NOT_NULL : The mutex handle. * @see None. * @note None. */ void *HAL_MutexCreate(void) { int err_num; pthread_mutex_t *mutex = (pthread_mutex_t *)HAL_Malloc(sizeof(pthread_mutex_t)); if (NULL == mutex) { return NULL; } if (0 != (err_num = pthread_mutex_init(mutex, NULL))) { hal_err("create mutex failed"); HAL_Free(mutex); return NULL; } return mutex; } /** * @brief Destroy the specified mutex object, it will release related resource. * * @param [in] mutex @n The specified mutex. * @return None. * @see None. * @note None. */ void HAL_MutexDestroy(void *mutex) { int err_num; if (!mutex) { hal_warning("mutex want to destroy is NULL!"); return; } if (0 != (err_num = pthread_mutex_destroy((pthread_mutex_t *)mutex))) { hal_err("destroy mutex failed"); } HAL_Free(mutex); } /** * @brief Waits until the specified mutex is in the signaled state. * * @param [in] mutex @n the specified mutex. * @return None. * @see None. * @note None. */ void HAL_MutexLock(void *mutex) { int err_num; if (0 != (err_num = pthread_mutex_lock((pthread_mutex_t *)mutex))) { hal_err("lock mutex failed: - '%s' (%d)", strerror(err_num), err_num); } } /** * @brief Releases ownership of the specified mutex object.. * * @param [in] mutex @n the specified mutex. * @return None. * @see None. * @note None. */ void HAL_MutexUnlock(void *mutex) { int err_num; if (0 != (err_num = pthread_mutex_unlock((pthread_mutex_t *)mutex))) { hal_err("unlock mutex failed - '%s' (%d)", strerror(err_num), err_num); } } /** * @brief Writes formatted data to stream. * * @param [in] fmt: @n String that contains the text to be written, it can optionally contain embedded format specifiers that specifies how subsequent arguments are converted for output. * @param [in] ...: @n the variable argument list, for formatted and inserted in the resulting string replacing their respective specifiers. * @return None. * @see None. * @note None. */ void HAL_Printf(const char *fmt, ...) { va_list args; va_start(args, fmt); vprintf(fmt, args); va_end(args); fflush(stdout); } uint32_t HAL_Random(uint32_t region) { return (region > 0) ? (esp_random() % region) : 0; } /** * @brief Sleep thread itself. * * @param [in] ms @n the time interval for which execution is to be suspended, in milliseconds. * @return None. * @see None. * @note None. */ void HAL_SleepMs(uint32_t ms) { usleep(1000 * ms); } /** * @brief Writes formatted data to string. * * @param [out] str: @n String that holds written text. * @param [in] len: @n Maximum length of character will be written * @param [in] fmt: @n Format that contains the text to be written, it can optionally contain embedded format specifiers that specifies how subsequent arguments are converted for output. * @param [in] ...: @n the variable argument list, for formatted and inserted in the resulting string replacing their respective specifiers. * @return bytes of character successfully written into string. * @see None. * @note None. */ int HAL_Snprintf(char *str, const int len, const char *fmt, ...) { va_list args; int rc; va_start(args, fmt); rc = vsnprintf(str, len, fmt, args); va_end(args); return rc; } void HAL_Srandom(uint32_t seed) { return; } /** * @brief Destroy the specific TCP connection. * * @param [in] fd: @n Specify the TCP connection by handle. * * @return The result of destroy TCP connection. * @retval < 0 : Fail. * @retval 0 : Success. */ int HAL_TCP_Destroy(uintptr_t fd) { int rc; /* Shutdown both send and receive operations. */ rc = shutdown((int) fd, 2); if (0 != rc) { hal_err("shutdown error"); return -1; } rc = close((int) fd); if (0 != rc) { hal_err("closesocket error"); return -1; } return 0; } int HAL_TCP_Timeout(int fd, unsigned long usec) { int ret = 0; struct timeval tm; fd_set set; int error = -1, len = sizeof(int); tm.tv_sec = usec / 1000000; tm.tv_usec = usec % 1000000; FD_ZERO(&set); FD_SET(fd, &set); if (select(fd + 1, NULL, &set, NULL, &tm) <= 0) { ret = false; } else { getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *) &len); ret = (error == 0) ? true : false; } return ret; } /** * @brief Establish a TCP connection. * * @param [in] host: @n Specify the hostname(IP) of the TCP server * @param [in] port: @n Specify the TCP port of TCP server * * @return The handle of TCP connection. @retval 0 : Fail. @retval > 0 : Success, the value is handle of this TCP connection. */ uintptr_t HAL_TCP_Establish(const char *host, uint16_t port) { struct addrinfo hints; struct addrinfo *addrInfoList = NULL; struct addrinfo *cur = NULL; int fd = 0; int rc = 0; char service[6]; memset(&hints, 0, sizeof(hints)); hal_info("establish tcp connection with server(host='%s', port=[%u])", host, port); hints.ai_family = AF_INET; /* only IPv4 */ hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; sprintf(service, "%u", port); if ((rc = getaddrinfo(host, service, &hints, &addrInfoList)) != 0) { hal_err("getaddrinfo error(%d), host = '%s', port = [%d]", rc, host, port); return -1; } for (cur = addrInfoList; cur != NULL; cur = cur->ai_next) { if (cur->ai_family != AF_INET) { hal_err("socket type error"); rc = -1; continue; } fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol); if (fd < 0) { hal_err("create socket error"); rc = -1; continue; } if (connect(fd, cur->ai_addr, cur->ai_addrlen) == 0) { rc = fd; break; } close(fd); hal_err("connect error"); rc = -1; } if (-1 == rc) { hal_err("fail to establish tcp"); } else { hal_info("success to establish tcp, fd=%d", rc); } freeaddrinfo(addrInfoList); return (uintptr_t)rc; } /** * @brief Read data from the specific TCP connection with timeout parameter. * The API will return immediately if 'len' be received from the specific TCP connection. * * @param [in] fd @n A descriptor identifying a TCP connection. * @param [out] buf @n A pointer to a buffer to receive incoming data. * @param [out] len @n The length, in bytes, of the data pointed to by the 'buf' parameter. * @param [in] timeout_ms @n Specify the timeout value in millisecond. In other words, the API block 'timeout_ms' millisecond maximumly. * * @retval -2 : TCP connection error occur. * @retval -1 : TCP connection be closed by remote server. * @retval 0 : No any data be received in 'timeout_ms' timeout period. * @retval (0, len] : The total number of bytes be received in 'timeout_ms' timeout period. * @see None. */ int32_t HAL_TCP_Read(uintptr_t fd, char *buf, uint32_t len, uint32_t timeout_ms) { int ret, err_code; uint32_t len_recv; uint64_t t_end, t_left; fd_set sets; struct timeval timeout; t_end = _linux_get_time_ms() + timeout_ms; len_recv = 0; err_code = 0; do { t_left = _linux_time_left(t_end, _linux_get_time_ms()); if (0 == t_left) { break; } FD_ZERO(&sets); FD_SET(fd, &sets); timeout.tv_sec = t_left / 1000; timeout.tv_usec = (t_left % 1000) * 1000; ret = select(fd + 1, &sets, NULL, NULL, &timeout); if (ret > 0) { ret = recv(fd, buf + len_recv, len - len_recv, 0); if (ret > 0) { len_recv += ret; } else if (0 == ret) { hal_err("connection is closed"); err_code = -1; break; } else { if (EINTR == errno) { hal_err("EINTR be caught"); continue; } hal_err("recv fail"); err_code = -2; break; } } else if (0 == ret) { break; } else { hal_err("select-recv fail"); err_code = -2; break; } } while ((len_recv < len)); /* priority to return data bytes if any data be received from TCP connection. */ /* It will get error code on next calling */ return (0 != len_recv) ? len_recv : err_code; } /** * @brief Write data into the specific TCP connection. * The API will return immediately if 'len' be written into the specific TCP connection. * * @param [in] fd @n A descriptor identifying a connection. * @param [in] buf @n A pointer to a buffer containing the data to be transmitted. * @param [in] len @n The length, in bytes, of the data pointed to by the 'buf' parameter. * @param [in] timeout_ms @n Specify the timeout value in millisecond. In other words, the API block 'timeout_ms' millisecond maximumly. * * @retval < 0 : TCP connection error occur.. * @retval 0 : No any data be write into the TCP connection in 'timeout_ms' timeout period. * @retval (0, len] : The total number of bytes be written in 'timeout_ms' timeout period. * @see None. */ int32_t HAL_TCP_Write(uintptr_t fd, const char *buf, uint32_t len, uint32_t timeout_ms) { int ret; uint32_t len_sent; uint64_t t_end, t_left; fd_set sets; t_end = _linux_get_time_ms() + timeout_ms; len_sent = 0; ret = 1; /* send one time if timeout_ms is value 0 */ do { t_left = _linux_time_left(t_end, _linux_get_time_ms()); if (0 != t_left) { struct timeval timeout; FD_ZERO(&sets); FD_SET(fd, &sets); timeout.tv_sec = t_left / 1000; timeout.tv_usec = (t_left % 1000) * 1000; ret = select(fd + 1, NULL, &sets, NULL, &timeout); if (ret > 0) { if (0 == FD_ISSET(fd, &sets)) { hal_err("Should NOT arrive"); /* If timeout in next loop, it will not sent any data */ ret = 0; continue; } } else if (0 == ret) { hal_err("select-write timeout %d", (int)fd); break; } else { if (EINTR == errno) { hal_err("EINTR be caught"); continue; } hal_err("select-write fail"); break; } } if (ret > 0) { ret = send(fd, buf + len_sent, len - len_sent, 0); if (ret > 0) { len_sent += ret; } else if (0 == ret) { hal_err("No data be sent"); } else { if (EINTR == errno) { hal_err("EINTR be caught"); continue; } hal_err("send fail"); break; } } } while ((len_sent < len) && (_linux_time_left(t_end, _linux_get_time_ms()) > 0)); return len_sent; } /** * @brief Retrieves the number of milliseconds that have elapsed since the system was boot. * * @return the number of milliseconds. * @see None. * @note None. */ uint64_t HAL_UptimeMs(void) { uint64_t time_ms; struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); time_ms = ((uint64_t)ts.tv_sec * (uint64_t)1000) + (ts.tv_nsec / 1000 / 1000); return time_ms; } int HAL_Vsnprintf(char *str, const int len, const char *format, va_list ap) { return vsnprintf(str, len, format, ap); }
然后搞个工程main.c(可以复制别的工程出来改)
#include <string.h> #include <sys/param.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_wifi.h" #include "esp_event_loop.h" #include "esp_log.h" #include "nvs_flash.h" #include "lwip/err.h" #include "lwip/sockets.h" #include "lwip/sys.h" #include <lwip/netdb.h> #include "dev_sign_api.h" #include "mqtt_api.h" #include "infra_compat.h" /* FreeRTOS event group to signal when we are connected & ready to make a request */ static EventGroupHandle_t wifi_event_group; const int IPV4_GOTIP_BIT = BIT0; static const char *TAG = "example"; static esp_err_t event_handler(void *ctx, system_event_t *event) { switch (event->event_id) { case SYSTEM_EVENT_STA_START: esp_wifi_connect(); ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START"); break; case SYSTEM_EVENT_STA_CONNECTED: break; case SYSTEM_EVENT_STA_GOT_IP: xEventGroupSetBits(wifi_event_group, IPV4_GOTIP_BIT); ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP"); break; case SYSTEM_EVENT_STA_DISCONNECTED: /* This is a workaround as ESP32 WiFi libs don't currently auto-reassociate. */ esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, IPV4_GOTIP_BIT); break; default: break; } return ESP_OK; } static void initialise_wifi(void) { tcpip_adapter_init(); wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); wifi_config_t wifi_config = { .sta = { .ssid = "TaterLi_Labs", .password = "@#%$#!#%#$@%^&%", }, }; ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); ESP_ERROR_CHECK( esp_wifi_start() ); } static void wait_for_ip() { uint32_t bits = IPV4_GOTIP_BIT ; ESP_LOGI(TAG, "Waiting for AP connection..."); xEventGroupWaitBits(wifi_event_group, bits, false, true, portMAX_DELAY); ESP_LOGI(TAG, "Connected to AP"); } int example_publish(void *handle) { int res = 0; const char *fmt = "/%s/%s/update"; char *topic = NULL; int topic_len = 0; char *payload = "{"message":"hello!"}"; topic_len = strlen(fmt) + strlen("xxxxxx") + strlen("xxxxxxxxxxxxxxxxxx") + 1; topic = malloc(topic_len); if (topic == NULL) { ESP_LOGI(TAG,"memory not enough"); return -1; } memset(topic, 0, topic_len); snprintf(topic, topic_len, fmt, "xxxxxx", "xxxxxxxxxxxxxxxxxx"); res = IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, strlen(payload)); if (res < 0) { ESP_LOGI(TAG,"publish failed, res = %d", res); free(topic); return -1; } free(topic); return 0; } void example_event_handle(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg) { ESP_LOGI(TAG,"msg->event_type : %d", msg->event_type); } void app_main() { void *pclient = NULL; int loop_cnt = 0; iotx_mqtt_param_t mqtt_params; ESP_ERROR_CHECK( nvs_flash_init() ); initialise_wifi(); wait_for_ip(); memset(&mqtt_params, 0x0, sizeof(mqtt_params)); mqtt_params.request_timeout_ms = 5000; mqtt_params.clean_session = 0; mqtt_params.keepalive_interval_ms = 60000; mqtt_params.read_buf_size = 1024; mqtt_params.write_buf_size = 1024; mqtt_params.handle_event.h_fp = example_event_handle; mqtt_params.handle_event.pcontext = NULL; IOT_SetLogLevel(IOT_LOG_DEBUG); pclient = IOT_MQTT_Construct(&mqtt_params); if (NULL == pclient) { ESP_LOGI(TAG, "MQTT construct failed"); while(1); } while (1) { if (0 == loop_cnt % 20) { example_publish(pclient); } IOT_MQTT_Yield(pclient, 200); loop_cnt += 1; } while(1); }
执行效果:
上线了,数据已经上行.
至于ESP8266的,在IO用的不多的情况下,ESP-01是非常完美的选择,ESP8266下执行效果基本一样.
懒人配置包:
同理可得,腾讯云也是这个架构的代码,理论上,也是可以移植到ESP8266的.
楼主我按照你的操作 make 之后很多行都未找到命令
@Sun 先检查ESP-IDF/ESP8266-RTOS-SDK是否配置正确.
你好,我按照你的做之后出现
/home/x/Documents/ESP8266_RTOS_SDK/components/libiot_sdk/wrappers/wrapper.c:357: undefined reference to `pthread_mutex_destroy’
/home/x/Documents/ESP8266_RTOS_SDK/myproject/test1/build/libiot_sdk/liblibiot_sdk.a(wrapper.o): In function `HAL_MutexLock’:
这类pthread没有链接的错误,请问要怎么解决
@duncan 看原文,你没添加上-lpthread
楼主 你好,请问有cmake的方法吗?
COMPONENT_ADD_LDFLAGS += -lpthread -L$(COMPONENT_PATH)/lib $(addprefix -l,$(LIBS)) 怎么转去cmake呢
我编译失败了,方便把你编译好的文件发我一份吗?