ESP8266学习笔记(14)——接入乐鑫云Iot·Espressif

一、创建乐鑫云设备

1.1 注册乐鑫云账号

打开链接并按提示进行注册
https://iot.espressif.cn/#/

1.2 创建产品

点击 Device ,选择 Create


选择 Create New Product 输入设备名,产品名,产品类型

1.3 创建数据流

点击 Product,点击蓝色名字进入产品



创建一个开关状态的数据流 plug-status

1.4 创建设备

点击 Device ,选择 Create


选择刚刚创建的产品进行设备创建
下载 Master Device Key 用于等等烧录使用。

二、移植文件

https://pan.baidu.com/s/1Ed1QW462zDjV0uX2_N-8OA[dldk]

2.1 ESP平台初始化

在user_main.c中

void ICACHE_FLASH_ATTR
user_init(void)
{
    LoadAllParamFromFlash();                                            // 从Flash中加载所有参数
    EspPlatformInit();                                                  // ESP平台初始化
}

2.2 检查是否被分配IP

在user_esp_platform.c中

/**
 @brief ESP平台TCP客户端初始化
 @param 无
 @return 无
*/
void ICACHE_FLASH_ATTR
EspPlatformInit(void)
{
    printIotVersionInfo();                                                                  // 打印版本信息
    printResetInfo();                                                                       // 打印重启信息

    uint8 activeStatus = s_flahSavedParam.activeStatus;
    if(activeStatus != 1)
    {
        if(wifi_get_opmode() == STATION_MODE)
        {
            wifi_set_opmode(STATIONAP_MODE);                                                // WIFI模式重置为STA+AP
        }                                                               
    }

    if(wifi_get_opmode() != SOFTAP_MODE)
    {
        startEspPlatformCheckIpTimer();
    }
}

startEspPlatformCheckIpTimer() 开启定时器,周期检查是否被分配IP

/**
 @brief ESP平台检查IP定时器的回调函数
 @param resetFlag -[in] 重启标志
 @return 无
*/
static void ICACHE_FLASH_ATTR
espPlatformCheckIpTimerCallback(uint8 resetFlag)
{
    stopEspPlatformCheckTimer();

    struct ip_info ipInfo;
    wifi_get_ip_info(STATION_IF, &ipInfo);
    uint8 wifiStationConnectStatus = wifi_station_get_connect_status();

    if(wifiStationConnectStatus == STATION_GOT_IP && ipInfo.ip.addr != 0)
    {
        s_deviceStatus = DEVICE_CONNECTING;

        if(resetFlag)                                                                   // 重启过
        {
            s_reconnectCount = 0;
        }

        tcpClientInit();                                                                // TCP客户端初始化
    }
    else
    {
        if((wifiStationConnectStatus == STATION_WRONG_PASSWORD ||
            wifiStationConnectStatus == STATION_NO_AP_FOUND ||
            wifiStationConnectStatus == STATION_CONNECT_FAIL))
        {
            wifi_station_disconnect();                                                  // 断开连接路由
            if(wifi_get_opmode() == STATION_MODE)
            {
                wifi_set_opmode(STATIONAP_MODE);                                        // WIFI模式重置为STA+AP
            }                                                           
        }
        else
        {
            startEspPlatformCheckIpTimer();
        }
    }
}

2.3 TCP客户端初始化

wifiStationConnectStatus == STATION_GOT_IP 检查到获取IP后

/**
 @brief TCP客户端初始化
 @param 无
 @return 无
*/
static void ICACHE_FLASH_ATTR
tcpClientInit(void)
{
    s_espPlatformTcpEspconn.type = ESPCONN_TCP;
    s_espPlatformTcpEspconn.state = ESPCONN_NONE;
    s_espPlatformTcpEspconn.proto.tcp = (esp_tcp *) os_zalloc(sizeof(esp_tcp));

    s_pingStatus = 1;

    char ip[16] = "115.29.202.58";                                                      // 默认服务器IP
    g_tcpCloudServerUrl.ip.addr = ipaddr_addr(ip);
    g_tcpCloudServerUrl.port = 8000;                                                    // 默认服务器端口
    connectEspPlatformByIp();
}

可选择通过IP和端口进行连接 connectEspPlatformByIp()
或选择通过DNS域名解析连接 onnectEspPlatformByDns()

2.4 注册连接/重连/断连函数

