Просмотр исходного кода

Merge branch 'master' of https://gitee.com/openLuat/LuatOS

alienwalker 1 год назад
Родитель
Сommit
dbc6eba2ac

+ 7 - 4
components/multimedia/luat_audio_es8311.c

@@ -289,7 +289,7 @@ static uint8_t es8311_get_mute(luat_audio_codec_conf_t* conf){
 
 // voice_vol
 static uint8_t es8311_set_voice_vol(luat_audio_codec_conf_t* conf,uint8_t vol){
-    if(vol < 0 || vol > 100) return -1;
+    if(vol > 100) return -1;
 	es8311_write_reg(conf,ES8311_DAC_REG32,(uint8_t)(vol * 2550 / 1000));
 	return 0;
 }
@@ -301,7 +301,7 @@ static uint8_t es8311_get_voice_vol(luat_audio_codec_conf_t* conf){
 
 // mic_vol
 static uint8_t es8311_set_mic_vol(luat_audio_codec_conf_t* conf,uint8_t vol){
-    if(vol < 0 || vol > 100) return -1;
+    if(vol > 100) return -1;
 	es8311_write_reg(conf,ES8311_ADC_REG17,(uint8_t)(vol * 2550 / 1000));
 	return 0;
 }
@@ -438,6 +438,7 @@ static inline void es8311_reset(luat_audio_codec_conf_t* conf){
 static int es8311_codec_init(luat_audio_codec_conf_t* conf,uint8_t mode){
     luat_audio_power(conf->multimedia_id,1);
     luat_rtos_task_sleep(50);
+    luat_audio_conf_t* audio_conf = luat_audio_get_config(conf->multimedia_id);
     uint8_t temp1 = es8311_read_reg(conf,ES8311_CHD1_REGFD);
     uint8_t temp2 = es8311_read_reg(conf,ES8311_CHD2_REGFE);
     uint8_t temp3 = es8311_read_reg(conf,ES8311_CHVER_REGFF);
@@ -458,8 +459,10 @@ static int es8311_codec_init(luat_audio_codec_conf_t* conf,uint8_t mode){
 
     es8311_write_reg(conf,ES8311_SYSTEM_REG0B, 0x00);
     es8311_write_reg(conf,ES8311_SYSTEM_REG0C, 0x00);
-
-    es8311_write_reg(conf,ES8311_SYSTEM_REG10, (0x1C*ES8311_DAC_HP_ON) + (0x60*ES8311_VDDA_VOLTAGE) + 0x03);
+    if (audio_conf)
+        es8311_write_reg(conf,ES8311_SYSTEM_REG10, (0x1C*ES8311_DAC_HP_ON) + (0x60 * (audio_conf->voltage ? ES8311_VDDA_1V8 : ES8311_VDDA_3V3)) + 0x03);
+    else
+        es8311_write_reg(conf,ES8311_SYSTEM_REG10, (0x1C*ES8311_DAC_HP_ON) + (0x60 * ES8311_VDDA_VOLTAGE) + 0x03);
     es8311_write_reg(conf,ES8311_SYSTEM_REG11, 0x7F);	
 
     es8311_write_reg(conf,ES8311_CLK_MANAGER_REG01,0x3F + (ES8311_MCLK_SOURCE<<7));

+ 9 - 0
components/multimedia/luat_lib_multimedia_audio.c

@@ -697,6 +697,11 @@ static int l_audio_set_output_bus(lua_State *L) {
 			audio_conf->codec_conf.i2s_id = luaL_checknumber(L, -1);
 		}
 		lua_pop(L, 1);
+		lua_pushstring(L, "voltage");
+		if (LUA_TNUMBER == lua_gettable(L, 3)) {
+			audio_conf->voltage = luaL_checknumber(L, -1);
+		}
+		lua_pop(L, 1);
     }
     ret |= luat_audio_init(id, 0, 0);
     lua_pushboolean(L, !ret);
@@ -795,6 +800,10 @@ static const rotable_Reg_t reg_audio[] =
 	{ "BUS_I2S", 		ROREG_INT(LUAT_AUDIO_BUS_I2S)},
 	//@const BUS_SOFT_DAC number 硬件输出总线,软件模式DAC类型
 	{ "BUS_SOFT_DAC", 		ROREG_INT(LUAT_AUDIO_BUS_SOFT_DAC)},
+    //@const VOLTAGE_1800 number 可配置的codec工作电压,1.8V
+	{ "VOLTAGE_1800", 		ROREG_INT(LUAT_AUDIO_VOLTAGE_1800)},
+    //@const VOLTAGE_3300 number 可配置的codec工作电压,3.3V
+	{ "VOLTAGE_3300", 		ROREG_INT(LUAT_AUDIO_VOLTAGE_3300)},
 	{ NULL,            ROREG_INT(0)}
 };
 

+ 6 - 2
components/network/websocket/luat_lib_websocket.c

@@ -192,10 +192,11 @@ static int l_websocket_set_debug(lua_State *L)
 
 /*
 websocket客户端创建
-@api websocket.create(adapter, url, keepalive)
-@int 适配器序号, 只能是socket.ETH0, socket.STA, socket.AP,如果不填,会选择平台自带的方式,然后是最后一个注册的适配器
+@api websocket.create(adapter, url, keepalive, use_ipv6)
+@int 适配器序号, 参考socket库的常量,默认为nil,会选择平台自带的方式
 @string 连接字符串,参考usage
 @int 心跳间隔,默认60秒. 2024.4.28新增
+@boolean 是否使用ipv6,默认false. 2024.6.17新增
 @return userdata 若成功会返回websocket客户端实例,否则返回nil
 @usage
 -- 普通TCP链接
@@ -218,6 +219,9 @@ static int l_websocket_create(lua_State *L)
 	if (lua_isinteger(L, 3)) {
 		opts.keepalive = luaL_checkinteger(L, 3);
 	}
+	if (lua_type(L, 4) == LUA_TBOOLEAN) {
+		opts.use_ipv6 = lua_toboolean(L, 4);
+	}
 
 	luat_websocket_ctrl_t *websocket_ctrl = (luat_websocket_ctrl_t *)lua_newuserdata(L, sizeof(luat_websocket_ctrl_t));
 	if (!websocket_ctrl)

+ 3 - 0
components/network/websocket/luat_websocket.c

@@ -252,6 +252,9 @@ int luat_websocket_set_connopts(luat_websocket_ctrl_t *websocket_ctrl, luat_webs
 	if (opts->keepalive > 0) {
 		websocket_ctrl->keepalive = opts->keepalive;
 	}
+	if (opts->use_ipv6) {
+		network_connect_ipv6_domain(websocket_ctrl->netc, 1);
+	}
 	return 0;
 }
 

+ 1 - 0
components/network/websocket/luat_websocket.h

@@ -42,6 +42,7 @@ typedef struct luat_websocket_connopts
 {
 	const char *url;
 	uint16_t keepalive;
+	uint8_t use_ipv6;
 } luat_websocket_connopts_t;
 
 typedef struct luat_websocket_pkg

+ 10 - 1
components/nimble/src/luat_lib_nimble.c

@@ -296,6 +296,7 @@ nimble.mac(string.fromHex("1234567890AB"))
 static int l_nimble_mac(lua_State *L) {
     int rc = 0;
     uint8_t own_addr_type = 0;
+    uint8_t addr_val[6] = {0};
     if (lua_type(L, 1) == LUA_TSTRING) {
         size_t len = 0;
         const char* tmac = luaL_checklstring(L, 1, &len);
@@ -305,6 +306,14 @@ static int l_nimble_mac(lua_State *L) {
         }
         luat_nimble_mac_set(tmac);
     }
+    #ifdef TLS_CONFIG_CPU_XT804
+    if (1) {
+        extern int luat_nimble_mac_get(uint8_t* mac);
+        luat_nimble_mac_get(addr_val);
+        lua_pushlstring(L, (const char*)addr_val, 6);
+        return 1;
+    }
+    #endif
     rc = ble_hs_util_ensure_addr(0);
     if (rc != 0) {
         LLOGW("fail to fetch BLE MAC, rc %d", rc);
@@ -319,7 +328,7 @@ static int l_nimble_mac(lua_State *L) {
     }
 
     /* Printing ADDR */
-    uint8_t addr_val[6] = {0};
+    
     rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
     if (rc == 0) {
         lua_pushlstring(L, (const char*)addr_val, 6);

+ 2 - 88
demo/adc/main.lua

@@ -1,17 +1,4 @@
 
---[[
-1. Air101,Air103 模块上的ADC0脚-PA1, 0~2.4v,不要超过范围使用!!!
-2. Air101,Air103模块上的ADC1脚-PA4, 0~2.4v,不要超过范围使用!!!
-3. Air103 模块上的ADC2脚-PA2, 0~2.4v,不要超过范围使用!!! 
-4. Air103 模块上的ADC3脚-PA3, 0~2.4v,不要超过范围使用!!! 
-5. Air101,Air103 adc.CH_CPU 为内部温度 ,adc.CH_VBAT为VBAT
-6. Air105 adc参考电压是1.88V,所有通道一致,
-7. Air105内部分压没有隔离措施,在开启内部分压后,量程有所变化,具体看寄存器手册,1~5分压后能测到3.6,6通道能接近5V,但是不能直接测5V,可以测4.2V 0通道是始终开启无法关闭分压。
-8. Air780E内部ADC接口为12bits 外部直流分压为0-3.4V
-9. Air780E内部具有2个ADC接口,ADC0 -- AIO3 ADC1 -- AIO4 
-10. 特殊通道, CPU内部温度Temp -- adc.CH_CPU 主供电脚电压 VBAT -- adc.CH_VBAT
-]]
-
 -- LuaTools需要PROJECT和VERSION这两个信息
 PROJECT = "adcdemo"
 VERSION = "1.0.0"
@@ -27,83 +14,10 @@ if wdt then
     sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
 end
 
-local rtos_bsp = rtos.bsp()
-function adc_pin() -- 根据不同开发板,设置ADC编号
-    if rtos_bsp == "AIR101" then -- Air101开发板ADC编号
-        return 0,1,255,255,adc.CH_CPU ,adc.CH_VBAT 
-    elseif rtos_bsp == "AIR103" then -- Air103开发板ADC编号
-        return 0,1,2,3,adc.CH_CPU ,adc.CH_VBAT 
-    elseif rtos_bsp == "AIR105" then -- Air105开发板ADC编号
-        -- 默认不开启分压,范围是0-1.8v精度高
-        -- adc.setRange(adc.ADC_RANGE_3_6)
-        return 0,5,6,255,255,255
-    elseif rtos_bsp == "ESP32C3" then -- ESP32C3开发板ADC编号
-        return 0,1,2,3,adc.CH_CPU , 255
-    elseif rtos_bsp == "ESP32C2" then -- ESP32C2开发板ADC编号
-        return 0,1,2,3,adc.CH_CPU , 255
-    elseif rtos_bsp == "ESP32S3" then -- ESP32S3开发板ADC编号
-        return 0,1,2,3,adc.CH_CPU , 255
-    elseif rtos_bsp == "EC618" then --Air780E开发板ADC编号
-        -- 默认不开启分压,范围是0-1.2v精度高
-        -- adc.setRange(adc.ADC_RANGE_3_8)
-        return 0,1,255,255,adc.CH_CPU ,adc.CH_VBAT 
-    elseif rtos_bsp == "EC718P" then --Air780EP开发板ADC编号
-        -- 默认不开启分压,范围是0-1.6v精度高
-        -- 开启分压后,外部输入最大不可超过3.3V
-        -- adc.setRange(adc.ADC_RANGE_MAX)
-        return 0,1,255,255,adc.CH_CPU ,adc.CH_VBAT 
-    elseif rtos_bsp == "UIS8850BM" then 
-        return 0,255,255,255, adc.CH_CPU ,adc.CH_VBAT 
-    else
-        log.info("main", "define ADC pin in main.lua")
-        return 255,255,255,255, adc.CH_CPU ,adc.CH_VBAT 
-    end
-end
-local adc_pin_0,adc_pin_1,adc_pin_2,adc_pin_3,adc_pin_temp,adc_pin_vbat=adc_pin()
-sys.taskInit(function()
-    if rtos_bsp == "AIR105" then
-        adc.setRange(adc.ADC_RANGE_3_6) --开启的内部分压,可以把量程扩大
-    end
-    if adc_pin_0 and adc_pin_0 ~= 255 then adc.open(adc_pin_0) end
-    if adc_pin_1 and adc_pin_1 ~= 255 then adc.open(adc_pin_1) end
-    if adc_pin_2 and adc_pin_2 ~= 255 then adc.open(adc_pin_2) end
-    if adc_pin_3 and adc_pin_3 ~= 255 then adc.open(adc_pin_3) end
-    if adc_pin_temp and adc_pin_temp ~= 255 then adc.open(adc_pin_temp) end
-    if adc_pin_vbat and adc_pin_vbat ~= 255 then adc.open(adc_pin_vbat) end
-
-    -- 下面是循环打印, 接地不打印0也是正常现象
-    -- ADC的精度都不会太高, 若需要高精度ADC, 建议额外添加adc芯片
-    while true do
-        if adc_pin_0 and adc_pin_0 ~= 255 then
-            log.debug("adc", "adc" .. tostring(adc_pin_0), adc.get(adc_pin_0)) -- 若adc.get报nil, 改成adc.read
-        end
-        if adc_pin_1 and adc_pin_1 ~= 255 then
-            log.debug("adc", "adc" .. tostring(adc_pin_1), adc.get(adc_pin_1))
-        end
-        if adc_pin_2 and adc_pin_2 ~= 255 then
-            log.debug("adc", "adc" .. tostring(adc_pin_2), adc.get(adc_pin_2))
-        end
-        if adc_pin_3 and adc_pin_3 ~= 255 then
-            log.debug("adc", "adc" .. tostring(adc_pin_3), adc.get(adc_pin_3))
-        end
-        if adc_pin_temp and adc_pin_temp ~= 255 then
-            log.debug("adc", "CPU TEMP", adc.get(adc_pin_temp))
-        end
-        if adc_pin_vbat and adc_pin_vbat ~= 255 then
-            log.debug("adc", "VBAT", adc.get(adc_pin_vbat))
-        end
-        sys.wait(1000)
-    end
+local testAdc = require "testAdc"
+sys.taskInit(testAdc.dotest)
 
-    -- 若不再读取, 可关掉adc, 降低功耗, 非必须
-    if adc_pin_0 and adc_pin_0 ~= 255 then adc.close(adc_pin_0) end
-    if adc_pin_1 and adc_pin_1 ~= 255 then adc.close(adc_pin_1) end
-    if adc_pin_2 and adc_pin_2 ~= 255 then adc.close(adc_pin_2) end
-    if adc_pin_3 and adc_pin_3 ~= 255 then adc.close(adc_pin_3) end
-    if adc_pin_temp and adc_pin_temp ~= 255 then adc.close(adc_pin_temp) end
-    if adc_pin_vbat and adc_pin_vbat ~= 255 then adc.close(adc_pin_vbat) end
 
-end)
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句
 sys.run()

+ 113 - 0
demo/adc/testAdc.lua

@@ -0,0 +1,113 @@
+
+--[[
+1. Air101,Air103 模块上的ADC0脚-PA1, 0~2.4v,不要超过范围使用!!!
+2. Air101,Air103模块上的ADC1脚-PA4, 0~2.4v,不要超过范围使用!!!
+3. Air103 模块上的ADC2脚-PA2, 0~2.4v,不要超过范围使用!!! 
+4. Air103 模块上的ADC3脚-PA3, 0~2.4v,不要超过范围使用!!! 
+5. Air101,Air103 adc.CH_CPU 为内部温度 ,adc.CH_VBAT为VBAT
+6. Air105 adc参考电压是1.88V,所有通道一致,
+7. Air105内部分压没有隔离措施,在开启内部分压后,量程有所变化,具体看寄存器手册,1~5分压后能测到3.6,6通道能接近5V,但是不能直接测5V,可以测4.2V 0通道是始终开启无法关闭分压。
+8. Air780E内部ADC接口为12bits 外部直流分压为0-3.4V
+9. Air780E内部具有2个ADC接口,ADC0 -- AIO3 ADC1 -- AIO4 
+10. 特殊通道, CPU内部温度Temp -- adc.CH_CPU 主供电脚电压 VBAT -- adc.CH_VBAT
+11. 设置分压(adc.setRange)要在adc.open之前设置,否则无效!!
+]]
+
+local testAdc = {}
+
+local rtos_bsp = rtos.bsp()
+function adc_pin() -- 根据不同开发板,设置ADC编号
+    if rtos_bsp == "AIR101" then -- Air101开发板ADC编号
+        return 0,1,255,255,adc.CH_CPU ,adc.CH_VBAT 
+    elseif rtos_bsp == "AIR103" then -- Air103开发板ADC编号
+        return 0,1,2,3,adc.CH_CPU ,adc.CH_VBAT 
+    elseif rtos_bsp == "AIR105" then -- Air105开发板ADC编号
+        -- 默认不开启分压,范围是0-1.8v精度高
+        -- 设置分压要在adc.open之前设置,否则无效!!
+        -- adc.setRange(adc.ADC_RANGE_3_6)
+        return 0,5,6,255,255,255
+    elseif rtos_bsp == "ESP32C3" then -- ESP32C3开发板ADC编号
+        return 0,1,2,3,adc.CH_CPU , 255
+    elseif rtos_bsp == "ESP32C2" then -- ESP32C2开发板ADC编号
+        return 0,1,2,3,adc.CH_CPU , 255
+    elseif rtos_bsp == "ESP32S3" then -- ESP32S3开发板ADC编号
+        return 0,1,2,3,adc.CH_CPU , 255
+    elseif rtos_bsp == "EC618" then --Air780E开发板ADC编号
+        -- 默认不开启分压,范围是0-1.2v精度高
+        -- 设置分压要在adc.open之前设置,否则无效!!
+        -- adc.setRange(adc.ADC_RANGE_3_8)
+        return 0,1,255,255,adc.CH_CPU ,adc.CH_VBAT 
+    elseif rtos_bsp == "EC718P" then --Air780EP开发板ADC编号
+        -- 默认不开启分压,范围是0-1.6v精度高
+        -- 开启分压后,外部输入最大不可超过3.3V
+        -- 设置分压要在adc.open之前设置,否则无效!!
+        -- adc.setRange(adc.ADC_RANGE_MAX)
+        return 0,1,255,255,adc.CH_CPU ,adc.CH_VBAT 
+    elseif rtos_bsp == "UIS8850BM" then 
+        return 0,255,255,255, adc.CH_CPU ,adc.CH_VBAT 
+    else
+        log.info("main", "define ADC pin in main.lua")
+        return 255,255,255,255, adc.CH_CPU ,adc.CH_VBAT 
+    end
+end
+local adc_pin_0,adc_pin_1,adc_pin_2,adc_pin_3,adc_pin_temp,adc_pin_vbat=adc_pin()
+
+
+function testAdc.dotest()
+    if rtos_bsp == "AIR105" then
+        adc.setRange(adc.ADC_RANGE_3_6) --开启的内部分压,可以把量程扩大
+    end
+    if adc_pin_0 and adc_pin_0 ~= 255 then adc.open(adc_pin_0) end
+    if adc_pin_1 and adc_pin_1 ~= 255 then adc.open(adc_pin_1) end
+    if adc_pin_2 and adc_pin_2 ~= 255 then adc.open(adc_pin_2) end
+    if adc_pin_3 and adc_pin_3 ~= 255 then adc.open(adc_pin_3) end
+    if adc_pin_temp and adc_pin_temp ~= 255 then adc.open(adc_pin_temp) end
+    if adc_pin_vbat and adc_pin_vbat ~= 255 then adc.open(adc_pin_vbat) end
+
+    if adc_pin_0 and adc_pin_0 ~= 255 and mcu and mcu.ticks then
+        sys.wait(1000)
+        log.info("开始读取ADC")
+        local ms_start = mcu.ticks()
+        for i = 1, 100, 1 do
+            adc.get(adc_pin_0)
+        end
+        local ms_end = mcu.ticks()
+        log.info("结束读取ADC")
+        log.info("adc", "读取耗时", "100次", ms_end - ms_start, "ms", "单次", (ms_end - ms_start) // 100, "ms")
+    end
+
+    -- 下面是循环打印, 接地不打印0也是正常现象
+    -- ADC的精度都不会太高, 若需要高精度ADC, 建议额外添加adc芯片
+    while true do
+        if adc_pin_0 and adc_pin_0 ~= 255 then
+            log.debug("adc", "adc" .. tostring(adc_pin_0), adc.get(adc_pin_0)) -- 若adc.get报nil, 改成adc.read
+        end
+        if adc_pin_1 and adc_pin_1 ~= 255 then
+            log.debug("adc", "adc" .. tostring(adc_pin_1), adc.get(adc_pin_1))
+        end
+        if adc_pin_2 and adc_pin_2 ~= 255 then
+            log.debug("adc", "adc" .. tostring(adc_pin_2), adc.get(adc_pin_2))
+        end
+        if adc_pin_3 and adc_pin_3 ~= 255 then
+            log.debug("adc", "adc" .. tostring(adc_pin_3), adc.get(adc_pin_3))
+        end
+        if adc_pin_temp and adc_pin_temp ~= 255 then
+            log.debug("adc", "CPU TEMP", adc.get(adc_pin_temp))
+        end
+        if adc_pin_vbat and adc_pin_vbat ~= 255 then
+            log.debug("adc", "VBAT", adc.get(adc_pin_vbat))
+        end
+        sys.wait(1000)
+    end
+
+    -- 若不再读取, 可关掉adc, 降低功耗, 非必须
+    if adc_pin_0 and adc_pin_0 ~= 255 then adc.close(adc_pin_0) end
+    if adc_pin_1 and adc_pin_1 ~= 255 then adc.close(adc_pin_1) end
+    if adc_pin_2 and adc_pin_2 ~= 255 then adc.close(adc_pin_2) end
+    if adc_pin_3 and adc_pin_3 ~= 255 then adc.close(adc_pin_3) end
+    if adc_pin_temp and adc_pin_temp ~= 255 then adc.close(adc_pin_temp) end
+    if adc_pin_vbat and adc_pin_vbat ~= 255 then adc.close(adc_pin_vbat) end
+
+end
+
+return testAdc

+ 24 - 5
demo/air780epvh_gnsstest/hdgnss.lua

@@ -3,7 +3,7 @@ local lbsLoc2 = require "lbsLoc2"
 
 local hdgnss = {}
 
-local function hdcrc(str, len)
+function hdcrc(str, len)
     if not len then
         len = #str
     end
@@ -70,7 +70,24 @@ function hdgnss.aideph()
         --     -- sys.waitUntil("UART2_SEND", 100)
         --     sys.wait(100) -- 等100ms反而更成功
         -- end
-        uart.write(gps_uart_id, body)
+        -- uart.write(gps_uart_id, body)
+        local offset = 1
+        local size = #body
+        while offset < size do
+            -- 前两个字符是F1D9
+            if body:byte(offset) == 0xF1 and body:byte(offset + 1) == 0xD9 then
+                -- log.info("hdgnss", "星历数据", body:sub(1, 16):toHex())
+                local len = body:byte(offset + 4) + body:byte(offset + 5) * 256
+                local tmp = body:sub(offset, offset + len + 7)
+                -- log.info("hdgnss", "写入星历片段", offset, len, #tmp, tmp:toHex())
+                uart.write(gps_uart_id, tmp)
+                offset = offset + len + 8
+                sys.wait(20)
+            else
+                log.warn("hdgnss", "分隔出错了", offset, size)
+                break
+            end
+        end
     end
     sys.wait(20)
 end
@@ -90,7 +107,7 @@ local function exec_agnss()
     -- 注入当前时间
     if os.date("*t").year > 2023 then
         hdgnss.aidtime()
-        sys.wait(20)
+        sys.wait(100)
     end
 
     -- 读取之前的位置信息
@@ -108,7 +125,7 @@ local function exec_agnss()
     -- 写入参考位置
     if lat and lng and lat ~= 0 and lng ~= 0 then
         hdgnss.aidpos(lat, lng)
-        sys.wait(20)
+        sys.wait(100)
     else
         log.info("hdgnss", "当前无辅助位置信息")
     end
@@ -132,6 +149,8 @@ local function exec_agnss()
     end
 
     hdgnss.aideph()
+
+    -- sys.wait(1000)
 end
 
 function hdgnss.aidpos(lat, lng)
@@ -162,7 +181,7 @@ function hdgnss.aidtime()
             0x0B, 0x11, 0x14, 0x00,           -- 指令ID: 0x0B 0x11, 载荷20字节
             0x00,                             -- UTC 0, TNOW 1
             0x00,                             -- 保留参数,填0
-            0x12,                             -- 闰秒
+            0x11,                             -- 闰秒
             date.year & 0xFF, date.year >> 8, -- 年
             date.month,                       -- 月
             date.day,                         -- 日

+ 1 - 0
demo/air780epvh_gnsstest/testGnss.lua

@@ -12,6 +12,7 @@ sys.taskInit(function()
     log.debug("提醒", "室内无GNSS信号,定位不会成功, 要到空旷的室外,起码要看得到天空")
     hdgnss.setup({
         uart_id=2,
+        uart_forward = uart.VUART_0, -- 转发到虚拟串口,方便对接GnssToolKit3
         debug=true
     })
     hdgnss.start()

+ 13 - 234
demo/aliyun/main.lua

@@ -1,247 +1,26 @@
 PROJECT = "aliyundemo"
 VERSION = "1.0.0"
 local sys = require "sys"
-local libfota = require("libfota")
-local aliyun = require "aliyun"
 
---根据自己的服务器修改以下参数
---新版已经合并, 没有了地域, 1883同时兼容加密和非加密通信,非加密会下线
--- 阿里云资料:https://help.aliyun.com/document_detail/147356.htm?spm=a2c4g.73742.0.0.4782214ch6jkXb#section-rtu-6kn-kru
-tPara = {
-    -- 是否为一型一密, true为一型一密, false为一机一密(预注册)
-    Registration = false,
-    -- 设备名名称, 必须唯一
-    DeviceName = (mobile and mobile.imei() or (wlan and wlan.mac() or mcu.unique_id():toHex())),
-    -- 产品key, 在产品详情页面
-    ProductKey = "k0ti3QNOFaH",     --产品key
-    --产品secret,一型一密就需要填, 一机一密(预注册)不填
-    ProductSecret = "",
-    --设备密钥,一型一密就不填, 一机一密(预注册)必须填
-    DeviceSecret = "62a17dfe2192526f90bc5fad7cd951fc", --设备secret
-    -- 填写实例id,在实例详情页面
-    InstanceId = "iot-06z00hmbog1v175",
-    -- 固定值, 不要修改
-    mqtt_port = 1883,                --    mqtt端口
-    --是否使用ssl加密连接,注意: 如果使用一型一密,则必须使用ssl加密
-    mqtt_isssl = false,
- }
-
--- 低功耗测试, 打开之后, 连接阿里云后5秒请求低功耗
-local PM_TEST = false
-
- -- 统一联网函数
-sys.taskInit(function()
-    -----------------------------
-    -- 统一联网函数, 可自行删减
-    ----------------------------
-    if wlan and wlan.connect then
-        -- wifi 联网, ESP32系列均支持
-        local ssid = "luatos1234"
-        local password = "12341234"
-        log.info("wifi", ssid, password)
-        -- TODO 改成自动配网
-        wlan.init()
-        wlan.setMode(wlan.STATION) -- 默认也是这个模式,不调用也可以
-        wlan.connect(ssid, password, 1)
-    elseif mobile then
-        -- Air780E/Air600E系列
-        --mobile.simid(2) -- 自动切换SIM卡
-        -- LED = gpio.setup(27, 0, gpio.PULLUP)
-        -- device_id = mobile.imei()
-    elseif w5500 then
-        -- w5500 以太网, 当前仅Air105支持
-        w5500.init(spi.HSPI_0, 24000000, pin.PC14, pin.PC01, pin.PC00)
-        w5500.config() --默认是DHCP模式
-        w5500.bind(socket.ETH0)
-        -- LED = gpio.setup(62, 0, gpio.PULLUP)
-    elseif socket or mqtt then
-        -- 适配的socket库也OK
-        -- 没有其他操作, 单纯给个注释说明
-    else
-        -- 其他不认识的bsp, 循环提示一下吧
-        while 1 do
-            sys.wait(1000)
-            log.info("bsp", "本bsp可能未适配网络层, 请查证")
-        end
-    end
-    -- 默认都等到联网成功
-    sys.waitUntil("IP_READY")
-    sys.publish("net_ready")
-end)
-
- --阿里云客户端是否处于连接状态
-local sConnected
-
-local publishCnt = 1
-
---[[
-函数名:pubqos1testackcb
-功能  :发布1条qos为1的消息后收到PUBACK的回调函数
-参数  :
-		usertag:调用mqttclient:publish时传入的usertag
-		result:任意数字表示发布成功,nil表示失败
-返回值:无
-]]
-local function publishTestCb(result,para)
-    log.info("aliyun", "发布后的反馈", result,para)
-    sys.timerStart(publishTest,20000)
-    publishCnt = publishCnt+1
-end
-
---发布一条QOS为1的消息
-function publishTest()
-    if sConnected then
-        --注意:在此处自己去控制payload的内容编码,aLiYun库中不会对payload的内容做任何编码转换
-        -- aliyun.publish(topic,qos,payload,cbFnc,cbPara)
-        log.info("aliyun", "上行数据")
-        aliyun.publish("/"..tPara.ProductKey.."/"..tPara.DeviceName.."/user/get",1,"LUATOS_CESHI",publishTestCb,"publishTest_"..publishCnt)
-    end
-end
-
----数据接收的处理函数
--- @string topic,UTF8编码的消息主题
--- @string payload,原始编码的消息负载
-local function rcvCbFnc(topic,payload,qos,retain,dup)
-    log.info("aliyun", "收到下行数据", topic,payload,qos,retain,dup)
+-- 一型一密优先使用fskv存储密钥
+if fskv then
+    fskv.init()
 end
 
---- 连接结果的处理函数
--- @bool result,连接结果,true表示连接成功,false或者nil表示连接失败
-local function connectCbFnc(result)
-    log.info("aliyun","连接结果", result)
-    sConnected = result
-    if result then
-        sys.publish("aliyun_ready")
-        log.info("aliyun", "连接成功")
-        --订阅主题
-        --根据自己的项目需要订阅主题,下面注释掉的一行代码中的主题是非法的,所以不能打开,一旦打开,会导致订阅失败
-        --订阅OTA主题
-        aliyun.subscribe("/ota/device/upgrade/"..tPara.ProductKey.."/"..tPara.DeviceName,1)
-        if not verRpted then        
-            --上报固件版本号给云端
-            verRpt()
-        end
-        --上报固件版本号给云端
-        sys.timerStart(verRpt,10000)
-        -- aliyun.subscribe(topic,qos)
-        -- aliyun.subscribe("/"..tPara.ProductKey.."/"..tPara.DeviceName.."/user/ceshi",1)
-        
-        --使用掉电不消失的kv文件区来储存的deviceSecret,deviceToken
-        local used = fskv.get("deviceSecret")
-        local total = fskv.get("deviceToken")
-        local cid = fskv.get("clientid")
-        --储存到kv分区的返回值
-        -- log.info("kv分区的返回值",used,total,cid)
-        local Secret = aliyun.getDeviceSecret()
-        local Token = aliyun.getDeviceToken()
-        local Clid = aliyun.getClientid()
-         --如果三元组不为空,就把三元组写入到kv区
-        if tPara.DeviceName and tPara.ProductKey then
-            fskv.set("DeviceName",tPara.DeviceName)
-            fskv.set("ProductKey",tPara.ProductKey)
-        end
-        if not tPara.Registration then
-            if Secret == nil then
-                log.info("aliyun","掉线重连")
-            else
-                fskv.set("deviceSecret",aliyun.getDeviceSecret())
-            end
-        else
-            if Token == nil and Clid == nil then
-                log.info("aliyun","掉线重连")
-            else
-                fskv.set("deviceToken",aliyun.getDeviceToken())
-                fskv.set("clientid",aliyun.getClientid())
-            end
-        end
+-- 联网函数,非必须
+-- require "netready"
 
-        --注册数据接收的处理函数
-        aliyun.on("receive",rcvCbFnc)
-        --PUBLISH消息测试
-        publishTest()
-    else
-        log.warn("aliyun", "连接失败")
-    end
-end
+-- 阿里云事件处理函数
+require "testEvt"
 
-aliyun.on("connect",connectCbFnc)
+-- 一机一密的演示
+require "testYjym"
 
---根据掉电不消失的kv文件区来储存的deviceSecret,clientid,deviceToken来判断是进行正常连接还是掉电重连
-sys.taskInit(function()
-    sys.waitUntil("net_ready")
-    socket.sntp()
-    sys.waitUntil("NTP_UPDATE", 2000)
-    log.info("已联网", "开始初始化aliyun库")
-    fskv.init()
-    local name = fskv.get("DeviceName")
-    local key = fskv.get("ProductKey")
-    local used = fskv.get("deviceSecret")
-    local total = fskv.get("deviceToken")
-    local cid = fskv.get("clientid")
-    local host = tPara.InstanceId..".mqtt.iothub.aliyuncs.com"
-    --判断是否是同一三元组,不是的话就重新连接
-    if name == tPara.DeviceName and key == tPara.ProductKey then
-        if not tPara.Registration then
-            if used == nil then
-                aliyun.setup(tPara)
-            else
-                aliyun.clientGetDirectDataTask(name,tPara.ProductKey,host,tPara.mqtt_port,tPara.mqtt_isssl,tPara.Registration,used,total,cid)
-            end
-        else
-            if total == nil and cid == nil then
-                aliyun.setup(tPara)
-            else
-                aliyun.clientGetDirectDataTask(name,tPara.ProductKey,host,tPara.mqtt_port,tPara.mqtt_isssl,tPara.Registration,used,total,cid)
-            end
-        end
-    else
-        fskv.del("deviceToken")
-        fskv.del("clientid")
-        fskv.del("DeviceName")
-        fskv.del("deviceSecret")
-        fskv.del("ProductKey")
-        --删除kv区的数据,重新建立连接
-        aliyun.setup(tPara)
-    end
-end)
-
-function verRpt()
-    log.info("aliyun", "上报版本信息", _G.VERSION)
-    aliyun.publish("/ota/device/inform/"..tPara.ProductKey.."/"..tPara.DeviceName,1,"{\"id\":1,\"params\":{\"version\":\"".._G.VERSION.."\"}}")
-end
+-- 一型一密的演示, 一定要修改成自己的信息才能跑通!!!
+-- require "testYxym"
 
--- 低功耗演示
-sys.taskInit(function()
-    if not PM_TEST then
-        return 
-    end
-    sys.waitUntil("aliyun_ready")
-    log.info("aliyun.pm", "阿里云已经连接成功, 5秒后请求进入低功耗模式, USB功能会断开")
-    sys.wait(5000)
-    local bsp = rtos.bsp():upper()
-    -- 进入低功耗模式
-    if bsp == "EC618" then
-        log.info("aliyun.pm", "EC618方案进入低功耗模式")
-        -- gpio.setup(23,nil)
-        -- gpio.close(33)
-        -- mobile.rtime(2)  -- RRC快速释放减少connect时间能大幅降低功耗,但是会带来可能得离线风险,可选择延迟时间或者不用
-        pm.power(pm.USB, false)
-        pm.force(pm.LIGHT)
-    elseif bsp == "EC718P" or bsp == "EC718PV" then
-        log.info("aliyun.pm", "EC718P/EC718PV方案进入低功耗模式")
-        -- mobile.rtime(2) -- RRC快速释放减少connect时间能大幅降低功耗,但是会带来可能得离线风险,可选择延迟时间或者不用
-        pm.power(pm.USB, false)
-        pm.force(pm.LIGHT)
-    elseif bsp == "AIR101" or bsp == "AIR601" or bsp == "AIR103" then
-        log.info("aliyun.pm", "XT804方案进入低功耗模式")
-        while 1 do
-            pm.dtimerStart(0, 30000)
-            pm.request(pm.LIGHT)
-            sys.wait(30000)
-        end
-    end
-    
-end)
+-- aliyun+低功耗的演示, 需要开启testYjym或者testYxym
+-- require "testPm"
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句

+ 41 - 0
demo/aliyun/netready.lua

@@ -0,0 +1,41 @@
+local sys = require "sys"
+
+ -- 统一联网函数
+sys.taskInit(function()
+    -----------------------------
+    -- 统一联网函数, 可自行删减
+    ----------------------------
+    if wlan and wlan.connect then
+        -- wifi 联网, ESP32系列均支持
+        local ssid = "luatos1234"
+        local password = "12341234"
+        log.info("wifi", ssid, password)
+        -- TODO 改成自动配网
+        wlan.init()
+        wlan.setMode(wlan.STATION) -- 默认也是这个模式,不调用也可以
+        wlan.connect(ssid, password, 1)
+    elseif mobile then
+        -- Air780E/Air600E系列
+        --mobile.simid(2) -- 自动切换SIM卡
+        -- LED = gpio.setup(27, 0, gpio.PULLUP)
+        -- device_id = mobile.imei()
+    elseif w5500 then
+        -- w5500 以太网, 当前仅Air105支持
+        w5500.init(spi.HSPI_0, 24000000, pin.PC14, pin.PC01, pin.PC00)
+        w5500.config() --默认是DHCP模式
+        w5500.bind(socket.ETH0)
+        -- LED = gpio.setup(62, 0, gpio.PULLUP)
+    elseif socket or mqtt then
+        -- 适配的socket库也OK
+        -- 没有其他操作, 单纯给个注释说明
+    else
+        -- 其他不认识的bsp, 循环提示一下吧
+        while 1 do
+            sys.wait(1000)
+            log.info("bsp", "本bsp可能未适配网络层, 请查证")
+        end
+    end
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready")
+end)

+ 74 - 0
demo/aliyun/testEvt.lua

@@ -0,0 +1,74 @@
+
+local sys = require "sys"
+local aliyun = require "aliyun"
+
+--[[
+函数名:pubqos1testackcb
+功能  :发布1条qos为1的消息后收到PUBACK的回调函数
+参数  :
+		usertag:调用mqttclient:publish时传入的usertag
+		result:任意数字表示发布成功,nil表示失败
+返回值:无
+]]
+local publishCnt = 1
+local function publishTestCb(result,para)
+    log.info("aliyun", "发布后的反馈", result,para)
+    sys.timerStart(publishTest,20000)
+    publishCnt = publishCnt+1
+end
+
+--发布一条QOS为1的消息
+function publishTest()
+    --注意:在此处自己去控制payload的内容编码,aLiYun库中不会对payload的内容做任何编码转换
+    -- aliyun.publish(topic,qos,payload,cbFnc,cbPara)
+    log.info("aliyun", "上行数据")
+    aliyun.publish("/"..aliyun.opts.ProductKey.."/"..aliyun.opts.DeviceName.."/user/get",1,"LUATOS_CESHI",publishTestCb,"publishTest_"..publishCnt)
+end
+
+---数据接收的处理函数
+-- @string topic,UTF8编码的消息主题
+-- @string payload,原始编码的消息负载
+local function rcvCbFnc(topic,payload,qos,retain,dup)
+    log.info("aliyun", "收到下行数据", topic,payload,qos,retain,dup)
+end
+
+--- 连接结果的处理函数
+-- @bool result,连接结果,true表示连接成功,false或者nil表示连接失败
+local function connectCbFnc(result)
+    log.info("aliyun","连接结果", result)
+    if result then
+        sys.publish("aliyun_ready")
+        log.info("aliyun", "连接成功")
+        --订阅主题
+        --根据自己的项目需要订阅主题
+        -- aliyun.subscribe(topic,qos)
+        -- aliyun.subscribe("/".. aliyun.opts.ProductKey.."/".. aliyun.opts.DeviceName.."/user/ceshi",1)
+
+        --PUBLISH消息测试
+        publishTest()
+    else
+        log.warn("aliyun", "连接失败")
+    end
+end
+
+-- 连接状态的处理函数
+aliyun.on("connect",connectCbFnc)
+
+-- 数据接收的处理函数
+aliyun.on("receive",rcvCbFnc)
+
+-- 一型一密的注册回调函数, 2024.6.17 添加
+-- aliyun.on("reg", function(result)
+--     aliyun.store(result)
+-- end)
+
+-- 数据发送的处理函数, 一般不需要
+-- aliyun.on("receive", sentCbFnc)
+
+-- OTA状态的处理函数
+-- aliyun.on("ota",function(result)
+--     if result == 0 then
+--         log.info("aliyun", "OTA成功")
+--         rtos.reboot()
+--     end
+-- end)

+ 29 - 0
demo/aliyun/testPm.lua

@@ -0,0 +1,29 @@
+
+-- 低功耗演示
+sys.taskInit(function()
+    sys.waitUntil("aliyun_ready")
+    log.info("aliyun.pm", "阿里云已经连接成功, 5秒后请求进入低功耗模式, USB功能会断开")
+    sys.wait(5000)
+    local bsp = rtos.bsp():upper()
+    -- 进入低功耗模式
+    if bsp == "EC618" then
+        log.info("aliyun.pm", "EC618方案进入低功耗模式")
+        -- gpio.setup(23,nil)
+        -- gpio.close(33)
+        -- mobile.rtime(2)  -- RRC快速释放减少connect时间能大幅降低功耗,但是会带来可能得离线风险,可选择延迟时间或者不用
+        pm.power(pm.USB, false)
+        pm.force(pm.LIGHT)
+    elseif bsp == "EC718P" or bsp == "EC718PV" then
+        log.info("aliyun.pm", "EC718P/EC718PV方案进入低功耗模式")
+        -- mobile.rtime(2) -- RRC快速释放减少connect时间能大幅降低功耗,但是会带来可能得离线风险,可选择延迟时间或者不用
+        pm.power(pm.USB, false)
+        pm.force(pm.LIGHT)
+    elseif bsp == "AIR101" or bsp == "AIR601" or bsp == "AIR103" then
+        log.info("aliyun.pm", "XT804方案进入低功耗模式")
+        while 1 do
+            pm.dtimerStart(0, 30000)
+            pm.request(pm.LIGHT)
+            sys.wait(30000)
+        end
+    end
+end)

+ 29 - 0
demo/aliyun/testYjym.lua

@@ -0,0 +1,29 @@
+
+local sys = require "sys"
+local aliyun = require "aliyun"
+
+--根据自己的服务器修改以下参数
+-- 阿里云资料:https://help.aliyun.com/document_detail/147356.htm?spm=a2c4g.73742.0.0.4782214ch6jkXb#section-rtu-6kn-kru
+local tPara = {
+    -- 一机一密
+    -- 设备名名称, 必须唯一
+    DeviceName = "azNhIbNNTdsVwY2mhZno",
+    -- 产品key, 在产品详情页面
+    ProductKey = "a1YFuY6OC1e",     --产品key
+    --设备密钥,一型一密就不填, 一机一密(预注册)必须填
+    DeviceSecret = "5iRxTePbEMguOuZqltZrJBR0JjWJSdA7", --设备secret
+    -- 填写实例id,在实例详情页面, 如果是旧的公共实例, 请填RegionId参数
+    InstanceId = "",
+    RegionId = "cn-shanghai",
+    --是否使用ssl加密连接
+    mqtt_isssl = false,
+}
+
+-- 等待联网, 然后初始化aliyun库
+sys.taskInit(function()
+    -- sys.waitUntil("IP_READY")
+    sys.waitUntil("net_ready")
+
+    log.info("已联网", "开始初始化aliyun库")
+    aliyun.setup(tPara)
+end)

+ 58 - 0
demo/aliyun/testYxym.lua

@@ -0,0 +1,58 @@
+
+local sys = require "sys"
+local aliyun = require "aliyun"
+
+--根据自己的服务器修改以下参数
+-- 阿里云资料:https://help.aliyun.com/document_detail/147356.htm?spm=a2c4g.73742.0.0.4782214ch6jkXb#section-rtu-6kn-kru
+tPara = {
+    -- 一型一密 - ProductSecret要填产品secret
+    -- 一型一密 分2种: 预注册和免预注册
+    -- 公共实例只支持 预注册, Registration 填false
+    -- 企业实例支持 预注册 和 免预注册, 如需使用免预注册, Registration 填true, 否则填false
+    Registration = false,
+    -- 设备名名称, 必须唯一
+    DeviceName = "abcd123",
+    -- 产品key, 在产品详情页面
+    ProductKey = "a1DtzomWBme",     --产品key
+    --产品secret,一型一密就需要填
+    ProductSecret = "dxV3o2IekLLsOMFn",
+    -- 填写实例id,在实例详情页面, 如果是旧的公共实例, 请填host参数
+    InstanceId = "",
+    RegionId = "cn-shanghai",
+    --是否使用ssl加密连接
+    mqtt_isssl = true,
+}
+
+--根据掉电不消失的kv文件区来储存的deviceSecret,clientid,deviceToken来判断是进行正常连接还是掉电重连
+sys.taskInit(function()
+    sys.waitUntil("IP_READY")
+    log.info("已联网", "开始初始化aliyun库")
+
+    local store = aliyun.store()
+
+    --判断是否是同一三元组,不是的话就重新连接
+    if store.deviceName ~= tPara.DeviceName or store.productKey ~= tPara.ProductKey then
+        -- 清除fskv区的注册信息
+        if fskv then
+            fskv.del("DeviceName")
+            fskv.del("ProductKey")
+            fskv.del("deviceToken")
+            fskv.del("deviceSecret")
+            fskv.del("clientid")
+        else
+            os.remove("/alireg.json")
+        end
+        store = {}
+    end
+
+    if store.clientid and store.deviceToken and #store.clientid > 0 and #store.deviceToken > 0 then
+        tPara.clientId = store.clientid
+        tPara.deviceToken = store.deviceToken
+        tPara.reginfo = true
+    elseif store.deviceSecret and #store.deviceSecret > 0 then
+        tPara.deviceSecret = store.deviceSecret
+        tPara.reginfo = true
+    end
+    aliyun.setup(tPara)
+end)
+

+ 10 - 10
demo/multimedia/main.lua

@@ -60,7 +60,7 @@ function audio_setup()
         -- end
         audio.config(0, 25, 1, 3, 100)
     elseif bsp == "EC718P" then
-		-- CORE+音频小板是这个配置
+		-- CORE+音频小板是这个配置/云喇叭开发板同为这个配置
 
         pm.power(pm.LDO_CTL, false)  --开发板上ES8311由LDO_CTL控制上下电
         sys.wait(100)
@@ -79,7 +79,7 @@ function audio_setup()
         local pa_pin = 25
         local pa_on_level = 1
         local pa_delay = 100
-        local power_pin = 16
+        local power_pin = 16 -- Air780EPVH 请修改成17
         local power_on_level = 1
         local power_delay = 3
         local power_time_delay = 100
@@ -223,15 +223,15 @@ local function audio_task()
             log.info("手动关闭")
             audio.playStop(0)
         end
-		audio.pm(0,audio.SHUTDOWN)
-		--低功耗测试打开下面的代码
+		audio.pm(0,audio.SHUTDOWN)
+		--低功耗测试打开下面的代码
 		--[[
-		audio.pm(0,audio.POWEROFF)	--低功耗可以选择SHUTDOWN或者POWEROFF,如果codec无法断电用SHUTDOWN
-		pm.power(pm.USB, false)
-		mobile.flymode(0, true)
-		pm.request(pm.LIGHT)
-		sys.wait(20000)
-		mobile.flymode(0, false)
+		audio.pm(0,audio.POWEROFF)	--低功耗可以选择SHUTDOWN或者POWEROFF,如果codec无法断电用SHUTDOWN
+		pm.power(pm.USB, false)
+		mobile.flymode(0, true)
+		pm.request(pm.LIGHT)
+		sys.wait(20000)
+		mobile.flymode(0, false)
 		]]
         log.info(rtos.meminfo("sys"))
         log.info(rtos.meminfo("lua"))

+ 1 - 1
demo/wlan/wifi_scan/main.lua

@@ -20,7 +20,7 @@ sys.taskInit(function()
     wlan.init()
     while 1 do
         wlan.scan()
-        sys.wait(15000)
+        sys.wait(15000) -- 注意, 尤其是Air780系列, 这个时间只能更长不能短
     end
 end)
 

+ 5 - 0
luat/include/luat_audio.h

@@ -58,6 +58,7 @@ typedef struct luat_audio_conf {
 	uint8_t power_pin;													// 电源控制
 	uint8_t power_on_level;                                             // 电源使能电平
 	uint8_t pa_is_control_enable;
+	uint8_t voltage;
 } luat_audio_conf_t;
 
 typedef enum{
@@ -66,6 +67,10 @@ typedef enum{
     LUAT_AUDIO_PM_SHUTDOWN,         /* 关断模式 */
 	LUAT_AUDIO_PM_POWER_OFF,        /* 完全断电模式 */
 }luat_audio_pm_mode_t;
+typedef enum{
+    LUAT_AUDIO_VOLTAGE_3300 = 0,         	 /* 工作在3.3V */
+	LUAT_AUDIO_VOLTAGE_1800,       			 /* 工作在1.8V */
+}luat_audio_voltage_t;
 
 typedef enum{
 	LUAT_AUDIO_BUS_DAC=0,

+ 293 - 275
script/libs/aliyun.lua

@@ -15,98 +15,105 @@ local libfota = require("libfota")
 
 -- 总的库对象
 local aliyun = {}
+local mqttc = nil
+
 local ClientId,PassWord,UserName,SetClientidFnc,SetDeviceTokenFnc,SetDeviceSecretFnc
-local OutQueue =
-{
-    SUBSCRIBE = {},
-    PUBLISH = {},
-}
-local Item = {}
 local EvtCb = {}
-local mqttc = nil
-local Key,Dname
+local opts = {}
 
---添加
-local function insert(type,topic,qos,payload,cbFnc,cbPara)
-    table.insert(OutQueue[type],{t=topic,q=qos,p=payload,cb=cbFnc,para=cbPara})
+-------------------------------------------------------
+---- FOTA 相关
+-------------------------------------------------------
+-- fota的回调函数
+local function libfota_cb(result)
+    log.info("fota", "result", result)
+    -- fota成功
+    if result == 0 then
+        rtos.reboot()
+    end
 end
-
---删除
-local function remove(type)
-    if #OutQueue[type]>0 then return table.remove(OutQueue[type],1) end
+--收到云端固件升级通知消息时的回调函数
+local function aliyun_upgrade(payload)
+    local jsonData, result = json.decode(payload)
+    if result and jsonData.data and jsonData.data.url then
+        log.info("aliyun", "ota.url", jsonData.data.url)
+        libfota.request(EvtCb["ota"] or libfota_cb, jsonData.data.url)
+    end
 end
 
---订阅步骤
-local function procSubscribe(client)
-        local i
+-------------------------------------------------------
+--- 用户侧的回调处理
+-------------------------------------------------------
 
-        for i=1,#OutQueue["SUBSCRIBE"] do
-            if not client:subscribe(OutQueue["SUBSCRIBE"][i].t , OutQueue["SUBSCRIBE"][i].q) then
-                OutQueue["SUBSCRIBE"] = {}
-                return false,"procSubscribe"
-            end
-        end
-        OutQueue["SUBSCRIBE"] = {}
-        return true
-end
 
---发布
-local function procSend(client)
-    if not procSubscribe(client) then
-        return false,"procSubscribe"
-    end
 
-    if #OutQueue["PUBLISH"] == 0 then
-        sys.waitUntil("ALIYUN_PUB")
-    end
+--底层libMQTT回调函数,上层的回调函数,通过 aliyun.on注册
+local function mqtt_cbevent(mqtt_client, event, data, payload,metas)
+    log.debug("aliyun", "event", event, "data", data)
+    if event == "conack" then
+        log.info("aliyun", "conack")
+        -- if opts.ProductKey and opts.DeviceName then
+        aliyun.subscribe("/ota/device/upgrade/"..opts.ProductKey.."/"..opts.DeviceName,1)
+        aliyun.publish("/ota/device/inform/"..opts.ProductKey.."/"..opts.DeviceName,1,"{\"id\":1,\"params\":{\"version\":\"".._G.VERSION.."\"}}")
+        -- end
+        sys.publish("aliyun_conack")
+        if EvtCb["connect"] then
+            EvtCb["connect"](true)
+        end
+    elseif event == "recv" then -- 服务器下发的数据
+        log.debug("aliyun", "downlink", "topic", data, "payload", payload)
+        --OTA消息
+        if data =="/ota/device/upgrade/".. opts.ProductKey.."/".. opts.DeviceName then
+            aliyun_upgrade(payload)
+        end
 
-    while #OutQueue["PUBLISH"] > 0 do
-        Item = table.remove(OutQueue["PUBLISH"],1)
-        local result = client:publish(Item.t,Item.p,Item.q)
-        if type(result) == nil then
-            if Item.cb then Item.cb(false,Item.para) end
-        else
-            local result, data = sys.waitUntil("PUB_SENT")
-            if Item.cb then Item.cb(data,Item.para) end
+        if EvtCb["receive"] then
+            EvtCb["receive"](data, payload, metas.qos, metas.retain, metas.dup)
+        end
+    elseif event == "sent" then
+        log.info("aliyun", "sent", data)
+        if data then
+            sys.publish("aliyun_evt", "sent", data)
+            if EvtCb["publish"] then
+                EvtCb["publish"](data)
+            end
+        end
+    elseif event == "disconnect" then
+        log.info("aliyun", "disconnect")
+        if EvtCb["connect"] then
+            EvtCb["connect"](false)
         end
     end
-    return true,"procSend"
 end
 
---二次连接
--- local function clientDataTask(ClientId,user,PassWord,mqtt_host,mqtt_port,mqtt_isssl,DeviceName,ProductKey)
-local function clientDataTask(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,passtoken,Registration)
-    Key = ProductKey
-    Dname = DeviceName
-    sys.taskInit(function()
-        if not Registration then
-            local client_id,user_name,password = iotauth.aliyun(ProductKey,DeviceName,SetDeviceSecretFnc)
-            mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl)  --mqtt客户端创建
-            mqttc:auth(client_id,user_name,password) --mqtt三元组配置
-        else
-            mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl)  --mqtt客户端创建
-            mqttc:auth(DeviceName,ProductKey,passtoken) --mqtt三元组配置
-        end
+local function mqtt_task(mqtt_host, mqtt_port, mqtt_isssl, client_id, user_name, password)
+    mqttc = mqtt.create(nil,mqtt_host, mqtt_port, mqtt_isssl)  --mqtt客户端创建
+    log.debug("aliyun", "mqtt三元组", client_id,user_name,password)
+    mqttc:auth(client_id,user_name,password) --mqtt三元组配置
 
-        mqttc:keepalive(300) -- 默认值240s
-        mqttc:autoreconn(true, 20000) -- 自动重连机制
-        mqttc:on(mqtt_cbevent)  --mqtt回调注册
-        mqttc:connect()
-
-        local conres = sys.waitUntil("aliyun_conack",30000)
-        if mqttc:ready() and conres then
-            -- if connectCb then connectCb(true,ProductKey,DeviceName) end
-            -- if EvtCb["connect"] then EvtCb["connect"](true) end
+    mqttc:keepalive(300) -- 默认值240s
+    mqttc:autoreconn(true, 20000) -- 自动重连机制
+    mqttc:on(mqtt_cbevent)  --mqtt回调注册
+    -- mqttc:debug(true)
+    mqttc:connect()
+end
 
-            local result,prompt = procSubscribe(mqttc)
-            if result then
-                while true do
-                    procSend(mqttc)
-                end
-            end
+---------------------------------------------------
+---              一型一密
+--------------------------------------------------
 
-            -- if connectCb then connectCb(false,ProductKey,DeviceName) end
-            -- if EvtCb["connect"] then EvtCb["connect"](false) end
+-- 二次连接, 也就是真正的连接
+local function clientDataTask(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,passtoken,Registration)
+    sys.taskInit(function()
+        if not Registration then -- 预注册
+            local client_id,user_name,password = iotauth.aliyun(opts.ProductKey, opts.DeviceName,SetDeviceSecretFnc)
+            -- mqttc = mqtt.create(nil,opts.mqtt_host, opts.mqtt_port, opts.mqtt_isssl)  --mqtt客户端创建
+            -- mqttc:auth(client_id,user_name,password) --mqtt三元组配置
+            mqtt_task(mqtt_host or opts.mqtt_host, mqtt_port or opts.mqtt_port, mqtt_isssl or opts.mqtt_isssl, client_id, user_name, password)
+        else -- 免预注册
+            -- mqttc = mqtt.create(nil,opts.mqtt_host, opts.mqtt_port, opts.mqtt_isssl)  --mqtt客户端创建
+            -- mqttc:auth(opts.DeviceName, opts.ProductKey, passtoken) --mqtt三元组配置
+            mqtt_task(mqtt_host or opts.mqtt_host, mqtt_port or opts.mqtt_port, mqtt_isssl or opts.mqtt_isssl, opts.DeviceName, opts.ProductKey, passtoken)
         end
     end)
 end
@@ -116,216 +123,121 @@ local function directProc(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,R
     if not Registration then
         local ClientId = DeviceName.."|securemode=3,signmethod=hmacmd5,timestamp=789|"
         local UserName = DeviceName.."&"..ProductKey
-        
+
         local content = "ClientId"..DeviceName.."deviceName"..DeviceName.."productKey"..ProductKey.."timestamp789"
         local signKey= SetDeviceSecretFnc
         PassWord = crypto.hmac_md5(content,signKey)
-        
+
         clientDataTask(ClientId,UserName,PassWord,mqtt_host,mqtt_port,mqtt_isssl,DeviceName,ProductKey)
     else
         local ClientId = SetClientidFnc.."|securemode=-2,authType=connwl|"
         local UserName = DeviceName.."&"..ProductKey
         local PassWord = SetDeviceTokenFnc
-        
+
         clientDataTask(ClientId,UserName,mqtt_host,mqtt_port,mqtt_isssl,PassWord,Registration)
     end
 end
 
---获取预注册和免预注册一型一密一次连接返回的数据
+--获取一型一密的连接参数
 local function clientEncryptionTask(Registration,DeviceName,ProductKey,ProductSecret,InstanceId,mqtt_host,mqtt_port,mqtt_isssl)
-    Key = ProductKey
-    Dname = DeviceName
     sys.taskInit(function()
-        local tm = os.time()
-        --一型一密
-            --预注册
-            if not Registration then
-                ClientId = DeviceName.."|securemode=2,authType=register,random="..tm..",signmethod=hmacmd5|"
-            --免预注册
+        --预注册
+        if not Registration then
+            ClientId = DeviceName.."|securemode=2,authType=register,random=123,signmethod=hmacmd5|"
+        --免预注册
+        else
+            if InstanceId and #InstanceId > 0 then
+                ClientId = DeviceName.."|securemode=-2,authType=regnwl,random=123,signmethod=hmacmd5,instanceId="..InstanceId.."|"
             else
-                ClientId = DeviceName.."|securemode=-2,authType=regnwl,random="..tm..",signmethod=hmacmd5,instanceId="..InstanceId.."|"
+                ClientId = DeviceName.."|securemode=-2,authType=regnwl,random=123,signmethod=hmacmd5|"
             end
-            UserName = DeviceName.."&"..ProductKey
-            local content = "deviceName"..DeviceName.."productKey"..ProductKey.."random"..tm
-            PassWord = crypto.hmac_md5(content,ProductSecret)
-            
-            local mqttClient = mqtt.create(nil,mqtt_host,mqtt_port, true)  --客户端创建
-            log.info("mqtt三元组", ClientId,UserName,PassWord)
-            mqttClient:auth(ClientId,UserName,PassWord) --三元组配置
-            mqttClient:on(function(mqtt_client, event, data, payload)  --mqtt回调注册
-                -- 用户自定义代码
-                if event == "conack" then
-                    -- 无需订阅topic, 阿里云会主动下发通知
-                elseif event == "recv" then
-                    log.info("mqtt", "downlink", "topic", data, "payload", payload)
-                    if payload then
-                        local tJsonDecode,res = json.decode(payload)
-                        if not Registration then
-                            --预注册
-                            if res and tJsonDecode["deviceName"] and tJsonDecode["deviceSecret"] then
-                                
-                                SetDeviceSecretFnc = tJsonDecode["deviceSecret"]
-                                mqttClient:disconnect()
-                                -- directProc(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,Registration)
-                                clientDataTask(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl)
+        end
+        local UserName = DeviceName.."&"..ProductKey
+        local content = "deviceName"..DeviceName.."productKey"..ProductKey.."random123"
+        local PassWord = crypto.hmac_md5(content, ProductSecret)
+
+        local mqttClient = mqtt.create(nil, mqtt_host, mqtt_port, true)  --客户端创建
+        if mqttClient == nil then
+            log.error("aliyun", "一型一密要求固件支持TLS加密, 当前固件不支持!!!")
+            return
+        end
+        log.debug("aliyun", "一型一密认证三元组", ClientId, UserName, PassWord)
+        mqttClient:auth(ClientId,UserName,PassWord) --三元组配置
+        mqttClient:autoreconn(true, 30000)
+        local flag = true
+        mqttClient:on(function(mqtt_client, event, data, payload)  --mqtt回调注册
+            if event == "recv" then
+                -- 无需订阅topic, 阿里云会主动下发通知
+                log.info("aliyun", "downlink", "topic", data, "payload", payload)
+                if payload then
+                    local tJsonDecode,res = json.decode(payload)
+                    if not Registration then
+                        --预注册
+                        if res and tJsonDecode["deviceName"] and tJsonDecode["deviceSecret"] then
+                            SetDeviceSecretFnc = tJsonDecode["deviceSecret"]
+                            log.debug("aliyun", "一型一密(预注册)", tJsonDecode["deviceName"], SetDeviceSecretFnc)
+                            if EvtCb["reg"] then
+                                EvtCb["reg"](tJsonDecode)
+                            else
+                                aliyun.store(tJsonDecode)
                             end
-                        else
-                             --免预注册
-                            if res and tJsonDecode["deviceName"] and tJsonDecode["deviceToken"] then
-                                SetDeviceTokenFnc = tJsonDecode["deviceToken"]
-                                SetClientidFnc = tJsonDecode["clientId"]
-                                mqttClient:disconnect()
-                                directProc(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,Registration)
+                            mqttClient:autoreconn(false)
+                            mqttClient:disconnect()
+                            flag = false
+                            clientDataTask(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl)
+                        end
+                    else
+                         --免预注册
+                        if res and tJsonDecode["deviceName"] and tJsonDecode["deviceToken"] then
+                            SetDeviceTokenFnc = tJsonDecode["deviceToken"]
+                            SetClientidFnc = tJsonDecode["clientId"]
+                            log.debug("aliyun", "一型一密(免预注册)", SetClientidFnc, SetDeviceTokenFnc)
+                            if EvtCb["reg"] then
+                                EvtCb["reg"](tJsonDecode)
+                            else
+                                aliyun.store(tJsonDecode)
                             end
+                            mqttClient:autoreconn(false)
+                            mqttClient:disconnect()
+                            flag = false
+                            directProc(DeviceName, ProductKey, mqtt_host, mqtt_port, mqtt_isssl, Registration)
                         end
-                        
                     end
-                elseif event == "sent" then
-                    log.info("mqtt", "sent", "pkgid", data)
                 end
-            end)
-    
-            mqttClient:connect()
-    end)
-end
-
-
---底层libMQTT回调函数,上层的回调函数,通过 aliyun.on注册
-function mqtt_cbevent(mqtt_client, event, data, payload,metas)
-    if event == "conack" then
-        sys.publish("aliyun_conack")
-        EvtCb["connect"](true) 
-    elseif event == "recv" then -- 服务器下发的数据
-        log.info("mqtt", "downlink", "topic", data, "payload", payload,"qos",metas.qos,"retain",metas.retain,"dup",metas.dup)
-
-        log.info("aliyun.procReceive",data,string.toHex(payload))
-        --OTA消息
-        if data =="/ota/device/upgrade/"..Key.."/"..Dname then
-            if upgrade then
-                upgrade(payload)
             end
+        end)
+        mqttClient:connect()
+        while flag do
+            sys.wait(1000)
         end
-
-        if EvtCb["receive"] then
-            EvtCb["receive"](data, payload,metas.qos,metas.retain,metas.dup)
-        end
-    elseif event == "sent" then
-        if type(data) == "number" then
-            sys.publish("PUB_SENT",true)
-        end
-
-    elseif event == "disconnect" then
-        log.info("mqtt", "disconnect")
-
-        sys.publish("PUB_SENT",false)
-
-        if EvtCb["connect"] then 
-            EvtCb["connect"](false) 
-        end
-    end
-end
-
-
---[[
-配置阿里云物联网套件的产品信息和设备信息
-@api aliyun.setup(tPara)
-@table 阿里云物联网套件的产品信息和设备信息
-@return nil
-@usage
-aliyun.setup(tPara)
--- 参数说明
-一机一密认证方案时,ProductSecret参数传入nil
-一型一密认证方案时,ProductSecret参数传入真实的产品密钥
-Registration 是否是预注册 已预注册为false,未预注册为true
-DeviceName 设备名称
-ProductKey 产品key
-ProductSecret 产品secret,根据此信息判断是一机一密还是一型一密
-DeviceSecret 设备secret
-InstanceId 如果没有注册需要填写实例id,在实例详情页面
-mqtt_port mqtt端口
-mqtt_isssl 是否使用ssl加密连接,true为无证书最简单的加密
-]]
-function aliyun.setup(tPara)
-    mqtt_host = tPara.host or tPara.InstanceId..".mqtt.iothub.aliyuncs.com"
-    if tPara.ProductSecret == "" or tPara.ProductSecret == nil then
-        confiDentialTask(tPara.DeviceName,tPara.ProductKey,tPara.DeviceSecret,mqtt_host,tPara.mqtt_port,tPara.mqtt_isssl)
-    else
-        clientEncryptionTask(tPara.Registration,tPara.DeviceName,tPara.ProductKey,tPara.ProductSecret,tPara.InstanceId,mqtt_host,tPara.mqtt_port,tPara.mqtt_isssl)
-    end
+    end)
 end
 
---一机一密连接  confiDentialTask
-function confiDentialTask(DeviceName,ProductKey,DeviceSecret,mqtt_host,mqtt_port,mqtt_isssl)
-    Key = ProductKey
-    Dname = DeviceName
+---------------------------------------------
+-- 一机一密
+---------------------------------------------
+local function confiDentialTask()
     sys.taskInit(function()
-        local client_id,user_name,password = iotauth.aliyun(ProductKey,DeviceName,DeviceSecret)
-        mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl)  --mqtt客户端创建
-        log.info("mqtt三元组", client_id,user_name,password)
-        mqttc:auth(client_id,user_name,password) --mqtt三元组配置
-
-
-        mqttc:keepalive(300) -- 默认值240s
-        mqttc:autoreconn(true, 20000) -- 自动重连机制
-        mqttc:connect()
-        mqttc:on(mqtt_cbevent)  --mqtt回调注册
-
-        local conres = sys.waitUntil("aliyun_conack",30000)
-        if mqttc:ready() and conres then
-            -- if connectCb then connectCb(true,ProductKey,DeviceName) end
-            -- if EvtCb["connect"] then EvtCb["connect"](true) end
-
-            local result,prompt = procSubscribe(mqttc)
-            if result then
-                while true do
-                    procSend(mqttc)
-                end
-            end
-
-            -- if connectCb then connectCb(false,ProductKey,DeviceName) end
-            -- if EvtCb["connect"] then EvtCb["connect"](false) end
-        end
+        local client_id,user_name,password = iotauth.aliyun(opts.ProductKey, opts.DeviceName, opts.DeviceSecret)
+        mqtt_task(opts.mqtt_host, opts.mqtt_port, opts.mqtt_isssl, client_id, user_name, password)
     end)
 end
 
 
---正常连接 预注册一型一密获取DeviceSecret后就是正常的一机一密连接   
+--正常连接 预注册一型一密获取DeviceSecret后就是正常的一机一密连接
 function aliyun.clientGetDirectDataTask(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,Registration,DeviceSecret,deviceToken,cid)
-    Key = ProductKey
-    Dname = DeviceName
     sys.taskInit(function()
         if not Registration then
             local client_id,user_name,password = iotauth.aliyun(ProductKey,DeviceName,DeviceSecret)
-            mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl)  --mqtt客户端创建
-            mqttc:auth(client_id,user_name,password) --mqtt三元组配置
+            -- mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl)  --mqtt客户端创建
+            -- mqttc:auth(client_id,user_name,password) --mqtt三元组配置
+            mqtt_task(mqtt_host, mqtt_port,mqtt_isssl, client_id, user_name, password)
         else
             local clientId = cid.."|securemode=-2,authType=connwl|"
-
             local client_id,user_name,password = iotauth.aliyun(ProductKey,DeviceName,deviceToken)
-            mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl)  --mqtt客户端创建
-    
-            mqttc:auth(clientId,user_name,deviceToken) --mqtt三元组配置
-        end
-
-        mqttc:keepalive(30) -- 默认值240s
-        mqttc:autoreconn(true, 20000) -- 自动重连机制
-        mqttc:connect()
-        mqttc:on(mqtt_cbevent)  --mqtt回调注册
-
-        local conres = sys.waitUntil("aliyun_conack",30000)
-        if mqttc:ready() and conres then
-            -- if connectCb then connectCb(true,ProductKey,DeviceName) end
-            -- if EvtCb["connect"] then EvtCb["connect"](true) end
-
-            local result,prompt = procSubscribe(mqttc)
-            if result then
-                while true do
-                    procSend(mqttc)
-                end
-            end
-
-            -- if connectCb then connectCb(false,ProductKey,DeviceName) end
-            -- if EvtCb["connect"] then EvtCb["connect"](false) end
+            -- mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl)  --mqtt客户端创建
+            -- mqttc:auth(clientId, user_name, deviceToken) --mqtt三元组配置
+            mqtt_task(mqtt_host, mqtt_port,mqtt_isssl, clientId, user_name, deviceToken)
         end
     end)
 end
@@ -335,13 +247,15 @@ end
 订阅主题
 @api aliyun.subscribe(topic,qos)
 @string 主题内容为UTF8编码
-@number qos为number类型(0/1,默认0)
+@number qos为number类型(0/1,默认1)
 @return nil
 @usage
-aliyun.subscribe("/b0FMK1Ga5cp/862991234567890/get", 0)
+aliyun.subscribe("/b0FMK1Ga5cp/862991234567890/get", 1)
 ]]
 function aliyun.subscribe(topic,qos)
-    insert("SUBSCRIBE",topic,qos)
+    if mqttc and mqttc:ready() then
+        mqttc:subscribe(topic,qos or 1)
+    end
 end
 
 
@@ -359,8 +273,30 @@ aliyun.publish("/b0FMK1Ga5cp/862991234567890/update",0,"test")
 aliyun.publish("/b0FMK1Ga5cp/862991234567890/update",1,"test",cbFnc,"cbFncPara")
 ]]
 function aliyun.publish(topic,qos,payload,cbFnc,cbPara)
