Li Zheng flyskywhy@gmail.com 也可参见我在 github.com 上的博客 AliSmartLiving使用详解.md
安装设备端编译环境
由于 Ubuntu 用来编译 alios 的工具 aos 只能通过 python3 相关的组件 python3-pip 来安装,而 ali-smartliving-device-alios-things 中一些官方提供的 python 脚本仍然是 python2 语法的,所以需要如下罗嗦的方法来安装编译环境,而非仅仅只是安装 aos 即可。
安装 python 和 aos
sudo apt-get install python2 python3 python3-pip
pip3 install aos-cube
让 python 脚本运行在 python2 中
如果通过如下命令
ls -l /usr/bin/python
发现
/usr/bin/python -> python3
则
cd /usr/bin
sudo ln -f -s python2 python
否则编译时会出现 NameError: name 'file' is not defined
等错误。
让 aos 运行在 python3 中
用文本编辑器打开 ~/.local/bin/aos
,确保第一行是 #!/usr/bin/python3
。
否则编译时会出现 ImportError: No module named aos.__main__
错误。
其它
我也曾想要通过简单将脚本从 python2 升级到 python3 语法来解决 NameError: name 'file' is not defined
等错误,但是又带来了其它语法问题,这已经不是我们而是 sdk 提供者需要再花时间在上面了,还是如上所述安装 python2 吧。之前听说 python 2 和 3 不兼容因而我一直拒绝深入学习使用 python ,果然 python 就是差劲啊,开源世界还不如将用到 python 的地方都切换为 nodejs 呢。
注,上述简单将 python2 的 file
改为 python3 的 open
后,还会出现如下报错信息:
Traceback (most recent call last):
File "tools/bk7231u/gen_firmware_img_uart0.py", line 39, in <module>
pack_image(sys.argv[1], sys.argv[2])
File "tools/bk7231u/gen_firmware_img_uart0.py", line 30, in pack_image
f.write("\xff")
TypeError: a bytes-like object is required, not 'str'
设备端编译 Ali SmartLiving
参见 ali-smartliving-device-alios-things/README.md
中 build.sh
的使用方法。
设备端添加 WebSocket Server 支持
添加 WebSocket Server 之后,就可以配合模块厂商(不是阿里官方)的 WiFi 热点代码,让 APP 直连控制设备,而无需
Ali SmartLiving 官方的扫描、配网、控制设备流程,无需烧录产品 id 等三元组信息,因此也就无需向阿里购买这些三元组(因为直连模式不走阿里云生活物联网平台也能用),甚至无需烧录可能也要花钱购买的 mac 地址!之所以使用 WebSocket 而非传统的 Socket ,是因为浏览器不支持创建 Socket 连接,而使用 WebSocket 的话,(react-native 和 react-native-web 同一套代码编写的)手机版或网页版 APP 都可以进行连接。
之所以在 Comparison of WebSocket implementations 中选择 libwebsockets 而非 noPoll 等 C 语言 WebSocket 实现,是因为 Ali SmartLiving 所依赖的 AliOS-Things 曾经采用过一段时间的 libwebsockets ,所以可以参考当时的阿里官方适配代码。
以下列出针对 Ali SmartLiving 的飞燕 SDK 1.6.0 添加 WebSocket Server 支持的方法
添加 libwebsockets 的第 1 步: 简单复制 https://github.com/warmcat/libwebsockets/tree/v2.4.1/lib/ 整个目录
将该 lib/
目录复制为 Living_SDK/framework/libwebsockets/
。之所以选择 v2.4.1
,是因为经比较发现,阿里官方曾经适配过的 libwebsockets 代码就是这个版本的。
添加 libwebsockets 的第 2 步: 复制阿里官方的适配代码,来自 https://github.com/alibaba/AliOS-Things/commit/9326fc864c282e6c3209b2fd00082b451effa54e
将该提交点中新增的文件复制到 Living_SDK/framework/libwebsockets/
中。
添加 libwebsockets 的第 3 步: 在你自己的代码中使用 libwebsockets 并开启 websocket server
Living_SDK/framework/libwebsockets/libwebsockets.mk
将 Living_SDK/framework/libwebsockets/websockets.mk
重命名为 libwebsockets.mk
,并做如下修改
@@ -1,17 +1,16 @@
-NAME := websockets
+NAME := libwebsockets
GLOBAL_CFLAGS += -Wall
-$(NAME)_COMPONENTS := mbedtls alicrypto connectivity.websockets.mbedtls_wrapper
+$(NAME)_COMPONENTS := imbedtls alicrypto libwebsockets.mbedtls_wrapper
$(NAME)_SOURCES := handshake.c libwebsockets.c service.c pollfd.c output.c context.c alloc.c header.c ssl.c
$(NAME)_SOURCES += misc/base64-decode.c misc/lws-ring.c misc/lws-genhash.c misc/sha-1.c
-#$(NAME)_SOURCES += server/ranges.c server/server.c server/parsers.c server/ssl-server.c server/server-handshake.c
-$(NAME)_SOURCES += server/parsers.c
+$(NAME)_SOURCES += server/server.c server/parsers.c server/ssl-server.c server/server-handshake.c
-$(NAME)_SOURCES += client/client.c client/client-handshake.c client/client-parser.c client/ssl-client.c
+# $(NAME)_SOURCES += client/client.c client/client-handshake.c client/client-parser.c client/ssl-client.c
Living_SDK/framework/libwebsockets/lws_config.h
@@ -75,10 +75,10 @@
#define LWS_NO_DAEMONIZE
/* Build without server support */
-#define LWS_NO_SERVER
+/* #undef LWS_NO_SERVER */
/* Build without client support */
-/* #undef LWS_NO_CLIENT */
+#define LWS_NO_CLIENT
Living_SDK/framework/libwebsockets/mbedtls_wrapper/mbedtls_wrapper.mk
-$(NAME)_COMPONENTS := mbedtls alicrypto
+$(NAME)_COMPONENTS := imbedtls alicrypto
Living_SDK/framework/libwebsockets/sync.py
-sync = False #do not sync this module
+# sync = False #do not sync this module
Products/example/YourAPP/YourAPP.mk
$(NAME)_COMPONENTS += framework/protocol/linkkit/sdk \
framework/protocol/linkkit/hal \
framework/netmgr \
+ framework/libwebsockets \
framework/common \
utility/cjson \
framework/uOTA
Products/example/YourAPP/makefile
@@ -14,6 +14,9 @@ INCLUDE = $(INCLUDE_PATH)/ \
-I$(PWD)/../../../prebuild/include/platform/arch/arm/armv7m/gcc/m4 \
-I$(PWD)/../../../prebuild/include/board/$(BOARD) \
-I$(PWD)/../../../Living_SDK/kernel/protocols/net/include \
+ -I$(PWD)/../../../Living_SDK/security/imbedtls/include \
+ -I$(PWD)/../../../Living_SDK/framework/libwebsockets \
+ -I$(PWD)/../../../Living_SDK/framework/libwebsockets/mbedtls_wrapper/include \
-I$(PWD)/../../../Living_SDK/platform/mcu/bk7231u/beken/alios/entry \
-I$(PWD)/../../../Living_SDK/platform/mcu/bk7231u/beken/alios/lwip-2.0.2/port \
-I$(PWD)/../../../Living_SDK/platform/mcu/bk7231u/beken/common \
Products/example/YourAPP/softap.c
#include <lwip/sockets.h>
#include <libwebsockets.h>
#define LWS_PLUGIN_STATIC
#include "aos/kv.h"
#define SOCK_IN_PORT 7681
typedef void*(*property_rw_func)(const char *request, const int request_len);
property_rw_func softap_property_setting_cb = 0;
static unsigned char softap_flag = 0;
static bool softap_launch = false;
static bool connected = false;
void SetSoftApPropertySettingHandle(void* cb)
{
if(cb != NULL){
softap_property_setting_cb = cb;
}
}
void softap_msg_response_handle(char* msg, int len)
{
// lws_callback_on_writable
return;
}
static void softap_property_setting_handle(const char* buffer, int len)
{
if(softap_property_setting_cb != NULL){
softap_property_setting_cb(buffer, len);
}
}
bool softap_isstart(void){
return softap_launch;
}
void softap_task_stop(void){
softap_launch = false;
}
void softap_task_start(void){
softap_launch = true;
}
// 参见下面 TODO 中的说明
struct per_session_data__minimal {
struct per_session_data__minimal *pss_list;
struct lws *wsi;
int last; /* the last message number we sent */
};
static int
callback_smartliving(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
LOGD("LWS_CALLBACK %d", reason);
switch (reason) {
case LWS_CALLBACK_PROTOCOL_INIT:
break;
case LWS_CALLBACK_ESTABLISHED:
if (connected) {
// TODO: allow more than 1 connection?
// 不过直连类的应用,同时只需要连一个手机就可以了吧?
// 后续如果真要同时连接好几个,可以参考
// https://github.com/warmcat/libwebsockets/blob/master/minimal-examples/ws-server/minimal-ws-server/minimal-ws-server.c
// 中的相关代码。
// 其实上面的 per_session_data__minimal 就是那些代码的残留。
// UPDATE: 实际上,经测试,现有代码就能同时建立 2 个 connection ,
// 测试用的 1 个是 macbook 中的网页, 1 个是手机 APP ,理论上可以
// 有更多连接。
// 之所以能够不用折腾 per_session_data__minimal 就支持多个连接
// 可能与我们用的是 ws:// 而非 wss:// 有关?
// 真正的连接数限制可能与下面的 info.max_http_header_pool
// 或 info.ip_limit_ah 等相关。
LOG("only one connection is allowed\n");
lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION,
(unsigned char *)"only one", 5);
return -1;
} else {
connected = true;
LOG("websocket established\n");
}
break;
case LWS_CALLBACK_CLOSED:
connected = false;
LOG("websocket closed\n");
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
// TODO: 后续再考虑发送数据给手机,先简化吧
// 如果要编写发送数据的代码,记得参考
// https://libwebsockets.org/lws-api-doc-v2.4-stable/html/md_READMEs_README_8coding.html
// 中提到的使用 lws_callback_on_writable 的方式进行,
// 还可以一定程度上参考
// https://github.com/warmcat/libwebsockets/blob/master/minimal-examples/ws-server/minimal-ws-server/minimal-ws-server.c
// 中 lws_callback_on_writable 的使用,比如在其它需要发送数据的 .c 文件中调用
// 本文件中内含 lws_callback_on_writable 的 softap_msg_response_handle()
break;
case LWS_CALLBACK_RECEIVE:
if(len <= 0){
return -1;
}
LOG("[SoftAP]recv data = [%d]%s\n", len, (const char *)in);
softap_property_setting_handle((const char *)in, len);
break;
default:
break;
}
return 0;
}
static struct lws_protocols protocols[] = {
// { "http", lws_callback_http_dummy, 0, 0 },
{
"local-ali-smartliving",
callback_smartliving,
sizeof(struct per_session_data__minimal),
200,
0, NULL, 0
},
{ NULL, NULL, 0, 0 } /* terminator */
};
static void websocket_server(void)
{
struct lws_context_creation_info info;
struct lws_context *context;
const char *p;
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
/* for LLL_ verbosity above NOTICE to be built into lws,
* lws must have been configured and built with
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
/* | LLL_DEBUG */;
lws_set_log_level(logs, NULL);
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.port = SOCK_IN_PORT;
info.protocols = protocols;
info.max_http_header_pool = 5;
// info.vhost_name = "localhost";
// info.options =
// LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;
// info.ip_limit_ah = 3;
// info.ip_limit_wsi = 5;
context = lws_create_context(&info);
if (!context) {
LWIP_ASSERT("[SoftAP]WebSocket bind failed.", 0);
// return;
}
LOG("[SoftAP]enter websocket_server loop");
while (true)
{
while(!softap_launch){
aos_msleep(1000);
}
n = lws_service(context, 50);
}
lws_context_destroy(context);
LOG("[SoftAP]exit soft ap task");
aos_task_exit(0);
}
void softap_task(void)
{
LOG("[SoftAP]enter soft ap task");
websocket_server();
}
int init_softap_flag()
{
unsigned char value;
int ret, len = sizeof(value);
ret = aos_kv_get("soft ap flag", &value, &len);
if (ret == 0 && len > 0) {
softap_flag = value;
LOG("soft ap flag = %d", (int)value);
}else{
LOG("soft ap get err ret = %d", ret);
softap_flag = 1;
aos_kv_set("soft ap flag", &softap_flag, len, 1);
}
return 0;
}
int set_softap_flag(unsigned char value)
{
int ret, len = sizeof(value);
softap_flag = value;
ret = aos_kv_set("soft ap flag", &value, len, 1);
if(ret != 0){
LOG("kv set failed ret = %d", ret);
}
}
inline const unsigned char get_softap_flag(void)
{
return softap_flag;
}
tools/bk7231udevkitc.sh
if [[ "$2" == httpapp ]] || [[ "$2" == coapapp ]];then
- COMMON_LIBS="$p/$2.a $p/board_bk7231u.a $p/vcall.a $p/kernel_init.a $p/auto_component.a $p/libiot_sdk.a $p/iotx-hal.a $p/hal_init.a $p/netmgr.a $p/framework.a $p/cjson.a $p/cli.a $p/$ARCH.a $p/newlib_stub.a $p/rhino.a $p/digest_algorithm.a $p/net.a $p/log.a $p/activation.a $p/chip_code.a $p/imbedtls.a $p/kv.a $p/yloop.a $p/hal.a $p/alicrypto.a $p/vfs.a $p/vfs_device.a $p/awss_security.a $p/libaiotss.a "
+ COMMON_LIBS="$p/$2.a $p/mbedtls_wrapper.a $p/libwebsockets.a $p/board_bk7231u.a $p/vcall.a $p/kernel_init.a $p/auto_component.a $p/libiot_sdk.a $p/iotx-hal.a $p/hal_init.a $p/netmgr.a $p/framework.a $p/cjson.a $p/cli.a $p/$ARCH.a $p/newlib_stub.a $p/rhino.a $p/digest_algorithm.a $p/net.a $p/log.a $p/activation.a $p/chip_code.a $p/imbedtls.a $p/kv.a $p/yloop.a $p/hal.a $p/alicrypto.a $p/vfs.a $p/vfs_device.a $p/awss_security.a $p/libaiotss.a "
else
- COMMON_LIBS="$p/$2.a $p/board_bk7231u.a $p/vcall.a $p/kernel_init.a $p/auto_component.a $p/libiot_sdk.a $p/iotx-hal.a $p/hal_init.a $p/netmgr.a $p/framework.a $p/cjson.a $p/ota.a $p/cli.a $p/ota_hal.a $p/$ARCH.a $p/newlib_stub.a $p/rhino.a $p/digest_algorithm.a $p/net.a $p/log.a $p/activation.a $p/chip_code.a $p/imbedtls.a $p/kv.a $p/yloop.a $p/hal.a $p/ota_transport.a $p/ota_download.a $p/ota_verify.a $p/base64.a $p/alicrypto.a $p/vfs.a $p/vfs_device.a $p/awss_security.a $p/libaiotss.a "
+ COMMON_LIBS="$p/$2.a $p/mbedtls_wrapper.a $p/libwebsockets.a $p/board_bk7231u.a $p/vcall.a $p/kernel_init.a $p/auto_component.a $p/libiot_sdk.a $p/iotx-hal.a $p/hal_init.a $p/netmgr.a $p/framework.a $p/cjson.a $p/ota.a $p/cli.a $p/ota_hal.a $p/$ARCH.a $p/newlib_stub.a $p/rhino.a $p/digest_algorithm.a $p/net.a $p/log.a $p/activation.a $p/chip_code.a $p/imbedtls.a $p/kv.a $p/yloop.a $p/hal.a $p/ota_transport.a $p/ota_download.a $p/ota_verify.a $p/base64.a $p/alicrypto.a $p/vfs.a $p/vfs_device.a $p/awss_security.a $p/libaiotss.a "
fi