shenyuanyuan 9 месяцев назад
Родитель
Сommit
95bafbaf1a
100 измененных файлов с 9621 добавлено и 0 удалено
  1. 407 0
      module/Air780EHM/demo/JT808/main.lua
  2. 66 0
      module/Air780EHM/demo/SPI/main.lua
  3. 650 0
      module/Air780EHM/demo/SPI/rc522.lua
  4. 26 0
      module/Air780EHM/demo/SPI/rc522_test.lua
  5. 24 0
      module/Air780EHM/demo/adc/main.lua
  6. 83 0
      module/Air780EHM/demo/adc/testAdc.lua
  7. 90 0
      module/Air780EHM/demo/airlbs/main.lua
  8. 25 0
      module/Air780EHM/demo/apn/main.lua
  9. 52 0
      module/Air780EHM/demo/bit/main.lua
  10. 114 0
      module/Air780EHM/demo/bit64/main.lua
  11. 115 0
      module/Air780EHM/demo/camera/spi_cam/bf30a2.lua
  12. 328 0
      module/Air780EHM/demo/camera/spi_cam/gc0310.lua
  13. 361 0
      module/Air780EHM/demo/camera/spi_cam/gc032a.lua
  14. 223 0
      module/Air780EHM/demo/camera/spi_cam/main.lua
  15. 73 0
      module/Air780EHM/demo/can/main.lua
  16. BIN
      module/Air780EHM/demo/crypto/logo.jpg
  17. 242 0
      module/Air780EHM/demo/crypto/main.lua
  18. 62 0
      module/Air780EHM/demo/errDump/main.lua
  19. 99 0
      module/Air780EHM/demo/fota2/main.lua
  20. 133 0
      module/Air780EHM/demo/fs/main.lua
  21. 105 0
      module/Air780EHM/demo/fskv/main.lua
  22. 92 0
      module/Air780EHM/demo/ftp/main.lua
  23. 37 0
      module/Air780EHM/demo/gpio/AGPIO/main.lua
  24. 39 0
      module/Air780EHM/demo/gpio/GPIO上拉下拉模式/main.lua
  25. 27 0
      module/Air780EHM/demo/gpio/GPIO中断(触发)模式/main.lua
  26. 34 0
      module/Air780EHM/demo/gpio/GPIO中断(计数)模式/main.lua
  27. 34 0
      module/Air780EHM/demo/gpio/GPIO翻转测速/main.lua
  28. 41 0
      module/Air780EHM/demo/gpio/GPIO输入模式/main.lua
  29. 37 0
      module/Air780EHM/demo/gpio/GPIO输出模式/main.lua
  30. 21 0
      module/Air780EHM/demo/helloworld/main.lua
  31. 30 0
      module/Air780EHM/demo/hmeta/main.lua
  32. 3 0
      module/Air780EHM/demo/http/luatos_uploadFile.txt
  33. 306 0
      module/Air780EHM/demo/http/main.lua
  34. 41 0
      module/Air780EHM/demo/i2c-aht10/main.lua
  35. 61 0
      module/Air780EHM/demo/i2c-sht20/main.lua
  36. 218 0
      module/Air780EHM/demo/iconv/main.lua
  37. 83 0
      module/Air780EHM/demo/iotcloud/aliyun/示例1 一型一密(免预注册,但需要是企业版实例平台)方式连接云平台/main.lua
  38. 82 0
      module/Air780EHM/demo/iotcloud/aliyun/示例2 一型一密(预注册)方式连接云平台/main.lua
  39. 82 0
      module/Air780EHM/demo/iotcloud/aliyun/示例3 一机一密方式连接云平台/main.lua
  40. 70 0
      module/Air780EHM/demo/iotcloud/baiduiot/main.lua
  41. 88 0
      module/Air780EHM/demo/iotcloud/huaweiiot/main.lua
  42. 157 0
      module/Air780EHM/demo/iotcloud/onenet/main.lua
  43. 79 0
      module/Air780EHM/demo/iotcloud/tencentiot/main.lua
  44. 132 0
      module/Air780EHM/demo/iotcloud/tlink/main.lua
  45. 70 0
      module/Air780EHM/demo/iotcloud/tuyaiot/示例1 一机一密/main.lua
  46. 81 0
      module/Air780EHM/demo/json/main.lua
  47. 26 0
      module/Air780EHM/demo/lbsLoc2/main.lua
  48. BIN
      module/Air780EHM/demo/lcd/logo.jpg
  49. 92 0
      module/Air780EHM/demo/lcd/main.lua
  50. BIN
      module/Air780EHM/demo/lcd/picture.jpg
  51. 21 0
      module/Air780EHM/demo/led/main.lua
  52. 64 0
      module/Air780EHM/demo/log/main.lua
  53. 87 0
      module/Air780EHM/demo/lowpower/low_power_dissipation.lua
  54. 17 0
      module/Air780EHM/demo/lowpower/main.lua
  55. 67 0
      module/Air780EHM/demo/lowpower/normal.lua
  56. 76 0
      module/Air780EHM/demo/lowpower/ultra_low_power.lua
  57. 41 0
      module/Air780EHM/demo/miniz/main.lua
  58. 122 0
      module/Air780EHM/demo/mobile/main.lua
  59. 155 0
      module/Air780EHM/demo/modbus/ascii/master_ascii/main.lua
  60. 70 0
      module/Air780EHM/demo/modbus/ascii/slave_ascii/main.lua
  61. 154 0
      module/Air780EHM/demo/modbus/rtu/master_rtu/main.lua
  62. 70 0
      module/Air780EHM/demo/modbus/rtu/slave_rtu/main.lua
  63. 59 0
      module/Air780EHM/demo/modbus/tcp/master_tcp/lan.lua
  64. 161 0
      module/Air780EHM/demo/modbus/tcp/master_tcp/main.lua
  65. 60 0
      module/Air780EHM/demo/modbus/tcp/slave_tcp/lan.lua
  66. 69 0
      module/Air780EHM/demo/modbus/tcp/slave_tcp/main.lua
  67. BIN
      module/Air780EHM/demo/modbus/test_core/LuatOS-SoC_V2007_Air780EPM_TEMP_20250610_150400.soc
  68. 30 0
      module/Air780EHM/demo/mqtt/main.lua
  69. 226 0
      module/Air780EHM/demo/mqtt/multilink_mqtt.lua
  70. 177 0
      module/Air780EHM/demo/mqtt/single_mqtt.lua
  71. 311 0
      module/Air780EHM/demo/netdrv/ch390/lan/dhcpsrv.lua
  72. 113 0
      module/Air780EHM/demo/netdrv/ch390/lan/dnsproxy.lua
  73. 57 0
      module/Air780EHM/demo/netdrv/ch390/lan/lan.lua
  74. 21 0
      module/Air780EHM/demo/netdrv/ch390/lan/main.lua
  75. 21 0
      module/Air780EHM/demo/netdrv/ch390/wan/main.lua
  76. 38 0
      module/Air780EHM/demo/netdrv/ch390/wan/wan.lua
  77. 123 0
      module/Air780EHM/demo/ntp/main.lua
  78. 9 0
      module/Air780EHM/demo/onewire/onewire_multi_18b20_swich_read/main.lua
  79. 125 0
      module/Air780EHM/demo/onewire/onewire_multi_18b20_swich_read/switch_read.lua
  80. 112 0
      module/Air780EHM/demo/onewire/onewire_single_18b20/main.lua
  81. 61 0
      module/Air780EHM/demo/openai/deepseek/main.lua
  82. 160 0
      module/Air780EHM/demo/openai/deepseek/openai.lua
  83. 124 0
      module/Air780EHM/demo/os/main.lua
  84. 49 0
      module/Air780EHM/demo/pack/main.lua
  85. 72 0
      module/Air780EHM/demo/protobuf/main.lua
  86. 7 0
      module/Air780EHM/demo/protobuf/person.pb
  87. 6 0
      module/Air780EHM/demo/protobuf/person.pbtxt
  88. 34 0
      module/Air780EHM/demo/pwm/示例1 PWM输出/main.lua
  89. 58 0
      module/Air780EHM/demo/pwm/示例2 呼吸灯效果/main.lua
  90. 87 0
      module/Air780EHM/demo/sms/main.lua
  91. 113 0
      module/Air780EHM/demo/string/main.lua
  92. 134 0
      module/Air780EHM/demo/tcp/TCP-UART/main.lua
  93. 139 0
      module/Air780EHM/demo/tcp/TCP单向认证/main.lua
  94. 134 0
      module/Air780EHM/demo/tcp/TCP断链续连/main.lua
  95. 51 0
      module/Air780EHM/demo/timer/main.lua
  96. 157 0
      module/Air780EHM/demo/u8g2/main.lua
  97. 51 0
      module/Air780EHM/demo/uart/uart/main.lua
  98. 49 0
      module/Air780EHM/demo/uart/uart_rs485/main.lua
  99. 77 0
      module/Air780EHM/demo/uart/uart_two/main.lua
  100. 61 0
      module/Air780EHM/demo/uart/uart_vir/main.lua

+ 407 - 0
module/Air780EHM/demo/JT808/main.lua

@@ -0,0 +1,407 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "JT808"
+VERSION = "2.0.0"
+
+--[[
+本demo演示使用string.pack与unpack函数,实现JT808 终端注册协议数据生成与解析
+]]
+
+--加载sys库
+sys = require("sys")
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+local netLed = require("netLed")
+--GPIO27配置为输出,用作网络指示灯
+local LEDA= gpio.setup(27, 0, gpio.PULLUP)
+
+-- 自定义位运算函数
+local bit = {}
+
+--终端消息ID
+T_COMMON_RSP = 0x0001
+T_REGISTER = 0x0100
+
+function bit.bxor(a, b)
+    return a ~ b  -- 按位异或
+end
+
+--十六进制转二进制
+local function hexToBinary(hexStr)
+    local binaryData = {}
+    for i = 1, #hexStr, 2 do
+        local byte = tonumber(hexStr:sub(i, i+1), 16) -- 每两个字符转换为一个字节
+        table.insert(binaryData, string.char(byte))
+    end
+    return table.concat(binaryData) -- 拼接成二进制字符串
+end
+--字符串转字节数组
+local function stringToBytes(hexStr)
+    local bytes = {}
+    for i = 1, #hexStr, 2 do
+        local byteStr = hexStr:sub(i, i+1)
+        local byte = tonumber(byteStr, 16)
+        table.insert(bytes, byte)
+    end
+    return bytes
+end
+--BCD编码
+local function encodeBcdNum(d, n)
+    if d:len() < n then
+        return (string.rep('0', n - d:len()) .. d):fromHex()
+    else
+        return (d:sub(1, n)):fromHex()
+    end
+end
+--计算异或校验(十六进制字符串)
+local function calculateXor(data)
+    local sum = 0
+     for i = 1, #data, 2 do
+        local byte = tonumber(data:sub(i, i+1), 16)
+
+        -- 检查是否转换成功
+        if not byte then
+           error(string.format("Invalid hex character: '%s' at position %d", byteStr, i))
+        end
+
+        -- 打印当前字节的值(十六进制和十进制)
+        --print(string.format("当前字节: %s (十进制: %d)", byteStr, byte))
+
+        if not byte then
+            error("Invalid hex character in input string!")
+        end
+        sum = bit.bxor(sum, byte)
+        -- 打印异或后的中间结果
+        --print(string.format("异或后 sum = %d (十六进制: 0x%X)", sum, sum))
+    end
+    return sum
+end
+--
+local function calCrc(hexStr)
+
+    -- 解析hexStr转换为十六进制字符数组
+    local byteArray = HexOutput(hexStr)
+
+    -- 计算 CRC
+    local crc = calculateXor(byteArray)
+    log.info("CRCxor:",crc)
+
+    -- 返回 CRC 值
+    return crc
+end
+
+--将时间转换为BCD格式
+function timeToBCD()
+    local t = os.date("*t")
+    
+    -- 转换为BCD格式
+    local year = string.format("%02d", t.year % 100)
+    local month = string.format("%02d", t.month)
+    local day = string.format("%02d", t.day)
+    local hour = string.format("%02d", t.hour)
+    local min = string.format("%02d", t.min)
+    local sec = string.format("%02d", t.sec)
+    
+    -- 组合BCD格式字符串
+    local bcdTime = year .. month .. day .. hour .. min .. sec
+    
+    return bcdTime
+end
+
+
+--构建通用应答
+function cmnRsp(svrSeq, svrId, result)
+    return string.pack('>HHb', svrSeq, svrId, result)   
+    -- >表示使用大端字节序;  
+    -- HH 表示两个 16 位无符号整数(svrSeq 和 svrId); 
+    -- b 表示一个 8 位有符号整数(result)
+end
+--构建设备注册
+function register(ProvinceId,CityId,ManufactureId,TerminalModule,TerminalId,CarColor,CarId)
+    local termMd = TerminalModule..string.rep(string.char(0), 16 - (TerminalModule):len())..string.rep(string.char(0), 4)
+    --log.info("HexDataByteLength:",#termMd)
+    --log.info("HexData is:       ",termMd)
+    return string.pack('>HHc5c20c7Bc11',ProvinceId,CityId,ManufactureId,termMd,TerminalId:fromHex(),CarColor,CarId)
+end
+--构建位置信息汇报
+function locRpt(alarm, status, lat, lng, alt, spd, course, tm, extInfo)
+    -- 使用 '>iiiiHHH' 格式打包数据
+    return string.pack('>iiiiHHH', alarm, status, lat, lng, alt, spd, course)..tm:fromHex().. extInfo
+end
+
+function HexOutput(data)
+    log.info("HexDataByteLength:",#data)
+    local hex = ""
+    for i = 1, #data do
+        hex = hex.. string.format("%02x", string.byte(data, i))
+    end
+    log.info("HexData is:       ",hex)
+    return hex
+end
+--封装一个完整的JT808终端注册数据帧
+function encodeReg(phoneNo,tSendSeq)
+    --消息体
+    local msgBody = register(12,123,'10001','GT808','00000000000002',0,'41048063212')
+    --消息头
+    local msgHead =string.pack('>HH',T_REGISTER,msgBody:len())..encodeBcdNum(phoneNo,12)..tSendSeq
+    local curSeq = tSendSeq
+    tSendSeq = (tSendSeq == 0xFFFF) and 0 or (tSendSeq + 1)
+    --校验码
+    HexOutput(msgHead .. msgBody)
+    local frame = msgHead .. msgBody
+    log.info("encodeReg Frame length is:       ",#frame)
+    local crc = calCrc(frame)
+    
+    --转义
+    local s = msgHead .. msgBody .. string.char(crc)
+    s = s:gsub('\125', '\125\1') -- 7D -> 7D 01
+    s = s:gsub('\126', '\125\2') -- 7E -> 7D 02
+    return string.char(0x7E) .. s .. string.char(0x7E), curSeq
+
+end
+
+function decodeCmnRsp(s)
+
+    log.info("hereNow.",s)
+    --local start, tail, msg = s:find('^(7E[^7E]+7E)$')
+    --local decPacket, msgAttr = {}
+-- 检查是否以 7E 开头和结尾
+    if not s:match("^7E") or not s:match("7E$") then
+        log.warn("prot.decode", "packet does not start or end with 7E")
+        return nil
+    end
+
+    -- 匹配以 7E 开头和结尾的数据包
+    local start, tail, msg = s:find('^(7E.+7E)$')  -- 改为允许中间部分包含 7E
+    log.info("start:", start)
+    log.info("tail:", tail)
+    log.info("msg:", msg)
+
+    if not msg then
+        -- 如果未匹配到
+        log.warn('prot.decode', 'wait packet complete')
+        return nil
+    end
+    
+    --反转义
+    msg = msg:gsub('\125\2', '\126') -- 7D 02 -> 7E
+    msg = msg:gsub('\125\1', '\125') -- 7D 01 -> 7D
+
+    log.info("msg:", msg,msg:len())
+
+    if msg:len() < 13 then
+        log.error('prot.decode', 'packet len is too short')
+        return s:sub(tail + 1, -1), nil
+    end
+
+    local hexArray = stringToBytes(msg)
+
+    local dataHexStr = msg:sub(3, -5)
+    log.info("消息体和其字节长度:", dataHexStr,dataHexStr:len())
+
+    local crcVal = calculateXor(dataHexStr)
+    print("XOR校验值:", crcVal)
+
+    if  crcVal ~= hexArray[#hexArray-1] then
+        log.error('prot.decode', 'crc value error!')
+        return s:sub(tail + 1, -1), nil
+    else
+        log.info("XOR:校验通过")
+    end
+
+    ----------------------------解析消息id
+    msgdata = msg:sub(3, 6)
+
+    -- 转换为二进制数据
+    local binaryData = hexToBinary(msgdata)
+
+    -- 解析二进制数据
+    msgId = string.unpack(">H", binaryData)
+
+    -- 输出结果
+    log.info("消息id十六进制:", string.format("0x%04X", msgId))
+
+    ----------------------------解析应答结果
+    msgdata = msg:sub(7, 8)
+
+    -- 转换为二进制数据
+    local binaryData = hexToBinary(msgdata)
+
+    -- 解析二进制数据
+    result = string.unpack(">B", binaryData)
+
+    -- 输出结果
+    log.info("结果十六进制:", string.format("0x%02X", result))
+
+    ----------------------------解析鉴权码
+    msgdata = msg:sub(9, 10)
+
+    -- 转换为二进制数据
+    local binaryData = hexToBinary(msgdata)
+
+    -- 解析二进制数据
+    result = string.unpack(">B", binaryData)
+
+    -- 输出结果
+    log.info("鉴权码十六进制:", string.format("0x%02X", result))
+
+    ----------------------------解析电话号码
+    msgdata = msg:sub(11, 22)
+
+    -- 转换为二进制数据
+    local binaryData = hexToBinary(msgdata)
+
+    -- 解析二进制数据
+    local b1, b2, b3, b4, b5, b6 = string.unpack(">BBBBBB", binaryData)
+
+    -- 格式化输出为 6 字节的十六进制字符串
+    local TerminalPhoneNo = string.format("0x%02X%02X%02X%02X%02X%02X", b1, b2, b3, b4, b5, b6)
+
+    -- 输出电话号码
+    log.info("电话号码十六进制:", TerminalPhoneNo)
+
+    ----------------------------解析标头流水号
+    msgdata = msg:sub(23, 26)
+
+    -- 转换为二进制数据
+    local binaryData = hexToBinary(msgdata)
+
+    -- 解析二进制数据
+    local ManualMsgNum = string.unpack(">H", binaryData)
+
+    -- 输出结果
+    log.info("标头流水号十六进制:", string.format("0x%04X", ManualMsgNum))
+
+    ----------------------------解析标尾流水号
+    msgdata = msg:sub(23, 26)
+
+    -- 转换为二进制数据
+    local binaryData = hexToBinary(msgdata)
+
+    -- 解析二进制数据
+    local MsgNum = string.unpack(">H", binaryData)
+
+    -- 输出结果
+    log.info("标尾流水号十六进制:", string.format("0x%04X", MsgNum))
+
+    ----------------------------解析鉴权码
+    msgdata = msg:sub(33, 50)
+
+    -- 转换为二进制数据
+    local binaryData = hexToBinary(msgdata)
+
+    local b1, b2, b3, b4, b5, b6, b7, b8, b9 = string.unpack(">BBBBBBBBB", binaryData)
+
+    -- 格式化输出为 6 字节的十六进制字符串
+    local JianquanCode = string.format("0x%02X%02X%02X%02X%02X%02X%02X%02X%02X", b1, b2, b3, b4, b5, b6, b7, b8, b9)
+
+    -- 输出结果
+    log.info("鉴权码十六进制:", JianquanCode)
+end
+
+--新建任务,每休眠2000ms继续一次
+sys.taskInit(function()
+    
+    --实验1:构建立通用应答数据帧,协议可参考JT808协议8.1与8.2
+    --输入参数:
+    --应答流水号为123,
+    --应答 ID为456,
+    --结果为1,
+    local data = cmnRsp(123, 456, 1)
+    HexOutput(data)  --HexDataByteLength:5,HexData is:007b01c801  007b为123,01c8为456,1为1
+
+    --实验2:构建终端注册数据帧,协议可参考JT808协议8.5
+    --输入参数:
+    --12,                   --省域 ID  WORD
+    --123,                  --市县域 ID  WORD
+    --'10001',              --制造商 ID  BYTE[5]
+    --'GT808'               --终端型号 BYTE[20]
+    --'00000000000002',     --终端 ID BYTE[7]
+    --0,                    --车牌颜色  BYTE
+    --'41048063212')        --车辆标识  STRING  VIN或车牌号均为固定长度,这里假设字符串长度为11
+    local data = register(12,123,'10001','GT808','00000000000002',0,'41048063212')
+    HexOutput(data)--[HexDataByteLength:]	48,[HexData is:       ]	
+
+    --输出解析:
+    --000c,对应12,                   --省域 ID  WORD
+    --007b,对应123,                  --市县域 ID  WORD
+    --3130303031,对应'10001',              --制造商 ID  BYTE[5]
+    --4754383038,对应'GT808'               --终端型号 BYTE[20],不足字节补0
+    --0000000000
+    --0000000000
+    --0000000000
+    --00000000000002 对应'00000000000002',     --终端 ID BYTE[7]
+    --000,                    --车牌颜色  BYTE
+    --3431303438303633323132,对应'41048063212')        --车辆标识  STRING 
+
+ 
+    --实验3:构建位置信息汇报数据帧,协议可参考JT808协议8.18
+    --输入参数:
+    --0, 报警标志 DWORD
+    --1, 状态位 DWORD
+    --12345678, 纬度 DWORD
+    --87654321, 经度 DWORD
+    --100, 高程 WORD
+    --60, 速度 WORD
+    --180, 方向 WORD
+    --os.time(), 时间 BCD[6]  YY-MM-DD-hh-mm-ss(GMT+8 时间,本标准中之后涉及的时间均采用此时区)
+    --"extra"
+
+    local time = timeToBCD();
+    log.info("CurrentTime:       ",time)
+
+    local data = locRpt(0, 1, 12345678, 87654321, 100, 60, 180, time, "extra")
+    HexOutput(data)--[HexDataByteLength:]	33,[HexData is:       ]	000000000000000100bc614e05397fb10064003c00b42411241232046578747261
+    --输出解析:
+    --00000000,对应 0, 报警标志 DWORD,4字节
+    --00000001,对应 1, 状态位 DWORD
+    --00bc614e,对应 12345678, 纬度 DWORD
+    --05397fb1, 对应 87654321, 经度 DWORD
+    --0064,对应 100, 高程 WORD
+    --003c,对应60, 速度 WORD
+    --00b4,对应180, 方向 WORD
+    --241124123204,对应时间 BCD[6]  24-11-24-12-32-04
+    --6578747261,对应"extra"
+
+    --实验4:封闭一个完整的JT808注册数据帧
+    --输入参数:
+    --'018068821447',手机号不足 12位,则在前补充数字,大陆手机号补充数字 0
+    --'101',消息流水号
+    local data = encodeReg('018068821447','101')
+    HexOutput(data) --[HexDataByteLength:]	64
+                    --[HexData is:       ]	7e01000030018068821447313031000c007b3130303031475438303800000000000000000000000000000000000000000002003431303438303633323132627e
+
+    --输出解析:
+    --7e,标识位固定为0x7e
+    --0100,消息 ID
+    --0030,消息体属性
+    --018068821447,手机号
+    --313031,消息流水号
+    --000c007b3130303031475438303800000000000000000000000000000000000000000002003431303438303633323132,注册消息体
+    --62,校验
+    --7e,标识位固定为0x7e
+
+    --实验5:解析JT808终端注册应答数据帧
+    local hexStr = '7E8100000C0404571031660030003000736875616E67776569E07E'
+    decodeCmnRsp(hexStr)
+end)
+-- 这里演示4G模块上网后,会自动点亮网络灯,方便用户判断模块是否正常开机
+sys.taskInit(function()
+    while true do
+        sys.wait(6000)
+                if mobile.status() == 1 then
+                        gpio.set(27, 1)  
+                else
+                        gpio.set(27, 0) 
+                        mobile.reset()
+        end
+    end
+end)
+-- 用户代码已结束---------------------------------------------
+-- 运行lua task,只能调用一次,而且必须写在末尾
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 66 - 0
module/Air780EHM/demo/SPI/main.lua

@@ -0,0 +1,66 @@
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+
+本demo演示的功能为:
+使用Air780EPM开发板通过spi接口驱动RC522传感器读取ic数据卡数据
+]]
+PROJECT = "spi_demo"
+VERSION = "001.000.000"
+
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+sys.timerLoopStart(function()
+    log.info("mem.lua", rtos.meminfo())
+    log.info("mem.sys", rtos.meminfo("sys"))
+ end, 3000)
+
+
+ -- 加载spi测试模块
+ --引入rc522测试demo
+ require "rc522_test"
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 650 - 0
module/Air780EHM/demo/SPI/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
+
+
+

+ 26 - 0
module/Air780EHM/demo/SPI/rc522_test.lua

@@ -0,0 +1,26 @@
+--[[
+本demo演示的功能为:
+使用Air780EPM开发板通过spi接口驱动RC522传感器读取ic数据卡数据
+通过spi0通道
+cs片选脚接gpio8
+rst接gpio16
+]]
+
+local rc522 = require "rc522"  --引入rc522驱动模块
+function rc522_test()
+    spi_rc522 = spi.setup(0,8,0,0,8,10*1000*1000,spi.MSB,1,0)  --初始化spi接口
+    rc522.init(0,8,16)   --初始化rc522相关接口
+    wdata = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} --测试写入数据
+    while 1 do
+        -- rc522.write_datablock(7,wdata) --测试ic卡写入
+        for i=0,63 do
+            local a,b = rc522.read_datablock(i) --测试ic卡读取
+            if a then
+                print("read",i,b:toHex())  --打印读取的数据
+            end
+            sys.wait(50)
+        end
+        sys.wait(500)
+    end
+end
+sys.taskInit(rc522_test)

+ 24 - 0
module/Air780EHM/demo/adc/main.lua

@@ -0,0 +1,24 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "adcdemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 一定要添加sys.lua !!!!
+sys = require("sys")
+
+-- 添加硬狗防止程序卡死
+if wdt then
+    wdt.init(9000) -- 初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
+end
+
+local testAdc = require "testAdc"
+sys.taskInit(testAdc.dotest)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 83 - 0
module/Air780EHM/demo/adc/testAdc.lua

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

+ 90 - 0
module/Air780EHM/demo/airlbs/main.lua

@@ -0,0 +1,90 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "paid_lbs"
+VERSION = "1.0.0"
+
+-- 使用合宙iot平台时需要这个参数
+PRODUCT_KEY = "xxx" -- 到 iot.openluat.com 创建项目,获取正确的项目id
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+sysplus = require("sysplus")
+libnet = require "libnet"
+
+log.info("main", PROJECT, VERSION)
+
+if wdt then
+    -- 添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(20000) -- 初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
+end
+
+local airlbs = require "airlbs"
+
+local timeout = 60 -- 扫描基站/wifi 做 基站/wifi定位 的超时时间,最小5S,最大60S
+
+-- 此为收费服务,需自行联系销售申请
+local airlbs_project_id = ""
+local airlbs_project_key = ""
+
+-- --多基站定位
+-- sys.taskInit(function()
+--     sys.waitUntil("IP_READY") -- 等待底层上报联网成功
+--     socket.sntp() -- 进行NTP授时,放置部分联通卡没有基站授时
+--     sys.waitUntil("NTP_UPDATE", 1000)
+--     while 1 do
+--         if airlbs_project_id and airlbs_project_key then
+--             local result, data = airlbs.request({
+--                 project_id = airlbs_project_id,
+--                 project_key = airlbs_project_key,
+--                 timeout = timeout * 1000 -- 实际的超时时间(单位:ms)
+--             })
+--             if result then
+--                 log.info("airlbs多基站定位返回的经纬度数据为", json.encode(data))
+--             end
+--         else
+--             log.warn("请检查project_id和project_key")
+--         end
+--         sys.wait(20000)
+--     end
+
+-- end)
+
+-- wifi/基站混合定位
+sys.taskInit(function()
+    sys.waitUntil("IP_READY")
+    -- 如需wifi定位,需要硬件以及固件支持wifi扫描功能
+    local wifi_info = nil
+    if wlan then
+        sys.wait(3000) -- 网络可用后等待一段时间才再调用wifi扫描功能,否则可能无法获取wifi信息
+        wlan.init()
+        wlan.scan()
+        sys.waitUntil("WLAN_SCAN_DONE", timeout * 1000)
+        wifi_info = wlan.scanResult()
+        log.info("scan", "wifi_info", #wifi_info)
+    end
+
+    socket.sntp()
+    sys.waitUntil("NTP_UPDATE", 1000)
+
+    while 1 do
+        local result, data = airlbs.request({
+            project_id = airlbs_project_id,
+            project_key = airlbs_project_key,
+            wifi_info = wifi_info,
+            timeout = timeout * 1000
+        })
+        if result then
+                log.info("airlbs多基站定位返回的经纬度数据为", json.encode(data))
+        else
+            log.warn("请检查project_id和project_key")
+        end
+        sys.wait(20000) -- 循环20S一次wifi定位
+    end
+
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!
+

+ 25 - 0
module/Air780EHM/demo/apn/main.lua

@@ -0,0 +1,25 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "setApnDemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 一定要添加sys.lua !!!!
+sys = require("sys")
+
+mobile.apn(0,1,"name","user","password",nil,3) -- 专网卡设置的demo,name,user,password联系卡商获取
+
+local function main_task()
+    while true do
+        sys.wait(2000)
+        local apn = mobile.apn(0,1,"","","",nil,0) --获取APN,第三个参数不填就是获取APN
+        log.info("main  apn", apn)
+    end 
+end
+
+sys.taskInit(main_task)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 52 - 0
module/Air780EHM/demo/bit/main.lua

@@ -0,0 +1,52 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "bit64_test"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+
+
+local function log_bit()
+        log.info("按位取反,输出-6",bit.bnot(5))
+        log.info("与,--输出1",bit.band(1,1))
+        log.info("或,--输出3",bit.bor(1,2))
+        log.info("异或结果为4",bit.bxor(2,3,5))
+        log.info("逻辑左移,“100”,输出为4",bit.lshift(1,2))
+
+        log.info("逻辑右移,“001”,输出为1",bit.rshift(4,2))
+        log.info("算数右移,左边添加的数与符号有关,输出为0",bit.arshift(2,2))
+        log.info("参数是位数,作用是1向左移动两位,打印出4",bit.bit(2))
+        log.info("测试位数是否被置1",bit.isset(5,0))--第一个参数是是测试数字,第二个是测试位置.从右向左数0到7.是1返回true,否则返回false,该返回true
+        log.info("测试位数是否被置1",bit.isset(5,1))--打印false
+        log.info("测试位数是否被置1",bit.isset(5,2))--打印true
+        log.info("测试位数是否被置1",bit.isset(5,3))--返回返回false
+        log.info("测试位数是否被置0",bit.isclear(5,0))----与上面的相反
+        log.info("测试位数是否被置0",bit.isclear(5,1))
+        log.info("测试位数是否被置0",bit.isclear(5,2))
+        log.info("测试位数是否被置0",bit.isclear(5,3))
+        log.info("把0的第0,1,2,3位值为1",bit.set(0,0,1,2,3))--在相应的位数置1,打印15
+        log.info("把5的第0,2位置为0",bit.clear(5,0,2))--在相应的位置置0,打印0
+end
+
+sys.taskInit(log_bit)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 114 - 0
module/Air780EHM/demo/bit64/main.lua

@@ -0,0 +1,114 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "bit64_test"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+
+local data,b64,b32,a,b
+
+if bit64 then
+	log.style(1)
+	log.info("bit64 演示")
+	data = 12345678
+	b64 = bit64.to64(data)
+	b32 = bit64.to32(b64)
+	log.info("i32", b32, mcu.x32(b32))
+	data = -12345678
+	b64 = bit64.to64(data)
+	b32 = bit64.to32(b64)
+	log.info("i32", b32, mcu.x32(b32))
+	data = 12.34234
+	b64 = bit64.to64(data)
+	b32 = bit64.to32(b64)
+	log.info("f32", data, b32)
+	data = -12.34234
+	b64 = bit64.to64(data)
+	b32 = bit64.to32(b64)
+	log.info("f32", data, b32)
+
+
+	a = bit64.to64(87654321)
+	b = bit64.to64(12345678)
+	log.info("87654321+12345678=", bit64.show(bit64.plus(a,b)))
+	log.info("87654321-12345678=", bit64.show(bit64.minus(a,b)))
+	log.info("87654321*12345678=", bit64.show(bit64.multi(a,b)))
+	log.info("87654321/12345678=", bit64.show(bit64.pide(a,b)))
+
+	a = bit64.to64(87654321)
+	b = 1234567
+	log.info("87654321+1234567=", bit64.show(bit64.plus(a,b)))
+	log.info("87654321-1234567=", bit64.show(bit64.minus(a,b)))
+	log.info("87654321*1234567=", bit64.show(bit64.multi(a,b)))
+	log.info("87654321/1234567=", bit64.show(bit64.pide(a,b)))
+
+
+	a = bit64.to64(87654.326)
+	b = bit64.to64(12345)
+	log.info("87654.326+12345=", 87654.326 + 12345)
+	log.info("87654.326+12345=", bit64.show(bit64.plus(a,b)))
+	log.info("87654.326-12345=", bit64.show(bit64.minus(a,b)))
+	log.info("87654.326*12345=", bit64.show(bit64.multi(a,b)))
+	log.info("87654.326/12345=", bit64.show(bit64.pide(a,b)))
+
+	a = bit64.to64(87654.32)
+	b = bit64.to64(12345.67)
+	log.info("float", "87654.32+12345.67=", 87654.32 + 12345.67)
+	log.info("double","87654.32+12345.67=", bit64.show(bit64.plus(a,b)))
+	log.info("double to float","87654.32+12345.67=", bit64.to32(bit64.plus(a,b)))
+	log.info("87654.32-12345.67=", bit64.show(bit64.minus(a,b)))
+	log.info("87654.32*12345.67=", bit64.show(bit64.multi(a,b)))
+	log.info("87654.32/12345.67=", bit64.show(bit64.pide(a,b)))
+	log.info("double to int64", "87654.32/12345.67=", bit64.show(bit64.pide(a,b,nil,true)))
+
+	a = bit64.to64(0xc0000000)
+	b = 2
+	a = bit64.shift(a,8,true)
+	log.info("0xc0000000 << 8 =", bit64.show(a, 16))
+	log.info("0xc000000000+2=", bit64.show(bit64.plus(a,b), 16))
+	log.info("0xc000000000-2=", bit64.show(bit64.minus(a,b), 16))
+	log.info("0xc000000000*2=", bit64.show(bit64.multi(a,b), 16))
+	log.info("0xc000000000/2=", bit64.show(bit64.pide(a,b), 16))
+	log.style(0)
+
+	if bit64.strtoll then
+		local data = bit64.strtoll("864040064024194", 10)
+		log.info("data", data:toHex())
+		log.info("data", bit64.show(data))
+	end
+end
+
+local function sys_run_time()
+	local tick64, per = mcu.tick64(true)
+	local per_cnt = per * 1000000
+	while true do
+		tick64, per = mcu.tick64(true)
+		log.info("work time","当前时间", bit64.to32(bit64.pide(tick64,per_cnt)))
+		sys.wait(1000)
+	end
+end
+
+if mcu.tick64 then
+	sys.taskInit(sys_run_time)
+end
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 115 - 0
module/Air780EHM/demo/camera/spi_cam/bf30a2.lua

@@ -0,0 +1,115 @@
+
+local reg_table = {
+	{0xf2,0x01},
+	{0xcf,0xb0},
+	{0x12,0x20},
+	{0x15,0x80},
+	{0x6b,0x71},
+	{0x00,0x40},
+	{0x04,0x00},
+	{0x06,0x26},
+	{0x08,0x07},
+	{0x1c,0x12},
+	{0x20,0x20},
+	{0x21,0x20},
+	{0x34,0x02},
+	{0x35,0x02},
+	{0x36,0x21},
+	{0x37,0x13},
+	{0xca,0x23},
+	{0xcb,0x22},
+	{0xcc,0x89},
+	{0xcd,0x4c},
+	{0xce,0x6b},
+	{0xa0,0x8e},
+	{0x01,0x1b},
+	{0x02,0x1d},
+	{0x13,0x08},
+	{0x87,0x13},
+	{0x8b,0x08},
+	{0x70,0x17},
+	{0x71,0x43},
+	{0x72,0x0a},
+	{0x73,0x62},
+	{0x74,0xa2},
+	{0x75,0xbf},
+	{0x76,0x00},
+	{0x77,0xcc},
+	{0x40,0x32},
+	{0x41,0x28},
+	{0x42,0x26},
+	{0x43,0x1d},
+	{0x44,0x1a},
+	{0x45,0x14},
+	{0x46,0x11},
+	{0x47,0x0f},
+	{0x48,0x0e},
+	{0x49,0x0d},
+	{0x4B,0x0c},
+	{0x4C,0x0b},
+	{0x4E,0x0a},
+	{0x4F,0x09},
+	{0x50,0x09},
+	{0x24,0x30},
+	{0x25,0x36},
+	{0x80,0x00},
+	{0x81,0x20},
+	{0x82,0x40},
+	{0x83,0x30},
+	{0x84,0x50},
+	{0x85,0x30},
+	{0x86,0xd8},
+	{0x89,0x45},
+	{0x8a,0x33},
+	{0x8f,0x81},
+	{0x91,0xff},
+	{0x92,0x08},
+	{0x94,0x82},
+	{0x95,0xfd},
+	{0x9a,0x20},
+	{0x9e,0xbc},
+	{0xf0,0x87},
+	{0x51,0x06},
+	{0x52,0x25},
+	{0x53,0x2b},
+	{0x54,0x0f},
+	{0x57,0x2a},
+	{0x58,0x22},
+	{0x59,0x2c},
+	{0x23,0x33},
+	{0xa1,0x93},
+	{0xa2,0x0f},
+	{0xa3,0x2a},
+	{0xa4,0x08},
+	{0xa5,0x26},
+	{0xa7,0x80},
+	{0xa8,0x80},
+	{0xa9,0x1e},
+	{0xaa,0x19},
+	{0xab,0x18},
+	{0xae,0x50},
+	{0xaf,0x04},
+	{0xc8,0x10},
+	{0xc9,0x15},
+	{0xd3,0x0c},
+	{0xd4,0x16},
+	{0xee,0x06},
+	{0xef,0x04},
+	{0x55,0x34},
+	{0x56,0x9c},
+	{0xb1,0x98},
+	{0xb2,0x98},
+	{0xb3,0xc4},
+	{0xb4,0x0c},
+	{0xa0,0x8f},
+	{0x13,0x07},
+}
+
+function bf30a2Init(cspiId,i2cId,speed,scanMode,onlyY)
+	local id = camera.init(cspiId,speed,0,0,1,0,0,onlyY,scanMode,240,320)
+	for i=1,#reg_table do
+		i2c.send(i2cId,0x6e,reg_table[i],1)
+	end
+	camera.start(id)
+	return id
+end

+ 328 - 0
module/Air780EHM/demo/camera/spi_cam/gc0310.lua

@@ -0,0 +1,328 @@
+local reg_table = {
+        {0xfe, 0xf0},
+        {0xfe, 0xf0},
+        {0xfe, 0x00},
+        {0xfc, 0x16}, 
+        {0xfc, 0x16}, 
+        {0xf2, 0x07}, 
+        {0xf3, 0x83}, 
+        {0xf5, 0x07}, 
+        {0xf7, 0x88}, 
+        {0xf8, 0x00}, 
+        {0xf9, 0x4f}, 
+        {0xfa, 0x11}, 
+        {0xfc, 0xce},
+        {0xfd, 0x00},
+
+        {0x00, 0x2f},
+        {0x01, 0x0f},
+        {0x02, 0x04},
+        {0x03, 0x02},
+        {0x04, 0x12},
+        {0x09, 0x00},
+        {0x0a, 0x00},
+        {0x0b, 0x00},
+        {0x0c, 0x04},
+        {0x0d, 0x01},
+        {0x0e, 0xe8},
+        {0x0f, 0x02},
+        {0x10, 0x88},
+        {0x16, 0x00},
+        {0x17, 0x14},
+        {0x18, 0x1a},
+        {0x19, 0x14},
+        {0x1b, 0x48},
+        {0x1c, 0x6c},
+        {0x1e, 0x6b},
+        {0x1f, 0x28},
+        {0x20, 0x8b},
+        {0x21, 0x49},
+        {0x22, 0xd0},
+        {0x23, 0x04},
+        {0x24, 0xff},
+        {0x34, 0x20},
+
+        {0x26, 0x23}, 
+        {0x28, 0xff}, 
+        {0x29, 0x00}, 
+        {0x32, 0x04},
+        {0x33, 0x10}, 
+        {0x37, 0x20}, 
+        {0x38, 0x10},
+        {0x47, 0x80}, 
+        {0x4e, 0x66}, 
+        {0xa8, 0x02}, 
+        {0xa9, 0x80},
+
+        {0x40, 0xff},
+        {0x41, 0x21}, 
+        {0x42, 0xcf}, 
+        {0x44, 0x00},
+        {0x45, 0xa0},
+        {0x46, 0x02}, 
+        {0x4a, 0x11},
+        {0x4b, 0x01},
+        {0x4c, 0x20}, 
+        {0x4d, 0x05}, 
+        {0x4f, 0x01},
+        {0x50, 0x01}, 
+        {0x55, 0x01}, 
+        {0x56, 0xe0},
+        {0x57, 0x02}, 
+        {0x58, 0x80},
+
+        {0x70, 0x70},
+        {0x5a, 0x84},
+        {0x5b, 0xc9},
+        {0x5c, 0xed},
+        {0x77, 0x74},
+        {0x78, 0x40},
+        {0x79, 0x5f},
+
+        {0x82, 0x14},
+        {0x83, 0x0b},
+        {0x89, 0xf0},
+
+        {0x8f, 0xaa},
+        {0x90, 0x8c},
+        {0x91, 0x90},
+        {0x92, 0x03},
+        {0x93, 0x03},
+        {0x94, 0x05},
+        {0x95, 0x65},
+        {0x96, 0xf0},
+
+        {0xfe, 0x00},
+        {0x9a, 0x20},
+        {0x9b, 0x80},
+        {0x9c, 0x40},
+        {0x9d, 0x80},
+        {0xa1, 0x30},
+        {0xa2, 0x32},
+        {0xa4, 0x30}, 
+        {0xa5, 0x30}, 
+        {0xaa, 0x10}, 
+        {0xac, 0x22},
+
+        {0xfe, 0x00},
+        {0xbf, 0x08},
+        {0xc0, 0x16},
+        {0xc1, 0x28},
+        {0xc2, 0x41},
+        {0xc3, 0x5a},
+        {0xc4, 0x6c},
+        {0xc5, 0x7a},
+        {0xc6, 0x96},
+        {0xc7, 0xac},
+        {0xc8, 0xbc},
+        {0xc9, 0xc9},
+        {0xca, 0xd3},
+        {0xcb, 0xdd},
+        {0xcc, 0xe5},
+        {0xcd, 0xf1},
+        {0xce, 0xfa},
+        {0xcf, 0xff},
+
+        {0xd0, 0x40},
+        {0xd1, 0x34},
+        {0xd2, 0x34},
+        {0xd3, 0x40},
+        {0xd6, 0xf2},
+        {0xd7, 0x1b},
+        {0xd8, 0x18},
+        {0xdd, 0x03},
+
+        {0xfe, 0x01},
+        {0x05, 0x30},
+        {0x06, 0x75},
+        {0x07, 0x40},
+        {0x08, 0xb0},
+        {0x0a, 0xc5},
+        {0x0b, 0x11},
+        {0x0c, 0x00},
+        {0x12, 0x52},
+        {0x13, 0x38},
+        {0x18, 0x95},
+        {0x19, 0x96},
+        {0x1f, 0x20},
+        {0x20, 0xc0},
+        {0x3e, 0x40},
+        {0x3f, 0x57},
+        {0x40, 0x7d},
+        {0x03, 0x60},
+        {0x44, 0x00},
+
+        {0xfe, 0x01},
+        {0x1c, 0x91},
+        {0x21, 0x15},
+        {0x50, 0x80},
+        {0x56, 0x04},
+        {0x59, 0x08},
+        {0x5b, 0x02},
+        {0x61, 0x8d},
+        {0x62, 0xa7},
+        {0x63, 0xd0},
+        {0x65, 0x06},
+        {0x66, 0x06},
+        {0x67, 0x84},
+        {0x69, 0x08},
+        {0x6a, 0x25},
+        {0x6b, 0x01},
+        {0x6c, 0x00},
+        {0x6d, 0x02},
+        {0x6e, 0xf0},
+        {0x6f, 0x80},
+        {0x76, 0x80},
+        {0x78, 0xaf},
+        {0x79, 0x75},
+        {0x7a, 0x40},
+        {0x7b, 0x50},
+        {0x7c, 0x0c},
+        {0x90, 0xc9}, 
+        {0x91, 0xbe},
+        {0x92, 0xe2},
+        {0x93, 0xc9},
+        {0x95, 0x1b},
+        {0x96, 0xe2},
+        {0x97, 0x49},
+        {0x98, 0x1b},
+        {0x9a, 0x49},
+        {0x9b, 0x1b},
+        {0x9c, 0xc3},
+        {0x9d, 0x49},
+        {0x9f, 0xc7},
+        {0xa0, 0xc8},
+        {0xa1, 0x00},
+        {0xa2, 0x00},
+        {0x86, 0x00},
+        {0x87, 0x00},
+        {0x88, 0x00},
+        {0x89, 0x00},
+        {0xa4, 0xb9},
+        {0xa5, 0xa0},
+        {0xa6, 0xba},
+        {0xa7, 0x92},
+        {0xa9, 0xba},
+        {0xaa, 0x80},
+        {0xab, 0x9d},
+        {0xac, 0x7f},
+        {0xae, 0xbb},
+        {0xaf, 0x9d},
+        {0xb0, 0xc8},
+        {0xb1, 0x97},
+        {0xb3, 0xb7},
+        {0xb4, 0x7f},
+        {0xb5, 0x00},
+        {0xb6, 0x00},
+        {0x8b, 0x00},
+        {0x8c, 0x00},
+        {0x8d, 0x00},
+        {0x8e, 0x00},
+        {0x94, 0x55},
+        {0x99, 0xa6},
+        {0x9e, 0xaa},
+        {0xa3, 0x0a},
+        {0x8a, 0x00},
+        {0xa8, 0x55},
+        {0xad, 0x55},
+        {0xb2, 0x55},
+        {0xb7, 0x05},
+        {0x8f, 0x00},
+        {0xb8, 0xcb},
+        {0xb9, 0x9b},
+
+        {0xfe, 0x01},
+        {0xd0, 0x38}, 
+        {0xd1, 0x00},
+        {0xd2, 0x02},
+        {0xd3, 0x04},
+        {0xd4, 0x38},
+        {0xd5, 0x12},
+        {0xd6, 0x30},
+        {0xd7, 0x00},
+        {0xd8, 0x0a},
+        {0xd9, 0x16},
+        {0xda, 0x39},
+        {0xdb, 0xf8},
+
+        {0xfe, 0x01},
+        {0xc1, 0x3c},
+        {0xc2, 0x50},
+        {0xc3, 0x00},
+        {0xc4, 0x40},
+        {0xc5, 0x30},
+        {0xc6, 0x30},
+        {0xc7, 0x10},
+        {0xc8, 0x00},
+        {0xc9, 0x00},
+        {0xdc, 0x20},
+        {0xdd, 0x10},
+        {0xdf, 0x00},
+        {0xde, 0x00},
+
+        {0x01, 0x10},
+        {0x0b, 0x31}, 
+        {0x0e, 0x50},
+        {0x0f, 0x0f},
+        {0x10, 0x6e},
+        {0x12, 0xa0},
+        {0x15, 0x60},
+        {0x16, 0x60},
+        {0x17, 0xe0},
+
+        {0xcc, 0x0c}, 
+        {0xcd, 0x10},
+        {0xce, 0xa0},
+        {0xcf, 0xe6},
+
+    
+        {0x45, 0xf7},
+        {0x46, 0xff}, 
+        {0x47, 0x15},
+        {0x48, 0x03}, 
+        {0x4f, 0x60}, 
+  
+        {0xfe, 0x00},
+        {0x05, 0x01},
+        {0x06, 0x32}, 
+        {0x07, 0x00},
+        {0x08, 0x0c}, 
+        {0xfe, 0x01},
+        {0x25, 0x00}, 
+        {0x26, 0x3c},
+        {0x27, 0x01}, 
+        {0x28, 0xdc},
+        {0x29, 0x01}, 
+        {0x2a, 0xe0},
+        {0x2b, 0x01}, 
+        {0x2c, 0xe0},
+        {0x2d, 0x01}, 
+        {0x2e, 0xe0},
+        {0x3c, 0x20},
+
+	--SPI配置
+	{0xfe, 0x03},
+	{0x52, 0xa2},
+	{0x53, 0x24}, 
+	{0x54, 0x20},
+	{0x55, 0x00},
+	{0x59, 0x1f}, 
+	{0x5a, 0x00},
+	{0x5b, 0x80},
+	{0x5c, 0x02},
+	{0x5d, 0xe0},
+	{0x5e, 0x01},
+	{0x51, 0x03},
+	{0x64, 0x04},
+	{0xfe, 0x00},
+	{0x44, 0x02},
+}
+
+function gc0310Init(cspiId,i2cId,speed,scanMode,onlyY)
+	local id = camera.init(cspiId,speed,1,1,2,1,0x00010101,onlyY,scanMode,640,480)
+	for i=1,#reg_table do
+		i2c.send(i2cId,0x21,reg_table[i],1)
+	end
+	camera.start(id)
+	return id
+end

+ 361 - 0
module/Air780EHM/demo/camera/spi_cam/gc032a.lua

@@ -0,0 +1,361 @@
+local reg_table = {
+   {0xf3, 0x83}, 
+   {0xf5, 0x08},
+   {0xf7, 0x01},
+   {0xf8, 0x01}, 
+   {0xf9, 0x4e},
+   {0xfa, 0x00},
+   {0xfc, 0x02},
+   {0xfe, 0x02},
+   {0x81, 0x03},
+
+   {0xfe, 0x00},
+   {0x77, 0x64},
+   {0x78, 0x40},
+   {0x79, 0x60},
+
+   {0xfe, 0x00},
+   {0x03, 0x01},
+   {0x04, 0xcb},
+   {0x05, 0x01},
+   {0x06, 0xb2},
+   {0x07, 0x00},
+   {0x08, 0x10},
+
+   {0x0a, 0x00},
+   {0x0c, 0x00},
+   {0x0d, 0x01},
+   {0x0e, 0xe8},
+   {0x0f, 0x02},
+   {0x10, 0x88},
+
+   {0x17, 0x54},
+   {0x19, 0x08},
+   {0x1a, 0x0a},
+   {0x1f, 0x40},
+   {0x20, 0x30},
+   {0x2e, 0x80},
+   {0x2f, 0x2b},
+   {0x30, 0x1a},
+   {0xfe, 0x02},
+   {0x03, 0x02},
+   {0x05, 0xd7},
+   {0x06, 0x60},
+   {0x08, 0x80},
+   {0x12, 0x89},
+
+
+   {0xfe, 0x03},
+   {0x52, 0xba}, 
+   {0x53, 0x24},
+   {0x54, 0x20},
+   {0x55, 0x00},
+   {0x59, 0x1f}, 
+   {0x5a, 0x00}, 
+   {0x5b, 0x80},
+   {0x5c, 0x02},
+   {0x5d, 0xe0},
+   {0x5e, 0x01},
+   {0x51, 0x03},
+   {0x64, 0x04},
+   {0xfe, 0x00},
+
+
+   {0xfe, 0x00},
+   {0x18, 0x02},
+   {0xfe, 0x02},
+   {0x40, 0x22},
+   {0x45, 0x00},
+   {0x46, 0x00},
+   {0x49, 0x20},
+   {0x4b, 0x3c},
+   {0x50, 0x20},
+   {0x42, 0x10},
+
+
+   {0xfe, 0x01},
+   {0x0a, 0xc5},
+   {0x45, 0x00},
+   {0xfe, 0x00},
+   {0x40, 0xff},
+   {0x41, 0x25},
+   {0x42, 0xef},
+   {0x43, 0x10},
+   {0x44, 0x83}, 
+   {0x46, 0x22},
+   {0x49, 0x03},
+   {0x52, 0x02},
+   {0x54, 0x00},
+   {0xfe, 0x02},
+   {0x22, 0xf6},
+
+
+   {0xfe, 0x01},
+   {0xc1, 0x38},
+   {0xc2, 0x4c},
+   {0xc3, 0x00},
+   {0xc4, 0x2c},
+   {0xc5, 0x24},
+   {0xc6, 0x18},
+   {0xc7, 0x28},
+   {0xc8, 0x11},
+   {0xc9, 0x15},
+   {0xca, 0x20},
+   {0xdc, 0x7a},
+   {0xdd, 0xa0},
+   {0xde, 0x80},
+   {0xdf, 0x88},
+
+
+   {0xfe, 0x01},
+   {0x50, 0xc1},
+   {0x56, 0x34},
+   {0x58, 0x04},
+   {0x65, 0x06},
+   {0x66, 0x0f},
+   {0x67, 0x04},
+   {0x69, 0x20},
+   {0x6a, 0x40},
+   {0x6b, 0x81},
+   {0x6d, 0x12},
+   {0x6e, 0xc0},
+   {0x7b, 0x2a},
+   {0x7c, 0x0c},
+   {0xfe, 0x01},
+   {0x90, 0xe3},
+   {0x91, 0xc2},
+   {0x92, 0xff},
+   {0x93, 0xe3},
+   {0x95, 0x1c},
+   {0x96, 0xff},
+   {0x97, 0x44},
+   {0x98, 0x1c},
+   {0x9a, 0x44},
+   {0x9b, 0x1c},
+   {0x9c, 0x64},
+   {0x9d, 0x44},
+   {0x9f, 0x71},
+   {0xa0, 0x64},
+   {0xa1, 0x00},
+   {0xa2, 0x00},
+   {0x86, 0x00},
+   {0x87, 0x00},
+   {0x88, 0x00},
+   {0x89, 0x00},
+   {0xa4, 0xc2},
+   {0xa5, 0x9b},
+   {0xa6, 0xc8},
+   {0xa7, 0x92},
+   {0xa9, 0xc9},
+   {0xaa, 0x96},
+   {0xab, 0xa9},
+   {0xac, 0x99},
+   {0xae, 0xce},
+   {0xaf, 0xa9},
+   {0xb0, 0xcf},
+   {0xb1, 0x9d},
+   {0xb3, 0xcf},
+   {0xb4, 0xac},
+   {0xb5, 0x00},
+   {0xb6, 0x00},
+   {0x8b, 0x00},
+   {0x8c, 0x00},
+   {0x8d, 0x00},
+   {0x8e, 0x00},
+   {0x94, 0x55},
+   {0x99, 0xa6},
+   {0x9e, 0xaa},
+   {0xa3, 0x0a},
+   {0x8a, 0x00},
+   {0xa8, 0x55},
+   {0xad, 0x55},
+   {0xb2, 0x55},
+   {0xb7, 0x05},
+   {0x8f, 0x00},
+   {0xb8, 0xc7},
+   {0xb9, 0xa0},
+
+   {0xfe, 0x01},
+   {0xd0, 0x40},
+   {0xd1, 0x00},
+   {0xd2, 0x00},
+   {0xd3, 0xfa},
+   {0xd4, 0x4a},
+   {0xd5, 0x02},
+
+   {0xd6, 0x44},
+   {0xd7, 0xfa},
+   {0xd8, 0x04},
+   {0xd9, 0x08},
+   {0xda, 0x5c},
+   {0xdb, 0x02},
+   {0xfe, 0x00},
+
+
+   {0xfe, 0x00},
+   {0xba, 0x00},
+   {0xbb, 0x04},
+   {0xbc, 0x0a},
+   {0xbd, 0x0e},
+   {0xbe, 0x22},
+   {0xbf, 0x30},
+   {0xc0, 0x3d},
+   {0xc1, 0x4a},
+   {0xc2, 0x5d},
+   {0xc3, 0x6b},
+   {0xc4, 0x7a},
+   {0xc5, 0x85},
+   {0xc6, 0x90},
+   {0xc7, 0xa5},
+   {0xc8, 0xb5},
+   {0xc9, 0xc2},
+   {0xca, 0xcc},
+   {0xcb, 0xd5},
+   {0xcc, 0xde},
+   {0xcd, 0xea},
+   {0xce, 0xf5},
+   {0xcf, 0xff},
+
+
+   {0xfe, 0x00},
+   {0x5a, 0x08},
+   {0x5b, 0x0f},
+   {0x5c, 0x15},
+   {0x5d, 0x1c},
+   {0x5e, 0x28},
+   {0x5f, 0x36},
+   {0x60, 0x45},
+   {0x61, 0x51},
+   {0x62, 0x6a},
+   {0x63, 0x7d},
+   {0x64, 0x8d},
+   {0x65, 0x98},
+   {0x66, 0xa2},
+   {0x67, 0xb5},
+   {0x68, 0xc3},
+   {0x69, 0xcd},
+   {0x6a, 0xd4},
+   {0x6b, 0xdc},
+   {0x6c, 0xe3},
+   {0x6d, 0xf0},
+   {0x6e, 0xf9},
+   {0x6f, 0xff},
+
+
+   {0xfe, 0x00},
+   {0x70, 0x50},
+
+
+   {0xfe, 0x00},
+   {0x4f, 0x01},
+   {0xfe, 0x01},
+   {0x0c, 0x01},
+   {0x0d, 0x00}, 
+   {0x12, 0xa0},
+   {0x13, 0x38},
+   {0x1f, 0x40},
+   {0x20, 0x40},
+   {0x23, 0x0a},
+   {0x26, 0x9a},
+   {0x3e, 0x20},
+   {0x3f, 0x2d},
+   {0x40, 0x40},
+   {0x41, 0x5b},
+   {0x42, 0x82},
+   {0x43, 0xb7},
+   {0x04, 0x0a},
+   {0x02, 0x79},
+   {0x03, 0xc0},
+
+  
+   {0xfe, 0x01},
+   {0xcc, 0x08},
+   {0xcd, 0x08},
+   {0xce, 0xa4},
+   {0xcf, 0xec},
+
+ 
+   {0xfe, 0x00},
+   {0x81, 0xb8},
+   {0x82, 0x04},
+   {0x83, 0x10},
+   {0x84, 0x01},
+   {0x86, 0x50},
+   {0x87, 0x18},
+   {0x88, 0x10},
+   {0x89, 0x70},
+   {0x8a, 0x20},
+   {0x8b, 0x10},
+   {0x8c, 0x08},
+   {0x8d, 0x0a},
+
+
+   {0xfe, 0x00},
+   {0x8f, 0xaa},
+   {0x90, 0x1c},
+   {0x91, 0x52},
+   {0x92, 0x03},
+   {0x93, 0x03},
+   {0x94, 0x08},
+   {0x95, 0x6a},
+   {0x97, 0x00},
+   {0x98, 0x00},
+
+ 
+   {0xfe, 0x00},
+   {0x9a, 0x30},
+   {0x9b, 0x50},
+   {0xa1, 0x30},
+   {0xa2, 0x66},
+   {0xa4, 0x28},
+   {0xa5, 0x30},
+   {0xaa, 0x28},
+   {0xac, 0x32},
+
+  
+   {0xfe, 0x00},
+   {0xd1, 0x3f},
+   {0xd2, 0x3f},
+   {0xd3, 0x38},
+   {0xd6, 0xf4},
+   {0xd7, 0x1d},
+   {0xdd, 0x72},
+   {0xde, 0x84},
+
+   {0xfe, 0x00},
+   {0x05, 0x01},
+   {0x06, 0xad},
+   {0x07, 0x00},
+   {0x08, 0x10},
+
+   {0xfe, 0x01},
+   {0x25, 0x00},
+   {0x26, 0x4d},
+
+   {0x27, 0x01},
+   {0x28, 0xce}, 
+   {0x29, 0x01},
+   {0x2a, 0xce}, 
+   {0x2b, 0x01},
+   {0x2c, 0xce}, 
+   {0x2d, 0x01},
+   {0x2e, 0xce}, 
+   {0x2f, 0x01},
+   {0x30, 0xce}, 
+   {0x31, 0x01},
+   {0x32, 0xce}, 
+   {0x33, 0x01},
+   {0x34, 0xce}, 
+   {0x3c, 0x10}, 
+   {0xfe, 0x00},
+   {0x44, 0x03},
+}
+
+function gc032aInit(cspiId,i2cId,speed,scanMode,onlyY)
+	local id = camera.init(cspiId,speed,1,1,2,1,0x00010101,onlyY,scanMode,640,480)
+	for i=1,#reg_table do
+		i2c.send(i2cId,0x21,reg_table[i],1)
+	end
+	camera.start(id)
+	return id
+end

+ 223 - 0
module/Air780EHM/demo/camera/spi_cam/main.lua

@@ -0,0 +1,223 @@
+PROJECT = "camera_30W_480_320_demo"
+VERSION = "4.0.0"
+-- 实际使用时选1个就行
+-- require "bf30a2"
+require "gc032a"
+-- require "gc0310"
+sys = require("sys")
+log.style(1)
+
+
+pm.ioVol(pm.IOVOL_ALL_GPIO, 3000)
+
+--  mcu.altfun(mcu.I2C, 0, 66, 2, nil)
+--  mcu.altfun(mcu.I2C, 0, 67, 2, nil)
+
+gpio.setup(2,1)--GPIO2打开给camera_3.3V供电
+
+-- 注意:V1.2的开发板需要打开GPIO28,V1.3的开发板需要打开GPIO29
+-- gpio.setup(28, 1) -- GPIO28打开给lcd电源供电 
+gpio.setup(29, 1) -- GPIO29打开给lcd电源供电 
+
+gpio.setup(14, nil)
+gpio.setup(15, nil)
+
+local SCAN_MODE = 0 -- 写1演示扫码(使用摄像头对二维码、条形码或其他类型的图案进行扫描和识别)
+local scan_pause = true -- 扫码启动与停止标志位
+local getRawStart = false
+local RAW_MODE = 0 -- 写1演示获取原始图像
+-- SCAN_MODE和RAW_MODE都没有写1就是拍照
+
+------------------------------------
+------------ 初始化 LCD ------------
+------------------------------------
+-- 根据不同的BSP返回不同的值
+-- spi_id,pin_reset,pin_dc,pin_cs,bl
+local function lcd_pin()
+    local rtos_bsp = rtos.bsp()
+    if string.find(rtos_bsp,"780EPM") then
+        return lcd.HWID_0, 36, 0xff, 0xff, 0xff -- 注意:EC718P有硬件lcd驱动接口, 无需使用spi,当然spi驱动也支持
+    else
+        log.info("main", "没找到合适的cat.1芯片",rtos_bsp)
+        return
+    end
+end
+local spi_id, pin_reset, pin_dc, pin_cs, bl = lcd_pin()
+if spi_id ~= lcd.HWID_0 then
+    spi_lcd = spi.deviceSetup(spi_id, pin_cs, 0, 0, 8, 20 * 1000 * 1000, spi.MSB, 1, 0)
+    port = "device"
+else
+    port = spi_id
+end
+
+
+lcd.init("st7796", {
+    port = port,
+    pin_dc = pin_dc,
+    pin_pwr = bl,
+    pin_rst = pin_reset,
+    direction = 0,
+    -- direction0 = 0x00,
+    w = 320,
+    h = 480,
+    xoffset = 0,
+    yoffset = 0,
+    sleepcmd = 0x10,
+    wakecmd = 0x11,
+})
+
+------------------------------------
+------------ 初始化串口 -------------
+------------------------------------
+local uartid = uart.VUART_0 -- 根据实际设备选取不同的uartid
+-- 初始化
+local result = uart.setup(uartid, -- 串口id
+600000, -- 波特率
+8, -- 数据位
+1 -- 停止位
+)
+
+------------------------------------
+----------- 初始化摄像头 -----------
+------------------------------------
+camera.on(0, "scanned", function(id, str)
+    if type(str) == 'string' then -- 如果是扫码模式(使用摄像头对二维码、条形码或其他类型的图案进行扫描和识别)
+        log.info("扫码结果", str)
+    elseif str == false then -- 如果摄像头没有正常工作
+        log.error("摄像头没有数据")
+    else -- 如果摄像头正常工作,并且不是扫码模式
+        log.info("摄像头数据", str)
+        sys.publish("capture done", true)
+    end
+end) -- 注册 camera 0 的 "scanned" 事件
+
+------------------------------------
+-------- 注册 boot 按键中断 ---------
+------------------------------------
+local function press_key()
+    log.info("boot press")
+    sys.publish("PRESS", true)
+end
+gpio.setup(0, press_key, gpio.PULLDOWN, gpio.RISING)
+gpio.debounce(0, 100, 1)
+local rawbuff, err
+if RAW_MODE ~= 1 then
+    -- 如果 RAW_MODE 不等于 1,创建一个大小为 60KB 的缓冲区
+    rawbuff, err = zbuff.create(60 * 1024, 0, zbuff.HEAP_AUTO)
+else
+    -- 如果 RAW_MODE 等于 1
+    rawbuff, err = zbuff.create(640 * 480 * 2, 0, zbuff.HEAP_AUTO) -- 创建一个大小为 640x480 像素,每个像素占用 2 字节的缓冲区,适用于 gc032a 摄像头
+    -- local rawbuff = zbuff.create(240 * 320 * 2, zbuff.HEAP_AUTO)  -- 创建一个大小为 240x320 像素,每个像素占用 2 字节的缓冲区,适用于 bf302a 摄像头
+end
+if rawbuff == nil then
+    log.info(err)
+end
+
+--------------------------------------------------
+---- 将文件系统中存储的jpg文件通过串口发送给电脑 ----
+--------------------------------------------------
+local function sendFile()
+    sys.taskInit(function()
+        local fileHandle = io.open("/testCamera.jpg","rb")
+        -- log.info("文件大小",fileHandle)
+        if not fileHandle then
+            log.error("打开文件失败")
+            return            
+        else
+            log.info("文件打开成功,文件大小为",io.fileSize("/testCamera.jpg"))
+        end
+
+        while true do
+            local data = fileHandle:read(1460)
+            -- log.info("data我看看",data)
+            if not data then break end
+            log.info("虚拟uart发送数据",uart.write(uartid, data))
+            sys.wait(10)
+            -- sys.waitUntil("UART_SENT2MCU_OK")
+        end
+        fileHandle:close()
+
+        os.remove("/testCamera.jpg") -- 删除文件
+    end)
+end
+
+sys.taskInit(function()
+    log.info("摄像头启动")
+
+    -- 初始化摄像头
+    local cspiId, i2cId = 1, 1 -- spi的id和摄像头的id
+    local camera_id
+    i2c.setup(i2cId, i2c.FAST) -- I2C1设置为快速模式,不开启上拉
+    gpio.setup(5, 0) -- 将 GPIO5 下拉,用于 SPI 片选信号
+    -- camera_id = bf30a2Init(cspiId,i2cId,25500000,SCAN_MODE,SCAN_MODE)
+    -- camera_id = gc0310Init(cspiId, i2cId, 25500000, SCAN_MODE, SCAN_MODE)
+    camera_id = gc032aInit(cspiId,i2cId,24000000,SCAN_MODE,SCAN_MODE) -- 通过 I2C1 配置摄像头,SPI1 时钟频率为 24 MHz
+    camera.stop(camera_id) -- 暂停摄像头捕获数据。仅停止了图像捕获,未影响预览功能。
+    camera.preview(camera_id, true) -- 打开LCD预览功能(直接将摄像头数据输出到LCD)
+
+    log.info("按下boot开始测试")
+    -- 总内存大小,单位字节;当前已使用的内存大小,单位字节;历史最高已使用的内存大小,单位字节
+    log.info(rtos.meminfo("sys")) -- 打印系统内存信息
+    log.info(rtos.meminfo("psram")) -- 打印PSRAM内存信息
+    while 1 do
+        result, data = sys.waitUntil("PRESS", 30 * 1000) -- 等待 "PRESS" 事件 30 秒(等待boot按键被按下)
+        if result == true and data == true then -- 如果 30 秒内检测到 "PRESS" 事件往下执行
+            if SCAN_MODE == 1 then
+                if scan_pause then
+                    log.info("启动扫码")
+                    -- camera_id = gc0310Init(cspiId,i2cId,25500000,SCAN_MODE,SCAN_MODE)
+                    camera.start(camera_id)
+                    scan_pause = false
+                    sys.wait(200)
+                    log.info(rtos.meminfo("sys"))
+                    log.info(rtos.meminfo("psram"))
+                else
+                    log.info("停止扫码")
+                    -- camera.close(camera_id)	--完全关闭摄像头才用这个
+                    camera.stop(camera_id)
+                    scan_pause = true
+                    sys.wait(200)
+                    log.info(rtos.meminfo("sys"))
+                    log.info(rtos.meminfo("psram"))
+                end
+            elseif RAW_MODE == 1 then -- 摄像头捕获原始数据
+                if getRawStart == false then
+                    getRawStart = true
+                    log.debug("摄像头首次捕获原始图像")
+                    camera.startRaw(camera_id, 640, 480, rawbuff) -- gc032a
+                    -- camera.startRaw(camera_id,240,320,rawbuff) --bf302a
+                else
+                    log.debug("摄像头继续捕获原始图像")
+                    camera.getRaw(camera_id)
+                end
+                result, data = sys.waitUntil("capture done", 30000)
+                log.info("摄像头捕获原始图像完成")
+                camera.stop(camera_id) -- 暂停摄像头捕获数据。仅停止了图像捕获,未影响预览功能。
+                log.info(rtos.meminfo("sys"))
+                log.info(rtos.meminfo("psram"))
+                -- uart.tx(uartid, rawbuff) --找个能保存数据的串口工具保存成文件就能在电脑上看了, 格式为JPG
+            else -- 拍照模式
+                log.debug("摄像头拍照")
+                -- camera_id = gc0310Init(cspiId,i2cId,25500000,SCAN_MODE,SCAN_MODE)
+                -- 将摄像头拍照的数据存入 "/testCamera.jpg" 文件中
+                -- jpeg压缩质量1最差,占用空间小,3最高,占用空间大,2和3需要非常多非常多的psram,尽量不要用
+                camera.capture(camera_id, "/testCamera.jpg", 1) -- 2和3需要非常多非常多的psram,尽量不要用
+                result, data = sys.waitUntil("capture done", 30000)
+                -- log.info(rawbuff:used())
+                -- camera.close(camera_id)	--完全关闭摄像头才用这个
+                camera.stop(camera_id) -- 暂停摄像头捕获数据。仅停止了图像捕获,未影响预览功能。
+                sendFile() -- 创建一个任务将摄像头数据通过串口发送到电脑
+                -- rawbuff:resize(60 * 1024)
+                -- log.info(rtos.meminfo("sys"))
+                -- log.info(rtos.meminfo("psram"))
+            end
+
+        end
+    end
+
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 73 - 0
module/Air780EHM/demo/can/main.lua

@@ -0,0 +1,73 @@
+PROJECT = "candemo"
+VERSION = "1.0.0"
+sys = require("sys")
+log.style(1)
+local SELF_TEST_FLAG = false --自测模式标识,写true就进行自收自发模式,写false就进行正常收发模式
+local node_a = true   -- A节点写true, B节点写false
+local can_id = 0
+local rx_id
+local tx_id
+local stb_pin = 28		-- stb引脚根据实际情况写,不用的话,也可以不写
+if node_a then          -- A/B节点区分,互相传输测试
+    rx_id = 0x12345678
+    tx_id = 0x12345677
+else
+    rx_id = 0x12345677
+    tx_id = 0x12345678
+end
+local test_cnt = 0
+local tx_buf = zbuff.create(8)  --创建zbuff
+local function can_cb(id, cb_type, param)
+    if cb_type == can.CB_MSG then
+        log.info("有新的消息")
+        local succ, id, id_type, rtr, data = can.rx(id)
+        while succ do
+            log.info(mcu.x32(id), #data, data:toHex())
+            succ, id, id_type, rtr, data = can.rx(id)
+        end
+    end
+    if cb_type == can.CB_TX then
+        if param then
+            log.info("发送成功")
+        else
+            log.info("发送失败")
+        end
+    end
+    if cb_type == can.CB_ERR then
+        log.info("CAN错误码", mcu.x32(param))
+    end
+    if cb_type == can.CB_STATE then
+        log.info("CAN新状态", param)
+    end
+end
+
+local function can_tx_test(data)
+    if node_a then
+        log.info("node a tx")
+    else
+        log.info("node b tx")
+    end
+	test_cnt = test_cnt + 1
+	if test_cnt > 8 then
+		test_cnt = 1
+	end
+	tx_buf:set(0,test_cnt)  --zbuff的类似于memset操作,类似于memset(&buff[start], num, len)
+	tx_buf:seek(test_cnt)   --zbuff设置光标位置(可能与当前指针位置有关;执行后指针会被设置到指定位置)
+    can.tx(can_id, tx_id, can.EXT, false, true, tx_buf)
+end
+-- can.debug(true)
+-- gpio.setup(stb_pin,0)   -- 配置STB引脚为输出低电平
+gpio.setup(stb_pin,1)	-- 如果开发板上STB信号有逻辑取反,则要配置成输出高电平
+can.init(can_id, 128)            -- 初始化CAN,参数为CAN ID,接收缓存消息数的最大值
+can.on(can_id, can_cb)            -- 注册CAN的回调函数
+can.timing(can_id, 1000000, 5, 4, 3, 2)     --CAN总线配置时序
+if SELF_TEST_FLAG then
+	can.node(can_id, tx_id, can.EXT)	-- 测试模式下,允许接收的ID和发送ID一致才会有新数据提醒
+	can.mode(can_id, can.MODE_TEST)     -- 如果只是自身测试硬件好坏,可以用测试模式来验证,如果发送成功就OK
+else
+	can.node(can_id, rx_id, can.EXT)
+	can.mode(can_id, can.MODE_NORMAL)   -- 一旦设置mode就开始正常工作了,此时不能再设置node,timing,filter等
+end
+
+sys.timerLoopStart(can_tx_test, 2000)
+sys.run()

BIN
module/Air780EHM/demo/crypto/logo.jpg


+ 242 - 0
module/Air780EHM/demo/crypto/main.lua

@@ -0,0 +1,242 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "cryptodemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+sys.taskInit(function()
+
+    sys.wait(1000)
+
+    -- MD5,输出结果已经hex编码
+    log.info("md5", crypto.md5("abc"))
+    log.info("hmac_md5", crypto.hmac_md5("abc", "1234567890"))
+
+    -- SHA1,输出结果已经hex编码
+    log.info("sha1", crypto.sha1("abc"))
+    log.info("hmac_sha1", crypto.hmac_sha1("abc", "1234567890"))
+
+    -- SHA256,输出结果已经hex编码
+    log.info("sha256", crypto.sha256("abc"))
+    log.info("hmac_sha256", crypto.hmac_sha256("abc", "1234567890"))
+
+    -- SHA512,输出结果已经hex编码
+    log.info("sha512", crypto.sha512("abc"))
+    log.info("hmac_sha512", crypto.hmac_sha512("abc", "1234567890"))
+
+	local data_encrypt = crypto.cipher_encrypt("AES-128-ECB", "ZERO", "023001", "HZBIT@WLW/YSBKEY")
+	log.info("AES", "aes-128-ecb", data_encrypt:toHex())
+	local data_decrypt = crypto.cipher_decrypt("AES-128-ECB", "ZERO", data_encrypt, "HZBIT@WLW/YSBKEY")
+	log.info("AES", "aes-128-ecb", data_decrypt)
+
+    -- AES加密, 未经Hex编码. AES-128-ECB 算法,待加密字符串如果超过32字节会报错,待查. by wendal 20200812
+    local data_encrypt = crypto.cipher_encrypt("AES-128-ECB", "PKCS7", "12345678901234 > 123456", "1234567890123456")
+    local data2_encrypt = crypto.cipher_encrypt("AES-128-CBC", "PKCS7", "12345678901234 > 123456", "1234567890123456", "1234567890666666")
+    log.info("AES", "aes-128-ecb", data_encrypt:toHex())
+    log.info("AES", "aes-128-cbc", data2_encrypt:toHex())
+
+    -- AES解密, 未经Hex编码
+    local data_decrypt = crypto.cipher_decrypt("AES-128-ECB", "PKCS7", data_encrypt, "1234567890123456")
+    local data2_decrypt = crypto.cipher_decrypt("AES-128-CBC", "PKCS7", data2_encrypt, "1234567890123456", "1234567890666666")
+    log.info("AES", "aes-128-ecb", data_decrypt)
+    log.info("AES", "aes-128-cbc", data2_decrypt)
+    log.info("mem", rtos.meminfo("sys"))
+
+    -- DES-ECB 加解密
+    local data1 = crypto.cipher_encrypt("DES-ECB", "PKCS7", "abcdefg", "12345678")
+    if data1 then -- DES-ECB 在某些平台不支持的
+        log.info("des", data1:toHex())
+        local data2 = crypto.cipher_decrypt("DES-ECB", "PKCS7", data1, "12345678")
+        log.info("des", data2)
+    else
+        log.info("des", "当前固件不支持DES/3DES")
+    end
+
+    -- 3DES-ECB 加解密
+    local data1 = crypto.cipher_encrypt("DES-EDE3-ECB", "PKCS7", "abcdefg!!--ZZSS", "123456781234567812345678")
+    if data1 then -- DES-ECB 在某些平台不支持的
+        log.info("3des", data1:toHex())
+        local data2 = crypto.cipher_decrypt("DES-EDE3-ECB", "PKCS7", data1, "123456781234567812345678")
+        log.info("3des", data2)
+    else
+        log.info("3des", "当前固件不支持DES/3DES")
+    end
+
+
+    -- 打印所有支持的cipher
+    if crypto.cipher_list then
+        log.info("cipher", "list", json.encode(crypto.cipher_list()))
+    else
+        log.info("cipher", "当前固件不支持crypto.cipher_list")
+    end
+    -- 打印所有支持的cipher suites
+    if crypto.cipher_suites then
+        log.info("cipher", "suites", json.encode(crypto.cipher_suites()))
+    else
+        log.info("cipher", "当前固件不支持crypto.cipher_suites")
+    end
+
+    ---------------------------------------
+    log.info("随机数测试")
+    for i=1, 10 do
+        sys.wait(100)
+        log.info("crypto", "真随机数",string.unpack("I",crypto.trng(4)))
+        -- log.info("crypto", "伪随机数",math.random()) -- 输出的是浮点数,不推荐
+        -- log.info("crypto", "伪随机数",math.random(1, 65525)) -- 不推荐
+    end
+
+    -- totp的密钥
+    log.info("totp的密钥")
+    local secret = "VK54ZXPO74ISEM2E"
+    --写死时间戳用来测试
+    local ts = 1646796576
+    --生成十分钟的动态码验证下
+    for i=1,600,30 do
+        local r = crypto.totp(secret,ts+i)
+        local time = os.date("*t",ts+i + 8*3600)--东八区
+        log.info("totp", string.format("%06d" ,r),time.hour,time.min,time.sec)
+    end
+
+    -- 文件测试
+    log.info("文件hash值测试")
+    if crypto.md_file then
+        log.info("md5", crypto.md_file("MD5", "/luadb/logo.jpg"))
+        log.info("sha1", crypto.md_file("SHA1", "/luadb/logo.jpg"))
+        log.info("sha256", crypto.md_file("SHA256", "/luadb/logo.jpg"))
+        
+        log.info("hmac_md5", crypto.md_file("MD5", "/luadb/logo.jpg", "123456"))
+        log.info("hmac_sha1", crypto.md_file("SHA1", "/luadb/logo.jpg", "123456"))
+        log.info("hmac_sha256", crypto.md_file("SHA256", "/luadb/logo.jpg", "123456"))
+    else
+        log.info("文件hash值测试", "当前固件不支持crypto.md_file")
+    end
+
+    if crypto.checksum then
+        log.info("checksum", "OK", string.char(crypto.checksum("OK")):toHex())
+        log.info("checksum", "357E", string.char(crypto.checksum("357E", 1)):toHex())
+    else
+        log.info("checksum", "当前固件不支持crypto.checksum")
+    end
+
+    -- 流式hash测试
+    log.info("流式hash测试")
+    if crypto.hash_init then
+        -- MD5
+        local md5_obj = crypto.hash_init("MD5")
+        crypto.hash_update(md5_obj, "1234567890")
+        crypto.hash_update(md5_obj, "1234567890")
+        crypto.hash_update(md5_obj, "1234567890")
+        crypto.hash_update(md5_obj, "1234567890")
+        local md5_result = crypto.hash_finish(md5_obj)
+        log.info("md5_stream", md5_result)
+        log.info("md5", crypto.md5("1234567890123456789012345678901234567890"))
+
+        -- HMAC_MD5
+        local hmac_md5_obj = crypto.hash_init("MD5", "1234567890")
+        crypto.hash_update(hmac_md5_obj, "1234567890")
+        crypto.hash_update(hmac_md5_obj, "1234567890")
+        crypto.hash_update(hmac_md5_obj, "1234567890")
+        crypto.hash_update(hmac_md5_obj, "1234567890")
+        local hmac_md5_result = crypto.hash_finish(hmac_md5_obj)
+        log.info("hmac_md5_stream", hmac_md5_result)
+        log.info("hmac_md5", crypto.hmac_md5("1234567890123456789012345678901234567890", "1234567890"))
+
+        -- SHA1
+        local sha1_obj = crypto.hash_init("SHA1")
+        crypto.hash_update(sha1_obj, "1234567890")
+        crypto.hash_update(sha1_obj, "1234567890")
+        crypto.hash_update(sha1_obj, "1234567890")
+        crypto.hash_update(sha1_obj, "1234567890")
+        local sha1_result = crypto.hash_finish(sha1_obj)
+        log.info("sha1_stream", sha1_result)
+        log.info("sha1", crypto.sha1("1234567890123456789012345678901234567890"))
+
+        -- HMAC_SHA1
+        local hmac_sha1_obj = crypto.hash_init("SHA1", "1234567890")
+        crypto.hash_update(hmac_sha1_obj, "1234567890")
+        crypto.hash_update(hmac_sha1_obj, "1234567890")
+        crypto.hash_update(hmac_sha1_obj, "1234567890")
+        crypto.hash_update(hmac_sha1_obj, "1234567890")
+        local hmac_sha1_result = crypto.hash_finish(hmac_sha1_obj)
+        log.info("hmac_sha1_stream", hmac_sha1_result)
+        log.info("hmac_sha1", crypto.hmac_sha1("1234567890123456789012345678901234567890", "1234567890"))
+
+        -- SHA256
+        local sha256_obj = crypto.hash_init("SHA256")
+        crypto.hash_update(sha256_obj, "1234567890")
+        crypto.hash_update(sha256_obj, "1234567890")
+        crypto.hash_update(sha256_obj, "1234567890")
+        crypto.hash_update(sha256_obj, "1234567890")
+        local sha256_result = crypto.hash_finish(sha256_obj)
+        log.info("sha256_stream", sha256_result)
+        log.info("sha256", crypto.sha256("1234567890123456789012345678901234567890"))
+
+        -- HMAC_SHA256
+        local hmac_sha256_obj = crypto.hash_init("SHA256", "1234567890")
+        crypto.hash_update(hmac_sha256_obj, "1234567890")
+        crypto.hash_update(hmac_sha256_obj, "1234567890")
+        crypto.hash_update(hmac_sha256_obj, "1234567890")
+        crypto.hash_update(hmac_sha256_obj, "1234567890")
+        local hmac_sha256_result = crypto.hash_finish(hmac_sha256_obj)
+        log.info("hmac_sha256_stream", hmac_sha256_result)
+        log.info("hmac_sha256", crypto.hmac_sha256("1234567890123456789012345678901234567890", "1234567890"))
+
+        -- SHA512
+        local sha512_obj = crypto.hash_init("SHA512")
+        if sha512_obj then
+            crypto.hash_update(sha512_obj, "1234567890")
+            crypto.hash_update(sha512_obj, "1234567890")
+            crypto.hash_update(sha512_obj, "1234567890")
+            crypto.hash_update(sha512_obj, "1234567890")
+            local sha512_result = crypto.hash_finish(sha512_obj)
+            log.info("sha512_stream", sha512_result)
+            log.info("sha512", crypto.sha512("1234567890123456789012345678901234567890"))
+        end
+
+        -- HMAC_SHA512
+        local hmac_sha512_obj = crypto.hash_init("SHA512", "1234567890")
+        if hmac_sha512_obj then
+            crypto.hash_update(hmac_sha512_obj, "1234567890")
+            crypto.hash_update(hmac_sha512_obj, "1234567890")
+            crypto.hash_update(hmac_sha512_obj, "1234567890")
+            crypto.hash_update(hmac_sha512_obj, "1234567890")
+            local hmac_sha512_result = crypto.hash_finish(hmac_sha512_obj)
+            log.info("hmac_sha512_stream", hmac_sha512_result)
+            log.info("hmac_sha512", crypto.hmac_sha512("1234567890123456789012345678901234567890", "1234567890"))
+        end
+    else
+        log.info("crypto", "当前固件不支持crypto.hash_init")
+    end
+
+    log.info("crc7测试")
+    if crypto.crc7 then
+        local result = crypto.crc7(string.char(0xAA), 0xE5, 0x00)
+        log.info("crc7测试", result, string.format("%02X", result))
+    else
+        log.info("crypto", "当前固件不支持crypto.crc7")
+    end
+
+    log.info("crypto", "ALL Done")
+    sys.wait(100000)
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 62 - 0
module/Air780EHM/demo/errDump/main.lua

@@ -0,0 +1,62 @@
+PROJECT = "errdump_test"
+VERSION = "1.0"
+PRODUCT_KEY = "s1uUnY6KA06ifIjcutm5oNbG3MZf5aUv" --换成自己的
+-- sys库是标配
+_G.sys = require("sys")
+_G.sysplus = require("sysplus")
+log.style(1)
+
+--下面演示自动发送
+-- errDump.config(true, 600, "user_id")	-- 默认是关闭,用这个可以额外添加用户标识,比如用户自定义的ID之类
+
+-- local function test_user_log()
+-- 	while true do
+-- 		sys.wait(15000)
+-- 		errDump.record("测试一下用户的记录功能")
+-- 	end
+-- end
+
+-- local function test_error_log()
+-- 	sys.wait(60000)
+-- 	-- lllllllllog.record("测试一下用户的记录功能") --默认写错代码死机
+-- end
+
+
+
+-- 下面演示手动获取信息
+errDump.config(true, 0)
+local function test_user_log()
+	local buff = zbuff.create(4096)
+	local new_flag = errDump.dump(buff, errDump.TYPE_SYS)		-- 开机手动读取一次异常日志
+	if buff:used() > 0 then
+		log.info(buff:toStr(0, buff:used()))	-- 打印出异常日志
+	end
+	new_flag = errDump.dump(buff, errDump.TYPE_SYS)
+	if not new_flag then
+		log.info("没有新数据了,删除系统错误日志")
+		errDump.dump(nil, errDump.TYPE_SYS, true)
+	end
+	while true do
+		sys.wait(15000)
+		errDump.record("测试一下用户的记录功能")
+		local new_flag = errDump.dump(buff, errDump.TYPE_USR)
+		if new_flag then
+			log.info("errBuff", buff:toStr(0, buff:used()))
+		end
+		new_flag = errDump.dump(buff, errDump.TYPE_USR)
+		if not new_flag then
+			log.info("没有新数据了,删除用户错误日志")
+			errDump.dump(nil, errDump.TYPE_USR, true)
+		end
+
+	end
+end
+
+local function test_error_log()
+	sys.wait(60000)
+	lllllllllog.record("测试一下用户的记录功能") --默认写错代码死机
+end
+
+sys.taskInit(test_user_log)
+sys.taskInit(test_error_log)
+sys.run()

+ 99 - 0
module/Air780EHM/demo/fota2/main.lua

@@ -0,0 +1,99 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "fotademo"
+-- iot限制,只能上传xxx.yyy.zzz格式的三位数的版本号,但实际上现在只用了XXX和ZZZ,中间yyy暂未使用
+-- 需要注意的是,因为yyy不生效,所以111.222.333版本和111.444.333版本,对iot平台来说都一样,所以建议中间那一位永远写000
+VERSION = "001.000.000"
+
+-- 使用合宙iot平台时需要这个参数
+PRODUCT_KEY = "123" -- 到 iot.openluat.com 创建项目,获取正确的项目id
+
+sys = require "sys"
+libfota2 = require "libfota2"
+
+-- 联网函数, 可自行删减
+sys.taskInit(function()
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    log.info("4G网络链接成功")
+    sys.publish("net_ready")
+end)
+
+-- 循环打印版本号, 方便看版本号变化, 非必须
+sys.taskInit(function()
+    while 1 do
+        sys.wait(5000)
+        log.info("降功耗 找合宙")
+        -- log.info("fota", "脚本版本号", VERSION)
+        log.info("fota", "脚本版本号", VERSION, "core版本号", rtos.version())
+    end
+end)
+
+-- 升级结果的回调函数
+-- 功能:获取fota的回调函数
+-- 参数:
+-- result:number类型
+--   0表示成功
+--   1表示连接失败
+--   2表示url错误
+--   3表示服务器断开
+--   4表示接收报文错误
+--   5表示使用iot平台VERSION需要使用 xxx.yyy.zzz形式
+local function fota_cb(ret)
+    log.info("fota", ret)
+    if ret == 0 then
+        log.info("升级包下载成功,重启模块")
+        rtos.reboot()
+    elseif ret == 1 then
+        log.info("连接失败", "请检查url拼写或服务器配置(是否为内网)")
+    elseif ret == 2 then
+        log.info("url错误", "检查url拼写")
+    elseif ret == 3 then
+        log.info("服务器断开", "检查服务器白名单配置")
+    elseif ret == 4 then
+        log.info("接收报文错误", "检查模块固件或升级包内文件是否正常")
+    elseif ret == 5 then
+        log.info("版本号书写错误", "iot平台版本号需要使用xxx.yyy.zzz形式")
+    else
+        log.info("不是上面几种情况 ret为", ret)
+    end
+end
+local ota_opts = {}
+
+-- 使用合宙iot平台进行升级,不需要管下面这段代码
+-- 使用第三方服务器时打开下面这段代码
+--[[local ota_opts = {
+    url = "", 
+    -- 合宙IOT平台的默认升级URL, 不填就是这个默认值
+    -- 如果是自建的OTA服务器, 则需要填写正确的URL, 例如 http://192.168.1.5:8000/update
+    -- 如果自建OTA服务器,且url包含全部参数,不需要额外添加参数, 请在url前面添加 ### 
+    -- 如果不加###,则默认会上传如下参数
+    -- 1. opts.version string 版本号, 默认是 BSP版本号.x.z格式
+    -- 2. opts.timeout int 请求超时时间, 默认300000毫秒,单位毫秒
+    -- 3. opts.project_key string 合宙IOT平台的项目key, 默认取全局变量PRODUCT_KEY. 自建服务器不用填
+    -- 4. opts.imei string 设备识别码, 默认取IMEI(Cat.1模块)或WLAN MAC地址(wifi模块)或MCU唯一ID
+    -- 5. opts.firmware_name string 底层版本号
+    -- 请求的版本号, 合宙IOT有一套版本号体系,不传就是合宙规则, 自建服务器的话当然是自行约定版本号了
+    version = ""
+    -- 其他更多参数, 请查阅libfota2的文档 https://wiki.luatos.com/api/libs/libfota2.html
+}]]--
+sys.taskInit(function()
+    -- 这个判断是提醒要设置PRODUCT_KEY的,实际生产请删除
+    if "123" == _G.PRODUCT_KEY and not ota_opts.url then
+        while 1 do
+            sys.wait(1000)
+            log.info("fota", "请修改正确的PRODUCT_KEY")
+        end
+    end
+    -- 等待网络就行后开始检查升级
+    sys.waitUntil("net_ready")
+    log.info("开始检查升级")
+    sys.wait(500)
+    libfota2.request(fota_cb, ota_opts)
+end)
+-- 演示定时自动升级, 每隔4小时自动检查一次
+sys.timerLoopStart(libfota2.request, 4 * 3600000, fota_cb, ota_opts)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 133 - 0
module/Air780EHM/demo/fs/main.lua

@@ -0,0 +1,133 @@
+-- main.lua文件
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "fs_demo"
+VERSION = "1.0.0"
+
+log.info("---------------------文件创建---------------")  
+local ret, errio = io.mkdir("/data/")
+if ret then       
+       log.info("文件夹创建成功")  
+   else    
+       log.error("文件夹创建失败")  
+end
+--[[
+-- 方法1:使用io.open创建空文件(如果文件已存在,则覆盖)  
+local fd = io.open("/newfile.txt", "w")  
+if fd then
+  -- 文件已成功创建(或覆盖),此时文件为空    
+  fd:close()   
+    log.info("文件创建成功(空文件)")  
+else  
+   log.error("文件创建失败")  
+end  
+]]
+-- 方法2:通过写入内容创建文件  
+
+log.info("---------------------文件创建---------------")  
+
+local content = "这是文件的内容"  
+local fd = io.open("/data/newfile_with_content.txt", "w")  
+if fd then    
+fd:write(content)   
+fd:close()    
+    log.info("文件创建成功并写入内容")  
+else    
+    log.error("文件创建失败")  
+end
+
+
+log.info("-------------------文件追加---------------")  
+-- 打开文件以追加模式  
+local fd = io.open("/data/newfile_with_content.txt", "rb")  
+if fd then    -- 写入内容   
+ local data_old = fd:read("*a")  
+ log.info("文件创建初始内容:",data_old)  
+ -- 关闭文件   
+ fd:close() 
+ local fd1 = io.open("/data/newfile_with_content.txt", "a")  
+ fd1:write("我是追加的内容\n")    
+ -- 关闭文件   
+ fd1:close() 
+ local fd2 = io.open("/data/newfile_with_content.txt", "rb") 
+ local data_new = fd2:read("*a")  
+ log.info("文件追加之后的内容:",data_new)  
+ -- 关闭文件   
+ fd2:close() 
+end
+
+
+log.info("----------------命名文件---------------")  
+-- 重命名文件  
+local success, err = os.rename("/data/newfile_with_content.txt", "/data/newname.txt")  
+if success then    
+log.info("文件重命名成功")  
+else   
+ log.error("文件重命名失败:" .. err)  
+end
+
+log.info("----------------文件拷贝---------------")  
+---文件拷贝
+-- 读取源文件内容  
+local fd_src = io.open("/data/newname.txt", "rb")  
+if fd_src then   
+    local content = fd_src:read("*a")  
+    fd_src:close()  
+    -- 写入目标文件  
+   local fd_dest = io.open("/data/destination.txt", "wb")   
+    if fd_dest then       
+       fd_dest:write(content)       
+       fd_dest:close()       
+       log.info("文件拷贝成功")   
+   else       
+       log.error("无法打开目标文件")  
+   end  
+else   
+  log.error("无法打开源文件")  
+end
+
+log.info("----------------移动文件---------------")  
+local ret, errio = io.mkdir("/destination/")
+if ret then       
+       log.info("文件夹创建成功")  
+   else    
+       log.error("文件夹创建失败")  
+end
+-- 移动文件:重命名(适用于同一文件系统)  
+local success, err = os.rename("/data/newname.txt", "/destination/source.txt")  
+if success then   
+   log.info("文件移动成功(重命名)")  
+else   
+ log.error("文件移动失败(重命名):" .. err)  
+end  
+
+-- 获取文件大小  
+local size = io.fileSize("/data/newname.txt")  
+if size then   
+ log.info("文件大小:" .. size .. " 字节")  
+else   
+ log.error("无法获取文件大小")  
+end
+
+
+-- 列出目录下的文件  
+
+local ret, data = io.lsdir("/data/",10,0)  
+if ret then
+   log.info("fs", "lsdir", json.encode(data))
+ else
+   log.info("fs", "lsdir", "fail", ret, data)
+ end
+
+-- 删除文件  
+local success, err = os.remove("/destination/source.txt")  
+if success then   
+   log.info("文件删除成功")  
+else   
+   log.error("文件删除失败:" .. err)  
+end
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 105 - 0
module/Air780EHM/demo/fskv/main.lua

@@ -0,0 +1,105 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "fskvdemo"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+
+sys.taskInit(function()
+    sys.wait(1000) -- 免得日志刷没了, 生产环境不需要
+
+    -- 检查一下当前固件是否支持fskv
+    if not fskv then
+        while true do
+            log.info("fskv", "this demo need fskv")
+            sys.wait(1000)
+        end
+    end
+
+    -- 初始化kv数据库
+    fskv.init()
+    log.info("fskv", "init complete")
+    -- 先放入一堆值
+    local bootime = fskv.get("boottime")
+    if bootime == nil or type(bootime) ~= "number" then
+        bootime = 0
+    else
+        bootime = bootime + 1
+    end
+    fskv.set("boottime", bootime)
+
+    fskv.set("my_bool", true)
+    fskv.set("my_int", 123)
+    fskv.set("my_number", 1.23)
+    fskv.set("my_str", "luatos")
+    fskv.set("my_table", {name="wendal",age=18})
+    
+    fskv.set("my_str_int", "123")
+    fskv.set("1", "123") -- 单字节key
+    --fskv.set("my_nil", nil) -- 会提示失败,不支持空值
+
+
+    log.info("fskv", "boottime",      type(fskv.get("boottime")),    fskv.get("boottime"))
+    log.info("fskv", "my_bool",      type(fskv.get("my_bool")),    fskv.get("my_bool"))
+    log.info("fskv", "my_int",       type(fskv.get("my_int")),     fskv.get("my_int"))
+    log.info("fskv", "my_number",    type(fskv.get("my_number")),  fskv.get("my_number"))
+    log.info("fskv", "my_str",       type(fskv.get("my_str")),     fskv.get("my_str"))
+    log.info("fskv", "my_table",     type(fskv.get("my_table")),   json.encode(fskv.get("my_table")))
+    log.info("fskv", "my_str_int",     type(fskv.get("my_str_int")),   fskv.get("my_str_int"))
+    log.info("fskv", "1 byte key",     type(fskv.get("1")),   json.encode(fskv.get("1")))
+
+    -- 删除测试
+    fskv.del("my_bool")
+    local t = fskv.get("my_bool")
+    log.info("fskv", "my_bool",      type(t),    t)
+
+    -- 查询kv数据库状态
+    -- local used, total,kv_count = fskv.stat()
+    -- log.info("fdb", "kv", used,total,kv_count)
+
+    -- fskv.clr()
+    -- local used, total,kv_count = fskv.stat()
+    -- log.info("fdb", "kv", used,total,kv_count)
+    
+
+    -- 压力测试
+    -- local start = mcu.ticks()
+    -- local count = 1000
+    -- for i=1,count do
+    --     -- sys.wait(10)
+    --     -- count = count - 1
+    --     -- fskv.set("BENT1", "--" .. os.date() .. "--")
+    --     -- fskv.set("BENT2", "--" .. os.date() .. "--")
+    --     -- fskv.set("BENT3", "--" .. os.date() .. "--")
+    --     -- fskv.set("BENT4", "--" .. os.date() .. "--")
+    --     fskv.get("my_bool")
+    -- end
+    -- log.info("fskv", mcu.ticks() - start)
+
+    if fskv.sett then
+        -- 设置数据, 字符串,数值,table,布尔值,均可
+        -- 但不可以是nil, function, userdata, task
+        log.info("fdb", fskv.sett("mytable", "wendal", "goodgoodstudy"))
+        log.info("fdb", fskv.sett("mytable", "upgrade", true))
+        log.info("fdb", fskv.sett("mytable", "timer", 1))
+        log.info("fdb", fskv.sett("mytable", "bigd", {name="wendal",age=123}))
+        
+        -- 下列语句将打印出4个元素的table
+        log.info("fdb", fskv.get("mytable"), json.encode(fskv.get("mytable")))
+        -- 注意: 如果key不存在, 或者原本的值不是table类型,将会完全覆盖
+        -- 例如下列写法,最终获取到的是table,而非第一行的字符串
+        log.info("fdb", fskv.set("mykv", "123"))
+        log.info("fdb", fskv.sett("mykv", "age", "123")) -- 保存的将是 {age:"123"}
+
+        -- 删除测试
+        log.info("fdb", fskv.set("mytable", {age=18, name="wendal"}))
+        log.info("fdb", fskv.sett("mytable", "name", nil))
+        log.info("fdb", fskv.get("mytable"), json.encode(fskv.get("mytable")))
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 92 - 0
module/Air780EHM/demo/ftp/main.lua

@@ -0,0 +1,92 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "ftpdemo"
+VERSION = "1.0.0"
+
+--[[
+本demo需要ftp库, 大部分能联网的设备都具有这个库
+ftp也是内置库, 无需require
+]]
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用ftp库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+sys.taskInit(function()
+    -----------------------------
+    -- 统一联网函数, 可自行删减
+    ----------------------------
+    if wlan and wlan.connect then
+        -- wifi 联网, ESP32系列均支持
+        local ssid = "luatos1234"
+        local password = "12341234"
+        log.info("wifi", ssid, password)
+        -- TODO 改成esptouch配网
+        -- LED = gpio.setup(12, 0, gpio.PULLUP)
+        wlan.init()
+        wlan.setMode(wlan.STATION)
+        wlan.connect(ssid, password, 1)
+        local result, data = sys.waitUntil("IP_READY", 30000)
+        log.info("wlan", "IP_READY", result, data)
+        device_id = wlan.getMac()
+        -- TODO 获取mac地址作为device_id
+    elseif mobile then
+        -- Air780E/Air600E系列
+        --mobile.simid(2)
+        -- LED = gpio.setup(27, 0, gpio.PULLUP)
+        device_id = mobile.imei()
+        sys.waitUntil("IP_READY", 30000)
+    end
+
+    -- -- 打印一下支持的加密套件, 通常来说, 固件已包含常见的99%的加密套件
+    -- if crypto.cipher_suites then
+    --     log.info("cipher", "suites", json.encode(crypto.cipher_suites()))
+    -- end
+    while true do
+        sys.wait(1000)
+        log.info("ftp 启动")
+        print(ftp.login(nil,"121.43.224.154",21,"ftp_user","3QujbiMG").wait())
+    
+        print(ftp.command("NOOP").wait())
+        print(ftp.command("SYST").wait())
+
+        print(ftp.command("TYPE I").wait())
+        print(ftp.command("PWD").wait())
+        print(ftp.command("MKD QWER").wait())
+        print(ftp.command("CWD /QWER").wait())
+
+        print(ftp.command("CDUP").wait())
+        print(ftp.command("RMD QWER").wait())
+
+        print(ftp.command("LIST").wait())
+
+        -- io.writeFile("/1222.txt", "23noianfdiasfhnpqw39fhawe;fuibnnpw3fheaios;fna;osfhisao;fadsfl")
+        -- print(ftp.push("/1222.txt","/12222.txt").wait())
+        
+        print(ftp.pull("/122224.txt","/122224.txt").wait())
+
+        local f = io.open("/122224.txt", "r")
+        if f then
+            local data = f:read("*a")
+            f:close()
+            log.info("fs", "writed data", data)
+        else
+            log.info("fs", "open file for read failed")
+        end
+
+        print(ftp.command("DELE /12222.txt").wait())
+        print(ftp.push("/122224.txt","/12222.txt").wait())
+        print(ftp.close().wait())
+        log.info("meminfo", rtos.meminfo("sys"))
+        sys.wait(15000)
+    end
+
+
+end)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 37 - 0
module/Air780EHM/demo/gpio/AGPIO/main.lua

@@ -0,0 +1,37 @@
+-- 本示例对比了普通GPIO和AGPIO的进入休眠模式前后的区别。
+-- Luatools需要PROJECT和VERSION这两个信息
+PROJECT = "AGPIO_GPIO_testdemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+if wdt then
+    -- 添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000) -- 初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
+end
+
+local gpio_number = 1   -- 普通GPIO GPIO号为1,休眠后掉电。
+local Agpio_number = 27 -- AGPIO GPIO号为27,休眠后可保持电平。
+
+gpio.setup(gpio_number, 1)
+gpio.setup(Agpio_number, 1)
+
+sys.taskInit(function()
+    sys.wait(16000)
+    -- 关闭USB电源
+    pm.power(pm.USB, false)
+    -- 进入低功耗模式
+    pm.power(pm.WORK_MODE, 3)
+    sys.wait(10000)
+    pm.power(pm.USB, true)
+    pm.power(pm.WORK_MODE, 0)
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 39 - 0
module/Air780EHM/demo/gpio/GPIO上拉下拉模式/main.lua

@@ -0,0 +1,39 @@
+-- Luatools需要PROJECT和VERSION这两个信息
+PROJECT = "gpio2demo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+--配置gpio7为中断上拉模式
+--配置gpio27为中断下拉模式
+--请根据实际需求更改gpio编号和上下拉
+local gpio_pin1 = 7
+local gpio_pin2 = 27
+-- 按键防抖函数
+gpio.debounce(gpio_pin1, 50)
+gpio.debounce(gpio_pin2, 50)
+
+-- 设置GPIO7引脚为上拉输入模式
+gpio.setup(gpio_pin1, nil, gpio.PULLUP)
+
+-- 设置GPIO27引脚为下拉输入模式
+gpio.setup(gpio_pin2, nil, gpio.PULLDOWN)
+
+
+sys.timerLoopStart(function ()
+    log.info("GPIO",gpio_pin1,"电平",gpio.get(gpio_pin1))
+    log.info("GPIO",gpio_pin2,"电平",gpio.get(gpio_pin2))
+end,1000)
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 27 - 0
module/Air780EHM/demo/gpio/GPIO中断(触发)模式/main.lua

@@ -0,0 +1,27 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "gpio_irq"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+if wdt then
+    -- 添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000) -- 初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
+end
+
+-- 配置gpio24为中断模式,上升沿(gpio.RISING)和下降沿(gpio.FALLING)均触发(gpio.BOTH)
+-- 请根据实际需求更改gpio编号和触发模式
+local gpio_pin = 24
+gpio.debounce(gpio_pin, 100)
+gpio.setup(gpio_pin, function()
+    log.info("gpio", gpio_pin, "被触发")
+end, gpio.PULLUP, gpio.BOTH)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 34 - 0
module/Air780EHM/demo/gpio/GPIO中断(计数)模式/main.lua

@@ -0,0 +1,34 @@
+-- Luatools需要PROJECT和VERSION这两个信息
+PROJECT = "gpio_irq"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+--配置gpio24为中断计数模式
+--请根据实际需求更改gpio编号和上下拉
+local gpio_pin = 24
+-- gpio.setup(gpio_pin, gpio.count, gpio.PULLUP, gpio.FALLING)
+gpio.setup(gpio_pin, gpio.count)
+
+--配置PWM4输出 1kHZ 占空比50%的方波作为信号源
+pwm.open(4,1000,50)
+
+--每隔1S统计一次中断触发的次数
+sys.taskInit(function()
+    while true do
+        sys.wait(1000)
+        log.info("irq cnt", gpio.count(gpio_pin))
+    end
+end)
+
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 34 - 0
module/Air780EHM/demo/gpio/GPIO翻转测速/main.lua

@@ -0,0 +1,34 @@
+-- Luatools需要PROJECT和VERSION这两个信息
+PROJECT = "gpio2demo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+if wdt then
+    -- 添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000) -- 初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
+end
+
+local test_gpio_number = 27
+
+gpio.setup(test_gpio_number, 0, gpio.PULLUP)
+
+sys.taskInit(function()
+    sys.wait(100)
+    while true do
+        sys.wait(100)
+        -- 通过GPIO27脚输出输出8组电平变化
+        -- 0xA9就是输出的电平高低状态,即 10101001
+        gpio.pulse(test_gpio_number, 0xA9, 8, 0)
+        log.info("gpio----------->pulse2")
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 41 - 0
module/Air780EHM/demo/gpio/GPIO输入模式/main.lua

@@ -0,0 +1,41 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "gpio_irq"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+if wdt then
+    -- 添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000) -- 初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
+end
+
+-- 配置gpio24为输入模式
+-- 配置GPIO27(即开发板上LED灯)为输出模式
+
+-- 请根据实际需求更改gpio编号和上下拉
+
+local inputpin = 24
+local ledpin = 27
+
+local input = gpio.setup(inputpin,nil)
+local led = gpio.setup(ledpin, 1)
+
+gpio.debounce(inputpin, 50)
+--GPIO24检测到有高低电平输入后,会返回GPIO24当前获取到的电平为高还是低,高返回值为1,低返回值为0
+--将这个返回值,传给GPIO27(LED),为0 则GPIO27输出低电平(LED灯灭),为1则输出高电平(LED灯亮)
+sys.taskInit(function ()
+    while true do
+        led(input())
+        sys.wait(500)
+    end
+end)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 37 - 0
module/Air780EHM/demo/gpio/GPIO输出模式/main.lua

@@ -0,0 +1,37 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "gpiodemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+if wdt then
+    -- 添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000) -- 初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
+end
+
+local gpio_number = 27 -- Air780EPM开发板上的网络指示灯(蓝灯)与GPIO27相连
+
+LED = gpio.setup(gpio_number, 1) -- 设置GPIO27为输出模式
+
+sys.taskInit(function()
+    -- 开始呼吸灯
+    local count = 0
+    while 1 do
+        -- 呼吸灯程序
+        LED(1)
+        log.info("GPIO", "Go Go Go", count, rtos.bsp())
+        sys.wait(500)--点亮时间 500ms
+        LED(0)
+        sys.wait(500)--熄灭时间 500ms
+        count = count + 1
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 21 - 0
module/Air780EHM/demo/helloworld/main.lua

@@ -0,0 +1,21 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "helloworld"
+VERSION = "1.0.0"
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+-- log.info("main", "hello world")
+
+-- print(_VERSION)
+
+sys.timerLoopStart(function()
+    print("hello world")
+end, 3000)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 30 - 0
module/Air780EHM/demo/hmeta/main.lua

@@ -0,0 +1,30 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "hmetademo"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+
+sys.taskInit(function()
+    while hmeta do
+        -- hmeta识别底层模组类型的
+        -- 不同的模组可以使用相同的bsp,但根据封装的不同,根据内部数据仍可识别出具体模块
+        log.info("hmeta", hmeta.model(), hmeta.hwver and hmeta.hwver())
+        log.info("bsp",   rtos.bsp())
+        sys.wait(3000)
+    end
+    log.info("这个bsp不支持hmeta库哦")
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 3 - 0
module/Air780EHM/demo/http/luatos_uploadFile.txt

@@ -0,0 +1,3 @@
+测试文本   adcc1234x衣二三
+	84as1c188
+-*accxxx6-4

+ 306 - 0
module/Air780EHM/demo/http/main.lua

@@ -0,0 +1,306 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "httpdemo"
+VERSION = "1.0.0"
+
+--[[
+本demo需要http库, 大部分能联网的设备都具有这个库
+http也是内置库, 无需require
+
+1. 如需上传大文件,请使用 httpplus 库, 对应demo/httpplus
+2. 
+]]
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用http库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+
+sys.taskInit(function()
+    -----------------------------
+    -- 统一联网函数, 可自行删减
+    ----------------------------
+    if wlan and wlan.connect then
+        -- wifi 联网, ESP32系列均支持
+        local ssid = "luatos1234"
+        local password = "12341234"
+        log.info("wifi", ssid, password)
+        -- TODO 改成esptouch配网
+        -- LED = gpio.setup(12, 0, gpio.PULLUP)
+        wlan.init()
+        wlan.setMode(wlan.STATION)
+        wlan.connect(ssid, password, 1)
+        local result, data = sys.waitUntil("IP_READY", 30000)
+        log.info("wlan", "IP_READY", result, data)
+        device_id = wlan.getMac()
+    elseif mobile then
+        -- Air780E/Air600E系列
+        --mobile.simid(2)
+        -- LED = gpio.setup(27, 0, gpio.PULLUP)
+        device_id = mobile.imei()
+        log.info("ipv6", mobile.ipv6(true))
+        sys.waitUntil("IP_READY", 30000)
+    elseif http then
+        sys.waitUntil("IP_READY")
+    else
+        while 1 do
+            sys.wait(1000)
+            log.info("http", "当前固件未包含http库")
+        end
+    end
+    log.info("已联网")
+    sys.publish("net_ready")
+end)
+
+function demo_http_get()
+    -- 最普通的Http GET请求
+    local code, headers, body = http.request("GET", "https://www.air32.cn/").wait()
+    log.info("http.get", code, headers, body)
+    local code, headers, body = http.request("GET", "https://mirrors6.tuna.tsinghua.edu.cn/", nil, nil, {ipv6=true}).wait()
+    log.info("http.get", code, headers, body)
+    sys.wait(100)
+    local code, headers, body = http.request("GET", "https://www.luatos.com/").wait()
+    log.info("http.get", code, headers, body)
+
+    -- 按需打印
+    -- code 响应值, 若大于等于 100 为服务器响应, 小于的均为错误代码
+    -- headers是个table, 一般作为调试数据存在
+    -- body是字符串. 注意lua的字符串是带长度的byte[]/char*, 是可以包含不可见字符的
+    -- log.info("http", code, json.encode(headers or {}), #body > 512 and #body or body)
+end
+
+function demo_http_post_json()
+    -- POST request 演示
+    local req_headers = {}
+    req_headers["Content-Type"] = "application/json"
+    local body = json.encode({name="LuatOS"})
+    local code, headers, body = http.request("POST","http://site0.cn/api/httptest/simple/date", 
+            req_headers,
+            body -- POST请求所需要的body, string, zbuff, file均可
+    ).wait()
+    log.info("http.post", code, headers, body)
+end
+
+function demo_http_post_form()
+    -- POST request 演示
+    local req_headers = {}
+    req_headers["Content-Type"] = "application/x-www-form-urlencoded"
+    local params = {
+        ABC = "123",
+        DEF = 345
+    }
+    local body = ""
+    for k, v in pairs(params) do
+        body = body .. tostring(k) .. "=" .. tostring(v):urlEncode() .. "&"
+    end
+    local code, headers, body = http.request("POST","http://echohttp.wendal.cn/post", 
+            req_headers,
+            body -- POST请求所需要的body, string, zbuff, file均可
+    ).wait()
+    log.info("http.post.form", code, headers, body)
+end
+
+-- local function http_download_callback(content_len,body_len,userdata)
+--     print("http_download_callback",content_len,body_len,userdata)
+-- end
+
+-- local http_userdata = "123456789"
+
+function demo_http_download()
+
+    -- POST and download, task内的同步操作
+    local opts = {}                 -- 额外的配置项
+    opts["dst"] = "/data.bin"       -- 下载路径,可选
+    opts["timeout"] = 30000         -- 超时时长,单位ms,可选
+    -- opts["adapter"] = socket.ETH0  -- 使用哪个网卡,可选
+    -- opts["callback"] = http_download_callback
+    -- opts["userdata"] = http_userdata
+
+    for k, v in pairs(opts) do
+        print("opts",k,v)
+    end
+    
+    local code, headers, body = http.request("POST","http://site0.cn/api/httptest/simple/date",
+            {}, -- 请求所添加的 headers, 可以是nil
+            "", 
+            opts
+    ).wait()
+    log.info("http.post", code, headers, body) -- 只返回code和headers
+
+    -- local f = io.open("/data.bin", "rb")
+    -- if f then
+    --     local data = f:read("*a")
+    --     log.info("fs", "data", data, data:toHex())
+    -- end
+    
+    -- GET request, 开个task让它自行执行去吧, 不管执行结果了
+    sys.taskInit(http.request("GET","http://site0.cn/api/httptest/simple/time").wait)
+end
+
+function demo_http_post_file()
+        -- -- POST multipart/form-data模式 上传文件---手动拼接
+        local boundary = "----WebKitFormBoundary"..os.time()
+        local req_headers = {
+            ["Content-Type"] = "multipart/form-data; boundary="..boundary,
+        }
+        local body = "--"..boundary.."\r\n"..
+                     "Content-Disposition: form-data; name=\"uploadFile\"; filename=\"luatos_uploadFile_TEST01.txt\""..
+                     "\r\nContent-Type: text/plain\r\n\r\n"..
+                     "1111http_测试一二三四654zacc\r\n"..
+                     "--"..boundary
+
+        log.info("headers: ", "\r\n"..json.encode(req_headers))
+        log.info("body: ", "\r\n"..body)
+        local code, headers, body = http.request("POST","http://airtest.openluat.com:2900/uploadFileToStatic",
+                req_headers,
+                body -- POST请求所需要的body, string, zbuff, file均可
+        ).wait()
+        log.info("http.post", code, headers, body)
+
+        -- 也可用postMultipartFormData(url, params) 上传文件
+        postMultipartFormData(
+            "http://airtest.openluat.com:2900/uploadFileToStatic",
+            {
+                -- texts = 
+                -- {
+                --     ["imei"] = "862991234567890",
+                --     ["time"] = "20180802180345"
+                -- },
+                
+                files =
+                {
+                    ["uploadFile"] = "/luadb/luatos_uploadFile.txt",
+                }
+            }
+        )
+end
+
+
+local function demo_http_get_gzip()
+    -- 这里用 和风天气 的API做演示
+    -- 这个API的响应, 总会gzip压缩过, 需要配合miniz库进行解压
+    local code, headers, body = http.request("GET", "https://devapi.qweather.com/v7/weather/now?location=101010100&key=0e8c72015e2b4a1dbff1688ad54053de").wait()
+    log.info("http.gzip", code)
+    if code == 200 then
+        local re = miniz.uncompress(body:sub(11), 0)
+        log.info("和风天气", re)
+        if re then
+            local jdata = json.decode(re)
+            log.info("jdata", jdata)
+            if jdata then
+                log.info("和风天气", jdata.code)
+                if jdata.now then
+                    log.info("和风天气", "天气", jdata.now.text)
+                    log.info("和风天气", "温度", jdata.now.temp)
+                end
+            end
+        end
+    end
+end
+
+sys.taskInit(function()
+    sys.wait(100)
+    -- 打印一下支持的加密套件, 通常来说, 固件已包含常见的99%的加密套件
+    -- if crypto.cipher_suites then
+    --     log.info("cipher", "suites", json.encode(crypto.cipher_suites()))
+    -- end
+
+    -------------------------------------
+    -------- HTTP 演示代码 --------------
+    -------------------------------------
+    sys.waitUntil("net_ready") -- 等联网
+
+    while 1 do
+        -- 演示GET请求
+        demo_http_get()
+        -- 表单提交
+        -- demo_http_post_form()
+        -- POST一个json字符串
+        -- demo_http_post_json()
+        -- 上传文件, mulitform形式
+        -- demo_http_post_file()
+        -- 文件下载
+        -- demo_http_download()
+        -- gzip压缩的响应, 以和风天气为例
+        -- demo_http_get_gzip()
+
+        sys.wait(1000)
+        -- 打印一下内存状态
+        log.info("sys", rtos.meminfo("sys"))
+        log.info("lua", rtos.meminfo("lua"))
+        sys.wait(600000)
+    end
+end)
+
+---- MultipartForm上传文件
+-- url string 请求URL地址
+-- req_headers table 请求头
+-- params table 需要传输的数据参数
+function postMultipartFormData(url, params)
+    local boundary = "----WebKitFormBoundary"..os.time()
+    local req_headers = {
+        ["Content-Type"] = "multipart/form-data; boundary="..boundary,
+    }
+    local body = {}
+
+    -- 解析拼接 body
+    for k,v in pairs(params) do
+        if k=="texts" then
+            local bodyText = ""
+            for kk,vv in pairs(v) do
+                print(kk,vv)
+                bodyText = bodyText.."--"..boundary.."\r\nContent-Disposition: form-data; name=\""..kk.."\"\r\n\r\n"..vv.."\r\n"
+            end
+            table.insert(body, bodyText)
+        elseif k=="files" then
+            local contentType =
+            {
+                txt = "text/plain",             -- 文本
+                jpg = "image/jpeg",             -- JPG 格式图片
+                jpeg = "image/jpeg",            -- JPEG 格式图片
+                png = "image/png",              -- PNG 格式图片   
+                gif = "image/gif",              -- GIF 格式图片
+                html = "image/html",            -- HTML
+                json = "application/json"       -- JSON
+            }
+            
+            for kk,vv in pairs(v) do
+                if type(vv) == "table" then
+                    for i=1, #vv do
+                        print(kk,vv[i])
+                        table.insert(body, "--"..boundary.."\r\nContent-Disposition: form-data; name=\""..kk.."\"; filename=\""..vv[i]:match("[^%/]+%w$").."\"\r\nContent-Type: "..contentType[vv[i]:match("%.(%w+)$")].."\r\n\r\n")
+                        table.insert(body, io.readFile(vv[i]))
+                        table.insert(body, "\r\n")
+                    end
+                else
+                    print(kk,vv)
+                    table.insert(body, "--"..boundary.."\r\nContent-Disposition: form-data; name=\""..kk.."\"; filename=\""..vv:match("[^%/]+%w$").."\"\r\nContent-Type: "..contentType[vv:match("%.(%w+)$")].."\r\n\r\n")
+                    table.insert(body, io.readFile(vv))
+                    table.insert(body, "\r\n")
+                end
+            end
+        end
+    end 
+    table.insert(body, "--"..boundary.."--\r\n")
+    body = table.concat(body)
+    log.info("headers: ", "\r\n" .. json.encode(req_headers), type(body))
+    log.info("body: " .. body:len() .. "\r\n" .. body)
+    local code, headers, body = http.request("POST",url,
+            req_headers,
+            body
+    ).wait()   
+    log.info("http.post", code, headers, body)
+end
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 41 - 0
module/Air780EHM/demo/i2c-aht10/main.lua

@@ -0,0 +1,41 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "aht10demo"
+VERSION = "1.0.0"
+
+-- sys库是标配
+sys = require("sys")
+
+local aht10 = require "aht10"
+
+-- 接线
+--[[
+AHT10 --- 模块
+SDA   -   I2C_SDA
+SCL   -   I2C_SCL
+VCC   -   VDDIO
+GND   -   GND
+]]
+
+--电平设置为3.3v
+pm.ioVol(pm.IOVOL_ALL_GPIO, 3300)
+--设置gpio2输出,给camera_sda、camera_scl引脚提供上拉
+gpio.setup(2, 1)
+
+i2cid = 1
+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)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 61 - 0
module/Air780EHM/demo/i2c-sht20/main.lua

@@ -0,0 +1,61 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "sht20demo"
+VERSION = "1.0.0"
+
+-- sys库是标配
+sys = require("sys")
+
+-- 接线
+--[[
+SHT20 --- 模块
+SDA   -   I2C_SDA
+SCL   -   I2C_SCL
+VCC   -   VDDIO
+GND   -   GND
+]]
+
+--电平设置为3.3v
+pm.ioVol(pm.IOVOL_ALL_GPIO, 3300)
+--设置gpio2输出,给camera_sda、camera_scl引脚提供上拉
+gpio.setup(2, 1)
+
+-- 启动个task, 定时查询SHT20的数据
+sys.taskInit(function()
+
+    local tmp,hum -- 原始数据
+    local temp,hump -- 真实值
+
+    --1010 000x
+    local addr = 0x40
+    -- 按实际修改哦
+    local id = 1
+
+    log.info("i2c", "initial",i2c.setup(id))
+
+    while true do
+        --第一种方式
+        i2c.send(id, addr, string.char(0xF3))
+        sys.wait(100)
+        tmp = i2c.recv(id, addr, 2)
+        log.info("SHT20", "read tem data", tmp:toHex())
+
+        i2c.send(id, addr, string.char(0xF5))
+        sys.wait(100)
+        hum = i2c.recv(id, addr, 2)
+        log.info("SHT20", "read hum data", hum:toHex())
+        local _,tval = pack.unpack(tmp,'>H')
+        local _,hval = pack.unpack(hum,'>H')
+        if tval and hval then
+            temp = (((17572 * tval) >> 16) - 4685)/100
+            hump = (((12500 * hval) >> 16) - 600)/100
+            log.info("SHT20", "temp,humi",temp,hump)
+        end
+        sys.wait(1000)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 218 - 0
module/Air780EHM/demo/iconv/main.lua

@@ -0,0 +1,218 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "my_test"
+VERSION = "1.2"
+PRODUCT_KEY = "s1uUnY6KA06ifIjcutm5oNbG3MZf5aUv" -- 换成自己的
+-- sys库是标配
+_G.sys = require("sys")
+_G.sysplus = require("sysplus")
+
+--- unicode小端编码 转化为 gb2312编码
+-- @string ucs2s unicode小端编码数据
+-- @return string data,gb2312编码数据
+-- @usage local data = common.ucs2ToGb2312(ucs2s)
+function ucs2ToGb2312(ucs2s)
+    local cd = iconv.open("gb2312", "ucs2")
+    return cd:iconv(ucs2s)
+end
+
+--- gb2312编码 转化为 unicode小端编码
+-- @string gb2312s gb2312编码数据
+-- @return string data,unicode小端编码数据
+-- @usage local data = common.gb2312ToUcs2(gb2312s)
+function gb2312ToUcs2(gb2312s)
+    local cd = iconv.open("ucs2", "gb2312")
+    return cd:iconv(gb2312s)
+end
+
+--- unicode大端编码 转化为 gb2312编码
+-- @string ucs2s unicode大端编码数据
+-- @return string data,gb2312编码数据
+-- @usage data = common.ucs2beToGb2312(ucs2s)
+function ucs2beToGb2312(ucs2s)
+    local cd = iconv.open("gb2312", "ucs2be")
+    return cd:iconv(ucs2s)
+end
+
+--- gb2312编码 转化为 unicode大端编码
+-- @string gb2312s gb2312编码数据
+-- @return string data,unicode大端编码数据
+-- @usage local data = common.gb2312ToUcs2be(gb2312s)
+function gb2312ToUcs2be(gb2312s)
+    local cd = iconv.open("ucs2be", "gb2312")
+    return cd:iconv(gb2312s)
+end
+
+--- unicode小端编码 转化为 utf8编码
+-- @string ucs2s unicode小端编码数据
+-- @return string data,utf8编码数据
+-- @usage data = common.ucs2ToUtf8(ucs2s)
+function ucs2ToUtf8(ucs2s)
+    local cd = iconv.open("utf8", "ucs2")
+    return cd:iconv(ucs2s)
+end
+
+--- utf8编码 转化为 unicode小端编码
+-- @string utf8s utf8编码数据
+-- @return string data,unicode小端编码数据
+-- @usage local data = common.utf8ToUcs2(utf8s)
+function utf8ToUcs2(utf8s)
+    local cd = iconv.open("ucs2", "utf8")
+    return cd:iconv(utf8s)
+end
+
+--- unicode大端编码 转化为 utf8编码
+-- @string ucs2s unicode大端编码数据
+-- @return string data,utf8编码数据
+-- @usage data = common.ucs2beToUtf8(ucs2s)
+function ucs2beToUtf8(ucs2s)
+    local cd = iconv.open("utf8", "ucs2be")
+    return cd:iconv(ucs2s)
+end
+
+--- utf8编码 转化为 unicode大端编码
+-- @string utf8s utf8编码数据
+-- @return string data,unicode大端编码数据
+-- @usage local data = common.utf8ToUcs2be(utf8s)
+function utf8ToUcs2be(utf8s)
+    local cd = iconv.open("ucs2be", "utf8")
+    return cd:iconv(utf8s)
+end
+
+--- utf8编码 转化为 gb2312编码
+-- @string utf8s utf8编码数据
+-- @return string data,gb2312编码数据
+-- @usage local data = common.utf8ToGb2312(utf8s)
+function utf8ToGb2312(utf8s)
+    local cd = iconv.open("ucs2", "utf8")
+    local ucs2s = cd:iconv(utf8s)
+    cd = iconv.open("gb2312", "ucs2")
+    return cd:iconv(ucs2s)
+end
+
+--- gb2312编码 转化为 utf8编码
+-- @string gb2312s gb2312编码数据
+-- @return string data,utf8编码数据
+-- @usage local data = common.gb2312ToUtf8(gb2312s)
+function gb2312ToUtf8(gb2312s)
+    local cd = iconv.open("ucs2", "gb2312")
+    local ucs2s = cd:iconv(gb2312s)
+    cd = iconv.open("utf8", "ucs2")
+    return cd:iconv(ucs2s)
+end
+
+--------------------------------------------------------------------------------------------------------
+--[[
+函数名:ucs2ToGb2312
+功能  :unicode小端编码 转化为 gb2312编码,并打印出gb2312编码数据
+参数  :
+        ucs2s:unicode小端编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testucs2ToGb2312(ucs2s)
+    print("ucs2ToGb2312")
+    local gb2312num = ucs2ToGb2312(ucs2s)--调用的是common.ucs2ToGb2312,返回的是编码所对应的字符串
+    --print("gb2312  code:",gb2312num)
+    print("gb2312  code:",string.toHex(gb2312num))
+end
+
+--[[
+函数名:gb2312ToUcs2
+功能  :gb2312编码 转化为 unicode十六进制小端编码数据并打印
+参数  :
+        gb2312s:gb2312编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testgb2312ToUcs2(gb2312s)
+    print("gb2312ToUcs2")
+    local ucs2num = gb2312ToUcs2(gb2312s)
+    print("unicode little-endian code:" .. string.toHex(ucs2num)) -- 要将二进制转换为十六进制,否则无法输出
+end
+
+--[[
+函数名:ucs2beToGb2312
+功能  :unicode大端编码 转化为 gb2312编码,并打印出gb2312编码数据,
+大端编码数据是与小端编码数据位置调换
+参数  :
+        ucs2s:unicode大端编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testucs2beToGb2312(ucs2s)
+    print("ucs2beToGb2312")
+    local gb2312num = ucs2beToGb2312(ucs2s) -- 转化后的数据直接变成字符可以直接输出
+    print("gb2312 code :" .. string.toHex(gb2312num))
+end
+
+--[[
+函数名:gb2312ToUcs2be
+功能  :gb2312编码 转化为 unicode大端编码,并打印出unicode大端编码
+参数  :
+        gb2312s:gb2312编码数据,注意输入参数的字节数
+返回值:unicode大端编码数据
+]]
+function testgb2312ToUcs2be(gb2312s)
+    print("gb2312ToUcs2be")
+    local ucs2benum = gb2312ToUcs2be(gb2312s)
+    print("unicode big-endian code :" .. string.toHex(ucs2benum))
+end
+
+--[[
+函数名:ucs2ToUtf8
+功能  :unicode小端编码 转化为 utf8编码,并打印出utf8十六进制编码数据
+参数  :
+        ucs2s:unicode小端编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testucs2ToUtf8(ucs2s)
+    print("ucs2ToUtf8")
+    local utf8num = ucs2ToUtf8(ucs2s)
+    print("utf8  code:" .. string.toHex(utf8num))
+
+end
+
+--[[
+函数名:utf8ToGb2312
+功能  :utf8编码 转化为 gb2312编码,并打印出gb2312编码数据
+参数  :
+        utf8s:utf8编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testutf8ToGb2312(utf8s)
+    print("utf8ToGb2312")
+    local gb2312num = utf8ToGb2312(utf8s)
+    print("gb2312 code:" .. string.toHex(gb2312num))
+
+end
+
+--[[
+函数名:gb2312ToUtf8
+功能  :gb2312编码 转化为 utf8编码,并打印出utf8编码数据
+参数  :
+        gb2312s:gb2312s编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testgb2312ToUtf8(gb2312s)
+    print("gb2312ToUtf8")
+    local utf8s = gb2312ToUtf8(gb2312s)
+    print("utf8s code:" .. utf8s)
+
+end
+
+
+
+sys.taskInit(function()
+    while 1 do
+        sys.wait(1000)
+        testucs2ToGb2312(string.fromHex("1162")) -- "1162"是"我"字的ucs2编码,这里调用了string.fromHex将参数转化为二进制,也就是两个字节。
+        testgb2312ToUcs2(string.fromHex("CED2")) -- "CED2"是"我"字的gb2312编码
+        testucs2beToGb2312(string.fromHex("6211")) -- "6211"是"我"字的ucs2be编码
+        testgb2312ToUcs2be(string.fromHex("CED2"))
+        testucs2ToUtf8(string.fromHex("1162"))
+        testutf8ToGb2312(string.fromHex("E68891")) -- "E68891"是"我"字的utf8编码
+        testgb2312ToUtf8(string.fromHex("CED2"))
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 83 - 0
module/Air780EHM/demo/iotcloud/aliyun/示例1 一型一密(免预注册,但需要是企业版实例平台)方式连接云平台/main.lua

@@ -0,0 +1,83 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "aliyun_yxym_myzc"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+local iotcloud = require("iotcloud")
+
+
+-- 统一联网函数
+sys.taskInit(function()
+    local device_id = mcu.unique_id():toHex()
+    if mobile then
+        device_id = mobile.imei()
+    else
+        -- 其他不认识的bsp, 循环提示一下吧
+        while 1 do
+            sys.wait(1000)
+            log.info("bsp", "本bsp可能未适配网络层, 请查证")
+        end
+    end
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready", device_id)
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local ret, device_id = sys.waitUntil("net_ready")
+
+    --------    以下接入方式根据自己需要修改,相关参数修改为自己的    ---------
+
+    -- 阿里云  
+    -- 动态注册(免预注册)(一型一密)(仅企业版支持)
+    iotcloudc = iotcloud.new(iotcloud.ALIYUN,{instance_id = "xxx",product_id = "xxx",product_secret = "xxx"}) -- 企业版公共实例
+    -- 动态注册(预注册)(一型一密)
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{product_id = "xxx",device_name = "xxx",product_secret = "xxx"})                     -- 旧版公共实例
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{instance_id = "xxx",product_id = "xxx",device_name = "xxx",product_secret = "xxx"}) -- 新版公共实例
+    -- 密钥校验 (预注册)(一机一密)
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{product_id = "xxx",device_name = "xxx",device_secret = "xxx"})                    -- 旧版公共实例
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{instance_id = "xxx",product_id = "xxx",device_name = "xxx",device_secret = "xxx"})-- 新版公共实例
+
+    if iotcloudc then
+        iotcloudc:connect()
+    end
+
+end)
+
+sys.subscribe("iotcloud", function(cloudc,event,data,payload)
+    -- 注意,此处不是协程内,复杂操作发消息给协程内进行处理
+    if event == iotcloud.CONNECT then -- 云平台联上了
+        print("iotcloud","CONNECT", "云平台连接成功")
+        iotcloudc:subscribe("/"..iotcloudc.product_id.."/"..iotcloudc.device_name.."/user/get") -- 订阅主题,用于下发消息
+    elseif event == iotcloud.RECEIVE then
+        print("iotcloud","topic", data, "payload", payload)
+        -- 用户处理代码
+    elseif event ==  iotcloud.OTA then
+        if data then
+            rtos.reboot()
+        end
+    elseif event == iotcloud.DISCONNECT then -- 云平台断开了
+        -- 用户处理代码
+    end
+end)
+
+-- 每隔2秒发布一次qos为1的消息到云平台
+sys.taskInit(function()
+    while 1 do
+        sys.wait(2000)
+        if iotcloudc then
+            iotcloudc:publish("/"..iotcloudc.product_id.."/"..iotcloudc.device_name.."/user/update", "hello world", 1) -- 上传数据
+        end
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 82 - 0
module/Air780EHM/demo/iotcloud/aliyun/示例2 一型一密(预注册)方式连接云平台/main.lua

@@ -0,0 +1,82 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "aliyun_yxym_yzc"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+local iotcloud = require("iotcloud")
+
+-- 统一联网函数
+sys.taskInit(function()
+    local device_id = mcu.unique_id():toHex()
+    if mobile then
+        device_id = mobile.imei()
+    else
+        -- 其他不认识的bsp, 循环提示一下吧
+        while 1 do
+            sys.wait(1000)
+            log.info("bsp", "本bsp可能未适配网络层, 请查证")
+        end
+    end
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready", device_id)
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local ret, device_id = sys.waitUntil("net_ready")
+
+    --------    以下接入方式根据自己需要修改,相关参数修改为自己的    ---------
+
+    -- 阿里云  
+    -- 动态注册(免预注册)(一型一密)(仅企业版支持)
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{instance_id = "xxx",product_id = "xxx",product_secret = "xxx"}) -- 企业版公共实例
+    -- 动态注册(预注册)(一型一密)
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{product_id = "xxx",device_name = "xxx",product_secret = "xxx"})                     -- 旧版公共实例
+    iotcloudc = iotcloud.new(iotcloud.ALIYUN,{instance_id = "xxx",product_id = "xxx",device_name = "xxx",product_secret = "xxx"}) -- 新版公共实例
+    -- 密钥校验 (预注册)(一机一密)
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{product_id = "xxx",device_name = "xxx",device_secret = "xxx"})                    -- 旧版公共实例
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{instance_id = "xxx",product_id = "xxx",device_name = "xxx",device_secret = "xxx"})-- 新版公共实例
+
+    if iotcloudc then
+        iotcloudc:connect()
+    end
+
+end)
+
+sys.subscribe("iotcloud", function(cloudc,event,data,payload)
+    -- 注意,此处不是协程内,复杂操作发消息给协程内进行处理
+    if event == iotcloud.CONNECT then -- 云平台联上了
+        print("iotcloud","CONNECT", "云平台连接成功")
+        iotcloudc:subscribe("/"..iotcloudc.product_id.."/"..iotcloudc.device_name.."/user/get") -- 订阅主题,用于下发消息
+    elseif event == iotcloud.RECEIVE then
+        print("iotcloud","topic", data, "payload", payload)
+        -- 用户处理代码
+    elseif event ==  iotcloud.OTA then
+        if data then
+            rtos.reboot()
+        end
+    elseif event == iotcloud.DISCONNECT then -- 云平台断开了
+        -- 用户处理代码
+    end
+end)
+
+-- 每隔2秒发布一次qos为1的消息到云平台
+sys.taskInit(function()
+    while 1 do
+        sys.wait(2000)
+        if iotcloudc then
+            iotcloudc:publish("/"..iotcloudc.product_id.."/"..iotcloudc.device_name.."/user/update", "hello world", 1) -- 上传数据
+        end
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 82 - 0
module/Air780EHM/demo/iotcloud/aliyun/示例3 一机一密方式连接云平台/main.lua

@@ -0,0 +1,82 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "aliyun_yjym_yzc"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+local iotcloud = require("iotcloud")
+
+-- 统一联网函数
+sys.taskInit(function()
+    local device_id = mcu.unique_id():toHex()
+    if mobile then
+        device_id = mobile.imei()
+    else
+        -- 其他不认识的bsp, 循环提示一下吧
+        while 1 do
+            sys.wait(1000)
+            log.info("bsp", "本bsp可能未适配网络层, 请查证")
+        end
+    end
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready", device_id)
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local ret, device_id = sys.waitUntil("net_ready")
+
+    --------    以下接入方式根据自己需要修改,相关参数修改为自己的    ---------
+
+    -- 阿里云  
+    -- 动态注册(免预注册)(一型一密)(仅企业版支持)
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{instance_id = "xxx",product_id = "xxx",product_secret = "xxx"}) -- 企业版公共实例
+    -- 动态注册(预注册)(一型一密)
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{product_id = "xxx",device_name = "xxx",product_secret = "xxx"})                     -- 旧版公共实例
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{instance_id = "xxx",product_id = "xxx",device_name = "xxx",product_secret = "xxx"}) -- 新版公共实例
+    -- 密钥校验 (预注册)(一机一密)
+    -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{product_id = "xxx",device_name = "xxx",device_secret = "xxx"})                    -- 旧版公共实例
+    iotcloudc = iotcloud.new(iotcloud.ALIYUN,{instance_id = "xxx",product_id = "xxx",device_name = "xxx",device_secret = "xxx"})-- 新版公共实例
+
+    if iotcloudc then
+        iotcloudc:connect()
+    end
+
+end)
+
+sys.subscribe("iotcloud", function(cloudc,event,data,payload)
+    -- 注意,此处不是协程内,复杂操作发消息给协程内进行处理
+    if event == iotcloud.CONNECT then -- 云平台联上了
+        print("iotcloud","CONNECT", "云平台连接成功")
+        iotcloudc:subscribe("/"..iotcloudc.product_id.."/"..iotcloudc.device_name.."/user/get") -- 订阅主题,用于下发消息
+    elseif event == iotcloud.RECEIVE then
+        print("iotcloud","topic", data, "payload", payload)
+        -- 用户处理代码
+    elseif event ==  iotcloud.OTA then
+        if data then
+            rtos.reboot()
+        end
+    elseif event == iotcloud.DISCONNECT then -- 云平台断开了
+        -- 用户处理代码
+    end
+end)
+
+-- 每隔2秒发布一次qos为1的消息到云平台
+sys.taskInit(function()
+    while 1 do
+        sys.wait(2000)
+        if iotcloudc then
+            iotcloudc:publish("/"..iotcloudc.product_id.."/"..iotcloudc.device_name.."/user/update", "hello world", 1) -- 上传数据
+        end
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 70 - 0
module/Air780EHM/demo/iotcloud/baiduiot/main.lua

@@ -0,0 +1,70 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "iotclouddemo"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+local iotcloud = require("iotcloud")
+
+-- 统一联网函数
+sys.taskInit(function()
+    local device_id = mcu.unique_id():toHex()
+    if mobile then
+        LED = gpio.setup(27, 0, gpio.PULLUP)
+        device_id = mobile.imei()
+    end
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready", device_id)
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local ret, device_id = sys.waitUntil("net_ready")
+
+    -- 百度云
+    -- 密钥认证(手动注册)
+    -- iotcloudc = iotcloud.new(iotcloud.BAIDU,{produt_id = "aakyhyw",device_name = "869329069169988",device_secret = "IzNIWqXdGRPEoUlS"})
+    -- 证书认证(自动注册)
+    iotcloudc = iotcloud.new(iotcloud.BAIDU,{produt_id = "aakyhyw",device_name = "869329069169988"},{tls={server_cert=io.readFile("/luadb/GlobalSign.cer"),client_cert=io.readFile("/luadb/client_cert.txt"),client_key=io.readFile("/luadb/client_private_key.txt")}})
+
+    if iotcloudc then
+        iotcloudc:connect()
+    end
+end)
+
+sys.subscribe("iotcloud", function(cloudc,event,data,payload)
+    -- 注意,此处不是协程内,复杂操作发消息给协程内进行处理
+    if event == iotcloud.CONNECT then -- 云平台联上了
+        print("iotcloud","CONNECT", "云平台连接成功")
+        iotcloudc:subscribe("$iot/869329069169988/user/fortest") -- 可以自由订阅主题等
+
+    elseif event == iotcloud.RECEIVE then
+        print("iotcloud","topic", data, "payload", payload)
+        if payload == "open" then
+            log.info("main", "收到云平台下发的指令")
+            LED(1)
+        elseif payload == "close" then
+            LED(0)
+        end
+        -- 用户处理代码
+    elseif event ==  iotcloud.OTA then
+        if data then
+            rtos.reboot()
+        end
+    elseif event == iotcloud.DISCONNECT then -- 云平台断开了
+        -- 用户处理代码
+        print("iotcloud","DISCONNECT", "云平台连接断开")
+    end
+end)
+
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 88 - 0
module/Air780EHM/demo/iotcloud/huaweiiot/main.lua

@@ -0,0 +1,88 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "iotclouddemo"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+local iotcloud = require("iotcloud")
+
+-- 统一联网函数
+sys.taskInit(function()
+    local device_id = mcu.unique_id():toHex()
+    if mobile then
+        -- LED = gpio.setup(27, 0, gpio.PULLUP)
+        device_id = mobile.imei()
+    end
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready", device_id)
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local ret, device_id = sys.waitUntil("net_ready")
+
+    -- -- 华为云
+    -- -- 动态注册(免预注册)
+    -- iotcloudc = iotcloud.new(iotcloud.HUAWEI,{produt_id = "670c7b2dfc8d5a4ea71c6a79",
+    --                                             project_id = "c086a58ebd714bfcb1a0fea2f0edde36",
+    --                                             endpoint = "9098a2ff3c.st1",
+    --                                             iam_username="hao",
+    --                                             iam_password="Wsh1322764769",
+    --                                             iam_domain="hao15738882476"})
+    -- 密钥校验 (预注册)
+    iotcloudc = iotcloud.new(iotcloud.HUAWEI,{produt_id = "670c7b2dfc8d5a4ea71c6a79",endpoint = "5341624af8.st1",device_name = "869329069169988",device_secret = "XXX"})
+
+    if iotcloudc then
+        iotcloudc:connect()
+    end
+end)
+
+sys.subscribe("iotcloud", function(cloudc,event,data,payload)
+    -- 注意,此处不是协程内,复杂操作发消息给协程内进行处理
+    if event == iotcloud.CONNECT then -- 云平台联上了
+        print("iotcloud","CONNECT", "云平台连接成功")
+        -- iotcloudc:subscribe("/huawei/down/869329069169988") -- 可以自由定阅主题等
+        -- iotcloudc:subscribe("$oc/devices/869329069169988/user/869329069169988")
+    elseif event == iotcloud.RECEIVE then
+        print("iotcloud","topic", data, "payload", payload)
+        -- local test_value = json.decode(payload).content.switch
+        -- print("test value:", test_value)
+
+        -- if test_value == 1 then
+        --     LED(1)
+        -- elseif test_value == 0 then
+        --     LED(0)
+        -- end
+        -- 用户处理代码
+    elseif event ==  iotcloud.OTA then
+        if data then
+            rtos.reboot()
+        end
+    elseif event == iotcloud.DISCONNECT then -- 云平台断开了
+        -- 用户处理代码
+        print("iotcloud","DISCONNECT", "云平台连接断开")
+    end
+end)
+
+-- -- 每隔2秒发布一次qos为1的消息到云平台
+-- sys.taskInit(function()
+--     while 1 do
+--         sys.wait(2000)
+--         if iotcloudc then
+--             iotcloudc:publish("$oc/devices/869329069169988/user/869329069169988", "hello world!", 1) -- 上传数据
+--         end
+--     end
+-- end)
+
+
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 157 - 0
module/Air780EHM/demo/iotcloud/onenet/main.lua

@@ -0,0 +1,157 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "oneNET_demo"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+lbsLoc2 = require("lbsLoc2")
+local iotcloud = require("iotcloud")
+mobile.simid(2, true)
+
+local produt_id = "4qM5N1Sa4T"
+local userid = "226691"
+local userkey = "pk1M3FKXBvvmjF8If/xDfSFFmr96NZCEg00sxlLBMjjh9vOD5hpIs42rmAYnMh5b3m9B1+0rmYdqzUyoQVrxow=="
+local device_name = mobile.imei()
+local send_data_time = 5 * 60 * 1000 -- 定时发送数据的时间,单位ms
+
+-- 统一联网函数
+sys.taskInit(function()
+    local device_id = mcu.unique_id():toHex()
+
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready", device_id)
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local ret, device_id = sys.waitUntil("net_ready")
+
+    --------    以下接入方式根据自己需要修改,相关参数修改为自己的    ---------
+
+    -- ONENET云
+    -- 动态注册
+    iotcloudc = iotcloud.new(iotcloud.ONENET, {
+        device_name = device_name,
+        produt_id = produt_id,
+        userid = userid,
+        userkey = userkey
+    })
+    -- 一型一密
+    -- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",product_secret = "xxx"})
+    -- 一机一密
+    -- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",device_name = "xxx",device_secret = "xxx"})
+
+    if iotcloudc then
+        iotcloudc:connect()
+    end
+
+end)
+
+-- 发布和订阅的主题
+
+local oneNET_sub = "$sys/" .. produt_id .. "/" .. device_name .. "/thing/property/post/reply"
+
+local oneNET_pub = "$sys/" .. produt_id .. "/" .. device_name .. "/thing/property/post"
+
+local function oneNET_send_data()
+    log.info("oneNET 链接成功,准备开始发送数据")
+    while 1 do
+        -- 没有mobile库就没有基站定位
+        mobile.reqCellInfo(15)
+        -- 由于基站定位需要等待扫描周围基站,推荐扫描时间为15S
+        sys.waitUntil("CELL_INFO_UPDATE", 15000)
+        local lat, lng, t = lbsLoc2.request(5000)
+        log.info("lbsLoc2", lat, lng, (json.encode(t or {})))
+        -- 如果没扫描到基站则给lat和lng赋值为0
+        if lat and lng then
+            log.info("扫描到了,有位置信息")
+        else
+            lat = "0"
+            lng = "0"
+        end
+        -- 读取CPU温度, 单位为0.001摄氏度, 是内部温度, 非环境温度
+        adc.open(adc.CH_CPU)
+        local cpu_temp = adc.get(adc.CH_CPU)
+        adc.close(adc.CH_CPU)
+        local gpio_pin = 6 -- GPIO编号
+        local gpio_state = gpio.get(gpio_pin)
+        local send_data = {
+            id = "123",
+            verson = VERSION,
+            params = {
+                gpio_state = {
+                    value = gpio_state
+                },
+                cpu_temp = {
+                    value = cpu_temp / 1000
+                },
+                lbs_lat = {
+                    value = tonumber(lat)
+                },
+                lbs_lng = {
+                    value = tonumber(lng)
+                    -- value = lng
+                }
+            }
+        }
+        local send_data = json.encode(send_data)
+        log.info("发送的数据为", send_data)
+        -- 正式发布数据
+        iotcloudc:publish(oneNET_pub, send_data)
+        -- 循环发送数据的定时时间
+        sys.wait(send_data_time)
+    end
+
+end
+
+local con = 0
+--oneNET断开后的处理函数,
+local function oneNET_DISCONNECT()
+    log.info("云平台断开了,隔一分钟重连一次,如果10次都没有连上则重启设备")
+    while con < 10 do
+        sys.wait(60*1000)
+        log.info("oneNET reconnection",con)
+        iotcloudc:connect()
+    end
+    pm.reboot()
+end
+sys.subscribe("iotcloud", function(cloudc, event, data, payload)
+    -- 注意,此处不是协程内,复杂操作发消息给协程内进行处理
+    if event == iotcloud.CONNECT then -- 云平台联上了
+        log.info("iotcloud", "CONNECT", "oneNET平台连接成功")
+        iotcloudc:subscribe({
+            [oneNET_sub] = 1
+        }) -- 订阅服务器下发数据的主题
+        -- 链接成功,启动一个task专门用来定时发消息
+        sys.taskInit(oneNET_send_data)
+
+    elseif event == iotcloud.RECEIVE then
+        log.info("收到服务器下发的数据")
+        log.info("iotcloud", "topic", data, "payload", payload)
+
+        -- 用户处理代码
+        if payload then
+            payload = json.decode(payload)
+            if payload["code"] == 200 then
+                log.info("服务器收到了刚刚上传的数据", payload["msg"])
+            else
+                log.info("服务器接收数据有误", "错误码为", payload["code"], "错误信息为",
+                    payload["msg"])
+            end
+        end
+
+    elseif event == iotcloud.SEND then
+        log.info("发送数据成功")
+
+    elseif event == iotcloud.DISCONNECT then -- 云平台断开了
+       sys.taskInit(oneNET_DISCONNECT)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 79 - 0
module/Air780EHM/demo/iotcloud/tencentiot/main.lua

@@ -0,0 +1,79 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "iotclouddemo"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+local iotcloud = require("iotcloud")
+
+-- 统一联网函数
+sys.taskInit(function()
+    local device_id = mcu.unique_id():toHex()
+    if mobile then
+        LED = gpio.setup(27, 0, gpio.PULLUP)
+        device_id = mobile.imei()
+    end
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready", device_id)
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local ret, device_id = sys.waitUntil("net_ready")
+
+    -- -- 腾讯云
+    -- -- 动态注册
+    -- iotcloudc = iotcloud.new(iotcloud.TENCENT,{produt_id = "ZAJCHA24SH" ,product_secret = "vGGwnSkyM4eD5ddbNrQoGJox"})
+    -- -- 密钥校验
+    iotcloudc = iotcloud.new(iotcloud.TENCENT,{produt_id = "ZAJCHA24SH",device_name = "869329069169988",device_secret = "xNWcxDX0qzSM74oFteehvw=="})
+
+    if iotcloudc then
+        iotcloudc:connect()
+    end
+end)
+
+sys.subscribe("iotcloud", function(cloudc,event,data,payload)
+    -- 注意,此处不是协程内,复杂操作发消息给协程内进行处理
+    if event == iotcloud.CONNECT then -- 云平台联上了
+        print("iotcloud","CONNECT", "云平台连接成功")
+        iotcloudc:subscribe("$thing/down/property/ZAJCHA24SH/869329069169988") -- 可以自由定阅主题等
+    elseif event == iotcloud.RECEIVE then
+        print("iotcloud","topic", data, "payload", payload)
+        local test_value = json.decode(payload).params.switch
+        print("test value:", test_value)
+
+        if test_value == 1 then
+            LED(1)
+        elseif test_value == 0 then
+            LED(0)
+        end
+        -- 用户处理代码
+    elseif event ==  iotcloud.OTA then
+        if data then
+            rtos.reboot()
+        end
+    elseif event == iotcloud.DISCONNECT then -- 云平台断开了
+        -- 用户处理代码
+        print("iotcloud","DISCONNECT", "云平台连接断开")
+    end
+end)
+
+-- 每隔2秒发布一次qos为1的消息到云平台
+sys.taskInit(function()
+    while 1 do
+        sys.wait(2000)
+        if iotcloudc then
+            iotcloudc:publish("$thing/up/property/ZAJCHA24SH/869329069169988", "hello world!", 1) -- 上传数据
+        end
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 132 - 0
module/Air780EHM/demo/iotcloud/tlink/main.lua

@@ -0,0 +1,132 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "IOTCLOUD_TLINK"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+local iotcloud = require("iotcloud")
+
+-- TLINK平台设备序列号
+local serialNumber = "XXXXXXXXXXXXXX"
+-- TLINK平台登录账号
+local username = "XXXXXXXXXXXXXX"
+-- TLINK平台登录密码
+local password = "XXXXXXXXXXXXXX"
+
+-- TLINK平台添加的传感器ID
+local sensorsId = "XXXXXXXXXXXXXX"
+
+-- TLINK平台添加的传感器对应的TOPIC,iotcloud库会自动为设备订阅通配主题,用户只需判断报文是否来自该传感器的TOPIC即可
+local tlinkSubTopic = serialNumber .. "/" .. sensorsId
+
+-- 是否使用自动重连
+local autoReconnect = false
+local connectConfig = {
+    keepalive = 60 -- mqtt心跳设置为60秒
+}
+
+-- 手动重连定时器ID
+local reconnectTimerId
+
+if autoReconnect then
+    connectConfig.autoreconn = 3000 -- 云平台断开3秒后自动重连
+end
+
+-- TLINK平台switch类型传感器回传报文
+local function switchSensorPub(cloudc, switch)
+    local tmp = {
+        sensorDatas = {{
+            sensorsId = sensorsId,
+            switcher = switch
+        }}
+    }
+    local payload = json.encode(tmp)
+    log.info("回传的报文", payload)
+    cloudc:publish(serialNumber, payload)
+end
+
+-- 将gpio27设置为输出模式,低电平,gpio27在开发板上控制的是蓝灯
+local ledCtrl = gpio.setup(27, 0)
+
+-- 订阅来自iotcloud库发布的消息主题
+sys.subscribe("iotcloud", function(cloudc, event, data, payload)
+    -- 注意,此处不是协程内,复杂操作发消息给协程内进行处理
+    if event == iotcloud.CONNECT then -- 云平台连上了
+        if not autoReconnect then
+            if reconnectTimerId and sys.timerIsActive(reconnectTimerId) then
+                sys.timerStop(reconnectTimerId)
+            end
+        end
+        log.info("TLINK 云平台连接成功")
+        -- cloudc:subscribe("test") -- 可以自由订阅主题,详情可参考TLINK开发文档
+    elseif event == iotcloud.RECEIVE then
+        log.info("TLINK 发布消息", "topic", data, "payload", payload)
+        -- 判断主题是否来自sensorsId的报文
+        if data == tlinkSubTopic then
+            -- json解析
+            local userPayload, result, err = json.decode(payload)
+            -- 防止异常的措施,防止json解析失败代码运行异常
+            if result == 1 and userPayload and type(userPayload) == "table" then
+                if userPayload.sensorDatas and userPayload.sensorDatas[1] then
+                    local switch = userPayload.sensorDatas[1].switcher
+                    -- 如果开关状态等于1,则打开蓝灯,否则关闭蓝灯
+                    if switch == 1 then
+                        ledCtrl(1)
+                    else
+                        ledCtrl(0)
+                    end
+                    -- 回传报文
+                    switchSensorPub(cloudc, switch)
+                end
+            end
+        end
+    elseif event == iotcloud.OTA then
+        -- 用户处理代码
+        -- TLINK 不会有这条消息上报,可忽略
+    elseif event == iotcloud.DISCONNECT then -- 云平台断开了
+        -- 用户处理代码
+        log.info("云平台连接断开")
+        if not autoReconnect then
+            if reconnectTimerId and sys.timerIsActive(reconnectTimerId) then
+                sys.timerStop(reconnectTimerId)
+            end
+            reconnectTimerId = sys.timerStart(function()
+                cloudc:connect()
+            end, 3000)
+        end
+    end
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local result
+    while true do
+        log.info("等待联网")
+        result = sys.waitUntil("IP_READY", 30000)
+        if result then
+            break
+        end
+    end
+    log.info("联网成功")
+
+    -- 创建TLINK云平台实例,在iotcloud TLINK平台中,device_name对应mqtt三元组的clientId,product_id对应mqtt三元组的username,product_secret对应mqtt三元组的password
+    iotcloudc = iotcloud.new(iotcloud.TLINK, {
+        device_name = serialNumber,
+        produt_id = username,
+        product_secret = password
+    }, connectConfig)
+    -- 创建成功,则连接,创建失败,就结束程序
+    if iotcloudc then
+        iotcloudc:connect()
+    else
+        log.error("iotcloud", "创建失败, 请检查参数")
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 70 - 0
module/Air780EHM/demo/iotcloud/tuyaiot/示例1 一机一密/main.lua

@@ -0,0 +1,70 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "iotclouddemo"
+VERSION = "1.0.0"
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+local iotcloud = require("iotcloud")
+
+-- 统一联网函数
+sys.taskInit(function()
+    local device_id = mcu.unique_id():toHex()
+    -----------------------------
+    if mobile then
+        device_id = mobile.imei()
+    else
+        -- 其他不认识的bsp, 循环提示一下吧
+        while 1 do
+            sys.wait(1000)
+            log.info("bsp", "本bsp可能未适配网络层, 请查证")
+        end
+    end
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready", device_id)
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local ret, device_id = sys.waitUntil("net_ready")
+
+    --------    以下接入方式根据自己需要修改,相关参数修改为自己的    ---------
+
+    -- -- 涂鸦云 
+    iotcloudc = iotcloud.new(iotcloud.TUYA,{device_name = "xxx",device_secret = "xxx"})
+
+    if iotcloudc then
+        iotcloudc:connect()
+    else
+        log.error("iotcloud", "创建失败, 请检查参数")
+    end
+    
+end)
+
+sys.subscribe("iotcloud", function(cloudc,event,data,payload)
+    -- 注意,此处不是协程内,复杂操作发消息给协程内进行处理
+    if event == iotcloud.CONNECT then -- 云平台联上了
+        print("iotcloud","CONNECT", "云平台连接成功")
+        iotcloudc : subscribe("tylink/${deviceId}/thing/property/set") -- 订阅信息
+        iotcloudc : publish("tylink/${deviceId}/thing/property/report" , '{"data":{"device_status":"999"}}' , 1) -- 上报信息
+    elseif event == iotcloud.RECEIVE then
+        print("iotcloud","topic", data, "payload", payload)
+        -- 用户处理代码
+    elseif event ==  iotcloud.OTA then
+        if data then
+            rtos.reboot()
+        end
+    elseif event == iotcloud.DISCONNECT then -- 云平台断开了
+        -- 用户处理代码
+    end
+end)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 81 - 0
module/Air780EHM/demo/json/main.lua

@@ -0,0 +1,81 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "jsondemo"
+VERSION = "1.0.0"
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+log.info("main", PROJECT, VERSION)
+
+-- json库支持将 table 转为 字符串, 或者反过来, 字符串 转 table
+-- 若转换失败, 会返回nil值, 强烈建议在使用时添加额外的判断
+sys.taskInit(function()
+    while 1 do
+        sys.wait(1000)
+        -- table 转为 字符串
+        local t = {abc=123, def="123", ttt=true}
+        local jdata = json.encode(t)
+        log.info("json", jdata)  									--日志输出:{"ttt":true,"def":"123","abc":123}
+
+        -- 字符串转table
+        local str = "{\"abc\":1234545}" -- 字符串可以来源于任何地方,网络,文本,用户输入,都可以
+        local t = json.decode(str)
+        if t then
+			-- 若解码成功,t不为nil
+			log.info("json", "decode", t.abc) 						--日志输出:decode	1234545
+		else
+			-- 若解码失败,t为nil
+			log.info("json", "decode failed")
+		end
+
+        -- lua中的table是 数组和hashmap的混合体
+        -- 这对json来说会有一些困扰, 尤其是空的table
+        local t = {abc={}}
+        -- 假设从业务上需要输出 {"abc":[]}
+        -- 实际会输出 {"abc": {}} , 空table是优先输出 hashmap (即字典模式)形式, 而非数组形式,Lua语言中数组优先级低于hashmap优先级
+        log.info("json", "encode", json.encode(t)) 					--日志输出:encode	{"abc":{}}
+        -- 混合场景, json场景应避免使用
+        t.abc.def = "123"
+        t.abc[1] = 345
+        -- 输出的内容是 {"abc":{"1":345,"def":"123"}}
+        log.info("json", "encode2", json.encode(t))  				--日志输出:encode2	{"abc":{"1":345,"def":"123"}}
+
+        -- 浮点数演示
+        log.info("json", json.encode({abc=1234.300}))  				--日志输出:{"abc":1234.300}
+        -- 限制小数点到1位
+        log.info("json", json.encode({abc=1234.300}, "1f")) 		--日志输出:{"abc":1234.3}
+
+ 
+        local tmp = "ABC\r\nDEF\r\n"
+        local tmp2 = json.encode({str=tmp}) --在JSON中,\r\n 被保留为字符串的一部分
+        log.info("json", tmp2)                                    	--日志输出:{"str":"ABC\r\nDEF\r\n"}
+        local tmp3 = json.decode(tmp2)								
+        log.info("json", "tmp3", tmp3.str, tmp3.str == tmp)			--日志输出:tmp3	ABC
+																		--DEF
+																		--		true  注:true前存在一个TAB长度(这个TAB原因未知,但不影响使用)
+        -- break
+
+        log.info("json.null", json.encode({name=json.null}))		--日志输出:{}  为空对象
+        log.info("json.null", json.decode("{\"abc\":null}").abc == json.null)  	--日志输出:false    在 Lua 中,nil 是一种特殊类型,用于表示“无值”或“未定义”。它与任何其他值(包括自定义的 json.null)都不相等
+        log.info("json.null", json.decode("{\"abc\":null}").abc == nil)			 --日志输出:false
+
+    end
+end)
+
+
+-- 这里演示4G模块上网后,会自动点亮网络灯,方便用户判断模块是否正常开机
+sys.taskInit(function()
+    while true do
+        sys.wait(6000)
+                if mobile.status() == 1 then
+                        gpio.set(27, 1)  
+                else
+                        gpio.set(27, 0) 
+                        mobile.reset()
+        end
+    end
+end)
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 26 - 0
module/Air780EHM/demo/lbsLoc2/main.lua

@@ -0,0 +1,26 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "lbsLoc2demo"
+VERSION = "1.0.0"
+
+local lbsLoc2 = require("lbsLoc2")
+
+sys.taskInit(function()
+    sys.waitUntil("IP_READY", 30000)
+    -- mobile.reqCellInfo(60)
+    -- sys.wait(1000)
+    while mobile do -- 没有mobile库就没有基站定位
+        mobile.reqCellInfo(15)--进行基站扫描
+        sys.waitUntil("CELL_INFO_UPDATE", 3000)--等到扫描成功,超时时间3S
+        local lat, lng, t = lbsLoc2.request(5000)--仅需要基站定位给出的经纬度
+        --local lat, lng, t = lbsLoc2.request(5000,nil,nil,true)--需要经纬度和当前时间
+        --(时间格式{"year":2024,"min":56,"month":11,"day":12,"sec":44,"hour":14})
+        log.info("lbsLoc2", lat, lng, (json.encode(t or {})))
+        sys.wait(60000)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

BIN
module/Air780EHM/demo/lcd/logo.jpg


+ 92 - 0
module/Air780EHM/demo/lcd/main.lua

@@ -0,0 +1,92 @@
+PROJECT = "lcddemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+sys = require("sys")
+
+
+-- 添加硬狗防止程序卡死
+if wdt then
+    wdt.init(9000) -- 初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
+end
+
+pm.ioVol(pm.IOVOL_ALL_GPIO, 3000)--所有IO电平开到3V,电平匹配
+
+-- 注意:V1.2的开发板需要打开GPIO28,V1.3的开发板需要打开GPIO29
+-- gpio.setup(28, 1) -- GPIO28打开给lcd电源供电 
+gpio.setup(29, 1) -- GPIO29打开给lcd电源供电 
+
+local rtos_bsp = rtos.bsp()
+-- local chip_type = hmeta.chip()
+-- 根据不同的BSP返回不同的值
+-- 根据不同的BSP返回不同的值
+-- spi_id,pin_reset,pin_dc,pin_cs,bl
+local function lcd_pin()
+    local rtos_bsp = rtos.bsp()
+    if string.find(rtos_bsp, "780EPM") then
+        return lcd.HWID_0, 36, 0xff, 0xff, 25 -- 注意:EC718P有硬件lcd驱动接口, 无需使用spi,当然spi驱动也支持
+    else
+        log.info("main", "没找到合适的cat.1芯片", rtos_bsp)
+        return
+    end
+end
+
+local spi_id, pin_reset, pin_dc, pin_cs, bl = lcd_pin()
+if spi_id ~= lcd.HWID_0 then
+    spi_lcd = spi.deviceSetup(spi_id, pin_cs, 0, 0, 8, 20 * 1000 * 1000, spi.MSB, 1, 0)
+    port = "device"
+else
+    port = spi_id
+end
+
+lcd.init("st7796", {
+    port = port,
+    pin_dc = pin_dc,
+    pin_pwr = bl,
+    pin_rst = pin_reset,
+    direction = 0,
+    -- direction0 = 0x00,
+    w = 320,
+    h = 480,
+    xoffset = 0,
+    yoffset = 0,
+    sleepcmd = 0x10,
+    wakecmd = 0x11,
+})
+
+-- 不在内置驱动的, 看demo/lcd_custom
+
+sys.taskInit(function()
+    -- 开启缓冲区, 刷屏速度回加快, 但也消耗2倍屏幕分辨率的内存
+    -- lcd.setupBuff()          -- 使用lua内存
+    lcd.setupBuff(nil, true) -- 使用sys内存, 只需要选一种
+    lcd.autoFlush(false)
+
+    while 1 do
+        lcd.clear()
+        log.info("合宙 780EPM LCD演示")
+        -- API 文档 https://wiki.luatos.com/api/lcd.html
+        if lcd.showImage then
+            -- 注意, jpg需要是常规格式, 不能是渐进式JPG
+            -- 如果无法解码, 可以用画图工具另存为,新文件就能解码了
+            lcd.showImage(0, 0, "/luadb/picture.jpg")
+            sys.wait(100)
+        end
+        -- log.info("lcd.drawLine", lcd.drawLine(100, 240, 240, 240, 0x001F)) -- 画线
+        -- log.info("lcd.drawRectangle", lcd.drawRectangle(100, 240, 240, 70, 0xF800)) -- 画框
+        -- log.info("lcd.drawCircle", lcd.drawCircle(150, 240, 100, 0x0CE0)) -- 画圆
+
+        -- lcd.setFont(lcd.font_opposansm32)
+        -- lcd.drawStr(60,240,"hello hezhou") --显示字符
+        lcd.flush()
+        sys.wait(1000)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

BIN
module/Air780EHM/demo/lcd/picture.jpg


+ 21 - 0
module/Air780EHM/demo/led/main.lua

@@ -0,0 +1,21 @@
+-- 本示例对比了普通GPIO和AGPIO的进入休眠模式前后的区别。
+-- Luatools需要PROJECT和VERSION这两个信息
+PROJECT = "leddemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+
+local gpio_number = 27 -- AGPIO GPIO号为27,也是核心板上的网络指示灯(蓝灯)
+
+
+gpio.setup(gpio_number, 1)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 64 - 0
module/Air780EHM/demo/log/main.lua

@@ -0,0 +1,64 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "LOG"
+VERSION = "2.0.0"
+
+--[[
+本demo演示 string字符串的基本操作
+1. lua的字符串是带长度, 这意味着, 它不依赖0x00作为结束字符串, 可以包含任意数据
+2. lua的字符串是不可变的, 就不能直接修改字符串的一个字符, 修改字符会返回一个新的字符串
+]]
+
+-- sys库是标配
+_G.sys = require("sys")
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+local netLed = require("netLed")
+--GPIO18配置为输出,默认输出低电平,可通过setGpio18Fnc(0或者1)设置输出电平
+local LEDA= gpio.setup(27, 0, gpio.PULLUP)
+
+sys.taskInit(function ()
+    sys.wait(1000) -- 免得看不到日志
+    local tmp
+
+	--实验1:输出四个等级的日志,日志等级排序从低到高为 debug < info < warn < error
+	log.debug(PROJECT, "debug message")
+	log.info(PROJECT, "info message")
+	log.warn(PROJECT, "warn message")
+	log.error(PROJECT, "error message")
+	
+	
+	--实验2:输出INFO及更高级别日志,即debug日志不输出
+	log.setLevel("INFO")
+	print(log.getLevel())
+
+	-- 这条debug级别的日志不会输出
+	log.debug(PROJECT, "debug message")
+	log.info(PROJECT, "info message")
+	log.warn(PROJECT, "warn message")
+	log.error(PROJECT, "error message")
+	
+	--实验3:通过日志输出变量内容
+	local myInteger = 42
+    log.info("Integer", myInteger)
+end)
+-- 这里演示4G模块上网后,会自动点亮网络灯,方便用户判断模块是否正常开机
+sys.taskInit(function()
+    while true do
+        sys.wait(6000)
+                if mobile.status() == 1 then
+                        gpio.set(27, 1)  
+                else
+                        gpio.set(27, 0) 
+                        mobile.reset()
+        end
+    end
+end)
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 87 - 0
module/Air780EHM/demo/lowpower/low_power_dissipation.lua

@@ -0,0 +1,87 @@
+
+-- netlab.luatos.com上打开TCP 有测试服务器
+local server_ip = "112.125.89.8"
+local server_port = 47523
+local is_udp = false --用户根据自己实际情况选择
+
+--是UDP服务器就赋值为true,是TCP服务器就赋值为flase
+--UDP服务器比TCP服务器功耗低
+--如果用户对数据的丢包率有极为苛刻的要求,最好选择TCP
+
+local Heartbeat_interval = 5 -- 发送数据的间隔时间,单位分钟
+
+-- 数据内容  
+local heart_data = string.rep("1234567890", 10)
+local rxbuf = zbuff.create(8192)
+
+local function netCB(netc, event, param)
+    if param ~= 0 then
+        sys.publish("socket_disconnect")
+        return
+    end
+    if event == socket.LINK then
+    elseif event == socket.ON_LINE then
+        -- 链接上服务器以后发送的第一包数据是 hello,luatos
+        socket.tx(netc, "hello,luatos!")
+
+    elseif event == socket.EVENT then
+        socket.rx(netc, rxbuf)
+        socket.wait(netc)
+        if rxbuf:used() > 0 then
+            log.info("收到", rxbuf:toStr(0, rxbuf:used()), "数据长度", rxbuf:used())
+        end
+        rxbuf:del()
+    elseif event == socket.TX_OK then
+        socket.wait(netc)
+        log.info("发送完成")
+    elseif event == socket.CLOSED then
+        sys.publish("socket_disconnect")
+    end
+end
+
+local function socketTask()
+    local netc = socket.create(nil, netCB) --创建一个链接
+    socket.debug(netc, true)--开启socket层的debug日志,方便寻找问题
+    socket.config(netc, nil, is_udp, nil, 300, 5, 6)  --配置TCP链接的参数,开启保活,防止长时间无数据交互服务器踢掉模块
+    while true do
+        --真正去链接服务器
+        local succ, result = socket.connect(netc, server_ip, server_port)
+        --链接成功后循环发送数据
+        while succ do
+            local Heartbeat_interval = Heartbeat_interval * 60 * 1000
+            sys.wait(Heartbeat_interval)
+            socket.tx(netc, heart_data)
+        end
+        --链接不成功5S重连一次
+        if not succ then
+            log.info("未知错误,5秒后重连")
+            uart.write(1, "未知错误,5秒后重连")
+        else
+            local result, msg = sys.waitUntil("socket_disconnect")
+        end
+        log.info("服务器断开了,5秒后重连")
+        uart.write(1, "服务器断开了,5秒后重连")
+        socket.close(netc)
+        sys.wait(5000)
+    end
+end
+
+function socketDemo()
+
+    --配置GPIO以达到最低功耗的目的
+    gpio.setup(23, nil)
+    gpio.close(33) -- 如果功耗偏高,开始尝试关闭WAKEUPPAD1
+    gpio.close(35) -- 这里pwrkey接地才需要,不接地通过按键控制的不需要
+
+    --关闭USB以后可以降低约150ua左右的功耗,如果不需要USB可以关闭
+    pm.power(pm.USB, false)
+
+     --进入低功耗长连接模式
+    pm.power(pm.WORK_MODE, 1)
+
+    sys.taskInit(socketTask)
+
+end
+
+sys.taskInit(socketDemo)
+

+ 17 - 0
module/Air780EHM/demo/lowpower/main.lua

@@ -0,0 +1,17 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "socket_low_power"
+VERSION = "1.0"
+PRODUCT_KEY = "123" --换成自己的
+-- sys库是标配
+_G.sys = require("sys")
+_G.sysplus = require("sysplus")
+log.style(1)
+
+--require "normal" --正常模式
+--require "low_power_dissipation" --低功耗模式
+ require "ultra_low_power" --超低功耗模式(PSM+模式)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 67 - 0
module/Air780EHM/demo/lowpower/normal.lua

@@ -0,0 +1,67 @@
+
+-- netlab.luatos.com上打开TCP,然后修改IP和端口号,自动回复netlab下发的数据,自收自发测试
+
+local server_ip = "112.125.89.8"
+local server_port = 47523
+
+local rxbuf = zbuff.create(8192)
+
+sys.subscribe("IP_READY", function(ip, adapter)
+    log.info("mobile", "IP_READY", ip, (adapter or -1) == socket.LWIP_GP)
+    sys.publish("net_ready")
+end)
+
+local function netCB(netc, event, param)
+    if param ~= 0 then
+        sys.publish("socket_disconnect")
+        return
+    end
+	if event == socket.LINK then
+	elseif event == socket.ON_LINE then
+        socket.tx(netc, "hello,luatos!")
+	elseif event == socket.EVENT then
+        socket.rx(netc, rxbuf)
+        socket.wait(netc)
+        if rxbuf:used() > 0 then
+            log.info("收到", rxbuf:toStr(0,rxbuf:used()):toHex())
+            log.info("发送", rxbuf:used(), "bytes")
+            socket.tx(netc, rxbuf)
+        end
+        rxbuf:del()
+	elseif event == socket.TX_OK then
+        socket.wait(netc)
+        log.info("发送完成")
+	elseif event == socket.CLOSED then
+        sys.publish("socket_disconnect")
+    end
+end
+
+local function socketTask()
+    sys.waitUntil("net_ready")
+    log.info("联网成功,准备链接服务器")
+    pm.power(pm.WORK_MODE,0) --进入正常模式
+	local netc = socket.create(nil, netCB)
+	socket.debug(netc, true)
+	socket.config(netc, nil, nil, nil, 300, 5, 6)   --开启TCP保活,防止长时间无数据交互被运营商断线
+    while true do
+        log.info("开始链接服务器")
+        local succ, result = socket.connect(netc, server_ip, server_port)
+        if not succ then
+            log.info("未知错误,5秒后重连")
+        else
+            log.info("链接服务器成功")
+            local result, msg = sys.waitUntil("socket_disconnect")
+        end
+        log.info("服务器断开了,5秒后重连")
+        socket.close(netc)
+        log.info(rtos.meminfo("sys"))
+        sys.wait(5000)
+    end
+end
+
+function socketDemo()
+	sys.taskInit(socketTask)
+end
+
+
+socketDemo()

+ 76 - 0
module/Air780EHM/demo/lowpower/ultra_low_power.lua

@@ -0,0 +1,76 @@
+local server_ip = "112.125.89.8" 
+local server_port = 47523 -- 换成自己的
+local period = 3 * 60 * 60 * 1000 -- 3小时唤醒一次
+
+local reason, slp_state = pm.lastReson() -- 获取唤醒原因
+log.info("wakeup state", pm.lastReson())
+local libnet = require "libnet"
+
+local d1Name = "D1_TASK"
+local function netCB(msg)
+    log.info("未处理消息", msg[1], msg[2], msg[3], msg[4])
+end
+
+local function testTask(ip, port)
+    local txData
+    if reason == 0 then
+        txData = "normal wakeup"
+    elseif reason == 1 then
+        txData = "timer wakeup"
+    elseif reason == 2 then
+        txData = "pad wakeup"
+    elseif reason == 3 then
+        txData = "uart1 wakeup"
+    end
+    if slp_state > 0 then
+        mobile.flymode(0, false) -- 退出飞行模式,进入psm+前进入飞行模式,唤醒后需要主动退出
+    end
+
+    --gpio.close(32)
+
+    local netc, needBreak
+    local result, param, is_err
+    netc = socket.create(nil, d1Name)
+    socket.debug(netc, false)
+    socket.config(netc) 
+    local retry = 0
+    while retry < 3 do
+        log.info(rtos.meminfo("sys"))
+        result = libnet.waitLink(d1Name, 0, netc)
+        result = libnet.connect(d1Name, 5000, netc, ip, port)
+        if result then
+            log.info("服务器连上了")
+            result, param = libnet.tx(d1Name, 15000, netc, txData)
+            if not result then
+                log.info("服务器断开了", result, param)
+                break
+            else
+                needBreak = true
+            end
+        else
+            log.info("服务器连接失败")
+        end
+        libnet.close(d1Name, 5000, netc)
+        retry = retry + 1
+        if needBreak then
+            break
+        end
+    end
+
+    uart.setup(1, 9600) -- 配置uart1,外部唤醒用
+    
+    -- 配置GPIO以达到最低功耗的目的
+	gpio.close(23) 
+    gpio.close(45) 
+    gpio.close(46) --这里pwrkey接地才需要,不接地通过按键控制的不需要
+
+    pm.dtimerStart(3, period) -- 启动深度休眠定时器
+
+    mobile.flymode(0, true) -- 启动飞行模式,规避可能会出现的网络问题
+    pm.power(pm.WORK_MODE, 3) -- 进入极致功耗模式
+
+    sys.wait(15000) -- demo演示唤醒时间是三十分钟,如果15s后模块重启,则说明进入极致功耗模式失败,
+    log.info("进入极致功耗模式失败,尝试重启")
+    rtos.reboot()
+end
+sysplus.taskInitEx(testTask, d1Name, netCB, server_ip, server_port)

+ 41 - 0
module/Air780EHM/demo/miniz/main.lua

@@ -0,0 +1,41 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "minizdemo"
+VERSION = "1.0.0"
+
+sys = require("sys")
+
+--添加硬狗防止程序卡死
+if wdt then
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+sys.taskInit(function()
+    sys.wait(1000)
+    -- 压缩过的字符串, 为了方便演示, 这里用了base64编码
+    -- 大部分MCU设备的内存都比较小, miniz.compress 通常在服务器端完成,这里就不演示了
+    -- miniz能解压标准zlib数据流
+    local b64str = "eAEFQIGNwyAMXOUm+E2+OzjhCCiOjYyhyvbVR7K7IR0l+iau8G82eIW5jXVoPzF5pse/B8FaPXLiWTNxEMsKI+WmIR0l+iayEY2i2V4UbqqPh5bwimyEuY11aD8xeaYHxAquvom6VDFUXqQjG1Fek6efCFfCK0b0LUnQMjiCxhUT05GNL75dFUWCSMcjN3EE5c4Wvq42/36R41fa"
+    local str = b64str:fromBase64()
+
+    local dstr = miniz.uncompress(str)
+    -- 压缩过的数据长度 156
+    -- 解压后的数据长度,即原始数据的长度 235
+    log.info("miniz", "compressed", #str, "uncompressed", #dstr)
+
+    -- 演示压缩解压
+    local ostr = "abcd12345"
+    -- 压缩字符串
+    local zstr = miniz.compress(ostr)
+    log.info("压缩后的字符串:",zstr:toHex())
+    -- 解压字符串
+    local lstr = miniz.uncompress(zstr)
+    log.info("miniz","compress zstr",#zstr,"uncompress lstr data",lstr)
+end)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 122 - 0
module/Air780EHM/demo/mobile/main.lua

@@ -0,0 +1,122 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "mobiledemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+
+-- 对于双卡的设备, 可以设置为自动选sim卡
+-- 但是, 这样SIM1所在管脚就强制复用为SIM功能, 不可以再复用为GPIO
+-- mobile.simid(2)
+mobile.simid(2,true)--优先用SIM0
+
+
+sys.taskInit(function()
+
+	log.info("status", mobile.status())
+    local band = zbuff.create(40)
+    local band1 = zbuff.create(40)
+    mobile.getBand(band)
+    log.info("当前使用的band:")
+    for i=0,band:used()-1 do
+        log.info("band", band[i])
+    end
+    band1[0] = 38
+    band1[1] = 39
+    band1[2] = 40
+    mobile.setBand(band1, 3)    --改成使用38,39,40
+    band1:clear()
+    mobile.getBand(band1)
+    log.info("修改后使用的band:")
+    for i=0,band1:used()-1 do
+        log.info("band", band1[i])
+    end
+    mobile.setBand(band, band:used())    --改回原先使用的band,也可以下载的时候选择清除fs
+
+    mobile.getBand(band1)
+    log.info("修改回默认使用的band:")
+    for i=0,band1:used()-1 do
+        log.info("band", band1[i])
+    end
+	-- mobile.vsimInit()
+	-- mobile.flymode(nil,true)
+	-- mobile.vsimOnOff(true)
+	-- mobile.flymode(nil,false)
+    -- mobile.apn(0,2,"") -- 使用默认APN激活CID2
+    -- mobile.rtime(3) -- 在无数据交互时,RRC 3秒后自动释放
+    -- 下面是配置自动搜索小区间隔,和轮询搜索冲突,开启1个就可以了
+    -- mobile.setAuto(10000,30000, 5) -- SIM暂时脱离后自动恢复,30秒搜索一次周围小区信息
+	log.info("status", mobile.status())
+    sys.wait(2000)
+    while 1 do
+        log.info("imei", mobile.imei())
+        log.info("imsi", mobile.imsi())
+        local sn = mobile.sn()
+        if sn then
+            log.info("sn",   sn:toHex())
+        end
+		log.info("status", mobile.status())
+        
+
+        log.info("iccid", mobile.iccid())
+        log.info("csq", mobile.csq()) -- 4G模块的CSQ并不能完全代表强度
+        log.info("rssi", mobile.rssi()) -- 需要综合rssi/rsrq/rsrp/snr一起判断
+        log.info("rsrq", mobile.rsrq())
+        log.info("rsrp", mobile.rsrp())
+        log.info("snr", mobile.snr())
+        log.info("simid", mobile.simid()) -- 这里是获取当前SIM卡槽
+        log.info("apn", mobile.apn(0,1))
+        log.info("ip", socket.localIP())
+		log.info("lua", rtos.meminfo())
+        -- sys内存
+        log.info("sys", rtos.meminfo("sys"))
+        sys.wait(15000)
+    end
+end)
+
+-- 基站数据的查询
+
+-- 订阅式, 模块本身会周期性查询基站信息,但通常不包含临近小区
+sys.subscribe("CELL_INFO_UPDATE", function()
+    log.info("cell", json.encode(mobile.getCellInfo()))
+end)
+
+-- 轮询式, 包含临近小区信息,这是手动搜索,和上面的自动搜索冲突,开启一个就行
+sys.taskInit(function()
+    sys.wait(5000)
+	mobile.config(mobile.CONF_SIM_WC_MODE, 2)
+    while 1 do
+        mobile.reqCellInfo(10)
+        sys.wait(11000)
+        log.info("cell", json.encode(mobile.getCellInfo()))
+		mobile.config(mobile.CONF_SIM_WC_MODE, 2)
+    end
+end)
+
+-- 获取sim卡的状态
+
+sys.subscribe("SIM_IND", function(status, value)
+    log.info("sim status", status)
+    if status == 'GET_NUMBER' then
+        log.info("number", mobile.number(0))
+    end
+	if status == "SIM_WC" then
+        log.info("sim", "write counter", value)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 155 - 0
module/Air780EHM/demo/modbus/ascii/master_ascii/main.lua

@@ -0,0 +1,155 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "modbus_master_ascii"
+VERSION = "1.0.0"
+log.style(1)
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+
+--初始化通讯串口
+local uartid = 1        -- 根据实际设备选取不同的uartid
+local uart485Pin = 24   -- 用于控制485接收和发送的使能引脚
+gpio.setup(1, 1)        --打开电源(开发板485供电脚是gpio1,用开发板测试需要开机初始化拉高gpio1)
+uart.setup(uartid, 115200, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)
+
+
+-- 创建主站设备,ASCII模式
+-- 设置通讯间隔时间,主站将按每隔 设置时间 的频率向从站问询数据(默认100ms),当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
+-- 设置通讯超时时间和消息发送超时重发次数,当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送,默认1)
+-- 设置断线重连时间间隔,当从站与主站断连后,主站将在设置时间内重新连接从站(默认5000ms)
+mb_ascii = modbus.create_master(modbus.MODBUS_ASCII, uartid,3000,2000,1,5000)
+
+
+-- 为主站添加从站,从站ID为1,可使用modbus.add_slave(master_handler, slave_id)接口添加多个从站,最多可以添加247个
+mb_slave1 = modbus.add_slave(mb_ascii, 1)
+-- -- 为主站添加从站,从站ID为2
+-- mb_slave2 = modbus.add_slave(mb_ascii, 2)
+
+
+-- 为从站1创建数据存储区,并创建通讯消息,默认为自动loop模式
+slave1_msg1_buf = zbuff.create(1)
+mb_slave1_msg1 = modbus.create_msg(mb_ascii, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf)
+slave1_msg1_buf:clear()
+
+-- -- 为从站1创建数据存储区,并创建通讯消息,如需要使用手动模式,须在这里设置为手动模式
+-- slave1_msg1_buf = zbuff.create(1)
+-- mb_slave1_msg1 = modbus.create_msg(mb_ascii, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf,1,modbus.EXEC)
+-- slave1_msg1_buf:clear()
+
+-- -- 为从站2创建数据存储区,并创建通讯消息,如设置多个从站,需要给每个从站创建数据储存区
+-- slave2_msg1_buf = zbuff.create(1)
+-- mb_slave2_msg1 = modbus.create_msg(mb_ascii, mb_slave2, modbus.REGISTERS,  modbus.READ, 0, 10, slave2_msg1_buf)
+-- slave2_msg1_buf:clear()
+
+
+-- 启动Modubs设备
+modbus.master_start(mb_ascii)
+
+
+-- -- 设置通讯间隔时间,设置后主站将按每隔 设置时间 的频率向从站问询数据,当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
+-- modbus.set_comm_interval_time(mb_ascii, 3000)
+
+
+-- -- 设置通讯超时时间,当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送)
+-- modbus.set_comm_timeout(mb_ascii, 2000)
+
+
+-- -- 设置消息发送失败、超时重发次数,如果主站在设置超时时间内未接收到数据,将按设置次数问询数据
+-- modbus.set_comm_resend_count(mb_ascii,2)
+
+
+-- -- 设置消息通讯周期,搭配modbus.create_master/modbus.set_comm_interval_time(mb_rtu, 3000)设置通讯时间使用,若设置通讯周期为2次,将在2倍的通讯时间后向从站问询数据
+-- modbus.set_msg_comm_period(mb_slave1_msg1, 2)
+
+
+-- 获取所有从站状态,如果所有从站状态为正常,返回true,其他情况返回false,将在每隔5秒的时间获取所有从站状态,并在日志中打印状态(仅方便调试使用,量产时可删除)
+sys.timerLoopStart(function()
+    local status = modbus.get_all_slave_state(mb_ascii)
+    log.info("modbus", status)
+end, 5000)
+
+
+-- 获取从站1的状态,每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
+sys.timerLoopStart(function()
+    local status = modbus.get_slave_state(mb_slave1)
+    log.info("modbus1", status)
+end, 5000)
+
+-- -- 获取从站2的状态,每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
+-- sys.timerLoopStart(function()
+--     local status = modbus.get_slave_state(mb_slave2)
+--     log.info("modbus2", status)
+-- end, 5000)
+
+
+-- -- 每隔5秒执行一次mb_slave1_msg1消息,使用modbus.exec(master_handler, msg_handler)接口须先在modbus.set_msg_comm_period(msg_handler, comm_period)接口中设置为手动模式;成功返回true,其他情况返回false
+-- sys.timerLoopStart(function()
+--     local status=modbus.exec(mb_ascii, mb_slave1_msg1)
+--     log.info("msg",status)
+-- end,5000)
+
+
+-- -- 测试删除一个从站对象,并删除与之相关的通讯消息句柄。需在主站停止时(modbus.master_stop)执行该操作,否则无效。
+-- -- 将在3分钟后删除从站1(主站已关闭),删除与之相关的通讯消息句柄,并在5秒后重启主站,可以观察从站是否删除成功。
+-- sys.timerStart(function()
+--     local status = modbus.remove_slave(mb_ascii, mb_slave1)
+--     log.info("modbus", "slave1 remove after 3 minutes")
+--     log.info("remove", status)
+    
+-- -- 移除从站后,5秒后重新启动Modbus主站
+--     sys.timerStart(function()
+--         modbus.master_start(mb_ascii)
+--         log.info("modbus", "Modbus master restarted after slave removal")
+--     end, 5000)
+-- end, 180000) 
+
+
+-- 获取从站1的状态,每1秒获取一次数据并转换为JSON
+sys.timerLoopStart(function()
+    -- 检查从站状态
+    local status = modbus.get_slave_state(mb_slave1)
+    if status == 0 then  -- 0表示正常
+        -- 读取缓冲区数据
+        slave1_msg1_buf:seek(0)  -- 重置指针到起始位置
+        
+        -- 读取4个寄存器的值(每个寄存器2字节)
+        local reg1 = slave1_msg1_buf:readU16()
+        local reg2 = slave1_msg1_buf:readU16()
+        local reg3 = slave1_msg1_buf:readU16()
+        local reg4 = slave1_msg1_buf:readU16()
+        
+        -- 创建数据表
+        local data = {
+            addr = 1,  -- 从站地址
+            fun = 3,   -- 功能码03
+            reg1 = reg1 / 10,  -- 假设原始数据需要除以10得到实际值
+            reg2 = reg2 / 10,
+            reg3 = reg3 / 10,
+            reg4 = reg4 / 10,
+            timestamp = os.time()  -- 添加时间戳
+        }
+        
+        -- 转换为JSON
+        local json_str = json.encode(data)
+        log.info("Modbus数据转JSON:", json_str)
+
+    else
+        log.warn("从站1状态异常:", status)
+    end
+end, 1000)
+
+
+
+-- -- 将在主站开启2分钟后停止modbus主站
+-- sys.timerStart(function()
+--     modbus.master_stop(mb_ascii)
+--     log.info("modbus", "Modbus stopped after 2 minutes")
+-- end, 120000) 
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 70 - 0
module/Air780EHM/demo/modbus/ascii/slave_ascii/main.lua

@@ -0,0 +1,70 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "modbus_slave_ascii"
+VERSION = "1.0.0"
+log.style(1)
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+--初始化通讯串口
+local uartid = 1        -- 根据实际设备选取不同的uartid
+local uart485Pin = 24   -- 用于控制485接收和发送的使能引脚
+gpio.setup(1, 1)        --打开电源(开发板485供电脚是gpio1,用开发板测试需要开机初始化拉高gpio1)
+uart.setup(uartid, 9600, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)
+
+
+-- 创建从站设备,此demo用作测试ASCII。
+local slave_id = 1
+mb_ascii_s = modbus.create_slave(modbus.MODBUS_ASCII, slave_id, uartid)
+
+
+-- 添加一块寄存器内存区
+registers = zbuff.create(1)
+modbus.add_block(mb_ascii_s, modbus.REGISTERS, 0, 32, registers)
+registers:clear()
+
+-- 创建线圈数据区
+ciols = zbuff.create(1)
+modbus.add_block(mb_ascii_s, modbus.CIOLS, 0, 32, ciols)
+ciols:clear()
+
+
+-- 启动modbus从站
+modbus.slave_start(mb_ascii_s)
+
+
+local counter = 0
+-- 修改和读取modbus值
+function modify_data()
+    counter = counter + 1  
+    -- 写入寄存器数据 (16位无符号整数)
+    registers:seek(0)
+    for i=0,31 do
+        registers:writeU16((counter + i) % 65536)  -- 写入递增数字,限制在0-65535
+    end  
+    -- 写入线圈数据 (1位布尔值)
+    ciols:seek(0)
+    for i=0,31 do
+        ciols:writeU8((counter + i) % 2)  -- 交替写入0和1
+    end
+  
+    -- 读取并打印部分数据用于调试
+    registers:seek(0)
+    ciols:seek(0)
+    log.info("registers:", registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16())
+    log.info("ciols    :", ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8())
+end
+sys.timerLoopStart(modify_data,1000)
+
+-- -- 测试停止modbus从站,将在从站启动两分钟后关闭
+-- sys.timerStart(function()
+--     modbus.slave_stop(mb_ascii_s)
+--     log.info("Modbus", "2分钟时间到,停止Modbus从站")
+-- end, 2 * 60 * 1000)  -- 2分钟(单位:毫秒)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 154 - 0
module/Air780EHM/demo/modbus/rtu/master_rtu/main.lua

@@ -0,0 +1,154 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "modbus_master_rtu"
+VERSION = "1.0.0"
+log.style(1)
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+
+--初始化通讯串口
+local uartid = 1        -- 根据实际设备选取不同的uartid
+local uart485Pin = 24   -- 用于控制485接收和发送的使能引脚
+gpio.setup(1, 1)        --打开电源(开发板485供电脚是gpio1,用开发板测试需要开机初始化拉高gpio1)
+uart.setup(uartid, 115200, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)
+
+
+-- 创建主站设备,RTU模式
+-- 设置通讯间隔时间,主站将按每隔 设置时间 的频率向从站问询数据(默认100ms),当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
+-- 设置通讯超时时间和消息发送超时重发次数,当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送,默认1)
+-- 设置断线重连时间间隔,当从站与主站断连后,主站将在设置时间内重新连接从站(默认5000ms)
+mb_rtu = modbus.create_master(modbus.MODBUS_RTU, uartid,3000,2000,1,5000)
+
+-- 为主站添加从站,从站ID为1,可使用modbus.add_slave(master_handler, slave_id)接口添加多个从站,最多可以添加247个
+mb_slave1 = modbus.add_slave(mb_rtu, 1)
+-- -- 为主站添加从站,从站ID为2
+-- mb_slave2 = modbus.add_slave(mb_rtu, 2)
+
+
+-- 为从站1创建数据存储区,并创建通讯消息,默认为自动loop模式
+slave1_msg1_buf = zbuff.create(1)
+mb_slave1_msg1 = modbus.create_msg(mb_rtu, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf)
+slave1_msg1_buf:clear()
+
+-- -- 为从站1创建数据存储区,并创建通讯消息,如需要使用手动模式,须在这里设置为手动模式
+-- slave1_msg1_buf = zbuff.create(1)
+-- mb_slave1_msg1 = modbus.create_msg(mb_rtu, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf,1,modbus.EXEC)
+-- slave1_msg1_buf:clear()
+
+-- -- 为从站2创建数据存储区,并创建通讯消息,如设置多个从站,需要给每个从站创建数据储存区
+-- slave2_msg1_buf = zbuff.create(1)
+-- mb_slave2_msg1 = modbus.create_msg(mb_rtu, mb_slave2, modbus.REGISTERS,  modbus.READ, 0, 10, slave2_msg1_buf)
+-- slave2_msg1_buf:clear()
+
+
+-- 启动Modubs设备
+modbus.master_start(mb_rtu)
+
+
+-- -- 设置通讯间隔时间,设置后主站将按每隔 设置时间 的频率向从站问询数据,当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
+-- modbus.set_comm_interval_time(mb_rtu, 3000)
+
+
+-- -- 设置通讯超时时间,当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送)
+-- modbus.set_comm_timeout(mb_rtu, 2000)
+
+
+-- -- 设置消息发送失败、超时重发次数,如果主站在设置超时时间内未接收到数据,将按设置次数问询数据
+-- modbus.set_comm_resend_count(mb_rtu,2)
+
+
+-- -- 设置消息通讯周期,搭配modbus.create_master/modbus.set_comm_interval_time(mb_rtu, 3000)设置通讯时间使用,若设置通讯周期为2次,将在2倍的通讯时间后向从站问询数据
+-- modbus.set_msg_comm_period(mb_slave1_msg1, 2)
+
+
+-- 获取所有从站状态,如果所有从站状态为正常,返回true,其他情况返回false,将在每隔5秒的时间获取所有从站状态,并在日志中打印状态(仅方便调试使用,量产时可删除)
+sys.timerLoopStart(function()
+    local status = modbus.get_all_slave_state(mb_rtu)
+    log.info("modbus", status)
+end, 5000)
+
+
+-- 获取从站1的状态,每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
+sys.timerLoopStart(function()
+    local status = modbus.get_slave_state(mb_slave1)
+    log.info("modbus1", status)
+end, 5000)
+
+-- -- 获取从站2的状态,每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
+-- sys.timerLoopStart(function()
+--     local status = modbus.get_slave_state(mb_slave2)
+--     log.info("modbus2", status)
+-- end, 5000)
+
+
+-- -- 每隔5秒执行一次mb_slave1_msg1消息,使用modbus.exec(master_handler, msg_handler)接口须先在modbus.set_msg_comm_period(msg_handler, comm_period)接口中设置为手动模式;成功返回true,其他情况返回false
+-- sys.timerLoopStart(function()
+--     local status=modbus.exec(mb_rtu, mb_slave1_msg1)
+--     log.info("msg",status)
+-- end,5000)
+
+
+-- -- 测试删除一个从站对象,并删除与之相关的通讯消息句柄。需在主站停止时(modbus.master_stop)执行该操作,否则无效。
+-- -- 将在3分钟后删除从站1(主站已关闭),删除与之相关的通讯消息句柄,并在5秒后重启主站,可以观察从站是否删除成功。
+-- sys.timerStart(function()
+--     local status = modbus.remove_slave(mb_rtu, mb_slave1)
+--     log.info("modbus", "slave1 remove after 3 minutes")
+--     log.info("remove", status)
+    
+-- -- 移除从站后,5秒后重新启动Modbus主站
+--     sys.timerStart(function()
+--         modbus.master_start(mb_rtu)
+--         log.info("modbus", "Modbus master restarted after slave removal")
+--     end, 5000)
+-- end, 180000) 
+
+
+-- 获取从站1的状态,每1秒获取一次数据并转换为JSON
+sys.timerLoopStart(function()
+    -- 检查从站状态
+    local status = modbus.get_slave_state(mb_slave1)
+    if status == 0 then  -- 0表示正常
+        -- 读取缓冲区数据
+        slave1_msg1_buf:seek(0)  -- 重置指针到起始位置
+        
+        -- 读取4个寄存器的值(每个寄存器2字节)
+        local reg1 = slave1_msg1_buf:readU16()
+        local reg2 = slave1_msg1_buf:readU16()
+        local reg3 = slave1_msg1_buf:readU16()
+        local reg4 = slave1_msg1_buf:readU16()
+        
+        -- 创建数据表
+        local data = {
+            addr = 1,  -- 从站地址
+            fun = 3,   -- 功能码03
+            reg1 = reg1 / 10,  -- 假设原始数据需要除以10得到实际值
+            reg2 = reg2 / 10,
+            reg3 = reg3 / 10,
+            reg4 = reg4 / 10,
+            timestamp = os.time()  -- 添加时间戳
+        }
+        
+        -- 转换为JSON
+        local json_str = json.encode(data)
+        log.info("Modbus数据转JSON:", json_str)
+
+    else
+        log.warn("从站1状态异常:", status)
+    end
+end, 1000)
+
+
+
+-- -- 将在主站开启2分钟后停止modbus主站
+-- sys.timerStart(function()
+--     modbus.master_stop(mb_rtu)
+--     log.info("modbus", "Modbus stopped after 2 minutes")
+-- end, 120000) 
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 70 - 0
module/Air780EHM/demo/modbus/rtu/slave_rtu/main.lua

@@ -0,0 +1,70 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "modbus_slave_rtu"
+VERSION = "1.0.0"
+log.style(1)
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+--初始化通讯串口
+local uartid = 1        -- 根据实际设备选取不同的uartid
+local uart485Pin = 24   -- 用于控制485接收和发送的使能引脚
+gpio.setup(1, 1)        --打开电源(开发板485供电脚是gpio1,用开发板测试需要开机初始化拉高gpio1)
+uart.setup(uartid, 115200, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)
+
+
+-- 创建从站设备,此demo仅用作测试RTU。
+local slave_id = 1
+mb_rtu_s = modbus.create_slave(modbus.MODBUS_RTU, slave_id, uartid)
+
+
+-- 添加一块寄存器内存区
+registers = zbuff.create(1)
+modbus.add_block(mb_rtu_s, modbus.REGISTERS, 0, 32, registers)
+registers:clear()
+
+-- 创建线圈数据区
+ciols = zbuff.create(1)
+modbus.add_block(mb_rtu_s, modbus.CIOLS, 0, 32, ciols)
+ciols:clear()
+
+
+-- 启动modbus从站
+modbus.slave_start(mb_rtu_s)
+
+
+local counter = 0
+-- 修改和读取modbus值
+function modify_data()
+    counter = counter + 1  
+    -- 写入寄存器数据 (16位无符号整数)
+    registers:seek(0)
+    for i=0,31 do
+        registers:writeU16((counter + i) % 65536)  -- 写入递增数字,限制在0-65535
+    end  
+    -- 写入线圈数据 (1位布尔值)
+    ciols:seek(0)
+    for i=0,31 do
+        ciols:writeU8((counter + i) % 2)  -- 交替写入0和1
+    end
+  
+    -- 读取并打印部分数据用于调试
+    registers:seek(0)
+    ciols:seek(0)
+    log.info("registers:", registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16())
+    log.info("ciols    :", ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8())
+end
+sys.timerLoopStart(modify_data,1000)
+
+-- -- 测试停止modbus从站,将在从站启动两分钟后关闭
+-- sys.timerStart(function()
+--     modbus.slave_stop(mb_rtu_s)
+--     log.info("Modbus", "2分钟时间到,停止Modbus从站")
+-- end, 2 * 60 * 1000)  -- 2分钟(单位:毫秒)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 59 - 0
module/Air780EHM/demo/modbus/tcp/master_tcp/lan.lua

@@ -0,0 +1,59 @@
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+sysplus = require("sysplus")
+
+dhcps = require "dhcpsrv"
+dnsproxy = require "dnsproxy"
+
+sys.taskInit(function ()
+    -- sys.wait(3000)
+    local result = spi.setup(
+        0,--串口id
+        nil,
+        0,--CPHA
+        0,--CPOL
+        8,--数据宽度
+        25600000--,--频率
+        -- spi.MSB,--高低位顺序    可选,默认高位在前
+        -- spi.master,--主模式     可选,默认主
+        -- spi.full--全双工       可选,默认全双工
+    )
+    log.info("main", "open",result)
+    if result ~= 0 then--返回值为0,表示打开成功
+        log.info("main", "spi open error",result)
+        return
+    end
+
+    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spiid=0,cs=8})
+    sys.wait(3000)
+    local ipv4,mark, gw = netdrv.ipv4(socket.LWIP_ETH, "192.168.4.1", "255.255.255.0", "192.168.4.1")
+    log.info("ipv4", ipv4,mark, gw)
+    while netdrv.link(socket.LWIP_ETH) ~= true do
+        sys.wait(100)
+    end
+    dhcps.create({adapter=socket.LWIP_ETH})
+
+
+    -- dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_GP)
+    -- netdrv.napt(socket.LWIP_GP)
+
+
+    dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_ETH)
+    netdrv.napt(socket.LWIP_ETH)
+
+    -- netdrv.dhcp(socket.LWIP_ETH, true)
+end)
+
+
+sys.taskInit(function()
+    -- sys.waitUntil("IP_READY")
+    while 1 do
+        sys.wait(300000)
+        -- log.info("http", http.request("GET", "http://httpbin.air32.cn/bytes/4096", nil, nil, {adapter=socket.LWIP_ETH}).wait())
+        log.info("lua", rtos.meminfo())
+        log.info("sys", rtos.meminfo("sys"))
+        -- log.info("psram", rtos.meminfo("psram"))
+    end
+end)
+
+

+ 161 - 0
module/Air780EHM/demo/modbus/tcp/master_tcp/main.lua

@@ -0,0 +1,161 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "modbus_master_tcp"
+VERSION = "1.0.0"
+log.style(1)
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+_G.sysplus = require("sysplus")
+
+--初始化网络
+log.info("ch390", "打开LDO供电")
+gpio.setup(20, 1)  --打开lan供电
+
+mcu.hardfault(0) -- 死机后停机,一般用于调试状态
+require "lan"
+
+
+-- 创建主站设备,TCP模式
+-- 设置连接方式为socket.LWIP_ETH
+-- 设置通讯间隔时间,主站将按每隔 设置时间 的频率向从站问询数据(默认100ms),当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
+-- 设置通讯超时时间和消息发送超时重发次数,当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送,默认1)
+-- 设置断线重连时间间隔,当从站与主站断连后,主站将在设置时间内重新连接从站(默认5000ms)
+mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH,3000,1,5000)
+
+
+-- 为主站添加从站,从站ID为1,ip地址为 192.168.0.104,端口号为 6000,最多可添加247个从站
+mb_slave1 = modbus.add_slave(mb_tcp, 1, "192.168.4.100", 6000)
+-- 为主站添加从站,从站ID为2,ip地址为 192.168.0.104,端口号为 6001
+-- mb_slave2 = modbus.add_slave(mb_tcp, 2, "192.168.4.100", 6001)
+
+
+-- 为从站1创建数据存储区,并创建通讯消息,默认为自动loop模式
+slave1_msg1_buf = zbuff.create(1)
+mb_slave1_msg1 = modbus.create_msg(mb_tcp, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf)
+slave1_msg1_buf:clear()
+
+-- -- 为从站1创建数据存储区,并创建通讯消息,如需要使用手动模式,须在这里设置为手动模式
+-- slave1_msg1_buf = zbuff.create(1)
+-- mb_slave1_msg1 = modbus.create_msg(mb_tcp, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf,1,modbus.EXEC)
+-- slave1_msg1_buf:clear()
+
+-- 为从站2创建数据存储区,并创建通讯消息
+-- slave2_msg1_buf = zbuff.create(1)
+-- mb_slave2_msg1 = modbus.create_msg(mb_tcp, mb_slave2, modbus.REGISTERS, modbus.WRITE, 0, 10, slave2_msg1_buf)
+-- slave2_msg1_buf:clear()
+
+
+-- 启动Modubs设备
+modbus.master_start(mb_tcp)
+log.info("start modbus master")
+
+-- -- 设置通讯间隔时间,设置后主站将按每隔 设置时间 的频率向从站问询数据,当添加了多个从站后,主站向每个从站问询的时间间隔将叠加
+-- modbus.set_comm_interval_time(mb_tcp,3000)
+
+
+-- -- 设置通讯超时时间,当主站未在 设置的时间 内接收到从站数据,将向从站再次发送问询(问询次数按设置的 消息超时重发次数 发送)
+-- modbus.set_comm_timeout(mb_tcp, 3000)
+
+
+-- -- 设置消息发送失败、超时重发次数,如果主站在设置超时时间内未接收到数据,将按设置次数问询数据
+-- modbus.set_comm_resend_count(mb_tcp,1)
+
+
+-- 设置断线重连时间间隔,若从站与主站断连,主站将在设置时间内重新连接从站
+-- modbus.set_comm_reconnection_time(mb_tcp, 5000)
+
+
+-- -- 设置消息通讯周期,搭配modbus.create_master/modbus.set_comm_interval_time(mb_tcp,3000)设置通讯时间使用,若设置通讯周期为2次,将在2倍的通讯时间后向从站问询数据
+-- modbus.set_msg_comm_period(mb_slave1_msg1, 2)
+
+
+-- 获取所有从站状态,如果所有从站状态为正常,返回true,其他情况返回false,将在每隔5秒的时间获取所有从站状态,并在日志中打印状态(仅方便调试使用,量产时可删除)
+sys.timerLoopStart(function()
+    local status = modbus.get_all_slave_state(mb_tcp)
+    log.info("modbus", status)
+end, 5000)
+
+
+-- 获取从站1的状态,每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
+sys.timerLoopStart(function()
+    local status = modbus.get_slave_state(mb_slave1)
+    log.info("modbus1", status)
+end, 5000)
+
+-- -- 获取从站2的状态,每隔5秒获取从站状态并在日志打印出来(仅方便调试使用,量产时可删除)
+-- sys.timerLoopStart(function()
+--     local status = modbus.get_slave_state(mb_slave2)
+--     log.info("modbus2", status)
+-- end, 5000)
+
+
+-- -- 每隔5秒执行一次 mb_slave1_msg1 消息,使用modbus.exec(master_handler, msg_handler)接口须先在modbus.set_msg_comm_period(msg_handler, comm_period)接口中设置为手动模式;成功返回true,其他情况返回false
+-- sys.timerLoopStart(function()
+--     local status=modbus.exec(mb_tcp, mb_slave1_msg1)
+--     log.info("msg",status)
+-- end,5000)
+
+
+-- -- 测试删除一个从站对象,并删除与之相关的通讯消息句柄。需在主站停止时(modbus.master_stop)执行该操作,否则无效。
+-- -- 将在3分钟后删除从站2(主站已关闭),删除与之相关的通讯消息句柄,并在5秒后重启主站,可以观察从站是否删除成功。
+-- sys.timerStart(function()
+--     local status = modbus.remove_slave(mb_tcp, mb_slave2)
+--     log.info("modbus", "slave2 remove after 3 minutes")
+--     log.info("remove", status)
+    
+--     -- 移除从站后,5秒后重新启动Modbus主站
+--     sys.timerStart(function()
+--         modbus.master_start(mb_tcp)
+--         log.info("modbus", "Modbus master restarted after slave removal")
+--     end, 5000)
+-- end, 180000) 
+
+
+-- 获取从站1的状态,每1秒获取一次数据并转换为JSON
+sys.timerLoopStart(function()
+    -- 检查从站状态
+    local status = modbus.get_slave_state(mb_slave1)
+    if status == 0 then  -- 0表示正常
+        -- 读取缓冲区数据
+        slave1_msg1_buf:seek(0)  -- 重置指针到起始位置
+        
+        -- 读取4个寄存器的值(每个寄存器2字节)
+        local reg1 = slave1_msg1_buf:readU16()
+        local reg2 = slave1_msg1_buf:readU16()
+        local reg3 = slave1_msg1_buf:readU16()
+        local reg4 = slave1_msg1_buf:readU16()
+        
+        -- 创建数据表
+        local data = {
+            addr = 1,  -- 从站地址
+            fun = 3,   -- 功能码03
+            reg1 = reg1 / 10,  -- 假设原始数据需要除以10得到实际值
+            reg2 = reg2 / 10,
+            reg3 = reg3 / 10,
+            reg4 = reg4 / 10,
+            timestamp = os.time()  -- 添加时间戳
+        }
+        
+        -- 转换为JSON
+        local json_str = json.encode(data)
+        log.info("Modbus数据转JSON:", json_str)
+
+    else
+        log.warn("从站1状态异常:", status)
+    end
+end, 1000)
+
+
+
+-- -- 将在主站开启2分钟后停止modbus主站
+-- sys.timerStart(function()
+--     modbus.master_stop(mb_tcp)
+--     log.info("modbus", "Modbus stopped after 2 minutes")
+-- end, 120000) 
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 60 - 0
module/Air780EHM/demo/modbus/tcp/slave_tcp/lan.lua

@@ -0,0 +1,60 @@
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+sysplus = require("sysplus")
+
+dhcps = require "dhcpsrv"
+dnsproxy = require "dnsproxy"
+
+sys.taskInit(function ()
+    -- sys.wait(3000)
+    local result = spi.setup(
+        0,--串口id
+        nil,
+        0,--CPHA
+        0,--CPOL
+        8,--数据宽度
+        25600000--,--频率
+        -- spi.MSB,--高低位顺序    可选,默认高位在前
+        -- spi.master,--主模式     可选,默认主
+        -- spi.full--全双工       可选,默认全双工
+    )
+    log.info("main", "open",result)
+    if result ~= 0 then--返回值为0,表示打开成功
+        log.info("main", "spi open error",result)
+        return
+    end
+
+    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spiid=0,cs=8})
+    sys.wait(3000)
+    local ipv4,mark, gw = netdrv.ipv4(socket.LWIP_ETH, "192.168.4.1", "255.255.255.0", "192.168.4.1")
+    log.info("ipv4", ipv4,mark, gw)
+    while netdrv.link(socket.LWIP_ETH) ~= true do
+        sys.wait(100)
+    end
+    dhcps.create({adapter=socket.LWIP_ETH})
+
+
+    -- dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_GP)
+    -- netdrv.napt(socket.LWIP_GP)
+
+
+    dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_ETH)
+    netdrv.napt(socket.LWIP_ETH)
+
+    -- netdrv.dhcp(socket.LWIP_ETH, true)
+end)
+
+
+sys.taskInit(function()
+    sys.waitUntil("IP_READY")
+    while 1 do
+        sys.wait(300000)
+        -- log.info("http", http.request("GET", "http://httpbin.air32.cn/bytes/4096", nil, nil, {adapter=socket.LWIP_ETH}).wait())
+        log.info("lua", rtos.meminfo())
+        log.info("sys", rtos.meminfo("sys"))
+        -- log.info("psram", rtos.meminfo("psram"))
+    end
+end)
+
+

+ 69 - 0
module/Air780EHM/demo/modbus/tcp/slave_tcp/main.lua

@@ -0,0 +1,69 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "modbus_slave_tcp"
+VERSION = "1.0.0"
+log.style(1)
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+_G.sysplus = require("sysplus")
+
+log.info("ch390", "打开LDO供电")
+gpio.setup(20, 1)  --打开lan供电
+require "lan"
+
+-- 创建从站设备,可选择RTU、ASCII、TCP,此demo仅用作测试TCP。设置该从站端口号为6000,网卡适配器序列号为socket.LWIP_ETH。
+local slave_id = 1
+mb_tcp_s = modbus.create_slave(modbus.MODBUS_TCP, slave_id, 6000, socket.LWIP_ETH)
+
+
+-- 创建寄存器数据区
+registers = zbuff.create(1)
+modbus.add_block(mb_tcp_s, modbus.REGISTERS, 0, 32, registers)
+registers:clear()
+-- 创建线圈数据区
+ciols = zbuff.create(1)
+modbus.add_block(mb_tcp_s, modbus.CIOLS, 0, 32, ciols)
+ciols:clear()
+
+
+-- 启动modbus从站
+modbus.slave_start(mb_tcp_s)
+log.info("start modbus slave")
+
+
+local counter = 0
+-- 修改和读取modbus值
+function modify_data()
+    counter = counter + 1
+    -- 写入寄存器数据 (16位无符号整数)
+    registers:seek(0)
+    for i=0,31 do
+        registers:writeU16((counter + i) % 65536)  -- 写入递增数字,限制在0-65535
+    end
+    -- 写入线圈数据 (1位布尔值)
+    ciols:seek(0)
+    for i=0,31 do
+        ciols:writeU8((counter + i) % 2)  -- 交替写入0和1
+    end
+    
+    -- 读取并打印部分数据用于调试
+    registers:seek(0)
+    ciols:seek(0)
+    log.info("registers:", registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16())
+    log.info("ciols    :", ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8())
+end
+sys.timerLoopStart(modify_data,1000)
+
+
+-- -- 测试停止modbus从站,从站将在开启两分钟后关闭
+-- sys.timerStart(function()
+--     modbus.slave_stop(mb_rtu_s)
+--     log.info("Modbus", "2分钟时间到,停止Modbus从站")
+-- end, 120000)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

BIN
module/Air780EHM/demo/modbus/test_core/LuatOS-SoC_V2007_Air780EPM_TEMP_20250610_150400.soc


+ 30 - 0
module/Air780EHM/demo/mqtt/main.lua

@@ -0,0 +1,30 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "mqttdemo"
+VERSION = "1.0.0"
+
+--[[
+本demo需要mqtt库, 大部分能联网的设备都具有这个库
+mqtt也是内置库, 无需require
+]]
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+require "single_mqtt"       -- MQTT单链接
+
+-- require "multilink_mqtt"    -- MQTT多链接
+
+-- require "ssl_mqtt"             -- MQTTS SSL链接
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 226 - 0
module/Air780EHM/demo/mqtt/multilink_mqtt.lua

@@ -0,0 +1,226 @@
+-- 自动低功耗, 轻休眠模式
+-- Air780E、Air780EP支持uart唤醒和网络数据下发唤醒, 但需要断开USB,或者pm.power(pm.USB, false) 但这样也看不到日志了
+-- pm.request(pm.LIGHT)
+
+--根据自己的服务器修改以下参数
+local mqtt_host = "lbsmqtt.airm2m.com"
+local mqtt_port = 1884
+local mqtt_isssl = false
+local client1_id = "abc"
+local client2_id = "abc2"
+local user_name = "user"
+local password = "password"
+local device_id = mobile.imei()
+
+local mqttc1 = nil
+local pub_topic_client = "/luatos/pub/client1/"
+local sub_topic_client = "/luatos/sub/client1/"
+-- local pub_topic2_client = "/luatos/2"
+-- local pub_topic3_client = "/luatos/3"
+
+
+local mqttc2 = nil
+local pub_topic_client2 = "/luatos/pub/client2/"
+local sub_topic_client2 = "/luatos/sub/client2/"
+-- local pub_topic2_client2 = "/luatos/2"
+-- local pub_topic3_client2 = "/luatos/3"
+-- 统一联网函数
+sys.taskInit(function()
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready")
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    
+    local ret= sys.waitUntil("net_ready")
+    -- 下面的是mqtt的参数均可自行修改
+    pub_topic_client = pub_topic_client .. device_id
+    sub_topic_client = sub_topic_client .. device_id
+
+    -- 打印一下上报(pub)和下发(sub)的topic名称
+    -- 上报: 设备 ---> 服务器
+    -- 下发: 设备 <--- 服务器
+    -- 可使用mqtt.x等客户端进行调试
+    log.info("mqtt", "pub", pub_topic_client)
+    log.info("mqtt", "sub", sub_topic_client)
+
+    -- 打印一下支持的加密套件, 通常来说, 固件已包含常见的99%的加密套件
+    -- if crypto.cipher_suites then
+    --     log.info("cipher", "suites", json.encode(crypto.cipher_suites()))
+    -- end
+    if mqtt == nil then
+        while 1 do
+            sys.wait(1000)
+            log.info("bsp", "本bsp未适配mqtt库, 请查证")
+        end
+    end
+
+    -------------------------------------
+    -------- MQTT 演示代码 --------------
+    -------------------------------------
+
+    mqttc1 = mqtt.create(nil, mqtt_host, mqtt_port, mqtt_isssl)
+
+    mqttc1:auth(client1_id,user_name,password) -- client_id必填,其余选填
+    -- mqttc1:keepalive(240) -- 默认值240s
+    mqttc1:autoreconn(true, 3000) -- 自动重连机制
+
+    mqttc1:on(function(mqtt_client, event, data, payload)
+        -- 用户自定义代码
+        log.info("mqtt", "event", event, mqtt_client, data, payload)
+        if event == "conack" then
+            -- 联上了
+            sys.publish("mqtt_conack")
+            mqtt_client:subscribe(sub_topic_client)--单主题订阅
+            -- mqtt_client:subscribe({[topic1]=1,[topic2]=1,[topic3]=1})--多主题订阅
+        elseif event == "recv" then
+            log.info("mqtt", "downlink", "topic", data, "payload", payload)
+            sys.publish("mqtt_payload", data, payload)
+        elseif event == "sent" then
+            -- log.info("mqtt", "sent", "pkgid", data)
+        -- elseif event == "disconnect" then
+            -- 非自动重连时,按需重启mqttc
+            -- mqtt_client:connect()
+        end
+    end)
+
+    -- mqttc自动处理重连, 除非自行关闭
+    mqttc1:connect()
+	sys.waitUntil("mqtt_conack")
+    while true do
+        -- 演示等待其他task发送过来的上报信息
+        local ret, topic, data, qos = sys.waitUntil("mqtt_pub", 300000)
+        if ret then
+            -- 提供关闭本while循环的途径, 不需要可以注释掉
+            if topic == "close" then break end
+            mqttc1:publish(topic, data, qos)
+        end
+        -- 如果没有其他task上报, 可以写个空等待
+        --sys.wait(60000000)
+    end
+    mqttc1:close()
+    mqttc1 = nil
+end)
+
+-- 这里演示在另一个task里上报数据, 会定时上报数据,不需要就注释掉
+sys.taskInit(function()
+    sys.wait(3000)
+	local data = "123,"
+	local qos = 1 -- QOS0不带puback, QOS1是带puback的
+    while true do
+        sys.wait(3000)
+        if mqttc1 and mqttc1:ready() then
+            local pkgid = mqttc1:publish(pub_topic_client, data .. os.date(), qos)
+            -- local pkgid = mqttc1:publish(topic2, data, qos)
+            -- local pkgid = mqttc1:publish(topic3, data, qos)
+        end
+    end
+end)
+
+
+-- mqtt多链接示例
+sys.taskInit(function()
+    -- 等待联网
+    local ret= sys.waitUntil("net_ready")
+    -- 下面的是mqtt的参数均可自行修改
+    client2_id = device_id.."2"
+    pub_topic_client2 = pub_topic_client2 .. device_id
+    sub_topic_client2 = sub_topic_client2 .. device_id
+
+    -- 打印一下上报(pub)和下发(sub)的topic名称
+    -- 上报: 设备 ---> 服务器
+    -- 下发: 设备 <--- 服务器
+    -- 可使用mqtt.x等客户端进行调试
+    log.info("mqtt", "pub", pub_topic_client2)
+    log.info("mqtt", "sub", sub_topic_client2)
+
+    -- 打印一下支持的加密套件, 通常来说, 固件已包含常见的99%的加密套件
+    -- if crypto.cipher_suites then
+    --     log.info("cipher", "suites", json.encode(crypto.cipher_suites()))
+    -- end
+    if mqtt == nil then
+        while 1 do
+            sys.wait(1000)
+            log.info("bsp", "本bsp未适配mqtt库, 请查证")
+        end
+    end
+
+    -------------------------------------
+    -------- MQTT 演示代码 --------------
+    -------------------------------------
+
+    mqttc2 = mqtt.create(nil, mqtt_host, mqtt_port, mqtt_isssl)
+
+    mqttc2:auth(client2_id,user_name,password) -- client_id必填,其余选填
+    -- mqttc2:keepalive(240) -- 默认值240s
+    mqttc2:autoreconn(true, 3000) -- 自动重连机制
+
+    mqttc2:on(function(mqtt_client, event, data, payload)
+        -- 用户自定义代码
+        log.info("mqtt", "event", event, mqtt_client, data, payload)
+        if event == "conack" then
+            -- 联上了
+            sys.publish("mqtt_conack")
+            mqtt_client:subscribe(sub_topic_client2)--单主题订阅
+            -- mqtt_client:subscribe({[topic1]=1,[topic2]=1,[topic3]=1})--多主题订阅
+        elseif event == "recv" then
+            log.info("mqtt", "downlink", "topic", data, "payload", payload)
+            sys.publish("mqtt_payload", data, payload)
+        elseif event == "sent" then
+            log.info("mqtt", "sent", "pkgid", data)
+        elseif event == "disconnect" then
+            -- 非自动重连时,按需重启mqttc
+            mqtt_client:connect()
+        end
+    end)
+
+    -- mqttc自动处理重连, 除非自行关闭
+    mqttc2:connect()
+	sys.waitUntil("mqtt_conack")
+    while true do
+        -- 演示等待其他task发送过来的上报信息
+        local ret, topic, data, qos = sys.waitUntil("mqtt_pub", 300000)
+        if ret then
+            -- 提供关闭本while循环的途径, 不需要可以注释掉
+            if topic == "close" then break end
+            mqttc2:publish(topic, data, qos)
+        end
+        -- 如果没有其他task上报, 可以写个空等待
+        --sys.wait(60000000)
+    end
+    mqttc2:close()
+    mqttc2 = nil
+end)
+
+
+-- 这里演示在另一个task里上报数据, 会定时上报数据,不需要就注释掉
+sys.taskInit(function()
+    sys.wait(3000)
+	local data = "123,"
+	local qos = 1 -- QOS0不带puback, QOS1是带puback的
+    while true do
+        sys.wait(3000)
+        if mqttc2 and mqttc2:ready() then
+            local pkgid = mqttc2:publish(pub_topic_client2, data .. os.date(), qos)
+            -- local pkgid = mqttc2:publish(topic2, data, qos)
+            -- local pkgid = mqttc2:publish(topic3, data, qos)
+        end
+    end
+end)
+
+
+sys.taskInit(function ()
+    while true do
+        sys.wait(3000)
+        log.info("lua", rtos.meminfo())
+        log.info("sys", rtos.meminfo("sys"))
+    end
+end)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 177 - 0
module/Air780EHM/demo/mqtt/single_mqtt.lua

@@ -0,0 +1,177 @@
+-- 自动低功耗, 轻休眠模式
+-- Air780E支持uart唤醒和网络数据下发唤醒, 但需要断开USB,或者pm.power(pm.USB, false) 但这样也看不到日志了
+-- pm.request(pm.LIGHT)
+
+--根据自己的服务器修改以下参数
+local mqtt_host = "lbsmqtt.airm2m.com"
+local mqtt_port = 1884
+local mqtt_isssl = false
+local client_id = "abc"
+local user_name = "user"
+local password = "password"
+
+local pub_topic = "/luatos/pub/" .. (mcu.unique_id():toHex())
+local sub_topic = "/luatos/sub/" .. (mcu.unique_id():toHex())
+-- local topic2 = "/luatos/2"
+-- local topic3 = "/luatos/3"
+
+local mqttc = nil
+
+-- 统一联网函数
+sys.taskInit(function()
+    local device_id = mcu.unique_id():toHex()
+    -----------------------------
+    -- 统一联网函数, 可自行删减
+    ----------------------------
+    if wlan and wlan.connect then
+        -- wifi 联网, ESP32系列均支持
+        local ssid = "luatos1234"
+        local password = "12341234"
+        log.info("wifi", ssid, password)
+        -- TODO 改成自动配网
+        -- LED = gpio.setup(12, 0, gpio.PULLUP)
+        wlan.init()
+        wlan.setMode(wlan.STATION) -- 默认也是这个模式,不调用也可以
+        device_id = wlan.getMac()
+        wlan.connect(ssid, password, 1)
+    elseif mobile then
+        -- Air780E/Air600E系列
+        --mobile.simid(2) -- 自动切换SIM卡
+        -- LED = gpio.setup(27, 0, gpio.PULLUP)
+        device_id = mobile.imei()
+    elseif w5500 then
+        -- w5500 以太网, 当前仅Air105支持
+        w5500.init(spi.HSPI_0, 24000000, pin.PC14, pin.PC01, pin.PC00)
+        w5500.config() --默认是DHCP模式
+        w5500.bind(socket.ETH0)
+        -- LED = gpio.setup(62, 0, gpio.PULLUP)
+    elseif socket or mqtt then
+        -- 适配的socket库也OK
+        -- 没有其他操作, 单纯给个注释说明
+    else
+        -- 其他不认识的bsp, 循环提示一下吧
+        while 1 do
+            sys.wait(1000)
+            log.info("bsp", "本bsp可能未适配网络层, 请查证")
+        end
+    end
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready", device_id)
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local ret, device_id = sys.waitUntil("net_ready")
+    -- 下面的是mqtt的参数均可自行修改
+    client_id = device_id
+    pub_topic = "/luatos/pub/" .. device_id
+    sub_topic = "/luatos/sub/" .. device_id
+
+    -- 打印一下上报(pub)和下发(sub)的topic名称
+    -- 上报: 设备 ---> 服务器
+    -- 下发: 设备 <--- 服务器
+    -- 可使用mqtt.x等客户端进行调试
+    log.info("mqtt", "pub", pub_topic)
+    log.info("mqtt", "sub", sub_topic)
+
+    -- 打印一下支持的加密套件, 通常来说, 固件已包含常见的99%的加密套件
+    -- if crypto.cipher_suites then
+    --     log.info("cipher", "suites", json.encode(crypto.cipher_suites()))
+    -- end
+    if mqtt == nil then
+        while 1 do
+            sys.wait(1000)
+            log.info("bsp", "本bsp未适配mqtt库, 请查证")
+        end
+    end
+
+    -------------------------------------
+    -------- MQTT 演示代码 --------------
+    -------------------------------------
+
+    mqttc = mqtt.create(nil, mqtt_host, mqtt_port, mqtt_isssl)
+
+    mqttc:auth(client_id,user_name,password) -- client_id必填,其余选填
+    -- mqttc:keepalive(240) -- 默认值240s
+    mqttc:autoreconn(true, 3000) -- 自动重连机制
+
+    mqttc:on(function(mqtt_client, event, data, payload)
+        -- 用户自定义代码
+        log.info("mqtt", "event", event, mqtt_client, data, payload)
+        if event == "conack" then
+            -- 联上了
+            sys.publish("mqtt_conack")
+            mqtt_client:subscribe(sub_topic)--单主题订阅
+            -- mqtt_client:subscribe({[topic1]=1,[topic2]=1,[topic3]=1})--多主题订阅
+        elseif event == "recv" then
+            log.info("mqtt", "downlink", "topic", data, "payload", payload)
+            sys.publish("mqtt_payload", data, payload)
+        elseif event == "sent" then
+            -- log.info("mqtt", "sent", "pkgid", data)
+        -- elseif event == "disconnect" then
+            -- 非自动重连时,按需重启mqttc
+            -- mqtt_client:connect()
+        end
+    end)
+
+    -- mqttc自动处理重连, 除非自行关闭
+    mqttc:connect()
+	sys.waitUntil("mqtt_conack")
+    while true do
+        -- 演示等待其他task发送过来的上报信息
+        local ret, topic, data, qos = sys.waitUntil("mqtt_pub", 300000)
+        if ret then
+            -- 提供关闭本while循环的途径, 不需要可以注释掉
+            if topic == "close" then break end
+            mqttc:publish(topic, data, qos)
+        end
+        -- 如果没有其他task上报, 可以写个空等待
+        --sys.wait(60000000)
+    end
+    mqttc:close()
+    mqttc = nil
+end)
+
+-- 这里演示在另一个task里上报数据, 会定时上报数据,不需要就注释掉
+sys.taskInit(function()
+    sys.wait(3000)
+	local data = "123,"
+	local qos = 1 -- QOS0不带puback, QOS1是带puback的
+    while true do
+        sys.wait(3000)
+        if mqttc and mqttc:ready() then
+            local pkgid = mqttc:publish(pub_topic, data .. os.date(), qos)
+            -- local pkgid = mqttc:publish(topic2, data, qos)
+            -- local pkgid = mqttc:publish(topic3, data, qos)
+        end
+    end
+end)
+
+-- 以下是演示与uart结合, 简单的mqtt-uart透传实现,不需要就注释掉
+local uart_id = 1
+uart.setup(uart_id, 9600)
+uart.on(uart_id, "receive", function(id, len)
+    local data = ""
+    while 1 do
+        local tmp = uart.read(uart_id)
+        if not tmp or #tmp == 0 then
+            break
+        end
+        data = data .. tmp
+    end
+    log.info("uart", "uart收到数据长度", #data)
+    sys.publish("mqtt_pub", pub_topic, data)
+end)
+sys.subscribe("mqtt_payload", function(topic, payload)
+    log.info("uart", "uart发送数据长度", #payload)
+    uart.write(1, payload)
+end)
+
+sys.taskInit(function ()
+    while true do
+        sys.wait(3000)
+        log.info("lua", rtos.meminfo())
+        log.info("sys", rtos.meminfo("sys"))
+    end
+end)

+ 311 - 0
module/Air780EHM/demo/netdrv/ch390/lan/dhcpsrv.lua

@@ -0,0 +1,311 @@
+
+local dhcpsrv = {}
+
+local udpsrv = require("udpsrv")
+
+local TAG = "dhcpsrv"
+
+----
+-- 参考地址
+-- https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol
+
+local function dhcp_decode(buff)
+    -- buff:seek(0)
+    local dst = {}
+    -- 开始解析dhcp
+    dst.op = buff[0]
+    dst.htype = buff[1]
+    dst.hlen = buff[2]
+    dst.hops = buff[3]
+    buff:seek(4)
+    dst.xid = buff:read(4)
+
+    _, dst.secs = buff:unpack(">H")
+    _, dst.flags = buff:unpack(">H")
+    dst.ciaddr = buff:read(4)
+    dst.yiaddr = buff:read(4)
+    dst.siaddr = buff:read(4)
+    dst.giaddr = buff:read(4)
+    dst.chaddr = buff:read(16)
+
+    -- 跳过192字节
+    buff:seek(192, zbuff.SEEK_CUR)
+
+    -- 解析magic
+    _, dst.magic = buff:unpack(">I")
+
+    -- 解析option
+    local opt = {}
+    while buff:len() > buff:used() do
+        local tag = buff:read(1):byte()
+        if tag ~= 0 then
+            local len = buff:read(1):byte()
+            if tag == 0xFF or len == 0 then
+                break
+            end
+            local data = buff:read(len)
+            if tag == 53 then
+                -- 53: DHCP Message Type
+                dst.msgtype = data:byte()
+            end
+            table.insert(opt, {tag, data})
+            -- log.info(TAG, "tag", tag, "data", data:toHex())
+        end
+    end
+    if dst.msgtype == nil then
+        return -- 没有解析到msgtype,直接返回
+    end
+    dst.opts = opt
+    return dst
+end
+
+local function dhcp_buff2ip(buff)
+    return string.format("%d.%d.%d.%d", buff:byte(1), buff:byte(2), buff:byte(3), buff:byte(4))
+end
+
+local function dhcp_print_pkg(pkg)
+    log.info(TAG, "XID",  pkg.xid:toHex())
+    log.info(TAG, "secs", pkg.secs)
+    log.info(TAG, "flags", pkg.flags)
+    log.info(TAG, "chaddr", pkg.chaddr:sub(1, pkg.hlen):toHex())
+    log.info(TAG, "yiaddr", dhcp_buff2ip(pkg.yiaddr))
+    log.info(TAG, "siaddr", dhcp_buff2ip(pkg.siaddr))
+    log.info(TAG, "giaddr", dhcp_buff2ip(pkg.giaddr))
+    log.info(TAG, "ciaddr", dhcp_buff2ip(pkg.ciaddr))
+    log.info(TAG, "magic", string.format("%08X", pkg.magic))
+    for _, opt in pairs(pkg.opts) do
+        if opt[1] == 53 then
+            log.info(TAG, "msgtype", opt[2]:byte())
+        elseif opt[1] == 60 then
+            log.info(TAG, "auth", opt[2])
+        elseif opt[1] == 57 then
+            log.info(TAG, "Maximum DHCP message size", opt[2]:byte() * 256 + opt[2]:byte(2))
+        elseif opt[1] == 61 then
+            log.info(TAG, "Client-identifier", opt[2]:toHex())
+        elseif opt[1] == 55 then
+            log.info(TAG, "Parameter request list", opt[2]:toHex())
+        elseif opt[1] == 12 then
+            log.info(TAG, "Host name", opt[2])
+        -- elseif opt[1] == 58 then
+        --     log.info(TAG, "Renewal (T1) time value", opt[2]:unpack(">I"))
+        end
+    end
+end
+
+local function dhcp_encode(pkg, buff)
+    -- 合成DHCP包
+    buff:seek(0)
+    buff[0] = pkg.op
+    buff[1] = pkg.htype
+    buff[2] = pkg.hlen
+    buff[3] = pkg.hops
+    buff:seek(4)
+    -- 写入XID
+    buff:write(pkg.xid)
+    -- 几个重要的参数
+    buff:pack(">H", pkg.secs)
+    buff:pack(">H", pkg.flags)
+    buff:write(pkg.ciaddr)
+    buff:write(pkg.yiaddr)
+    buff:write(pkg.siaddr)
+    buff:write(pkg.giaddr)
+    -- 写入MAC地址
+    buff:write(pkg.chaddr)
+    -- 跳过192字节
+    buff:seek(192, zbuff.SEEK_CUR)
+    -- 写入magic
+    buff:pack(">I", pkg.magic)
+    -- 写入option
+    for _, opt in pairs(pkg.opts) do
+        buff:write(opt[1])
+        buff:write(#opt[2])
+        buff:write(opt[2])
+    end
+    buff:write(0xFF, 0x00)
+end
+
+----
+
+local function dhcp_send_x(srv, pkg, client, msgtype)
+    local buff = zbuff.create(300)
+    pkg.op = 2
+    pkg.ciaddr = "\0\0\0\0"
+    pkg.yiaddr = string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], client.ip)
+    pkg.siaddr = string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])
+    pkg.giaddr = "\0\0\0\0"
+    pkg.secs = 0
+
+    pkg.opts = {} -- 复位option
+    table.insert(pkg.opts, {53, string.char(msgtype)})
+    table.insert(pkg.opts, {1, string.char(srv.opts.mark[1], srv.opts.mark[2], srv.opts.mark[3], srv.opts.mark[4])})
+    table.insert(pkg.opts, {3, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
+    table.insert(pkg.opts, {51, "\x00\x00\x1E\x00"}) -- 7200秒, 大概
+    table.insert(pkg.opts, {54, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
+    table.insert(pkg.opts, {6, string.char(223, 5, 5, 5)})
+    table.insert(pkg.opts, {6, string.char(119, 29, 29, 29)})
+    table.insert(pkg.opts, {6, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
+
+    dhcp_encode(pkg, buff)
+
+    local dst = "255.255.255.255"
+    if 4 == msgtype then
+        dst = string.format("%d.%d.%d.%d", srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], client.ip)
+    end
+    -- log.info(TAG, "发送", msgtype, dst, buff:query():toHex())
+    srv.udp:send(buff, dst, 68)
+end
+
+local function dhcp_send_offer(srv, pkg, client)
+    dhcp_send_x(srv, pkg, client, 2)
+end
+
+local function dhcp_send_ack(srv, pkg, client)
+    dhcp_send_x(srv, pkg, client, 5)
+end
+
+local function dhcp_send_nack(srv, pkg, client)
+    dhcp_send_x(srv, pkg, client, 6)
+end
+
+local function dhcp_handle_discover(srv, pkg)
+    local mac = pkg.chaddr:sub(1, pkg.hlen)
+    -- 看看是不是已经分配了ip
+    for _, client in pairs(srv.clients) do
+        if client.mac == mac then
+            log.info(TAG, "发现已经分配的mac地址, send offer")
+            dhcp_send_offer(srv, pkg, client)
+            return
+        end
+    end
+    -- TODO 清理已经过期的IP分配记录
+    -- 分配一个新的ip
+    if #srv.clients >= (srv.opts.ip_end - srv.opts.ip_start) then
+        log.info(TAG, "没有可分配的ip了")
+        return
+    end
+    local ip = nil
+    for i = srv.opts.ip_start, srv.opts.ip_end, 1 do
+        if srv.clients[i] == nil then
+            ip = i
+            break
+        end
+    end
+    if ip == nil then
+        log.info(TAG, "没有可分配的ip了")
+        return
+    end
+    log.info(TAG, "分配ip", mac:toHex(), string.format("%d.%d.%d.%d", srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], ip))
+    local client = {
+        mac = mac,
+        ip = ip,
+        tm = mcu.ticks() // mcu.hz(),
+        stat = 1
+    }
+    srv.clients[ip] = client
+    log.info(TAG, "send offer")
+    dhcp_send_offer(srv, pkg, client)
+end
+
+local function dhcp_handle_request(srv, pkg)
+    local mac = pkg.chaddr:sub(1, pkg.hlen)
+    -- 看看是不是已经分配了ip
+    for _, client in pairs(srv.clients) do
+        if client.mac == mac then
+            log.info(TAG, "request,发现已经分配的mac地址, send ack")
+            client.tm = mcu.ticks() // mcu.hz()
+            stat = 3
+            dhcp_send_ack(srv, pkg, client)
+            return
+        end
+    end
+    -- 没有找到, 那应该返回NACK
+    log.info(TAG, "request,没有分配的mac地址, send nack")
+    dhcp_send_nack(srv, pkg, {ip=pkg.yiaddr:byte(1)})
+end
+
+local function dhcp_pkg_handle(srv, pkg)
+    -- 进行基本的检查
+    if pkg.magic ~= 0x63825363 then
+        log.warn(TAG, "dhcp数据包的magic不对劲,忽略该数据包", pkg.magic)
+        return
+    end
+    if pkg.op ~= 1 then
+        log.info(TAG, "op不对,忽略该数据包", pkg.op)
+        return
+    end
+    if pkg.htype ~= 1 or pkg.hlen ~= 6 then
+        log.warn(TAG, "htype/hlen 不认识, 忽略该数据包")
+        return
+    end
+    -- 看看是不是能处理的类型, 当前只处理discover/request
+    if pkg.msgtype == 1 or pkg.msgtype == 3 then
+    else
+        log.warn(TAG, "msgtype不是discover/request, 忽略该数据包", pkg.msgtype)
+        return
+    end
+    -- 检查一下mac地址是否合法
+    local mac = pkg.chaddr:sub(1, pkg.hlen)
+    if mac == "\0\0\0\0\0\0" or mac == "\xFF\xFF\xFF\xFF\xFF\xFF" then
+        log.warn(TAG, "mac地址为空, 忽略该数据包")
+        return
+    end
+
+    -- 处理discover包
+    if pkg.msgtype == 1 then
+        log.info(TAG, "是discover包")
+        dhcp_handle_discover(srv, pkg)
+    elseif pkg.msgtype == 3 then
+        log.info(TAG, "是request包")
+        dhcp_handle_request(srv, pkg)
+    end
+    -- TODO 处理结束, 打印一下客户的列表?
+end
+
+local function dhcp_task(srv)
+    while 1 do
+        -- log.info("ulwip", "等待DHCP数据")
+        local result, data = sys.waitUntil(srv.udp_topic, 1000)
+        if result then
+            -- log.info("ulwip", "收到dhcp数据包", data:toHex())
+            -- 解析DHCP数据包
+            local pkg = dhcp_decode(zbuff.create(#data, data))
+            if pkg then
+                -- dhcp_print_pkg(pkg)
+                dhcp_pkg_handle(srv, pkg)
+            end
+        end
+    end
+end
+function dhcpsrv.create(opts)
+    local srv = {}
+    if not opts then
+        opts = {}
+    end
+    srv.udp_topic = "dhcpd_inc"
+    -- 补充参数
+    if not opts.mark then
+        opts.mark = {255, 255, 255, 0}
+    end
+    if not opts.gw then
+        opts.gw = {192, 168, 4, 1}
+    end
+    if not opts.dns then
+        opts.dns = opts.gw
+    end
+    if not opts.ip_start then
+        opts.ip_start = 100
+    end
+    if not opts.ip_end then
+        opts.ip_end = 200
+    end
+
+    srv.clients = {}
+    srv.opts = opts
+
+    srv.udp = udpsrv.create(67, srv.udp_topic, opts.adapter)
+    srv.task = sys.taskInit(dhcp_task, srv)
+    return srv
+end
+
+
+return dhcpsrv

+ 113 - 0
module/Air780EHM/demo/netdrv/ch390/lan/dnsproxy.lua

@@ -0,0 +1,113 @@
+--[[
+@module dnsproxy
+@summary DNS代理转发
+@version 1.0
+@date    2024.4.20
+@author  wendal
+@demo    socket
+@tag LUAT_USE_NETWORK
+@usage
+-- 具体用法请查阅demo
+]]
+
+local sys = require "sys"
+
+local dnsproxy = {}
+dnsproxy.map = {}
+dnsproxy.txid = 0x123
+dnsproxy.rxbuff = zbuff.create(1500)
+
+function dnsproxy.on_request(sc, event)
+    if event == socket.EVENT then
+        local rxbuff = dnsproxy.rxbuff
+        while 1 do
+            rxbuff:seek(0)
+            local succ, data_len, remote_ip, remote_port = socket.rx(sc, rxbuff)
+            if succ and data_len and data_len > 0 then
+                -- log.info("dnsproxy", "收到DNS查询数据", rxbuff:query():toHex())
+                if remote_ip and #remote_ip == 5 then
+                    local ip1,ip2,ip3,ip4 = remote_ip:byte(2),remote_ip:byte(3),remote_ip:byte(4),remote_ip:byte(5)
+                    remote_ip = string.format("%d.%d.%d.%d", ip1, ip2, ip3, ip4)
+                    local txid_request = rxbuff[0] + rxbuff[1] * 256
+                    local txid_map = dnsproxy.txid
+                    dnsproxy.txid = dnsproxy.txid + 1
+                    if dnsproxy.txid > 65000 then
+                        dnsproxy.txid = 0x123
+                    end
+                    table.insert(dnsproxy.map, {txid_request, txid_map, remote_ip, remote_port})
+                    rxbuff[0] = txid_map % 256
+                    rxbuff[1] = txid_map // 256
+                    socket.tx(dnsproxy.main_sc, rxbuff, "223.5.5.5", 53)
+                end
+            else
+                break
+            end
+        end
+    end
+end
+
+function dnsproxy.on_response(sc, event)
+    if event == socket.EVENT then
+        local rxbuff = dnsproxy.rxbuff
+        while 1 do
+            rxbuff:seek(0)
+            local succ, data_len = socket.rx(sc, rxbuff)
+            if succ and data_len and data_len > 0 then
+                if true then
+                    -- local ip1,ip2,ip3,ip4 = remote_ip:byte(2),remote_ip:byte(3),remote_ip:byte(4),remote_ip:byte(5)
+                    -- remote_ip = string.format("%d.%d.%d.%d", ip1, ip2, ip3, ip4)
+                    local txid_resp = rxbuff[0] + rxbuff[1] * 256
+                    local index = -1
+                    for i, mapit in pairs(dnsproxy.map) do
+                        if mapit[2] == txid_resp then
+                            local txid_request = mapit[1]
+                            local remote_ip = mapit[3]
+                            local remote_port = mapit[4]
+                            rxbuff[0] = txid_request % 256
+                            rxbuff[1] = txid_request // 256
+                            socket.tx(dnsproxy.sc, rxbuff, remote_ip, remote_port)
+                            index = i
+                            break
+                        end
+                    end
+                    if index > 0 then
+                        table.remove(dnsproxy.map, index)
+                    end
+                end
+            else
+                break
+            end
+        end
+    end
+end
+
+--[[
+创建UDP服务器
+@api dnsproxy.create(adapter, main_adapter)
+@int 监听的网络适配器id
+@int 网络适配编号, 默认为nil,可选
+@return table UDP服务的实体, 若创建失败会返回nil
+]]
+function dnsproxy.setup(adapter, main_adapter)
+    log.info("dnsproxy", adapter, main_adapter)
+    dnsproxy.adapter = adapter
+    dnsproxy.main_adapter = main_adapter
+    dnsproxy.sc = socket.create(dnsproxy.adapter, dnsproxy.on_request)
+    dnsproxy.main_sc = socket.create(dnsproxy.main_adapter, dnsproxy.on_response)
+    socket.config(dnsproxy.sc, 53, true)
+    socket.config(dnsproxy.main_sc, 1053, true)
+    dnsproxy.on_ip_ready()
+    return true
+end
+
+function dnsproxy.on_ip_ready()
+    socket.close(dnsproxy.sc)
+    socket.close(dnsproxy.main_sc)
+    log.info("dnsproxy", "开启DNS代理")
+    socket.connect(dnsproxy.sc, "255.255.255.255", 0)
+    socket.connect(dnsproxy.main_sc, "223.5.5.5", 53)
+end
+
+sys.subscribe("IP_READY", dnsproxy.on_ip_ready)
+
+return dnsproxy

+ 57 - 0
module/Air780EHM/demo/netdrv/ch390/lan/lan.lua

@@ -0,0 +1,57 @@
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+sysplus = require("sysplus")
+
+dhcps = require "dhcpsrv"
+dnsproxy = require "dnsproxy"
+
+sys.taskInit(function ()
+    -- sys.wait(3000)
+    local result = spi.setup(
+        0,--串口id
+        nil,
+        0,--CPHA
+        0,--CPOL
+        8,--数据宽度
+        51200000--,--频率
+        -- spi.MSB,--高低位顺序    可选,默认高位在前
+        -- spi.master,--主模式     可选,默认主
+        -- spi.full--全双工       可选,默认全双工
+    )
+    log.info("main", "open",result)
+    if result ~= 0 then--返回值为0,表示打开成功
+        log.info("main", "spi open error",result)
+        return
+    end
+
+    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spi=0,cs=8})
+    sys.wait(3000)
+    local ipv4,mark, gw = netdrv.ipv4(socket.LWIP_ETH, "192.168.4.1", "255.255.255.0", "192.168.4.1")
+    log.info("ipv4", ipv4,mark, gw)
+    while netdrv.link(socket.LWIP_ETH) ~= true do
+        sys.wait(100)
+    end
+    while netdrv.link(socket.LWIP_GP) ~= true do
+        sys.wait(100)
+    end
+    dhcps.create({adapter=socket.LWIP_ETH})
+    dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_GP)
+    netdrv.napt(socket.LWIP_GP)
+    if iperf then
+        log.info("启动iperf服务器端")
+        iperf.server(socket.LWIP_ETH)
+    end
+end)
+
+
+sys.taskInit(function()
+    -- sys.waitUntil("IP_READY")
+    while 1 do
+        sys.wait(300000)
+        -- log.info("http", http.request("GET", "http://httpbin.air32.cn/bytes/4096", nil, nil, {adapter=socket.LWIP_ETH}).wait())
+        log.info("lua", rtos.meminfo())
+        log.info("sys", rtos.meminfo("sys"))
+        -- log.info("psram", rtos.meminfo("psram"))
+    end
+end)

+ 21 - 0
module/Air780EHM/demo/netdrv/ch390/lan/main.lua

@@ -0,0 +1,21 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "ch390h"
+VERSION = "1.0.0"
+
+-- 使用合宙iot平台时需要这个参数
+PRODUCT_KEY = "xxx" -- 到 iot.openluat.com 创建项目,获取正确的项目id
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+sysplus = require("sysplus")
+
+log.info("ch390", "打开LDO供电")
+gpio.setup(20, 1)  --打开lan供电
+
+require "lan"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!
+

+ 21 - 0
module/Air780EHM/demo/netdrv/ch390/wan/main.lua

@@ -0,0 +1,21 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "ch390h"
+VERSION = "1.0.0"
+
+-- 使用合宙iot平台时需要这个参数
+PRODUCT_KEY = "xxx" -- 到 iot.openluat.com 创建项目,获取正确的项目id
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+sysplus = require("sysplus")
+
+log.info("ch390", "打开LDO供电")
+gpio.setup(20, 1)  --打开lan供电
+
+require "wan"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!
+

+ 38 - 0
module/Air780EHM/demo/netdrv/ch390/wan/wan.lua

@@ -0,0 +1,38 @@
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+sysplus = require("sysplus")
+
+sys.taskInit(function ()
+    -- sys.wait(3000)
+    local result = spi.setup(
+        0,--串口id
+        nil,
+        0,--CPHA
+        0,--CPOL
+        8,--数据宽度
+        25600000--,--频率
+        -- spi.MSB,--高低位顺序    可选,默认高位在前
+        -- spi.master,--主模式     可选,默认主
+        -- spi.full--全双工       可选,默认全双工
+    )
+    log.info("main", "open",result)
+    if result ~= 0 then--返回值为0,表示打开成功
+        log.info("main", "spi open error",result)
+        return
+    end
+
+    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spi=0,cs=8})
+    netdrv.dhcp(socket.LWIP_ETH, true)
+end)
+
+
+sys.taskInit(function()
+    -- sys.waitUntil("IP_READY")
+    while 1 do
+        sys.wait(6000)
+        log.info("http", http.request("GET", "http://httpbin.air32.cn/bytes/4096", nil, nil, {adapter=socket.LWIP_ETH}).wait())
+        log.info("lua", rtos.meminfo())
+        log.info("sys", rtos.meminfo("sys"))
+    end
+end)

+ 123 - 0
module/Air780EHM/demo/ntp/main.lua

@@ -0,0 +1,123 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "sntpdemo"
+VERSION = "1.0.0"
+
+--[[
+本demo需要mqtt库, 大部分能联网的设备都具有这个库
+mqtt也是内置库, 无需require
+]]
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用mqtt库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+-- 统一联网函数
+sys.taskInit(function()
+    local device_id = mcu.unique_id():toHex()
+    -----------------------------
+    -- 统一联网函数, 可自行删减
+    ----------------------------
+    if wlan and wlan.connect then
+        -- wifi 联网, ESP32系列均支持
+        local ssid = "luatos1234"
+        local password = "12341234"
+        log.info("wifi", ssid, password)
+        -- TODO 改成自动配网
+        -- LED = gpio.setup(12, 0, gpio.PULLUP)
+        wlan.init()
+        wlan.setMode(wlan.STATION) -- 默认也是这个模式,不调用也可以
+        device_id = wlan.getMac()
+        wlan.connect(ssid, password, 1)
+    elseif mobile then
+        -- Air780E/Air600E系列
+        --mobile.simid(2) -- 自动切换SIM卡
+        -- LED = gpio.setup(27, 0, gpio.PULLUP)
+        device_id = mobile.imei()
+    elseif w5500 then
+        -- w5500 以太网, 当前仅Air105支持
+        w5500.init(spi.HSPI_0, 24000000, pin.PC14, pin.PC01, pin.PC00)
+        w5500.config() --默认是DHCP模式
+        w5500.bind(socket.ETH0)
+        -- LED = gpio.setup(62, 0, gpio.PULLUP)
+    elseif socket then
+        -- 适配的socket库也OK
+        -- 没有其他操作, 单纯给个注释说明
+    else
+        -- 其他不认识的bsp, 循环提示一下吧
+        while 1 do
+            sys.wait(1000)
+            log.info("bsp", "本bsp可能未适配网络层, 请查证")
+        end
+    end
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    sys.publish("net_ready", device_id)
+end)
+
+sys.taskInit(function()
+    -- 等待联网
+    local ret, device_id = sys.waitUntil("net_ready")
+    sys.wait(1000)
+    -- 对于Cat.1模块, 移动/电信卡, 通常会下发基站时间,  那么sntp就不是必要的, 而联通卡通常不会下发, 就需要sntp了
+    -- 对应ESP32系列模块, 固件默认也会执行sntp, 所以手动调用sntp也是可选的
+    -- sntp内置了几个常用的ntp服务器, 也支持自选服务器
+    while 1 do
+        -- 使用内置的ntp服务器地址, 包括阿里ntp
+        log.info("开始执行SNTP")
+        socket.sntp()
+        -- 自定义ntp地址
+        -- socket.sntp("ntp.aliyun.com")
+        -- socket.sntp({"baidu.com", "abc.com", "ntp.air32.cn"})
+        -- 通常只需要几百毫秒就能成功
+        local ret = sys.waitUntil("NTP_UPDATE", 5000)
+        if ret then
+            -- 以下是获取/打印时间的演示,注意时区问题
+            log.info("sntp", "时间同步成功", "本地时间", os.date())
+            log.info("sntp", "时间同步成功", "UTC时间", os.date("!%c"))
+            log.info("sntp", "时间同步成功", "RTC时钟(UTC时间)", json.encode(rtc.get()))
+            -- os.time(rtc.get()) 需要 2023.07.21 之后的版本, 因为月份的命名差异mon/month
+            -- log.info("sntp", "时间同步成功", "utc时间戳", os.time(rtc.get()))
+            log.info("sntp", "时间同步成功", "本地时间戳", os.time())
+            local t = os.date("*t")
+            log.info("sntp", "时间同步成功", "本地时间os.date() json格式", json.encode(t))
+            log.info("sntp", "时间同步成功", "本地时间os.date(os.time())", os.time(t))
+            -- log.info("sntp", "时间同步成功", "本地时间", os.time())
+            -- 正常使用, 一小时一次, 已经足够了, 甚至1天一次也可以
+            -- sys.wait(3600000) 
+            -- 这里为了演示, 用5秒一次
+            sys.wait(5000)
+        else
+            log.info("sntp", "时间同步失败")
+            sys.wait(60000) -- 1分钟后重试
+        end
+
+        -- 时间戳, 精确到毫秒. 2023.11.15 新增
+        -- 注意, 至少成功完成2次sntp,该时间戳才比较准确
+        -- 如果仅完成了一次sntp, 时间戳比标准时间会慢一个网络延时的时长(10~500ms)不等
+        if socket.ntptm then
+            local tm = socket.ntptm()
+            log.info("tm数据", json.encode(tm))
+            log.info("时间戳", string.format("%u.%03d", tm.tsec, tm.tms))
+            sys.wait(5000)
+        end
+    end
+end)
+
+sys.subscribe("NTP_ERROR", function()
+    log.info("socket", "sntp error")
+    -- socket.sntp()
+end)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 9 - 0
module/Air780EHM/demo/onewire/onewire_multi_18b20_swich_read/main.lua

@@ -0,0 +1,9 @@
+PROJECT = "DS18B20_GPIO_SWITCH_READ_temperature"
+VERSION = "1.0.0"
+sys = require("sys")
+log.style(1)
+
+
+require"switch_read"
+
+sys.run()

+ 125 - 0
module/Air780EHM/demo/onewire/onewire_multi_18b20_swich_read/switch_read.lua

@@ -0,0 +1,125 @@
+
+pm.ioVol(pm.IOVOL_ALL_GPIO, 3300) -- 所有GPIO高电平输出3.3V(方便使用VDD_EXT给18B20供电)
+gpio.setup(2, 1) --GPIO2控制780EPM开发板V1.3版本camera电源打开和关闭
+gpio.setup(31, 1) -- GPIO31控制780EPM开发板V1.4版本camera电源打开和关闭
+
+local onewire_pin = 54 --18B20接的pin 54脚
+
+
+pins.setup(onewire_pin, "ONEWIRE") -- PAD54脚既是GPIO3也是cam_mclk 
+
+--读取当前pin脚上的18B20温度
+local function read_ds18b20(id)
+log.info("读取温度",id)
+    local tbuff = zbuff.create(10)
+    local succ, crc8c, range, t
+    local rbuff = zbuff.create(9)
+    tbuff:write(0x55)
+    tbuff:copy(nil, id)
+    tbuff:write(0xb8)
+  
+    tbuff[tbuff:used() - 1] = 0x44
+    succ = onewire.tx(0, tbuff, false, true, true)
+    if not succ then
+        return
+    end
+
+    succ = onewire.reset(0, true)
+    if not succ then
+        return
+    end
+    if onewire.bit(0) > 0 then
+        log.info("温度转换完成")
+    end
+    tbuff[tbuff:used() - 1] = 0xbe
+    succ = onewire.tx(0, tbuff, false, true, true)
+    if not succ then
+        return
+    end
+    succ, rx_data = onewire.rx(0, 9, nil, rbuff, false, false, false)
+    crc8c = crypto.crc8(rbuff:toStr(0, 8), 0x31, 0, true)
+    if crc8c == rbuff[8] then
+        range = (rbuff[4] >> 5) & 0x03
+        -- rbuff[0] = 0xF8
+        -- rbuff[1] = 0xFF
+        t = rbuff:query(0, 2, false, true)
+        t = t * (5000 >> range)
+        t = t / 10000
+        log.info("当前温度", t,"原始值为",mcu.x32(rbuff[8]))
+    else
+        log.info("RAM DATA CRC校验不对", mcu.x32(crc8c), mcu.x32(rbuff[8]))
+        return
+    end
+
+end
+
+--初始化当前pin脚上的18B20,并读取18B20的唯一识别ID
+local function test_ds18b20()
+    local succ, rx_data
+    local id = zbuff.create(8)
+    local crc8c
+    onewire.init(0) -- 初始化单总线
+    onewire.timing(0, false, 0, 500, 500, 15, 240, 70, 1, 15, 10, 2)
+    id:set() -- 清空id
+    succ, rx_data = onewire.rx(0, 8, 0x33, id, false, true, true)
+    if succ then
+        if id[0] == 0x28 then
+            crc8c = crypto.crc8(id:query(0, 7), 0x31, 0, true)
+            if crc8c == id[7] then
+                log.info("探测到DS18B20", "18B20对应唯一ID为", id:query(0, 7):toHex())
+                read_ds18b20(id)
+                -- log.info("DS18B20离线,重新探测")
+            else
+                log.info("ROM ID CRC校验不对", mcu.x32(crc8c), mcu.x32(id[7]))
+            end
+        else
+            log.info("ROM ID不正确", mcu.x32(id[0]))
+        end
+    else
+        log.info("没有检测到DS18B20")
+    end
+end
+
+local switchover_pin = gpio.PWR_KEY --选择powerkey作为切换18B20读数的信号脚
+
+gpio.debounce(switchover_pin, 100) --设置防抖
+
+
+--设置powerkey按下和抬起的功能(18B20温度的取值再54pin和56pin之间切换)
+--如果客户需要其他pin(22/54/56/78)则改动switchover_pin为用户需要的即可
+--注意,由于powerkey不能做单边沿触发,所以一次按下和抬起的动作,会触发两次切换pin
+--看效果的话,可以先一直按住powerkey几秒,再松开
+gpio.setup(switchover_pin, function()
+    log.info("Touch_pin", switchover_pin, "被触发")
+    log.info("当前单总线pad", onewire_pin)
+    if onewire_pin == 54 then
+        log.info("给PAD" .. onewire_pin .. "配置到GPIO3上去", pins.setup(onewire_pin, "GPIO3"))
+        log.info("给GPIO3设置为高电平输出模式",gpio.setup(3,1))--一定要执行gpio.setup,至于是哪种模式,用户根据自身需求
+        onewire_pin = 56
+    else
+        log.info("给PAD" .. onewire_pin .. "配置到GPIO7上去", pins.setup(onewire_pin, "GPIO7"))
+        log.info("给GPIO7设置为高电平输出模式",gpio.setup(7,1))
+
+        onewire_pin = 54
+    end
+    log.info("设置后单总线pad", onewire_pin)
+
+    onewire.deinit(0) -- 切换的时候一定要先关闭单总线,在读取的时候重新初始化
+
+    log.info("给" .. onewire_pin .. "配置到onewire上去", pins.setup(onewire_pin, "ONEWIRE"))
+    sys.publish("powerkey被按下")
+end, gpio.PULLUP, gpio.RISING)
+
+sys.taskInit(function()
+    while 1 do
+        -- sys.waitUntil("powerkey被按下")
+        log.info("1S后读取"..onewire_pin.."脚上的18B20")
+        sys.wait(1000)
+        log.info("开始读取18B20")
+        test_ds18b20()
+    end
+end)
+
+
+
+

+ 112 - 0
module/Air780EHM/demo/onewire/onewire_single_18b20/main.lua

@@ -0,0 +1,112 @@
+PROJECT = "onewiredemo"
+VERSION = "1.0.0"
+sys = require("sys")
+log.style(1)
+
+--[[
+接线说明:
+   DS18B20    Air780EPM
+1. GND    -> GND
+2. VDD    -> 3.3V
+3. DATA    -> GPIO2
+
+注意:
+1. 3.3v在老版本的开发板上没有引脚, 所以需要外接, 一定要确保共地
+2. ONEWIRE功能支持在4个引脚使用, 但硬件通道只有一个, 默认是GPIO2
+3. 如需切换到其他脚, 参考如下切换逻辑, 选其中一种
+
+mcu.altfun(mcu.ONEWIRE, 0, 17, 4, 0) -- GPIO2, 也就是默认值
+mcu.altfun(mcu.ONEWIRE, 0, 18, 4, 0) -- GPIO3
+mcu.altfun(mcu.ONEWIRE, 0, 22, 4, 0) -- GPIO7
+mcu.altfun(mcu.ONEWIRE, 0, 53, 4, 0) -- GPIO28
+]]
+
+local function read_ds18b20(id)
+    local tbuff = zbuff.create(10)
+    local succ,crc8c,range,t
+    local rbuff = zbuff.create(9)
+    --如果有多个DS18B20,需要带上ID
+    tbuff:write(0x55)
+    tbuff:copy(nil, id)
+    tbuff:write(0xb8)
+    --如果只有1个DS18B20,就用无ID方式
+    --tbuff:write(0xcc,0xb8)
+    while true do
+        tbuff[tbuff:used() - 1] = 0x44
+        succ = onewire.tx(0, tbuff, false, true, true)
+        if not succ then
+            return
+        end
+        while true do
+            succ = onewire.reset(0, true)
+            if not succ then
+                return
+            end
+            if onewire.bit(0) > 0 then
+                log.info("温度转换完成")
+                break
+            end
+            sys.wait(10)
+        end
+        tbuff[tbuff:used() - 1] = 0xbe
+        succ = onewire.tx(0, tbuff, false, true, true)
+        if not succ then
+            return
+        end
+        succ,rx_data = onewire.rx(0, 9, nil, rbuff, false, false, false)
+        crc8c = crypto.crc8(rbuff:toStr(0,8), 0x31, 0, true)
+        if crc8c == rbuff[8] then
+            range = (rbuff[4] >> 5) & 0x03
+            -- rbuff[0] = 0xF8
+            -- rbuff[1] = 0xFF
+            t = rbuff:query(0,2,false,true)
+            t = t * (5000 >> range)
+            t = t / 10000
+            log.info(t)
+        else
+            log.info("RAM DATA CRC校验不对",  mcu.x32(crc8c), mcu.x32(rbuff[8]))
+            return
+        end
+        sys.wait(500)
+    end
+end
+
+local function test_ds18b20()
+    local succ,rx_data
+    local id = zbuff.create(8)
+
+    local crc8c
+    onewire.init(0)
+    onewire.timing(0, false, 0, 500, 500, 15, 240, 70, 1, 15, 10, 2)
+    while true do
+        id:set() --清空id
+        succ,rx_data = onewire.rx(0, 8, 0x33, id, false, true, true)
+        if succ then
+            if id[0] == 0x28 then
+                crc8c = crypto.crc8(id:query(0,7), 0x31, 0, true)
+                if crc8c == id[7] then
+                    log.info("探测到DS18B20", id:query(0, 7):toHex())
+                    read_ds18b20(id)
+                    log.info("DS18B20离线,重新探测")
+                else
+                    log.info("ROM ID CRC校验不对",  mcu.x32(crc8c), mcu.x32(id[7]))
+                end
+            else
+                log.info("ROM ID不正确", mcu.x32(id[0]))
+            end
+        end
+        log.info("没有检测到DS18B20, 5秒后重试")
+        sys.wait(5000)
+
+    end
+    
+
+end
+
+if onewire then
+    sys.taskInit(test_ds18b20)
+else
+    log.info("no onewire")
+end
+
+sys.run()

+ 61 - 0
module/Air780EHM/demo/openai/deepseek/main.lua

@@ -0,0 +1,61 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "deepseek"
+VERSION = "1.0.0"
+
+sys = require "sys"
+require "sysplus"
+openai = require "openai"
+
+local uartid = 2
+
+local opts = {
+    apikey = "sk-e3d44d7c15a",--此处填用户自己去https://platform.deepseek.com/api_keys这里获取的key
+    apiurl = "https://api.deepseek.com",
+    model = "deepseek-chat",
+}
+uart.setup(uartid, 115200)
+
+-- 收取数据会触发回调, 这里的"receive" 是固定值
+uart.on(uartid, "receive", function(id, len)
+    local s = ""
+    repeat
+        s = uart.read(id, 1024)
+        if #s > 0 then -- #s 是取字符串的长度
+            log.info("uart", "receive", id, #s, s)
+            uart.write(uartid,
+                "消息发送成功,请等待回复,若串口60S没有回复,请检查luatools打印的日志\r\n")
+            sys.publish("uart_rx", s)
+        end
+    until s == ""
+end)
+
+sys.taskInit(function()
+    sys.waitUntil("IP_READY")
+    sys.wait(2000)
+
+    local chat = openai.completions(opts)
+    if chat then
+        uart.write(uartid, "大语言模型初始化完成,可以开始对话了\r\n")
+    else
+        uart.write(uartid, "大语言模型初始化失败,请检查代码\r\n")
+    end
+    -- -- uart交互演示
+    while 1 do
+        local re, data = sys.waitUntil("uart_rx")
+        if data then
+            local resp = chat:talk(data)
+            if resp and type(resp) == "table" then
+                log.info("deepseek回复", resp.content)
+                uart.write(uartid, resp.content)
+            else
+                local re_data = "大语言模型返回失败,错误原因:\r\n"
+                log.info(re_data,resp)
+                uart.write(uartid,re_data)
+                uart.write(uartid,resp)
+            end
+        end
+    end
+end)
+
+sys.run()
+

+ 160 - 0
module/Air780EHM/demo/openai/deepseek/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

+ 124 - 0
module/Air780EHM/demo/os/main.lua

@@ -0,0 +1,124 @@
+
+PROJECT = "os_test"
+VERSION = "1.0.0"
+local function test_os_remove()
+    log.info("os.remove", "测试开始")
+    -- 先创建一个测试文件
+    local testFile = "/test_remove.txt"
+    local f = io.open(testFile, "w")
+    if f then
+        f:write("test content")
+        f:close()
+        log.info("os.remove", "测试文件创建成功")
+    else
+        log.error("os.remove", "无法创建测试文件")
+        return
+    end
+
+    -- 测试删除文件
+    local result, err = os.remove(testFile)
+    if result then
+        log.info("os.remove", "文件删除成功")
+    else
+        log.error("os.remove", "文件删除失败", err)
+    end
+
+    -- 测试删除不存在的文件
+    result, err = os.remove("/nonexistent.txt")
+    if not result then
+        log.info("os.remove", "删除不存在文件返回预期结果", err)
+    end
+end
+
+-- 测试 os.rename() 文件重命名功能
+local function test_os_rename()
+    log.info("os.rename", "测试开始")
+    -- 创建源文件
+    local srcFile = "/test_rename_src.txt"
+    local f = io.open(srcFile, "w")
+    if f then
+        f:write("test content")
+        f:close()
+        log.info("os.rename", "源文件创建成功")
+    else
+        log.error("os.rename", "无法创建源文件")
+        return
+    end
+
+    -- 目标文件路径
+    local dstFile = "/test_rename_dst.txt"
+
+    -- 测试重命名
+    local result, err = os.rename(srcFile, dstFile)
+    if result then
+        log.info("os.rename", "文件重命名成功")
+
+        -- 验证新文件是否存在
+        if io.open(dstFile, "r") then
+            log.info("os.rename", "验证新文件存在")
+            os.remove(dstFile)
+        end
+    else
+        log.error("os.rename", "文件重命名失败", err)
+        os.remove(srcFile)
+    end
+
+    -- 测试重命名不存在的文件
+    result, err = os.rename("/nonexistent_src.txt", "/nonexistent_dst.txt")
+    if not result then
+        log.info("os.rename", "重命名不存在文件返回预期结果", err)
+    end
+end
+
+-- 测试 os.date() 和 os.time() 功能
+local function test_os_date_time()
+    log.info("os.date/time", "测试开始")
+
+    -- 获取当前时间戳
+    local currentTimestamp = os.time()
+    log.info("os.time", "当前时间戳", currentTimestamp)
+
+    -- 测试 os.date() 各种格式
+    log.info("os.date", "默认格式本地时间", os.date())
+    log.info("os.date", "默认格式UTC时间", os.date("!%c"))
+    log.info("os.date", "自定义格式本地时间", os.date("%Y-%m-%d %H:%M:%S"))
+    log.info("os.date", "自定义格式UTC时间", os.date("!%Y-%m-%d %H:%M:%S"))
+
+    -- 测试特定时间
+    local testTime = {year=2000, mon=1, day=1, hour=0, min=0, sec=0}
+    local testTimestamp = os.time(testTime)
+    log.info("os.time", "2000-01-01 00:00:00 时间戳", testTimestamp)
+    log.info("os.date", "格式化特定时间", os.date("!%Y-%m-%d %H:%M:%S", testTimestamp))
+
+    -- 测试获取时间表
+    local localTimeTable = os.date("*t")
+    log.info("os.date", "本地时间表", json.encode(localTimeTable))
+    local utcTimeTable = os.date("!*t")
+    log.info("os.date", "UTC时间表", json.encode(utcTimeTable))
+
+    -- 测试 os.difftime()
+    local time1 = os.time()
+    sys.wait(1000) -- 等待1秒
+    local time2 = os.time()
+    local diff = os.difftime(time2, time1)
+    log.info("os.difftime", "时间差(应该约等于1)", diff)
+end
+
+-- 主测试函数
+local function test_all()
+    log.info("OS接口测试", "===== 开始测试 =====")
+    test_os_remove()
+    test_os_rename()
+    test_os_date_time()
+    log.info("OS接口测试", "===== 测试完成 =====")
+end
+
+-- 启动测试
+sys.taskInit(function()
+    sys.wait(1000) -- 等待系统初始化
+    test_all()
+end)
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 49 - 0
module/Air780EHM/demo/pack/main.lua

@@ -0,0 +1,49 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "pack"
+VERSION = "2.0.0"
+
+--[[
+本demo演示 string字符串的基本操作
+1. lua的字符串是带长度, 这意味着, 它不依赖0x00作为结束字符串, 可以包含任意数据
+2. lua的字符串是不可变的, 就不能直接修改字符串的一个字符, 修改字符会返回一个新的字符串
+]]
+
+-- sys库是标配
+_G.sys = require("sys")
+sysplus = require("sysplus")
+
+-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
+if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
+    pm.power(pm.PWK_MODE, false)
+end
+
+sys.taskInit(function ()
+    sys.wait(1000) -- 免得看不到日志
+    local tmp
+
+	--实验1:以小端方式编码
+	local data = string.pack("<I", 0xAABBCCDD)      --‘<’表示以小端方式编码,'I'表示,unsigned int , 4字节
+	log.info("pack:", 	string.format("%02X", data:byte(1)), 	--输出小端编码后的数据
+						string.format("%02X", data:byte(2)), 
+						string.format("%02X", data:byte(3)), 
+						string.format("%02X", data:byte(4)))
+	
+	--实验2:以大端方式编码
+	local data = string.pack(">I", 0xAABBCCDD)
+	log.info("pack:", 	string.format("%02X", data:byte(1)),   --输出大端编码后的数据
+						string.format("%02X", data:byte(2)), 
+						string.format("%02X", data:byte(3)), 
+						string.format("%02X", data:byte(4)))
+						
+	--实验3:对上面已经完成的大端编码,再次进行解包为每个字节					
+	local byte1,byte2,byte3,byte4 = string.unpack(">BBBB", data)  --将32位数据拆成4个8位字节数据
+    --log.info("Unpack", byte1,byte2,byte3,byte4)		
+	log.info("Unpack:", string.format("%02X", byte1),   --以十六进制形式输出拆解后的4个字节数据
+						string.format("%02X", byte2), 
+						string.format("%02X", byte3), 
+						string.format("%02X", byte4))
+end)
+-- 这里演示4G模块上网后,会自动点亮网络灯,方便用户判断模块是否正常开机
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 72 - 0
module/Air780EHM/demo/protobuf/main.lua

@@ -0,0 +1,72 @@
+--- 模块功能:Google ProtoBuffs 编解码
+-- @module pb
+-- @author wendal
+-- @release 2022.9.8
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "pbdemo"
+VERSION = "1.0.1"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+--添加硬狗防止程序卡死
+if wdt then
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+sys.taskInit(function()
+    sys.wait(500)
+    -- 如果没有这个库, 就云编译一份吧: https://wiki.luatos.com/develop/compile/Cloud_compilation.html
+    if not protobuf then
+        log.info("protobuf", "this demo need protobuf lib")
+        return
+    end
+    -- 加载 pb 文件, 这个是从pbtxt 转换得到的
+    -- 下载资源到模块时不需要下载pbtxt
+    -- 转换命令: protoc.exe -operson.pb person.pbtxt
+    -- protoc.exe 下载地址: https://github.com/protocolbuffers/protobuf/releases
+    local pb_file = "/luadb/person.pb"
+    
+    if io.exists(pb_file) then
+        protobuf.load(io.readFile(pb_file))
+        sys.publish("pb_file_exists")
+    else
+        log.info("protobuf","Failed to load file")
+    end
+
+    local tb = {
+        name = "wendal",
+        id = 123,
+        email = "abc@qq.com"
+    }
+    while 1 do
+        sys.waitUntil("pb_file_exists")
+        sys.wait(1000)
+        -- 用 protobuf 编码数据
+        local pbdata = protobuf.encode("Person", tb)
+        if pbdata then
+            -- 打印数据长度. 编码后的数据含不可见字符, toHex是方便显示
+            log.info("protobuf", "encode",  #pbdata, (pbdata:toHex()))
+        end
+        -- 用 json 编码数据, 用于对比大小
+        local jdata = json.encode(tb)
+        if jdata then
+            log.info("json", #jdata, jdata)
+        end
+        -- 可见 protobuffs 比 json 节省很多空间
+
+        -- 后续是演示解码
+        local re = protobuf.decode("Person", pbdata)
+        if re then
+            -- 打印数据, 因为table不能直接显示, 这里转成json来显示
+            log.info("protobuf", "decode", json.encode(re))
+        end
+    end
+end)
+
+-- 主循环, 必须加
+sys.run()

+ 7 - 0
module/Air780EHM/demo/protobuf/person.pb

@@ -0,0 +1,7 @@
+
+R
+person.pbtxt"B
+Person
+name (	Rname
+id (Rid
+email (	Remail

+ 6 - 0
module/Air780EHM/demo/protobuf/person.pbtxt

@@ -0,0 +1,6 @@
+syntax = "proto2";
+message Person {
+  optional string name = 1;
+  optional int32 id = 2;
+  optional string email = 3;
+}

+ 34 - 0
module/Air780EHM/demo/pwm/示例1 PWM输出/main.lua

@@ -0,0 +1,34 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "pwmdemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+--添加硬狗防止程序卡死
+if wdt then
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+sys.taskInit(function()
+    while true do
+        -- 开启pwm通道4,设置脉冲频率为1kHz,分频精度为1000,占空比为10/1000=1% 持续输出
+        pwm.open(4, 1000, 10, 0, 1000) -- 小灯微微发光
+        sys.wait(1000)
+        -- 开启pwm通道4,设置脉冲频率为1kHz,分频精度为1000,占空比为500/1000=50% 持续输出
+        pwm.open(4, 1000, 500, 0, 1000) -- 小灯中等亮度
+        sys.wait(1000)
+        -- 开启pwm通道4,设置脉冲频率为1kHz,分频精度为1000,占空比为1000/1000=100% 持续输出
+        pwm.open(4, 1000, 1000, 0, 1000) -- 小灯很高亮度
+        sys.wait(1000)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 58 - 0
module/Air780EHM/demo/pwm/示例2 呼吸灯效果/main.lua

@@ -0,0 +1,58 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "pwmdemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+--添加硬狗防止程序卡死
+if wdt then
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+local PWM_ID = 0
+local rtos_bsp = rtos.bsp()
+if rtos_bsp == "EC618" then
+    PWM_ID = 1 -- GPIO 24
+elseif string.find(rtos_bsp,"EC718") then
+    PWM_ID = 2 -- GPIO 25
+elseif rtos_bsp == "AIR101" or rtos_bsp == "AIR103" or rtos_bsp == "AIR601"  then
+    PWM_ID = 4 -- GPIO 4
+elseif rtos_bsp:startsWith("ESP32") then
+    -- 注意, ESP32系列的PWM, PWM通道均与GPIO号相同
+    -- 例如需要用GPIO1输出PWM, 对应的PWM通道就是1
+    -- 需要用GPIO16输出PWM, 对应的PWM通道就是16
+    if rtos_bsp == "ESP32C3" then
+        PWM_ID = 12 -- GPIO 12
+    elseif rtos_bsp == "ESP32S3" then
+        PWM_ID = 11 -- GPIO 11
+    end
+elseif rtos_bsp == "AIR105" then
+    PWM_ID = 1 -- GPIO 17
+end
+
+sys.taskInit(function()
+    log.info("pwm", "ch", PWM_ID)
+    while 1 do
+        -- 仿呼吸灯效果
+        log.info("pwm", ">>>>>")
+        for i = 10,1,-1 do 
+            pwm.open(PWM_ID, 1000, i*9) -- 频率1000hz, 占空比0-100
+            sys.wait(100 + i*10)
+        end
+        for i = 10,1,-1 do 
+            pwm.open(PWM_ID, 1000, 100 - i*9)
+            sys.wait(100 + i*10)
+        end
+        sys.wait(2000)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 87 - 0
module/Air780EHM/demo/sms/main.lua

@@ -0,0 +1,87 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "smsdemo"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+require "sysplus" -- http库需要这个sysplus
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+log.info("main", "sms demo")
+
+-- 辅助发送http请求, 因为http库需要在task里运行
+function http_post(url, headers, body)
+    sys.taskInit(function()
+        local code, headers, body = http.request("POST", url, headers, body).wait()
+        log.info("resp", code)
+    end)
+end
+
+function sms_handler(num, txt)
+    -- num 手机号码
+    -- txt 文本内容
+    log.info("sms", num, txt, txt:toHex())
+
+    -- http演示1, 发json
+    local body = json.encode({phone=num, txt=txt})
+    local headers = {}
+    headers["Content-Type"] = "application/json"
+    log.info("json", body)
+    http_post("http://www.luatos.com/api/sms/blackhole", headers, body)
+    -- http演示2, 发表单的
+    headers = {}
+    headers["Content-Type"] = "application/x-www-form-urlencoded"
+    local body = string.format("phone=%s&txt=%s", num:urlEncode(), txt:urlEncode())
+    log.info("params", body)
+    http_post("http://www.luatos.com/api/sms/blackhole", headers, body)
+    -- http演示3, 不需要headers,直接发
+    http_post("http://www.luatos.com/api/sms/blackhole", nil, num .. "," .. txt)
+    -- 如需发送到钉钉, 参考 demo/dingding
+    -- 如需发送到飞书, 参考 demo/feishu
+end
+
+--------------------------------------------------------------------
+-- 接收短信, 支持多种方式, 选一种就可以了
+-- 1. 设置回调函数
+--sms.setNewSmsCb(sms_handler)
+-- 2. 订阅系统消息
+--sys.subscribe("SMS_INC", sms_handler)
+-- 3. 在task里等着
+sys.taskInit(function()
+    while 1 do
+        local ret, num, txt = sys.waitUntil("SMS_INC", 300000)
+        if num then
+            -- 方案1, 交给自定义函数处理
+            sms_handler(num, txt)
+            -- 方案2, 因为这里是task内, 可以直接调用http.request
+            -- local body = json.encode({phone=num, txt=txt})
+            -- local headers = {}
+            -- headers["Content-Type"] = "application/json"
+            -- log.info("json", body)
+            -- local code, headers, body = http.request("POST", "http://www.luatos.com/api/sms/blackhole", headers, body).wait()
+            -- log.info("resp", code)
+        end
+    end
+end)
+
+-------------------------------------------------------------------
+-- 发送短信, 直接调用sms.send就行, 是不是task无所谓
+sys.taskInit(function()
+    sys.wait(10000)
+    -- 中移动卡查短信
+    -- sms.send("+8610086", "301")
+    -- 联通卡查话费
+     sms.send("10010", "101")
+end)
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 113 - 0
module/Air780EHM/demo/string/main.lua

@@ -0,0 +1,113 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "strtest"
+VERSION = "2.0.0"
+
+--[[
+本demo演示 string字符串的基本操作
+1. lua的字符串是带长度, 这意味着, 它不依赖0x00作为结束字符串, 可以包含任意数据
+2. lua的字符串是不可变的, 就不能直接修改字符串的一个字符, 修改字符会返回一个新的字符串
+]]
+
+-- sys库是标配
+_G.sys = require("sys")
+
+sys.taskInit(function ()
+    sys.wait(1000) -- 免得看不到日志
+    local tmp
+
+    ----------------------------------------------
+    --================================================
+    -- 字符串的声明和生成
+    --================================================
+
+    -- 常量声明
+    local str = "123455" 
+    log.info("str", str)														--日志输出:123455
+
+    -- 合成式
+    str = string.char(0x31, 0x32, 0x33, 0x34)  --0x31为字符 1的ASCII码
+    log.info("str", str)           												--日志输出:1234                   
+    -- lua的字符串可以包含任意数据, 包括 0x00
+    str = string.char(0x12, 0x00, 0xF1, 0x3A)
+    log.info("str", str:toHex()) -- 注意, 这里用toHex(), 因为包含了不可见字符   --日志输出:1200F13A	8(其中8为输出字符串长度)
+    -- 使用转义字符
+    str = "\x00\x12ABC"			 -- 字符串中的\x表示十六进制转义序列								
+    log.info("str", str:toHex()) -- 注意, 这里用toHex(), 因为包含了不可见字符   --日志输出:0012414243	10 (其中41,42,43分别分字符 ABC的ASCII值的十六进制形式,10为输出字符串长度)
+    str = "ABC\r\n\t"
+    log.info("str", str:toHex()) -- 注意, 这里用toHex(), 因为包含了不可见字符   --日志输出:4142430D0A09	12(其中0D为\r回车键值的ASCII值的十六进制形式,
+	                                                                            --0A为\n换行键值的ASCII值的十六进制形式,\t 是一个转义字符,表示一个水平制表符(Tab))
+
+
+
+    -- 解析生成
+    str = string.fromHex("AABB00EE")											
+    log.info("str", str:toHex())												--日志输出:AABB00EE	8
+    str = string.fromHex("393837363433")       --将字符串转换为十六进制形式
+    log.info("str", #str, str)												    --日志输出:6	987643(其中6为输出字符长度,987643为输出字符串)
+
+    -- 连接字符串, 操作符 ".."
+    str = "123" .. "," .. "ABC"  --将3段字符串连接起来
+    log.info("str", #str, str)												    --日志输出:7	123,ABC(其中7为输出字符长度,123,ABC为连接后的字符串)
+
+
+    -- 格式化生成
+    str = string.format("%s,%d,%f", "123", 45678, 1.5)		--格式化输出,	%s为字符串输出,%d为十进制输出,%f为浮点形式输出			
+    log.info("str", #str, str)													--日志输出:18	123,45678,1.500000
+
+
+    --================================================
+    -- 字符串的解析与处理
+    --================================================
+    -- 获取长度
+    str = "1234567"
+    log.info("str", #str)														--日志输出:7为字符串长度
+    -- 获取字符串的HEX字符串显示
+    log.info("str", str:toHex())												--日志输出:31323334353637	14(用字符格式输出十六进制)
+
+    -- 获取指定位置的值, 注意lua的下标是1开始的
+    str = "123ddss"
+    log.info("str[1]", str:byte(1))                                             --日志输出:49	 (字符1,对应十进制ASCII值)
+    log.info("str[4]", str:byte(4))												--日志输出: 100	 (字符d,对应十进制ASCII值)
+    log.info("str[1]", string.byte(str, 1))										--日志输出:49   (str位置1的字符,也是数字1)
+    log.info("str[4]", string.byte(str, 4))										--日志输出: 100	 (str位置4的字符,也是数字d)
+
+    -- 按字符串分割
+    str = "12,2,3,4,5"
+    tmp = str:split(",")
+    log.info("str.split", #tmp, tmp[1], tmp[3])									--日志输出:5	12	3
+    tmp = string.split(str, ",") -- 与前面的等价
+    log.info("str.split", #tmp, tmp[1], tmp[3])                                 --日志输出:5	12	3
+    str = "/tmp//def/1234/"
+    tmp = str:split("/")
+    log.info("str.split", #tmp, json.encode(tmp))								--日志输出:3	["tmp","def","1234"]
+
+    -- 2023.04.11新增的, 可以保留空的分割片段
+	--在 Lua 中,str:split("/", true) 语句表示将字符串 str 按照字符 "/" 进行分割,并且 true 参数通常用于表示保留空字符串(这取决于具体的 split 函数实现,因为 Lua 标准库中没有内置的 split 函数)。根据你的描述,输出结果是 6 ["","tmp","","def","1234",""]。这是因为:
+    --假设 str 是 "/tmp//def/1234/",在这种情况下,字符串以 "/" 开头和结尾,并且有连续的 "/"。
+    --split 函数将字符串分割成多个部分,每个 "/" 都会作为一个分割符。
+    --因为 true 参数表示保留空字符串,所以在分割过程中,连续的 "/" 和开头、结尾的 "/" 都会导致空字符串被保留。
+
+    tmp = str:split("/", true) 
+    log.info("str.split", #tmp, json.encode(tmp))								--日志输出:6	["","tmp","","def","1234",""]
+
+    -- 更多资料
+    -- https://wiki.luatos.com/develop/hex_string.html
+    -- https://wiki.luatos.com/_static/lua53doc/manual.html#3.4
+end)
+-- 这里演示4G模块上网后,会自动点亮网络灯,方便用户判断模块是否正常开机
+sys.taskInit(function()
+    while true do
+        sys.wait(6000)
+                if mobile.status() == 1 then
+                        gpio.set(27, 1)  
+                else
+                        gpio.set(27, 0) 
+                        mobile.reset()
+        end
+    end
+end)
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 134 - 0
module/Air780EHM/demo/tcp/TCP-UART/main.lua

@@ -0,0 +1,134 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "uart_tcp"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+_G.sysplus = require("sysplus")
+local taskName = "TCP_TASK"             -- sysplus库用到的任务名称,也作为任务id
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+local uartid = 1 -- 根据实际设备选取不同的uartid
+local uart_rx_buff = zbuff.create(1024)     -- 串口接收到的数据
+local libnet = require "libnet"         -- libnet库,支持tcp、udp协议所用的同步阻塞接口
+local ip = "112.125.89.8"               -- 连接tcp服务器的ip地址
+local port = 46244                 -- 连接tcp服务器的端口
+local netCB = nil                       -- socket服务的回调函数
+local connect_state = false             -- 连接状态 true:已连接   false:未连接
+local protocol = false                  -- 通讯协议 true:UDP协议  false:TCP协议
+local ssl = false                       -- 加密传输 true:加密     false:不加密
+local tx_buff = zbuff.create(1024)      -- 发送至tcp服务器的数据
+local rx_buff = zbuff.create(1024)      -- 从tcp服务器接收到的数据
+
+--初始化
+uart.setup(
+    uartid,--串口id
+    115200,--波特率
+    8,--数据位
+    1--停止位
+)
+function TCP_TASK()
+    -- 打印一下连接的目标ip和端口号
+    log.info("connect ip: ", ip, "port:", port)
+
+    sys.waitUntil("IP_READY")                -- 等待联网成功
+    netCB = socket.create(nil, taskName)     -- 创建socket对象
+    socket.debug(netCB, true)                -- 打开调试日志
+    socket.config(netCB, nil, protocol, ssl)      -- 此配置为TCP连接,无SSL加密
+
+    -- 串口和TCP服务器的交互逻辑
+    while true do
+        -- 连接服务器,返回是否连接成功
+        result = libnet.connect(taskName, 15000, netCB, ip, port)
+
+        -- 收取数据会触发回调, 这里的"receive" 是固定值不要修改。
+        uart.on(uartid, "receive", function(id, len)
+            while true do
+                local len = uart.rx(id, uart_rx_buff)   -- 接收串口收到的数据,并赋值到uart_rx_buff
+                if len <= 0 then    -- 接收到的字节长度为0 则退出
+                    break
+                end
+                -- 如果已经在线了,则发送socket.EVENT消息来打断任务里的阻塞等待状态,让任务循环继续
+                if connect_state then
+                    sys_send(taskName, socket.EVENT, 0)
+                end
+            end
+        end)
+
+        -- 如果连接成功,则改变连接状态参数,并且随便发一条数据到服务器,看服务器能不能收到
+        if result then
+            connect_state = true
+            libnet.tx(taskName, 0, netCB, "TCP  CONNECT")
+        end
+
+        -- 连接上服务器后,等待处理接收服务器下行至模块的数据 和 发送串口的数据到服务器
+        while result do
+            succ, param, _, _ = socket.rx(netCB, rx_buff)   -- 接收数据
+            if not succ then
+                log.info("服务器断开了", succ, param, ip, port)
+                break
+            end
+
+            if rx_buff:used() > 0 then
+                log.info("收到服务器数据,长度", rx_buff:used())
+
+                uart.tx(uartid, rx_buff)    -- 从服务器收到的数据转发 从串口输出
+                rx_buff:del()
+            end
+
+            tx_buff:copy(nil, uart_rx_buff)         -- 将串口数据赋值给tcp待发送数据的buff中
+            uart_rx_buff:del()                      -- 清除串口buff的数据长度
+            if tx_buff:used() > 0 then
+                log.info("发送到服务器数据,长度", tx_buff:used())
+                local result = libnet.tx(taskName, 0, netCB, tx_buff)   -- 发送数据
+                if not result then
+                    log.info("发送失败了", result, param)
+                    break
+                end
+            end
+            tx_buff:del()
+
+            -- 如果zbuff对象长度超出,需要重新分配下空间
+            if uart_rx_buff:len() > 1024 then
+                uart_rx_buff:resize(1024)
+            end
+            if tx_buff:len() > 1024 then
+                tx_buff:resize(1024)
+            end
+            if rx_buff:len() > 1024 then
+                rx_buff:resize(1024)
+            end
+            log.info(rtos.meminfo("sys"))   -- 打印系统内存
+
+            -- 阻塞等待新的消息到来,比如服务器下发,串口接收到数据
+            result, param = libnet.wait(taskName, 15000, netCB)
+            if not result then
+                log.info("服务器断开了", result, param)
+                break
+            end
+        end
+
+        -- 服务器断开后的行动,由于while true的影响,所以会再次重新执行进行 重新连接。
+        connect_state = false
+        libnet.close(taskName, 5000, netCB)
+        tx_buff:clear(0)
+        rx_buff:clear(0)
+        sys.wait(1000)
+    end
+
+end
+
+-- libnet库依赖于sysplus,所以只能通过sysplus.taskInitEx创建的任务函数中运行
+sysplus.taskInitEx(TCP_TASK, taskName, netCB)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 139 - 0
module/Air780EHM/demo/tcp/TCP单向认证/main.lua

@@ -0,0 +1,139 @@
+-- main.lua文件
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "uart_tcp"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+_G.sysplus = require("sysplus")
+local taskName = "TCP_TASK"             -- sysplus库用到的任务名称,也作为任务id
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+local uartid = 1 -- 根据实际设备选取不同的uartid
+local uart_rx_buff = zbuff.create(1024)     -- 串口接收到的数据
+
+local libnet = require "libnet"         -- libnet库,支持tcp、udp协议所用的同步阻塞接口
+local ip = "112.125.89.8"               -- 连接tcp服务器的ip地址
+local port = 46428            -- 连接tcp服务器的端口
+local netCB = nil                       -- socket服务的回调函数
+local connect_state = false             -- 连接状态 true:已连接   false:未连接
+local protocol = false                  -- 通讯协议 true:UDP协议  false:TCP协议
+local ssl = true                     -- 加密传输 true:加密     false:不加密
+local tx_buff = zbuff.create(1024)      -- 发送至tcp服务器的数据
+local rx_buff = zbuff.create(1024)      -- 从tcp服务器接收到的数据
+
+--初始化
+uart.setup(
+    uartid,--串口id
+    115200,--波特率
+    8,--数据位
+    1--停止位
+)
+
+function TCP_TASK()
+    -- 打印一下连接的目标ip和端口号
+    log.info("connect ip: ", ip, "port:", port)
+
+    sys.waitUntil("IP_READY")                -- 等待联网成功
+    netCB = socket.create(nil, taskName)     -- 创建socket对象
+    socket.debug(netCB, true)                -- 打开调试日志
+    socket.config(netCB, nil, protocol, ssl)      -- 此配置为TCP连接,无SSL加密
+
+    -- 串口和TCP服务器的交互逻辑
+    while true do
+        -- 连接服务器,返回是否连接成功
+        result = libnet.connect(taskName, 15000, netCB, ip, port)
+
+        -- 收取数据会触发回调, 这里的"receive" 是固定值不要修改。
+        uart.on(uartid, "receive", function(id, len)
+            while true do
+                local len = uart.rx(id, uart_rx_buff)   -- 接收串口收到的数据,并赋值到uart_rx_buff
+                if len <= 0 then    -- 接收到的字节长度为0 则退出
+                    break
+                end
+                -- 如果已经在线了,则发送socket.EVENT消息来打断任务里的阻塞等待状态,让任务循环继续
+                if connect_state then
+                    sys_send(taskName, socket.EVENT, 0)
+                end
+            end
+        end)
+
+        -- 如果连接成功,则改变连接状态参数,并且随便发一条数据到服务器,看服务器能不能收到
+        if result then
+            connect_state = true
+            libnet.tx(taskName, 0, netCB, "TCP  CONNECT")
+        end
+
+        -- 连接上服务器后,等待处理接收服务器下行至模块的数据 和 发送串口的数据到服务器
+        while result do
+            succ, param, _, _ = socket.rx(netCB, rx_buff)   -- 接收数据
+            if not succ then
+                log.info("服务器断开了", succ, param, ip, port)
+                break
+            end
+
+            if rx_buff:used() > 0 then
+                log.info("收到服务器数据,长度", rx_buff:used())
+
+                uart.tx(uartid, rx_buff)    -- 从服务器收到的数据转发 从串口输出
+                rx_buff:del()
+            end
+
+            tx_buff:copy(nil, uart_rx_buff)         -- 将串口数据赋值给tcp待发送数据的buff中
+            uart_rx_buff:del()                      -- 清除串口buff的数据长度
+            if tx_buff:used() > 0 then
+                log.info("发送到服务器数据,长度", tx_buff:used())
+                local result = libnet.tx(taskName, 0, netCB, tx_buff)   -- 发送数据
+                if not result then
+                    log.info("发送失败了", result, param)
+                    break
+                end
+            end
+            tx_buff:del()
+
+            -- 如果zbuff对象长度超出,需要重新分配下空间
+            if uart_rx_buff:len() > 1024 then
+                uart_rx_buff:resize(1024)
+            end
+            if tx_buff:len() > 1024 then
+                tx_buff:resize(1024)
+            end
+            if rx_buff:len() > 1024 then
+                rx_buff:resize(1024)
+            end
+            log.info(rtos.meminfo("sys"))   -- 打印系统内存
+
+            -- 阻塞等待新的消息到来,比如服务器下发,串口接收到数据
+            result, param = libnet.wait(taskName, 15000, netCB)
+            if not result then
+                log.info("服务器断开了", result, param)
+                break
+            end
+        end
+
+        -- 服务器断开后的行动,由于while true的影响,所以会再次重新执行进行 重新连接。
+        connect_state = false
+        libnet.close(taskName, 5000, netCB)
+        tx_buff:clear(0)
+        rx_buff:clear(0)
+        sys.wait(1000)
+    end
+
+end
+
+-- libnet库依赖于sysplus,所以只能通过sysplus.taskInitEx创建的任务函数中运行
+sysplus.taskInitEx(TCP_TASK, taskName, netCB)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 134 - 0
module/Air780EHM/demo/tcp/TCP断链续连/main.lua

@@ -0,0 +1,134 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "uart_tcp"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+_G.sysplus = require("sysplus")
+local taskName = "TCP_TASK"             -- sysplus库用到的任务名称,也作为任务id
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+local uartid = 1 -- 根据实际设备选取不同的uartid
+local uart_rx_buff = zbuff.create(1024)     -- 串口接收到的数据
+local libnet = require "libnet"         -- libnet库,支持tcp、udp协议所用的同步阻塞接口
+local ip = "112.125.89.8"               -- 连接tcp服务器的ip地址
+local port = 46244                 -- 连接tcp服务器的端口
+local netCB = nil                       -- socket服务的回调函数
+local connect_state = false             -- 连接状态 true:已连接   false:未连接
+local protocol = false                  -- 通讯协议 true:UDP协议  false:TCP协议
+local ssl = false                       -- 加密传输 true:加密     false:不加密
+local tx_buff = zbuff.create(1024)      -- 发送至tcp服务器的数据
+local rx_buff = zbuff.create(1024)      -- 从tcp服务器接收到的数据
+
+--初始化
+uart.setup(
+    uartid,--串口id
+    115200,--波特率
+    8,--数据位
+    1--停止位
+)
+function TCP_TASK()
+    -- 打印一下连接的目标ip和端口号
+    log.info("connect ip: ", ip, "port:", port)
+
+    sys.waitUntil("IP_READY")                -- 等待联网成功
+    netCB = socket.create(nil, taskName)     -- 创建socket对象
+    socket.debug(netCB, true)                -- 打开调试日志
+    socket.config(netCB, nil, protocol, ssl)      -- 此配置为TCP连接,无SSL加密
+
+    -- 串口和TCP服务器的交互逻辑
+    while true do
+        -- 连接服务器,返回是否连接成功
+        result = libnet.connect(taskName, 15000, netCB, ip, port)
+
+        -- 收取数据会触发回调, 这里的"receive" 是固定值不要修改。
+        uart.on(uartid, "receive", function(id, len)
+            while true do
+                local len = uart.rx(id, uart_rx_buff)   -- 接收串口收到的数据,并赋值到uart_rx_buff
+                if len <= 0 then    -- 接收到的字节长度为0 则退出
+                    break
+                end
+                -- 如果已经在线了,则发送socket.EVENT消息来打断任务里的阻塞等待状态,让任务循环继续
+                if connect_state then
+                    sys_send(taskName, socket.EVENT, 0)
+                end
+            end
+        end)
+
+        -- 如果连接成功,则改变连接状态参数,并且随便发一条数据到服务器,看服务器能不能收到
+        if result then
+            connect_state = true
+            libnet.tx(taskName, 0, netCB, "TCP  CONNECT")
+        end
+
+        -- 连接上服务器后,等待处理接收服务器下行至模块的数据 和 发送串口的数据到服务器
+        while result do
+            succ, param, _, _ = socket.rx(netCB, rx_buff)   -- 接收数据
+            if not succ then
+                log.info("服务器断开了", succ, param, ip, port)
+                break
+            end
+
+            if rx_buff:used() > 0 then
+                log.info("收到服务器数据,长度", rx_buff:used())
+
+                uart.tx(uartid, rx_buff)    -- 从服务器收到的数据转发 从串口输出
+                rx_buff:del()
+            end
+
+            tx_buff:copy(nil, uart_rx_buff)         -- 将串口数据赋值给tcp待发送数据的buff中
+            uart_rx_buff:del()                      -- 清除串口buff的数据长度
+            if tx_buff:used() > 0 then
+                log.info("发送到服务器数据,长度", tx_buff:used())
+                local result = libnet.tx(taskName, 0, netCB, tx_buff)   -- 发送数据
+                if not result then
+                    log.info("发送失败了", result, param)
+                    break
+                end
+            end
+            tx_buff:del()
+
+            -- 如果zbuff对象长度超出,需要重新分配下空间
+            if uart_rx_buff:len() > 1024 then
+                uart_rx_buff:resize(1024)
+            end
+            if tx_buff:len() > 1024 then
+                tx_buff:resize(1024)
+            end
+            if rx_buff:len() > 1024 then
+                rx_buff:resize(1024)
+            end
+            log.info(rtos.meminfo("sys"))   -- 打印系统内存
+
+            -- 阻塞等待新的消息到来,比如服务器下发,串口接收到数据
+            result, param = libnet.wait(taskName, 15000, netCB)
+            if not result then
+                log.info("服务器断开了", result, param)
+                break
+            end
+        end
+
+        -- 服务器断开后的行动,由于while true的影响,所以会再次重新执行进行 重新连接。
+        connect_state = false
+        libnet.close(taskName, 5000, netCB)
+        tx_buff:clear(0)
+        rx_buff:clear(0)
+        sys.wait(1000)
+    end
+
+end
+
+-- libnet库依赖于sysplus,所以只能通过sysplus.taskInitEx创建的任务函数中运行
+sysplus.taskInitEx(TCP_TASK, taskName, netCB)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 51 - 0
module/Air780EHM/demo/timer/main.lua

@@ -0,0 +1,51 @@
+
+-- main.lua文件
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "timer_demo"
+VERSION = "1.0.0"
+
+sys = require("sys")
+-- 定义一个单次触发的定时器回调函数  
+local function oneShotCallback(message)  
+    log.info("One-shot timer triggered: " .. message)  
+end  
+-- 定义一个周期性触发的定时器回调函数  
+local function periodicCallback(count)  
+    log.info("Periodic timer triggered (Count: " .. count .. ")")  
+end  
+-- 定义一个周期性触发的定时器回调函数  
+local function periodicCallback1(count)  
+    log.info("Periodic timer triggered1 (Count: " .. count .. ")")  
+end  
+
+-- 初始化计数器,用于周期性定时器  
+local periodicCount = 0  
+  
+-- 启动一个单次触发的定时器,延迟3秒后触发  
+local oneShotTimerId = sys.timerStart(oneShotCallback, 3000, 0, "Hello from one-shot timer!")  
+  
+-- 启动一个周期性触发的定时器,每2秒触发一次  
+
+sys.timerStart(periodicCallback,7000,"first")
+sys.timerStart(periodicCallback,6000,"second")
+sys.timerStart(periodicCallback,5000,"third")
+
+local periodicTimerId2 = sys.timerLoopStart(function()  
+    periodicCount = periodicCount + 1  
+    periodicCallback1(periodicCount)  
+end, 2000)  
+
+    -- 停止所有定时器(仅作为测试,实际应用中应根据需要停止)  
+
+    sys.timerStart(function()
+        sys.timerStop(periodicTimerId2)
+        log.info("stop 2s loop timer periodicCallback1")
+    end,5000)
+
+    sys.timerStart(function()
+        sys.timerStopAll(periodicCallback)  
+        log.info("stop periodicCallback loop timer ")
+    end,4000)
+
+
+sys.run()-- sys.run()-此后不要在添加其他函数

+ 157 - 0
module/Air780EHM/demo/u8g2/main.lua

@@ -0,0 +1,157 @@
+--- 模块功能:u8g2demo
+-- @module u8g2
+-- @author Dozingfiretruck
+-- @release 2021.01.25
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "u8g2demo"
+VERSION = "1.0.1"
+
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+
+--[[接线方式  780EPM开发板----------------------------------SSD1306
+LCD_VCC(LCD那组引脚以sim卡卡槽旁的LCD母排为1的第二个排母孔) ---(VCC)
+I2C1_SCL(CAMERA_SCL)----------------------------------------(SCL)
+I2C1_SDA(CAMERA_SDA)----------------------------------------(SDA)
+GND---------------------------------------------------------(GND)
+]]
+
+-- 添加硬狗防止程序卡死
+wdt.init(9000) -- 初始化watchdog设置为9s
+sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
+
+-- gpio.setup(14, nil) -- 关闭GPIO14,防止camera复用关系出问题
+-- gpio.setup(15, nil) -- 关闭GPIO15,防止camera复用关系出问题
+
+-- mcu.altfun(mcu.I2C, 4, 67, 3, nil)
+-- mcu.altfun(mcu.I2C, 4, 66, 3, nil)
+
+local rtos_bsp = rtos.bsp()
+
+-- hw_i2c_id,sw_i2c_scl,sw_i2c_sda,spi_id,spi_res,spi_dc,spi_cs
+function u8g2_pin()
+    if string.find(rtos_bsp, "780EPM") or string.find(rtos_bsp, "718PM") then
+        return 1, 14, 15, 0, 14, 10, 8
+    else
+        log.info("main", "你用的不是780EPM 请更换demo测试")
+        return
+    end
+end
+
+local hw_i2c_id, sw_i2c_scl, sw_i2c_sda, spi_id, spi_res, spi_dc, spi_cs = u8g2_pin()
+
+-- 日志TAG, 非必须
+local TAG = "main"
+local chinese =true
+-- 主流程
+sys.taskInit(function()
+
+    gpio.setup(2, 1) -- GPIO2打开给camera电源供电
+    gpio.setup(28, 1) -- 1.2版本 GPIO28打开给lcd电源供电
+    gpio.setup(29, 1) -- 1.3硬件版本 GPIO29打开给lcd电源供电
+    sys.wait(2000)
+
+    -- 初始化显示屏
+    log.info(TAG, "init ssd1306")
+
+    -- 初始化硬件i2c的ssd1306
+    log.info("setup SSD1306", u8g2.begin({
+        ic = "ssd1306",
+        direction = 0,
+        mode = "i2c_hw",
+        i2c_id = hw_i2c_id
+    })) -- direction 可选0 90 180 270
+
+    log.info("设置字体模式", u8g2.SetFontMode(1))
+    log.info("清屏", u8g2.ClearBuffer())
+    log.info("设置字体为 oppo字体", u8g2.SetFont(u8g2.font_opposansm8))
+    log.info("在显示屏上展示U8G2+LUATOS", u8g2.DrawUTF8("U8G2+LUATOS", 32, 22))
+
+    if u8g2.font_opposansm12_chinese then
+        u8g2.SetFont(u8g2.font_opposansm12_chinese)
+    elseif u8g2.font_opposansm10_chinese then
+        u8g2.SetFont(u8g2.font_opposansm10_chinese)
+    elseif u8g2.font_sarasa_m12_chinese then
+        u8g2.SetFont(u8g2.font_sarasa_m12_chinese)
+    elseif u8g2.font_sarasa_m10_chinese then
+        u8g2.SetFont(u8g2.font_sarasa_m10_chinese)
+    else
+        print("没有中文字库")
+        chinese = false
+    end
+
+    if chinese then
+    log.info("在显示屏显示中文", u8g2.DrawUTF8("中文测试", 40, 38)) -- 若中文不显示或乱码,代表所刷固件不带这个字号的字体数据, 可自行云编译一份. wiki.luatos.com 有文档.
+        
+    end
+    log.info("将存储器帧缓冲区的内容发送到显示器", u8g2.SendBuffer())
+    sys.wait(2000)
+    u8g2.ClearBuffer()
+    if chinese then
+        u8g2.DrawUTF8("屏幕宽度:" .. u8g2.GetDisplayWidth(), 40, 24)
+        u8g2.DrawUTF8("屏幕高度:" .. u8g2.GetDisplayHeight(), 40, 42)
+    else
+        u8g2.DrawUTF8("width:" .. u8g2.GetDisplayWidth(), 40, 24)
+        u8g2.DrawUTF8("height:" .. u8g2.GetDisplayHeight(), 40, 42)
+    end
+    sys.wait(5000)
+    u8g2.SendBuffer()
+
+    u8g2.ClearBuffer()
+    u8g2.DrawUTF8("画线测试:", 30, 24)
+    for i = 0, 128, 8 do
+        u8g2.DrawLine(0, 40, i, 40)
+        u8g2.DrawLine(0, 60, i, 60)
+        u8g2.SendBuffer()
+        sys.wait(100)
+    end
+
+    sys.wait(1000)
+    u8g2.ClearBuffer()
+    u8g2.DrawUTF8("画圆测试:", 30, 24)
+    u8g2.DrawCircle(30, 50, 10, 15)
+    u8g2.DrawDisc(90, 50, 10, 15)
+    u8g2.SendBuffer()
+
+    sys.wait(1000)
+    u8g2.ClearBuffer()
+    u8g2.DrawUTF8("椭圆测试:", 30, 24)
+    u8g2.DrawEllipse(30, 50, 6, 10, 15)
+    u8g2.DrawFilledEllipse(90, 50, 6, 10, 15)
+    u8g2.SendBuffer()
+
+    sys.wait(1000)
+    u8g2.ClearBuffer()
+    u8g2.DrawUTF8("方框测试:", 30, 24)
+    u8g2.DrawBox(30, 40, 30, 24)
+    u8g2.DrawFrame(90, 40, 30, 24)
+    u8g2.SendBuffer()
+
+    sys.wait(1000)
+    u8g2.ClearBuffer()
+    u8g2.DrawUTF8("圆角方框:", 30, 24)
+    u8g2.DrawRBox(30, 40, 30, 24, 8)
+    u8g2.DrawRFrame(90, 40, 30, 24, 8)
+    u8g2.SendBuffer()
+
+    sys.wait(1000)
+    u8g2.ClearBuffer()
+    u8g2.DrawUTF8("三角测试:", 30, 24)
+    u8g2.DrawTriangle(30, 60, 60, 30, 90, 60)
+    u8g2.SendBuffer()
+
+    -- qrcode测试
+    sys.wait(1000)
+    u8g2.ClearBuffer()
+    u8g2.DrawDrcode(4, 4, "https://wiki.luatos.com", 30);
+
+    u8g2.SendBuffer()
+
+    -- sys.wait(1000)
+    log.info("main", "u8g2 demo done")
+end)
+
+-- 主循环, 必须加
+sys.run()

+ 51 - 0
module/Air780EHM/demo/uart/uart/main.lua

@@ -0,0 +1,51 @@
+-- Luatools需要PROJECT和VERSION这两个信息
+PROJECT = "uart"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+log.info("main", "uart demo run......")
+
+local uartid = 1 -- 根据实际设备选取不同的uartid
+
+--初始化
+uart.setup(
+    uartid,--串口id
+    115200,--波特率
+    8,--数据位
+    1--停止位
+)
+
+-- 收取数据会触发回调, 这里的"receive" 是固定值
+uart.on(uartid, "receive", function(id, len)
+    local s = ""
+    repeat
+        s = uart.read(id, 128)
+        if #s > 0 then -- #s 是取字符串的长度
+            -- 关于收发hex值,请查阅 https://doc.openluat.com/article/583
+            log.info("uart", "receive", id, #s, s)
+            -- log.info("uart", "receive", id, #s, s:toHex()) --如果传输二进制/十六进制数据, 部分字符不可见, 不代表没收到
+        end
+    until s == ""
+end)
+
+sys.taskInit(function()
+    -- 循环两秒向串口发一次数据
+    while true do
+        sys.wait(2000)
+        uart.write(uartid, "test data.")
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 49 - 0
module/Air780EHM/demo/uart/uart_rs485/main.lua

@@ -0,0 +1,49 @@
+-- Luatools需要PROJECT和VERSION这两个信息
+PROJECT = "uart_RS485"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+log.info("main", "uart demo run......")
+
+local uartid = 1        -- 根据实际设备选取不同的uartid
+local uart485Pin = 25   -- 用于控制485接收和发送的使能引脚
+
+gpio.setup(1, 1)        --打开电源(开发板485供电脚是gpio1,用开发板测试需要开机初始化拉高gpio1)
+
+--初始化
+uart.setup(uartid, 9600, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)
+
+-- 收取数据会触发回调, 这里的"receive" 是固定值
+uart.on(uartid, "receive", function(id, len)
+    local s = ""
+    repeat
+        s = uart.read(id, 128)
+        if #s > 0 then -- #s 是取字符串的长度
+            -- 关于收发hex值,请查阅 https://doc.openluat.com/article/583
+            log.info("uart", "receive", id, #s, s)
+            -- log.info("uart", "receive", id, #s, s:toHex()) --如果传输二进制/十六进制数据, 部分字符不可见, 不代表没收到
+        end
+    until s == ""
+end)
+
+sys.taskInit(function()
+    -- 循环两秒向串口发一次数据
+    while true do
+        sys.wait(2000)
+        uart.write(uartid, "test data.")
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 77 - 0
module/Air780EHM/demo/uart/uart_two/main.lua

@@ -0,0 +1,77 @@
+-- Luatools需要PROJECT和VERSION这两个信息
+PROJECT = "uart_two"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+log.info("main", "uart_two demo run......")
+
+-- 根据实际设备选取不同的uartid
+local uartid1 = 1 -- 第一个串口id
+local uartid2 = 2 -- 第二个串口id
+
+-- 初始化第一个串口
+uart.setup(
+    uartid1,--串口id
+    115200,--波特率
+    8,--数据位
+    1--停止位
+)
+
+-- 初始化第一个串口
+uart.setup(
+    uartid2,--串口id
+    115200,--波特率
+    8,--数据位
+    1--停止位
+)
+
+-- 第一个串口接收数据回调函数
+-- 收取数据会触发回调, 这里的"receive" 是固定值
+uart.on(uartid1, "receive", function(id, len)
+    local s = ""
+    repeat
+        s = uart.read(id, 128)
+        if #s > 0 then -- #s 是取字符串的长度
+            -- 关于收发hex值,请查阅 https://doc.openluat.com/article/583
+            log.info("uart", "receive", id, #s, s)
+            -- log.info("uart", "receive", id, #s, s:toHex()) --如果传输二进制/十六进制数据, 部分字符不可见, 不代表没收到
+        end
+    until s == ""
+end)
+
+-- 第二个串口接收数据回调函数
+-- 收取数据会触发回调, 这里的"receive" 是固定值
+uart.on(uartid2, "receive", function(id, len)
+    local s = ""
+    repeat
+        s = uart.read(id, 128)
+        if #s > 0 then -- #s 是取字符串的长度
+            -- 关于收发hex值,请查阅 https://doc.openluat.com/article/583
+            log.info("uart", "receive", id, #s, s)
+            -- log.info("uart", "receive", id, #s, s:toHex()) --如果传输二进制/十六进制数据, 部分字符不可见, 不代表没收到
+        end
+    until s == ""
+end)
+
+sys.taskInit(function()
+    -- 循环两秒分别向两个串口发一次数据
+    while true do
+        sys.wait(2000)
+        uart.write(uartid1, "uart1 test data.")
+        uart.write(uartid2, "uart2 test data.")
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 61 - 0
module/Air780EHM/demo/uart/uart_vir/main.lua

@@ -0,0 +1,61 @@
+-- Luatools需要PROJECT和VERSION这两个信息
+PROJECT = "uart_vir"
+VERSION = "1.0.0"
+
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+if wdt then
+    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+end
+
+log.info("main", "uart demo run......")
+
+local uartid = uart.VUART_0 -- 使用USB虚拟串口,固定id
+
+--初始化
+uart.setup(
+    uartid,--串口id
+    115200,--波特率
+    8,--数据位
+    1--停止位
+)
+
+-- 收取数据会触发回调, 这里的"receive" 是固定值
+uart.on(uartid, "receive", function(id, len)
+    local s = ""
+    repeat
+        s = uart.read(id, 128)
+        if #s > 0 then -- #s 是取字符串的长度
+            -- 关于收发hex值,请查阅 https://doc.openluat.com/article/583
+            log.info("uart", "receive", id, #s, s)
+            -- log.info("uart", "receive", id, #s, s:toHex()) --如果传输二进制/十六进制数据, 部分字符不可见, 不代表没收到
+        end
+    until s == ""
+end)
+
+sys.taskInit(function()
+    local data =
+    {
+        host = "abcdefg.com",
+        port = "1883",
+        clientID = "c88885",
+        username = "user",
+        password = "123456",
+        ca_self = {ssl=false},
+    }
+    local jsondata = json.encode(data)
+    -- 循环每两秒向串口发一次数据
+    while true do
+        sys.wait(2000)
+        uart.write(uartid, jsondata)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

Некоторые файлы не были показаны из-за большого количества измененных файлов