-    insert("PUBLISH",topic,qos,payload,cbFnc,cbPara)
-    sys.publish("ALIYUN_PUB")
+    if mqttc and mqttc:ready() then
+        local pkgid = mqttc:publish(topic, payload, qos)
+        if cbFnc then
+            if pkgid then
+                sys.taskInit(function()
+                    local timeout = 15000
+                    while timeout > 0 do
+                        local result, evt, tmp = sys.waitUntil("aliyun_evt", 1000)
+                        -- log.debug("aliyun", "等待publish的sent事件", result, evt, tmp)
+                        if evt == "sent" and pkgid == tmp then
+                            cbFnc(true, cbPara)
+                            return
+                        end
+                        timeout = timeout - 1000
+                    end
+                    cbFnc(false, cbPara)
+                end)
+            else
+                cbFnc(true, cbPara)
+            end
+        end
+    else
+        cbFnc(false, cbPara)
+    end
 end
 
 
@@ -413,32 +349,114 @@ end
 
 
 
+--[[
+配置阿里云物联网套件的产品信息和设备信息
+@api aliyun.setup(tPara)
+@table 阿里云物联网套件的产品信息和设备信息
+@return nil
+@usage
+aliyun.setup(tPara)
+-- 参数说明
+一机一密认证方案时,ProductSecret参数传入nil
+一型一密认证方案时,ProductSecret参数传入真实的产品密钥
+Registration 是否是预注册 已预注册为false,未预注册为true
+DeviceName 设备名称
+ProductKey 产品key
+ProductSecret 产品secret,根据此信息判断是一机一密还是一型一密
+DeviceSecret 设备secret
+InstanceId 如果没有注册需要填写实例id,在实例详情页面
+mqtt_port mqtt端口
+mqtt_isssl 是否使用ssl加密连接,true为无证书最简单的加密
+]]
+function aliyun.setup(tPara)
+    opts = tPara
+    aliyun.opts = opts
+    if not opts.mqtt_host then
+        if opts.host then
+            opts.mqtt_host = opts.host
+        elseif tPara.InstanceId and #tPara.InstanceId > 0 then
+            opts.mqtt_host = tPara.InstanceId..".mqtt.iothub.aliyuncs.com"
+        else
+            opts.mqtt_host = tPara.ProductKey .. ".iot-as-mqtt."..tPara.RegionId..".aliyuncs.com"
+        end
+    end
+    -- log.debug("aliyun", "mqtt host", opts.mqtt_host)
+    if not tPara.ProductSecret or #tPara.ProductSecret == 0 then
+        log.info("aliyun", "一机一密模式")
+        confiDentialTask()
+    else
+        log.info("aliyun", string.format("一型一密(%s)模式 - %s", tPara.Registration and "预注册" or "免预注册", tPara.reginfo and "已获取注册信息" or "开始获取注册信息"))
+        if tPara.reginfo then
+            aliyun.clientGetDirectDataTask(tPara.DeviceName,tPara.ProductKey, opts.mqtt_host, 1883, tPara.mqtt_isssl,
+                tPara.Registration,
+                tPara.deviceSecret, tPara.deviceToken, tPara.clientId)
+        else
+            clientEncryptionTask(tPara.Registration,tPara.DeviceName,tPara.ProductKey,tPara.ProductSecret,tPara.InstanceId, opts.mqtt_host, 1883,tPara.mqtt_isssl)
+        end
+    end
+end
 