/**
 @brief 通过IP连接ESP平台
 @param 无
 @return 无
*/
static void ICACHE_FLASH_ATTR
connectEspPlatformByIp(void)
{
    os_memcpy(s_espPlatformTcpEspconn.proto.tcp->remote_ip, (uint8 *)(&g_tcpCloudServerUrl.ip.addr), 4);
    s_espPlatformTcpEspconn.proto.tcp->remote_port = g_tcpCloudServerUrl.port;
    s_espPlatformTcpEspconn.proto.tcp->local_port = espconn_port();

    os_printf("espPlatform:ip %d.%d.%d.%d\n",
                s_espPlatformTcpEspconn.proto.tcp->remote_ip[0], s_espPlatformTcpEspconn.proto.tcp->remote_ip[1],
                s_espPlatformTcpEspconn.proto.tcp->remote_ip[2], s_espPlatformTcpEspconn.proto.tcp->remote_ip[3]);
    os_printf("espPlatform:port %d\n", s_espPlatformTcpEspconn.proto.tcp->remote_port);

    espconn_regist_connectcb(&s_espPlatformTcpEspconn, connectCallback);
    espconn_regist_disconcb(&s_espPlatformTcpEspconn, disconnectCallback);
    espconn_regist_reconcb(&s_espPlatformTcpEspconn, reconnectCallback);
    espconn_connect(&s_espPlatformTcpEspconn);
}

/**
 @brief 连接成功的回调函数
 @param arg -[in] 指向传递给这个回调函数来使用的参数
 @return 无
*/
static void ICACHE_FLASH_ATTR
connectCallback(void *arg)
{
    struct espconn *pEspconn = arg;
    s_reconnectCount = 0;
    espconn_regist_recvcb(pEspconn, receiveDataCallback);
    sendActiveRequest();
}

/**
 @brief 断连的回调函数
 @param arg -[in] 指向传递给这个回调函数来使用的参数
 @return 无
*/
static void ICACHE_FLASH_ATTR
disconnectCallback(void *arg)
{
    struct espconn *pEspconn = arg;

    stopSendPingTimer();

    if(pEspconn == NULL)
    {
        return ;
    }

    pEspconn->proto.tcp->local_port = espconn_port();

    espPlatformCheckReconnectTimerCallback();
}

/**
 @brief 重连的回调函数
 @param arg -[in] 指向传递给这个回调函数来使用的参数
 @param error -[in] 错误码
 @return 无
*/
static void ICACHE_FLASH_ATTR
reconnectCallback(void *arg, sint8 error)
{
    stopSendPingTimer();

    if(++s_reconnectCount == 5)
    {
        s_deviceStatus = DEVICE_CONNECT_SERVER_FAIL;
        if(wifi_get_opmode() == STATION_MODE)
        {
            wifi_set_opmode(STATIONAP_MODE);
        }

        if(s_firstConnectFlag == true)
        {
            return ;
        }
    }

    startEspPlatformCheckReconnectTimer();
}

2.5 连接成功后发送设备激活请求

connectCallback() 中调用 sendActiveRequest()

/**
 @brief 发送激活请求
 @param 无
 @return 无
*/
static void ICACHE_FLASH_ATTR
sendActiveRequest(void)
{
    uint8 deviceKey[TOKEN_SIZE] = {0};
    uint32 nonce;
    char *pSendData = (char *) os_zalloc(SEND_DATA_SIZE);

    os_memcpy(deviceKey, s_flahSavedParam.devkey, sizeof(s_flahSavedParam.devkey));
    uint8 activeStatus = s_flahSavedParam.activeStatus;

    if(activeStatus == 0xFF)
    {
        activeStatus = 0;
    }

    if(pSendData != NULL)
    {
        if(activeStatus == 0)                                                           // 未激活
        {
            uint8 token[TOKEN_SIZE] = {0};
            uint8 wifiMac[23] = {0};

            os_memcpy(token, s_flahSavedParam.token, sizeof(s_flahSavedParam.token));

            uint8 macAddr[23] = {0};
            wifi_get_macaddr(STATION_IF, macAddr);                                      // 获取STA模式的MAC地址
            os_sprintf(wifiMac, MACSTR, MAC2STR(macAddr));                              // MAC地址
            
            s_activeNonce = os_random() & 0x7FFFFFFF;

            os_sprintf(pSendData, ACTIVE_FRAME, s_activeNonce, token, wifiMac, s_iotVersion, deviceKey);
        }
        else                                                                            // 已激活
        {
            nonce = os_random() & 0x7FFFFFFF;
            os_sprintf(pSendData, FIRST_FRAME, nonce , deviceKey);
        }

        os_printf("%s\n", pSendData);

        espconn_sent(&s_espPlatformTcpEspconn, pSendData, os_strlen(pSendData));

        os_free(pSendData);
        pSendData = NULL;
    }
}

