2026/10/22 18:47:32

微信小程序智慧农业平台全栈拆解:从设备上报到MySQL落库

微信小程序智慧农业平台全栈拆解:从设备上报到MySQL落库 简介这份微信小程序智慧农业平台源码包面向计算机、物联网、通信工程等专业的高校学生与开发者可用于毕业设计、课程设计、项目立项演示或自学进阶。项目采用前后端分离架构并集成物联网设备数据采集与控制完整覆盖从终端到云端的业务链路。压缩包共441个文件约3.59MB以js逻辑脚本、json配置、wxss样式、wxml页面结构为主另有png图片资源、tsx与ts组件、md说明文档及wxs脚本等目录组织清晰便于按模块阅读与二次开发。已有56人学习关注。资源包含设计文档与可运行源码读者可借此掌握小程序页面搭建、接口联调、数据库迁移及设备接入等关键环节也能在此基础上修改功能直接用于毕设、课设或作业提交遇到配置与运行问题还可获得远程指导适合希望快速上手智慧农业类项目的学习者。1. 从一份小程序毕设包说起智慧农业平台到底能跑通什么去年帮几个做物联网方向的同学看毕设十份里有七份是智慧农业——但真正能把小程序、后端、设备端三端数据打通的不到两份。大部分是前端页面写得很漂亮点一下浇水按钮后端返回一个写死的{status: ok}传感器数据全靠Math.random()生成。这份《微信小程序智慧农业平台前端后端物联网设备集成》的资源包从文件结构看至少把三端都放进去了index.html和index.css是前端展示层多个index.js分散在不同目录对应小程序页面逻辑和后端服务migrate_to_mysql.js说明数据层用的是 MySQL 而非内存存储publish.js大概率是设备端数据上报脚本。如果你正在找一个小程序毕设的完整骨架或者想看看物联网数据从设备到小程序这条链路在代码层面怎么串起来这份资源值得拆开看。它适合计算机、物联网、通信工程方向的学生做课设或毕设起步也适合刚接触小程序全栈的开发者拿来做二次开发。2. 拆包先看目录三端文件怎么分工拿到一个压缩包我习惯先不急着跑而是把目录结构过一遍。这份资源的文件命名不算规范——多个index.js同名散落在不同目录如果不先理清谁是谁后面调试会非常痛苦。2.1 前端层小程序页面与静态资源从文件清单看前端部分包含index.html、index.css和若干image3.jpeg这类图片资源。这里有个容易误判的点微信小程序的页面文件标准后缀是.wxml和.wxss而不是.html和.css。所以有两种可能——要么这份资源的前端其实是一个 H5 页面用index.html做展示通过 web-view 嵌入小程序要么是开发者用了某种转换工具把 HTML 转成了 WXML。不管是哪种你拿到手第一件事是确认前端入口文件的实际类型。常见做法是打开index.html看里面有没有script引用小程序 API比如wx.开头的调用。如果有说明它是混合方案如果没有那它就是一个纯 H5 展示页需要你手动迁移到小程序原生页面。# 先看目录结构理清文件分布 find . -type f -name *.js | head -20 find . -type f -name *.html -o -name *.css -o -name *.json这段命令的作用是快速列出所有 JS 文件和前端资源文件的位置。find . -type f表示从当前目录递归查找普通文件-name *.js按扩展名过滤。跑完之后你会得到一张文件地图后面改代码时就知道该去哪个目录找。2.2 后端层Node.js 服务与 MySQL 迁移脚本migrate_to_mysql.js这个文件名信息量很大。它说明项目原本可能用的是 SQLite 或直接读 JSON 文件后来迁移到了 MySQL。迁移脚本通常包含建表语句、字段映射和数据导入逻辑。你需要在跑起来之前先确认两件事MySQL 版本是否兼容脚本里的语法以及连接配置写在哪里。// migrate_to_mysql.js 典型结构根据文件名推断 const mysql require(mysql2); const connection mysql.createConnection({ host: localhost, // 数据库地址本地开发一般不改 user: root, // 用户名按自己环境改 password: your_pass, // 密码必须改 database: smart_farm // 库名建议保持默认 }); // 建表传感器数据表 connection.query( CREATE TABLE IF NOT EXISTS sensor_data ( id INT AUTO_INCREMENT PRIMARY KEY, device_id VARCHAR(50) NOT NULL, temperature FLOAT, humidity FLOAT, soil_moisture FLOAT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) , (err) { if (err) console.error(建表失败:, err.message); else console.log(sensor_data 表就绪); });上面这段是根据migrate_to_mysql.js这个文件名推断的典型结构。关键参数有三个host一般本地开发不用改user和password必须换成你自己 MySQL 的配置database如果脚本里写死了库名你得先在 MySQL 里CREATE DATABASE smart_farm否则连接会直接报ER_BAD_DB_ERROR。字段方面device_id是设备唯一标识temperature、humidity、soil_moisture对应温度、湿度、土壤含水量created_at用TIMESTAMP自动记录写入时间——这个设计在后续做历史曲线查询时很关键。2.3 设备端publish.js 的数据上报逻辑publish.js从命名看是设备端的数据发布脚本。物联网设备通常通过 MQTT 协议把传感器读数发到 broker后端再订阅 topic 拿数据。如果这份资源用的是 HTTP 轮询而不是 MQTT那publish.js里应该是一个setInterval定时向后台接口 POST 数据。// publish.js 模拟设备上报HTTP 方式 const axios require(axios); const DEVICE_ID sensor_001; // 设备编号多设备时需区分 const REPORT_INTERVAL 5000; // 上报间隔单位毫秒 setInterval(async () { const payload { device_id: DEVICE_ID, temperature: (20 Math.random() * 10).toFixed(1), // 模拟 20-30 度 humidity: (40 Math.random() * 30).toFixed(1), // 模拟 40-70% soil_moisture: (30 Math.random() * 40).toFixed(1) // 模拟 30-70% }; try { await axios.post(http://localhost:3000/api/report, payload); console.log(上报成功:, payload); } catch (e) { console.error(上报失败:, e.message); } }, REPORT_INTERVAL);这段代码的核心逻辑是每隔 5 秒生成一组模拟传感器数据通过 POST 请求发到后端/api/report接口。DEVICE_ID在多设备场景下必须唯一否则后端无法区分数据来源。REPORT_INTERVAL设 5000 毫秒是常见做法太快会给后端压力太慢曲线会断。注意toFixed(1)把浮点数转成一位小数的字符串如果后端字段类型是FLOATMySQL 会自动转换但如果接口做了严格类型校验可能会报错——这是实际调试中容易翻车的地方。3. 把项目跑起来环境配置与启动顺序文件看明白了接下来是让它真正跑起来。三端项目的启动有先后顺序搞错了就会出现前端报跨域、后端连不上库、设备端发出去没人收的连锁问题。3.1 后端服务启动与数据库初始化先起后端因为前端和设备端都依赖它。Node.js 项目第一步永远是装依赖。# 进入后端目录假设后端在 server/ 或根目录 cd server # 如果后端就在根目录则跳过 # 安装依赖 npm install # 先跑迁移脚本建表 node migrate_to_mysql.js # 启动后端服务 node index.js # 或者如果 package.json 里有 start 脚本 npm startnpm install会根据package.json里的dependencies字段拉取所有依赖包。如果项目没有package.json有些毕设包确实会漏你需要手动装npm install express mysql2 axios。node migrate_to_mysql.js必须在启动服务之前跑否则后端第一次查询就会因为表不存在而报ER_NO_SUCH_TABLE。启动成功后终端一般会打印Server running on port 3000之类的信息记下这个端口号后面配前端接口地址要用。提示如果npm install卡住不动先检查 npm 源。国内环境常见做法是临时指定镜像源但不要写进项目配置文件里否则换台机器就失效。3.2 小程序端配置与接口地址修改小程序端要跑起来首先得有微信开发者工具。导入项目时选择包含app.json的目录——如果这份资源里没有app.json说明前端可能还是 H5 形态你需要先在开发者工具里新建一个小程序项目再把页面文件迁移过去。接口地址是第二个要改的地方。后端跑在localhost:3000但小程序开发者工具里的请求默认走的是https://且需要配置合法域名。本地调试阶段在开发者工具右上角详情里勾选不校验合法域名就能绕过。// 小程序端 request 封装常见写法 const BASE_URL http://localhost:3000; // 改成你后端实际地址 function request(path, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: ${BASE_URL}${path}, method, data, success: (res) { if (res.statusCode 200) resolve(res.data); else reject(new Error(HTTP ${res.statusCode})); }, fail: reject }); }); } // 调用示例获取最新传感器数据 request(/api/latest).then(data { console.log(温度:, data.temperature); }).catch(err console.error(err));BASE_URL是唯一需要改的配置项指向后端服务地址。wx.request是小程序原生网络请求 API返回的是 Promise 包装后的结果。statusCode 200判断是基础容错实际项目里还要处理 401、500 等状态。注意localhost在小程序真机上是不通的——真机调试时要把BASE_URL换成电脑的局域网 IP比如http://192.168.1.100:3000且手机和电脑必须在同一网络下。3.3 设备端上报脚本运行与数据验证后端和小程序都就绪后最后启动publish.js。# 新开一个终端窗口运行设备上报脚本 node publish.js跑起来后你会看到终端每隔 5 秒打印一条上报成功。这时候去小程序页面刷新应该能看到传感器数值在变化。如果小程序端数据不动按这个顺序排查先看publish.js终端有没有报错再看后端终端有没有收到 POST 请求的日志最后检查小程序请求的接口路径和后端路由是否一致。验证数据是否真正落库最直接的方式是查 MySQL-- 查最近 10 条传感器数据 SELECT device_id, temperature, humidity, soil_moisture, created_at FROM sensor_data ORDER BY created_at DESC LIMIT 10;如果这条 SQL 能查出数据说明设备端 → 后端 → 数据库这条链路是通的。查不出来就回到上一步看后端有没有报错。ORDER BY created_at DESC保证最新的数据排在最前面LIMIT 10避免一次拉太多。4. 避坑指南跑这个包最容易翻车的五个地方这份资源的功能覆盖是完整的但毕设包的通病它基本都有——配置硬编码、依赖版本锁死、文档缺失。下面五条是我实际拆这类项目时踩过的坑按出现频率从高到低排。4.1 数据库连接报 ER_ACCESS_DENIED_ERROR现象跑migrate_to_mysql.js或启动后端时终端报Access denied for user rootlocalhost。原因脚本里的数据库密码是原作者本机的跟你环境不一致。有些包甚至写的是空密码或123456。解决找到数据库配置文件通常在migrate_to_mysql.js和后端index.js里各有一处把password改成你本机 MySQL 的实际密码。如果 MySQL 8.0 还报ER_NOT_SUPPORTED_AUTH_MODE是因为 8.0 默认用caching_sha2_password插件而旧版mysql包不支持换成mysql2包即可。4.2 小程序请求报不在以下 request 合法域名列表中现象小程序模拟器里点按钮没反应控制台报域名校验失败。原因微信开发者工具默认开启域名校验而localhost不在合法域名列表里。解决开发者工具右上角详情 → 本地设置 → 勾选不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书。这只在开发阶段有效上线前必须把接口部署到已备案的 HTTPS 域名并配置到小程序后台。4.3 publish.js 跑起来但后端收不到数据现象publish.js终端显示上报成功但数据库里没数据后端也没日志。原因publish.js里的接口地址写的是http://localhost:3000/api/report但后端实际路由可能是/api/data/report或/report路径不匹配时后端返回 404而publish.js的catch可能只打印了e.message没打印状态码看起来像成功。解决在后端index.js里搜app.post找到实际路由路径跟publish.js里的 URL 对齐。同时把publish.js的错误处理改成打印完整响应console.error(上报失败:, e.response?.status, e.response?.data)。4.4 多个 index.js 改错文件现象改了半天代码重启服务后行为没变化。原因这份资源里有多个同名index.js分布在小程序页面目录、后端目录、可能还有工具目录。你改的那个不是实际运行的那个。解决用find . -name index.js -exec grep -l app.listen {} \;找到真正启动 HTTP 服务的那个文件。-exec grep -l表示对每个找到的文件执行 grep 并只输出匹配的文件名app.listen是 Express 启动服务的标志性调用。4.5 传感器数据曲线不刷新现象小程序页面能显示数据但数值一直不变手动下拉刷新才更新。原因页面没有做定时轮询或 WebSocket 推送只在onLoad时请求了一次。解决在页面onShow里加setInterval定时请求页面onHide时clearInterval。间隔建议 5-10 秒跟设备上报频率对齐。如果项目用的是 MQTT检查小程序端wx.connectSocket的连接状态和 topic 订阅是否正确。5. 二次开发切入点从能跑到能用还差哪几步把项目跑通只是第一步。毕设答辩时老师最爱问的两个问题是你的创新点在哪和这个数据是真的吗。前者需要你在现有骨架上加功能后者需要你把模拟数据换成真实传感器。5.1 用真实传感器替换模拟数据publish.js里的Math.random()是模拟数据换成真实传感器有两种常见路径。一种是 ESP8266/ESP32 通过串口读 DHT11 温湿度传感器再用 HTTP 或 MQTT 发出去另一种是树莓派上用 Python 读 GPIO通过requests库 POST 到后端。不管哪种后端接口不用改只需要把publish.js里的数据生成逻辑替换成传感器读取逻辑。// 以串口读取为例需要 npm install serialport const SerialPort require(serialport); const Readline require(serialport/parser-readline); const port new SerialPort({ path: COM3, baudRate: 9600 }); const parser port.pipe(new Readline({ delimiter: \n })); parser.on(data, async (line) { // 假设传感器输出格式为 T:25.3,H:60.1,S:45.2 const parts line.trim().split(,); const payload { device_id: sensor_001, temperature: parseFloat(parts[0].split(:)[1]), humidity: parseFloat(parts[1].split(:)[1]), soil_moisture: parseFloat(parts[2].split(:)[1]) }; await axios.post(http://localhost:3000/api/report, payload); });path: COM3是 Windows 下的串口号Linux 下一般是/dev/ttyUSB0Mac 下是/dev/tty.usbserial-xxx。baudRate: 9600要和传感器固件里的波特率一致不一致会读到乱码。delimiter: \n表示按换行符切分数据帧如果你的传感器固件不是按行输出这里要改。5.2 加一个历史数据查询接口现有接口大概率只有最新数据和上报数据两个。答辩时老师往往会问能不能看历史曲线这时候你需要加一个按时间范围查询的接口。// 后端新增GET /api/history?device_idxxxhours24 app.get(/api/history, async (req, res) { const { device_id, hours 24 } req.query; const sql SELECT temperature, humidity, soil_moisture, created_at FROM sensor_data WHERE device_id ? AND created_at DATE_SUB(NOW(), INTERVAL ? HOUR) ORDER BY created_at ASC ; try { const [rows] await pool.query(sql, [device_id, parseInt(hours)]); res.json({ code: 0, data: rows }); } catch (e) { res.status(500).json({ code: 1, msg: e.message }); } });DATE_SUB(NOW(), INTERVAL ? HOUR)是 MySQL 的日期减法函数NOW()取当前时间减掉hours小时得到起始时间点。parseInt(hours)是必须的因为req.query拿到的都是字符串直接传给 MySQL 的INTERVAL会报语法错误。返回结果按created_at ASC升序排列方便前端直接画折线图。5.3 小程序端加 ECharts 折线图小程序原生没有图表组件常见做法是用echarts-for-weixin。把ec-canvas组件拷到项目里在页面 JSON 里注册然后onReady时初始化图表实例把/api/history返回的数据喂给series[0].data。// 页面 js 中初始化图表 import * as echarts from ../../components/ec-canvas/echarts; function initChart(canvas, width, height, dpr) { const chart echarts.init(canvas, null, { width, height, devicePixelRatio: dpr }); canvas.setChart(chart); chart.setOption({ xAxis: { type: category, data: [] }, yAxis: { type: value }, series: [{ type: line, data: [], smooth: true }] }); return chart; } // 拿到历史数据后更新 request(/api/history?device_idsensor_001hours24).then(res { const times res.data.map(item item.created_at.slice(11, 16)); // 取 HH:mm const temps res.data.map(item item.temperature); chart.setOption({ xAxis: { data: times }, series: [{ data: temps }] }); });slice(11, 16)是从2024-01-15 14:30:00这种格式里截出14:30只显示时分让 X 轴不至于太挤。smooth: true让折线变平滑视觉上更像真实传感器曲线。注意ec-canvas的初始化必须在onReady之后在onLoad里调会拿不到 canvas 节点。从那以后我每次拿到毕设包都强制先跑一遍find . -name *.js | xargs grep -l listen\|connect把真正的入口文件找出来再动手改配置。这份智慧农业平台的骨架是完整的三端链路能跑通剩下的就是往里面填真实数据和业务逻辑。希望帮到你。本文还有配套的精品资源点击获取