-
---收到云端固件升级通知消息时的回调函数
-function upgrade(payload)
-    local result
-    local jsonData,result = json.decode(payload)
-    if result and jsonData.data and jsonData.data.url then
-        -- sys.taskInit(downloadTask,jsonData.data.url,jsonData.data.size,jsonData.data.md5,jsonData.data.version)
-        libfota.request(libfota_cb,jsonData.data.url)
+--[[
+判断阿里云物联网套件是否已经连接
+@api aliyun.ready()
+@return boolean 阿里云物联网套件是否已经连接
+@usage
+-- 本函数于2024.6.17新增
+if aliyun.ready() then
+    log.info("aliyun", "已连接")
+end
+]]
+function aliyun.ready()
+    if mqttc and mqttc:ready() then
+        return true
     end
 end
 
--- 功能:获取fota的回调函数
--- 参数:
--- result:number类型
---   0表示成功
---   1表示连接失败
---   2表示url错误
---   3表示服务器断开
---   4表示接收报文错误
---   5表示使用iot平台VERSION需要使用 xxx.yyy.zzz形式
-function libfota_cb(result)
-    log.info("fota", "result", result)
-    -- fota成功
-    if result == 0 then
-        rtos.reboot()   --如果还有其他事情要做,就不要立刻reboot
+--[[
+获取或存储注册信息
+@api aliyun.store(result)
+@table result 注册结果,如果为nil则表示获取注册信息
+@return table 注册信息,如果为nil则表示获取失败
+@usage
+-- 获取注册信息
+local store = aliyun.store()
+-- 存储注册信息
+aliyun.store(result)
+]]
+function aliyun.store(result)
+    if result then
+        log.debug("aliyun", "注册结果", json.encode(result))
+        if fskv then
+            fskv.set("ProductKey", result["productKey"])
+            fskv.set("DeviceName",result["deviceName"])
+            if result["deviceSecret"] then
+                fskv.set("deviceSecret",result["deviceSecret"])
+            else
+                fskv.set("deviceToken",result["deviceToken"])
+                fskv.set("clientId", result["clientId"])
+            end
+        else
+            log.debug("aliyun", "fskv not found, use io/fs")
+            io.writeFile("/alireg.json", json.encode(result))
+        end
+    else
+        local store = {}
+        if fskv then
+            store.deviceName = fskv.get("DeviceName") or fskv.get("deviceName")
+            store.productKey = fskv.get("ProductKey") or fskv.get("productKey")
+            store.deviceSecret = fskv.get("deviceSecret")
+            store.deviceToken = fskv.get("deviceToken")
+            store.clientid = fskv.get("clientid")
+        else
+            local tmp = io.readFile("/alireg.json")
+            if tmp then
+                store = json.decode(tmp)
+                if not store then
+                    store = {}
+                end
+            end
+        end
+        return store
     end
 end
 

