浏览代码

move: demolib不再作为lib的一部分, 拷贝到module下

Wendal Chen 6 月之前
父节点
当前提交
6cd1afe0a3
共有 100 个文件被更改,包括 12180 次插入0 次删除
  1. 0 0
      module/Air780E/demolib/ads1115.lua
  2. 0 0
      module/Air780E/demolib/ads1115plus.lua
  3. 0 0
      module/Air780E/demolib/adxl34x.lua
  4. 0 0
      module/Air780E/demolib/aht10.lua
  5. 0 0
      module/Air780E/demolib/ak8963.lua
  6. 0 0
      module/Air780E/demolib/aliyun.lua
  7. 0 0
      module/Air780E/demolib/am2320.lua
  8. 0 0
      module/Air780E/demolib/ap3216c.lua
  9. 0 0
      module/Air780E/demolib/bh1750.lua
  10. 0 0
      module/Air780E/demolib/bmx.lua
  11. 0 0
      module/Air780E/demolib/cht8305c.lua
  12. 0 0
      module/Air780E/demolib/ds3231.lua
  13. 0 0
      module/Air780E/demolib/ec11.lua
  14. 0 0
      module/Air780E/demolib/espblufi.lua
  15. 0 0
      module/Air780E/demolib/fm17622.lua
  16. 0 0
      module/Air780E/demolib/gnss.lua
  17. 0 0
      module/Air780E/demolib/gt911.lua
  18. 0 0
      module/Air780E/demolib/gy53l1.lua
  19. 0 0
      module/Air780E/demolib/ina226.lua
  20. 0 0
      module/Air780E/demolib/joystick.lua
  21. 0 0
      module/Air780E/demolib/l3g4200d.lua
  22. 0 0
      module/Air780E/demolib/lis2dh12.lua
  23. 0 0
      module/Air780E/demolib/lm75.lua
  24. 0 0
      module/Air780E/demolib/max31856.lua
  25. 0 0
      module/Air780E/demolib/mcp2515.lua
  26. 0 0
      module/Air780E/demolib/mlx90614.lua
  27. 0 0
      module/Air780E/demolib/modbus_rtu.lua
  28. 0 0
      module/Air780E/demolib/mpu6xxx.lua
  29. 0 0
      module/Air780E/demolib/necir.lua
  30. 0 0
      module/Air780E/demolib/onenetcoap.lua
  31. 0 0
      module/Air780E/demolib/openai.lua
  32. 0 0
      module/Air780E/demolib/pac9685.lua
  33. 0 0
      module/Air780E/demolib/pca9555.lua
  34. 0 0
      module/Air780E/demolib/pca9685.lua
  35. 0 0
      module/Air780E/demolib/pcf8563t.lua
  36. 0 0
      module/Air780E/demolib/pcf8574.lua
  37. 0 0
      module/Air780E/demolib/qmc5883l.lua
  38. 0 0
      module/Air780E/demolib/rc522.lua
  39. 0 0
      module/Air780E/demolib/rtkv.lua
  40. 0 0
      module/Air780E/demolib/sc7a20.lua
  41. 0 0
      module/Air780E/demolib/shift595.lua
  42. 0 0
      module/Air780E/demolib/si24r1.lua
  43. 0 0
      module/Air780E/demolib/spl06.lua
  44. 0 0
      module/Air780E/demolib/tcs3472.lua
  45. 0 0
      module/Air780E/demolib/tm1637.lua
  46. 0 0
      module/Air780E/demolib/tm1640.lua
  47. 0 0
      module/Air780E/demolib/tm1650.lua
  48. 0 0
      module/Air780E/demolib/tsl2561.lua
  49. 0 0
      module/Air780E/demolib/vl6180.lua
  50. 0 0
      module/Air780E/demolib/ze08g_ch2o.lua
  51. 0 0
      module/Air780E/demolib/zh07.lua
  52. 162 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/ads1115.lua
  53. 167 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/ads1115plus.lua
  54. 193 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/adxl34x.lua
  55. 101 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/aht10.lua
  56. 165 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/ak8963.lua
  57. 463 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/aliyun.lua
  58. 73 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/am2320.lua
  59. 135 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/ap3216c.lua
  60. 105 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/bh1750.lua
  61. 618 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/bmx.lua
  62. 77 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/cht8305c.lua
  63. 162 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/ds3231.lua
  64. 74 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/ec11.lua
  65. 565 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/espblufi.lua
  66. 985 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/fm17622.lua
  67. 989 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/gnss.lua
  68. 131 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/gt911.lua
  69. 198 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/gy53l1.lua
  70. 124 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/ina226.lua
  71. 103 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/joystick.lua
  72. 132 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/l3g4200d.lua
  73. 304 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/lis2dh12.lua
  74. 80 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/lm75.lua
  75. 283 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/max31856.lua
  76. 543 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/mcp2515.lua
  77. 96 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/mlx90614.lua
  78. 267 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/modbus_rtu.lua
  79. 251 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/mpu6xxx.lua
  80. 306 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/necir.lua
  81. 180 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/onenetcoap.lua
  82. 160 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/openai.lua
  83. 0 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/pac9685.lua
  84. 119 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/pca9555.lua
  85. 181 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/pca9685.lua
  86. 202 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/pcf8563t.lua
  87. 108 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/pcf8574.lua
  88. 123 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/qmc5883l.lua
  89. 650 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/rc522.lua
  90. 269 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/rtkv.lua
  91. 189 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/sc7a20.lua
  92. 103 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/shift595.lua
  93. 383 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/si24r1.lua
  94. 252 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/spl06.lua
  95. 360 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/tcs3472.lua
  96. 167 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/tm1637.lua
  97. 226 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/tm1640.lua
  98. 302 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/tm1650.lua
  99. 151 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/tsl2561.lua
  100. 203 0
      module/Air780EHM_Air780EHV_Air780EGH/demolib/vl6180.lua

+ 0 - 0
script/demolib/ads1115.lua → module/Air780E/demolib/ads1115.lua


+ 0 - 0
script/demolib/ads1115plus.lua → module/Air780E/demolib/ads1115plus.lua


+ 0 - 0
script/demolib/adxl34x.lua → module/Air780E/demolib/adxl34x.lua


+ 0 - 0
script/demolib/aht10.lua → module/Air780E/demolib/aht10.lua


+ 0 - 0
script/demolib/ak8963.lua → module/Air780E/demolib/ak8963.lua


+ 0 - 0
script/demolib/aliyun.lua → module/Air780E/demolib/aliyun.lua


+ 0 - 0
script/demolib/am2320.lua → module/Air780E/demolib/am2320.lua


+ 0 - 0
script/demolib/ap3216c.lua → module/Air780E/demolib/ap3216c.lua


+ 0 - 0
script/demolib/bh1750.lua → module/Air780E/demolib/bh1750.lua


+ 0 - 0
script/demolib/bmx.lua → module/Air780E/demolib/bmx.lua


+ 0 - 0
script/demolib/cht8305c.lua → module/Air780E/demolib/cht8305c.lua


+ 0 - 0
script/demolib/ds3231.lua → module/Air780E/demolib/ds3231.lua


+ 0 - 0
script/demolib/ec11.lua → module/Air780E/demolib/ec11.lua


+ 0 - 0
script/demolib/espblufi.lua → module/Air780E/demolib/espblufi.lua


+ 0 - 0
script/demolib/fm17622.lua → module/Air780E/demolib/fm17622.lua


+ 0 - 0
script/demolib/gnss.lua → module/Air780E/demolib/gnss.lua


+ 0 - 0
script/demolib/gt911.lua → module/Air780E/demolib/gt911.lua


+ 0 - 0
script/demolib/gy53l1.lua → module/Air780E/demolib/gy53l1.lua


+ 0 - 0
script/demolib/ina226.lua → module/Air780E/demolib/ina226.lua


+ 0 - 0
script/demolib/joystick.lua → module/Air780E/demolib/joystick.lua


+ 0 - 0
script/demolib/l3g4200d.lua → module/Air780E/demolib/l3g4200d.lua


+ 0 - 0
script/demolib/lis2dh12.lua → module/Air780E/demolib/lis2dh12.lua


+ 0 - 0
script/demolib/lm75.lua → module/Air780E/demolib/lm75.lua


+ 0 - 0
script/demolib/max31856.lua → module/Air780E/demolib/max31856.lua


+ 0 - 0
script/demolib/mcp2515.lua → module/Air780E/demolib/mcp2515.lua


+ 0 - 0
script/demolib/mlx90614.lua → module/Air780E/demolib/mlx90614.lua


+ 0 - 0
script/demolib/modbus_rtu.lua → module/Air780E/demolib/modbus_rtu.lua


+ 0 - 0
script/demolib/mpu6xxx.lua → module/Air780E/demolib/mpu6xxx.lua


+ 0 - 0
script/demolib/necir.lua → module/Air780E/demolib/necir.lua


+ 0 - 0
script/demolib/onenetcoap.lua → module/Air780E/demolib/onenetcoap.lua


+ 0 - 0
script/demolib/openai.lua → module/Air780E/demolib/openai.lua


+ 0 - 0
script/demolib/pac9685.lua → module/Air780E/demolib/pac9685.lua


+ 0 - 0
script/demolib/pca9555.lua → module/Air780E/demolib/pca9555.lua


+ 0 - 0
script/demolib/pca9685.lua → module/Air780E/demolib/pca9685.lua


+ 0 - 0
script/demolib/pcf8563t.lua → module/Air780E/demolib/pcf8563t.lua


+ 0 - 0
script/demolib/pcf8574.lua → module/Air780E/demolib/pcf8574.lua


+ 0 - 0
script/demolib/qmc5883l.lua → module/Air780E/demolib/qmc5883l.lua


+ 0 - 0
script/demolib/rc522.lua → module/Air780E/demolib/rc522.lua


+ 0 - 0
script/demolib/rtkv.lua → module/Air780E/demolib/rtkv.lua


+ 0 - 0
script/demolib/sc7a20.lua → module/Air780E/demolib/sc7a20.lua


+ 0 - 0
script/demolib/shift595.lua → module/Air780E/demolib/shift595.lua


+ 0 - 0
script/demolib/si24r1.lua → module/Air780E/demolib/si24r1.lua


+ 0 - 0
script/demolib/spl06.lua → module/Air780E/demolib/spl06.lua


+ 0 - 0
script/demolib/tcs3472.lua → module/Air780E/demolib/tcs3472.lua


+ 0 - 0
script/demolib/tm1637.lua → module/Air780E/demolib/tm1637.lua


+ 0 - 0
script/demolib/tm1640.lua → module/Air780E/demolib/tm1640.lua


+ 0 - 0
script/demolib/tm1650.lua → module/Air780E/demolib/tm1650.lua


+ 0 - 0
script/demolib/tsl2561.lua → module/Air780E/demolib/tsl2561.lua


+ 0 - 0
script/demolib/vl6180.lua → module/Air780E/demolib/vl6180.lua


+ 0 - 0
script/demolib/ze08g_ch2o.lua → module/Air780E/demolib/ze08g_ch2o.lua


+ 0 - 0
script/demolib/zh07.lua → module/Air780E/demolib/zh07.lua


+ 162 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/ads1115.lua

@@ -0,0 +1,162 @@
+--[[
+@module ads1115
+@summary ads1115 模数转换器 
+@version 1.0
+@date    2022.03.18
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+--注意:ads1115的配置需按照项目需求配置,您需要按照配置寄存器说明重新配置 ADS1115_CONF_HCMD 和 ADS1115_CONF_LCMD !!!
+-- 用法实例
+local ads1115 = require "ads1115"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    ads1115.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local ads1115_data = ads1115.get_val()
+        log.info("ads1115", ads1115_data)
+        sys.wait(1000)
+    end
+end)
+]]
+
+local ads1115 = {}
+
+local sys = require "sys"
+
+local i2cid
+local i2cslaveaddr
+
+local ADS1115_ADDRESS_AD0_LOW       =   0x48 -- address pin low (GND), default for InvenSense evaluation board
+local ADS1115_ADDRESS_AD0_HIGH      =   0x49 -- address pin high (VCC)
+local ADS1115_ADDRESS_AD0_SDA       =   0x4A
+local ADS1115_ADDRESS_AD0_SCL       =   0x4B
+
+-- ADS1115 registers define
+local ADS1115_DATA_REG   	        =   0x00    --转换寄存器
+local ADS1115_CONF_REG			    =   0x01    --配置寄存器
+local ADS1115_LOTH_REG			    =   0x02    --阈值比较器高字节寄存器
+local ADS1115_HITH_REG	            =   0x03    --阈值比较器低字节寄存器
+
+--[[ 
+配置寄存器说明
+config register
+-----------------------------------------------------------------------------------
+CRH[15:8](R/W)
+BIT      15      14      13      12      11      10      9       8
+NAME     OS      MUX2    MUX1    MUX0    PGA2    PGA1    PGA0    MODE
+-----------------------------------------------------------------------------------
+L[7:0] (R/W)
+BIT      7       6       5       4       3       2       1       0
+NAME    DR0     DR1     DR0   COM_MODE COM_POL COM_LAT COM_QUE1 COM_QUE0
+-----------------------------------------------------------------------------------
+15    | OS             |  运行状态和单次转换开始
+    |                | 写时:
+    |                | 0   : 无效
+    |                | 1   : 开始单次转换(处于掉电状态时)
+    |                | 读时:
+    |                | 0   : 正在转换
+    |                | 1   : 未执行转换
+-----------------------------------------------------------------------------------
+14:12 | MUX [2:0]      | 输入复用多路配置
+    |                | 000 : AINP = AIN0 and AINN = AIN1 (default)
+    |                | 001 : AINP = AIN0 and AINN = AIN3
+    |                | 010 : AINP = AIN1 and AINN = AIN3
+    |                | 011 : AINP = AIN2 and AINN = AIN3
+    |                | 100 : AINP = AIN0 and AINN = GND
+    |                | 101 : AINP = AIN1 and AINN = GND
+    |                | 110 : AINP = AIN2 and AINN = GND
+    |                | 111 : AINP = AIN3 and AINN = GND
+-----------------------------------------------------------------------------------
+11:9  | PGA [2:0]      | 可编程增益放大器配置(FSR  full scale range)
+    |                | 000 : FSR = В±6.144 V
+    |                | 001 : FSR = В±4.096 V
+    |                | 010 : FSR = В±2.048 V (默认)
+    |                | 011 : FSR = В±1.024 V
+    |                | 100 : FSR = В±0.512 V
+    |                | 101 : FSR = В±0.256 V
+    |                | 110 : FSR = В±0.256 V
+    |                | 111 : FSR = В±0.256 V
+-----------------------------------------------------------------------------------
+8     | MODE           | 工作模式
+    |                | 0   : 连续转换
+    |                | 1   : 单次转换
+-----------------------------------------------------------------------------------
+7:5   | DR [2:0]       | 采样频率
+    |                | 000 : 8 SPS
+    |                | 001 : 16 SPS
+    |                | 010 : 32 SPS
+    |                | 011 : 64 SPS
+    |                | 100 : 128 SPS (默认)
+    |                | 101 : 250 SPS
+    |                | 110 : 475 SPS
+    |                | 111 : 860 SPS
+-----------------------------------------------------------------------------------
+4     | COMP_MODE      | 比较器模式
+    |                | 0   : 传统比较器 (default)
+    |                | 1   : 窗口比较器
+-----------------------------------------------------------------------------------
+3     | COMP_POL       | Comparator polarity
+    |                | 0   : 低电平有效 (default)
+    |                | 1   : 高电平有效
+-----------------------------------------------------------------------------------
+2     | COMP_LAT       | Latching comparator
+    |                | 0   : 非锁存比较器. (default)
+    |                | 1   : 锁存比较器.
+-----------------------------------------------------------------------------------
+1:0   | COMP_QUE [1:0] | Comparator queue and disable
+    |                | 00  : Assert after one conversion
+    |                | 01  : Assert after two conversions
+    |                | 10  : Assert after four conversions
+    |                | 11  : 禁用比较器并将ALERT/RDY设置为高阻抗 (default)
+-----------------------------------------------------------------------------------
+]]
+
+local ADS1115_CONF_HCMD	            =   0x42	-- AIN0单端输入 В±4.096 V  连续模式  0 100 001 0
+local ADS1115_CONF_LCMD	            =   0x83  	-- 128sps 传统比较器 输出低有效  非锁存比较器 禁用比较器并将ALERT/RDY设置为高阻抗 100 0 0 0 11
+
+--[[
+ADS1115初始化
+@api ads1115.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+ads1115.init(0)
+]]
+function ads1115.init(i2c_id)
+    i2cid = i2c_id
+    -- i2cslaveaddr = ADS1115_ADDRESS_AD0_LOW
+    log.info("ADS1115", "init_ok")
+    return true
+end
+
+--[[
+获取ADS1115数据
+@api ads1115.get_val()
+@return number 光照强度数据,若读取失败会返回nil
+@usage
+local ads1115_data = ads1115.get_val()
+log.info("ads1115", ads1115_data)
+]]
+function ads1115.get_val()
+    i2c.send(i2cid, ADS1115_ADDRESS_AD0_LOW,{ADS1115_CONF_REG,ADS1115_CONF_HCMD,ADS1115_CONF_LCMD})
+    sys.wait(5)
+    i2c.send(i2cid, ADS1115_ADDRESS_AD0_LOW, ADS1115_DATA_REG)
+    local _,val_data_raw = pack.unpack(i2c.recv(i2cid, ADS1115_ADDRESS_AD0_LOW, 2), ">h")
+    if not val_data_raw then
+        return
+    end
+    if val_data_raw>=0x8000 then
+        return ((0xffff-val_data_raw)/32767.0)*4.096
+    else
+        return (val_data_raw/32768.0)*4.096
+    end
+end
+
+return ads1115
+
+
+
+

+ 167 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/ads1115plus.lua

@@ -0,0 +1,167 @@
+--[[
+@module  ads1115plus
+@summary ADS1115驱动
+测量2次,逼近量程测量方案
+可以用8种通道方式,4种差分,4种in0-in3
+ads1115完全驱动
+@version 1.0
+@data    2023/12/14
+@author  杨壮壮
+@usage
+require 'ads1115plus'
+sys.taskInit(function ()
+	i2c.setup(i2cid, i2c_speed)
+    ads1115plus.Setup(i2cid) -- 一次初始化
+	
+	while true do
+		log.info("ads1115:",ads1115plus.task_read(5))
+		sys.wait(1000)
+	end
+
+end)
+]]
+
+
+require 'sys'
+
+_G.sys = require ("sys")
+
+
+
+
+
+
+ads1115plus = {}    --定义存放程序的TABLE 全局变量
+
+local i2cid=1
+local i2cslaveaddr = 0x48
+
+
+--[[
+ADS1115初始化
+@api ads1115plus.Setup(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+i2c.setup(0, i2c_speed)
+ads1115plus.Setup(0)
+]]
+
+function ads1115plus.Setup(i2c_id)
+    i2cid=i2c_id
+    i2c.writeReg(i2cid, i2cslaveaddr, 0x02, string.char(0x00, 0x00)) --给低阈值寄存器写进去值 A1高八位 A2 低八位
+    i2c.writeReg(i2cid, i2cslaveaddr, 0x03, string.char(0xff, 0xff)) --给高阈值寄存器写进去值 A1高八位 A2 低八位
+    log.info("ADS1115", "init_ok")
+    return true
+end
+
+
+
+--[[
+获取ADS1115原始数据
+@api ads1115plus.task_recv(MU1,FSR)
+@number MUX:通道选择 0-7  MUX=0 AIN0 AIN1   MUX=7 AIN3 GND
+MU1
+    |                | 000 : AINP = AIN0 and AINN = AIN1 (default)
+    |                | 001 : AINP = AIN0 and AINN = AIN3
+    |                | 010 : AINP = AIN1 and AINN = AIN3
+    |                | 011 : AINP = AIN2 and AINN = AIN3
+    |                | 100 : AINP = AIN0 and AINN = GND
+    |                | 101 : AINP = AIN1 and AINN = GND
+    |                | 110 : AINP = AIN2 and AINN = GND
+    |                | 111 : AINP = AIN3 and AINN = GND
+
+
+@number FSR:量程选择 0-7   2 2.048v 
+-- 这个参数的选择:8.1:噪声性能
+FSR
+    |                | 000 : FSR = В±6.144 V
+    |                | 001 : FSR = В±4.096 V
+    |                | 010 : FSR = В±2.048 V (默认)
+    |                | 011 : FSR = В±1.024 V
+    |                | 100 : FSR = В±0.512 V
+    |                | 101 : FSR = В±0.256 V
+    |                | 110 : FSR = В±0.256 V
+    |                | 111 : FSR = В±0.256 V
+
+@return number ADC数据,若读取失败会返回nil
+@usage
+local ads1115_data = ads1115plus.task_recv(0,2)
+log.info("ads1115", ads1115_data)
+]]
+function ads1115plus.task_recv( MU1,FSR )   
+    a1 = 0x80 + (MU1 * 16) + (FSR * 2) + 1  --a1为配置寄存器高八位  a2为低八位
+    if FSR > 0x05 then                  --FSR大于5时,切换速率减少噪声,提高采样数据精确度
+        a2 = (0x3 * 32) + 3               --量程超过5的时候,速率配置为64sps
+        i2c.writeReg(i2cid, i2cslaveaddr, 0x01, string.char(a1, a2)) --给配置寄存器写进去值 A1高八位 A2 低八位
+        sys.wait(60)
+    else
+        a2 = (0x4 * 32) + 3               --其他情况下速率为128sps
+        i2c.writeReg(i2cid, i2cslaveaddr, 0x01, string.char(a1, a2)) --给配置寄存器写进去值 A1高八位 A2 低八位
+        sys.wait(30)
+    end
+    
+    -- 从转换就绪引脚读取转换好的采集数据2个字节(四个十六进制的数值)的数据
+    local data = i2c.readReg(i2cid, i2cslaveaddr,0x00 , 2)  
+
+    local _,val_data_raw = pack.unpack(data, ">h")
+
+    if not val_data_raw then
+        return
+    end
+    if val_data_raw>=0x8000 then
+        return ((0xffff-val_data_raw))
+    else
+        return (val_data_raw)
+    end
+
+end
+
+--[[
+ADS1115读取mv值
+@api ads1115plus.task_read(MU1)
+@number 所在的i2c总线id
+@return number ADC的mv数据,若读取失败会返回nil
+@usage
+log.info("ads1115plus:",ads1115plus.task_read(5))
+]]
+
+
+function ads1115plus.task_read(MU1)
+    local ads = ads1115plus.task_recv(MU1,0) --先用大量程测量正负6.144去测量 逼近
+	if ads == nil then 
+		return nil             --判断采样数值是否非空
+	end
+    local ads_ADC=0
+	if ads < 0 then
+		ads_ADC = -ads
+	else
+		ads_ADC = ads
+	end
+    local mv =0
+	if 	ads_ADC > 10922 then	
+        if ads_ADC>20000 then
+            mv = ads * 0.1875
+        else
+            ads = ads1115plus.task_recv(MU1,1)
+            mv = ads * 0.125
+        end
+		
+	elseif 	ads_ADC > 5461 then	
+		ads = ads1115plus.task_recv(MU1,2)
+		mv = ads * 0.0625
+	elseif 	ads_ADC > 2730 then
+		ads = ads1115plus.task_recv(MU1,3)
+		mv = ads * 0.03125
+	elseif ads_ADC > 1365 then 	
+		ads = ads1115plus.task_recv(MU1,4)
+		mv = ads * 0.015625
+	else
+		ads = ads1115plus.task_recv(MU1,5)
+		mv = ads * 0.0078125
+	end
+	return mv   --返回采集到的电压值 单位(mV)有正有负  
+end
+
+return ads1115plus
+

+ 193 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/adxl34x.lua

@@ -0,0 +1,193 @@
+--[[
+@module adxl34x
+@summary adxl34x 3轴加速度计 目前支持 adxl345 adxl346
+@version 1.0
+@date    2022.04.11
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local adxl34x = require "adxl34x"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    adxl34x.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local adxl34x_data = adxl34x.get_data()
+        log.info("adxl34x_data", "adxl34x_data.x"..(adxl34x_data.x),"adxl34x_data.y"..(adxl34x_data.y),"adxl34x_data.z"..(adxl34x_data.z))
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local adxl34x = {}
+local sys = require "sys"
+local i2cid
+
+local ADXL34X_ADDRESS_ADR
+
+local ADXL34X_ADDRESS_ADR_LOW     =   0x53
+local ADXL34X_ADDRESS_ADR_HIGH    =   0x1D
+
+local ADXL34X_CHIP_ID_CHECK       =   0x00
+local ADXL345_CHIP_ID             =   0xE5
+local ADXL346_CHIP_ID             =   0xE6
+
+---器件所用地址
+
+local ADXL34X_THRESH_TAP          =   0x1D --敲击阈值
+local ADXL34X_OFSX                =   0x1E --X轴偏移
+local ADXL34X_OFSY                =   0x1F --Y轴偏移
+local ADXL34X_OFSZ                =   0x20 --Z轴偏移
+local ADXL34X_DUR                 =   0x21 --敲击持续时间
+local ADXL34X_Latent              =   0x22 --敲击延迟
+local ADXL34X_Window              =   0x23 --敲击窗口
+local ADXL34X_THRESH_ACT          =   0x24 --活动阈值
+local ADXL34X_THRESH_INACT        =   0x25 --静止阈值
+local ADXL34X_TIME_INACT          =   0x26 --静止时间
+local ADXL34X_ACT_INACT_CTL       =   0x27 --轴使能控制活动和静止检测
+local ADXL34X_THRESH_FF           =   0x28 --自由落体阈值
+local ADXL34X_TIME_FF             =   0x29 --自由落体时间
+local ADXL34X_TAP_AXES            =   0x2A --单击/双击轴控制
+local ADXL34X_ACT_TAP_STATUS      =   0x2B --单击/双击源
+local ADXL34X_BW_RATE             =   0x2C --数据速率及功率模式控制
+local ADXL34X_POWER_CTL           =   0x2D --省电特性控制
+local ADXL34X_INT_ENABLE          =   0x2E --中断使能控制
+local ADXL34X_INT_MAP             =   0x2F --中断映射控制
+local ADXL34X_INT_SOURCE          =   0x30 --中断源
+local ADXL34X_DATA_FORMAT         =   0x31 --数据格式控制
+local ADXL34X_DATAX0              =   0x32 --X轴数据0
+local ADXL34X_DATAX1              =   0x33 --X轴数据1
+local ADXL34X_DATAY0              =   0x34 --Y轴数据0
+local ADXL34X_DATAY1              =   0x35 --Y轴数据1
+local ADXL34X_DATAZ0              =   0x36 --Z轴数据0
+local ADXL34X_DATAZ1              =   0x37 --Z轴数据1
+local ADXL34X_FIFO_CTL            =   0x38 --FIFO控制
+local ADXL34X_FIFO_STATUS         =   0x39 --FIFO状态
+
+local ADXL346_TAP_SIGN            =   0x3A --单击/双击的符号和来源
+local ADXL346_ORIENT_CONF         =   0x3B --方向配置
+local ADXL346_Orient              =   0x3C --方向状态
+
+--器件ID检测
+local function chip_check()
+    i2c.send(i2cid, ADXL34X_ADDRESS_ADR_LOW, ADXL34X_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, ADXL34X_ADDRESS_ADR_LOW, 1)
+    if revData:byte() ~= nil then
+        ADXL34X_ADDRESS_ADR = ADXL34X_ADDRESS_ADR_LOW
+    else
+        i2c.send(i2cid, ADXL34X_ADDRESS_ADR_HIGH, ADXL34X_CHIP_ID_CHECK)--读器件地址
+        sys.wait(50)
+        local revData = i2c.recv(i2cid, ADXL34X_ADDRESS_ADR_HIGH, 1)
+        if revData:byte() ~= nil then
+            ADXL34X_ADDRESS_ADR = ADXL34X_ADDRESS_ADR_HIGH
+        else
+            log.info("i2c", "Can't find adxl34x device")
+            return false
+        end
+    end
+    i2c.send(i2cid, ADXL34X_ADDRESS_ADR, ADXL34X_CHIP_ID_CHECK)--读器件地址
+    sys.wait(50)
+    local revData = i2c.recv(i2cid, ADXL34X_ADDRESS_ADR, 1)
+    if revData:byte() == ADXL345_CHIP_ID then
+        log.info("Device i2c id is: ADXL345")
+    elseif revData:byte() == ADXL346_CHIP_ID then
+        log.info("Device i2c id is: ADXL346")
+    else
+        log.info("i2c", "Can't find adxl34x device")
+        return false
+    end
+    return true
+end
+
+--[[
+adxl34x 初始化
+@api adxl34x.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+adxl34x.init(0)
+]]
+function adxl34x.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(20)--20 毫秒等待设备稳定
+    if chip_check() then
+        i2c.send(i2cid, ADXL34X_ADDRESS_ADR, {ADXL34X_BW_RATE,0X0D})
+        i2c.send(i2cid, ADXL34X_ADDRESS_ADR, {ADXL34X_POWER_CTL,0X08})
+        i2c.send(i2cid, ADXL34X_ADDRESS_ADR, {ADXL34X_DATA_FORMAT,0X2B})
+        log.info("adxl34x init_ok")
+        sys.wait(20)
+        return true
+    end
+    return false
+end
+
+--[[
+获取 adxl34x 数据
+@api adxl34x.get_data()
+@return table adxl34x 数据
+@usage
+local adxl34x_data = adxl34x.get_data()
+log.info("adxl34x_data", "adxl34x_data.x"..(adxl34x_data.x),"adxl34x_data.y"..(adxl34x_data.y),"adxl34x_data.z"..(adxl34x_data.z))
+]]
+function adxl34x.get_data()
+    local accel={x=nil,y=nil,z=nil}
+    i2c.send(i2cid, ADXL34X_ADDRESS_ADR,ADXL34X_DATAX0)
+    _,accel.x = pack.unpack(i2c.recv(i2cid, ADXL34X_ADDRESS_ADR, 2),">h")
+    i2c.send(i2cid, ADXL34X_ADDRESS_ADR,ADXL34X_DATAY0)
+    _,accel.y = pack.unpack(i2c.recv(i2cid, ADXL34X_ADDRESS_ADR, 2),">h")
+    i2c.send(i2cid, ADXL34X_ADDRESS_ADR,ADXL34X_DATAZ0)
+    _,accel.z = pack.unpack(i2c.recv(i2cid, ADXL34X_ADDRESS_ADR, 2),">h")
+    return accel
+end
+
+--[[
+获取 adxl34x 中断源
+@api adxl34x.get_int_source()
+@number 所在的i2c总线id
+@usage
+adxl34x.get_int_source(i2cid)
+]]
+function adxl34x.get_int_source(i2cid)
+    i2c.readReg(i2cid, ADXL34X_ADDRESS_ADR, ADXL34X_INT_SOURCE, 2)
+end
+
+--[[
+设置 adxl34x 活动和静止阀值
+@api adxl34x.set_thresh(i2cid, activity, inactivity, time_inactivity)
+@number 所在的i2c总线id
+@number 活动阀值
+@number 静止阀值
+@number 静止时间
+@usage
+adxl34x.set_thresh(i2cid, string.char(0x05), string.char(0x02), string.char(0x05)) 
+log.info("adxl34x_data", "adxl34x_data.x"..(adxl34x_data.x),"adxl34x_data.y"..(adxl34x_data.y),"adxl34x_data.z"..(adxl34x_data.z))
+]]
+function adxl34x.set_thresh(i2cid, activity, inactivity, time_inactivity)
+    i2c.writeReg(i2cid, ADXL34X_ADDRESS_ADR, ADXL34X_THRESH_ACT, activity)
+    i2c.writeReg(i2cid, ADXL34X_ADDRESS_ADR, ADXL34X_THRESH_INACT, inactivity)
+    i2c.writeReg(i2cid, ADXL34X_ADDRESS_ADR, ADXL34X_THRESH_INACT, time_inactivity)
+end
+
+--[[
+adxl34x 中断设置
+@api adxl34x.set_irqf(i2cid, irqf_map, irqf_act_ctl, irqf_enable)
+@number 所在的i2c总线id
+@number 中断映射
+@number 中断活动控制
+@number 中断使能
+@usage
+adxl34x.set_irqf(i2cid, string.char(0x10), string.char(0xff), string.char(0x10))
+]]
+function adxl34x.set_irqf(i2cid, irqf_map, irqf_act_ctl, irqf_enable)
+    i2c.writeReg(i2cid, ADXL34X_ADDRESS_ADR, ADXL34X_INT_ENABLE, string.char(0x00))     -- 关闭所有中断
+    i2c.writeReg(i2cid, ADXL34X_ADDRESS_ADR, ADXL34X_INT_MAP, irqf_map)
+    i2c.writeReg(i2cid, ADXL34X_ADDRESS_ADR, ADXL34X_ACT_INACT_CTL, irqf_act_ctl)
+    i2c.writeReg(i2cid, ADXL34X_ADDRESS_ADR, ADXL34X_INT_ENABLE, irqf_enable)           -- 开启中断
+end
+
+return adxl34x
+
+

+ 101 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/aht10.lua

@@ -0,0 +1,101 @@
+--[[
+@module aht10
+@summary aht10 温湿度传感器
+@version 1.0
+@date    2022.03.10
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local aht10 = require "aht10"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    aht10.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local aht10_data = aht10.get_data()
+        log.info("aht10_data", "aht10_data.RH:"..(aht10_data.RH*100).."%","aht10_data.T"..(aht10_data.T).."℃")
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local aht10 = {}
+local sys = require "sys"
+local i2cid
+
+local AHT10_ADDRESS_ADR_LOW       =   0x38
+
+---器件所用地址
+local AHT10_INIT                  =   0xE1 --初始化命令
+local AHT10_MEASURE               =   0xAC --触发测量命令
+local AHT10_SOFT_RESET            =   0xBA --软复位命令,软复位所需时间不超过20毫秒.
+
+local AHT10_STATE                 =   0x71 --状态字.
+
+--[[
+aht10初始化
+@api aht10.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+aht10.init(0)
+]]
+function aht10.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(40)--40 毫秒等待设备稳定
+    i2c.send(i2cid, AHT10_ADDRESS_ADR_LOW, AHT10_SOFT_RESET)--软复位
+    sys.wait(20)
+    i2c.send(i2cid, AHT10_ADDRESS_ADR_LOW, AHT10_STATE)
+    local data = i2c.recv(i2cid, AHT10_ADDRESS_ADR_LOW, 1)
+    local _,state = pack.unpack(data, "b")
+    if not state then
+        log.info("aht10", "not found")
+        return
+    end
+    if bit.isclear(state,3) then
+        i2c.send(i2cid, AHT10_ADDRESS_ADR_LOW, {AHT10_INIT,0x08,0x00})--初始化
+    end
+    sys.wait(20)
+    log.info("aht10 init_ok")
+    return true
+end
+
+--获取原始数据
+local function aht10_get_raw_data()
+    local raw_data={Srh=nil,St=nil}
+    i2c.send(i2cid, AHT10_ADDRESS_ADR_LOW, {AHT10_MEASURE, 0x33, 0x00})
+    sys.wait(80)--等待80毫秒以上
+    i2c.send(i2cid, AHT10_ADDRESS_ADR_LOW, AHT10_STATE)
+    local data = i2c.recv(i2cid, AHT10_ADDRESS_ADR_LOW, 1)
+    local _,state = pack.unpack(data, "b")
+    -- if bit.isclear(state,7) then
+        local data = i2c.recv(i2cid, AHT10_ADDRESS_ADR_LOW, 6)
+        local _, data1, data2, data3, data4, data5, data6 = pack.unpack(data, "b6")
+        raw_data.Srh = bit.bor(bit.bor(bit.rshift(data4, 4), bit.lshift(data3, 4)),bit.lshift(data2, 12))
+        raw_data.St = bit.bor(bit.bor(bit.lshift(bit.band(data4, 0x0f), 16), bit.lshift(data5, 8)), data6)
+    -- end
+    return raw_data or 0
+end
+
+--[[
+获取aht10数据
+@api aht10.get_data()
+@return table aht10数据
+@usage
+local aht10_data = aht10.get_data()
+log.info("aht10_data", "aht10_data.RH:"..(aht10_data.RH*100).."%","aht10_data.T"..(aht10_data.T).."℃")
+]]
+function aht10.get_data()
+    local aht10_data={RH=nil,T=nil}
+    local raw_data = aht10_get_raw_data()
+    aht10_data.RH = raw_data.Srh/1048576
+    aht10_data.T = raw_data.St/1048576*200-50
+    return aht10_data or 0
+end
+
+return aht10
+
+

+ 165 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/ak8963.lua

@@ -0,0 +1,165 @@
+--[[
+@module ak8963
+@summary ak8963 地磁传感器
+@version 1.0
+@date    2023.06.07
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local ak8963 = require "ak8963"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    ak8963.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        sys.wait(100)
+        local mag = ak8963.get_mag()--获取地磁仪
+        log.info("ak8963 mag", "mag.x",mag.x,"mag.y",mag.y,"mag.z",mag.z)
+    end
+end)
+]]
+
+
+local ak8963 = {}
+local sys = require "sys"
+local i2cid
+local i2cslaveaddr
+local deviceid
+
+local AK8963_ADDRESS_AD0_LOW     =   0x0C  
+local AK8963_ADDRESS_AD0_HIGH    =   0x0D  
+
+---器件通讯地址
+local AK8963_WHO_AM_I            =   0x48 -- AK8963
+
+---AK8963所用地址
+-- Read-only Reg
+local AK8963_RA_WHO_AM_I         =   0x00
+local AK8963_REG_INFO            =   0x01
+local AK8963_REG_ST1             =   0x02
+local AK8963_REG_HXL             =   0x03
+local AK8963_REG_HXH             =   0x04
+local AK8963_REG_HYL             =   0x05
+local AK8963_REG_HYH             =   0x06
+local AK8963_REG_HZL             =   0x07
+local AK8963_REG_HZH             =   0x08
+local AK8963_REG_ST2             =   0x09
+-- Write/Read Reg
+local AK8963_REG_CNTL1           =   0x0A
+local AK8963_REG_CNTL2           =   0x0B
+local AK8963_REG_ASTC            =   0x0C
+local AK8963_REG_TS1             =   0x0D
+local AK8963_REG_TS2             =   0x0E
+local AK8963_REG_I2CDIS          =   0x0F
+-- Read-only Reg (ROM)
+local AK8963_REG_ASAX            =   0x10
+local AK8963_REG_ASAY            =   0x11
+local AK8963_REG_ASAZ            =   0x12
+-- Status
+local AK8963_STATUS_DRDY         =   0x01
+local AK8963_STATUS_DOR          =   0x02
+local AK8963_STATUS_HOFL         =   0x08
+
+local AK8963_ASAX
+local AK8963_ASAY
+local AK8963_ASAZ
+
+--器件ID检测
+local function ak8963_check()
+    i2c.send(i2cid, AK8963_ADDRESS_AD0_LOW, AK8963_RA_WHO_AM_I)--读器件地址
+    sys.wait(50)
+    local revData = i2c.recv(i2cid, AK8963_ADDRESS_AD0_LOW, 1)
+    if revData:byte() ~= nil then
+        i2cslaveaddr = AK8963_ADDRESS_AD0_LOW
+    else
+        i2c.send(i2cid, AK8963_ADDRESS_AD0_HIGH, AK8963_RA_WHO_AM_I)--读器件地址
+        sys.wait(50)
+        local revData = i2c.recv(i2cid, AK8963_ADDRESS_AD0_HIGH, 1)
+        if revData:byte() ~= nil then
+            i2cslaveaddr = AK8963_ADDRESS_AD0_HIGH
+        else
+            log.info("i2c", "Can't find device")
+            return false
+        end
+    end
+    i2c.send(i2cid, i2cslaveaddr, AK8963_RA_WHO_AM_I)--读器件地址
+    sys.wait(50)
+    local revData = i2c.recv(i2cid, i2cslaveaddr, 1)
+    log.info("Device i2c address is:", revData:toHex())
+    if revData:byte() == AK8963_WHO_AM_I then
+        deviceid = AK8963_WHO_AM_I
+        log.info("Device i2c id is: AK8963")
+    else
+        log.info("i2c", "Can't find device")
+        return false
+    end
+    return true
+end
+
+--[[
+ak8963初始化
+@api ak8963.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+ak8963.init(0)
+]]
+function ak8963.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(20)
+    if ak8963_check() then
+        i2c.send(i2cid, i2cslaveaddr, {AK8963_REG_CNTL1, 0x0F})
+        i2c.send(i2cid, i2cslaveaddr,AK8963_REG_ASAX)
+        AK8963_ASAX = i2c.recv(i2cid, i2cslaveaddr, 1):byte()
+        i2c.send(i2cid, i2cslaveaddr,AK8963_REG_ASAY)
+        AK8963_ASAY = i2c.recv(i2cid, i2cslaveaddr, 1):byte()
+        i2c.send(i2cid, i2cslaveaddr,AK8963_REG_ASAZ)
+        AK8963_ASAZ = i2c.recv(i2cid, i2cslaveaddr, 1):byte()
+        i2c.send(i2cid, i2cslaveaddr, {AK8963_REG_CNTL1, 0x10})
+        sys.wait(10)
+        log.info("ak8963 init_ok")
+        return true
+    end
+    return false
+end
+
+--获取地磁计的原始数据
+local function ak8963_get_mag_raw()
+    local mag={x=nil,y=nil,z=nil}
+    i2c.send(i2cid, i2cslaveaddr,AK8963_REG_HXL)
+    local x = i2c.recv(i2cid, i2cslaveaddr, 2)
+    _,mag.x = pack.unpack(x,"<h")
+    i2c.send(i2cid, i2cslaveaddr,AK8963_REG_HYL)
+    local y = i2c.recv(i2cid, i2cslaveaddr, 2)
+    _,mag.y = pack.unpack(y,"<h")
+    i2c.send(i2cid, i2cslaveaddr,AK8963_REG_HZL)
+    local z = i2c.recv(i2cid, i2cslaveaddr, 2)
+    _,mag.z = pack.unpack(z,"<h")
+    return mag or 0
+end
+
+
+--[[
+获取地磁仪的数据
+@api ak8963.get_mag()
+@return table 地磁仪数据
+@usage
+local mag = ak8963.get_mag()--获取地磁仪
+log.info("ak8963 mag", "mag.x",mag.x,"mag.y",mag.y,"mag.z",mag.z)
+]]
+function ak8963.get_mag()
+    local mag={x=nil,y=nil,z=nil}
+    i2c.send(i2cid, i2cslaveaddr, {AK8963_REG_CNTL1, 0x11})
+    sys.wait(10)
+    local tmp = ak8963_get_mag_raw()
+    mag.x = tmp.x*((AK8963_ASAX-128)*0.5/128+1)
+    mag.y = tmp.y*((AK8963_ASAY-128)*0.5/128+1)
+    mag.z = tmp.z*((AK8963_ASAZ-128)*0.5/128+1)
+    return mag
+end
+
+return ak8963
+
+

+ 463 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/aliyun.lua

@@ -0,0 +1,463 @@
+--[[
+@module aliyun
+@summary AliYun阿里云物联网平台
+@version 1.0
+@date    2023.06.07
+@author  wendal
+@demo    aliyun
+@usage
+-- 请查阅demo
+]]
+_G.sys = require("sys")
+--[[特别注意, 使用http库需要下列语句]]
+_G.sysplus = require("sysplus")
+local libfota = require("libfota")
+
+-- 总的库对象
+local aliyun = {}
+local mqttc = nil
+
+local ClientId,PassWord,UserName,SetClientidFnc,SetDeviceTokenFnc,SetDeviceSecretFnc
+local EvtCb = {}
+local opts = {}
+
+-------------------------------------------------------
+---- FOTA 相关
+-------------------------------------------------------
+-- fota的回调函数
+local function libfota_cb(result)
+    log.info("fota", "result", result)
+    -- fota成功
+    if result == 0 then
+        rtos.reboot()
+    end
+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
+
+-------------------------------------------------------
+--- 用户侧的回调处理
+-------------------------------------------------------
+
+
+
+--底层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
+
+        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
+end
+
+local function mqtt_task(mqtt_host, mqtt_port, mqtt_isssl, client_id, user_name, password)
+    mqttc = mqtt.create(nil,mqtt_host, mqtt_port or 1883, 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:debug(true)
+    mqttc:connect()
+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
+
+--根据返回的数据进行二次加密
+local function directProc(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,Registration)
+    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)
+    sys.taskInit(function()
+        --预注册
+        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=123,signmethod=hmacmd5|"
+            end
+        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
+                            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
+                end
+            end
+        end)
+        mqttClient:connect()
+        while flag do
+            sys.wait(1000)
+        end
+    end)
+end
+
+---------------------------------------------
+-- 一机一密
+---------------------------------------------
+local function confiDentialTask()
+    sys.taskInit(function()
+        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后就是正常的一机一密连接
+function aliyun.clientGetDirectDataTask(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,Registration,DeviceSecret,deviceToken,cid)
+    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三元组配置
+            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三元组配置
+            mqtt_task(mqtt_host, mqtt_port,mqtt_isssl, clientId, user_name, deviceToken)
+        end
+    end)
+end
+
+
+--[[
+订阅主题
+@api aliyun.subscribe(topic,qos)
+@string 主题内容为UTF8编码
+@number qos为number类型(0/1,默认1)
+@return nil
+@usage
+aliyun.subscribe("/b0FMK1Ga5cp/862991234567890/get", 1)
+]]
+function aliyun.subscribe(topic,qos)
+    if mqttc and mqttc:ready() then
+        mqttc:subscribe(topic,qos or 1)
+    end
+end
+
+
+--[[
+发布一条消息
+@api aliyun.publish(topic,qos,payload,cbFnc,cbPara)
+@string UTF8编码的主题
+@number qos质量等级,0/1,默认0
+@string payload 负载内容,UTF8编码
+@function cbFnc 消息发布结果的回调函数,回调函数的调用形式为:cbFnc(result,cbPara)。result为true表示发布成功,false或者nil表示订阅失败;cbPara为本接口中的第5个参数
+@param cbPara 消息发布结果回调函数的回调参数
+@return nil
+@usage
+aliyun.publish("/b0FMK1Ga5cp/862991234567890/update",0,"test")
+aliyun.publish("/b0FMK1Ga5cp/862991234567890/update",1,"test",cbFnc,"cbFncPara")
+]]
+function aliyun.publish(topic,qos,payload,cbFnc,cbPara)
+    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
+
+
+--[[
+注册事件的处理函数
+@api aliyun.on(evt,cbFnc)
+@string evt事件,
+"connect"表示接入服务器连接结果事件,
+"receive"表示接收到接入服务器的消息事件,
+"publish"表示发送消息的结果事件
+@function cbFnc 事件的处理函数
+当evt为"connect"时,cbFnc的调用形式为:cbFnc(result),result为true表示连接成功,false或者nil表示连接失败,
+当evt为"receive"时,cbFnc的调用形式为:cbFnc(topic,payload),topic为UTF8编码的主题(string类型),payload为原始编码的负载(string类型),
+当evt为"publish"时,cbFnc的调用形式为:cbFnc(result),result为true表示发送成功,false或者nil表示发送失败
+@return nil
+@usage
+aliyun.on("connect",cbFnc)
+]]
+function aliyun.on(evt,cbFnc)
+	EvtCb[evt] = cbFnc
+end
+
+
+--[[
+@api aliyun.getDeviceSecret()
+@return string 预注册一型一密阿里云返回的DeviceSecret
+可以在应用层使用kv区来保存该参数并使用判断来避免重启后无法连接
+]]
+function aliyun.getDeviceSecret()
+    return SetDeviceSecretFnc
+end
+
+--[[
+@api aliyun.getDeviceToken()
+@return string 免预注册一型一密阿里云返回的DeviceToken
+可以在应用层使用kv区来保存该参数并使用判断来避免重启后无法连接
+]]
+function aliyun.getDeviceToken()
+    return SetDeviceTokenFnc
+end
+
+--[[
+@api aliyun.getClientid()
+@return string 免预注册一型一密阿里云返回的Clientid
+可以在应用层使用kv区来保存该参数并使用判断来避免重启后无法连接
+]]
+function aliyun.getClientid()
+    return SetClientidFnc
+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
+
+--[[
+判断阿里云物联网套件是否已经连接
+@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
+
+--[[
+获取或存储注册信息
+@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
+
+return aliyun

+ 73 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/am2320.lua

@@ -0,0 +1,73 @@
+--[[
+@module am2320
+@summary am2320 温湿度传感器
+@version 1.0
+@date    2023.07.31
+@author  wendal
+@demo    am2320
+@usage
+-- 用法实例
+local am2320 = require "am2320"
+sys.taskInit(function()
+    local i2c_id = 0
+    i2c.setup(i2c_id, i2c.FAST)
+    while 1 do
+        local t, h = am2320.read(i2c_id)
+        log.info("am2320", "温度", t, "湿度", h)
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local am2320 = {}
+
+--[[
+读取温湿度数据
+@api am2320.read(i2c_id)
+@int i2c总线的id, 默认是0, 需要按实际接线来填, 例如0/1/2/3
+@return number 温度,单位摄氏度,若读取失败会返回nil
+@return number 相对湿度,单位1%,若读取失败会返回nil
+]]
+function am2320.read(i2c_id)
+    if not i2c_id then
+        i2c_id = 0
+    end
+    local i2cslaveaddr = 0x5C -- 8bit地址为0xb8 7bit 为0x5C
+    i2c.send(i2c_id, i2cslaveaddr, 0x03)
+    -- 查询功能码:0x03 查询的寄存器首地址:0 长度:4
+    i2c.send(i2c_id, i2cslaveaddr, {0x03, 0x00, 0x04})
+    local _, ismain = coroutine.running()
+    if ismain then
+        timer.mdelay(2)
+    else
+        sys.wait(2)
+    end
+    local data = i2c.recv(i2c_id, i2cslaveaddr, 8)
+
+    -- 传感器返回的8位数据格式:
+    --    1       2       3       4       5       6       7       8
+    --  0x03    0x04    0x03    0x39     0x01    0x15    0xE1    0XFE
+    -- 功能码  数据长度   湿度高位 湿度数据 温度高位  温度低位 CRC低  CRC高
+
+    if data == nil or #data ~= 8 then
+        return
+    end
+    -- log.info("AM2320", "buf data:", buf)
+    -- log.info("AM2320", "HEX data:", data:toHex())
+
+    local _, crc = pack.unpack(data, '<H', 7)
+    data = data:sub(1, 6)
+    if crc == crypto.crc16_modbus(data, 6) then
+        local _, hum, tmp = pack.unpack(string.sub(data, 3, -1), '>H2')
+        -- 正负温度处理
+        if tmp >= 0x8000 then
+            tmp = 0x8000 - tmp
+        end
+        tmp, hum = tmp / 10, hum / 10
+        -- log.info("AM2320", "data(tmp hum):", tmp, hum)
+        return tmp, hum
+    end
+end
+
+return am2320

+ 135 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/ap3216c.lua

@@ -0,0 +1,135 @@
+
+--[[
+@module ap3216c
+@summary ap3216c 光照传感器 
+@version 1.0
+@date    2023.12.20
+@author  xwtx
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+PROJECT = "ap3216c"
+VERSION = "1.0.0"
+sys = require("sys")
+ap3216c=require("ap3216c")
+
+local i2c_id = 1
+
+function ap32_test()
+    i2c.setup(i2c_id,i2c.SLOW)
+    ap3216c.init(i2c_id)
+    sys.wait(120)
+    while true do
+        local ir=ap3216c.ir()
+        local ALS=ap3216c.als()
+        local PS=ap3216c.ps()
+        log.info("ap3216 read ir",ir)
+        log.info("ap3216 read ALS",ALS)
+        log.info("ap3216 read PS",PS)
+        sys.wait(500)
+    end
+end
+
+sys.taskInit(ap32_test)
+
+
+sys.run()
+]]
+
+
+local ap3216c={}
+
+local AP3216C_Addr=0x1e
+local i2c_id=1
+
+--[[
+初始化ap3216c
+@api ap3216c.init(i2cid)
+@int i2cid 使用的i2c id, 或者是软件i2c的实例
+@return nil 无返回值
+]]
+function ap3216c.init(i2cid)
+    i2c_id = i2cid
+    i2c.send(i2c_id, AP3216C_Addr, {0x00,0x04})
+    sys.wait(50)
+    i2c.send(i2c_id, AP3216C_Addr, {0x00,0x03})
+    --i2c.send(i2c_id, AP3216C_Addr, {0x00})
+    --local data = i2c.recv(i2c_id, AP3216C_Addr, 1)
+    --log.info("date",data:toHex())
+end
+
+--[[
+读取红外强度
+@api ap3216c.ir()
+@return int 返回红外强度值,如果读取失败会返回nil
+]]
+function ap3216c.ir()
+    i2c.send(i2c_id, AP3216C_Addr, {0x0a})
+    local data0= i2c.recv(i2c_id, AP3216C_Addr, 1)
+    --log.info("date",data0:toHex())
+    i2c.send(i2c_id, AP3216C_Addr, {0x0b})
+    local data1= i2c.recv(i2c_id, AP3216C_Addr, 1)
+    --log.info("date",data1:toHex())
+    if #data0 ~= 1 and #data1 ~=1 then
+        return
+    end
+
+    local _,ir_l = pack.unpack(data0, "b")
+    local _,ir_h = pack.unpack(data1, "b")
+    if bit.band(ir_l,0x80) ==0 then
+        return bit.bor(bit.lshift( ir_h, 2 ),bit.band(ir_l,0x03))
+    else
+        log.info("read IR failed")
+        return nil
+    end
+end
+
+--[[
+读取光强
+@api ap3216c.als()
+@return int 返回光强值,如果读取失败会返回nil
+]]
+function ap3216c.als()
+    i2c.send(i2c_id, AP3216C_Addr, {0x0c})
+    local data0= i2c.recv(i2c_id, AP3216C_Addr, 1)
+    --log.info("date",data0:toHex())
+    i2c.send(i2c_id, AP3216C_Addr, {0x0d})
+    local data1= i2c.recv(i2c_id, AP3216C_Addr, 1)
+    --log.info("date",data1:toHex())
+
+    if #data0 ~= 1 and #data1 ~=1 then
+        return
+    end
+
+    local _,ir_l = pack.unpack(data0, "b")
+    local _,ir_h = pack.unpack(data1, "b")
+   
+    return bit.bor(bit.lshift( ir_h, 8 ),ir_l)
+  
+end
+
+--[[
+读取距离
+@api ap3216c.ps()
+@return int 返回距离值,如果读取失败会返回nil
+]]
+function ap3216c.ps()
+    i2c.send(i2c_id, AP3216C_Addr, {0x0e})
+    local data0= i2c.recv(i2c_id, AP3216C_Addr, 1)
+    --log.info("date",data0:toHex())
+    i2c.send(i2c_id, AP3216C_Addr, {0x0f})
+    local data1= i2c.recv(i2c_id, AP3216C_Addr, 1)
+    --log.info("date",data1:toHex())
+    
+    if #data0 ~= 1 and #data1 ~=1 then
+        return
+    end
+
+    local _,ir_l = pack.unpack(data0, "b")
+    local _,ir_h = pack.unpack(data1, "b")
+   
+    return bit.bor(bit.lshift(bit.band(ir_h,0x3f), 4),bit.band(ir_l,0x0f))
+  
+end
+
+return ap3216c

+ 105 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/bh1750.lua

@@ -0,0 +1,105 @@
+--[[
+@module bh1750
+@summary bh1750 数字型光强度传感器
+@version 1.0
+@date    2022.03.15
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local bh1750 = require "bh1750"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    bh1750.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local bh1750_data = bh1750.read_light()
+        log.info("bh1750_read_light", bh1750_data)
+        sys.wait(1000)
+    end
+end)
+]]
+
+local bh1750 = {}
+
+local sys = require "sys"
+
+local i2cid
+
+local BH1750_ADDRESS_AD0_LOW     =   0x23 -- address pin low (GND), default for InvenSense evaluation board
+local BH1750_ADDRESS_AD0_HIGH    =   0x24 -- address pin high (VCC)
+
+local i2cslaveaddr = BH1750_ADDRESS_AD0_LOW
+
+-- bh1750 registers define
+local BH1750_POWER_DOWN   	    =   0x00	-- power down
+local BH1750_POWER_ON			=   0x01	-- power on
+local BH1750_RESET			    =   0x07	-- reset
+local BH1750_CON_H_RES_MODE	    =   0x10	-- Continuously H-Resolution Mode
+local BH1750_CON_H_RES_MODE2	=   0x11	-- Continuously H-Resolution Mode2
+local BH1750_CON_L_RES_MODE	    =   0x13	-- Continuously L-Resolution Mode
+local BH1750_ONE_H_RES_MODE	    =   0x20	-- One Time H-Resolution Mode
+local BH1750_ONE_H_RES_MODE2	=   0x21	-- One Time H-Resolution Mode2
+local BH1750_ONE_L_RES_MODE	    =   0x23	-- One Time L-Resolution Mode
+
+local function i2c_send(data)
+    i2c.send(i2cid, i2cslaveaddr, data)
+end
+
+local function i2c_recv(num)
+    local revData = i2c.recv(i2cid, i2cslaveaddr, num)
+    return revData
+end
+
+function bh1750.power_on()
+    i2c_send(BH1750_POWER_ON)
+end
+
+function bh1750.power_down()
+    i2c_send(BH1750_POWER_DOWN)
+end
+
+local function bh1750_set_measure_mode(mode,time)
+    i2c_send(BH1750_RESET)
+    i2c_send(mode)
+    sys.wait(time)
+end
+
+--[[
+bh1750初始化
+@api bh1750.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+bh1750.init(0)
+]]
+function bh1750.init(i2c_id)
+    i2cid = i2c_id
+    bh1750.power_on()
+    log.info("bh1750 init_ok")
+    return true
+end
+
+--[[
+获取bh1750数据
+@api bh1750.read_light()
+@return number 光照强度数据, 若读取失败会返回nil
+@usage
+local bh1750_data = bh1750.read_light()
+log.info("bh1750_read_light", bh1750_data)
+]]
+function bh1750.read_light()
+    bh1750_set_measure_mode(BH1750_CON_H_RES_MODE, 180)
+    -- local _,light = pack.unpack(i2c_recv(2),">h") -- 极端情况下数据溢出导致的光照出现负值, 如string.toHex(i2c_recv(2)) == "FFFF"
+    local _,light = pack.unpack(i2c_recv(2),">H")
+    if light then
+        return light / 1.2
+    end
+end
+
+return bh1750
+
+
+
+

+ 618 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/bmx.lua

@@ -0,0 +1,618 @@
+--[[
+@module bmx
+@summary bmx 气压传感器 目前支持bmp180 bmp280 bme280 bme680 会自动判断器件
+@version 1.0
+@date    2022.04.9
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local bmx = require "bmx"
+i2cid = 0
+i2c_speed = i2c.SLOW
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    bmx.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local bmx_data = bmx.get_data()
+        if bmx_data.temp then
+            log.info("bmx_data_data.temp:"..(bmx_data.temp).."°C")
+        end
+        if bmx_data.press then
+            log.info("bmx_data_data.press:"..(bmx_data.press).."hPa")
+        end
+        if bmx_data.high then
+            log.info("bmx_data_data.high:"..(bmx_data.high).."m")
+        end
+        if bmx_data.hum then
+            log.info("bmx_data_data.hum:"..(bmx_data.hum).."%")
+        end
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local bmx = {}
+
+local sys = require "sys"
+
+local i2cid
+local BMX_ADDRESS_ADR
+local CHIP_ID
+
+local BMX_ADDRESS_ADR_LOW           =   0x76
+local BMX_ADDRESS_ADR_HIGH          =   0x77
+
+---器件所用地址
+local BMX_CHIP_ID_CHECK			    =   0xD0	
+local BMP180_CHIP_ID                =   0x55
+local BMP280_CHIP_ID                =   0x58
+local BME280_CHIP_ID                =   0x60
+local BME680_CHIP_ID                =   0x61
+
+local BMX_RESET			            =   0xE0
+local BMX_RESET_VALUE		        =   0xB6
+
+local BMP180_AC1_ADDR				=   0xAA
+local BMP180_AC2_ADDR               =   0xAC  
+local BMP180_AC3_ADDR               =   0xAE  
+local BMP180_AC4_ADDR               =   0xB0  
+local BMP180_AC5_ADDR               =   0xB2  
+local BMP180_AC6_ADDR               =   0xB4
+local BMP180_B1_ADDR                =   0xB6  
+local BMP180_B2_ADDR                =   0xB8  
+local BMP180_MB_ADDR                =   0xBA  
+local BMP180_MC_ADDR                =   0xBC  
+local BMP180_MD_ADDR                =   0xBE
+
+local BMP180_CTRL_ADDR		        =   0xF4 --控制寄存器
+local BMP180_CTRL_TEMP		        =   0x2E --转换温度 4.5MS
+local BMP180_CTRL_POSS0		        =   0x34 --转换大气压 4.5ms
+local BMP180_CTRL_POSS1		        =   0x74 --转换大气压 7.5ms
+local BMP180_CTRL_POSS2		        =   0xB4 --转换大气压 13.5ms
+local BMP180_CTRL_POSS3		        =   0xF4 --转换大气压 25.5ms
+
+local BMP180_AD_MSB			        =   0xF6 --ADC输出高8位
+local BMP180_AD_LSB			        =   0xF7 --ADC输出低8位
+local BMP180_AD_XLSB			    =   0xF8 --19位测量时,ADC输出最低3位
+
+local BMX280_T1_ADDR				=   0x88
+local BMX280_T2_ADDR				=   0x8A
+local BMX280_T3_ADDR				=   0x8C
+local BMX280_P1_ADDR				=   0x8E
+local BMX280_P2_ADDR				=   0x90
+local BMX280_P3_ADDR				=   0x90
+local BMX280_P4_ADDR				=   0x94
+local BMX280_P5_ADDR				=   0x96
+local BMX280_P6_ADDR				=   0x98
+local BMX280_P7_ADDR				=   0x9A
+local BMX280_P8_ADDR				=   0x9C
+local BMX280_P9_ADDR				=   0x9E
+
+local BME280_H1_ADDR				=   0xA1
+local BME280_H2_ADDR				=   0xE1
+local BME280_H3_ADDR				=   0xE3
+local BME280_H4_ADDR				=   0xE4
+local BME280_H5_ADDR				=   0xE5
+local BME280_H6_ADDR				=   0xE7
+
+local BMX280_CTRLMHUM_ADDR		    =   0xF2 --控制寄存器
+local BMX280_STATUS_ADDR		    =   0xF3 --状态寄存器
+local BMX280_CTRLMEAS_ADDR		    =   0xF4 --控制寄存器
+local BMX280_CONFIG_ADDR		    =   0xF5 --配置寄存器
+
+local BMX280_SLEEP_MODE             =   0x00 
+local BMX280_FORCED_MODE            =   0x01 
+local BMX280_NORMAL_MODE            =   0x03 
+
+local BMX280_P_MODE_SKIP            =   0x00 --skipped
+local BMX280_P_MODE_1               =   0x01 --x1
+local BMX280_P_MODE_2               =   0x02 --x2
+local BMX280_P_MODE_3               =   0x03 --x4
+local BMX280_P_MODE_4               =   0x04 --x8
+local BMX280_P_MODE_5               =   0x05 --x16
+local BMX280_T_MODE_SKIP            =   0x00 --skipped
+local BMX280_T_MODE_1               =   0x01 --x1
+local BMX280_T_MODE_2               =   0x02 --x2
+local BMX280_T_MODE_3               =   0x03 --x4
+local BMX280_T_MODE_4               =   0x04 --x8
+local BMX280_T_MODE_5               =   0x05 --x16
+
+local BMX280_FILTER_OFF             =   0x00 --filter off
+local BMX280_FILTER_MODE_1		    =   0x01 --0.223*ODR     x2
+local BMX280_FILTER_MODE_2		    =   0x02 --0.092*ODR     x4
+local BMX280_FILTER_MODE_3		    =   0x03 --0.042*ODR     x8
+local BMX280_FILTER_MODE_4		    =   0x04 --0.021*ODR     x16
+
+local BMX280_T_SB1                  =   0x00 --0.5ms
+local BMX280_T_SB2			        =   0x01 --62.5ms
+local BMX280_T_SB3			        =   0x02 --125ms
+local BMX280_T_SB4			        =   0x03 --250ms
+local BMX280_T_SB5			        =   0x04 --500ms
+local BMX280_T_SB6			        =   0x05 --1000ms
+local BMX280_T_SB7			        =   0x06 --2000ms
+local BMX280_T_SB8			        =   0x07 --4000ms
+
+local BMX280_H_SB0			        =   0x00 --Skipped (output set to 0x8000)
+local BMX280_H_SB1			        =   0x01 --oversampling ×1
+local BMX280_H_SB2			        =   0x02 --oversampling ×2
+local BMX280_H_SB3			        =   0x03 --oversampling ×4
+local BMX280_H_SB4			        =   0x04 --oversampling ×8
+local BMX280_H_SB5			        =   0x05 --oversampling ×16
+
+local BMX280_SPI_DISABLE			=   0x00
+local BMX280_SPI_ENABLE			    =   0x01
+
+local BME680_CONFIG_ADDR			=   0x75
+local BME680_CTRLMEAS_ADDR			=   0x74
+local BME680_CTRLHUM_ADDR			=   0x72
+local BME680_CTRLGAS1_ADDR			=   0x71
+local BME680_CTRLGAS0_ADDR			=   0x70
+
+local BME680_GASRLSB_ADDR			=   0x2B
+local BME680_GASRMSB_ADDR			=   0x2A
+local BME680_HUMLSB_ADDR			=   0x26
+local BME680_HUMMSB_ADDR			=   0x25
+local BME680_TEMPXLSB_ADDR			=   0x24
+local BME680_TEMPLSB_ADDR			=   0x23
+local BME680_TEMPMSB_ADDR			=   0x22
+local BME680_PRESSXLSB_ADDR			=   0x21
+local BME680_PRESSLSB_ADDR			=   0x20
+local BME680_PRESSMSB_ADDR			=   0x1F
+local BME680_EASSTATUS0_ADDR		=   0x1D
+
+local BME680_T1_ADDR				=   0xE9
+local BME680_T2_ADDR				=   0x8A
+local BME680_T3_ADDR				=   0x8C
+
+local BME680_P1_ADDR				=   0x8E
+local BME680_P2_ADDR				=   0x90
+local BME680_P3_ADDR				=   0x92
+local BME680_P4_ADDR				=   0x94
+local BME680_P5_ADDR				=   0x96
+local BME680_P6_ADDR				=   0x99
+local BME680_P7_ADDR				=   0x98
+local BME680_P8_ADDR				=   0x9C
+local BME680_P9_ADDR				=   0x9E
+local BME680_P10_ADDR				=   0xA0
+
+local BME680_H1M_ADDR				=   0xE3
+local BME680_H1_ADDR				=   0xE2
+local BME680_H2M_ADDR				=   0xE1
+local BME680_H3_ADDR				=   0xE4
+local BME680_H4_ADDR				=   0xE5
+local BME680_H5_ADDR				=   0xE6
+local BME680_H6_ADDR				=   0xE7
+local BME680_H7_ADDR				=   0xE8
+
+--按照官方手册配置一种模式,此处可自行修改
+local osrs_t                        =   BMX280_T_MODE_1
+local osrs_p                        =   BMX280_P_MODE_3
+local osrs_h                        =   BMX280_H_SB1
+local mode                          =   BMX280_NORMAL_MODE
+local t_sb                          =   BMX280_T_SB1
+local filter                        =   BMX280_FILTER_MODE_4
+local spi_en                        =   BMX280_SPI_DISABLE
+
+local ctrl_hum                      =   osrs_h
+local ctrl_meas                     =   bit.bor(bit.lshift(osrs_t,5), bit.lshift(osrs_p, 2), mode)
+local config                        =   bit.bor(bit.lshift(t_sb,5), bit.lshift(filter, 2), spi_en)
+
+local BMX180_CTRL_TEMP		        =   0x2E --转换温度 4.5MS
+local BMX180_CTRL_POSS0		        =   0x34 --转换大气压 4.5ms
+local BMX180_CTRL_POSS1		        =   0x74 --转换大气压 7.5ms
+local BMX180_CTRL_POSS2		        =   0xB4 --转换大气压 13.5ms
+local BMX180_CTRL_POSS3		        =   0xF4 --转换大气压 25.5ms
+
+local BMX280_PRESS_MSB			    =   0xF7
+local BMX280_PRESS_LSB			    =   0xF8
+local BMX280_PRESS_XLSB			    =   0xF9
+
+local BMX280_TEMP_MSB			    =   0xFA
+local BMX280_TEMP_LSB			    =   0xFB
+local BMX280_TEMP_XLSB			    =   0xFC
+
+local BMX280_HUM_MSB			    =   0xFD
+local BMX280_HUM_LSB			    =   0xFE
+
+local PRESSURE_OF_SEA			    =   101325	-- 参考海平面压强
+
+local AC1,AC2,AC3,AC4,AC5,AC6,B1,B2,MB,MC,MD
+local H1,H2,H3,H4,H5,H6,H7,T1,T2,T3,P1,P2,P3,P4,P5,P6,P7,P8,P9,P10
+
+
+--器件ID检测
+local function chip_check()
+    i2c.send(i2cid, BMX_ADDRESS_ADR_LOW, BMX_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, BMX_ADDRESS_ADR_LOW, 1)
+    if revData:byte() ~= nil then
+        BMX_ADDRESS_ADR = BMX_ADDRESS_ADR_LOW
+    else
+        i2c.send(i2cid, BMX_ADDRESS_ADR_HIGH, BMX_CHIP_ID_CHECK)--读器件地址
+        sys.wait(50)
+        local revData = i2c.recv(i2cid, BMX_ADDRESS_ADR_HIGH, 1)
+        if revData:byte() ~= nil then
+            BMX_ADDRESS_ADR = BMX_ADDRESS_ADR_HIGH
+        else
+            log.info("i2c", "Can't find bmx device")
+            return false
+        end
+    end
+    i2c.send(i2cid, BMX_ADDRESS_ADR, BMX_CHIP_ID_CHECK)--读器件地址
+    sys.wait(50)
+    local revData = i2c.recv(i2cid, BMX_ADDRESS_ADR, 1)
+    if revData:byte() == BMP180_CHIP_ID then
+        CHIP_ID = BMP180_CHIP_ID
+        log.info("Device i2c id is: bmp180")
+    elseif revData:byte() == BMP280_CHIP_ID then
+        CHIP_ID = BMP280_CHIP_ID
+        log.info("Device i2c id is: bmp280")
+    elseif revData:byte() == BME280_CHIP_ID then
+        log.info("Device i2c id is: bme280")
+        CHIP_ID = BME280_CHIP_ID
+    elseif revData:byte() == BME680_CHIP_ID then
+        log.info("Device i2c id is: bme680")
+        CHIP_ID = BME680_CHIP_ID
+    else
+        log.info("i2c", "Can't find bmx device")
+        return false
+    end
+    return true
+end
+
+--器件初始化
+function bmx.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(20)
+    if chip_check() then
+        i2c.send(i2cid, BMX_ADDRESS_ADR, {BMX_RESET,BMX_RESET_VALUE})--软复位
+        sys.wait(20)
+        if CHIP_ID == BMP180_CHIP_ID then
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_AC1_ADDR)
+            _,AC1 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_AC2_ADDR)
+            _,AC2 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_AC3_ADDR)
+            _,AC3 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_AC4_ADDR)
+            _,AC4 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">H")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_AC5_ADDR)
+            _,AC5 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">H")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_AC6_ADDR)
+            _,AC6 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">H")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_B1_ADDR)
+            _,B1 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_B2_ADDR)
+            _,B2 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_MB_ADDR)
+            _,MB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_MC_ADDR)
+            _,MC = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMP180_MD_ADDR)
+            _,MD = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">h")
+        elseif CHIP_ID == BME680_CHIP_ID then
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_T1_ADDR)
+            _,T1 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_T2_ADDR)
+            _,T2 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_T3_ADDR)
+            _,T3 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_P1_ADDR)
+            _,P1 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<H")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_P2_ADDR)
+            _,P2 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<H")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_P3_ADDR)
+            _,P3 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_P4_ADDR)
+            _,P4 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_P5_ADDR)
+            _,P5 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_P6_ADDR)
+            _,P6 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_P7_ADDR)
+            _,P7 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_P8_ADDR)
+            _,P8 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_P9_ADDR)
+            _,P9 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_P10_ADDR)
+            _,P10 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+            
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_H1M_ADDR)
+            local _,tmp1 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_H1_ADDR)
+            local _,tmp2 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_H2M_ADDR)
+            local _,tmp3 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+
+            H1 = bit.bor(bit.lshift(tmp1,4),bit.band(tmp2,0x0F))
+            H2 = bit.bor(bit.lshift(tmp3,4),bit.band(bit.rshift(tmp2,4),0x0F))
+
+            if bit.band(H1,0x0080)~=0 then H4 = bit.bxor(-H1,0x00FF) + 1 end
+            if bit.band(H2,0x0080)~=0 then H5 = bit.bxor(-H2,0x00FF) + 1 end
+
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_H3_ADDR)
+            _,H3 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_H4_ADDR)
+            _,H4 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_H5_ADDR)
+            _,H5 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_H6_ADDR)
+            _,H6 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BME680_H7_ADDR)
+            _,H7 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+
+        else
+            if CHIP_ID == BME280_CHIP_ID then
+                i2c.send(i2cid, BMX_ADDRESS_ADR, BME280_H1_ADDR)
+                _,H1 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<b")
+                i2c.send(i2cid, BMX_ADDRESS_ADR, BME280_H2_ADDR)
+                _,H2 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+                i2c.send(i2cid, BMX_ADDRESS_ADR, BME280_H3_ADDR)
+                _,H3 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<b")
+
+                i2c.send(i2cid, BMX_ADDRESS_ADR, 0xE4)
+                local _,tmp1 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+                i2c.send(i2cid, BMX_ADDRESS_ADR, 0xE5)
+                local _,tmp2 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+                i2c.send(i2cid, BMX_ADDRESS_ADR, 0xE6)
+                local _,tmp3 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+
+                H4 = bit.bor(bit.lshift(tmp1,4),bit.band(tmp2,0x0F))
+                H5 = bit.bor(bit.lshift(tmp3,4),bit.band(bit.rshift(tmp2,4),0x0F))
+
+                if bit.band(H4,0x0080)~=0 then H4 = bit.bxor(-H4,0x00FF) + 1 end
+                if bit.band(H5,0x0080)~=0 then H5 = bit.bxor(-H5,0x00FF) + 1 end
+                
+                i2c.send(i2cid, BMX_ADDRESS_ADR, BME280_H6_ADDR)
+                _,H6 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "<c")
+            end
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_T1_ADDR)
+            _,T1 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<H")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_T2_ADDR)
+            _,T2 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_T3_ADDR)
+            _,T3 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_P1_ADDR)
+            _,P1 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<H")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_P2_ADDR)
+            _,P2 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_P3_ADDR)
+            _,P3 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_P4_ADDR)
+            _,P4 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_P5_ADDR)
+            _,P5 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_P6_ADDR)
+            _,P6 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_P7_ADDR)
+            _,P7 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_P8_ADDR)
+            _,P8 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+            i2c.send(i2cid, BMX_ADDRESS_ADR, BMX280_P9_ADDR)
+            _,P9 = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), "<h")
+        end
+        if CHIP_ID == BME680_CHIP_ID then
+            i2c.send(i2cid, BMX_ADDRESS_ADR, {BME680_CONFIG_ADDR,0x1C})
+            i2c.send(i2cid, BMX_ADDRESS_ADR, {BME680_CTRLMEAS_ADDR,ctrl_meas})
+            i2c.send(i2cid, BMX_ADDRESS_ADR, {BME680_CTRLHUM_ADDR,ctrl_hum})
+            i2c.send(i2cid, BMX_ADDRESS_ADR, {BME680_CTRLGAS1_ADDR,0x19})
+            i2c.send(i2cid, BMX_ADDRESS_ADR, {BME680_CTRLGAS0_ADDR,0x08})
+        else
+            if CHIP_ID == BME280_CHIP_ID then
+                i2c.send(i2cid, BMX_ADDRESS_ADR, {BMX280_CTRLMHUM_ADDR,ctrl_hum})
+            end
+            if CHIP_ID == BMP280_CHIP_ID or CHIP_ID == BME280_CHIP_ID then
+                i2c.send(i2cid, BMX_ADDRESS_ADR, {BMX280_CTRLMEAS_ADDR,ctrl_meas})
+                i2c.send(i2cid, BMX_ADDRESS_ADR, {BMX280_CONFIG_ADDR,config})
+            end
+        end
+        
+        sys.wait(20)--跳过首次数据
+        log.info("bmx init_ok")
+        return true
+    end
+    return false
+end
+
+--获取温度的原始数据
+local function bmx_get_temp_raw()
+    local temp_raw
+    if CHIP_ID == BMP180_CHIP_ID then
+        i2c.send(i2cid, BMX_ADDRESS_ADR,{BMP180_CTRL_ADDR,BMP180_CTRL_TEMP})
+        sys.wait(5)
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMP180_AD_MSB)
+        local buffer = i2c.recv(i2cid, BMX_ADDRESS_ADR, 2)--获取2字节
+        _, temp_raw = pack.unpack(buffer, ">h")
+    elseif CHIP_ID == BME680_CHIP_ID then
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BME680_TEMPMSB_ADDR)
+        local _, MSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BME680_TEMPLSB_ADDR)
+        local _, LSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BME680_TEMPXLSB_ADDR)
+        local _, XLSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        temp_raw = bit.bor(bit.lshift(MSB,12), bit.lshift(LSB, 4), bit.rshift(XLSB, 4))
+    else
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMX280_TEMP_MSB)
+        local _, MSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMX280_TEMP_LSB)
+        local _, LSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMX280_TEMP_XLSB)
+        local _, XLSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        temp_raw = bit.bor(bit.lshift(MSB,12), bit.lshift(LSB, 4), bit.rshift(XLSB, 4))
+    end
+    return temp_raw or 0
+end
+
+--获取气压的原始数据
+local function bmx_get_pressure_raw()
+    local pressure_raw
+    if CHIP_ID == BMP180_CHIP_ID then
+        i2c.send(i2cid, BMX_ADDRESS_ADR,{BMP180_CTRL_ADDR,BMP180_CTRL_POSS3})
+        sys.wait(26)
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMP180_AD_MSB)
+        local _, MSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMP180_AD_LSB)
+        local _, LSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMP180_AD_XLSB)
+        local _, XLSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        pressure_raw = bit.rshift((bit.lshift(MSB, 16)+bit.lshift(LSB, 8)+XLSB),5)
+    elseif CHIP_ID == BME680_CHIP_ID then
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BME680_PRESSMSB_ADDR)
+        local _, MSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BME680_PRESSLSB_ADDR)
+        local _, LSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BME680_PRESSXLSB_ADDR)
+        local _, XLSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        pressure_raw = bit.bor(bit.lshift(MSB,12), bit.lshift(LSB, 4), bit.rshift(XLSB, 4))
+    else
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMX280_PRESS_MSB)
+        local _, MSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMX280_PRESS_LSB)
+        local _, LSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMX280_PRESS_XLSB)
+        local _, XLSB = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 1), "b")
+        pressure_raw = bit.bor(bit.lshift(MSB,12), bit.lshift(LSB, 4), bit.rshift(XLSB, 4))
+    end
+    return pressure_raw or 0
+end
+
+--获取湿度的原始数据 
+local function bmx_get_humidity_raw()
+    local humidity
+    if CHIP_ID == BME680_CHIP_ID then
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BME680_HUMMSB_ADDR)
+        _, humidity = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">h")
+    else
+        i2c.send(i2cid, BMX_ADDRESS_ADR,BMX280_HUM_MSB)
+        _, humidity = pack.unpack(i2c.recv(i2cid, BMX_ADDRESS_ADR, 2), ">h")
+    end
+    return humidity or 0
+end
+
+--[[
+获取bmx数据
+@api bmx.get_data()
+@return table bmx数据
+@usage
+local bmx_data = bmx.get_data()
+if bmx_data.temp then
+    log.info("bmx_data_data.temp:"..(bmx_data.temp).."°C")
+end
+if bmx_data.press then
+    log.info("bmx_data_data.press:"..(bmx_data.press).."hPa")
+end
+if bmx_data.high then
+    log.info("bmx_data_data.high:"..(bmx_data.high).."m")
+end
+if bmx_data.hum then
+    log.info("bmx_data_data.hum:"..(bmx_data.hum).."%")
+end
+]]
+
+function bmx.get_data()
+    local bme_data={}
+    local temp_raw = bmx_get_temp_raw()
+    local pressure_raw = bmx_get_pressure_raw()
+    if CHIP_ID == BMP180_CHIP_ID then
+        local P = 0
+        local X1 = ((temp_raw - AC6) * AC5) / 32768
+        local X2 = (MC * 2048) / (X1 + MD)
+        local B5 = X1 + X2 
+        temp_raw  = (B5 + 8)/16
+        bme_data.temp = temp_raw/10
+        local B6 = B5 - 4000
+        X1 = (B2 * (B6 * B6 /4096))/2048
+        X2 = AC2 * B6/2048
+        local X3 = X1 + X2 
+        local B3 = (((AC1 * 4 + X3)*8) + 2) /4
+        X1 = (AC3 * B6)/8192
+        X2 = (B1 * (B6 * B6 /4096))/65536
+        X3 = ((X1 + X2) + 2)/4
+        local B4 = AC4 * (X3 + 32768)/32768
+        local B7 = (pressure_raw - B3) * (50000/8)
+        if (B7 < 0x80000000)then
+            P = (B7 * 2) / B4 
+        else
+            P = (B7 / B4) * 2
+        end
+        X1 = (P/256) * (P/256)
+        X1 = (X1 * 3038)/65536
+        X2 = (-7357 * P)/65536
+        P = P + ((X1 + X2 + 3791)/16)
+        bme_data.press = P /100
+        bme_data.high = 44330 * (1 - math.pow((P / PRESSURE_OF_SEA), (1.0 / 5.255)))
+        bme_data.high = 44330 * (1 - math.pow((P / PRESSURE_OF_SEA), (1.0 / 5.255)))
+    elseif CHIP_ID == BME680_CHIP_ID then
+        local var1 = (temp_raw/16384.0 - T1/1024.0) * T2
+        local var2 = ((temp_raw/131072.0 - T1/8192.0) *(temp_raw/131072.0 - T1/8192.0)) * T3 * 16
+        local t_fine = var1 + var2
+        bme_data.temp = t_fine / 5120.0
+
+        var1 = t_fine/2.0 - 64000.0
+        var2 = var1 * var1 * P6 / 131072.0
+        var2 = var2 + var1 * P5 * 2.0
+        var2 = var2 / 4.0 + P4 * 65536.0
+        var1 = (P3 * var1 * var1 / 16384.0 + P2 * var1) / 524288.0
+        var1 = (1.0 + var1 / 32768.0)*P1
+        if var1==0 then
+            return bme_data
+        end
+        local p = 1048576.0 - pressure_raw
+        p = (p - (var2 / 4096.0)) * 6250.0 / var1
+        var1 = P9 * p * p / 2147483648.0
+        var2 = p * P8 / 32768.0
+        local var3 = p / 256.0 * p / 256.0 * p / 256.0 * P10 / 131072.0
+        p = p + (var1 + var2 + var3 + P7 * 128.0) / 16.0
+        bme_data.press = p /100
+        bme_data.high = 44330 * (1 - math.pow((p / PRESSURE_OF_SEA), (1.0 / 5.255)))
+
+        local humidity_raw = bmx_get_humidity_raw()
+        local var1 = humidity_raw - (H1 * 16.0 + H3 / 2.0 * bme_data.temp)
+        local var2 = var1 * (H2 / 262144.0 * (1.0 + H4 / 16384.0 * bme_data.temp + H5 / 1048576.0 * bme_data.temp * bme_data.temp))
+        local var3 = H6 / 16384.0
+        local var4 = H7 / 2097152.0;
+        bme_data.hum = var2 + ((var3 + var4 * bme_data.temp) * var2 * var2)
+    else
+        local var1 = (temp_raw/16384.0 - T1/1024.0) * T2
+        local var2 = ((temp_raw/131072.0 - T1/8192.0) *(temp_raw/131072.0 - T1/8192.0)) * T3
+        local t_fine = var1 + var2
+        bme_data.temp = (var1 + var2) / 5120.0
+        var1 = t_fine/2.0 - 64000.0
+        var2 = var1 * var1 * P6 / 32768.0
+        var2 = var2 + var1 * P5 * 2.0
+        var2 = var2 / 4.0 + P4 * 65536.0
+        var1 = (P3 * var1 * var1 / 524288.0 + P2 * var1) / 524288.0
+        var1 = (1.0 + var1 / 32768.0)*P1
+        if var1==0 then
+            return bme_data
+        end
+        local p = 1048576.0 - pressure_raw
+        p = (p - (var2 / 4096.0)) * 6250.0 / var1
+        var1 = P9 * p * p / 2147483648.0
+        var2 = p * P8 / 32768.0
+        p = p + (var1 + var2 + P7) / 16.0
+        bme_data.press = p /100
+        bme_data.high = 44330 * (1 - math.pow((p / PRESSURE_OF_SEA), (1.0 / 5.255)))
+        if CHIP_ID == BME280_CHIP_ID then
+            local humidity_raw = bmx_get_humidity_raw()
+            local var_H = t_fine - 76800.0
+            var_H = (humidity_raw - (H4 * 64.0 + H5 / 16384.0 * var_H)) * (H2 / 65536.0 * (1.0 + H6 / 67108864.0 * var_H * (1.0 + H3 / 67108864.0 * var_H)))
+            var_H = var_H * (1.0 - H1 * var_H / 524288.0)
+            if var_H > 100.0 then
+                var_H = 100.0
+            elseif var_H < 0.0 then
+                var_H = 0.0
+            end
+            bme_data.hum=var_H
+        end
+    end 
+    return bme_data
+end
+
+return bmx
+
+
+

+ 77 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/cht8305c.lua

@@ -0,0 +1,77 @@
+--[[
+@module cht8305c
+@summary cht8305c 温湿度传感器
+@version 1.0
+@date    2023.07.21
+@author  wendal
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local cht8305c = require "cht8305c"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    cht8305c.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local data = cht8305c.get_data()
+        if data and data.RH then
+            local tmp = string.format("RH: %.2f T: %.2f ℃", data.RH*100, data.T)
+            log.info("cht8305c", tmp)
+        else
+            log.info("cht8305c", "读取失败")
+        end
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local cht8305c = {}
+local sys = require "sys"
+local i2cid
+
+local cht8305c_addr       =   0x40
+
+--[[
+cht8305c初始化
+@api cht8305c.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+cht8305c.init(0)
+]]
+function cht8305c.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(20)
+    local Manufacture = i2c.readReg(i2cid, cht8305c_addr, 0xFE, 2)
+    -- log.info("cht8305c", "Manufacture", Manufacture and Manufacture:toHex() or "??")
+    if Manufacture and Manufacture:byte() == 0x59 then
+        log.info("cht8305c init_ok")
+        return true
+    else
+        log.info("cht8305c init_fail")
+    end
+end
+
+--[[
+获取cht8305c数据
+@api cht8305c.get_data()
+@return table cht8305c数据
+]]
+function cht8305c.get_data()
+    local data={RH=nil,T=nil}
+    i2c.send(i2cid, cht8305c_addr, string.char(0))
+    sys.wait(22)
+    local tmp = i2c.recv(i2cid, cht8305c_addr, 4)
+    -- log.info("CHT8305C", tmp and tmp:toHex() or "??")
+    if tmp and #tmp == 4 then
+        local _, temp, hum = pack.unpack(tmp, ">HH")
+        data.T = (165 * temp) / 65535.0 - 40
+        data.RH = hum / 65535.0
+        -- log.info("CHT8305C", temp, hum)
+    end
+    return data
+end
+
+return cht8305c

+ 162 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/ds3231.lua

@@ -0,0 +1,162 @@
+--[[
+@module ds3231
+@summary ds3231 实时时钟传感器
+@version 1.0
+@date    2022.03.16
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local ds3231 = require "ds3231"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    ds3231.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        log.info("ds3231.get_temperature", ds3231.get_temperature())
+        local time = ds3231.read_time()
+        log.info("ds3231.read_time",time.tm_year,time.tm_mon,time.tm_mday,time.tm_hour,time.tm_min,time.tm_sec)
+        sys.wait(5000)
+        local set_time = {tm_year=2021,tm_mon=3,tm_mday=0,tm_wday=0,tm_hour=0,tm_min=0,tm_sec=0}
+        ds3231.set_time(set_time)
+        time = ds3231.read_time()
+        log.info("ds3231_read_time",time.tm_year,time.tm_mon,time.tm_mday,time.tm_hour,time.tm_min,time.tm_sec)
+        sys.wait(1000)
+    end
+end)
+]]
+
+local ds3231 = {}
+
+local sys = require "sys"
+
+local i2cid
+
+local DS3231_ADDRESS            =   0x68 -- address pin low (GND), default for InvenSense evaluation board
+local i2cslaveaddr              =   DS3231_ADDRESS --slave address
+
+---DS3231所用地址
+
+local REG_SEC				    =   0x00
+local REG_MIN				    =   0x01
+local REG_HOUR			        =   0x02
+local REG_DAY				    =   0x03
+local REG_WEEK			        =   0x04
+local REG_MON				    =   0x05
+local REG_YEAR			        =   0x06
+local REG_ALM1_SEC  		    =   0x07
+local REG_ALM1_MIN 	  	        =   0x08
+local REG_ALM1_HOUR     	    =   0x09
+local REG_ALM1_DAY_DATE 	    =   0x0A
+local REG_ALM2_MIN  		    =   0x0B
+local REG_ALM2_HOUR     	    =   0x0C
+local REG_ALM2_DAY_DATE 	    =   0x0D
+local REG_CONTROL               =   0x0E
+local REG_STATUS                =   0x0F
+local REG_AGING_OFFSET          =   0x10
+local REG_TEMP_MSB 		        =   0x11
+local REG_TEMP_LSB 		        =   0x12
+
+
+local function i2c_send(data)
+    i2c.send(i2cid, i2cslaveaddr, data)
+end
+local function i2c_recv(data,num)
+    i2c.send(i2cid, i2cslaveaddr, data)
+    local revData = i2c.recv(i2cid, i2cslaveaddr, num)
+    return revData
+end
+
+local function bcd_to_hex(data)
+    local hex = bit.rshift(data,4)*10+bit.band(data,0x0f)
+    return hex;
+end
+
+local function hex_to_bcd(data)
+    local bcd = bit.lshift(math.floor(data/10),4)+data%10
+    return bcd;
+end
+
+--[[
+ds3231初始化
+@api ds3231.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+ds3231.init(0)
+]]
+function ds3231.init(i2c_id)
+    i2cid = i2c_id
+    i2c_send({REG_CONTROL, 0x04})--close clock out
+    log.info("ds3231 init_ok")
+    return true
+end
+
+--[[
+获取温度数据
+@api ds3231.get_temperature()
+@return number 温度数据
+@usage
+log.info("ds3231.get_temperature", ds3231.get_temperature())
+]]
+function ds3231.get_temperature()
+    local temp
+    local T = i2c_recv(REG_TEMP_MSB,2)
+    if bit.band(T:byte(1),0x80) then
+        --negative temperature
+        temp = T:byte(1)
+        temp = temp - (bit.rshift(T:byte(2),6)*0.25)--0.25C resolution
+    else
+        --positive temperature
+        temp =  T:byte(1)
+        temp = temp + (bit.band(bit.rshift(T:byte(2),6),0x03)*0.25)
+    end
+	return temp;
+end
+
+--[[
+获取时间
+@api ds3231.read_time()
+@return table 时间表
+@usage
+local time = ds3231.read_time()
+log.info("ds3231.read_time",time.tm_year,time.tm_mon,time.tm_mday,time.tm_hour,time.tm_min,time.tm_sec)
+]]
+function ds3231.read_time()
+    -- read time
+    local time_data = {}
+    local data = i2c_recv(REG_SEC,7)
+    time_data.tm_year  = bcd_to_hex(data:byte(7)) + 2000
+    time_data.tm_mon   = bcd_to_hex(bit.band(data:byte(6),0x7f)) - 1
+    time_data.tm_mday  = bcd_to_hex(data:byte(5))
+    time_data.tm_hour  = bcd_to_hex(data:byte(3))
+    time_data.tm_min   = bcd_to_hex(data:byte(2))
+    time_data.tm_sec   = bcd_to_hex(data:byte(1))
+	return time_data
+end
+
+--[[
+设置时间
+@api ds3231.set_time(time)
+@table time 时间表
+@usage
+local set_time = {tm_year=2021,tm_mon=3,tm_mday=0,tm_wday=0,tm_hour=0,tm_min=0,tm_sec=0}
+ds3231.set_time(set_time)
+]]
+function ds3231.set_time(time)
+    -- set time
+    local data7 = hex_to_bcd(time.tm_year + 2000)
+    local data6 = hex_to_bcd(time.tm_mon + 1)
+    local data5 = hex_to_bcd(time.tm_mday)
+    local data4 = hex_to_bcd(time.tm_wday+1)
+    local data3 = hex_to_bcd(time.tm_hour)
+    local data2 = hex_to_bcd(time.tm_min)
+    local data1 = hex_to_bcd(time.tm_sec)
+    i2c_send({REG_SEC, data1,data2,data3,data4,data5,data6,data7})
+end
+
+return ds3231
+
+
+

+ 74 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/ec11.lua

@@ -0,0 +1,74 @@
+--[[
+@module ec11
+@summary ec11 旋转编码器
+@version 1.0
+@date    2023.03.27
+@author  Dozingfiretruck
+@usage
+-- 用法实例, 当前支持一定一脉冲
+local ec11 = require("ec11")
+
+-- 按实际接线写
+local GPIO_A = 6
+local GPIO_B = 7
+ec11.init(GPIO_A,GPIO_B)
+
+-- 演示接收旋转效果
+local count = 0
+local function ec11_callBack(direction)
+    if direction == "left" then
+        -- 往左选,逆时针
+        count = count - 1
+    else
+        -- 往右旋,顺时针
+        count = count + 1
+    end
+    log.info("ec11", direction, count)
+end
+
+sys.subscribe("ec11",ec11_callBack)
+]]
+
+
+local ec11 = {}
+local sys = require "sys"
+
+local A = false
+local B = false
+
+--[[
+初始化ec11
+@api ec11.init(GPIO_A,GPIO_B)
+@number GPIO_A A引脚对应的GPIO编号, 例如 GPIO6, 就写6
+@number GPIO_B B引脚对应的GPIO编号, 例如 GPIO7, 就写7
+@usage
+ec11.init(6,7)
+]]
+function ec11.init(GPIO_A,GPIO_B)
+    gpio.debounce(GPIO_A, 10)
+    gpio.debounce(GPIO_B, 10)
+
+    gpio.setup(GPIO_A, function()
+        if B then
+            sys.publish("ec11","left")
+            A = false
+            B = false
+        else
+            A = true
+        end
+    end,gpio.PULLUP,gpio.FALLING)
+
+    gpio.setup(GPIO_B, function()
+        if A then
+            sys.publish("ec11","right")
+            A = false
+            B = false
+        else
+            B = true
+        end
+    end,gpio.PULLUP,gpio.FALLING)
+end
+
+return ec11
+
+

+ 565 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/espblufi.lua

@@ -0,0 +1,565 @@
+--[[
+@module espblufi
+@summary espblufi esp blufi 蓝牙配网(注意:初版暂不支持加密功能!!!!!!!!)
+@version 1.0
+@date    2025.07.01
+@author  Dozingfiretruck
+@usage
+-- 此为Blufi 配网库
+-- BluFi 配网指南:https://www.espressif.com/sites/default/files/documentation/esp32_bluetooth_networking_user_guide_cn.pdf
+
+-- 安卓测试APP下载地址:https://github.com/EspressifApp/EspBlufiForAndroid/releases
+-- 安卓APP源码下载地址:https://github.com/EspressifApp/EspBlufiForAndroid
+
+-- IOS测试APP下载地址:https://apps.apple.com/cn/app/espblufi/id1450614082
+-- IOSAPP源码下载地址:https://github.com/EspressifApp/EspBlufiForiOS
+
+-- 小程序测试:微信搜索小程序:ESP Config
+-- 小程序源码下载地址:https://github.com/EspressifApps/ESP-Config-WeChat
+
+-- 注意:初版暂不支持加密功能!!!!!!!!
+
+-- 用法实例
+local espblufi = require("espblufi")
+
+local function espblufi_callback(event,data)
+    if event == espblufi.EVENT_STA_INFO then
+        for i, v in pairs(data) do
+            print(i,v)
+        end
+    elseif event == espblufi.EVENT_SOFTAP_INFO then
+        for i, v in pairs(data) do
+            print(i,v)
+        end
+    elseif event == espblufi.EVENT_CUSTOM_DATA then
+        espblufi.send_custom_data(data)
+    end
+end
+
+sys.taskInit(function()
+    local bluetooth_device = bluetooth.init()
+    espblufi.init(bluetooth_device, espblufi_callback)
+    espblufi.start()
+    while 1 do
+        sys.wait(1000)
+    end
+end)
+
+]]
+local espblufi = {}
+
+local sys = require "sys"
+
+local BLUFI_TOPIC = "espblufi"
+
+local BTC_BLUFI_GREAT_VER   =   0x01  --Version + Subversion
+local BTC_BLUFI_SUB_VER     =   0x03  --Version + Subversion
+local BTC_BLUFI_VERSION     =   ((BTC_BLUFI_GREAT_VER<<8)|BTC_BLUFI_SUB_VER)  --Version + Subversion
+
+-- packet type
+local BLUFI_TYPE_MASK       =   0x03
+local BLUFI_TYPE_SHIFT      =   0
+local BLUFI_SUBTYPE_MASK    =   0xFC
+local BLUFI_SUBTYPE_SHIFT   =   2
+
+local function BLUFI_GET_TYPE(type) return ((type) & BLUFI_TYPE_MASK) end
+local function BLUFI_GET_SUBTYPE(type) return (((type) & BLUFI_SUBTYPE_MASK) >>BLUFI_SUBTYPE_SHIFT) end
+local function BLUFI_BUILD_TYPE(type,subtype) return (((type) & BLUFI_TYPE_MASK) | ((subtype)<<BLUFI_SUBTYPE_SHIFT)) end
+
+local BLUFI_TYPE_CTRL                                 = 0x0
+local BLUFI_TYPE_CTRL_SUBTYPE_ACK                     = 0x00
+local BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE            = 0x01
+local BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE         = 0x02
+local BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP              = 0x03
+local BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP         = 0x04
+local BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS         = 0x05
+local BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA      = 0x06
+local BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION             = 0x07
+local BLUFI_TYPE_CTRL_SUBTYPE_DISCONNECT_BLE          = 0x08
+local BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_LIST           = 0x09
+
+local BLUFI_TYPE_DATA                                 = 0x1
+local BLUFI_TYPE_DATA_SUBTYPE_NEG                     = 0x00
+local BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID               = 0x01
+local BLUFI_TYPE_DATA_SUBTYPE_STA_SSID                = 0x02
+local BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD              = 0x03
+local BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID             = 0x04
+local BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD           = 0x05
+local BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM     = 0x06
+local BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE        = 0x07
+local BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL          = 0x08
+local BLUFI_TYPE_DATA_SUBTYPE_USERNAME                = 0x09
+local BLUFI_TYPE_DATA_SUBTYPE_CA                      = 0x0a
+local BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT             = 0x0b
+local BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT             = 0x0c
+local BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY         = 0x0d
+local BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY         = 0x0e
+local BLUFI_TYPE_DATA_SUBTYPE_WIFI_REP                = 0x0f
+local BLUFI_TYPE_DATA_SUBTYPE_REPLY_VERSION           = 0x10
+local BLUFI_TYPE_DATA_SUBTYPE_WIFI_LIST               = 0x11
+local BLUFI_TYPE_DATA_SUBTYPE_ERROR_INFO              = 0x12
+local BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA             = 0x13
+local BLUFI_TYPE_DATA_SUBTYPE_STA_MAX_CONN_RETRY      = 0x14
+local BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_END_REASON     = 0x15
+local BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_RSSI           = 0x16
+
+local function BLUFI_TYPE_IS_CTRL(type) return (BLUFI_GET_TYPE((type)) == BLUFI_TYPE_CTRL) end
+local function BLUFI_TYPE_IS_DATA(type) return (BLUFI_GET_TYPE((type)) == BLUFI_TYPE_DATA) end
+
+-- packet frame control
+local BLUFI_FC_ENC_MASK         = 0x01
+local BLUFI_FC_CHECK_MASK       = 0x02
+local BLUFI_FC_DIR_MASK         = 0x04
+local BLUFI_FC_REQ_ACK_MASK     = 0x08
+local BLUFI_FC_FRAG_MASK        = 0x10
+
+local BLUFI_FC_ENC              = 0x01
+local BLUFI_FC_CHECK            = 0x02
+local BLUFI_FC_DIR_P2E          = 0x00
+local BLUFI_FC_DIR_E2P          = 0x04
+local BLUFI_FC_REQ_ACK          = 0x08
+local BLUFI_FC_FRAG             = 0x10
+
+local BLUFI_SEQUENCE_ERROR      = 0x00
+local BLUFI_CHECKSUM_ERROR      = 0x01
+local BLUFI_DECRYPT_ERROR       = 0x02
+local BLUFI_ENCRYPT_ERROR       = 0x03
+local BLUFI_INIT_SECURITY_ERROR = 0x04
+local BLUFI_DH_MALLOC_ERROR     = 0x05
+local BLUFI_DH_PARAM_ERROR      = 0x06
+local BLUFI_READ_PARAM_ERROR    = 0x07
+local BLUFI_MAKE_PUBLIC_ERROR   = 0x08
+local BLUFI_DATA_FORMAT_ERROR   = 0x09
+local BLUFI_CALC_MD5_ERROR      = 0x0a
+local BLUFI_WIFI_SCAN_FAIL      = 0x0b
+local BLUFI_MSG_STATE_ERROR     = 0x0c
+
+local BLUFI_OPMODE_NULL         = 0x00
+local BLUFI_OPMODE_STA          = 0x01
+local BLUFI_OPMODE_SOFTAP       = 0x02
+local BLUFI_OPMODE_SOFTAPSTA    = 0x03
+
+local BLUFI_STA_CONN_SUCCESS    = 0x00
+local BLUFI_STA_CONN_FAIL       = 0x01
+local BLUFI_STA_CONNECTING      = 0x02
+local BLUFI_STA_NO_IP           = 0x03
+
+local BLUFI_BLE_STATE_DISCONN   = 0x00
+local BLUFI_BLE_STATE_CONNED    = 0x01
+
+local BLUFI_WLAN_STATE_DISCONN  = 0x00
+local BLUFI_WLAN_STATE_CONNING  = 0x01
+local BLUFI_WLAN_STATE_CONNED   = 0x02
+
+local BLUFI_SEQUENCE_ERROR      = 0x00
+local BLUFI_CHECKSUM_ERROR      = 0x01
+local BLUFI_DECRYPT_ERROR       = 0x02
+local BLUFI_ENCRYPT_ERROR       = 0x03
+local BLUFI_INIT_SECURITY_ERROR = 0x04
+local BLUFI_DH_MALLOC_ERROR     = 0x05
+local BLUFI_DH_PARAM_ERROR      = 0x06
+local BLUFI_READ_PARAM_ERROR    = 0x07
+local BLUFI_MAKE_PUBLIC_ERROR   = 0x08
+local BLUFI_DATA_FORMAT_ERROR   = 0x09
+local BLUFI_CALC_MD5_ERROR      = 0x0a
+local BLUFI_WIFI_SCAN_FAIL      = 0x0b
+local BLUFI_MSG_STATE_ERROR     = 0x0c
+
+local function BLUFI_FC_IS_ENC(fc) return      ((fc) & BLUFI_FC_ENC_MASK) ~= 0 end
+local function BLUFI_FC_IS_CHECK(fc) return     ((fc) & BLUFI_FC_CHECK_MASK) ~= 0 end
+local function BLUFI_FC_IS_REQ_ACK(fc) return   ((fc) & BLUFI_FC_REQ_ACK_MASK) ~= 0 end
+local function BLUFI_FC_IS_FRAG(fc) return      ((fc) & BLUFI_FC_FRAG_MASK) ~= 0 end
+
+local BLUFI_PROTOCOL_DATA   = "BLUFI_PROTOCOL_DATA"
+local BLUFI_TASK_EXIT       = "BLUFI_TASK_EXIT"
+
+local espblufi_uuid_service  = "0xFFFF"
+local espblufi_uuid2device   = "0xFF01"
+local espblufi_uuid2mobile   = "0xFF02"
+
+local espblufi_att_db = {
+    string.fromHex(espblufi_uuid_service),
+    {string.fromHex(espblufi_uuid2device),ble.WRITE,},
+    {string.fromHex(espblufi_uuid2mobile),ble.NOTIFY|ble.READ}
+}
+
+local blufi_env = {ble_device=nil,callback=nil,isfrag=nil,
+                    ble_state = BLUFI_BLE_STATE_DISCONN,
+                    wlan_state = BLUFI_WLAN_STATE_DISCONN,
+                    opmode = BLUFI_OPMODE_NULL,
+                    recv_seq=0,send_seq=0,sec_mode=0,
+                    softap_conn_num = 0, 
+                    softap_auth_mode=0,
+                    softap_max_conn_num=0,
+                    softap_channel = nil,
+                    sta_max_conn_retry = nil,
+                    sta_conn_end_reason = nil,
+                    sta_ssid=nil,sta_passwd=nil,
+                    softap_ssid=nil,softap_passwd=nil}
+
+local blufi_hdr = {type=nil,fc=nil,seq=nil,data_len=nil,data=nil}
+
+espblufi.EVENT_STA_INFO     = 0x01
+espblufi.EVENT_SOFTAP_INFO  = 0x02
+espblufi.EVENT_CUSTOM_DATA  = 0x03
+
+sys.subscribe("WLAN_AP_INC", function(state, mac)
+    if state == "CONNECTED" then
+        blufi_env.softap_conn_num = blufi_env.softap_conn_num+1
+    elseif state == "DISCONNECTED" and blufi_env.softap_conn_num>0 then
+        blufi_env.softap_conn_num = blufi_env.softap_conn_num-1
+    end
+end)
+
+local function blufi_crc_checksum(data)
+    return crypto.crc16("IBM",data)
+end
+
+local function blufi_send_encap(blufi_hdr_send)
+    local send_data = string.char(blufi_hdr_send.type,blufi_hdr_send.fc,blufi_hdr_send.seq,#blufi_hdr_send.data) .. blufi_hdr_send.data
+    if BLUFI_TYPE_IS_CTRL(blufi_hdr_send.type) then
+        blufi_hdr_send.fc = blufi_hdr_send.fc|BLUFI_FC_CHECK
+        send_data = send_data .. blufi_crc_checksum(send_data)
+    end
+    blufi_env.send_seq = blufi_env.send_seq + 1
+    blufi_env.ble_device:write_notify({uuid_service=string.fromHex(espblufi_uuid_service),uuid_characteristic=string.fromHex(espblufi_uuid2mobile)},send_data)
+end
+
+local function btc_blufi_send_encap(type,data)
+    if blufi_env.ble_state == BLUFI_BLE_STATE_DISCONN then
+        return
+    end
+    local blufi_hdr_send = {type=type,fc=0,seq=blufi_env.send_seq,data=data}
+    blufi_send_encap(blufi_hdr_send)
+end
+
+local function btc_blufi_wifi_conn_report(sta_conn_state)
+    local data = string.char(blufi_env.opmode,blufi_env.wlan_state,blufi_env.softap_conn_num)
+    local wlan_info = wlan.getInfo()
+    if wlan_info then
+        if wlan_info.bssid then
+            data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID,6) .. string.fromHex(wlan_info.bssid)
+        end
+        if wlan_info.rssi then
+            data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_RSSI,1,wlan_info.rssi%256)
+        end
+    end
+    if blufi_env.sta_ssid then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_SSID,#blufi_env.sta_ssid) .. blufi_env.sta_ssid
+    end
+    if blufi_env.sta_passwd then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD,#blufi_env.sta_passwd) .. blufi_env.sta_passwd
+    end
+    if blufi_env.softap_ssid then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID,#blufi_env.softap_ssid) .. blufi_env.softap_ssid
+    end
+    if blufi_env.softap_passwd then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD,#blufi_env.softap_passwd) .. blufi_env.softap_passwd
+    end
+    if blufi_env.softap_authmode then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE,1) .. blufi_env.softap_authmode
+    end
+    if blufi_env.softap_max_conn_num then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM,1) .. blufi_env.softap_max_conn_num
+    end
+    if blufi_env.softap_channel then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL,1) .. blufi_env.softap_channel
+    end
+    if blufi_env.sta_max_conn_retry then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_MAX_CONN_RETRY,1) .. blufi_env.sta_max_conn_retry
+    end
+    if blufi_env.sta_conn_end_reason then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_END_REASON,1) .. blufi_env.sta_conn_end_reason
+    end
+    btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_REP), data);
+end
+
+local function btc_blufi_send_wifi_list(results)
+    local data = ""
+    for k,v in pairs(results) do data = data .. string.char(#v["ssid"]+1,v["rssi"]%256) .. v["ssid"] end
+    btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_LIST),data)
+end
+
+local function btc_blufi_send_ack(seq)
+    btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_CTRL, BLUFI_TYPE_CTRL_SUBTYPE_ACK),string.char(seq))
+end
+
+local function btc_blufi_send_error_info(state)
+    btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_ERROR_INFO),string.char(state))
+end
+
+local function btc_blufi_send_custom_data(data)
+    btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA),data)
+end
+
+local SEC_TYPE_DH_PARAM_LEN   = 0x00
+local SEC_TYPE_DH_PARAM_DATA  = 0x01
+local SEC_TYPE_DH_P           = 0x02
+local SEC_TYPE_DH_G           = 0x03
+local SEC_TYPE_DH_PUBLIC      = 0x04
+
+local blufi_sec = {dh_param_len=0}
+
+local function blufi_dh_negotiate_data_handler(data)
+    if #data<3 then
+        btc_blufi_send_error_info(BLUFI_DATA_FORMAT_ERROR)
+    end
+
+    local type = data:byte(1)
+    if type == SEC_TYPE_DH_PARAM_LEN then
+        blufi_sec.dh_param_len = ((data:byte(2)<<8)|data:byte(3));
+        -- print("dh_param_len",blufi_sec.dh_param_len)
+    elseif type == SEC_TYPE_DH_PARAM_DATA then
+        -- print("SEC_TYPE_DH_PARAM_DATA")
+        if #data < (blufi_sec.dh_param_len + 1) then
+            btc_blufi_send_error_info(BLUFI_DH_PARAM_ERROR);
+            return;
+        end
+
+        -- 秘钥待实现,功能需要mbedtls引出c接口
+        btc_blufi_send_error_info(BLUFI_INIT_SECURITY_ERROR);
+
+    elseif type == SEC_TYPE_DH_P then
+        print("SEC_TYPE_DH_P")
+    elseif type == SEC_TYPE_DH_G then
+        print("SEC_TYPE_DH_G")
+    elseif type == SEC_TYPE_DH_PUBLIC then
+        print("SEC_TYPE_DH_PUBLIC")
+    end
+
+end
+
+local function btc_blufi_protocol_handler(parse_data)
+    local target_data_len = 0
+    local parse_data_len = #parse_data
+
+    if parse_data_len<4 then return end
+
+    blufi_hdr.type,blufi_hdr.fc,blufi_hdr.seq,blufi_hdr.data_len = string.unpack('<BBBB',parse_data)
+
+    if BLUFI_FC_IS_CHECK(blufi_hdr.fc) then
+        target_data_len = blufi_hdr.data_len + 4 + 2 -- // Data + (Type + Frame Control + Sequence Number + Data Length) + Checksum
+    else
+        target_data_len = blufi_hdr.data_len + 4 -- Data + (Type + Frame Control + Sequence Number + Data Length)
+    end
+    -- print("target_data_len",target_data_len,"parse_data_len",parse_data_len)
+    if target_data_len ~= parse_data_len then
+        return
+    end
+
+    if blufi_hdr.seq ~= blufi_env.recv_seq then
+        return
+    end
+    blufi_env.recv_seq = blufi_env.recv_seq + 1
+
+    if BLUFI_FC_IS_ENC(blufi_hdr.fc) then
+        -- 解密功能需要mbedtls引出c接口
+    end
+    if BLUFI_FC_IS_CHECK(blufi_hdr.fc) and crypto then
+        -- 需要app配合调试,暂时不强制校验
+        local checksum = blufi_crc_checksum()
+    end
+
+    if BLUFI_FC_IS_REQ_ACK(blufi_hdr.fc) then
+        btc_blufi_send_ack(blufi_hdr.seq)
+    end
+
+    if blufi_hdr.data_len and blufi_hdr.data_len>0 then
+        if blufi_env.isfrag then
+            blufi_hdr.data = blufi_hdr.data .. parse_data:sub(5)
+        else
+            blufi_hdr.data = parse_data:sub(5)
+        end
+    end
+
+    if BLUFI_FC_IS_FRAG(blufi_hdr.fc) then
+        blufi_env.isfrag = true
+        return
+    else
+        blufi_env.isfrag = false
+    end
+
+    -- print(blufi_hdr.type,blufi_hdr.fc,blufi_hdr.seq,blufi_hdr.data_len)
+    -- print(blufi_hdr.data,blufi_hdr.data:toHex())
+
+    local blufi_type = BLUFI_GET_TYPE(blufi_hdr.type)
+    local blufi_subtype = BLUFI_GET_SUBTYPE(blufi_hdr.type)
+    if blufi_type == BLUFI_TYPE_CTRL then
+        if blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_ACK then
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE then
+            blufi_env.sec_mode = blufi_hdr.data;
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE then
+            blufi_env.opmode = blufi_hdr.data:byte(1)
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP then
+            blufi_env.callback(espblufi.EVENT_STA_INFO,
+                                {ssid=blufi_env.sta_ssid,password=blufi_env.sta_passwd})
+            wlan.connect(blufi_env.sta_ssid, blufi_env.sta_passwd)
+            blufi_env.wlan_state = BLUFI_WLAN_STATE_CONNING
+            local results = sys.waitUntil("IP_READY",3000)
+            if results then blufi_env.wlan_state = BLUFI_WLAN_STATE_CONNED 
+            else blufi_env.wlan_state = BLUFI_WLAN_STATE_DISCONN end
+            btc_blufi_wifi_conn_report()
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP then
+            wlan.disconnect()
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS then
+            btc_blufi_wifi_conn_report()
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA then
+            -- print("BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA")
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION then
+            btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_REPLY_VERSION), string.char(BTC_BLUFI_GREAT_VER,BTC_BLUFI_SUB_VER))
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_DISCONNECT_BLE then
+            blufi_env.ble_device:disconnect()
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_LIST then
+            wlan.scan()
+            sys.waitUntil("WLAN_SCAN_DONE", 15000)
+            local results = wlan.scanResult()
+            if results and #results>0 then
+                btc_blufi_send_wifi_list(results)
+            else
+                btc_blufi_send_error_info(BLUFI_WIFI_SCAN_FAIL)
+            end
+        end
+    elseif blufi_type == BLUFI_TYPE_DATA then
+        if blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_NEG then
+            local data = blufi_dh_negotiate_data_handler(blufi_hdr.data)
+            if data then
+                btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_NEG),data)
+            end
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID then
+            blufi_env.sta_bssid = blufi_hdr.data
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_STA_SSID then
+            blufi_env.sta_ssid = blufi_hdr.data
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD then
+            blufi_env.sta_passwd = blufi_hdr.data
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID then
+            blufi_env.softap_ssid = blufi_hdr.data
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD then
+            blufi_env.softap_passwd = blufi_hdr.data
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM then
+            blufi_env.softap_max_conn_num = blufi_hdr.data:byte(1)
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE then
+            blufi_env.softap_auth_mode = blufi_hdr.data:byte(1)
+            blufi_env.callback(espblufi.EVENT_SOFTAP_INFO,
+                                {ssid=blufi_env.softap_ssid,password=blufi_env.softap_passwd,})
+            wlan.createAP(blufi_env.softap_ssid, blufi_env.softap_passwd,nil,nil,blufi_env.softap_max_channel,{max_conn  = blufi_env.softap_max_conn_num})
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL then
+            blufi_env.softap_max_channel = blufi_hdr.data:byte(1)
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_USERNAME then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_USERNAME")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_CA then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_CA")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA then
+            blufi_env.callback(espblufi.EVENT_CUSTOM_DATA,blufi_hdr.data)
+        end
+    else
+        return
+    end
+end
+
+local function espblufi_task() 
+    while true do
+        local result, event, data = sys.waitUntil(BLUFI_TOPIC)
+        if result then
+            if event == BLUFI_PROTOCOL_DATA then
+                btc_blufi_protocol_handler(data)
+            elseif event == BLUFI_TASK_EXIT then
+                break
+            end
+        end
+    end
+
+end
+
+local function espblufi_ble_callback(ble_device, ble_event, ble_param)
+    if ble_event == ble.EVENT_CONN then
+        blufi_env.ble_state = BLUFI_BLE_STATE_CONNED
+    elseif ble_event == ble.EVENT_DISCONN then
+        blufi_env.ble_state = BLUFI_BLE_STATE_DISCONN
+    elseif ble_event == ble.EVENT_WRITE then
+        sys.publish(BLUFI_TOPIC,BLUFI_PROTOCOL_DATA,ble_param.data)
+    end
+end
+
+--[[
+初始化espblufi
+@api espblufi.init(bluetooth_device,espblufi_callback,local_name)
+@userdata bluetooth_device 蓝牙设备对象
+@function 事件回调函数
+@number 蓝牙名,可选,默认为"BLUFI_xxx",xxx为设备型号(因为esp的配网测试app默认过滤蓝牙名称为BLUFI_开头的设备进行显示,可手动修改)
+@usage
+espblufi.init(espblufi_callback)
+]]
+function espblufi.init(bluetooth_device,espblufi_callback,local_name)
+    if not bluetooth or not ble or not wlan then
+        log.error("need bluetooth ble and wlan")
+        return
+    end
+    if bluetooth_device == nil or type(bluetooth_device) ~= "userdata" then
+        log.error("bluetooth_device is nil")
+        return
+    end
+    if not espblufi_callback then
+        log.error("espblufi_callback is nil")
+        return
+    else
+        blufi_env.callback = espblufi_callback
+    end
+    if not local_name then
+        local_name = "BLUFI_"..rtos.bsp()
+    end
+    wlan.init()
+    local ble_device = bluetooth_device:ble(espblufi_ble_callback)
+    ble_device:gatt_create(espblufi_att_db)
+    ble_device:adv_create({
+        adv_data = {{ble.FLAGS,string.char(0x06)},{ble.COMPLETE_LOCAL_NAME, local_name}},
+    })
+    blufi_env.ble_device = ble_device
+    return
+end
+
+--[[
+开始配网
+@api espblufi.start()
+@usage
+espblufi.start()
+]]
+function espblufi.start()
+    sys.taskInit(espblufi_task)
+    blufi_env.ble_device:adv_start()
+end
+
+--[[
+停止配网
+@api espblufi.stop()
+@usage
+espblufi.stop()
+]]
+function espblufi.stop()
+    blufi_env.ble_device:adv_stop()
+    sys.publish(BLUFI_TOPIC,BLUFI_TASK_EXIT)
+end
+
+function espblufi.deinit()
+    
+end
+
+--[[
+发送自定义数据,一般用于接收到客户端发送的自定义命令后进行回复
+@api espblufi.send_custom_data(data)
+@string 回复的数据包内容
+@usage
+espblufi.send_custom_data(data)
+]]
+function espblufi.send_custom_data(data)
+    btc_blufi_send_custom_data(data)
+end
+
+return espblufi

+ 985 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/fm17622.lua

@@ -0,0 +1,985 @@
+--[[
+
+@module fm17622
+@summary fm17622的LPCD和读写驱动
+@author  杨壮壮
+@data    2023/11/20
+@usage
+    fm17622=require "fm17622"
+    i2c.setup(1, i2c.FAST)
+    fm17622.setup(1, 0x28,20,32,function()
+
+        local Key_A = string.char(0x01,0x02,0x03,0x04,0x05,0x06)
+        local wdata = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
+        fm17622.write_datablock(4,wdata,0,Key_A)
+        local strat,rdata=fm17622.read_datablock(4,0,Key_A)
+        if strat then
+            log.info(json.encode(rdata))     
+        end
+
+    end,true)
+]]
+
+
+
+
+
+
+local fm17622 = {}
+
+local sys = require "sys"
+
+local fm17622_iic_id, fm17622_iic_addr, fm17622_NPD,debug
+local endcall
+local nfc_Data = {}
+
+local nfc_work=false--防冲突
+
+-- LPCD配置信息
+local LPCD_THRSH_H = 0x00
+local LPCD_THRSH_L = 0x10
+local LPCD_CWP = 10 -- LPCD PMOS输出驱动 0~63
+local LPCD_CWN = 10 -- LPCD NMOS输出驱动 0~15
+local LPCD_SLEEPTIME = 10 --LPCD 唤醒间隔时间,每一档为32ms,休眠时间:(16+1)*32=544ms
+
+local TX1_TX2_CW_DISABLE = 0
+local TX1_CW_ENABLE = 1
+local TX2_CW_ENABLE = 2
+local TX1_TX2_CW_ENABLE = 3
+
+local JREG_PAGE0 = 0x00 -- Page register in page 0
+local JREG_COMMAND = 0x01 -- Contains Command bits, PowerDown bit and bit to switch receiver off.
+local JREG_COMMIEN = 0x02 -- Contains Communication interrupt enable bits andbit for Interrupt inversion.
+local JREG_DIVIEN = 0x03 -- Contains RfOn, RfOff, CRC and Mode Interrupt enable and bit to switch Interrupt pin to PushPull mode.
+local JREG_COMMIRQ = 0x04 -- Contains Communication interrupt request bits.
+local JREG_DIVIRQ = 0x05 -- Contains RfOn, RfOff, CRC and Mode Interrupt request.
+local JREG_ERROR = 0x06 -- Contains Protocol, Parity, CRC, Collision, Buffer overflow, Temperature and RF error flags.
+local JREG_STATUS1 = 0x07 -- Contains status information about Lo- and HiAlert, RF-field on, Timer, Interrupt request and CRC status.
+local JREG_STATUS2 = 0x08 -- Contains information about internal states (Modemstate),Mifare states and possibility to switch Temperature sensor off.
+local JREG_FIFODATA = 0x09 -- Gives access to FIFO. Writing to register increments theFIFO level (register =0x0A), reading decrements it.
+local JREG_FIFOLEVEL = 0x0A -- Contains the actual level of the FIFO.     
+local JREG_WATERLEVEL = 0x0B -- Contains the Waterlevel value for the FIFO 
+local JREG_CONTROL = 0x0C -- Contains information about last received bits, Initiator mode bit, bit to copy NFCID to FIFO and to Start and stopthe Timer unit.
+local JREG_BITFRAMING = 0x0D -- Contains information of last bits to send, to align received bits in FIFO and activate sending in Transceive]]
+local JREG_COLL = 0x0E -- Contains all necessary bits for Collission handling 
+local JREG_RFU0F = 0x0F -- Currently not used.                                 
+
+local JREG_PAGE1 = 0x10 -- Page register in page 1
+local JREG_MODE = 0x11 -- Contains bits for auto wait on Rf, to detect SYNC byte in NFC mode and MSB first for CRC calculation
+local JREG_TXMODE = 0x12 -- Contains Transmit Framing, Speed, CRC enable, bit for inverse mode and TXMix bit.                            
+local JREG_RXMODE = 0x13 -- Contains Transmit Framing, Speed, CRC enable, bit for multiple receive and to filter errors.                 
+local JREG_TXCONTROL = 0x14 -- Contains bits to activate and configure Tx1 and Tx2 and bit to activate 100% modulation.                      
+local JREG_TXAUTO = 0x15 -- Contains bits to automatically switch on/off the Rf and to do the collission avoidance and the initial rf-on.
+local JREG_TXSEL = 0x16 -- Contains SigoutSel, DriverSel and LoadModSel bits.
+local JREG_RXSEL = 0x17 -- Contains UartSel and RxWait bits.                 
+local JREG_RXTRESHOLD = 0x18 -- Contains MinLevel and CollLevel for detection.    
+local JREG_DEMOD = 0x19 -- Contains bits for time constants, hysteresis and IQ demodulator settings. 
+local JREG_FELICANFC = 0x1A -- Contains bits for minimum FeliCa length received and for FeliCa syncronisation length.
+local JREG_FELICANFC2 = 0x1B -- Contains bits for maximum FeliCa length received.      
+local JREG_MIFARE = 0x1C -- Contains Miller settings, TxWait settings and MIFARE halted mode bit.
+local JREG_MANUALRCV = 0x1D -- Currently not used.                          
+local JREG_RFU1E = 0x1E -- Currently not used.                          
+local JREG_SERIALSPEED = 0x1F -- Contains speed settings for serila interface.
+
+local JREG_PAGE2 = 0x20 -- Page register in page 2 
+local JREG_CRCRESULT1 = 0x21 -- Contains MSByte of CRC Result.                
+local JREG_CRCRESULT2 = 0x22 -- Contains LSByte of CRC Result.                
+local JREG_GSNLOADMOD = 0x23 -- Contains the conductance and the modulation settings for the N-MOS transistor only for load modulation (See difference to JREG_GSN!). 
+local JREG_MODWIDTH = 0x24 -- Contains modulation width setting.                    
+local JREG_TXBITPHASE = 0x25 -- Contains TxBitphase settings and receive clock change.
+local JREG_RFCFG = 0x26 -- Contains sensitivity of Rf Level detector, the receiver gain factor and the RfLevelAmp.
+local JREG_GSN = 0x27 -- Contains the conductance and the modulation settings for the N-MOS transistor during active modulation (no load modulation setting!).
+local JREG_CWGSP = 0x28 -- Contains the conductance for the P-Mos transistor.    
+local JREG_MODGSP = 0x29 -- Contains the modulation index for the PMos transistor.
+local JREG_TMODE = 0x2A -- Contains all settings for the timer and the highest 4 bits of the prescaler.
+local JREG_TPRESCALER = 0x2B -- Contais the lowest byte of the prescaler.   
+local JREG_TRELOADHI = 0x2C -- Contains the high byte of the reload value. 
+local JREG_TRELOADLO = 0x2D -- Contains the low byte of the reload value.  
+local JREG_TCOUNTERVALHI = 0x2E -- Contains the high byte of the counter value.
+local JREG_TCOUNTERVALLO = 0x2F -- Contains the low byte of the counter value. 
+
+local JREG_PAGE3 = 0x30 -- Page register in page 3
+local JREG_TESTSEL1 = 0x31 -- Test register                              
+local JREG_TESTSEL2 = 0x32 -- Test register                              
+local JREG_TESTPINEN = 0x33 -- Test register                              
+local JREG_TESTPINVALUE = 0x34 -- Test register                              
+local JREG_TESTBUS = 0x35 -- Test register                              
+local JREG_AUTOTEST = 0x36 -- Test register                              
+local JREG_VERSION = 0x37 -- Contains the product number and the version .
+local JREG_ANALOGTEST = 0x38 -- Test register                              
+local JREG_TESTSUP1 = 0x39 -- Test register                              
+local JREG_TESTSUP2 = 0x3A -- Test register                              
+local JREG_TESTADC = 0x3B -- Test register                              
+local JREG_ANALOGUETEST1 = 0x3C -- Test register                              
+local JREG_ANALOGUETEST0 = 0x3D -- Test register                              
+local JREG_ANALOGUETPD_A = 0x3E -- Test register                              
+local JREG_ANALOGUETPD_B = 0x3F -- Test register                              
+
+local CMD_MASK = 0xF0
+
+local CMD_IDLE = 0x00
+local CMD_CONFIGURE = 0x01
+local CMD_GEN_RAND_ID = 0x02
+local CMD_CALC_CRC = 0x03
+local CMD_TRANSMIT = 0x04
+local CMD_NOCMDCHANGE = 0x07
+local CMD_RECEIVE = 0x08
+local CMD_TRANSCEIVE = 0x0C
+local CMD_AUTOCOLL = 0x0D
+local CMD_AUTHENT = 0x0E
+local CMD_SOFT_RESET = 0x0F
+-- ============================================================================
+local JREG_EXT_REG_ENTRANCE = 0x0F -- ext register entrance
+-- ============================================================================
+local JBIT_EXT_REG_WR_ADDR = 0x40 -- wrire address cycle
+local JBIT_EXT_REG_RD_ADDR = 0x80 -- read address cycle
+local JBIT_EXT_REG_WR_DATA = 0xC0 -- write data cycle
+local JBIT_EXT_REG_RD_DATA = 0x00 -- read data cycle
+
+-- ============================================================================
+local JREG_LPCDTEST = 0x21
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDGMSEL = 0x24
+-- ============================================================================
+-- LPCD_ENB_AGC_AVDD = 0x00
+-- LPCD_ENB_AGC_DVDD = 0x10
+
+-- LPCD_RXCHANGE_JUDGE_MODE_0 = 0x00
+-- LPCD_RXCHANGE_JUDGE_MODE_1 = 0x04
+
+-- LPCD_GM_SEL_0 = 0x00
+-- LPCD_GM_SEL_1 = 0x01
+-- LPCD_GM_SEL_2 = 0x02
+-- LPCD_GM_SEL_3 = 0x03
+
+-- ============================================================================
+local JREG_LPCDSARADC1 = 0x25
+-- ============================================================================
+-- SARADC_SOC_CFG_4 = 0x00
+-- SARADC_SOC_CFG_8 = 0x01
+-- SARADC_SOC_CFG_16 = 0x02
+-- SARADC_SOC_CFG_24 = 0x03
+
+-- ============================================================================
+local JREG_LPCDDELTA_HI = 0x26
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDDELTA_LO = 0x27
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDICURR_HI = 0x28
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDICURR_LO = 0x29
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDQCURR_HI = 0x2A
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDQCURR_LO = 0x2B
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDILAST_HI = 0x2C
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDILAST_LO = 0x2D
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDQLAST_HI = 0x2E
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDQLAST_LO = 0x2F
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDAUX = 0x30
+-- ============================================================================
+-- IBN2U = 0x00
+-- TEST_BG = 0x01
+-- TEST_SAMPLE_I = 0x02
+-- TEST_AMP_OUT_IN = 0x03
+-- TEST_AMP_OUT_IP = 0x04
+-- TEST_AMP_OUT_QN = 0x05
+-- TEST_AMP_OUT_QP = 0x06
+-- OSC_64K = 0x07
+-- VBN2 = 0x08
+-- VBN1 = 0x09
+-- TEST_BG_VREF = 0x0A
+-- AVSS = 0x0B
+-- TEST_SAMPLE_Q = 0x0C
+-- DVDD = 0x0D
+-- TEST_CRYSTAL_VDD = 0x0E
+-- AVDD = 0x0F
+-- FLOAT_IN = 0x10
+
+-- ============================================================================
+local JREG_LPCDMISSWUP = 0x31
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDFLAGINV = 0x32
+-- ============================================================================
+-- LPCD_FLAG_INV_DISABLE = 0x00
+-- LPCD_FLAG_INV_ENABLE = 0x20
+
+-- ============================================================================
+local JREG_LPCDSLEEPTIMER = 0x33
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDTHRESH_H = 0x34
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDTHRESH_L = 0x35
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDREQATIMER = 0x37
+-- ============================================================================
+-- LPCD_IRQMISSWUP_ENABLE = 0x20
+-- LPCD_IRQMISSWUP_DISABLE = 0x00
+
+-- LPCD_REQA_TIME_80us = 0x00
+-- LPCD_REQA_TIME_100us = 0x01
+-- LPCD_REQA_TIME_120us = 0x02
+-- LPCD_REQA_TIME_150us = 0x03
+-- LPCD_REQA_TIME_200us = 0x04
+-- LPCD_REQA_TIME_250us = 0x05
+-- LPCD_REQA_TIME_300us = 0x06
+-- LPCD_REQA_TIME_400us = 0x07
+-- LPCD_REQA_TIME_500us = 0x08
+-- LPCD_REQA_TIME_600us = 0x09
+-- LPCD_REQA_TIME_800us = 0x0A
+-- LPCD_REQA_TIME_1ms = 0x0B
+-- LPCD_REQA_TIME_1ms2 = 0x0C
+-- LPCD_REQA_TIME_1ms6 = 0x0D
+-- LPCD_REQA_TIME_2ms = 0x0E
+-- LPCD_REQA_TIME_2ms5 = 0x0F
+-- LPCD_REQA_TIME_3ms = 0x10
+-- LPCD_REQA_TIME_3ms5 = 0x11
+-- LPCD_REQA_TIME_4ms = 0x12
+-- LPCD_REQA_TIME_5ms = 0x13
+-- LPCD_REQA_TIME_7ms = 0x14
+
+-- ============================================================================
+local JREG_LPCDREQAANA = 0x38
+-- ============================================================================
+-- LPCD_RXGAIN_23DB = 0x00
+-- LPCD_RXGAIN_33DB = 0x10 -- default
+-- LPCD_RXGAIN_38DB = 0x20
+-- LPCD_RXGAIN_43DB = 0x30
+
+-- LPCD_MINLEVEL_3 = 0x00
+-- LPCD_MINLEVEL_6 = 0x04
+-- LPCD_MINLEVEL_9 = 0x08
+-- LPCD_MINLEVEL_C = 0x0C
+
+-- LPCD_MODWIDTH_32 = 0x00
+-- LPCD_MODWIDTH_38 = 0x02
+
+-- ============================================================================
+local JREG_LPCDDETECTMODE = 0x39
+-- ============================================================================
+-- LPCD_TXSCALE_0 = 0x00 -- 1/8 of the TX power configured by CWGSP/CWGSNON
+-- LPCD_TXSCALE_1 = 0x08 -- 1/4 of the TX power configured by CWGSP/CWGSNON
+-- LPCD_TXSCALE_2 = 0x10 -- 1/2 of the TX power configured by CWGSP/CWGSNON
+-- LPCD_TXSCALE_3 = 0x18 -- 3/4 of the TX power configured by CWGSP/CWGSNON
+-- LPCD_TXSCALE_4 = 0x20 -- equal to of the TX power configured by CWGSP/CWGSNON
+-- LPCD_TXSCALE_5 = 0x28 -- twice of the TX power configured by CWGSP/CWGSNON
+-- LPCD_TXSCALE_6 = 0x30 -- 3 times of the TX power configured by CWGSP/CWGSNON
+-- LPCD_TXSCALE_7 = 0x31 -- 4 times of the TX power configured by CWGSP/CWGSNON
+
+-- LPCD_RX_CHANGE_MODE = 0x00
+-- LPCD_REQA_MODE = 0x01
+-- LPCD_COMBINE_MODE = 0x02
+
+-- ============================================================================
+local JREG_LPCDCTRLMODE = 0x3A
+-- ============================================================================
+-- RF_DET_ENABLE = 0x40
+-- RF_DET_DISABLE = 0x00
+
+-- RF_DET_SEN_135mV = 0x00
+-- RF_DET_SEN_180mV = 0x08
+-- RF_DET_SEN_240mV = 0x10
+-- RF_DET_SEN_300mV = 0x18
+
+-- LPCD_DISABLE = 0x00
+-- LPCD_ENABLE = 0x02
+
+-- ============================================================================
+local JREG_LPCDIRQ = 0x3B
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDRFTIMER = 0x3C
+-- ============================================================================
+-- LPCD_IRQINV_ENABLE = 0x20
+-- LPCD_IRQINV_DISABLE = 0x00
+
+-- LPCD_IRQ_PUSHPULL = 0x10
+-- LPCD_IRQ_OD = 0x00
+
+-- LPCD_RFTIME_5us = 0x00
+-- LPCD_RFTIME_10us = 0x01
+-- LPCD_RFTIME_15us = 0x02
+-- LPCD_RFTIME_20us = 0x03
+-- LPCD_RFTIME_25us = 0x04 -- default
+-- LPCD_RFTIME_30us = 0x05
+-- LPCD_RFTIME_35us = 0x06
+-- LPCD_RFTIME_40us = 0x07
+-- LPCD_RFTIME_50us = 0x08
+-- LPCD_RFTIME_60us = 0x09
+-- LPCD_RFTIME_70us = 0x0A
+-- LPCD_RFTIME_80us = 0x0B
+-- LPCD_RFTIME_100us = 0x0C
+-- LPCD_RFTIME_120us = 0x0D
+-- LPCD_RFTIME_150us = 0x0E
+-- LPCD_RFTIME_200us = 0x0F
+
+-- ============================================================================
+local JREG_LPCDTXCTRL1 = 0x3D
+-- ============================================================================
+-- LPCD_TX2_ENABLE = 0x20
+-- LPCD_TX2_DISABLE = 0x00
+
+-- LPCD_TX1_ENABLE = 0x10
+-- LPCD_TX1_DISABLE = 0x00
+
+-- LPCD_TX2ON_INV_ENABLE = 0x08
+-- LPCD_TX2ON_INV_DISABLE = 0x00
+
+-- LPCD_TX1ON_INV_ENABLE = 0x04
+-- LPCD_TX1ON_INV_DISABLE = 0x00
+
+-- LPCD_TX2OFF_INV_ENABLE = 0x02
+-- LPCD_TX2OFF_INV_DISABLE = 0x00
+
+-- LPCD_TX1OFF_INV_ENABLE = 0x01
+-- LPCD_TX1OFF_INV_DISABLE = 0x00
+
+-- ============================================================================
+local JREG_LPCDTXCTRL2 = 0x3E
+-- ============================================================================
+
+-- ============================================================================
+local JREG_LPCDTXCTRL3 = 0x3F
+-- ============================================================================
+-- LPCD_FLAG_TOUT_ENABLE = 0x20 -- TOUT Pad, set while LPCD DETECT, clear while LPCD SLEEP/SETUP(LPCD_TXEN_FLAG_INV = 0);
+-- LPCD_FLAG_TOUT_DISABLE = 0x00 -- disable TOUT Pad as the LPCD DETECT flag.
+
+-- LPCD_FLAG_D3_ENABLE = 0x10 -- D3 Pad, set while LPCD DETECT, clear while LPCD SLEEP/SETUP(LPCD_TXEN_FLAG_INV = 0);
+-- LPCD_FLAG_D3_DISABLE = 0x00 -- disable D3 Pad as the LPCD DETECT flag.
+
+local fm17622_SUCCESS = 0x00
+local fm17622_COMM_ERR = 0xf4
+
+--[[
+写fm17622寄存器
+SetReg(address, value)
+@number address 地址
+@number value    值
+@usage
+write_rawrc(fm17622_bit_framing,0x07)
+]]
+local function SetReg(address, value)
+
+    -- log.info("寄存器写:0x",string.format("%X",address),"值为0x",string.format("%X",value))
+
+    i2c.writeReg(fm17622_iic_id, fm17622_iic_addr, address, string.char(value))
+end
+
+--[[
+读fm17622寄存器
+GetReg(address,len)
+@number address 地址
+@number len 读取长度
+@return number 寄存器值
+@usage
+local n = read_rawrc(fm17622_com_irq,1) 
+]]
+
+local function GetReg(address, len)
+    if len == nil then
+        len = 1
+    end
+
+    local val = i2c.readReg(fm17622_iic_id, fm17622_iic_addr, address, len)
+
+    -- log.info("iic",val,type(val),#val)
+    if #val == 0 then
+        return nil;
+    end
+    -- log.info("寄存器读:0x",string.format("%X",address),"值为0x",string.format("%X",string.byte(val)))
+    return string.byte(val)
+end
+
+local function SetReg_Ext(address, value)
+    -- log.info("扩展寄存器写:0x",string.format("%X",address),"值为0x",string.format("%X",string.byte(value)))
+    SetReg(JREG_EXT_REG_ENTRANCE, (JBIT_EXT_REG_WR_ADDR + address) % 256)
+    SetReg(JREG_EXT_REG_ENTRANCE, (JBIT_EXT_REG_WR_DATA + value) % 256)
+    return;
+end
+
+-- //***********************************************
+-- //函数名称:GetReg_Ext(unsigned char ExtRegAddr)
+-- //函数功能:读取扩展寄存器值
+-- //入口参数:ExtRegAddr:扩展寄存器地址 
+-- //出口参数:unsigned char  TRUE:读取成功   FALSE:失败
+-- //***********************************************
+local function GetReg_Ext(address)
+    -- log.info("扩展寄存器读:0x",string.format("%X",address))
+    SetReg(JREG_EXT_REG_ENTRANCE, (JBIT_EXT_REG_RD_ADDR + address))
+    return GetReg(JREG_EXT_REG_ENTRANCE)
+
+end
+
+BIT0 = 0x01
+BIT1 = 0x02
+BIT2 = 0x04
+BIT3 = 0x08
+BIT4 = 0x10
+BIT5 = 0x20
+BIT6 = 0x40
+BIT7 = 0x80
+--[[
+对rc522寄存器置位
+@api ModifyReg(address, mask)
+@number address 地址
+@number mask    置位值
+@number set     选择置位还是清位
+@usage
+ModifyReg (rc522_fifo_level, 0x80)	
+]]
+local function ModifyReg(addr, mask, set)
+    local regdata = GetReg(addr)
+    if set == 1 then
+        regdata = regdata | mask
+    else
+        regdata = regdata & (~mask);
+    end
+    SetReg(addr, regdata);
+end
+
+--[[
+对fm17622寄存器置位
+set_bit_mask(address, mask)
+@number address 地址
+@number mask    置位值
+@usage
+
+]]
+local function set_bit_mask(address, mask)
+    ModifyReg(address, mask, 1)
+end
+
+--[[
+对fm17622寄存器清位
+clear_bit_mask(address, mask)
+@number address 地址
+@number mask    清位值
+@usage
+clear_bit_mask(rc522_com_irq, 0x80 )
+]]
+local function clear_bit_mask(address, mask)
+    ModifyReg(address, mask, 0)
+end
+
+-- //*************************************
+-- //函数  名:fm17622_Initial_ReaderA
+-- 设置A卡模式
+-- //入口参数:
+-- //出口参数:
+-- //*************************************
+local function fm17622_Initial_ReaderA()
+    SetReg(JREG_MODWIDTH, 0x26) -- MODWIDTH = 106kbps
+    ModifyReg(JREG_TXAUTO, BIT6, 1) -- Force 100ASK = 1
+    SetReg(JREG_GSN, (0x0f << 4)) -- Config GSN Config ModGSN 	
+    SetReg(JREG_CWGSP, 63) -- Config GSP  0-63
+    SetReg(JREG_CONTROL, BIT4) -- Initiator = 1
+    SetReg(JREG_RFCFG, 5 << 4) -- Config RxGain
+    SetReg(JREG_RXTRESHOLD, (8 << 4) | 4) -- Config MinLevel Config CollLevel	
+end
+local function ReaderA_Request()
+    SetReg(JREG_TXMODE, 0) -- Disable TxCRC
+    SetReg(JREG_RXMODE, 0) -- Disable RxCRC
+    SetReg(JREG_COMMAND, 0) -- command = Idel
+    SetReg(JREG_FIFOLEVEL, 128) -- Clear FIFO
+    SetReg(JREG_FIFODATA, 0x26)
+    SetReg(JREG_COMMAND, 12) -- command = Transceive
+    SetReg(JREG_BITFRAMING, 0x87) -- Start Send
+    sys.wait(5)
+    local reg_data = GetReg(JREG_FIFOLEVEL)
+
+    if reg_data == 2 then
+        -- 
+        nfc_Data["ATQA"] = GetReg(JREG_FIFODATA) * 256 + GetReg(JREG_FIFODATA)
+        return fm17622_SUCCESS
+    end
+    return fm17622_COMM_ERR
+end
+local function ReaderA_AntiColl()
+    SetReg(JREG_TXMODE, 0) -- Disable TxCRC
+    SetReg(JREG_RXMODE, 0) -- Disable RxCRC
+    SetReg(JREG_COMMAND, 0) -- command = Idel
+    SetReg(JREG_FIFOLEVEL, 128) -- Clear FIFO
+    SetReg(JREG_FIFODATA, 0x93)
+    SetReg(JREG_FIFODATA, 0x20)
+    SetReg(JREG_COMMAND, 12) -- command = Transceive
+    SetReg(JREG_BITFRAMING, 0x80) -- Start Send
+    sys.wait(5)
+    local reg_data = GetReg(JREG_FIFOLEVEL)
+
+    if reg_data == 5 then
+
+        local uid1 = GetReg(JREG_FIFODATA)
+        nfc_Data["UID"] = {}
+        nfc_Data["UID"][1] = uid1
+
+        local uid2 = GetReg(JREG_FIFODATA)
+        nfc_Data["UID"][2] = uid2
+
+        local uid3 = GetReg(JREG_FIFODATA)
+        nfc_Data["UID"][3] = uid3
+
+        local uid4 = GetReg(JREG_FIFODATA)
+        nfc_Data["UID"][4] = uid4
+
+        nfc_Data["BCC"] = GetReg(JREG_FIFODATA)
+
+        if (uid1 ~ uid2 ~ uid3 ~ uid4) == nfc_Data["BCC"] then
+            return fm17622_SUCCESS
+        end
+
+        return fm17622_COMM_ERR
+    end
+    return fm17622_COMM_ERR
+end
+
+local function ReaderA_Select()
+    SetReg(JREG_TXMODE, 0x80) -- Enable TxCRC
+    SetReg(JREG_RXMODE, 0x80) -- Enable RxCRC
+    SetReg(JREG_COMMAND, 0) -- command = Idel
+    SetReg(JREG_FIFOLEVEL, 128) -- Clear FIFO
+    SetReg(JREG_FIFODATA, 0x93)
+    SetReg(JREG_FIFODATA, 0x70)
+    SetReg(JREG_FIFODATA, nfc_Data["UID"][1])
+    SetReg(JREG_FIFODATA, nfc_Data["UID"][2])
+    SetReg(JREG_FIFODATA, nfc_Data["UID"][3])
+    SetReg(JREG_FIFODATA, nfc_Data["UID"][4])
+    SetReg(JREG_FIFODATA, nfc_Data["BCC"])
+    SetReg(JREG_COMMAND, 12) -- command = Transceive
+    SetReg(JREG_BITFRAMING, 0x80) -- Start Send
+    sys.wait(5)
+    local reg_data = GetReg(JREG_FIFOLEVEL)
+    if reg_data == 1 then
+        nfc_Data["SAK"] = GetReg(JREG_FIFODATA)
+        return fm17622_SUCCESS
+    end
+    return fm17622_COMM_ERR
+end
+
+-- *************************************
+-- 函数  名:ReaderA_CardActivate
+-- 开始寻卡,找卡选卡
+-- 入口参数:
+-- 出口参数:fm17622_SUCCESS, fm17622_COMM_ERR
+-- *************************************
+
+local function ReaderA_CardActivate()
+    local result = ReaderA_Request()
+    if result ~= fm17622_SUCCESS then
+        return fm17622_COMM_ERR
+    end
+
+    result = ReaderA_AntiColl()
+    if result ~= fm17622_SUCCESS then
+        return fm17622_COMM_ERR
+    end
+
+    result = ReaderA_Select()
+    if result ~= fm17622_SUCCESS then
+        return fm17622_COMM_ERR
+    end
+
+    return result
+end
+
+local function SetCW(cw_mode)
+    local reg
+    SetReg(JREG_TXCONTROL, 0x80);
+    if cw_mode == TX1_TX2_CW_DISABLE then
+        ModifyReg(JREG_TXCONTROL, 1 | 2, 0);
+        reg = GetReg(JREG_TXCONTROL)
+    end
+    if cw_mode == TX1_CW_ENABLE then
+        ModifyReg(JREG_TXCONTROL, 1, 1);
+        ModifyReg(JREG_TXCONTROL, 2, 0);
+    end
+    if cw_mode == TX2_CW_ENABLE then
+        ModifyReg(JREG_TXCONTROL, 1, 0);
+        ModifyReg(JREG_TXCONTROL, 2, 1);
+    end
+    if cw_mode == TX1_TX2_CW_ENABLE then
+        ModifyReg(JREG_TXCONTROL, 1 | 2, 1);
+    end
+    sys.wait(5)
+end
+
+--[[ 
+fm17622 发送命令通讯
+@number command 发送(0x0e)还是放松并接受(0x0c)
+@number data  发送是数据
+]]
+
+local function command(cmd, data)
+    local out_data = {}
+    local irqEn
+    local waitFor
+    -- SetReg(JREG_TXMODE, 0) -- Disable TxCRC
+    -- SetReg(JREG_RXMODE, 0) -- Disable RxCRC
+    SetReg(JREG_COMMAND, 0) -- command = Idel
+    SetReg(JREG_FIFOLEVEL, 128) -- Clear FIFO
+    for i = 1, #data do
+        SetReg(JREG_FIFODATA, data[i])
+    end
+    SetReg(JREG_COMMAND, cmd) -- command = Transceive
+    SetReg(JREG_BITFRAMING, 0x80) -- Start Send
+
+    sys.wait(25)
+    clear_bit_mask(JREG_BITFRAMING, 0x80)
+
+    local reg_data = GetReg(JREG_FIFOLEVEL)
+
+    if reg_data == 0 then
+        return nil
+    end
+    for i = 1, reg_data do
+        out_data[i] = GetReg(JREG_FIFODATA)
+    end
+    return out_data
+end
+
+--[[ 
+fm17622 LPCD初始化
+]]
+function fm17622.LPCD_setup()
+
+    SetReg(JREG_COMMAND, CMD_SOFT_RESET)
+    sys.wait(5)
+    if GetReg(JREG_COMMAND) ~= 0x20 then
+        log.debug("没有复位成功", GetReg(JREG_COMMAND))
+        return false;
+    end
+
+    SetReg_Ext(JREG_LPCDGMSEL, 0x10 | 0x04 | 0x00)
+    SetReg_Ext(JREG_LPCDSARADC1, 0x38 | 0x02);
+    SetReg_Ext(JREG_LPCDCTRLMODE, 0 | 0 | 2)
+    SetReg_Ext(JREG_LPCDDETECTMODE, 0x20 | 0)
+    SetReg_Ext(JREG_LPCDSLEEPTIMER, LPCD_SLEEPTIME)
+    SetReg_Ext(JREG_LPCDRFTIMER, 0x20 | 0x10 | 0x01)
+    SetReg_Ext(JREG_LPCDTHRESH_L, LPCD_THRSH_L)
+    SetReg_Ext(JREG_LPCDTXCTRL2, LPCD_CWP)
+    SetReg_Ext(JREG_LPCDTXCTRL3, LPCD_CWN)
+    log.debug("复位成功")
+    return true
+end
+
+-- //***********************************************
+-- //函数名称:Lpcd_Get_ADC_Value()
+-- //函数功能:Lpcd_Get_ADC_Value读取LPCD的ADC数据
+--             用于调试的lpcd参数
+-- //入口参数:
+-- //出口参数:
+-- //***********************************************
+local function Lpcd_Get_ADC_Value()
+
+    local reg = GetReg_Ext(JREG_LPCDICURR_HI);
+    local reg1 = GetReg_Ext(JREG_LPCDICURR_LO);
+    local Current1 = (reg << 6) + reg1;
+    Current2 = reg % 4 -- ((reg<<6)>>8);
+    log.debug("-> LPCD I Current is:", Current2, Current1, "\r\n");
+
+    reg = GetReg_Ext(JREG_LPCDQCURR_HI);
+    reg1 = GetReg_Ext(JREG_LPCDQCURR_LO);
+    Current1 = (reg << 6) + reg1;
+    Current2 = reg % 4 -- ((reg<<6)>>8);
+    log.debug("-> LPCD Q Current is:", Current2, Current1, "\r\n");
+
+    reg = GetReg_Ext(JREG_LPCDILAST_HI);
+    reg1 = GetReg_Ext(JREG_LPCDILAST_LO);
+    Current1 = (reg << 6) + reg1;
+    Current2 = reg % 4 -- ((reg<<6)>>8);
+    log.debug("-> LPCD I Last is:", Current2, Current1, "\r\n");
+
+    reg = GetReg_Ext(JREG_LPCDQLAST_HI);
+    reg1 = GetReg_Ext(JREG_LPCDQLAST_LO);
+    Current1 = (reg << 6) + reg1;
+    Current2 = reg % 4 -- ((reg<<6)>>8);
+    log.debug("-> LPCD Q Last is:", Current2, Current1, "\r\n");
+
+    reg = GetReg_Ext(JREG_LPCDDELTA_HI);
+    reg1 = GetReg_Ext(JREG_LPCDDELTA_LO);
+    Current1 = (reg << 6) + reg1;
+    Current2 = reg % 4 -- ((reg<<6)>>8);
+    log.debug("-> LPCD Delta is:", Current2, Current1, "\r\n");
+
+end
+
+
+
+
+-- //***********************************************
+-- //函数名称:Lpcd_Card_Event()
+-- //函数功能:LPCD检测到卡
+-- //入口参数:
+-- //出口参数:
+-- //***********************************************
+
+local function Lpcd_Card_Event()
+    fm17622_Initial_ReaderA()
+    SetCW(TX1_TX2_CW_ENABLE)
+    nfc_Data = {}--清空选卡
+    result = ReaderA_CardActivate()
+    if result == fm17622_SUCCESS then
+        if endcall ~= nil then
+            endcall()
+        end
+    end
+    SetCW(TX1_TX2_CW_DISABLE)
+    nfc_Data = {}--清空结束
+end
+
+-- //***********************************************
+-- //函数名称:Lpcd_IRQ_Event()
+-- //函数功能:LPCD中断处理
+-- //入口参数:
+-- //出口参数:
+-- //***********************************************
+function fm17622.Lpcd_IRQ_Event()
+    log.debug("fm17622.Lpcd_IRQ_Event");
+    gpio.set(fm17622_NPD, 1)
+    sys.wait(5)
+    local reg = GetReg_Ext(JREG_LPCDIRQ) -- 读取LPCD中断标志,LPCD中断需要判断LPCD IRQ RXCHANGE(0x04)是否置位
+    SetReg_Ext(JREG_LPCDIRQ, reg) -- CLEAR LPCD IRQ
+
+    if (reg & 0x08) ~= 0 then
+        log.debug("fm17622.Lpcd_IRQ_Event", "-> LPCD IRQ RFDET SET!\r\n");
+    end
+    if (reg & 0x04) ~= 0 then
+        log.debug("fm17622.Lpcd_IRQ_Event", "-> LPCD IRQ RXCHANGE SET!\r\n"); -- LPCD中断标志
+        if debug~=nil then
+            if debug==true then
+                Lpcd_Get_ADC_Value()
+            end
+        end
+        
+
+        Lpcd_Card_Event()
+
+    end
+    if (reg & 0x02) ~= 0 then
+        log.debug("fm17622.Lpcd_IRQ_Event", "-> LPCD IRQ ATQAREC SET!\r\n");
+    end
+    if (reg & 0x10) ~= 0 then
+        log.debug("fm17622.Lpcd_IRQ_Event", "-> LPCD IRQ MISSWUP SET!\r\n");
+    end
+
+    gpio.set(fm17622_NPD, 0)
+    sys.wait(5)
+    gpio.set(fm17622_NPD, 1)
+    sys.wait(5)
+    for i = 1, 10, 1 do
+        if fm17622.LPCD_setup() == true then
+            break
+        end
+        gpio.set(fm17622_NPD, 0)
+        sys.wait(5)
+        gpio.set(fm17622_NPD, 1)
+        sys.wait(5)
+    end
+
+    sys.wait(5)
+    gpio.set(fm17622_NPD, 0)
+end
+
+--[[
+fm17622初始化
+@api  fm17622.setup(iic_id, iic_addr, NPD,IRQ,ENDCall)
+@number iic_id iic端口号
+@number iic_addr   iic地址
+@number NPD     rst引脚
+@number  IRQ    INT引脚
+@number  Debug  是否调试
+@number ENDCall 终端回调执行不用可未空
+@return boolean 初始化结果
+@usage
+    fm17622=require "fm17622"
+    i2c.setup(1, i2c.FAST)
+    fm17622.setup(1, 0x28,20,32,function()
+
+        local Key_A = string.char(0x01,0x02,0x03,0x04,0x05,0x06)
+        local wdata = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
+        fm17622.write_datablock(4,wdata,0,Key_A)
+        local strat,rdata=fm17622.read_datablock(4,0,Key_A)
+        if strat then
+            log.info(json.encode(rdata))     
+        end
+
+    end,true)
+]]
+function fm17622.setup(iic_id, iic_addr, NPD,IRQ,ENDCall,Debug)
+    fm17622_iic_id = iic_id
+    fm17622_iic_addr = iic_addr
+    fm17622_NPD = NPD
+    endcall=ENDCall
+    debug=Debug;
+
+    gpio.setup(fm17622_NPD, 0, gpio.PULLUP)
+    sys.wait(5)
+    gpio.set(fm17622_NPD, 1)
+    sys.wait(5)
+
+
+
+    gpio.debounce(32, 200)
+    gpio.setup(32, function(var)
+        if nfc_work==false then
+            nfc_work=true;
+            sys.taskInit(function()
+                fm17622.Lpcd_IRQ_Event()
+                nfc_work=false
+            end)
+            
+        end
+    end, gpio.PULLDOWN, gpio.FALLING)
+
+
+
+    if GetReg(JREG_VERSION) == 0xA2 then
+        log.debug("IC Version = fm17622 or FM17610 \r\n")
+        fm17622.LPCD_setup()
+        sys.wait(5)
+        gpio.set(fm17622_NPD, 0)
+        return true
+    end
+
+
+    return false;
+end
+
+--[[
+写入ic卡数据
+@api  write_datablock(addr, data, mode, key)
+@number addr    ic卡地址块0到64
+@number data    写入数据
+@number A/B密码选择   A为0,B为1
+@number key      密码字符串
+@return boolean  true为成功,false失败
+@usage
+local Key_A = string.char(0x01,0x02,0x03,0x04,0x05,0x06)
+        local wdata = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
+        fm17622.write_datablock(4,wdata,0,Key_A)
+]]
+function fm17622.write_datablock(addr, data, mode, key)
+    if #data ~= 16 then
+        log.error("data must be 16 bytes")
+        return false
+    end
+    if nfc_Data["UID"] == nil then
+        log.error("UID 不能为空")
+        return false
+    end
+    if #key ~= 6 then
+        log.error("key must be 6 bytes")
+        return false
+    end
+
+    -- 验证密码
+    local buff = {}
+    buff[1] = mode + 0x60
+    buff[2] = addr
+    for i = 1, 6 do
+        buff[i + 2] = key:byte(i)
+    end
+    for i = 1, 4 do
+        buff[i + 8] = nfc_Data["UID"][i]
+    end
+    command(0x0E, buff)
+    if (GetReg(JREG_STATUS2) & 0x08) == 0 then
+        log.info("密码错误", (GetReg(JREG_STATUS2) & 0x08))
+        return false
+    end
+    
+    buff = {}
+    buff[1] = 0xa0
+    buff[2] = addr
+    command(0x0C, buff)
+    command(0x0C, data)
+    return true
+
+end
+
+--[[
+读取ic卡数据
+@api   fm17622.read_datablock(addr, mode, key)
+@number addr    ic卡地址块0到64
+@number A/B密码选择   A为0,B为1
+@number key      密码字符串
+@return boolean  true为成功,false失败
+@return table  读取到的值
+@usage
+local Key_A = string.char(0x01,0x02,0x03,0x04,0x05,0x06)
+local strat,rdata=fm17622.read_datablock(4,0,Key_A)
+        if strat then
+            log.info(json.encode(rdata))     
+        end
+]]
+function fm17622.read_datablock(addr, mode, key)
+
+    if nfc_Data["UID"] == nil then
+        log.error("UID 不能为空")
+        return false
+    end
+    if #key ~= 6 then
+        log.error("key must be 6 bytes")
+        return false
+    end
+
+    -- 验证密码
+    local buff = {}
+    buff[1] = mode + 0x60
+    buff[2] = addr
+    for i = 1, 6 do
+        buff[i + 2] = key:byte(i)
+    end
+    for i = 1, 4 do
+        buff[i + 8] = nfc_Data["UID"][i]
+    end
+    command(0x0E, buff)
+    if (GetReg(JREG_STATUS2) & 0x08) == 0 then
+        log.info("密码错误", (GetReg(JREG_STATUS2) & 0x08))
+        return false
+    end
+
+    -- 读取数据
+    local buff = {}
+    buff[1] = 0x30
+    buff[2] = addr
+    return true, command(0x0C, buff)
+
+end
+
+return fm17622

+ 989 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/gnss.lua

@@ -0,0 +1,989 @@
+--[[
+@module gnss
+@summary gnss拓展库
+@version 1.0
+@date    2025.07.16
+@author  李源龙
+@usage
+-- 用法实例
+-- 注意:gnss.lua适用的产品范围,只能用于合宙内部集成GNSS功能的产品,目前有Air780EGH,Air8000系列
+-- 提醒: 本库输出的坐标,均为 WGS84 坐标系
+-- 如需要在国内地图使用, 要转换成对应地图的坐标系, 例如 GCJ02 BD09
+-- 相关链接: https://lbsyun.baidu.com/index.php?title=coordinate
+-- 相关链接: https://www.openluat.com/GPS-Offset.html
+
+--关于gnss的三种应用场景:
+gnss.DEFAULT:
+--- gnss应用模式1.
+-- 打开gnss后,gnss定位成功时,如果有回调函数,会调用回调函数
+-- 使用此应用模式调用gnss.open打开的“gnss应用”,必须主动调用gnss.close
+-- 或者gnss.closeAll才能关闭此“gnss应用”,主动关闭时,即使有回调函数,也不会调用回调函数
+-- 通俗点说就是一直打开,除非自己手动关闭掉
+
+gnss.TIMERORSUC:
+--- gnss应用模式2.
+-- 打开gnss后,如果在gnss开启最大时长到达时,没有定位成功,如果有回调函数,
+-- 会调用回调函数,然后自动关闭此“gnss应用”
+-- 打开gnss后,如果在gnss开启最大时长内,定位成功,如果有回调函数,
+-- 会调用回调函数,然后自动关闭此“gnss应用”
+-- 打开gnss后,在自动关闭此“gnss应用”前,可以调用gnss.close或者
+-- gnss.closeAll主动关闭此“gnss应用”,主动关闭时,即使有回调函数,也不会调用回调函数
+-- 通俗点说就是设置规定时间打开,如果规定时间内定位成功就会自动关闭此应用,
+-- 如果没有定位成功,时间到了也会自动关闭此应用
+
+gnss.TIMER:
+--- gnss应用模式3.
+-- 打开gnss后,在gnss开启最大时长时间到达时,无论是否定位成功,如果有回调函数,
+-- 会调用回调函数,然后自动关闭此“gnss应用”
+-- 打开gnss后,在自动关闭此“gnss应用”前,可以调用gnss.close或者gnss.closeAll
+-- 主动关闭此“gnss应用”,主动关闭时,即使有回调函数,也不会调用回调函数
+-- 通俗点说就是设置规定时间打开,无论是否定位成功,到了时间都会自动关闭此应用,
+-- 和第二种的区别在于定位成功之后不会自动关闭,到时间之后才会自动关闭
+
+gnss=require("gnss")    
+
+local function mode1_cb(tag)
+    log.info("TAGmode1_cb+++++++++",tag)
+    log.info("nmea", "rmc", json.encode(gnss.getRmc(2)))
+end
+
+local function mode2_cb(tag)
+    log.info("TAGmode2_cb+++++++++",tag)
+    log.info("nmea", "rmc", json.encode(gnss.getRmc(2)))
+end
+
+local function mode3_cb(tag)
+    log.info("TAGmode3_cb+++++++++",tag)
+    log.info("nmea", "rmc", json.encode(gnss.getRmc(2)))
+end
+
+local function gnss_fnc()
+    local gnssotps={
+        gnssmode=1, --1为卫星全定位,2为单北斗
+        agps_enable=true,    --是否使用AGPS,开启AGPS后定位速度更快,会访问服务器下载星历,星历时效性为北斗1小时,GPS4小时,默认下载星历的时间为1小时,即一小时内只会下载一次
+        debug=true,    --是否输出调试信息
+        -- uart=2,    --使用的串口,780EGH和8000默认串口2
+        -- uartbaud=115200,    --串口波特率,780EGH和8000默认115200
+        -- bind=1, --绑定uart端口进行GNSS数据读取,是否设置串口转发,指定串口号
+        -- rtc=false    --定位成功后自动设置RTC true开启,flase关闭
+    }
+    --设置gnss参数
+    gnss.setup(gnssotps)
+    --开启gnss应用
+    gnss.open(gnss.TIMER,{tag="MODE1",val=60,cb=mode1_cb})
+    gnss.open(gnss.DEFAULT,{tag="MODE2",cb=mode2_cb})
+    gnss.open(gnss.TIMERORSUC,{tag="MODE3",val=60,cb=mode3_cb})
+    sys.wait(40000)
+    log.info("关闭一个gnss应用,然后查看下所有应用的状态")
+    --关闭一个gnss应用
+    gnss.close(gnss.TIMER,{tag="MODE1"})
+    --查询3个gnss应用状态
+    log.info("gnss应用状态1",gnss.isActive(gnss.TIMER,{tag="MODE1"}))
+    log.info("gnss应用状态2",gnss.isActive(gnss.DEFAULT,{tag="MODE2"}))
+    log.info("gnss应用状态3",gnss.isActive(gnss.TIMERORSUC,{tag="MODE3"}))
+    sys.wait(10000)
+    --关闭所有gnss应用
+    gnss.closeAll()
+    --查询3个gnss应用状态
+    log.info("gnss应用状态1",gnss.isActive(gnss.TIMER,{tag="MODE1"}))
+    log.info("gnss应用状态2",gnss.isActive(gnss.DEFAULT,{tag="MODE2"}))
+    log.info("gnss应用状态3",gnss.isActive(gnss.TIMERORSUC,{tag="MODE3"}))
+    --查询最后一次定位结果
+    local loc= gnss.getlastloc()
+    if loc then
+        log.info("lastloc", loc.lat,loc.lng)
+    end
+end
+
+sys.taskInit(gnss_fnc)
+
+
+--GNSS定位状态的消息处理函数:
+local function gnss_state(event, ticks)
+    -- event取值有
+    -- FIXED:string类型 定位成功
+    -- LOSE: string类型 定位丢失
+    -- CLOSE: string类型 GNSS关闭,仅配合使用gnss.lua有效
+
+    -- ticks number类型 是事件发生的时间,一般可以忽略
+    log.info("gnss", "state", event)
+end
+sys.subscribe(gnss_state)
+
+]]
+local gnss = {}
+--gnss开启标志,true表示开启状态,false或者nil表示关闭状态
+local openFlag
+--gnss定位标志,true表示,其余表示未定位
+local fixFlag=nil
+
+--串口配置
+local uart_baudrate = 115200
+local uart_id = 2
+
+--gnss 的串口线程是否在工作;
+local taskFlag=false
+
+local agpsFlag=false
+
+--保存经纬度到文件区
+function gnss.saveloc(lat, lng)
+    if not lat or not lng then
+        if libgnss.isFix() then
+            local rmc = libgnss.getRmc(0)
+            if rmc then
+                lat, lng = rmc.lat, rmc.lng
+            end
+        end
+    end
+    if lat and lng then
+        -- log.info("待保存的GPS位置", lat, lng)
+        local locStr = string.format('{"lat":%.5f,"lng":%.5f}', lat, lng)
+        -- log.info("gnss", "保存GPS位置", locStr)
+        io.writeFile("/hxxtloc", locStr)
+    end
+    local now = os.time()
+    io.writeFile("/hxxt_tm", tostring(now))
+    -- log.info("now", now)
+end
+local tid
+
+sys.subscribe("GNSS_STATE", function(event)
+    -- log.info("libagps","libagps is "..event)
+    if event == "FIXED" then
+        gnss.saveloc()
+        tid=sys.timerLoopStart(gnss.saveloc,600000)
+    elseif event == "LOSE" or event == "CLOSE" then
+        -- log.info("libagps","libagps is close")
+        sys.timerStop(tid)
+    end
+end)
+
+--agps操作,联网访问服务器获取星历数据
+local function _agps()
+    local lat, lng
+
+    --此逻辑在agps定位成功之后,还会继续开启10s-15s,
+    --原因是因为如果第一次冷启动之后,定位成功之后,
+    --如果直接关闭gnss会导致gnss芯片的星历没有解析完毕,会影响下一次的定位为冷启动
+    --如果对功耗有需求,需要定位快,可以每次都使用agps,不需要这句,直接屏蔽掉即可
+    --代价是每次定位都会进行基站定位,
+    gnss.open(gnss.TIMER,{tag="libagps",val=20}) 
+    -- 判断星历时间和下载星历   
+    local now = os.time()
+    local agps_time = tonumber(io.readFile("/hxxt_tm") or "0") or 0
+    log.info("os.time",now)
+    log.info("agps_time",agps_time)
+    if now - agps_time > 3600 or io.fileSize("/hxxt.dat") < 1024 then
+        local url = gnss.opts.url
+        if not gnss.opts.url then
+            if gnss.opts.gnssmode and 2 == gnss.opts.gnssmode then
+                -- 单北斗
+                url = "http://download.openluat.com/9501-xingli/HXXT_BDS_AGNSS_DATA.dat"
+            else
+                url = "http://download.openluat.com/9501-xingli/HXXT_GPS_BDS_AGNSS_DATA.dat"
+            end
+        end
+        local code = http.request("GET", url, nil, nil, {dst="/hxxt.dat"}).wait()
+        if code and code == 200 then
+            log.info("gnss.opts", "下载星历成功", url)
+            io.writeFile("/hxxt_tm", tostring(now))
+        else
+            log.info("gnss.opts", "下载星历失败", code)
+        end
+    else
+        log.info("gnss.opts", "星历不需要更新", now - agps_time)
+    end
+    --进行基站定位,给到gnss芯片一个大概的位置
+    if mobile then
+        local lbsLoc2 = require("lbsLoc2")
+        lat, lng = lbsLoc2.request(5000)
+        -- local lat, lng, t = lbsLoc2.request(5000, "bs.openluat.com")
+        -- log.info("lbsLoc2", lat, lng)
+        if lat and lng then
+            lat = tonumber(lat)
+            lng = tonumber(lng)
+            log.info("lbsLoc2", lat, lng)
+            -- 转换单位
+            local lat_dd,lat_mm = math.modf(lat)
+            local lng_dd,lng_mm = math.modf(lng)
+            lat = lat_dd * 100 + lat_mm * 60
+            lng = lng_dd * 100 + lng_mm * 60
+        end
+    elseif wlan then
+        -- wlan.scan()
+        -- sys.waitUntil("WLAN_SCAN_DONE", 5000)
+    end
+    --获取基站定位失败则使用本地之前保存的位置
+    if not lat then
+        -- 获取最后的本地位置
+        local locStr = io.readFile("/hxxtloc")
+        if locStr then
+            local jdata = json.decode(locStr)
+            if jdata and jdata.lat then
+                lat = jdata.lat
+                lng = jdata.lng
+            end
+        end
+    end
+    local gps_uart_id = uart_id
+
+    -- 写入星历
+    local agps_data = io.readFile("/hxxt.dat")
+    if agps_data and #agps_data > 1024 then
+        log.info("gnss.opts", "写入星历数据", "长度", #agps_data)
+        for offset=1,#agps_data,512 do
+            log.info("gnss", "AGNSS", "write >>>", #agps_data:sub(offset, offset + 511))
+            uart.write(gps_uart_id, agps_data:sub(offset, offset + 511))
+            sys.wait(100) -- 等100ms反而更成功
+        end
+        -- uart.write(gps_uart_id, agps_data)
+    else
+        log.info("gnss.opts", "没有星历数据")
+        return
+    end
+    -- "lat":23.4068813,"min":27,"valid":true,"day":27,"lng":113.2317505
+    --如果没有经纬度的话,定位时间会变长,大概10-20s左右
+    if not lat or not lng then
+        -- lat, lng = 23.4068813, 113.2317505
+        log.info("gnss.opts", "没有GPS坐标", lat, lng)
+        return --暂时不写入参考位置
+    else
+        log.info("gnss.opts", "写入GPS坐标", lat, lng)
+    end
+    --写入时间
+    local date = os.date("!*t")
+    if date.year > 2023 then
+        local str = string.format("$AIDTIME,%d,%d,%d,%d,%d,%d,000", date["year"], date["month"], date["day"],
+            date["hour"], date["min"], date["sec"])
+        log.info("gnss.opts", "参考时间", str)
+        uart.write(gps_uart_id, str .. "\r\n")
+        sys.wait(20)
+    end
+    -- 写入参考位置
+    local str = string.format("$AIDPOS,%.7f,%s,%.7f,%s,1.0\r\n",
+    lat > 0 and lat or (0 - lat), lat > 0 and 'N' or 'S',
+    lng > 0 and lng or (0 - lng), lng > 0 and 'E' or 'W')
+    log.info("gnss.opts", "写入AGPS参考位置", str)
+    uart.write(gps_uart_id, str)
+
+    -- 结束
+    gnss.opts.agps_tm = now
+    agpsFlag=true
+end
+
+--执行agps操作判断
+function gnss.agps()
+    -- 如果不是强制写入AGPS信息, 而且是已经定位成功的状态,那就没必要了
+    if libgnss.isFix() then return end
+    -- 先判断一下时间
+    while not socket.adapter() do
+        log.warn("gnss_agps", "wait IP_READY")
+        -- 在此处阻塞等待WIFI连接成功的消息"IP_READY"
+        -- 或者等待30秒超时退出阻塞等待状态
+        local result=sys.waitUntil("IP_READY", 30000)
+        if result == false then
+            log.warn("gnss_agps", "wait IP_READY timeout")
+            return
+        end
+    end
+    if not gnss.opts.agps_tm then
+        socket.sntp()
+        sys.waitUntil("NTP_UPDATE", 5000)
+    end
+    local now = os.time()
+    local agps_time = tonumber(io.readFile("/hxxt_tm") or "0") or 0
+    if ((not gnss.opts.agps_tm) and (now - agps_time > 300))  or  now - agps_time > 3600 then
+        -- 执行AGPS
+        log.info("gnss.opts", "开始执行AGPS")
+        sys.taskInit(_agps)
+    else
+        log.info("gnss.opts", "暂不需要写入AGPS")
+    end
+end
+
+--打开gnss,内部函数使用,不推荐给脚本层使用
+local function _open()
+    if openFlag then return end
+    libgnss.clear() -- 清空数据,兼初始化
+    uart.setup(uart_id, uart_baudrate)
+    -- pm.power(pm.GPS, false)
+    pm.power(pm.GPS, true)
+    if gnss.opts.gnssmode==1 then
+        --默认全开启
+        log.info("全卫星开启")
+        elseif gnss.opts.gnssmode==2 then
+        --默认开启单北斗
+        sys.timerStart(function()
+            uart.write(uart_id, "$CFGSYS,h10\r\n")
+        end,200)
+        log.info("单北斗开启")
+    end
+    if gnss.opts.debug==true then
+        log.info("debug开启")
+        libgnss.debug(true)
+    elseif gnss.opts.debug==false then
+        log.info("debug关闭")
+        libgnss.debug(false)
+    end
+    if type(gnss.opts.bind)=="number"  then
+        log.info("绑定bind事件")
+        libgnss.bind(uart_id,gnss.opts.bind)
+    else
+        libgnss.bind(uart_id)
+    end
+    if gnss.opts.rtc==true then
+        log.info("rtc开启")
+        libgnss.rtcAuto(true)
+    elseif gnss.opts.rtc==false then
+        log.info("rtc关闭")
+        libgnss.rtcAuto(false)
+    end
+    if gnss.opts.agps_enable==true then
+        log.info("agps开启")
+        sys.taskInit(gnss.agps)
+    end
+    --设置输出VTG内容
+    sys.timerStart(function()
+        uart.write(uart_id,"$CFGMSG,0,5,1,1\r\n")
+    end,800)
+     --设置输出ZDA内容
+     sys.timerStart(function()
+        uart.write(uart_id,"$CFGMSG,0,6,1,1\r\n")
+    end,900)
+    openFlag = true
+    sys.publish("GNSS_STATE","OPEN")
+    log.info("gnss._open")
+end
+
+--关闭gnss,内部函数使用,不推荐给脚本层使用
+local function _close()
+    if not openFlag then return end
+    gnss.saveloc()
+    pm.power(pm.GPS, false)
+    uart.close(uart_id)
+    openFlag = false
+    fixFlag = false
+    sys.publish("GNSS_STATE","CLOSE",fixFlag)    
+    log.info("gnss._close")
+    libgnss.clear()
+end
+
+
+--- gnss应用模式1.
+--
+-- 打开gnss后,gnss定位成功时,如果有回调函数,会调用回调函数
+--
+-- 使用此应用模式调用gnss.open打开的“gnss应用”,必须主动调用gnss.close或者gnss.closeAll才能关闭此“gnss应用”,主动关闭时,即使有回调函数,也不会调用回调函数
+gnss.DEFAULT = 1
+--- gnss应用模式2.
+--
+-- 打开gnss后,如果在gnss开启最大时长到达时,没有定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“gnss应用”
+--
+-- 打开gnss后,如果在gnss开启最大时长内,定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“gnss应用”
+--
+-- 打开gnss后,在自动关闭此“gnss应用”前,可以调用gnss.close或者gnss.closeAll主动关闭此“gnss应用”,主动关闭时,即使有回调函数,也不会调用回调函数
+gnss.TIMERORSUC = 2
+--- gnss应用模式3.
+--
+-- 打开gnss后,在gnss开启最大时长时间到达时,无论是否定位成功,如果有回调函数,会调用回调函数,然后自动关闭此“gnss应用”
+--
+-- 打开gnss后,在自动关闭此“gnss应用”前,可以调用gnss.close或者gnss.closeAll主动关闭此“gnss应用”,主动关闭时,即使有回调函数,也不会调用回调函数
+gnss.TIMER = 3
+
+--“gnss应用”表
+local tList = {}
+
+--[[
+函数名:delItem
+功能  :从“gnss应用”表中删除一项“gnss应用”,并不是真正的删除,只是设置一个无效标志
+参数  :
+        mode:gnss应用模式
+        para:
+            para.tag:“gnss应用”标记
+            para.val:gnss开启最大时长
+            para.cb:回调函数
+返回值:无
+]]
+local function delItem(mode,para)
+    for i=1,#tList do
+        --标志有效 并且 gnss应用模式相同 并且 “gnss应用”标记相同
+        if tList[i].flag and tList[i].mode==mode and tList[i].para.tag==para.tag then
+            --设置无效标志
+            tList[i].flag,tList[i].delay = false
+            break
+        end
+    end
+end
+
+
+--[[
+函数名:addItem
+功能  :新增一项“gnss应用”到“gnss应用”表
+参数  :
+        mode:gnss应用模式
+        para:
+            para.tag:“gnss应用”标记
+            para.val:gnss开启最大时长
+            para.cb:回调函数
+返回值:无
+]]
+local function addItem(mode,para)
+    --删除相同的“gnss应用”
+    delItem(mode,para)
+    local item,i,fnd = {flag=true, mode=mode, para=para}
+    --如果是TIMERORSUC或者TIMER模式,初始化gnss工作剩余时间
+    if mode==gnss.TIMERORSUC or mode==gnss.TIMER then item.para.remain = para.val end
+    for i=1,#tList do
+        --如果存在无效的“gnss应用”项,直接使用此位置
+        if not tList[i].flag then
+            tList[i] = item
+            fnd = true
+            break
+        end
+    end
+    --新增一项
+    if not fnd then table.insert(tList,item) end
+end
+
+--退出GNSS定时器
+local function existTimerItem()
+    for i=1,#tList do
+        if tList[i].flag and (tList[i].mode==gnss.TIMERORSUC or tList[i].mode==gnss.TIMER or tList[i].para.delay) then return true end
+    end
+end
+
+--GNSS定时器
+local function timerFnc()
+    for i=1,#tList do
+        if tList[i].flag then
+            log.info("gnss.timerFnc@"..i,tList[i].mode,tList[i].para.tag,tList[i].para.val,tList[i].para.remain,tList[i].para.delay)
+            local rmn,dly,md,cb = tList[i].para.remain,tList[i].para.delay,tList[i].mode,tList[i].para.cb
+
+            if rmn and rmn>0 then
+                tList[i].para.remain = rmn-1
+            end
+            if dly and dly>0 then
+                tList[i].para.delay = dly-1
+            end
+            rmn = tList[i].para.remain
+
+            if libgnss.isFix() and md==gnss.TIMER and rmn==0 and not tList[i].para.delay then
+                tList[i].para.delay = 1
+            end
+            dly = tList[i].para.delay
+            if libgnss.isFix() then
+                if dly and dly==0 then
+                    if cb then cb(tList[i].para.tag) end
+                    if md == gnss.DEFAULT then
+                        tList[i].para.delay = nil
+                    else
+                        gnss.close(md,tList[i].para)
+                    end
+                end
+            else
+                if rmn and rmn == 0 then
+                    if cb then cb(tList[i].para.tag) end
+                    gnss.close(md,tList[i].para)
+                end
+            end
+        end
+    end
+    if existTimerItem() then sys.timerStart(timerFnc,1000) end
+end
+
+--[[
+函数名:statInd
+功能  :处理gnss定位成功的消息
+参数  :
+        evt:gnss消息类型
+返回值:无
+]]
+local function statInd(evt)
+    --定位成功的消息
+    if evt == "FIXED" then
+        fixFlag = true
+        for i=1,#tList do
+            log.info("gnss.statInd@"..i,tList[i].flag,tList[i].mode,tList[i].para.tag,tList[i].para.val,tList[i].para.remain,tList[i].para.delay,tList[i].para.cb)
+            if tList[i].flag then
+                if tList[i].mode ~= gnss.TIMER then
+                    tList[i].para.delay = 1
+                    if tList[i].mode == gnss.DEFAULT then
+                        if existTimerItem() then sys.timerStart(timerFnc,1000) end
+                    end
+                end
+            end
+        end
+    end
+end
+
+
+--[[
+设置gnss定位参数
+@api gnss.setup(opts)
+@table opts gnss定位参数,可选值gnssmode:定位卫星模式,1为卫星全定位,2为单北斗,默认为卫星全定位
+agps_enable:是否启用AGPS,true为启用,false为不启用,默认为false
+debug:是否输出调试信息到luatools,true为输出,false为不输出,默认为false
+uart:GNSS串口配置,780EGH和8000默认为uart2,可不填
+uartbaud:GNSS串口波特率,780EGH和8000默认为115200,可不填
+bind:绑定uart端口进行GNSS数据读取,是否设置串口转发,指定串口号,不需要转发可不填
+rtc:定位成功后自动设置RTC true开启,flase关闭,默认为flase,不需要可不填
+@return nil
+@usage
+local gnssotps={
+        gnssmode=1, --1为卫星全定位,2为单北斗
+        agps_enable=true,    --是否使用AGPS,开启AGPS后定位速度更快,会访问服务器下载星历,星历时效性为北斗1小时,GPS4小时,默认下载星历的时间为1小时,即一小时内只会下载一次
+        debug=true,    --是否输出调试信息
+        -- uart=2,    --使用的串口,780EGH和8000默认串口2
+        -- uartbaud=115200,    --串口波特率,780EGH和8000默认115200
+        -- bind=1, --绑定uart端口进行GNSS数据读取,是否设置串口转发,指定串口号
+        -- rtc=false    --定位成功后自动设置RTC true开启,flase关闭
+    }
+    gnss.setup(gnssotps)
+]]
+function gnss.setup(opts)
+    gnss.opts=opts
+    if hmeta.model():find("780EGH") or hmeta.model():find("8000") then
+        uart_id=2
+        uart_baudrate=115200
+    else
+        if gnss.opts.uart_id then
+            uart_id=gnss.opts.uart_id
+        else
+            uart_id=2    
+        end
+        if gnss.opts.uartbaud then
+            uart_baudrate=gnss.opts.uartbaud
+        else
+            uart_baudrate=115200
+        end
+    end   
+end
+
+--[[
+打开一个“gnss应用”
+@api gnss.open(mode,para)
+@number mode gnss应用模式,支持gnss.DEFAULT,gnss.TIMERORSUC,gnss.TIMER三种
+@param para table类型,gnss应用参数,para.tag:string类型,gnss应用标记,para.val:number类型,gnss应用开启最大时长,单位:秒,mode参数为gnss.TIMERORSUC或者gnss.TIMER时,此值才有意义;使用close接口时,不需要传入此参数,para.cb:gnss应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用close接口时,不需要传入此参数
+@return nil
+@usage
+-- “gnss应用”:指的是使用gnss功能的一个应用
+-- 例如,假设有如下3种需求,要打开gnss,则一共有3个“gnss应用”:
+-- “gnss应用1”:每隔1分钟打开一次gnss
+-- “gnss应用2”:设备发生震动时打开gnss
+-- “gnss应用3”:收到一条特殊短信时打开gnss
+-- 只有所有“gnss应用”都关闭了,才会去真正关闭gnss
+-- 每个“gnss应用”打开或者关闭gnss时,最多有4个参数,其中 gnss应用模式和gnss应用标记 共同决定了一个唯一的“gnss应用”:
+-- 1、gnss应用模式(必选)
+-- 2、gnss应用标记(必选)
+-- 3、gnss开启最大时长[可选]
+-- 4、回调函数[可选]
+-- 例如gnss.open(gnss.TIMER,{tag="MODE1",val=60,cb=mode1_cb})
+-- gnss.TIMER为gnss应用模式,"MODE1"为gnss应用标记,60秒为gnss开启最大时长,mode1_cb为回调函数
+gnss.open(gnss.TIMER,{tag="MODE1",val=60,cb=mode1_cb})
+gnss.open(gnss.DEFAULT,{tag="MODE2",cb=mode2_cb})
+gnss.open(gnss.TIMERORSUC,{tag="MODE3",val=60,cb=mode3_cb})
+]]
+function gnss.open(mode,para)
+    assert((para and type(para) == "table" and para.tag and type(para.tag) == "string"),"gnss.open para invalid")
+    log.info("gnss.open",mode,para.tag,para.val,para.cb)
+    --如果gnss定位成功
+    if libgnss.isFix() then
+        if mode~=gnss.TIMER then
+            --执行回调函数
+            if para.cb then para.cb(para.tag) end
+            if mode==gnss.TIMERORSUC then return end
+        end
+    end
+    addItem(mode,para)
+    --真正去打开gnss
+    _open()
+    --启动1秒的定时器
+    if existTimerItem() and not sys.timerIsActive(timerFnc) then
+        sys.timerStart(timerFnc,1000)
+    end
+end
+
+
+--[[
+关闭一个“gnss应用”,只是从逻辑上关闭一个gnss应用,并不一定真正关闭gnss,是有所有的gnss应用都处于关闭状态,才会去真正关闭gnss
+@api gnss.close()
+@number mode gnss应用模式,支持gnss.DEFAULT,gnss.TIMERORSUC,gnss.TIMER三种
+@param para table类型,gnss应用参数,para.tag:string类型,gnss应用标记,para.val:number类型,gnss应用开启最大时长,单位:秒,mode参数为gnss.TIMERORSUC或者gnss.TIMER时,此值才有意义;使用close接口时,不需要传入此参数,para.cb:gnss应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用close接口时,不需要传入此参数
+@return nil
+@usage
+gnss.open(gnss.TIMER,{tag="MODE1",val=60,cb=mode1_cb})
+gnss.close(gnss.TIMER,{tag="MODE1"})
+]]
+function gnss.close(mode,para)
+    assert((para and type(para)=="table" and para.tag and type(para.tag)=="string"),"gnss.close para invalid")
+    log.info("gnss.close",mode,para.tag,para.val,para.cb)
+    --删除此“gnss应用”
+    delItem(mode,para)
+    local valid,i
+    for i=1,#tList do
+        if tList[i].flag then
+            valid = true
+        end
+    end
+    --如果没有一个“gnss应用”有效,则关闭gnss
+    if not valid then _close() end
+end
+
+--[[
+关闭所有“gnss应用”
+@api gnss.closeAll()
+@return nil
+@usage
+gnss.open(gnss.TIMER,{tag="MODE1",val=60,cb=mode1_cb})
+gnss.open(gnss.DEFAULT,{tag="MODE2",cb=mode2_cb})
+gnss.open(gnss.TIMERORSUC,{tag="MODE3",val=60,cb=mode3_cb})
+gnss.closeAll()
+]]
+function gnss.closeAll()
+    for i=1,#tList do
+        if tList[i].flag and tList[i].para.cb then tList[i].para.cb(tList[i].para.tag) end
+        gnss.close(tList[i].mode,tList[i].para)
+    end
+end
+
+--[[
+判断一个“gnss应用”是否处于激活状态
+@api gnss.isActive(mode,para)
+@number mode gnss应用模式,支持gnss.DEFAULT,gnss.TIMERORSUC,gnss.TIMER三种
+@param para table类型,gnss应用参数,para.tag:string类型,gnss应用标记,para.val:number类型,gnss应用开启最大时长,单位:秒,mode参数为gnss.TIMERORSUC或者gnss.TIMER时,此值才有意义;使用close接口时,不需要传入此参数,para.cb:gnss应用结束时的回调函数,回调函数的调用形式为para.cb(para.tag);使用close接口时,不需要传入此参数,gnss应用模式和gnss应用标记唯一确定一个“gnss应用”,调用本接口查询状态时,mode和para.tag要和gnss.open打开一个“gnss应用”时传入的mode和para.tag保持一致
+@return bool result,处于激活状态返回true,否则返回nil
+@usage
+gnss.open(gnss.TIMER,{tag="MODE1",val=60,cb=mode1_cb})
+gnss.open(gnss.DEFAULT,{tag="MODE2",cb=mode2_cb})
+gnss.open(gnss.TIMERORSUC,{tag="MODE3",val=60,cb=mode3_cb})
+log.info("gnss应用状态1",gnss.isActive(gnss.TIMER,{tag="MODE1"}))
+log.info("gnss应用状态2",gnss.isActive(gnss.DEFAULT,{tag="MODE2"}))
+log.info("gnss应用状态3",gnss.isActive(gnss.TIMERORSUC,{tag="MODE3"}))
+]]
+function gnss.isActive(mode,para)
+    assert((para and type(para)=="table" and para.tag and type(para.tag)=="string"),"gnss.isActive para invalid")
+    for i=1,#tList do
+        if tList[i].flag and tList[i].mode==mode and tList[i].para.tag==para.tag then return true end
+    end
+end
+
+sys.subscribe("GNSS_STATE",statInd)
+
+
+--[[
+当前是否已经定位成功
+@api gnss.isFix()
+@return boolean   true/false,定位成功返回true,否则返回false
+@usage
+log.info("nmea", "isFix", gnss.isFix())
+]]
+function gnss.isFix()
+   return libgnss.isFix()
+end
+
+
+--[[
+获取number类型的位置和速度信息
+@api gnss.getIntLocation(speed_type)
+@number 速度单位,默认是m/h,
+0 - m/h 米/小时, 默认值, 整型
+1 - m/s 米/秒, 浮点数
+2 - km/h 千米/小时, 浮点数
+3 - kn/h 英里/小时, 浮点数
+@return number lat数据, 格式为 DDDDDDDDD,示例:343482649,DDDDDDDDD格式是由DD.DDDDDDD*10000000转换而来,目的是作为整数,方便某些场景使用
+@return number lng数据, 格式为 DDDDDDDDD,示例:1135039700,DDDDDDDDD格式是由DD.DDDDDDD*10000000转换而来,目的是作为整数,方便某些场景使用
+@return number speed数据, 单位根据speed_type决定,m/h, m/s, km/h, kn/h
+@usage
+--DDDDDDDDD格式是由DD.DDDDDDD*10000000转换而来,目的是作为整数,方便某些场景使用,示例:343482649对应的原始值是34.3482649
+-- 该数据是通过RMC转换的,如果想获取更详细的可以用gnss.getRmc(1)
+-- speed数据默认 米/小时,返回值例如:343482649	1135039700	390m/h
+log.info("nmea", "loc", gnss.getIntLocation())
+-- speed数据米/秒,返回值例如:343482649	1135039700	0.1085478m/s
+log.info("nmea", "loc", gnss.getIntLocation(1))
+-- speed数据千米/小时,返回值例如:343482649	1135039700	0.3907720km/h
+log.info("nmea", "loc", gnss.getIntLocation(2))
+-- speed数据英里/小时,返回值例如:343482649	1135039700	0.2110000kn/h
+log.info("nmea", "loc", gnss.getIntLocation(3))
+]]
+function gnss.getIntLocation(speed_type)
+    return libgnss.getIntLocation(speed_type)
+end
+
+
+--[[
+获取RMC的信息,经纬度,时间,速度,航向,定位是否有效,磁偏角
+@api gnss.getRmc(lnglat_mode)
+@number 经纬度数据的格式, 0-ddmm.mmmmm格式, 1-DDDDDDDDD格式, 2-DD.DDDDDDD格式, 3-原始RMC字符串
+@return table/string rmc数据
+@usage
+-- 解析nmea
+log.info("nmea", "rmc", json.encode(gnss.getRmc(2)))
+-- 实例输出,获取值的解释
+-- {
+--     "course":344.9920044,     // 地面航向,单位为度,从北向起顺时针计算
+--     "valid":true,   // true定位成功,false定位丢失
+--     "lat":34.5804405,  // 纬度, 正数为北纬, 负数为南纬
+--     "lng":113.8399506,  // 经度, 正数为东经, 负数为西经
+--     "variation":0,  // 磁偏角,固定为0
+--     "speed":0.2110000       // 地面速度, 单位为"节"
+--     "year":2023,    // 年份
+--     "month":1,      // 月份, 1-12
+--     "day":5,        // 月份天, 1-31
+--     "hour":7,       // 小时,0-23
+--     "min":23,       // 分钟,0-59
+--     "sec":20,       // 秒,0-59
+-- }
+--模式0示例:
+--json.encode默认输出"7f"格式保留7位小数,可以根据自己需要的格式调整小数位,本示例保留5位小数
+log.info("nmea", "rmc0", json.encode(gnss.getRmc(0),"5f"))
+{"variation":0,"lat":3434.82666,"min":54,"valid":true,"day":17,"lng":11350.39746,"speed":0.21100,"year":2025,"month":7,"sec":30,"hour":11,"course":344.99200}
+--模式1示例:
+--DDDDDDDDD格式是由DD.DDDDDDD*10000000转换而来,目的是作为整数,方便某些场景使用
+log.info("nmea", "rmc1", json.encode(gnss.getRmc(1)))
+{"variation":0,"lat":345804414,"min":54,"valid":true,"day":17,"lng":1138399500,"speed":0.2110000,"year":2025,"month":7,"sec":30,"hour":11,"course":344.9920044}
+--模式2示例:
+--json.encode默认输出"7f"格式保留7位小数,可以根据自己需要的格式调整小数位
+log.info("nmea", "rmc2", json.encode(gnss.getRmc(2)))
+{"variation":0,"lat":34.5804405,"min":54,"valid":true,"day":17,"lng":113.8399506,"speed":0.2110000,"year":2025,"month":7,"sec":30,"hour":11,"course":344.9920044}
+--模式3示例:
+log.info("nmea", "rmc3", gnss.getRmc(3))
+$GNRMC,115430.000,A,3434.82649,N,11350.39700,E,0.211,344.992,170725,,,A,S*02\r
+]]
+function gnss.getRmc(lnglat_mode)
+    return libgnss.getRmc(lnglat_mode)
+end
+
+--[[
+获取原始GSV信息
+@api gnss.getGsv()
+@return table 原始GSV数据
+@usage
+-- 解析nmea
+log.info("nmea", "gsv", json.encode(gnss.getGsv()))
+-- 实例输出
+-- {
+--     "total_sats":24,      // 总可见卫星数量
+--     "sats":[
+--         {
+--             "snr":27,     // 信噪比
+--             "azimuth":278, // 方向角
+--             "elevation":59, // 仰角
+--             "tp":0,        // 0 - GPS, 1 - BD, 2 - GLONASS, 3 - Galileo, 4 - QZSS
+--             "nr":4         // 卫星编号
+--         },
+--         // 这里忽略了22个卫星的信息
+--         {
+--             "snr":0,
+--             "azimuth":107,
+--             "elevation":19,
+--             "tp":1,
+--             "nr":31
+--         }
+--     ]
+-- }
+]]
+function gnss.getGsv() 
+    return libgnss.getGsv() 
+end
+
+
+--[[
+获取原始GSA信息
+@api gnss.getGsa(data_mode)
+@number 模式,默认为0 -所有卫星系统全部输出在一起,1 - 每个卫星系统单独分开输出
+@return table 原始GSA数据
+@usage
+-- 获取
+log.info("nmea", "gsa", json.encode(gnss.getGsa()))
+-- 示例数据(模式0, 也就是默认模式)
+--sysid:1为GPS,4为北斗,2为GLONASS,3为Galileo
+{"pdop":1.1770000,  垂直精度因子,0.00 - 99.99,不定位时值为 99.99
+"sats":[15,13,5,18,23,20,24,30,24,13,33,38,8,14,28,41,6,39,25,16,32,27],    // 正在使用的卫星编号
+"vdop":1.0160000,   垂直精度因子,0.00 - 99.99,不定位时值为 99.99
+"hdop":0.5940000,   // 位置精度因子,0.00 - 99.99,不定位时值为 99.99
+"sysid":1,         // 卫星系统编号1为GPS,4为北斗,2为GLONASS,3为Galileo
+"fix_type":3       // 定位模式, 1-未定位, 2-2D定位, 3-3D定位
+}
+
+--模式1
+log.info("nmea", "gsa", json.encode(gnss.getGsa()))
+
+[{"pdop":1.1770000,"sats":[15,13,5,18,23,20,24],"vdop":1.0160000,"hdop":0.5940000,"sysid":1,"fix_type":3},
+{"pdop":1.1770000,"sats":[30,24,13,33,38,8,14,28,41,6,39,25],"vdop":1.0160000,"hdop":0.5940000,"sysid":4,"fix_type":3},
+{"pdop":1.1770000,"sats":[16,32,27],"vdop":1.0160000,"hdop":0.5940000,"sysid":4,"fix_type":3},
+{"pdop":1.1770000,"sats":{},"vdop":1.0160000,"hdop":0.5940000,"sysid":2,"fix_type":3},
+{"pdop":1.1770000,"sats":{},"vdop":1.0160000,"hdop":0.5940000,"sysid":3,"fix_type":3}]
+
+]]
+
+function gnss.getGsa(data_mode)
+    return libgnss.getGsa(data_mode)
+end
+
+
+--[[
+获取VTG速度信息
+@api gnss.getVtg(data_mode)
+@number 可选, 3-原始字符串, 不传或者传其他值, 则返回浮点值
+@return table/string 原始VTG数据
+@usage
+-- 解析nmea
+log.info("nmea", "vtg", json.encode(gnss.getVtg()))
+-- 示例
+{
+    "speed_knots":0,        // 速度, 英里/小时
+    "true_track_degrees":0,  // 真北方向角
+    "magnetic_track_degrees":0, // 磁北方向角
+    "speed_kph":0           // 速度, 千米/小时
+}
+
+--模式3
+log.info("nmea", "vtg", gnss.getVtg(3))
+-- 返回值:$GNVTG,0.000,T,,M,0.000,N,0.000,K,A*13\r
+-- 提醒: 在速度<5km/h时, 不会返回方向角
+]]
+function gnss.getVtg(data_mode)
+    return  libgnss.getVtg(data_mode)
+end
+
+--获取原始ZDA时间和日期信息
+--[[
+获取原始ZDA时间和日期信息
+@api gnss.getZda()
+@return table 原始zda数据
+@usage
+log.info("nmea", "zda", json.encode(gnss.getZda()))
+-- 实例输出
+-- {
+--     "minute_offset":0,   // 本地时区的分钟, 一般固定输出0
+--     "hour_offset":0,     // 本地时区的小时, 一般固定输出0
+--     "year":2023         // UTC 年,四位数字
+--     "month":1,          // UTC 月,两位,01 ~ 12
+--     "day":5,            // UTC 日,两位数字,01 ~ 31
+--     "hour":7,           // 小时
+--     "min":50,           // 分
+--     "sec":14,           // 秒
+-- }
+]]
+function gnss.getZda()
+    return  libgnss.getZda()
+end
+
+--[[
+获取GGA数据
+@api gnss.getGga(lnglat_mode)
+@number 经纬度数据的格式, 0-ddmm.mmmmm格式, 1-DDDDDDDDD格式, 2-DD.DDDDDDD格式, 3-原始GGA字符串
+@return table GGA数据, 若如不存在会返回nil
+@usage
+local gga = gnss.getGga(2)
+log.info("GGA", json.encode(gga, "11g"))
+--实例输出,获取值的解释:
+-- {
+--     "dgps_age":0,             // 差分校正时延,单位为秒
+--     "fix_quality":1,          // 定位状态标识 0 - 无效,1 - 单点定位,2 - 差分定位
+--     "satellites_tracked":14,  // 参与定位的卫星数量
+--     "altitude":0.255,         // 海平面分离度, 或者成为海拔, 单位是米,
+--     "hdop":0.0335,            // 水平精度因子,0.00 - 99.99,不定位时值为 99.99
+--     "longitude":113.231,      // 经度, 正数为东经, 负数为西经
+--     "latitude":23.4067,       // 纬度, 正数为北纬, 负数为南纬
+--     "height":0                // 椭球高,固定输出 1 位小数
+-- }
+模式0示例:
+json.encode默认输出"7f"格式保留7位小数,可以根据自己需要的格式调整小数位,本示例保留5位小数
+local gga = gnss.getGga(0)
+if gga then
+    log.info("GGA0", json.encode(gga, "5f"))
+end
+{"longitude":11419.19531,"dgps_age":0,"altitude":86.40000,"hdop":0.59400,"height":-13.70000,"fix_quality":1,"satellites_tracked":22,"latitude":3447.86914}
+模式1示例:
+DDDDDDDDD格式是由DD.DDDDDDD*10000000转换而来,目的是作为整数,方便某些场景使用
+local gga1 = gnss.getGga(1)
+if gga1 then
+    log.info("GGA1", json.encode(gga1))
+end
+{"longitude":1143199103,"dgps_age":0,"altitude":86.4000015,"hdop":0.5940000,"height":-13.6999998,"fix_quality":1,"satellites_tracked":22,"latitude":347978178}
+模式2示例:
+json.encode默认输出"7f"格式保留7位小数,可以根据自己需要的格式调整小数位
+local gga2 = gnss.getGga(2)
+if gga2 then
+    log.info("GGA2", json.encode(gga2))
+end
+{"longitude":114.3199081,"dgps_age":0,"altitude":86.4000015,"hdop":0.5940000,"height":-13.6999998,"fix_quality":1,"satellites_tracked":22,"latitude":34.7978172}
+模式3示例:
+local gga3 = gnss.getGga(3)
+if gga3 then
+    log.info("GGA3", gga3)
+end
+$GNGGA,131241.000,3434.81372,N,11350.39930,E,1,05,4.924,165.5,M,-15.2,M,,*6D\r
+]]
+function gnss.getGga(lnglat_mode)
+    return  libgnss.getGga(lnglat_mode)
+end
+
+--[[
+获取GLL数据
+@api gnss.getGll(data_mode)
+@number 经纬度数据的格式, 0-ddmm.mmmmm格式, 1-DDDDDDDDD格式, 2-DD.DDDDDDD格式
+@return table GLL数据, 若如不存在会返回nil
+@usage
+local gll = gnss.getGll(2)
+if gll then
+    log.info("GLL", json.encode(gll, "11g"))
+end
+-- 实例数据,获取值的解释:
+-- {
+--     "status":"A",        // 定位状态, A有效, B无效
+--     "mode":"A",          // 定位模式, V无效, A单点解, D差分解
+--     "sec":20,            // 秒, UTC时间为准
+--     "min":23,            // 分钟, UTC时间为准
+--     "hour":7,            // 小时, UTC时间为准
+--     "longitude":113.231, // 经度, 正数为东经, 负数为西经
+--     "latitude":23.4067,  // 纬度, 正数为北纬, 负数为南纬
+--     "us":0               // 微妙数, 通常为0
+-- }
+--模式0示例:
+--json.encode默认输出"7f"格式保留7位小数,可以根据自己需要的格式调整小数位,本示例保留5位小数
+local gll = gnss.getGll(0)
+if gll then
+    log.info("GLL0", json.encode(gll, "5f"))
+end
+{"longitude":11419.19531,"sec":14,"min":32,"mode":"A","hour":6,"us":0,"status":"A","latitude":3447.86914}
+--模式1示例:
+--DDDDDDDDD格式是由DD.DDDDDDD*10000000转换而来,目的是作为整数,方便某些场景使用
+local gll1 = gnss.getGll(1)
+if gll1 then
+    log.info("GLL1", json.encode(gll1))
+end
+{"longitude":1143199103,"sec":14,"min":32,"mode":"A","hour":6,"us":0,"status":"A","latitude":347978178}
+模式2示例:
+--json.encode默认输出"7f"格式保留7位小数,可以根据自己需要的格式调整小数位
+local gll2 = gnss.getGll(2)
+if gll2 then
+    log.info("GLL2", json.encode(gll2))
+end
+{"longitude":114.3199081,"sec":14,"min":32,"mode":"A","hour":6,"us":0,"status":"A","latitude":34.7978172}
+]]
+function gnss.getGll(data_mode)
+    return  libgnss.getGll(data_mode)
+end
+--[[
+获取最后的经纬度数据
+@api gnss.getlastloc()
+@return table 经纬度数据,格式:ddmm.mmmmm0000,返回nil表示没有数据,此数据在定位成功,关闭gps时,会自动保存到文件系统中,定位成功之后每10分钟如果还处于定位成功状态会更新
+@usage
+local loc= gnss.getlastloc()
+if loc then
+    log.info("lastloc", loc.lat,loc.lng)
+end
+输出示例:
+3434.7937000000 11350.386720000
+]]
+function gnss.getlastloc()
+    local locStr = io.readFile("/hxxtloc")
+    if locStr then
+        local jdata = json.decode(locStr)
+        return jdata 
+    end
+end
+return gnss

+ 131 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/gt911.lua

@@ -0,0 +1,131 @@
+--[[
+@module gt911
+@summary gt911 驱动
+@version 1.0
+@date    2022.05.26
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local gt911 = require "gt911"
+
+local i2cid = 0
+local gt911_res = pin.PA07
+local gt911_int = pin.PA00
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    gt911.init(i2cid,gt911_res,gt911_int)
+    while 1 do
+        sys.wait(1000)
+    end
+end)
+
+local function gt911CallBack(press_sta,i,x,y)
+    print(press_sta,i,x,y)
+end
+
+sys.subscribe("GT911",gt911CallBack)
+]]
+
+
+local gt911 = {}
+local sys = require "sys"
+local i2cid
+
+local gt911_id
+local gt911_id_2 = 0x14
+local gt911_id_1 = 0x5D
+
+local function gt911_send(...)
+    i2c.send(i2cid, gt911_id, {...})
+end
+
+local function gt911_recv(...)
+    i2c.send(i2cid, gt911_id, {...})
+    local _, read_data = pack.unpack(i2c.recv(0, gt911_id, 1), "b")
+    return read_data
+end
+
+local press_sta = false
+
+local point = {{x=0,y=0},{x=0,y=0},{x=0,y=0},{x=0,y=0},{x=0,y=0}}
+
+--器件ID检测
+local function chip_check()
+    local ret = i2c.send(i2cid, gt911_id_1,string.char(0x00, 0x00))
+    if ret then
+        gt911_id = gt911_id_1
+    else
+        ret = i2c.send(i2cid, gt911_id_2,string.char(0x00, 0x00))
+        if ret then
+            gt911_id = gt911_id_2
+        else
+            log.info("gt911", "Can't find device")
+            return false
+        end
+    end
+    log.info("gt911", "find device",gt911_id)
+    return true
+end
+
+--[[
+gt911初始化
+@api gt911.init(gt911_i2c,gt911_res,gt911_int)
+@number gt911_i2c i2c_id
+@number gt911_res 复位引脚
+@number gt911_int 中断引脚
+@return bool   成功返回true
+@usage
+gt911.init(0)
+]]
+function gt911.init(gt911_i2c,gt911_res,gt911_int)
+    i2cid = gt911_i2c
+    gpio.setup(gt911_int, 0)
+    gpio.setup(gt911_res, 0)
+
+    gpio.set(gt911_int, 0)
+    gpio.set(gt911_res, 1)
+
+    gpio.set(gt911_res, 0)
+    sys.wait(10)
+    gpio.set(gt911_res, 1)
+    sys.wait(10)
+    if chip_check()~=true then
+        return false
+    end
+    gpio.setup(gt911_int, 
+        function(val) 
+            local count
+            local data = gt911_recv(0x81, 0x4E)
+            if data ~=0 then
+                press_sta = true
+                press_times = 0
+                count = data-128
+                -- print("触控点数",count)
+                if press_sta == true and count==0 then
+                    press_sta = false
+                    -- print("抬起")
+                    sys.publish("GT911",press_sta,1,point[1].x,point[1].y)
+                end
+                for i=1,data-128 do
+                    point[i].x=gt911_recv(0x81, 0x50+(i-1)*8)+gt911_recv(0x81, 0x51+(i-1)*8)*256
+                    point[i].y=gt911_recv(0x81, 0x52+(i-1)*8)+gt911_recv(0x81, 0x53+(i-1)*8)*256
+                    -- print(i,point[i].x,point[i].y)
+                    sys.publish("GT911",press_sta,i,point[i].x,point[i].y)
+                end
+                gt911_send(0x81, 0x4E, 0x00)
+            else
+            end
+        end,nil,gpio.RISING)
+        
+    gt911_send(0x80, 0x40, 0x02)
+    gt911_send(0x80, 0x40, 0x00)
+    local touchic_id = gt911_recv(0x81, 0x40)
+    print("touchic_id",touchic_id)
+    return true
+end
+
+return gt911
+
+

+ 198 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/gy53l1.lua

@@ -0,0 +1,198 @@
+--[[
+@module  gy53l1
+@summary gy53l1激光测距传感器 
+@version 1.0
+@date    2023.11.14
+@author  dingshuaifei
+@usage
+测量说明:
+测量范围:5-4000mm(可选择短、中、长测量模式)
+单次测量:测量一次后需要重新发送单次输出距离数据指令
+
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+gy53l1=require"gy53l1"
+local uart2=2
+sys.taskInit(function()
+
+    sys.wait(2000)
+    --初始化
+    gy53l1.init(uart2)
+    
+    --设置模式,不设置为默认模式,设置模式要有一定的间隔时间
+    sys.wait(1000)
+    gy53l1.mode(uart2,gy53l1.measuring_short)
+    sys.wait(1000)
+    gy53l1.mode(uart2,gy53l1.measuring_time_1)
+
+    local data,mode,time
+    while true do
+        sys.wait(100)
+        --设置单次测量,设置一次返回一次值
+        --gy53l1.mode(uart2,gy53l1.out_mode_query)
+
+        data,mode,time=gy53l1.get()
+        log.info('距离',data,'模式',mode,'时间',time)
+    end
+end)
+]]
+
+gy53l1={}
+
+--接收的数据
+local uart_recv_val=""
+--数据包table
+local recv_data={}
+--数据帧
+--recv_data.data=0
+--帧头1
+recv_data.head1=0
+--帧头2
+recv_data.head2=0
+--本帧数据类型
+recv_data.type=0
+--数据量
+recv_data.amount=0
+--数据高8位
+recv_data.hight=0
+--数据低八位
+recv_data.low=0
+--测量模式
+recv_data.mode=0
+--校验和
+recv_data.check_sum=0
+--距离
+local range=0
+
+-----------------------------------------------可选择测量模式---------------------------------------------------
+
+--默认模式连续输出、中距离、测量时间110ms、波特率9600
+
+--输出模式设置指令:
+gy53l1.out_mode_coiled=string.char(0xA5,0x45,0xEA)    ---------------连续输出距离数据---1
+--[[若设置为查询指令,则发一次指令测量一次]]
+gy53l1.out_mode_query=string.char(0xA5,0x15,0xBA)     ---------------单次输出距离数据---2
+
+--保存配置指令:
+gy53l1.save=string.char(0xA5,0x25,0xCA)               ---------------掉电保存当前配置;包括波特率(重新上电起效)、测量模
+                                                       ---------------式、测量时间、输出模式设置
+--测量模式设置指令:
+gy53l1.measuring_short=string.char(0xA5,0x51,0xF6)    ---------------短距离测量模式---1
+gy53l1.measuring_middle=string.char(0xA5,0x52,0xF7)   ---------------中距离测量模式(默认)---2
+gy53l1.measuring_long=string.char(0xA5,0x53,0xF8)     ---------------长距离测量模式---3
+--测量时间设置指令:
+gy53l1.measuring_time_1=string.char(0xA5,0x61,0x06)   ---------------测量时间 110ms(默认)---1
+gy53l1.measuring_time_2=string.char(0xA5,0x62,0x07)   ---------------测量时间 200ms ---2
+gy53l1.measuring_time_3=string.char(0xA5,0x63,0x08)   ---------------测量时间 300ms ---3
+gy53l1.measuring_time_4=string.char(0xA5,0x64,0x09)   ---------------测量时间 55ms ---0
+--波特率配置:
+gy53l1.ste_baut_1=string.char(0xA5,0xAE,0x53)         ---------------9600(默认)---1
+gy53l1.ste_baut_2=string.char(0xA5,0xAF,0x54)         ---------------115200---2
+
+--例:
+-- uart.write(2,measuring_short)  设置工作模式为短距离
+-----------------------------------------------可选择测量模式---------------------------------------------------
+
+--[[
+    参数:str 传入串口接收到的string类型的数据
+    返回值:失败返回-1
+]]
+local function data_dispose(str)  
+    recv_data.head1=string.byte(str,1)
+    recv_data.head2=string.byte(str,2)
+    recv_data.type=string.byte(str,3)
+    recv_data.amount=string.byte(str,4)
+    recv_data.hight=string.byte(str,5)
+    recv_data.low=string.byte(str,6)
+    recv_data.mode=string.byte(str,7)
+    recv_data.check_sum=string.byte(str,8)
+
+    if recv_data.head1 ~= 0x5A then
+        log.info('帧头错误')
+        return -1
+    end
+    --校验和计算
+    local sum=recv_data.head1+recv_data.head2+recv_data.type+ recv_data.amount+recv_data.hight+recv_data.low+recv_data.mode
+    sum=sum & 0xff
+    if sum ==recv_data.check_sum then
+        --输出距离值
+        range=(recv_data.hight<<8)|recv_data.low
+        --log.info("距离:mm",range,"测量模式:",recv_data.mode & 0x03,"测量时间:",(recv_data.mode>>2) & 0x03)
+    else
+        log.info('校验错误')
+        return -1
+    end
+end
+
+--[[
+gy53l1初始化
+@api gy53l1.init(id)
+@number  id 串口id
+@return  bool 成功返回true失败返回false
+@usage
+gy53l1.init(2) 
+]]
+function gy53l1.init(id)
+    -- 初始化
+    local uart_s=uart.setup(id, 9600, 8, 1)
+    if uart_s ~=0 then 
+        return false
+    end
+
+    --设置工作模式
+    -- 收取数据会触发回调, 这里的"receive" 是固定值
+    uart.on(id, "receive", function(id, len)
+        local s = ""
+        repeat
+            -- s = uart.read(id, 1024)
+            s = uart.read(id, len)
+            if #s > 0 then -- #s 是取字符串的长度
+                -- 如果传输二进制/十六进制数据, 部分字符不可见, 不代表没收到
+                -- 关于收发hex值,请查阅 https://doc.openluat.com/article/583
+                --log.info("uart", "receive", id, #s, s)
+                data_dispose(s) 
+            end
+            if #s == len then
+                break
+            end
+        until s == ""
+    end)
+    return true
+end
+
+--[[
+gy53l1设置工作模式
+@api gy53l1.mode(id,mode)
+@number id 串口id
+@string mode 可选择配置模式
+@return  bool 成功返回true失败返回false
+@usage
+gy53l1.mode(2,gy53l1.save)--掉电保存当前配置
+gy53l1.mode(2,gy53l1.measuring_time_3)--测量时间 300ms
+gy53l1.mode(2,gy53l1.measuring_long)--测量距离选择
+]]
+function gy53l1.mode(id,mode)
+    local ret_data=uart.write(id,mode)
+    if recv_data ~=0 then
+        return true
+    else
+        return false
+    end
+end
+
+--[[
+gy53l1获取数据
+@api gy53l1.get()
+@return number data 距离数据
+@return number mode 当前测量模式
+@return number time 当前测量时间
+@usage
+local data,mode,timer=gy53l1.get()
+log.info("距离",data,"模式",mode,"时间",timer)
+]]
+function gy53l1.get()
+    local data,mode,time= range , recv_data.mode & 0x03 , (recv_data.mode>>2) & 0x03
+    return data,mode,time
+end
+
+return gy53l1

+ 124 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/ina226.lua

@@ -0,0 +1,124 @@
+--[[
+@module ina226
+@summary ina226 驱动
+@version 1.0
+@date    2023.04.06
+@author  Dozingfiretruck
+@usage
+--注意:校准和算法根据自己设计情况进行调节
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local ina226 = require "ina226"
+
+local i2cid = 0
+sys.taskInit(function()
+    i2c.setup(i2cid, i2c.FAST)
+    ina226.init(i2cid)
+    while 1 do
+        local ina226_data = ina226.get_data()
+        log.info("ina226_data", "shunt_voltage",ina226_data.shunt_voltage,"bus_voltage",ina226_data.bus_voltage,"power",ina226_data.power,"current",ina226_data.current)
+        sys.wait(1000)
+    end
+end)
+]]
+local ina226 = {}
+local sys = require "sys"
+local i2cid
+
+
+local INA226_ADDRESS_ADR        =   0x40
+
+--寄存器
+local INA226_CONFIG_REG         =   0x00    -- 配置
+local INA226_SHUNT_VOL_REG      =   0x01    -- 分压电压
+local INA226_BUS_VOL_REG        =   0x02    -- 总线电压
+local INA226_POWER_REG          =   0x03    -- 功率
+local INA226_CURRENT_REG        =   0x04    -- 电流
+local INA226_CALIBRA_REG        =   0x05    -- 校准
+local INA226_MASK_REG           =   0x06    -- 屏蔽  启用
+local INA226_ALERT_REG          =   0x07    -- 警报
+local INA226_MANUFACTURER_ID_REG=   0xFE    -- 制造商ID
+local INA226_DIE_ID_REG         =   0xFF    -- 器件ID
+
+local INA226_MANUFACTURER_ID    =   0x5449
+local INA226_DIE_ID             =   0x2260
+
+local function ina226_send(...)
+    i2c.send(i2cid, INA226_ADDRESS_ADR, {...})
+end
+
+local function ina226_recv_short(...)
+    i2c.send(i2cid, INA226_ADDRESS_ADR, {...})
+    local _, read_data = pack.unpack(i2c.recv(0, INA226_ADDRESS_ADR, 2), ">H")
+    return read_data
+end
+
+--器件ID检测
+local function chip_check()
+    if ina226_recv_short(INA226_MANUFACTURER_ID_REG) == INA226_MANUFACTURER_ID and ina226_recv_short(INA226_DIE_ID_REG) == INA226_DIE_ID then
+        log.info("Device i2c id is: INA226")
+        return true
+    else
+        log.info("Can't find INA226 device")
+    end
+end
+
+--[[
+ina226初始化
+@api ina226.init(ina226_i2c, conf, cal)
+@number 挂载ina226的i2c总线id
+@table 配置数据, 默认值 {0x47,0x27}, 即0100 0111 0010 0111
+@table 校准数据, 默认值 {0x0A,0x00}, 即5.12 / (0.1 * 0.02)
+@return bool   成功返回true
+@usage
+-- 使用默认值进行初始化
+ina226.init(0)
+]]
+function ina226.init(ina226_i2c, conf, cal)
+    i2cid = ina226_i2c
+    if not conf then
+        conf = {0x47,0x27}
+    end
+    if not cal then
+        cal =  {0x0A,0x00}
+    end
+    if chip_check() then
+        ina226_send(INA226_CONFIG_REG,0x80,0x00)
+        sys.wait(20)
+        ina226_send(INA226_CONFIG_REG, table.unpack(conf))-- 0100 0111 0010 0111
+        ina226_send(INA226_CALIBRA_REG, table.unpack(cal))--5.12 / (0.1 * 0.02)
+        return true
+    end
+end
+
+--[[
+获取 ina226 分压电压数据
+@api ina226.get_data()
+@return table ina226 数据
+@usage
+local ina226_data = ina226.get_data()
+log.info("ina226_data", "shunt_voltage",ina226_data.shunt_voltage,"bus_voltage",ina226_data.bus_voltage,"power",ina226_data.power,"current",ina226_data.current)
+]]
+function ina226.get_data()
+    local ina226_data = {}
+    local shunt = ina226_recv_short(INA226_SHUNT_VOL_REG)
+    -- print("shunt",shunt)
+    if shunt == 0 then ina226_data.shunt_voltage = 0 else ina226_data.shunt_voltage = shunt*0.0025 end
+
+    local bus = ina226_recv_short(INA226_BUS_VOL_REG)
+    -- print("bus",bus)
+    if bus == 0 then ina226_data.bus_voltage = 0 else ina226_data.bus_voltage = bus*1.25 end
+
+    local power = ina226_recv_short(INA226_POWER_REG)
+    -- print("power",power)
+    if power == 0 then ina226_data.power = 0 else ina226_data.power = power*0.5 end
+
+    local current = ina226_recv_short(INA226_CURRENT_REG)
+    -- print("current",current)
+    if current == 0 then ina226_data.current = 0 else ina226_data.current = current*0.02 end
+    return ina226_data
+end
+
+return ina226
+
+

+ 103 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/joystick.lua

@@ -0,0 +1,103 @@
+--[[
+@module joystick
+@summary joystick 驱动
+@version 1.0
+@date    2023.04.10
+@author  Dozingfiretruck
+@usage
+-- 用法实例
+
+local joystick = require "joystick"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    joystick.init(i2cid)--初始化,传入i2c_id
+    -- while 1 do
+        local button_a = joystick.get_button_status(joystick.BUTOON_A)
+        local button_b = joystick.get_button_status(joystick.BUTOON_B)
+        local button_select = joystick.get_button_status(joystick.BUTOON_D)
+        local button_start = joystick.get_button_status(joystick.BUTOON_C)
+        local joystick_x = joystick.get_joystick_status(joystick.JOYSTICK_X)
+        local joystick_y = joystick.get_joystick_status(joystick.JOYSTICK_Y)
+        log.info("joystick", button_a,button_b,button_select,button_start,joystick_x,joystick_y)
+        sys.wait(1000)
+    -- end
+end)
+]]
+local joystick = {}
+local sys = require "sys"
+local i2cid
+
+local JOYSTICK_ADDRESS_ADDR     =   0x5A
+
+---器件所用地址
+local JOYSTICK_BASE_REG         =   0x10
+local JOYSTICK_LEFT_X_REG       =   0x10
+local JOYSTICK_LEFT_Y_REG       =   0x11
+local JOYSTICK_RIGHT_X_REG      =   0x12
+local JOYSTICK_RIGHT_Y_REG      =   0x13
+
+local BUTOON_BASE               =   0x20
+local BUTOON_LEFT_REG           =   0x24
+local BUTOON_RIGHT_REG          =   0x23
+local BUTOON_UP_REG             =   0x22
+local BUTOON_DOWN_REG           =   0x21
+local JOYSTICK_BUTTON_REG       =   0x20
+
+joystick.PRESS_DOWN             =   0
+joystick.PRESS_UP               =   1
+joystick.PRESS_REPEAT           =   2
+joystick.SINGLE_CLICK           =   3
+joystick.DOUBLE_CLICK           =   4 
+joystick.LONG_PRESS_START       =   5
+joystick.LONG_PRESS_HOLD        =   6
+joystick.number_of_event        =   7
+joystick.NONE_PRESS             =   8
+
+joystick.BUTOON_D               =   0 
+joystick.BUTOON_B               =   1 
+joystick.BUTOON_A               =   2 
+joystick.BUTOON_C               =   3 
+joystick.BUTTON_J               =   4 
+joystick.JOYSTICK_X             =   5
+joystick.JOYSTICK_Y             =   6
+
+local function joystick_recv(...)
+    i2c.send(i2cid, JOYSTICK_ADDRESS_ADDR, {...})
+    local _, read_data = pack.unpack(i2c.recv(0, JOYSTICK_ADDRESS_ADDR, 1), "b")
+    return read_data
+end
+
+function joystick.init(joystick_i2c)
+    -- i2c.setup(joystick_i2c, i2c.FAST)
+    i2cid = joystick_i2c
+end
+
+function joystick.get_button_status(button)
+    local status = 0
+    if button == joystick.BUTOON_D then
+        status = joystick_recv(BUTOON_LEFT_REG)
+    elseif button == joystick.BUTOON_B then
+        status = joystick_recv(BUTOON_RIGHT_REG)
+    elseif button == joystick.BUTOON_A then
+        status = joystick_recv(BUTOON_UP_REG)
+    elseif button == joystick.BUTOON_C then
+        status = joystick_recv(BUTOON_DOWN_REG)
+    elseif button == joystick.BUTTON_J then
+        status = joystick_recv(JOYSTICK_BUTTON_REG)
+    end
+    return status
+end
+
+function joystick.get_joystick_status(joystick_xy)
+    local status = 0
+    if joystick_xy == joystick.JOYSTICK_X then
+        status = joystick_recv(JOYSTICK_LEFT_X_REG)
+    elseif joystick_xy == joystick.JOYSTICK_Y then
+        status = joystick_recv(JOYSTICK_LEFT_Y_REG)
+    end
+    return status
+end
+
+return joystick

+ 132 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/l3g4200d.lua

@@ -0,0 +1,132 @@
+--[[
+@module l3g4200d
+@summary l3g4200d 三轴数字陀螺仪传感器
+@version 1.0
+@date    2022.04.12
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local l3g4200d = require "l3g4200d"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    l3g4200d.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+    local l3g4200d_data = l3g4200d.get_data()
+    log.info("l3g4200d_data", l3g4200d_data.x,l3g4200d_data.y,l3g4200d_data.z)
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local l3g4200d = {}
+local sys = require "sys"
+local i2cid
+
+local L3G4200D_ADDRESS_ADR
+
+local L3G4200D_ADDRESS_ADR_LOW       =   0x68
+local L3G4200D_ADDRESS_ADR_HIGH      =   0x69
+
+local L3G4200D_CHIP_ID_CHECK         =   0x0F
+local L3G4200D_CHIP_ID               =   0xD3
+
+---器件所用地址
+
+local L3G4200D_CTRL_REG1             =   0x20
+local L3G4200D_CTRL_REG2             =   0x21
+local L3G4200D_CTRL_REG3             =   0x22
+local L3G4200D_CTRL_REG4             =   0x23
+local L3G4200D_CTRL_REG5             =   0x24
+local L3G4200D_REFERENCE             =   0x25
+local L3G4200D_OUT_TEMP              =   0x26
+local L3G4200D_STATUS_REG            =   0x27
+local L3G4200D_OUT_X_L               =   0x28
+local L3G4200D_OUT_X_H               =   0x29
+local L3G4200D_OUT_Y_L               =   0x2A
+local L3G4200D_OUT_Y_H               =   0x2B
+local L3G4200D_OUT_Z_L               =   0x2C
+local L3G4200D_OUT_Z_H               =   0x2D
+local L3G4200D_FIFO_CTRL_REG         =   0x2E
+local L3G4200D_FIFO_SRC_REG          =   0x2F
+local L3G4200D_INT1_CFG              =   0x30
+local L3G4200D_INT1_SRC              =   0x31
+local L3G4200D_INT1_TSH_XH           =   0x32
+local L3G4200D_INT1_TSH_XL           =   0x33
+local L3G4200D_INT1_TSH_YH           =   0x34
+local L3G4200D_INT1_TSH_YL           =   0x35
+local L3G4200D_INT1_TSH_ZH           =   0x36
+local L3G4200D_INT1_TSH_ZL           =   0x37
+local L3G4200D_INT1_DURATION         =   0x38
+
+--器件ID检测
+local function chip_check()
+    i2c.send(i2cid, L3G4200D_ADDRESS_ADR_HIGH, L3G4200D_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, L3G4200D_ADDRESS_ADR_HIGH, 1)
+    if revData:byte() ~= nil then
+        L3G4200D_ADDRESS_ADR = L3G4200D_ADDRESS_ADR_HIGH
+    else
+        i2c.send(i2cid, L3G4200D_ADDRESS_ADR_LOW, L3G4200D_CHIP_ID_CHECK)--读器件地址
+        sys.wait(50)
+        local revData = i2c.recv(i2cid, L3G4200D_ADDRESS_ADR_LOW, 1)
+        if revData:byte() ~= nil then
+            L3G4200D_ADDRESS_ADR = L3G4200D_ADDRESS_ADR_LOW
+        else
+            log.info("Can't find L3G4200D device")
+            return false
+        end
+    end
+
+    i2c.send(i2cid, L3G4200D_ADDRESS_ADR, L3G4200D_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, L3G4200D_ADDRESS_ADR, 1)
+    if revData:byte() == L3G4200D_CHIP_ID then
+        log.info("Device i2c id is: L3G4200D")
+        return true
+    else
+        log.info("Can't find L3G4200D device")
+        return false
+    end
+end
+
+--[[
+l3g4200d 初始化
+@api l3g4200d.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+l3g4200d.init(0)
+]]
+function l3g4200d.init(i2c_id)
+    i2cid = i2c_id
+    if chip_check() then
+        i2c.send(i2cid, L3G4200D_ADDRESS_ADR, {L3G4200D_CTRL_REG1,0x0F})
+        i2c.send(i2cid, L3G4200D_ADDRESS_ADR, {L3G4200D_CTRL_REG2,0x00})
+        i2c.send(i2cid, L3G4200D_ADDRESS_ADR, {L3G4200D_CTRL_REG3,0x08})
+        i2c.send(i2cid, L3G4200D_ADDRESS_ADR, {L3G4200D_CTRL_REG4,0x30})
+        i2c.send(i2cid, L3G4200D_ADDRESS_ADR, {L3G4200D_CTRL_REG5,0x00})
+    end
+    return true
+end
+
+--[[
+获取 l3g4200d 数据
+@api l3g4200d.get_data()
+@return table l3g4200d 数据
+@usage
+local l3g4200d_data = l3g4200d.get_data()
+log.info("l3g4200d_data", l3g4200d_data.x,l3g4200d_data.y,l3g4200d_data.z)
+]]
+function l3g4200d.get_data()
+    local l3g4200d_data = {}
+    i2c.send(i2cid, L3G4200D_ADDRESS_ADR,L3G4200D_OUT_X_L)
+    local data = i2c.recv(i2cid, L3G4200D_ADDRESS_ADR, 6)
+    _, l3g4200d_data.x, l3g4200d_data.y, l3g4200d_data.z = pack.unpack(data, "<h3")
+    return l3g4200d_data
+end
+
+return l3g4200d
+
+

+ 304 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/lis2dh12.lua

@@ -0,0 +1,304 @@
+--[[
+@module lis2dh12
+@summary lis2dh12 三轴传感器
+@version 1.0
+@date    2022.04.20
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local lis2dh12 = require "lis2dh12"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    lis2dh12.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local lis2dh12_data = lis2dh12.get_data()
+        log.info("lis2dh12_data", "lis2dh12_data.x"..(lis2dh12_data.x),"lis2dh12_data.y"..(lis2dh12_data.y),"lis2dh12_data.z"..(lis2dh12_data.z),"lis2dh12_data.temp"..(lis2dh12_data.temp))
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local lis2dh12 = {}
+
+local sys = require "sys"
+
+local i2cid
+local LIS2DH12_ADDRESS_ADR
+
+local LIS2DH12_ADDRESS_ADR_LOW      =   0x18
+local LIS2DH12_ADDRESS_ADR_HIGH     =   0x19
+
+local LIS2DH12_CHIP_ID_CHECK        =   0x0F
+local LIS2DH12_CHIP_ID              =   0x33
+
+
+---器件所用地址
+local LIS2DH12_STATUS_REG_AUX		=   0x07
+
+local LIS2DH12_OUT_TEMP_L           =   0x0C
+local LIS2DH12_OUT_TEMP_H           =   0x0D
+
+local LIS2DH12_CTRL_REG0		    =   0x1E
+local LIS2DH12_TEMP_CFG_REG		    =   0x1F
+
+local LIS2DH12_CTRL_REG1			=   0x20
+local LIS2DH12_CTRL_REG2			=   0x21
+local LIS2DH12_CTRL_REG3			=   0x22
+local LIS2DH12_CTRL_REG4			=   0x23
+local LIS2DH12_CTRL_REG5			=   0x24
+local LIS2DH12_CTRL_REG6			=   0x25
+local LIS2DH12_REFERENCE			=   0x26
+local LIS2DH12_STATUS_REG			=   0x27
+local LIS2DH12_OUT_X_L			    =   0x28
+local LIS2DH12_OUT_X_H			    =   0x29
+local LIS2DH12_OUT_Y_L			    =   0x2A
+local LIS2DH12_OUT_Y_H			    =   0x2B
+local LIS2DH12_OUT_Z_L			    =   0x2C
+local LIS2DH12_OUT_Z_H			    =   0x2D
+local LIS2DH12_FIFO_CTRL_REG		=   0x2E
+local LIS2DH12_FIFO_SRC_REG		    =   0x2F
+
+local LIS2DH12_INT1_CFG			    =   0x30
+local LIS2DH12_INT1_SRC			    =   0x31
+local LIS2DH12_INT1_THS			    =   0x32
+local LIS2DH12_INT1_DURATION		=   0x33
+local LIS2DH12_INT2_CFG			    =   0x34
+local LIS2DH12_INT2_SRC			    =   0x35
+local LIS2DH12_INT2_THS			    =   0x36
+
+local LIS2DH12_INT2_DURATION	    =   0x37
+local LIS2DH12_CLICK_CFG			=   0x38
+local LIS2DH12_CLICK_SRC			=   0x39
+local LIS2DH12_CLICK_THS		    =   0x3A
+local LIS2DH12_TIME_LIMIT			=   0x3B
+local LIS2DH12_TIME_LATENCY			=   0x3C
+local LIS2DH12_TIME_WINDOW			=   0x3D
+local LIS2DH12_ACT_THS			    =   0x3E
+local LIS2DH12_ACT_DUR			    =   0x3F
+
+local  LIS2DH12_POWER_DOWN        = 0
+local  LIS2DH12_ODR_1Hz           = 1
+local  LIS2DH12_ODR_10Hz          = 2
+local  LIS2DH12_ODR_25Hz          = 3
+local  LIS2DH12_ODR_50Hz          = 4
+local  LIS2DH12_ODR_100Hz         = 5
+local  LIS2DH12_ODR_200Hz         = 6
+local  LIS2DH12_ODR_400Hz         = 7
+local  LIS2DH12_ODR_1kHz620_LP    = 8
+local  LIS2DH12_ODR_5kHz376_LP    = 9
+local  LIS2DH12_ODR_1kHz344_NM_HP = 9
+
+local  LIS2DH12_2g   = 0
+local  LIS2DH12_4g   = 1
+local  LIS2DH12_8g   = 2
+local  LIS2DH12_16g  = 3
+
+local LIS2DH12_HR_12bit   = 0
+local LIS2DH12_NM_10bit   = 1
+local LIS2DH12_LP_8bit    = 2
+
+local PROPERTY_DISABLE  = 0
+local PROPERTY_ENABLE   = 1
+
+local LIS2DH12_TEMP_DISABLE  = 0
+local LIS2DH12_TEMP_ENABLE   = 3
+
+--器件ID检测
+local function chip_check()
+    i2c.send(i2cid, LIS2DH12_ADDRESS_ADR_LOW, LIS2DH12_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, LIS2DH12_ADDRESS_ADR_LOW, 1)
+    if revData:byte() ~= nil then
+        LIS2DH12_ADDRESS_ADR = LIS2DH12_ADDRESS_ADR_LOW
+    else
+        i2c.send(i2cid, LIS2DH12_ADDRESS_ADR_HIGH, LIS2DH12_CHIP_ID_CHECK)--读器件地址
+        sys.wait(50)
+        local revData = i2c.recv(i2cid, LIS2DH12_ADDRESS_ADR_HIGH, 1)
+        if revData:byte() ~= nil then
+            LIS2DH12_ADDRESS_ADR = LIS2DH12_ADDRESS_ADR_HIGH
+        else
+            log.info("i2c", "Can't find lis2dh12 device")
+            return false
+        end
+    end
+    i2c.send(i2cid, LIS2DH12_ADDRESS_ADR, LIS2DH12_CHIP_ID_CHECK)--读器件地址
+    sys.wait(50)
+    local revData = i2c.recv(i2cid, LIS2DH12_ADDRESS_ADR, 1)
+    if revData:byte() == LIS2DH12_CHIP_ID then
+        log.info("Device i2c id is: lis2dh12")
+    else
+        log.info("i2c", "Can't find lis2dh12 device")
+        return false
+    end
+    return true
+end
+
+local function lis2dh12_read_reg(reg)
+    i2c.send(i2cid, LIS2DH12_ADDRESS_ADR,reg)
+    return i2c.recv(i2cid, LIS2DH12_ADDRESS_ADR, 1):byte(1)
+end
+
+local function lis2dh12_write_reg(reg,val)
+    i2c.send(i2cid, LIS2DH12_ADDRESS_ADR,{reg,val})
+end
+
+local function lis2dh12_block_data_update_set(val)
+    local rdval = lis2dh12_read_reg(LIS2DH12_CTRL_REG4)
+    val   = (val==1) and 0x80 or 0x00
+    rdval = bit.band(rdval,0x7F)
+    rdval = bit.bor(rdval, val)
+    lis2dh12_write_reg(LIS2DH12_CTRL_REG4,rdval)
+end
+
+local function lis2dh12_data_rate_set(val)
+    val = bit.lshift(bit.band(val,0x0F),4)
+    local rdval = lis2dh12_read_reg(LIS2DH12_CTRL_REG1)
+    rdval = bit.band(rdval,0x0F)
+    rdval = bit.bor(rdval, val)
+    lis2dh12_write_reg(LIS2DH12_CTRL_REG1,rdval)
+end
+
+local function lis2dh12_full_scale_set(val)
+    val = bit.lshift(bit.band(val,0x03),4)
+    local rdval = lis2dh12_read_reg(LIS2DH12_CTRL_REG4)
+    rdval = bit.band(rdval,0xCF)
+    rdval = bit.bor(rdval, val)
+    lis2dh12_write_reg(LIS2DH12_CTRL_REG4,rdval)
+end
+
+local function lis2dh12_temperature_meas_set( val)
+    val = bit.lshift(bit.band(val,0x03),6)
+    local rdval = lis2dh12_read_reg(LIS2DH12_TEMP_CFG_REG)
+    rdval = bit.band(rdval,0x3F)
+    rdval = bit.bor(rdval, val)
+    lis2dh12_write_reg(LIS2DH12_TEMP_CFG_REG,rdval)
+end
+
+local function lis2dh12_operating_mode_set(val)
+    local rdval, lpen, hr
+    if  val == LIS2DH12_HR_12bit then
+        lpen = 0x00
+        hr   = 0x08
+    elseif val == LIS2DH12_NM_10bit then
+        lpen = 0x00
+        hr   = 0x00
+    elseif val == LIS2DH12_LP_8bit then 
+        lpen = 0x80
+        hr   = 0x00
+    end
+    rdval = lis2dh12_read_reg(LIS2DH12_CTRL_REG1)
+    rdval = bit.band(rdval,0xF7)
+    rdval = bit.bor(rdval, lpen)
+    lis2dh12_write_reg(LIS2DH12_CTRL_REG1,rdval)
+
+    rdval = lis2dh12_read_reg(LIS2DH12_CTRL_REG4)
+    rdval = bit.band(rdval,0xF7)
+    rdval = bit.bor(rdval, hr)
+    lis2dh12_write_reg(LIS2DH12_CTRL_REG4,rdval)
+end
+
+local function lis2dh12_status_get()
+    return lis2dh12_read_reg(LIS2DH12_STATUS_REG)
+end
+
+local function lis2dh12_temp_data_ready_get()
+    local rdval = lis2dh12_read_reg(LIS2DH12_STATUS_REG_AUX)
+    return bit.band(rdval,0x20)
+end
+
+local function lis2dh12_acceleration_raw_get()
+    local xl,xh,yl,yh,zl,zh,x,y,z
+    xl = lis2dh12_read_reg(LIS2DH12_OUT_X_L)
+    xh = lis2dh12_read_reg(LIS2DH12_OUT_X_H)
+    yl = lis2dh12_read_reg(LIS2DH12_OUT_Y_L)
+    yh = lis2dh12_read_reg(LIS2DH12_OUT_Y_H)
+    zl = lis2dh12_read_reg(LIS2DH12_OUT_Z_L)
+    zh = lis2dh12_read_reg(LIS2DH12_OUT_Z_H)
+    x = xh * 256 + xl
+    y = yh * 256 + yl
+    z = zh * 256 + zl
+    return x,y,z
+end
+
+local function  lis2dh12_temperature_raw_get()
+    local tl,th
+    tl = lis2dh12_read_reg(LIS2DH12_OUT_TEMP_L)
+    th = lis2dh12_read_reg(LIS2DH12_OUT_TEMP_H)
+    return  th * 256 + tl
+
+end
+
+--器件初始化
+function lis2dh12.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(20)
+    if chip_check() then
+        lis2dh12_block_data_update_set(PROPERTY_ENABLE)
+        lis2dh12_data_rate_set(LIS2DH12_ODR_10Hz)
+        lis2dh12_full_scale_set(LIS2DH12_2g)
+    
+        lis2dh12_temperature_meas_set(LIS2DH12_TEMP_ENABLE)
+        lis2dh12_operating_mode_set(LIS2DH12_HR_12bit)
+        sys.wait(20)--跳过首次数据
+        log.info("lis2dh12 init_ok")
+        return true
+    end
+    return false
+end
+
+local function LIS2DH12_FROM_FS_2g_HR_TO_mg(lsb)   return (bit.rshift(lsb,4)) * 1.0  end
+local function LIS2DH12_FROM_FS_4g_HR_TO_mg(lsb)   return (bit.rshift(lsb,4)) * 2.0  end
+local function LIS2DH12_FROM_FS_8g_HR_TO_mg(lsb)   return (bit.rshift(lsb,4)) * 4.0  end
+local function LIS2DH12_FROM_FS_16g_HR_TO_mg(lsb)  return (bit.rshift(lsb,4)) * 12.0 end
+local function LIS2DH12_FROM_LSB_TO_degC_HR(lsb)   return (bit.rshift(lsb,6)) / 4.0  +25.0  end
+
+--[[
+获取lis2dh12数据
+@api lis2dh12.get_data()
+@return table lis2dh12数据
+@usage
+local lis2dh12_data = lis2dh12.get_data()
+log.info("lis2dh12_data", "lis2dh12_data.x"..(lis2dh12_data.x),"lis2dh12_data.y"..(lis2dh12_data.y),"lis2dh12_data.z"..(lis2dh12_data.z),"lis2dh12_data.temp"..(lis2dh12_data.temp))
+]]
+
+function lis2dh12.get_data()
+    local lis2dh12_data={}
+    lis2dh12_data.raw_x,lis2dh12_data.raw_y,lis2dh12_data.raw_z = lis2dh12_acceleration_raw_get()
+    if lis2dh12_temp_data_ready_get() >0 then
+        local temp_raw = lis2dh12_temperature_raw_get()
+        lis2dh12_data.temp = LIS2DH12_FROM_LSB_TO_degC_HR(temp_raw)
+    end
+	-- x, y, z 轴加速度
+	local acc_x = math.abs(LIS2DH12_FROM_FS_2g_HR_TO_mg(lis2dh12_data.raw_x))
+	local acc_y = math.abs(LIS2DH12_FROM_FS_2g_HR_TO_mg(lis2dh12_data.raw_y))
+	local acc_z = math.abs(LIS2DH12_FROM_FS_2g_HR_TO_mg(lis2dh12_data.raw_z))
+	
+    lis2dh12_data.x = acc_x
+    lis2dh12_data.y = acc_y
+    lis2dh12_data.z = acc_z
+
+	-- 重力加速度
+	lis2dh12_data.acc_g = math.sqrt(math.pow(acc_x, 2) + math.pow(acc_y, 2) + math.pow(acc_z, 2))
+
+    if acc_z > lis2dh12_data.acc_g then
+        acc_z = lis2dh12_data.acc_g
+    end
+
+	-- angle_z/90 = asin(acc_z/acc_g)/π/2
+	local angle_z = math.asin(acc_z/lis2dh12_data.acc_g) * 2 / 3.14 * 90
+	angle_z = 90 - angle_z
+    if angle_z < 0 then
+        angle_z = 0
+    end
+    lis2dh12_data.angle_z = angle_z
+
+    return lis2dh12_data
+end
+
+return lis2dh12
+
+
+

+ 80 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/lm75.lua

@@ -0,0 +1,80 @@
+--[[
+@module lm75
+@summary lm75 温度传感器 支持lm75a lm75b
+@version 1.0
+@date    2022.04.12
+@author  Dozingfiretruck
+@usage
+-- 用法实例
+local lm75 = require "lm75"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    lm75.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local lm75_data = lm75.get_data()
+        if lm75_data then
+            log.info("lm75_data", lm75_data.."℃")
+        end
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local lm75 = {}
+local sys = require "sys"
+local i2cid
+
+local LM75_ADDRESS_ADR         =   0x48
+
+---器件所用地址
+
+local LM75_CONF                =   0x01 --配置寄存器
+local LM75_TEMP                =   0x00 --温度寄存器
+local LM75_TOS                 =   0x03 --过热关断阈值寄存器
+local LM75_THYST               =   0x02 --滞后寄存器
+
+
+--[[
+lm75_data 初始化
+@api lm75_data.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+lm75_data.init(0)
+]]
+function lm75.init(i2c_id)
+    i2cid = i2c_id
+    return true
+end
+
+--[[
+获取 lm75 数据
+@api lm75.get_data()
+@return table lm75 数据
+@usage
+local lm75_data = lm75.get_data()
+if lm75_data then
+    log.info("lm75_data", lm75_data.."℃")
+end
+
+]]
+function lm75.get_data()
+    local temp
+    i2c.send(i2cid, LM75_ADDRESS_ADR,LM75_TEMP)
+    local _,data = pack.unpack(i2c.recv(i2cid, LM75_ADDRESS_ADR, 2),">h")
+    if data then
+        if bit.isclear(bit.rshift(data,5), 10) then
+            temp = bit.rshift(data,5)*0.125
+        else
+            temp = -(bit.bxor(bit.rshift(data,5),0x3F8)+1)*0.125
+        end
+    end
+    return temp
+end
+
+return lm75
+
+

+ 283 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/max31856.lua

@@ -0,0 +1,283 @@
+--[[
+@module max31856
+@summary max31856 热电偶温度检测 
+@version 1.0
+@date    2024.06.17
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+--注意:ads1115的配置需按照项目需求配置,您需要按照配置寄存器说明重新配置 ADS1115_CONF_HCMD 和 ADS1115_CONF_LCMD !!!
+-- 用法实例
+max31856 = require("max31856")
+
+sys.taskInit(function()
+    max31856_spi_device = spi.deviceSetup(1,pin.PB11,1,1,8,5*1000*1000,spi.MSB,1,0)
+    max31856.init(max31856_spi_device)
+    while 1 do
+        local cj_temp = max31856.read_cj_temp()
+        if cj_temp then
+            log.info("max31856 cj_temp: ", cj_temp)
+        end
+        local tc_temp = max31856.read_tc_temp()
+        if tc_temp then
+            log.info("max31856 tc_temp: ", tc_temp)
+        end
+        log.info("max31856 fault: ", max31856.read_fault())
+        sys.wait(1000)
+    end
+end)
+]]
+
+--[[
+    Register Memory Map
+    ADDRESS READ/WRITE NAME     FACTORY FUNCTION
+                                DEFAULT
+    00h/80h Read/Write CR0      00h     Configuration 0 Register
+    01h/81h Read/Write CR1      03h     Configuration 1 Register
+    02h/82h Read/Write MASK     FFh     Fault Mask Register
+    03h/83h Read/Write CJHF     7Fh     Cold-Junction High Fault Threshold
+    04h/84h Read/Write CJLF     C0h     Cold-Junction Low Fault Threshold
+    05h/85h Read/Write LTHFTH   7Fh     Linearized Temperature High Fault Threshold MSB
+    06h/86h Read/Write LTHFTL   FFh     Linearized Temperature High Fault Threshold LSB
+    07h/87h Read/Write LTLFTH   80h     Linearized Temperature Low Fault Threshold MSB
+    08h/88h Read/Write LTLFTL   00h     Linearized Temperature Low Fault Threshold LSB
+    09h/89h Read/Write CJTO     00h     Cold-Junction Temperature Offset Register
+    0Ah/8Ah Read/Write CJTH     00h     Cold-Junction Temperature Register, MSB
+    0Bh/8Bh Read/Write CJTL     00h     Cold-Junction Temperature Register, LSB
+    0Ch     Read Only  LTCBH    00h     Linearized TC Temperature, Byte 2
+    0Dh     Read Only  LTCBM    00h     Linearized TC Temperature, Byte 1
+    0Eh     Read Only  LTCBL    00h     Linearized TC Temperature, Byte 0
+    0Fh     Read Only  SR       00h     Fault Status Register
+]]
+
+local max31856 = {}
+
+local sys = require "sys"
+
+max31856.TCTYPE_B                   =   0x00    -- 类型B
+max31856.TCTYPE_E                   =   0x01    -- 类型E
+max31856.TCTYPE_J                   =   0x02    -- 类型J
+max31856.TCTYPE_K                   =   0x03    -- 类型K
+max31856.TCTYPE_N                   =   0x04    -- 类型N
+max31856.TCTYPE_R                   =   0x05    -- 类型R
+max31856.TCTYPE_S                   =   0x06    -- 类型S
+max31856.TCTYPE_T                   =   0x07    -- 类型T
+
+max31856.ONESHOT                    =   0x00    -- 单次转换模式
+max31856.CONTINUOUS                 =   0x01    -- 自动转换模式
+
+max31856.SAMPLE1                    =   0x00    -- 1个样品
+max31856.SAMPLE2                    =   0x01    -- 2个样品
+max31856.SAMPLE4                    =   0x02    -- 4个样品
+max31856.SAMPLE8                    =   0x03    -- 8个样品
+max31856.SAMPLE16                   =   0x04    -- 16个样品
+
+local MAX31856_CR0_REG   	        =   0x00    -- 配置寄存器0
+local MAX31856_CR1_REG       	    =   0x01    -- 配置寄存器1
+local MAX31856_MASK_REG      	    =   0x02    -- 故障屏蔽寄存器
+local MAX31856_CJHF_REG      	    =   0x03    -- 冷端上限故障
+local MAX31856_CJLF_REG      	    =   0x04    -- 冷接下限故障
+local MAX31856_LTHFTH_REG      	    =   0x05    -- 线性化温度上限故障 MSB
+local MAX31856_LTHFTL_REG      	    =   0x06    -- 线性化温度上限故障 LSB
+local MAX31856_LTLFTH_REG      	    =   0x07    -- 线性化温度下限故障 MSB
+local MAX31856_LTLFTL_REG      	    =   0x08    -- 线性化温度下限故障 LSB
+local MAX31856_CJTO_REG      	    =   0x09    -- 冷端温度偏移寄存器
+local MAX31856_CJTH_REG      	    =   0x0A    -- 冷端温度寄存器 MSB
+local MAX31856_CJTL_REG      	    =   0x0B    -- 冷端温度寄存器 LSB
+local MAX31856_LTCBH_REG      	    =   0x0C    -- 线性化TC温度,字节2
+local MAX31856_LTCBM_REG      	    =   0x0D    -- 线性化TC温度,字节1
+local MAX31856_LTCBL_REG      	    =   0x0E    -- 线性化TC温度,字节0
+local MAX31856_SR_REG      	        =   0x0F    -- 故障状态寄存器
+
+local max31856_spi_device
+local max31856_conversion_mode
+local max31856_sample_mode
+
+local function max31856_write_cmd(reg, data)
+    -- log.info("max31856_write_cmd "..(reg|0x80).." "..data)
+    max31856_spi_device:send({reg|0x80, data})
+end
+
+local function max31856_read_cmd(reg)
+    local data = max31856_spi_device:transfer(string.char(reg))
+    -- log.info("max31856_read_cmd "..reg.." "..data:byte())
+    return data:byte()
+end
+
+local function trigger_oneshot()
+    local cr0 = max31856_read_cmd(MAX31856_CR0_REG)
+    cr0 = cr0&(~0x80)
+    cr0 = cr0|0x40
+    max31856_write_cmd(MAX31856_CR0_REG, cr0)
+end
+
+local function oneshot_conversion_complete()
+    return (max31856_read_cmd(MAX31856_CR0_REG) & 0x40) == 0
+end
+
+--[[
+设置热电偶类型
+@api max31856.set_tc_type(type)
+@number type max31856.TCTYPE_B max31856.TCTYPE_E max31856.TCTYPE_J max31856.TCTYPE_K max31856.TCTYPE_N max31856.TCTYPE_R max31856.TCTYPE_S max31856.TCTYPE_T
+@usage
+max31856.set_tc_type(max31856.TCTYPE_K) -- 设置类型为K
+]]
+function max31856.set_tc_type(type)
+    local cr1 = max31856_read_cmd(MAX31856_CR1_REG)
+    cr1 = cr1&0xF0
+    max31856_write_cmd(MAX31856_CR1_REG, cr1|type)
+end
+
+--[[
+设置热电偶电压转换平均模式 
+@api max31856.set_avgsel(sample_count)
+@number sample_count max31856.SAMPLE1 max31856.SAMPLE2 max31856.SAMPLE4 max31856.SAMPLE8 max31856.SAMPLE16
+@usage
+max31856.set_avgsel(max31856.SAMPLE1) -- 设置平均模式为1个样品
+]]
+function max31856.set_avgsel(sample_count)
+    max31856_sample_mode = sample_count
+    local cr1 = max31856_read_cmd(MAX31856_CR1_REG)
+    max31856_write_cmd(MAX31856_CR1_REG, cr1|(sample_count<<4))
+end
+
+--[[
+设置转化模式
+@api max31856.set_cmode(type)
+@number type max31856.ONESHOT max31856.CONTINUOUS
+@usage
+max31856.set_cmode(max31856.ONESHOT) -- 设置转化模式为单次转换
+]]
+function max31856.set_cmode(type)
+    max31856_conversion_mode = type
+    local cr0 = max31856_read_cmd(MAX31856_CR0_REG)
+    if type == max31856.ONESHOT then
+        cr0 = cr0&(~0x80)
+        cr0 = cr0|0x40
+    else
+        cr0 = cr0|0x80
+        cr0 = cr0&(~0x40)
+    end
+    max31856_write_cmd(MAX31856_CR0_REG, cr0)
+end
+
+--[[
+读取错误码
+@api max31856.read_fault()
+@return number 错误码
+@usage
+local fault = max31856.read_fault()
+]]
+function max31856.read_fault()
+    return max31856_read_cmd(MAX31856_SR_REG)
+end
+
+--[[
+读取冷端温度
+@api max31856.read_cj_temp()
+@return number 冷端温度
+@usage
+local cj_temp = max31856.read_cj_temp()
+]]
+function max31856.read_cj_temp()
+    if max31856_conversion_mode == max31856.ONESHOT then
+        trigger_oneshot()
+        sys.wait(145+(max31856_sample_mode-1)*33)
+        local wait = 20
+        while oneshot_conversion_complete() == false do
+            sys.wait(10)
+            wait = wait - 1
+            if wait == 0 then return end
+        end
+    end
+    local CJTH = max31856_read_cmd(MAX31856_CJTH_REG)
+    local CJTL = max31856_read_cmd(MAX31856_CJTL_REG)
+    return ((CJTH<<8)|CJTL) / 256.0
+end
+
+--[[
+读取tc温度
+@api max31856.read_tc_temp()
+@return number tc温度
+@usage
+local tc_temp = max31856.read_tc_temp()
+]]
+function max31856.read_tc_temp()
+    if max31856_conversion_mode == max31856.ONESHOT then
+        trigger_oneshot()
+        sys.wait(145+(max31856_sample_mode-1)*33)
+        local wait = 20
+        while oneshot_conversion_complete() == false do
+            sys.wait(10)
+            wait = wait - 1
+            if wait == 0 then return end
+        end
+    end
+    local LTCBH = max31856_read_cmd(MAX31856_LTCBH_REG)
+    local LTCBM = max31856_read_cmd(MAX31856_LTCBM_REG)
+    local LTCBL = max31856_read_cmd(MAX31856_LTCBL_REG)
+    local temp24 = (LTCBH<<16)|(LTCBM<<8)|LTCBL
+    if temp24 & 0x800000 ~= 0 then
+        temp24 = temp24 | 0xFF000000
+    end
+    temp24 = temp24 >> 5
+    return temp24 * 0.0078125
+end
+
+--[[
+max31856 初始化
+@api max31856.init(spi_device)
+@userdata spi_device spi设备句柄
+@return boolean 初始化成功返回true
+@usage
+max31856.init(spi_device)
+]]
+function max31856.init(spi_device)
+    if type(spi_device) ~= "userdata" then
+        return
+    end
+    max31856_spi_device = spi_device
+
+    max31856_write_cmd(MAX31856_MASK_REG, 0x00) -- 断言所有错误
+    max31856_write_cmd(MAX31856_CR0_REG, 0x10) -- 开启开路故障检测
+    max31856_write_cmd(MAX31856_CJTO_REG, 0x00) -- 冷端温度偏移设置为零
+
+    max31856.set_tc_type(max31856.TCTYPE_K) -- 设置类型为K
+
+    max31856.set_cmode(max31856.ONESHOT) -- 设置转化模式为单次转换
+    max31856.set_avgsel(max31856.SAMPLE1) -- 设置平均模式为1个样品
+    return true
+end
+
+return max31856
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 543 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/mcp2515.lua

@@ -0,0 +1,543 @@
+--[[
+@module mcp2515
+@summary mcp2515 CAN协议控制器驱动
+@version 1.0
+@date    2022.07.08
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local mcp2515 = require "mcp2515"
+
+-- mcp2515    mcu
+-- csk      spi_sck
+-- si       spi_mosi
+-- so       spi_miso
+-- cs       spi_cs
+-- int      gpio
+
+sys.subscribe("mcp2515", function(len,buff,config)
+    print("mcp2515", len,buff:byte(1,len))
+    for k, v in pairs(config) do
+        print(k,v)
+    end
+end)
+
+sys.taskInit(function()
+    local mcp2515_spi= 0
+    local mcp2515_cs= pin.PB04
+    local mcp2515_int= pin.PB01
+    spi_mcp2515 = spi.setup(mcp2515_spi,nil,0,0,8,10*1000*1000,spi.MSB,1,0)
+    mcp2515.init(mcp2515_spi,mcp2515_cs,mcp2515_int,mcp2515.CAN_500Kbps)
+
+    mcp2515.send_buffer({id = 0x7FF,ide = false,rtr = false},0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07)--标准帧,数据帧
+    mcp2515.send_buffer({id = 0x1FFFFFE6,ide = true,rtr = false},0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07)--扩展帧,数据帧
+    mcp2515.send_buffer({id = 0x7FF,ide = false,rtr = true},0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07)--标准帧,远程帧
+    mcp2515.send_buffer({id = 0x1FFFFFE6,ide = true,rtr = true},0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07)--扩展帧,远程帧
+
+end)
+]]
+
+local mcp2515 = {}
+local sys = require "sys"
+
+-- SPI 指令集
+mcp2515.RESET         =     0xC0
+mcp2515.READ          =     0x03
+mcp2515.RD_RX_BUFF    =     0x90
+mcp2515.RD_RXB0SIDH   =     0x90
+mcp2515.RD_RXB0D0     =     0x92
+mcp2515.RD_RXB1SIDH   =     0x94
+mcp2515.RD_RXB1D0     =     0x96
+mcp2515.WRITE         =     0x02
+mcp2515.LOAD_TX       =     0X40  
+mcp2515.LOAD_TXB0SIDH =     0X40
+mcp2515.LOAD_TXB0D0   =     0X41
+mcp2515.LOAD_TXB1SIDH =     0X42
+mcp2515.LOAD_TXB1D0   =     0X43
+mcp2515.LOAD_TXB2SIDH =     0X44
+mcp2515.LOAD_TXB2D0   =     0X45
+mcp2515.RTS           =     0x80
+mcp2515.RTS_TXB0      =     0x81
+mcp2515.RTS_TXB1      =     0x82
+mcp2515.RTS_TXB2      =     0x84
+mcp2515.RD_STATUS     =     0xA0
+mcp2515.RX_STATUS     =     0xB0
+mcp2515.BIT_MODIFY    =     0x05  
+-- Configuration Registers
+mcp2515.CANSTAT         = 0x0E
+mcp2515.CANCTRL         = 0x0F
+mcp2515.BFPCTRL         = 0x0C
+mcp2515.TEC             = 0x1C
+mcp2515.REC             = 0x1D
+mcp2515.CNF3            = 0x28
+mcp2515.CNF2            = 0x29
+mcp2515.CNF1            = 0x2A
+mcp2515.CANINTE         = 0x2B
+mcp2515.CANINTF         = 0x2C
+mcp2515.EFLG            = 0x2D
+mcp2515.TXRTSCTRL       = 0x0D
+--  Recieve Filters
+mcp2515.RXF0SIDH        = 0x00
+mcp2515.RXF0SIDL        = 0x01
+mcp2515.RXF0EID8        = 0x02
+mcp2515.RXF0EID0        = 0x03
+mcp2515.RXF1SIDH        = 0x04
+mcp2515.RXF1SIDL        = 0x05
+mcp2515.RXF1EID8        = 0x06
+mcp2515.RXF1EID0        = 0x07
+mcp2515.RXF2SIDH        = 0x08
+mcp2515.RXF2SIDL        = 0x09
+mcp2515.RXF2EID8        = 0x0A
+mcp2515.RXF2EID0        = 0x0B
+mcp2515.RXF3SIDH        = 0x10
+mcp2515.RXF3SIDL        = 0x11
+mcp2515.RXF3EID8        = 0x12
+mcp2515.RXF3EID0        = 0x13
+mcp2515.RXF4SIDH        = 0x14
+mcp2515.RXF4SIDL        = 0x15
+mcp2515.RXF4EID8        = 0x16
+mcp2515.RXF4EID0        = 0x17
+mcp2515.RXF5SIDH        = 0x18
+mcp2515.RXF5SIDL        = 0x19
+mcp2515.RXF5EID8        = 0x1A
+mcp2515.RXF5EID0        = 0x1B
+-- CNF1
+mcp2515.SJW_1TQ         = 0x40
+mcp2515.SJW_2TQ         = 0x80
+mcp2515.SJW_3TQ         = 0x90
+mcp2515.SJW_4TQ         = 0xC0
+-- CNF2 
+mcp2515.BTLMODE_CNF3    = 0x80
+mcp2515.BTLMODE_PH1_IPT = 0x00
+mcp2515.SMPL_3X         = 0x40
+mcp2515.SMPL_1X         = 0x00
+mcp2515.PHSEG1_8TQ      = 0x38
+mcp2515.PHSEG1_7TQ      = 0x30
+mcp2515.PHSEG1_6TQ      = 0x28
+mcp2515.PHSEG1_5TQ      = 0x20
+mcp2515.PHSEG1_4TQ      = 0x18
+mcp2515.PHSEG1_3TQ      = 0x10
+mcp2515.PHSEG1_2TQ      = 0x08
+mcp2515.PHSEG1_1TQ      = 0x00
+mcp2515.PRSEG_8TQ       = 0x07
+mcp2515.PRSEG_7TQ       = 0x06
+mcp2515.PRSEG_6TQ       = 0x05
+mcp2515.PRSEG_5TQ       = 0x04
+mcp2515.PRSEG_4TQ       = 0x03
+mcp2515.PRSEG_3TQ       = 0x02
+mcp2515.PRSEG_2TQ       = 0x01
+mcp2515.PRSEG_1TQ       = 0x00
+-- CNF3
+mcp2515.PHSEG2_8TQ      = 0x07
+mcp2515.PHSEG2_7TQ      = 0x06
+mcp2515.PHSEG2_6TQ      = 0x05
+mcp2515.PHSEG2_5TQ      = 0x04
+mcp2515.PHSEG2_4TQ      = 0x03
+mcp2515.PHSEG2_3TQ      = 0x02
+mcp2515.PHSEG2_2TQ      = 0x01
+mcp2515.PHSEG2_1TQ      = 0x00
+mcp2515.SOF_ENABLED     = 0x80
+mcp2515.WAKFIL_ENABLED  = 0x40
+mcp2515.WAKFIL_DISABLED = 0x00
+mcp2515.CAN_10Kbps    =   0x31
+mcp2515.CAN_25Kbps	=   0x13
+mcp2515.CAN_50Kbps	=   0x09
+mcp2515.CAN_100Kbps	=   0x04
+mcp2515.CAN_125Kbps	=   0x03
+mcp2515.CAN_250Kbps	=   0x01
+mcp2515.CAN_500Kbps   =   0x00
+-- CANINTE
+mcp2515.RX0IE_ENABLED   = 0x01
+mcp2515.RX0IE_DISABLED  = 0x00
+mcp2515.RX1IE_ENABLED   = 0x02
+mcp2515.RX1IE_DISABLED  = 0x00
+mcp2515.G_RXIE_ENABLED  = 0x03
+mcp2515.G_RXIE_DISABLED = 0x00
+mcp2515.TX0IE_ENABLED   = 0x04
+mcp2515.TX0IE_DISABLED  = 0x00
+mcp2515.TX1IE_ENABLED   = 0x08
+mcp2515.TX2IE_DISABLED  = 0x00
+mcp2515.TX2IE_ENABLED   = 0x10
+mcp2515.TX2IE_DISABLED  = 0x00
+mcp2515.G_TXIE_ENABLED  = 0x1C
+mcp2515.G_TXIE_DISABLED = 0x00
+mcp2515.ERRIE_ENABLED   = 0x20
+mcp2515.ERRIE_DISABLED  = 0x00
+mcp2515.WAKIE_ENABLED   = 0x40
+mcp2515.WAKIE_DISABLED  = 0x00
+mcp2515.IVRE_ENABLED    = 0x80
+mcp2515.IVRE_DISABLED   = 0x00
+-- CANINTF
+mcp2515.RX0IF_SET       = 0x01
+mcp2515.RX0IF_RESET     = 0x00
+mcp2515.RX1IF_SET       = 0x02
+mcp2515.RX1IF_RESET     = 0x00
+mcp2515.TX0IF_SET       = 0x04
+mcp2515.TX0IF_RESET     = 0x00
+mcp2515.TX1IF_SET       = 0x08
+mcp2515.TX2IF_RESET     = 0x00
+mcp2515.TX2IF_SET       = 0x10
+mcp2515.TX2IF_RESET     = 0x00
+mcp2515.ERRIF_SET       = 0x20
+mcp2515.ERRIF_RESET     = 0x00
+mcp2515.WAKIF_SET       = 0x40
+mcp2515.WAKIF_RESET     = 0x00
+mcp2515.IVRF_SET        = 0x80
+mcp2515.IVRF_RESET      = 0x00
+-- CANCTRL 
+mcp2515.REQOP_CONFIG    = 0x80--配置模式
+mcp2515.REQOP_LISTEN    = 0x60--监听模式
+mcp2515.REQOP_LOOPBACK  = 0x40--回环模式 测试用
+mcp2515.REQOP_SLEEP     = 0x20--睡眠模式
+mcp2515.REQOP_NORMAL    = 0x00--正常模式
+mcp2515.ABORT           = 0x10
+mcp2515.OSM_ENABLED     = 0x08
+mcp2515.CLKOUT_ENABLED  = 0x04
+mcp2515.CLKOUT_DISABLED = 0x00
+mcp2515.CLKOUT_PRE_8    = 0x03
+mcp2515.CLKOUT_PRE_4    = 0x02
+mcp2515.CLKOUT_PRE_2    = 0x01
+mcp2515.CLKOUT_PRE_1    = 0x00
+-- RXBnCTRL
+mcp2515.RXM_RCV_ALL     = 0x60
+mcp2515.RXM_VALID_EXT   = 0x40
+mcp2515.RXM_VALID_STD   = 0x20
+mcp2515.RXM_VALID_ALL   = 0x00
+mcp2515.RXRTR_REMOTE    = 0x08
+mcp2515.RXRTR_NO_REMOTE = 0x00
+mcp2515.BUKT_ROLLOVER   = 0x04
+mcp2515.BUKT_NO_ROLLOVER =    0x00
+mcp2515.FILHIT0_FLTR_1  = 0x01
+mcp2515.FILHIT0_FLTR_0  = 0x00
+mcp2515.FILHIT1_FLTR_5  = 0x05
+mcp2515.FILHIT1_FLTR_4  = 0x04
+mcp2515.FILHIT1_FLTR_3  = 0x03
+mcp2515.FILHIT1_FLTR_2  = 0x02
+mcp2515.FILHIT1_FLTR_1  = 0x01
+mcp2515.FILHIT1_FLTR_0  = 0x00
+-- TXBnCTRL
+mcp2515.TXREQ_SET       = 0x08
+mcp2515.TXREQ_CLEAR     = 0x00
+mcp2515.TXP_HIGHEST     = 0x03
+mcp2515.TXP_INTER_HIGH  = 0x02
+mcp2515.TXP_INTER_LOW   = 0x01
+mcp2515.TXP_LOWEST      = 0x00
+
+mcp2515.DLC_0          =  0x00
+mcp2515.DLC_1          =  0x01
+mcp2515.DLC_2          =  0x02
+mcp2515.DLC_3          =  0x03
+mcp2515.DLC_4          =  0x04
+mcp2515.DLC_5          =  0x05
+mcp2515.DLC_6          =  0x06
+mcp2515.DLC_7          =  0x07    
+mcp2515.DLC_8          =  0x08
+-- Receive Masks
+mcp2515.RXM0SIDH        = 0x20
+mcp2515.RXM0SIDL        = 0x21
+mcp2515.RXM0EID8        = 0x22
+mcp2515.RXM0EID0        = 0x23
+mcp2515.RXM1SIDH        = 0x24
+mcp2515.RXM1SIDL        = 0x25
+mcp2515.RXM1EID8        = 0x26
+mcp2515.RXM1EID0        = 0x27
+-- Tx Buffer 0
+mcp2515.TXB0CTRL        = 0x30
+mcp2515.TXB0SIDH        = 0x31
+mcp2515.TXB0SIDL        = 0x32
+mcp2515.TXB0EID8        = 0x33
+mcp2515.TXB0EID0        = 0x34
+mcp2515.TXB0DLC         = 0x35
+mcp2515.TXB0D0          = 0x36
+mcp2515.TXB0D1          = 0x37
+mcp2515.TXB0D2          = 0x38
+mcp2515.TXB0D3          = 0x39
+mcp2515.TXB0D4          = 0x3A
+mcp2515.TXB0D5          = 0x3B
+mcp2515.TXB0D6          = 0x3C
+mcp2515.TXB0D7          = 0x3D
+-- Tx Buffer 1
+mcp2515.TXB1CTRL        = 0x40
+mcp2515.TXB1SIDH        = 0x41
+mcp2515.TXB1SIDL        = 0x42
+mcp2515.TXB1EID8        = 0x43
+mcp2515.TXB1EID0        = 0x44
+mcp2515.TXB1DLC         = 0x45
+mcp2515.TXB1D0          = 0x46
+mcp2515.TXB1D1          = 0x47
+mcp2515.TXB1D2          = 0x48
+mcp2515.TXB1D3          = 0x49
+mcp2515.TXB1D4          = 0x4A
+mcp2515.TXB1D5          = 0x4B
+mcp2515.TXB1D6          = 0x4C
+mcp2515.TXB1D7          = 0x4D
+-- Tx Buffer 2
+mcp2515.TXB2CTRL        = 0x50
+mcp2515.TXB2SIDH        = 0x51
+mcp2515.TXB2SIDL        = 0x52
+mcp2515.TXB2EID8        = 0x53
+mcp2515.TXB2EID0        = 0x54
+mcp2515.TXB2DLC         = 0x55
+mcp2515.TXB2D0          = 0x56
+mcp2515.TXB2D1          = 0x57
+mcp2515.TXB2D2          = 0x58
+mcp2515.TXB2D3          = 0x59
+mcp2515.TXB2D4          = 0x5A
+mcp2515.TXB2D5          = 0x5B
+mcp2515.TXB2D6          = 0x5C
+mcp2515.TXB2D7          = 0x5D
+-- Rx Buffer 0
+mcp2515.RXB0CTRL        = 0x60
+mcp2515.RXB0SIDH        = 0x61
+mcp2515.RXB0SIDL        = 0x62
+mcp2515.RXB0EID8        = 0x63
+mcp2515.RXB0EID0        = 0x64
+mcp2515.RXB0DLC         = 0x65
+mcp2515.RXB0D0          = 0x66
+mcp2515.RXB0D1          = 0x67
+mcp2515.RXB0D2          = 0x68
+mcp2515.RXB0D3          = 0x69
+mcp2515.RXB0D4          = 0x6A
+mcp2515.RXB0D5          = 0x6B
+mcp2515.RXB0D6          = 0x6C
+mcp2515.RXB0D7          = 0x6D
+-- Rx Buffer 1
+mcp2515.RXB1CTRL        = 0x70
+mcp2515.RXB1SIDH        = 0x71
+mcp2515.RXB1SIDL        = 0x72
+mcp2515.RXB1EID8        = 0x73
+mcp2515.RXB1EID0        = 0x74
+mcp2515.RXB1DLC         = 0x75
+mcp2515.RXB1D0          = 0x76
+mcp2515.RXB1D1          = 0x77
+mcp2515.RXB1D2          = 0x78
+mcp2515.RXB1D3          = 0x79
+mcp2515.RXB1D4          = 0x7A
+mcp2515.RXB1D5          = 0x7B
+mcp2515.RXB1D6          = 0x7C
+mcp2515.RXB1D7          = 0x7D
+
+
+function mcp2515.write(addr,...)
+    mcp2515.cs(0)
+    spi.send(mcp2515.spi, string.char(mcp2515.WRITE,addr,...))
+	mcp2515.cs(1)
+end
+
+function mcp2515.read(addr,len)
+    mcp2515.cs(0)
+    spi.send(mcp2515.spi, string.char(mcp2515.READ,addr))
+    local val = spi.recv(mcp2515.spi,len or 1)
+	mcp2515.cs(1)
+    if val then
+        return string.byte(val,1,len)
+    end
+end
+
+--[[ 
+mcp2515 复位
+@api mcp2515.reset()
+@usage
+mcp2515.reset()
+]]
+function mcp2515.reset()
+    mcp2515.cs(0)
+    spi.send(mcp2515.spi, string.char(mcp2515.RESET))
+	mcp2515.cs(1)
+end
+
+--[[ 
+mcp2515 数据发送
+@api mcp2515.send_buffer(config,...)
+@table config 接收数据参数 id:报文ID ide:是否为扩展帧 rtr:是否为远程帧
+@number ... 发送数据 数据个数不可大于8
+@usage
+mcp2515.send_buffer({id = 0x7FF,ide = false,rtr = false},0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07)--标准帧,数据帧
+mcp2515.send_buffer({id = 0x1FFFFFE6,ide = true,rtr = false},0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07)--扩展帧,数据帧
+mcp2515.send_buffer({id = 0x7FF,ide = false,rtr = true},0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07)--标准帧,远程帧
+mcp2515.send_buffer({id = 0x1FFFFFE6,ide = true,rtr = true},0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07)--扩展帧,远程帧
+]]
+function mcp2515.send_buffer(config,...)
+    if config.ide then
+        mcp2515.write(mcp2515.TXB0SIDH,(config.id>>21)&0xFF)-- 发送缓冲器0标准标识符高位
+        mcp2515.write(mcp2515.TXB0SIDL,((config.id>>16)&0x03)|0x08|((config.id>>13)&0xE0))-- 发送缓冲器0标准标识符低位与缓冲器0标拓展识符最高两位(第3位为发送拓展标识符使能位)
+        mcp2515.write(mcp2515.TXB0EID8,(config.id>>8)&0xFF)-- 发送缓冲器0标拓展识符高位
+        mcp2515.write(mcp2515.TXB0EID0,config.id&0xFF)-- 发送缓冲器0标拓展识符低位
+    else
+        mcp2515.write(mcp2515.TXB0SIDH,(config.id>>3)&0xFF)-- 发送缓冲器0标准标识符高位
+        mcp2515.write(mcp2515.TXB0SIDL,(config.id&0x07)<<5)-- 发送缓冲器0标准标识符低位
+    end
+    if select("#",...)>8 then
+        log.error("mcp2515","send_buffer")
+        return
+    end
+    local delay = 0
+    while mcp2515.read(mcp2515.TXB0CTRL)&0x08 ~=0 and delay<5 do
+        sys.wait(10)
+        delay = delay+1
+    end
+    mcp2515.write(mcp2515.TXB0D0,...)--将待发送的数据写入发送缓冲寄存器
+    if config.rtr then
+        mcp2515.write(mcp2515.TXB0DLC,select("#",...)|0x40)--将本帧待发送的数据长度写入发送缓冲器0的发送长度寄存器
+    else
+        mcp2515.write(mcp2515.TXB0DLC,select("#",...))--将本帧待发送的数据长度写入发送缓冲器0的发送长度寄存器
+    end
+    mcp2515.write(mcp2515.TXB0CTRL,0x08)--请求发送报文
+end
+
+--[[ 
+mcp2515 数据接收
+@api mcp2515.receive_buffer()
+@return number len 接收数据长度
+@return string buff 接收数据
+@return table config 接收数据参数 id:报文ID ide:是否为扩展帧 rtr:是否为远程帧
+@usage
+sys.subscribe("mcp2515", function(len,buff,config)
+    print("mcp2515", len,buff:byte(1,len))
+    for k, v in pairs(config) do
+        print(k,v)
+    end
+end)
+]]
+function mcp2515.receive_buffer()
+    local config = {}
+    local len
+    local buff
+    local temp = mcp2515.read(mcp2515.CANINTF)
+    if temp & 0x01 ~= 0  then
+        local sidh=mcp2515.read(mcp2515.RXB0SIDH)
+        local sidl=mcp2515.read(mcp2515.RXB0SIDL)
+        if sidl&0x08 ==0 then
+            config.ide = false
+            config.id = sidh<<3|sidl>>5
+            if sidl&0x10 ==0 then
+                config.rtr = false
+            else
+                config.rtr = true
+            end
+        else
+            config.ide = true
+            local eidh=mcp2515.read(mcp2515.RXB0EID8)
+            local eidl=mcp2515.read(mcp2515.RXB0EID0)
+            config.id = sidh<<21|(sidl&0xE0)<<13|(sidl&0x03)<<16|eidh<<8|eidl
+        end
+        local dlc=mcp2515.read(mcp2515.RXB0DLC)
+        if config.ide then
+            if dlc&0x40 == 0 then
+                config.rtr = false
+            else
+                config.rtr = true
+            end
+        end
+        len = dlc&0x0F
+        buff = string.char(mcp2515.read(mcp2515.RXB0D0,len))
+    end
+    mcp2515.write(mcp2515.CANINTF,0)
+    return len,buff,config
+end
+
+local function mcp2515_int(val)
+    if val==0 then
+        local len,buff,config = mcp2515.receive_buffer()
+        if len then
+            sys.publish("mcp2515", len,buff,config)
+        end
+    end
+end
+
+--[[
+mcp2515 设置模式
+@api mcp2515.mode(mode)
+@number mode     模式
+@usage
+mcp2515.mode(mcp2515.REQOP_NORMAL)--进入正常模式
+]]
+function mcp2515.mode(mode)
+    mcp2515.write(mcp2515.CANCTRL,mode|mcp2515.CLKOUT_ENABLED)
+	local temp = mcp2515.read(mcp2515.CANSTAT)
+    if mode ~= (temp&0xE0) then
+        mcp2515.write(mcp2515.CANCTRL,mode|mcp2515.CLKOUT_ENABLED)
+    end
+end
+
+--[[
+mcp2515 设置波特率(注意:需在配置模式使用)
+@api mcp2515.baud(baud)
+@number baud     波特率
+@usage
+mcp2515.baud(mcp2515.CAN_500Kbps)
+]]
+function mcp2515.baud(baud)
+    mcp2515.write(mcp2515.CNF1,baud)
+end
+
+--[[
+mcp2515 设置过滤表(注意:需在配置模式使用)
+@api mcp2515.filter(id,ide,shield)
+@number id     id
+@bool ide     是否为扩展帧
+@bool shield     是否为屏蔽表
+@usage
+mcp2515.filter(0x1FF,false,false)
+]]
+function mcp2515.filter(id,ide,shield)
+    mcp2515.mode(mcp2515.REQOP_CONFIG)--进入配置模式
+    if ide then
+        if shield then
+            mcp2515.write(mcp2515.RXM0SIDH,(id>>21)&0xFF)--配置验收屏蔽寄存器n标准标识符高位
+	        mcp2515.write(mcp2515.RXM0SIDL,((id>>16)&0x03)|((id>>13)&0xE0))--配置验收屏蔽寄存器n标准标识符低位
+            mcp2515.write(mcp2515.RXM0EID8,(id>>8)&0xFF)--配置验收屏蔽寄存器n拓展标识符高位
+	        mcp2515.write(mcp2515.RXM0EID0,id&0xFF)--配置验收屏蔽寄存器n拓展标识符低位
+        else
+            mcp2515.write(mcp2515.RXF0SIDH,(id>>21)&0xFF)--配置验收滤波寄存器n标准标识符高位
+            mcp2515.write(mcp2515.RXF0SIDL,((id>>16)&0x03)|0x08|((id>>13)&0xE0))--配置验收滤波寄存器n标准标识符低位(第3位为接收拓展标识符使能位)
+            mcp2515.write(mcp2515.RXF0EID8,(id>>8)&0xFF)--配置验收滤波寄存器n标准标识符高位
+	        mcp2515.write(mcp2515.RXF0EID0,id&0xFF)--配置验收滤波寄存器n标准标识符低位
+        end
+    else
+        if shield then
+            mcp2515.write(mcp2515.RXM0SIDH,(id>>3)&0xFF)--配置验收屏蔽寄存器n标准标识符高位
+	        mcp2515.write(mcp2515.RXM0SIDL,(id&0x07)<<5)--配置验收屏蔽寄存器n标准标识符低位
+        else
+            mcp2515.write(mcp2515.RXF0SIDH,(id>>3)&0xFF)--配置验收滤波寄存器n标准标识符高位
+	        mcp2515.write(mcp2515.RXF0SIDL,(id&0x07)<<5)--配置验收滤波寄存器n标准标识符低位(第3位为接收拓展标识符使能位)
+        end
+    end
+    mcp2515.mode(mcp2515.REQOP_NORMAL)--进入正常模式
+end
+
+--[[
+mcp2515 初始化
+@api mcp2515.init(spi_id,cs,int,baud)
+@number spi_id spi端口号
+@number cs      cs引脚
+@number int     int引脚
+@number baud     波特率
+@return bool 初始化结果
+@usage
+spi_mcp2515 = spi.setup(mcp2515_spi,nil,0,0,8,20*1000*1000,spi.MSB,1,0)
+mcp2515.init(mcp2515_spi,mcp2515_cs,mcp2515_int,mcp2515.CAN_500Kbps)
+]]
+function mcp2515.init(spi_id,cs,int,baud)
+    mcp2515.spi = spi_id
+    mcp2515.cs = gpio.setup(cs, 0, gpio.PULLUP) 
+    mcp2515.cs(1)
+    gpio.setup(int,mcp2515_int, gpio.PULLUP)
+    mcp2515.reset()
+    -- 以下部分根据需求参考手册修改
+    -- 配置CNF1,CNF2,CNF3,
+    mcp2515.baud(baud)
+	mcp2515.write(mcp2515.CNF2,0x80|mcp2515.PHSEG1_3TQ|mcp2515.PRSEG_1TQ)
+	mcp2515.write(mcp2515.CNF3,mcp2515.PHSEG2_3TQ)
+	mcp2515.write(mcp2515.RXB0SIDH,0x00)--清空接收缓冲器0的标准标识符高位
+	mcp2515.write(mcp2515.RXB0SIDL,0x00)--清空接收缓冲器0的标准标识符低位
+    mcp2515.write(mcp2515.RXB0EID8,0x00)--清空接收缓冲器0的拓展标识符高位
+	mcp2515.write(mcp2515.RXB0EID0,0x00)--清空接收缓冲器0的拓展标识符低位
+	mcp2515.write(mcp2515.CANINTF,0x00)--清空CAN中断标志寄存器的所有位(必须由MCU清空)
+	mcp2515.write(mcp2515.CANINTE,0x01)--配置CAN中断使能寄存器的接收缓冲器0满中断使能,其它位禁止中断
+    mcp2515.mode(mcp2515.REQOP_NORMAL)--进入正常模式
+    return true
+end
+
+return mcp2515

+ 96 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/mlx90614.lua

@@ -0,0 +1,96 @@
+--[[
+@module mlx90614
+@summary mlx90614 红外温度
+@version 1.0
+@date    2023.01.12
+@author  Dozingfiretruck
+@usage
+-- 用法实例
+local mlx90614 = require "mlx90614"
+
+
+sys.taskInit(function()
+    -- 硬件i2c方式 618不支持此方式因为618发送会发送停止信号
+    i2cid = 0
+    i2c_speed = i2c.SLOW
+    print("i2c",i2c.setup(i2cid,i2c_speed)) 
+
+    -- 软件i2c 此方式通用,需要 2023.5.8之后编译的固件
+    --i2cid = i2c.createSoft(18,19)
+
+    mlx90614.init(i2cid)
+    while 1 do
+        print("mlx90614 ambient",mlx90614.ambient()) 
+        print("mlx90614 object",mlx90614.object()) 
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local mlx90614 = {}
+local sys = require "sys"
+local i2cid
+
+local MLX90614_ADDRESS_ADR          =   0x5A
+
+local MLX90614_TA                   =   0x06
+local MLX90614_TOBJ1                =   0x07
+local MLX90614_TOBJ2                =   0x08
+
+--[[
+mlx90614 初始化
+@api mlx90614.init(i2c_id)
+@number 所在的i2c总线硬件/软件id
+@return bool   成功返回true
+@usage
+mlx90614.init(0)
+]]
+function mlx90614.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(20)--20 毫秒等待设备稳定
+end
+
+local rxbuff = zbuff.create(3)
+
+--[[
+获取 mlx90614 环境温度
+@api mlx90614.ambient()
+@return table mlx90614 环境温度
+@usage
+local mlx90614_ambient = mlx90614.ambient()
+log.info("mlx90614_ambient", mlx90614_ambient)
+]]
+function mlx90614.ambient()
+    local ambient
+    rxbuff:del()
+    local ret = i2c.transfer(i2cid, MLX90614_ADDRESS_ADR, MLX90614_TA, rxbuff, 3)
+    if ret then
+        ambient = rxbuff[0] | (rxbuff[1] << 8)
+        ambient = ambient*0.02 - 273.15
+    end
+    return ambient
+end
+
+--[[
+获取 mlx90614 环境温度
+@api mlx90614.ambient()
+@return table mlx90614 环境温度
+@usage
+local mlx90614_ambient = mlx90614.ambient()
+log.info("mlx90614_ambient", mlx90614_ambient)
+]]
+function mlx90614.object()
+    local object
+    rxbuff:del()
+    local ret = i2c.transfer(i2cid, MLX90614_ADDRESS_ADR, MLX90614_TOBJ1, rxbuff, 3)
+    if ret then
+        object = rxbuff[0] | (rxbuff[1] << 8)
+        object = object*0.02 - 273.15
+    end
+    return object
+end
+
+return mlx90614
+
+

+ 267 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/modbus_rtu.lua

@@ -0,0 +1,267 @@
+--[[
+@module modbus_rtu
+@summary modbus_rtu MODBUS_RTU协议 
+@version 1.0
+@date    2025.01.10
+@author  HH
+@usage
+--注意:
+--注意:
+-- 用法实例
+local modbus_rtu = require "modbus_rtu"
+
+-- 初始化modbus_rtu
+modbus_rtu.init({
+    uartid = 1, -- 接收/发送数据的串口id
+    baudrate = 4800, -- 波特率
+    gpio_485 = 25, -- 转向GPIO编号
+    tx_delay = 50000 -- 转向延迟时间,单位us
+
+-- 定义modbus_rtu数据接收回调
+local function on_modbus_rtu_receive(frame)
+    log.info("modbus_rtu frame received:", json.encode(frame))
+    if frame.fun == 0x03 then -- 功能码0x03表示读取保持寄存器
+        local byte = frame.byte
+        local payload = frame.payload
+        -- log.info("modbus_rtu payload (hex):", payload:toHex())
+
+        -- 解析数据(假设数据为16位寄存器值)
+        local values_big = {} -- 大端序解析结果
+        for i = 1, #payload, 2 do
+            local msb = payload:byte(i)
+            local lsb = payload:byte(i + 1)
+
+            -- 大端序解析
+            local result_big = (msb * 256) + lsb
+            table.insert(values_big, result_big)
+        end
+
+        -- 输出大端序的解析结果
+        log.info("输出大端序的解析结果:", table.concat(values_big, ", "))
+
+        -- 第一个寄存器是湿度,第二个是温度,除以10以获取实际值
+        if #values_big == 2 then
+            log.info("测试同款485温湿度计")
+            local humidity = values_big[1] / 10
+            local temperature = values_big[2] / 10
+
+            -- 打印湿度和温度
+            log.info(string.format("湿度: %.1f%%", humidity))
+            log.info(string.format("温度: %.1f°C", temperature))
+
+        else
+            log.info("用户自己的485下位机,共有" .. #values_big .. "组数据")
+            for index, value in ipairs(values_big) do
+                log.info(string.format("寄存器 %d: %d (大端序)", index, value))
+            end
+
+        end
+    else
+        log.info("功能码不是03")
+    end
+end
+
+-- 设置modbus_rtu数据接收回调
+modbus_rtu.set_receive_callback(1, on_modbus_rtu_receive)
+
+local function send_modbus_rtu_command()
+    local addr = 0x01 -- 设备地址,此处填客户自己的
+    local fun = 0x03 -- 功能码(03为读取保持寄存器),此处填客户自己的
+    local data = string.char(0x00, 0x00, 0x00, 0x02) -- 起始地址和寄存器数量(此处填客户自己的起始地址进而寄存器数量)
+
+    -- modbus_rtu.send_command(1, addr, fun, data) -- 只发送一次命令并等待响应处理
+    modbus_rtu.send_command(1, addr, fun, data, 5000) -- 循环5S发送一次
+
+end
+
+sys.taskInit(function()
+    sys.wait(5000)
+    send_modbus_rtu_command()
+
+end)
+
+]]
+
+
+
+
+local modbus_rtu = {}
+
+-- 默认配置
+local DEFAULT_CONFIG = {
+    uartid = 1,          -- 串口ID
+    baudrate = 4800,     -- 波特率
+    databits = 8,        -- 数据位
+    stopbits = 1,        -- 停止位
+    parity = uart.None,  -- 校验位
+    endianness = uart.LSB, -- 字节序
+    buffer_size = 1024,  -- 缓冲区大小
+    gpio_485 = 25,       -- 485转向GPIO
+    rx_level = 0,        -- 485模式下RX的GPIO电平
+    tx_delay = 10000,    -- 485模式下TX向RX转换的延迟时间(us)
+}
+
+--[[
+modbus_rtu初始化
+@api modbus_rtu.init(config)
+@number 
+config为table,table里元素如下,用户根据自己实际情况修改对应参数,如果某个参数缺失会自动补充默认值,默认值为下
+ config= {
+    uartid = 1,          -- 串口ID
+    baudrate = 4800,     -- 波特率
+    databits = 8,        -- 数据位
+    stopbits = 1,        -- 停止位
+    parity = uart.None,  -- 校验位
+    endianness = uart.LSB, -- 字节序
+    buffer_size = 1024,  -- 缓冲区大小
+    gpio_485 = 25,       -- 485转向GPIO
+    rx_level = 0,        -- 485模式下RX的GPIO电平
+    tx_delay = 10000,    -- 485模式下TX向RX转换的延迟时间(us)
+}
+
+@table 485转串口设置 
+@return 无
+@usage
+modbus_rtu.init(config)
+]]
+
+-- 初始化modbus_rtu
+function modbus_rtu.init(config)
+    config = config or {}
+     -- 遍历 DEFAULT_CONFIG,为缺省的参数赋值
+     for key, default_value in pairs(DEFAULT_CONFIG) do
+        if config[key] == nil then
+            config[key] = default_value
+        end
+    end
+    -- 初始化UART
+    uart.setup(
+        config.uartid, config.baudrate, config.databits, config.stopbits,
+        config.parity, config.endianness, config.buffer_size,
+        config.gpio_485, config.rx_level, config.tx_delay
+    )
+    log.info("modbus_rtu 当前串口初始化配置为:", json.encode(config))
+end
+
+--[[
+对数据进行CRC16_RTU校验
+@api modbus_rtu.crc16()
+@return int 原始数据对应的CRC16值
+
+@usage
+ local crc16_data = modbus_rtu.crc16("01024B")
+log.info("crc16_data", crc16_data)
+]]
+
+function modbus_rtu.crc16(data)
+    local crc16_data = crypto.crc16_modbus(data)
+    -- log.info("crc16end = ",crc16_data)
+    return crc16_data
+end
+
+
+--[[
+对下位机返回过来的数据进行modbus_rtu解析
+@api  modbus_rtu.parse_frame()
+@number 下位机返回的数据(一般是hex的)
+@return 成功返回table(地址码,功能码,有效字节数 ,真实数据值,crc校验值)失败返回nil和"CRC error"
+@usage
+ local crc16_data = modbus_rtu.parse_frame("01030401E6FF9F1BA0")
+log.info("crc16_data", crc16_data.addr,crc16_data.fun,crc16_data.byte,crc16_data.payload,crc16_data.crc)
+]]
+
+function modbus_rtu.parse_frame(data)
+    local str = data or ""
+    local addr = str:byte(1) or"" -- 地址位
+    local fun = str:byte(2) or ""  -- 功能码
+    local byte = str:byte(3)or "" -- 有效字节数
+    local payload = str:sub(4, 4 + byte - 1)or"" -- 数据部分(根据有效字节数动态截取)
+    local crc_data = str:sub(-2, -1) or ""
+    local idx,crc = pack.unpack(crc_data, "H")  -- CRC校验值
+
+    -- 校验CRC
+    if crc == modbus_rtu.crc16(str:sub(1, -3)) then
+        log.info("modbus_rtu CRC校验成功")
+        return {
+            addr = addr,
+            fun = fun,
+            byte = byte,
+            payload = payload,
+            crc = crc,
+        }
+    else
+        log.info("modbus_rtu CRC校验失败",crc)
+        return nil, "CRC error"
+    end
+end
+
+-- 确保build_frame使用正确的CRC和帧结构
+function modbus_rtu.build_frame(addr, fun, data)
+    local frame = string.char(addr, fun) .. data
+    local crc = modbus_rtu.crc16(frame)
+    -- log.info("CRC部分为",crc:toHex())
+    local pack_crc = pack.pack("H", crc)
+    -- log.info("pack后CRC为",aaa:toHex())
+    return frame .. pack_crc
+end
+
+--[[
+对发送给下位机的数据进行校验和发送次数的设置
+@api  modbus_rtu.send_command(uartid, addr, fun, data, interval)
+@number uartid 485转串口对应的串口id,main_uart为1,aux_uart为2
+@number addr 发送给下位机命令里的地址码
+@number fun 发送给下位机命令里的功能码
+@number data 发送给下位机命令里的有效字节数和命令码
+@number interval(可选) 为nil时命令只发一次,为数字时时间隔发送命令的秒数
+@return 成功返回table(地址码,功能码,有效字节数 ,真实数据值,crc校验值)失败返回nil和"CRC error"
+@usage
+ local crc16_data = modbus_rtu.parse_frame("01030401E6FF9F1BA0")
+log.info("crc16_data", crc16_data.addr,crc16_data.fun,crc16_data.byte,crc16_data.payload,crc16_data.crc)
+]]
+
+-- 发送modbus_rtu命令
+function modbus_rtu.send_command(uartid, addr, fun, data, interval)
+    local cmd = modbus_rtu.build_frame(addr, fun, data)
+    if interval then
+        -- 如果传入了interval,则启用循环发送
+        sys.timerLoopStart(function ()
+            -- log.info("每隔"..interval.."秒发一次指令",cmd:toHex())
+            uart.write(uartid,cmd)
+        end,interval)
+        -- sys.timerLoopStart(uart.write, interval, uartid, cmd)
+        -- log.info("modbus_rtu 循环发送的间隔时间为", interval, "ms",cmd:toHex())
+    else
+        -- 否则只发送一次
+        uart.write(uartid, cmd)
+        -- log.info("modbus_rtu 只发送一次", cmd:toHex())
+    end
+end
+
+-- 设置modbus_rtu数据接收回调
+function modbus_rtu.set_receive_callback(uartid, callback)
+    uart.on(uartid, "receive", function(id, len)
+        local s = ""
+        repeat
+            s = uart.read(id, 128)
+            if #s > 0 then
+                log.info("modbus_rtu 收到的下位机回复:", s:toHex())
+                local frame, err = modbus_rtu.parse_frame(s)
+                if frame then
+                    callback(frame)
+                else
+                    log.info("modbus_rtu 数据错误", err)
+                end
+            end
+        until s == ""
+    end)
+end
+
+-- 设置modbus_rtu数据发送回调
+function modbus_rtu.set_sent_callback(uartid, callback)
+    uart.on(uartid, "sent", function(id)
+        log.info("modbus_rtu 数据发送:", id)
+        if callback then callback(id) end
+    end)
+end
+
+return modbus_rtu

+ 251 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/mpu6xxx.lua

@@ -0,0 +1,251 @@
+--[[
+@module mpu6xxx
+@summary mpu6xxx 六轴/九轴传感器 支持 mpu6500,mpu6050,mpu9250,icm2068g,icm20608d
+@version 1.0
+@date    2022.03.10
+@author  Dozingfiretruck
+@usage
+--支持mpu6500,mpu6050,mpu9250,icm2068g,icm20608d,自动判断器件id,只需要配置i2c id就可以
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local mpu6xxx = require "mpu6xxx"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    mpu6xxx.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        sys.wait(100)
+        local temp = mpu6xxx.get_temp()--获取温度
+        log.info("6050temp", temp)
+        local accel = mpu6xxx.get_accel()--获取加速度
+        log.info("6050accel", "accel.x",accel.x,"accel.y",accel.y,"accel.z",accel.z)
+        local gyro = mpu6xxx.get_gyro()--获取陀螺仪
+        log.info("6050gyro", "gyro.x",gyro.x,"gyro.y",gyro.y,"gyro.z",gyro.z)
+    end
+end)
+]]
+
+
+local mpu6xxx = {}
+local sys = require "sys"
+local i2cid
+local i2cslaveaddr
+local deviceid
+
+local MPU6XXX_ADDRESS_AD0_LOW     =   0x68 -- address pin low (GND), default for InvenSense evaluation board
+local MPU6XXX_ADDRESS_AD0_HIGH    =   0x69 -- address pin high (VCC)
+
+---器件通讯地址
+local MPU6050_WHO_AM_I            =   0x68 -- mpu6050
+local MPU6500_WHO_AM_I            =   0x70 -- mpu6500
+local MPU9250_WHO_AM_I            =   0x71 -- mpu9250
+local ICM20608G_WHO_AM_I          =   0xAF -- icm20608G
+local ICM20608D_WHO_AM_I          =   0xAE -- icm20608D
+
+---MPU6XXX所用地址
+local MPU6XXX_RA_ACCEL_XOUT_H     =   0x3B
+local MPU6XXX_RA_ACCEL_XOUT_L     =   0x3C
+local MPU6XXX_RA_ACCEL_YOUT_H     =   0x3D
+local MPU6XXX_RA_ACCEL_YOUT_L     =   0x3E
+local MPU6XXX_RA_ACCEL_ZOUT_H     =   0x3F
+local MPU6XXX_RA_ACCEL_ZOUT_L     =   0x40
+local MPU6XXX_RA_TEMP_OUT_H       =   0x41
+local MPU6XXX_RA_TEMP_OUT_L       =   0x42
+local MPU6XXX_RA_GYRO_XOUT_H      =   0x43
+local MPU6XXX_RA_GYRO_XOUT_L      =   0x44
+local MPU6XXX_RA_GYRO_YOUT_H      =   0x45
+local MPU6XXX_RA_GYRO_YOUT_L      =   0x46
+local MPU6XXX_RA_GYRO_ZOUT_H      =   0x47
+local MPU6XXX_RA_GYRO_ZOUT_L      =   0x48
+
+local MPU6XXX_ACCEL_SEN           =   16384
+local MPU6XXX_GYRO_SEN            =   16384
+
+local MPU60X0_TEMP_SEN            =   340
+local MPU60X0_TEMP_OFFSET         =   36.5
+
+local MPU6500_TEMP_SEN            =   333.87
+local MPU6500_TEMP_OFFSET         =   21
+
+local MPU6XXX_RA_PWR_MGMT_1     =   0x6B	--电源管理,典型值:0x00(正常启用)
+local MPU6XXX_RA_SMPLRT_DIV		=   0x19	--陀螺仪采样率,典型值:0x07(125Hz)
+local MPU6XXX_RA_CONFIG			=   0x1A	--低通滤波频率,典型值:0x06(5Hz)
+local MPU6XXX_RA_GYRO_CONFIG	=   0x1B	--陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
+local MPU6XXX_RA_ACCEL_CONFIG	=   0x1C	--加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
+local MPU6XXX_RA_FIFO_EN        =   0x23    --fifo使能
+local MPU6XXX_RA_INT_PIN_CFG    =   0x37    --int引脚有效电平
+local MPU6XXX_RA_INT_ENABLE     =   0x38    --中断使能
+local MPU6XXX_RA_USER_CTRL      =   0x6A
+local MPU6XXX_RA_PWR_MGMT_1     =   0x6B
+local MPU6XXX_RA_PWR_MGMT_2     =   0x6C
+local MPU6XXX_RA_WHO_AM_I       =   0x75
+--器件ID检测
+local function mpu6xxx_check()
+    i2c.send(i2cid, MPU6XXX_ADDRESS_AD0_LOW, MPU6XXX_RA_WHO_AM_I)--读器件地址
+    sys.wait(50)
+    local revData = i2c.recv(i2cid, MPU6XXX_ADDRESS_AD0_LOW, 1)
+    if revData:byte() ~= nil then
+        i2cslaveaddr = MPU6XXX_ADDRESS_AD0_LOW
+    else
+        i2c.send(i2cid, MPU6XXX_ADDRESS_AD0_HIGH, MPU6XXX_RA_WHO_AM_I)--读器件地址
+        sys.wait(50)
+        local revData = i2c.recv(i2cid, MPU6XXX_ADDRESS_AD0_HIGH, 1)
+        if revData:byte() ~= nil then
+            i2cslaveaddr = MPU6XXX_ADDRESS_AD0_HIGH
+        else
+            log.info("i2c", "Can't find device")
+            return false
+        end
+    end
+    i2c.send(i2cid, i2cslaveaddr, MPU6XXX_RA_WHO_AM_I)--读器件地址
+    sys.wait(50)
+    local revData = i2c.recv(i2cid, i2cslaveaddr, 1)
+    log.info("Device i2c address is:", revData:toHex())
+    if revData:byte() == MPU6050_WHO_AM_I then
+        deviceid = MPU6050_WHO_AM_I
+        log.info("Device i2c id is: MPU6050")
+    elseif revData:byte() == MPU6500_WHO_AM_I then
+        deviceid = MPU6500_WHO_AM_I
+        log.info("Device i2c id is: MPU6500")
+    elseif revData:byte() == MPU9250_WHO_AM_I then
+        deviceid = MPU9250_WHO_AM_I
+        log.info("Device i2c id is: MPU9250")
+    elseif revData:byte() == ICM20608G_WHO_AM_I then
+        deviceid = ICM20608G_WHO_AM_I
+        log.info("Device i2c id is: ICM20608G")
+    elseif revData:byte() == ICM20608D_WHO_AM_I then
+        deviceid = ICM20608D_WHO_AM_I
+        log.info("Device i2c id is: ICM20608D")
+    else
+        log.info("i2c", "Can't find device")
+        return false
+    end
+    return true
+end
+
+--[[
+mpu6xxx初始化
+@api mpu6xxx.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+mpu6xxx.init(0)
+]]
+function mpu6xxx.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(20)
+    if mpu6xxx_check() then
+        i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_PWR_MGMT_1, 0x80})--复位
+        sys.wait(100)
+        i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_PWR_MGMT_1, 0x00})--唤醒
+        sys.wait(100)
+        i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_SMPLRT_DIV, 0x07})--陀螺仪采样率,典型值:0x07(125Hz)
+        i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_CONFIG, 0x06})--低通滤波频率,典型值:0x06(5Hz)
+        i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_GYRO_CONFIG, 0x18})--陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
+        i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_ACCEL_CONFIG, 0x01})--加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
+        --i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_FIFO_EN, 0x00})--关闭fifo
+        --i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_INT_ENABLE, 0x00})--关闭所有中断
+        --i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_USER_CTRL, 0x00})--I2C主模式关闭
+        i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_PWR_MGMT_1, 0x01})--设置x轴的pll为参考
+        i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_PWR_MGMT_2, 0x00})--加速度计与陀螺仪开启
+        if deviceid == MPU9250_WHO_AM_I then
+            i2c.send(i2cid, i2cslaveaddr, {MPU6XXX_RA_INT_PIN_CFG, 0x02})--开启直通模式
+        end
+        log.info("mpu6xxx init_ok")
+        return true
+    end
+    return false
+end
+--获取温度的原始数据
+local function mpu6xxx_get_temp_raw()
+    i2c.send(i2cid, i2cslaveaddr,MPU6XXX_RA_TEMP_OUT_H)--获取的地址
+    local buffer = i2c.recv(i2cid, i2cslaveaddr, 2)--获取2字节
+    local _, temp = pack.unpack(buffer, ">h")
+    return temp or 0
+end
+--获取加速度计的原始数据
+local function mpu6xxx_get_accel_raw()
+    local accel={x=nil,y=nil,z=nil}
+    i2c.send(i2cid, i2cslaveaddr,MPU6XXX_RA_ACCEL_XOUT_H)--获取的地址
+    local x = i2c.recv(i2cid, i2cslaveaddr, 2)--获取6字节
+    _,accel.x = pack.unpack(x,">h")
+    i2c.send(i2cid, i2cslaveaddr,MPU6XXX_RA_ACCEL_YOUT_H)--获取的地址
+    local y = i2c.recv(i2cid, i2cslaveaddr, 2)--获取6字节
+    _,accel.y = pack.unpack(y,">h")
+    i2c.send(i2cid, i2cslaveaddr,MPU6XXX_RA_ACCEL_ZOUT_H)--获取的地址
+    local z = i2c.recv(i2cid, i2cslaveaddr, 2)--获取6字节
+    _,accel.z = pack.unpack(z,">h")
+    return accel or 0
+end
+--获取陀螺仪的原始数据
+local function mpu6xxx_get_gyro_raw()
+    local gyro={x=nil,y=nil,z=nil}
+    i2c.send(i2cid, i2cslaveaddr,MPU6XXX_RA_GYRO_XOUT_H)--获取的地址
+    local x = i2c.recv(i2cid, i2cslaveaddr, 2)--获取6字节
+    _,gyro.x = pack.unpack(x,">h")
+    i2c.send(i2cid, i2cslaveaddr,MPU6XXX_RA_GYRO_YOUT_H)--获取的地址
+    local y = i2c.recv(i2cid, i2cslaveaddr, 2)--获取6字节
+    _,gyro.y = pack.unpack(y,">h")
+    i2c.send(i2cid, i2cslaveaddr,MPU6XXX_RA_GYRO_ZOUT_H)--获取的地址
+    local z = i2c.recv(i2cid, i2cslaveaddr, 2)--获取6字节
+    _,gyro.z = pack.unpack(z,">h")
+    return gyro or 0
+end
+
+--[[
+获取温度数据
+@api mpu6xxx.get_temp()
+@return number 温度数据
+@usage
+local temp = mpu6xxx.get_temp()--获取温度
+log.info("6050temp", temp)
+]]
+function mpu6xxx.get_temp()
+    local temp=nil
+    local tmp = mpu6xxx_get_temp_raw()
+    if deviceid == MPU6050_WHO_AM_I then
+        temp = tmp / MPU60X0_TEMP_SEN + MPU60X0_TEMP_OFFSET
+    else
+        temp = tmp / MPU6500_TEMP_SEN + MPU6500_TEMP_OFFSET
+    end
+    return temp
+end
+
+--[[
+获取加速度计的数据,单位: mg
+@api mpu6xxx.get_accel()
+@return table 加速度数据
+@usage
+local accel = mpu6xxx.get_accel()--获取加速度
+log.info("6050accel", "accel.x",accel.x,"accel.y",accel.y,"accel.z",accel.z)
+]]
+function mpu6xxx.get_accel()
+    local accel={x=nil,y=nil,z=nil}
+    local tmp = mpu6xxx_get_accel_raw()
+    accel.x = tmp.x*1000/MPU6XXX_ACCEL_SEN
+    accel.y = tmp.y*1000/MPU6XXX_ACCEL_SEN
+    accel.z = tmp.z*1000/MPU6XXX_ACCEL_SEN
+    return accel
+end
+
+--[[
+获取陀螺仪的数据,单位: deg / 10s
+@api mpu6xxx.get_gyro()
+@return table 陀螺仪数据
+@usage
+local gyro = mpu6xxx.get_gyro()--获取陀螺仪
+log.info("6050gyro", "gyro.x",gyro.x,"gyro.y",gyro.y,"gyro.z",gyro.z)
+]]
+function mpu6xxx.get_gyro()
+    local gyro={x=nil,y=nil,z=nil}
+    local tmp = mpu6xxx_get_gyro_raw()
+    gyro.x = tmp.x*100/MPU6XXX_GYRO_SEN
+    gyro.y = tmp.y*100/MPU6XXX_GYRO_SEN
+    gyro.z = tmp.z*100/MPU6XXX_GYRO_SEN
+    return gyro
+end
+
+return mpu6xxx
+
+

+ 306 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/necir.lua

@@ -0,0 +1,306 @@
+--[[
+@module necir
+@summary necir NEC协议红外接收
+@version 3.5
+@date    2023.09.03
+@author  lulipro
+@usage
+--注意:
+--1、本库在Air101和Air103测试通过,Air105由于SPI传输帧与帧之间存在间隔因此暂不支持。
+--2、本库实现了NEC红外数据接收,发送请使用LuatOS底层固件自带的ir.sendNEC()函数。
+--3、由于本库基于标准四线SPI接口实现,所以虽然只用到了MISO引脚,但是其他3个SPI相关引脚在使用期间
+--  无法作为其他用途,除非执行necir.close()并等待necir.isClosed()为true后,SPI将完全释放,才可以用于其他用途。
+--硬件模块:VS1838及其兼容的一体化接收头
+--接线示意图:
+--1、支持单IO模式,即仅使用一个SPI_MISO引脚,此时necir.init的irq_pin参数必须是SPI_MISO所在引脚。
+--2、支持多从机,如果SPI总线上需要挂接其他从设备,为了在不使用红外通信期间,避免VS1838干扰总线的MISO,则可以用
+--  一个NMOS(例如AO3400)控制VS1838的电源地,如下所示,GPIO充当VS1838的片选信号,当GPIO输出低
+--  则禁用VS1838,此时VS1838不工作,其OUT引脚为高阻,不会干扰MISO;当GPIO输出高,则启用VS1838,
+--  此时其OUT引脚会输出红外通信信号到单片机。由于一般的SPI从机片选逻辑为低使能,因此这样可以用
+--  同一个片选GPIO来控制VS1838以及另一个SPI从机,因为片选逻辑是相反的。配合necir库的necir.close()
+--  和necir.isClosed()可以最大化的复用SPI接口,避免SPI的独占而浪费。
+--  ____________________                ____________________
+-- |                    |    单IO      |                    |
+-- |           SPI_MISO |--------------| OUT                |
+-- | Air10x             |              |       VS1838       |
+-- |                    |              |     一体化接收头    |
+-- |               GPIO |----      ----| GND                |
+-- |____________________|   |      |   |____________________| 
+--                          |      |
+--                          |  ____|________ 
+--                          | |    D        |
+--                          --| G      NMOS | 
+--                            |____S________|
+--                                 |
+--                                GND
+--用法实例:演示用同一个SPI接口驱动VS1838和W25QXX
+--用法实例:
+local necir = require("necir")
+
+--定义用户回调函数
+local function my_ir_cb(frameTab)
+    log.info('get ir msg','addr=',frameTab[1],frameTab[2],'data=',frameTab[3])
+end
+
+sys.taskInit(function()
+    local CS = gpio.setup(pin.PA07,0)  --VS1838(NMOS控制其GND)与W25QXX共用的片选引脚
+    necir.init(spi.SPI_0,pin.PB03,my_ir_cb)
+
+    while 1 do
+        --===============================
+        log.info('------necir start------')
+        CS(1)     --使能VS1838
+        necir.start()  --开启necir数据接收过程
+        sys.wait(10000)
+        log.info('necir request to close')
+        necir.close()   --请求关闭necir
+        while not (necir.isClosed()) do
+            sys.wait(200)
+        end
+        CS(0)    --除能VS1838
+        log.info('necir closed')
+        sys.wait(1000)
+
+        --===============================
+        log.info('------setup to read w25qxx chip id------')
+        spi.setup(spi.SPI_0,nil,
+            0,--CPHA
+            0,--CPOL
+            8,--数据宽度
+            100000,--频率
+            spi.MSB,--高低位顺序  
+            spi.master,--主模式
+            spi.full--全双工
+        )
+        --读取W25QXX chi id,0XEF15,表示芯片型号为W25Q32,0XEF16,表示芯片型号为W25Q64
+        CS(0)   --片选W25QXX
+        spi.send(spi.SPI_0,string.char(0x90)..string.char(0x00)..string.char(0x00)..string.char(0x00))
+        local chip_id = spi.recv(spi.SPI_0,2)
+        log.info('w25qxx id=',chip_id:toHex())
+        CS(1)   --取消片选W25QXX
+        sys.wait(1000)
+    end
+end)
+]]
+
+local necir = {}
+
+local sys = require "sys"
+
+local NECIR_IRQ_PIN               --上升沿中断检测引脚
+local NECIR_SPI_ID                --使用的SPI接口的ID
+local NECIR_SPI_BAUDRATE       = (14222*4)        --SPI时钟频率,单位HZ
+local NECIR_SPI_RECV_BUFF_LEN  = (32+192+192)     --SPI接收数据的长度  
+
+local recvBuff              --SPI接收数据缓冲区
+local recvNECFrame={}       --依次存储:地址码,地址码取反,数据码,数据码取反
+
+local recvCallback          --NEC报文接收成功后的用户回调函数
+local isNeedTaskFlag        --接收任务是否需要运行的标志
+local isClosedFlag          --necir是否已经完全关闭的标志
+--[[
+==============实现原理================================================
+NEC协议中无论是引导信号,逻辑0还是逻辑1,都由若干个562.5us的周期组成。
+例如
+  引导信号: 16个562.5us的低电平+8个562.5us的高电平组成  
+  逻 辑 1 :1个562.5us的低电平+3个562.5us的高电平组成 
+  逻 辑 0 :1个562.5us的低电平+1个562.5us的高电平组成
+
+采样定理告诉我们,一切数字信号,都可以通过高倍速采样还原。
+我们使用SPI的MISO引脚对红外接收管的输出进行【连续采样】。
+我们使用4个SPI字节去采样一个562.5us的红外信号周期,因此SPI的时钟频率设置为 (14222*4) Hz
+则
+  引导信号: 16*4个0x00 + 8*4个0xff组成
+  逻 辑 1 :1*4个0x00 + 3*4个0xff组成,共16字节
+  逻 辑 0 :1*4个0x00 + 1*4个0xff组成 ,共8字节
+
+NEC的引导信号由一段低电平+一段高电平组成,为了降低采样深度,避免空间占用,我们选择
+从后面的高电平产生的上升沿开始进行SPI接收采样 而不是从第一个下降沿就开始。
+
+确定采样深度。NEC协议中,地址码和数据码都是连续传输2次,连续的2个字节是相互取反的关系,
+因此这2个字节的总的传输时间是固定,因为前一个字节的某个位是1,则必定后一个字节对应位是0,
+则总传输时间就是 = (逻辑1传输时间+逻辑0传输时间)* 8,
+则(地址码+ 地址码取反) 和 (数据码+数据码取反)的采样深度都是 (16+8)*8 = 192字节。
+引导码高电平部分则是8*4字节。这样我们就确定了SPI的总传输字节数= 32 + 192 + 192=416字节。
+
+理想情况下中断检测到引导码产生的上升沿就会立刻开始SPI数据采集,这样SPI传输 416 字节
+能刚好采集引导码的4.5ms高电平加后面的4字节数据,但由于LuatOS中断响应存在一定延迟,导致真正开始
+SPI采集会在引导码上升沿开始后的若干个毫秒延迟后才开始,经过测试一般延迟在1ms以内,但具体延迟取决于
+LuatOS的行为,只要这个延迟能让SPI采集到完整的引导码后面的4字节数据就都是可以接受的,即延迟不能超过
+4.5ms,一般这个情况是能满足的。
+
+本方法需要的SPI频率为56888Hz,而Air101和Air103的SPI频率 = 40MHz/(2*(div_REG+1)),
+则div_REG = 350即可实现。如果芯片的SPI无法配置频率也将不支持此方案。
+
+考虑到LuatOS中断响应存在一定延迟,导致SPI接收的信号与实际输出的信号存在一定的
+滞后导致字节错位。因此对接收到的SPI字节数据 依次向后 采用16字节长度的窗口切出子串,
+对这个子串进行模式匹配,来确定当前片段对应是NEC的逻辑1还是逻辑0。
+即:
+  在当前位置开始的后面的16个字节形成的子串中,如果存在连续的9个0xff,
+  则认为当前位置是逻辑1,否则认为是逻辑0。
+  为什什么是9个,考虑下面的情形:
+        
+逻辑1: 0x00 0x00 0x00 0x00  0xff 0xff 0xff 0xff ... 0xff 
+       [     4  个       ]  [         12 个            ]
+                           
+逻辑0: 0x00 0x00 0x00 0x00  0xff 0xff 0xff 0xff  0x00 0x00 0x00 0x00 0xff 0xff 0xff 0xff
+       [     4  个       ]  [       4 个      ]  [    下一个逻辑位(部分)               ]
+
+可以发现,如果是逻辑0,则连续的16个字节窗口中最多出现4个连续的0xff,因此可以作为区分。
+lua中的string.find()可以方便的实现字符串模式匹配,因此代码实现很简单。
+]]
+
+
+--从收到的SPI数据中解析出NEC报文
+local function parseRecvData()
+    local fs,fe
+    local si
+
+    --数据重新初始化
+    recvNECFrame[1] = 0
+    recvNECFrame[2] = 0
+    recvNECFrame[3] = 0
+    recvNECFrame[4] = 0
+
+    --尝试找到第一个不是0xff出现的位置,用于跳过引导信号字节,这部分最多32字节
+    si=1
+    while si <= 32+5 do
+        if 0xff ~= string.byte(string.sub(recvBuff,si,si)) then
+            break
+        end
+        si = si + 1
+    end
+    
+    --32+5个0xff后依然没有出现地址数据,属于不正常的信号,直接退出
+    if 0xff == string.byte(string.sub(recvBuff,si,si)) then
+        return
+    end
+
+    --遍历引导信号后续的信号,整个收到的SPI数据进行NEC数据解析还原
+    while si<=NECIR_SPI_RECV_BUFF_LEN do
+
+        for k = 1, 4, 1 do  --解析出NEC报文的4个字节
+            for i = 0, 7, 1 do  --每个字节8位
+                --在当前位置开始的后面的16个字节中,如果存在连续的9个0xff的值,
+                --则认为当前位置是逻辑1
+                fs,fe = string.find(
+                    string.sub(recvBuff,si,si+16-1),
+                    '\xff\xff\xff\xff\xff\xff\xff\xff\xff'
+                )
+                if fs and fe then --找到这种模式
+                    --这个bit为1(NEC协议大小端为LSB First)
+                    recvNECFrame[k] = (recvNECFrame[k] | (1<<i))
+                    si = si + 16
+                else  --没找到这种模式
+                    --这个bit为0
+                    si = si + 8
+                end
+            end  --每个字节8位
+        end --解析出NEC报文的4个字节
+
+        break  --成功解析出4个字节后跳出循环
+
+    end --遍历整个收到的SPI数据
+
+    --对收到的红外数据进行校验并调用 用户回调函数
+    --有的遥控的2个地址字节不是相互取反的关系,因此这里不对地址码校验
+    if ((recvNECFrame[3]+recvNECFrame[4]) == 255) then
+        --log.info('necir','DataValid,go CallBack')
+        if recvCallback then recvCallback(recvNECFrame) end
+    end
+    --log.info('necir',recvNECFrame[1],recvNECFrame[2],recvNECFrame[3],recvNECFrame[4])
+end
+
+
+--检测引导产生的上升沿的的中断函数
+local function irq_func()    
+    gpio.close(NECIR_IRQ_PIN)  --关闭GPIO功能,防止中断反复触发
+    spi.setup(NECIR_SPI_ID,nil,0,0,8,NECIR_SPI_BAUDRATE,spi.MSB,spi.master,spi.full)--重新打开SPI接口
+    
+    recvBuff =  spi.recv(NECIR_SPI_ID, NECIR_SPI_RECV_BUFF_LEN) --通过SPI接收红外接收头输出的解调数据
+    sys.publish('NECIR_SPI_DONE')  --发布消息,让任务对收到的SPI数据分析处理
+end
+
+
+local function recvTaskFunc()
+
+    while true do
+        sys.waitUntil('NECIR_START',5000)
+
+        while isNeedTaskFlag do
+            spi.close(NECIR_SPI_ID)  --关闭SPI接口在,这样才能把MISO空出来做中断检测
+            gpio.setup(NECIR_IRQ_PIN,irq_func,gpio.PULLUP ,gpio.RISING)--打开GPIO中断检测功能
+    
+            local result, _ = sys.waitUntil('NECIR_SPI_DONE',1000)
+            if result then  --SPI完成采集,开始解析数据
+                parseRecvData()
+            end
+        end
+        --关闭接收过程时做清理工作
+        if not isClosedFlag then
+            gpio.close(NECIR_IRQ_PIN)  --关闭GPIO功能
+            spi.close(NECIR_SPI_ID)   --关闭SPI接口
+            isClosedFlag = true
+            --log.info('necir','recv task closed')
+        end
+    end --task main loop
+end
+
+--[[
+necir初始化
+@api necir.init(spi_id,irq_pin,recv_cb)
+@number spi_id,使用的SPI接口的ID
+@number irq_pin,使用的中断引脚,在单IO模式下这个引脚必须是SPI的MISO引脚
+@function recv_cb,红外数据接收完成后的回调函数,回调函数有1个table类型参数,分别存储了地址码,地址码取反,数据码,数据码取反
+@usage
+local function my_ir_cb(frameTab)
+    log.info('get ir msg','addr=',frameTab[1],frameTab[2],'data=',frameTab[3])
+end
+
+necir.init(spi.SPI_0,pin.PB03,my_ir_cb)
+]]
+function necir.init(spi_id,irq_pin,recv_cb)
+    NECIR_SPI_ID     = spi_id
+    NECIR_IRQ_PIN    = irq_pin
+    recvCallback     = recv_cb
+
+    isNeedTaskFlag = false      --接收任务是否需要运行的标志
+    isClosedFlag   = true       --necir是否已经完全关闭的标志
+    --启动红外数据接收任务
+    sys.taskInit(recvTaskFunc)
+end
+
+--[[
+开启necir数据接收过程
+@api necir.start()
+@usage
+necir.start()
+]]
+function necir.start()
+    isNeedTaskFlag     = true
+    isClosedFlag       = false
+    sys.publish('NECIR_START')
+end
+
+--[[
+请求关闭necir数据接收过程。此函数执行后并不能保证立刻关闭,具体是否已经关闭需要使用necir.isClosed()来查询。
+@api necir.close()
+@usage
+necir.close()
+]]
+function necir.close()
+    isNeedTaskFlag = false
+end
+
+--[[
+判断necir是否已经完全关闭,关闭后所使用的SPI接口将释放,可以复用为其他功能。如需再次开启,则需要再次调用necir.start()
+@api necir.isClosed()
+@return bool   关闭成功返回true
+@usage
+necir.isClosed()
+]]
+function necir.isClosed()
+    return isClosedFlag
+end
+
+return necir

+ 180 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/onenetcoap.lua

@@ -0,0 +1,180 @@
+--[[
+@module onenetcoap
+@summary 中移动OneNet平台COAP接入
+@version 1.0
+@date    2023.11.17
+@author  wendal
+@demo    onenet/coap
+@tag LUAT_USE_NETWORK
+@usage
+-- 使用方法请查阅demo
+]]
+
+
+local onenet = {}
+local TAG = "onenet.coap"
+
+--[[
+初始化onenet coap库
+@onenet.setup(conf)
+@table 配置项信息,必须有
+@return boolean 成功返回true,否则返回nil
+@usage
+-- 配置信息是一个table,具体键值如下:
+-- product_id 产品id,字符串,必须有
+-- device_name 设备名称,字符串,必须有
+-- device_key 设备密钥,字符串,必须有
+-- host coap服务器地址,字符串,默认"183.230.102.122"
+-- port coap服务器端口,整数,默认5683
+-- auto_reply 自动回复thing获取,布尔值,默认关闭false
+-- thing 物模型,类型是table,默认 {params={}}
+-- debug 调试开关,默认关闭false
+-- adapter 适配器编号,默认是最后一个注册的适配器
+-- callback 收到合法coap数据时的用户回调
+]]
+function onenet.setup(conf)
+    if not conf then
+        return
+    end
+    if not conf.product_id then
+        log.error(TAG, "配置信息缺product_id")
+        return
+    end
+    if not conf.device_name then
+        log.error(TAG, "配置信息缺device_name")
+        return
+    end
+    if not conf.device_key then
+        log.error(TAG, "配置信息缺device_key")
+        return
+    end
+    onenet.product_id = conf.product_id
+    onenet.device_name = conf.device_name
+    -- log.info(">>", onenet.product_id, onenet.device_name, conf.device_key)
+    _, _, onenet.login_token = iotauth.onenet(onenet.product_id, onenet.device_name, conf.device_key, "sha1")
+    -- log.info("onenet.login_token", onenet.login_token)
+
+    onenet.host = conf.host or "183.230.102.122"
+    onenet.port = conf.port or 5683
+
+    onenet.auto_reply = conf.auto_reply
+    onenet.topic = conf.topic or "onenet_udp_inc"
+    onenet.thing = conf.thing or {
+        params = {}
+    }
+    onenet.debug = conf.debug
+    onenet.thing_id = 1
+    onenet.state = 0
+    onenet.adapter = conf.adapter
+    onenet.timeout = conf.timeout or 3000
+    onenet.rx_buff = zbuff.create(1500)
+    onenet.callback = conf.callback
+    return true
+end
+
+function onenet.netc_cb(sc, event)
+    -- log.info("udp", sc, string.format("%08X", event))
+    local rxbuff = onenet.rx_buff
+    if event == socket.EVENT then
+        local ok, len, remote_ip, remote_port = socket.rx(sc, rxbuff)
+        if ok then
+            local data = rxbuff:query()
+            rxbuff:del()
+            log.info(TAG, "读到数据", data:toHex())
+            ercoap.print(data)
+            local resp = ercoap.parse(data)
+            if resp and resp.code == 201 then
+                log.info(TAG, "login success", resp.code, resp.payload:toHex())
+                -- 这里非常重要, 获取其他请求所需要的token值
+                onenet.post_token = resp.payload
+                onenet.state = 2
+                sys.publish(onenet.topic)
+            end
+            if resp then
+                if onenet.callback then
+                    onenet.callback(resp)
+                else
+                    sys.publish(onenet.topic, resp)
+                end
+            end
+        else
+            log.info(TAG, "服务器断开了连接")
+            onenet.state = 0
+            sys.publish(onenet.topic)
+        end
+    elseif event == socket.TX_OK then
+        log.info(TAG, "上行完成")
+    elseif event == socket.ON_LINE then
+        log.info(TAG, "UDP已准备就绪,可以上行")
+        -- 上行登陆包
+        -- log.info("登陆参数", onenet.product_id, onenet.device_name, onenet.login_token)
+        local data = ercoap.onenet("login", onenet.product_id, onenet.device_name, onenet.login_token)
+        -- log.info("上行登陆包", data:toHex())
+        socket.tx(sc, data)
+    else
+        log.info(TAG, "其他事件", event)
+    end
+end
+
+function onenet.main_task()
+    onenet.state = 1
+    while onenet.state ~= 0 do
+        local ok = onenet.main_loop()
+        if not ok then
+            sys.wait(500)
+        end
+    end
+end
+
+function onenet.main_loop()
+    if onenet.netc == nil then
+        onenet.netc = socket.create(onenet.adapter, onenet.netc_cb)
+    end
+    if onenet.netc == nil then
+        log.info(TAG, "创建socket失败!!! 3秒后重试")
+        return
+    end
+    local netc = onenet.netc
+    if onenet.state ~= 2 then
+        socket.config(netc, nil, true)
+        if onenet.debug then
+            socket.debug(netc, true)
+        end
+        socket.connect(netc, onenet.host, onenet.port)
+        local result, resp = sys.waitUntil(onenet.topic, onenet.timeout)
+        if not result then
+            log.info(TAG, "等待底层连接成功超时了")
+            return
+        end
+    end
+    sys.waitUntil(onenet.topic, 3000)
+    return
+end
+
+function onenet.start()
+    if onenet.state ~= 0 then
+        log.info("onenet", "coap", "已经在启动状态,不需要再启动")
+        return
+    end
+    sys.taskInit(onenet.main_task)
+    return true
+end
+
+function onenet.uplink(tp, payload)
+    if not tp then
+        return
+    end
+    if payload and payload["id"] == nil then
+        payload["id"] = "1"
+    end
+    if type(payload) == "table" then
+        payload = json.encode(payload, "7f")
+    end
+    -- log.info("uplink", onenet.product_id, onenet.device_name, onenet.post_token:toHex(), payload)
+    local tmp = ercoap.onenet(tp, onenet.product_id, onenet.device_name, onenet.post_token, payload)
+    -- log.info("uplink", tp, tmp:toHex())
+    socket.tx(onenet.netc, tmp)
+    return true
+end
+
+return onenet

+ 160 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/openai.lua

@@ -0,0 +1,160 @@
+--[[
+@module openai
+@summary 对接OpenAI兼容的平台,例如deepseek
+@version 1.0
+@date    2025.1.27
+@author  wendal
+@tag LUAT_USE_NETWORK
+@demo openai
+@usage
+-- 对接deepseek演示 请阅demo/openai
+
+-- 本API正在积极设计中
+]] local openai = {
+    conf = {}
+}
+
+-- 定义默认参数表
+local defaultOpts = {
+    apikey = "123456",
+    apiurl = "https://api.deepseek.com",
+    model = "deepseek-chat"
+}
+
+--比较传入的table和默认table,如果传入的值里少了某个值,默认table顶上去
+local function mergeWithDefaults(opts, defaults)
+    -- 创建一个新的表来存储合并结果
+    local mergedOpts = {}
+    
+    -- 先加载默认值
+    for k, v in pairs(defaults) do
+        mergedOpts[k] = v
+    end
+    
+    -- 使用传入的 opts 表覆盖默认值
+    for k, v in pairs(opts) do
+        mergedOpts[k] = v
+    end
+
+    return mergedOpts
+end
+
+
+local function talk(self, msg)
+    local rheaders = {
+        ['Content-Type'] = "application/json",
+        ['Accept'] = "application/json",
+        ['Authorization'] = "Bearer " .. self.opts.apikey
+    }
+    if not msg then
+        return
+    end
+    if type(msg) == "table" then
+        table.insert(self.msgs, msg)
+    else
+        table.insert(self.msgs, {
+            role = "user",
+            content = msg
+        })
+    end
+
+    local rbody = {
+        model = self.opts.model,
+        messages = self.msgs,
+        stream = false
+    }
+    local url = self.opts.apiurl .. "/chat/completions"
+    -- log.info("openai", "request", url, json.encode(rheaders), json.encode(rbody))
+    local code, headers, body = http.request("POST", url, rheaders, (json.encode(rbody)), {
+        timeout = 60 * 1000
+    }).wait()
+    local tag = ""
+    -- log.info("openai", code, json.encode(headers) or "", body or "")
+    if code == 200 then
+        -- log.info("openai", "执行完成!!!")
+        local jdata = json.decode(body)
+        if jdata and jdata.choices and #jdata.choices > 0 then
+            -- 自动选用第一个回应
+            local ch = jdata.choices[1].message
+            table.insert(self.msgs, {
+                role = ch.role,
+                content = ch.content
+            })
+            return ch
+        end
+    elseif code == 400 then
+        tag = "请求体格式错误,请根据错误信息提示修改请求体"
+        log.warn(tag)
+    elseif code == 401 then
+        tag = "API key错误,认证失败,请检查您的API key是否正确,如没有API key,请先创建API key"
+        log.warn(tag)
+    elseif code == 402 then
+        tag = "账号余额不足,请充值"
+        log.warn(tag)
+    elseif code == 422 then
+        tag = "请求体参数错误,请根据错误信息提示修改请求体"
+        log.warn(tag)
+    elseif code == 429 then
+        tag = "请求速率(TPM 或 RPM)达到上限,请稍后再试"
+        log.warn(tag)
+    elseif code == 500 then
+        tag = "服务器内部故障,请等待后重试,若问题一直存在,请联系deepseek官方解决"
+        log.warn(tag)
+    elseif code == 503 then
+        tag = "服务器负载过高,请稍后重试您的请求"
+        log.warn(tag)
+    elseif code < 0 then
+        tag = "异常,大概率是服务器问题" .. code
+        if code == -8 then
+            tag = "链接服务超时或读取数据超时" .. code
+        end
+    end
+    log.info("openai", code, json.encode(headers) or "", body or "")
+    return tag
+end
+
+--[[
+创建一个对话
+@api openai.completions(opts, prompt)
+@table 调用选项,有必填参数,请看实例
+@string 起始提示语,可选
+@return 对话实例
+@usage
+-- 以deepseek为例, 请填充真实的apikey
+sys = require "sys"
+require "sysplus"
+openai = require "openai"
+
+local opts = {
+    apikey = "sk-123456",
+    apiurl = "https://api.deepseek.com",
+    model = "deepseek-chat"
+}
+local chat = openai.completions(opts)
+sys.taskInit(function()
+    sys.waitUntil("IP_READY")
+    sys.wait(100)
+    -- 固定问答演示
+    local resp = chat:talk("你好,请问LuatOS是什么软件?应该如何学习呢?")
+    if resp then
+        log.info("deepseek回复", resp.content)
+    else
+        log.info("deepseek执行失败")
+    end
+end)
+]]
+function openai.completions(opts, prompt)
+    opts = mergeWithDefaults(opts, defaultOpts)
+    local chat = {
+        opts = opts,
+        talk = talk,
+        msgs = {prompt and {
+            role = "system",
+            content = prompt
+        }}
+    }
+    return chat
+
+end
+
+return openai

+ 0 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/pac9685.lua


+ 119 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/pca9555.lua

@@ -0,0 +1,119 @@
+--[[
+
+@module pca9555
+@summary pca9555 IO扩展
+@version 1.0
+@date    2024/1/15
+@author  小牛
+@usage
+    i2c.setup(0, i2c.FAST)
+    pca9555.setup(0, 0x20, 0x0000)
+    for i=0,7 do
+        pca9555.pin(i,0)
+    end
+]]
+
+local pca9555 = {}
+
+local PCA9555_i2cid=0
+local PCA9555_addr=0x20
+local PCA9555_mode=0x0000 --IO全为输出
+local PCA9555_IOstart=0xffff
+
+-- pca9555 registers define
+local PCA9555_REG_IN0 = 0x00                  --输入端口寄存器0 默认值由外部逻辑决定 只可读不可写
+local PCA9555_REG_IN1 = 0x01                  --输入端口寄存器1 默认值由外部逻辑决定 只可读不可写
+local PCA9555_REG_OUT0 = 0x02                 --输出端口寄存器0 默认值为1 可读可写
+local PCA9555_REG_OUT1 = 0x03                 --输入端口寄存器1 默认值为1 可读可写
+local PCA9555_REG_POL0 = 0x04                 --极性反转寄存器0 默认值为0 可读可写
+local PCA9555_REG_POL1 = 0x05                 --极性反转寄存器1 默认值为0 可读可写
+local PCA9555_REG0_CFG0 = 0x06                --配置端口寄存器0 默认为1 可读可写
+local PCA9555_REG1_CFG1 = 0x07                --配置端口寄存器1 默认为1 可读可写
+
+--[[
+配置寄存器说明          1为输入模式     0为输出模式
+Config registers
+---------------------------------------------------------------------------------------
+Config          prot 0 register
+Bit                   7       6       5       4       3       2       1
+Symbol              C0.7    C0.6    C0.5    C0.4    C0.3    C0.2    C0.0
+Default               1       1       1       1       1       1       1
+---------------------------------------------------------------------------------------
+Config          prot 1 register
+Bit                   7       6       5       4       3       2       1
+Symbol              C1.7    C1.6    C1.5    C1.4    C1.3    C1.2    C1.0
+Default               1       1       1       1       1       1       1
+---------------------------------------------------------------------------------------
+]]
+
+--[[
+pca9555初始化
+@api pca955.setup(i2cid, addr,mode)
+@number i2cid 所在的i2c总线
+@number addr pca9555设备的i2c地址
+@number mode 配置输入输出模式  --0xffff 高八位配置IO 17-10 低八位配置IO 7-0
+@return 初始化失败,返回nil
+@usage
+    i2c.setup(0, i2c.FAST)
+    pca9555.setup(0,0x20,0xffff)
+]]
+function pca9555.setup(i2cid,addr,mode)
+    PCA9555_i2cid=i2cid
+    PCA9555_addr=addr
+    PCA9555_mode=mode
+    readEncoder()
+    local tmp=i2c.readReg(PCA9555_i2cid, PCA9555_addr, 0x02, 2)
+    if tmp==nil then
+        log.info("PCA9555初始化失败")
+        return nil
+    end
+    if  PCA9555_mode~=nil  then
+        i2c.writeReg(PCA9555_i2cid,PCA9555_addr,0x06,pack.pack("<H",PCA9555_mode))
+        i2c.writeReg(PCA9555_i2cid,PCA9555_addr,0x02,pack.pack("<H",PCA9555_IOstart))
+    else
+        local recvData=i2c.readReg(PCA9555_i2cid, PCA9555_addr,0x00,2)
+        _,recvData=pack.unpack(recvData,"<H")
+        log.info("pca9555默认为完全输入模式",recvData)
+    end
+end
+
+--[[
+pca9555 pin控制
+@api pca9555.pin(pin,val)
+@number pin 引脚 0-7 10-17
+@number val 高/低电平 1/0
+@return number 如果val未填,则读取pin的输出电平
+@usage
+    for i=0,7 do
+        pca9555.pin(i,0)
+    end
+]]
+function pca9555.pin(pin,val)
+    if pin==nil then
+        log.info("请选择引脚")
+    elseif pin then
+        if val==nil then
+            if pin>9 then
+                pin=pin-2
+            end
+            local tmp=i2c.readReg(PCA9555_i2cid, PCA9555_addr,0x00,2)
+            _,tmp=pack.unpack(tmp,"<H")
+            val=(tmp&(1<<pin))>>pin
+            return val
+        elseif val==0 then
+            if pin>9 then
+                pin=pin-2
+            end
+            PCA9555_IOstart=PCA9555_IOstart&(~(1<<pin))
+            i2c.writeReg(PCA9555_i2cid,PCA9555_addr,0x02,pack.pack("<H",PCA9555_IOstart))
+        else
+            if pin>9 then
+                pin=pin-2
+            end
+            PCA9555_IOstart=PCA9555_IOstart|(1<<pin)
+            i2c.writeReg(PCA9555_i2cid,PCA9555_addr,0x02,pack.pack("<H",PCA9555_IOstart))
+        end
+    end
+end
+
+return pca9555

+ 181 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/pca9685.lua

@@ -0,0 +1,181 @@
+--[[
+@module pca9685
+@summary pca9685 16路PWM驱动舵机 
+@version 1.0
+@date    2023.12.26
+@author  xwtx
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+PROJECT = "pca9685"
+VERSION = "1.0.0"
+
+
+sys = require("sys")
+pca9685=require("pca9685")
+
+local i2c_id = 1
+sys.taskInit(
+    function()
+        i2c.setup(i2c_id,i2c.SLOW)
+        sys.wait(1000)
+        pca9685.init(i2c_id,60)
+        sys.wait(1000)
+        local i=0
+        while true do
+            pca9685.setpwm(i2c_id,0,i)
+            if i >= 100 then
+                i=0
+            end
+            i=i+10
+            sys.wait(2000)
+        end
+    end
+)
+
+sys.run()
+]]
+
+
+
+
+
+local pca9685={}
+
+
+
+
+local PCA_Addr = 0x40   --pwm通道地址
+local PCA_Model =0x00   --工作模式:读1/写0
+local LED0_ON_L =0x06
+local LED0_ON_H =0x07
+local LED0_OFF_L= 0x08
+local LED0_OFF_H =0x09
+local PCA_Pre = 0xFE
+
+local function pca9685_write(i2cId,addr, data)
+   local ret
+   ret=i2c.send(i2cId, PCA_Addr, { addr, data })
+   sys.wait(15)
+   if ret then
+      return true
+   else
+      return 
+   end
+end
+
+local function pca9685_read(i2cId,addr)
+   i2c.send(i2cId, PCA_Addr, addr)
+   sys.wait(5)
+   local data = i2c.recv(i2cId,0x40, 1)
+   if #data==0 then
+      return 
+   else
+      return data
+   end
+end
+--[[
+pca9685 初始化
+@api pca9685.init(i2cId,hz)
+@int i2cid 使用的i2c id, 或者是软件i2c的实例
+@int hz pca9685的输出频率
+@return 成功返回true 失败返回nil
+]]
+function pca9685.init(i2cId,hz)
+   local ret=0
+   ret=pca9685_write(i2cId,PCA_Model,0x00)
+   if not ret then
+      return 
+   end
+   ret=pca9685.setfreq(i2cId,hz)
+   if not ret then
+      return 
+   end
+   pca9685.setpwm(i2cId,0,0);
+	pca9685.setpwm(i2cId,1,0);
+   pca9685.setpwm(i2cId,2,0);
+   pca9685.setpwm(i2cId,3,0);
+   pca9685.setpwm(i2cId,4,0);
+   pca9685.setpwm(i2cId,5,0);
+   pca9685.setpwm(i2cId,6,0);
+   pca9685.setpwm(i2cId,7,0);
+   pca9685.setpwm(i2cId,8,0);
+   pca9685.setpwm(i2cId,9,0);
+   pca9685.setpwm(i2cId,10,0);
+   pca9685.setpwm(i2cId,11,0);
+   pca9685.setpwm(i2cId,12,0);
+   pca9685.setpwm(i2cId,13,0);
+   pca9685.setpwm(i2cId,14,0);
+   pca9685.setpwm(i2cId,15,0);
+   sys.wait(100)
+   return true
+end
+
+
+--[[
+pca9685 设置频率
+@api pca9685.setfreq(i2cId,freq)
+@int i2cid 使用的i2c id, 或者是软件i2c的实例
+@int freq pca9685的输出频率,范围为24HZ~1526HZ
+@return 成功返回true 失败返回nil
+]]
+function pca9685.setfreq(i2cId,freq) --PCA9685频率设置
+   local prescale
+   local oldmode
+   local newmode
+   local prescaleval
+   local ret
+   prescaleval = 25000000;
+   prescaleval = prescaleval / 4096
+   prescaleval = prescaleval / freq
+   prescaleval = prescaleval - 1
+   prescale = math.floor(prescaleval + 0.5)
+   oldmode = string.toHex(pca9685_read(i2cId,PCA_Model))
+   newmode = (oldmode & 0x7F)|0x10
+   ret=pca9685_write(i2cId,PCA_Model, newmode)
+   if not ret then
+      return 
+   end
+   ret=pca9685_write(i2cId,PCA_Pre, prescale)
+   if not ret then
+      return 
+   end
+   ret=pca9685_write(i2cId,PCA_Model, oldmode)
+   if not ret then
+      return 
+   end
+   sys.wait(5)
+   ret=pca9685_write(i2cId,PCA_Model, oldmode|0xa1)
+   if not ret then
+      return 
+   end
+   return true
+end
+
+--[[
+pca9685 设置PWM占空比
+@api pca9685.setpwm(i2cId,num, duty_cycle)
+@int i2cid 使用的i2c id, 或者是软件i2c的实例
+@int num 通道号
+@int duty_cycle 占空比 0~100
+@return 成功返回true 失败返回nil
+]]
+function pca9685.setpwm(i2cId,num, duty_cycle)
+   local  on = 0
+   if duty_cycle==50 then
+      on=0
+   else
+      on=math.floor(4096*((100-duty_cycle)/100))
+   end
+   local off=on+math.floor(4096*(duty_cycle/100))
+   local ret = i2c.send(i2cId, PCA_Addr, { LED0_ON_L + 4 * num, on & 0xFF, on >> 8, off & 0xFF, off >> 8 })
+   sys.wait(5)
+   if ret==false then
+      return 
+   end
+   return true
+end
+return pca9685
+
+
+

+ 202 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/pcf8563t.lua

@@ -0,0 +1,202 @@
+--[[
+@module pcf8563t
+@summary pcf8563t时钟模块
+@version 1.0
+@date    2023.11.02
+@author  wendal
+@usage
+-- 数据手册 https://www.nxp.com.cn/docs/zh/data-sheet/PCF8563.pdf
+local pcf8563t = require "pcf8563t"
+
+local i2cid = 1
+i2c.setup(i2cid, i2c.FAST)
+pcf8563t.setup(i2cid) -- 选一个i2c, 也可以是软件i2c对象
+
+-- 设置时间
+local time = {year=2023,mon=11,day=2,wday=5,hour=13,min=14,sec=15}
+pcf8563t.write(time)
+
+-- 读取时间
+local time = pcf8563t.read()
+log.info("time",time.year,time.mon,time.day, time.hour,time.min,time.sec, "week=".. time.wday)
+
+-- 设置闹钟, 并自动清除中断标志,开启alarm功能
+alarm = {day=2,hour=13,min=14,sec=15}
+pcf8563t.alarm(alarm)
+local alarm_int = 1 -- 选一个GPIO,接时钟模块的INT脚
+gpio.setup(1, function()
+    log.info("alarm!!!")
+    pcf8563t.control(nil, nil, 0, nil)
+end, gpio.PULLUP)
+
+-- 主动清除中断标志
+pcf8563t.control(nil, nil, 0, nil)
+-- 关闭alarm
+pcf8563t.control(0, nil, 0, nil)
+]]
+local pcf8563t = {}
+
+local i2cid = 1             --i2cid
+local i2cslaveaddr =   0X51 --slave address
+local baseyear = 2000
+
+local REG_SEC = 0X02 
+local REG_ALARM = 0x09
+
+local function bcd_to_hex(data)
+    return bit.rshift(data,4)*10+bit.band(data,0x0f)
+ 
+end
+
+local function hex_to_bcd(data)
+    return bit.lshift(data//10,4)+data%10
+end
+
+--[[
+初始化
+@api pcf8563t.setup(id, by)
+@int i2c设备的id, 或者软件i2c的对象,必须填
+@int 年份基准,默认是2000, 可以不填
+@return boolean 成功返回true, 否则返回false
+]]
+function pcf8563t.setup(id, by)
+    i2cid = id
+    local t = pcf8563t.read()
+    if by then
+        baseyear = by
+    end
+    return t and t.year ~= nil
+end
+
+--[[
+读出时间
+@api pcf8563t.read()
+@return table 时间信息,如果读取失败会返回空的表
+@usage
+local time = pcf8563t.read()
+if time and time.year then
+    log.info("time", json.encode(time))
+end
+-- time 是个table, 有以下字段, 与os.date是兼容的
+-- time.year 年份
+-- time.mon  月份
+-- time.day  月内的天数
+-- time.hour 小时, 24进制
+-- time.min  分钟
+-- time.sec  秒
+-- time.wday 周N, 周日为1, 周六为7
+]]
+function pcf8563t.read()
+    -- read time
+    local time_data = {}
+    local data = i2c.readReg(i2cid, i2cslaveaddr, REG_SEC, 7)
+    if not data or #data ~= 7 then
+        return time_data
+    end
+    time_data.year = bcd_to_hex(data:byte(7)) + baseyear
+    time_data.mon = bcd_to_hex(data:byte(6) & 0x9F)
+    time_data.wday = bcd_to_hex(data:byte(5) & 0x07) + 1
+    time_data.day = bcd_to_hex(data:byte(4) & 0x3F)
+    time_data.hour = bcd_to_hex(data:byte(3) & 0x3F)
+    time_data.min = bcd_to_hex(data:byte(2) & 0x7F)
+    time_data.sec = bcd_to_hex(data:byte(1) & 0x7F)
+	return time_data
+end
+
+--[[
+设置时间
+@api pcf8563t.write(time)
+@table 时间信息,与pcf8563t.read的字段规则是一样的
+@return nil 无返回值
+@usage
+local time = {year=2023,mon=11,day=2,wday=5,hour=13,min=14,sec=15}
+pcf8563t.write(time)
+]]
+function pcf8563t.write(time)
+    -- set time
+    local data7 = hex_to_bcd(time.year - baseyear)
+    local data6 = hex_to_bcd(time.mon)
+    local data5 = hex_to_bcd(time.wday - 1)
+    local data4 = hex_to_bcd(time.day)
+    local data3 = hex_to_bcd(time.hour)
+    local data2 = hex_to_bcd(time.min)
+    local data1 = hex_to_bcd(time.sec)
+    i2c.writeReg(i2cid, i2cslaveaddr, REG_SEC, string.char(data1,data2,data3,data4,data5,data6,data7))
+end
+
+--[[
+设置闹钟
+@api pcf8563t.alarm(time)
+@table 时间信息
+@return nil 无返回值
+@usage
+-- 设置闹钟, 并自动清除中断标志,开启alarm功能
+-- 注意只能设置天(wday)/小时(hour)/分钟(min)/周天(wday), 无法设置年月
+alarm = {day=2,hour=13,min=14}
+pcf8563t.alarm(alarm)
+local alarm_int = 1 -- 选一个GPIO,接时钟模块的INT脚
+gpio.setup(1, function()
+    log.info("alarm!!!")
+    pcf8563t.control(nil, nil, 0, nil)
+end, gpio.PULLUP)
+
+-- 注意规则!!! 传入的值,需要全部匹配才会触发
+-- 例如, 每当min=1时触发, 不判断hour/day/wday
+alarm = {min=1}
+-- 例如, 每当min=1且hour=0时触发, 不判断day/wday
+alarm = {min=1, hour=0}
+-- 全部day/hour/min都满足才触发
+alarm = {day=2,hour=13,min=14}
+-- 全部day/hour/min/wday都满足才触发
+alarm = {day=2,hour=13,min=14,wday=2}
+]]
+function pcf8563t.alarm(time)
+    -- set time
+    local data5 = time.wday and hex_to_bcd(time.wday - 1) or  0x80
+    local data4 = time.day and hex_to_bcd(time.day) or  0x80
+    local data3 = time.hour and hex_to_bcd(time.hour) or  0x80
+    local data2 = time.min and hex_to_bcd(time.min) or  0x80
+    -- local data1 = time.sec and hex_to_bcd(time.sec) or 0
+    i2c.writeReg(i2cid, i2cslaveaddr, REG_ALARM, string.char(data2,data3,data4,data5))
+    local dataN = i2c.readReg(i2cid, i2cslaveaddr, REG_ALARM, 4)
+    log.info("数据对比", string.char(data2,data3,data4,data5):toHex(), dataN:toHex())
+    pcf8563t.control(1, nil, 0, nil)
+end
+
+--[[
+设置闹钟
+@api pcf8563t.control(AIE, TIE, AF, TF)
+@int 闹钟开关, 0 关闭, 1 开始, nil保持原样
+@int 定时器开关, 0 关闭, 1 开始, nil保持原样
+@int 清除闹钟中断, 0 清除, nil保持原样
+@int 清除定时器中断, 0 清除, nil保持原样
+@return nil 无返回值
+@usage
+-- 主动清除中断标志
+pcf8563t.control(nil, nil, 0, nil)
+-- 关闭alarm
+pcf8563t.control(0, nil, 0, nil)
+]]
+function pcf8563t.control(AIE, TIE, AF, TF)
+    local data = i2c.readReg(i2cid, i2cslaveaddr, 0x01, 1)
+    if data and #data == 1 then
+        data = data:byte()
+        if AIE then
+            data = data | ((AIE & 0x01) << 1)
+        end
+        if TIE then
+            data = data | (TIE & 0x01)
+        end
+        if AF then
+            data = data | ((AF & 0x01) << 3)
+        end
+        if TF then
+            data = data | ((TF & 0x01) << 2)
+        end
+        i2c.writeReg(i2cid, i2cslaveaddr, 0x01, string.char(data))
+        local data2 = i2c.readReg(i2cid, i2cslaveaddr, 0x01, 1)
+        log.info("数据对比", string.char(data):toHex(), data2:toHex())
+    end 
+end
+
+return pcf8563t

+ 108 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/pcf8574.lua

@@ -0,0 +1,108 @@
+--[[
+@module pcf8574
+@summary pcf8574 IO扩展
+@version 1.0
+@date    2022.07.29
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local pcf8574 = require "pcf8574"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    pcf8574.init(i2cid)--初始化,传入i2c_id
+    for i=0,7 do
+        print(pcf8574.pin(i))
+    end
+    pcf8574.pin(0,1)
+    for i=0,7 do
+        print(pcf8574.pin(i))
+    end
+end)
+]]
+
+
+local pcf8574 = {}
+local sys = require "sys"
+local i2cid
+
+local PCF8574_ADDRESS
+local PCF8574_ADDRESS_ADR       =   0x20   -- 0x20-0x27
+local PCF8574A_ADDRESS_ADR      =   0x38   -- 0x38-0x3F
+local PCF8574_DATA
+
+--器件ID检测
+local function chip_check()
+    for i=0,7 do
+        local revData = i2c.recv(i2cid, PCF8574_ADDRESS_ADR+i, 1)
+        if revData:byte() ~= nil then
+            PCF8574_ADDRESS = PCF8574_ADDRESS_ADR+i
+            PCF8574_DATA = revData:byte()
+            break
+        end
+    end
+    if PCF8574_ADDRESS==nil then
+        for i=0,7 do
+            local revData = i2c.recv(i2cid, PCF8574A_ADDRESS_ADR+i, 1)
+            if revData:byte() ~= nil then
+                PCF8574_ADDRESS = PCF8574A_ADDRESS_ADR+i
+                PCF8574_DATA = revData:byte()
+                break
+            end
+        end
+    end
+    if PCF8574_ADDRESS then
+        log.info("i2c", "Device is: pcf8574")
+    else
+        log.info("i2c", "Can't find pcf8574 device")
+        return false
+    end
+    return true
+end
+
+--[[
+pcf8574初始化
+@api pcf8574.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+pcf8574.init(0)
+]]
+function pcf8574.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(40)--40 毫秒等待设备稳定
+    chip_check()
+    log.info("pcf8574 init_ok")
+    return true
+end
+
+--[[
+pcf8574 pin控制
+@api pcf8574.pin(pin,val)
+@number pin 0-7
+@number val 0/1 可选,不填则读取电平
+@return number 如val未填则返回读取电平
+@usage
+pcf8574.pin(0,1)
+print(pcf8574.pin(0))
+]]
+function pcf8574.pin(pin,val)
+    if val then
+        if val==0 then
+            PCF8574_DATA = PCF8574_DATA&~(1<<pin)
+        else
+            PCF8574_DATA = PCF8574_DATA|val<<pin
+        end
+        i2c.send(i2cid, PCF8574_ADDRESS,string.char(PCF8574_DATA))
+    else
+        local revData = i2c.recv(i2cid, PCF8574_ADDRESS, 1)
+        PCF8574_DATA = revData:byte()
+        return (PCF8574_DATA&(1<<pin))>>pin
+    end
+end
+
+return pcf8574
+
+

+ 123 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/qmc5883l.lua

@@ -0,0 +1,123 @@
+--[[
+@module qmc5883l
+@summary qmc5883l 地磁传感器
+@version 1.0
+@date    2022.04.12
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local qmc5883l = require "qmc5883l"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    qmc5883l.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local qmc5883l_data = qmc5883l.get_data()
+        log.info("qmc5883l_data", qmc5883l_data.x,qmc5883l_data.y,qmc5883l_data.z,qmc5883l_data.heading,qmc5883l_data.headingDegrees)
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local qmc5883l = {}
+local sys = require "sys"
+local i2cid
+
+local QMC5883L_ADDRESS_ADR
+
+local QMC5883L_ADDRESS_ADR_LOW   =   0x0C
+local QMC5883L_ADDRESS_ADR_HIGH  =   0x0D
+
+local QMC5883L_CHIP_ID_CHECK     =   0x0D
+local QMC5883L_CHIP_ID           =   0xFF
+
+---器件所用地址
+
+local QMC5883L_X_LSB             =   0x00
+local QMC5883L_X_MSB             =   0x01
+local QMC5883L_Y_LSB             =   0x02
+local QMC5883L_Y_MSB             =   0x03
+local QMC5883L_Z_LSB             =   0x04
+local QMC5883L_Z_MSB             =   0x05
+local QMC5883L_STATUS            =   0x06
+local QMC5883L_T_LSB             =   0x07
+local QMC5883L_T_MSB             =   0x08
+local QMC5883L_CONTROL1          =   0x09
+local QMC5883L_CONTROL2          =   0x0A
+local QMC5883L_PERIOD            =   0x0B
+
+--器件ID检测
+local function chip_check()
+    i2c.send(i2cid, QMC5883L_ADDRESS_ADR_HIGH, QMC5883L_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, QMC5883L_ADDRESS_ADR_HIGH, 1)
+    if revData:byte() ~= nil then
+        QMC5883L_ADDRESS_ADR = QMC5883L_ADDRESS_ADR_HIGH
+    else
+        i2c.send(i2cid, QMC5883L_ADDRESS_ADR_LOW, QMC5883L_CHIP_ID_CHECK)--读器件地址
+        sys.wait(50)
+        local revData = i2c.recv(i2cid, QMC5883L_ADDRESS_ADR_LOW, 1)
+        if revData:byte() ~= nil then
+            QMC5883L_ADDRESS_ADR = QMC5883L_ADDRESS_ADR_LOW
+        else
+            log.info("Can't find QMC5883L device")
+            return false
+        end
+    end
+
+    i2c.send(i2cid, QMC5883L_ADDRESS_ADR, QMC5883L_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, QMC5883L_ADDRESS_ADR, 1)
+    if revData:byte() == QMC5883L_CHIP_ID then
+        log.info("Device i2c id is: QMC5883L")
+        return true
+    else
+        log.info("Can't find QMC5883L device")
+        return false
+    end
+end
+
+--[[
+qmc5883l 初始化
+@api qmc5883l.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+qmc5883l.init(0)
+]]
+function qmc5883l.init(i2c_id)
+    i2cid = i2c_id
+    if chip_check() then
+        i2c.send(i2cid, QMC5883L_ADDRESS_ADR, {QMC5883L_PERIOD,0x01})
+        i2c.send(i2cid, QMC5883L_ADDRESS_ADR, {QMC5883L_CONTROL1,0x1D})
+    end
+    return true
+end
+
+--[[
+获取 qmc5883l 数据
+@api qmc5883l.get_data()
+@return table qmc5883l 数据
+@usage
+local qmc5883l_data = qmc5883l.get_data()
+log.info("qmc5883l_data", qmc5883l_data.x,qmc5883l_data.y,qmc5883l_data.z,qmc5883l_data.heading,qmc5883l_data.headingDegrees)
+]]
+function qmc5883l.get_data()
+    local qmc5883l_data = {}
+    i2c.send(i2cid, QMC5883L_ADDRESS_ADR,QMC5883L_X_LSB)
+    local data = i2c.recv(i2cid, QMC5883L_ADDRESS_ADR, 6)
+    _, qmc5883l_data.x, qmc5883l_data.y, qmc5883l_data.z = pack.unpack(data, "<h3")
+    local heading = math.atan (qmc5883l_data.y ,qmc5883l_data.x)
+    qmc5883l_data.heading = heading
+    local declinationAngle = 0.0404
+    heading = heading+declinationAngle
+    if heading < 0 then heading = heading+2*math.pi end
+    if heading > 2*math.pi then heading = heading-2*math.pi end
+    qmc5883l_data.headingDegrees = heading * 180/math.pi
+    return qmc5883l_data
+end
+
+return qmc5883l
+
+

+ 650 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/rc522.lua

@@ -0,0 +1,650 @@
+--[[
+@module rc522
+@summary rc522 非接触式读写卡驱动
+@version 1.0
+@date    2022.06.14
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local rc522 = require "rc522"
+sys.taskInit(function()
+    spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB,1,0)
+    rc522.init(0,pin.PB04,pin.PB01)
+    wdata = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
+    while 1 do
+        rc522.write_datablock(8,wdata)
+        for i=0,63 do
+            local a,b = rc522.read_datablock(i)
+            if a then
+                print("read",i,b:toHex())
+            end
+        end
+        sys.wait(500)
+    end
+end)
+]]
+
+
+local rc522 = {}
+
+local sys = require "sys"
+
+local rc522_spi,rc522_rst,rc522_irq,rc522_cs
+
+---器件所用地址
+local rc522_idle              =       0x00   --取消当前命令
+local rc522_authent           =       0x0E   --验证密钥
+local rc522_receive           =       0x08   --接收数据
+local rc522_transmit          =       0x04   --发送数据
+local rc522_transceive        =       0x0C   --发送并接收数据
+local rc522_resetphase        =       0x0F   --复位
+local rc522_calccrc           =       0x03   --CRC计算
+
+--Mifare_One卡片命令字
+rc522.reqidl                 =       0x26   --寻天线区内未进入休眠状态
+rc522.reqall                 =       0x52   --寻天线区内全部卡
+local rc522_anticoll1        =       0x93   --防冲撞
+local rc522_anticoll2        =       0x95   --防冲撞
+local rc522_authent1a        =       0x60   --验证A密钥
+local rc522_authent1b        =       0x61   --验证B密钥
+local rc522_read             =       0x30   --读块
+local rc522_write            =       0xA0   --写块
+local rc522_decrement        =       0xC0   --扣款
+local rc522_increment        =       0xC1   --充值
+local rc522_restore          =       0xC2   --调块数据到缓冲区
+local rc522_transfer         =       0xB0   --保存缓冲区中数据
+local rc522_halt             =       0x50   --休眠
+
+--MF522寄存器定义
+-- PAGE 0
+local rc522_rfu00            =       0x00 --保留为将来之用    
+local rc522_com_mand         =       0x01 --启动和停止命令的执行      
+local rc522_com_ie           =       0x02 --中断请求传递的使能和禁能控制位      
+local rc522_divl_en          =       0x03 --中断请求传递的使能和禁能控制位      
+local rc522_com_irq          =       0x04 --包含中断请求标志      
+local rc522_div_irq          =       0x05 --包含中断请求标志  
+local rc522_error            =       0x06 --错误标志,指示执行的上个命令的错误状态      
+local rc522_status1          =       0x07 --包含通信的状态标志      
+local rc522_status2          =       0x08 --包含接收器和发送器的状态标志      
+local rc522_fifo_data        =       0x09 --64 字节 FIFO 缓冲区的输入和输出  
+local rc522_fifo_level       =       0x0A --指示 FIFO 中存储的字节数  
+local rc522_water_level      =       0x0B --定义 FIFO 下溢和上溢报警的 FIFO 深度  
+local rc522_control          =       0x0C --不同的控制寄存器  
+local rc522_bit_framing      =       0x0D --面向位的帧的调节  
+local rc522_coll             =       0x0E --RF 接口上检测到的第一个位冲突的位的位置  
+local rc522_rfu0f            =       0x0F --保留为将来之用  
+-- PAGE 1     
+local RFU10                  =       0x10 --保留为将来之用
+local rc522_mode             =       0x11 --定义发送和接收的常用模式
+local rc522_tx_mode          =       0x12 --定义发送过程的数据传输速率
+local rc522_rx_mode          =       0x13 --定义接收过程中的数据传输速率
+local rc522_tx_control       =       0x14 --控制天线驱动器管脚 TX1 和 TX2 的逻辑特性
+local rc522_tx_ayto          =       0x15 --控制天线驱动器的设置
+local rc522_tx_sel           =       0x16 --选择天线驱动器的内部源
+local rc522_rx_sel           =       0x17 --选择内部的接收器设置
+local rc522_rx_threshold     =       0x18 --选择位译码器的阈值
+local rc522_demod            =       0x19 --定义解调器的设置
+local rc522_rfu1a            =       0x1A --保留为将来之用
+local rc522_rfu1b            =       0x1B --保留为将来之用
+local rc522_mifare           =       0x1C --控制 ISO 14443/MIFARE 模式中 106kbit/s 的通信
+local rc522_rfu1d            =       0x1D --保留为将来之用
+local rc522_rfu1e            =       0x1E --保留为将来之用
+local rc522_serial_speed     =       0x1F --选择串行 UART 接口的速率
+-- PAGE 2    
+local rc522_rfu20            =       0x20 --保留为将来之用  
+local rc522_crcresult_m      =       0x21 --显示 CRC 计算的实际 MSB 值
+local rc522_crcresult_l      =       0x22 --显示 CRC 计算的实际 LSB 值
+local rc522_rfu23            =       0x23 --保留为将来之用
+local rc522_mod_width        =       0x24 --控制 ModWidth 的设置
+local rc522_rfu25            =       0x25 --保留为将来之用
+local rc522_rfcfg            =       0x26 --配置接收器增益
+local rc522_gsn              =       0x27 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_cwgscfg          =       0x28 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_modgscfg         =       0x29 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_tmode            =       0x2A --定义内部定时器的设置
+local rc522_tprescaler       =       0x2B --定义内部定时器的设置
+local rc522_tpreload_h       =       0x2C --描述 16 位长的定时器重装值
+local rc522_tpreload_l       =       0x2D --描述 16 位长的定时器重装值
+local rc522_tcounter_value_h =       0x2E --描述 16 位长的定时器重装值
+local rc522_tcounter_value_l =       0x2F --描述 16 位长的定时器重装值
+-- PAGE 3      
+local rc522_rfu30            =       0x30 --保留为将来之用
+local rc522_testsel1         =       0x31 --常用测试信号的配置
+local rc522_testsel2         =       0x32 --常用测试信号的配置和 PRBS 控制
+local rc522_testpin_en       =       0x33 --D1-D7 输出驱动器的使能管脚(注:仅用于串行接口)
+local rc522_testpin_value    =       0x34 --定义 D1-D7 用作 I/O 总线时的值
+local rc522_testbus          =       0x35 --显示内部测试总线的状态
+local rc522_autotest         =       0x36 --控制数字自测试
+local rc522_version          =       0x37 --显示版本
+local rc522_analogtest       =       0x38 --控制管脚 AUX1 和 AUX2
+local rc522_testadc1         =       0x39 --定义 TestDAC1 的测试值  
+local rc522_testadc2         =       0x3A --定义 TestDAC2 的测试值   
+local rc522_testadc          =       0x3B --显示 ADC I 和 Q 通道的实际值   
+local rc522_rfu3c            =       0x3C --保留用于产品测试   
+local rc522_rfu3d            =       0x3D --保留用于产品测试   
+local rc522_rfu3e            =       0x3E --保留用于产品测试   
+local rc522_rfu3f		     =       0x3F --保留用于产品测试
+
+local Key_A = string.char(0xff,0xff,0xff,0xff,0xff,0xff)
+local Key_B = string.char(0xff,0xff,0xff,0xff,0xff,0xff)
+
+--[[
+写rc522寄存器
+@api rc522.set_bit_mask(address, value)
+@number address 地址
+@number value    值
+@usage
+write_rawrc(rc522_bit_framing,0x07)
+]]
+local function write_rawrc(address, value)
+    rc522_cs(0)
+    local data = string.char((address<<1)&0x7E) .. string.char(value)
+    spi.send(rc522_spi, data)
+    -- rc522_spi:send(data)
+    rc522_cs(1)
+end
+
+--[[
+读rc522寄存器
+@api rc522.read_rawrc(address)
+@number address 地址
+@return number 寄存器值
+@usage
+local n = read_rawrc(rc522_com_irq) 
+]]
+local function read_rawrc(address)
+    rc522_cs(0)
+    local data = string.char(((address<<1)&0x7E)|0x80)
+    spi.send(rc522_spi, data)
+    local val = spi.recv(rc522_spi,1)
+    -- rc522_spi:send(data)
+    -- local val = rc522_spi:recv(1)
+    rc522_cs(1)
+    return string.byte(val)
+end
+
+--[[
+对rc522寄存器置位
+@api rc522.set_bit_mask(address, mask)
+@number address 地址
+@number mask    置位值
+@usage
+rc522.set_bit_mask (rc522_fifo_level, 0x80)	
+]]
+function rc522.set_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.bor(current, mask))
+end
+
+--[[
+对rc522寄存器清位
+@api rc522.clear_bit_mask(address, mask)
+@number address 地址
+@number mask    清位值
+@usage
+rc522.clear_bit_mask(rc522_com_irq, 0x80 )
+]]
+function rc522.clear_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.band(current, bit.bnot(mask)))
+end
+
+--[[ 
+命令通讯
+@api rc522.command(command,data)
+@number command 
+@number data 
+@return status data len 结果,返回数据,收到的位长度
+@usage
+rc522.version()
+]]
+function rc522.command(command,data)
+    local out_data = {}
+    local len = 0
+    local status = false
+    local Irq_en  = 0x00
+    local waitfor = 0x00
+    local last_bits,n,l
+    if command==rc522_authent then
+        Irq_en   = 0x12;		
+        waitfor = 0x10;		
+    elseif command==rc522_transceive then 
+        Irq_en   = 0x77;		
+        waitfor = 0x30;		
+    end
+    write_rawrc(0x02, bit.bor(Irq_en, 0x80))
+    rc522.clear_bit_mask(rc522_com_irq, 0x80 )
+    write_rawrc (rc522_com_mand, rc522_idle)	 
+    rc522.set_bit_mask (rc522_fifo_level, 0x80)	
+    for i=1,#data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand,command )	
+    if ( command == rc522_transceive )then
+        rc522.set_bit_mask(rc522_bit_framing,0x80)
+    end
+    l = 12;
+    while true do
+        sys.wait(1)
+        n = read_rawrc(rc522_com_irq) 
+        l = l - 1
+        if  not ((l ~= 0) and (bit.band(n, 0x01) == 0) and (bit.band(n, waitfor) == 0)) then
+            break
+        end
+    end
+    rc522.clear_bit_mask(rc522_bit_framing, 0x80 )
+    if (l ~= 0)then 
+        if bit.band(read_rawrc(rc522_error), 0x1B) == 0x00 then
+            status = true
+            if bit.band(n,Irq_en,0x01)~=0then			
+                status = false
+            end   
+            if (command == rc522_transceive )then
+                n = read_rawrc(rc522_fifo_level)
+                last_bits = bit.band(read_rawrc(rc522_control),0x07)	
+                if last_bits ~= 0 then
+                    len = (n - 1) * 8 + last_bits  
+                else
+                    len = n * 8  
+                end
+                if n == 0 then	
+                    n = 1  
+                end  
+                for i=1, n do
+                    out_data [ i ] = read_rawrc(rc522_fifo_data)
+                end 
+            end
+        end   
+    else
+        status = false
+    end
+    rc522.set_bit_mask (rc522_control,0x80 )
+    write_rawrc(rc522_com_mand,rc522_idle )
+    return status,out_data,len 
+end
+
+--[[ 
+防冲撞
+@api rc522.anticoll(id)
+@string id 卡片序列号,4字节
+@return status uid 结果,uid
+@usage
+local status,uid = rc522.anticoll(array_id)
+]]
+function rc522.anticoll(id)
+    local check = 0
+    local uid
+    rc522.clear_bit_mask(rc522_status2,0x08)
+    write_rawrc( rc522_bit_framing, 0x00);
+    rc522.clear_bit_mask (rc522_coll, 0x80);			  
+    local status, back_data = rc522.command(rc522_transceive,{0x93,0x20})
+    if back_data and #back_data >= 5 then		            
+        for i=1,4 do
+            check = bit.bxor(check,back_data[i])
+        end
+        if check ~= back_data[5] then
+            status = false;
+        end   			
+        uid = string.char(table.unpack(back_data,1,4))	
+    end
+    rc522.clear_bit_mask( rc522_coll, 0x80 )
+    return status,uid;		
+end
+
+--[[ 
+crc计算
+@api calculate_crc(data)
+@table data 数据
+@return table crc值
+@usage
+local crc = calculate_crc(buff)
+]]
+local function calculate_crc(data)
+    local ret_data = {}
+    rc522.clear_bit_mask(rc522_div_irq, 0x04)
+    write_rawrc(rc522_com_mand, rc522_idle) 
+    rc522.set_bit_mask (rc522_fifo_level,0x80 ) 
+    for i=1,#data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, rc522_calccrc) 
+    local i = 0xFF
+    while true do
+        local n = read_rawrc(0x05)
+        i = i - 1
+        if not ((i ~= 0) and not (n&0x04)) then
+            break
+        end
+    end
+    ret_data[1] = read_rawrc(0x22)
+    ret_data[2] = read_rawrc(0x21)
+    return ret_data
+end
+
+--[[ 
+验证卡片密码
+@api authstate(mode, addr,key,uid )
+@number mode 模式
+@number addr 地址
+@string key 密码
+@string uid uid
+@return bool 结果
+@usage
+status = authstate(rc522_authent1b, addr,Key_B,uid )
+]]
+local function authstate( mode, addr,key,uid )
+    local buff = {}
+    buff[1] = mode
+    buff[2] = addr
+    for i=1,6 do
+        buff[i+2]=key:byte(i)
+    end
+    for i=1,4 do
+        buff[i+8]=uid:byte(i)
+    end
+    local status, back_data = rc522.command(rc522_authent,buff)
+    if status == true and (read_rawrc(rc522_status2)& 0x08) ~= 0 then
+        return true
+    end
+    return false	
+end
+
+--[[ 
+写数据到M1卡一块
+@api rc522.write(addr, data)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 数据
+@return bool 结果
+@usage
+rc522.write(addr, data)
+]]
+function rc522.write(addr, data)
+    local buff = {}
+    buff[1] = rc522_write;
+    buff[2] = addr;
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive,buff)
+    if status == true and back_bits == 4 and (back_data[1]& 0x0F)==0x0A then
+        local buf_w = {}
+        for i=0, 16 do
+            table.insert(buf_w, data[i])
+        end
+        local crc = calculate_crc(buf_w)
+        table.insert(buf_w, crc[1])
+        table.insert(buf_w, crc[2])
+        status, back_data, back_bits = rc522.command(rc522_transceive,buf_w)
+        if status == true and back_bits == 4 and (back_data[1]& 0x0F)==0x0A then
+            return status;
+        end
+    end
+    return status;		
+end
+
+--[[ 
+写数据到M1卡一块
+@api rc522.read(addr)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool,string 结果,数据
+@usage
+rc522.read(addr, data)
+]]
+local function read(addr)
+    local buff = {}
+    buff[1] = rc522_read;
+    buff[2] = addr;
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive,buff)
+    if status == true and back_bits == 0x90 then
+        local data = string.char(table.unpack(back_data,1,16))
+        return status,data
+    end
+    return false
+end
+
+--[[ 
+rc522 硬件版本
+@api rc522.version()
+@return number 硬件版本
+@usage
+rc522.version()
+]]
+function rc522.version()
+    return read_rawrc(rc522_version)
+end
+
+--[[ 
+rc522 命令卡片进入休眠状态
+@api rc522.halt()
+@return bool 结果
+@usage
+rc522.halt()
+]]
+function rc522.halt()
+    local buff = {}
+    buff[1] = rc522_halt;
+    buff[2] = 0;
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status = rc522.command(rc522_transceive,buff)
+    return status
+end
+
+--[[ 
+rc522 复位
+@api rc522.reset()
+@usage
+rc522.reset()
+]]
+function rc522.reset()
+    rc522_rst(1)
+    rc522_rst(0)
+    rc522_rst(1)
+    write_rawrc(rc522_com_mand, 0x0f)
+    write_rawrc(rc522_mode, 0x3D)        
+    write_rawrc(rc522_tpreload_l, 30)       
+    write_rawrc(rc522_tpreload_h, 0)			
+    write_rawrc(rc522_tmode, 0x8D)			
+    write_rawrc(rc522_tprescaler, 0x3E)	
+    write_rawrc(rc522_tx_ayto, 0x40)		
+end
+
+--[[ 
+开启天线 
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_on()
+    local uc = read_rawrc( rc522_tx_control )
+    if (( uc & 0x03 )==0 ) then
+        rc522.set_bit_mask(rc522_tx_control, 0x03)
+    end
+end
+
+--[[ 
+关闭天线 
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_off()
+    rc522.clear_bit_mask( rc522_tx_control, 0x03 )
+end
+
+--[[ 
+设置rc522工作方式为ISO14443_A
+@api rc522_config_isotype()
+@usage
+rc522_config_isotype()
+]]
+local function rc522_config_isotype()
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_rx_sel, 0x86)
+    write_rawrc(rc522_rfcfg, 0x7F)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    rc522.antenna_on()
+end
+
+--[[
+rc522 寻卡
+@api rc522.request(req_code)
+@number req_code rc522.reqidl 寻天线区内未进入休眠状态 rc522.reqall 寻天线区内全部卡
+@return bool tagtype 结果,卡类型 
+@usage
+status,array_id = rc522.request(rc522.reqall)
+]]
+function rc522.request(req_code)
+    rc522.clear_bit_mask(rc522_status2,0x08)
+    write_rawrc(rc522_bit_framing,0x07)
+    rc522.set_bit_mask(rc522_tx_control,0x03)
+    local tagtype
+    local status, back_data, back_bits = rc522.command(rc522_transceive,{req_code})
+    if status == true and back_bits == 0x10 then
+        tagtype = string.char(table.unpack(back_data,1,2))
+        return status, tagtype
+    end
+    return false
+end
+
+--[[
+选定卡片
+@api rc522.select(id)
+@number id 卡片序列号,4字节
+@return bool 结果 
+@usage
+status = rc522.select(id)
+]]
+function rc522.select(id)
+    if not id then
+        return false
+    end
+    local buff = {}
+    buff[1]=rc522_anticoll1
+    buff[2]=0x70
+    buff[7]=0
+    for i=1,4 do
+        buff[i+2]=id:byte(i)
+        buff[7]= bit.bxor(buff[7],id:byte(i))
+    end
+    local crc = calculate_crc(buff)
+    buff[8]=crc[1]
+    buff[9]=crc[2]
+    rc522.clear_bit_mask( rc522_status2, 0x08 )
+    local status, back_data, back_bits = rc522.command(rc522_transceive,buff)
+    if status == true and back_bits == 0x18 then
+        return true
+    end
+    return false	
+end
+
+--[[
+按照rc522操作流程写入16字节数据到块
+@api rc522.write_datablock(addr,data)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 指向要写入的数据,必须为16个字符
+@return bool 结果 
+@usage
+rc522.write_datablock(addr,data)
+]]
+function rc522.write_datablock(addr,data)
+    if #data ~= 16 then
+        return false
+    end
+    local status,array_id = rc522.request(rc522.reqall)
+    if  status ~= true then
+        status,array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status,uid = rc522.anticoll(array_id)
+        if not uid then
+            return false
+        end
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate( rc522_authent1b, addr,Key_B,uid )
+            if status == true then
+                status = rc522.write(addr,data)
+                rc522.halt()
+                return status
+            end
+        end
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程读取块
+@api rc522.read_datablock(addr)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool string 结果 数据
+@usage
+    for i=0,63 do
+        local a,b = rc522.read_datablock(i)
+        if a then
+            print("read",i,b:toHex())
+        end
+    end
+]]
+function rc522.read_datablock(addr)
+    local status,array_id = rc522.request(rc522.reqall)
+    if  status ~= true then
+        status,array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status,uid = rc522.anticoll(array_id)
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate( rc522_authent1b, addr,Key_B,uid )
+            if status == true then
+                local status,data = read(addr)
+                if status == true then
+                    return true, data
+                end
+                rc522.halt()
+            end
+        end
+    end
+    return false
+end
+
+--[[
+rc522 初始化
+@api rc522.init(spi_id, cs, rst)
+@number spi_id spi端口号
+@number cs      cs引脚
+@number rst     rst引脚
+@return bool 初始化结果
+@usage
+spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB,1,1)
+rc522.init(0,pin.PB04,pin.PB01)
+]]
+function rc522.init(spi_id,cs,rst)
+    rc522_spi = spi_id
+    rc522_cs = gpio.setup(cs, 0, gpio.PULLUP) 
+    rc522_cs(1)
+    rc522_rst = gpio.setup(rst, 0, gpio.PULLUP) 
+    rc522_rst(1)
+    rc522.reset()
+    rc522_config_isotype()
+    log.debug("rc522.version",rc522.version())
+    return true
+end
+
+function rc522.test()
+
+end
+
+return rc522
+
+
+

+ 269 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/rtkv.lua

@@ -0,0 +1,269 @@
+--[[
+@module rtkv
+@summary 远程KV数据库
+@version 1.0
+@date    2023.07.17
+@author  wendal
+@tag LUAT_USE_NETWORK
+@usage
+
+-- 是否还在为上报几个数据值而烦恼?
+-- 是否还在为数据存入数据库而头痛不已?
+-- 没有外网服务器, 内网穿透又很麻烦?
+-- 不懂mqtt, 也没有下发需求, 只是想上报一些值?
+
+-- 那本API就很适合您
+-- 它可以:
+--   将数据存到服务器,例如温湿度,GPS坐标,GPIO状态
+--   读取服务器的数据,例如OTA信息
+--   服务器会保存历史记录,也支持绘制成图表
+-- 它不可以:
+--   实时下发数据给设备
+--   上传巨量数据
+
+-- 网站首页, 输入设备识别号就能看数据 https://rtkv.air32.cn
+-- 示例设备 http://rtkv.air32.cn/d/6055F9779010
+
+-- 场景举例1, 上报温湿度数据到服务器, 然后网站查看地址是 XXX
+rtkv.setup()
+sys.taskInit(function()
+    sys.waitUntil("IP_READY")
+    while 1 do
+        local val,result = sensor.ds18b20(17, true) 
+        if result then
+            rtkv.set("ds18b20_temp", val)
+        end
+        sys.wait(60*1000) -- 一分钟上报一次
+    end
+end)
+
+-- 场景举例2, 简易版OTA
+rtkv.setup()
+sys.taskInit(function()
+    sys.waitUntil("IP_READY")
+    sys.wait(1000)
+    while 1 do
+        local ota_version = rtkv.get("ota_version")
+        if ota_version and ota_version ~= _G.VERSION then
+            local ota_url = rtkv.get("ota_url")
+            if ota_url then
+                -- 执行OTA, 以esp32c3为例
+                local code = http.request("GET", ota_url, nil, nil, {dst="/update.bin"}).wait()
+                if code and code == 200 then
+                    log.info("ota", "ota包下载完成, 5秒后重启")
+                    sys.wait(5000)
+                    rtos.reboot()
+                end
+            end
+        end
+        sys.wait(4*3600*1000) -- 4小时检查一次
+    end
+end)
+
+-- 场景举例3, 非实时下发控制
+rtkv.setup()
+sys.taskInit(function()
+    local LED = gpio.setup(27, 0, nil, gpio.PULLUP)
+    local INPUT = gpio.setup(22, nil)
+    sys.waitUntil("IP_READY")
+    sys.wait(1000)
+    while 1 do
+        local gpio27 = rtkv.get("gpio27")
+        if gpio27 then
+            LED(gpio27 == "1" and 1 or 0)
+        end
+        rtkv.set("gpio22", INPUT()) -- 上报GPIO22的状态
+        sys.wait(15*1000) -- 15秒查询一次
+    end
+end)
+]]
+
+local rtkv = {}
+
+--[[
+rtkv初始化
+@api rtkv.setup(conf)
+@table 配置信息,详细说明看下面的示例
+@return nil 没有返回值
+@usage
+-- 本函数只需要调用一次, 通常在main.lua里
+
+-- 默认初始化, 开启了调试日志
+rtkv.setup()
+-- 初始化,并关闭调试日志
+rtkv.setup({nodebug=true})
+-- 详细初始化, 可以只填需要配置的项
+rtkv.setup({
+    apiurl = "http://rtkv.air32.cn", -- 服务器地址,可以自行部署 https://gitee.com/openLuat/luatos-service-rtkv
+    device = "abc", -- 设备识别号,只能是英文字符+数值,区别大小写
+    token = "123456", -- 设备密钥, 默认是设备的唯一id, 即mcu.unique_id()
+    nodebug = false,  -- 关闭调试日志,默认false
+    timeout = 3000, -- 请求超时, 单位毫秒, 默认3000毫秒
+})
+
+-- 关于device值的默认值
+-- 若支持4G, 会取IMEI
+-- 若支持wifi, 会取MAC
+-- 其余情况取 mcu.unique_id() 即设备的唯一id
+]]
+function rtkv.setup(conf)
+    if not conf then
+        conf = {}
+    end
+    rtkv.conf = conf
+    if not rtkv.conf.apiurl then
+        conf.apiurl = "http://rtkv.air32.cn"
+    end
+    if not conf.device then
+        if mobile then
+            conf.device = mobile.imei()
+        elseif wlan then
+            conf.device = wlan.getMac()
+        else
+            conf.device = mcu.unique_id():toHex()
+        end
+    end
+    if not conf.token then
+        conf.token = mcu.unique_id():toHex()
+    end
+    if not conf.timeout then
+        conf.timeout = 3000
+    end
+    if not conf.nodebug then
+        -- log.info("rtkv", "apiurl", conf.apiurl)
+        log.info("rtkv", "device", conf.device)
+        log.info("rtkv", "token", conf.token)
+        log.info("rtkv", "pls visit", conf.apiurl .. "/d/" .. conf.device)
+    end
+    return true
+end
+
+--[[
+设置指定键对应的值
+@api rtkv.set(key, value)
+@string 键, 不能为nil,建议只使用英文字母/数字
+@string 值, 不能为nil,一般建议不超过512字节
+@return bool   成功返回true, 否则返回nil
+@usage
+
+-- 如果关心执行结果, 则需要在task里执行
+-- 非task上下文, 会返回nil, 然后后台执行
+rtkv.set("age", "18")
+rtkv.set("version", _G.VERSION)
+rtkv.set("project", _G.PROJECT)
+
+-- 关于值的类型的说明
+-- 支持传入字符串,布尔值,整数,浮点数, 最终还是会转为字符串上传
+-- 通过 rtkv.get 获取值的时候, 返回的值的类型也会是字符串
+]]
+function rtkv.set(key, value)
+    if not rtkv.conf or not key or not value then
+        return
+    end
+    local url = rtkv.conf.apiurl .. "/api/rtkv/set?"
+    url = url .. "device=" .. rtkv.conf.device
+    url = url .. "&token=" .. rtkv.conf.token
+    url = url .. "&key=" .. tostring(key):urlEncode()
+    url = url .. "&value=" .. tostring(value):urlEncode()
+    if rtkv.conf.debug then
+        log.debug("rtkv", url)
+    end
+    local co, ismain = coroutine.running()
+    if ismain then
+        sys.taskInit(http.request, "GET", url)
+    else
+        local code, headers, body = http.request("GET", url, nil, nil, {timeout=rtkv.conf.timeout}).wait()
+        if rtkv.conf.debug then
+            log.info("rtkv", code, body)
+        end
+        if code and code == 200 and body == "ok" then
+            return true
+        end
+    end
+end
+
+--[[
+批量设置键值
+@api rtkv.sets(datas)
+@table 需要设置的键值对
+@return bool   成功返回true, 否则返回nil
+@usage
+-- 如果关心执行结果, 则需要在task里执行
+-- 非task上下文, 会返回nil, 然后后台执行
+rtkv.sets({
+    age = "18",
+    vbat = 4193,
+    temp = 23423
+})
+]]
+function rtkv.sets(datas)
+    local conf = rtkv.conf
+    if not conf or not datas then
+        return
+    end
+    local url = conf.apiurl .. "/api/rtkv/sets"
+    local rbody = json.encode({
+        device = conf.device,
+        token  = conf.token,
+        data = datas
+    })
+    if not rbody then
+        log.info("rtkv", "rbody is nil")
+        return
+    end
+    if not conf.nodebug then
+        log.debug("rtkv", url, rbody)
+    end
+    local rheaders = {}
+    rheaders["Content-Type"] = "application/json"
+    local co, ismain = coroutine.running()
+    if ismain then
+        sys.taskInit(http.request, "POST", url, rheaders, rbody, {timeout=conf.timeout})
+    else
+        local code, headers, body = http.request("POST", url, rheaders, rbody, {timeout=conf.timeout}).wait()
+        if not conf.nodebug then
+            log.info("rtkv", code, body)
+        end
+        if code and code == 200 and body == "ok" then
+            return true
+        end
+    end
+end
+
+--[[
+获取指定键对应的值
+@api rtkv.get(key)
+@string 键, 不能为nil,长度需要2字节以上
+@return string 成功返回字符,其他情况返回nil
+@usage
+-- 注意, 必须在task里执行,否则必返回nil
+local age = rtkv.get("age")
+]]
+function rtkv.get(key)
+    local conf = rtkv.conf
+    if not conf or key then
+        return
+    end
+    local url = conf.apiurl .. "/api/rtkv/get?"
+    url = url .. "device=" .. conf.device
+    url = url .. "&token=" .. conf.token
+    url = url .. "&key=" .. tostring(key):urlEncode()
+    if not conf.nodebug then
+        log.debug("rtkv", "url", url)
+    end
+    local co, ismain = coroutine.running()
+    if ismain then
+        log.warn("rtkv", "must call in a task/thread")
+        return
+    else
+        local code, headers, body = http.request("GET", url, nil, nil, {timeout=conf.timeout}).wait()
+        if not conf.nodebug then
+            log.info("rtkv", code, body)
+        end
+        if code and code == 200 and body == "ok" then
+            return true
+        end
+    end
+end
+
+return rtkv

+ 189 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/sc7a20.lua

@@ -0,0 +1,189 @@
+--[[
+@module sc7a20
+@summary sc7a20 
+@version 1.0
+@date    2022.04.11
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local sc7a20 = require "sc7a20"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    sc7a20.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local sc7a20_data = sc7a20.get_data()
+        log.info("sc7a20_data", "sc7a20_data.x"..(sc7a20_data.x),"sc7a20_data.y"..(sc7a20_data.y),"sc7a20_data.z"..(sc7a20_data.z))
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local sc7a20 = {}
+local sys = require "sys"
+local i2cid
+
+local SC7A20_ADDRESS_ADR                    -- sc7a20设备地址
+
+local SC7A20_ADDRESS_ADR_LOW     =   0x18
+local SC7A20_ADDRESS_ADR_HIGH    =   0x19
+local SC7A20_CHIP_ID_CHECK       =   0x0f   -- 器件ID检测
+local SC7A20_CHIP_ID             =   0x11   -- 器件ID
+
+local SC7A20_REG_CTRL_REG1       =   0x20   -- 控制寄存器1
+local SC7A20_REG_CTRL_REG2       =   0x21   -- 控制寄存器2
+local SC7A20_REG_CTRL_REG3       =   0x22   -- 控制寄存器3
+local SC7A20_REG_CTRL_REG4       =   0x23   -- 控制寄存器4
+local SC7A20_REG_CTRL_REG5       =   0x24   -- 控制寄存器5
+local SC7A20_REG_CTRL_REG6       =   0x25   -- 控制寄存器6
+local SC7A20_REG_REFERENCE       =   0x26   -- 参考寄存器
+local SC7A20_REG_STATUS_REG      =   0x27   -- 状态寄存器
+local SC7A20_REG_OUT_X_L         =   0x28   -- X轴数据低字节
+local SC7A20_REG_OUT_X_H         =   0x29   -- X轴数据高字节
+local SC7A20_REG_OUT_Y_L         =   0x2A   -- Y轴数据低字节
+local SC7A20_REG_OUT_Y_H         =   0x2B   -- Y轴数据高字节
+local SC7A20_REG_OUT_Z_L         =   0x2C   -- Z轴数据低字节
+local SC7A20_REG_OUT_Z_H         =   0x2D   -- Z轴数据高字节
+local SC7A20_REG_FIFO_CTRL_REG   =   0x2E   -- FIFO控制寄存器
+local SC7A20_REG_FIFO_SRC_REG    =   0x2F   -- FIFO源寄存器
+local SC7A20_REG_INT1_CFG        =   0x30   -- 中断1配置寄存器
+local SC7A20_REG_INT1_SRC        =   0x31   -- 中断1源寄存器
+local SC7A20_REG_INT1_THS        =   0x32   -- 中断1阈值寄存器
+local SC7A20_REG_INT1_DURATION   =   0x33   -- 中断1持续时间寄存器
+local SC7A20_REG_INT2_CFG        =   0x34   -- 中断2配置寄存器
+local SC7A20_REG_INT2_SRC        =   0x35   -- 中断2源寄存器
+local SC7A20_REG_INT2_THS        =   0x36   -- 中断2阈值寄存器
+local SC7A20_REG_INT2_DURATION   =   0x37   -- 中断2持续时间寄存器
+local SC7A20_REG_CLICK_CFG       =   0x38   -- 单击配置寄存器
+local SC7A20_REG_CLICK_SRC       =   0x39   -- 单击源寄存器
+local SC7A20_REG_CLICK_THS       =   0x3A   -- 单击阈值寄存器
+local SC7A20_REG_TIME_LIMIT      =   0x3B   -- 单击时间限制寄存器
+local SC7A20_REG_TIME_LATENCY    =   0x3C   -- 单击时间延迟寄存器
+local SC7A20_REG_TIME_WINDOW     =   0x3D   -- 单击时间窗口寄存器
+local SC7A20_REG_ACT_THS         =   0x3E   -- 活动阈值寄存器
+local SC7A20_REG_ACT_DUR         =   0x3F   -- 活动持续时间寄存器
+
+
+--器件ID检测
+local function chip_check()
+    i2c.send(i2cid, SC7A20_ADDRESS_ADR_LOW, SC7A20_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, SC7A20_ADDRESS_ADR_LOW, 1)
+    if revData:byte() ~= nil then
+        SC7A20_ADDRESS_ADR = SC7A20_ADDRESS_ADR_LOW
+    else
+        i2c.send(i2cid, SC7A20_ADDRESS_ADR_HIGH, SC7A20_CHIP_ID_CHECK)--读器件地址
+        sys.wait(50)
+        local revData = i2c.recv(i2cid, SC7A20_ADDRESS_ADR_HIGH, 1)
+        if revData:byte() ~= nil then
+            SC7A20_ADDRESS_ADR = SC7A20_ADDRESS_ADR_HIGH
+        else
+            log.info("i2c", "Can't find sc7a20 device")
+            return false
+        end
+    end
+    i2c.send(i2cid, SC7A20_ADDRESS_ADR, SC7A20_CHIP_ID_CHECK)--读器件地址
+    sys.wait(50)
+    local revData = i2c.recv(i2cid, SC7A20_ADDRESS_ADR, 1)
+    if revData:byte() == SC7A20_CHIP_ID then
+        log.info("Device i2c id is: SC7A20")
+    else
+        log.info("i2c", "Can't find sc7a20 device")
+        return false
+    end
+    return true
+end
+
+
+--[[
+sc7a20 初始化
+@api sc7a20.init(i2c_id)
+@number i2c id
+@return bool   成功返回true
+@usage
+sc7a20.init(0)
+]]
+function sc7a20.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(20)--20 毫秒等待设备稳定
+    if chip_check() then
+        i2c.send(i2cid, SC7A20_ADDRESS_ADR, {SC7A20_REG_CTRL_REG1,0X47})
+        i2c.send(i2cid, SC7A20_ADDRESS_ADR, {SC7A20_REG_CTRL_REG2,0X00})
+        i2c.send(i2cid, SC7A20_ADDRESS_ADR, {SC7A20_REG_CTRL_REG3,0X00})
+        i2c.send(i2cid, SC7A20_ADDRESS_ADR, {SC7A20_REG_CTRL_REG4,0X88})
+        i2c.send(i2cid, SC7A20_ADDRESS_ADR, {SC7A20_REG_CTRL_REG6,0X00})
+        log.info("sc7a20 init_ok")
+        sys.wait(20)
+        return true
+    end
+    return false
+end
+
+--[[
+获取 sc7a20 数据
+@api sc7a20.get_data()
+@return table sc7a20 数据
+@usage
+local sc7a20_data = sc7a20.get_data()
+log.info("sc7a20_data", "sc7a20_data.x"..(sc7a20_data.x),"sc7a20_data.y"..(sc7a20_data.y),"sc7a20_data.z"..(sc7a20_data.z))
+]]
+function sc7a20.get_data()
+    local accel={x=nil,y=nil,z=nil}
+    i2c.send(i2cid, SC7A20_ADDRESS_ADR,SC7A20_REG_OUT_X_L)
+    _,accel.x = pack.unpack(i2c.recv(i2cid, SC7A20_ADDRESS_ADR, 2),">h")
+    i2c.send(i2cid, SC7A20_ADDRESS_ADR,SC7A20_REG_OUT_Y_L)
+    _,accel.y = pack.unpack(i2c.recv(i2cid, SC7A20_ADDRESS_ADR, 2),">h")
+    i2c.send(i2cid, SC7A20_ADDRESS_ADR,SC7A20_REG_OUT_Z_L)
+    _,accel.z = pack.unpack(i2c.recv(i2cid, SC7A20_ADDRESS_ADR, 2),">h")
+    return accel
+end
+
+
+--[[
+设置 sc7a20 活动阀值
+@api sc7a20.set_thresh (i2cid, activity, time_inactivity)
+@number i2c id
+@number 活动阀值
+@number 活动持续时间
+@usage
+sc7a20.set_thresh(0, string.char(0x05), string.char(0x05)) 
+]]
+function sc7a20.set_thresh(i2cid, activity, time_inactivity)
+    i2c.writeReg(i2cid, SC7A20_ADDRESS_ADR, SC7A20_REG_ACT_THS, activity)
+    i2c.writeReg(i2cid, SC7A20_ADDRESS_ADR, SC7A20_REG_ACT_DUR, time_inactivity)
+end
+
+
+--[[
+设置 sc7a20 中断设置
+@api sc7a20.set_irqf(i2cid, int, irqf_ths, irqf_duration, irqf_cfg)
+@number i2c id
+@number 中断脚 传入1及配置INT1脚,传入2及配置INT2脚
+@number 中断阀值
+@number 中断持续时间
+@number 中断配置
+@usage
+sc7a20.set_irqf(0, 1, string.char(0x05), string.char(0x05), string.char(0x00))
+]]
+function sc7a20.set_irqf(i2cid, int, irqf_ths, irqf_duration, irqf_cfg)
+    if int == 1 then
+        i2c.send(i2cid, SC7A20_ADDRESS_ADR, {SC7A20_REG_CTRL_REG3,0X40})        -- AOI1中断映射到INT1上
+        i2c.writeReg(i2cid, SC7A20_ADDRESS_ADR, SC7A20_REG_INT1_THS, irqf_ths)
+        i2c.writeReg(i2cid, SC7A20_ADDRESS_ADR, SC7A20_REG_INT1_DURATION, irqf_duration)
+        i2c.writeReg(i2cid, SC7A20_ADDRESS_ADR, SC7A20_REG_INT1_CFG, irqf_cfg)
+    elseif int == 2 then
+        i2c.send(i2cid, SC7A20_ADDRESS_ADR, {SC7A20_REG_CTRL_REG6,0X42})        -- AOI2中断映射到INT2上 并且配置低电平触发中断
+        i2c.writeReg(i2cid, SC7A20_ADDRESS_ADR, SC7A20_REG_INT2_THS, irqf_ths)
+        i2c.writeReg(i2cid, SC7A20_ADDRESS_ADR, SC7A20_REG_INT2_DURATION, irqf_duration)
+        i2c.writeReg(i2cid, SC7A20_ADDRESS_ADR, SC7A20_REG_INT2_CFG, irqf_cfg)
+    else
+        log.info("sc7a20", "int Parameter error")
+    end
+end
+
+
+return sc7a20
+
+

+ 103 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/shift595.lua

@@ -0,0 +1,103 @@
+--[[
+@module shift595
+@summary shift595 74HC595芯片
+@version 1.0
+@date    2023.08.30
+@author  lulipro
+@usage
+--注意:
+--1、初始化时必须提供sclk移位时钟引脚和dat数据引脚,rclk根据应用需求可选
+--2、AIR101官方核心板,底层为LuatOS-SoC_V0017_AIR101.soc,经测试此脚本库的串行时钟频率为18KHz
+--用法实例:
+--硬件模块:双595驱动的共阳极4位数码管
+local shift595 = require("shift595")
+sys.taskInit(function() 
+
+    shift595.init(pin.PB08,pin.PB09,pin.PB10)  -- sclk,dat,rclk
+    
+    while 1 do
+        local wei = 1
+        for i = 0, 3, 1 do
+            shift595.out(0x82,shift595.MSB)--发送段数据 ,然后是位选数据
+            shift595.out(wei,shift595.MSB)--发送段数据 ,然后是位选数据
+            shift595.latch() --锁存
+            wei = wei<<1
+            sys.wait(500)
+        end
+        sys.wait(1000)
+    end
+end
+)
+]]
+
+local shift595 = {}
+
+local sys = require "sys"
+
+shift595.MSB=0     --字节串行输出时先发送最高位
+shift595.LSB=1     --字节串行输出时先发送最低位
+
+local SHIFT595_SCLK     --串行移位时钟引脚
+local SHIFT595_DAT      --串行数据引脚
+local SHIFT595_RCLK     --锁存信号时钟引脚
+
+
+--[[
+75hc595芯片初始化
+@api shift595.init(sclk,dat,rclk)
+@number sclk,定义驱动595串行时钟信号的引脚
+@number dat,定义驱动595串行数据的引脚
+@number rclk,定义驱动595锁存信号的引脚,可选
+@usage
+shift595.init(pin.PB08,pin.PB09,pin.PB10)  -- sclk,dat,rclk
+]]
+function shift595.init(sclk,dat,rclk)
+    SHIFT595_SCLK = gpio.setup(sclk, 1)
+    SHIFT595_DAT  = gpio.setup(dat, 1)
+    
+    if rclk then
+        SHIFT595_RCLK = gpio.setup(rclk, 1)
+    else
+        SHIFT595_RCLK = nil
+    end
+end
+
+
+--[[
+串行输出一个字节到74hc595芯片的移位寄存器中
+@api shift595.out(dat,endian)
+@number dat,发送的字节数据
+@number endian,指定发送字节数据时的大小端模式,有shift595.MSB和shift595.LSB两种参数可选。默认shift595.MSB
+@usage
+shift595.out(0x82,shift595.MSB)
+shift595.out(0x82)  --默认shift595.MSB,与上面等价
+]]
+function shift595.out(dat,endian)
+    local mbit
+    for i = 0, 7, 1 do
+        SHIFT595_SCLK(0)
+        if endian == shift595.LSB then
+            mbit = ((dat>>i)&0x01~=0) and 1 or 0
+        else
+            mbit = ((dat<<i)&0x80~=0) and 1 or 0
+        end
+        SHIFT595_DAT(mbit)
+        SHIFT595_SCLK(1)
+    end
+end
+
+
+--[[
+给74hc595芯片的RCLK线一个高脉冲,使得移位寄存器中的数据转移到锁存器中,当OE使能时,数据就输出到QA~QH引脚上。如果初始化时没用到rclk引脚则此函数调用无效。
+@api shift595.latch()
+@usage
+shift595.latch()
+]]
+function shift595.latch()
+    if SHIFT595_RCLK then
+        SHIFT595_RCLK(0)
+        SHIFT595_RCLK(1)
+    end
+end
+
+return shift595

+ 383 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/si24r1.lua

@@ -0,0 +1,383 @@
+--[[
+@module si24r1
+@summary si24r1 驱动
+@version 1.0
+@date    2022.06.17
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local si24r1 = require "si24r1"
+
+sys.taskInit(function()
+    spi_si24r1 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB,1,1)
+    si24r1.init(0,pin.PB04,pin.PB01,pin.PB00)
+    if si24r1.chip_check() then
+        si24r1.set()
+    end
+
+    --发送示例
+    -- si24r1.set_mode( si24r1.MODE_TX );		--发送模式	
+    -- while 1 do
+    --     sys.wait(1000)
+    --     local a = si24r1.txpacket("si24r1test")
+    --     print("a",a)
+    -- end
+
+    --接收示例 
+    si24r1.set_mode( si24r1.MODE_RX );		--接收模式	
+    while 1 do
+        local i,data = si24r1.rxpacket( );		--接收字节
+        print("rxbuf",i,data)
+    end
+end)
+]]
+
+
+local si24r1 = {}
+local sys = require "sys"
+
+local si24r1_device
+local si24r1_spi
+local si24r1_ce
+local si24r1_cs
+local si24r1_irq
+
+local si24r1_cspin,si24r1_cepin,si24r1_irqpin
+
+local LM75_ADDRESS_ADR         =   0x48
+
+---器件所用地址
+local    REPEAT_CNT          =  15		--重复次数
+local    INIT_ADDR           =  string.char(0x34,0x43,0x10,0x10,0x01)
+
+si24r1.MODE_TX = 0
+si24r1.MODE_RX = 1
+
+local    NRF_READ_REG       =   0x00	--读配置寄存器,低5位为寄存器地址
+local    NRF_WRITE_REG      =   0x20	--写配置寄存器,低5位为寄存器地址
+local    RD_RX_PLOAD        =   0x61	--读RX有效数据,1~32字节
+local    WR_TX_PLOAD        =   0xA0	--写TX有效数据,1~32字节
+local    FLUSH_TX           =   0xE1	--清除TX FIFO寄存器,发射模式下使用
+local    FLUSH_RX           =   0xE2	--清除RX FIFO寄存器,接收模式下使用
+local    REUSE_TX_PL        =   0xE3	--重新使用上一包数据,CE为高,数据包被不断发送
+local    R_RX_PL_WID        =   0x60
+local    NOP                =   0xFF	--空操作,可以用来读状态寄存器
+local    W_ACK_PLOAD	    =   0xA8
+local    WR_TX_PLOAD_NACK   =   0xB0
+
+local    CONFIG             =   0x00	--配置寄存器地址,bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能;
+                                        --bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能	
+local    EN_AA              =   0x01	--使能自动应答功能 bit0~5 对应通道0~5
+local    EN_RXADDR          =   0x02	--接收地址允许 bit0~5 对应通道0~5
+local    SETUP_AW           =   0x03	--设置地址宽度(所有数据通道) bit0~1: 00,3字节 01,4字节, 02,5字节
+local    SETUP_RETR         =   0x04	--建立自动重发;bit0~3:自动重发计数器;bit4~7:自动重发延时 250*x+86us
+local    RF_CH              =   0x05	--RF通道,bit0~6工作通道频率
+local    RF_SETUP           =   0x06	--RF寄存器,bit3:传输速率( 0:1M 1:2M);bit1~2:发射功率;bit0:噪声放大器增益
+local    STATUS             =   0x07	--状态寄存器;bit0:TX FIFO满标志;bit1~3:接收数据通道号(最大:6);bit4:达到最多次重发次数
+                                        --bit5:数据发送完成中断;bit6:接收数据中断
+local    MAX_TX  		    =   0x10	--达到最大发送次数中断
+local    TX_OK   		    =   0x20	--TX发送完成中断
+local    RX_OK   		    =   0x40	--接收到数据中断
+
+local    OBSERVE_TX         =   0x08	--发送检测寄存器,bit7~4:数据包丢失计数器;bit3~0:重发计数器
+local    CD                 =   0x09	--载波检测寄存器,bit0:载波检测
+local    RX_ADDR_P0         =   0x0A	--数据通道0接收地址,最大长度5个字节,低字节在前
+local    RX_ADDR_P1         =   0x0B	--数据通道1接收地址,最大长度5个字节,低字节在前
+local    RX_ADDR_P2         =   0x0C	--数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等
+local    RX_ADDR_P3         =   0x0D	--数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等
+local    RX_ADDR_P4         =   0x0E	--数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等
+local    RX_ADDR_P5         =   0x0F	--数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等
+local    TX_ADDR            =   0x10	--发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与地址相等
+local    RX_PW_P0           =   0x11	--接收数据通道0有效数据宽度(1~32字节),设置为0则非法
+local    RX_PW_P1           =   0x12	--接收数据通道1有效数据宽度(1~32字节),设置为0则非法
+local    RX_PW_P2           =   0x13	--接收数据通道2有效数据宽度(1~32字节),设置为0则非法
+local    RX_PW_P3           =   0x14	--接收数据通道3有效数据宽度(1~32字节),设置为0则非法
+local    RX_PW_P4           =   0x15	--接收数据通道4有效数据宽度(1~32字节),设置为0则非法
+local    RX_PW_P5           =   0x16	--接收数据通道5有效数据宽度(1~32字节),设置为0则非法
+local    NRF_FIFO_STATUS    =   0x17	--FIFO状态寄存器;bit0:RX FIFO寄存器空标志;bit1:RX FIFO满标志;bit2~3保留
+                                        --bit4:TX FIFO 空标志;bit5:TX FIFO满标志;bit6:1,循环发送上一数据包.0,不循环								
+local    DYNPD			    =   0x1C
+local    FEATRUE			=   0x1D
+
+local    MASK_RX_DR   	    =   6 
+local    MASK_TX_DS   	    =   5 
+local    MASK_MAX_RT  	    =   4 
+local    EN_CRC       	    =   3 
+local    CRCO         	    =   2 
+local    PWR_UP       	    =   1 
+local    PRIM_RX      	    =   0 
+
+local    ENAA_P5      	    =   5 
+local    ENAA_P4      	    =   4 
+local    ENAA_P3      	    =   3 
+local    ENAA_P2      	    =   2 
+local    ENAA_P1      	    =   1 
+local    ENAA_P0      	    =   0 
+
+local    ERX_P5       	    =   5 
+local    ERX_P4       	    =   4 
+local    ERX_P3       	    =   3 
+local    ERX_P2      	    =   2 
+local    ERX_P1       	    =   1 
+local    ERX_P0       	    =   0 
+
+local    AW_RERSERVED 	    =   0x0 
+local    AW_3BYTES    	    =   0x1
+local    AW_4BYTES    	    =   0x2
+local    AW_5BYTES    	    =   0x3
+
+local    ARD_250US    	    =   (0x00<<4)
+local    ARD_500US    	    =   (0x01<<4)
+local    ARD_750US    	    =   (0x02<<4)
+local    ARD_1000US   	    =   (0x03<<4)
+local    ARD_2000US   	    =   (0x07<<4)
+local    ARD_4000US   	    =   (0x0F<<4)
+local    ARC_DISABLE   	    =   0x00
+local    ARC_15        	    =   0x0F
+
+local    CONT_WAVE     	    =   7 
+local    RF_DR_LOW     	    =   5 
+local    PLL_LOCK      	    =   4 
+local    RF_DR_HIGH    	    =   3 
+-- bit2-bit1:
+local    PWR_18DB  		    =   (0x00<<1)
+local    PWR_12DB  		    =   (0x01<<1)
+local    PWR_6DB   		    =   (0x02<<1)
+local    PWR_0DB   		    =   (0x03<<1)
+
+local    RX_DR         	    =   6 
+local    TX_DS         	    =   5 
+local    MAX_RT        	    =   4 
+-- for bit3-bit1, 
+local    TX_FULL_0     	    =   0 
+
+local    RPD           	    =   0 
+
+local    TX_REUSE      	    =   6 
+local    TX_FULL_1     	    =   5 
+local    TX_EMPTY      	    =   4 
+-- bit3-bit2, reserved, only '00'
+local    RX_FULL       	    =   1 
+local    RX_EMPTY      	    =   0 
+
+local    DPL_P5        	    =   5 
+local    DPL_P4        	    =   4 
+local    DPL_P3        	    =   3 
+local    DPL_P2        	    =   2 
+local    DPL_P1        	    =   1 
+local    DPL_P0        	    =   0 
+
+local    EN_DPL        	    =   2 
+local    EN_ACK_PAY    	    =   1 
+local    EN_DYN_ACK    	    =   0 
+local    IRQ_ALL            =   ( (1<<RX_DR) | (1<<TX_DS) | (1<<MAX_RT) )
+
+local check_string = string.char(0X11, 0X22, 0X33, 0X44, 0X55)
+
+local function write_reg(address, value)
+    si24r1_cs(0)
+    if value then
+        spi.send(si24r1_spi,string.char(NRF_WRITE_REG|address).. value)
+    else
+        spi.send(si24r1_spi,string.char(NRF_WRITE_REG|address))
+    end
+    si24r1_cs(1)
+end
+
+local function read_reg(address,len)
+    si24r1_cs(0)
+    spi.send(si24r1_spi, string.char(NRF_READ_REG|address))
+    local val = spi.recv(si24r1_spi,len or 1)
+    si24r1_cs(1)
+    return val
+end
+
+--[[
+si24r1 器件检测
+@api si24r1.chip_check()
+@return bool   成功返回true
+@usage
+if si24r1.chip_check() then
+    si24r1.set()
+end
+]]
+function si24r1.chip_check()
+    write_reg(TX_ADDR, check_string)
+    local recv_string = read_reg(TX_ADDR,5)
+    if recv_string == check_string then
+        return true
+    end
+    log.info("si24r1","Can't find si24r1 device")
+    return false
+end
+
+local function read_status_register()
+    return read_reg(NRF_READ_REG + STATUS);
+end
+
+local function clear_iqr_flag(IRQ_Source)
+    local btmp = 0;
+    IRQ_Source = IRQ_Source & ( 1 << RX_DR ) | ( 1 << TX_DS ) | ( 1 << MAX_RT );	--中断标志处理
+    btmp = read_status_register():byte(1);			--读状态寄存器
+    write_reg(NRF_WRITE_REG + STATUS)
+    write_reg(IRQ_Source | btmp)
+    return ( read_status_register():byte(1))			--返回状态寄存器状态
+end
+
+local function set_txaddr( pAddr )
+    write_reg( TX_ADDR, pAddr)	--写地址
+end
+
+local function set_rxaddr( PipeNum,pAddr )
+    write_reg( RX_ADDR_P0 + PipeNum, pAddr)	--写地址
+end
+
+--[[
+si24r1 设置模式
+@api si24r1.set_mode( Mode )
+@number Mode si24r1.MODE_TX si24r1.MODE_RX
+@usage
+si24r1.set_mode( si24r1.MODE_TX )
+]]
+function si24r1.set_mode( Mode )
+    local controlreg = 0;
+	controlreg = read_reg( CONFIG ):byte(1);
+    if ( Mode == si24r1.MODE_TX ) then       
+		controlreg =controlreg & ~( 1<< PRIM_RX );
+    elseif ( Mode == si24r1.MODE_RX ) then 
+		controlreg = controlreg|( 1<< PRIM_RX ); 
+	end
+    write_reg( CONFIG, string.char(controlreg) );
+end
+
+--[[
+si24r1 发送
+@api si24r1.txpacket(buff)
+@string buff 
+@return number 0x20:发送成功 0x10:达到最大发送次数中断 0xff:发送失败
+@usage
+local a = si24r1.txpacket("si24r1test")
+]]
+function si24r1.txpacket(buff)
+    local l_Status = 0
+    local l_RxLength = 0
+    local l_10MsTimes = 0
+    
+    spi.send(si24r1_spi,string.char(FLUSH_TX))
+    si24r1_ce(0)
+    write_reg(WR_TX_PLOAD, buff)
+    si24r1_ce(1)
+
+	while( 0 ~= si24r1_irq())do
+		sys.wait(10)
+        if( 50 == l_10MsTimes )then		
+            si24r1.init(si24r1_spi,si24r1_cspin,si24r1_cepin,si24r1_irqpin)
+            si24r1.set()
+			si24r1.set_mode( si24r1.MODE_TX )
+			break;
+        end
+        l_10MsTimes = l_10MsTimes+1
+	end
+    l_Status = read_reg( STATUS )		--读状态寄存器
+	write_reg( STATUS,l_Status )		--清中断标志
+
+	if( l_Status:byte(1) & MAX_TX )~=0then	--接收到数据
+		write_reg( FLUSH_TX,string.char(0xff) )				--清除TX FIFO
+		return MAX_TX
+    end
+
+    if( l_Status:byte(1) & TX_OK ~=0 )~=0then	--接收到数据
+		return TX_OK
+    end
+	return 0xFF
+end
+
+--[[
+si24r1 接收
+@api si24r1.rxpacket()
+@return number len,buff 长度 数据
+@usage
+local i,data = si24r1.rxpacket()		--接收字节
+print("rxbuf",i,data)
+]]
+function si24r1.rxpacket()
+	local l_Status = 0
+    local l_RxLength = 0
+    local l_100MsTimes = 0
+
+    spi.send(si24r1_spi,string.char(FLUSH_RX))
+	
+	while( 0 ~= si24r1_irq())do
+		sys.wait( 100 )
+		if( 30 == l_100MsTimes )then		--3s没接收过数据,重新初始化模块
+            si24r1.init(si24r1_spi,si24r1_cspin,si24r1_cepin,si24r1_irqpin)
+            si24r1.set()
+			si24r1.set_mode( si24r1.MODE_RX )
+			break;
+        end
+        l_100MsTimes = l_100MsTimes+1
+	end
+	l_Status = read_reg( STATUS )		--读状态寄存器
+	write_reg( STATUS,l_Status )		--清中断标志
+	if( l_Status:byte(1) & RX_OK ~=0 )~=0then	--接收到数据
+		l_RxLength = read_reg( R_RX_PL_WID )		--读取接收到的数据个数
+		local rxbuf = read_reg( RD_RX_PLOAD,l_RxLength:byte(1) )	--接收到数据 
+		write_reg( FLUSH_RX,string.char(0xff) )				--清除RX FIFO
+		return l_RxLength:byte(1),rxbuf
+    end
+	return 0;				--没有收到数据	
+end
+
+--[[
+si24r1 配置参数
+@api si24r1.set()
+@usage
+si24r1.set()
+]]
+function si24r1.set()
+    si24r1_ce(1)
+    clear_iqr_flag(IRQ_ALL)
+
+    write_reg( DYNPD, string.char( 1 << 0 ) )	--使能通道1动态数据长度
+    write_reg( FEATRUE, string.char(0x07) )
+    write_reg( DYNPD )
+    write_reg( FEATRUE )
+
+    write_reg( CONFIG,string.char(( 1 << EN_CRC ) |   ( 1 << PWR_UP )) )
+    write_reg( EN_AA, string.char( 1 << ENAA_P0 ) )   		--通道0自动应答
+    write_reg( EN_RXADDR, string.char( 1 << ERX_P0 ) )		--通道0接收
+    write_reg( SETUP_AW, string.char(AW_5BYTES) )     			--地址宽度 5个字节
+    write_reg( SETUP_RETR, string.char(ARD_4000US | ( REPEAT_CNT & 0x0F )) )         	--重复等待时间 250us
+    write_reg( RF_CH, string.char(60) )             			--初始化通道
+    write_reg( RF_SETUP, string.char(0x26) )
+
+    set_txaddr( INIT_ADDR)                      --设置TX地址
+    set_rxaddr( 0, INIT_ADDR)                   --设置RX地址
+end
+
+--[[
+si24r1 初始化
+@api si24r1.init(spi_id,cs,ce,irq)
+@number spi_id spi_id
+@return bool   成功返回true
+@usage
+lm75_data.init(0)
+]]
+function si24r1.init(spi_id,cs,ce,irq)
+    -- si24r1_device = spi_device
+    si24r1_spi = spi_id
+    si24r1_cspin = cs
+    si24r1_cepin = ce
+    si24r1_irqpin = irq
+
+    si24r1_cs = gpio.setup(si24r1_cspin, 0, gpio.PULLUP) 
+    si24r1_cs(1)
+    si24r1_irq= gpio.setup(si24r1_irqpin, nil,gpio.PULLUP)
+    si24r1_ce= gpio.setup(si24r1_cepin, 0)
+    si24r1_ce(0)
+end
+
+return si24r1
+
+

+ 252 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/spl06.lua

@@ -0,0 +1,252 @@
+--[[
+@module spl06
+@summary spl06_01 气压传感器
+@version 1.0
+@date    2022.08.01
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local spl06 = require "spl06"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    spl06.init(i2cid)--初始化,传入i2cid
+    while 1 do
+        local spl06_data = spl06.get_data()
+        log.info("spl06_data", "spl06_data.P:"..(spl06_data.P*100),"spl06_data.T"..(spl06_data.T))
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local spl06 = {}
+local sys = require "sys"
+local i2cid
+
+local SPL06_ADDRESS_ADR
+local SPL06_ADDRESS_ADR_LOW       =   0x76
+local SPL06_ADDRESS_ADR_HIGH      =   0x77
+
+---器件所用地址
+local SPL06_CHIP_ID_CHECK           = 0x0D
+local SPL06_CHIP_ID                 = 0x10
+
+---i2c读数据
+local function i2cRead(i2caddr,regaddr,size)
+    ---读寄存器
+    i2c.send(i2cid,i2caddr,regaddr)
+    ---读数据
+    --return string.toHex(i2c.recv(i2cId,i2caddr,size))
+    local _,data = pack.unpack(i2c.recv(i2cid,i2caddr,size),'b1')
+    return data
+end
+
+---2个数据和3个数据的高低位顺序不一样---
+---i2c读连续地址3个数据
+local function i2cReadThreeData(i2caddr,regaddr)
+    --log.info("i2cReadThreeData","addr:"..i2caddr.." reg:"..regaddr)
+    local msb = i2cRead(i2caddr,regaddr,1)
+    local lsb = i2cRead(i2caddr,regaddr+1,1)
+    local xlsb = i2cRead(i2caddr,regaddr+2,1)
+    if msb == "" or lsb == "" or xlsb == "" or msb == nil or lsb == nil or xlsb == nil then
+        msb = 0
+        lsb = 0
+        xlsb = 0
+        log.info("spl06 three data","the data is null")
+        --return msb4096+lsb16+xlsb/16
+        return msb<<16 + lsb<<8 + xlsb
+    else
+    --log.info("spl06 three data","msb:"..msb.." lsb:"..lsb.." xlsb:"..xlsb)
+        if regaddr ==0x13 then
+            return bit.lshift(msb,12) + bit.lshift(lsb,4) + bit.rshift((xlsb&0xF0),4)
+        elseif regaddr ==0x15 then
+            return bit.lshift((msb&0x0f),16) + bit.lshift(lsb,8) +xlsb
+        else
+            --return msb4096+lsb16+xlsb/16
+            return bit.lshift(msb,16) + bit.lshift(lsb,8) + xlsb
+        end
+    end
+end
+
+---i2c读连续地址2个数据
+local function i2cReadTwoData(i2caddr,regaddr)
+    local msb = i2cRead(i2caddr,regaddr,1)
+    local lsb = i2cRead(i2caddr,regaddr+1,1)
+    if lsb == "" or msb == "" or lsb == nil or msb == nil then
+        msb = 0
+        lsb = 0
+        log.info("spl06 two data","the data is null")
+        return msb256+lsb
+    else
+    --log.info("spl06 two data","msb:"..msb.." lsb:"..lsb)
+        if regaddr ==0x10 then
+            return msb16 + bit.rshift((lsb&0xF0),4)
+        elseif regaddr ==0x11 then
+            return (msb&0x0f)*256 + lsb
+        else
+            return msb*256+lsb
+        end
+    end
+end
+    
+
+--器件ID检测
+local function chip_check()
+    i2c.send(i2cid, SPL06_ADDRESS_ADR_HIGH, SPL06_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, SPL06_ADDRESS_ADR_HIGH, 1)
+    if revData:byte() ~= nil then
+        SPL06_ADDRESS_ADR = SPL06_ADDRESS_ADR_HIGH
+    else
+        i2c.send(i2cid, SPL06_ADDRESS_ADR_LOW, SPL06_CHIP_ID_CHECK)--读器件地址
+        sys.wait(50)
+        local revData = i2c.recv(i2cid, SPL06_ADDRESS_ADR_LOW, 1)
+        if revData:byte() ~= nil then
+            SPL06_ADDRESS_ADR = SPL06_ADDRESS_ADR_LOW
+        else
+            log.info("i2c", "Can't find adxl34x device")
+            return false
+        end
+    end
+    i2c.send(i2cid, SPL06_ADDRESS_ADR, SPL06_CHIP_ID_CHECK)--读器件地址
+    sys.wait(50)
+    local revData = i2c.recv(i2cid, SPL06_ADDRESS_ADR, 1)
+    if revData:byte() == SPL06_CHIP_ID then
+        log.info("Device i2c id is: SPL06")
+    else
+        log.info("i2c", "Can't find SPL06 device")
+        return false
+    end
+    return true
+end
+
+--[[
+spl06初始化
+@api spl06.init(i2cid)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+spl06.init(0)
+]]
+function spl06.init(i2cid)
+    i2cid = i2c_id
+    sys.wait(20)--20 毫秒等待设备稳定
+    if chip_check() then
+        i2c.send(i2cid, SPL06_ADDRESS_ADR,{0x0c,0x89})--reset
+        log.info("spl06 init_ok")
+        sys.wait(20)
+        return true
+    end
+    return false
+end
+
+
+--[[
+获取spl06数据
+@api spl06.get_data()
+@return table spl06数据
+@usage
+local spl06_data = spl06.get_data()
+log.info("spl06_data", "spl06_data.P:"..(spl06_data.P*100),"spl06_data.T"..(spl06_data.T))
+]]
+function spl06.get_data()
+    local spl06_data={P=nil,T=nil}
+    local Total_Number_24 = 16777216.0
+    local Total_Number_20 = 1048576.0
+    local Total_Number_16 = 65535.0
+    local Total_Number_12 = 4096.0
+    ---读取压力补偿值
+    local C0  = i2cReadTwoData(SPL06_ADDRESS_ADR,0x10)
+    local C1  = i2cReadTwoData(SPL06_ADDRESS_ADR,0x11)
+    local C00 = i2cReadThreeData(SPL06_ADDRESS_ADR,0x13)
+    local C10 = i2cReadThreeData(SPL06_ADDRESS_ADR,0x15)
+    local C01 = i2cReadTwoData(SPL06_ADDRESS_ADR,0x18)
+    local C11 = i2cReadTwoData(SPL06_ADDRESS_ADR,0x1a)
+    local C20 = i2cReadTwoData(SPL06_ADDRESS_ADR,0x1c)
+    local C21 = i2cReadTwoData(SPL06_ADDRESS_ADR,0x1e)
+    local C30 = i2cReadTwoData(SPL06_ADDRESS_ADR,0x20)
+    if C0>0x800 then
+        C0 = C0 - Total_Number_12
+    end
+    if C1>0x800 then
+        C1 = C1 - Total_Number_12
+    end
+    if C00>0x80000 then
+        C00 = C00 - Total_Number_20
+    end
+    if C10>0x80000 then
+        C10 = C10 - Total_Number_20
+    end
+    if C01>0x8000 then
+        C01 = C01 - Total_Number_16
+    end
+    if C01>0x8000 then
+        C11 = C11 - Total_Number_16
+    end
+    if C20>0x8000 then
+        C20 = C20 - Total_Number_16
+    end
+    if C21>0x8000 then
+        C21 = C21 - Total_Number_16
+    end
+    if C30>0x8000 then
+        C30 = C30 - Total_Number_16
+    end
+    -- log.info("C0 is:",C0)
+    -- log.info("C1 is:",C1)
+    -- log.info("C00 is:",C00)
+    -- log.info("C10 is:",C10)
+    -- log.info("C01 is:",C01)
+    -- log.info("C11 is:",C11)
+    -- log.info("C20 is:",C20)
+    -- log.info("C21 is:",C21)
+    -- log.info("C30 is:",C30)
+
+    i2c.send(i2cid, SPL06_ADDRESS_ADR,{0x06,0x73})--PRS_CFG PM_RATE_128,TMP_PRC_8
+    i2c.send(i2cid, SPL06_ADDRESS_ADR,{0x07,0xF3})--TMP_CFG PM_RATE_128,TMP_PRC_8
+    -- id = i2cRead(SPL06_ADDRESS_ADR,0x09,1)
+    -- log.info("spl06 id:",id)
+    -- i2c.send(i2cid, SPL06_ADDRESS_ADR,{0x09,id|0x08})--oversampling times>8时必须使用
+    i2c.send(i2cid, SPL06_ADDRESS_ADR,{0x08,0x07})--连续气压温度测量
+    sys.wait(50)
+
+    local adc_P = i2cReadThreeData(SPL06_ADDRESS_ADR,0x00)
+    local adc_T = i2cReadThreeData(SPL06_ADDRESS_ADR,0x03)
+    if adc_P>0x800000 then
+        adc_P = adc_P - Total_Number_24
+    end
+    if adc_T>0x800000 then
+        adc_T = adc_T - Total_Number_24
+    end
+    --log.info("adc_P is:",adc_P)
+    --log.info("adc_T is:",adc_T)
+    if adc_P == 0 then
+        log.info("adc_P is 0")
+        return toint(9996),toint(9996)
+    end
+    if not adc_P then
+        log.info("adc_P is nil")
+        return toint(9996),toint(9996)
+    end
+
+    local kP = 7864320
+    local kT = 7864320
+    local Praw_src = adc_P / kP
+    local Traw_src = adc_T / kT
+    --log.info("Traw_src is:",Traw_src)
+    --log.info("Praw_src is:",Praw_src)
+    --计算气压
+    local qua2 = C10 + Praw_src * (C20 + Praw_src* C30)
+    local qua3 = Traw_src * Praw_src * (C11 + Praw_src * C21)
+    spl06_data.P = C00 + Praw_src * qua2 + Traw_src * C01 + qua3
+    --计算温度
+    spl06_data.T = C0*0.5 + Traw_src * C1
+
+    return spl06_data or 0
+end
+
+return spl06
+
+

+ 360 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/tcs3472.lua

@@ -0,0 +1,360 @@
+--[[
+@module tcs3472
+@summary tcs3472 颜色传感器
+@version 1.0
+@date    2022.03.14
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local tcs3472 = require "tcs3472"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    tcs3472.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local rgb_data = tcs3472.get_rgb()
+        log.info("rgb_data.R:",rgb_data.R)
+        log.info("rgb_data.G:",rgb_data.G)
+        log.info("rgb_data.B:",rgb_data.B)
+        log.info("rgb_data.C:",rgb_data.C)
+        if rgb_data.R then
+            local lux_date = tcs3472.get_lux(rgb_data)
+            log.info("lux_date:",lux_date)
+        end
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local tcs3472 = {}
+
+local sys = require "sys"
+
+local i2cid
+
+local TCS3472_ADDRESS_ADR           =   0x29
+
+---器件所用地址
+local TCS3472_CMD_BIT               =   0x80
+local TCS3472_CMD_Read_Byte         =   0x00
+local TCS3472_CMD_Read_Word         =   0x20
+local TCS3472_CMD_Clear_INT         =   0x66    -- RGBC Interrupt flag clear
+
+local TCS3472_ENABLE                =   0x00     
+local TCS3472_ENABLE_AIEN           =   0x10    -- RGBC Interrupt Enable 
+local TCS3472_ENABLE_WEN            =   0x08    -- Wait enable - Writing 1 activates the wait timer 
+local TCS3472_ENABLE_AEN            =   0x02    -- RGBC Enable - Writing 1 actives the ADC, 0 disables it 
+local TCS3472_ENABLE_PON            =   0x01    -- Power on - Writing 1 activates the internal oscillator, 0 disables it 
+
+local TCS3472_ATIME                 =   0x01    -- Integration time 
+local TCS3472_WTIME                 =   0x03    -- Wait time (if TCS34725_ENABLE_WEN is asserted) 
+local TCS3472_WTIME_2_4MS           =   0xFF    -- WLONG0 = 2.4ms   WLONG1 = 0.029s 
+local TCS3472_WTIME_204MS           =   0xAB    -- WLONG0 = 204ms   WLONG1 = 2.45s  
+local TCS3472_WTIME_614MS           =   0x00    -- WLONG0 = 614ms   WLONG1 = 7.4s   
+
+local TCS3472_AILTL                 =   0x04    -- Clear channel lower interrupt threshold 
+local TCS3472_AILTH                 =   0x05
+local TCS3472_AIHTL                 =   0x06    -- Clear channel upper interrupt threshold 
+local TCS3472_AIHTH                 =   0x07
+
+local TCS3472_PERS                  =   0x0C    -- Persistence register - basic SW filtering mechanism for interrupts 
+local TCS3472_PERS_NONE             =   0x00    -- Every RGBC cycle generates an interrupt                                
+local TCS3472_PERS_1_CYCLE          =   0x01    -- 1 clean channel value outside threshold range generates an interrupt   
+local TCS3472_PERS_2_CYCLE          =   0x02    -- 2 clean channel values outside threshold range generates an interrupt  
+local TCS3472_PERS_3_CYCLE          =   0x03    -- 3 clean channel values outside threshold range generates an interrupt  
+local TCS3472_PERS_5_CYCLE          =   0x04    -- 5 clean channel values outside threshold range generates an interrupt  
+local TCS3472_PERS_10_CYCLE         =   0x05    -- 10 clean channel values outside threshold range generates an interrupt 
+local TCS3472_PERS_15_CYCLE         =   0x06    -- 15 clean channel values outside threshold range generates an interrupt 
+local TCS3472_PERS_20_CYCLE         =   0x07    -- 20 clean channel values outside threshold range generates an interrupt 
+local TCS3472_PERS_25_CYCLE         =   0x08    -- 25 clean channel values outside threshold range generates an interrupt 
+local TCS3472_PERS_30_CYCLE         =   0x09    -- 30 clean channel values outside threshold range generates an interrupt 
+local TCS3472_PERS_35_CYCLE         =   0x0a    -- 35 clean channel values outside threshold range generates an interrupt 
+local TCS3472_PERS_40_CYCLE         =   0x0b    -- 40 clean channel values outside threshold range generates an interrupt 
+local TCS3472_PERS_45_CYCLE         =   0x0c    -- 45 clean channel values outside threshold range generates an interrupt 
+local TCS3472_PERS_50_CYCLE         =   0x0d    -- 50 clean channel values outside threshold range generates an interrupt 
+local TCS3472_PERS_55_CYCLE         =   0x0e    -- 55 clean channel values outside threshold range generates an interrupt 
+local TCS3472_PERS_60_CYCLE         =   0x0f    -- 60 clean channel values outside threshold range generates an interrupt 
+
+local TCS3472_CONFIG                =   0x0D
+local TCS3472_CONFIG_WLONG          =   0x02    -- Choose between short and long (12x) wait times via TCS34725_WTIME 
+
+local TCS3472_CONTROL               =   0x0F    -- Set the gain level for the sensor 
+local TCS3472_CHIP_ID_CHECK         =   0x12    -- 0x44 = TCS34721/TCS34725, 0x4D = TCS34723/TCS34727 
+local TCS34721_TCS34725_CHIP_ID     =   0x44
+local TCS34723_TCS34727_CHIP_ID     =   0x4D
+
+local TCS3472_STATUS                =   0x13
+local TCS3472_STATUS_AINT           =   0x10    -- RGBC Clean channel interrupt 
+local TCS3472_STATUS_AVALID         =   0x01    -- Indicates that the RGBC channels have completed an integration cycle 
+
+local TCS3472_CDATAL                =   0x14    -- Clear channel data 
+local TCS3472_CDATAH                =   0x15
+local TCS3472_RDATAL                =   0x16    -- Red channel data 
+local TCS3472_RDATAH                =   0x17
+local TCS3472_GDATAL                =   0x18    -- Green channel data 
+local TCS3472_GDATAH                =   0x19
+local TCS3472_BDATAL                =   0x1A    -- Blue channel data 
+local TCS3472_BDATAH                =   0x1B
+
+-- Offset and Compensated
+local TCS3472_R_Coef                =   0.136 
+local TCS3472_G_Coef                =   1.000
+local TCS3472_B_Coef                =   -0.444
+local TCS3472_GA                    =   1.0
+local TCS3472_DF                    =   310.0
+local TCS3472_CT_Coef               =   3810.0
+local TCS3472_CT_Offset             =   1391.0
+
+-- Integration Time
+local TCS3472_INTEGRATIONTIME_2_4MS =   0xFF    -- 2.4ms - 1 cycle    - Max Count: 1024  
+local TCS3472_INTEGRATIONTIME_24MS  =   0xF6    -- 24ms  - 10 cycles  - Max Count: 10240 
+local TCS3472_INTEGRATIONTIME_50MS  =   0xEB    -- 50ms  - 20 cycles  - Max Count: 20480 
+local TCS3472_INTEGRATIONTIME_101MS =   0xD5    -- 101ms - 42 cycles  - Max Count: 43008 
+local TCS3472_INTEGRATIONTIME_154MS =   0xC0    -- 154ms - 64 cycles  - Max Count: 65535 
+local TCS3472_INTEGRATIONTIME_700MS =   0x00    -- 700ms - 256 cycles - Max Count: 65535 
+
+--Gain
+local TCS3472_GAIN_1X               =   0x00    -- No gain  
+local TCS3472_GAIN_4X               =   0x01    -- 4x gain  
+local TCS3472_GAIN_16X              =   0x02    -- 16x gain 
+local TCS3472_GAIN_60X              =   0x03    -- 60x gain 
+
+local integrationTime_t,gain_t
+
+--[[ 
+    Writes an 8-bit value to the specified register/address
+]]
+local function tcs3472_writebyte(add, data)
+    i2c.send(i2cid, TCS3472_ADDRESS_ADR, {bit.bor(add,TCS3472_CMD_BIT), data})
+end
+
+--[[ 
+    Read an unsigned byte from the I2C device
+]]
+local function tcs3472_readbyte(add)
+    i2c.send(i2cid, TCS3472_ADDRESS_ADR, bit.bor(add,TCS3472_CMD_BIT))
+    local revData = i2c.recv(i2cid, TCS3472_ADDRESS_ADR, 1)
+    return revData:byte()
+end
+--[[ 
+    Read an unsigned word from the I2C device
+]]
+local function tcs3472_readword(add)
+    i2c.send(i2cid, TCS3472_ADDRESS_ADR, bit.bor(add,TCS3472_CMD_BIT))
+    local _,revData = pack.unpack(i2c.recv(i2cid, TCS3472_ADDRESS_ADR, 2), "<H")
+    return revData
+end
+
+--[[ 
+function:   TCS3472 wake up
+]]
+local function tcs3472_enable()
+    tcs3472_writebyte(TCS3472_ENABLE, TCS3472_ENABLE_PON)
+    -- sys.wait(3)
+    tcs3472_writebyte(TCS3472_ENABLE, bit.bor(TCS3472_ENABLE_PON,TCS3472_ENABLE_AEN))
+    sys.wait(3)
+end
+
+--[[ 
+function:   TCS3472 Sleep
+]]
+local function tcs3472_disable()
+    --Turn the device off to save power
+    local reg = tcs3472_readbyte(TCS3472_ENABLE)
+    tcs3472_writebyte(TCS3472_ENABLE, bit.band(reg,bit.bnot(bit.bor(TCS3472_ENABLE_PON,TCS3472_ENABLE_AEN))))
+end
+
+--[[ 
+function:   TCS3472 Set Integration Time
+]]
+local function tcs3472_set_integration_time(time)
+    --Update the timing register
+    tcs3472_writebyte(TCS3472_ATIME, time);
+    integrationTime_t = time;
+end
+
+--[[ 
+function:   TCS3472 Set gain
+]]
+local function tcs3472_set_gain(gain)
+    tcs3472_writebyte(TCS3472_CONTROL, gain)
+    gain_t = gain;
+end
+
+--[[ 
+function:   Interrupt Enable
+]]
+local function tcs3472_interrupt_enable()
+    local data = tcs3472_readbyte(TCS3472_ENABLE);
+    tcs3472_writebyte(TCS3472_ENABLE, bit.bor(data,TCS3472_ENABLE_AIEN))
+end
+
+--[[ 
+function:   Interrupt Disable
+]]
+local function tcs3472_interrupt_disable()
+    local data = tcs3472_readbyte(TCS3472_ENABLE);
+    tcs3472_writebyte(TCS3472_ENABLE, bit.band(data,bit.bnot(TCS3472_ENABLE_AIEN)))
+end
+
+--[[ 
+function:   Set Interrupt Persistence register, Interrupts need to be maintained 
+            for several cycles
+]]
+local function tcs3472_set_interrupt_persistence_reg(tcs3472_per)
+    if tcs3472_per < 0x10 then
+        tcs3472_writebyte(TCS3472_PERS, tcs3472_per)
+    else 
+        tcs3472_writebyte(TCS3472_PERS, TCS3472_PERS_60_CYCLE)
+    end
+end
+
+--[[ 
+function:   Set Interrupt Threshold
+parameter	:
+    Threshold_H,Threshold_L: 
+    Two 16-bit interrupt threshold registers allow the user to set limits 
+    below and above a desired light level. An interrupt can be generated 
+    when the Clear data (CDATA) is less than the Clear interrupt low 
+    threshold (AILTx) or is greater than the Clear interrupt high 
+    threshold (AIHTx)(Clear is the Clear ADC Channel Data Registers)
+]]
+local function tcs3472_set_interrupt_threshold(threshold_h, threshold_l)
+    tcs3472_writebyte(TCS3472_AILTL, bit.band(threshold_l,0xff))
+    tcs3472_writebyte(TCS3472_AILTH, bit.rshift(threshold_l, 8))
+    tcs3472_writebyte(TCS3472_AIHTL, bit.band(threshold_h,0xff))
+    tcs3472_writebyte(TCS3472_AIHTH, bit.rshift(threshold_h, 8))
+end
+
+--[[ 
+function:   Clear interrupt flag
+]]
+local function tcs3472_Clear_Interrupt_Flag()
+    tcs3472_writebyte(TCS3472_CMD_Clear_INT, 0x00)
+end
+
+--器件ID检测
+local function tcs3472_check()
+    local chip_id = tcs3472_readbyte(TCS3472_CHIP_ID_CHECK)--读器件地址
+    if chip_id == TCS34721_TCS34725_CHIP_ID then
+        log.info("Device i2c id is: TCS34721/TCS34725")
+    elseif chip_id == TCS34723_TCS34727_CHIP_ID then
+        log.info("Device i2c id is: TCS34723/TCS34727")
+    else
+        log.info("i2c", "Can't find TCS3472")
+        return false
+    end
+    return true
+end
+
+--[[
+tcs3472初始化
+@api tcs3472.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+tcs3472.init(0)
+]]
+function tcs3472.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(20)
+    if tcs3472_check() then
+        --- Set the integration time and gain
+        tcs3472_set_integration_time(TCS3472_INTEGRATIONTIME_2_4MS)
+	    tcs3472_set_gain(TCS3472_GAIN_60X);
+        --- Set Interrupt
+        -- tcs3472_set_interrupt_threshold(0xff00, 0x00ff)--Interrupt upper and lower threshold
+        -- tcs3472_set_interrupt_persistence_reg(TCS3472_PERS_2_CYCLE)
+        tcs3472_enable()
+        -- tcs3472_interrupt_enable()
+
+        sys.wait(800)
+        log.info("tsc3472 init_ok")
+        return true
+    end
+    return false
+end
+
+--[[
+获取RGB的数据
+@api tcs3472.get_rgb()
+@return table tcs3472 rgb数据
+@usage
+local rgb_data = tcs3472.get_rgb()
+log.info("rgb_data.R:",rgb_data.R)
+log.info("rgb_data.G:",rgb_data.G)
+log.info("rgb_data.B:",rgb_data.B)
+log.info("rgb_data.C:",rgb_data.C)
+]]
+function tcs3472.get_rgb()
+    local status = TCS3472_STATUS_AVALID;
+    local rgb_data={R=nil,G=nil,B=nil,C=nil}
+    status = tcs3472_readbyte(TCS3472_CDATAL)
+    if status == nil then
+        log.error("tcs3472","read status error")
+        return rgb_data
+    end
+    if bit.band(status,TCS3472_STATUS_AVALID) then
+        rgb_data.C = tcs3472_readword(TCS3472_CDATAL)
+        rgb_data.R = tcs3472_readword(TCS3472_RDATAL)
+        rgb_data.G = tcs3472_readword(TCS3472_GDATAL)
+        rgb_data.B = tcs3472_readword(TCS3472_BDATAL)
+    end
+    
+    if integrationTime_t== TCS3472_INTEGRATIONTIME_2_4MS then
+        sys.wait(3)
+    elseif integrationTime_t== TCS3472_INTEGRATIONTIME_24MS then
+        sys.wait(24)
+    elseif integrationTime_t== TCS3472_INTEGRATIONTIME_50MS then
+        sys.wait(50)
+    elseif integrationTime_t== TCS3472_INTEGRATIONTIME_101MS then
+        sys.wait(101)
+    elseif integrationTime_t== TCS3472_INTEGRATIONTIME_154MS then
+        sys.wait(154)
+    elseif integrationTime_t== TCS3472_INTEGRATIONTIME_700MS then
+        sys.wait(700)
+    end
+    return rgb_data
+end
+
+--[[
+获取lux的数据
+@api tcs3472.get_lux()
+@table  rgb_data rgb数据
+@return number lux数据
+@usage
+local lux_date = tcs3472.get_lux(rgb_data)
+log.info("lux_date:",lux_date)
+]]
+function tcs3472.get_lux(rgb)
+    local lux,cpl,atime_ms
+    local gain_temp=1
+    local ir=1
+    local r_comp,g_comp,b_comp
+    atime_ms = ((256 - integrationTime_t) * 2.4)
+    if rgb.R + rgb.G + rgb.B > rgb.C then
+        ir = (rgb.R + rgb.G + rgb.B - rgb.C) / 2
+    else
+        ir = 0
+    end
+    r_comp = rgb.R - ir
+    g_comp = rgb.G - ir
+    b_comp = rgb.B - ir
+    if gain_t == TCS3472_GAIN_1X then
+        gain_temp = 1
+    elseif gain_t == TCS3472_GAIN_4X then
+        gain_temp = 4
+    elseif gain_t == TCS3472_GAIN_16X then
+        gain_temp = 16
+    elseif gain_t == TCS3472_GAIN_60X then
+        gain_temp = 60
+    end
+    cpl = (atime_ms * gain_temp) / (TCS3472_GA * TCS3472_DF)
+    lux = (TCS3472_R_Coef * r_comp + TCS3472_G_Coef * g_comp +  TCS3472_B_Coef * b_comp) / cpl
+    return lux
+end
+
+return tcs3472

+ 167 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/tm1637.lua

@@ -0,0 +1,167 @@
+--[[
+@module tm1637
+@summary tm1637 数码管
+@version 1.0
+@date    2022.04.06
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local tm1637 = require "tm1637"
+sys.taskInit(function()
+    count = 0
+    tm1637.init(1,4)
+    tm1637.singleShow(0,2)
+    tm1637.singleShow(1,1,true)
+    tm1637.singleShow(2,3)
+    tm1637.singleShow(3,6)
+    while 1 do
+        sys.wait(1000)
+        if count > 7 then
+            count = 0
+        end
+        log.info("调整亮度", count)
+        tm1637.setLight(count)
+        count = count + 1
+    end
+end)
+]]
+local tm1637 = {}
+
+local sys = require "sys"
+
+local TM1637_SCL
+local TM1637_SDA
+
+local function i2c_init(scl,sda)
+    TM1637_SCL = gpio.setup(scl, 0, gpio.PULLUP)
+    TM1637_SDA = gpio.setup(sda, 0, gpio.PULLUP)
+end
+
+local function i2c_strat()
+    TM1637_SDA(1)
+    TM1637_SCL(1)
+    TM1637_SDA(0)
+    TM1637_SCL(0)
+    -- sys.wait(10)
+end
+local function i2c_send(data)
+    for i = 0, 7, 1 do
+        TM1637_SCL(0)
+        local mbit = bit.isset(data, i) and 1 or 0
+        TM1637_SDA(mbit)
+        TM1637_SCL(1)
+    end
+    TM1637_SCL(0)
+    TM1637_SDA(1)
+    TM1637_SCL(1)
+    -- mack = TM1637_SDA()
+    -- if mack == 0 then TM1637_SDA(0) end
+    TM1637_SDA(0)
+    TM1637_SCL(0)
+
+end
+local function i2c_stop()
+    TM1637_SCL(0)
+    TM1637_SDA(0)
+    TM1637_SCL(1)
+    TM1637_SDA(1)
+end
+
+local function i2c_recv(num)
+    local revData = i2c.recv(i2cid, i2cslaveaddr, num)
+    return revData
+end
+
+--[[ 
+      0
+     ---
+  5 |   | 1   *
+     -6-      7 (on 2nd segment)
+  4 |   | 2   *
+     ---
+      3
+76543210
+ ]]
+
+            --[[   0   1    2    3    4    5    6    7    8    9    A    b    C    d    E    F    G     H    I   J    K     L    M    n    O    P    q    r    S    t    U    v    W    X    y   Z          -   * ]]
+local hextable = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x3d,0x76,0x06,0x1e,0x76,0x38,0x55,0x54,0x3f,0x73,0x67,0x50,0x6d,0x78,0x3e,0x1c,0x2a,0x76,0x6e,0x5b,0x00,0x40,0x63};
+
+
+--[[
+TM1637显示清空
+@api tm1637.clear()
+@usage
+tm1637.clear()
+]]
+function tm1637.clear()
+    i2c_strat()
+    i2c_send(0x40) -- 自加模式
+    i2c_stop()
+    i2c_strat()
+    i2c_send(0xc0)
+    for i = 1, 4 do i2c_send(0x00) end
+    i2c_stop()
+end
+
+--[[
+TM1637显示
+@api tm1637.singleShow(com,date,comma)
+@number com com端口号
+@number date 显示数字,0-9即显示0-9,10会显示a以此类推
+@bool   comma 是否带逗号或冒号
+@usage
+tm1637.singleShow(0,2)
+tm1637.singleShow(1,1,true)
+tm1637.singleShow(2,3)
+tm1637.singleShow(3,6)
+]]
+function tm1637.singleShow(com,date,comma)
+    if com then
+        i2c_strat()
+        i2c_send(0x44) -- 地址模式
+        i2c_stop()
+        i2c_strat()
+        i2c_send(0xc0+com)
+        if comma then
+            i2c_send(bit.bor(hextable[date+1],0x80))
+        else
+            i2c_send(hextable[date+1])
+        end
+        i2c_stop()
+    end
+end
+
+--[[
+TM1637设置亮度
+@api tm1637.setLight(light)
+@number light 亮度,0-7
+@usage
+tm1637.setLight(3)
+]]
+function tm1637.setLight(light)
+    i2c_strat()
+    i2c_send(bit.bor(light,0x88))
+    i2c_stop()
+end
+
+--[[
+TM1637初始化
+@api tm1637.init(scl,sda)
+@number scl i2c_scl
+@number sda i2c_sda
+@return bool   成功返回true
+@usage
+tm1637.init(1,4)
+]]
+function tm1637.init(scl,sda)
+    i2c_init(scl,sda)
+    sys.wait(200)
+    tm1637.clear()
+    i2c_strat()
+    i2c_send(0x8f)
+    i2c_stop()
+    return true
+end
+
+return tm1637

+ 226 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/tm1640.lua

@@ -0,0 +1,226 @@
+--[[
+@module tm1640
+@summary tm1640 数码管和LED驱动芯片
+@version 1.0
+@date    2023.08.29
+@author  lulipro
+@usage
+--注意:
+--1、tm1640驱动的数码管应该选用共阴数码管
+--2、tm1640也可以驱动LED,如果是LED,则应该将LED连接成共阴数码管内部相同的电路
+--3、AIR101官方核心板,底层为LuatOS-SoC_V0017_AIR101.soc,经测试此脚本库的串行时钟频率为20KHz
+-- 用法实例:
+local tm1640 = require("tm1640")
+
+sys.taskInit(function ()
+    --共阴段码表,0~9的数字
+    local NUM_TABLE_AX = {
+        [0]=0x3f,[1]=0x06,[2]=0x5b,[3]=0x4f,[4]=0x66,
+        [5]=0x6d,[6]=0x7d,[7]=0x07,[8]=0x7f,[9]=0x6f
+    };   
+
+    tm1640.init(pin.PB06,pin.PB07)  --clk,dat
+    
+    while 1 do
+        for i = 0, 9, 1 do
+            tm1640.setBright(tm1640.BRIGHT1)
+            for grid = tm1640.GRID1, tm1640.GRID16, 1 do
+                tm1640.sendDisplayData(grid,NUM_TABLE_AX[i])
+            end
+            sys.wait(200)
+            tm1640.setBright(tm1640.BRIGHT3)
+            sys.wait(200)
+            tm1640.setBright(tm1640.BRIGHT5)
+            sys.wait(200)
+            tm1640.setBright(tm1640.BRIGHT8)
+            sys.wait(200)
+        end
+
+        sys.wait(1000)
+
+        tm1640.setBright(tm1640.BRIGHT5)
+        for i = 0, 9, 1 do
+            tm1640.clear()
+            for grid = tm1640.GRID1, tm1640.GRID16, 1 do
+                tm1640.sendDisplayData(grid,NUM_TABLE_AX[i])
+                sys.wait(100)
+            end
+        end
+    end
+end)
+]]
+local tm1640 = {}
+
+local sys = require "sys"
+
+--数码管位选常量定义
+tm1640.GRID1 = 0
+tm1640.GRID2 = 1
+tm1640.GRID3 = 2
+tm1640.GRID4 = 3
+tm1640.GRID5 = 4
+tm1640.GRID6 = 5
+tm1640.GRID7 = 6
+tm1640.GRID8 = 7
+tm1640.GRID9 = 8
+tm1640.GRID10 = 9
+tm1640.GRID11 = 10
+tm1640.GRID12 = 11
+tm1640.GRID13 = 12
+tm1640.GRID14 = 13
+tm1640.GRID15 = 14
+tm1640.GRID16 = 15
+
+--八级亮度常量定义
+tm1640.BRIGHT1 = 0
+tm1640.BRIGHT2 = 1
+tm1640.BRIGHT3 = 2
+tm1640.BRIGHT4 = 3
+tm1640.BRIGHT5 = 4
+tm1640.BRIGHT6 = 5
+tm1640.BRIGHT7 = 6
+tm1640.BRIGHT8 = 7
+
+local is_display_on = 1                --显示开关标志变量
+local bright        = tm1640.BRIGHT5   --亮度级别变量
+
+local TM1640_CLK    --驱动时钟线的引脚
+local TM1640_DAT    --驱动数据线的引脚
+
+
+--串行通信起始条件
+local function tm1640_strat()
+    TM1640_CLK(1)
+    TM1640_DAT(1)
+    TM1640_DAT(0)
+    TM1640_CLK(0)
+end
+--串行通信结束条件
+local function tm1640_stop()
+    TM1640_CLK(0)
+    TM1640_DAT(0)
+    TM1640_CLK(1)
+    TM1640_DAT(1)
+end
+
+--串行发送一个字节,LSB First
+local function tm1640_writeByte(data)
+  if data~=nil then--防止未接入数码管或者测试时接线不稳定,传来空数据导致程序运行异常
+    for i = 0, 7, 1 do
+        local mbit = (data&0x01~=0) and 1 or 0
+        TM1640_DAT(mbit)
+        TM1640_CLK(1)
+        data = data>>1
+        TM1640_CLK(0)
+        --data = data>>1 将这句话放在上面是为了尽量让时钟接近50%占空比
+    end
+  end
+end
+
+
+
+--[[
+向TM1640的一个指定的位(grid)对应的显存发送指定的段数据进行显示
+@api tm1640.sendDisplayData(grid,seg_data)
+@number grid,定义位选参数,取值为tm1640.GRID1~tm1640.GRID16
+@number seg_data,定义段数据参数
+@usage
+tm1640.sendDisplayData(tm1640.GRID1,0xff)
+]]
+function tm1640.sendDisplayData(grid,seg_data)
+    if (grid >= tm1640.GRID1) and (grid <= tm1640.GRID16) then
+        tm1640_strat()
+        tm1640_writeByte(0xc0|grid)
+        tm1640_writeByte(seg_data)
+        tm1640_stop()
+    end
+end
+
+--[[
+清除TM1640的所有位(grid)对应的显存数据,即全部刷写为0
+@api tm1640.clear()
+@usage
+tm1640.clear()
+]]
+function tm1640.clear()
+    for i = tm1640.GRID1, tm1640.GRID16, 1 do
+        tm1640.sendDisplayData(i,0)
+    end
+end
+
+--[[
+打开TM1640的显示,此操作不影响显存中的数据
+@api tm1640.open()
+@usage
+tm1640.open()
+]]
+function tm1640.open()
+    is_display_on=1
+    tm1640_strat()
+    tm1640_writeByte(0x80|(is_display_on<<3)|(bright))
+    tm1640_stop()
+end
+
+--[[
+关闭TM1640的显示,此操作不影响显存中的数据
+@api tm1640.close()
+@usage
+tm1640.close()
+]]
+function tm1640.close()
+    is_display_on=0
+    tm1640_strat()
+    tm1640_writeByte(0x80|(is_display_on<<3)|(bright))
+    tm1640_stop()
+end
+
+--[[
+设置TM1640的显示亮度,此操作不影响显存中的数据
+@api tm1640.setBright(bri)
+@number 亮度参数,取值为tm1640.BRIGHT1~tm1640.BRIGHT8
+@usage
+tm1640.setBright(tm1640.BRIGHT8)
+]]
+function tm1640.setBright(bri)
+    if bri>tm1640.BRIGHT8 then bri = tm1640.BRIGHT8 end
+    if bri<tm1640.BRIGHT1 then bri = tm1640.BRIGHT1 end
+
+    bright = bri
+    tm1640_strat()
+    tm1640_writeByte(0x80|(is_display_on<<3)|(bright))
+    tm1640_stop()
+end
+
+
+--[[
+TM1640的初始化
+@api tm1640.init(clk,dat,bri)
+@number clk,定义了时钟线驱动引脚
+@number dat,定义了数据线驱动引脚
+@number bri,初始亮度参数,可取的值为tm1640.BRIGHT1~tm1640.BRIGHT8。可选,默认值为tm1640.BRIGHT5。
+@usage
+tm1640.init(pin.PB06,pin.PB07)
+tm1640.init(pin.PB06,pin.PB07,tm1640.BRIGHT8)
+]]
+function tm1640.init(clk,dat,bri)
+    TM1640_CLK = gpio.setup(clk, 1)
+    TM1640_DAT = gpio.setup(dat, 1)
+
+    --设置为固定地址模式
+    tm1640_strat()
+    tm1640_writeByte(0x44)
+    tm1640_stop()
+
+    --如果设置了初始亮度
+    if bri then
+        if bri>tm1640.BRIGHT8 then bri = tm1640.BRIGHT8 end
+        if bri<tm1640.BRIGHT1 then bri = tm1640.BRIGHT1 end
+        bright = bri
+    end
+
+    tm1640.open()  --打开显示
+    tm1640.clear() --清除显存数据
+end
+
+
+return tm1640

+ 302 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/tm1650.lua

@@ -0,0 +1,302 @@
+
+--[[
+@module tm1650
+@summary tm1650 数码管和按键扫描芯片
+@version 1.0
+@date    2023.09.07
+@author  lulipro
+@usage
+--注意:
+--1、tm1650驱动的数码管应该选用共阴数码管
+--2、tm1650也可以驱动LED,如果是LED,则应该将LED连接成共阴数码管内部相同的电路
+--3、支持按键扫描,此模式下tm1650的DP/KP引脚为中断输出引脚
+--=========按键扫描例子==========
+local function tm1650_kcb(key_code)
+    log.info('tm1650 user cb,key code=',string.format("%02X",key_code))
+end
+sys.taskInit(function()
+    tm1650.init(pin.PB06,pin.PB07,tm1650.MODE_KEY_INPUT,pin.PB08,tm1650_kcb)
+    while 1 do
+        sys.wait(2000)
+    end
+end)
+--==========数码管显示例子===========
+sys.taskInit(function()
+    --共阴段码表,0~9的数字
+    local NUM_TABLE_AX = {
+        [0]=0x3f,[1]=0x06,[2]=0x5b,[3]=0x4f,[4]=0x66,
+        [5]=0x6d,[6]=0x7d,[7]=0x07,[8]=0x7f,[9]=0x6f
+    };   
+    tm1650.init(pin.PB06,pin.PB07,tm1650.MODE_LED_OUTPUT)
+    while 1 do
+        for i = tm1650.DIG1, tm1650.DIG4, 1 do
+            tm1650.print(i,NUM_TABLE_AX[6])
+            sys.wait(500)
+        end
+        sys.wait(1000)
+        for i = tm1650.BRIGHT1, tm1650.BRIGHT8, 1 do
+            tm1650.setBright(i)
+            sys.wait(500)
+        end
+        for i = 1, 8, 1 do
+            sys.wait(500)
+            tm1650.close()    
+            sys.wait(500)
+            tm1650.open()
+        end
+        sys.wait(2000)
+        tm1650.clear()
+    end
+end)
+]]
+
+local tm1650 = {}
+
+local sys = require "sys"
+
+--数码管位选定义
+tm1650.DIG1 = 0
+tm1650.DIG2 = 1
+tm1650.DIG3 = 2
+tm1650.DIG4 = 3
+
+--八级亮度常量定义
+tm1650.BRIGHT1 = 1
+tm1650.BRIGHT2 = 2
+tm1650.BRIGHT3 = 3
+tm1650.BRIGHT4 = 4
+tm1650.BRIGHT5 = 5
+tm1650.BRIGHT6 = 6
+tm1650.BRIGHT7 = 7
+tm1650.BRIGHT8 = 8   --第8级亮度时寄存器二进制位为000
+
+
+--工作模式定义
+tm1650.MODE_LED_OUTPUT = 0x71   --数码管LED驱动模式(7级亮度,8段模式,打开显示)
+tm1650.MODE_KEY_INPUT  = 0x79   --按键扫描模式
+
+
+
+local TM1650_SCL
+local TM1650_SDA
+local TM1650_SDA_PIN      --SDA引脚
+local TM1650_IRQ_PIN      --按键检测使用的中断引脚号
+local TM1650_KEY_CALLBACK --按键回调函数
+
+local bright          = tm1650.BRIGHT7   --亮度变量
+local is_display_on   = 1                --显示开关标志变量
+
+local function tm1650_writeReg(reg,value)
+    local dat
+    local mbit
+    --=====start========
+    TM1650_SCL(1)
+    TM1650_SDA(1)
+    TM1650_SDA(0)
+    TM1650_SCL(0)
+
+    --======写寄存器地址==========
+    dat = reg
+    for i = 0, 7, 1 do
+        TM1650_SCL(0)
+        mbit = (((dat<<i)&0x80) ~= 0) and 1 or 0
+        TM1650_SDA(mbit)
+        TM1650_SCL(1)
+        TM1650_SCL(0)
+    end
+    TM1650_SDA(1)  --释放 SDA
+    TM1650_SCL(0)
+    TM1650_SCL(1)
+    TM1650_SCL(0)
+
+    --======写数据==========
+    dat = value
+    for i = 0, 7, 1 do
+        TM1650_SCL(0)
+        mbit = (((dat<<i)&0x80) ~= 0) and 1 or 0
+        TM1650_SDA(mbit)
+        TM1650_SCL(1)
+        TM1650_SCL(0)
+    end
+    TM1650_SDA(1)  --释放 SDA
+    TM1650_SCL(0)
+    TM1650_SCL(1)
+    TM1650_SCL(0)
+
+    --=======stop=======
+    TM1650_SCL(0)
+    TM1650_SDA(0)
+    TM1650_SCL(1)
+    TM1650_SDA(1)
+end
+
+
+local function tm1650_readReg(reg)
+    local dat
+    local mbit
+    --=====start========
+    TM1650_SCL(1)
+    TM1650_SDA(1)
+    TM1650_SDA(0)
+    TM1650_SCL(0)
+
+    --======写寄存器地址==========
+    dat = reg
+    for i = 0, 7, 1 do
+        TM1650_SCL(0)
+        mbit = (((dat<<i)&0x80) ~= 0) and 1 or 0
+        TM1650_SDA(mbit)
+        TM1650_SCL(1)
+        TM1650_SCL(0)
+    end
+    TM1650_SDA(1)  --释放 SDA
+    TM1650_SCL(0)
+    TM1650_SCL(1)
+    TM1650_SCL(0)
+
+    --=======读数据=========
+    dat = 0
+    gpio.setup(TM1650_SDA_PIN,nil,gpio.PULLUP)  --SDA输入模式
+    for i = 0, 7, 1 do
+        TM1650_SCL(0)
+        dat = (dat << 1)
+        TM1650_SCL(1)
+        if gpio.HIGH ==  gpio.get(TM1650_SDA_PIN) then
+            dat = ( dat | 0x01)
+        end 
+        TM1650_SCL(0)
+    end
+
+    TM1650_SCL(0)
+    TM1650_SCL(1)
+    TM1650_SCL(0)
+
+    TM1650_SDA = gpio.setup(TM1650_SDA_PIN,1)  --SDA重新设置为输出模式
+    --=======stop=======
+    TM1650_SCL(0)
+    TM1650_SDA(0)
+    TM1650_SCL(1)
+    TM1650_SDA(1)
+
+    return dat
+end
+
+
+--[[
+TM1650初始化,根据mode参数可以设置为数码管显示或者按键扫描模式
+@api tm1650.init(scl_pin,sda_pin,mode,irq_pin,key_cb)
+@number scl_pin,定义了时钟线驱动引脚
+@number sda_pin,定义了数据线驱动引脚
+@number mode,定义了工作模式,tm1650.MODE_LED_OUTPUT,数码管LED驱动模式;tm1650.MODE_KEY_INPUT,按键检测模式
+@number irq_pin,定义按键中断引脚
+@function key_cb,按键用户回调函数,此函数有一个number类型参数,为按下的按键的按键代码
+@usage
+tm1650.init(pin.PB06,pin.PB07,tm1650.MODE_LED_OUTPUT) --数码管显示模式
+tm1650.init(pin.PB06,pin.PB07,tm1650.MODE_KEY_INPUT,pin.PB08,tm1650_kcb)  --按键扫描模式
+]]
+function tm1650.init(scl_pin,sda_pin,mode,irq_pin,key_cb)
+
+    TM1650_SCL =  gpio.setup(scl_pin, 1)
+    TM1650_SDA =  gpio.setup(sda_pin, 1)
+    TM1650_SDA_PIN = sda_pin
+
+    if mode == tm1650.MODE_KEY_INPUT then
+        TM1650_IRQ_PIN      = irq_pin
+        TM1650_KEY_CALLBACK = key_cb
+
+        --配置为按键扫描模式
+        tm1650_writeReg(0x48,tm1650.MODE_KEY_INPUT)
+
+        gpio.setup(
+            TM1650_IRQ_PIN,       --中断gpio编号
+            function (val)        --中断处理函数,下降沿触发时
+                local key_code = tm1650_readReg(0x49)  --读按键代码
+                if TM1650_KEY_CALLBACK then
+                    TM1650_KEY_CALLBACK(key_code)
+                end
+                --log.info('tm1650 key get',string.format("%02X",key_code))   --输出按键值
+            end,
+            gpio.PULLUP,    --启用上拉
+            gpio.FALLING   --下降沿触发
+        )
+    else
+        --配置为数码管LED驱动模式,7级亮度,打开显示
+        tm1650_writeReg(0x48,tm1650.MODE_LED_OUTPUT)
+        tm1650.clear()
+    end
+
+end
+
+--[[
+设置TM1650的显示亮度,此操作不影响显存中的数据
+@api tm1650.setBright(bri)
+@number 亮度参数,取值为tm1650.BRIGHT1~tm1650.BRIGHT8
+@usage
+tm1650.setBright(tm1650.BRIGHT8)
+]]
+function tm1650.setBright(bri)
+    if bri>tm1650.BRIGHT8 then bri = tm1650.BRIGHT8 end
+    if bri<tm1650.BRIGHT1 then bri = tm1650.BRIGHT1 end
+    bright = bri
+
+    local bright_bits = bright
+    if bright == tm1650.BRIGHT8 then bright_bits=0 end  --亮度8的寄存器二进制为000
+    
+    tm1650_writeReg(0x48,((bright_bits<<4)|is_display_on))
+end
+
+--[[
+打开TM1650的显示,此操作不影响显存中的数据
+@api tm1650.open()
+@usage
+tm1650.open()
+]]
+function tm1650.open()
+    local bright_bits = bright
+    if bright == tm1650.BRIGHT8 then bright_bits=0 end  --亮度8的寄存器二进制为000
+    is_display_on=1
+
+    tm1650_writeReg(0x48,((bright_bits<<4)|is_display_on))
+end
+
+--[[
+关闭TM1650的显示,此操作不影响显存中的数据
+@api tm1650.close()
+@usage
+tm1650.close()
+]]
+function tm1650.close()
+    local bright_bits = bright
+    if bright == tm1650.BRIGHT8 then bright_bits=0 end  --亮度8的寄存器二进制为000
+    is_display_on=0
+
+    tm1650_writeReg(0x48,((bright_bits<<4)|is_display_on))
+end
+
+--[[
+向TM1650的一个指定的位对应的显存发送指定的段数据进行显示
+@api tm1650.print(dig,seg_data)
+@number dig,定义位选参数,取值为tm1650.DIG1~tm1650.DIG4
+@number seg_data,定义段数据参数
+@usage
+tm1650.print(tm1650.DIG1,0x3f)
+]]
+function tm1650.print(dig,seg_data)
+    if (dig>=tm1650.DIG1) and (dig<=tm1650.DIG4) then
+        tm1650_writeReg(dig*2+0x68,seg_data)
+    end
+end
+
+--[[
+清除TM1650的所有位对应的显存数据,即全部刷写为0
+@api tm1650.clear()
+@usage
+tm1650.clear()
+]]
+function tm1650.clear()
+    for i = tm1650.DIG1, tm1650.DIG4, 1 do
+        tm1650.print(i,0)
+    end
+end
+
+return tm1650

+ 151 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/tsl2561.lua

@@ -0,0 +1,151 @@
+--[[
+@module tsl2561
+@summary tsl2561 光强传感器 
+@version 1.0
+@date    2022.04.11
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+local tsl2561 = require "tsl2561"
+i2cid = 0
+i2c_speed = i2c.FAST
+sys.taskInit(function()
+    i2c.setup(i2cid,i2c_speed)
+    tsl2561.init(i2cid)--初始化,传入i2c_id
+    while 1 do
+        local tsl2561_data = tsl2561.get_data()
+        log.info("tsl2561_data", tsl2561_data.."Lux")
+        sys.wait(1000)
+    end
+end)
+]]
+
+
+local tsl2561 = {}
+local sys = require "sys"
+local i2cid
+
+local TSL2561_ADDRESS_ADR = 0x39
+
+local TSL2561_ADDRESS_ADR_LOW     =   0x29
+local TSL2561_ADDRESS_ADR_FLOAT   =   0x39
+local TSL2561_ADDRESS_ADR_HIGH     =   0x49
+
+local TSL2561_CHIP_ID_CHECK       =   0x0A
+
+---器件所用地址
+
+local TSL2561_CONTROL             =   0x80 --Control of basic functions
+local TSL2561_TIMING              =   0x81 --Integration time/gain control
+local TSL2561_THRESHLOWLOW        =   0x82 --Low byte of low interrupt threshold
+local TSL2561_THRESHLOWHIGH       =   0x83 --High byte of low interrupt threshold
+local TSL2561_THRESHHIGHLOW       =   0x84 --Low byte of high interrupt threshold
+local TSL2561_THRESHHIGHHIGH      =   0x85 --High byte of high interrupt threshold
+local TSL2561_INTERRUPT           =   0x86 --Interrupt control
+local TSL2561_CRC                 =   0x88 --Factory test — not a user register
+local TSL2561_ID                  =   0x8A --Part number/ Rev ID
+local TSL2561_DATA0LOW            =   0x8C --Low byte of ADC channel 0
+local TSL2561_DATA0HIGH           =   0x8D --High byte of ADC channel 0
+local TSL2561_DATA1LOW            =   0x8E --Low byte of ADC channel 1
+local TSL2561_DATA1HIGH           =   0x8F --High byte of ADC channel 1
+
+local TSL2561_PowerUp             =   0x03
+local TSL2561_PowerDown           =   0x00
+
+--最后两位设置积分时间,NOMINAL INTEGRATION TIME 13.7ms 101ms 402ms
+local TSL2561_TIMING_13MS	      =   0x00	--积分时间13.7ms
+local TSL2561_TIMING_101MS	      =   0x01  --积分时间101ms
+local TSL2561_TIMING_402MS	      =   0x02  --积分时间402ms
+
+local TSL2561_TIMING_GAIN_1X      =   0x00 --增益1
+local TSL2561_TIMING_GAIN_16X     =   0x10 --增益16倍
+
+--器件ID检测
+local function chip_check()
+    i2c.send(i2cid, TSL2561_ADDRESS_ADR_LOW, TSL2561_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, TSL2561_ADDRESS_ADR_LOW, 1)
+    if revData:byte() ~= nil then
+        TSL2561_ADDRESS_ADR = TSL2561_ADDRESS_ADR_LOW
+    else
+        i2c.send(i2cid, TSL2561_ADDRESS_ADR_HIGH, TSL2561_CHIP_ID_CHECK)--读器件地址
+        sys.wait(50)
+        local revData = i2c.recv(i2cid, TSL2561_ADDRESS_ADR_HIGH, 1)
+        if revData:byte() ~= nil then
+            TSL2561_ADDRESS_ADR = TSL2561_ADDRESS_ADR_HIGH
+        else
+            i2c.send(i2cid, TSL2561_ADDRESS_ADR_FLOAT, TSL2561_CHIP_ID_CHECK)--读器件地址
+            sys.wait(50)
+            local revData = i2c.recv(i2cid, TSL2561_ADDRESS_ADR_FLOAT, 1)
+            if revData:byte() ~= nil then
+                TSL2561_ADDRESS_ADR = TSL2561_ADDRESS_ADR_FLOAT
+            else
+                log.info("i2c", "Can't find tsl2561 device")
+                return false
+            end
+        end
+    end
+    sys.wait(50)
+    i2c.send(i2cid, TSL2561_ADDRESS_ADR, TSL2561_CHIP_ID_CHECK)--读器件地址
+    local revData = i2c.recv(i2cid, TSL2561_ADDRESS_ADR, 1)
+    local id = revData:toHex()
+    log.info("Device i2c id is:",id)
+    return true
+end
+
+--[[
+tsl2561 初始化
+@api tsl2561.init(i2c_id)
+@number 所在的i2c总线id
+@return bool   成功返回true
+@usage
+tsl2561.init(0)
+]]
+function tsl2561.init(i2c_id)
+    i2cid = i2c_id
+    sys.wait(20)--20 毫秒等待设备稳定
+    if chip_check() then
+        i2c.send(i2cid, TSL2561_ADDRESS_ADR, {TSL2561_CONTROL,TSL2561_PowerUp})
+        i2c.send(i2cid, TSL2561_ADDRESS_ADR, {TSL2561_TIMING,TSL2561_TIMING_402MS|TSL2561_TIMING_GAIN_16X})
+        log.info("tsl2561 init_ok")
+        sys.wait(20)
+        return true
+    end
+    return false
+end
+
+--[[
+获取 tsl2561 数据
+@api tsl2561.get_data()
+@return table tsl2561 数据
+@usage
+local tsl2561_data = tsl2561.get_data()
+log.info("tsl2561_data", tsl2561_data.."Lux")
+]]
+function tsl2561.get_data()
+    local Lux
+    i2c.send(i2cid, TSL2561_ADDRESS_ADR,TSL2561_DATA0LOW)
+    local _, ch0, ch1 = pack.unpack(i2c.recv(i2cid, TSL2561_ADDRESS_ADR, 4),"<HH")
+     if ch0 ~= nil and ch1 ~= nil then
+        if 0.0 < ch1/ch0 and ch1/ch0 <= 0.50 then
+            Lux = 0.0304*ch0 - 0.062*ch0*math.pow(ch1/ch0,1.4)
+        end
+        if 0.50 < ch1/ch0 and ch1/ch0 <= 0.61 then
+            Lux = 0.0224*ch0 - 0.031*ch1
+        end
+        if 0.61 < ch1/ch0 and ch1/ch0 <= 0.80 then
+            Lux = 0.0128*ch0 - 0.0153*ch1
+        end
+        if 0.80 < ch1/ch0 and ch1/ch0 <= 1.30 then
+            Lux = 0.00146*ch0 - 0.00112*ch1
+        end
+        if ch1/ch0 > 1.30 then
+            Lux = 0;
+        end
+    end
+    return Lux or 0
+end
+
+return tsl2561
+
+

+ 203 - 0
module/Air780EHM_Air780EHV_Air780EGH/demolib/vl6180.lua

@@ -0,0 +1,203 @@
+--[[
+@module  vl6180
+@summary VL6180激光测距传感器 
+@version 1.0
+@date    2023.11.14
+@author  dingshuaifei
+@usage
+
+--MCU                        vl6180
+--3V3                        VIN
+--GND                        GND
+--I2CSCL                     SCL
+--I2CSDA                     SDA
+--GPIO                       GPIO1(SHDN/中断输出)
+--GPIO                       GPIO0(CE)
+
+vl6180测量说明:
+1、只能单次测量,测量0-10cm的绝对距离
+2、测量有效范围在20-30cm
+
+--注意:因使用了sys.wait()所有api需要在协程中使用
+-- 用法实例
+vl6180=require"vl6180"
+local CE=4
+local INT=21
+local I2C_ID=0
+sys.taskInit(function()
+    sys.wait(2000)
+    log.info('初始化')
+    vl6180.init(CE,INT,I2C_ID)
+    while true do
+        sys.wait(200)
+        --单次测量开始
+        log.info('距离:',vl6180.get())
+    end
+end)
+]]
+
+vl6180={}
+
+--配置GPIO为CE和INT
+local CE   --gpio
+local INT  --gpio
+--i2c id
+local i2c_id
+--设备地址
+local addr=0x29
+--测量距离数据
+local vldata 
+
+--设置中断和CE
+local function it_ce_init()
+    --设置CE引脚上拉输出,默认1
+    gpio.setup(CE, 0, gpio.PULLUP)
+    --设置INT引脚中断,测量完成后会进入回调
+    gpio.setup(INT, function(val) 
+        sys.publish("VL6180_INC")
+    end, gpio.PULLUP)
+end
+
+--配置i2c
+local function i2c_init()
+    --i2c 1 快速模式
+    i2c.setup(i2c_id,i2c.FAST)
+end
+
+--[[
+    写设备寄存器值:
+    写入寄存器会判断写入的值是否写入成功,若不成功则会打印出相应的寄存器地址,以及当前寄存器的值。
+    注意:有的寄存器位写入值后,会被硬件清除,不代表写失败,具体参考相应寄存器
+    @regaddr 寄存器地址
+    @val 写入的值
+]]
+local function write_register(regaddr,val)
+    local stu=i2c.send(i2c_id,addr,string.char(regaddr>>8,regaddr&0xff,val))
+    i2c.send(i2c_id,addr,string.char(regaddr>>8,regaddr&0xff))
+    local reg=i2c.recv(i2c_id,addr,1)
+    if string.byte(reg) ~= val then
+        log.info('写入失败地址',string.toHex(string.char(regaddr)),'读值',string.toHex(reg),'写发送状态',stu)
+    end
+end
+--[[
+    读设备寄存器的值:
+    @regaddr 寄存器地址
+    @bit 读多少位
+]]
+local function read_register(regaddr,bit)
+    local regval
+    i2c.send(i2c_id,addr,string.char(regaddr>>8,regaddr&0xff))
+    regval=i2c.recv(i2c_id,addr,bit)
+    return string.byte(regval)
+end
+
+--[[
+    系统功能配置
+]]
+local function SetMode()
+    --设置GPIO1为中断输出,极性低
+    write_register(0x0011,0x10)
+
+    --复位平均采样周期
+    write_register(0x010A,0x30)
+    --模拟增益设置1.0
+    write_register(0x003F,0x46)
+    --系统范围VHV重复率
+    write_register(0x0031,0xFF)
+
+    --积分周期100ms
+    write_register(0x0041,0x63)
+    --对测距传感器进行单次温度校准
+    --write_register(0x002E,0x00)
+    write_register(0x002E,0x01)
+
+    --测量周期,10ms
+    write_register(0x001B,0x09)
+    write_register(0x003E,0x31)
+    --系统中断配置GPIO
+    write_register(0x0014,0x24)
+end
+
+--[[
+vl6180初始化
+@api vl6180.init(ce,int,id)
+@number  ce gpio编号[控制] 
+@number  int gpio编号[中断]
+@number  id i2c总线id 
+@return  bool 成功返回true失败返回false
+@usage
+vl6180.Init(4,21,0)
+]]
+function vl6180.init(ce,int,i2cid)
+
+    --判断id是否存在
+    if i2c.exist(i2cid)~=true then 
+        log.info('i2c不存在')
+        return false
+    end
+    --赋值
+    CE       =ce
+    INT      =int
+    i2c_id   =i2cid
+
+    --初始化INT和CE
+    it_ce_init()
+    --初始化i2c
+    i2c_init()
+
+    --开启vl6180
+    gpio.set(CE, 0)
+    sys.wait(10)
+    gpio.set(CE, 1)
+ 
+    --读设备号
+    local send_s=i2c.send(i2c_id,addr,string.char(0x00,0x00))
+    if send_s ~=true then 
+        log.info('设备不存在')
+        return false
+    end
+    local recv_data=i2c.recv(i2c_id,addr,1)
+    log.info('设备号:',string.toHex(recv_data))
+
+    --系统模式配置
+    SetMode()
+    return true
+
+end
+
+--[[
+vl6180获取测量距离值 单位:mm
+@api vl6180.get()
+@return number 成功返回vl6180数据,失败返回0
+@usage
+local data=vl6180.get()
+log.info("measuring val:",data)
+]]
+function vl6180.get()
+    --等待设备就绪
+    local recv_data=read_register(0x004D,1)
+    if recv_data & 0x1 ~=0 then
+        --启动测量,单次测距
+        i2c.send(i2c_id,addr,string.char(0x00,0x18,0x01))
+        --判断ALS低阈值事件
+        recv_data=read_register(0x004F,1)
+        if recv_data & 0x04 ~= 0 then
+            --读取距离数据,单位mm
+            vldata = read_register(0x0062,1)
+            --清除全部中断标志位
+            i2c.send(i2c_id,addr,string.char(0x00,0x15,0x07))
+            if sys.waitUntil("VL6180_INC")  then
+                return vldata
+            else
+                return 0
+            end
+        else
+            log.info('低阈值事件等待就绪')
+        end
+    else
+        log.info('设备忙')
+        return 0
+    end
+end
+
+return vl6180

部分文件因为文件数量过多而无法显示