This commit is contained in:
meng 2024-03-24 20:57:03 +08:00
parent 2260db4cfb
commit 76605d1d61
24 changed files with 993 additions and 102 deletions

Binary file not shown.

View File

@ -1,6 +1,9 @@
return {
NOTIFY_TYPE = "uart",
-- DINGTALK_WEBHOOK = "",
UART_ID = 1,
STATUS_GPIO = 1,
--
-- 定时查询流量间隔, 单位毫秒, 设置为 0 关闭 (建议检查 util_mobile.lua 文件中运营商号码和查询代码是否正确, 以免发错短信导致扣费, 收到查询结果短信发送通知会消耗流量)
QUERY_TRAFFIC_INTERVAL = 0,
@ -15,7 +18,7 @@ return {
NOTIFY_APPEND_MORE_INFO = true,
--
-- 通知最大重发次数
NOTIFY_RETRY_MAX = 20,
NOTIFY_RETRY_MAX = 50,
--
-- 开启低功耗模式, USB 断开连接无法查看日志, RNDIS 网卡会断开
LOW_POWER_MODE = false,

View File

@ -13,24 +13,16 @@ require "sysplus"
wdt.init(9000)
sys.timerLoopStart(wdt.feed, 3000)
-- 设置 DNS
socket.setDNS(nil, 1, "119.29.29.29")
socket.setDNS(nil, 2, "223.5.5.5")
-- 设置 SIM 自动恢复(单位: 毫秒), 搜索小区信息间隔(单位: 毫秒), 最大搜索时间(单位: 秒)
mobile.setAuto(1000 * 10)
-- 开启 IPv6
-- mobile.ipv6(true)
-- POWERKEY
local button_last_press_time, button_last_release_time = 0, 0
gpio.setup(
35,
10,
function()
local current_time = mcu.ticks()
-- 按下
if gpio.get(35) == 0 then
if gpio.get(10) == gpio.LOW then
button_last_press_time = current_time -- 记录最后一次按下时间
return
end
@ -62,6 +54,8 @@ util_mobile = require "util_mobile"
-- util_location = require "util_location"
util_notify = require "util_notify"
-- 传输状态机 GPIO1 默认低 内部下拉
gpio.setup(config.STATUS_GPIO, nil, gpio.PULLDOWN)
-- 短信接收回调
sms.setNewSmsCb(
function(sender_number, sms_content, m)
@ -76,60 +70,119 @@ sms.setNewSmsCb(
sms.send(receiver_number, sms_content_to_be_sent)
is_sms_ctrl = true
end
local my_number = mobile.number(mobile.simid())
-- local my_number = mobile.number(mobile.simid())
-- 发送通知
util_notify.add(
{
"#" .. string.gsub(my_number,"+86","",1) .. "收到短信来自" .. sender_number,
"",
sms_content,
"",
"发件时间: " .. time,
"短信状态: " .. (is_sms_ctrl and "控制" or "正常")
from = sender_number,
sms = sms_content,
from_time = time,
sms_type = (is_sms_ctrl and "控制" or "正常"),
type = "sms"
}
)
end
)
old_ststus = ""
sys.taskInit(
function()
-- 等待网络环境准备就绪
sys.waitUntil("IP_READY")
util_netled.init()
-- 开机通知
if config.BOOT_NOTIFY then
util_notify.add("#开机通知")
util_notify.add(
{
sms = "设备开机",
type = "boot"
}
)
end
-- 等待网络环境准备就绪
-- sys.waitUntil("IP_READY")
sys.subscribe("IP_READY", function()
log.info("main", "IP_READY")
end)
-- 定时查询流量
if config.QUERY_TRAFFIC_INTERVAL and config.QUERY_TRAFFIC_INTERVAL >= 1000 * 60 then
sys.timerLoopStart(util_mobile.queryTraffic, config.QUERY_TRAFFIC_INTERVAL)
end
-- if config.QUERY_TRAFFIC_INTERVAL and config.QUERY_TRAFFIC_INTERVAL >= 1000 * 60 then
-- sys.timerLoopStart(util_mobile.queryTraffic, config.QUERY_TRAFFIC_INTERVAL)
-- end
-- -- 定时基站定位
-- if config.LOCATION_INTERVAL and config.LOCATION_INTERVAL >= 1000 * 30 then
-- sys.timerLoopStart(util_location.refresh, config.LOCATION_INTERVAL, 30)
-- end
-- 断网后会发一次这个消息
sys.subscribe("SIM_IND", function(status, value)
-- log.info("上次状态", old_ststus,"当前状态",status,old_ststus ~= string.gsub(status, "%s+", ""))
if old_ststus ~= string.gsub(status, "%s+", "") then
old_ststus = string.gsub(status, "%s+", "")
-- status的取值有:
-- RDY SIM卡就绪, value为nil
-- NORDY 无SIM卡, value为nil
-- SIM_PIN 需要输入PIN, value为nil
-- GET_NUMBER 获取到电话号码(不一定有值), value为nil
-- SIM_WC SIM卡的写入次数统计,掉电归0, value为统计值
if status == "RDY" then
log.info("main", "SIM卡就绪")
elseif status == "NORDY" then
log.info("main", "无SIM卡")
util_notify.add(
{
sms = "设备没安装SIM卡",
type = "boot"
}
)
elseif status == "SIM_PIN" then
log.info("main", "需要输入PIN")
util_notify.add(
{
sms = "设备SIM卡需要输入PIN码解锁",
type = "boot"
}
)
end
end
end)
-- 电源键短按发送测试通知
sys.subscribe(
"POWERKEY_SHORT_PRESS",
function()
util_notify.add("#ALIVE")
log.info("main", "POWERKEY_SHORT_PRESS")
util_notify.add(
{
from = "15264925507",
sms = "本地测试短信通知并没有实际收到短信",
from_time = "2024-03-14 00:00:00",
sms_type = "正常",
type = "sms"
}
)
end
)
-- 电源键长按查询流量
sys.subscribe("POWERKEY_LONG_PRESS", util_mobile.queryTraffic)
sys.subscribe(
"POWERKEY_LONG_PRESS",
function()
log.info("main", "POWERKEY_LONG_PRESS")
util_notify.add(
{
from = "15264925507",
sms = "长按本地测试短信通知并没有实际收到短信",
from_time = "2024-03-14 00:00:00",
sms_type = "正常",
type = "sms"
}
)
end
)
-- 开启低功耗模式
if config.LOW_POWER_MODE then
sys.wait(1000 * 15)
log.warn("main", "即将关闭 usb 电源, 如需查看日志请在配置中关闭低功耗模式")
sys.wait(1000 * 5)
gpio.setup(23, nil)
gpio.close(33)
-- gpio.setup(23, nil)
-- gpio.close(33)
pm.power(pm.USB, false) -- 关闭 USB
pm.power(pm.GPS, false)
pm.power(pm.GPS_ANT, false)

View File

@ -28,7 +28,7 @@ local notify = {
local res_body = uart.write(config.UART_ID, msg)
return 200, '', res_body
end,
-- -- 发送到 dingtalk
-- 发送到 dingtalk
-- ["dingtalk"] = function(msg)
-- if config.DINGTALK_WEBHOOK == nil or config.DINGTALK_WEBHOOK == "" then
-- log.error("util_notify", "未配置 `config.DINGTALK_WEBHOOK`")
@ -76,14 +76,15 @@ local notify = {
-- end,
}
local function append()
local msg = "\n"
local function append(msg)
if type(msg) ~= "table" then
log.error("util_notify.append", "添加扩展信息错误", "参数类型错误", type(msg))
return msg
end
-- 本机号码
local my_number = mobile.number(mobile.simid())
local number = string.gsub(my_number,"+86","",1)
local number = mobile.number(mobile.simid())
if number then
msg = msg .. "\n本机号码: " .. number
msg.to = string.gsub(number,"+86","",1)
end
-- 开机时长
@ -95,50 +96,21 @@ local function append()
minutes = minutes % 60
local boot_time = string.format("%02d:%02d:%02d", hours, minutes, seconds)
if ms >= 0 then
msg = msg .. "\n开机时长: " .. boot_time
msg.start_time = boot_time
end
-- 运营商
local oper = util_mobile.getOper(true)
if oper ~= "" then
msg = msg .. "\n运营商: " .. oper
msg.oper = oper
end
-- 信号
local rsrp = mobile.rsrp()
if rsrp ~= 0 then
msg = msg .. "\n信号: " .. rsrp .. "dBm"
msg.rsrp = rsrp
end
-- -- 频段
-- local band = util_mobile.getBand()
-- if band >= 0 then
-- msg = msg .. "\n频段: B" .. band
-- end
-- 流量统计
-- local uplinkGB, uplinkB, downlinkGB, downlinkB = mobile.dataTraffic()
-- uplinkB = uplinkGB * 1024 * 1024 * 1024 + uplinkB
-- downlinkB = downlinkGB * 1024 * 1024 * 1024 + downlinkB
-- local function formatBytes(bytes)
-- if bytes < 1024 then
-- return bytes .. "B"
-- elseif bytes < 1024 * 1024 then
-- return string.format("%.2fKB", bytes / 1024)
-- elseif bytes < 1024 * 1024 * 1024 then
-- return string.format("%.2fMB", bytes / 1024 / 1024)
-- else
-- return string.format("%.2fGB", bytes / 1024 / 1024 / 1024)
-- end
-- end
-- msg = msg .. "\n流量: ↑" .. formatBytes(uplinkB) .. " ↓" .. formatBytes(downlinkB)
-- 位置
-- local _, _, map_link = util_location.get()
-- if map_link ~= "" then
-- msg = msg .. "\n位置: " .. map_link -- 这里使用 U+00a0 防止换行
-- end
return msg
end
@ -150,12 +122,12 @@ function util_notify.send(msg, channel)
log.info("util_notify.send", "发送通知", channel)
-- 判断消息内容 msg
if type(msg) ~= "string" then
if type(msg) ~= "table" then
log.error("util_notify.send", "发送通知失败", "参数类型错误", type(msg))
return true
end
if msg == "" then
log.error("util_notify.send", "发送通知失败", "消息为空")
if msg.sms == "" then
log.error("util_notify.send", "发送通知失败", "消息内容为空")
return true
end
@ -167,11 +139,11 @@ function util_notify.send(msg, channel)
-- 通知内容追加更多信息
if config.NOTIFY_APPEND_MORE_INFO then
msg = msg .. append()
msg = append(msg)
end
-- 发送通知
local code, headers, body = notify[channel](msg)
local code, headers, body = notify[channel](json.encode(msg))
if code == nil then
log.info("util_notify.send", "发送通知失败, 无需重发", "code:", code, "body:", body)
return true
@ -182,9 +154,6 @@ function util_notify.send(msg, channel)
return true
end
if code >= 200 and code < 500 then
-- http 2xx 成功
-- http 3xx 重定向, 重发也不会成功
-- http 4xx 客户端错误, 重发也不会成功
log.info("util_notify.send", "发送通知成功", "code:", code, "body:", body)
return true
end
@ -196,16 +165,13 @@ end
-- @param msg 消息内容
-- @param channels 通知渠道
function util_notify.add(msg, channels)
if type(msg) == "table" then
msg = table.concat(msg, "\n")
if type(msg) ~= "table" then
log.info("util_notify.add", "添加消息失败", "参数错误")
end
channels = channels or config.NOTIFY_TYPE
if type(channels) ~= "table" then
channels = {channels}
end
for _, channel in ipairs(channels) do
table.insert(msg_queue, {channel = channel, msg = msg, retry = 0})
end
@ -220,25 +186,29 @@ local function poll()
local item, result
while true do
-- 消息队列非空, 且网络已注册
if next(msg_queue) ~= nil and mobile.status() == 1 then
if next(msg_queue) ~= nil then
-- log.debug("util_notify.poll等待信号",gpio.get(config.STATUS_GPIO),gpio.HIGH)
if gpio.get(config.STATUS_GPIO) == gpio.HIGH then
log.debug("util_notify.poll", "轮询消息队列中, 当前队列长度:", #msg_queue)
item = msg_queue[1]
table.remove(msg_queue, 1)
if item.retry > (config.NOTIFY_RETRY_MAX or 100) then
log.error("util_notify.poll", "超过最大重发次数", "msg:", item.msg)
log.error("util_notify.poll", "超过最大重发次数", "消息内容:", json.encode(item.msg))
else
result = util_notify.send(item.msg, item.channel)
item.retry = item.retry + 1
if not result then
-- 发送失败, 移到队尾
table.insert(msg_queue, item)
sys.wait(5000)
sys.wait(50)
end
sys.wait(1000)
end
sys.wait(50)
else
-- log.debug("util_notify.poll", "等待主机状态下发, 当前队列长度:", #msg_queue)
sys.wait(50)
end
else
sys.waitUntil("NEW_MSG", 1000 * 10)
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,129 @@
local air780_helper = {
-- 是否正在读取串口数据
}
air780_helper.reading = false
air780_helper.cs_index = 1
local sys = require("sys")
local utils = require("utils")
local config = require("config")
local util_notify = require("util_notify")
local uart_timeout = 100
-- 使用UART 1接口与Air780E通信
-- ESP32 <--> Air780E
-- 02(UART1_TX) <--> 31(UART1_RXD)
-- 03(UART1_RX) <--> 30(UART1_TXD)
local uart_setup_result = uart.setup(config.UART_ID, 115200, 8, 1)
if uart_setup_result ~= 0 then
log.error("air780_helper", "UART初始化失败返回值"..uart_setup_result.."ESP32将重启")
sys.wait(1000)
rtos.reboot()
else
log.info("air780_helper", "UART初始化成功")
end
-- 串口读缓冲区
local send_queue = {}
-- 注册串口接收事件回调
uart.on(config.UART_ID, "receive", function(id, length)
air780_helper.reading = true
local s = ""
repeat
-- log.error("air780_helper", "开始设备串口")
s = uart.read(id, length)
if #s > 0 then
table.insert(send_queue, s)
sys.timerStart(sys.publish, config.UART_TIMEOUT, "UART_READY")
-- sys.publish("UART_READY")
end
until s == ""
end)
sys.subscribe("UART_READY", function()
led_helper.shut_working_led()
led_helper.blink_status_led(config.LED_BLINK_DURATION.initializing)
-- 拼接所有收到的数据
local data = table.concat(send_queue)
log.info("air780_helper", '读取短信完成',data)
-- 读取完成后清空缓冲区
utils.clear_table(send_queue)
air780_helper.reading = false
log.info("air780_helper", "去NEW_MSG处理消息类型和内容")
sys.publish("NEW_MSG",data)
-- while #data > 0 do
-- air780_helper.reading = false
-- sys.publish(config.UART_NEW_MSG,data)
-- end
end)
-- 收到串口新消息 添加到发送队列
sys.subscribe("NEW_MSG", function(data)
if data == nil then
return
end
if #data == 0 then
return
end
local msg = json.decode(data)
if utils.table_len(msg) <= 0 then
return
end
if msg == nil then
return
end
local numbers = fskv.kv_get("numbers")
-- log.info("air780_helper", "测试numbers", json.encode(numbers),type(numbers))
local cs_index = air780_helper.cs_index - 1
if utils.is_empty(msg.sms) == false then
air780_helper.reading = false
msg.index = tostring(cs_index)
-- 是否屏蔽自检信息
local disable_boot = fskv.kv_get("disable_boot")
if disable_boot == 1 and msg.type == "boot" then
return
end
-- 如果没有读到号码就是用设置的号码
if utils.is_empty(msg.to) then
if fskv.kv_get("numbers") then
if numbers[cs_index] ~= nil then
msg.to = numbers[cs_index]
end
end
end
if fskv.kv_get("hostname") then
msg.hostname = fskv.kv_get("hostname")
else
msg.hostname = "weiguo_" .. wlan.getMac()
end
if utils.is_empty(msg.from) and msg.type == "boot" then
msg.from = nil
msg.sms = "收到设备通知:" .. msg.sms
msg.index = nil
end
util_notify.add(msg)
utils.clear_table(msg)
msg = nil
end
end)
-- 发送AT指令
function air780_helper.send_at_command(command)
uart.write(config.UART_ID, command)
uart.write(config.UART_ID, "\r\n")
log.debug("air780_helper", "发送AT指令\""..command.."\"")
end
-- 发送AT指令并等待指定topic
function air780_helper.send_at_command_and_wait(command, topic_listen_to, timeout)
while true do
air780_helper.send_at_command(command)
local is_successful, r1, r2, r3 = sys.waitUntil(topic_listen_to, timeout or 1000)
if is_successful then
return r1, r2, r3
end
end
end
return air780_helper

42
esp32c3/script/config.lua Normal file
View File

@ -0,0 +1,42 @@
return {
-- 轮询间隔
POLL_INTERVAL = 3000,
UART_ID = 1,
-- 串口读取延时 毫秒
UART_TIMEOUT = 500,
-- 设备选择用GPIO 对应5个子设备 默认下拉
CS_GPIO = {2,3,10,6,7},
DNS_SERVERS = {
"114.114.114.114",
"223.5.5.5",
"8.8.8.8",
"1.1.1.1"
},
LOG_LEVEL = log.LOG_INFO,
-- LED针脚
LED = {
LED_A = 12,
LED_B = 13
},
-- LED闪烁间隔
LED_BLINK_DURATION = {
working = 50,
initializing = 500
},
-- 通知最大重发次数
NOTIFY_RETRY_MAX = 50,
-- 通知配置 ========================================
NOTIFY_TYPE = {
"dingtalk",
"wecom"
},
NOTIFY_CHANNEL = {
-- 钉钉机器人的webhook地址
DINGTALK_WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token=a6927dc518beeec0fe75e355e4e5cf42f1671e25741ce9da2904d3ed1432da85",
-- 企业微信机器人的webhook地址
WECOM_WEBHOOK = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=89564a1c-50d4-4dea-b3f7-511c3213ade7",
-- inotify推送地址
INOTIFY_URL = "",
}
}

View File

@ -0,0 +1,85 @@
local led_helper = {}
local sys = require("sys")
local config = require("config")
local status_led = gpio.setup(
config.LED.LED_A,
0,
gpio.PULLUP)
local working_led = gpio.setup(
config.LED.LED_B,
0,
gpio.PULLUP
)
local function stop_and_clear_timer(timer)
sys.timerStop(timer)
timer = nil
end
local is_status_led_on = true
local status_led_blink_timer = nil
function led_helper.blink_status_led(duration)
if status_led_blink_timer then
stop_and_clear_timer(status_led_blink_timer)
end
status_led_blink_timer = sys.timerLoopStart(
function ()
status_led(is_status_led_on and 1 or 0)
is_status_led_on = not is_status_led_on
end,
duration)
end
local is_working_led_on = true
local working_led_blink_timer = nil
function led_helper.blink_working_led(duration)
if working_led_blink_timer then
stop_and_clear_timer(working_led_blink_timer)
end
working_led_blink_timer = sys.timerLoopStart(
function ()
working_led(is_working_led_on and 1 or 0)
is_working_led_on = not is_working_led_on
end,
duration
)
end
function led_helper.light_status_led()
if status_led_blink_timer then
stop_and_clear_timer(status_led_blink_timer)
end
status_led(1)
end
function led_helper.shut_status_led()
if status_led_blink_timer then
stop_and_clear_timer(status_led_blink_timer)
end
status_led(0)
end
function led_helper.light_working_led()
if working_led_blink_timer then
stop_and_clear_timer(working_led_blink_timer)
end
working_led(1)
end
function led_helper.shut_working_led()
if working_led_blink_timer then
stop_and_clear_timer(working_led_blink_timer)
end
working_led(0)
end
return led_helper

View File

@ -0,0 +1,98 @@
PROJECT = "sms_forwarder_wifi"
VERSION = "1.0.0"
local sys = require("sys")
local config = require("config")
local constants = require("constants")
local air780 = require("air780_helper")
local led_helper = require("led_helper")
local utils = require("utils")
require("sysplus")
require("notification_helper")
if wdt then
wdt.init(9000)
sys.timerLoopStart(wdt.feed, 3000)
end
log.setLevel(config.log_level)
log.style(1)
log.info("bsp", rtos.bsp())
log.info("mem_sys", rtos.meminfo("sys"))
log.info("mem_lua", rtos.meminfo("lua"))
-- 每秒完整GC一次防止内存不足问题
sys.timerLoopStart(function()
collectgarbage("collect")
end, 1000)
led_helper.blink_status_led(constants.led_blink_duration.initializing)
sys.taskInit(function()
local logging_tag = "main - 初始化网络"
log.info(logging_tag, "正在连接无线网络"..config.wifi.ssid)
wlan.init()
wlan.setMode(wlan.STATION)
wlan.connect(config.wifi["ssid"], config.wifi.password)
sys.waitUntil("IP_READY")
local ip_address = wlan.getIP()
log.info(logging_tag, "无线网络连接成功IP地址"..ip_address)
for index, value in ipairs(config.dns_servers) do
log.info(logging_tag, "配置第"..index.."个DNS服务器为"..value)
socket.setDNS(nil, index, value)
end
log.info(logging_tag, "等待时间同步")
sys.waitUntil("NTP_UPDATE")
log.info(logging_tag, "时间同步完成")
end)
sys.taskInit(function ()
local logging_tag = "main - 初始化Air780"
local at_command_result
if config.disable_netled then
log.info(logging_tag, "正在关闭NET灯闪烁")
air780.send_at_command("AT+CNETLIGHT=0")
end
log.info(logging_tag, "初始化完成,等待新短信...")
-- 测试短信推送,解除注释可开机时自动模拟推送一条,用于模块独立测试
-- sys.publish(
-- constants.air780_message_topic_new_notification_request,
-- '10086',
-- '测试短信内容')
led_helper.light_status_led()
end)
-- 收到新消息派发推送
sys.subscribe(constants.air780_message_topic_new_sms_received,
function(data)
led_helper.blink_working_led(constants.led_blink_duration.working)
sms_content = table.concat(data, "\n")
if sms_content then
log.info("main", "收到短信:"..sms_content)
sys.publish(
constants.air780_message_topic_new_notification_request,
sms_content)
led_helper.shut_working_led()
return
else
log.info("main", "收到来自"..phone_number.."的短信,即将转发...")
sys.publish(
constants.air780_message_topic_new_notification_request,
sms_content)
led_helper.shut_working_led()
return
end
end)
sys.run()

234
esp32c3/script/main.lua Normal file
View File

@ -0,0 +1,234 @@
PROJECT = "sms_to_wifi"
VERSION = "1.0.0"
local sys = require("sys")
local config = require("config")
local led_helper = require("led_helper")
local utils = require("utils")
local util_notify = require("util_notify")
local air780_helper = require("air780_helper")
require("sysplus")
if wdt then
wdt.init(9000)
sys.timerLoopStart(wdt.feed, 3000)
end
log.setLevel(config.LOG_LEVEL)
log.style(1)
log.info("bsp", rtos.bsp())
log.info("mem_sys", rtos.meminfo("sys"))
log.info("mem_lua", rtos.meminfo("lua"))
-- 协议头
local http_header = {["Content-Type"] = "application/json"}
-- 每秒完整GC一次防止内存不足问题
sys.timerLoopStart(function()
collectgarbage("collect")
end, 1000)
led_helper.blink_status_led(config.LED_BLINK_DURATION.initializing)
-- 初始化fskv非易失存储
-- utils.clear_fskv()
-- 初始化网络
sys.taskInit(function()
local logging_tag = "main - 初始化网络"
fskv.init()
if fskv.kv_get("hostname") then
wlan.hostname(fskv.kv_get("hostname"))
else
local mac = wlan.getMac()
wlan.hostname("weiguo_" .. mac)
end
wlan.init()
if fskv.kv_get("wlan_ssid") then
wlan.connect(fskv.kv_get("wlan_ssid"), fskv.kv_get("wlan_passwd"))
return
end
log.info(logging_tag, "未配置wifi信息开始配网")
wlan.smartconfig(wlan.AIRKISS)
log.info(logging_tag, "开始微信配网")
local ret, ssid, passwd = sys.waitUntil("SC_RESULT", 120*1000) -- 等2分钟
if ret == true then
fskv.kv_set("wlan_ssid", ssid)
fskv.kv_set("wlan_passwd", passwd)
log.info(logging_tag, "获取到wifi信息正在重启",ssid, passwd)
sys.wait(1000)
-- 官方推荐重启
-- rtos.reboot()
else
-- smartconfig配置失败开始AP模式网页配网。
log.info(logging_tag, "网页配网","请连接ssid:weiguo_开头的wifi,打开:http://192.168.4.1进行配网")
wlan.smartconfig(wlan.STOP)
-- 设置为AP模式, 广播ssid, 接收wifi客户端的链接
sys.wait(1000)
wlan.setMode(wlan.AP)
wlan.createAP("weiguo_" .. mac, "", "192.168.4.1", "255.255.255.0", 6)
-- 监听80端口
httpsrv.start(80, function(client, method, uri, headers, body)
log.info("httpsrv", method, uri, json.encode(headers), body)
if uri == "/" then
return 200, http_header, '{"code":"1","msg":"等待配网"}'
end
if uri == "/config_wifi" then
local wan_config = json.decode(body)
if wan_config.ssid and wan_config.password then
fskv.kv_set("wlan_ssid", wan_config.ssid)
fskv.kv_set("wlan_passwd", wan_config.password)
log.info(logging_tag, "配置成功,正在重启")
wlan.connect(wan_config.ssid, wan_config.password)
return 200, http_header, '{"code":"0","msg":"配置成功"}'
-- 官方推荐重启
-- rtos.reboot()
else
return 200, http_header, '{"code":"1","msg":"配置失败"}'
end
end
return 404, {}, "Not Found" .. uri
end)
end
end)
-- 初始化设备CS针脚
local function init_cs()
for index, value in ipairs(config.CS_GPIO) do
log.info("设备选择GPIO初始化", "配置第"..index.."个设备,GPIO:"..value,air780_helper.cs_index)
gpio.setup(value, 0, gpio.PULLDOWN)
end
end
-- 轮巡主函数
local function loop_cs()
if air780_helper.reading then
return
end
if air780_helper.cs_index > #config.CS_GPIO then
air780_helper.cs_index = 1
end
-- 统一拉低
for index, value in ipairs(config.CS_GPIO) do
gpio.setup(value, 0, gpio.PULLDOWN)
end
log.info("轮巡设备", air780_helper.cs_index," GPIO",config.CS_GPIO[air780_helper.cs_index])
gpio.set(config.CS_GPIO[air780_helper.cs_index], gpio.HIGH)
air780_helper.cs_index = air780_helper.cs_index + 1
end
sys.taskInit(
function()
-- 联网完成 初始化
sys.subscribe("IP_READY", function(ip)
log.info("联网完成", "ip ready", ip)
for index, value in ipairs(config.DNS_SERVERS) do
log.info("联网完成", "配置第"..index.."个DNS服务器为"..value)
socket.setDNS(nil, index, value)
end
led_helper.shut_status_led()
led_helper.blink_working_led(config.LED_BLINK_DURATION.initializing)
-- 设置设备CS针脚
init_cs()
-- 开启定时轮询
sys.timerLoopStart(loop_cs, config.POLL_INTERVAL)
end)
log.info("bsp", rtos.bsp())
log.info("mac", wlan.getMac())
log.info("hostname", "weiguo_" .. wlan.getMac())
-- 电源键短按发送测试通知
sys.subscribe(
"POWERKEY_SHORT_PRESS",
function()
log.info("main", "POWERKEY_SHORT_PRESS")
util_notify.add(
{
from = "15264925507",
sms = "本地测试短信通知并没有实际收到短信",
from_time = "2024-03-14 00:00:00",
sms_type = "正常",
type = "sms"
}
)
end
)
end
)
-- 配置
sys.taskInit(
function()
-- 监听80端口
httpsrv.start(80, function(client, method, uri, headers, body)
log.info("httpsrv", method, uri, json.encode(headers),"b", body)
if uri == "/" then
local numbers = fskv.kv_get("numbers")
local ssid = fskv.kv_get("wlan_ssid")
local passwd = fskv.kv_get("wlan_passwd")
local config = {
code = 0,
msg = "完成",
numbers = fskv.kv_get("numbers"),
ssid = fskv.kv_get("wlan_ssid"),
passwd = fskv.kv_get("wlan_passwd"),
hostname = fskv.kv_get("hostname"),
disable_boot = fskv.kv_get("disable_boot"),
notify_type = config.NOTIFY_TYPE,
notify_channel = {
dingtalk = fskv.kv_get("dingtalk"),
wecom = fskv.kv_get("wecom")
}
}
return 200, http_header, json.encode(config)
end
if uri == "/config" then
local web_config = json.decode(body)
log.info("httpsrv", "配置数据", json.encode(web_config))
if web_config.numbers ~= nil then
fskv.kv_set("numbers", web_config.numbers)
end
if web_config.hostname ~= nil then
fskv.kv_set("hostname", web_config.hostname)
end
if web_config.ssid ~= nil then
fskv.kv_set("wlan_ssid", web_config.ssid)
end
if web_config.passwd ~= nil then
fskv.kv_set("wlan_passwd", web_config.passwd)
end
if web_config.disable_boot == 1 or web_config.disable_boot == 0 then
fskv.kv_set("disable_boot", web_config.disable_boot)
end
if web_config.ssid ~= nil or web_config.passwd ~= nil then
log.info(logging_tag, "配置成功,正在联网")
wlan.connect(wan_config.ssid, wan_config.password)
-- 官方推荐重启
-- rtos.reboot()
end
local notify_channel = {
dingtalk = fskv.kv_get("dingtalk"),
wecom = fskv.kv_get("wecom")
}
if web_config.notify_channel.dingtalk ~= nil then
fskv.kv_set("dingtalk", web_config.notify_channel.dingtalk)
end
if web_config.notify_channel.wecom ~= nil then
fskv.kv_set("wecom", web_config.notify_channel.wecom)
end
web_config.code = 0
web_config.msg = "配置成功"
if web_config.ssid ~= nil or web_config.passwd ~= nil then
web_config.msg = "配置成功,设备即将重启!"
return 200, http_header, json.encode(web_config)
else
return 200, http_header, json.encode(web_config)
end
end
return 404, {}, "Not Found" .. uri
end)
end
)
-- 启动
sys.run()

View File

@ -0,0 +1,212 @@
local config = require("config")
local utils = require("utils")
local util_notify = {}
local sys = require("sys")
-- 消息队列
local msg_queue = {}
local function urlencodeTab(params)
local msg = {}
for k, v in pairs(params) do
table.insert(msg, string.urlEncode(k) .. "=" .. string.urlEncode(v))
table.insert(msg, "&")
end
table.remove(msg)
return table.concat(msg)
end
local notify = {
-- 发送到 dingtalk
["dingtalk"] = function(msg)
local dingtalk_webhook = fskv.kv_get("dingtalk")
if utils.is_empty(dingtalk_webhook) then
log.error("util_notify", "未配置钉钉机器人webhook地址")
return
end
local title = ""
if utils.is_empty(msg.from) == false then
title = "来自" .. msg.from .. "的短信"
else
title = "短信设备通知"
end
local text = "##### " .. msg.sms .. "\n"
if utils.is_empty(msg.from) == false then
text = text .. "- 发送号码:".. msg.from .."\n"
end
if utils.is_empty(msg.to) == false then
text = text .. "- 设备号码:".. msg.to .."\n"
end
if utils.is_empty(msg.hostname) == false then
text = text .. "- 设备名称:".. msg.hostname .."\n"
end
if utils.is_empty(msg.index) == false then
text = text .. "- 子设备ID:".. msg.index .."\n"
end
if utils.is_empty(msg.from_time) == false then
text = text .. "- 接收时间:".. msg.from_time .."\n"
end
if utils.is_empty(msg.oper) == false then
text = text .. "- 运营商:".. msg.oper .."\n"
end
if utils.is_empty(msg.rsrp) == false then
text = text .. "- 信号强度:".. msg.rsrp .."\n"
end
local header = {
["Content-Type"] = "application/json; charset=utf-8"
}
local body = {
msgtype = "markdown",
markdown = {
title = title,
text = text
}
}
local json_data = json.encode(body)
-- LuatOS Bug, json.encode 会将 \n 转换为 \b
json_data = string.gsub(json_data, "\\b", "\\n")
log.info("util_notify", "POST", dingtalk_webhook)
return utils.fetch(nil, "POST", dingtalk_webhook, header, json_data)
end,
-- 发送到 wecom
["wecom"] = function(msg)
local wecom_webhook = fskv.kv_get("wecom")
if utils.is_empty(wecom_webhook) then
log.error("util_notify", "未配置企业微信机器人地址")
return
end
local header = {
["Content-Type"] = "application/json; charset=utf-8"
}
local text = msg.sms .. "\n"
if utils.is_empty(msg.from) == false then
text = text .. "发送号码:".. msg.from .."\n"
end
if utils.is_empty(msg.to) == false then
text = text .. "设备号码:".. msg.to .."\n"
end
if utils.is_empty(msg.hostname) == false then
text = text .. "设备名称:".. msg.hostname .."\n"
end
if utils.is_empty(msg.index) == false then
text = text .. "设备ID:".. msg.index .."\n"
end
if utils.is_empty(msg.from_time) == false then
text = text .. "接收时间:".. msg.from_time .."\n"
end
if utils.is_empty(msg.oper) == false then
text = text .. "运营商:".. msg.oper .."\n"
end
if utils.is_empty(msg.rsrp) == false then
text = text .. "信号强度:".. msg.rsrp .."\n"
end
local body = {
msgtype = "text",
text = {
content = text
}
}
local json_data = json.encode(body)
-- LuatOS Bug, json.encode 会将 \n 转换为 \b
json_data = string.gsub(json_data, "\\b", "\\n")
log.info("util_notify", "POST", wecom_webhook)
return utils.fetch(nil, "POST", wecom_webhook, header, json_data)
end
}
--- 发送通知
-- @param msg 消息内容
-- @param channel 通知渠道
-- @return true: 无需重发, false: 需要重发
function util_notify.send(msg, channel)
log.info("util_notify.send", "发送通知", channel)
-- 判断消息内容 msg
if type(msg) ~= "table" then
log.error("util_notify.send", "发送通知失败", "参数类型错误", type(msg))
return true
end
if msg.sms == "" then
log.error("util_notify.send", "发送通知失败", "消息内容为空")
return true
end
-- 判断通知渠道 channel
if channel and notify[channel] == nil then
log.error("util_notify.send", "发送通知失败", "未知通知渠道", channel)
return true
end
-- 发送通知
local code, headers, body = notify[channel](msg)
if code == nil then
log.info("util_notify.send", "发送通知失败, 无需重发", "code:", code, "body:", body)
return true
end
if code == -6 then
-- 发生在 url 过长时, 重发也不会成功
log.info("util_notify.send", "发送通知失败, 无需重发", "code:", code, "body:", body)
return true
end
if code >= 200 and code < 500 then
log.info("util_notify.send", "发送通知成功", "code:", code, "body:", body)
return true
end
log.error("util_notify.send", "发送通知失败, 等待重发", "code:", code, "body:", body)
return false
end
--- 添加到消息队列
-- @param msg 消息内容
-- @param channels 通知渠道
function util_notify.add(msg, channels)
log.info("util_notify.add", "添加到消息队列")
if type(msg) ~= "table" then
log.info("util_notify.add", "添加消息失败", "参数错误")
end
channels = channels or config.NOTIFY_TYPE
if type(channels) ~= "table" then
channels = {channels}
end
for _, channel in ipairs(channels) do
table.insert(msg_queue, {channel = channel, msg = msg, retry = 0})
end
sys.publish("ADD_NEW_MSG")
log.debug("util_notify.add", "添加到消息队列, 当前队列长度:", #msg_queue)
end
-- 轮询消息队列
-- 发送成功则从消息队列中删除
-- 发送失败则等待下次轮询
local function poll()
local item, result
while true do
-- 消息队列非空, 且网络已注册
if next(msg_queue) ~= nil then
log.debug("util_notify.poll", "轮询消息队列中, 当前队列长度:", #msg_queue)
item = msg_queue[1]
table.remove(msg_queue, 1)
if item.retry > (config.NOTIFY_RETRY_MAX or 100) then
log.error("util_notify.poll", "超过最大重发次数", "消息内容:", json.encode(item.msg))
else
result = util_notify.send(item.msg, item.channel)
item.retry = item.retry + 1
if not result then
-- 发送失败, 移到队尾
table.insert(msg_queue, item)
sys.wait(5000)
end
end
sys.wait(50)
else
sys.waitUntil("ADD_NEW_MSG", 1000 * 10)
end
end
end
sys.taskInit(poll)
return util_notify

65
esp32c3/script/utils.lua Normal file
View File

@ -0,0 +1,65 @@
local utils = {}
local sys = require("sys")
function utils.bool_to_number(value)
return value and 1 or 0
end
function utils.is_empty(str)
return str == nil or str == ""
end
function utils.clear_table(table)
for i = 0, #table do
table[i] = nil
end
end
function utils.table_len(t)
local leng=0
if type(t) ~= "table" then
return leng
end
for k, v in pairs(t) do
leng=leng+1
end
return leng;
end
function utils.clear_fskv()
fskv.init()
fskv.clear()
end
-- 用于生成 http 请求的 id
local http_count = 0
--- 对 LuatOS-Air http.request 的封装
-- @param timeout (number) 超时时间
-- @param method (string) 请求方法
-- @param url (string) 请求地址
-- @param headers (table) 请求头
-- @param body (string) 请求体
-- @return (number, table, string) 状态码, 响应头, 响应体
function utils.fetch(timeout, method, url, headers, body)
local code,headers,body = http.request(method, url, headers,body).wait()
return code, headers, body
-- timeout = timeout or 1000 * 30
-- http_count = http_count + 1
-- local id = "http_c" .. http_count .. "_r" .. math.random(1000, 9999)
-- local function callback(res_result, res_prompt, res_headers, res_body)
-- sys.publish(id, {res_result, res_prompt, res_headers, res_body})
-- end
-- log.info("utils.fetch", "开始请求", "id:", id)
-- http.request(method, url, nil, headers, body, timeout, callback)
-- local result, data = sys.waitUntil(id, 1000 * 60)
-- log.info("utils.fetch", "请求结束", "id:", id)
-- if result == false then
-- log.warn("utils.fetch", "请求超时", "id:", id)
-- return 501, {}, "utils.fetch 请求超时"
-- end
-- return tonumber(data[2]) or -99, data[3] or {}, data[4] or ""
end
return utils