+ 20 - 21
script/turnkey/hz201p/ccVolte.lua

@@ -3,14 +3,14 @@ sys.subscribe("CC_IND", function(state)
     if state == "READY" then
         sys.publish("CC_READY")
     elseif state == "INCOMINGCALL" then
-		cnt = cnt + 1
-		if cnt > 1 then
-			cc.accept(0)
-		end
+        cnt = cnt + 1
+        if cnt > 1 then
+            cc.accept(0)
+        end
     elseif state == "HANGUP_CALL_DONE" or state == "MAKE_CALL_FAILED" or state == "DISCONNECTED" then
-		audio.pm(0,audio.STANDBY)
-		-- audio.pm(0,audio.SHUTDOWN)	--低功耗可以选择SHUTDOWN或者POWEROFF,如果codec无法断电用SHUTDOWN
-	end
+        audio.pm(0, audio.STANDBY)
+        -- audio.pm(0,audio.SHUTDOWN)	--低功耗可以选择SHUTDOWN或者POWEROFF,如果codec无法断电用SHUTDOWN
+    end
 end)
 
 sys.taskInit(function()
@@ -23,43 +23,42 @@ sys.taskInit(function()
     local i2s_channel_format = i2s.MONO_R
     local i2s_communication_format = i2s.MODE_LSB
     local i2s_channel_bits = 16
-
-    local pa_pin = 25
+    local pa_pin = 23
     local pa_on_level = 1
     local pa_delay = 100
     local power_pin = 255
     local power_on_level = 1
     local power_delay = 3
     local power_time_delay = 100
-
     local voice_vol = 70
     local mic_vol = 65
-
     local find_es8311 = false
-	--自适应开发板,如果明确是I2C几就不用了
+    mcu.altfun(mcu.I2C, i2c_id, 13, 2, 0)
+    mcu.altfun(mcu.I2C, i2c_id, 14, 2, 0)
     i2c.setup(i2c_id, i2c.FAST)
-	pm.power(pm.LDO_CTL, false)  --开发板上ES8311由LDO_CTL控制上下电
     sys.wait(10)
-    pm.power(pm.LDO_CTL, true)  --开发板上ES8311由LDO_CTL控制上下电
-	sys.wait(10)
     if i2c.send(i2c_id, 0x18, 0xfd) == true then
         find_es8311 = true
     end
-
     if not find_es8311 then
         while true do
             log.info("not find es8311")
             sys.wait(1000)
         end
     end
-    i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, i2s_bits_per_sample, i2s_channel_format, i2s_communication_format,i2s_channel_bits)
+    i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, i2s_bits_per_sample, i2s_channel_format, i2s_communication_format, i2s_channel_bits)
     audio.config(multimedia_id, pa_pin, pa_on_level, power_delay, pa_delay, power_pin, power_on_level, power_time_delay)
