This action will force synchronization from HEKR_CLOUD/hekr-esp8266-sdk-ra, which will overwrite any changes that you have made since you forked the repository, and can not be recovered!!!
Synchronous operation will process in the background and will refresh the page when finishing processing. Please be patient.
#氦氪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 ,优化心跳机制
Sign in for post a comment
Comments ( 0 )