intv_ai_mk11惊艳生成:Llama架构下中文语义理解与表达连贯性实证展示
2026/4/3 18:32:29
通过mqtt使用webhook转发消息实现远程查看单片机日志
我做的一个开机棒放在办公室,我在家里想远程唤醒开机,结果没开唤醒成功,mqtt也没回复唤醒结果,就想远程看一下开机棒的状态,开机棒又是用esp32做的,当然没法像ssh这种远程,就想可以把日志或者自身的状态发送给服务器,在服务器上查看信息。(最后发现是同事把我的开机棒连接的插座断电了o(╥﹏╥)o,像这种后续还要加一个掉电检测)
只需一个公网服务器即可,当然你如果你都是在局域网下也可以,不过这样也就没有远程查看的需求了。
公网服务器搭建mqtt服务器,然后配置webhook转发,最后再写一个脚本监听webhook的转发达到存取日志的目的。
搭建mqtt服务器没什么好说的,记得搭建好之后写一个开机自启的服务,这样就能使用systemctl去控制了
/lib/systemd/system/emqx.service[Unit]Description=emqxautostartAfter=network.target[Service]Type=forkingEnvironment=HOME=/root/app/emqx# 换成自己的emqx路径ExecStart=/root/app/emqx/bin/emqx start# 换成自己的emqx路径#ExecReload=/root/app/emqx/bin/emqxt restart # 换成自己的emqx路径ExecStop=/root/app/emqx/bin/emqx stop# 换成自己的emqx路径PrivateTmp=true[Install]WantedBy=multi-user.targethttp://服务器地址:端口号/url
规则:
验证了js和py两种版本的,实现效果完全一样,看自己的喜好
importosimportjsonfromdatetimeimportdatetimefromflaskimportFlask,requestimportloggingfromlogging.handlersimportRotatingFileHandler app=Flask(__name__)defensure_log_directory(date_str):"""确保日志目录存在"""script_dir=os.path.dirname(os.path.abspath(__file__))log_dir=os.path.join(script_dir,'logPy',date_str[:6])# 使用年月作为子目录名ifnotos.path.exists(log_dir):os.makedirs(log_dir,exist_ok=True)returnlog_dir@app.route('/py_webhook',methods=['POST'])defpy_webhook():# 获取请求体数据ifrequest.is_json:received_data=request.get_json()else:received_data=json.loads(request.data.decode('utf-8'))# 提取时间戳和有效载荷timestamp_ms=received_data.get('timestamp')payload=received_data.get('payload')iftimestamp_ms:# 将毫秒时间戳转换为datetime对象timestamp=datetime.fromtimestamp(timestamp_ms/1000.0)else:# 如果没有提供时间戳,使用当前时间timestamp=datetime.now()log_time=timestamp.strftime('%Y-%m-%d %H:%M:%S')# 使用年月格式作为目录名(如 "202601")month_str=timestamp.strftime('%Y%m')# 使用年月日格式作为文件名(如 "20260121.log")date_str=timestamp.strftime('%Y%m%d')# 确保日志目录存在log_dir=ensure_log_directory(month_str)log_file_path=os.path.join(log_dir,f'{date_str}.log')# 创建日志条目ifisinstance(payload,str):log_entry=f'[{log_time}]{payload}\n'else:log_entry=f'[{log_time}]{json.dumps(payload)}\n'# 写入日志文件try:withopen(log_file_path,'a',encoding='utf-8')aslog_file:log_file.write(log_entry)print(f'日志已写入:{log_file_path}',flush=True)exceptExceptionase:print(f'写入日志文件失败:{e}',flush=True)print(f'Received webhook payload:{payload}',flush=True)print(f'Received webhook timeStamp:{log_time}',flush=True)# 返回响应response_data={'status':'success','message':'Webhook received successfully'}returnjson.dumps(response_data),200,{'Content-Type':'application/json'}if__name__=='__main__':# 确保主日志目录存在script_dir=os.path.dirname(os.path.abspath(__file__))main_log_dir=os.path.join(script_dir,'logPy')ifnotos.path.exists(main_log_dir):os.makedirs(main_log_dir)app.run(host='0.0.0.0',port=3001,debug=False)# 使用localhost不行constexpress=require('express');constmysql=require('mysql');constfs=require('fs');constpath=require('path');constbodyParser=require('body-parser');constapp=express();// 使用 raw body parser 来捕获原始请求体 (既能处理json,又能文本)app.use(express.raw({type:()=>true,limit:'10mb'}));// 中间件:将原始 body 转换为适当的格式app.use((req,res,next)=>{// 尝试解析为 JSONtry{req.body=JSON.parse(req.body.toString());}catch(e){// 如果不是 JSON,保持原始字符串console.log("e : "+e+" "+req.body)req.body=req.body.toString();}next();});// 创建日志目录的函数functionensureLogDirectory(dateStr){constlogDir=path.join(__dirname,'logJs',dateStr);if(!fs.existsSync(logDir)){fs.mkdirSync(logDir,{recursive:true});}returnlogDir;}// 处理Webhook请求的路由处理程序app.post('/js_webhook',(req,res)=>{constreceivedData=req.body;// 获取Webhook请求正文中的数据// 提取时间戳和有效载荷consttimestampMs=receivedData.timestamp;constpayload=receivedData.payload;lettimeStamp=0;if(timestampMs){// 使用接收到的时间戳timeStamp=newDate(timestampMs);}else{timeStamp=newDate();console.log("timestamp is null! body : "+receivedData);}// 将时间戳转换为日志格式 (yyyy-MM-dd HH:mm:ss)constlogTime=timeStamp.getFullYear()+'-'+String(timeStamp.getMonth()+1).padStart(2,'0')+'-'+String(timeStamp.getDate()).padStart(2,'0')+' '+String(timeStamp.getHours()).padStart(2,'0')+':'+String(timeStamp.getMinutes()).padStart(2,'0')+':'+String(timeStamp.getSeconds()).padStart(2,'0');// 使用年月格式作为目录名(如 "202601")constmonthStr=timeStamp.getFullYear()+String(timeStamp.getMonth()+1).padStart(2,'0');// 使用年月日格式作为文件名(如 "20260121.log")constdateStr=timeStamp.getFullYear()+String(timeStamp.getMonth()+1).padStart(2,'0')+String(timeStamp.getDate()).padStart(2,'0');// 确保日志目录存在constlogDir=ensureLogDirectory(monthStr);constlogFilePath=path.join(logDir,`${dateStr}.log`);// 文件名格式为 YYYYMMDD.log// 创建日志条目constlogEntry=`[${logTime}]${JSON.stringify(payload)}\n`;// 写入日志文件fs.appendFile(logFilePath,logEntry,(err)=>{if(err){console.error('写入日志文件失败:',err);}else{console.log(`日志已写入:${logFilePath}`);}});console.log('Received webhook payload:',payload);console.log('Received webhook timeStamp:'+logTime+" "+timestampMs);res.status(200).json({status:'success',message:'Webhook received successfully'});});// 启动服务器监听指定端口(例如:3000)app.listen(3000,()=>{console.log('Server started on port 3000');});可以跟emqx那种服务也行,也可以配置rc.local脚本也行,看自己喜好,我这里提供了rc.local方式,rc.local存放到/etc/rc.local位置
# !!!都换成自己的路径!!!# 我用的是nvm的node,系统自带的版本太低nohup/root/.nvm/versions/node/v24.13.0/bin/node /root/sh/emqx_webhook/main.js>/root/sh/emqx_webhook/outputJs.log2>&1&nohuppython3 /root/sh/emqx_webhook/main.py>/root/sh/emqx_webhook/outputPy.log2>&1&可以看脚本运行的效果outputJs.log、outputPy.log,mqtt转发的日志按照日期文件夹存放到相应的文件夹
都是血的教训