-    audio.setBus(multimedia_id, audio.BUS_I2S,{chip = "es8311", i2cid = i2c_id, i2sid = i2s_id})	--通道0的硬件输出通道设置为I2S
+    audio.setBus(multimedia_id, audio.BUS_I2S, {
+        chip = "es8311",
+        i2cid = i2c_id,
+        i2sid = i2s_id,
+        voltage = audio.VOLTAGE_1800
+    }) -- 通道0的硬件输出通道设置为I2S
     audio.vol(multimedia_id, voice_vol)
     audio.micVol(multimedia_id, mic_vol)
     cc.init(multimedia_id)
-	audio.pm(0,audio.STANDBY)
+    audio.pm(0, audio.STANDBY)
     sys.waitUntil("CC_READY")
-    sys.wait(100)   
-    --cc.dial(0,"114") --拨打电话
+    sys.wait(100)
+    -- cc.dial(0,"114") --拨打电话
 end)

+ 24 - 9
script/turnkey/hz201p/da267.lua

@@ -2,17 +2,27 @@ local i2cId = 1
 local da267Addr = 0x26
 local intPin = 39
 local function ind()
+    log.info("int", gpio.get(intPin))
     if gpio.get(intPin) == 1 then
         log.info("da267", "interrupt")
         i2c.send(i2cId, da267Addr, 0x02, 1)
         local data = i2c.recv(i2cId, da267Addr, 6)
         if data and #data == 6 then
