#氦氪4.x嵌入式SDK应用手册 v1.1.4 by zejun.zhao@hekr.me 2016/3/30 11:03:38
##总体描述
编写目的: 该文档旨在指导开发者快速上手氦氪4.x嵌入式SDK,进行对WIFI模块ESP8266的二次开发,熟悉目录结构、了解api接口各模块功能,以及编程中需注意的事项。
SDK开发者:基于esp8266硬件平台,采用Hekr配网模式、连接氦氪云进行开发的开发者群体。同时,开发者也无需关注网络重连及服务器重连机制,便于开发者进行二次应用开发。
api接口使用说明,请参考文档document\ra_embedded_api.chm
├── app
| └─ user
├── bin
| └─ upgrade
├── document
├── examples
├── include
| └─ ra
├── ld
├── lib
└── tools
app
:用户工作区。用户在此目录下执行make编译操作,用户级代码及头文件均放在此目录下
user
:用户级代码目录,SDK入口函数为ra_user_main()
,在user_main.c
中bin
:二进制文件目录。该目录存放了编译生成的bin文件
upgrade
:该子目录存放编译生成的支持云端升级(FOTA)的固件(如user1.bin
或user2.bin
)document
:文档目录include
:头文件目录,包含了用户可使用的API函数声明以及相关宏定义
ra
:RA库头文件目录,开发者进行二次开发所需API函数、宏定义、类型定义均在此目录头文件中examples
:实例项目代码
Smart Plug
: 智能插座项目ld
:SDK编译链接时所需文件,用户无需修改lib
:SDK编译链接时所需的库文件。
libra.a
tools
:包含编译工具和烧录工具等,用户无需修改app
目录下,执行编译命令make
,在bin\upgrade
目录下生成用户固件1.bin
。注:make
编译前,可以根据flash大小来进行配置,配置文件为app\Makefile
。默认为BOOT=new、APP=1、SPI_SPEED=40、SPI_MODE=QIO、SPI_SIZE_MAP=3
,即flash map
默认为3 = 2048KB(512K+512K)
。
固件编译时SPI_SIZE_MAP
仅支持2/3/4
,flash map
对应表如下:
SPI_SIZE_MAP | flash map |
2 | 1024KB( 512KB+ 512KB) |
3 | 2048KB( 512KB+ 512KB) |
4 | 4096KB( 512KB+ 512KB) |
params_section_blank.bin
用于擦除flash中的配置参数(如prodKey
等)以及ra_set_parameter_string/ra_set_parameter_integer
接口存入的参数。flash map
,即SPI_SIZE_MAP
。固件烧录时需要烧录下表中5个bin文件,烧录地址与SPI_SIZE_MAP
对应表如下:
bin文件 | 2 | 3 | 4 |
boot_v1.5.bin | 0x0 | 0x0 | 0x0 |
1.bin | 0x1000 | 0x1000 | 0x1000 |
params_section_blank.bin | 0x7D000 | 0x7D000 | 0x7D000 |
esp_init_data_default.bin | 0xFC000 | 0x1FC000 | 0x3FC000 |
blank.bin | 0xFE000 | 0x1FE000 | 0x3FE000 |
ra_user_main()
,在app/user/user_main.c
中。ra_user_main()
前,系统已经做了部分初始化,具体有:CPU频率为80MHz
、wifi mode为STATION_MODE
、设定uart0和uart1波特率为9600
且打印终端为uart0
。此外,在ra_user_main()
中,需要执行一些函数完成对配置初始化,请参考以下代码:
#include <ra_parameter.h>
#include <ra_utils.h>
#include <ra_uart.h>
void ra_user_main(void)
{
//必选
ra_set_parameter_string("prodKey", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); //prodKey设置
ra_set_current_firmware_version("4.1.1.1"); //固件版本号设置,格式必须是"xx.xx.xx.xx"
//可选
ra_uart_set_terminal(RA_UART1); //设置打印终端,可将SDK内部调试信息输出
}
###5.1 打印终端配置
esp8266有两个串口(uart0和uart1),均可作为打印终端,调用ra_uart_set_terminal()
。os_printf
的打印终端默认为uart0。
示例代码:(设置uart1为打印终端)
#include <ra_uart.h>
ra_uart_set_terminal(RA_UART1);
os_printf("hello world\n");
uart1输出:
hello world
注:ra_uart_set_terminal()
设置后,SDK内部调试信息也会打印出来,如云端登陆流程的信息,方便开发者调试
串口相关属性配置可参考:5.5.1 UART操作接口
###5.2 用户参数存取接口
SDK提供K/V格式的存参接口。开发者可自定义Key,并可选择两种Value类型(int和string)。此外还提供了K/V的读取和删除接口。接口说明请参考ra_parameter.h
。
ctrlKey
、bindKey
和token
,Hekr云端登录机制有使用到它们。spi_flash_write()
接口。示例代码:
#include <ra_parameter.h>
int ret = -1;
char *key_1 = "key_1";
char *key_2 = "key_2";
ra_int32_t value_1 = 1000, value_int = 0;
char *value_2 = "12345678123456781234567812345678";
char value_str[64];
//store params
ra_set_parameter_integer(key_1, value_1);
ra_set_parameter_string(key_2, value_2);
//load params
ra_get_parameter_integer(key_1, &value_int);
ret = ra_get_parameter_string(key_2, value_str, 64);
os_printf("value_int = %d\n", value_int);
os_printf("ret = %d, value_str = %s\n", ret, value_str);
//delete key_2
ra_delete_parameter(key_2);
os_memset(value_str, 0, 64);
ret = ra_get_parameter_string(key_2, value_str, 64);
os_printf("ret = %d, value_str = %s\n", ret, value_str);
uart1输出:
value_int = 1000
ret = 32, value_str = 12345678123456781234567812345678
[log]ra_parameter.c:173: key_2 isn't exist //此为SDK内部打印
ret = -1, value_str =
###5.2 GPIO接口
#####5.2.1 逻辑引脚和物理引脚映射
SDK中gpio逻辑序号与esp8266的gpio物理序号一致。如gpio逻辑序号为12对应GPIO12,而它的引脚名称为MTDI_U
#####5.2.2 配置GPIO为输出
配置GPIO14引脚为输出,且输出高电平:
示例代码:
#include <ra_gpio.h>
ra_gpio_set_direction(14, RA_GPIO_DIRECTION_OUTPUT);
ra_gpio_output(14, 1);
#####5.2.3 配置GPIO为输入
配置GPIO13引脚为输入,且输出高电平:
示例代码:
#include <ra_gpio.h>
ra_gpio_set_direction(13, RA_GPIO_DIRECTION_INPUT);
ra_gpio_state_t state = ra_gpio_input(13);
os_printf("gpio13 = %d\n", state);
###5.3 按键接口
SDK中基于gpio封装了注册按键中断处理回调接口,输入参数包括gpio序号、gpio中断类型、长按时间阈值、短按处理函数指针以及长按处理函数指针。
示例代码:
按键管脚为序号为13的管脚,下降沿触发,长按阈值为3000ms,短按处理函数指针指向short_press_handler()
,长按处理函数指针指向long_press_handler()
。
#include <ra_button.h>
void short_press_handler(void)
{
os_printf("short press\n");
}
void long_press_handler(void)
{
os_printf("long press\n");
}
void ra_user_main(void)
{
ra_register_button_irq_handler(13, RA_GPIO_IRQ_TYPE_NEGEDGE, 3000, short_press_handler, long_press_handler);
}
gpio13电平拉低3s,则进入长按处理回调函数,打印long press
;gpio13电平拉低短于3s,则进入短按处理回调函数,打印short press
。
RA_GPIO_IRQ_TYPE_NEGEDGE
###5.4 定时任务接口
SDK中定时接口是对底层api进行的二次封装,且增加了一些新特性,如查询定时任务剩余时间、查询定时任务是否可重复。
#####5.4.1 定时任务新建和启动
示例代码:
注册一个每隔2s重复执行的定时任务,每次执行打印输出hello world
#include <ra_timer.h>
void timer_callback(ra_timer_t timer, void *arg)
{
os_printf("hello world\n");
}
void ra_user_main(void)
{
ra_timer_t timer;
ra_timer_new(&timer);
ra_timer_set_callback(timer, timer_callback, NULL);
ra_timer_start(timer, 2000, ra_true);
}
ra_timer_start()
第三个参数值为ra_false
时,定时任务不可重复#####5.4.2 定时任务的停止与注销
ra_timer_stop()
:定时器停止工作,但不释放定时器timer。无需调用ra_timer_new()
来新建定时器
ra_timer_delete()
:定时器停止工作,且释放定时器timer。
###5.5 UART接口
SDK可以设置UART0或者UART1作为串口输出,UART0的TXD、RXD管脚分别对应ESP8266的TXD、RXD引脚,UART1对应的TXD管脚为GPIO2。
ra_uart_recv_enable()
只支持RA_UART0
。#####5.5.1 UART属性设置
UART可设置的属性包括波特率、数据位、奇偶校验位、停止位。(注:底层esp8266暂不支持奇偶校验)。默认属性为9600
、8
、无
、1
。
示例代码:
设置UART0波特率115200
、数据位7
,并从UART0重复输出hello world
,同时注册串口接收回调函数。
#include <ra_timer.h>
#include <ra_uart.h>
void timer_callback(ra_timer_t timer, void *arg)
{
char *string = "hello world";
ra_uart_send_data(RA_UART0, string, os_strlen(string));
}
void uart_recv_cb(char *buf, ra_size_t len)
{
os_printf("%s, len = %d\n", buf, len);
}
void ra_user_main(void)
{
//设置UART0属性
ra_uart_set_rate(RA_UART0, RA_UART_RATE_115200);
ra_uart_set_data_bits(RA_UART0, RA_UART_DATA_BITS_7);
//定时从UART0发送
ra_timer_t timer;
ra_timer_new(&timer);
ra_timer_set_callback(timer, timer_callback, NULL);
ra_timer_start(timer, 3000, 1);
//设置UART0的数据接收回调函数
ra_uart_register_recv_callback(RA_UART0, uart_recv_cb);
//UART0接收中断使能
ra_uart_recv_enable(RA_UART0);
}
#####5.5.2 UART通信配置
UART发送配置:
ra_uart_set_sending_queue_interval()
:用于设置串口数据发送队列间隔,默认为100ms。当发送数据帧太快,数据帧会缓存在队列中,然后以一定的间隔时间依次发送出去。
UART接收配置:
ra_uart_set_recv_timeout()
:设置串口接收中断超时时间,默认为100ms。UART中断接收数据,如果超时时间内没有再接收到数据,则进入串口接收回调函数。
ra_uart_set_recv_buffer_size_limit()
:设置串口接收回调触发长度,默认为256Byte。UART中断接收数据,当buffer长度达到设定值,则进入串口接收回调函数。
###5.6 设备状态接口
#####5.6.1 设备状态的读写操作
为了方便开发者对设备状态进行监控及控制,SDK中使用了设备状态机制。开发者可以根据设备当前实际状态,将对应的设备状态项item
的值置为ra_true
,待实际状态发生改变时,将该item
置为ra_false
。同时,开发者可在任意时候通过读取某item
的值来判断设备是否处于该状态下。
typedef enum
{
RA_DEVICE_STATE_WLAN_CONNECTED = 0, //路由器连接成功
RA_DEVICE_STATE_WLAN_CONNECT_FAILED, //路由器连接失败
RA_DEVICE_STATE_CLOUD_CONNECTED, //服务器连接成功
RA_DEVICE_STATE_HEKR_CONFIG_RUNNING, //Hekr config模式下
RA_DEVICE_STATE_AIRKISS_CONFIG_RUNNING, //airkiss config模式下
} ra_device_state_type_t;
ra_device_state_type_t
枚举定义中包含了开发者可能用到的设备状态项,可以对它们进行store
(设置)和load
(读取)操作。
ra_device_state_store()
:设置某个设备状态项item
的值。开发者须根据设备实际状态来设置对应item
值。
ra_device_state_store(RA_DEVICE_STATE_WLAN_CONNECTED, ra_true);
//设备连接路由器成功,设置item "RA_DEVICE_STATE_WLAN_CONNECTED"为ra_true。
ra_device_state_load()
:读取某个设备状态项item
的值。根据该item
值来判断设备当前状态,以便进行逻辑处理。
ra_device_state_load(RA_DEVICE_STATE_WLAN_CONNECTED);
//根据返回值判断设备是否连接成功路由器,ra_true表示成功,ra_false表示失败。
ra_device_state_type_t
中已有的设备状态项的store
操作已经在SDK实现,开发者只需通过load
操作来获取设备状态。ra_device_state_type_t
枚举定义中添加其他设备状态项,它的store
和load
操作则自行实现。注:设备状态项请添加在已有项的后面。ra_false
。#####5.6.2 设备状态改变时的回调函数
为方便开发者快速接入氦氪云端,SDK中提供了wifi连接及服务器登陆的接口,在此过程中,设备状态会出现多种变化(如路由器连接成功、路由器连接失败、服务器登陆成功、服务器登陆失败等)。SDK中提供了设备状态改变的回调函数,当设备状态改变时(如连接上路由器)开发者可以根据item
的值来判断设备当前状态,进行分支操作。
示例代码:可参考5.8节的相关示例
为了不影响当前设备状态项,在ra_device_state_type_t
枚举定义中后面添加一条设备状态 RA_DEVICE_STATE_DO_ACTION
#include <ra_device_state.h>
#include <ra_utils.h>
void device_state_changed_callback(ra_device_state_type_t item, ra_bool current_state)
{
if (item == RA_DEVICE_STATE_DO_ACTION)
{
os_printf("RA_DEVICE_STATE_DO_ACTION. state = %d\n", current_state);
}
}
void ra_user_main(void)
{
ra_register_device_state_changed_callback(device_state_changed_callback);//注册设备状态改变时的回调函数
if (ra_device_state_load(RA_DEVICE_STATE_DO_ACTION) == ra_false)
{
ra_device_state_store(RA_DEVICE_STATE_DO_ACTION, ra_true);
}
}
uart1输出:
RA_DEVICE_STATE_DO_ACTION. state = 1
###5.7 wifi配网接口
Hekr config
和airkiss config
(暂不支持)调用配网接口ra_start_wifi_config(RA_WIFI_CONFIG_TYPE_HEKR)
即进入Hekr config
模式,此时使用Hekr APP对设备进行一键配网,设备获取到ssid和password即进入配网处理回调函数中。
示例代码:
#include <ra_wifi.h>
void wifi_config_finish_callback(char *ssid, char *password)
{
if ((ssid != NULL) && (password != NULL))
{
os_printf("wifi_config successed\n");
os_printf("ssid = %s, password = %s\n", ssid, password);
//配网成功,获取到ssid及password,接着连接路由器
}
else
{
os_printf("wifi_config failed\n");
//配网失败
}
}
void ra_user_main()
{
os_printf("wifi_config start\n");
ra_register_wifi_config_callback(wifi_config_finish_callback);//注册wifi config配网回调函数
ra_start_wifi_config(RA_WIFI_CONFIG_TYPE_HEKR);//启动wifi config
}
********hekr_config run********
即表示进入配网模式。wifi_station_set_config()
进行存储,避免重复配网。wifi_station_get_config(struct station_config *config)
接口的config参数来判断是否需要进入配网模式。###5.8 设备连接wifi及登陆服务器
设备须先连接上wifi路由器,然后进行登陆服务器操作。开发者可以使用hekr config
一键配网获取ssid,也连接一个固定的ssid。以下示例配置固定的ssid。
示例代码:
#include <ra_wifi.h>
#include <ra_device_state.h>
#include <ra_utils.h>
void recv_cloud_msg_callback(char *buf, ra_uint16_t len)
{
os_printf("buf = %s, len = %d\n", buf, len);
//接收到云端数据
}
void device_state_changed_callback(ra_device_state_type_t item, ra_bool current_state)
{
if (item == RA_DEVICE_STATE_WLAN_CONNECTED&&
current_state == ra_true)
{
//路由器连接成功,接着登录服务器
os_printf("wifi connect successed\n");
ra_register_cloud_recv_callback(recv_cloud_msg_callback);//注册登录云端后,数据接收的回调函数
ra_connect_to_cloud();//登录云端服务器
}
else if (item == RA_DEVICE_STATE_WLAN_CONNECT_FAILED&&
current_state == ra_true)
{
os_printf("wifi connect failed\n");
}
}
void ra_user_main(void)
{
ra_register_device_state_changed_callback(device_state_changed_callback);//注册设备状态改变时的回调函数
ra_connect_wifi("WDD_TEST", "56781234", NULL, 30 * 1000);
}
ra_connect_wifi()
中第四个参数为wifi连接的超时时间,超时后设备停止wifi连接,并进入到设备状态改变的回调函数中,此时的item == RA_DEVICE_STATE_WLAN_CONNECT_FAILED && current_state == ra_true
。prodKey
(在HEKR console平台创建产品时生成,长度为32 Bytes),请在ra_user_main()
中调用ra_set_parameter_string()
来设置,如ra_set_parameter_string("prodKey", "12345678123456781234567812345678");
。###5.9 UDP局域网接口
SDK局域网通信采用UDP协议,wifi模块端作为Server,监听固定端口,端口号为10000,APP端作为Client。
Client端的ip和port存在于UDP接收回调函数的参数
ra_remote_info_t *
中。
示例代码:
#include <ra_lan_comm.h>
//UDP数据接收回调函数中,ra_remote_info_t *remote是client端的ip和port
void lan_recv_cb(ra_remote_info_t *remote, char *data, ra_uint16_t size)
{
os_printf("remote ip:%u:%u:%u:%u port:%u\n",
remote->remote_ip[0],
remote->remote_ip[1],
remote->remote_ip[2],
remote->remote_ip[3],
remote->remote_port);
os_printf("data = %s\n", data);
ra_lan_comm_send(remote, data, size); //send data to remote
}
void ra_user_main()
{
ra_lan_comm_register_recv_callback(lan_recv_cb); //注册UDP数据接收回调函数
ra_lan_comm_server_start(); //启动UDP server,监听10000端口
//连接wifi
ra_connect_wifi("WDD_TEST", "56781234", NULL, 30 * 1000);
}
###5.10 OTA升级
esp8266的OTA升级方式将flash map分为A/B两个区,固件默认烧录到A区,OTA升级将新固件下载到B区,升级完成后系统重启从B区启动。再次升级时则固件下载至A分区,并从A分区启动,依次更替。
OTA升级指令(action为devUpgrade
)是由云端主动下发,用户需要将指令中参数URL、MD5和固件类型提取出来,然后调用ra_start_dev_upgrade()
启动OTA升级。用户在解析devUpgrade
指令时可以不校验参数,ra_start_dev_upgrade()
内部会进行校验,如果参数无误,则返回值为0,向云端回应devUpgradeResp时code为200;如果参数有误,则返回值为非0,向云端回应devUpgradeResp时code为非200(自行定义)。
注:如果代码中调用了
ra_enable_cloud_data_parse()
函数,则SDK内部集成了OTA升级,开发者无需调用OTA相关API来实现OTA升级。
示例代码:
void OTA_successful_callback(void)
{
os_printf("OTA_successful!\n");
//升级成功后,要执行ra_system_upgrade_reboot()重启完成升级
ra_timer_t timer;
ra_timer_new(&timer);
ra_timer_set_callback(timer, ra_system_upgrade_reboot, NULL);
ra_timer_start(timer, 2*1000, ra_false);
}
void OTA_fail_callback(int error_code)
{
os_printf("OTA_fail!\n");
}
void ra_user_main()
{
int ret;
ra_register_dev_upgrade_success_callback(OTA_successful_callback); //注册OTA成功回调函数
ra_register_dev_upgrade_failed_callback(OTA_fail_callback); //注册OTA失败回调函数
ret = ra_start_dev_upgrade(dev_TID, URL, MD5, B); //启动模块OTA升级,OTA的固件类型为B
if(ret == 0)
{
//上报devUpgradeResp,code为200
}
else
{
//上报devUpgradeResp,code为非200
}
}
ra_system_upgrade_reboot()
,此时系统会重启,方可正常运行新版本固件。###5.11 浮点打印的解决方案
由于esp8266 SDK并不支持浮点打印(包括printf
及sprintf
),氦氪在此基础上移植了一种变通解决方案来支持浮点打印。为了区分原生api,支持浮点的api命名为c_printf
和c_sprintf
,在#include <c_sprintf.h>
中。
include/ra
目录下提供的api函数均是以ra_
为前缀命名,是该SDK主推供开发者调用的API接口。由于该SDK本身是基于原生ESP8266 NON_RTOS SDK
深度开发而来,故ESP8266底层API也对开发者开放可供调用,但我们不推荐开发者使用底层API,以免造成RA接口的失效。ra_
前缀api使用说明请参考ra_embedded_api.chm
文档。500ms
的函数不要在ra_user_main()
直接调用,推荐在系统初始化完成后。可在入口函数中调用ra_register_system_init_done_callback()
来注册系统初始化完成的回调函数。然后在回调函数中调用敏感api。ra_disconnect_wifi()
。FUN_ATTRIBUTE
:函数修饰符。函数前不加它,则上电启动时将函数代码从flash
加载到iram
;在函数前加上它,则在上电启动后函数被调用时才被加载到iram
中。由于芯片iram
空间有限,编码时请在函数前加上此修饰符,但不能给中断回调函数添加(定时器回调函数除外)。RODATA_ATTRIBUTE
:变量修饰符。在变量(包括数组)定义前添加它,它将被写入flash中,减少系统运行期间堆的占用,只在变量使用时从flash中读取到ram
中。V1.0.2
: SDK更新至v1.5.4.1 ,优化心跳机制
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。