基于 MQTT 的远程投饵机(喂鱼机):ESP32-C3 通过 360° 连续旋转舵机控制出料, 支持手机 App / MQTT / 本地网页 / 定时四种控制方式,全部投喂动作自动记录日志。
项目早期使用 LuatOS 固件(
main.lua),现已完全重写为 Rust,LuatOS 相关代码已移除, 仅保留 Rust 版本固件。MQTT 主题中的/luatos/前缀为兼容旧协议而保留。
| 部分 | 说明 |
|---|---|
servo_feeder/ |
设备固件:Rust(no_std,esp-hal 1.1 + esp-rtos + esp-radio + embassy),ESP32-C3(4MB Flash) |
app/(Flutter 工程) |
手机 App:Flutter,MQTT 远程控制 / 定时管理 / 投喂记录 / 局域网设备发现 |
doc/ |
设计文档与使用说明(中英双语) |
output/ |
发布产物:APK + 固件烧录包(合并镜像,可直接用 flash_download_tool 烧录) |
3dprint/ |
3D 打印模型(外壳 / 投饵支架 / 拉簧) |
- MQTT 远程控制:TLS(8883) 或明文(1883),自研精简 MQTT 3.1.1 客户端(QoS0),断线自动重连
- 定时投饵:NTP 自动对时,多组定时(时间/启用/次数),配置存 flash 断电不丢
- 本地网页:同一 WiFi 下浏览器访问设备 IP,手动投饵 / MQTT 设置 / 投喂记录 / 网络看门狗配置
- 投喂记录:flash 环形日志(500 条:时间/时长/转速/来源),网页与 MQTT 均可读取
- AP 配网:首次上电或长按按键进入配网模式,手机连接热点即可配置 WiFi 与 MQTT
- 设备发现:设备 UDP 广播(端口 8266),App 被动监听即可发现同网段设备
- 看门狗:硬件 RWDT(15s 超时)+ 可选网络看门狗(HTTP 探测,连续失败自动重启)
| 主题 | 方向 |
|---|---|
/luatos/sub/ep32c3-1 |
App/服务器 → 设备(设备 ID 可配置) |
/luatos/pub/<设备ID> |
设备 → App/服务器(按设备 ID 区分,多设备回执互不串扰) |
连接成功后设备发布 boot sucess(拼写沿用旧固件)。下行命令(JSON):
{"action":"trigger","second":2,"angle":180} // 以转速 180 出料 2 秒 → 回执 has been done{原payload}
{"action":"reboot"} // 远程重启 → 回执 do reboot{原payload}
{"action":"read_timer"} // 回读定时配置
{"action":"save_timer","timer_setting":[
{"time":"08:00","enable":true,"volume":2}]} // 保存定时配置
{"action":"read_log"} // 读最近 20 条投喂记录转速语义(360° 舵机,脉宽=转速/方向):90=停止,180=正转全速,0=反转全速。
| GPIO | 功能 |
|---|---|
| 9 | 舵机 PWM(50Hz,与 BOOT 键共脚) |
| 12 | 状态灯(MQTT 在线 / 配网模式常亮) |
| 13 | 动作灯(出料期间点亮) |
| 10 | 配网按键(长按 5s 进配网) |
| 6 | 广播按键(长按 5s 开启 UDP 广播) |
固件(需要 Rust nightly + espflash,详见 servo_feeder/README.md):
cd servo_feeder
cargo build --release
cargo run --release # espflash 烧录 + 串口监视App:
cd app
flutter build apk # 或直接安装 output/mqtt_remote.apk成品烧录:用 flash_download_tool 加载 output/firmware/servo-feeder-flash-4mb.bin,地址 0x0。
├── app/ # Flutter App(MQTT 控制 / 定时 / 记录 / 设置)
├── servo_feeder/ # Rust 固件(ESP32-C3)
├── doc/ # 设计文档 / 使用说明(中英双语)
├── output/ # 发布产物(APK + 固件烧录包)
├── 3dprint/ # 3D 打印模型
└── README.md
An MQTT-based remote fish feeder: an ESP32-C3 dispenses feed via a 360° continuous-rotation servo, with four control methods — mobile App / MQTT / local web page / scheduled feeding. Every feeding action is logged automatically.
The project originally ran a LuatOS firmware (
main.lua). It has been fully rewritten in Rust; all LuatOS-related code has been removed and only the Rust firmware is maintained. The/luatos/topic prefix is kept verbatim for backward protocol compatibility.
| Part | Description |
|---|---|
servo_feeder/ |
Device firmware: Rust (no_std; esp-hal 1.1 + esp-rtos + esp-radio + embassy) for ESP32-C3 (4MB flash) |
app/ (Flutter project) |
Mobile app: Flutter — MQTT remote control, schedule management, feed logs, LAN device discovery |
doc/ |
Design document & user guide (bilingual Chinese/English) |
output/ |
Release artifacts: APK + firmware flash bundle (merged image, flash with flash_download_tool) |
3dprint/ |
3D printable models (enclosure / feed bracket / tension spring) |
- MQTT remote control: TLS (8883) or plaintext (1883); hand-rolled minimal MQTT 3.1.1 client (QoS0 subset) with automatic reconnection
- Scheduled feeding: NTP time sync; multiple schedules (time/enabled/count) persisted in flash
- Local web page: browse to the device IP on the same LAN — manual feeding, MQTT settings, feed logs, network watchdog configuration
- Feed logs: ring log in flash (500 entries: timestamp/duration/speed/source), readable via web page and MQTT
- AP provisioning: entered on first boot or long-press; connect to the hotspot to configure WiFi and MQTT from a phone
- Device discovery: the device broadcasts UDP packets (port 8266); the app passively listens to discover devices on the same subnet
- Watchdogs: hardware RWDT (15s timeout) + optional network watchdog (HTTP probe, auto-reboot after consecutive failures)
| Topic | Direction |
|---|---|
/luatos/sub/ep32c3-1 |
App/server → device (device ID configurable) |
/luatos/pub/<device ID> |
device → App/server (per-device; acks from multiple devices don't mix) |
On connect the device publishes boot sucess (spelling kept from the legacy firmware).
Downlink commands (JSON):
{"action":"trigger","second":2,"angle":180} // run at speed 180 for 2s -> ack: has been done{orig payload}
{"action":"reboot"} // remote reboot -> ack: do reboot{orig payload}
{"action":"read_timer"} // read schedule config
{"action":"save_timer","timer_setting":[
{"time":"08:00","enable":true,"volume":2}]} // save schedule config
{"action":"read_log"} // read the latest 20 feed-log entriesSpeed semantics (360° servo, pulse width = speed/direction): 90 = stop, 180 = full speed forward, 0 = full speed reverse; linear in between.
| GPIO | Function |
|---|---|
| 9 | Servo PWM (50Hz, shared with the BOOT button) |
| 12 | Status LED (solid on when MQTT online / in provisioning mode) |
| 13 | Action LED (on while dispensing) |
| 10 | Provisioning button (long-press 5s to enter provisioning) |
| 6 | Broadcast button (long-press 5s to enable UDP broadcast) |
Firmware (requires Rust nightly + espflash, see servo_feeder/README.md):
cd servo_feeder
cargo build --release
cargo run --release # flash via espflash + serial monitorApp:
cd app
flutter build apk # or install output/mqtt_remote.apk directlyPrebuilt flashing: load output/firmware/servo-feeder-flash-4mb.bin in flash_download_tool
at address 0x0.
├── app/ # Flutter app (MQTT control / schedule / logs / settings)
├── servo_feeder/ # Rust firmware (ESP32-C3)
├── doc/ # Design document / user guide (bilingual)
├── output/ # Release artifacts (APK + firmware flash bundle)
├── 3dprint/ # 3D printable models
└── README.md