-            local xl, xm, yl, ym, zl, zm = string.unpack(">bbbbbb", data)
-            local x, y, z = (xm << 8 | xl >> 4), (ym << 8 | yl >> 4), (zm << 8 | zl >> 4)
+            local xl, xm, yl, ym, zl, zm = string.byte(data, 1, 1), string.byte(data, 2, 2), string.byte(data, 3, 3), string.byte(data, 4, 4), string.byte(data, 5, 5), string.byte(data, 6, 6)
+            local x, y, z = (xm << 8 | xl ) >> 4, (ym << 8 | yl) >> 4, (zm << 8 | zl ) >> 4
             log.info("da267", "x:", x, "y:", y, "z:", z)
         else
             sys.publish("RESTORE_GSENSOR")
         end
+        i2c.send(i2cId, da267Addr, 0x0D, 1)
+        local data = i2c.recv(i2cId, da267Addr, 2)
+        if data and #data == 2 then
+            local xl, xm = string.byte(data, 1, 1), string.byte(data, 2, 2)
+            local step = ((xl << 8) + xm) // 2
+            log.info("da267", "step:", step)
+        else
+            sys.publish("RESTORE_GSENSOR")
+        end
     end
 end
 gpio.setup(intPin, ind)
@@ -21,28 +31,33 @@ local function init()
     i2c.close(i2cId)
     i2c.setup(i2cId, i2c.SLOW)
     i2c.send(i2cId, da267Addr, {0x00, 0x24}, 1)
