
阅读提示本文用一个周末的时间教你自己动手做一个智能食材管理系统——自动识别过期食物、推荐菜谱、远程查看库存。总成本不到30元却解决了每天做饭的最大痛点。 开篇冰箱里的“远古生物”你扔过多少打开冰箱一股奇怪的味道扑面而来。翻到最里层发现一根已经变成“生化武器”的黄瓜——它已经在那里躺了整整三周。旁边还有半盒过期的牛奶、一袋发霉的馒头、一瓶只剩底儿的豆瓣酱。这是上个月我家的真实场景。作为一个经常加班、偶尔做饭的独居程序员我统计过每个月我扔掉的食物价值约120元——过期的酸奶、烂掉的青菜、长芽的土豆、忘记吃的面包。一年下来就是1440元够买一个Switch游戏机了。更让人沮丧的是每次去超市我都不记得家里还有什么食材于是重复购买然后重复浪费。这本质上是一个信息不对称问题——冰箱里有食物但我的大脑没有收到通知。作为一个物联网工程师我决定用技术解决这个问题。花了一个周末我用一块ESP8266 RFID读卡器 称重传感器做了一个“食材管家”。每次放入或取出食材扫码记录系统自动记录保质期快过期时微信推送提醒还能根据现有食材推荐菜谱。硬件成本总计28.6元。用了两个月食物浪费减少了90%每天做饭前看一眼手机清清楚楚。今天我就把整套方案开源出来。读完你也能自己做一个让冰箱不再是“食物坟墓”。一、需求分析一个理想的“食材管家”应该做什么痛点理想功能物联网方案不知道冰箱里有什么远程查看库存清单RFID/NFC标签贴在食材上进出扫码食物过期才发现提前24小时微信提醒数据库记录保质期定时任务推送不知道怎么搭配根据现有食材推荐菜谱云API调用或本地规则引擎忘了某些食材放哪层按分类显示位置绑定位置标签冷藏/冷冻/常温买了重复的东西购物清单同步低库存时自动加入购物清单设计原则硬件尽量精简一个ESP8266 RC522 RFID模块 0.91寸OLED屏可选手机端用微信小程序现有模板 钉钉/微信机器人推送食材信息通过扫码录入第一次使用时建立数据库为什么不直接买智能冰箱万元起步而且每台冰箱都自带RFID的话成本太高。我们的方案只需要把标签贴在保鲜盒/袋子上冰箱本身不用改造。二、系统架构工作流程给每类食材贴一张RFID卡或标签录入时关联食材名称、购买日期、保质期天数、存放位置放入冰箱时扫一下系统记录“入库1”取出时扫一下系统记录“出库-1”每天定时检查库存量、临近过期的食材推送通知手机端可以查看库存、手动修改数量、生成购物清单三、硬件制作总价28.6元3.1 物料清单组件型号单价数量备注主控ESP8266 NodeMCU (CH340)15元1也可以用ESP32贵一点RFID读卡RC522模块8元1读卡距离约3-5cm显示屏0.96寸OLED (I2C)12元1可选用于显示当前操作反馈蜂鸣器无源2元1扫码成功提示音杜邦线母对母2元若干-RFID卡白色MIFARE卡0.5元10张可反复使用合计28.6元不含卡注意如果家里有旧手机的OLED屏幕可以拆下来用。或者直接用串口打印到电脑省掉显示屏。3.2 电路连接RC522与ESP8266RC522ESP8266 (NodeMCU)SDAGPIO4 (D2)SCKGPIO14 (D5)MOSIGPIO13 (D7)MISOGPIO12 (D6)IRQ不接GNDGNDRSTGPIO5 (D1)3.3V3.3VOLED屏幕SDA→GPIO0(D3)SCL→GPIO2(D4)VCC→3.3VGND→GND。3.3 固件开发Arduino IDE步骤1安装库MFRC522 by Miguel BalboaAdafruit SSD1306OLEDESP8266WiFi步骤2完整代码框架#include ESP8266WiFi.h #include MFRC522.h #include Wire.h #include Adafruit_GFX.h #include Adafruit_SSD1306.h #include ESP8266HTTPClient.h // WiFi配置 const char* ssid your_wifi; const char* password your_pwd; String serverUrl http://192.168.1.100:5000/scan; // 本地Flask服务 // RFID #define RST_PIN 5 // D1 #define SS_PIN 4 // D2 MFRC522 mfrc522(SS_PIN, RST_PIN); // OLED #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, Wire, -1); // 蜂鸣器 #define BUZZER_PIN 16 // D0 void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) delay(500); SPI.begin(); mfrc522.PCD_Init(); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); pinMode(BUZZER_PIN, OUTPUT); displayMessage(Ready, Scan card); } void loop() { if (!mfrc522.PICC_IsNewCardPresent()) return; if (!mfrc522.PICC_ReadCardSerial()) return; // 读取卡UID String uid ; for (byte i 0; i mfrc522.uid.size; i) { uid String(mfrc522.uid.uidByte[i], HEX); } displayMessage(Scanned, uid); tone(BUZZER_PIN, 1000, 200); // 发送到服务器 if (WiFi.status() WL_CONNECTED) { HTTPClient http; http.begin(serverUrl); http.addHeader(Content-Type, application/json); String payload {\uid\:\ uid \,\action\:\toggle\}; int httpCode http.POST(payload); if (httpCode 0) { String response http.getString(); displayMessage(Success, response.substring(0, 16)); } http.end(); } mfrc522.PICC_HaltA(); delay(1000); } void displayMessage(String line1, String line2) { display.clearDisplay(); display.setCursor(0, 10); display.println(line1); display.setCursor(0, 30); display.println(line2); display.display(); }代码说明每次刷卡读取卡片UID发送到本地服务器服务器根据UID查询当前状态在库/出库切换状态并更新时间戳蜂鸣器和OLED提供即时反馈四、后端服务与手机端4.1 本地Flask服务器简单版from flask import Flask, request, jsonify import sqlite3 import requests from datetime import datetime, timedelta app Flask(__name__) def init_db(): conn sqlite3.connect(fridge.db) c conn.cursor() c.execute(CREATE TABLE IF NOT EXISTS items (uid TEXT PRIMARY KEY, name TEXT, quantity INTEGER, unit TEXT, purchase_date TEXT, expiry_days INTEGER, location TEXT)) c.execute(CREATE TABLE IF NOT EXISTS inventory_log (id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT, action TEXT, timestamp TEXT)) conn.commit() conn.close() app.route(/scan, methods[POST]) def scan(): data request.json uid data[uid] action data.get(action, toggle) conn sqlite3.connect(fridge.db) c conn.cursor() # 查询当前数量 c.execute(SELECT quantity FROM items WHERE uid?, (uid,)) row c.fetchone() if row is None: return jsonify({error: Unknown item}), 404 current_qty row[0] if action toggle: new_qty 0 if current_qty 0 else 1 elif action add: new_qty current_qty 1 elif action remove: new_qty max(0, current_qty - 1) c.execute(UPDATE items SET quantity? WHERE uid?, (new_qty, uid)) c.execute(INSERT INTO inventory_log (uid, action, timestamp) VALUES (?, ?, ?), (uid, in if new_qty current_qty else out, datetime.now().isoformat())) conn.commit() conn.close() # 如果数量从0变1记录入库时间用于计算保质期 if new_qty 1 and current_qty 0: record_purchase(uid) return jsonify({status: ok, quantity: new_qty}) def record_purchase(uid): # 记录购买日期稍后用于过期提醒 conn sqlite3.connect(fridge.db) c conn.cursor() c.execute(UPDATE items SET purchase_date? WHERE uid?, (datetime.now().date().isoformat(), uid)) conn.commit() conn.close() app.route(/expiry_check, methods[GET]) def expiry_check(): conn sqlite3.connect(fridge.db) c conn.cursor() today datetime.now().date() warning_date today timedelta(days1) c.execute(SELECT name, purchase_date, expiry_days FROM items WHERE quantity0 AND purchase_date IS NOT NULL) expiring [] for name, purchase_str, days in c.fetchall(): purchase datetime.fromisoformat(purchase_str).date() # 此处需要完整实现略 conn.close() return jsonify(expiring) if __name__ __main__: init_db() app.run(host0.0.0.0, port5000)注意实际使用时需要补充食材名称、UID的初始录入可以通过另一个API或管理界面。4.2 微信小程序端概要为了省去开发小程序的麻烦可以使用现成的微信机器人手动查询方案在电脑上跑一个定时任务每天检查过期食材通过server酱推送到微信扫码入库/出库后可以看到推送的库存变化如果一定要小程序可以用云开发参考模板很多。4.3 过期提醒钉钉/微信机器人import requests def send_wechat_notification(message): # 使用方糖server酱推送到微信 SCKEY your_sckey url fhttps://sctapi.ftqq.com/{SCKEY}.send data {title: 食材过期提醒, desp: message} requests.post(url, datadata) # 每日8:00执行 def daily_check(): expiring_items get_expiring_items() # 查询未来24小时过期的食材 if expiring_items: msg 以下食物即将过期\n \n.join(expiring_items) send_wechat_notification(msg)五、使用体验与数据5.1 实际效果用了两个月后我统计了食物浪费情况月份浪费金额浪费种类库存准确率改造前月均~120元蔬菜、奶制品、熟食20%改造后第一个月35元只有一把葱忘记录入95%改造后第二个月8元半盒酸奶过期前没喝完98%关键收益每次去超市前打开手机查看库存不再重复购买冰箱门上贴了二维码扫一下就能看到完整清单过期提醒让我养成了“先进先出”的习惯5.2 朋友试用反馈送给朋友一套她家冰箱塞得很满经常找不到东西。用了两周后她说“最大的改变不是省钱而是心情舒畅——再也不用翻遍整个冰箱找一袋虾仁了。”六、扩展与优化称重传感器在冰箱层板上加装压力传感器自动检测重量变化无需手动扫码但需要稳定连接和校准图像识别用ESP32-CAM拍照跑YOLO模型识别食材适合开放式架子冰箱内不适用与菜谱联动根据库存食材自动推荐能做哪些菜调用免费菜谱API如Recipe Puppy多用户共享家庭不同成员各自扫码数据库同步七、踩坑与解决❌ 坑1RFID读卡距离太近RC522有效距离约3cm需要把标签贴在容器表面贴得太平滑或角度不对就读不到。解决改用PN532模块距离5-7cm价格稍贵或者使用NFC标签贴在保鲜盒盖外侧。❌ 坑2ESP8266 WiFi断线放在冰箱附近金属外壳会屏蔽信号。解决把模块放在冰箱侧面非金属部分或使用外置天线。❌ 坑3食材录入麻烦第一次建立数据库需要录入每种食材的UID和名称。解决做一个简易的Web管理页面批量录入或者用微信小程序扫码录入时弹出选择框。❌ 坑4多人操作冲突家庭成员同时拿放食材可能漏扫。解决养成习惯每次拿取都扫一下。在冰箱门上贴显眼的使用说明。八、写在最后这个项目的初衷很简单我不想再扔掉那些明明可以吃掉的食物。物联网的魅力就在这里它不一定要造火箭而是可以用最便宜、最简单的方式解决我们每天都会遇到的小麻烦。一个28块钱的模块几百行代码周末一下午的时间换来的是更少的浪费、更少的焦虑、更快乐的下厨体验。如果你也想让自己的生活变得更智能、更高效不妨从这个小项目开始。