Compare commits
2 Commits
10525dcfcc
...
9b13d413c4
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b13d413c4 | |||
| cd0f51057a |
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
100
README.md
100
README.md
@@ -1,3 +1,99 @@
|
||||
# pig-house-controller
|
||||
# 猪舍主控
|
||||
|
||||
猪舍主控节点,根据上位机的指令控制当前猪舍内所有设备(传感器,阀门,电机等),并汇聚当前猪舍传感器数据统一上报
|
||||
## 简介
|
||||
|
||||
猪舍主控系统根据上位机的指令控制当前猪舍内所有设备(传感器,阀门,电机等),并汇聚当前猪舍传感器数据统一上报。本系统作为猪场智能化管理的重要组成部分,实现了猪舍环境的自动化监控与调节。
|
||||
|
||||
## 功能概述
|
||||
|
||||
### 与上位机交互
|
||||
1. 根据上位机指令定期采集栏内所有传感器的数据,并统一上报
|
||||
2. 根据上位机指令启动或关闭栏内设备,上位机发送的启动指令分两种:
|
||||
- 常开指令:收到后启动设备,直到收到关闭指令后关闭设备
|
||||
- 短暂开启指令:收到后启动设备,但需要上位机每过两秒发送一次指令,超过五秒没收到下一个开启指令或受到关闭指令将会关闭设备
|
||||
3. 定期检查栏内设备状态,发现异常立即上报上位机
|
||||
4. 定期向上位机发送心跳包
|
||||
5. 接收上位机发送的总线上各机器的位置和类型
|
||||
6. 根据上位机指令调整设备功率大小
|
||||
7. 接收上位机批量控制指令并执行
|
||||
8. 接收上位机发送的配置信息
|
||||
|
||||
### 与设备交互
|
||||
1. 控制栏内设备启停
|
||||
2. 调整风机等功率可调设备的功率
|
||||
3. 定时检查栏内设备状态
|
||||
4. 定时采集栏内数据
|
||||
|
||||
### 数据管理
|
||||
1. 保存总线上各机器的位置和类型
|
||||
2. 临时保存上位机发送的指令
|
||||
3. 保存上位机发送的配置信息
|
||||
4. 汇总栏内所有传感器数据
|
||||
5. 临时保存栏内设备故障信息,直到上报成功后清除
|
||||
6. 根据批量指令控制对应设备工作
|
||||
|
||||
## 系统架构
|
||||
|
||||
### 通信协议
|
||||
- 上位机和猪舍主控间通过LoRa协议互联
|
||||
- 主控与设备/传感器通过Modbus RTU协议通信
|
||||
|
||||
### 硬件组成
|
||||
- 主控制器:树莓派PICO RP2040
|
||||
- 通信接口:RS485总线、LoRa无线模块
|
||||
- 传感器:硫化氢、氨气、二氧化碳、光照、温度、湿度、风速、气压等
|
||||
- 执行设备:风机、水帘、喷淋系统、除臭水帘、刮粪机、电磁阀等
|
||||
|
||||
### 硬件拓扑图
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[猪场主控] -->|LoRa| B[猪舍主控]
|
||||
B -->|Modbus RTU| C[RS485总线1]
|
||||
B -->|Modbus RTU| D[RS485总线2]
|
||||
C -->|Modbus RTU| E1[硫化氢传感器]
|
||||
C -->|Modbus RTU| E2[氨气传感器]
|
||||
C -->|Modbus RTU| E3[二氧化碳传感器]
|
||||
C -->|Modbus RTU| E4[光照传感器]
|
||||
C -->|Modbus RTU| E5[温度传感器]
|
||||
C -->|Modbus RTU| E6[湿度传感器]
|
||||
C -->|Modbus RTU| E7[风速传感器]
|
||||
C -->|Modbus RTU| E8[气压传感器]
|
||||
D -->|Modbus RTU| F[继电器]
|
||||
F -->|通/断电| G1[风机]
|
||||
F -->|通/断电| G2[水帘]
|
||||
F -->|通/断电| G3[栏内喷淋]
|
||||
F -->|通/断电| G4[除臭水帘]
|
||||
F -->|通/断电| G5[刮粪机]
|
||||
F -->|通/断电| H[电磁五通阀]
|
||||
H -->|通/断气| I[气动三通阀]
|
||||
```
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 硬件准备
|
||||
1. 树莓派 PICO RP2040 开发板
|
||||
2. RS485 转 UART 模块
|
||||
3. LoRa 模块 (如 SX1276/SX1278)
|
||||
4. 传感器和设备(如上述硬件拓扑图所示)
|
||||
|
||||
### 安装步骤
|
||||
1. 按照硬件拓扑图连接所有设备和传感器
|
||||
2. 将程序上传到树莓派 PICO
|
||||
3. 配置系统参数和设备信息
|
||||
4. 启动系统
|
||||
|
||||
### 启动系统
|
||||
```bash
|
||||
# 使用 mpremote 运行主程序
|
||||
mpremote connect [设备路径] run main.py
|
||||
```
|
||||
或者直接将 main.py 设置为启动脚本,让 PICO 上电后自动运行
|
||||
|
||||
## 开发相关
|
||||
|
||||
详细的开发指南、项目结构、配置说明和技术规范请参考 [DEVELOPMENT.md](DEVELOPMENT.md) 文件。
|
||||
|
||||
## 许可证
|
||||
|
||||
禁止未经授权使用本项目代码,否则后果自负。
|
||||
1
comms/__init__.py
Normal file
1
comms/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# 通信层
|
||||
1
comms/base_comm.py
Normal file
1
comms/base_comm.py
Normal file
@@ -0,0 +1 @@
|
||||
# 通信接口
|
||||
1
comms/lora.py
Normal file
1
comms/lora.py
Normal file
@@ -0,0 +1 @@
|
||||
# lora实现
|
||||
1
core/command_handler.py
Normal file
1
core/command_handler.py
Normal file
@@ -0,0 +1 @@
|
||||
# 处理接收的指令
|
||||
1
core/heartbeat.py
Normal file
1
core/heartbeat.py
Normal file
@@ -0,0 +1 @@
|
||||
# 心跳包
|
||||
1
core/scheduler.py
Normal file
1
core/scheduler.py
Normal file
@@ -0,0 +1 @@
|
||||
# 定时任务
|
||||
1
devices/__init__.py
Normal file
1
devices/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# 设备驱动
|
||||
1
devices/base_device.py
Normal file
1
devices/base_device.py
Normal file
@@ -0,0 +1 @@
|
||||
# 设备抽象层
|
||||
1
devices/feed_port.py
Normal file
1
devices/feed_port.py
Normal file
@@ -0,0 +1 @@
|
||||
# 下料口
|
||||
1
devices/temperature.py
Normal file
1
devices/temperature.py
Normal file
@@ -0,0 +1 @@
|
||||
# 温度传感器
|
||||
1
storage/__init__.py
Normal file
1
storage/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# 持久化存储
|
||||
11
storage/base_storage.py
Normal file
11
storage/base_storage.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# 存储接口
|
||||
|
||||
class BaseStorage:
|
||||
def save(self, key: str, value):
|
||||
raise NotImplementedError
|
||||
|
||||
def load(self, key: str, default=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def delete(self, key: str):
|
||||
raise NotImplementedError
|
||||
40
storage/json_storage.py
Normal file
40
storage/json_storage.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# json 文件实现
|
||||
|
||||
import json
|
||||
|
||||
from storage.base_storage import BaseStorage
|
||||
|
||||
|
||||
class JSONStorage(BaseStorage):
|
||||
def __init__(self, filename="data.json"):
|
||||
self.filename = filename
|
||||
# 如果文件不存在,先创建空字典
|
||||
try:
|
||||
with open(self.filename, "r") as f:
|
||||
pass
|
||||
except OSError:
|
||||
with open(self.filename, "w") as f:
|
||||
f.write("{}")
|
||||
|
||||
def _read_all(self):
|
||||
with open(self.filename, "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
def _write_all(self, data):
|
||||
with open(self.filename, "w") as f:
|
||||
json.dump(data, f)
|
||||
|
||||
def save(self, key, value):
|
||||
data = self._read_all()
|
||||
data[key] = value
|
||||
self._write_all(data)
|
||||
|
||||
def load(self, key, default=None):
|
||||
data = self._read_all()
|
||||
return data.get(key, default)
|
||||
|
||||
def delete(self, key):
|
||||
data = self._read_all()
|
||||
if key in data:
|
||||
del data[key]
|
||||
self._write_all(data)
|
||||
1
storage/memory_storage.py
Normal file
1
storage/memory_storage.py
Normal file
@@ -0,0 +1 @@
|
||||
# 内存实现
|
||||
1
tests/test_comm.py
Normal file
1
tests/test_comm.py
Normal file
@@ -0,0 +1 @@
|
||||
#
|
||||
1
tests/test_core.py
Normal file
1
tests/test_core.py
Normal file
@@ -0,0 +1 @@
|
||||
# 核心逻辑测试
|
||||
1
tests/test_devices.py
Normal file
1
tests/test_devices.py
Normal file
@@ -0,0 +1 @@
|
||||
#
|
||||
1
tests/test_storage.py
Normal file
1
tests/test_storage.py
Normal file
@@ -0,0 +1 @@
|
||||
#
|
||||
1
utils/crc.py
Normal file
1
utils/crc.py
Normal file
@@ -0,0 +1 @@
|
||||
# 校验工具
|
||||
57
utils/fs.py
Normal file
57
utils/fs.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# 兼容PC和MicroPython的文件操作
|
||||
|
||||
# compat_fs.py
|
||||
try:
|
||||
import uos as os # MicroPython
|
||||
MICROPYTHON = True
|
||||
except ImportError:
|
||||
import os # CPython
|
||||
MICROPYTHON = False
|
||||
|
||||
def list_dir(path="."):
|
||||
"""列出目录内容"""
|
||||
return os.listdir(path)
|
||||
|
||||
def make_dir(path):
|
||||
"""创建目录"""
|
||||
if MICROPYTHON:
|
||||
os.mkdir(path)
|
||||
else:
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
def remove_file(path):
|
||||
"""删除文件"""
|
||||
os.remove(path)
|
||||
|
||||
def read_file(path, mode="r"):
|
||||
"""读取文件内容"""
|
||||
with open(path, mode) as f:
|
||||
return f.read()
|
||||
|
||||
def write_file(path, data, mode="w"):
|
||||
"""写入文件内容"""
|
||||
with open(path, mode) as f:
|
||||
f.write(data)
|
||||
|
||||
def is_file(path):
|
||||
"""判断是否是文件"""
|
||||
try:
|
||||
st = os.stat(path)
|
||||
# MicroPython: stat()[0] >> 14 & 0xF == 8 表示普通文件
|
||||
if MICROPYTHON:
|
||||
return (st[0] >> 14) & 0xF == 8
|
||||
else:
|
||||
return os.path.isfile(path)
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
def is_dir(path):
|
||||
"""判断是否是目录"""
|
||||
try:
|
||||
st = os.stat(path)
|
||||
if MICROPYTHON:
|
||||
return (st[0] >> 14) & 0xF == 2
|
||||
else:
|
||||
return os.path.isdir(path)
|
||||
except OSError:
|
||||
return False
|
||||
1
utils/logger.py
Normal file
1
utils/logger.py
Normal file
@@ -0,0 +1 @@
|
||||
# 日志
|
||||
1
utils/parser.py
Normal file
1
utils/parser.py
Normal file
@@ -0,0 +1 @@
|
||||
# 数据解析工具
|
||||
Reference in New Issue
Block a user