-    i2c.send(i2cId, da267Addr, {0x0F, 0x00}, 1)
     sys.wait(20)
+    i2c.send(i2cId, da267Addr, {0x0F, 0x00}, 1)
     i2c.send(i2cId, da267Addr, {0x11, 0x34}, 1)
-    i2c.send(i2cId, da267Addr, {0x10, 0x06}, 1)
+    i2c.send(i2cId, da267Addr, {0x10, 0x07}, 1)
+
     sys.wait(50)
 
-    -- reduce power consumption
-    i2c.send(i2cId, da267Addr, {0x8C, 0x00}, 1)
+    -- int set1
+    i2c.send(i2cId, da267Addr, {0x16, 0x87}, 1)
 
     -- init active interrupt
-    i2c.send(i2cId, da267Addr, {0x16, 0x87}, 1)
-    i2c.send(i2cId, da267Addr, {0x34, 0x00}, 1)
-    i2c.send(i2cId, da267Addr, {0x38, 0x05}, 1)
+    i2c.send(i2cId, da267Addr, {0x38, 0x03}, 1)
     i2c.send(i2cId, da267Addr, {0x39, 0x05}, 1)
     i2c.send(i2cId, da267Addr, {0x3A, 0x05}, 1)
+    i2c.send(i2cId, da267Addr, {0x3B, 0x05}, 1)
     i2c.send(i2cId, da267Addr, {0x19, 0x04}, 1)
 
