一屿储、創(chuàng)建樂鑫云設(shè)備
1.1 注冊樂鑫云賬號
打開鏈接并按提示進(jìn)行注冊
https://iot.espressif.cn/#/
1.2 創(chuàng)建產(chǎn)品
點擊 Device
,選擇 Create
選擇
Create New Product
輸入設(shè)備名惋耙,產(chǎn)品名澄峰,產(chǎn)品類型1.3 創(chuàng)建數(shù)據(jù)流
點擊 Product
盲泛,點擊藍(lán)色名字進(jìn)入產(chǎn)品
創(chuàng)建一個開關(guān)狀態(tài)的數(shù)據(jù)流
plug-status
1.4 創(chuàng)建設(shè)備
點擊 Device
,選擇 Create
選擇剛剛創(chuàng)建的產(chǎn)品進(jìn)行設(shè)備創(chuàng)建
下載
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中加載所有參數(shù)
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定時器的回調(diào)函數(shù)
@param resetFlag -[in] 重啟標(biāo)志
@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"; // 默認(rèn)服務(wù)器IP
g_tcpCloudServerUrl.ip.addr = ipaddr_addr(ip);
g_tcpCloudServerUrl.port = 8000; // 默認(rèn)服務(wù)器端口
connectEspPlatformByIp();
}
可選擇通過IP和端口進(jìn)行連接 connectEspPlatformByIp()
或選擇通過DNS域名解析連接 onnectEspPlatformByDns()
2.4 注冊連接/重連/斷連函數(shù)
/**
@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 連接成功的回調(diào)函數(shù)
@param arg -[in] 指向傳遞給這個回調(diào)函數(shù)來使用的參數(shù)
@return 無
*/
static void ICACHE_FLASH_ATTR
connectCallback(void *arg)
{
struct espconn *pEspconn = arg;
s_reconnectCount = 0;
espconn_regist_recvcb(pEspconn, receiveDataCallback);
sendActiveRequest();
}
/**
@brief 斷連的回調(diào)函數(shù)
@param arg -[in] 指向傳遞給這個回調(diào)函數(shù)來使用的參數(shù)
@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 重連的回調(diào)函數(shù)
@param arg -[in] 指向傳遞給這個回調(diào)函數(shù)來使用的參數(shù)
@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 連接成功后發(fā)送設(shè)備激活請求
connectCallback()
中調(diào)用 sendActiveRequest()
/**
@brief 發(fā)送激活請求
@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 注冊接收回調(diào)函數(shù)
/**
@brief 接收數(shù)據(jù)的回調(diào)函數(shù)
@param arg -[in] 指向傳遞給這個回調(diào)函數(shù)來使用的參數(shù)
@param pData -[in] 接收的數(shù)據(jù)
@param len -[in] 接收的數(shù)據(jù)長度
@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平臺激活響應(yīng)后開啟周期Ping
receiveDataCallback()
中調(diào)用 startSendPingTimer()
/**
@brief 開始發(fā)送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 發(fā)送PING定時器的回調(diào)函數(shù)
@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] 接收的數(shù)據(jù)
@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); // 配置設(shè)備激活
}
else if((pStr = (char *) os_strstr(s_receiveData, "/v1/device/timers/")) != NULL)
{
configDeviceTimer(); // 配置設(shè)備時鐘
}
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(); // 處理設(shè)備密鑰請求
if(wifi_get_opmode() == STATIONAP_MODE)
{
wifi_set_opmode(STATION_MODE); // WIFI模式重置為STA
}
SetFirstConnectEspPlatformFlag(false); // 清除首次連接標(biāo)志
}
}
2.8.1 處理GET請求URL
/**
@brief 處理GET請求URL路徑
@param pRecvData -[in] 接收的數(shù)據(jù)
@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); // 配置系統(tǒng)升級
}
}
else if((pStr = (char *) os_strstr(pRecvData, "\"action\": \"sys_reboot\"")) != NULL)
{
configSystemReboot(); // 配置系統(tǒng)重啟
}
else if((pStr = (char *) os_strstr(pRecvData, "\"action\": \"switch\"")) != NULL)
{
sendRelayStatusResponse(pRecvData);
}
}
2.8.2 處理POST請求URL
/**
@brief 處理POST請求URL路徑
@param pRecvData -[in] 接收的數(shù)據(jù)
@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 處理請求和響應(yīng)
/**
@brief 發(fā)送繼電器狀態(tài)響應(yīng)
@param pRecvData -[in] 接收的數(shù)據(jù)
@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); // 修改為自己獲取繼電器狀態(tài)函數(shù)
os_printf("%s\n", pSendData);
espconn_sent(&s_espPlatformTcpEspconn, pSendData, os_strlen(pSendData));
os_free(pSendData);
pSendData = NULL;
}
}
/**
@brief 處理繼電器狀態(tài)請求
@param pRecvData -[in] 接收的數(shù)據(jù)
@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); // 修改為自己設(shè)置繼電器狀態(tài)函數(shù)
}
else if (os_strncmp(pStr + 27, "0", 1) == 0)
{
SetRelayStatus(SWITCH_OFF); // 修改為自己設(shè)置繼電器狀態(tài)函數(shù)
}
}
sendRelayStatusResponse(pRecvData);
}
三挨决、燒錄
例如4M的Flash
? 由 Leung 寫于 2019 年 8 月 16 日
? 參考:ESP8266 Non-OS SDK API參考[7qq6]
ESP8266 Non-OS SDK IoT Demo指南[kvle]