一、创建乐鑫云设备
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]