+    -- enable active
+    i2c.send(i2cId, da267Addr, {0x11, 0x30}, 1)
+
     -- init step counter
     i2c.send(i2cId, da267Addr, {0x33, 0x80}, 1)
 end
 
 sys.taskInit(function()
+    mcu.altfun(mcu.I2C, i2cId, 23, 2, 0)
+    mcu.altfun(mcu.I2C, i2cId, 24, 2, 0)
     while true do
         init()
         while true do

+ 26 - 18
script/turnkey/hz201p/main.lua

@@ -1,21 +1,25 @@
 -- LuaTools需要PROJECT和VERSION这两个信息
 PROJECT = "HZ201P"
 VERSION = "0.0.1"
-
 log.info("main", PROJECT, VERSION)
-
 -- 引入必要的库文件(lua编写), 内部库不需要require
 _G.sys = require "sys"
+pm.ioVol(pm.IOVOL_ALL_GPIO, 1800)
 
+-- gnss的备电和gsensor的供电
+local vbackup = gpio.setup(24, 1)
+-- gnss的供电
+local gpsPower = gpio.setup(26, 1)
+-- gnss的复位
+local gpsRst = gpio.setup(27, 1)
 
 -- Gsensor
 require "da267"
-
 -- 780ep不支持VOLTE,支持CAMERA
 -- 780epv固件支持VOLTE,但固件空间放不下CAMERA
 
 -- CAMERA
-require "camCapture"
+-- require "camCapture"
 
 -- VOLTE
 -- require "ccVolte"
@@ -26,27 +30,31 @@ sys.taskInit(function()
     local pwrLed = gpio.setup(16, 0, nil, nil, 4)
     while 1 do
         netLed(1)
-        pwrLed(1)
-        sys.wait(5000)
-        netLed(0)
         pwrLed(0)
-        sys.wait(5000)
+        sys.wait(500)
+        netLed(0)
+        pwrLed(1)
+        sys.wait(500)
     end
 end)
+-- GNSS
+uart.setup(2, 115200)
+-- 调试日志,可选
+libgnss.debug(true)
+libgnss.bind(2)
+-- SIM DETECT
+local simCheck = gpio.setup(41, function()
+    log.info("sim status", gpio.get(41))
+end)
 
--- 默认是IDLE模式,可替换为pm.LIGHT
-pm.request(pm.IDLE)
-
-sys.taskInit(function()
-    while 1 do
-        log.info("lua", rtos.meminfo())
-        log.info("lua", rtos.meminfo("sys"))
-        sys.wait(1000)
+sys.subscribe("GNSS_STATE", function(state)
+    if state == "FIXED" then
+        log.info("定位成功")
+    elseif state == "LOSE" then
+        log.info("定位丢失")
     end
 end)
 
-
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句
 sys.run()
--- sys.run()之后后面不要加任何语句!!!!!