一、创建乐鑫云设备

1.1 注册乐鑫云账号

1.2 创建产品

点击 Device ,选择 Create

ESP8266连接巴法云的AT指令 esp8266接入云平台_回调函数


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

ESP8266连接巴法云的AT指令 esp8266接入云平台_乐鑫云_02

1.3 创建数据流

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

ESP8266连接巴法云的AT指令 esp8266接入云平台_回调函数_03


ESP8266连接巴法云的AT指令 esp8266接入云平台_ESP8266_04


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

ESP8266连接巴法云的AT指令 esp8266接入云平台_Espressif_05

1.4 创建设备

点击 Device ,选择 Create

ESP8266连接巴法云的AT指令 esp8266接入云平台_回调函数


选择刚刚创建的产品进行设备创建

下载 Master Device Key 用于等等烧录使用。

ESP8266连接巴法云的AT指令 esp8266接入云平台_ESP8266_07

二、移植文件

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);
}

三、烧录

ESP8266连接巴法云的AT指令 esp8266接入云平台_Espressif_08


ESP8266连接巴法云的AT指令 esp8266接入云平台_ESP8266连接巴法云的AT指令_09


ESP8266连接巴法云的AT指令 esp8266接入云平台_Espressif_10


例如4M的Flash

ESP8266连接巴法云的AT指令 esp8266接入云平台_ESP8266连接巴法云的AT指令_11