2.6 注册接收回调函数

/**
 @brief 接收数据的回调函数
 @param arg -[in] 指向传递给这个回调函数来使用的参数
 @param pData -[in] 接收的数据
 @param len -[in] 接收的数据长度
 @return 无
*/
static void ICACHE_FLASH_ATTR
receiveDataCallback(void *arg, char *pData, unsigned short len)
{
    os_printf("espPlatform recvData: %s\n", pData);

    if(len == 1460)
    {
        os_memcpy(s_receiveData, pData, len);
    }
    else
    {
        os_memcpy(s_receiveData + os_strlen(s_receiveData), pData, len);
        parseUrl(pData);
        os_memset(s_receiveData, 0, sizeof(s_receiveData));
    }

    startSendPingTimer();
}

2.7 接收ESP平台激活响应后开启周期Ping

receiveDataCallback() 中调用 startSendPingTimer()

/**
 @brief 开始发送PING的定时器
 @param 无
 @return 无
*/
static void ICACHE_FLASH_ATTR
startSendPingTimer(void)
{
    os_timer_disarm(&s_pingTimer);
    os_timer_setfn(&s_pingTimer, (os_timer_func_t *) sendPingTimerCallback, NULL);
    os_timer_arm(&s_pingTimer, ESP_PLATFORM_PING_PERIOD, false);
}

/**
 @brief 发送PING定时器的回调函数
 @param 无
 @return 无
*/
static void ICACHE_FLASH_ATTR
sendPingTimerCallback(void)
{
    if(s_espPlatformTcpEspconn.state == ESPCONN_CONNECT)
    {
        uint8 activeStatus = s_flahSavedParam.activeStatus;
        if(activeStatus == 0)
        {
            os_printf("Err:please check device is activated.\n");
            sendActiveRequest();
        }
        else
        {
            uint8 devicekey[TOKEN_SIZE] = {0};
            os_memcpy(devicekey, s_flahSavedParam.devkey, sizeof(s_flahSavedParam.devkey));

            os_printf("sendPingTimerCallback %u\n", system_get_time());

            if(s_pingStatus == 0)
            {
                os_printf("Err:sendPingTimerCallback sent fail!\n");
                os_printf("espPlatform disconnect\n");
                espconn_disconnect(&s_espPlatformTcpEspconn);
            }
            else
            {
                char *pSendData = (char *) os_zalloc(SEND_DATA_SIZE);

                if(pSendData != NULL)
                {
                    os_sprintf(pSendData, PING_FRAME, devicekey);

                    espconn_sent(&s_espPlatformTcpEspconn, pSendData, os_strlen(pSendData));

                    s_pingStatus = 0;
                    startSendPingTimer();

                    os_free(pSendData);
                    pSendData = NULL;
                }
            }
        }
    }
    else
    {
        os_printf("Err:sendPingTimerCallback sent fail!\n");
        os_printf("espPlatform disconnect\n");
        espconn_disconnect(&s_espPlatformTcpEspconn);
    }
}

2.8 解析URL

/**
 @brief 解析URL
 @param pRecvData -[in] 接收的数据
 @return 无
*/
static void ICACHE_FLASH_ATTR
parseUrl(char *pRecvData)
{
    char *pStr = NULL;

    if((pStr = (char *) os_strstr(s_receiveData, "\"activate_status\": ")) != NULL &&
        parseNonceFromReceiveData(s_receiveData) == s_activeNonce)
    {
        configDeviceActive(pStr);                                                       // 配置设备激活
    }
    else if((pStr = (char *) os_strstr(s_receiveData, "/v1/device/timers/")) != NULL)
    {
        configDeviceTimer();                                                            // 配置设备时钟
    }
    else if((pStr = (char *) os_strstr(s_receiveData, "\"method\": ")) != NULL)
    {
        if(os_strncmp(pStr + 11, "GET", 3) == 0)                                        // 处理GET请求
        {
            handleGetUrlPath(s_receiveData);
        }
        else if(os_strncmp(pStr + 11, "POST", 4) == 0)
        {
            handlePostUrlPath(s_receiveData);                                           // 处理POST请求
        }
    }
    else if((pStr = (char *) os_strstr(s_receiveData, "ping success")) != NULL)
    {
        os_printf("ping success\n");
        s_pingStatus = 1;
    }
    else if((pStr = (char *) os_strstr(s_receiveData, "send message success")) != NULL)
    {
    }
    else if((pStr = (char *) os_strstr(s_receiveData, "timers")) != NULL)
    {
        user_platform_timer_start(s_receiveData , &s_espPlatformTcpEspconn);
    }
    else if((pStr = (char *) os_strstr(s_receiveData, "device")) != NULL)
    {
        handleDeviceKeyRequest();                                                       // 处理设备密钥请求

        if(wifi_get_opmode() == STATIONAP_MODE)
        {
            wifi_set_opmode(STATION_MODE);                                              // WIFI模式重置为STA
        }

        SetFirstConnectEspPlatformFlag(false);                                          // 清除首次连接标志
    }
}

