【平头哥RVB2601创意应用开发】 RVB2601之WiFi联网获取天气信息方式介绍和使用
[复制链接]
项目往期连载文章传送门:
【平头哥RVB2601创意应用开发】 RVB2601之KV组件介绍和使用
【平头哥RVB2601创意应用开发】 RVB2601之OLED-SSD1306移植介绍和使用
【平头哥RVB2601创意应用开发】 RVB2601之cJSON介绍和使用
一、联网
开发板有W800的WiFi芯片,是与MCU是通过SPI连接通讯的。对于一个可以联网的设备,我们一定要好好使用起来它,那就去做个获取天气的小设备吧。
我的联网demo,很多是参考历程OTA的,那么一点点把做的事情说明白,希望其他网友也可以尝试用我的代码自己修改做出自己想要的功能。
1.网络初始化
研究demo,我们知道了,初始化网络需要调用的接口是”void network_init()”,这里面实现了对W800的驱动,以及通过网络组件netmgr进行的网络管理,在这个函数里,先创建了微服务netmgr,然后我们主要去实现,设置WiFi要连接的SSID和PASSWORD,并且实现对W800模块的注册和做好当WiFi拿到IP或者断开时候的回调函数
static void network_init()
{
w800_wifi_param_t w800_param;
/* init wifi driver and network */
w800_param.reset_pin = PA21;
w800_param.baud = 1*1000000;
w800_param.cs_pin = PA15;
w800_param.wakeup_pin = PA25;
w800_param.int_pin = PA22;
w800_param.channel_id = 0;
w800_param.buffer_size = 4*1024;
wifi_w800_register(NULL, &w800_param);
app_netmgr_hdl = netmgr_dev_wifi_init();
if (app_netmgr_hdl) {
utask_t *task = utask_new("netmgr", 2 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI);
netmgr_service_init(task);
netmgr_config_wifi(app_netmgr_hdl, W800_SSID, sizeof(W800_SSID), W800_PASSWARD, sizeof(W800_PASSWARD));
// w800_module_init(task, &w800_param);
netmgr_start(app_netmgr_hdl);
/* Subscribe */
event_subscribe(EVENT_NETMGR_GOT_IP, network_event, NULL);
event_subscribe(EVENT_NETMGR_NET_DISCON, network_event, NULL);
}
}
-
当WiFi连接上热点,获取到IP之后,就开始启动获取天气的线程:app_weather_start();
static void network_event(uint32_t event_id, const void *param, void *context)
{
switch(event_id) {
case EVENT_NETMGR_GOT_IP:
LOGD(TAG, "net got ip");
app_weather_start();
break;
case EVENT_NETMGR_NET_DISCON:
LOGD(TAG, "net disconnect");
break;
}
/*do exception process */
app_exception_event(event_id);
}
3.在 void board_yoc_init(void) 里,对network,进行初始化void network_init()
这样第一步联网功能就完成了。当然记得库要加上w800的,在我们设置好ssid和password之后,给设备烧录程序,重启设备,就可以在日志里看到设备连接网络成功的情况以及获取到的IP了。
// 图
二、移植http的接口和network的接口
设备连上网络还不能去获取天气信息,需要有http get的请求接口,以及底层对http get之类API接口的实现。
这里参考了 OTA demo,设备通过http post 请求更新的固件。将里面http和network的接口做了一部分修改,放到了应用文件中,命名为:app_http.c 和 app_network.c 以及对应的头文件 http.h 和 network.h
这里直接贴代码吧,大家自己研究下,或者直接拿来拷贝就好了
app_http.c
/*
* Copyright (C) 2019-2020 Alibaba Group Holding Limited
*/
#include <stdlib.h>
#include "http.h"
#include <aos/debug.h>
#include "network.h"
#define BUFFER_SIZE 2048
#define TAG "http"
http_t *http_init(const char *path)
{
char *ip, *port_str;
char *end_point;
char *host = NULL;
int port;
// FIXME: only support HTTP now.
if (path == NULL || !strstr(path, "http://")) {
return NULL;
}
http_t *http = aos_zalloc_check(sizeof(http_t));
http->url = strdup(path);
if (http->url == NULL) {
goto error;
}
host = http->url + sizeof("http://") - 1;
if (strlen(host) == 0) {
goto error;
}
http->port = 80;
ip = http->url + strlen("http://");
port_str = strrchr(host, ':');
if (port_str != NULL) {
*port_str = 0;
port_str ++;
port = strtol(port_str, &end_point, 10);
http->port = port;
} else {
port = 80;
end_point = strchr(ip, '/');
}
if (end_point != NULL) {
http->path = strdup(end_point);
LOGD(TAG, "http: path %s", http->path);
if (http->path == NULL) {
goto error;
}
} else {
goto error;
}
http->buffer = (uint8_t *)aos_malloc(BUFFER_SIZE);
if (http->buffer == NULL) {
goto error;
}
memset(http->buffer, 0, BUFFER_SIZE);
*end_point = 0;
http->host = strdup(ip);
if (http->host == NULL) {
goto error;
}
LOGD(TAG, "http connect: %s:%d", http->host, port);
network_init(&http->net);
if (http->net.net_connect(&http->net, http->host, port, SOCK_STREAM) != 0) {
goto error;
}
return http;
error:
if (http) {
if (http->url) aos_free(http->url);
if (http->path) aos_free(http->path);
if (http->host) aos_free(http->host);
if (http->buffer) aos_free(http->buffer);
aos_free(http);
}
return NULL;
}
int http_head_sets(http_t *http, const char *key, const char *value)
{
int len;
if (http->buffer == NULL) {
return -1;
}
len = snprintf((char*)http->buffer + http->buffer_offset, BUFFER_SIZE - http->buffer_offset,
"%s: %s\\r\\n", key, value);
if (len <= 0) {
return -1;
}
http->buffer_offset += len;
return 0;
}
int http_head_seti(http_t *http, const char *key, int value)
{
int len;
if (http->buffer == NULL) {
return -1;
}
len = snprintf((char*)http->buffer + http->buffer_offset, BUFFER_SIZE - http->buffer_offset,
"%s: %d\\r\\n", key, value);
if (len <= 0) {
return -1;
}
http->buffer_offset += len;
return 0;
}
int http_post(http_t *http, char *playload, int timeoutms)
{
char *temp_buff;
if ((temp_buff = strdup((char *)http->buffer)) == NULL) {
return -1;
}
// temp_buff = strdup((char *)http->buffer);
memset(http->buffer, 0, BUFFER_SIZE);
snprintf((char *)http->buffer, BUFFER_SIZE, "POST %s HTTP/1.1\\r\\n", http->path);
strcat((char *)http->buffer, temp_buff);
strcat((char *)http->buffer, "\\r\\n");
strcat((char *)http->buffer, playload);
// LOGD(TAG, "http post sendbuffer: %s", http->buffer);
aos_free(temp_buff);
return http->net.net_write(&http->net, http->buffer, strlen((char*)http->buffer), timeoutms);
}
int http_get(http_t *http, int timeoutms)
{
char *temp_buff;
if ((temp_buff = strdup((char *)http->buffer)) == NULL) {
return -1;
}
// temp_buff = strdup((char *)http->buffer);
memset(http->buffer, 0, BUFFER_SIZE);
snprintf((char *)http->buffer, BUFFER_SIZE, "GET %s HTTP/1.1\\r\\n", http->path);
strcat((char *)http->buffer, temp_buff);
strcat((char *)http->buffer, "\\r\\n");
// LOGD(TAG, "http get sendbuffer: %s", http->buffer);
aos_free(temp_buff);
return http->net.net_write(&http->net, http->buffer, strlen((char*)http->buffer), timeoutms);
}
static int http_parse_head(http_t *http, char **head)
{
char *content;
char *endpoint;
int content_len;
char *code_str;
int code;
char *end_point;
*head = strstr((char*)http->buffer, "\\r\\n\\r\\n");
if (*head == NULL) {
return 0;
}
*head += 4;
// FIXME: server connection close, need reconnect
if (strstr((char*)http->buffer, "Connection: close")) {
return -2;
}
if ((content = strstr((char*)http->buffer, "Content-Length: ")) == NULL) {
code_str = strstr((char *)http->buffer, "HTTP/1.1");
code = atoi(code_str + strlen("HTTP/1.1"));
if (code != 206 && code != 200) {
LOGD(TAG, "http code :%d", code);
return -1;
}
content_len = strtol(*head, &end_point, 16);
if (end_point == NULL) {
return 0;
}
} else {
// content_len = atoi(content + strlen("Content-Length: "));
content_len = strtol(content + strlen("Content-Length: "), &endpoint, 10);
if (endpoint == NULL || *endpoint != '\\r') {
return -1;
}
}
// LOGD(TAG, "http parse head: %s %d", http->buffer, content_len);
return content_len;
}
static int net_quick_reconnect(http_t *http)
{
LOGW(TAG, "i need reconnect http");
http->net.net_disconncet(&http->net);
if (http->net.net_connect(&http->net, http->host, http->port, SOCK_STREAM) != 0) {
LOGE(TAG, "reconnect failed!");
return -1;
}
return 0;
}
static int net_read(int fd, unsigned char *buffer, int len, int timeout_ms)
{
struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};
if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 100)) {
interval.tv_sec = 0;
interval.tv_usec = 10000;
}
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval,
sizeof(struct timeval))) {
return -1;
}
int rc = recv(fd, buffer, len, 0);
return rc;
}
int http_wait_resp(http_t *http, char **head_end, int timeoutms)
{
int recv_len = 0;
int len;
int content_len;
again:
recv_len = 0;
*head_end = NULL;
content_len = 0;
memset(http->buffer, 0, BUFFER_SIZE);
while(recv_len < BUFFER_SIZE) {
int pre_read = BUFFER_SIZE - recv_len;
if(pre_read > 512) {
pre_read = 512;
}
len = net_read(http->net.fd, http->buffer + recv_len, pre_read, timeoutms);
//printf("pre read %d\\n", len);
if (len <= 0) {
LOGE(TAG, "net read len=%d errno=%d", len, errno);
return -1;
}
recv_len += len;
if ((content_len = http_parse_head(http, head_end)) == 0) {
continue;
}
if (content_len < 0) {
if (content_len == -2) {
if (net_quick_reconnect(http) < 0) {
return 0;
}
return -2;
}
return -1;
} else {
break;
}
}
if (content_len <= 0 && !(*head_end)) {
goto again;
}
int head_len = (*head_end - (char *)http->buffer);
int total_len = content_len + head_len;
//int left_len = content_len + head_len - len;
//printf("head len %d, left_len %d, total %d, recv len %d\\n", head_len, left_len, total_len, recv_len);
if ( total_len > BUFFER_SIZE ) {
LOGE(TAG, "ERR total len %d >BUFFER_SIZE; return -1", total_len);
return -1;
}
// LOGD(TAG, "buffer: %.*s", *head_end - (char*)http_ins->buffer, http_ins->buffer);
while(recv_len < total_len) {
if (content_len <= recv_len - (*head_end - (char *)http->buffer)) {
break;
}
len = http->net.net_read(&http->net, http->buffer + recv_len, total_len - recv_len, timeoutms);
//printf("left read %d\\n", len);
if (len <= 0) {
LOGE(TAG, "net read len=%d errno=%d", len, errno);
return -1;
}
recv_len += len;
}
return content_len;
}
char *http_head_get(http_t *http, char *key, int *length)
{
char *temp_key;
char *temp_value;
char *temp_end;
if ((temp_key = strstr((char *)http->buffer, key)) == NULL) {
LOGD(TAG, "no key %s", key);
return NULL;
}
if ((temp_value = strstr(temp_key, ":")) == NULL) {
LOGD(TAG, "no delimiter");
return NULL;
}
if ((temp_end = strstr(temp_value, "\\r\\n")) == NULL) {
LOGD(TAG, "no end");
return NULL;
}
*length = (int)(temp_end - temp_value - 2) >= 0 ? (int)(temp_end - temp_value - 2) : 0;// 1 space
return temp_value + 1;
}
char *http_read_data(http_t *http)
{
char *buffer;
buffer = strstr((char *)http->buffer, "\\r\\n\\r\\n");
if (buffer == NULL) {
return NULL;
}
return buffer + 4;
}
int http_deinit(http_t *http)
{
if (http) {
http->net.net_disconncet(&http->net);
if (http->url) aos_free(http->url);
if (http->path) aos_free(http->path);
if (http->host) aos_free(http->host);
if (http->buffer) aos_free(http->buffer);
aos_free(http);
}
return 0;
}
http.h
/*
* Copyright (C) 2019-2020 Alibaba Group Holding Limited
*/
#ifndef __HTTP_H__
#define __HTTP_H__
#include "network.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct http {
char *path;
char *host;
uint8_t *buffer;
int buffer_offset;
char *url;
int port;
network_t net;
} http_t;
// char *json_getvalue(char *body, char *key, int *len);
http_t *http_init(const char *path);
int http_head_sets(http_t *http, const char *key, const char *value);
int http_head_seti(http_t *http, const char *key, int value);
int http_post(http_t *http, char *playload, int timeoutms);
int http_get(http_t *http, int timeoutms);
int http_wait_resp(http_t *http, char **head_end, int timeoutms);
char *http_head_get(http_t *http, char *key, int *length);
char *http_read_data(http_t *http);
int http_deinit(http_t *http);
#ifdef __cplusplus
}
#endif
#endif
app_network.c
/*
* Copyright (C) 2019-2020 Alibaba Group Holding Limited
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include "aos/kernel.h"
#include "network.h"
#include <aos/debug.h>
static int net_read(network_t *n, unsigned char *buf, int count, int timeout_ms)
{
fd_set fds;
int lfirst = 1;
int rc, len = 0, fd;
struct timeval tv;
unsigned long long delta;
struct timeval t1 = {0}, t2 = {0};
if (!(n && buf && count)) {
return -1;
}
fd = n->fd;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
gettimeofday(&t1, NULL);
while (len < count) {
FD_ZERO(&fds);
FD_SET(fd, &fds);
/* FIXME: patch for select. */
if (lfirst) {
lfirst = 0;
} else {
gettimeofday(&t2, NULL);
delta = (t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec) / 1000;
if (delta >= timeout_ms)
break;
}
rc = select(fd + 1, &fds, NULL, NULL, &tv);
if (rc < 0) {
if ((errno == EINTR) || (errno == EAGAIN)) {
aos_msleep(20);
continue;
}
return -1;
} else if (rc == 0) {
/* time out */
break;
}
if (!FD_ISSET(fd, &fds)) {
aos_msleep(20);
continue;
}
rc = recv(fd, buf + len, count - len, 0);
if (rc < 0) {
if ((errno == EINTR) || (errno == EAGAIN)) {
aos_msleep(20);
continue;
}
return -1;
} else if (rc == 0) {
/* the sockfd may be closed */
break;
}
len += rc;
}
return len;
}
/**
* @brief write n bytes from a sockfd with timeout
* @param [in] fd
* @param [in] buf
* @param [in] count
* @param [in] timeout_ms
* @return -1 on err
*/
static int net_write(network_t *n, unsigned char *buf, int count, int timeout_ms)
{
fd_set fds;
int lfirst = 1;
int rc, len = 0, fd;
struct timeval tv;
unsigned long long delta;
struct timeval t1 = {0}, t2 = {0};
if (!(n && buf && count)) {
return -1;
}
fd = n->fd;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
gettimeofday(&t1, NULL);
while (len < count) {
FD_ZERO(&fds);
FD_SET(fd, &fds);
/* FIXME: patch for select. */
if (lfirst) {
lfirst = 0;
} else {
gettimeofday(&t2, NULL);
delta = (t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec) / 1000;
if (delta >= timeout_ms)
break;
}
rc = select(fd + 1, NULL, &fds, NULL, &tv);
if (rc < 0) {
if ((errno == EINTR) || (errno == EAGAIN)) {
aos_msleep(20);
continue;
}
return -1;
} else if (rc == 0) {
/* time out */
break;
}
if (!FD_ISSET(fd, &fds)) {
aos_msleep(20);
continue;
}
rc = sendto(fd, buf + len, count - len, 0,
(struct sockaddr *)&(n->address),
sizeof(n->address));
if (rc < 0) {
if ((errno == EINTR) || (errno == EAGAIN)) {
aos_msleep(20);
continue;
}
return -1;
} else if (rc == 0) {
/* the sockfd may be closed */
break;
}
len += rc;
}
return len;
}
static int net_connect(network_t *n, char *addr, int port, int net_type)
{
int rc = -1;
sa_family_t family = AF_INET;
struct addrinfo *result = NULL;
struct addrinfo hints = {0, AF_UNSPEC, net_type, 0, 0, NULL, NULL, NULL};
if ((rc = getaddrinfo(addr, NULL, &hints, &result)) == 0) {
struct addrinfo *res = result;
/* prefer ip4 addresses */
while (res) {
if (res->ai_family == AF_INET) {
result = res;
break;
}
res = res->ai_next;
}
if (result->ai_family == AF_INET) {
n->address.sin_port = htons(port);
n->address.sin_family = family = AF_INET;
n->address.sin_addr = ((struct sockaddr_in *)(result->ai_addr))->sin_addr;
} else {
rc = -1;
}
freeaddrinfo(result);
}
if (rc == 0) {
n->fd = socket(family, net_type, 0);
if (n->fd != -1) {
rc = connect(n->fd, (struct sockaddr *)&n->address, sizeof(n->address));
}
if (rc < 0) {
close(n->fd);
n->fd = 0;
}
}
return rc;
}
static void net_disconnect(network_t *n)
{
close(n->fd);
n->fd = 0;
}
void network_init(network_t *n)
{
n->fd = 0;
n->net_read = net_read;
n->net_write = net_write;
n->net_connect = net_connect;
n->net_disconncet = net_disconnect;
}
network.h
/*
* Copyright (C) 2019-2020 Alibaba Group Holding Limited
*/
#ifndef __NETWORK_H__
#define __NETWORK_H__
#include <sys/socket.h>
#include <arpa/inet.h>
#include <lwip/netdb.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct network {
int fd;
struct sockaddr_in address;
int (*net_connect)(struct network *n, char *addr, int port, int net_type);
int (*net_read)(struct network *, unsigned char *, int, int);
int (*net_write)(struct network *, unsigned char *, int, int);
void (*net_disconncet)(struct network *n);
} network_t;
void network_init(network_t *n);
#ifdef __cplusplus
}
#endif
#endif
三、进行http get请求,获取天气信息
有了http get的接口,又有了我们上一篇文章里可以获取到天气信息的URL,让我们开心的http get请求吧。
- 这里直接贴请求天气的接口函数 static int aos_http_get_weather(void)
static int aos_http_get_weather(void) {
http_t *http;
char *payload;
char *body;
int value_len;
char *cptr;
char getvalue[64];
int rc;
memset(getvalue, 0, sizeof(getvalue));
get_weather_url(getvalue, sizeof(getvalue));
if ((http = http_init(getvalue)) == NULL) {
LOGD(TAG, "error http init");
aos_free(payload);
return -1;
}
memset(http->buffer, 0, 2048);
http->buffer_offset = 0;
http_head_sets(http, "Host", http->host);
http_head_seti(http, "Content-Length", strlen(payload));
http_head_sets(http, "Connection", "keep-alive");
http_head_sets(http, "Content-Type", "application/json");
http_head_sets(http, "Cache-Control", "no-cache");
http_get(http, 10000);
LOGD(TAG, "resp body: %200s", body);
parse_http_data(body);
http_deinit(http);
return 0;
}
- 其中 函数static void parse_http_data(char *http_data),就是解析 获取天气信息JSON的函数,这个上一篇cJSON的介绍和使用了有讲解,可以翻看之前的内容。
四、感想
- 其实YoC的很多内容就是alios-things,这些年看到组件越来越丰富,越来越好用,真的很高心。
- 还记得很早以前,同事告诉我看网络,可以看看netmgr组件,一晃好多年过去了,可惜我还是菜鸡,希望可以越来越好。
- 基本上这个项目到这里就进入尾声吧,虽然想试试音频部分,但觉得以后可以用其他的项目跑,暂时又因为最近生活发生了很多不好的事情,还需要花时间去面对,没有心思和精力去再深入的玩一下,加油了,大家!
|