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]

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 148,637评论 1 318
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 63,443评论 1 266
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 99,164评论 0 218
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,075评论 0 188
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,080评论 1 266
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,365评论 1 184
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,901评论 2 283
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,649评论 0 176
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,122评论 0 223
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,734评论 2 225
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,093评论 1 236
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,548评论 2 222
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,028评论 3 216
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,765评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,291评论 0 178
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,162评论 2 239
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,293评论 2 242

推荐阅读更多精彩内容