2.8.1 处理GET请求URL

/**
 @brief 处理GET请求URL路径
 @param pRecvData -[in] 接收的数据
 @return 无
*/
static void ICACHE_FLASH_ATTR
handleGetUrlPath(uint8 *pRecvData)
{
    char *pStr = NULL;
    if((pStr = (char *) os_strstr(pRecvData, "\"action\": \"sys_upgrade\"")) != NULL)
    {
        if((pStr = (char *) os_strstr(pRecvData, "\"version\":")) != NULL)
        {
            configSystemUpgrade(pStr);                                                  // 配置系统升级
        }
    }
    else if((pStr = (char *) os_strstr(pRecvData, "\"action\": \"sys_reboot\"")) != NULL)
    {
        configSystemReboot();                                                           // 配置系统重启
    }
    else if((pStr = (char *) os_strstr(pRecvData, "\"action\": \"switch\"")) != NULL)
    {
        sendRelayStatusResponse(pRecvData);
    }
}

2.8.2 处理POST请求URL

/**
 @brief 处理POST请求URL路径
 @param pRecvData -[in] 接收的数据
 @return 无
*/
static void ICACHE_FLASH_ATTR
handlePostUrlPath(uint8 *pRecvData)
{
    char *pStr = NULL;
    if((pStr = (char *) os_strstr(pRecvData, "plug-status")) != NULL)
    {
        handleRelayStatusRequest(pRecvData);
    }
}

2.9 处理请求和响应


/**
 @brief 发送继电器状态响应
 @param pRecvData -[in] 接收的数据
 @return 无
*/
static void ICACHE_FLASH_ATTR
sendRelayStatusResponse(uint8 *pRecvData)
{
    int nonce = 0;
    char *pSendData = (char *) os_zalloc(SEND_DATA_SIZE);

    nonce = parseNonceFromReceiveData(pRecvData);

    if(pSendData != NULL)
    {
        os_sprintf(pSendData, TCP_SERVER_SWITCH_RESPONSE_FRAME, GetRelayStatus(), nonce); // 修改为自己获取继电器状态函数

        os_printf("%s\n", pSendData);

        espconn_sent(&s_espPlatformTcpEspconn, pSendData, os_strlen(pSendData));

        os_free(pSendData);
        pSendData = NULL;
    }
}

/**
 @brief 处理继电器状态请求
 @param pRecvData -[in] 接收的数据
 @return 无
*/
static void ICACHE_FLASH_ATTR
handleRelayStatusRequest(uint8 *pRecvData)
{
    char *pStr = NULL;
    pStr = (char *) os_strstr(pRecvData, "body");

    if(pStr != NULL)
    {
        if(os_strncmp(pStr + 27, "1", 1) == 0)
        {
            SetRelayStatus(SWITCH_ON);      // 修改为自己设置继电器状态函数
        }
        else if (os_strncmp(pStr + 27, "0", 1) == 0)
        {
            SetRelayStatus(SWITCH_OFF);     // 修改为自己设置继电器状态函数
        }
    }

    sendRelayStatusResponse(pRecvData);
}

三、烧录




例如4M的Flash



• 由 Leung 写于 2019 年 8 月 16 日

• 参考:ESP8266 Non-OS SDK API参考[7qq6]
    ESP8266 Non-OS SDK IoT Demo指南[kvle]

你可能感兴趣的:(ESP8266学习笔记(14)——接入乐鑫云Iot·Espressif)