From 9d1922da16563aadbbc60d8e8b8a987394d80c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E5=85=A8=E5=85=A8?= Date: Mon, 3 Sep 2018 19:32:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 53 +- Makefile | 222 ++++++ README.md | 94 +++ examples/ilopicaapp/app_event.c | 72 ++ examples/ilopicaapp/app_prop.c | 118 ++++ examples/ilopicaapp/app_service.c | 17 + examples/ilopicaapp/ilopmain.c | 127 ++++ examples/ilopicaapp/main.c | 41 ++ examples/ilopicaapp/main.h | 62 ++ examples/iloprawapp/app_event.c | 72 ++ examples/iloprawapp/app_prop_light.c | 74 ++ examples/iloprawapp/app_prop_wifi.c | 102 +++ examples/iloprawapp/app_service.c | 17 + examples/iloprawapp/ilopmain.c | 104 +++ examples/iloprawapp/main.c | 15 + examples/iloprawapp/main.h | 43 ++ examples/sdsapp/sdsapp.c | 90 +++ examples/sdsapp/sdsapp.mk | 13 + examples/test/test.c | 40 ++ platform/linux/mx_serial.c | 158 +++++ platform/linux/mx_stdio.c | 143 ++++ platform/linux/mx_tick.c | 60 ++ platform/linux/mx_uart.c | 241 +++++++ platform/mx_hal.h | 315 +++++++++ src/alicloud_ilop/alicloud_ilop.c | 351 ++++++++++ src/alicloud_ilop/alicloud_ilop.h | 221 ++++++ src/alicloud_ilop/ica_protocol.c | 183 +++++ src/alicloud_ilop/ica_protocol.h | 68 ++ src/alicloud_ilop/raw_protocol.c | 617 +++++++++++++++++ src/alicloud_ilop/raw_protocol.h | 223 ++++++ src/alicloud_sds/alicloud_sds.c | 471 +++++++++++++ src/alicloud_sds/alicloud_sds.h | 203 ++++++ src/helper/cli/mx_cli.c | 498 ++++++++++++++ src/helper/cli/mx_cli.h | 103 +++ src/helper/emhost/ATCmdParser/ATCmdParser.c | 374 ++++++++++ src/helper/emhost/ATCmdParser/ATCmdParser.h | 177 +++++ .../emhost/ATCmdParser/ATCmdParserTest.c | 24 + src/helper/emhost/emh_alisds.c | 234 +++++++ src/helper/emhost/emh_api.h | 428 ++++++++++++ src/helper/emhost/emh_arg.c | 167 +++++ src/helper/emhost/emh_arg.h | 212 ++++++ src/helper/emhost/emh_ilop.c | 295 ++++++++ src/helper/emhost/emh_module.c | 144 ++++ src/helper/emhost/emh_wlan.c | 83 +++ src/helper/jsmn/jsmn.c | 282 ++++++++ src/helper/jsmn/jsmn.h | 72 ++ src/helper/jsmn/json_escape_str.c | 106 +++ src/helper/jsmn/json_escape_str.h | 24 + src/helper/jsmn/json_generator.c | 341 +++++++++ src/helper/jsmn/json_generator.h | 380 +++++++++++ src/helper/jsmn/json_parser.h | 630 +++++++++++++++++ src/helper/jsmn/json_utils.c | 72 ++ src/helper/jsmn/json_utils.h | 6 + src/helper/jsmn/json_wrappers.c | 503 ++++++++++++++ src/helper/mx_common.h | 645 ++++++++++++++++++ src/helper/mx_debug.h | 485 +++++++++++++ src/helper/mx_opt.h | 101 +++ src/helper/mx_toolchain.h | 437 ++++++++++++ src/helper/mx_utils/mx_ringbuffer.c | 85 +++ src/helper/mx_utils/mx_ringbuffer.h | 107 +++ 60 files changed, 11593 insertions(+), 52 deletions(-) create mode 100644 Makefile create mode 100644 README.md create mode 100644 examples/ilopicaapp/app_event.c create mode 100644 examples/ilopicaapp/app_prop.c create mode 100644 examples/ilopicaapp/app_service.c create mode 100644 examples/ilopicaapp/ilopmain.c create mode 100644 examples/ilopicaapp/main.c create mode 100644 examples/ilopicaapp/main.h create mode 100644 examples/iloprawapp/app_event.c create mode 100644 examples/iloprawapp/app_prop_light.c create mode 100644 examples/iloprawapp/app_prop_wifi.c create mode 100644 examples/iloprawapp/app_service.c create mode 100644 examples/iloprawapp/ilopmain.c create mode 100644 examples/iloprawapp/main.c create mode 100644 examples/iloprawapp/main.h create mode 100644 examples/sdsapp/sdsapp.c create mode 100644 examples/sdsapp/sdsapp.mk create mode 100644 examples/test/test.c create mode 100644 platform/linux/mx_serial.c create mode 100644 platform/linux/mx_stdio.c create mode 100644 platform/linux/mx_tick.c create mode 100644 platform/linux/mx_uart.c create mode 100644 platform/mx_hal.h create mode 100644 src/alicloud_ilop/alicloud_ilop.c create mode 100644 src/alicloud_ilop/alicloud_ilop.h create mode 100644 src/alicloud_ilop/ica_protocol.c create mode 100644 src/alicloud_ilop/ica_protocol.h create mode 100644 src/alicloud_ilop/raw_protocol.c create mode 100644 src/alicloud_ilop/raw_protocol.h create mode 100644 src/alicloud_sds/alicloud_sds.c create mode 100644 src/alicloud_sds/alicloud_sds.h create mode 100644 src/helper/cli/mx_cli.c create mode 100644 src/helper/cli/mx_cli.h create mode 100644 src/helper/emhost/ATCmdParser/ATCmdParser.c create mode 100644 src/helper/emhost/ATCmdParser/ATCmdParser.h create mode 100644 src/helper/emhost/ATCmdParser/ATCmdParserTest.c create mode 100644 src/helper/emhost/emh_alisds.c create mode 100644 src/helper/emhost/emh_api.h create mode 100644 src/helper/emhost/emh_arg.c create mode 100644 src/helper/emhost/emh_arg.h create mode 100644 src/helper/emhost/emh_ilop.c create mode 100644 src/helper/emhost/emh_module.c create mode 100644 src/helper/emhost/emh_wlan.c create mode 100644 src/helper/jsmn/jsmn.c create mode 100644 src/helper/jsmn/jsmn.h create mode 100644 src/helper/jsmn/json_escape_str.c create mode 100644 src/helper/jsmn/json_escape_str.h create mode 100644 src/helper/jsmn/json_generator.c create mode 100644 src/helper/jsmn/json_generator.h create mode 100644 src/helper/jsmn/json_parser.h create mode 100644 src/helper/jsmn/json_utils.c create mode 100644 src/helper/jsmn/json_utils.h create mode 100644 src/helper/jsmn/json_wrappers.c create mode 100644 src/helper/mx_common.h create mode 100644 src/helper/mx_debug.h create mode 100644 src/helper/mx_opt.h create mode 100644 src/helper/mx_toolchain.h create mode 100644 src/helper/mx_utils/mx_ringbuffer.c create mode 100644 src/helper/mx_utils/mx_ringbuffer.h diff --git a/.gitignore b/.gitignore index c6127b3..a007fea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,52 +1 @@ -# Prerequisites -*.d - -# Object files -*.o -*.ko -*.obj -*.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf +build/* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2d9b386 --- /dev/null +++ b/Makefile @@ -0,0 +1,222 @@ +###################################### +# target +###################################### +.PHONY:test ilopicaapp iloprawapp sdsapp + +###################################### +# building variables +###################################### +# debug build? +DEBUG = 1 +# optimization +OPT = -Og + + +####################################### +# paths +####################################### +# firmware library path +PERIFLIB_PATH = + +# Build path +BUILD_DIR = build + +###################################### +# source +###################################### +# C sources +C_SOURCES = \ + platform/linux/mx_serial.c \ + platform/linux/mx_stdio.c \ + platform/linux/mx_tick.c \ + platform/linux/mx_uart.c \ + src/helper/cli/mx_cli.c \ + src/helper/emhost/ATCmdParser/ATCmdParser.c \ + src/helper/emhost/emh_alisds.c \ + src/helper/emhost/emh_arg.c \ + src/helper/emhost/emh_ilop.c \ + src/helper/emhost/emh_module.c \ + src/helper/emhost/emh_wlan.c \ + src/helper/mx_utils/mx_ringbuffer.c + + +# ASM sources +ASM_SOURCES = + + +###################################### +# firmware library +###################################### +PERIFLIB_SOURCES = + + +####################################### +# binaries +####################################### +BINPATH = +PREFIX = +CC = $(PREFIX)gcc +AS = $(PREFIX)gcc -x assembler-with-cpp +CP = $(PREFIX)objcopy +AR = $(PREFIX)ar +SZ = $(PREFIX)size +HEX = $(CP) -O ihex +BIN = $(CP) -O binary -S + +####################################### +# CFLAGS +####################################### +# cpu +CPU = + +# fpu +FPU = + +# float-abi +FLOAT-ABI = + +# mcu +MCU = + +# macros for gcc +# AS defines +AS_DEFS = + +# C defines +C_DEFS = -DMX_DEBUG -DMX_CLI_ENABLE + +# AS includes +AS_INCLUDES = + + +# C includes +C_INCLUDES = \ +-Iplatform \ +-Isrc/helper/cli \ +-Isrc/helper/emhost/ATCmdParser \ +-Isrc/helper/emhost \ +-Isrc/helper/mx_utils \ +-Isrc/helper + + +# compile gcc flags +ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections + +CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections + +ifeq ($(DEBUG), 1) +CFLAGS += -g -gdwarf-2 +endif + +# Generate dependency information +CFLAGS += -MMD + + +####################################### +# LDFLAGS +####################################### + +# libraries +LIBS = +LIBDIR = +LDFLAGS = -lpthread -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections + +####################################### +# TARGET +####################################### +ifeq (test,$(findstring test,$(MAKECMDGOALS))) +TARGET = iot-test +C_SOURCES += examples/test/test.c +endif + +ifeq (ilopicaapp,$(findstring ilopicaapp,$(MAKECMDGOALS))) +TARGET = iot-ilopicaapp +C_SOURCES += examples/ilopicaapp/app_event.c \ + examples/ilopicaapp/app_prop.c \ + examples/ilopicaapp/app_service.c \ + examples/ilopicaapp/ilopmain.c \ + examples/ilopicaapp/main.c \ + src/alicloud_ilop/alicloud_ilop.c \ + src/alicloud_ilop/ica_protocol.c + +C_INCLUDES += -Iexamples/ilopicaapp -Isrc/alicloud_ilop + +CFLAGS += -DAT_SUPPORT_ILOP -DILOP_USE_ICA +endif + +ifeq (iloprawapp,$(findstring iloprawapp,$(MAKECMDGOALS))) +TARGET = iot-iloprawapp +C_SOURCES += examples/iloprawapp/app_event.c \ + examples/iloprawapp/app_prop_light.c \ + examples/iloprawapp/app_prop_wifi.c \ + examples/iloprawapp/app_service.c \ + examples/iloprawapp/ilopmain.c \ + examples/iloprawapp/main.c \ + src/alicloud_ilop/alicloud_ilop.c \ + src/alicloud_ilop/raw_protocol.c + +C_INCLUDES += -Iexamples/iloprawapp -Isrc/alicloud_ilop + +CFLAGS += -DAT_SUPPORT_ILOP -DILOP_USE_RAW +endif + +ifeq (sdsapp,$(findstring sdsapp,$(MAKECMDGOALS))) +TARGET = iot-sdsapp +C_SOURCES += examples/sdsapp/sdsapp.c \ + src/alicloud_sds/alicloud_sds.c \ + src/helper/jsmn/jsmn.c \ + src/helper/jsmn/json_escape_str.c \ + src/helper/jsmn/json_generator.c \ + src/helper/jsmn/json_utils.c \ + src/helper/jsmn/json_wrappers.c + +C_INCLUDES += -Isrc/alicloud_sds -Isrc/helper/jsmn + +CFLAGS += -DAT_SUPPORT_ALISDS +endif + +# default action: build all +all test ilopicaapp iloprawapp sdsapp: $(BUILD_DIR)/$(TARGET).elf + +####################################### +# build the application +####################################### +# list of objects +OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o))) +vpath %.c $(sort $(dir $(C_SOURCES))) +# list of ASM program objects +OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o))) +vpath %.s $(sort $(dir $(ASM_SOURCES))) + +-include $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.d))) + +$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) + @echo compiling $< + @$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ + +$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR) + @echo compiling $< + @$(AS) -c $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile + @echo generating $@ + @$(CC) $(OBJECTS) $(LDFLAGS) -o $@ + @$(SZ) $@ + +$(BUILD_DIR): + mkdir $@ + +####################################### +# clean up +####################################### +clean: + @echo cleaning... + @-rm -fR .dep $(BUILD_DIR) + @echo done + +####################################### +# dependencies +####################################### +# -include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*) + +# *** EOF *** diff --git a/README.md b/README.md new file mode 100644 index 0000000..a99d1d9 --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +# How to use iot-sdk + + +## 1. 简介 +本 sdk 提供一个基于 Linux,通过与 Wi-Fi 模块进行 AT 指令串口通信,实现与阿里云 ILOP 和 sds 直连的 IoT 物联网典型开发应用示例源代码。 + +## 2. 目录结构 + +```c +|---example +| |---ilopicaapp /*ilop平台 ica标准数据格式的demo*/ +| |---iloprawapp /*ilop平台 自定义数据格式的demo*/ +| |---sdsapp /*sds平台的demo*/ +| |---test /*测试demo*/ +| +|---platform +| |---linux /*linux平台相关的接口实现*/ +| |---mx_hal.h +| +|---src + |---alicloud_ilop /*ilop平台的实现*/ + |---alicloud_sds /*sds平台的实现*/ + |---helper /*at指令解析内核*/ +``` + +## 3. Linux 平台开发 + +### 3.1 准备 + +- 串口接入PC,并查询设备,命令:`ls -l /dev/ttyUSB*` + +``` +parallels@parallels-vm:~/iot-sdk$ ls -l /dev/ttyUSB* +crw-rw---- 1 root dialout 188, 0 Sep 3 16:15 /dev/ttyUSB0 +``` + +### 3.2 编译 +iot-sdk 提供四个demo,编译demo的命令格式 `make ` + +- 清除上次的make命令所产生的build文件,命令:`make clean` + +``` +parallels@parallels-vm:~/iot-sdk$ make clean +cleaning... +done +``` + +- 编译测试demo,命令:`make test` + +``` +parallels@parallels-vm:~/iot-sdk$ make test +mkdir build +compiling platform/linux/mx_serial.c +compiling platform/linux/mx_stdio.c +compiling platform/linux/mx_tick.c +compiling platform/linux/mx_uart.c +compiling src/helper/cli/mx_cli.c +compiling src/helper/emhost/ATCmdParser/ATCmdParser.c +compiling src/helper/emhost/emh_alisds.c +compiling src/helper/emhost/emh_arg.c +compiling src/helper/emhost/emh_ilop.c +compiling src/helper/emhost/emh_module.c +compiling src/helper/emhost/emh_wlan.c +compiling src/helper/mx_utils/mx_ringbuffer.c +compiling examples/test/test.c +generating build/iot-test.elf + text data bss dec hex filename + 16294 928 3680 20902 51a6 build/iot-test.elf +``` + +- 编译完成后,在build目录下生成 `iot-test.elf` 文件 + +### 3.3 运行 + +- 运行build目录下的elf文件,命令:`./build/iot-test.elf ` + +``` +parallels@parallels-vm:~/iot-sdk$ sudo ./build/iot-test.elf /dev/ttyUSB0 +[sudo] password for parallels: +dev[/dev/ttyUSB0] +open at uart succeed +[APP: test.c: 19] FW version: ilop_AT_v2.1.4 +[APP: test.c: 20] System tick: 757 +``` + +## 4. IoT示例 + +### 4.1 阿里 ilop 平台的标准数据格式demo + +### 4.2 阿里 ilop 平台的透传demo + +## 4. 移植SDK + +- 只需要实现platform/mx_hal.h文件中的函数即可 diff --git a/examples/ilopicaapp/app_event.c b/examples/ilopicaapp/app_event.c new file mode 100644 index 0000000..dd326cd --- /dev/null +++ b/examples/ilopicaapp/app_event.c @@ -0,0 +1,72 @@ +#include "alicloud_ilop.h" +#include "main.h" +#include "mx_cli.h" +#include "mx_common.h" +#include "mx_debug.h" +#include "mx_hal.h" + +static char* err_code = "0"; +static int event_post = 0; + +static unsigned long long uptime_sec(void) +{ + static unsigned long long start_time = 0; + + if (start_time == 0) { + start_time = mx_hal_ms_ticker_read(); + } + + return (mx_hal_ms_ticker_read() - start_time) / 1000; +} + +static mx_status error_code_read_func(char** value) +{ + (*value) = err_code; + app_log("read value:%s", err_code); + return kNoErr; +} + +static const struct ilop_ica_attr_t ErrorCode_event = { ILOP_HANDLE_ATTR_ErrorCode, "Error.ErrorCode", EMH_ARG_ILOP_VT_EVENT, error_code_read_func, NULL }; + +#ifdef MX_CLI_ENABLE +static void handle_event_cmd(char* pwbuf, int blen, int argc, char** argv) +{ + if (argc != 2) + return; + + if (strcmp(argv[1], "enable") == 0) { + event_post = 1; + } else if (strcmp(argv[1], "disable") == 0) { + event_post = 0; + } +} + +static struct cli_command eventcmd = { "event", "event [enable|disable]", handle_event_cmd }; +#endif + +void event_register(void) +{ + ilop_ica_attr_register(&ErrorCode_event); +#ifdef MX_CLI_ENABLE + cli_register_command(&eventcmd); +#endif +} + +void event_task_loop(void) +{ + if (event_post == 0) + return; + + uint32_t now = uptime_sec(); + static uint32_t pre_sec = 0; + + if (pre_sec == now) + return; + + if ((now % 10) == 0) { + app_log("time[%d]", now); + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_ErrorCode); + } + + pre_sec = now; +} diff --git a/examples/ilopicaapp/app_prop.c b/examples/ilopicaapp/app_prop.c new file mode 100644 index 0000000..96c18ef --- /dev/null +++ b/examples/ilopicaapp/app_prop.c @@ -0,0 +1,118 @@ +#include "alicloud_ilop.h" +#include "main.h" +#include "mx_common.h" +#include "mx_debug.h" + +typedef struct _app_prop_t { + char switch_status[2]; + char color_temp[5]; + char rgb_red[4]; + char rgb_green[4]; + char rgb_blue[4]; +} app_prop_t; + +static app_prop_t prop; + +static mx_status light_switch_write_func(char* value) +{ + strcpy(prop.switch_status, value); + app_log("write LightSwitch:%s", prop.switch_status); + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_LightSwitch); + return kNoErr; +} + +static mx_status light_switch_read_func(char** value) +{ + (*value) = prop.switch_status; + app_log("read LightSwitch:%s", prop.switch_status); + return kNoErr; +} + +static mx_status color_temp_write_func(char* value) +{ + strcpy(prop.color_temp, value); + app_log("write ColorTemperature:%s", prop.color_temp); + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_ColorTemperature); + return kNoErr; +} + +static mx_status color_temp_read_func(char** value) +{ + (*value) = prop.color_temp; + app_log("read ColorTemperature:%s", prop.color_temp); + return kNoErr; +} + +static mx_status rgb_red_write_func(char* value) +{ + strcpy(prop.rgb_red, value); + app_log("write R:%s", prop.rgb_red); + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_RGBColor_Red); + return kNoErr; +} + +static mx_status rgb_red_read_func(char** value) +{ + (*value) = prop.rgb_red; + app_log("read R:%s", prop.rgb_red); + return kNoErr; +} + +static mx_status rgb_green_write_func(char* value) +{ + strcpy(prop.rgb_green, value); + app_log("write G:%s", prop.rgb_green); + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_RGBColor_Green); + return kNoErr; +} + +static mx_status rgb_green_read_func(char** value) +{ + (*value) = prop.rgb_green; + app_log("read G:%s", prop.rgb_green); + return kNoErr; +} + +static mx_status rgb_blue_write_func(char* value) +{ + strcpy(prop.rgb_blue, value); + app_log("write B:%s", prop.rgb_blue); + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_RGBColor_Blue); + return kNoErr; +} + +static mx_status rgb_blue_read_func(char** value) +{ + (*value) = prop.rgb_blue; + app_log("read B:%s", prop.rgb_blue); + return kNoErr; +} + +static const struct ilop_ica_attr_t LightSwitch_property = { ILOP_HANDLE_ATTR_LightSwitch, "LightSwitch", EMH_ARG_ILOP_VT_PROPERTY, light_switch_read_func, light_switch_write_func }; +static const struct ilop_ica_attr_t ColorTemperature_property = { ILOP_HANDLE_ATTR_ColorTemperature, "ColorTemperature", EMH_ARG_ILOP_VT_PROPERTY, color_temp_read_func, color_temp_write_func }; +static const struct ilop_ica_attr_t RGBColor_Red_property = { ILOP_HANDLE_ATTR_RGBColor_Red, "RGBColor.Red", EMH_ARG_ILOP_VT_PROPERTY, rgb_red_read_func, rgb_red_write_func }; +static const struct ilop_ica_attr_t RGBColor_Green_property = { ILOP_HANDLE_ATTR_RGBColor_Green, "RGBColor.Green", EMH_ARG_ILOP_VT_PROPERTY, rgb_green_read_func, rgb_green_write_func }; +static const struct ilop_ica_attr_t RGBColor_Blue_property = { ILOP_HANDLE_ATTR_RGBColor_Blue, "RGBColor.Blue", EMH_ARG_ILOP_VT_PROPERTY, rgb_blue_read_func, rgb_blue_write_func }; + +void property_register(void) +{ + memset(&prop, 0x00, sizeof(app_prop_t)); + strcpy(prop.switch_status, "0"); + strcpy(prop.color_temp, "4000"); + strcpy(prop.rgb_red, "0"); + strcpy(prop.rgb_green, "2"); + strcpy(prop.rgb_blue, "100"); + + ilop_ica_attr_register(&LightSwitch_property); + ilop_ica_attr_register(&ColorTemperature_property); + ilop_ica_attr_register(&RGBColor_Red_property); + ilop_ica_attr_register(&RGBColor_Green_property); + ilop_ica_attr_register(&RGBColor_Blue_property); + + //连上服务器后 上报状态 + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_LightSwitch); + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_ColorTemperature); + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_RGBColor_Red); + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_RGBColor_Green); + ilop_ica_attr_indicate_by_handle(ILOP_HANDLE_ATTR_RGBColor_Blue); +} diff --git a/examples/ilopicaapp/app_service.c b/examples/ilopicaapp/app_service.c new file mode 100644 index 0000000..10efc09 --- /dev/null +++ b/examples/ilopicaapp/app_service.c @@ -0,0 +1,17 @@ +#include "alicloud_ilop.h" +#include "main.h" +#include "mx_common.h" +#include "mx_debug.h" + +static mx_status timereset_write_func(char* value) +{ + app_log("TimeReset:%s", value); + return kNoErr; +} + +static const struct ilop_ica_attr_t TimeReset_service = { ILOP_HANDLE_ATTR_TimeReset, "TimeReset", EMH_ARG_ILOP_VT_SERVICE, NULL, timereset_write_func }; + +void service_register(void) +{ + ilop_ica_attr_register(&TimeReset_service); +} diff --git a/examples/ilopicaapp/ilopmain.c b/examples/ilopicaapp/ilopmain.c new file mode 100644 index 0000000..400891a --- /dev/null +++ b/examples/ilopicaapp/ilopmain.c @@ -0,0 +1,127 @@ +/** + ****************************************************************************** + * @file ilopmain.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief ILOP Service Main function + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include "alicloud_ilop.h" +#include "mx_common.h" +#include "mx_debug.h" + +#include "emh_api.h" +#include "main.h" +#include "mx_cli.h" +#include "mx_hal.h" + +const char thing[] = "{\"schema\":\"https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json\",\"profile\":{\"productKey\":\"a1MeLuIJPYL\"},\"services\":[{\"outputData\":[],\"identifier\":\"set\",\"inputData\":[{\"identifier\":\"LightSwitch\",\"dataType\":{\"specs\":{\"0\":\"关闭\",\"1\":\"开启\"},\"type\":\"bool\"},\"name\":\"主灯开关\"},{\"identifier\":\"ColorTemperature\",\"dataType\":{\"specs\":{\"unit\":\"K\",\"min\":\"2700\",\"unitName\":\"开尔文\",\"max\":\"6500\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"冷暖色温\"},{\"identifier\":\"NightLightSwitch\",\"dataType\":{\"specs\":{\"0\":\"关闭\",\"1\":\"开启\"},\"type\":\"bool\"},\"name\":\"夜灯开关\"},{\"identifier\":\"WorkMode\",\"dataType\":{\"specs\":{\"0\":\"手动\",\"1\":\"阅读\",\"2\":\"影院\",\"3\":\"夜灯\",\"4\":\"生活\",\"5\":\"柔和\"},\"type\":\"enum\"},\"name\":\"工作模式\"},{\"identifier\":\"RGBColor\",\"dataType\":{\"specs\":[{\"identifier\":\"Red\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"红色\"},{\"identifier\":\"Green\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"绿色\"},{\"identifier\":\"Blue\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"蓝色\"}],\"type\":\"struct\"},\"name\":\"RGB调色\"}],\"method\":\"thing.service.property.set\",\"name\":\"set\",\"required\":true,\"callType\":\"async\",\"desc\":\"属性设置\"},{\"outputData\":[{\"identifier\":\"LightSwitch\",\"dataType\":{\"specs\":{\"0\":\"关闭\",\"1\":\"开启\"},\"type\":\"bool\"},\"name\":\"主灯开关\"},{\"identifier\":\"ColorTemperature\",\"dataType\":{\"specs\":{\"unit\":\"K\",\"min\":\"2700\",\"unitName\":\"开尔文\",\"max\":\"6500\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"冷暖色温\"},{\"identifier\":\"NightLightSwitch\",\"dataType\":{\"specs\":{\"0\":\"关闭\",\"1\":\"开启\"},\"type\":\"bool\"},\"name\":\"夜灯开关\"},{\"identifier\":\"WorkMode\",\"dataType\":{\"specs\":{\"0\":\"手动\",\"1\":\"阅读\",\"2\":\"影院\",\"3\":\"夜灯\",\"4\":\"生活\",\"5\":\"柔和\"},\"type\":\"enum\"},\"name\":\"工作模式\"},{\"identifier\":\"RGBColor\",\"dataType\":{\"specs\":[{\"identifier\":\"Red\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"红色\"},{\"identifier\":\"Green\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"绿色\"},{\"identifier\":\"Blue\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"蓝色\"}],\"type\":\"struct\"},\"name\":\"RGB调色\"}],\"identifier\":\"get\",\"inputData\":[\"LightSwitch\",\"ColorTemperature\",\"NightLightSwitch\",\"WorkMode\",\"RGBColor\"],\"method\":\"thing.service.property.get\",\"name\":\"get\",\"required\":true,\"callType\":\"async\",\"desc\":\"属性获取\"},{\"outputData\":[],\"identifier\":\"TimeReset\",\"inputData\":[{\"identifier\":\"TimeReset\",\"dataType\":{\"specs\":{\"length\":\"255\"},\"type\":\"text\"},\"name\":\"TimeReset\"}],\"method\":\"thing.service.TimeReset\",\"name\":\"设备校时服务\",\"required\":false,\"callType\":\"async\"}],\"properties\":[{\"identifier\":\"LightSwitch\",\"dataType\":{\"specs\":{\"0\":\"关闭\",\"1\":\"开启\"},\"type\":\"bool\"},\"name\":\"主灯开关\",\"accessMode\":\"rw\",\"required\":true},{\"identifier\":\"ColorTemperature\",\"dataType\":{\"specs\":{\"unit\":\"K\",\"min\":\"2700\",\"unitName\":\"开尔文\",\"max\":\"6500\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"冷暖色温\",\"accessMode\":\"rw\",\"required\":false},{\"identifier\":\"NightLightSwitch\",\"dataType\":{\"specs\":{\"0\":\"关闭\",\"1\":\"开启\"},\"type\":\"bool\"},\"name\":\"夜灯开关\",\"accessMode\":\"rw\",\"required\":false},{\"identifier\":\"WorkMode\",\"dataType\":{\"specs\":{\"0\":\"手动\",\"1\":\"阅读\",\"2\":\"影院\",\"3\":\"夜灯\",\"4\":\"生活\",\"5\":\"柔和\"},\"type\":\"enum\"},\"name\":\"工作模式\",\"accessMode\":\"rw\",\"required\":false},{\"identifier\":\"RGBColor\",\"dataType\":{\"specs\":[{\"identifier\":\"Red\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"红色\"},{\"identifier\":\"Green\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"绿色\"},{\"identifier\":\"Blue\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"蓝色\"}],\"type\":\"struct\"},\"name\":\"RGB调色\",\"accessMode\":\"rw\",\"required\":false}],\"events\":[{\"outputData\":[{\"identifier\":\"LightSwitch\",\"dataType\":{\"specs\":{\"0\":\"关闭\",\"1\":\"开启\"},\"type\":\"bool\"},\"name\":\"主灯开关\"},{\"identifier\":\"ColorTemperature\",\"dataType\":{\"specs\":{\"unit\":\"K\",\"min\":\"2700\",\"unitName\":\"开尔文\",\"max\":\"6500\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"冷暖色温\"},{\"identifier\":\"NightLightSwitch\",\"dataType\":{\"specs\":{\"0\":\"关闭\",\"1\":\"开启\"},\"type\":\"bool\"},\"name\":\"夜灯开关\"},{\"identifier\":\"WorkMode\",\"dataType\":{\"specs\":{\"0\":\"手动\",\"1\":\"阅读\",\"2\":\"影院\",\"3\":\"夜灯\",\"4\":\"生活\",\"5\":\"柔和\"},\"type\":\"enum\"},\"name\":\"工作模式\"},{\"identifier\":\"RGBColor\",\"dataType\":{\"specs\":[{\"identifier\":\"Red\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"红色\"},{\"identifier\":\"Green\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"绿色\"},{\"identifier\":\"Blue\",\"dataType\":{\"specs\":{\"min\":\"0\",\"unitName\":\"无\",\"max\":\"255\",\"step\":\"1\"},\"type\":\"int\"},\"name\":\"蓝色\"}],\"type\":\"struct\"},\"name\":\"RGB调色\"}],\"identifier\":\"post\",\"method\":\"thing.event.property.post\",\"name\":\"post\",\"type\":\"info\",\"required\":true,\"desc\":\"属性上报\"},{\"outputData\":[{\"identifier\":\"ErrorCode\",\"dataType\":{\"specs\":{\"0\":\"恢复正常\"},\"type\":\"enum\"},\"name\":\"故障代码\"}],\"identifier\":\"Error\",\"method\":\"thing.event.Error.post\",\"name\":\"故障上报\",\"type\":\"error\",\"required\":true}]}"; + +static int awss_start = 0; +static int awss_press = 0; +static int reset = 0; + +const emh_ilop_config_t ilop_config = { + .tsl_thing = thing, + .tsl_len = strlen(thing), + .dm = EMH_ARG_ILOP_DM_ICA_BY_LOCAL, +}; + +const ilop_device_key_t device_key = { + .product_key = "a1MeLuIJPYL", + .product_secret = "SegyIuWW6xBQGuIc", + .device_name = "aQI0eEM5g2nlSr2njD0x", + .device_secret = "o7EZShzl6z8F3xq8qF3vN9P7HFGslRAV", +}; + +static void user_tast_loop(void) +{ + if (awss_start == 1) { + ilop_awss_start(); + awss_start = 0; + } + if (awss_press == 1) { + ilop_awss_press(); + awss_press = 0; + } + if (reset == 1) { + ilop_restore(); + reset = 0; + } +} + +#ifdef MX_CLI_ENABLE +static void handle_aws_cmd(char* pwbuf, int blen, int argc, char** argv) +{ + if (argc != 2) + return; + + if (strcmp(argv[1], "start") == 0) { + awss_start = 1; + } else if (strcmp(argv[1], "press") == 0) { + awss_press = 1; + } +} + +static void handle_reset_cmd(char* pwbuf, int blen, int argc, char** argv) +{ + reset = 1; +} + +static struct cli_command ilopcmds[] = { + { "aws", "aws [start|press]", handle_aws_cmd }, + { "reset", "clean wifi module and ilop service", handle_reset_cmd }, +}; +#endif + +void ilop_main(void) +{ + mx_status err = kNoErr; + + /* System initialization, ticker, stdio */ + mx_hal_ms_ticker_init(); + mx_hal_stdio_init(); + +#ifdef MX_CLI_ENABLE + cli_register_commands(ilopcmds, sizeof(ilopcmds) / sizeof(struct cli_command)); +#endif + + /* ILOP service initialization */ + err = ilop_init(&ilop_config); + if (err != kNoErr) + app_log("ilop init err"); + + /* Set the ILOP three tuple. If the module is pre burned, delete the function */ + ilop_set_device_key(&device_key); + + /* database initialization */ + ilop_ica_attr_init(ILOP_HANDLE_ATTR_MAX); + + property_register(); + service_register(); + event_register(); + + while (1) { + ilop_runloop(); + user_tast_loop(); + event_task_loop(); + } +} diff --git a/examples/ilopicaapp/main.c b/examples/ilopicaapp/main.c new file mode 100644 index 0000000..3c385b5 --- /dev/null +++ b/examples/ilopicaapp/main.c @@ -0,0 +1,41 @@ +/** + ****************************************************************************** + * @file main.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief Main function + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include "main.h" +#include "mx_common.h" +#include "mx_hal.h" + +int main(int argc, char* argv[]) +{ + if (argc != 2) { + printf("Usage: \"%s /dev/ttyUSB0\"\n", argv[0]); + return 0; + } + printf("dev[%s]\r\n", argv[1]); + mx_hal_uart_dev_set(argv[1]); + ilop_main(); + return 0; +} diff --git a/examples/ilopicaapp/main.h b/examples/ilopicaapp/main.h new file mode 100644 index 0000000..ded9208 --- /dev/null +++ b/examples/ilopicaapp/main.h @@ -0,0 +1,62 @@ +/** + ****************************************************************************** + * @file main.h + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief Application API header file + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef _MAIN_H_ +#define _MAIN_H_ + +/****************************************************************************** + * Macros + ******************************************************************************/ + +#define APP_DEBUG MX_DEBUG_ON +#define app_log(M, ...) MX_LOG(APP_DEBUG, "APP", M, ##__VA_ARGS__) + +/****************************************************************************** + * Enumerations + ******************************************************************************/ + +enum { + ILOP_HANDLE_ATTR_LightSwitch, + ILOP_HANDLE_ATTR_ColorTemperature, + ILOP_HANDLE_ATTR_RGBColor_Red, + ILOP_HANDLE_ATTR_RGBColor_Green, + ILOP_HANDLE_ATTR_RGBColor_Blue, + ILOP_HANDLE_ATTR_TimeReset, + ILOP_HANDLE_ATTR_ErrorCode, + ILOP_HANDLE_ATTR_MAX, +}; + +/****************************************************************************** + * Function Declarations + ******************************************************************************/ + +void ilop_main(void); +void property_register(void); +void service_register(void); +void event_register(void); +void event_task_loop(void); + +#endif diff --git a/examples/iloprawapp/app_event.c b/examples/iloprawapp/app_event.c new file mode 100644 index 0000000..86fac9f --- /dev/null +++ b/examples/iloprawapp/app_event.c @@ -0,0 +1,72 @@ +#include "alicloud_ilop.h" +#include "main.h" +#include "mx_cli.h" +#include "mx_common.h" +#include "mx_debug.h" +#include "mx_hal.h" + +static int event_post = 0; + +static unsigned long long uptime_sec(void) +{ + static unsigned long long start_time = 0; + + if (start_time == 0) { + start_time = mx_hal_ms_ticker_read(); + } + + return (mx_hal_ms_ticker_read() - start_time) / 1000; +} + +static mx_status error_code_read_func(ilop_att_val_t* value, uint32_t* outlen) +{ + (*value).boolValue = 0; + (*outlen) = sizeof((*value).boolValue); + app_log("read value:%d", (*value).boolValue); + return kNoErr; +} + +static const struct ilop_raw_attr_t ErrorCode_event = { ILOP_EVENT_ErrorCode, 0, TYPE_ID_BOOL, error_code_read_func, NULL }; + +#ifdef MX_CLI_ENABLE +static void handle_event_cmd(char* pwbuf, int blen, int argc, char** argv) +{ + if (argc != 2) + return; + + if (strcmp(argv[1], "enable") == 0) { + event_post = 1; + } else if (strcmp(argv[1], "disable") == 0) { + event_post = 0; + } +} + +static struct cli_command eventcmd = { "event", "event [enable|disable]", handle_event_cmd }; +#endif + +void event_register(void) +{ + ilop_raw_attr_register(ATTR_TYPE_EVENT, &ErrorCode_event); +#ifdef MX_CLI_ENABLE + cli_register_command(&eventcmd); +#endif +} + +void event_task_loop(void) +{ + if (event_post == 0) + return; + + uint32_t now = uptime_sec(); + static uint32_t pre_sec = 0; + + if (pre_sec == now) + return; + + if ((now % 10) == 0) { + app_log("time[%d]", now); + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_EVENT, ILOP_EVENT_ErrorCode); + } + + pre_sec = now; +} diff --git a/examples/iloprawapp/app_prop_light.c b/examples/iloprawapp/app_prop_light.c new file mode 100644 index 0000000..1559c3c --- /dev/null +++ b/examples/iloprawapp/app_prop_light.c @@ -0,0 +1,74 @@ +#include "alicloud_ilop.h" +#include "main.h" +#include "mx_common.h" +#include "mx_debug.h" + +static uint8_t switch_status = 0; +static uint16_t color_temp = 3000; +static uint8_t rgb_color[3] = { 0, 0, 0 }; + +static mx_status light_switch_write_func(ilop_att_val_t value, uint32_t inlen) +{ + switch_status = value.boolValue; + app_log("write value:%d", switch_status); + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_LightSwitch); + return kNoErr; +} + +static mx_status light_switch_read_func(ilop_att_val_t* value, uint32_t* outlen) +{ + (*value).boolValue = switch_status; + (*outlen) = sizeof(switch_status); + app_log("read value:%d", switch_status); + return kNoErr; +} + +static mx_status color_temp_write_func(ilop_att_val_t value, uint32_t inlen) +{ + color_temp = value.uint16Value; + app_log("write value:%d", color_temp); + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_ColorTemperature); + return kNoErr; +} + +static mx_status color_temp_read_func(ilop_att_val_t* value, uint32_t* outlen) +{ + (*value).uint16Value = color_temp; + (*outlen) = sizeof(color_temp); + app_log("read value:%d", color_temp); + return kNoErr; +} + +static mx_status rgb_color_write_func(ilop_att_val_t value, uint32_t inlen) +{ + rgb_color[0] = value.dataValue[0]; + rgb_color[1] = value.dataValue[1]; + rgb_color[2] = value.dataValue[2]; + app_log("write[%d] R[%d] G[%d] B[%d]", inlen, rgb_color[0], rgb_color[1], rgb_color[2]); + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_RGBColor); + return kNoErr; +} + +static mx_status rgb_color_read_func(ilop_att_val_t* value, uint32_t* outlen) +{ + (*value).dataValue = rgb_color; + (*outlen) = sizeof(rgb_color); + app_log("read[%d] R[%d] G[%d] B[%d]", (*outlen), (*value).dataValue[0], (*value).dataValue[1], (*value).dataValue[2]); + return kNoErr; +} + +static const struct ilop_raw_attr_t LightSwitch_property = { ILOP_PROPERTY_LightSwitch, 0, TYPE_ID_BOOL, light_switch_read_func, light_switch_write_func }; +static const struct ilop_raw_attr_t ColorTemperature_property = { ILOP_PROPERTY_ColorTemperature, 6, TYPE_ID_UINT16, color_temp_read_func, color_temp_write_func }; +static const struct ilop_raw_attr_t RGBColor_property = { ILOP_PROPERTY_RGBColor, 7, 30, rgb_color_read_func, rgb_color_write_func }; + +void property_register_light(void) +{ + ilop_raw_attr_register(ATTR_TYPE_PROPERTY, &LightSwitch_property); + ilop_raw_attr_register(ATTR_TYPE_PROPERTY, &ColorTemperature_property); + ilop_raw_attr_register(ATTR_TYPE_PROPERTY, &RGBColor_property); + + //连上服务器后 上报状态 + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_LightSwitch); + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_ColorTemperature); + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_RGBColor); +} diff --git a/examples/iloprawapp/app_prop_wifi.c b/examples/iloprawapp/app_prop_wifi.c new file mode 100644 index 0000000..aa2bd1c --- /dev/null +++ b/examples/iloprawapp/app_prop_wifi.c @@ -0,0 +1,102 @@ +#include "alicloud_ilop.h" +#include "main.h" +#include "mx_common.h" +#include "mx_debug.h" + +static char* wifi_band = "2.4G"; +static int8_t wifi_rssi = -40; +static char* wifi_bssid = "00:00:00:00:00:01"; +static uint8_t wifi_channel = 6; +static int8_t wifi_snr = 100; + +static mx_status wifi_band_read_func(ilop_att_val_t* value, uint32_t* outlen) +{ + (*value).stringValue = wifi_band; + (*outlen) = strlen(wifi_band); + app_log("read value:%s", wifi_band); + return kNoErr; +} + +static mx_status wifi_band_write_func(ilop_att_val_t value, uint32_t inlen) +{ + app_log("WIFI_BAND[%d]:%.*s", inlen, inlen, value.stringValue); + return kNoErr; +} + +static mx_status wifi_rssi_read_func(ilop_att_val_t* value, uint32_t* outlen) +{ + (*value).int8Value = wifi_rssi; + (*outlen) = sizeof(wifi_rssi); + app_log("read value:%d", wifi_rssi); + return kNoErr; +} + +static mx_status wifi_rssi_write_func(ilop_att_val_t value, uint32_t inlen) +{ + app_log("WIFI_RSSI[%d]", value.int8Value); + return kNoErr; +} + +static mx_status wifi_ap_bssid_read_func(ilop_att_val_t* value, uint32_t* outlen) +{ + (*value).stringValue = wifi_bssid; + (*outlen) = strlen(wifi_bssid); + app_log("read value:%s", wifi_bssid); + return kNoErr; +} + +static mx_status wifi_ap_bssid_write_func(ilop_att_val_t value, uint32_t inlen) +{ + app_log("WIFI_AP_BSSID[%d]:%.*s", inlen, inlen, value.stringValue); + return kNoErr; +} + +static mx_status wifi_channel_read_func(ilop_att_val_t* value, uint32_t* outlen) +{ + (*value).uint8Value = wifi_channel; + (*outlen) = sizeof(wifi_channel); + app_log("read value:%d", wifi_channel); + return kNoErr; +} + +static mx_status wifi_channel_write_func(ilop_att_val_t value, uint32_t inlen) +{ + app_log("WIFI_CHANNEL[%d]", value.uint8Value); + return kNoErr; +} + +static mx_status wifi_snr_read_func(ilop_att_val_t* value, uint32_t* outlen) +{ + (*value).int8Value = wifi_snr; + (*outlen) = sizeof(wifi_snr); + app_log("read value:%d", wifi_snr); + return kNoErr; +} + +static mx_status wifi_snr_write_func(ilop_att_val_t value, uint32_t inlen) +{ + app_log("WIFI_SNR[%d]", value.int8Value); + return kNoErr; +} + +static const struct ilop_raw_attr_t WIFI_Band_property = { ILOP_PROPERTY_WIFI_Band, 1, TYPE_ID_STRING, wifi_band_read_func, wifi_band_write_func }; +static const struct ilop_raw_attr_t WiFI_RSSI_property = { ILOP_PROPERTY_WiFI_RSSI, 2, TYPE_ID_INT8, wifi_rssi_read_func, wifi_rssi_write_func }; +static const struct ilop_raw_attr_t WIFI_AP_BSSID_property = { ILOP_PROPERTY_WIFI_AP_BSSID, 3, TYPE_ID_STRING, wifi_ap_bssid_read_func, wifi_ap_bssid_write_func }; +static const struct ilop_raw_attr_t WIFI_Channel_property = { ILOP_PROPERTY_WIFI_Channel, 4, TYPE_ID_UINT8, wifi_channel_read_func, wifi_channel_write_func }; +static const struct ilop_raw_attr_t WiFI_SNR_property = { ILOP_PROPERTY_WiFI_SNR, 5, TYPE_ID_INT8, wifi_snr_read_func, wifi_snr_write_func }; + +void property_register_wifi(void) +{ + ilop_raw_attr_register(ATTR_TYPE_PROPERTY, &WIFI_Band_property); + ilop_raw_attr_register(ATTR_TYPE_PROPERTY, &WiFI_RSSI_property); + ilop_raw_attr_register(ATTR_TYPE_PROPERTY, &WIFI_AP_BSSID_property); + ilop_raw_attr_register(ATTR_TYPE_PROPERTY, &WIFI_Channel_property); + ilop_raw_attr_register(ATTR_TYPE_PROPERTY, &WiFI_SNR_property); + + //连上服务器后 上报状态 + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_WIFI_Band); + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_WiFI_RSSI); + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_WIFI_AP_BSSID); + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_WIFI_Channel); + ilop_raw_attr_indicate_by_handle(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_WiFI_SNR); +} diff --git a/examples/iloprawapp/app_service.c b/examples/iloprawapp/app_service.c new file mode 100644 index 0000000..8360980 --- /dev/null +++ b/examples/iloprawapp/app_service.c @@ -0,0 +1,17 @@ +#include "alicloud_ilop.h" +#include "main.h" +#include "mx_common.h" +#include "mx_debug.h" + +static mx_status timereset_write_func(ilop_att_val_t value, uint32_t inlen) +{ + app_log("TimeReset[%d]:%s", inlen, (char*)value.dataValue); + return kNoErr; +} + +static const struct ilop_raw_attr_t TimeReset_service = { ILOP_SERVICE_TimeReset, 0, TYPE_ID_STRING, NULL, timereset_write_func }; + +void service_register(void) +{ + ilop_raw_attr_register(ATTR_TYPE_SERVICE, &TimeReset_service); +} diff --git a/examples/iloprawapp/ilopmain.c b/examples/iloprawapp/ilopmain.c new file mode 100644 index 0000000..89f1df5 --- /dev/null +++ b/examples/iloprawapp/ilopmain.c @@ -0,0 +1,104 @@ +#include "alicloud_ilop.h" +#include "emh_api.h" +#include "main.h" +#include "mx_cli.h" +#include "mx_common.h" +#include "mx_debug.h" +#include "mx_hal.h" + +static int awss_start = 0; +static int awss_press = 0; +static int reset = 0; + +const emh_ilop_config_t ilop_config = { + .tls_thing = NULL, + .tls_len = 0, + .dm = EMH_ARG_ILOP_DM_RAW, +}; + +const ilop_device_key_t device_key = { + .product_key = "b1CcQqrXoVR", + .product_secret = "yOrZrmtDcHLgVOJc", + .device_name = "sPhDWW7vvaSyUfNBOvM9", + .device_secret = "R90glnJXJNAUsKGSRRudMdlhyvA12OEM", +}; + +static void awss_tast_loop(void) +{ + if (awss_start == 1) { + ilop_awss_start(); + awss_start = 0; + } + if (awss_press == 1) { + ilop_awss_press(); + awss_press = 0; + } + if( reset == 1 ) + { + ilop_restore(); + reset = 0; + } +} + +#ifdef MX_CLI_ENABLE +static void handle_aws_cmd(char* pwbuf, int blen, int argc, char** argv) +{ + if (argc != 2) + return; + + if (strcmp(argv[1], "start") == 0) { + awss_start = 1; + } else if (strcmp(argv[1], "press") == 0) { + awss_press = 1; + } +} + +static void handle_reset_cmd(char* pwbuf, int blen, int argc, char** argv) +{ + reset = 1; +} + +static struct cli_command ilopcmds[] = { + { "aws", "aws [start|press]", handle_aws_cmd }, + { "reset", "clean wifi module and ilop service", handle_reset_cmd }, +}; +#endif + +void ilop_main(void) +{ + mx_status err = kNoErr; + + /* System initialization, ticker, stdio */ + mx_hal_ms_ticker_init(); + mx_hal_stdio_init(); + +#ifdef MX_CLI_ENABLE + cli_register_commands(ilopcmds, sizeof(ilopcmds) / sizeof(struct cli_command)); +#endif + + /* ILOP service initialization */ + err = ilop_init(&ilop_config); + if (err != kNoErr) + app_log("ilop init err"); + + /* Set the ILOP three tuple. If the module is pre burned, delete the function */ + ilop_set_device_key(&device_key); + + /* database initialization */ + ilop_raw_attr_init(ATTR_TYPE_PROPERTY, ILOP_PROPERTY_MAX); + ilop_raw_attr_init(ATTR_TYPE_EVENT, ILOP_EVENT_MAX); + ilop_raw_attr_init(ATTR_TYPE_SERVICE, ILOP_SERVICE_MAX); + + event_register(); + property_register_light(); + property_register_wifi(); + service_register(); + + while (1) { + ilop_runloop(); + awss_tast_loop(); + event_task_loop(); + } + + return; +} diff --git a/examples/iloprawapp/main.c b/examples/iloprawapp/main.c new file mode 100644 index 0000000..99690f2 --- /dev/null +++ b/examples/iloprawapp/main.c @@ -0,0 +1,15 @@ +#include "main.h" +#include "mx_common.h" +#include "mx_hal.h" + +int main(int argc, char* argv[]) +{ + if (argc != 2) { + printf("Usage: \"%s /dev/ttyUSB0\"\n", argv[0]); + return 0; + } + printf("dev[%s]\r\n", argv[1]); + mx_hal_uart_dev_set(argv[1]); + ilop_main(); + return 0; +} diff --git a/examples/iloprawapp/main.h b/examples/iloprawapp/main.h new file mode 100644 index 0000000..cd8717d --- /dev/null +++ b/examples/iloprawapp/main.h @@ -0,0 +1,43 @@ + +#ifndef _MAIN_H_ +#define _MAIN_H_ + +#define APP_DEBUG MX_DEBUG_ON +#define app_log(M, ...) MX_LOG(APP_DEBUG, "APP", M, ##__VA_ARGS__) + +//property +enum { + ILOP_PROPERTY_LightSwitch, + ILOP_PROPERTY_WIFI_Band, + ILOP_PROPERTY_WiFI_RSSI, + ILOP_PROPERTY_WIFI_AP_BSSID, + ILOP_PROPERTY_WIFI_Channel, + ILOP_PROPERTY_WiFI_SNR, + ILOP_PROPERTY_ColorTemperature, + ILOP_PROPERTY_RGBColor, + ILOP_PROPERTY_MAX, +}; + +//event +enum { + ILOP_EVENT_ErrorCode, + ILOP_EVENT_MAX, +}; + +//sevice +enum { + ILOP_SERVICE_TimeReset, + ILOP_SERVICE_MAX, +}; + +void ilop_main(void); + +void event_register(void); +void event_task_loop(void); + +void property_register_light(void); +void property_register_wifi(void); + +void service_register(void); + +#endif diff --git a/examples/sdsapp/sdsapp.c b/examples/sdsapp/sdsapp.c new file mode 100644 index 0000000..acebe2f --- /dev/null +++ b/examples/sdsapp/sdsapp.c @@ -0,0 +1,90 @@ +#include "alicloud_sds.h" +#include "mx_common.h" +#include "mx_debug.h" + +#include "emh_api.h" +#include "mx_hal.h" + +#define APP_DEBUG MX_DEBUG_ON +#define app_log(M, ...) MX_LOG(APP_DEBUG, "APP", M, ##__VA_ARGS__) + +static int light_on = 0; + +enum { + ALI_HANDLE_LIGHT_SWITCH, + ALI_HANDLE_MAX, +}; + +const emh_alisds_config_t alisds_config = { + .product_info = { + .name = "alink_product", + .module = "ALINKTEST_LIVING_LIGHT_ALINK_TEST", + .key = "5gPFl8G4GyFZ1fPWk20m", + .secret = "ngthgTlZ65bX5LpViKIWNsDPhOf2As9ChnoL9gQb", + .format = EMH_ARG_ALISDS_FORMAT_JSON, + }, + .dev_info = { + .type = "LIGHT", + .category = "LIVING", + .manufacture = "ALINKTEST", + } +}; + +void emh_ev_alisds_get_local_atts(emh_alisds_msg* attrs) +{ + alisds_attr_indicate_by_handle(ALI_HANDLE_LIGHT_SWITCH); +} + +mx_status handle_read_cur_light_switch(alisds_att_val_t* value) +{ + (*value).boolValue = light_on; + app_log("read switch:%d\r\n", light_on); + return kNoErr; +} + +mx_status handle_write_cur_light_switch(alisds_att_val_t value) +{ + light_on = value.boolValue; + app_log("write switch:%d\r\n", light_on); + return kNoErr; +} + +void switch_task_init(void) +{ + alisds_attr_t attr; + + attr.name = "Switch"; + attr.type = ALI_ATT_TYPE_BOOL; + attr.read_func = handle_read_cur_light_switch; + attr.write_func = handle_write_cur_light_switch; + alisds_attr_init(ALI_HANDLE_LIGHT_SWITCH, attr); +} + +static void sds_task(void) +{ + mx_status err = kNoErr; + + err = alisds_init(&alisds_config, 1); + require_noerr(err, exit); + + switch_task_init(); + + while (1) { + alisds_runloop(); + } + +exit: + return; +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) { + printf("Usage: \"%s /dev/ttyUSB0\"\n", argv[0]); + return 0; + } + printf("dev[%s]\r\n", argv[1]); + mx_hal_uart_dev_set(argv[1]); + sds_task(); + return 0; +} diff --git a/examples/sdsapp/sdsapp.mk b/examples/sdsapp/sdsapp.mk new file mode 100644 index 0000000..360dba2 --- /dev/null +++ b/examples/sdsapp/sdsapp.mk @@ -0,0 +1,13 @@ +NAME := sdsapp + +# at_support_alisds := 1 + +$(NAME)_SOURCES := sdsapp.c + +$(NAME)_COMPONENTS := yloop cli mxathost + +GLOBAL_DEFINES += AOS_NO_WIFI MX_DEBUG + +ifeq ($(at_support_alisds),1) +GLOBAL_DEFINES += AT_SUPPORT_ALISDS +endif diff --git a/examples/test/test.c b/examples/test/test.c new file mode 100644 index 0000000..57a61d5 --- /dev/null +++ b/examples/test/test.c @@ -0,0 +1,40 @@ +#include "mx_common.h" +#include "mx_debug.h" + +#include "emh_api.h" +#include "mx_hal.h" + +#define APP_DEBUG MX_DEBUG_ON +#define app_log(M, ...) MX_LOG(APP_DEBUG, "APP", M, ##__VA_ARGS__) + +static void at_task(void) +{ + mx_status err = kNoErr; + + mx_hal_ms_ticker_init(); + mx_hal_stdio_init(); + + err = emh_init(); + if (err == kNoErr) { + app_log("FW version: %s", emh_module_get_fw_version()); + app_log("System tick: %d", (int)emh_module_get_tick()); + } + + while (1) { + emh_runloop(); + } + + return; +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) { + printf("Usage: \"%s /dev/ttyUSB0\"\n", argv[0]); + return 0; + } + printf("dev[%s]\r\n", argv[1]); + mx_hal_uart_dev_set(argv[1]); + at_task(); + return 0; +} \ No newline at end of file diff --git a/platform/linux/mx_serial.c b/platform/linux/mx_serial.c new file mode 100644 index 0000000..dd4abdd --- /dev/null +++ b/platform/linux/mx_serial.c @@ -0,0 +1,158 @@ +/** + ****************************************************************************** + * @file mx_serial.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief UART driver used for AT parser + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include +#include "mx_common.h" +#include "mx_hal.h" +#include "mx_ringbuffer.h" + +/****************************************************************************** + * Variable Definitions + ******************************************************************************/ + +static int _timeout = 100; +static uint8_t at_buffer[MX_SERIAL_RX_BUF_SIZE]; +static struct ringbuffer at_rx; +static pthread_mutex_t at_mtx; + +/****************************************************************************** + * Type Definitions + ******************************************************************************/ + +static uart_dev_t uart_dev = { + AT_UART_PORT, + { + AT_UART_BAUDRATE, + DATA_WIDTH_8BIT, + NO_PARITY, + STOP_BITS_1, + FLOW_CONTROL_DISABLED, + MODE_TX_RX, + }, +}; + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +static void* uart_recv_task(void* arg) +{ + int ret = 0; + uint8_t RXData; + uint32_t recv_len = 0; + + while (1) { + ret = mx_hal_uart_recv(&uart_dev, &RXData, 1, &recv_len, _timeout); + if ((ret == 0) && (recv_len == 1)) { + // printf("%c\r\n", RXData); + pthread_mutex_lock(&at_mtx); + ringbuffer_put(&at_rx, RXData); + pthread_mutex_unlock(&at_mtx); + } + } + return NULL; +} + +static int32_t at_async_read(uint8_t* const buf, const uint16_t length) +{ + uint16_t was_read = 0; + uint32_t num; + + if (buf == 0 || length == 0) + return 0; + + pthread_mutex_lock(&at_mtx); + num = ringbuffer_num(&at_rx); + pthread_mutex_unlock(&at_mtx); + + while ((was_read < num) && (was_read < length)) { + ringbuffer_get(&at_rx, &buf[was_read++]); + } + + return (int32_t)was_read; +} + +void mx_hal_serial_init(int timeout) +{ + mx_status err; + _timeout = timeout; + pthread_t ntid; + + ringbuffer_init(&at_rx, at_buffer, MX_SERIAL_RX_BUF_SIZE); + + err = mx_hal_uart_init(&uart_dev); + require_noerr(err, exit); + + pthread_mutex_init(&at_mtx, NULL); + + err = pthread_create(&ntid, NULL, uart_recv_task, NULL); + require_noerr(err, exit); + +exit: + return; +} + +void mx_hal_serial_set_timeout(int timeout) +{ + _timeout = timeout; +} + +int mx_hal_serial_putc(char c) +{ + return mx_hal_uart_send(&uart_dev, &c, 1, _timeout); +} + +int mx_hal_serial_getc(void) +{ + uint32_t current = mx_hal_ms_ticker_read(); + uint8_t ch; + + do { + if (at_async_read(&ch, 1) == 1) + return ch; + } while ((mx_hal_ms_ticker_read() - current) < _timeout); + + return -1; +} + +bool mx_hal_serial_readable(void) +{ + if (ringbuffer_num(&at_rx)) + return true; + return false; +} + +void mx_hal_serial_flush(void) +{ + uint32_t num; + uint8_t tmp; + + pthread_mutex_lock(&at_mtx); + for (num = ringbuffer_num(&at_rx); num > 0; num--) { + ringbuffer_get(&at_rx, &tmp); + } + pthread_mutex_unlock(&at_mtx); +} diff --git a/platform/linux/mx_stdio.c b/platform/linux/mx_stdio.c new file mode 100644 index 0000000..1256c83 --- /dev/null +++ b/platform/linux/mx_stdio.c @@ -0,0 +1,143 @@ +/** + ****************************************************************************** + * @file mx_stdio.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief Initialize uart for printf output + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ +#include + +#include "mx_common.h" +#include "mx_hal.h" +#include "mx_ringbuffer.h" + +/****************************************************************************** + * Variable Definitions + ******************************************************************************/ + +static uint8_t cli_buffer[MX_CLI_RX_BUF_SIZE]; +static struct ringbuffer stdio_rx; +static pthread_mutex_t stdio_mtx; +extern int cli_init(void); + +/****************************************************************************** + * Type Definitions + ******************************************************************************/ + +static uart_dev_t cli_uart_dev = { + STDIO_UART_PORT, + { + STDIO_UART_BAUDRATE, + DATA_WIDTH_8BIT, + NO_PARITY, + STOP_BITS_1, + FLOW_CONTROL_DISABLED, + MODE_TX_RX, + }, +}; + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +#ifdef MX_CLI_ENABLE +static void* cli_recv_task(void* arg) +{ + int ret = 0; + uint8_t RXData; + uint32_t recv_len = 0; + + while (1) { + ret = mx_hal_uart_recv(&cli_uart_dev, &RXData, 1, &recv_len, 0); + if ((ret == 0) && (recv_len == 1)) { + pthread_mutex_lock(&stdio_mtx); + ringbuffer_put(&stdio_rx, RXData); + pthread_mutex_unlock(&stdio_mtx); + } + } + return NULL; +} + +static int32_t at_async_read(uint8_t* const buf, const uint16_t length) +{ + uint16_t was_read = 0; + uint32_t num; + + if (buf == 0 || length == 0) + return 0; + + pthread_mutex_lock(&stdio_mtx); + num = ringbuffer_num(&stdio_rx); + pthread_mutex_unlock(&stdio_mtx); + + while ((was_read < num) && (was_read < length)) { + ringbuffer_get(&stdio_rx, &buf[was_read++]); + } + + return (int32_t)was_read; +} +#endif + +void mx_hal_stdio_init(void) +{ + mx_status err; + + ringbuffer_init(&stdio_rx, cli_buffer, MX_CLI_RX_BUF_SIZE); + + err = mx_hal_uart_init(&cli_uart_dev); + require_noerr(err, exit); + +#ifdef MX_CLI_ENABLE + pthread_t ntid; + + pthread_mutex_lock(&stdio_mtx); + + err = pthread_create(&ntid, NULL, cli_recv_task, NULL); + require_noerr(err, exit); + + err = cli_init(); + require_noerr(err, exit); +#endif + +exit: + return; +} + +#ifdef MX_CLI_ENABLE +int mx_hal_cli_putstr(const char* str, uint32_t strlen) +{ + int ret = 0; + ret = mx_hal_uart_send(&cli_uart_dev, str, strlen, 0); + return ret; +} + +int mx_hal_cli_getc(void) +{ + uint8_t ch; + + if (at_async_read(&ch, 1) == 1) { + return ch; + } else { + return -1; + } +} + +#endif diff --git a/platform/linux/mx_tick.c b/platform/linux/mx_tick.c new file mode 100644 index 0000000..e4b4634 --- /dev/null +++ b/platform/linux/mx_tick.c @@ -0,0 +1,60 @@ +/** + ****************************************************************************** + * @file mx_tick.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief Tick for delay and timeout + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include +#include +#include +#include "mx_common.h" + +/****************************************************************************** + * Variable Definitions + ******************************************************************************/ + +static struct timeval sys_start_time; + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +void mx_hal_ms_ticker_init(void) +{ + return; +} + +uint32_t mx_hal_ms_ticker_read(void) +{ + struct timeval tv; + uint32_t ms; + gettimeofday(&tv, NULL); + timersub(&tv, &sys_start_time, &tv); + ms = (uint32_t)(tv.tv_sec * 1000LL) + tv.tv_usec / 1000; + return ms; +} + +void mx_hal_delay_ms(volatile uint32_t delay) +{ + usleep(delay * 1000); +} diff --git a/platform/linux/mx_uart.c b/platform/linux/mx_uart.c new file mode 100644 index 0000000..dca8dcf --- /dev/null +++ b/platform/linux/mx_uart.c @@ -0,0 +1,241 @@ +/** + ****************************************************************************** + * @file mx_uart.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief UART driver function + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "mx_common.h" +#include "mx_hal.h" + +/****************************************************************************** + * Variable Definitions + ******************************************************************************/ + +#define LINUX_UART_LINUX_DEV "/dev/ttyUSB1" + +static char linux_dev[15] = { 0 }; +static int at_uart_fd = -1; + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +static int read_and_discard_all_data(const int fd) +{ + int was_msg_already_printed = 0; + + while (1) { + char buffer[1024]; + const ssize_t read_count = read(fd, buffer, sizeof(buffer)); + + if (read_count == 0) { + /* "EOF" or "connection closed at the other end"*/ + return 0; + } + + if (read_count > 0) { + if (!was_msg_already_printed) { + printf("Some stale data was discarded.\r\n"); + was_msg_already_printed = 1; + } + + continue; + } + + assert(read_count == -1); /* According to the specification. */ + + const int errno_code = errno; + + if (errno_code == EINTR) + continue; + + if (errno_code == EAGAIN || errno_code == EWOULDBLOCK) { + /** + * We know that the file descriptor has been opened with + * O_NONBLOCK or O_NDELAY, and these codes mean that there + * is no data to read at present. + */ + return 0; + } + + /* Some other error has occurred. */ + return -1; + } +} + +void mx_hal_uart_dev_set(char* dev) +{ + strncpy(linux_dev, dev, 15); +} + +int32_t mx_hal_uart_init(uart_dev_t* uart) +{ + int fd; + struct termios t_opt; + speed_t baud; + + if (uart->port != AT_UART_PORT) + return 0; + + if ((at_uart_fd = open(linux_dev, + O_RDWR | O_NOCTTY | O_NDELAY)) + == -1) { + printf("open at uart failed\r\n"); + return -1; + } + + switch (uart->config.baud_rate) { + case 115200: + baud = B115200; + break; + default: + baud = B115200; + break; + } + + fd = at_uart_fd; + /* set the serial port parameters */ + fcntl(fd, F_SETFL, 0); + if (0 != tcgetattr(fd, &t_opt)) + return -1; + + if (0 != cfsetispeed(&t_opt, baud)) + return -1; + + if (0 != cfsetospeed(&t_opt, baud)) + return -1; + + // 8N1, flow control, etc. + t_opt.c_cflag |= (CLOCAL | CREAD); + if (uart->config.parity == NO_PARITY) + t_opt.c_cflag &= ~PARENB; + if (uart->config.stop_bits == STOP_BITS_1) + t_opt.c_cflag &= ~CSTOPB; + else + t_opt.c_cflag |= CSTOPB; + t_opt.c_cflag &= ~CSIZE; + switch (uart->config.data_width) { + case DATA_WIDTH_5BIT: + t_opt.c_cflag |= CS5; + break; + case DATA_WIDTH_6BIT: + t_opt.c_cflag |= CS6; + break; + case DATA_WIDTH_7BIT: + t_opt.c_cflag |= CS7; + break; + case DATA_WIDTH_8BIT: + t_opt.c_cflag |= CS8; + break; + default: + t_opt.c_cflag |= CS8; + break; + } + t_opt.c_lflag &= ~(ECHO | ECHOE | ISIG | ICANON); + if (uart->config.flow_control == FLOW_CONTROL_DISABLED) + t_opt.c_cflag &= ~CRTSCTS; + + /** + * AT is going to use a binary protocol, so make sure to + * turn off any CR/LF translation and the like. + */ + t_opt.c_iflag &= ~(IXON | IXOFF | IXANY | INLCR | ICRNL); + + t_opt.c_oflag &= ~OPOST; + t_opt.c_cc[VMIN] = 0; + t_opt.c_cc[VTIME] = 5; + + if (0 != tcsetattr(fd, TCSANOW, &t_opt)) { + return -1; + } + + printf("open at uart succeed\r\n"); + + // clear uart buffer + read_and_discard_all_data(fd); + + return 0; +} + +int32_t mx_hal_uart_send(uart_dev_t* uart, const void* data, + uint32_t size, uint32_t timeout) +{ + uint32_t ret, rmd = size; + + if (uart->port == AT_UART_PORT) { + while (rmd > 0) { + ret = write(at_uart_fd, data + size - rmd, rmd); + if (ret == -1) { + printf("write uart fd failed on error: %d.\r\n", errno); + return -1; + } + rmd -= ret; + } + } else + ret = write(0, data, size); + return 0; +} + +int32_t mx_hal_uart_recv(uart_dev_t* uart, void* data, uint32_t expect_size, + uint32_t* recv_size, uint32_t timeout) +{ + int fd, n; + + if (uart->port == AT_UART_PORT) + fd = at_uart_fd; + else + fd = 0; + + if ((n = read(fd, data, expect_size)) == -1) { + // printf("read failed\r\n"); + return -1; + } + + if (uart->port != AT_UART_PORT && *(char*)data == '\n') + *(char*)data = '\r'; + if (recv_size) + *recv_size = n; + + return 0; +} + +int32_t mx_hal_uart_finalize(uart_dev_t* uart) +{ + if (uart->port == AT_UART_PORT) + close(at_uart_fd); + return 0; +} + diff --git a/platform/mx_hal.h b/platform/mx_hal.h new file mode 100644 index 0000000..e326c55 --- /dev/null +++ b/platform/mx_hal.h @@ -0,0 +1,315 @@ +/** + ****************************************************************************** + * @file mx_hal.h + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief MCU peripheral driver PI header file + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef _MX_HAL_H_ +#define _MX_HAL_H_ + +#include "mx_common.h" +#include "mx_debug.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************** + * Macros + ******************************************************************************/ + +#define AT_UART_PORT (0) +#define AT_UART_BAUDRATE (115200) + +#define STDIO_UART_PORT (1) +#define STDIO_UART_BAUDRATE (115200) + +/****************************************************************************** + * UART Function + ******************************************************************************/ + +/* + * UART data width + */ +typedef enum { + DATA_WIDTH_5BIT, + DATA_WIDTH_6BIT, + DATA_WIDTH_7BIT, + DATA_WIDTH_8BIT, + DATA_WIDTH_9BIT +} mx_hal_uart_data_width_t; + +/* + * UART stop bits + */ +typedef enum { + STOP_BITS_1, + STOP_BITS_2 +} mx_hal_uart_stop_bits_t; + +/* + * UART flow control + */ +typedef enum { + FLOW_CONTROL_DISABLED, + FLOW_CONTROL_CTS, + FLOW_CONTROL_RTS, + FLOW_CONTROL_CTS_RTS +} mx_hal_uart_flow_control_t; + +/* + * UART parity + */ +typedef enum { + NO_PARITY, + ODD_PARITY, + EVEN_PARITY +} mx_hal_uart_parity_t; + +/* + * UART mode + */ +typedef enum { + MODE_TX, + MODE_RX, + MODE_TX_RX +} mx_hal_uart_mode_t; + +/* + * UART configuration + */ +typedef struct { + uint32_t baud_rate; + mx_hal_uart_data_width_t data_width; + mx_hal_uart_parity_t parity; + mx_hal_uart_stop_bits_t stop_bits; + mx_hal_uart_flow_control_t flow_control; + mx_hal_uart_mode_t mode; +} uart_config_t; + +typedef struct { + uint8_t port; /* uart port */ + uart_config_t config; /* uart config */ +} uart_dev_t; + +/** + * + * @brief Set up serial port device description + * @note only linux platform + * + * param[in] dev: device description, like '/dev/ttyUSB0' + * + * @return none + * + */ +void mx_hal_uart_dev_set(char* dev); + +/** + * + * @brief Initialises a UART interface + * + * param[in] uart: the interface which should be initialised + * + * @return status + * + */ +int32_t mx_hal_uart_init(uart_dev_t* uart); + +/** + * + * @brief Deinitialises a UART interface + * + * param[in] uart: the interface which should be deinitialised + * + * @return status + * + */ +int32_t mx_hal_uart_finalize(uart_dev_t* uart); + +/** + * + * @brief Transmit data on a UART interface + * + * param[in] uart : the UART interface + * param[in] data : pointer to the start of data + * param[in] size : number of bytes to transmit + * param[in] timeout : timeout in milisecond + * + * @return status + * + */ +int32_t mx_hal_uart_send(uart_dev_t* uart, const void* data, uint32_t size, uint32_t timeout); + +/** + * + * @brief Receive data on a UART interface + * + * param[in] uart : the UART interface + * param[in] data : pointer to the buffer which will store incoming data + * param[in] expect_size : number of bytes to Expected data received + * param[out] recv_size : number of bytes to Actual data received + * param[in] timeout : timeout in milisecond + * + * @return status + * + */ +int32_t mx_hal_uart_recv(uart_dev_t* uart, void* data, uint32_t expect_size, uint32_t* recv_size, uint32_t timeout); + +/****************************************************************************** + * Serial Function + * Serial port driver used for AT parser + ******************************************************************************/ + +/** + * + * @brief Initialises AT instruction port + * + * param[in] timeout: timeout in milisecond + * + * @return status + * + */ +void mx_hal_serial_init(int timeout); + +/** + * + * @brief AT instruction port receiving timeout time + * + * param[in] timeout : timeout in milisecond + * + * @return status + * + */ +void mx_hal_serial_set_timeout(int timeout); + +/** + * + * @brief AT instruction port output + * + * param[in] c : Output one byte + * + * @return status + * + */ +int mx_hal_serial_putc(char c); + +/** + * + * @brief AT instruction port input + * + * @return byte + * + */ +int mx_hal_serial_getc(void); + +/** + * + * @brief AT instruction port status + * + * @return status + * + */ +bool mx_hal_serial_readable(void); + +/** + * + * @brief AT instruction port flush + * + * @return none + * + */ +void mx_hal_serial_flush(void); + +/****************************************************************************** + * Stdio Function + * For debug info + ******************************************************************************/ + +/** + * + * @brief Initialises Debug port + * + * @return none + * + */ +void mx_hal_stdio_init(void); + +/** + * + * @brief Command line output + * + * param[in] c : Output one byte + * + * @return status + * + */ +int mx_hal_cli_putstr(const char* str, uint32_t strlen); + +/** + * + * @brief Command line intput + * + * @return byte + * + */ +int mx_hal_cli_getc(void); + +/****************************************************************************** + * Ticker Function + * Tick used for delay and timeout mechanism + ******************************************************************************/ + +/** + * + * @brief System clock initialization + * + * @return none + * + */ +void mx_hal_ms_ticker_init(void); + +/** + * + * @brief Get system clock + * + * @return system time + * + */ +uint32_t mx_hal_ms_ticker_read(void); + +/** + * + * @brief Suspend current thread for a specific time + * + * param[in] delay : A time interval (Unit: millisecond) + * + * @return none + * + */ +void mx_hal_delay_ms(volatile uint32_t delay); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/alicloud_ilop/alicloud_ilop.c b/src/alicloud_ilop/alicloud_ilop.c new file mode 100644 index 0000000..ff127da --- /dev/null +++ b/src/alicloud_ilop/alicloud_ilop.c @@ -0,0 +1,351 @@ +/** + ****************************************************************************** + * @file alicloud_ilop.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Step-2018 + * @brief AliCloud ILOP service functions and framework + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include "alicloud_ilop.h" +#include "ATCmdParser/ATCmdParser.h" +#include "mx_hal.h" + +/****************************************************************************** + * Enumerations + ******************************************************************************/ + +typedef enum { + Ilop_eState_M1_initialize = 1, /**< State machine: Reset and initialize module */ + Ilop_eState_M2_provision = 2, /**< State machine: Waiting for WiFi config and cloud connection */ + Ilop_eState_M3_normal = 3, /**< State machine: Connected to cloud, application running */ + Ilop_eState_M4_disconnected = 4, /**< State machine: Disconnect to cloud, all data transmission should stop */ + Ilop_eState_M5_fault = 5, /**< State machine: Drop in an unexpected error */ +} ilop_device_state_e; + +/****************************************************************************** + * Type Definitions + ******************************************************************************/ + +typedef struct { + ilop_device_state_e device_state; /**< Device state machine. */ + emh_arg_ilop_conn_t cloud_state; /**< SDS service connection state. */ +} ilop_context_t; + +/****************************************************************************** + * Variable Definitions + ******************************************************************************/ + +static const ilop_device_key_t* device_key = NULL; +static emh_ilop_config_t* ilop_config = NULL; +static ilop_context_t context; + +#ifdef ILOP_USE_ICA +extern void ilop_ica_indicate_local_atts(void); +extern void ica_protocol_format_handler(char* type, char* value, char* key); +#endif + +#ifdef ILOP_USE_RAW +mx_status raw_protocol_format_handler(uint8_t* data, uint32_t length); +void ilop_raw_indicate_local_property(void); +void ilop_raw_indicate_local_event(void); +#endif + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +MX_WEAK void ilop_event_handler(ilop_event_t event) +{ + switch (event) { + case ILOP_EVENT_WLAN_CONFIG_STARTED: { + ilop_log("Wi-Fi config...."); + break; + } + case ILOP_EVENT_WLAN_CONNECTED: { + ilop_log("Wi-Fi connected"); + break; + } + case ILOP_EVENT_WLAN_DISCONNECTED: { + ilop_log("Wi-Fi disconnected"); + break; + } + case ILOP_EVENT_CLOUD_CONNECTED: { + ilop_log("Cloud connected"); + break; + } + case ILOP_EVENT_CLOUD_DISCONNECTED: { + ilop_log("Cloud disconnected"); + break; + } + } +} + +static void ilop_device_key_printf(const char* pk, const char* ps, const char* ds, const char* dn) +{ + ilop_log("product key :[%s]", pk); + ilop_log("product secret :[%s]", ps); + ilop_log("device secret :[%s]", ds); + ilop_log("device name :[%s]", dn); +} + +mx_status ilop_init(const emh_ilop_config_t* config) +{ + mx_status err = kNoErr; + + context.device_state = Ilop_eState_M1_initialize; + context.cloud_state = EMH_ARG_ILOP_CONN_CLOUD_DISCONNECTED; + + ilop_config = (emh_ilop_config_t*)config; + + err = emh_init(); + require_noerr(err, exit); + +exit: + return err; +} + +mx_status ilop_set_device_key(const ilop_device_key_t* key) +{ + device_key = key; + return kNoErr; +} + +static mx_status _handle_state_initialize(void) +{ + mx_status err = kNoErr; + char ssid[33] = { 0 }, pwd[33]; + char pk[EMH_ILOP_PRODUCT_KEY_MAXLEN] = { 0 }; + char ps[EMH_ILOP_PRODUCT_SECRET_MAXLEN] = { 0 }; + char ds[EMH_ILOP_DEVICE_SECRET_MAXLEN] = { 0 }; + char dn[EMH_ILOP_DEVICE_NAME_MAXLEN] = { 0 }; + + ilop_log("FW version: %s", emh_module_get_fw_version()); + + err = emh_ilop_config(ilop_config, false); + require_noerr(err, exit); + + err = emh_ilop_get_key(pk, ps, ds, dn); + require_noerr(err, exit); + + if ((strlen(pk) == 0) || (strlen(ps) == 0) || (strlen(ds) == 0) || (strlen(dn) == 0)) { + if (device_key != NULL) { + err = emh_ilop_set_key(device_key->product_key, device_key->product_secret, device_key->device_secret, device_key->device_name); + require_noerr(err, exit); + ilop_device_key_printf(device_key->product_key, device_key->product_secret, device_key->device_secret, device_key->device_name); + } else { + ilop_log("not find device key..."); + err = kIDErr; + require_noerr(err, exit); + } + } else if (device_key != NULL) { + if ((strcmp(pk, device_key->product_key) != 0) || (strcmp(ps, device_key->product_secret) != 0) || (strcmp(ds, device_key->device_secret) != 0) || (strcmp(dn, device_key->device_name) != 0)) { + ilop_log("device key different, need to set"); + err = emh_ilop_set_key(device_key->product_key, device_key->product_secret, device_key->device_secret, device_key->device_name); + require_noerr(err, exit); + ilop_device_key_printf(device_key->product_key, device_key->product_secret, device_key->device_secret, device_key->device_name); + } else { + ilop_log("device key the same"); + ilop_device_key_printf(pk, ps, ds, dn); + } + } + + /* Start alisds daemon service*/ + err = emh_ilop_service_start(); + require_noerr(err, exit); + + /* Check Wi-Fi configuration */ + err = emh_wlan_get_para(ssid, pwd); + require_noerr(err, exit); + + if (strlen(ssid)) { + ilop_log("SSID: %s, PWD: %s", ssid, pwd); + if (EMH_ARG_ILOP_STATUS_CONNECTED == emh_ilop_get_stauts()) { + ilop_log("Alicloud connected."); + mx_hal_delay_ms(200); + context.device_state = Ilop_eState_M3_normal; + } else { + ilop_log("Waiting for Alicloud connection"); + context.device_state = Ilop_eState_M4_disconnected; + } + } else { + ilop_log("Wlan unconfigured, start config mode"); + + /* Start alisds Wi-Fi configuration */ + err = emh_ilop_awss_start(); + require_noerr(err, exit); + + ilop_event_handler(ILOP_EVENT_WLAN_CONFIG_STARTED); + context.device_state = Ilop_eState_M2_provision; + } + +exit: + return err; +} + +mx_status ilop_runloop(void) +{ + mx_status err = kNoErr; + + if (context.device_state == Ilop_eState_M3_normal) { +#ifdef ILOP_USE_ICA + ilop_ica_indicate_local_atts(); +#endif + +#ifdef ILOP_USE_RAW + ilop_raw_indicate_local_event(); + ilop_raw_indicate_local_property(); +#endif + } + + switch (context.device_state) { + case Ilop_eState_M1_initialize: { + err = _handle_state_initialize(); + require_noerr_action(err, exit, context.device_state = Ilop_eState_M5_fault); + break; + } + + case Ilop_eState_M2_provision: { + break; + } + + case Ilop_eState_M3_normal: + case Ilop_eState_M4_disconnected: { + break; + } + + case Ilop_eState_M5_fault: { + break; + } + + default: + ilop_log("STATE ERROR"); + err = kNoErr; + } + + emh_runloop(); +exit: + return err; +} + +void emh_ev_wlan(emh_arg_wlan_ev_t event) +{ + ilop_log("Wlan event: %s", emh_arg_for_type(EMH_ARG_WLAN_EV, event)); + if (event == EMH_ARG_WLAN_EV_STA_CONNECTED) { + ilop_event_handler(ILOP_EVENT_WLAN_CONNECTED); + } else if (event == EMH_ARG_WLAN_EV_STA_DISCONNECTED) { + ilop_event_handler(ILOP_EVENT_WLAN_DISCONNECTED); + } +} + +void emh_ev_ilop_connection(emh_arg_alisds_conn_t conn) +{ + ilop_log("AliCloud event: %s", emh_arg_for_type(EMH_ARG_ILOP_CONN, conn)); + + context.cloud_state = conn; + + if (conn == EMH_ARG_ILOP_CONN_CLOUD_CONNECTED) { + + /* EMW3080 has unexpected uart data lost after cloud connection, should fix in future */ + mx_hal_delay_ms(1000); + ilop_event_handler(ILOP_EVENT_CLOUD_CONNECTED); + + context.device_state = Ilop_eState_M3_normal; + } + + if (conn == EMH_ARG_ILOP_CONN_CLOUD_DISCONNECTED) { + ilop_event_handler(ILOP_EVENT_CLOUD_DISCONNECTED); + context.device_state = Ilop_eState_M4_disconnected; + } +} + +void emh_ev_ilop_set_local_attr(emh_ilop_msg* msg) +{ +#define ARG_LIST_NUM 13 + int num; + char* arg_list[ARG_LIST_NUM]; + + if (msg->format == EMH_ARG_ILOP_FORMAT_ICA) { + ilop_log("Set local attrs format ICA"); + num = ATCmdParser_analyse_args((char*)msg->data, arg_list, ARG_LIST_NUM); + if ((num < 3) || (num > ARG_LIST_NUM)) { + ilop_log("ERR analyse args num: %d", num); + goto exit; + } +#ifdef ILOP_USE_ICA + for (int i = 1; i < num; i += 2) { + ica_protocol_format_handler(arg_list[0], arg_list[i], arg_list[i + 1]); + } +#endif + } else if (msg->format == EMH_ARG_ILOP_FORMAT_RAW) { + ilop_log("Set local attrs format RAW"); + printf("\r\n"); + for (int i = 0; i < msg->len; i++) { + printf("%02X", msg->data[i]); + } + printf("\r\n"); +#ifdef ILOP_USE_RAW + raw_protocol_format_handler(msg->data, msg->len); +#endif + } + +exit: + return; +} + +mx_status ilop_awss_start(void) +{ + mx_status err; + + err = emh_ilop_service_stop(); + require_noerr(err, exit); + + err = emh_module_restore_settings(); + require_noerr(err, exit); + + context.device_state = Ilop_eState_M1_initialize; +exit: + return err; +} + +mx_status ilop_awss_press(void) +{ + if (context.device_state == Ilop_eState_M2_provision) { + return emh_ilop_awss_press(); + } + + return kGeneralErr; +} + +mx_status ilop_restore(void) +{ + mx_status err; + if (context.device_state == Ilop_eState_M3_normal) { + err = emh_ilop_unbind(); + require_noerr(err, exit); + } + + err = emh_module_restore_settings(); + require_noerr(err, exit); + + context.device_state = Ilop_eState_M1_initialize; +exit: + return err; +} diff --git a/src/alicloud_ilop/alicloud_ilop.h b/src/alicloud_ilop/alicloud_ilop.h new file mode 100644 index 0000000..a9a73c0 --- /dev/null +++ b/src/alicloud_ilop/alicloud_ilop.h @@ -0,0 +1,221 @@ +/** + ****************************************************************************** + * @file alicloud_ilop.h + * @author QQ Ding + * @version V1.0.0 + * @date 3-Step-2018 + * @brief AliCloud ILOP service functions and framework header file + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef _ALICLOUD_ILOP_H_ +#define _ALICLOUD_ILOP_H_ + +#include "emh_api.h" +#include "ica_protocol.h" +#include "raw_protocol.h" + +/** \addtogroup protocols */ +/** @{*/ + +/** \addtogroup Alicloud_ILOP_Service */ +/** @{*/ + +/****************************************************************************** + * Macros + ******************************************************************************/ + +#define ilop_log(M, ...) MX_LOG(CONFIG_CLOUD_DEBUG, "ILOP", M, ##__VA_ARGS__) + +/****************************************************************************** + * Constants + ******************************************************************************/ + +#define ILOP_INVALID_HANDLE (-1) + +/****************************************************************************** + * Enumerations + ******************************************************************************/ + +/** Alicloud ilop service events */ +enum ilop_event_e { + ILOP_EVENT_WLAN_CONFIG_STARTED, /**< AWS service is started to set wlan and cloud. */ + ILOP_EVENT_WLAN_CONNECTED, /**< Device is connected to Wi-Fi access point. */ + ILOP_EVENT_WLAN_DISCONNECTED, /**< Device is disconnected from Wi-Fi access point. */ + ILOP_EVENT_CLOUD_CONNECTED, /**< Alicloud ilop service is connected */ + ILOP_EVENT_CLOUD_DISCONNECTED, /**< Alicloud ilop service is disconnected. */ +}; +typedef uint8_t ilop_event_t; /**< Alicloud ilop service events */ + +/****************************************************************************** + * Type Definitions + ******************************************************************************/ + +typedef struct _ilop_device_key_t { + char product_key[EMH_ILOP_PRODUCT_KEY_MAXLEN]; + char product_secret[EMH_ILOP_PRODUCT_SECRET_MAXLEN]; + char device_name[EMH_ILOP_DEVICE_NAME_MAXLEN]; + char device_secret[EMH_ILOP_DEVICE_SECRET_MAXLEN]; +} ilop_device_key_t; + +/****************************************************************************** + * Function Declarations + ******************************************************************************/ + +/** + * @brief Initialize EMW module with product info registered on cloud + * console. + * + * @param[in] config : ILOP product info registered on cloud console + * + * @return mx_status + * + */ +mx_status ilop_init(const emh_ilop_config_t* config); + +/** + * + * @brief Alicould ilop service runloop, application should called + * periodically to handle events and transfer data. + * To save power, also can be called when uart data is ready + * to receive or sensor data is ready to send to cloud + * @note Never call this function in event handler + * + * @return mx_status + * + */ +mx_status ilop_runloop(void); + +/** + * + * @brief Start AWSS Wi-Fi configuration. EMW module start monitor mode + * + * @return mx_status + * + */ +mx_status ilop_awss_start(void); + +/** + * @brief Start AWSS press, called after ilop_awss_start() + * @note In the monitor mode, parsing the air package, connecting the server, binding the phone + * + * + * @return mx_status + */ +mx_status ilop_awss_press(void); + +/** + * + * @brief Try to procedure an unbind operation on cloud if connected and + * restore module settings to default. + * + * @return mx_status + * + */ +mx_status ilop_restore(void); + +/** + * @brief Set product key, product secret, device secret, device name is used to connect to ILOP service. + * @note The default key is stored in EMW module, use this function to write the new key. + * + * @param[in] key: see ilop_device_key_t + * + * @return status + */ +mx_status ilop_set_device_key(const ilop_device_key_t* key); + +/****************************************************************************** + * ICA Data Function + ******************************************************************************/ + +/** + * @brief ILOP ICA Attribute initialization + * + * @param[in] attr_num_handls : attribute handle Maximum value + * @return mx_status + * + */ +mx_status ilop_ica_attr_init(uint8_t attr_num_handls); + +/** + * @brief ILOP ICA Attribute register to database + * + * @param[in] attr : Alicloud ilop attribute description + * @return mx_status + * + */ +mx_status ilop_ica_attr_register(const struct ilop_ica_attr_t* attr); + +/** + * + * @brief Prepare to send attribute to cloud, attribute value will + * be read and send in ILOP runloop ilop_runloop + * @note This function do not send data use AT command, so it can + * be called inside the event handler. #ilop_runloop will + * do the actually read and send function. + * + * @param[in] handle : attribute handle + * @return none + * + */ +void ilop_ica_attr_indicate_by_handle(int handle); + +/****************************************************************************** + * RAW Data Function + ******************************************************************************/ + +/** + * @brief ILOP RAW Attribute initialization + * + * @param[in] attr_type : attribute type + * @param[in] attr_num_handls : attribute handle Maximum value + * @return mx_status + * + */ +mx_status ilop_raw_attr_init(attr_type_t attr_type, uint8_t attr_num_handls); + +/** + * @brief ILOP RAW Attribute register to database + * + * @param[in] attr_type : attribute type + * @param[in] attr : Alicloud ilop attribute description + * @return mx_status + * + */ +mx_status ilop_raw_attr_register(attr_type_t attr_type, const struct ilop_raw_attr_t* attr); + +/** + * + * @brief Prepare to send attribute to cloud, attribute value will + * be read and send in ILOP runloop ilop_runloop + * @note This function do not send data use AT command, so it can + * be called inside the event handler. #ilop_runloop will + * do the actually read and send function. + * + * @param[in] attr_type : attribute type + * @param[in] handle : attribute handle, see #ilop_raw_attr_t + * @return none + * + */ +void ilop_raw_attr_indicate_by_handle(attr_type_t attr_type, int handle); + +#endif //_ALICLOUD_ILOP_H_ + +/** @}*/ +/** @}*/ diff --git a/src/alicloud_ilop/ica_protocol.c b/src/alicloud_ilop/ica_protocol.c new file mode 100644 index 0000000..edf3925 --- /dev/null +++ b/src/alicloud_ilop/ica_protocol.c @@ -0,0 +1,183 @@ +/** + ****************************************************************************** + * @file ica_protocol.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Step-2018 + * @brief AliCloud ILOP service functions and ica data parser + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include "ica_protocol.h" +#include "alicloud_ilop.h" + +/****************************************************************************** + * Macros + ******************************************************************************/ + +#define MAX_ATTR 20 /**< attribute max value */ + +/****************************************************************************** + * Type Definitions + ******************************************************************************/ + +typedef struct _ilop_ica_attr_t { + const struct ilop_ica_attr_t* attr_db[MAX_ATTR]; /**< attribute db */ + int changed_handle[MAX_ATTR]; /**< ILOP characteristics numbers registered on cloud */ + int num_handles; /**< MAX, ILOP characteristics numbers registered on cloud */ +} ilop_ica_context_t; + +/****************************************************************************** + * Variable Definitions + ******************************************************************************/ + +static ilop_ica_context_t ica_db; + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +void ica_protocol_format_handler(char* type, char* value, char* key) +{ + int i, handle = 0; + + for (i = 0; i < ica_db.num_handles; i++) { + if (ica_db.attr_db[i] == NULL) + continue; + handle = ica_db.attr_db[i]->index; + + if (strcmp(value, ica_db.attr_db[handle]->name) == 0) { + if (emh_arg_for_arg(EMH_ARG_ILOP_VT, type) == ica_db.attr_db[handle]->type) { + if (ica_db.attr_db[handle]->write_func != NULL) { + ica_db.attr_db[handle]->write_func(key); + } + break; + } + } + } +} + +void ilop_ica_indicate_local_atts(void) +{ + mx_status err = kNoErr; + int i, handle = 0; + + if (ica_db.num_handles == 0) + return; + + for (i = 0; i < ica_db.num_handles; i++) { + if (ica_db.changed_handle[i] != ILOP_INVALID_HANDLE) { + handle = ica_db.changed_handle[i]; + break; + } + } + if (i >= ica_db.num_handles) + return; + + char prop_buff[512] = { 0 }; + char event_buff[100] = { 0 }; + const char* type; + char* value; + int prop_index = 0, event_index = 0; + + for (i = 0; i < ica_db.num_handles; i++) { + if (ica_db.changed_handle[i] == ILOP_INVALID_HANDLE) + continue; + + handle = ica_db.changed_handle[i]; + + if (ica_db.attr_db[handle]->read_func == NULL) + continue; + + if (ica_db.attr_db[handle]->type == EMH_ARG_ILOP_VT_PROPERTY) { + err = ica_db.attr_db[handle]->read_func(&value); + require_noerr(err, exit); + prop_index += sprintf(prop_buff + prop_index, "%s,%s,", ica_db.attr_db[handle]->name, value); + } + + if (ica_db.attr_db[handle]->type == EMH_ARG_ILOP_VT_EVENT) { + err = ica_db.attr_db[handle]->read_func(&value); + require_noerr(err, exit); + event_index += sprintf(event_buff + event_index, "%s,%s,", ica_db.attr_db[handle]->name, value); + } + } + + if (prop_index > 0) { + prop_buff[prop_index - 1] = '\0'; + ilop_log("Send Property to cloud %d bytes > %s", prop_index - 1, prop_buff); + type = emh_arg_for_type(EMH_ARG_ILOP_VT, EMH_ARG_ILOP_VT_PROPERTY); + err = emh_ilop_send_ica_to_cloud(type, prop_buff); + require_noerr(err, exit); + } + + if (event_index > 0) { + event_buff[event_index - 1] = '\0'; + ilop_log("Send Event to cloud %d bytes > %s", event_index - 1, event_buff); + type = emh_arg_for_type(EMH_ARG_ILOP_VT, EMH_ARG_ILOP_VT_EVENT); + err = emh_ilop_send_ica_to_cloud(type, event_buff); + require_noerr(err, exit); + } + +exit: + + for (i = 0; i < ica_db.num_handles; i++) { + ica_db.changed_handle[i] = ILOP_INVALID_HANDLE; + } + return; +} + +mx_status ilop_ica_attr_init(uint8_t attr_num_handls) +{ + int i; + + for (i = 0; i < MAX_ATTR; i++) { + ica_db.attr_db[i] = NULL; + } + + for (i = 0; i < MAX_ATTR; i++) { + ica_db.changed_handle[i] = ILOP_INVALID_HANDLE; + } + + ica_db.num_handles = attr_num_handls; + + return kNoErr; +} + +mx_status ilop_ica_attr_register(const struct ilop_ica_attr_t* attr) +{ + int handle = 0; + + if (ica_db.num_handles > MAX_ATTR) + return kRangeErr; + + handle = attr->index; + ica_db.attr_db[handle] = attr; + return kNoErr; +} + +void ilop_ica_attr_indicate_by_handle(int handle) +{ + if (ica_db.attr_db[handle] == NULL) + return; + if (ica_db.attr_db[handle]->index == handle) { + if (ica_db.changed_handle[handle] == handle) + return; + ica_db.changed_handle[handle] = handle; + } +} diff --git a/src/alicloud_ilop/ica_protocol.h b/src/alicloud_ilop/ica_protocol.h new file mode 100644 index 0000000..d2b9ccc --- /dev/null +++ b/src/alicloud_ilop/ica_protocol.h @@ -0,0 +1,68 @@ +/** + ****************************************************************************** + * @file ica_protocol.h + * @author QQ Ding + * @version V1.0.0 + * @date 3-Step-2018 + * @brief AliCloud ILOP ICA Data parser header file + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef __ICA_PROTOCOL_H__ +#define __ICA_PROTOCOL_H__ + +#include "emh_api.h" + +/** \addtogroup protocols */ +/** @{*/ + +/** \addtogroup Alicloud_ILOP_Service */ +/** @{*/ + +/** + * Cloud read attribute handler + * + * @param value[out] : point to attribute value buffer, application should write the correct value + * + * @return mx_status + */ +typedef mx_status (*ilop_ica_read_attr)(char** value); + +/** + * Cloud write attribute handler + * + * @param[in] value : New attribute vale sent from the ilop cloud + * + * @return mx_status + */ +typedef mx_status (*ilop_ica_write_attr)(char* value); + +/** Alicloud ilop attribute description */ +struct ilop_ica_attr_t { + uint8_t index; /**< characteristics numbers registered on cloud */ + char* name; /**< Attribute name */ + emh_arg_ilop_vt_t type; /**< Attribute value type */ + ilop_ica_read_attr read_func; /**< Attribute value read handler, optional if the value is readable*/ + ilop_ica_write_attr write_func; /**< Attribute value write handler, optional if the value is writable */ +}; + +#endif + +/** @}*/ +/** @}*/ diff --git a/src/alicloud_ilop/raw_protocol.c b/src/alicloud_ilop/raw_protocol.c new file mode 100644 index 0000000..123aec8 --- /dev/null +++ b/src/alicloud_ilop/raw_protocol.c @@ -0,0 +1,617 @@ +/** + ****************************************************************************** + * @file raw_protocol.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Step-2018 + * @brief AliCloud ILOP service functions and raw data parser + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include "raw_protocol.h" +#include "alicloud_ilop.h" + +/****************************************************************************** + * Macros + ******************************************************************************/ + +#define MAX_ATTR 20 /**< attribute max value */ + +/****************************************************************************** + * Type Definitions + ******************************************************************************/ + +typedef struct _ilop_raw_attr_t { + const struct ilop_raw_attr_t* attr_db[MAX_ATTR]; + int changed_handle[MAX_ATTR]; + int num_handles; +} ilop_raw_context_t; + +/****************************************************************************** + * Variable Definitions + ******************************************************************************/ + +static ilop_raw_context_t property_db; +static ilop_raw_context_t event_db; +static ilop_raw_context_t service_db; +static uint32_t seed_id = 0; + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +/* Computation and verification */ +static uint8_t cal_checksum(uint8_t* buffer, uint16_t length) +{ + int32_t i; + uint8_t check_sum = 0; + + for (i = 0; i < length; i++) { + check_sum += buffer[i]; + } + + return check_sum; +} + +static uint32_t get_seed_id(void) +{ + if (seed_id == 0xFFFFFFFF) + seed_id = 0; + + return seed_id++; +} + +/* Protocol header initialization */ +static void protFrameHeadInit(data_buffer_t* buff, uint8_t cmd) +{ + uint8_t check_sum; + raw_protocol_head_t* head = (raw_protocol_head_t*)buff->buffer; + + /* Requiring the use of big endian mode transmission */ + head->head = RAW_PROTOCOL_HEAD; + head->ver = RAW_PROTOCOL_VER; + head->cmd = cmd; + head->length = hton16(buff->length - 2); + check_sum = cal_checksum(buff->buffer, buff->length); + buff->buffer[buff->length++] = check_sum; /* write check sum */ +} + +static uint32_t protocol_value_handler(const struct ilop_raw_attr_t* attr, uint8_t* addr, uint32_t length) +{ + uint16_t size = 0; + ilop_att_val_t value; + + switch (attr->type_id) { + case TYPE_ID_BOOL: { + size = 1; + value.boolValue = *addr; + break; + } + + case TYPE_ID_INT8: { + size = 1; + value.int8Value = *addr; + break; + } + case TYPE_ID_UINT8: { + size = 1; + value.uint8Value = *addr; + break; + } + + case TYPE_ID_INT16: { + size = 2; + value.int16Value = (uint16_t)*addr; + value.int16Value = ntoh16(value.int16Value); + break; + } + + case TYPE_ID_UINT16: { + size = 2; + value.uint16Value = (uint16_t)*addr; + value.uint16Value = ntoh16(value.uint16Value); + break; + } + + case TYPE_ID_INT32: { + size = 4; + value.int32Value = (int32_t)*addr; + value.int32Value = ntoh32(value.int32Value); + break; + } + + case TYPE_ID_UINT32: { + size = 4; + value.uint32Value = (uint32_t)*addr; + value.uint32Value = ntoh32(value.uint32Value); + break; + } + + case TYPE_ID_FLOAT32: { + size = 4; + value.floatValue = (float)*addr; + value.floatValue = ntoh32(value.floatValue); + break; + } + + /*8 byte*/ + case TYPE_ID_DATE: + case TYPE_ID_UINT64: { + size = 8; + value.uint64Value = (uint64_t)*addr; + value.uint64Value = ntoh64(value.uint64Value); + break; + } + + case TYPE_ID_INT64: { + size = 8; + value.int64Value = (uint64_t)*addr; + value.int64Value = ntoh64(value.int64Value); + break; + } + + case TYPE_ID_FLOAT64: { + size = 8; + value.doubleValue = (uint64_t)*addr; + value.doubleValue = ntoh64(value.doubleValue); + break; + } + + /*string*/ + case TYPE_ID_STRING: { + size = hton16(*((uint16_t*)addr)); + value.stringValue = (char*)(addr + sizeof(size)); + break; + } + + default: { + size = length; + value.dataValue = (uint8_t*)(addr); + break; + } + } + + if (attr->write_func != NULL) { + attr->write_func(value, size); + if (attr->type_id == TYPE_ID_STRING) { + size += sizeof(size); + } + } + + return size; +} + +static uint32_t protocol_service_handler(const struct ilop_raw_attr_t* attr, uint8_t* addr, uint32_t size) +{ + ilop_att_val_t value; + + if (attr->write_func != NULL) { + value.dataValue = addr; + attr->write_func(value, size); + } + return size; +} + +static int get_property_thing_index(uint8_t attr_id, uint8_t type_id) +{ + int i, handle = 0; + + for (i = 0; i < property_db.num_handles; i++) { + if (property_db.attr_db[i] == NULL) + continue; + handle = property_db.attr_db[i]->index; + + if ((property_db.attr_db[handle]->attr_id == attr_id) && ((property_db.attr_db[handle]->type_id == type_id))) { + ilop_log("find property index:%d", handle); + return handle; + } + } + + return -1; +} + +static int get_service_thing_index(uint8_t attr_id) +{ + int i, handle = 0; + + ilop_log("search attr_id:%d", attr_id); + + for (i = 0; i < service_db.num_handles; i++) { + if (service_db.attr_db[i] == NULL) + continue; + handle = service_db.attr_db[i]->index; + + if (service_db.attr_db[handle]->attr_id == attr_id) { + ilop_log("find service index:%d", handle); + return handle; + } + } + + return -1; +} + +/* Processing SET requests issued under cloud */ +static mx_status protocol_method_handler(uint8_t method, uint8_t* data, uint16_t length) +{ + int32_t offset = 0, index = -1, ret = 0; + raw_body_format_t* elem; + + while ((length - offset) > 0) { + if (method == RAW_METHOD_SET) { + elem = (raw_body_format_t*)(data + offset); + index = get_property_thing_index(elem->attr_id, elem->type_id); + if (index < 0) { + ilop_log("attr_id or type_id err"); + return kGeneralErr; + } + + offset += sizeof(raw_body_format_t) - 1; + ret = protocol_value_handler(property_db.attr_db[index], &elem->value, length - 2); + } else if (method == RAW_METHOD_SERVICE) { + uint8_t attr_id = *data; + index = get_service_thing_index(attr_id); + if (index < 0) { + ilop_log("attr_id err"); + return kGeneralErr; + } + offset += 1; + ret = protocol_service_handler(service_db.attr_db[index], data + offset, length - 1); + } + if (ret == kUnsupportedErr) + return kNotReadableErr; + offset += ret; + } + + return kNoErr; +} + +/* Handling data transmitted by server side */ +static mx_status protocol_payloa_handler(uint8_t* data, uint16_t length) +{ + raw_payload_data_head_t* payload = (raw_payload_data_head_t*)data; + + switch (payload->method) { + /* Method of active sending from server side */ + // case RAW_METHOD_GET: + // protGetMethodHandler(&payload->data, length - sizeof(raw_payload_data_head_t) + 1); + // break; + + case RAW_METHOD_SET: + ilop_log("raw method set"); + protocol_method_handler(RAW_METHOD_SET, &payload->data, length - sizeof(raw_payload_data_head_t) + 1); + break; + + case RAW_METHOD_SERVICE: + ilop_log("raw method service"); + protocol_method_handler(RAW_METHOD_SERVICE, &payload->data, length - sizeof(raw_payload_data_head_t) + 1); + break; + + default: + ilop_wlog("Method(%d) not support\n", payload->method); + return kParamErr; + } + + return kNoErr; +} + +/* Processing protocol frames */ +mx_status raw_protocol_format_handler(uint8_t* data, uint32_t length) +{ + raw_protocol_head_t* head = (raw_protocol_head_t*)data; + uint8_t cmd = head->cmd; + + switch (cmd) { + case RAW_DOWN_DATA: + ilop_log("raw down data len:%d", ntoh16(head->length)); + protocol_payloa_handler(data + RAW_PROTOCOL_REGION_HEAD_LEN, + ntoh16(head->length) - RAW_PROTOCOL_REGION_LENGTH_FIXED); + break; + + default: + ilop_wlog("Cmd %02x not support\n", cmd); + break; + } + + return kNoErr; +} + +static void protocol_add_body(const struct ilop_raw_attr_t* attr, data_buffer_t* buf) +{ + ilop_att_val_t value; + uint32_t len; + + ilop_log("add body attr_id:%d", attr->attr_id); + + raw_body_format_t* body = (raw_body_format_t*)(buf->buffer + buf->length); + body->attr_id = attr->attr_id; + body->type_id = attr->type_id; + buf->length += sizeof(raw_body_format_t) - 1; + + attr->read_func(&value, &len); + + switch (attr->type_id) { + /*1 byte*/ + case TYPE_ID_BOOL: + case TYPE_ID_INT8: + case TYPE_ID_UINT8: + body->value = value.boolValue; + buf->length += 1; + break; + + /*2 byte*/ + case TYPE_ID_INT16: + case TYPE_ID_UINT16: { + uint16_t* p = (uint16_t*)&body->value; + *p = hton16(value.uint16Value); + buf->length += 2; + break; + } + + /*4 byte*/ + case TYPE_ID_INT32: + case TYPE_ID_UINT32: { + uint32_t* p = (uint32_t*)&body->value; + *p = hton32(value.uint32Value); + buf->length += 4; + break; + } + + case TYPE_ID_FLOAT32: { + float* p = (float*)&body->value; + *p = hton32(value.floatValue); + buf->length += 4; + break; + } + + /*8 byte*/ + case TYPE_ID_DATE: + case TYPE_ID_INT64: + case TYPE_ID_UINT64: { + uint64_t* p = (uint64_t*)&body->value; + *p = hton64(value.uint64Value); + buf->length += 4; + break; + } + + case TYPE_ID_FLOAT64: { + double* p = (double*)&body->value; + *p = hton32(value.doubleValue); + buf->length += 4; + break; + } + + /*string*/ + case TYPE_ID_STRING: { + uint8_t* p; + uint16_t* value_len = (uint16_t*)&body->value; + *value_len = hton16(len); + p = &body->value + sizeof(*value_len); + memcpy(p, value.stringValue, len); + buf->length += (len + 2); + break; + } + + default: { + uint8_t* p; + p = &body->value; + memcpy(p, value.dataValue, len); + buf->length += len; + ilop_log("default"); + break; + } + } +} + +/*To process the report attribute, it must be called in the ilop_runloop function.*/ +void ilop_raw_indicate_local_property(void) +{ + int i; + mx_status err = kNoErr; + + if ((property_db.num_handles == 0)) + return; + + for (i = 0; i < property_db.num_handles; i++) { + if (property_db.changed_handle[i] != ILOP_INVALID_HANDLE) + break; + } + if (i >= property_db.num_handles) + return; + + ilop_log("local property"); + + int handle; + + data_buffer_t data_buff; + memset(&data_buff, 0x00, sizeof(data_buffer_t)); + + data_buff.length = RAW_PROTOCOL_REGION_HEAD_LEN; + + raw_payload_data_head_t* payload = (raw_payload_data_head_t*)(data_buff.buffer + RAW_PROTOCOL_REGION_HEAD_LEN); + payload->method = RAW_METHOD_GET; + payload->id = hton32(get_seed_id()); + + data_buff.length += sizeof(raw_payload_data_head_t) - 1; + + for (i = 0; i < property_db.num_handles; i++) { + if (property_db.changed_handle[i] == ILOP_INVALID_HANDLE) + continue; + + handle = property_db.changed_handle[i]; + + if (property_db.attr_db[handle]->read_func == NULL) + continue; + + protocol_add_body(property_db.attr_db[handle], &data_buff); + } + + protFrameHeadInit(&data_buff, RAW_UP_DATA); + + printf("\r\n"); + for (int i = 0; i < data_buff.length; i++) { + printf("%02X", data_buff.buffer[i]); + } + printf("\r\n"); + + err = emh_ilop_send_raw_to_cloud(data_buff.buffer, data_buff.length); + require_noerr(err, exit); + + for (i = 0; i < property_db.num_handles; i++) { + property_db.changed_handle[i] = ILOP_INVALID_HANDLE; + } + +exit: + return; +} + +/*To process the report events, it must be called in the ilop_runloop function*/ +void ilop_raw_indicate_local_event(void) +{ + mx_status err = kNoErr; + int i, handle = 0; + + if (event_db.num_handles == 0) + return; + + for (i = 0; i < event_db.num_handles; i++) { + if (event_db.changed_handle[i] != ILOP_INVALID_HANDLE) { + handle = event_db.changed_handle[i]; + break; + } + } + if (i >= event_db.num_handles) + return; + + ilop_log("local event"); + + data_buffer_t data_buff; + memset(&data_buff, 0x00, sizeof(data_buffer_t)); + + data_buff.length = RAW_PROTOCOL_REGION_HEAD_LEN; + + raw_payload_data_head_t* payload = (raw_payload_data_head_t*)(data_buff.buffer + RAW_PROTOCOL_REGION_HEAD_LEN); + payload->method = RAW_METHOD_EVENT; + payload->id = hton32(get_seed_id()); + + data_buff.length += sizeof(raw_payload_data_head_t) - 1; + + if (event_db.attr_db[handle]->read_func == NULL) + goto exit; + + protocol_add_body(event_db.attr_db[handle], &data_buff); + + protFrameHeadInit(&data_buff, RAW_UP_DATA); + + printf("\r\n"); + for (int i = 0; i < data_buff.length; i++) { + printf("%02X", data_buff.buffer[i]); + } + printf("\r\n"); + + err = emh_ilop_send_raw_to_cloud(data_buff.buffer, data_buff.length); + require_noerr(err, exit); + + event_db.changed_handle[handle] = ILOP_INVALID_HANDLE; + +exit: + return; +} + +mx_status ilop_raw_attr_init(attr_type_t attr_type, uint8_t attr_num_handls) +{ + int i; + + for (i = 0; i < MAX_ATTR; i++) { + if (attr_type == ATTR_TYPE_PROPERTY) { + property_db.attr_db[i] = NULL; + } else if (attr_type == ATTR_TYPE_EVENT) { + event_db.attr_db[i] = NULL; + } else if (attr_type == ATTR_TYPE_SERVICE) { + service_db.attr_db[i] = NULL; + } + } + + for (i = 0; i < MAX_ATTR; i++) { + if (attr_type == ATTR_TYPE_PROPERTY) { + property_db.changed_handle[i] = ILOP_INVALID_HANDLE; + } else if (attr_type == ATTR_TYPE_EVENT) { + event_db.changed_handle[i] = ILOP_INVALID_HANDLE; + } else if (attr_type == ATTR_TYPE_SERVICE) { + service_db.changed_handle[i] = ILOP_INVALID_HANDLE; + } + } + + if (attr_type == ATTR_TYPE_PROPERTY) { + property_db.num_handles = attr_num_handls; + } else if (attr_type == ATTR_TYPE_EVENT) { + event_db.num_handles = attr_num_handls; + } else if (attr_type == ATTR_TYPE_SERVICE) { + service_db.num_handles = attr_num_handls; + } + + return kNoErr; +} + +mx_status ilop_raw_attr_register(attr_type_t attr_type, const struct ilop_raw_attr_t* attr) +{ + int handle = 0; + + if (attr_type == ATTR_TYPE_PROPERTY) { + if (property_db.num_handles > MAX_ATTR) + return kRangeErr; + + handle = attr->index; + property_db.attr_db[handle] = attr; + } else if (attr_type == ATTR_TYPE_EVENT) { + if (event_db.num_handles > MAX_ATTR) + return kRangeErr; + + handle = attr->index; + event_db.attr_db[handle] = attr; + } else if (attr_type == ATTR_TYPE_SERVICE) { + if (service_db.num_handles > MAX_ATTR) + return kRangeErr; + + handle = attr->index; + service_db.attr_db[handle] = attr; + } + + return kNoErr; +} + +void ilop_raw_attr_indicate_by_handle(attr_type_t attr_type, int handle) +{ + if (attr_type == ATTR_TYPE_PROPERTY) { + if (property_db.attr_db[handle] == NULL) + return; + if (property_db.attr_db[handle]->index == handle) { + if (property_db.changed_handle[handle] == handle) + return; + property_db.changed_handle[handle] = handle; + } + } else if (attr_type == ATTR_TYPE_EVENT) { + if (event_db.attr_db[handle] == NULL) + return; + if (event_db.attr_db[handle]->index == handle) { + if (event_db.changed_handle[handle] == handle) + return; + event_db.changed_handle[handle] = handle; + } + } +} diff --git a/src/alicloud_ilop/raw_protocol.h b/src/alicloud_ilop/raw_protocol.h new file mode 100644 index 0000000..dd61007 --- /dev/null +++ b/src/alicloud_ilop/raw_protocol.h @@ -0,0 +1,223 @@ +/** + ****************************************************************************** + * @file raw_protocol.h + * @author QQ Ding + * @version V1.0.0 + * @date 3-Step-2018 + * @brief AliCloud ILOP Raw Data parser header file + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef __RAW_PROTOCOL_H__ +#define __RAW_PROTOCOL_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "mx_common.h" + +/****************************************************************************** + * Enumerations + ******************************************************************************/ + +typedef enum{ + ATTR_TYPE_PROPERTY, + ATTR_TYPE_EVENT, + ATTR_TYPE_SERVICE, +}attr_type_t; + +typedef enum { + /* Basic data type */ + TYPE_ID_BOOL = 0, /* Boolean: 0 or 1 */ + TYPE_ID_INT8 = 1, /* Integer: -128 ~ 127 */ + TYPE_ID_UINT8 = 2, /* Integer: 0 ~ 255 */ + TYPE_ID_INT16 = 3, /* Integer: -32768 ~ 32767 */ + TYPE_ID_UINT16 = 4, /* Integer 0 ~ 65535 */ + TYPE_ID_INT32 = 5, /* Integer */ + TYPE_ID_UINT32 = 6, + TYPE_ID_INT64 = 7, + TYPE_ID_UINT64 = 8, + TYPE_ID_FLOAT32 = 9, /* Float */ + TYPE_ID_FLOAT64 = 10, + TYPE_ID_STRING = 11, /* String */ + TYPE_ID_DATE = 12, /* DATE */ + + /* Extended data type */ + TYPE_ID_ARRAY_BOOL = 16, + TYPE_ID_ARRAY_INT8 = 17, + TYPE_ID_ARRAY_UINT8 = 18, + TYPE_ID_ARRAY_INT16 = 19, + TYPE_ID_ARRAY_UINT16 = 20, + TYPE_ID_ARRAY_INT32 = 21, + TYPE_ID_ARRAY_UINT32 = 22, + TYPE_ID_ARRAY_INT64 = 23, + TYPE_ID_ARRAY_UINT64 = 24, + TYPE_ID_ARRAY_FLOAT32 = 25, + TYPE_ID_ARRAY_FLOAT64 = 26, + TYPE_ID_ARRAY_STRING = 27, + + NUM_TYPE_ID +} data_type_t; + +typedef union { + bool boolValue; + uint8_t uint8Value; + int8_t int8Value; + uint16_t uint16Value; + int16_t int16Value; + uint32_t uint32Value; + int32_t int32Value; + float floatValue; + double doubleValue; + uint64_t uint64Value; + int64_t int64Value; + char* stringValue; + uint8_t* dataValue; +} ilop_att_val_t; + +/** + * Cloud read attribute handler + * + * @param value[out] : point to attribute value buffer, application should write the correct value + * + * @return status + */ +typedef mx_status (*ilop_raw_read_attr)(ilop_att_val_t* value, uint32_t* outlen); + +/** + * Cloud write attribute handler + * + * @param[in] value : New attribute vale sent from the ILOP cloud + * + * @return status + */ +typedef mx_status (*ilop_raw_write_attr)(ilop_att_val_t value, uint32_t inlen); + +struct ilop_raw_attr_t { + uint8_t index; + uint8_t attr_id; + uint8_t type_id; + ilop_raw_read_attr read_func; + ilop_raw_write_attr write_func; +}; + +/* ============================================================================================== */ +/* MACRO & TYPE DEFINITION REGION */ +/* ============================================================================================== */ +/* + * 帧结构定义如下: + * ---------------------------------------------------------------- + * | HEAD | LENGTH | VER | CMD | PAYLOAD | CHKSUM | + * ---------------------------------------------------------------- + * 1Byte 2Byte 1Byte 1Byte n Byte 1Byte + */ + +/* + * PAYLOAD 帧结构定义如下: + * ----------------------------- + * | method | id | data | + * ----------------------------- + * 1Byte 4Byte n Byte + */ + +/* + * set/report 的 data 帧结构定义如下:(len 仅在类型为数组和文本(text)的情况下需要,表示长度) + * ------------------------------------------- + * | typeid | attrid | [len] | value | + * ------------------------------------------- + * 1Byte 1Byte 2Byte n Byte + */ + +#define RAW_PROTOCOL_HEAD 0xAA +#define RAW_PROTOCOL_REGION_HEAD_LEN 5 /* HEAD + LENGTH + VER + CMD */ +#define RAW_PROTOCOL_REGION_LENGTH_FIXED 3 /* LENGTH(VER+CMD+CHKSUM) */ +#define RAW_PROTOCOL_FRAME_LEN_FIXED (RAW_PROTOCOL_REGION_HEAD_LEN + 1) /* Fixed length in frames (except for other fields in DATA domain) */ +#define RAW_PROTOCOL_VER 0x01 + +#define CONFIG_MAX_PKG_SIZE 256 + +typedef enum { + RAW_UP_DATA = 0x00, + RAW_DOWN_DATA = 0x80, +} raw_elem_cmd_t; + +typedef enum { + /* Downlink method */ + RAW_METHOD_GET = 0x00, + RAW_METHOD_SET = 0x01, + RAW_METHOD_SERVICE = 0x02, + + /* Uplink method */ + RAW_METHOD_REPORT = 0x80, + RAW_METHOD_EVENT = 0x81, + + /* General response */ + RAW_METHOD_ACK = 0xFF +} raw_data_method_t; + +/* ============================================================================================== */ +/* DATA STRUCTURE DEFINITION REGION */ +/* ============================================================================================== */ + +/* 一般缓冲区 */ +typedef struct { + uint8_t buffer[CONFIG_MAX_PKG_SIZE]; + uint16_t size; /* Cache size */ + uint16_t length; /* Cache length */ +} data_buffer_t; + +#pragma pack(1) /* 1 byte alignment */ + +typedef struct +{ + uint8_t head; + uint16_t length; + uint8_t ver; + uint8_t cmd; +} raw_protocol_head_t; + +/* Transparent (Passthrough) frame payload domain header */ +typedef struct +{ + uint8_t method; + uint32_t id; + uint8_t data; +} raw_payload_data_head_t; + +typedef struct +{ + uint8_t type_id; + uint8_t attr_id; + uint8_t value; +} raw_body_format_t; + +typedef struct +{ + uint8_t service_id; + uint8_t data; +} raw_service_body_format_t; + +#pragma pack() /* Cancel custom byte alignment */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/src/alicloud_sds/alicloud_sds.c b/src/alicloud_sds/alicloud_sds.c new file mode 100644 index 0000000..85c445f --- /dev/null +++ b/src/alicloud_sds/alicloud_sds.c @@ -0,0 +1,471 @@ +/** + ****************************************************************************** + * @file alicloud_sds.c + * @author William Xu + * @version V1.0.0 + * @date 9-Apr-2018 + * @brief AliCloud SDS service functions and framework + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include "mx_hal.h" + +#include "alicloud_sds.h" +#include "json_generator.h" +#include "json_parser.h" + +/****************************************************************************** + * Macros + ******************************************************************************/ + +#define sds_log(M, ...) MX_LOG(CONFIG_CLOUD_DEBUG, "SDS", M, ##__VA_ARGS__) + +/****************************************************************************** + * Constants + ******************************************************************************/ + +#define SDS_NUM_TOKENS 30 /**< Used for JSON data parser, reduce to save memory */ + +/****************************************************************************** + * Enumerations + ******************************************************************************/ + +typedef enum { + eState_M1_initialize = 1, /**< State machine: Reset and initialize module */ + eState_M2_provision = 2, /**< State machine: Waiting for WiFi config and cloud connection */ + eState_M3_normal = 3, /**< State machine: Connected to cloud, application running */ + eState_M4_disconnected = 4, /**< State machine: Disconnect to cloud, all data transmission should stop */ + eState_M5_fault = 5, /**< State machine: Drop in an unexpected error */ +} cc_device_state_e; + +/****************************************************************************** + * Type Definitions + ******************************************************************************/ + +typedef struct { + cc_device_state_e device_state; /**< Device state machine. */ + emh_arg_alisds_conn_t cloud_state; /**< SDS service connection state. */ + bool delay_prov; /**< Send prov message after cloud is connected. */ + int num_handles; /**< Max. SDS characteristics numbers registered on cloud */ +} cc_context_t; + +/****************************************************************************** + * Variable Definitions + ******************************************************************************/ + +static const emh_alisds_config_t* ali_config = NULL; +static cc_context_t context; + +// Changed handles should bu update to cloud +static int* changed_handles = NULL; + +// Characteristic description and handler functions registered by application +static alisds_attr_t* alisds_attr_db = NULL; + +// Temporary storage for Characteristic name and value, de-packed from JSON message +static char incomming_val[ALISDS_ATTR_VAL_MAX_LEN]; +static char incomming_name[ALISDS_ATTR_NAME_MAX_LEN]; + +/****************************************************************************** + * Static Function Declarations + ******************************************************************************/ + +/** + * Send attribute value to SDS cloud + */ +static void alisds_indicate_local_atts(int attr_handles[], int num); + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +MX_WEAK void alisds_event_handler(alisds_event_t event) +{ + switch (event) { + case ALISDS_EVENT_WLAN_CONFIG_STARTED: { + sds_log("Wi-Fi config...."); + break; + } + case ALISDS_EVENT_WLAN_CONNECTED: { + sds_log("Wi-Fi connected"); + break; + } + case ALISDS_EVENT_WLAN_DISCONNECTED: { + sds_log("Wi-Fi disconnected"); + break; + } + case ALISDS_EVENT_CLOUD_CONNECTED: { + sds_log("Cloud connected"); + break; + } + case ALISDS_EVENT_CLOUD_DISCONNECTED: { + sds_log("Cloud disconnected"); + break; + } + } +} + +mx_status alisds_init(const emh_alisds_config_t* config, int num_handles) +{ + mx_status err = kNoErr; + + require_action(num_handles > 0, exit, err = kGeneralErr); + + changed_handles = malloc(num_handles * sizeof(int)); + require_action(changed_handles, exit, err = kNoMemoryErr); + + for (int i = 0; i < num_handles; i++) { + changed_handles[i] = ALISDS_INVALID_HANDLE; + } + + alisds_attr_db = malloc(num_handles * sizeof(alisds_attr_t)); + require_action(alisds_attr_db, exit, err = kNoMemoryErr); + memset(alisds_attr_db, 0x0, num_handles * sizeof(alisds_attr_t)); + + context.device_state = eState_M1_initialize; + context.cloud_state = EMH_ARG_ALISDS_CONN_DISCONNECTED; + context.delay_prov = false; + context.num_handles = num_handles; + + ali_config = config; + + err = emh_init(); + require_noerr(err, exit); + +exit: + if (err != kNoErr) { + if (changed_handles) + free(changed_handles); + if (alisds_attr_db) + free(alisds_attr_db); + } + return err; +} + +mx_status alisds_attr_init(alisds_attr_handle_t handle, alisds_attr_t attr) +{ + alisds_attr_db[handle].name = attr.name; + alisds_attr_db[handle].type = attr.type; + alisds_attr_db[handle].read_func = attr.read_func; + alisds_attr_db[handle].write_func = attr.write_func; + + alisds_attr_indicate_by_handle(handle); + return kNoErr; +} + +static mx_status _handle_state_initialize(void) +{ + mx_status err = kNoErr; + char ssid[33], pwd[33]; + + sds_log("FW version: %s", emh_module_get_fw_version()); + + err = emh_alisds_config(ali_config); + require_noerr(err, exit); + + /* Set cloud access token */ + //err = emh_alisds_set_key(dev_key, dev_sec); + //require_noerr(err, exit); + + /* Start alisds daemon service*/ + err = emh_alisds_start_service(); + require_noerr(err, exit); + + /* Check Wi-Fi configuration */ + err = emh_wlan_get_para(ssid, pwd); + require_noerr(err, exit); + + if (strlen(ssid)) { + sds_log("SSID: %s, PWD: %s", ssid, pwd); + if (EMH_ARG_ALISDS_STATUS_CONNECTED == emh_alisds_get_status()) { + sds_log("Alicloud connected."); + mx_hal_delay_ms(200); + context.device_state = eState_M3_normal; + } else { + sds_log("Waiting for Alicloud connection"); + context.device_state = eState_M4_disconnected; + } + } else { + sds_log("Wlan unconfigured, start provision mode"); + + /* Start alisds Wi-Fi configuration */ + err = emh_alisds_provision(true); + require_noerr(err, exit); + + alisds_event_handler(ALISDS_EVENT_WLAN_CONFIG_STARTED); + context.device_state = eState_M2_provision; + } + +exit: + return err; +} + +void alisds_attr_indicate_by_handle(int handle) +{ + int i; + for (i = 0; i < context.num_handles && changed_handles[i] != ALISDS_INVALID_HANDLE; i++) { + if (changed_handles[i] == handle) + return; + } + changed_handles[i] = handle; +} + +mx_status alisds_runloop(void) +{ + mx_status err = kNoErr; + alisds_indicate_local_atts(changed_handles, context.num_handles); + + switch (context.device_state) { + case eState_M1_initialize: { + err = _handle_state_initialize(); + require_noerr_action(err, exit, context.device_state = eState_M5_fault); + } break; + + case eState_M2_provision: { + } break; + + case eState_M3_normal: + case eState_M4_disconnected: { + } break; + case eState_M5_fault: { + } break; + + default: + sds_log("STATE ERROR"); + err = kNoErr; + } + + emh_runloop(); +exit: + return err; +} + +void emh_ev_wlan(emh_arg_wlan_ev_t event) +{ + sds_log("Wlan event: %s", emh_arg_for_type(EMH_ARG_WLAN_EV, event)); + if (event == EMH_ARG_WLAN_EV_STA_CONNECTED) { + alisds_event_handler(ALISDS_EVENT_WLAN_CONNECTED); + } else if (event == EMH_ARG_WLAN_EV_STA_DISCONNECTED) { + alisds_event_handler(ALISDS_EVENT_WLAN_DISCONNECTED); + } +} + +void emh_ev_alisds_connection(emh_arg_alisds_conn_t conn) +{ + sds_log("AliCloud event: %s", emh_arg_for_type(EMH_ARG_ALISDS_CONN, conn)); + + context.cloud_state = conn; + + if (conn == EMH_ARG_ALISDS_CONN_CONNECTED) { + if (context.delay_prov == true) { + alisds_provision(); + context.delay_prov = false; + } + + /* EMW3080 has unexpected uart data lost after cloud connection, should fix in future */ + mx_hal_delay_ms(1000); + alisds_event_handler(ALISDS_EVENT_CLOUD_CONNECTED); + + context.device_state = eState_M3_normal; + } + + if (conn == EMH_ARG_ALISDS_CONN_DISCONNECTED) { + alisds_event_handler(ALISDS_EVENT_CLOUD_DISCONNECTED); + context.device_state = eState_M4_disconnected; + } +} + +void emh_ev_alisds_set_local_atts(emh_alisds_msg* attrs) +{ + jsontok_t json_tokens[SDS_NUM_TOKENS]; + jobj_t jobj; + + alisds_att_val_t value; + int attr_handles[50]; + int num; + int handle; + + sds_log("Set local attrs event"); + require(attrs->format == EMH_ARG_ALISDS_FORMAT_JSON, exit); + + memset(attr_handles, 0, 50); + mx_status err = json_init(&jobj, json_tokens, SDS_NUM_TOKENS, (char*)attrs->data, attrs->len); + if (err != kNoErr) + return; + + err = json_get_array_object(&jobj, "attrSet", &num); + require_noerr(err, exit); + + for (int i = 0; i < num; i++) { + err = json_array_get_str(&jobj, i, incomming_name, ALISDS_ATTR_NAME_MAX_LEN); + require_noerr(err, exit); + + for (handle = 0; handle < context.num_handles; handle++) { + if (strcmp(incomming_name, alisds_attr_db[handle].name) == 0) { + attr_handles[i] = handle; + break; + } + } + } + + err = json_release_array_object(&jobj); + require_noerr(err, exit); + + for (int i = 0; i < num; i++) { + handle = attr_handles[i]; + + if (alisds_attr_db[handle].write_func == NULL) + continue; + if (kNoErr == json_get_composite_object(&jobj, alisds_attr_db[handle].name)) { + if (kNoErr == json_get_val_str(&jobj, "value", incomming_val, ALISDS_ATTR_VAL_MAX_LEN)) { + if (alisds_attr_db[handle].type == ALI_ATT_TYPE_BOOL + || alisds_attr_db[handle].type == ALI_ATT_TYPE_INT) { + sscanf(incomming_val, "%d", (int*)&value); + } else if (alisds_attr_db[handle].type == ALI_ATT_TYPE_FLOAT) { + sscanf(incomming_val, "%f", (float*)&value); + } else if (alisds_attr_db[handle].type == ALI_ATT_TYPE_STRING) { + value.stringValue = incomming_val; + } + alisds_attr_db[handle].write_func(value); + json_release_composite_object(&jobj); + alisds_attr_indicate_by_handle(handle); + } + } + } + +exit: + return; +} + +void alisds_indicate_local_atts(int attr_handles[], int num) +{ + if (context.device_state != eState_M3_normal) + return; + + struct json_str jstr; + char buff[512] = { 0 }; + char val_str[20]; + mx_status err = kNoErr; + alisds_att_val_t value; + int handle; + int i; + + if ((num == 0) || (attr_handles[0] == ALISDS_INVALID_HANDLE)) + return; + + sds_log("Indicate local attrs"); + + json_str_init(&jstr, buff, sizeof(buff)); + + err = json_start_object(&jstr); + require_noerr(err, exit); + + for (i = 0; i < num && attr_handles[i] != ALISDS_INVALID_HANDLE; i++) { + handle = attr_handles[i]; + if (alisds_attr_db[handle].read_func == NULL) + continue; + + err = json_push_object(&jstr, alisds_attr_db[handle].name); + require_noerr(err, exit); + + if (alisds_attr_db[handle].type == ALI_ATT_TYPE_BOOL) { + err = alisds_attr_db[handle].read_func(&value); + require_noerr(err, exit); + snprintf(val_str, 20, "%d", value.boolValue); + err = json_set_val_str(&jstr, "value", val_str); + require_noerr(err, exit); + } else if (alisds_attr_db[handle].type == ALI_ATT_TYPE_INT) { + err = alisds_attr_db[handle].read_func(&value); + require_noerr(err, exit); + snprintf(val_str, 20, "%d", value.intValue); + err = json_set_val_str(&jstr, "value", val_str); + require_noerr(err, exit); + } else if (alisds_attr_db[handle].type == ALI_ATT_TYPE_FLOAT) { + err = alisds_attr_db[handle].read_func(&value); + require_noerr(err, exit); + snprintf(val_str, 20, "%.2f", value.floatValue); + err = json_set_val_str(&jstr, "value", val_str); + require_noerr(err, exit); + } else if (alisds_attr_db[handle].type == ALI_ATT_TYPE_STRING) { + err = alisds_attr_db[handle].read_func(&value); + require_noerr(err, exit); + + err = json_set_val_str(&jstr, "value", value.stringValue); + require_noerr(err, exit); + } + err = json_pop_object(&jstr); + require_noerr(err, exit); + } + + /* Create attrset */ + json_push_array_object(&jstr, "attrSet"); + for (int i = 0; i < num && attr_handles[i] != ALISDS_INVALID_HANDLE; i++) { + handle = attr_handles[i]; + err = json_set_array_str(&jstr, alisds_attr_db[handle].name); + require_noerr(err, exit); + } + json_pop_array_object(&jstr); + + err = json_close_object(&jstr); + require_noerr(err, exit); + + sds_log("Send to cloud %ld bytes > %s", strlen(buff), buff); + err = emh_alisds_set_cloud_atts(EMH_ARG_ALISDS_FORMAT_JSON, (uint8_t*)buff, strlen(buff)); + require_noerr(err, exit); + + for (i = 0; i < num; i++) { + attr_handles[i] = ALISDS_INVALID_HANDLE; + } + +exit: + return; +} + +void alisds_provision(void) +{ + mx_status err = kNoErr; + char provision_ack[50]; + + if (context.cloud_state == EMH_ARG_ALISDS_CONN_CONNECTED) { + snprintf(provision_ack, 50, "{\"ErrorCode\":{\"value\":\"%d\"}}", 0); + err = emh_alisds_set_cloud_atts(EMH_ARG_ALISDS_FORMAT_JSON, (uint8_t*)provision_ack, strlen(provision_ack)); + require_noerr(err, exit); + + snprintf(provision_ack, 50, "{\"ErrorCode\":{\"value\":\"%d\"}}", 1); + err = emh_alisds_set_cloud_atts(EMH_ARG_ALISDS_FORMAT_JSON, (uint8_t*)provision_ack, strlen(provision_ack)); + require_noerr(err, exit); + + sds_log("Send provision acknowledgment to alisds"); + } else { + context.delay_prov = true; + } + +exit: + return; +} + +void alisds_restore(void) +{ + if (context.device_state == eState_M2_provision || context.device_state == eState_M3_normal) { + emh_alisds_unbound(); + } + + emh_module_restore_settings(); + context.device_state = eState_M1_initialize; +} diff --git a/src/alicloud_sds/alicloud_sds.h b/src/alicloud_sds/alicloud_sds.h new file mode 100644 index 0000000..2fb943e --- /dev/null +++ b/src/alicloud_sds/alicloud_sds.h @@ -0,0 +1,203 @@ +/** + ****************************************************************************** + * @file alicloud_sds.h + * @author William Xu + * @version V1.0.0 + * @date 9-Apr-2018 + * @brief AliCloud SDS service functions and framework header file + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef _ALICLOUD_SDS_H_ +#define _ALICLOUD_SDS_H_ + +#include "emh_api.h" + +/** \addtogroup protocols */ +/** @{*/ + +/** \addtogroup Alicloud_SDS_Service */ +/** @{*/ + +/****************************************************************************** + * Constants + ******************************************************************************/ + +#define ALISDS_INVALID_HANDLE (-1) + +/****************************************************************************** + * Enumerations + ******************************************************************************/ + +/** Alicloud sds service events */ +enum alisds_event_e { + ALISDS_EVENT_WLAN_CONFIG_STARTED, /**< AWS service is started to set wlan and cloud. */ + ALISDS_EVENT_WLAN_CONNECTED, /**< Device is connected to Wi-Fi access point. */ + ALISDS_EVENT_WLAN_DISCONNECTED, /**< Device is disconnected from Wi-Fi access point. */ + ALISDS_EVENT_CLOUD_CONNECTED, /**< Alicloud sds service is connected */ + ALISDS_EVENT_CLOUD_DISCONNECTED, /**< Alicloud sds service is disconnected. */ +}; +typedef uint8_t alisds_event_t; /**< Alicloud sds service events (see #alisds_event_e) */ + +/** Alicloud sds attribute types, protocol will convert values to SDS JSON packet according to these types */ +enum alisds_att_type_e { + ALI_ATT_TYPE_BOOL, /**< Boolean value. convert to "0 or "1" */ + ALI_ATT_TYPE_INT, /**< Integer value. convet to "123" */ + ALI_ATT_TYPE_FLOAT, /**< Float value. convet to "123.3" */ + ALI_ATT_TYPE_STRING, /**< String value. no need to convert */ + ALI_ATT_TYPE_DATA, /**< Raw data. convert to string with base64*/ + ALI_ATT_TYPE_NULL, +}; +typedef uint8_t alisds_att_type_t; /**< Alicloud sds attribute types (see #alisds_att_type_e) */ + +/****************************************************************************** + * Type Definitions + ******************************************************************************/ + +typedef int alisds_attr_handle_t; + +/** Alicloud sds attribute value */ +typedef union { + bool boolValue; + int intValue; + double floatValue; + char* stringValue; + char* dateValue; +} alisds_att_val_t; + +/****************************************************************************** + * Function Declarations + ******************************************************************************/ + +/** + * Cloud read attribute handler + * + * @param value[out] : point to attribute value buffer, application should write the correct value + * + * @return status + */ +typedef mx_status (*alisds_read_attr)(alisds_att_val_t* value); + +/** + * Cloud write attribute handler + * + * @param[in] value : New attribute vale sent from the SDS cloud + * + * @return status + */ +typedef mx_status (*alisds_write_attr)(alisds_att_val_t value); + +/** Alicloud sds attribute description */ +typedef struct { + char* name; /**< Attribute name */ + alisds_att_type_t type; /**< Attribute value type */ + alisds_read_attr read_func; /**< Attribute value read handler, optional if the value is readable*/ + alisds_write_attr write_func; /**< Attribute value write handler, optional if the value is writable */ +} alisds_attr_t; + +/****************************************************************************** + * Function Declarations + ******************************************************************************/ + +/** + * @brief Initialize EMW module with product info registered on cloud + * console, allocate memory for SDS attr handle database. + * Should be called before attr initialize see #alisds_attr_init + * + * @param[in] config : SDS product info registered on cloud console + * @param[in] num_handles : The max. attribute numbers, required to allocate + * memory for attr database + * + * @return status + * + */ +mx_status alisds_init(const emh_alisds_config_t* config, int num_handles); + +/** + * @brief SDS Attribute initialization, add the attribute to database + * + * @param[in] handle : attribute handle + * @param[in] attr : Alicloud sds attribute description + * @return status + * + */ +mx_status alisds_attr_init(alisds_attr_handle_t handle, alisds_attr_t attr); + +/** + * + * @brief Alicould sds service runloop, application should called + * periodically to handle events and transfer data. + * To save power, also can be called when uart data is ready + * to receive or sensor data is ready to send to cloud + * @note Never call this function in event handler + * + * @return status + * + */ +mx_status alisds_runloop(void); + +/** + * + * @brief Prepare to send attribute to cloud, attribute value will + * be read and send in SDS runloop alisds_runloop + * @note This function do not send data use AT command, so it can + * be called inside the event handler. #alisds_runloop will + * do the actually read and send function. + * + * @param[in] handle : attribute handle + * @param[in] attr : Alicloud sds attribute description + * @return status + * + */ +void alisds_attr_indicate_by_handle(int handle); + +/** + * + * @brief Send provision data to SDS cloud, this usually called after AWS + * Wi-Fi config to prove APP is bound to a correct device. provision + * data is defined on SDS cloud console. + * + * @return none + * + */ +void alisds_provision(void); + +/** + * + * @brief Try to procedure an unbound operation on cloud if connected and + * restore module settings to default. + * + * @return none + * + */ +void alisds_restore(void); + +/** + * + * @brief Alicloud SDS service event handler accomplished by application + * + * @return none + * + */ +MX_WEAK void alisds_event_handler(alisds_event_t event); + +#endif //_ALICLOUD_SDS_H_ + +/** @}*/ +/** @}*/ diff --git a/src/helper/cli/mx_cli.c b/src/helper/cli/mx_cli.c new file mode 100644 index 0000000..4731fbd --- /dev/null +++ b/src/helper/cli/mx_cli.c @@ -0,0 +1,498 @@ +/** + * UNPUBLISHED PROPRIETARY SOURCE CODE + * Copyright (c) 2016 MXCHIP Inc. + * + * The contents of this file may not be disclosed to third parties, copied or + * duplicated in any form, in whole or in part, without the prior written + * permission of MXCHIP Corporation. + * + */ + +#include "mx_cli.h" +#include "mx_common.h" +#include "mx_hal.h" +#include "stdarg.h" + +#ifdef MX_CLI_ENABLE + +/// CLI /// +#define RX_WAIT 0xFFFFFFFF +#define SEND_WAIT 0xFFFFFFFF + +#define RET_CHAR '\n' +#define END_CHAR '\r' +#define PROMPT "\r\n# " +#define EXIT_MSG "exit" +#define NUM_BUFFERS 1 +#define MAX_COMMANDS 50 +#define INBUF_SIZE 80 +#define OUTBUF_SIZE 1024 + +struct cli_st { + int initialized; + + unsigned int bp; /* buffer pointer */ + char inbuf[INBUF_SIZE]; + char outbuf[OUTBUF_SIZE]; + const struct cli_command* commands[MAX_COMMANDS]; + unsigned int num_commands; + int echo_disabled; +}; + +static struct cli_st pCli; + +static int cli_putstr(const char* msg); + +/* Find the command 'name' in the cli commands table. + * If len is 0 then full match will be performed else upto len bytes. + * Returns: a pointer to the corresponding cli_command struct or NULL. + */ +static const struct cli_command* lookup_command(char* name, int len) +{ + int i = 0; + int n = 0; + + while (i < MAX_COMMANDS && n < pCli.num_commands) { + if (pCli.commands[i] -> name == NULL) { + i++; + continue; + } + /* See if partial or full match is expected */ + if (len != 0) { + if (!strncmp(pCli.commands[i] -> name, name, len)) + return pCli.commands[i]; + } else { + if (!strcmp(pCli.commands[i] -> name, name)) + return pCli.commands[i]; + } + + i++; + n++; + } + + return NULL; +} + +/* Parse input line and locate arguments (if any), keeping count of the number + * of arguments and their locations. Look up and call the corresponding cli + * function if one is found and pass it the argv array. + * + * Returns: 0 on success: the input line contained at least a function name and + * that function exists and was called. + * 1 on lookup failure: there is no corresponding function for the + * input line. + * 2 on invalid syntax: the arguments list couldn't be parsed + */ +static int handle_input(char* inbuf) +{ + struct { + unsigned inArg : 1; + unsigned inQuote : 1; + unsigned done : 1; + } stat; + static char* argv[16]; + int argc = 0; + int i = 0; + const struct cli_command* command = NULL; + const char* p; + + memset((void*)&argv, 0, sizeof(argv)); + memset(&stat, 0, sizeof(stat)); + + do { + switch (inbuf[i]) { + case '\0': + if (stat.inQuote) + return 2; + stat.done = 1; + break; + + case '"': + if (i > 0 && inbuf[i - 1] == '\\' && stat.inArg) { + memcpy(&inbuf[i - 1], &inbuf[i], strlen(&inbuf[i]) + 1); + --i; + break; + } + if (!stat.inQuote && stat.inArg) + break; + if (stat.inQuote && !stat.inArg) + return 2; + + if (!stat.inQuote && !stat.inArg) { + stat.inArg = 1; + stat.inQuote = 1; + argc++; + argv[argc - 1] = &inbuf[i + 1]; + } else if (stat.inQuote && stat.inArg) { + stat.inArg = 0; + stat.inQuote = 0; + inbuf[i] = '\0'; + } + break; + + case ' ': + if (i > 0 && inbuf[i - 1] == '\\' && stat.inArg) { + memcpy(&inbuf[i - 1], &inbuf[i], strlen(&inbuf[i]) + 1); + --i; + break; + } + if (!stat.inQuote && stat.inArg) { + stat.inArg = 0; + inbuf[i] = '\0'; + } + break; + + default: + if (!stat.inArg) { + stat.inArg = 1; + argc++; + argv[argc - 1] = &inbuf[i]; + } + break; + } + } while (!stat.done && ++i < INBUF_SIZE); + + if (stat.inQuote) + return 2; + + if (argc < 1) + return 0; + + if (!pCli.echo_disabled) + cli_printf("\r\n"); + + /* + * Some comamands can allow extensions like foo.a, foo.b and hence + * compare commands before first dot. + */ + i = ((p = strchr(argv[0], '.')) == NULL) ? 0 : (p - argv[0]); + command = lookup_command(argv[0], i); + if (command == NULL) + return 1; + + memset(pCli.outbuf, 0, OUTBUF_SIZE); + cli_putstr("\r\n"); + command->function(pCli.outbuf, OUTBUF_SIZE, argc, argv); + cli_putstr(pCli.outbuf); + return 0; +} + +/* Perform basic tab-completion on the input buffer by string-matching the + * current input line against the cli functions table. The current input line + * is assumed to be NULL-terminated. */ +static void tab_complete(char* inbuf, unsigned int* bp) +{ + int i, n, m; + const char* fm = NULL; + + cli_printf("\r\n"); + + /* show matching commands */ + for (i = 0, n = 0, m = 0; i < MAX_COMMANDS && n < pCli.num_commands; i++) { + if (pCli.commands[i]->name != NULL) { + if (!strncmp(inbuf, pCli.commands[i]->name, *bp)) { + m++; + if (m == 1) + fm = pCli.commands[i]->name; + else if (m == 2) + cli_printf(" % s % s ", fm, pCli.commands[i]->name); + else + cli_printf(" % s ", pCli.commands[i]->name); + } + n++; + } + } + + /* there's only one match, so complete the line */ + if (m == 1 && fm) { + n = strlen(fm) - *bp; + if (*bp + n < INBUF_SIZE) { + memcpy(inbuf + *bp, fm + *bp, n); + *bp += n; + inbuf[(*bp)++] = ' '; + inbuf[*bp] = '\0'; + } + } + + /* just redraw input line */ + cli_printf(" % s % s", PROMPT, inbuf); +} + +/* Get an input line. + * + * Returns: 1 if there is input, 0 if the line should be ignored. */ +static int get_input(char* inbuf, unsigned int* bp) +{ + if (inbuf == NULL) { + return 0; + } + while (cli_getchar(&inbuf[*bp]) == 1) { + if (inbuf[*bp] == RET_CHAR) + continue; + if (inbuf[*bp] == END_CHAR) { /* end of input line */ + inbuf[*bp] = '\0'; + *bp = 0; + return 1; + } + + if ((inbuf[*bp] == 0x08) || /* backspace */ + (inbuf[*bp] == 0x7f)) { /* DEL */ + if (*bp > 0) { + (*bp)--; + if (!pCli.echo_disabled) + cli_printf("%c%c", 0x08, 0x08); + } + continue; + } + + if (inbuf[*bp] == '\t') { + inbuf[*bp] = '\0'; + tab_complete(inbuf, bp); + continue; + } + + if (!pCli.echo_disabled) + cli_printf("%c", inbuf[*bp]); + + (*bp)++; + if (*bp >= INBUF_SIZE) { + cli_printf("Error:input buffer overflow\r\n"); + cli_printf(PROMPT); + *bp = 0; + return 0; + } + } + + return 0; +} + +/* Print out a bad command string, including a hex + * representation of non-printable characters. + * Non-printable characters show as "\0xXX". + */ +static void print_bad_command(char* cmd_string) +{ + if (cmd_string != NULL) { + char* c = cmd_string; + cli_printf("command '"); + while (*c != '\0') { + if (isprint(*c)) { + cli_printf("%c", *c); + } else { + cli_printf("\\0x%x", *c); + } + ++c; + } + cli_printf("' not found\r\n"); + } +} + +static void help_command(char* pcWriteBuffer, int xWriteBufferLen, int argc, + char** argv); + +/* + * Command buffer API + */ + +static void echo_cmd_handler(char* pcWriteBuffer, int xWriteBufferLen, int argc, + char** argv) +{ + if (argc == 1) { + cmd_printf("Usage:echo on/off. Echo is currently %s\r\n", + pCli.echo_disabled ? "Disabled" : "Enabled"); + return; + } + + if (!strcasecmp(argv[1], "on")) { + cmd_printf("Enable echo\r\n"); + pCli.echo_disabled = 0; + } else if (!strcasecmp(argv[1], "off")) { + cmd_printf("Disable echo\r\n"); + pCli.echo_disabled = 1; + } +} + +static const struct cli_command built_ins[] = { + { "help", NULL, help_command }, + { "echo", NULL, echo_cmd_handler }, + // others +}; + +/* Built-in "help" command: prints all registered commands and their help + * text string, if any. */ +static void help_command(char* pcWriteBuffer, int xWriteBufferLen, int argc, + char** argv) +{ + int i, n; + uint32_t build_in_count = sizeof(built_ins) / sizeof(struct cli_command); + + cmd_printf(" ==== Build - in Commands ==== \r\n"); + for (i = 0, n = 0; i < MAX_COMMANDS && n < pCli.num_commands; i++) { + if (pCli.commands[i]->name) { + cmd_printf(" %s: %s\r\n", pCli.commands[i]->name, + pCli.commands[i]->help ? pCli.commands[i]->help : ""); + n++; + if (n == build_in_count) { + cmd_printf("\r\n ==== User Commands ==== \r\n"); + } + } + } +} + +int cli_register_command(const struct cli_command* command) +{ + int i; + if (!command->name || !command->function) + return 1; + + if (pCli.num_commands < MAX_COMMANDS) { + /* Check if the command has already been registered. + * Return 0, if it has been registered. + */ + for (i = 0; i < pCli.num_commands; i++) { + if (pCli.commands[i] == command) + return 0; + } + pCli.commands[pCli.num_commands++] = command; + return 0; + } + + return 1; +} + +int cli_unregister_command(const struct cli_command* command) +{ + int i; + if (!command->name || !command->function) + return 1; + + for (i = 0; i < pCli.num_commands; i++) { + if (pCli.commands[i] == command) { + pCli.num_commands--; + int remaining_cmds = pCli.num_commands - i; + if (remaining_cmds > 0) { + memmove(&pCli.commands[i], &pCli.commands[i + 1], + (remaining_cmds * sizeof(struct cli_command*))); + } + pCli.commands[pCli.num_commands] = NULL; + return 0; + } + } + + return 1; +} + +int cli_register_commands(const struct cli_command* commands, + int num_commands) +{ + int i; + for (i = 0; i < num_commands; i++) + if (cli_register_command(commands++)) + return 1; + return 0; +} + +int cli_unregister_commands(const struct cli_command* commands, + int num_commands) +{ + int i; + for (i = 0; i < num_commands; i++) + if (cli_unregister_command(commands++)) + return 1; + + return 0; +} + +/* Main CLI processing thread + * + * Waits to receive a command buffer pointer from an input collector, and + * then processes. Note that it must cleanup the buffer when done with it. + * + * Input collectors handle their own lexical analysis and must pass complete + * command lines to CLI. + */ +void cli_loop(void) +{ + int ret; + char* msg = NULL; + + if (pCli.initialized != 1) + return; + + if (!get_input(pCli.inbuf, &pCli.bp)) + return; + + msg = pCli.inbuf; + + if (msg != NULL) { + if (strcmp(msg, EXIT_MSG) == 0) + return; + ret = handle_input(msg); + if (ret == 1) + print_bad_command(msg); + else if (ret == 2) + cli_printf("syntax error\r\n"); + cli_printf(PROMPT); + } +} + +int cli_init(void) +{ + memset(&pCli, 0, sizeof(struct cli_st)); + + /* add our built-in commands */ + if (cli_register_commands(&built_ins[0], + sizeof(built_ins) / sizeof(struct cli_command))) { + printf("cli err\r\n"); + return kGeneralErr; + } + + pCli.initialized = 1; + + return kNoErr; +} + +/* ========= CLI input&output APIs ============ */ + +int cli_printf(const char* msg, ...) +{ + va_list ap; + char *pos, message[256]; + int sz; + int nMessageLen = 0; + + memset(message, 0, 256); + pos = message; + + sz = 0; + va_start(ap, msg); + nMessageLen = vsnprintf(pos, 256 - sz, msg, ap); + va_end(ap); + + if (nMessageLen <= 0) + return 0; + + cli_putstr((const char*)message); + return 0; +} + +int cli_putstr(const char* msg) +{ + if (msg[0] != 0) + mx_hal_cli_putstr((const char*)msg, strlen(msg)); + + return 0; +} + +int cli_getchar(char* inbuf) +{ + int ch; + ch = mx_hal_cli_getc(); + if (ch < 0) + return 0; + + inbuf[0] = (char)ch; + return 1; +} + +#endif diff --git a/src/helper/cli/mx_cli.h b/src/helper/cli/mx_cli.h new file mode 100644 index 0000000..67c469d --- /dev/null +++ b/src/helper/cli/mx_cli.h @@ -0,0 +1,103 @@ +/** + * UNPUBLISHED PROPRIETARY SOURCE CODE + * Copyright (c) 2016 MXCHIP Inc. + * + * The contents of this file may not be disclosed to third parties, copied or + * duplicated in any form, in whole or in part, without the prior written + * permission of MXCHIP Corporation. + * + */ + +#ifndef __MX_CLI_H__ +#define __MX_CLI_H__ +/** Structure for registering CLI commands */ +struct cli_command { + /** The name of the CLI command */ + const char* name; + /** The help text associated with the command */ + const char* help; + /** The function that should be invoked for this command. */ + void (*function)(char* pcWriteBuffer, int xWriteBufferLen, int argc, char** argv); +}; + +#define cmd_printf(...) \ + do { \ + if (xWriteBufferLen > 0) { \ + snprintf(pcWriteBuffer, xWriteBufferLen, __VA_ARGS__); \ + xWriteBufferLen -= strlen(pcWriteBuffer); \ + pcWriteBuffer += strlen(pcWriteBuffer); \ + } \ + } while (0) + +#define CLI_ARGS char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv + +/** Register a CLI command + * + * This function registers a command with the command-line interface. + * + * \param[in] command The structure to register one CLI command + * \return 0 on success + * \return 1 on failure + */ +int cli_register_command(const struct cli_command* command); + +/** Unregister a CLI command + * + * This function unregisters a command from the command-line interface. + * + * \param[in] command The structure to unregister one CLI command + * \return 0 on success + * \return 1 on failure + */ +int cli_unregister_command(const struct cli_command* command); + +/** Stop the CLI thread and carry out the cleanup + * + * \return kNoErr on success + * \return error code otherwise. + * + */ +int cli_stop(void); + +/** Register a batch of CLI commands + * + * Often, a module will want to register several commands. + * + * \param[in] commands Pointer to an array of commands. + * \param[in] num_commands Number of commands in the array. + * \return 0 on success + * \return 1 on failure + */ +int cli_register_commands(const struct cli_command* commands, int num_commands); + +/** Unregister a batch of CLI commands + * + * \param[in] commands Pointer to an array of commands. + * \param[in] num_commands Number of commands in the array. + * \return 0 on success + * \return 1 on failure + */ +int cli_unregister_commands(const struct cli_command* commands, + int num_commands); + +/* Get a CLI msg + * + * If an external input task wants to use the CLI, it can use + * cli_get_cmd_buffer() to get a command buffer that it can then + * submit to the CLI later using cli_submit_cmd_buffer(). + * + * \param buff Pointer to a char * to place the buffer pointer in. + * \return 0 on success + * \return error code otherwise. + */ +int cli_getchar(char* inbuf); + +/* Send CLI output msg + * + * \param buff Pointer to a char * buffer. + * \return 0 on success + * \return error code otherwise. + */ +int cli_printf(const char* buff, ...); + +#endif diff --git a/src/helper/emhost/ATCmdParser/ATCmdParser.c b/src/helper/emhost/ATCmdParser/ATCmdParser.c new file mode 100644 index 0000000..d8439dd --- /dev/null +++ b/src/helper/emhost/ATCmdParser/ATCmdParser.c @@ -0,0 +1,374 @@ +/** + ****************************************************************************** + * @file ATCmdParser.c + * @author William Xu + * @version V1.0.0 + * @date 9-Apr-2018 + * @brief EMW module AT command parser + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include "ATCmdParser.h" +#include "mx_hal.h" + +#include "mx_common.h" + +/****************************************************************************** + * Constants + ******************************************************************************/ + +#ifdef LF +#undef LF +#define LF 10 +#else +#define LF 10 +#endif + +#ifdef CR +#undef CR +#define CR 13 +#else +#define CR 13 +#endif + +/****************************************************************************** + * Variable Definitions + ******************************************************************************/ + +bool _dbg_on = false; +static struct oob* _oobs; +static char _buffer[AT_BUFFER_SIZE]; +static const char* _output_delimiter; +static int _output_delim_size; +static const char* _input_delimiter; +static int _input_delim_size; + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +static inline void debug_if(int condition, const char* format, ...) +{ + if (condition) { + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + } +} + +bool ATCmdParser_vrecv(const char* response, va_list args) +{ + char _in_prev = 0; + bool _aborted; +restart: + _aborted = false; + // Iterate through each line in the expected response + while (response[0]) { + // Since response is const, we need to copy it into our buffer to + // add the line's null terminator and clobber value-matches with asterisks. + // + // We just use the beginning of the buffer to avoid unnecessary allocations. + int i = 0; + int offset = 0; + bool whole_line_wanted = false; + + while (response[i]) { + if (response[i] == '%' && response[i + 1] != '%' && response[i + 1] != '*') { + _buffer[offset++] = '%'; + _buffer[offset++] = '*'; + i++; + } else { + _buffer[offset++] = response[i++]; + // Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification + if (response[i - 1] == '\n' && !(i >= 3 && response[i - 3] == '[' && response[i - 2] == '^')) { + whole_line_wanted = true; + break; + } + } + } + + // Scanf has very poor support for catching errors + // fortunately, we can abuse the %n specifier to determine + // if the entire string was matched. + _buffer[offset++] = '%'; + _buffer[offset++] = 'n'; + _buffer[offset++] = 0; + + debug_if(_dbg_on, "AT? %s\n", _buffer); + // To workaround scanf's lack of error reporting, we actually + // make two passes. One checks the validity with the modified + // format string that only stores the matched characters (%n). + // The other reads in the actual matched values. + // + // We keep trying the match until we succeed or some other error + // derails us. + int j = 0, dummy = 0; + int dummy_pos[20]; + + while (true) { + // Receive next character + int c = mx_hal_serial_getc(); + if (c < 0) { + debug_if(_dbg_on, "AT(Timeout)\n"); + return false; + } + + /* Possible not existed string may cause %n function failed: + example: "cmd:%*s\r\n%n" not match "cmd:\r\n", %n will not + give a valid value, so we give some dummy chars */ + if (c == '\n' && _in_prev == ':') { + dummy_pos[dummy++] = j; + _buffer[offset + j++] = 'x'; + } + + _in_prev = c; + + _buffer[offset + j++] = c; + _buffer[offset + j] = 0; + + struct oob* oob = _oobs; + // Check for oob data + for (; oob; oob = (struct oob*)oob->next) { + if ((unsigned)j == oob->len && memcmp(oob->prefix, _buffer + offset, oob->len) == 0) { + debug_if(_dbg_on, "AT! %s\n", oob->prefix); + oob->cb(); + + if (_aborted) { + debug_if(_dbg_on, "AT(Aborted)\n"); + return false; + } + // oob may have corrupted non-reentrant buffer, + // so we need to set it up again + goto restart; + } + } + + // Check for match + int count = -1; + if (whole_line_wanted && c != '\n') { + // Don't attempt scanning until we get delimiter if they included it in format + // This allows recv("Foo: %s\n") to work, and not match with just the first character of a string + // (scanf does not itself match whitespace in its format string, so \n is not significant to it) + } else { + sscanf(_buffer + offset, _buffer, &count); + debug_if(_dbg_on, "%s=====%s, %d,%d\r\n", _buffer, _buffer + offset, count, j); + } + + // We only succeed if all characters in the response are matched + if (count == j) { + /* Remove dummy chars*/ + for (int k = 0; k < dummy; k++, j--) { + dummy_pos[k] -= k; + memcpy(_buffer + offset + dummy_pos[k], _buffer + offset + dummy_pos[k] + 1, j - dummy_pos[k] - 1); + debug_if(_dbg_on, "AT= %s\n", _buffer + offset); + } + + debug_if(_dbg_on, "AT= %s\n", _buffer + offset); + // Reuse the front end of the buffer + memcpy(_buffer, response, i); + _buffer[i] = 0; + + // Store the found results + vsscanf(_buffer + offset, _buffer, args); + // Jump to next line and continue parsing + response += i; + break; + } + + // Clear the buffer when we hit a newline or ran out of space + // running out of space usually means we ran into binary data + if (c == '\n' || j + 1 >= AT_BUFFER_SIZE - offset) { + debug_if(_dbg_on, "AT< %s", _buffer + offset); + j = 0; + dummy = 0; + } + } + } + + return true; +} + +// Command parsing with line handling +bool ATCmdParser_vsend(const char* command, va_list args) +{ + while (ATCmdParser_process_oob()) + ; + // Create and send command + if (vsprintf(_buffer, command, args) < 0) { + return false; + } + + for (int i = 0; _buffer[i]; i++) { + if (mx_hal_serial_putc(_buffer[i]) < 0) { + return false; + } + } + + // Finish with newline + for (size_t i = 0; _output_delimiter[i]; i++) { + if (mx_hal_serial_putc(_output_delimiter[i]) < 0) { + return false; + } + } + + debug_if(_dbg_on, "AT> %s\n", _buffer); + return true; +} + +bool ATCmdParser_recv(const char* response, ...) +{ + va_list args; + va_start(args, response); + bool res = ATCmdParser_vrecv(response, args); + va_end(args); + return res; +} + +bool ATCmdParser_send(const char* command, ...) +{ + va_list args; + va_start(args, command); + bool res = ATCmdParser_vsend(command, args); + va_end(args); + return res; +} + +// read/write handling with timeouts +int ATCmdParser_write(const char* data, int size) +{ + int i = 0; + for (; i < size; i++) { + if (mx_hal_serial_putc(data[i]) < 0) { + return -1; + } + //mx_delay(1); + } + return i; +} + +int ATCmdParser_read(char* data, int size) +{ + int i = 0; + for (; i < size; i++) { + int c = mx_hal_serial_getc(); + if (c < 0) { + return -1; + } + data[i] = c; + } + return i; +} + +void ATCmdParser_debug(bool on) +{ + _dbg_on = on; +} + +void ATCmdParser_add_oob(const char* prefix, oob_callback cb) +{ + struct oob* oob = malloc(sizeof(struct oob)); + oob->len = strlen(prefix); + oob->prefix = prefix; + oob->cb = cb; + oob->next = _oobs; + _oobs = oob; +} + +bool ATCmdParser_process_oob(void) +{ + if (!mx_hal_serial_readable()) { + return false; + } + + int i = 0; + while (true) { + // Receive next character + int c = mx_hal_serial_getc(); + if (c < 0) { + return false; + } + _buffer[i++] = c; + _buffer[i] = 0; + + // Check for oob data + struct oob* oob = _oobs; + while (oob) { + if (i == (int)oob->len && memcmp(oob->prefix, _buffer, oob->len) == 0) { + debug_if(_dbg_on, "AT! %s\r\n", oob->prefix); + oob->cb(); + return true; + } + oob = oob->next; + } + + // Clear the buffer when we hit a newline or ran out of space + // running out of space usually means we ran into binary data + if (i + 1 >= AT_BUFFER_SIZE || strcmp(&_buffer[i - _input_delim_size], _input_delimiter) == 0) { + + debug_if(_dbg_on, "AT< %s, %d\r\n", _buffer, i); + i = 0; + } + } +} + +int ATCmdParser_analyse_args(char args[], char* arg_list[], int list_size) +{ + char _in_prev = 0; + int arg_num = 1; + size_t len = strlen(args); + + arg_list[0] = args; + + for (int i = 0; i <= len; i++) { + debug_if(_dbg_on, "check %c, %d\r\n", args[i], len); + if (args[i] == ',') { + debug_if(_dbg_on, "find ,\r\n"); + if (_in_prev == '\\') { + args[i - i] = ','; + memcpy(&args[i], &args[i + 1], len - (i + 1)); + } else { + args[i] = 0; + arg_list[arg_num++] = &args[i + 1]; + if (arg_num > list_size) + break; + } + } + _in_prev = args[i]; + } + return arg_num; +} + +void ATCmdParser_set_timeout(int timeout) +{ + mx_hal_serial_set_timeout(timeout); +} + +void ATCmdParser_init(const char* output_delimiter, const char* input_delimiter, int timeout, bool debug) +{ + _dbg_on = debug; + + _output_delimiter = output_delimiter; + _output_delim_size = strlen(output_delimiter); + + _input_delimiter = input_delimiter; + _input_delim_size = strlen(input_delimiter); + + mx_hal_serial_init(timeout); +} diff --git a/src/helper/emhost/ATCmdParser/ATCmdParser.h b/src/helper/emhost/ATCmdParser/ATCmdParser.h new file mode 100644 index 0000000..26a5a44 --- /dev/null +++ b/src/helper/emhost/ATCmdParser/ATCmdParser.h @@ -0,0 +1,177 @@ +/** + ****************************************************************************** + * @file ATCmdParser.h + * @author William Xu + * @version V1.0.0 + * @date 9-Apr-2018 + * @brief EMW module AT command parser function header file + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef _AT_CMD_PARSER_H_ +#define _AT_CMD_PARSER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \addtogroup emhost */ +/** @{*/ + +/** \addtogroup AT_parser */ +/** @{*/ + +/****************************************************************************** + * Constants + ******************************************************************************/ + +/****************************************************************************** + * Type Definitions + ******************************************************************************/ + +/** + * Incomming AT out-of-band packet handler + */ +typedef void (*oob_callback)(void); + +/** + * Incomming AT out-of-band packet format link node + */ +struct oob { + unsigned len; + const char* prefix; + oob_callback cb; + void* next; +}; + +/****************************************************************************** + * Function Declarations + ******************************************************************************/ + +/** + * @brief Enable debug mode + * + * @return none + */ +void ATCmdParser_debug(bool on); + +/** + * @brief AT command parser initialize + * + * @param[in] output_delimiter: AT output command delimiter chars, "\r", "\r\n", "\n", etc + * @param[in] input_delimiter: AT input respond and event delimiter chars, "\r", "\r\n", "\n", etc + * @param[in] timeout: AT receive timeout, also changable by #ATCmdParser_set_timeout + * @param[in] debug: Enable debug mode or not + * + * @return none + */ +void ATCmdParser_init(const char* output_delimiter, const char* input_delimiter, int timeout, bool debug); + +/** + * @brief Recv AT command respont, and parse AT parameters to variables + * + * @param[in] Incomming respond format, refer to printf + * + * @return true: Success, false: Timeout or format not match + */ +bool ATCmdParser_recv(const char* response, ...); + +/** + * @brief Send AT command + * + * @param[in] AT command format, refer to scanf + * + * @return true: Success, false: Serial port send error + */ +bool ATCmdParser_send(const char* command, ...); + +/** + * @brief Add a handler to an incomming out-of-band packet type, + * example: oob format: "\r\n+:[para-1,para-2,para-2,...,para-n]\r\n", + * "+" is the prefix + * @note Never send a AT command in the handler + * + * @param[in] prefix: incomming oob packet prefix. + * + * @return none + */ +void ATCmdParser_add_oob(const char* prefix, oob_callback cb); + +/** + * @brief Read raw data from AT command serial port + * + * @param[out] data: Buffer to store the incomming data + * @param[in] size: Buffer size + * + * @return size of the actually received data + */ +int ATCmdParser_read(char* data, int size); + +/** + * @brief Send raw data to AT command serial port + * + * @param[in] data: Point to the data buffer ready to send + *@param[in] size: Buffer size + * + * @return size of the actually sent data + */ +int ATCmdParser_write(const char* data, int size); + +/** + * @brief Set AT parser timeout + * + * @param[in] timeout: new timeout value + * + * @return none + */ +void ATCmdParser_set_timeout(int timeout); + +/** + * @brief Receive and parse incomming out-of-band packet + * + * @return true: proccessed a packet, false: no packet to process + */ +bool ATCmdParser_process_oob(void); + +/** + * @brief Analyse string parameters form AT command respond, + * Respond format: "[\r\n][+CMD:][para-1,para-2,para-3,......]<\r\n><\r\n>" + * First, read para list "para-1,para-2,para-3,......" from respond to string by #ATCmdParser_recv + * second, use this function to parse every string para into an array + * example: "111,222,333\,33,444" to "111", "222", "333,33", "444" + * + * @param[in] args: parameters string, like para-1,para-2,para-3,...... + * @param[out] arg_list: point to parameters list array + * @param[in] list_size: parameters list array size + * + * @return true: proccessed a packet, false: no packet to process + */ +int ATCmdParser_analyse_args(char args[], char* arg_list[], int list_size); + +/** @}*/ +/** @}*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif //_AT_CMD_PARSER_H_ diff --git a/src/helper/emhost/ATCmdParser/ATCmdParserTest.c b/src/helper/emhost/ATCmdParser/ATCmdParserTest.c new file mode 100644 index 0000000..db0ba54 --- /dev/null +++ b/src/helper/emhost/ATCmdParser/ATCmdParserTest.c @@ -0,0 +1,24 @@ + +#include + +#include "emh_api.h" +#include "mx_hal.h" + +char* return_str; + +void ATCmdParser_test(void) +{ + mx_status ret = kNoErr; + + mx_hal_ms_ticker_init(); + + ret = emh_init(); + if (ret == kNoErr) + printf("module init success\r\n"); + + printf("FW version: %s\r\n", emh_module_get_fw_version()); + printf("System tick: %d\r\n", (int)emh_module_get_tick()); + + while (1) + ; +} diff --git a/src/helper/emhost/emh_alisds.c b/src/helper/emhost/emh_alisds.c new file mode 100644 index 0000000..0ddd38c --- /dev/null +++ b/src/helper/emhost/emh_alisds.c @@ -0,0 +1,234 @@ +/** + ****************************************************************************** + * @file emh_alisds.c + * @author William Xu + * @version V1.0.0 + * @date 9-Apr-2018 + * @brief Alicloud SDS service AT commands API + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + + +#include +#include + +#include "emh_api.h" +#include "ATCmdParser/ATCmdParser.h" + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +MX_WEAK void emh_ev_alisds_get_local_atts(emh_alisds_msg *attrs) +{ + return; +} + +mx_status emh_alisds_config(const emh_alisds_config_t* config) +{ + char args[200], *arg_list[5]; + const char* format_arg = emh_arg_for_type(EMH_ARG_ALISDS_FORMAT, config->product_info.format); + + /* Check product info*/ + if (!(ATCmdParser_send("AT+ALINKPRODUCT?") + && ATCmdParser_recv("+ALINKPRODUCT:%200[^\r]\r\n",args) + && ATCmdParser_recv("OK\r\n"))) { + return kReadErr; + } + + if (4 != ATCmdParser_analyse_args(args, arg_list, 4)) { + return kMalformedErr; + } + + if (strcmp(arg_list[0], config->product_info.name) + || strcmp(arg_list[1], config->product_info.module) + || strcmp(arg_list[2], config->product_info.key) + || strcmp(arg_list[3], config->product_info.secret) ){ + + if (!(ATCmdParser_send("AT+ALINKPRODUCT=%s,%s,%s,%s,%s", + config->product_info.name, config->product_info.module, + config->product_info.key, config->product_info.secret, format_arg ) + && ATCmdParser_recv("OK\r\n"))) { + return kGeneralErr; + } + } + + /* Check device info*/ + if (!(ATCmdParser_send("AT+ALINKDEV?") + && ATCmdParser_recv("+ALINKDEV:%40[^\r]\r\n",args) + && ATCmdParser_recv("OK\r\n"))) { + return kReadErr; + } + + if (3 != ATCmdParser_analyse_args(args, arg_list, 3)) { + return kMalformedErr; + } + + if ((strcmp(arg_list[0], config->dev_info.type) + || strcmp(arg_list[1], config->dev_info.category) + || strcmp(arg_list[2], config->dev_info.manufacture))){ + + if (!(ATCmdParser_send("AT+ALINKDEV=%s,%s,%s", + config->dev_info.type, config->dev_info.category, + config->dev_info.manufacture ) + && ATCmdParser_recv("OK\r\n"))) { + return kGeneralErr; + } + } + + return kNoErr; +} + +mx_status emh_alisds_set_key (const char *dev_key, const char *dev_sec) +{ + if (ATCmdParser_send("AT+ALINKSDS=%s,%s", dev_key, dev_sec) + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +mx_status emh_alisds_start_service(void) +{ + if (ATCmdParser_send("AT+ALINKSTART") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +emh_arg_alisds_status_t emh_alisds_get_status(void) +{ + char arg[20]; + + if (!(ATCmdParser_send("AT+ALINKSTATUS?") + && ATCmdParser_recv("+ALINKSTATUS:%20[^\r]\r\n",arg) + && ATCmdParser_recv("OK\r\n"))) { + return EMH_ARG_ERR; + } + + return emh_arg_for_arg( EMH_ARG_ALISDS_STATUS, arg); +} + +mx_status emh_alisds_provision(bool on) +{ + if ((on? ATCmdParser_send("AT+ALINKAWSSTART"): + ATCmdParser_send("AT+ALINKAWSSTOP")) + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +mx_status emh_ali_start_provision(void) +{ + if (ATCmdParser_send("AT+ALINKAWSSTART") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +mx_status emh_alisds_unbound(void) +{ + if (ATCmdParser_send("AT+ALINKUNBIND") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +mx_status emh_ali_stop_provision(void) +{ + if (ATCmdParser_send("AT+ALINKAWSSTOP") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +mx_status emh_alisds_set_cloud_atts(emh_arg_alisds_format_t format, uint8_t *data, int32_t len) +{ + if (ATCmdParser_send("AT+ALINKSEND=%d", len) + && ATCmdParser_recv(">") + && ATCmdParser_write((char *)data, len) == len + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +void emh_alisds_event_handler(void) +{ + mx_status err = kNoErr; + char arg1[10], arg2[10]; + emh_arg_alisds_format_t format; + emh_arg_alisds_conn_t conn; + emh_alisds_msg attrs; + + // parse out the packet + require_action(ATCmdParser_recv("%10[^,],", arg1), exit, err = kMalformedErr); + + emh_arg_alisds_ev_t event = emh_arg_for_arg(EMH_ARG_ALISDS_EV, arg1); + require_action(event != EMH_ARG_ERR, exit, err = kMalformedErr); + + /* ALINK Server connection event */ + if (event == EMH_ARG_ALISDS_EV_ALINK) { + require_action(ATCmdParser_recv("%10[^\r]\r\n", arg2), exit, err = kMalformedErr); + conn = emh_arg_for_arg(EMH_ARG_ALISDS_CONN, arg2); + require_action(conn != EMH_ARG_ERR, exit, err = kMalformedErr); + emh_ev_alisds_connection(conn); + } + /* ALINK server <=== attribute value=== device */ + else if (event == EMH_ARG_ALISDS_EV_GET) { + require_action(ATCmdParser_recv("%10[^\r]\r\n", arg2), exit, err = kMalformedErr); + format = emh_arg_for_arg(EMH_ARG_ALISDS_FORMAT, arg2); + require_action(format != EMH_ARG_ERR, exit, err = kMalformedErr); + + attrs.format = format; + attrs.data = NULL; + attrs.len = 0; + emh_ev_alisds_get_local_atts(&attrs); + } + /* ALINK server === attribute value===> device */ + else if (event == EMH_ARG_ALISDS_EV_SET) { + require_action(ATCmdParser_recv("%10[^,],", arg2), exit, err = kMalformedErr); + format = emh_arg_for_arg(EMH_ARG_ALISDS_FORMAT, arg2); + require_action(format != EMH_ARG_ERR, exit, err = kMalformedErr); + + /* Read package data */ + int32_t count; + require_action(ATCmdParser_recv("%d,", &count), exit, err = kMalformedErr); + + uint8_t *data = malloc(count); + require_action(data, exit, err = kNoMemoryErr); + require_action(ATCmdParser_read((char *)data, count) == count, exit, err = kTimeoutErr); + + attrs.data = data; + attrs.format = format; + attrs.len = count; + emh_ev_alisds_set_local_atts(&attrs); + free(data); + } + +exit: + if (err == kMalformedErr) emh_ev_unknown(); + return; +} + diff --git a/src/helper/emhost/emh_api.h b/src/helper/emhost/emh_api.h new file mode 100644 index 0000000..2abb3d4 --- /dev/null +++ b/src/helper/emhost/emh_api.h @@ -0,0 +1,428 @@ +/** + ****************************************************************************** + * @file emh_arg.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief Emhost SDK aAPIs + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef _EMH_API_H_ +#define _EMH_API_H_ + +#include "emh_arg.h" +#include "mx_common.h" +#include "mx_debug.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \addtogroup emhost */ +/** @{*/ + +/****************************************************************************** + * Module controller + ******************************************************************************/ + +/** \addtogroup module */ +/** @{*/ + +/** + * @brief Emhost SDK initialize and EMW module initialize + * + * @return status + */ +mx_status emh_init(void); + +/** + * @brief Emhost SDK initialize and EMW module initialize + * + * @return none + */ +void emh_runloop(void); + +/** + * @brief Software reset EMW module + * + * @return status + */ +mx_status emh_module_reset(void); + +mx_status emh_module_check_ready(void); + +/** + * @brief Read EMW module firmware version + * + * @return Point to firmware version string + */ +const char* emh_module_get_fw_version(void); + +/** + * @brief Read EMW module internal tick + * + * @return Tick value + */ +uint32_t emh_module_get_tick(void); + +/** + * @brief Read EMW module internal tick + * + * @return status + */ +mx_status emh_module_restore_settings(void); + +/** + * @brief Receive an unknown event, or data format error + * + * @return none + */ +MX_WEAK void emh_ev_unknown(void); + +/** @}*/ +/****************************************************************************** + * Wlan management + ******************************************************************************/ + +/** \addtogroup wlan */ +/** @{*/ + +/** + * @brief Read current configured AP's SSID and Password + * + * @param[out] timeout: AT receive timeout, also changable by #ATCmdParser_set_timeout + * @param[out] debug: Enable debug mode or not + * + * @return status + */ +mx_status emh_wlan_get_para(char ssid[33], char pwd[33]); + +/** + * @brief Read wlan connection status + * + * @return Refer to #emh_arg_wlan_sta_t + */ +emh_arg_wlan_sta_t emh_wlan_get_sta_status(void); + +/** + * @brief Event: wlan connection status is changed + * + * @return none + */ +MX_WEAK void emh_ev_wlan(emh_arg_wlan_ev_t event); + +/** @}*/ +/****************************************************************************** + * Alicoud SDS service + ******************************************************************************/ + +/** \addtogroup alicloud_sds */ +/** @{*/ + +/** + * Alicloud sds service product information registered on cloud console + */ +typedef struct { + struct { + const char* name; /**< Reference to product TRD document */ + const char* module; /**< Reference to product TRD document */ + const char* key; /**< Reference to product TRD document */ + const char* secret; /**< Reference to product TRD document */ + emh_arg_alisds_format_t format; /**< Reference to product TRD document */ + } product_info; + struct { + const char* type; /**< Reference to product TRD document */ + const char* category; /**< Reference to product TRD document */ + const char* manufacture; /**< Reference to product TRD document */ + } dev_info; +} emh_alisds_config_t; + +/** + * Alicloud sds service message + */ +typedef struct { + int32_t len; /**< message length */ + uint8_t* data; /**< point to the buffer store the message */ + emh_arg_alisds_format_t format; /**< message format, json or raw data */ +} emh_alisds_msg; + +/** + * @brief Read product info data from module, write new data if not equal + * + * @param[in] config: Product information + * + * @return status + */ +mx_status emh_alisds_config(const emh_alisds_config_t* config); + +/** + * @brief Start cloud service on module + * + * @return status + */ +mx_status emh_alisds_start_service(void); + +/** + * @brief Start or stop AWS Wi-Fi configuration, cloud provision procedure bond module to APP + * @warning After bound device to APP, the device key can no longer used in other SDS products + * + * @param[in] on: true for start and false for stop + * + * @return status + */ +mx_status emh_alisds_provision(bool on); + +/** + * @brief Get current alicloud service connection status, + * + * @return connection status + */ +emh_arg_alisds_status_t emh_alisds_get_status(void); + +/** + * @brief Send message to cloud from local + * + * @param[in] format: message format, json or raw data + * @param[in] data: point to the buffer store the outgoing message + * @param[in] len: outgoing message length + * + * @return status + */ +mx_status emh_alisds_set_cloud_atts(emh_arg_alisds_format_t format, uint8_t* data, int32_t len); + +/** + * @brief Set device key, device key is used to connect to SDS service. + * @note The default key is stored in EMW module, use this function to write the new key. + * A factory restore command remove the new key and use the default key + * + * @param[in] dev_key: device key + * @param[in] dev_sec: device security + * + * @return status + */ +mx_status emh_alisds_set_key(const char* dev_key, const char* dev_sec); + +/** + * @brief Un-bond device from APP, on works when connected to cloud + * + * @return status + */ +mx_status emh_alisds_unbound(void); + +/** + * @brief Event: Alicloud SDS service connection status is changed + * + * @return none + */ +MX_WEAK void emh_ev_alisds_connection(emh_arg_alisds_conn_t conn); + +/** + * @brief Event: Alicloud SDS service is requesting data from local device + * + * @param[in] attrs: SDS service msg + * + * @return none + */ +MX_WEAK void emh_ev_alisds_get_local_atts(emh_alisds_msg* attrs); + +/** + * @brief Event: Alicloud SDS service is writing data to local device + * + * @param[in] attrs: SDS service msg + * + * @return none + */ +MX_WEAK void emh_ev_alisds_set_local_atts(emh_alisds_msg* attrs); + +/** @}*/ +/****************************************************************************** + * Alicoud ILOP service + ******************************************************************************/ + +/** \addtogroup alicloud_ilop */ +/** @{*/ + +#define EMH_ILOP_PRODUCT_KEY_MAXLEN (20 + 1) +#define EMH_ILOP_PRODUCT_SECRET_MAXLEN (64 + 1) +#define EMH_ILOP_DEVICE_NAME_MAXLEN (32 + 1) +#define EMH_ILOP_DEVICE_SECRET_MAXLEN (64 + 1) + +/** + * Alicloud ilop service product information registered on cloud console + */ +typedef struct { + const char* tsl_thing; /**< TSL model defined by Alibaba cloud platform(json string). if dm=EMH_ARG_ILOP_DM_RAW, tls_thing=NULL */ + uint32_t tsl_len; /**< The JSON string length of the TSL model. if dm=EMH_ARG_ILOP_DM_RAW, tls_len=0 */ + emh_arg_ilop_dm_t dm; /**< The method to access the TLS, raw , local, cloud.*/ +} emh_ilop_config_t; + +/** + * Alicloud ilop service message + */ +typedef struct { + int32_t len; /**< message length */ + uint8_t* data; /**< point to the buffer store the message */ + emh_arg_ilop_format_t format; /**< message format, ica or raw data */ +} emh_ilop_msg; + +/** + * @brief Read product info data from module, write new data if not equal + * + * @param[in] config: Product information + * @param[in] force: Force setting + * + * @return status + */ +mx_status emh_ilop_config(emh_ilop_config_t *config, bool force); + +/** + * @brief Set up TSL(json string) from AliCloud ilop platform + * + * @param[in] tsl_thing: TSL string + * @param[in] tsl_len: TSL string length + * + * @return status + */ +mx_status emh_ilop_set_tsl(const char* tsl_thing, uint32_t tsl_len); + +/** + * @brief Set product key, product secret, device secret, device name is used to connect to ILOP service. + * @note The default key is stored in EMW module, use this function to write the new key. + * + * @param[in] pk: product key + * @param[in] ps: product secret + * @param[in] ds: device secret + * @param[in] dn: device name + * + * @return status + */ +mx_status emh_ilop_set_key(const char* pk, const char* ps, const char* ds, const char* dn); + +/** + * @brief Get product key, product secret, device secret, device name form EMW module. + * + * @param[in] pk: product key + * @param[in] ps: product secret + * @param[in] ds: device secret + * @param[in] dn: device name + * + * @return status + */ +mx_status emh_ilop_get_key(char pk[EMH_ILOP_PRODUCT_KEY_MAXLEN], char ps[EMH_ILOP_PRODUCT_SECRET_MAXLEN], char ds[EMH_ILOP_DEVICE_SECRET_MAXLEN], char dn[EMH_ILOP_DEVICE_NAME_MAXLEN]); + +/** + * @brief Start AWSS Wi-Fi configuration. EMW module start monitor mode + * @note In the monitor mode, only capture packets. + * + * @return status + */ +mx_status emh_ilop_awss_start(void); + +/** + * @brief Start AWSS press + * @note In the monitor mode, parsing the air package, connecting the server, binding the phone + * + * @return status + */ +mx_status emh_ilop_awss_press(void); + +/** + * @brief Start cloud service on module + * + * @return status + */ +mx_status emh_ilop_service_start(void); + +/** + * @brief Get current alicloud ILOP service connection status. + * + * @return connection status + */ +emh_arg_ilop_status_t emh_ilop_get_stauts(void); + +/** + * @brief To unbind the user, the device must be connected to the service. + * + * @return mx_status + */ +mx_status emh_ilop_unbind(void); + +/** + * @brief Stop cloud service on module + * + * @return mx_status + */ +mx_status emh_ilop_service_stop(void); + +/** + * @brief Send message to cloud from local + * + * @param[in] type: Attributes type. see: emh_arg_ilop_vt_t + * @param[in] attr: outgoing message, format: key,value... + * + * @return mx_status + */ +mx_status emh_ilop_send_ica_to_cloud(const char* type, char* attr); + +/** + * @brief Send message to cloud from local + * + * @param[in] data: point to the buffer store the outgoing message + * @param[in] len: outgoing message length + * + * @return mx_status + */ +mx_status emh_ilop_send_raw_to_cloud(uint8_t* data, uint32_t len); + +/** + * @brief Event hadnle, incomming oob packet prefix. + * + * @param[in] attrs: SDS service msg + * + * @return none + */ +void emh_ilop_event_handler(void); + +/** + * @brief Event: Alicloud ILOP service connection status is changed + * + * @param[in] conn: service connection status + * + * @return none + */ +MX_WEAK void emh_ev_ilop_connection(emh_arg_ilop_conn_t conn); + +/** + * @brief Event: Alicloud ILOP service is writing data to local device + * + * @param[in] msg: ILOP service msg + * + * @return none + */ +MX_WEAK void emh_ev_ilop_set_local_attr(emh_ilop_msg* msg); + +/** @}*/ +/** @}*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/helper/emhost/emh_arg.c b/src/helper/emhost/emh_arg.c new file mode 100644 index 0000000..7695e94 --- /dev/null +++ b/src/helper/emhost/emh_arg.c @@ -0,0 +1,167 @@ +/** + ****************************************************************************** + * @file emh_arg.c + * @author William Xu + * @version V1.0.0 + * @date 9-Apr-2018 + * @brief AT command arguments to enum type convert + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include "emh_arg.h" + +/****************************************************************************** + * Constants + ******************************************************************************/ + +/****************************************************************************** + * Alicoud SDS service + ******************************************************************************/ + +#ifdef AT_SUPPORT_ALISDS +const char* EMH_ARG_ALISDS_FORMAT[] = +{ + [EMH_ARG_ALISDS_FORMAT_JSON] = "JSON", + [EMH_ARG_ALISDS_FORMAT_RAW] = "RAW", + [EMH_ARG_ALISDS_FORMAT_MAX] = "\0", +}; + +const char* EMH_ARG_ALISDS_CONN[] = +{ + [EMH_ARG_ALISDS_CONN_CONNECTED] = "CONNECT", + [EMH_ARG_ALISDS_CONN_DISCONNECTED] = "DISCONNECT", + [EMH_ARG_ALISDS_CONN_MAX] = "\0", +}; + +const char* EMH_ARG_ALISDS_STATUS[] = +{ + [EMH_ARG_ALISDS_STATUS_UNINITIALIZED] = "NONE", + [EMH_ARG_ALISDS_STATUS_INITIALIZED] = "INIT", + [EMH_ARG_ALISDS_STATUS_CONNECTED] = "CONNECT", + [EMH_ARG_ALISDS_STATUS_DISCONNECTED] = "DISCONNECT", + [EMH_ARG_ALISDS_STATUS_MAX] = "\0", +}; + +const char* EMH_ARG_ALISDS_EV[] = +{ + [EMH_ARG_ALISDS_EV_ALINK] = "ALINK", + [EMH_ARG_ALISDS_EV_GET] = "GET", + [EMH_ARG_ALISDS_EV_SET] = "SET", + [EMH_ARG_ALISDS_EV_MAX] = "\0", +}; +#endif + +/****************************************************************************** + * WiFi service + ******************************************************************************/ + +const char* EMH_ARG_WLAN_EV[] = +{ + [EMH_ARG_WLAN_EV_UAP_ESTABLISHED] = "AP_UP", + [EMH_ARG_WLAN_EV_UAP_DESTROYED] = "AP_DOWN", + [EMH_ARG_WLAN_EV_STA_CONNECTED] = "STATION_UP", + [EMH_ARG_WLAN_EV_STA_DISCONNECTED] = "STATION_DOWN", + [EMH_ARG_WLAN_EV_MAX] = "\0", +}; + +const char* EMH_ARG_WLAN_STA[] = +{ + [EMH_ARG_WLAN_STA_CONNECTED] = "STATION_UP", + [EMH_ARG_WLAN_STA_DISCONNECTED] = "STATION_DOWN", + [EMH_ARG_WLAN_STA_DISCONNECTED] = "CONNECTING", + [EMH_ARG_WLAN_STA_MAX] = "\0", +}; + +/****************************************************************************** + * Alicoud ILOP service + ******************************************************************************/ + +#ifdef AT_SUPPORT_ILOP +const char* EMH_ARG_ILOP_DM[] = +{ + [EMH_ARG_ILOP_DM_RAW] = "0", + [EMH_ARG_ILOP_DM_ICA_BY_CLOUD] = "1", + [EMH_ARG_ILOP_DM_ICA_BY_LOCAL] = "2", + [EMH_ARG_ILOP_DM_MAX] = "\0", +}; + +const char* EMH_ARG_ILOP_CONFIG[] = +{ + [EMH_ARG_ILOP_CONFIG_NONE] = "NONE", + [EMH_ARG_ILOP_CONFIG_TLS] = "CONFIG", + [EMH_ARG_ILOP_CONFIG_MAX] = "\0", +}; + +const char* EMH_ARG_ILOP_STATUS[] = +{ + [EMH_ARG_ILOP_STATUS_UNINITIALIZED] = "NONE", + [EMH_ARG_ILOP_STATUS_INITIALIZED] = "INIT", + [EMH_ARG_ILOP_STATUS_CONNECTED] = "CONNECT", + [EMH_ARG_ILOP_STATUS_DISCONNECTED] = "DISCONNECT", + [EMH_ARG_ILOP_STATUS_MAX] = "\0", +}; + +const char* EMH_ARG_ILOP_CONN[] = +{ + [EMH_ARG_ILOP_CONN_CLOUD_CONNECTED] = "CLOUD_CONNECT", + [EMH_ARG_ILOP_CONN_CLOUD_DISCONNECTED] = "CLOUD_DISCONNECT", + [EMH_ARG_ILOP_CONN_LOCAL_CONNECTED] = "LOCAL_CONNECT", + [EMH_ARG_ILOP_CONN_LOCAL_DISCONNECTED] = "LOCAL_DISCONNECT", + [EMH_ARG_ILOP_CONN_MAX] = "\0", +}; + +const char* EMH_ARG_ILOP_EV[] = +{ + [EMH_ARG_ILOP_EV_ILOP] = "ILOP", + [EMH_ARG_ILOP_EV_SETICA] = "SETICA", + [EMH_ARG_ILOP_EV_SETRAW] = "SETRAW", + [EMH_ARG_ILOP_EV_MAX] = "\0", +}; + +const char *EMH_ARG_ILOP_VT[] = +{ + [EMH_ARG_ILOP_VT_PROPERTY] = "property", + [EMH_ARG_ILOP_VT_EVENT] = "event", + [EMH_ARG_ILOP_VT_SERVICE] = "service", + [EMH_ARG_ILOP_VT_MAX] = "\0", +}; +#endif + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +const char* emh_arg_for_type(const char* emh_arg[], uint8_t type) +{ + return emh_arg[type]; +} + +uint8_t emh_arg_for_arg(const char* emh_arg[], char *arg) +{ + for (uint8_t i = 0; i < EMH_ARG_ERR; i++) { + if (*emh_arg[i] == 0x0) break; + if (memcmp(emh_arg[i], arg, strlen(emh_arg[i])) == 0) { + return i; + } + + } + return EMH_ARG_ERR; +} + + diff --git a/src/helper/emhost/emh_arg.h b/src/helper/emhost/emh_arg.h new file mode 100644 index 0000000..c4e4faa --- /dev/null +++ b/src/helper/emhost/emh_arg.h @@ -0,0 +1,212 @@ +/** + ****************************************************************************** + * @file emh_arg.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief AT command arguments to enum type convert header file + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef _EMH_ARG_H_ +#define _EMH_ARG_H_ + +#include "mx_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** \addtogroup emhost */ +/** @{*/ + +/****************************************************************************** + * Constants + ******************************************************************************/ + +#define EMH_ARG_ERR 0xFF + +/****************************************************************************** + * Alicoud SDS service + ******************************************************************************/ + +/** \addtogroup alicloud_sds */ +/** @{*/ +enum{ + EMH_ARG_ALISDS_CONN_DISCONNECTED, + EMH_ARG_ALISDS_CONN_CONNECTED, + EMH_ARG_ALISDS_CONN_MAX, +}; +typedef uint8_t emh_arg_alisds_conn_t; +extern const char* EMH_ARG_ALISDS_CONN[]; + +enum{ + EMH_ARG_ALISDS_STATUS_UNINITIALIZED, + EMH_ARG_ALISDS_STATUS_INITIALIZED, + EMH_ARG_ALISDS_STATUS_CONNECTED, + EMH_ARG_ALISDS_STATUS_DISCONNECTED, + EMH_ARG_ALISDS_STATUS_MAX, +}; +typedef uint8_t emh_arg_alisds_status_t; +extern const char* EMH_ARG_ALISDS_STATUS[]; + +enum{ + EMH_ARG_ALISDS_FORMAT_JSON, + EMH_ARG_ALISDS_FORMAT_RAW, + EMH_ARG_ALISDS_FORMAT_MAX, +}; +typedef uint8_t emh_arg_alisds_format_t; +extern const char* EMH_ARG_ALISDS_FORMAT[]; + +enum{ + EMH_ARG_ALISDS_EV_ALINK, + EMH_ARG_ALISDS_EV_SET, + EMH_ARG_ALISDS_EV_GET, + EMH_ARG_ALISDS_EV_MAX, +}; +typedef uint8_t emh_arg_alisds_ev_t; +extern const char* EMH_ARG_ALISDS_EV[]; +/** @}*/ + +/****************************************************************************** + * WiFi service + ******************************************************************************/ +/** \addtogroup wlan */ +/** @{*/ +enum{ + EMH_ARG_WLAN_EV_UAP_ESTABLISHED, + EMH_ARG_WLAN_EV_UAP_DESTROYED, + EMH_ARG_WLAN_EV_STA_CONNECTED, + EMH_ARG_WLAN_EV_STA_DISCONNECTED, + EMH_ARG_WLAN_EV_MAX, +}; +typedef uint8_t emh_arg_wlan_ev_t; +extern const char* EMH_ARG_WLAN_EV[]; + +enum{ + EMH_ARG_WLAN_STA_CONNECTED, + EMH_ARG_WLAN_STA_DISCONNECTED, + EMH_ARG_WLAN_STA_CONNECTING, + EMH_ARG_WLAN_STA_MAX, +}; +typedef uint8_t emh_arg_wlan_sta_t; /**< wlan connection status under station mode */ +extern const char* EMH_ARG_WLAN_STA[]; +/** @}*/ + +/****************************************************************************** + * Alicoud ILOP service + ******************************************************************************/ +/** \addtogroup ilop */ +/** @{*/ +enum{ + EMH_ARG_ILOP_DM_RAW, + EMH_ARG_ILOP_DM_ICA_BY_CLOUD, + EMH_ARG_ILOP_DM_ICA_BY_LOCAL, + EMH_ARG_ILOP_DM_MAX, +}; +typedef uint8_t emh_arg_ilop_dm_t; +extern const char* EMH_ARG_ILOP_DM[]; + +enum{ + EMH_ARG_ILOP_CONFIG_NONE, + EMH_ARG_ILOP_CONFIG_TLS, + EMH_ARG_ILOP_CONFIG_MAX, +}; +typedef uint8_t emh_arg_ilop_config_t; +extern const char* EMH_ARG_ILOP_CONFIG[]; + +enum{ + EMH_ARG_ILOP_STATUS_UNINITIALIZED, + EMH_ARG_ILOP_STATUS_INITIALIZED, + EMH_ARG_ILOP_STATUS_CONNECTED, + EMH_ARG_ILOP_STATUS_DISCONNECTED, + EMH_ARG_ILOP_STATUS_MAX, +}; +typedef uint8_t emh_arg_ilop_status_t; +extern const char* EMH_ARG_ILOP_STATUS[]; + +enum{ + EMH_ARG_ILOP_CONN_CLOUD_CONNECTED, + EMH_ARG_ILOP_CONN_CLOUD_DISCONNECTED, + EMH_ARG_ILOP_CONN_LOCAL_CONNECTED, + EMH_ARG_ILOP_CONN_LOCAL_DISCONNECTED, + EMH_ARG_ILOP_CONN_MAX, +}; +typedef uint8_t emh_arg_ilop_conn_t; +extern const char* EMH_ARG_ILOP_CONN[]; + +enum{ + EMH_ARG_ILOP_EV_ILOP, + EMH_ARG_ILOP_EV_SETICA, + EMH_ARG_ILOP_EV_SETRAW, + EMH_ARG_ILOP_EV_MAX, +}; +typedef uint8_t emh_arg_ilop_ev_t; +extern const char* EMH_ARG_ILOP_EV[]; + +enum{ + EMH_ARG_ILOP_FORMAT_ICA, + EMH_ARG_ILOP_FORMAT_RAW, + EMH_ARG_ILOP_FORMAT_MAX, +}; +typedef uint8_t emh_arg_ilop_format_t; + +enum{ + EMH_ARG_ILOP_VT_PROPERTY, + EMH_ARG_ILOP_VT_EVENT, + EMH_ARG_ILOP_VT_SERVICE, + EMH_ARG_ILOP_VT_MAX, +}; +typedef uint8_t emh_arg_ilop_vt_t; +extern const char *EMH_ARG_ILOP_VT[]; +/** @}*/ + +/****************************************************************************** + * Function Declarations + ******************************************************************************/ + +/** + * @brief Find string type argument from enum + * + * @param[in] emh_arg: Arg convert list array + * @param[in] type: enum argument type + * + * @return Argument string type + */ +const char* emh_arg_for_type(const char* emh_arg[], uint8_t type); + +/** + * @brief Find enum type argument from string + * + * @param[in] emh_arg: Arg convert list array + * @param[in] type: string argument read from AT command + * + * @return Argument enum type + */ +uint8_t emh_arg_for_arg(const char* emh_arg[], char *arg); + +/** @}*/ +/** @}*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/helper/emhost/emh_ilop.c b/src/helper/emhost/emh_ilop.c new file mode 100644 index 0000000..38dc332 --- /dev/null +++ b/src/helper/emhost/emh_ilop.c @@ -0,0 +1,295 @@ +/** + ****************************************************************************** + * @file emh_ilop.c + * @author QQ Ding + * @version V1.0.0 + * @date 3-Sept-2018 + * @brief Alicloud ILOP service AT commands API + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + + +#include +#include + +#include "emh_api.h" +#include "ATCmdParser/ATCmdParser.h" + + + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ +// MX_WEAK void emh_ev_ilop_connection(emh_arg_ilop_conn_t conn) +// { +// printf("conn=%d\r\n", conn); +// return; +// } + +// MX_WEAK void emh_ev_ilop_set_local_attr(emh_ilop_msg *msg) +// { +// if( msg->format == EMH_ARG_ILOP_FORMAT_ICA){ +// printf("msg=%s\r\n", msg->data); +// } +// return; +// } + +mx_status emh_ilop_config( emh_ilop_config_t *config, bool force ) +{ + if( config == NULL ) return kParamErr; + + char arg[10]; + + if( config->dm == EMH_ARG_ILOP_DM_ICA_BY_LOCAL ) + { + if( config->tsl_thing == NULL ) + return kParamErr; + } + + //check data format + if (!(ATCmdParser_send("AT+ILOPDM?") + && ATCmdParser_recv("+ILOPDM:%10[^\r]\r\n",arg) + && ATCmdParser_recv("OK\r\n"))) { + return kReadErr; + } + + //if dm value different, we need to be set + if( emh_arg_for_arg( EMH_ARG_ILOP_DM, arg) != config->dm ) + { + if ( !(ATCmdParser_send("AT+ILOPDM=%s", emh_arg_for_type(EMH_ARG_ILOP_DM, config->dm)) + && ATCmdParser_recv("OK\r\n"))) { + return kReadErr; + } + } + + //if dm value is raw, we shuold check tls setting + if( config->dm == EMH_ARG_ILOP_DM_ICA_BY_LOCAL ) + { + //check tls setting + if ( !(ATCmdParser_send("AT+ILOPCONFIG?") + && ATCmdParser_recv("+ILOPCONFIG:%10[^\r]\r\n",arg) + && ATCmdParser_recv("OK\r\n"))) { + return kReadErr; + } + + if( (force == true) || (emh_arg_for_arg( EMH_ARG_ILOP_CONFIG, arg ) != EMH_ARG_ILOP_CONFIG_TLS) ) + { + if( emh_ilop_set_tsl( config->tsl_thing, config->tsl_len ) != kNoErr) + return kWriteErr; + } + } + return kNoErr; +} + +mx_status emh_ilop_set_tsl( const char* tsl_thing, uint32_t tls_len ) +{ + int id_count=0, last = 0, i=0; + last = (tls_len%4096 == 0) ? 0 : 1; + id_count = tls_len/4096 + last; + + if ( !(ATCmdParser_send("AT+ILOPTSLPRE=%d,%d", tls_len, id_count) + && ATCmdParser_recv("OK\r\n"))) { + return kReadErr; + } + + for( i=0; i") + && ATCmdParser_write((char *)tsl_thing + (i*4096) , 4096) == 4096 + && ATCmdParser_recv("OK\r\n") + )) { + return kReadErr; + } + } + + if( last ) + { + if ( !(ATCmdParser_send("AT+ILOPTSL=%d", i) + && ATCmdParser_recv(">") + && ATCmdParser_write((char *)tsl_thing + (i*4096) , tls_len -i*4096) == (tls_len -i*4096) + && ATCmdParser_recv("OK\r\n") + )) { + return kReadErr; + } + } + + return kNoErr; +} + +mx_status emh_ilop_set_key( const char *pk, const char *ps, const char *ds, const char *dn ) +{ + if (ATCmdParser_send("AT+ILOPSET=%s,%s,%s,%s", pk, ps, ds, dn) + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +mx_status emh_ilop_get_key( char pk[EMH_ILOP_PRODUCT_KEY_MAXLEN], char ps[EMH_ILOP_PRODUCT_SECRET_MAXLEN], char ds[EMH_ILOP_DEVICE_SECRET_MAXLEN], char dn[EMH_ILOP_DEVICE_NAME_MAXLEN] ) +{ + char args[200], *arg_list[5]; + + if (!(ATCmdParser_send("AT+ILOPSET?") + && ATCmdParser_recv("+ILOPSET:%200[^\r]\r\n",args) + && ATCmdParser_recv("OK\r\n"))) { + return kReadErr; + } + + if (4 != ATCmdParser_analyse_args(args, arg_list, 4)) { + return kMalformedErr; + } + + strncpy( pk, arg_list[0], EMH_ILOP_PRODUCT_KEY_MAXLEN ); + strncpy( ps, arg_list[1], EMH_ILOP_PRODUCT_SECRET_MAXLEN ); + strncpy( ds, arg_list[2], EMH_ILOP_DEVICE_SECRET_MAXLEN ); + strncpy( dn, arg_list[3], EMH_ILOP_DEVICE_NAME_MAXLEN ); + + return kNoErr; +} + +mx_status emh_ilop_awss_start( void ) +{ + if (ATCmdParser_send("AT+ILOPAWSSTART") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +mx_status emh_ilop_awss_press( void ) +{ + if (ATCmdParser_send("AT+ILOPAWSPRESS") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +mx_status emh_ilop_service_start( void ) +{ + if (ATCmdParser_send("AT+ILOPSTART") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +emh_arg_ilop_status_t emh_ilop_get_stauts( void ) +{ + char arg[20]; + + if (!(ATCmdParser_send("AT+ILOPSTATUS?") + && ATCmdParser_recv("+ILOPSTATUS:%20[^\r]\r\n",arg) + && ATCmdParser_recv("OK\r\n"))) { + return EMH_ARG_ERR; + } + + return emh_arg_for_arg( EMH_ARG_ILOP_STATUS, arg); +} + +mx_status emh_ilop_unbind( void ) +{ + if (ATCmdParser_send("AT+ILOPRESET") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +mx_status emh_ilop_service_stop( void ) +{ + if (ATCmdParser_send("AT+ILOPSTOP") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +mx_status emh_ilop_send_ica_to_cloud( const char *type, char *attr ) +{ + if (ATCmdParser_send("AT+ILOPSENDICA=%s,%s", type, attr) + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + + return kGeneralErr; +} + +mx_status emh_ilop_send_raw_to_cloud( uint8_t *data, uint32_t len ) +{ + if (ATCmdParser_send("AT+ILOPSENDRAW=%d", len) + && ATCmdParser_recv(">") + && ATCmdParser_write((char *)data, len) == len + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +void emh_ilop_event_handler(void) +{ + mx_status err = kNoErr; + char arg1[10], arg2[256]; + emh_arg_ilop_conn_t conn; + emh_ilop_msg msg; + + require_action(ATCmdParser_recv("%10[^,],", arg1), exit, err = kMalformedErr); + + emh_arg_ilop_ev_t event = emh_arg_for_arg(EMH_ARG_ILOP_EV, arg1); + require_action(event != EMH_ARG_ERR, exit, err = kMalformedErr); + + /* ILOP Server connection event */ + if (event == EMH_ARG_ILOP_EV_ILOP) { + require_action(ATCmdParser_recv("%20[^\r]\r\n", arg2), exit, err = kMalformedErr); + conn = emh_arg_for_arg(EMH_ARG_ILOP_CONN, arg2); + require_action(conn != EMH_ARG_ERR, exit, err = kMalformedErr); + emh_ev_ilop_connection(conn); + } + /* ALINK server === ICA value===> device */ + else if(event == EMH_ARG_ILOP_EV_SETICA) + { + require_action(ATCmdParser_recv("%256[^\r]\r\n", arg2), exit, err = kMalformedErr); + msg.format = EMH_ARG_ILOP_FORMAT_ICA; + msg.data = (uint8_t *)&arg2; + msg.len = strlen(arg2); + emh_ev_ilop_set_local_attr( &msg ); + } + /* ALINK server === RAW value===> device */ + else if(event == EMH_ARG_ILOP_EV_SETRAW) + { + /* Read package data */ + int32_t count; + require_action(ATCmdParser_recv("%d,", &count), exit, err = kMalformedErr); + + uint8_t *data = malloc(count); + require_action(data, exit, err = kNoMemoryErr); + require_action(ATCmdParser_read((char *)data, count) == count, exit, err = kTimeoutErr); + + msg.data = data; + msg.format = EMH_ARG_ILOP_FORMAT_RAW; + msg.len = count; + emh_ev_ilop_set_local_attr(&msg); + free(data); + } + +exit: + if (err == kMalformedErr) emh_ev_unknown(); + return; +} diff --git a/src/helper/emhost/emh_module.c b/src/helper/emhost/emh_module.c new file mode 100644 index 0000000..80b2675 --- /dev/null +++ b/src/helper/emhost/emh_module.c @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file emh_module.c + * @author William Xu + * @version V1.0.0 + * @date 9-Apr-2018 + * @brief EMW module operation AT commands + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + + +#include "ATCmdParser/ATCmdParser.h" +#include "emh_api.h" + +/****************************************************************************** + * Variable Definitions + ******************************************************************************/ + +static char _fw_version[32]; + +/****************************************************************************** + * Function Declarations + ******************************************************************************/ + +extern void emh_alisds_event_handler(void); +extern void emh_wlan_event_handler(void); + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ +MX_WEAK void emh_ev_unknown(void) +{ + return; +} + +mx_status emh_module_reset(void) +{ + if (!(ATCmdParser_send("AT+REBOOT") + && ATCmdParser_recv("OK\r\n"))) { + return kGeneralErr; + } + for (int i = 0; i < 5; i++) { + if (ATCmdParser_send("AT") + && ATCmdParser_recv("OK\r\n") + && ATCmdParser_send("AT+UARTE=OFF") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + } + return kGeneralErr; +} + +mx_status emh_module_restore_settings(void) +{ + if (!(ATCmdParser_send("AT+FACTORY") + && ATCmdParser_recv("OK\r\n"))) { + return kGeneralErr; + } + + for (int i = 0; i < 5; i++) { + if (ATCmdParser_send("AT") + && ATCmdParser_recv("OK\r\n") + && ATCmdParser_send("AT+UARTE=OFF") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + } + + return kGeneralErr; +} + +mx_status emh_init(void) +{ + ATCmdParser_init("\r","\r\n", 1000, false); + + for (int i = 0; i < 2; i++) { + if ( kNoErr == emh_module_reset()) { +#ifdef AT_SUPPORT_ILOP + ATCmdParser_add_oob("+ILOPEVENT:", emh_ilop_event_handler); +#endif +#ifdef AT_SUPPORT_ALISDS + ATCmdParser_add_oob("+ALINKEVENT:", emh_alisds_event_handler); +#endif + ATCmdParser_add_oob("+WEVENT:", emh_wlan_event_handler); + return kNoErr; + } + } + + return kGeneralErr; +} + +mx_status emh_module_check_ready( void ) +{ + if ( ATCmdParser_send("AT") + && ATCmdParser_recv("OK\r\n")) { + return kNoErr; + } + return kGeneralErr; +} + +const char *emh_module_get_fw_version(void) +{ + if (!(ATCmdParser_send("AT+FWVER?") + && ATCmdParser_recv("+FWVER:%32[^\r]\r\nOK\r\n", _fw_version))) { + return NULL; + } + return _fw_version; +} + +uint32_t emh_module_get_tick(void) +{ + uint32_t tick; + if (!(ATCmdParser_send("AT+SYSTIME?") + && ATCmdParser_recv("+SYSTIME:%d\r\nOK\r\n", &tick))) { + return 0; + } + return tick; +} + +extern void cli_loop( void ); +void emh_runloop(void) { + while (ATCmdParser_process_oob()); + +#ifdef MX_CLI_ENABLE + cli_loop(); +#endif + +} diff --git a/src/helper/emhost/emh_wlan.c b/src/helper/emhost/emh_wlan.c new file mode 100644 index 0000000..5d57cb3 --- /dev/null +++ b/src/helper/emhost/emh_wlan.c @@ -0,0 +1,83 @@ +/** + ****************************************************************************** + * @file emh_wlan.c + * @author William Xu + * @version V1.0.0 + * @date 9-Apr-2018 + * @brief wlan management AT commands + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + + +#include "ATCmdParser/ATCmdParser.h" +#include "emh_api.h" + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +mx_status emh_wlan_get_para(char ssid[33], char pwd[33]) +{ + char args[100], *arg_list[2]; + + if (!(ATCmdParser_send("AT+WJAP=?") + && ATCmdParser_recv("+WJAP:%100[^\r]\r\n",args) + && ATCmdParser_recv("OK\r\n"))) { + return kReadErr; + } + + if (2 != ATCmdParser_analyse_args(args, arg_list, 2)) { + return kMalformedErr; + }; + if (ssid) strcpy(ssid, arg_list[0]); + if (pwd) strcpy(pwd, arg_list[1]); + + return kNoErr; +} + +emh_arg_wlan_sta_t emh_wlan_get_sta_status(void) +{ + char arg[20]; + + if (!(ATCmdParser_send("AT+WJAPS") + && ATCmdParser_recv("+WJAPS:%20[^\r]\r\n",arg) + && ATCmdParser_recv("OK\r\n"))) { + return EMH_ARG_ERR; + } + + return emh_arg_for_arg( EMH_ARG_WLAN_STA, arg); +} + +void emh_wlan_event_handler(void) +{ + mx_status err = kNoErr; + char arg[15]; + + // parse out the packet + require_action(ATCmdParser_recv("%100[^\r]\r\n", arg), exit, err = kMalformedErr); + + emh_arg_wlan_ev_t event = emh_arg_for_arg(EMH_ARG_WLAN_EV, arg); + require_action(event != EMH_ARG_ERR, exit, err = kMalformedErr); + + emh_ev_wlan(event); + +exit: + if (err == kMalformedErr) emh_ev_unknown(); + return; +} diff --git a/src/helper/jsmn/jsmn.c b/src/helper/jsmn/jsmn.c new file mode 100644 index 0000000..ee8b794 --- /dev/null +++ b/src/helper/jsmn/jsmn.c @@ -0,0 +1,282 @@ +#include + +#include + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, uint8_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int16_t start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Filsl next token with JSON string. + */ +static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + int i = 0; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\') { + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + + for(; i < 4 && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + jsmnerr_t r; + int i; + jsmntok_t *token; + int count = 0; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + uint8_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + diff --git a/src/helper/jsmn/jsmn.h b/src/helper/jsmn/jsmn.h new file mode 100644 index 0000000..b080bb0 --- /dev/null +++ b/src/helper/jsmn/jsmn.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010 Serge A. Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Note: struct jsontok_t is modified to reduce it's memory impact + */ + +#ifndef __JSMN_PRIV_H__ +#define __JSMN_PRIV_H__ + +#include "json_parser.h" + +#define JSMN_STRICT 1 +#define JSMN_PARENT_LINKS 1 +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_PRIMITIVE = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3 +} jsmntype_t; + +typedef enum { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3, +} jsmnerr_t; + +typedef json_parser_t jsmn_parser; +typedef jsontok_t jsmntok_t; + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, + * each describing a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +#endif /* __JSMN_PRIV_H__ */ diff --git a/src/helper/jsmn/json_escape_str.c b/src/helper/jsmn/json_escape_str.c new file mode 100644 index 0000000..e3f0990 --- /dev/null +++ b/src/helper/jsmn/json_escape_str.c @@ -0,0 +1,106 @@ +#include "mx_common.h" +#include "json_escape_str.h" + +int json_escape_str(struct json_printbuf *pb, const char *str, int len); +struct json_printbuf* json_printbuf_new(void); +void json_printbuf_reset(struct json_printbuf *p); +void json_printbuf_free(struct json_printbuf *p); + +struct json_printbuf* json_printbuf_new(void) +{ + struct json_printbuf *p; + + p = (struct json_printbuf*)calloc(1, sizeof(struct json_printbuf)); + if(!p) return NULL; + p->size = 4; + p->bpos = 0; + if(!(p->buf = (char*)malloc(p->size))) { + free(p); + return NULL; + } + return p; +} + +void json_printbuf_reset(struct json_printbuf *p) +{ + p->buf[0] = '\0'; + p->bpos = 0; +} + +void json_printbuf_free(struct json_printbuf *p) +{ + if(p) { + free(p->buf); + free(p); + } +} + +static int json_printbuf_memappend(struct json_printbuf *p, const char *buf, int size) +{ + char *t; + if(p->size - p->bpos <= size) { + int new_size = JSON_MAX(p->size * 2, p->bpos + size + 8); +//#ifdef json_printbuf_DEBUG +// MC_DEBUG("json_printbuf_memappend: realloc " +// "bpos=%d wrsize=%d old_size=%d new_size=%d\n", +// p->bpos, size, p->size, new_size); +//#endif /* json_printbuf_DEBUG */ + if(!(t = (char*)realloc(p->buf, new_size))) return -1; + p->size = new_size; + p->buf = t; + } + memcpy(p->buf + p->bpos, buf, size); + p->bpos += size; + p->buf[p->bpos]= '\0'; + return size; +} + +/* string escaping */ +int json_escape_str(struct json_printbuf *pb, const char *str, int len) +{ + int pos = 0, start_offset = 0; + const char *json_hex_chars = "0123456789abcdef"; + + unsigned char c; + + if(len == 0) { + json_printbuf_memappend(pb, str, pos); + return 0; + } + + while (len--) { + c = str[pos]; + switch(c) { + case '\b': + case '\n': + case '\r': + case '\t': + case '"': + case '\\': + case '/': + if(pos - start_offset > 0) + json_printbuf_memappend(pb, str + start_offset, pos - start_offset); + if(c == '\b') json_printbuf_memappend(pb, "\\b", 2); + else if(c == '\n') json_printbuf_memappend(pb, "\\n", 2); + else if(c == '\r') json_printbuf_memappend(pb, "\\r", 2); + else if(c == '\t') json_printbuf_memappend(pb, "\\t", 2); + else if(c == '"') json_printbuf_memappend(pb, "\\\"", 2); + else if(c == '\\') json_printbuf_memappend(pb, "\\\\", 2); + else if(c == '/') json_printbuf_memappend(pb, "\\/", 2); + start_offset = ++pos; + break; + default: + if(c < ' ') { + if(pos - start_offset > 0) + json_printbuf_memappend(pb, str + start_offset, pos - start_offset); + sprintf((char *)pb, "\\u00%c%c", + json_hex_chars[c >> 4], + json_hex_chars[c & 0xf]); + start_offset = ++pos; + } else pos++; + } + } + if(pos - start_offset > 0) + json_printbuf_memappend(pb, str + start_offset, pos - start_offset); + return 0; +} diff --git a/src/helper/jsmn/json_escape_str.h b/src/helper/jsmn/json_escape_str.h new file mode 100644 index 0000000..351c7f8 --- /dev/null +++ b/src/helper/jsmn/json_escape_str.h @@ -0,0 +1,24 @@ +#ifndef _JSON_ESCAPE_STR_H +#define _JSON_ESCAPE_STR_H + +#ifndef JSON_MIN +#define JSON_MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef JSON_MAX +#define JSON_MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +struct json_printbuf { + char *buf; + int bpos; + int size; +}; + +extern int json_escape_str(struct json_printbuf *pb, const char *str, int len); +extern struct json_printbuf* json_printbuf_new(void); +extern void json_printbuf_reset(struct json_printbuf *p); +extern void json_printbuf_free(struct json_printbuf *p); + + +#endif diff --git a/src/helper/jsmn/json_generator.c b/src/helper/jsmn/json_generator.c new file mode 100644 index 0000000..739d63d --- /dev/null +++ b/src/helper/jsmn/json_generator.c @@ -0,0 +1,341 @@ +/* + * Copyright 2008-2015, Marvell International Ltd. + * All Rights Reserved. + */ + +/* + * Simple JSON Generator + */ + +#include "mx_common.h" +#include "mx_debug.h" +#include "json_generator.h" +#include "json_parser.h" + + +#define json_e(...) \ + custom_log("json", ##__VA_ARGS__) +#define json_w(...) \ + wmlog_w("json", ##__VA_ARGS__) + +#ifdef CONFIG_JSON_DEBUG +#define json_d(...) \ + wmlog("json", ##__VA_ARGS__) +#else +#define json_d(...) +#endif /* ! CONFIG_JSON_DEBUG */ + +#define CONFIG_JSON_FLOAT + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) +{ + while (in && (unsigned char)*in <= 32) + in++; + return in; +} + +static const char *rev_skip(const char *in) +{ + while (in && (unsigned char)*in <= 32) + in--; + return in; +} + +#ifdef CONFIG_JSON_FLOAT +static void print_float(float val, short precision, char *str_val, + int len_str_val) +{ + int val_int, val_frac; + int scale = 1; + short pre_tmp = precision; + char sign[2] = ""; + + while (pre_tmp--) + scale *= 10; + + if (val < 0) { + val = -val; + sign[0] = '-'; + sign[1] = '\0'; + } + + val_int = (int)val; + val_frac = (int)((val - val_int) * scale); + + snprintf(str_val, len_str_val, "%s%d.%.*d", sign, val_int, precision, + val_frac); +} +#else +static void print_float(float val, short precision, char *str_val, + int len_str_val) +{ + snprintf(str_val, len_str_val, "\"unsupported\""); +} +#endif + +const char *verify_json_start(const char *buff) +{ + buff = skip(buff); + if (*buff != '{') { + json_e("Invalid JSON document"); + return NULL; + } else { + return ++buff; + } +} + +static int verify_buffer_limit(struct json_str *jptr) +{ + /* + * Check for buffer overflow condition here, and then copy remaining + * data using snprintf. This makes sure there is no mem corruption in + * json set operations. + */ + if (jptr->free_ptr >= (jptr->len - 1)) { + json_e("buffer maximum limit reached"); + return -1; + } else + return kNoErr; +} + +void json_str_init(struct json_str *jptr, char *buff, int len) +{ + jptr->buff = buff; + memset(jptr->buff, 0, len); + jptr->free_ptr = 0; + jptr->len = len; +} + +int json_push_object(struct json_str *jptr, const char *name) +{ + char *buff; + + if (verify_buffer_limit(jptr) < 0) + return kOverrunErr; + + /* From last skip cr/lf */ + buff = (char *)rev_skip(&jptr->buff[jptr->free_ptr - 1]); + if (*buff != '{') /* Element in object */ + jptr->buff[jptr->free_ptr++] = ','; + + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "\"%s\":{", name); + + jptr->free_ptr = strlen(jptr->buff); + return kNoErr; +} + +int json_push_array_object(struct json_str *jptr, const char *name) +{ + char *buff; + + if (verify_buffer_limit(jptr) < 0) + return kOverrunErr; + + /* From last skip cr/lf */ + buff = (char *)rev_skip(&jptr->buff[jptr->free_ptr - 1]); + if (*buff != '{') /* Element in object */ + jptr->buff[jptr->free_ptr++] = ','; + + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "\"%s\":[", name); + + jptr->free_ptr = strlen(jptr->buff); + return kNoErr; +} + +int json_start_object(struct json_str *jptr) +{ + char *buff; + + if (verify_buffer_limit(jptr) < 0) + return kOverrunErr; + + if (jptr->free_ptr) { + /* This should be first call after json_str_init so free_ptr + * should be 0 but if it is not then we add ',' before + * starting object as there could have been earlier object + * already present as case in array. + */ + /* From last skip cr/lf */ + buff = (char *)rev_skip(&jptr->buff[jptr->free_ptr - 1]); + + if (*buff == '}') + jptr->buff[jptr->free_ptr++] = ','; + } + jptr->buff[jptr->free_ptr++] = '{'; + return kNoErr; +} + +int json_close_object(struct json_str *jptr) +{ + if (verify_buffer_limit(jptr) < 0) + return kOverrunErr; + + jptr->buff[jptr->free_ptr++] = '}'; + + return kNoErr; +} + +int json_pop_array_object(struct json_str *jptr) +{ + if (verify_buffer_limit(jptr) < 0) + return kOverrunErr; + + jptr->buff[jptr->free_ptr++] = ']'; + + return kNoErr; +} + +int json_start_array(struct json_str *jptr) +{ + char *buff; + if (verify_buffer_limit(jptr) < 0) + return kOverrunErr; + + /* From last skip cr/lf */ + buff = (char *)rev_skip(&jptr->buff[jptr->free_ptr - 1]); + + if (*buff == ']') + jptr->buff[jptr->free_ptr++] = ','; + + jptr->buff[jptr->free_ptr++] = '['; + return kNoErr; +} + +int json_close_array(struct json_str *jptr) +{ + if (verify_buffer_limit(jptr) < 0) + return kOverrunErr; + + jptr->buff[jptr->free_ptr++] = ']'; + return kNoErr; +} + +int json_set_array_value(struct json_str *jptr, char *str, int value, float val, + json_data_types data) +{ + char *buff; + + if (!verify_json_start(jptr->buff)) + return kFormatErr; + + if (verify_buffer_limit(jptr) < 0) + return kOverrunErr; + + /* From last skip cr/lf */ + buff = (char *)rev_skip(&jptr->buff[jptr->free_ptr - 1]); + + if (*buff != '[') /* Element in object */ + jptr->buff[jptr->free_ptr++] = ','; + + switch (data) { + case JSON_VAL_STR: + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "\"%s\"", str); + break; + case JSON_VAL_INT: + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "%d", value); + break; + case JSON_VAL_FLOAT: + print_float(val, 2, &jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr); + break; + case JSON_VAL_BOOL: + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "%s", + (value == 1) ? "true" : "false"); + break; + default: + json_e("Invalid case in array set"); + } + + jptr->free_ptr = strlen(jptr->buff); + return kNoErr; +} + +int json_set_object_value(struct json_str *jptr, const char *name, + const char *str, int64_t value, float val, + short precision, json_data_types data) +{ + struct json_printbuf *pb; //add by wancghao 2016_12_14 + char *buff; + + if (!verify_json_start(jptr->buff)) + return kFormatErr; + + if (verify_buffer_limit(jptr) < 0) + return kOverrunErr; + + /* From last skip cr/lf */ + buff = (char *)rev_skip(&jptr->buff[jptr->free_ptr - 1]); + + if (*buff != '{') /* Element in object */ + jptr->buff[jptr->free_ptr++] = ','; + + switch (data) { + case JSON_VAL_STR: + //add by wangchao 2016_12_14 + pb = json_printbuf_new(); + if(pb == NULL) + { + return kNoMemoryErr; + } + + json_escape_str(pb, str, strlen(str)); + + if(pb->buf != NULL) + { + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "\"%s\":\"%s\"", + name, pb->buf); + } + + json_printbuf_free(pb); + + break; + + case JSON_VAL_INT: + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "\"%s\":%d", name, + (int) value); + break; + + case JSON_VAL_UINT: + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "\"%s\":%u", name, + (unsigned) value); + break; + + case JSON_VAL_UINT_64: + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "\"%s\":%llu", name, + value); + break; + + case JSON_VAL_FLOAT: + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "\"%s\":", name); + jptr->free_ptr = strlen(jptr->buff); + print_float(val, precision, &jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr); + break; + case JSON_VAL_BOOL: + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "\"%s\":%s", + name, (value == 1) ? "true" : "false"); + break; + case JSON_VAL_NULL: + snprintf(&jptr->buff[jptr->free_ptr], + jptr->len - jptr->free_ptr, "\"%s\":null", + name); + break; + default: + json_e("Invalid case in object set"); + } + + jptr->free_ptr = strlen(jptr->buff); + return kNoErr; +} diff --git a/src/helper/jsmn/json_generator.h b/src/helper/jsmn/json_generator.h new file mode 100644 index 0000000..f320c26 --- /dev/null +++ b/src/helper/jsmn/json_generator.h @@ -0,0 +1,380 @@ +/*! \file json_generator.h + * + * \brief JSON generator module + * + * This module is used to generate JSON strings + * + * \section json_gen_usage Usage + * + * The JSON Generator can create various JSON strings. Here is an example: + * + * \code + * json_str_init(&jstr, buff, sizeof(buff)); + * // This initializes the json generator to generate string in the buffer buff + * + * json_start_object(&jstr); + * json_set_val_str(&jstr, "name", "John Travolta"); + * json_set_val_int(&jstr, "roll", 123); + * json_push_object(&jstr, "address"); + * json_set_val_str(&jstr, "house", "Atlas"); + * json_set_val_str(&jstr, "street", "New Avenue"); + * json_set_val_int(&jstr, "pin", 39065); + * json_pop_object(&jstr); + * json_close_object(&jstr); + * + * \endcode + * will create the above mentioned JSON string. + * + * Similarly arrays can be created as: + * \code + * json_start_object(&jstr); + * json_push_array_object(&jstr, "roll"); + * json_set_array_int(&jstr, 10); + * json_set_array_int(&jstr, 20); + * json_set_array_int(&jstr, 30); + * json_pop_array_object(&jstr); + * json_close_object(&jstr); + * \endcode + * will create the JSON string + * + * \code + * { "roll": [ [ 10, 20, 30] ] } + * \endcode + * + * Similarly complex use-case like objects within arrays can be created as: + * \code + * json_str_init(&jstr, buff, sizeof(buff)); + * json_start_object(&jstr); + * json_push_array_object(&jstr, "arr"); + * json_start_object(&jstr); + * json_set_val_str(&jstr, "name", "John Travolta"); + * json_set_val_int(&jstr, "roll", 123); + * json_close_object(&jstr); + * json_start_object(&jstr); + * json_set_val_str(&jstr, "name", "Foo Bar"); + * json_set_val_int(&jstr, "roll", 112); + * json_close_object(&jstr); + * json_pop_array_object(&jstr); + * json_close_object(&jstr); + * \endcode + * will create the JSON string + * \code + * { "arr": [ + * { "name": "John Travolta", "roll": 123 }, + * { "name": "Foo Bar", "roll": 112 } + * ] + * } + * \endcode + */ + +/* + * Copyright (C) 2014, Marvell International Ltd. + * All Rights Reserved. + */ +#ifndef _JSON_GENERATOR_H_ +#define _JSON_GENERATOR_H_ + +#include "mx_common.h" +#include "json_escape_str.h" + +#define MAX_JSON_STR_LEN 64 /* Maximum object or member name length */ +#define MAX_JSON_VAL_LEN 128 /* Maximum value name length */ +typedef enum { + JSON_VAL_STR, + JSON_VAL_INT, + JSON_VAL_UINT, + JSON_VAL_UINT_64, + JSON_VAL_FLOAT, + JSON_VAL_BOOL, + JSON_VAL_NULL, +} json_data_types; + +struct json_str { + char *buff; + int len; + int free_ptr; /* Where the \0 currently is, start writing from this + * offset */ +}; +/** Initialize the JSON generator + * + * This function initializes the JSON generator. + * + * \param[out] jptr A pointer to the structure json_str which will be used as a + * handle for the rest of the json_str operations. + * \param[out] buff A pointer to a buffer that will be used to store the + * generated JSON data. + * \param[in] len The length of the buffer pointed to by buff + */ +void json_str_init(struct json_str *jptr, char *buff, int len); + +/** Start a new JSON object + * + * This function starts a new JSON object + * + * \param[in] jptr Pointer to json_str object. + * + * \return kOverrunErr if buffer available is insufficient or a buffer + * overflow has already occurred. + * \return kNoErr if operation successful. + */ +int json_start_object(struct json_str *jptr); + +/** Start a new composite object + * + * This function starts a new composite object which may have its own set of + * key-value pairs. Any json_set() calls performed after this will create + * key-value pairs within this composite object. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] name The name of the composite object that should be created. + * + * \return kOverrunErr if buffer available is insufficient or a buffer + * overflow has already occurred. + * \return kNoErr if operation successful. + */ +int json_push_object(struct json_str *jptr, const char *name); + +/** Close a composite object + * + * This closes a composite object. Any json_set() calls after this will create + * key-value pairs at the same depth as the composite object that was closed. + */ +#define json_pop_object(jptr) json_close_object((jptr)) + +/** Start a JSON array object. + * + * This function creates an element that has an array as its value. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] name The name of the array object + * + * \return kOverrunErr if buffer available is insufficient or a buffer + * overflow has already occurred. + * \return kNoErr if operation successful. + */ +int json_push_array_object(struct json_str *jptr, const char *name); + +/** Close a JSON array object. + * + * This closes a previously pushed JSON object. + * + * \param[in] jptr Pointer to json_str object. + * + * \return kOverrunErr if buffer available is insufficient or a buffer + * overflow has already occurred. + * \return kNoErr if operation successful. + */ +int json_pop_array_object(struct json_str *jptr); + +/** Close JSON object + * + * This function closes a JSON object that was started earlier. + * + * \param[in] jptr Pointer to json_str object. + * + * \return kOverrunErr if buffer available is insufficient or a buffer + * overflow has already occurred. + * \return kNoErr if operation successful. + */ +int json_close_object(struct json_str *jptr); + +int json_set_object_value(struct json_str *jptr, const char *name, + const char *str, int64_t value, float val, + short precision, json_data_types data); + +/** Create a key with a string value. + * + * This function adds a key-value pair to the JSON text with string as the + * value. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] name The name of the key + * \param[in] val The string value of the key + */ +#define json_set_val_str(jptr, name, val) \ + json_set_object_value(jptr, name, val, 0, 0.0, 0, \ + JSON_VAL_STR) + +/** Create a key with an integer value. + * + * This function adds a key-value pair to the JSON text with an integer as the + * value. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] name The name of the key + * \param[in] val The integer value of the key + */ +#define json_set_val_int(jptr, name, val) \ + json_set_object_value(jptr, name, NULL, val, 0.0, 0, \ + JSON_VAL_INT) + +/** Create a key with an unsigned integer value. + * + * This function adds a key-value pair to the JSON text with an unsigned integer + * as the value. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] name The name of the key + * \param[in] val The unsigned integer value of the key + */ +#define json_set_val_uint(jptr, name, val) \ + json_set_object_value(jptr, name, NULL, val, 0.0, 0, JSON_VAL_UINT) + +/** Create a key with an unsigned 64 bit integer value. + * + * This function adds a key-value pair to the JSON text with an unsigned 64 bit + * integer as the value. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] name The name of the key + * \param[in] val The unsigned 64 bit integer value of the key + */ +#define json_set_val_uint_64(jptr, name, val) \ + json_set_object_value(jptr, name, NULL, val, 0.0, 0, \ + JSON_VAL_UINT_64) + +/** Create a key with a float value. + * + * This function adds a key-value pair to the JSON text with a float as the + * value. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] name The name of the key + * \param[in] val The float value of the key + */ +#define json_set_val_float(jptr, name, val) \ + json_set_object_value(jptr, name, NULL, 0, val, 2, \ + JSON_VAL_FLOAT) + +/** Create a key with a float value and decimal precision. + * + * This function adds a key-value pair to the JSON text with a float as the + * value. + * + * \param jptr A pointer to the json_str handle returned by the json_str_init() + * call. + * \param[in] name The name of the key + * \param[in] val The float value of the key + * \param[in] precision The number of precision digits for float + */ +#define json_set_val_float_precision(jptr, name, val, precision) \ + json_set_object_value(jptr, name, NULL, 0, \ + val, precision, JSON_VAL_FLOAT) + +/** Create a key with an boolean value. + * + * This function adds a key-value pair to the JSON text with an integer as the + * value. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] name The name of the key + * \param[in] val The boolean value of the key. + */ +#define json_set_val_bool(jptr, name, val) ((val == true) ? \ + (json_set_object_value(jptr, name, NULL, 1, 0.0, 0, \ + JSON_VAL_BOOL)) : \ + (json_set_object_value(jptr, name, NULL, 0, 0.0, 0, \ + JSON_VAL_BOOL))) + +/** Create a key with a null value. + * + * This function adds a key-value pair to the JSON text with null as the + * value. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] name The name of the key + */ +#define json_set_val_null(jptr, name) \ + (json_set_object_value(jptr, name, NULL, 0, 0.0, 0, \ + JSON_VAL_NULL)) + +/** Start a JSON array + * + * This function starts a JSON array. This is different than the + * json_push_arraj_object(), in that json_push_array_object() creates an + * key-value pair with an array as the value, while this simply starts an array + * object. + * + * \param[in] jptr Pointer to json_str object. + * + * \return kOverrunErr if buffer available is insufficient or a buffer + * overflow has already occurred. + * \return kNoErr if operation successful. + */ +int json_start_array(struct json_str *jptr); + +/** Close a JSON array + * + * This function closes a JSON array started by json_start_array(). + * + * \param[in] jptr Pointer to json_str object. + * + * \return kOverrunErr if buffer available is insufficient or a buffer + * overflow has already occurred. + * \return kNoErr if operation successful. + */ +int json_close_array(struct json_str *jptr); + +int json_cli_init(void); + +int json_set_array_value(struct json_str *jptr, char *str, int value, + float val, json_data_types data); + +/** Create a string array element + * + * This function creates a string array element in a previously started JSON + * array. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] val A pointer to the string value of the array element. + */ +#define json_set_array_str(jptr, val) \ + json_set_array_value(jptr, val, 0, 0.0, JSON_VAL_STR) + +/** Create an integer array element + * + * This function creates an integer array element in a previously started JSON + * array. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] val The integer value of the array element. + */ +#define json_set_array_int(jptr, val) \ + json_set_array_value(jptr, NULL, val, 0.0, JSON_VAL_INT) + +/** Create a float array element + * + * This function creates a float array element in a previously started JSON + * array. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] val A pointer to the float value of the array element. + */ +#define json_set_array_float(jptr, val) \ + json_set_array_value(jptr, NULL, 0, val, JSON_VAL_FLOAT) + +/** Create an boolean array element + * + * This function creates an boolean array element in a previously started JSON + * array. + * + * \param[in] jptr A pointer to the json_str handle returned by the + * json_str_init() call. + * \param[in] val The boolean value of the array element. + */ +#define json_set_array_bool(jptr, val) ((val == true) ? \ + (json_set_array_value(jptr, NULL, 1, 0.0, JSON_VAL_BOOL)) : \ + (json_set_array_value(jptr, NULL, 0, 0.0, JSON_VAL_BOOL))) +#endif /* _JSON_GENERATOR_H_ */ diff --git a/src/helper/jsmn/json_parser.h b/src/helper/jsmn/json_parser.h new file mode 100644 index 0000000..19897b6 --- /dev/null +++ b/src/helper/jsmn/json_parser.h @@ -0,0 +1,630 @@ +/*! \file json_parser.h + * \brief JSON Parser module + * + * This module is used to parse a JSON string + * For JSON generation, please refer \ref json_generator.h + * + * \section json_parser_usage Usage + * + * The JSON data can be viewed as a tree of key-value pairs. Composite objects + * indicate special nodes within the tree that anchor other subtrees. Arrays + * are special nodes that hold a collection of JSON elements. + * + * The JSON parser module provides two sets of APIs, one to search inside a + * JSON object and the other to search inside a JSON array. + * + * \subsection obj_search Search inside an Object + * The APIs in this category are: + * \li json_get_val_bool() + * \li json_get_val_int() + * \li json_get_val_int64() + * \li json_get_val_float() + * \li json_get_val_str() + * \li json_get_composite_object() + * \li json_release_composite_object() + * \li json_get_array_object() + * \li json_release_array_object() + * + * \subsection array_search Search inside an array + * The APIs in this category are: + * \li json_array_get_bool() + * \li json_array_get_int() + * \li json_array_get_in64t() + * \li json_array_get_float() + * \li json_array_get_str() + * \li json_array_get_composite_object() + * \li json_array_release_composite_object() + * \li json_array_get_array_object() + * \li json_array_release_array_object() + * + * A valid JSON string is an object and so the object search APIs should be + * used first. + * + * The JSON data that looks like the following: + * \code + * { "name" : "JSON Parser", + * "version" : 2.0, + * "copyright" : 2014, + * "supported_el" : ["bool","int","float","str","object","array"], + * "features" : { "objects":true, + * "arrays":"yes" + * } + * } + * \endcode + * + * can be thought of as a tree with a root node that has 5 children "name", + * "version", "copyright", "supported_el" and "features". The node at + * "supported_el" is an array with 6 elements. The node at "features" further + * has 2 child nodes "objects", "arrays". + * The JSON can be much more complex with multiple levels of objects/arrays and + * with different types of elements inside an array or object. + * + * This JSON parser is based on JSMN which treats the JSON string as a + * collection of tokens. A token can be an object, array, string or primitive. + * In the above, "name", "JSON Parser", "version", etc. are all strings. + * 2.0, 2014, true are primitives. + * The entire thing is a JSON object, as is the portion beginning from "{" + * after "features" :. + * The part between [] is an array. + * So, in all, the above JSON string consists of 21 tokens. This detail is + * given here so that the application developers have an idea about how large + * the JSON token array should be, for successful JSON parsing. + * + * Using this JSON parser module you can parse the above elements with the + * following C code: + * + * \code + * #define NUM_TOKENS 30 + * // Define an array of JSON tokens + * jsontok_t json_tokens[NUM_TOKENS]; + * jobj_t jobj; + * // Initialise the JSON parser and parse the given string + * int err = json_init(&jobj, json_tokens, NUM_TOKENS, json_str_ptr, + * strlen(json_str_ptr)); + * if (err != kNoErr) + * return err; + * // Get the value of "name" which is a string + * json_get_val_str(&jobj, "name", sval, sizeof(sval)); + * // Get the value of "version" which is a float + * json_get_val_float(&jobj, "version", &fval); + * // Get the value of "copyright" which is an integer + * json_get_val_int(&jobj, "copyright", &ival); + * // Get the JSON array object "supported_el". After this function returns + * // successfully, only the array search APIs can be used unless the array + * // is released. + * json_get_array_object(&jobj, "supported_el", &num_elements); + * // Get the value at index 2 of the array which is a string value + * json_array_get_str(&jobj, 2, sval, sizeof(sval)); + * // Release the array object so that the scope of searches returns back + * // to the parent object. + * json_release_array_object(&jobj); + * // Get the JSON composite object "features". After this function returns + * // successfully, the scope of the subsequent searches is constrained to + * // within this object, unless the object is released. + * json_get_composite_object(&jobj, "features"); + * // Get the value of "objects" which is a boolean + * json_get_val_bool(&jobj, "objects", &bval); + * // Release the composite object so that the scope of searches returns back + * // to the parent object. + * json_release_composite_object(&jobj); + * + * \endcode + */ + +/* + * Copyright (C) 2008-2015, Marvell International Ltd. + * All Rights Reserved. + */ +#ifndef __JSON_PARSER_H__ +#define __JSON_PARSER_H__ + +#include "mx_common.h" + +/* Globally unique success code */ + +typedef int js_status; + +/** + * JSON token. Application just needs to define an array of these tokens and + * pass it to json_init(). + */ +typedef struct { + /** Type of the token, like object, string, array, etc. */ + uint8_t type; + /** Number of children of this token. Valid for objects and arrays. */ + uint8_t size; + /** Start position in JSON data string. */ + int16_t start; + /** End position in JSON data string. */ + int16_t end; + /** Parent token's index in the token's arrays. */ + int16_t parent; +} jsontok_t; + +/* + * JSON parser Object. + * + * This is for internal use only. + * This should not be used by application code + */ +typedef struct { + /* Offset in the JSON string */ + int16_t pos; + /* Next token to allocate */ + int16_t toknext; /* next token to allocate */ + /* Superior token node, e.g parent object or array */ + int16_t toksuper; +} json_parser_t; + +/** Object used by the JSON parser internally. This object needs to be + * defined by the application but all the elements will be set internally + * by the JSON parser. Please refer \ref json_parser_usage for more information. + * + * \note Please do not modify any elements of this object + */ +typedef struct { + /** Internal JSON parser object */ + json_parser_t parser; + /** Pointer to JSON string */ + char *js; + /** Pointer to JSON tokens array */ + jsontok_t *tokens; + /** Pointer to current active JSON token */ + jsontok_t *cur; + /** Number of tokens available for parser*/ + int num_tokens; +} jobj_t; + +/** Initialize the JSON Parser and parse the given JSON string. + * + * This function initializes the JSON object that will be used for + * all the subsequent JSON parser APIs. It also parses the given JSON + * string and sets up the internal jobj for the subsequent APIs. + * + * \param[in,out] jobj Pointer to a JSON object \ref jobj_t assigned by the + * application. This is set up for further use internally by this API. + * \param[in] tokens Pointer to an array of JSON tokens \ref jsontok_t + * assigned by the application. To get an idea about how large the array should + * be, please refer \ref json_parser_usage section. + * \param[in] num_tokens Number of tokens in the tokens array. + * \param[in] js Pointer to the JSON string. + * \param[in] js_len Length of the JSON string. + * + * \return kNoErr on successful parsing. json_is_object() and/or + * json_is_array() can then be used to find out if the parsed string is a + * JSON object or array so that the appropriate parsing APIs can be used. + * \return kNoMemoryErr if insufficient number of tokens are provided in + * json_init(). + * \return kFormatErr if there are invalid characters in the given JSON + * string. + * \return kMalformedErr if the given JSON string is incomplete. + * \return kFormatErr if the given json string is not a valid + * JSON object. + * \return kGeneralErr if any other error was encountered. + */ +int json_init(jobj_t *jobj, jsontok_t *tokens, int num_tokens, + char *js, int js_len); + +/** Find out if the current object is a JSON Object + * + * \param[in] jobj The current JSON object pointer of type \ref jobj_t + * + * \return true if it is an object + * \return false if it is not an object + */ +bool json_is_object(jobj_t *jobj); + +/** Find out if the current object is a JSON Array + * + * \param[in] jobj The current JSON object pointer of type \ref jobj_t + * + * \return true if it is an array + * \return false if it is not an array + */ +bool json_is_array(jobj_t *jobj); +/** Get JSON bool value + * + * Gets the value of a JSON boolean element from an object based on + * the given key. + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] key Name of the JSON element. + * \param[out] value Pointer to a boolean variable assigned by the application. + * This will hold the value on success. + * + * \return kNoErr on success + * \return kNotFoundErr if an element with the given key was not found. + * \return kFormatErr if an element with the given key was found + * but it was not a boolean. + * \return kFormatErr if the search object was not a valid JSON + * object. + * \return kGeneralErr if any other error was encountered. + */ +int json_get_val_bool(jobj_t *jobj, char *key, bool *value); + +/** Get JSON integer value + * + * Gets the value of a JSON integer element from an object based on + * the given key. + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] key Name of the JSON element. + * \param[out] value Pointer to an integer variable assigned by the + * application. This will hold the value on success. + * + * \return kNoErr on success + * \return kNotFoundErr if an element with the given key was not found. + * \return kFormatErr if an element with the given key was found + * but it was not an integer. + * \return kFormatErr if the search object was not a valid JSON + * object. + * \return kGeneralErr if any other error was encountered. + */ +int json_get_val_int(jobj_t *jobj, char *key, int *value); + +/** Get 64bit JSON integer value + * + * Gets the value of a JSON integer element from an object based on + * the given key. + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] key Name of the JSON element. + * \param[out] value Pointer to a 64bit integer variable assigned by the + * application. This will hold the value on success. + * + * \return kNoErr on success + * \return kNotFoundErr if an element with the given key was not found. + * \return kFormatErr if an element with the given key was found + * but it was not an integer. + * \return kFormatErr if the search object was not a valid JSON + * object. + * \return kGeneralErr if any other error was encountered. + */ +int json_get_val_int64(jobj_t *jobj, char *key, int64_t *value); + +/** Get JSON float value + * + * Gets the value of a JSON float element from an object based on + * the given key. + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] key Name of the JSON element. + * \param[out] value Pointer to a float variable assigned by the application. + * This will hold the value on success. + * + * \return kNoErr on success + * \return kNotFoundErr if an element with the given key was not found. + * \return kFormatErr if an element with the given key was found + * but it was not a float. + * \return kFormatErr if the search object was not a valid JSON + * object. + * \return kGeneralErr if any other error was encountered. + */ +int json_get_val_float(jobj_t *jobj, char *key, float *value); + +/** Get JSON string value + * + * Gets the value of a JSON string element from an object based on + * the given key. + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] key Name of the JSON element. + * \param[out] value Pointer to a buffer assigned by the application to hold + * the string. This will hold the null terminated string on success. + * \param[in] max_len Length of the buffer passed. + * + * \return kNoErr on success + * \return kNotFoundErr if an element with the given key was not found. + * \return kFormatErr if an element with the given key was found + * but it was not a string. + * \return kFormatErr if the search object was not a valid JSON + * object. + * \return kNoMemoryErr if the buffer provided was insufficient. + * \return kGeneralErr if any other error was encountered. + */ +int json_get_val_str(jobj_t *jobj, char *key, char *value, int max_len); + +/** Get JSON string length + * + * Gets the length of a JSON string element from an object based on + * the given key. This is useful when the application does not know + * what size of buffer to use for json_get_val_str() + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] key Name of the JSON element. + * \param[out] len Pointer to an integer assigned by the application to hold + * the length of string. Applications will have to use a buffer of size + * atleast 1 byte more than this length + * + * \return kNoErr on success + * \return kNotFoundErr if an element with the given key was not found. + * \return kFormatErr if an element with the given key was found + * but it was not a string. + * \return kFormatErr if the search object was not a valid JSON + * object. + * \return kGeneralErr if any other error was encountered. + */ +int json_get_val_str_len(jobj_t *jobj, char *key, int *len); + +/** Get JSON composite object + * + * Gets a composite JSON object from another object based on the given key. + * + * \param[in,out] jobj Current JSON search object \ref jobj_t. + * On success, the jobj will be modified such that the scope of subsequent + * searches will be limited to this composite object. Use + * json_release_composite_object() to expand the scope back to the parent + * object. + * \param[in] key Name of the JSON element. + * + * \return kNoErr on success + * \return kNotFoundErr if an element with the given key was not found. + * \return kFormatErr if an element with the given key was found + * but it was not an object. + * \return kFormatErr if the search object was not a valid JSON + * object or the object found was itself not a valid JSON object. + * \return kGeneralErr if any other error was encountered. + */ +int json_get_composite_object(jobj_t *jobj, char *key); + +/** Release a JSON composite object + * + * This function expands the scope of searches back to the parent object, if it + * was previously constrained using json_get_composite_object(). + * + * \param[in,out] jobj Current JSON search object \ref jobj_t. On success, this + * will be modified such that the scope of future searches will be expanded + * back to the parent object. + * + * \return kNoErr on success. + * \return kGeneralErr if any error was encountered. + */ +int json_release_composite_object(jobj_t *jobj); + +/** Get JSON array object + * + * Gets a JSON array object from another object based on the given key. + * + * \param[in,out] jobj Current JSON search object \ref jobj_t. + * On success, the jobj will be modified such that the scope of subsequent + * searches will be limited to this array. Use json_release_array_object() + * to expand the scope back to the parent object. + * \param[in] key Name of the JSON element. + * \param[out] num_elements Pointer to an integer assigned by the application. + * On sucess, this will hold the number of elements in the array found. + * + * \return kNoErr on success + * \return kNotFoundErr if an element with the given key was not found. + * \return kFormatErr if an element with the given key was found + * but it was not an array. + * \return kFormatErr if the search object was not a valid JSON + * object. + * \return kGeneralErr if any other error was encountered. + */ +int json_get_array_object(jobj_t *jobj, char *key, int *num_elements); + +/** Release a JSON array object + * + * This function expands the scope of searches back to the parent object, if it + * was previously constrained using json_get_array_object(). + * + * \param[in,out] jobj Current JSON search object \ref jobj_t. On success, this + * will be modified such that the scope of future searches will be expanded + * back to the parent object. + * + * \return kNoErr on success. + * \return kGeneralErr if any error was encountered. + */ +int json_release_array_object(jobj_t *jobj); + +/** Get number of elements in an array + * + * This API is valid only if the current scope is an array + * + * \param[in] jobj The current JSON object pointer. + * + * \return Number of elements in the JSON Array + * \return kGeneralErr if the current scope is not an array + */ +int json_array_get_num_elements(jobj_t *jobj); + +/** Get JSON bool value from array + * + * Gets the value of a JSON boolean element from an array based on the given + * index. + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] index Index in the array (beginning from 0). + * \param[out] value Pointer to a boolean variable assigned by the application. + * This will hold the value on success. + * + * \return kNoErr on success + * \return kFormatErr if the index provided was invalid. + * \return kFormatErr if an element was found at the given index, + * but it was not a boolean. + * \return kFormatErr if the search object was not a valid JSON + * array. + * \return kGeneralErr if any other error was encountered. + */ +int json_array_get_bool(jobj_t *jobj, uint16_t index, bool *value); + +/** Get JSON integer value from array + * + * Gets the value of a JSON integer element from an array based on the given + * index. + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] index Index in the array (beginning from 0). + * \param[out] value Pointer to an integer variable assigned by the + * application. This will hold the value on success. + * + * \return kNoErr on success + * \return kFormatErr if the index provided was invalid. + * \return kFormatErr if an element was found at the given index, + * but it was not an integer. + * \return kFormatErr if the search object was not a valid JSON + * array. + * \return kGeneralErr if any other error was encountered. + */ +int json_array_get_int(jobj_t *jobj, uint16_t index, int *value); + +/** Get 64bit JSON integer value from array + * + * Gets the value of a JSON integer element from an array based on the given + * index. + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] index Index in the array (beginning from 0). + * \param[out] value Pointer to a 64bit integer variable assigned by the + * application. This will hold the value on success. + * + * \return kNoErr on success + * \return kFormatErr if the index provided was invalid. + * \return kFormatErr if an element was found at the given index, + * but it was not an integer. + * \return kFormatErr if the search object was not a valid JSON + * array. + * \return kGeneralErr if any other error was encountered. + */ +int json_array_get_int64(jobj_t *jobj, uint16_t index, int64_t *value); + +/** Get JSON float value from array + * + * Gets the value of a JSON float element from an array based on the given + * index. + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] index Index in the array (beginning from 0). + * \param[out] value Pointer to a float variable assigned by the application. + * This will hold the value on success. + * + * \return kNoErr on success + * \return kFormatErr if the index provided was invalid. + * \return kFormatErr if an element was found at the given index, + * but it was not a float. + * \return kFormatErr if the search object was not a valid JSON + * array. + * \return kGeneralErr if any other error was encountered. + */ +int json_array_get_float(jobj_t *jobj, uint16_t index, float *value); + +/** Get JSON string value from array + * + * Gets the value of a JSON string element from an array based on the given + * index. + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] index Index in the array (beginning from 0). + * \param[out] value Pointer to a buffer assigned by the application to hold + * the string. This will hold the null terminated string on success. + * \param[in] maxlen Length of the buffer passed. + * + * \return kNoErr on success + * \return kFormatErr if the index provided was invalid. + * \return kFormatErr if an element was found at the given index, + * but it was not a string. + * \return kFormatErr if the search object was not a valid JSON + * array. + * \return kNoMemoryErr if the buffer provided was insufficient. + * \return kGeneralErr if any other error was encountered. + */ +int json_array_get_str(jobj_t *jobj, uint16_t index, char *value, int maxlen); + +/** Get JSON string length from array + * + * Gets the length of a JSON string element from an array based on + * the given index. This is useful when the application does not know + * what size of buffer to use for json_array_get_str() + * + * \param[in] jobj Current JSON search object \ref jobj_t. + * \param[in] index Index in the array (beginning from 0). + * \param[out] len Pointer to an integer assigned by the application to hold + * the length of string. Applications will have to use a buffer of size + * atleast 1 byte more than this length + * + * \return kNoErr on success + * \return kFormatErr if the index provided was invalid. + * \return kFormatErr if an element was found at the given index, + * but it was not a string. + * \return kFormatErr if the search object was not a valid JSON + * array. + * \return kGeneralErr if any other error was encountered. + */ +int json_array_get_str_len(jobj_t *jobj, uint16_t index, int *len); + +/** Get JSON composite object from array + * + * Gets a composite JSON object from an array based on the given index. + * + * \param[in,out] jobj Current JSON search object \ref jobj_t. + * On success, the jobj will be modified such that the scope of subsequent + * searches will be limited to this composite object. Use + * json_array_release_composite_object() to expand the scope back to the + * parent array. + * \param[in] index Index in the array (beginning from 0). + * + * \return kNoErr on success + * \return kFormatErr if the index provided was invalid. + * \return kFormatErr if an element was found at the given index, + * but it was not an object. + * \return kFormatErr if the search object was not a valid JSON + * array. + * \return kFormatErr if the object found was not a valid JSON + * object. + * \return kGeneralErr if any other error was encountered. + */ +int json_array_get_composite_object(jobj_t *jobj, uint16_t index); + +/** Release a JSON composite object from an array + * + * This function expands the scope of searches back to the parent array, if it + * was previously constrained using json_array_get_composite_object(). + * + * \param[in,out] jobj Current JSON search object \ref jobj_t. On success, this + * will be modified such that the scope of future searches will be expanded + * back to the parent array. + * + * \return kNoErr on success. + * \return kGeneralErr if any error was encountered. + */ +int json_array_release_composite_object(jobj_t *jobj); + +/** Get JSON array object from array + * + * Gets a JSON array object from another array based on the given index. + * + * \param[in,out] jobj Current JSON search object \ref jobj_t. + * On success, the jobj will be modified such that the scope of subsequent + * searches will be limited to this composite object. Use + * json_array_release_composite_object() to expand the scope back to the + * parent array. + * \param[in] index Index in the array (beginning from 0). + * \param[out] num_elements Pointer to an integer assigned by the application. + * On sucess, this will hold the number of elements in the array found. + * + * \return kNoErr on success + * \return kFormatErr if the index provided was invalid. + * \return kFormatErr if an element was found at the given index, + * but it was not an array. + * \return kFormatErr if the search object was not a valid JSON + * array. + * \return kGeneralErr if any other error was encountered. + */ +int json_array_get_array_object(jobj_t *jobj, uint16_t index, + int *num_elements); + +/** Release a JSON array object from an array + * + * This function expands the scope of searches back to the parent array, if it + * was previously constrained using json_array_get_array_object(). + * + * \param[in,out] jobj Current JSON search object \ref jobj_t. On success, this + * will be modified such that the scope of future searches will be expanded + * back to the parent array. + * + * \return kNoErr on success. + * \return kGeneralErr if any error was encountered. + */ +int json_array_release_array_object(jobj_t *jobj); + +#endif /* __JSON_PARSER_H__ */ diff --git a/src/helper/jsmn/json_utils.c b/src/helper/jsmn/json_utils.c new file mode 100644 index 0000000..a8c3ae4 --- /dev/null +++ b/src/helper/jsmn/json_utils.c @@ -0,0 +1,72 @@ +#include "mx_common.h" + +#define mico_int_part_of(x) ((int)(x)) + +static inline int mico_frac_part_of(float x, short precision) +{ + int scale = 1; + + while (precision--) + scale *= 10; + + return (x < 0 ? (int)(((int)x - x) * scale) : (int)((x - (int)x) * scale)); +} + +/* Function to convert a string to float. + * \param[in] str Pointer to a string to be addressed + * \param[out] endptr Pointer pointing to next character in a string */ +float mico_strtof(const char *str, char **endptr) +{ + char *start_ptr = (char *)str; + int sign = 1; + + if (endptr == NULL) { + char *end_ptr; + endptr = &end_ptr; + } + + if (*start_ptr == '-') { + sign = -1; + start_ptr++; + } + int dec_val = 0; + int powten = 1; + + int int_val = strtoul(start_ptr, endptr, 10); + if (**endptr == '.') { + start_ptr = *endptr + 1; + dec_val = strtoul(start_ptr, endptr, 10); + while (start_ptr++ != *endptr) + powten *= 10; + } else + return sign * (float)int_val; + + float dec_frac = (float)dec_val/powten; + float result = (float)(int_val + dec_frac); + + /* Below part is done in order to improve the accuracy of the result. + * Since addition above results in float value being drifted from + * the actual value by narrow margin. e.g 50.10 results in float + * equivalent of 50.09.*/ + /* TODO: Visit again to see if the below code really helps. + * Sometimes, reporting values differently is a result of the + * way float values are stored in memory. In that case, the below + * code will not improve anything. Eg. If 50.1 is stored as 50.09 + * in memory, we cannot do much about it. + */ + int result_int_value = mico_int_part_of(result); + float result_frac_value = (float)(result) - result_int_value; + + /* Generally difference between two float values comes out to be in + * the order of 1/powten. e.g 0.10-0.09 comes out to be 0.00...1. + * Hence we multiply the result of subtraction to achieve the accuracy + * within desired float precision. */ + if (mico_frac_part_of(dec_frac, powten/10) > + mico_frac_part_of(result_frac_value, powten/10)) + result += ((dec_frac - result_frac_value) * powten); + if (mico_frac_part_of(result_frac_value, powten/10) > + mico_frac_part_of(dec_frac, powten/10)) + result += ((result_frac_value - dec_frac) * powten); + + return sign * result; +} diff --git a/src/helper/jsmn/json_utils.h b/src/helper/jsmn/json_utils.h new file mode 100644 index 0000000..8facf01 --- /dev/null +++ b/src/helper/jsmn/json_utils.h @@ -0,0 +1,6 @@ +#ifndef _JSON_UTILS_H_ +#define _JSON_UTILS_H_ + +float mico_strtof(const char *str, char **endptr); + +#endif diff --git a/src/helper/jsmn/json_wrappers.c b/src/helper/jsmn/json_wrappers.c new file mode 100644 index 0000000..9666a74 --- /dev/null +++ b/src/helper/jsmn/json_wrappers.c @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2008-2015, Marvell International Ltd. + * All Rights Reserved. + */ +#include "mx_common.h" +#include "json_parser.h" +#include "jsmn.h" +#include "json_utils.h" + +#define CONFIG_JSON_FLOAT + +/* Parse states are used to get a value based on the key */ +typedef enum {KEY, FOUND, SKIP} parse_state; + +/* Returns true if an exact string match is found, else false */ +static bool json_token_streq(char *js, jsontok_t *t, char *s) +{ + return (strncmp(js + t->start, s, t->end - t->start) == 0 + && strlen(s) == (size_t) (t->end - t->start)); +} + +/* Skips to the last element of an array or object. + * If there is an array/object inside the given array/object, + * the function is called recursively to skip all elements + */ +static jsontok_t *skip_to_last(jsontok_t *element) +{ + jsontok_t *t = element; + if (t->size == 0) + return t; + int cnt = t->size; + while (cnt--) { + t++; + if (t->size) + t = skip_to_last(t); + } + return t; +} +/* Converts the value held by the token into a boolean */ +static int json_str_to_bool(jobj_t *jobj, jsontok_t *t, bool *value) +{ + if (!t || t->type != JSMN_PRIMITIVE) + return kFormatErr; + if (json_token_streq(jobj->js, t, "true") || + json_token_streq(jobj->js, t, "1")) + *value = true; + else if (json_token_streq(jobj->js, t, "false") || + json_token_streq(jobj->js, t, "0")) + *value = false; + else + return kFormatErr; + return kNoErr; + +} + +/* Converts the value held by the token into an integer */ +static int json_str_to_int(jobj_t *jobj, jsontok_t *t, int *value) +{ + if (!t || t->type != JSMN_PRIMITIVE) + return kFormatErr; + char *endptr; + int int_val = strtoul(&jobj->js[t->start], &endptr, 10); + if (endptr == &(jobj->js[t->end])) { + *value = int_val; + return kNoErr; + } else { + return kFormatErr; + } +} +/* Converts the value held by the token into an int64 */ +static int json_str_to_int64(jobj_t *jobj, jsontok_t *t, int64_t *value) +{ + if (!t || t->type != JSMN_PRIMITIVE) + return kFormatErr; + char *endptr; + int64_t int_val = strtoull(&jobj->js[t->start], &endptr, 10); + if (endptr == &(jobj->js[t->end])) { + *value = int_val; + return kNoErr; + } else { + return kFormatErr; + } +} +#ifdef CONFIG_JSON_FLOAT +/* Converts the value held by the token into a float */ +static int json_str_to_float(jobj_t *jobj, jsontok_t *t, float *value) +{ + if (!t || t->type != JSMN_PRIMITIVE) + return kFormatErr; + char *start_ptr = &jobj->js[t->start]; + char *endptr; + + *value = mico_strtof(start_ptr, &endptr); + if (endptr != &(jobj->js[t->end])) + return kFormatErr; + return kNoErr; +} +#endif /* CONFIG_JSON_FLOAT */ + +/* Converts the value held by the token into a null terminated string */ +static int json_str_to_str(jobj_t *jobj, jsontok_t *t, char *value, int maxlen) +{ + if (!t || t->type != JSMN_STRING) + return kFormatErr; + if ((t->end - t->start) >= maxlen) + return kNoMemoryErr; + strncpy(value, jobj->js + t->start, t->end - t->start); + value[t->end - t->start] = 0; + return kNoErr; +} + +/* Searches for json element inside an object based on the key and populates + * the val_t token if element is found and returns kNoErr. + * If not found, returns error. + */ +static int json_get_value(jobj_t *jobj, char *key, jsontok_t **val_t) +{ + jsontok_t *t = jobj->cur; + int num_children = t->size; + *val_t = NULL; + /* If there are no children or not even number of children, + * it is an error. + * Number of children should be even because the object consists + * of key:value pairs. + */ + if ((num_children == 0) || ((num_children % 2) != 0)) + return kGeneralErr; + + /* If the current token type is not an object, it is an error */ + if (t->type != JSMN_OBJECT) + return kFormatErr; + parse_state state = KEY; + while (num_children--) { + /* Increment the token pointer first so that we begin from the + * first token inside the object. + */ + t++; + /* For safety, check if the current token's end does not go + * beyond the parent object's end. This case is unlikely, yet, + * better to have a check. + */ + if (t->end > jobj->cur->end) + return kGeneralErr; + switch (state) { + case KEY: + /* First token inside an object should be a key. + * If not, it is an error. + */ + if (t->type != JSMN_STRING) + return kGeneralErr; + /* If the key matches with the given key, the member + * has been found. The next token will be its value. + * Else, just skip the value. + */ + if (json_token_streq(jobj->js, t, key)) + state = FOUND; + else + state = SKIP; + break; + case SKIP: + /* If the value to be skipped is an array or object, + * skip to its last element. + */ + if (t->type == JSMN_ARRAY || t->type == JSMN_OBJECT) + t = skip_to_last(t); + state = KEY; + break; + + case FOUND: + /* Value found. Populate the token pointer and return + * success. + */ + *val_t = t; + return kNoErr; + } + } + return kNotFoundErr; +} + +/* Search boolean value based on given key */ +int json_get_val_bool(jobj_t *jobj, char *key, bool *value) +{ + jsontok_t *t; + int ret = json_get_value(jobj, key, &t); + if (ret != kNoErr) + return ret; + return json_str_to_bool(jobj, t, value); +} +/* Search integer value based on given key */ +int json_get_val_int(jobj_t *jobj, char *key, int *value) +{ + jsontok_t *t; + int ret = json_get_value(jobj, key, &t); + if (ret != kNoErr) + return ret; + return json_str_to_int(jobj, t, value); +} +/* Search int64 value based on given key */ +int json_get_val_int64(jobj_t *jobj, char *key, int64_t *value) +{ + jsontok_t *t; + int ret = json_get_value(jobj, key, &t); + if (ret != kNoErr) + return ret; + return json_str_to_int64(jobj, t, value); +} +/* Search float value based on given key */ +#ifdef CONFIG_JSON_FLOAT +int json_get_val_float(jobj_t *jobj, char *key, float *value) +{ + jsontok_t *t; + int ret = json_get_value(jobj, key, &t); + if (ret != kNoErr) + return ret; + return json_str_to_float(jobj, t, value); +} +#else +int json_get_val_float(jobj_t *jobj, char *key, float *value) +{ + return kGeneralErr; +} +#endif /* CONFIG_JSON_FLOAT */ + +/* Search string value based on given key */ +int json_get_val_str(jobj_t *jobj, char *key, char *value, int maxlen) +{ + jsontok_t *t; + int ret = json_get_value(jobj, key, &t); + if (ret != kNoErr) + return ret; + return json_str_to_str(jobj, t, value, maxlen); +} + +int json_get_val_str_len(jobj_t *jobj, char *key, int *len) +{ + jsontok_t *t; + int ret = json_get_value(jobj, key, &t); + if (ret != kNoErr) + return ret; + if (!t || t->type != JSMN_STRING) + return kFormatErr; + *len = t->end - t->start; + return kNoErr; +} +/* Search composite object based on given key */ +int json_get_composite_object(jobj_t *jobj, char *key) +{ + jsontok_t *t; + int ret = json_get_value(jobj, key, &t); + if (ret != kNoErr) + return ret; + if (!t || t->type != JSMN_OBJECT) + return kFormatErr; + if (t->size % 2 != 0) + return kFormatErr; + /* Reduce the scope of subsequent searches to this object */ + jobj->cur = t; + return kNoErr; +} + +/* Release a composite object*/ +int json_release_composite_object(jobj_t *jobj) +{ + if (jobj->cur->parent < 0) + return kGeneralErr; + /* Increase the scope of subsequent searches the parent element */ + jobj->cur = &jobj->tokens[jobj->cur->parent]; + return kNoErr; +} + +/* Search array object based on given key */ +int json_get_array_object(jobj_t *jobj, char *key, int *num_elements) +{ + jsontok_t *t; + int ret = json_get_value(jobj, key, &t); + if (ret != kNoErr) + return ret; + if (!t || t->type != JSMN_ARRAY) + return kFormatErr; + /* Reduce the scope of subsequent searches to this array */ + jobj->cur = t; + /* Indicate the number of array elements found, if requested */ + if (num_elements) + *num_elements = t->size; + return kNoErr; +} +/* Release array object */ +int json_release_array_object(jobj_t *jobj) +{ + return json_release_composite_object(jobj); +} + +int json_array_get_num_elements(jobj_t *jobj) +{ + if (jobj->cur->type != JSMN_ARRAY) + return kGeneralErr; + return jobj->cur->size; + +} +/* Fetch the JSON value from an array based on index. + * val_t is appropriately populated if the element is found + * and kNoErr is returned. Else error is returned. + */ +static int json_get_array_index(jobj_t *jobj, uint16_t index, jsontok_t **val_t) +{ + *val_t = NULL; + if (jobj->cur->type != JSMN_ARRAY) + return kFormatErr; + /* Given index exceeds the size of array. */ + if (index >= jobj->cur->size) + return kFormatErr; + jsontok_t *t = jobj->cur; + /* Incrementing once so that the token pointer points to index 0*/ + t++; + while (index--) { + /* For safety, check if the current token's end does not go + * beyond the parent object's end. This case is unlikely, yet, + * better to have a check. + */ + if (t->end > jobj->cur->end) + return kGeneralErr; + /* If the element is an array or object, skip to its last + * element. + */ + if (t->type == JSMN_ARRAY || t->type == JSMN_OBJECT) + t = skip_to_last(t); + t++; + } + *val_t = t; + return kNoErr; +} + +/* Search boolean value inside an array based on given index */ +int json_array_get_bool(jobj_t *jobj, uint16_t index, bool *value) +{ + jsontok_t *t; + int ret = json_get_array_index(jobj, index, &t); + if (ret != kNoErr) + return ret; + return json_str_to_bool(jobj, t, value); +} +/* Search integer value inside an array based on given index */ +int json_array_get_int(jobj_t *jobj, uint16_t index, int *value) +{ + jsontok_t *t; + int ret = json_get_array_index(jobj, index, &t); + if (ret != kNoErr) + return ret; + return json_str_to_int(jobj, t, value); +} +/* Search int64 value inside an array based on given index */ +int json_array_get_int64(jobj_t *jobj, uint16_t index, int64_t *value) +{ + jsontok_t *t; + int ret = json_get_array_index(jobj, index, &t); + if (ret != kNoErr) + return ret; + return json_str_to_int64(jobj, t, value); +} +/* Search float value inside an array based on given index */ +#ifdef CONFIG_JSON_FLOAT +int json_array_get_float(jobj_t *jobj, uint16_t index, float *value) +{ + jsontok_t *t; + int ret = json_get_array_index(jobj, index, &t); + if (ret != kNoErr) + return ret; + return json_str_to_float(jobj, t, value); +} +#else +int json_array_get_float(jobj_t *jobj, uint16_t index, float *value) +{ + return kGeneralErr; +} +#endif /* CONFIG_JSON_FLOAT */ + +/* Search string value inside an array based on given index */ +int json_array_get_str(jobj_t *jobj, uint16_t index, char *value, int maxlen) +{ + jsontok_t *t; + int ret = json_get_array_index(jobj, index, &t); + if (ret != kNoErr) + return ret; + return json_str_to_str(jobj, t, value, maxlen); +} + +int json_array_get_str_len(jobj_t *jobj, uint16_t index, int *len) +{ + jsontok_t *t; + int ret = json_get_array_index(jobj, index, &t); + if (ret != kNoErr) + return ret; + if (!t || t->type != JSMN_STRING) + return kFormatErr; + *len = t->end - t->start; + return kNoErr; +} + +/* Search composite object inside an array based on given index */ +int json_array_get_composite_object(jobj_t *jobj, uint16_t index) +{ + jsontok_t *t; + int ret = json_get_array_index(jobj, index, &t); + if (ret != kNoErr) + return ret; + if (!t || t->type != JSMN_OBJECT) + return kFormatErr; + /* If number of children is not even, return error. + * Number of children should be even because the object consists + * of key:value pairs. + */ + if (t->size % 2 != 0) + return kFormatErr; + jobj->cur = t; + return kNoErr; +} +/* Release the composite object inside the array */ +int json_array_release_composite_object(jobj_t *jobj) +{ + return json_release_composite_object(jobj); +} + +/* Search an array inside an array based on given index */ +int json_array_get_array_object(jobj_t *jobj, uint16_t index, + int *num_elements) +{ + jsontok_t *t; + int ret = json_get_array_index(jobj, index, &t); + if (ret != kNoErr) + return ret; + if (!t || t->type != JSMN_ARRAY) + return kFormatErr; + jobj->cur = t; + *num_elements = t->size; + return kNoErr; +} +/* Release the array */ +int json_array_release_array_object(jobj_t *jobj) +{ + return json_release_composite_object(jobj); +} + +/* Initialize the JSON parser */ +static void json_obj_init(jobj_t *jobj, jsontok_t *tokens, int num_tokens) +{ + jobj->js = NULL; + jobj->tokens = tokens; + jobj->num_tokens = num_tokens; + jobj->cur = NULL; + jsmn_init(&jobj->parser); +} + +bool json_is_object(jobj_t *jobj) +{ + if (jobj->cur->type == JSMN_OBJECT) + return true; + else + return false; +} +bool json_is_array(jobj_t *jobj) +{ + if (jobj->cur->type == JSMN_ARRAY) + return true; + else + return false; +} +/* Parse the given JSON string */ +int json_init(jobj_t *jobj, jsontok_t *tokens, int num_tokens, + char *js, int js_len) +{ + json_obj_init(jobj, tokens, num_tokens); + int parsed_tokens = jsmn_parse(&jobj->parser, js, js_len, + jobj->tokens, jobj->num_tokens); + if (parsed_tokens < 0) { + switch (parsed_tokens) { + case JSMN_ERROR_NOMEM: + return kNoMemoryErr; + case JSMN_ERROR_INVAL: + return kFormatErr; + case JSMN_ERROR_PART: + return kMalformedErr; + default: + return kGeneralErr; + } + } + jobj->js = js; + jobj->num_tokens = parsed_tokens; + jobj->cur = jobj->tokens; + /* Number of children for an object should always be even as + * a JSON object consists of key:value pairs. + * If the type of first token is an object, and it has even + * number of children, it is valid. + * Else, if the type of the first token is array, it is valid. + * Else, the JSON string is invalid. + */ + if ((jobj->tokens->type == JSMN_OBJECT) && + ((jobj->tokens->size % 2) == 0)) + return kNoErr; + else if (jobj->tokens->type == JSMN_ARRAY) + return kNoErr; + else + return kFormatErr; +} diff --git a/src/helper/mx_common.h b/src/helper/mx_common.h new file mode 100644 index 0000000..9e0c6e9 --- /dev/null +++ b/src/helper/mx_common.h @@ -0,0 +1,645 @@ +/** + ****************************************************************************** + * @file mx_common.h + * @author William Xu + * @version V1.0.0 + * @date 18-Apr-2018 + * @brief his header contains common defines, macros and functions to be + * shared throughout the project. + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef __MX_COMMON_H__ +#define __MX_COMMON_H__ + + +// ==== STD LIB ==== +#include +#include +#include +#include +#include +#include + +#include "mx_toolchain.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define TARGET_RT_LITTLE_ENDIAN + +// ==== COMPATIBILITY TYPES + +#define MX_FALSE (0) +#define MX_TRUE (1) + +#if( !defined( INT_MAX ) ) + #define INT_MAX 2147483647 +#endif + +// ==== MiCO Timer Typedef ==== +#define NANOSECONDS 1000000UL +#define MICROSECONDS 1000 +#define MILLISECONDS (1) +#define SECONDS (1000) +#define MINUTES (60 * SECONDS) +#define HOURS (60 * MINUTES) +#define DAYS (24 * HOURS) + + +// ==== OSStatus ==== +typedef int mx_status; +typedef int OSStatus; + +#define kNoErr 0 //! No error occurred. +#define MICO_SUCCESS 0 //! No error occurred. +#define kGeneralErr -1 //! General error. +#define kInProgressErr 1 //! Operation in progress. + +// Generic error codes are in the range -6700 to -6779. + +#define kGenericErrorBase -6700 //! Starting error code for all generic errors. + +#define kUnknownErr -6700 //! Unknown error occurred. +#define kOptionErr -6701 //! Option was not acceptable. +#define kSelectorErr -6702 //! Selector passed in is invalid or unknown. +#define kExecutionStateErr -6703 //! Call made in the wrong execution state (e.g. called at interrupt time). +#define kPathErr -6704 //! Path is invalid, too long, or otherwise not usable. +#define kParamErr -6705 //! Parameter is incorrect, missing, or not appropriate. +#define kUserRequiredErr -6706 //! User interaction is required. +#define kCommandErr -6707 //! Command invalid or not supported. +#define kIDErr -6708 //! Unknown, invalid, or inappropriate identifier. +#define kStateErr -6709 //! Not in appropriate state to perform operation. +#define kRangeErr -6710 //! Index is out of range or not valid. +#define kRequestErr -6711 //! Request was improperly formed or not appropriate. +#define kResponseErr -6712 //! Response was incorrect or out of sequence. +#define kChecksumErr -6713 //! Checksum does not match the actual data. +#define kNotHandledErr -6714 //! Operation was not handled (or not handled completely). +#define kVersionErr -6715 //! Version is not correct or not compatible. +#define kSignatureErr -6716 //! Signature did not match what was expected. +#define kFormatErr -6717 //! Unknown, invalid, or inappropriate file/data format. +#define kNotInitializedErr -6718 //! Action request before needed services were initialized. +#define kAlreadyInitializedErr -6719 //! Attempt made to initialize when already initialized. +#define kNotInUseErr -6720 //! Object not in use (e.g. cannot abort if not already in use). +#define kAlreadyInUseErr -6721 //! Object is in use (e.g. cannot reuse active param blocks). +#define kTimeoutErr -6722 //! Timeout occurred. +#define kCanceledErr -6723 //! Operation canceled (successful cancel). +#define kAlreadyCanceledErr -6724 //! Operation has already been canceled. +#define kCannotCancelErr -6725 //! Operation could not be canceled (maybe already done or invalid). +#define kDeletedErr -6726 //! Object has already been deleted. +#define kNotFoundErr -6727 //! Something was not found. +#define kNoMemoryErr -6728 //! Not enough memory was available to perform the operation. +#define kNoResourcesErr -6729 //! Resources unavailable to perform the operation. +#define kDuplicateErr -6730 //! Duplicate found or something is a duplicate. +#define kImmutableErr -6731 //! Entity is not changeable. +#define kUnsupportedDataErr -6732 //! Data is unknown or not supported. +#define kIntegrityErr -6733 //! Data is corrupt. +#define kIncompatibleErr -6734 //! Data is not compatible or it is in an incompatible format. +#define kUnsupportedErr -6735 //! Feature or option is not supported. +#define kUnexpectedErr -6736 //! Error occurred that was not expected. +#define kValueErr -6737 //! Value is not appropriate. +#define kNotReadableErr -6738 //! Could not read or reading is not allowed. +#define kNotWritableErr -6739 //! Could not write or writing is not allowed. +#define kBadReferenceErr -6740 //! An invalid or inappropriate reference was specified. +#define kFlagErr -6741 //! An invalid, inappropriate, or unsupported flag was specified. +#define kMalformedErr -6742 //! Something was not formed correctly. +#define kSizeErr -6743 //! Size was too big, too small, or not appropriate. +#define kNameErr -6744 //! Name was not correct, allowed, or appropriate. +#define kNotPreparedErr -6745 //! Device or service is not ready. +#define kReadErr -6746 //! Could not read. +#define kWriteErr -6747 //! Could not write. +#define kMismatchErr -6748 //! Something does not match. +#define kDateErr -6749 //! Date is invalid or out-of-range. +#define kUnderrunErr -6750 //! Less data than expected. +#define kOverrunErr -6751 //! More data than expected. +#define kEndingErr -6752 //! Connection, session, or something is ending. +#define kConnectionErr -6753 //! Connection failed or could not be established. +#define kAuthenticationErr -6754 //! Authentication failed or is not supported. +#define kOpenErr -6755 //! Could not open file, pipe, device, etc. +#define kTypeErr -6756 //! Incorrect or incompatible type (e.g. file, data, etc.). +#define kSkipErr -6757 //! Items should be or was skipped. +#define kNoAckErr -6758 //! No acknowledge. +#define kCollisionErr -6759 //! Collision occurred (e.g. two on bus at same time). +#define kBackoffErr -6760 //! Backoff in progress and operation intentionally failed. +#define kNoAddressAckErr -6761 //! No acknowledge of address. +#define kInternalErr -6762 //! An error internal to the implementation occurred. +#define kNoSpaceErr -6763 //! Not enough space to perform operation. +#define kCountErr -6764 //! Count is incorrect. +#define kEndOfDataErr -6765 //! Reached the end of the data (e.g. recv returned 0). +#define kWouldBlockErr -6766 //! Would need to block to continue (e.g. non-blocking read/write). +#define kLookErr -6767 //! Special case that needs to be looked at (e.g. interleaved data). +#define kSecurityRequiredErr -6768 //! Security is required for the operation (e.g. must use encryption). +#define kOrderErr -6769 //! Order is incorrect. +#define kUpgradeErr -6770 //! Must upgrade. +#define kAsyncNoErr -6771 //! Async operation successfully started and is now in progress. +#define kDeprecatedErr -6772 //! Operation or data is deprecated. +#define kPermissionErr -6773 //! Permission denied. + +#define kGenericErrorEnd -6779 //! Last generic error code (inclusive) + +#define kJsonErrorBase -7000 //! Starting error code for all generic errors. +#define kJsonErrorEnd -7100 //! Last generic error code (inclusive) + + +// ==== C TYPE SAFE MACROS ==== +//--------------------------------------------------------------------------------------------------------------------------- +/*! @group ctype safe macros + @abstract Wrappers for the ctype.h macros make them safe when used with signed characters. + @discussion + + Some implementations of the ctype.h macros use the character value to directly index into a table. + This can lead to crashes and other problems when used with signed characters if the character value + is greater than 127 because the values 128-255 will appear to be negative if viewed as a signed char. + A negative subscript to an array causes it to index before the beginning and access invalid memory. + + To work around this, these *_safe wrappers mask the value and cast it to an unsigned char. +*/ + +#define isalnum_safe( X ) isalnum( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define isalpha_safe( X ) isalpha( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define iscntrl_safe( X ) iscntrl( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define isdigit_safe( X ) isdigit( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define isgraph_safe( X ) isgraph( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define islower_safe( X ) islower( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define isoctal_safe( X ) isoctal( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define isprint_safe( X ) isprint( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define ispunct_safe( X ) ispunct( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define isspace_safe( X ) isspace( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define isupper_safe( X ) isupper( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define isxdigit_safe( X ) isxdigit( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define tolower_safe( X ) tolower( ( (unsigned char)( ( X ) & 0xFF ) ) ) +#define toupper_safe( X ) toupper( ( (unsigned char)( ( X ) & 0xFF ) ) ) + +// ==== MIN / MAX ==== +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function Min + @abstract Returns the lesser of X and Y. +*/ +#if( !defined( Min ) ) + #define Min( X, Y ) ( ( ( X ) < ( Y ) ) ? ( X ) : ( Y ) ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function Max + @abstract Returns the greater of X and Y. +*/ +#if( !defined( Max ) ) + #define Max( X, Y ) ( ( ( X ) > ( Y ) ) ? ( X ) : ( Y ) ) +#endif + + +// ==== Alignment / Endian safe read/write/swap macros ==== +#define ReadBig16( PTR ) \ + ( (uint16_t)( \ + ( ( (uint16_t)( (uint8_t *)(PTR) )[ 0 ] ) << 8 ) | \ + ( (uint16_t)( (uint8_t *)(PTR) )[ 1 ] ) ) ) + +#define ReadBig32( PTR ) \ + ( (uint32_t)( \ + ( ( (uint32_t)( (uint8_t *)(PTR) )[ 0 ] ) << 24 ) | \ + ( ( (uint32_t)( (uint8_t *)(PTR) )[ 1 ] ) << 16 ) | \ + ( ( (uint32_t)( (uint8_t *)(PTR) )[ 2 ] ) << 8 ) | \ + ( (uint32_t)( (uint8_t *)(PTR) )[ 3 ] ) ) ) + +#define ReadBig48( PTR ) \ + ( (uint64_t)( \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 0 ] ) << 40 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 1 ] ) << 32 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 2 ] ) << 24 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 3 ] ) << 16 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 4 ] ) << 8 ) | \ + ( (uint64_t)( (uint8_t *)(PTR) )[ 5 ] ) ) ) + +#define ReadBig64( PTR ) \ + ( (uint64_t)( \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 0 ] ) << 56 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 1 ] ) << 48 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 2 ] ) << 40 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 3 ] ) << 32 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 4 ] ) << 24 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 5 ] ) << 16 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 6 ] ) << 8 ) | \ + ( (uint64_t)( (uint8_t *)(PTR) )[ 7 ] ) ) ) + +// Big endian Writing +#define WriteBig16( PTR, X ) \ + do \ + { \ + ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( ( (X) >> 8 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( (X) & 0xFF ); \ + \ + } while( 0 ) + +#define WriteBig32( PTR, X ) \ + do \ + { \ + ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( ( (X) >> 24 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( ( (X) >> 16 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 2 ] = (uint8_t)( ( (X) >> 8 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 3 ] = (uint8_t)( (X) & 0xFF ); \ + \ + } while( 0 ) + +#define WriteBig48( PTR, X ) \ + do \ + { \ + ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( ( (X) >> 40 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( ( (X) >> 32 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 2 ] = (uint8_t)( ( (X) >> 24 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 3 ] = (uint8_t)( ( (X) >> 16 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 4 ] = (uint8_t)( ( (X) >> 8 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 5 ] = (uint8_t)( (X) & 0xFF ); \ + \ + } while( 0 ) + +#define WriteBig64( PTR, X ) \ + do \ + { \ + ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( ( (X) >> 56 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( ( (X) >> 48 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 2 ] = (uint8_t)( ( (X) >> 40 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 3 ] = (uint8_t)( ( (X) >> 32 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 4 ] = (uint8_t)( ( (X) >> 24 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 5 ] = (uint8_t)( ( (X) >> 16 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 6 ] = (uint8_t)( ( (X) >> 8 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 7 ] = (uint8_t)( (X) & 0xFF ); \ + \ + } while( 0 ) + +// Little endian reading +#define ReadLittle16( PTR ) \ + ( (uint16_t)( \ + ( (uint16_t)( (uint8_t *)(PTR) )[ 0 ] ) | \ + ( ( (uint16_t)( (uint8_t *)(PTR) )[ 1 ] ) << 8 ) ) ) + +#define ReadLittle32( PTR ) \ + ( (uint32_t)( \ + ( (uint32_t)( (uint8_t *)(PTR) )[ 0 ] ) | \ + ( ( (uint32_t)( (uint8_t *)(PTR) )[ 1 ] ) << 8 ) | \ + ( ( (uint32_t)( (uint8_t *)(PTR) )[ 2 ] ) << 16 ) | \ + ( ( (uint32_t)( (uint8_t *)(PTR) )[ 3 ] ) << 24 ) ) ) + +#define ReadLittle48( PTR ) \ + ( (uint64_t)( \ + ( (uint64_t)( (uint8_t *)(PTR) )[ 0 ] ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 1 ] ) << 8 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 2 ] ) << 16 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 3 ] ) << 24 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 4 ] ) << 32 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 5 ] ) << 40 ) ) ) + +#define ReadLittle64( PTR ) \ + ( (uint64_t)( \ + ( (uint64_t)( (uint8_t *)(PTR) )[ 0 ] ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 1 ] ) << 8 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 2 ] ) << 16 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 3 ] ) << 24 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 4 ] ) << 32 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 5 ] ) << 40 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 6 ] ) << 48 ) | \ + ( ( (uint64_t)( (uint8_t *)(PTR) )[ 7 ] ) << 56 ) ) ) + +// Little endian writing +#define WriteLittle16( PTR, X ) \ + do \ + { \ + ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( (X) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( ( (X) >> 8 ) & 0xFF ); \ + \ + } while( 0 ) + +#define WriteLittle32( PTR, X ) \ + do \ + { \ + ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( (X) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( ( (X) >> 8 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 2 ] = (uint8_t)( ( (X) >> 16 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 3 ] = (uint8_t)( ( (X) >> 24 ) & 0xFF ); \ + \ + } while( 0 ) + +#define WriteLittle48( PTR, X ) \ + do \ + { \ + ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( (X) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( ( (X) >> 8 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 2 ] = (uint8_t)( ( (X) >> 16 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 3 ] = (uint8_t)( ( (X) >> 24 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 4 ] = (uint8_t)( ( (X) >> 32 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 5 ] = (uint8_t)( ( (X) >> 40 ) & 0xFF ); \ + \ + } while( 0 ) + +#define WriteLittle64( PTR, X ) \ + do \ + { \ + ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( (X) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( ( (X) >> 8 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 2 ] = (uint8_t)( ( (X) >> 16 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 3 ] = (uint8_t)( ( (X) >> 24 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 4 ] = (uint8_t)( ( (X) >> 32 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 5 ] = (uint8_t)( ( (X) >> 40 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 6 ] = (uint8_t)( ( (X) >> 48 ) & 0xFF ); \ + ( (uint8_t *)(PTR) )[ 7 ] = (uint8_t)( ( (X) >> 56 ) & 0xFF ); \ + \ + } while( 0 ) + + +#if( !defined( TARGET_RT_LITTLE_ENDIAN ) && !defined( TARGET_RT_BIG_ENDIAN ) ) + #error unknown byte order - update your Makefile to define the target platform endianness as TARGET_RT_BIG_ENDIAN / TARGET_RT_LITTLE_ENDIAN +#endif + +#if( !defined( TARGET_RT_LITTLE_ENDIAN ) ) + #define ReadHost16( PTR ) ReadBig16( (PTR) ) + #define ReadHost32( PTR ) ReadBig32( (PTR) ) + #define ReadHost48( PTR ) ReadBig48( (PTR) ) + #define ReadHost64( PTR ) ReadBig64( (PTR) ) + + #define WriteHost16( PTR, X ) WriteBig16( (PTR), (X) ) + #define WriteHost32( PTR, X ) WriteBig32( (PTR), (X) ) + #define WriteHost48( PTR, X ) WriteBig48( (PTR), (X) ) + #define WriteHost64( PTR, X ) WriteBig64( (PTR), (X) ) +#else + #define ReadHost16( PTR ) ReadLittle16( (PTR) ) + #define ReadHost32( PTR ) ReadLittle32( (PTR) ) + #define ReadHost48( PTR ) ReadLittle48( (PTR) ) + #define ReadHost64( PTR ) ReadLittle64( (PTR) ) + + #define WriteHost16( PTR, X ) WriteLittle16( (PTR), (X) ) + #define WriteHost32( PTR, X ) WriteLittle32( (PTR), (X) ) + #define WriteHost48( PTR, X ) WriteLittle48( (PTR), (X) ) + #define WriteHost64( PTR, X ) WriteLittle64( (PTR), (X) ) +#endif + +// Unconditional swap read/write. +#if( !defined( TARGET_RT_LITTLE_ENDIAN ) ) + #define ReadSwap16( PTR ) ReadLittle16( (PTR) ) + #define ReadSwap32( PTR ) ReadLittle32( (PTR) ) + #define ReadSwap48( PTR ) ReadLittle48( (PTR) ) + #define ReadSwap64( PTR ) ReadLittle64( (PTR) ) + + #define WriteSwap16( PTR, X ) WriteLittle16( (PTR), (X) ) + #define WriteSwap32( PTR, X ) WriteLittle32( (PTR), (X) ) + #define WriteSwap48( PTR, X ) WriteLittle48( (PTR), (X) ) + #define WriteSwap64( PTR, X ) WriteLittle64( (PTR), (X) ) +#else + #define ReadSwap16( PTR ) ReadBig16( (PTR) ) + #define ReadSwap32( PTR ) ReadBig32( (PTR) ) + #define ReadSwap48( PTR ) ReadBig48( (PTR) ) + #define ReadSwap64( PTR ) ReadBig64( (PTR) ) + + #define WriteSwap16( PTR, X ) WriteBig16( (PTR), (X) ) + #define WriteSwap32( PTR, X ) WriteBig32( (PTR), (X) ) + #define WriteSwap48( PTR, X ) WriteBig48( (PTR), (X) ) + #define WriteSwap64( PTR, X ) WriteBig64( (PTR), (X) ) +#endif + +// Memory swaps +#if( !defined( TARGET_RT_LITTLE_ENDIAN ) ) + #define HostToBig16Mem( SRC, LEN, DST ) do {} while( 0 ) + #define BigToHost16Mem( SRC, LEN, DST ) do {} while( 0 ) + + #define LittleToHost16Mem( SRC, LEN, DST ) Swap16Mem( (SRC), (LEN), (DST) ) + #define LittleToHost16Mem( SRC, LEN, DST ) Swap16Mem( (SRC), (LEN), (DST) ) +#else + #define HostToBig16Mem( SRC, LEN, DST ) Swap16Mem( (SRC), (LEN), (DST) ) + #define BigToHost16Mem( SRC, LEN, DST ) Swap16Mem( (SRC), (LEN), (DST) ) + + #define HostToLittle16Mem( SRC, LEN, DST ) do {} while( 0 ) + #define LittleToHost16Mem( SRC, LEN, DST ) do {} while( 0 ) +#endif + +// Unconditional endian swaps +#if !defined (Swap16) +#define Swap16( X ) \ + ( (uint16_t)( \ + ( ( ( (uint16_t)(X) ) << 8 ) & UINT16_C( 0xFF00 ) ) | \ + ( ( ( (uint16_t)(X) ) >> 8 ) & UINT16_C( 0x00FF ) ) ) ) +#endif + +#if !defined (Swap32) +#define Swap32( X ) \ + ( (uint32_t)( \ + ( ( ( (uint32_t)(X) ) << 24 ) & UINT32_C( 0xFF000000 ) ) | \ + ( ( ( (uint32_t)(X) ) << 8 ) & UINT32_C( 0x00FF0000 ) ) | \ + ( ( ( (uint32_t)(X) ) >> 8 ) & UINT32_C( 0x0000FF00 ) ) | \ + ( ( ( (uint32_t)(X) ) >> 24 ) & UINT32_C( 0x000000FF ) ) ) ) +#endif + +#if !defined (Swap64) +#define Swap64( X ) \ + ( (uint64_t)( \ + ( ( ( (uint64_t)(X) ) << 56 ) & UINT64_C( 0xFF00000000000000 ) ) | \ + ( ( ( (uint64_t)(X) ) << 40 ) & UINT64_C( 0x00FF000000000000 ) ) | \ + ( ( ( (uint64_t)(X) ) << 24 ) & UINT64_C( 0x0000FF0000000000 ) ) | \ + ( ( ( (uint64_t)(X) ) << 8 ) & UINT64_C( 0x000000FF00000000 ) ) | \ + ( ( ( (uint64_t)(X) ) >> 8 ) & UINT64_C( 0x00000000FF000000 ) ) | \ + ( ( ( (uint64_t)(X) ) >> 24 ) & UINT64_C( 0x0000000000FF0000 ) ) | \ + ( ( ( (uint64_t)(X) ) >> 40 ) & UINT64_C( 0x000000000000FF00 ) ) | \ + ( ( ( (uint64_t)(X) ) >> 56 ) & UINT64_C( 0x00000000000000FF ) ) ) ) +#endif + +// Host<->Network/Big endian swaps +#if( !defined( TARGET_RT_LITTLE_ENDIAN ) ) + #define hton16( X ) (X) + #define ntoh16( X ) (X) + + #define hton32( X ) (X) + #define ntoh32( X ) (X) + + #define hton64( X ) (X) + #define ntoh64( X ) (X) +#else + #define hton16( X ) Swap16( X ) + #define ntoh16( X ) Swap16( X ) + + #define hton32( X ) Swap32( X ) + #define ntoh32( X ) Swap32( X ) + + #define hton64( X ) Swap64( X ) + #define ntoh64( X ) Swap64( X ) +#endif + + #define htons( X ) hton16( X ) + #define ntohs( X ) ntoh16( X ) + + #define htonl( X ) hton32( X ) + #define ntohl( X ) ntoh32( X ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function BitArray + @abstract Macros for working with bit arrays. + @discussion + + This treats bit numbers starting from the left so bit 0 is 0x80 in byte 0, bit 1 is 0x40 in bit 0, + bit 8 is 0x80 in byte 1, etc. For example, the following ASCII art shows how the bits are arranged: + + 1 1 1 1 1 1 1 1 1 1 2 2 2 2 + Bit 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | x |x | x x| = 0x20 0x80 0x41 (bits 2, 8, 17, and 23). + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + Byte 0 1 2 +*/ +#define BitArray_MinBytes( ARRAY, N_BYTES ) memrlen( (ARRAY), (N_BYTES) ) +#define BitArray_MaxBytes( BITS ) ( ( (BITS) + 7 ) / 8 ) +#define BitArray_MaxBits( ARRAY_BYTES ) ( (ARRAY_BYTES) * 8 ) +#define BitArray_Clear( ARRAY_PTR, ARRAY_BYTES ) memset( (ARRAY_PTR), 0, (ARRAY_BYTES) ); +#define BitArray_GetBit( PTR, LEN, BIT ) \ + ( ( (BIT) < BitArray_MaxBits( (LEN) ) ) && ( (PTR)[ (BIT) / 8 ] & ( 1 << ( 7 - ( (BIT) & 7 ) ) ) ) ) +#define BitArray_SetBit( ARRAY, BIT ) ( (ARRAY)[ (BIT) / 8 ] |= ( 1 << ( 7 - ( (BIT) & 7 ) ) ) ) +#define BitArray_ClearBit( ARRAY, BIT ) ( (ARRAY)[ (BIT) / 8 ] &= ~( 1 << ( 7 - ( (BIT) & 7 ) ) ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @group BitRotates + @abstract Rotates X COUNT bits to the left or right. +*/ +#define ROTL( X, N, SIZE ) ( ( (X) << (N) ) | ( (X) >> ( (SIZE) - N ) ) ) +#define ROTR( X, N, SIZE ) ( ( (X) >> (N) ) | ( (X) << ( (SIZE) - N ) ) ) + +#define ROTL32( X, N ) ROTL( (X), (N), 32 ) +#define ROTR32( X, N ) ROTR( (X), (N), 32 ) + +#define ROTL64( X, N ) ROTL( (X), (N), 64 ) +#define ROTR64( X, N ) ROTR( (X), (N), 64 ) + +#define RotateBitsLeft( X, N ) ROTL( (X), (N), sizeof( (X) ) * 8 ) +#define RotateBitsRight( X, N ) ROTR( (X), (N), sizeof( (X) ) * 8 ) + +// ==== Macros for minimum-width integer constants ==== +#if( !defined( INT8_C ) ) + #define INT8_C( value ) value +#endif + +#if( !defined( INT16_C ) ) + #define INT16_C( value ) value +#endif + +#if( !defined( INT32_C ) ) + #define INT32_C( value ) value +#endif + +#define INT64_C_safe( value ) INT64_C( value ) +#if( !defined( INT64_C ) ) + #if( defined( _MSC_VER ) ) + #define INT64_C( value ) value ## i64 + #else + #define INT64_C( value ) value ## LL + #endif +#endif + +#define UINT8_C_safe( value ) UINT8_C( value ) +#if( !defined( UINT8_C ) ) + #define UINT8_C( value ) value ## U +#endif + +#define UINT16_C_safe( value ) UINT16_C( value ) +#if( !defined( UINT16_C ) ) + #define UINT16_C( value ) value ## U +#endif + +#define UINT32_C_safe( value ) UINT32_C( value ) +#if( !defined( UINT32_C ) ) + #define UINT32_C( value ) value ## U +#endif + +#define UINT64_C_safe( value ) UINT64_C( value ) +#if( !defined( UINT64_C ) ) + #if( defined( _MSC_VER ) ) + #define UINT64_C( value ) value ## UI64 + #else + #define UINT64_C( value ) value ## ULL + #endif +#endif + +// ==== SOCKET MACROS ==== +#define IsValidSocket( X ) ( ( X ) >= 0 ) + +/* Suppress unused parameter warning */ +#ifndef UNUSED_PARAMETER +#define UNUSED_PARAMETER(x) ( (void)(x) ) +#endif + +/* Suppress unused variable warning */ +#ifndef UNUSED_VARIABLE +#define UNUSED_VARIABLE(x) ( (void)(x) ) +#endif + +/* Suppress unused variable warning occurring due to an assert which is disabled in release mode */ +#ifndef REFERENCE_DEBUG_ONLY_VARIABLE +#define REFERENCE_DEBUG_ONLY_VARIABLE(x) ( (void)(x) ) +#endif + + +/** + ****************************************************************************** + * Convert a nibble into a hex character + * + * @param[in] nibble The value of the nibble in the lower 4 bits + * + * @return The hex character corresponding to the nibble + */ +static inline ALWAYS_INLINE char nibble_to_hexchar( uint8_t nibble ) +{ + if (nibble > 9) + { + return (char)('A' + (nibble - 10)); + } + else + { + return (char) ('0' + nibble); + } +} + + +/** + ****************************************************************************** + * Convert a nibble into a hex character + * + * @param[in] nibble The value of the nibble in the lower 4 bits + * + * @return The hex character corresponding to the nibble + */ +static inline ALWAYS_INLINE char hexchar_to_nibble( char hexchar, uint8_t* nibble ) +{ + if ( ( hexchar >= '0' ) && ( hexchar <= '9' ) ) + { + *nibble = (uint8_t)( hexchar - '0' ); + return 0; + } + else if ( ( hexchar >= 'A' ) && ( hexchar <= 'F' ) ) + { + *nibble = (uint8_t) ( hexchar - 'A' + 10 ); + return 0; + } + else if ( ( hexchar >= 'a' ) && ( hexchar <= 'f' ) ) + { + *nibble = (uint8_t) ( hexchar - 'a' + 10 ); + return 0; + } + return -1; +} + +#ifdef __cplusplus +} /*"C" */ +#endif + +#endif // __MX_COMMON_H__ + diff --git a/src/helper/mx_debug.h b/src/helper/mx_debug.h new file mode 100644 index 0000000..199fbb1 --- /dev/null +++ b/src/helper/mx_debug.h @@ -0,0 +1,485 @@ +/** + ****************************************************************************** + * @file mx_debug.h + * @author William Xu + * @version V1.0.0 + * @date 18-Apr-2018 + * @brief This header contains defines, macros, and functions to aid in + * debugging the project. + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef __MX_DEBUG_H__ +#define __MX_DEBUG_H__ + +#include "mx_opt.h" +#include "mx_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ==== LOGGING ==== +#ifdef __GNUC__ +#define SHORT_FILE strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__ +#else +#define SHORT_FILE strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__ +#endif + +#define YesOrNo(x) (x ? "YES" : "NO") + +#ifdef MX_DEBUG +#ifndef MX_DISABLE_STDIO + #define custom_log(N, M, ...) do {printf("[%s: %s:%4d] " M "\r\n", N, SHORT_FILE, __LINE__, ##__VA_ARGS__);}while(0==1) + #define custom_print(M, ...) do {printf( M, ##__VA_ARGS__);}while(0==1) + + #ifndef MX_ASSERT_INFO_DISABLE + #define debug_print_assert(A,B,C,D,E,F) do {printf("[MX:%s:%s:%4d] **ASSERT** %s""\r\n", D, F, E, (C!=NULL) ? C : "" );}while(0==1) + #else + #define debug_print_assert(A,B,C,D,E,F) + #endif + + #ifdef TRACE + #define custom_log_trace(N) do {printf("[%s: [TRACE] %s] %s()\r\n", N, SHORT_FILE, __PRETTY_FUNCTION__);}while(0==1) + #else // !TRACE + #define custom_log_trace(N) + #endif // TRACE +#else + #define custom_log(N, M, ...) + #define custom_print(M, ...) + #define custom_log_trace(N) + + #define debug_print_assert(A,B,C,D,E,F) +#endif //MX_DISABLE_STDIO +#else // DEBUG = 0 + // IF !DEBUG, make the logs NO-OP + #define custom_log(N, M, ...) + #define custom_print(M, ...) + #define custom_log_trace(N) + + #define debug_print_assert(A,B,C,D,E,F) +#endif // DEBUG + + +/** Debug level: ALL messages*/ +#define MX_DEBUG_LEVEL_ALL 0x00 +/** Debug level: Warnings. bad checksums, dropped packets, ... */ +#define MX_DEBUG_LEVEL_WARNING 0x01 +/** Debug level: Serious. memory allocation failures, ... */ +#define MX_DEBUG_LEVEL_SERIOUS 0x02 +/** Debug level: Severe */ +#define MX_DEBUG_LEVEL_SEVERE 0x03 + +#define MX_DEBUG_MASK_LEVEL 0x03 +/* compatibility define only */ +#define MX_DEBUG_LEVEL_OFF MX_DEBUG_LEVEL_ALL + +/** flag for LWIP_DEBUGF to enable that debug message */ +#define MX_DEBUG_ON 0x80U +/** flag for LWIP_DEBUGF to disable that debug message */ +#define MX_DEBUG_OFF 0x00U + +#ifdef MX_DEBUG +#define MX_LOG(D, T, M, ...) do { \ + if ( ((D) & MX_DEBUG_ON) && \ + ((D) & MX_DEBUG_TYPES_ON) && \ + ((int16_t)((D) & MX_DEBUG_MASK_LEVEL) >= MX_DEBUG_MIN_LEVEL)) { \ + custom_log(T, M, ##__VA_ARGS__); \ + } \ + } while(0) +#define MX_PRINT(D, M, ...) do { \ + if ( ((D) & MX_DEBUG_ON) && \ + ((D) & MX_DEBUG_TYPES_ON) && \ + ((int16_t)((D) & MX_DEBUG_MASK_LEVEL) >= MX_DEBUG_MIN_LEVEL)) { \ + custom_print(M, ##__VA_ARGS__); \ + } \ + } while(0) +#define MX_LOG_TRACE(T) custom_log_trace(T) +#else +#define MX_LOG(D, T, M, ...) +#define MX_LOG_TRACE(T) +#endif + +// ==== BRANCH PREDICTION & EXPRESSION EVALUATION ==== +#if( !defined( unlikely ) ) + //#define unlikely( EXPRESSSION ) __builtin_expect( !!(EXPRESSSION), 0 ) + #define unlikely( EXPRESSSION ) !!(EXPRESSSION) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check + @abstract Check that an expression is true (non-zero). + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method. + + Code inside check() statements is not compiled into production builds. +*/ + +#if( !defined( check ) ) + #define check( X ) \ + do \ + { \ + if( unlikely( !(X) ) ) \ + { \ + debug_print_assert( 0, #X, NULL, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__ ); \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_string + @abstract Check that an expression is true (non-zero) with an explanation. + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method. + + Code inside check() statements is not compiled into production builds. +*/ + +#if( !defined( check_string ) ) + #define check_string( X, STR ) \ + do \ + { \ + if( unlikely( !(X) ) ) \ + { \ + debug_print_assert( 0, #X, STR, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__ ); \ + MX_ASSERTION_FAIL_ACTION(); \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require + @abstract Requires that an expression evaluate to true. + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then jumps to a label. +*/ + +#if( !defined( require ) ) + #define require( X, LABEL ) \ + do \ + { \ + if( unlikely( !(X) ) ) \ + { \ + debug_print_assert( 0, #X, NULL, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__ ); \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_string + @abstract Requires that an expression evaluate to true with an explanation. + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method then jumps to a label. +*/ + +#if( !defined( require_string ) ) + #define require_string( X, LABEL, STR ) \ + do \ + { \ + if( unlikely( !(X) ) ) \ + { \ + debug_print_assert( 0, #X, STR, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__ ); \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_quiet + @abstract Requires that an expression evaluate to true. + @discussion + + If expression evalulates to false, this jumps to a label. No debugging information is printed. +*/ + +#if( !defined( require_quiet ) ) + #define require_quiet( X, LABEL ) \ + do \ + { \ + if( unlikely( !(X) ) ) \ + { \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr + @abstract Require that an error code is noErr (0). + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then jumps to a label. +*/ + +#if( !defined( require_noerr ) ) + #define require_noerr( ERR, LABEL ) \ + do \ + { \ + OSStatus localErr; \ + \ + localErr = (OSStatus)(ERR); \ + if( unlikely( localErr != 0 ) ) \ + { \ + debug_print_assert( localErr, NULL, NULL, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__ ); \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_string + @abstract Require that an error code is noErr (0). + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.), and a custom explanation string using the default debugging output method using the + default debugging output method then jumps to a label. +*/ + +#if( !defined( require_noerr_string ) ) + #define require_noerr_string( ERR, LABEL, STR ) \ + do \ + { \ + OSStatus localErr; \ + \ + localErr = (OSStatus)(ERR); \ + if( unlikely( localErr != 0 ) ) \ + { \ + debug_print_assert( localErr, NULL, STR, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__ ); \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_action_string + @abstract Require that an error code is noErr (0). + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.), and a custom explanation string using the default debugging output method using the + default debugging output method then executes an action and jumps to a label. +*/ + +#if( !defined( require_noerr_action_string ) ) + #define require_noerr_action_string( ERR, LABEL, ACTION, STR ) \ + do \ + { \ + OSStatus localErr; \ + \ + localErr = (OSStatus)(ERR); \ + if( unlikely( localErr != 0 ) ) \ + { \ + debug_print_assert( localErr, NULL, STR, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_quiet + @abstract Require that an error code is noErr (0). + @discussion + + If the error code is non-0, this jumps to a label. No debugging information is printed. +*/ + +#if( !defined( require_noerr_quiet ) ) + #define require_noerr_quiet( ERR, LABEL ) \ + do \ + { \ + if( unlikely( (ERR) != 0 ) ) \ + { \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_action + @abstract Require that an error code is noErr (0) with an action to execute otherwise. + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then executes an action and jumps to a label. +*/ + +#if( !defined( require_noerr_action ) ) + #define require_noerr_action( ERR, LABEL, ACTION ) \ + do \ + { \ + OSStatus localErr; \ + \ + localErr = (OSStatus)(ERR); \ + if( unlikely( localErr != 0 ) ) \ + { \ + debug_print_assert( localErr, NULL, NULL, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_action_quiet + @abstract Require that an error code is noErr (0) with an action to execute otherwise. + @discussion + + If the error code is non-0, this executes an action and jumps to a label. No debugging information is printed. +*/ + +#if( !defined( require_noerr_action_quiet ) ) + #define require_noerr_action_quiet( ERR, LABEL, ACTION ) \ + do \ + { \ + if( unlikely( (ERR) != 0 ) ) \ + { \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_action + @abstract Requires that an expression evaluate to true with an action to execute otherwise. + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then executes an action and jumps to a label. +*/ + +#if( !defined( require_action ) ) + #define require_action( X, LABEL, ACTION ) \ + do \ + { \ + if( unlikely( !(X) ) ) \ + { \ + debug_print_assert( 0, #X, NULL, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_action_string + @abstract Requires that an expression evaluate to true with an explanation and action to execute otherwise. + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method then executes an + action and jumps to a label. +*/ + +#if( !defined( require_action_string ) ) + #define require_action_string( X, LABEL, ACTION, STR ) \ + do \ + { \ + if( unlikely( !(X) ) ) \ + { \ + debug_print_assert( 0, #X, STR, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_action_quiet + @abstract Requires that an expression evaluate to true with an action to execute otherwise. + @discussion + + If expression evalulates to false, this executes an action and jumps to a label. No debugging information is printed. +*/ + +#if( !defined( require_action_quiet ) ) + #define require_action_quiet( X, LABEL, ACTION ) \ + do \ + { \ + if( unlikely( !(X) ) ) \ + { \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 1==0 ) +#endif + +// ==== ERROR MAPPING ==== +#define global_value_errno( VALUE ) ( errno ? errno : kUnknownErr ) + +#define map_global_value_errno( TEST, VALUE ) ( (TEST) ? 0 : global_value_errno(VALUE) ) +#define map_global_noerr_errno( ERR ) ( !(ERR) ? 0 : global_value_errno(ERR) ) +#define map_fd_creation_errno( FD ) ( IsValidFD( FD ) ? 0 : global_value_errno( FD ) ) +#define map_noerr_errno( ERR ) map_global_noerr_errno( (ERR) ) + +#define socket_errno( SOCK ) ( errno ? errno : kUnknownErr ) +#define socket_value_errno( SOCK, VALUE ) socket_errno( SOCK ) +#define map_socket_value_errno( SOCK, TEST, VALUE ) ( (TEST) ? 0 : socket_value_errno( (SOCK), (VALUE) ) ) +#define map_socket_noerr_errno( SOCK, ERR ) ( !(ERR) ? 0 : socket_errno( (SOCK) ) ) + + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_ptr_overlap + @abstract Checks that two ptrs do not overlap. +*/ + +#define check_ptr_overlap( P1, P1_SIZE, P2, P2_SIZE ) \ + do \ + { \ + check( !( ( (uintptr_t)(P1) >= (uintptr_t)(P2) ) && \ + ( (uintptr_t)(P1) < ( ( (uintptr_t)(P2) ) + (P2_SIZE) ) ) ) ); \ + check( !( ( (uintptr_t)(P2) >= (uintptr_t)(P1) ) && \ + ( (uintptr_t)(P2) < ( ( (uintptr_t)(P1) ) + (P1_SIZE) ) ) ) ); \ + \ + } while( 1==0 ) + +#define IsValidFD( X ) ( ( X ) >= 0 ) + +#ifdef __cplusplus +} /*"C" */ +#endif + +#endif // __MX_DEBUG_H__ + diff --git a/src/helper/mx_opt.h b/src/helper/mx_opt.h new file mode 100644 index 0000000..65616cc --- /dev/null +++ b/src/helper/mx_opt.h @@ -0,0 +1,101 @@ +/** + ****************************************************************************** + * @file mico_opt.h + * @author William Xu + * @version V1.0.0 + * @date 18-Apr-2018 + * @brief This file provide default configurations. + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef __MX_OPT_H__ +#define __MX_OPT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************** + * AliCloud SDS service + ******************************************************************************/ + +#if !defined ALISDS_ATTR_VAL_MAX_LEN +#define ALISDS_ATTR_VAL_MAX_LEN (1024) /**< Max possible attr value length, reduce to save memory */ +#endif + +#if !defined ALISDS_ATTR_NAME_MAX_LEN +#define ALISDS_ATTR_NAME_MAX_LEN (64) /**< Max possible attr name length, reduce to save memory */ +#endif + +/****************************************************************************** + * AliCloud SDS service + ******************************************************************************/ + + +/****************************************************************************** + * AT command parser + ******************************************************************************/ + +#if !defined AT_BUFFER_SIZE +#define AT_BUFFER_SIZE (512) /**< Max buffer used for AT command analyser, no used for recv raw data */ +#endif + +/****************************************************************************** + * MX HAL + ******************************************************************************/ + +#if !defined MX_SERIAL_RX_BUF_SIZE +#define MX_SERIAL_RX_BUF_SIZE (1024) /**< AT command receiver serial port input buffer size */ +#endif + +#if !defined MX_CLI_RX_BUF_SIZE +#define MX_CLI_RX_BUF_SIZE (128) /**< Cli command receiver serial port input buffer size */ +#endif + +/****************************************************************************** + * MX Debug Enabler + ******************************************************************************/ + +#if !defined MX_DEBUG_MIN_LEVEL +#define MX_DEBUG_MIN_LEVEL MX_DEBUG_LEVEL_ALL +#endif + +#if !defined MX_DEBUG_TYPES_ON +#define MX_DEBUG_TYPES_ON MX_DEBUG_ON +#endif + + +/****************************************************************************** + * Debug and Log + ******************************************************************************/ + +#if !defined CONFIG_APP_DEBUG +#define CONFIG_APP_DEBUG MX_DEBUG_ON +#endif + +#if !defined CONFIG_CLOUD_DEBUG +#define CONFIG_CLOUD_DEBUG MX_DEBUG_ON +#endif + + +#ifdef __cplusplus +} /*extern "C" */ +#endif + +#endif //__MX_OPT_H diff --git a/src/helper/mx_toolchain.h b/src/helper/mx_toolchain.h new file mode 100644 index 0000000..72828e1 --- /dev/null +++ b/src/helper/mx_toolchain.h @@ -0,0 +1,437 @@ +/** + ****************************************************************************** + * @file mx_toolchain.h + * @author William Xu + * @version V1.0.0 + * @date 18-Apr-2018 + * @brief This file provide toolchain independent macros + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef _MX_TOOLCHAIN_H_ +#define _MX_TOOLCHAIN_H_ + +/** MX_CONCAT + * Concatenate tokens together + * + * @note + * Expands tokens before concatenation + * + * @code + * // Creates a unique label based on the line number + * int MX_CONCAT(UNIQUE_LABEL_, __LINE__) = 1; + * @endcode + */ +#define MX_CONCAT(a, b) MX_CONCAT_(a, b) +#define MX_CONCAT_(a, b) a##b + +/** MX_STRINGIFY + * Converts tokens into strings + * + * @note + * Expands tokens before stringification + * + * @code + * // Creates a string based on the parameters + * const char *c = MX_STRINGIFY(This is a ridiculous way to create a string) + * @endcode + */ +#define MX_STRINGIFY(a) MX_STRINGIFY_(a) +#define MX_STRINGIFY_(a) #a + +// Warning for unsupported compilers +#if !defined(__GNUC__) /* GCC */ \ + && !defined(__CC_ARM) /* ARMCC */ \ + && !defined(__clang__) /* LLVM/Clang */ \ + && !defined(__ICCARM__) /* IAR */ \ + && !defined(__TI_ARM__) /* TI */ +#warning "This compiler is not yet supported." +#endif + + +// Attributes + +/** MX_PACKED + * Pack a structure, preventing any padding from being added between fields. + * + * @code + * #include "mx_toolchain.h" + * + * MX_PACKED(struct) foo { + * char x; + * int y; + * }; + * @endcode + */ +#ifndef MX_PACKED +#if defined(__ICCARM__) +#define MX_PACKED(struct) __packed struct +#else +#define MX_PACKED(struct) struct __attribute__((packed)) +#endif +#endif + +/** MX_ALIGN(N) + * Declare a variable to be aligned on an N-byte boundary. + * + * @note + * IAR does not support alignment greater than word size on the stack + * + * @code + * #include "mx_toolchain.h" + * + * MX_ALIGN(16) char a; + * @endcode + */ +#ifndef MX_ALIGN +#if defined(__ICCARM__) +#define MX_ALIGN(N) _Pragma(MX_STRINGIFY(data_alignment=N)) +#else +#define MX_ALIGN(N) __attribute__((aligned(N))) +#endif +#endif + +/** MX_UNUSED + * Declare a function argument to be unused, suppressing compiler warnings + * + * @code + * #include "mx_toolchain.h" + * + * void foo(MX_UNUSED int arg) { + * + * } + * @endcode + */ +#ifndef MX_UNUSED +#if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_UNUSED __attribute__((__unused__)) +#else +#define MX_UNUSED +#endif +#endif + +/** MX_USED + * Inform the compiler that a static variable is to be retained in the object file, even if it is unreferenced. + * + * @code + * #include "mx_toolchain.h" + * + * MX_USED int foo; + * + * @endcode + */ +#ifndef MX_USED +#if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_USED __attribute__((used)) +#elif defined(__ICCARM__) +#define MX_USED __root +#else +#define MX_USED +#endif +#endif + +/** MX_WEAK + * Mark a function as being weak. + * + * @note + * Functions should only be marked as weak in the source file. The header file + * should contain a regular function declaration to insure the function is emitted. + * A function marked weak will not be emitted if an alternative non-weak + * implementation is defined. + * + * @note + * Weak functions are not friendly to making code re-usable, as they can only + * be overridden once (and if they are multiply overridden the linker will emit + * no warning). You should not normally use weak symbols as part of the API to + * re-usable modules. + * + * @code + * #include "mx_toolchain.h" + * + * MX_WEAK void foo() { + * // a weak implementation of foo that can be overriden by a definition + * // without __weak + * } + * @endcode + */ +#ifndef MX_WEAK +#if defined(__ICCARM__) +#define MX_WEAK __weak +#else +#define MX_WEAK __attribute__((weak)) +#endif +#endif + +/** MX_PURE + * Hint to the compiler that a function depends only on parameters + * + * @code + * #include "mx_toolchain.h" + * + * MX_PURE int foo(int arg){ + * // no access to global variables + * } + * @endcode + */ +#ifndef MX_PURE +#if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_PURE __attribute__((const)) +#else +#define MX_PURE +#endif +#endif + +/** MX_NOINLINE + * Declare a function that must not be inlined. + * + * @code + * #include "mx_toolchain.h" + * + * MX_NOINLINE void foo() { + * + * } + * @endcode + */ +#ifndef MX_NOINLINE +#if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_NOINLINE __attribute__((noinline)) +#elif defined(__ICCARM__) +#define MX_NOINLINE _Pragma("inline=never") +#else +#define MX_NOINLINE +#endif +#endif + +/** MX_FORCEINLINE + * Declare a function that must always be inlined. Failure to inline + * such a function will result in an error. + * + * @code + * #include "mx_toolchain.h" + * + * MX_FORCEINLINE void foo() { + * + * } + * @endcode + */ +#ifndef MX_FORCEINLINE +#if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_FORCEINLINE static inline __attribute__((always_inline)) +#elif defined(__ICCARM__) +#define MX_FORCEINLINE _Pragma("inline=forced") static +#else +#define MX_FORCEINLINE static inline +#endif +#endif + +/** MX_NORETURN + * Declare a function that will never return. + * + * @code + * #include "mx_toolchain.h" + * + * MX_NORETURN void foo() { + * // must never return + * while (1) {} + * } + * @endcode + */ +#ifndef MX_NORETURN +#if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_NORETURN __attribute__((noreturn)) +#elif defined(__ICCARM__) +#define MX_NORETURN __noreturn +#else +#define MX_NORETURN +#endif +#endif + +/** MX_UNREACHABLE + * An unreachable statement. If the statement is reached, + * behaviour is undefined. Useful in situations where the compiler + * cannot deduce the unreachability of code. + * + * @code + * #include "mx_toolchain.h" + * + * void foo(int arg) { + * switch (arg) { + * case 1: return 1; + * case 2: return 2; + * ... + * } + * MX_UNREACHABLE; + * } + * @endcode + */ +#ifndef MX_UNREACHABLE +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_UNREACHABLE __builtin_unreachable() +#else +#define MX_UNREACHABLE while (1) +#endif +#endif + +/** MX_DEPRECATED("message string") + * Mark a function declaration as deprecated, if it used then a warning will be + * issued by the compiler possibly including the provided message. Note that not + * all compilers are able to display the message. + * + * @code + * #include "mx_toolchain.h" + * + * MX_DEPRECATED("don't foo any more, bar instead") + * void foo(int arg); + * @endcode + */ +#ifndef MX_DEPRECATED +#if defined(__CC_ARM) +#define MX_DEPRECATED(M) __attribute__((deprecated)) +#elif defined(__GNUC__) || defined(__clang__) || defined(__TI_ARM__) +#define MX_DEPRECATED(M) __attribute__((deprecated(M))) +#else +#define MX_DEPRECATED(M) +#endif +#endif + +/** MX_DEPRECATED_SINCE("version", "message string") + * Mark a function declaration as deprecated, noting that the declaration was + * deprecated on the specified version. If the function is used then a warning + * will be issued by the compiler possibly including the provided message. + * Note that not all compilers are able to display this message. + * + * @code + * #include "mx_toolchain.h" + * + * MX_DEPRECATED_SINCE("mbed-os-5.1", "don't foo any more, bar instead") + * void foo(int arg); + * @endcode + */ +#define MX_DEPRECATED_SINCE(D, M) MX_DEPRECATED(M " [since " D "]") + +/** MX_CALLER_ADDR() + * Returns the caller of the current function. + * + * @note + * This macro is only implemented for GCC and ARMCC. + * + * @code + * #include "mx_toolchain.h" + * + * printf("This function was called from %p", MX_CALLER_ADDR()); + * @endcode + * + * @return Address of the calling function + */ +#ifndef MX_CALLER_ADDR +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_CALLER_ADDR() __builtin_extract_return_addr(__builtin_return_address(0)) +#elif defined(__CC_ARM) +#define MX_CALLER_ADDR() __builtin_return_address(0) +#else +#define MX_CALLER_ADDR() (NULL) +#endif +#endif + +#ifndef MX_SECTION +#if (defined(__GNUC__) || defined(__clang__)) || defined(__CC_ARM) || defined(__TI_ARM__) || defined(__TI_ARM__) +#define MX_SECTION(name) __attribute__ ((section (name))) +#elif defined(__ICCARM__) +#define MX_SECTION(name) _Pragma(MX_STRINGIFY(location=name)) +#else +#error "Missing MX_SECTION directive" +#endif +#endif + +/** + * Macro expanding to a string literal of the enclosing function name. + * + * The string returned takes into account language specificity and yield human + * readable content. + * + * As an example, if the macro is used within a C++ function then the string + * literal containing the function name will contain the complete signature of + * the function - including template parameters - and namespace qualifications. + */ +#ifndef MX_PRETTY_FUNCTION +#define MX_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#endif + +#ifndef MX_PRINTF +#if defined(__GNUC__) || defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_PRINTF(format_idx, first_param_idx) __attribute__ ((__format__(__printf__, format_idx, first_param_idx))) +#else +#define MX_PRINTF(format_idx, first_param_idx) +#endif +#endif + +#ifndef MX_PRINTF_METHOD +#if defined(__GNUC__) || defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_PRINTF_METHOD(format_idx, first_param_idx) __attribute__ ((__format__(__printf__, format_idx+1, first_param_idx+1))) +#else +#define MX_PRINTF_METHOD(format_idx, first_param_idx) +#endif +#endif + +#ifndef MX_SCANF +#if defined(__GNUC__) || defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_SCANF(format_idx, first_param_idx) __attribute__ ((__format__(__scanf__, format_idx, first_param_idx))) +#else +#define MX_SCANF(format_idx, first_param_idx) +#endif +#endif + +#ifndef MX_SCANF_METHOD +#if defined(__GNUC__) || defined(__CC_ARM) || defined(__TI_ARM__) +#define MX_SCANF_METHOD(format_idx, first_param_idx) __attribute__ ((__format__(__scanf__, format_idx+1, first_param_idx+1))) +#else +#define MX_SCANF_METHOD(format_idx, first_param_idx) +#endif +#endif + +// FILEHANDLE declaration +#if defined(TOOLCHAIN_ARM) +#include +#endif + +#ifndef FILEHANDLE +typedef int FILEHANDLE; +#endif + +// Backwards compatibility +#ifndef WEAK +#define WEAK MX_WEAK +#endif + +#ifndef PACKED +#define PACKED MX_PACKED() +#endif + +#ifndef EXTERN +#define EXTERN extern +#endif + +#ifndef ALWAYS_INLINE +#define ALWAYS_INLINE __attribute__((always_inline)) +#endif + +#endif + +/** @}*/ +/** @}*/ diff --git a/src/helper/mx_utils/mx_ringbuffer.c b/src/helper/mx_utils/mx_ringbuffer.c new file mode 100644 index 0000000..5dac041 --- /dev/null +++ b/src/helper/mx_utils/mx_ringbuffer.c @@ -0,0 +1,85 @@ +#include "mx_ringbuffer.h" + +/** + * \brief Ringbuffer init + */ +int32_t ringbuffer_init(struct ringbuffer* const rb, void* buf, uint32_t size) +{ + mx_status err = kNoErr; + require_action(rb && buf && size, exit, err = kParamErr); + + /* + * buf size must be aligned to power of 2 + */ + require_action((size & (size - 1)) == 0, exit, err = kParamErr); + + /* size - 1 is faster in calculation */ + rb->size = size - 1; + rb->read_index = 0; + rb->write_index = rb->read_index; + rb->buf = (uint8_t*)buf; + +exit: + return err; +} + +/** + * \brief Get one byte from ringbuffer + * + */ +int32_t ringbuffer_get(struct ringbuffer* const rb, uint8_t* data) +{ + mx_status err = kNotFoundErr; + require_action(rb && data, exit, err = kParamErr); + + if (rb->write_index != rb->read_index) { + *data = rb->buf[rb->read_index & rb->size]; + rb->read_index++; + return kNoErr; + } + +exit: + return err; +} + +/** + * \brief Put one byte to ringbuffer + * + */ +int32_t ringbuffer_put(struct ringbuffer* const rb, uint8_t data) +{ + mx_status err = kNoErr; + require_action(rb, exit, err = kParamErr); + + rb->buf[rb->write_index & rb->size] = data; + + /* + * buffer full strategy: new data will overwrite the oldest data in + * the buffer + */ + if ((rb->write_index - rb->read_index) > rb->size) { + rb->read_index = rb->write_index - rb->size; + } + + rb->write_index++; + +exit: + return err; +} + +/** + * \brief Return the element number of ringbuffer + */ +uint32_t ringbuffer_num(const struct ringbuffer* const rb) +{ + return rb->write_index - rb->read_index; +} + +/** + * \brief Flush ringbuffer + */ +uint32_t ringbuffer_flush(struct ringbuffer* const rb) +{ + rb->read_index = rb->write_index; + return kNoErr; +} diff --git a/src/helper/mx_utils/mx_ringbuffer.h b/src/helper/mx_utils/mx_ringbuffer.h new file mode 100644 index 0000000..5385bbb --- /dev/null +++ b/src/helper/mx_utils/mx_ringbuffer.h @@ -0,0 +1,107 @@ +/** + ****************************************************************************** + * @file mx_ringbuffer.c + * @author William Xu + * @version V1.0.0 + * @date 18-Apr-2018 + * @brief Ringbuffer utils used for UART data receive + ****************************************************************************** + * + * Copyright (c) 2009-2018 MXCHIP Co.,Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#ifndef _MX_RINGBUFFER_H_ +#define _MX_RINGBUFFER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mx_common.h" +#include "mx_debug.h" + +/** \addtogroup utils */ +/** @{*/ + +/** + * \brief Ring buffer element type + */ +struct ringbuffer { + uint8_t* buf; /** Buffer base address */ + uint32_t size; /** Buffer size */ + uint32_t read_index; /** Buffer read index */ + uint32_t write_index; /** Buffer write index */ +}; + +/** + * \brief Ring buffer init + * + * \param[in] rb The pointer to a ring buffer structure instance + * \param[in] buf Space to store the data + * \param[in] size The buffer length, must be aligned with power of 2 + * + * \return ERR_NONE on success, or an error code on failure. + */ +int32_t ringbuffer_init(struct ringbuffer* const rb, void* buf, uint32_t size); + +/** + * \brief Get one byte from ring buffer, the user needs to handle the concurrent + * access on buffer via put/get/flush + * + * \param[in] rb The pointer to a ring buffer structure instance + * \param[in] data One byte space to store the read data + * + * \return ERR_NONE on success, or an error code on failure. + */ +int32_t ringbuffer_get(struct ringbuffer* const rb, uint8_t* data); + +/** + * \brief Put one byte to ring buffer, the user needs to handle the concurrent access + * on buffer via put/get/flush + * + * \param[in] rb The pointer to a ring buffer structure instance + * \param[in] data One byte data to be put into ring buffer + * + * \return ERR_NONE on success, or an error code on failure. + */ +int32_t ringbuffer_put(struct ringbuffer* const rb, uint8_t data); + +/** + * \brief Return the element number of ring buffer + * + * \param[in] rb The pointer to a ring buffer structure instance + * + * \return The number of elements in ring buffer [0, rb->size] + */ +uint32_t ringbuffer_num(const struct ringbuffer* const rb); + +/** + * \brief Flush ring buffer, the user needs to handle the concurrent access on buffer + * via put/get/flush + * + * \param[in] rb The pointer to a ring buffer structure instance + * + * \return ERR_NONE on success, or an error code on failure. + */ +uint32_t ringbuffer_flush(struct ringbuffer* const rb); + +/**@}*/ + +#ifdef __cplusplus +} +#endif +#endif /* _MX_RINGBUFFER_H_ */