移遠(yuǎn)4G模組撥號(hào)+socket獲取天氣數(shù)據(jù)
點(diǎn)擊上方「嵌入式大雜燴」,選擇「置頂公眾號(hào)」第一時(shí)間查看嵌入式筆記!
上一篇分享了《基于4G Cat.1的內(nèi)網(wǎng)穿透實(shí)踐》,這一篇筆記我們直接使用4G開發(fā)板訪問天氣服務(wù)器獲取天氣數(shù)據(jù)。
我們要使用移遠(yuǎn)4G模塊進(jìn)行網(wǎng)絡(luò)通信,要經(jīng)歷 3 個(gè)主要過程:網(wǎng)絡(luò)注冊(cè)
、網(wǎng)絡(luò)激活
和socket 創(chuàng)建
。
網(wǎng)絡(luò)注冊(cè)是自動(dòng)完成的,無需用戶代碼干預(yù)(網(wǎng)絡(luò)注冊(cè)前,請(qǐng)確認(rèn) SIM 卡是否正常識(shí)別且 SIM 卡無欠費(fèi)等業(yè)務(wù)異常)。
只有在網(wǎng)絡(luò)注冊(cè)成功以后,才可進(jìn)行網(wǎng)絡(luò)激活,即本文所述的撥號(hào)(下文皆稱為撥號(hào))。只有撥號(hào)成功后,才可進(jìn)行 socket 網(wǎng)絡(luò)通信。
撥號(hào)+socket 通信基本流程
確保撥號(hào)成功的必要條件:SIM 卡沒欠費(fèi);硬件良好,天線匹配;撥號(hào)前,調(diào)用 ql_network_register_wait 等待網(wǎng)絡(luò)注冊(cè)成功;撥號(hào)時(shí),向 ql_start_data_call 傳遞正確的參數(shù)。
Socket 通信流程
EC100 模塊 Socket 通信基本流程如上圖:
1、在進(jìn)行 socket 通信之前,必須確保撥號(hào)已經(jīng)成功,可以通過 ql_get_data_call_info()查詢需要進(jìn)行 socket 通信的網(wǎng)絡(luò)通道是否撥號(hào)成功。
2、撥號(hào)成功后進(jìn)行 socket 通信時(shí),必須對(duì)需要通信的網(wǎng)絡(luò)通道進(jìn)行 bind 操作。不管是 UDP 還是 TCP,都必須執(zhí)行 Bind 操作之后才可以正常進(jìn)行 socket 通信。
sdk中給我們提供了一個(gè)tcp_client
的demo,這個(gè)demo的設(shè)計(jì)思路完全按照上面兩張圖流程圖來設(shè)計(jì)。我們?cè)谶@個(gè)demo進(jìn)行修改,獲取天氣數(shù)據(jù)。
我們修改后的example_tcp_weather_client.c
代碼如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ql_type.h"
#include "ql_rtos.h"
#include "ql_application.h"
#include "ql_data_call.h"
#include "sockets.h"
#include "netdb.h"
#include "cJSON.h"
#define DEBUG 0
/* 心知天氣(www.seniverse.com)IP及端口 */
#define WEATHER_IP_ADDR "116.62.81.138"
#define WEATHER_PORT 80
#define TCP_CONNECT_TIMEOUT_S 10
#define TCP_RECV_TIMEOUT_S 10
#define TCP_CLOSE_LINGER_TIME_S 10
#define TCP_CLIENT_SEND_STR "tcp client send string"
#define PROFILE_IDX 1
#define PROFILE_IDX 1
#define KEY "2owqvhhd2dd9o9f9" // 每個(gè)用戶自己的一個(gè)key
/* GET請(qǐng)求包 */
#define GET_REQUEST_PACKAGE \
"GET https://api.seniverse.com/v3/weather/%s.json?key=%s&location=%s&language=zh-Hans&unit=c\r\n\r\n"
/* JSON數(shù)據(jù)包 */
#define NOW_JSON "now"
//....還用更多其他的天氣數(shù)據(jù)包可查閱心知天氣
/* 天氣數(shù)據(jù)結(jié)構(gòu)體 */
typedef struct
{
/* 實(shí)況天氣數(shù)據(jù) */
char id[32]; //id
char name[32]; //地名
char country[32]; //國(guó)家
char path[32]; //完整地名路徑
char timezone[32]; //時(shí)區(qū)
char timezone_offset[32]; //時(shí)差
char text[32]; //天氣預(yù)報(bào)文字
char code[32]; //天氣預(yù)報(bào)代碼
char temperature[32]; //氣溫
char last_update[32]; //最后一次更新的時(shí)間
}Weather;
// 全局變量
static struct in_addr ip4_addr = {0};
// 函數(shù)聲明
static void GetWeather(char *weather_json, char *location, Weather *result);
static int cJSON_NowWeatherParse(char *JSON, Weather *result);
static void DisplayWeather(Weather *weather_data);
static void ql_nw_status_callback(int profile_idx, int nw_status);
static void datacall_satrt(void);
/*******************************************************************************************************
** 函數(shù): sockets_weather_test
**------------------------------------------------------------------------------------------------------
** 參數(shù): void
** 返回: void
********************************************************************************************************/
static void sockets_weather_test(void * argv)
{
struct ql_data_call_info info = {0};
char ip4_addr_str[16] = {0};
Weather weather_data = {0};
char *location = "shenzhen";
printf("========== sockets tcp test will start ...\r\n");
/* 撥號(hào)開始 */
datacall_satrt();
/* 獲取撥號(hào)信息 */
ql_get_data_call_info(1, 0, &info);
printf("info.profile_idx: %d\r\n", info.profile_idx);
printf("info.ip_version: %d\r\n", info.ip_version);
printf("info.v4.state: %d\r\n", info.v4.state);
printf("info.v4.reconnect: %d\r\n", info.v4.reconnect);
inet_ntop(AF_INET, &info.v4.addr.ip, ip4_addr_str, sizeof(ip4_addr_str));
printf("info.v4.addr.ip: %s\r\n", ip4_addr_str);
inet_ntop(AF_INET, &info.v4.addr.pri_dns, ip4_addr_str, sizeof(ip4_addr_str));
printf("info.v4.addr.pri_dns: %s\r\n", ip4_addr_str);
inet_ntop(AF_INET, &info.v4.addr.sec_dns, ip4_addr_str, sizeof(ip4_addr_str));
printf("info.v4.addr.sec_dns: %s\r\n", ip4_addr_str);
ip4_addr = info.v4.addr.ip;
if(info.v4.state)
{
memset(&weather_data, 0, sizeof(weather_data)); // weather_data清零
GetWeather(NOW_JSON, location, &weather_data); // GET 并解析實(shí)況天氣數(shù)據(jù)
DisplayWeather(&weather_data); // 顯示天氣結(jié)果
}
printf("========== sockets tcp test finished\r\n");
return 0;
}
static void ql_nw_status_callback(int profile_idx, int nw_status)
{
printf("profile(%d) status: %d\r\n", profile_idx, nw_status);
}
static void datacall_satrt(void)
{
printf("wait for network register done\r\n");
/* 等待網(wǎng)絡(luò)注冊(cè)結(jié)果 */
if(ql_network_register_wait(120) != 0)
{
printf("*** network register fail ***\r\n");
}
else
{
printf("doing network activing ...\r\n");
ql_wan_start(ql_nw_status_callback); /* 撥號(hào)初始化:注冊(cè)撥號(hào)回調(diào)函數(shù) */
ql_set_auto_connect(1, TRUE); /* 設(shè)置撥號(hào)掉線是否自動(dòng)重連 */
ql_start_data_call(1, 0, NULL, NULL, NULL, 0); /* 開始撥號(hào) */
}
}
static void GetWeather(char *weather_json, char *location, Weather *result)
{
int sock_nbio = 1;
int ret = 0;
int sock_fd = -1;
int sock_error = 0;
socklen_t optlen = 0;
fd_set read_fds, write_fds;
struct timeval t;
struct addrinfo * res, hints;
struct sockaddr_in * ip4_svr_addr;
struct sockaddr_in ip4_local_addr = {0};
u8 dns_success = 0;
u8 recv_buf[128] = {0};
u8 GetRequestBuf[256] = {0};
u8 WeatherRecvBuf[2*1024] = {0};
/* */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if(getaddrinfo_with_pcid(WEATHER_IP_ADDR, NULL, &hints, &res, PROFILE_IDX) != 0)
{
printf("*** DNS fail ***\r\n");
goto exit;
}
dns_success = 1;
ret = socket(AF_INET, SOCK_STREAM, 0);
if(ret < 0)
{
printf("*** socket create fail ***\r\n");
goto exit;
}
sock_fd = ret;
ioctl(sock_fd, FIONBIO, &sock_nbio);
ip4_local_addr.sin_family = AF_INET;
ip4_local_addr.sin_port = htons(ql_soc_generate_port());
ip4_local_addr.sin_addr = ip4_addr;
ret = bind(sock_fd, (struct sockaddr *)&ip4_local_addr, sizeof(ip4_local_addr));
if(ret < 0)
{
printf("*** bind fail ***\r\n");
goto exit;
}
ip4_svr_addr = (struct sockaddr_in *)res->ai_addr;
ip4_svr_addr->sin_port = htons(WEATHER_PORT);
ret = connect(sock_fd, (struct sockaddr *)ip4_svr_addr, sizeof(struct sockaddr));
printf("connect ret: %d, errno: %u\r\n", ret, errno);
if(ret == -1 && errno != EINPROGRESS)
{
printf("*** connect fail ***\r\n");
goto exit;
}
t.tv_sec = TCP_CONNECT_TIMEOUT_S;
t.tv_usec = 0;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_SET(sock_fd, &read_fds);
FD_SET(sock_fd, &write_fds);
ret = select(sock_fd + 1, &read_fds, &write_fds, NULL, &t);
printf("select ret: %d\r\n", ret);
if(ret <= 0)
{
printf("*** select timeout or error ***\r\n");
goto exit;
}
if(!FD_ISSET(sock_fd, &read_fds) && !FD_ISSET(sock_fd, &write_fds))
{
printf("*** connect fail ***\r\n");
goto exit;
}
else if(FD_ISSET(sock_fd, &read_fds) && FD_ISSET(sock_fd, &write_fds))
{
optlen = sizeof(sock_error);
ret = getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &sock_error, &optlen);
if(ret == 0 && sock_error == 0)
{
printf("connect success\r\n");
}
else
{
printf("*** connect fail, sock_err = %d, errno = %u ***\r\n", sock_error, errno);
goto exit;
}
}
else if(!FD_ISSET(sock_fd, &read_fds) && FD_ISSET(sock_fd, &write_fds))
{
printf("connect success\r\n");
}
else if(FD_ISSET(sock_fd, &read_fds) && !FD_ISSET(sock_fd, &write_fds))
{
printf("*** connect fail ***\r\n");
goto exit;
}
else
{
printf("*** connect fail ***\r\n");
goto exit;
}
/* 組合GET請(qǐng)求包 */
sprintf(GetRequestBuf, GET_REQUEST_PACKAGE, weather_json, KEY, location);
/* 發(fā)送數(shù)據(jù)到服務(wù)端 */
ret = send(sock_fd, (const void*)GetRequestBuf, strlen(GetRequestBuf), 0);
if(ret < 0)
{
printf("*** send fail ***\r\n");
goto exit;
}
_recv_:
t.tv_sec = TCP_RECV_TIMEOUT_S;
t.tv_usec = 0;
FD_ZERO(&read_fds);
FD_SET(sock_fd, &read_fds);
ret = select(sock_fd + 1, &read_fds, NULL, NULL, &t);
printf("select ret: %d\r\n", ret);
if(ret <= 0)
{
printf("*** select timeout or error ***\r\n");
goto exit;
}
if(FD_ISSET(sock_fd, &read_fds))
{
ret = recv(sock_fd, WeatherRecvBuf, sizeof(WeatherRecvBuf), 0);
if(ret > 0)
{
printf("recv data: [%d]%s\r\n", ret, WeatherRecvBuf);
/* 解析天氣數(shù)據(jù)并保存到結(jié)構(gòu)體變量weather_data中 */
if (0 == strcmp(weather_json, NOW_JSON)) // 天氣實(shí)況
{
cJSON_NowWeatherParse(WeatherRecvBuf, result);
}
}
else if(ret == 0)
{
printf("*** peer closed ***\r\n");
goto exit;
}
else
{
if(!(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN))
{
printf("*** error occurs ***\r\n");
goto exit;
}
else
{
printf("wait for a while\r\n");
ql_rtos_task_sleep_ms(20);
goto _recv_;
}
}
}
exit:
if(dns_success) freeaddrinfo(res);
if(sock_fd >= 0)
{
struct linger linger = {0};
linger.l_onoff = 1;
linger.l_linger = TCP_CLOSE_LINGER_TIME_S;
setsockopt(sock_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
setsockopt(sock_fd, IPPROTO_TCP, TCP_CLOSE_TIMEROUT, &linger.l_linger, sizeof(linger.l_linger));
/* 清空緩沖區(qū) */
memset(GetRequestBuf, 0, 256);
memset(WeatherRecvBuf, 0, 2*1024);
close(sock_fd);
}
}
/*******************************************************************************************************
** 函數(shù): cJSON_NowWeatherParse,解析天氣實(shí)況數(shù)據(jù)
**------------------------------------------------------------------------------------------------------
** 參數(shù): JSON:天氣數(shù)據(jù)包 result:數(shù)據(jù)解析的結(jié)果
** 返回: void
********************************************************************************************************/
static int cJSON_NowWeatherParse(char *JSON, Weather *result)
{
cJSON *json,*arrayItem,*object,*subobject,*item;
json = cJSON_Parse(JSON); //解析JSON數(shù)據(jù)包
if(json == NULL) //檢測(cè)JSON數(shù)據(jù)包是否存在語法上的錯(cuò)誤,返回NULL表示數(shù)據(jù)包無效
{
printf("Error before: [%s]\n",cJSON_GetErrorPtr()); //打印數(shù)據(jù)包語法錯(cuò)誤的位置
return 1;
}
else
{
if((arrayItem = cJSON_GetObjectItem(json,"results")) != NULL); //匹配字符串"results",獲取數(shù)組內(nèi)容
{
int size = cJSON_GetArraySize(arrayItem); //獲取數(shù)組中對(duì)象個(gè)數(shù)
#if DEBUG
printf("cJSON_GetArraySize: size=%d\n",size);
#endif
if((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//獲取父對(duì)象內(nèi)容
{
/* 匹配子對(duì)象1:城市地區(qū)相關(guān) */
if((subobject = cJSON_GetObjectItem(object,"location")) != NULL)
{
// 匹配id
if((item = cJSON_GetObjectItem(subobject,"id")) != NULL)
{
memcpy(result->id, item->valuestring,strlen(item->valuestring)); // 保存數(shù)據(jù)供外部調(diào)用
}
// 匹配城市名
if((item = cJSON_GetObjectItem(subobject,"name")) != NULL)
{
memcpy(result->name, item->valuestring,strlen(item->valuestring)); // 保存數(shù)據(jù)供外部調(diào)用
}
// 匹配城市所在的國(guó)家
if((item = cJSON_GetObjectItem(subobject,"country")) != NULL)
{
memcpy(result->country, item->valuestring,strlen(item->valuestring)); // 保存數(shù)據(jù)供外部調(diào)用
}
// 匹配完整地名路徑
if((item = cJSON_GetObjectItem(subobject,"path")) != NULL)
{
memcpy(result->path, item->valuestring,strlen(item->valuestring)); // 保存數(shù)據(jù)供外部調(diào)用
}
// 匹配時(shí)區(qū)
if((item = cJSON_GetObjectItem(subobject,"timezone")) != NULL)
{
memcpy(result->timezone, item->valuestring,strlen(item->valuestring)); // 保存數(shù)據(jù)供外部調(diào)用
}
// 匹配時(shí)差
if((item = cJSON_GetObjectItem(subobject,"timezone_offset")) != NULL)
{
memcpy(result->timezone_offset, item->valuestring,strlen(item->valuestring)); // 保存數(shù)據(jù)供外部調(diào)用
}
}
/* 匹配子對(duì)象2:今天的天氣情況 */
if((subobject = cJSON_GetObjectItem(object,"now")) != NULL)
{
// 匹配天氣現(xiàn)象文字
if((item = cJSON_GetObjectItem(subobject,"text")) != NULL)
{
memcpy(result->text, item->valuestring,strlen(item->valuestring)); // 保存數(shù)據(jù)供外部調(diào)用
}
// 匹配天氣現(xiàn)象代碼
if((item = cJSON_GetObjectItem(subobject,"code")) != NULL)
{
memcpy(result->code, item->valuestring,strlen(item->valuestring)); // 保存數(shù)據(jù)供外部調(diào)用
}
// 匹配氣溫
if((item = cJSON_GetObjectItem(subobject,"temperature")) != NULL)
{
memcpy(result->temperature, item->valuestring,strlen(item->valuestring)); // 保存數(shù)據(jù)供外部調(diào)用
}
}
/* 匹配子對(duì)象3:數(shù)據(jù)更新時(shí)間(該城市的本地時(shí)間) */
if((subobject = cJSON_GetObjectItem(object,"last_update")) != NULL)
{
memcpy(result->last_update, subobject->valuestring,strlen(subobject->valuestring)); // 保存數(shù)據(jù)供外部調(diào)用
}
}
}
}
cJSON_Delete(json); //釋放cJSON_Parse()分配出來的內(nèi)存空間
return 0;
}
/*******************************************************************************************************
** 函數(shù): DisplayWeather,顯示天氣數(shù)據(jù)
**------------------------------------------------------------------------------------------------------
** 參數(shù): weather_data:天氣數(shù)據(jù)
** 返回: void
********************************************************************************************************/
static void DisplayWeather(Weather *weather_data)
{
printf("============%s today weather===========\n", weather_data->name);
printf("weather_data->text: %s\n", weather_data->text);
printf("weather_data->temperature: %s\n", weather_data->temperature);
printf("weather_data->timezone: %s\n", weather_data->timezone);
printf("weather_data->timezone_offset: %s\n", weather_data->timezone_offset);
printf("weather_data->last_update: %s\n", weather_data->last_update);
}
application_init(sockets_weather_test, "sockets_weather_test", 8, 4);
測(cè)試結(jié)果:
關(guān)于天氣獲取、解析代碼以及亂碼數(shù)據(jù)之前已經(jīng)有詳細(xì)分享過,這里不再解釋。相關(guān)文章:【socket應(yīng)用】基于C語言的天氣客戶端的實(shí)現(xiàn)
猜你喜歡
C語言、嵌入式應(yīng)用:TCP通信實(shí)例分析
一些不可不知的計(jì)算機(jī)網(wǎng)絡(luò)基礎(chǔ)
AT指令測(cè)試ESP8266通信模組并獲取天氣數(shù)據(jù)
基于RT-Thread的智慧路燈案例實(shí)驗(yàn)分享
基于LiteOS的智慧農(nóng)業(yè)案例實(shí)驗(yàn)分享
免責(zé)聲明:本文來源網(wǎng)絡(luò),免費(fèi)傳達(dá)知識(shí),版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請(qǐng)聯(lián)系我進(jìn)行刪除。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!