物联网农业土壤养分和气象站监测系统
前言
为监测户外和大棚的作物生长环境,我设计了一个物联网农业监测系统,使用Unihiker作为主要的监测核心,并结合了RS485土壤传感器,温湿度传感器,CO2传感器,环境光传感器等,采集不同节点的土壤养分,包括温湿度、PH和氮磷钾数据,同时采集空气二氧化碳、温湿度,环境光等的数据,同时还配备了摄像头,对作物的成长进行拍摄。Unihiker通过无线WIFI将数据上传至Lattepanda的本地服务器,我们可以使用Flask Web网页端查看数据,如果需要还可以对作物进行实时的监测。这个系统不仅记录大棚温室的环境数据,我还设立了一个Unihiker室外气象站,实时监测户外的重要环境参数,包括风向、风速、温度、气压和雨量。所有上传云端的数据,可以提供实时分析土壤养分、环境光照和环境参数的数据支持,以及为今后的智能农业发展提供数据标记和学习的基础。整体程序使用Python编写,具有自动运行和断网自动重连等功能。Lattepanda服务器可以显示不同节点的在线状态和数据,如果有节点出现问题,会显示节点不在线,方便及时维护。
利用此系统,我们可以更好地检测、控制、研究育种和植物生长。结合本地监测数据,实时掌握大棚内环境参数和图像,进行数据的采集、数据处理、数据可视化、数据分析等环节,为今后科学化育种育苗栽种进行合理方案设计。
硬件清单
- 土壤氮磷钾传感器 x5
- Lattepanda 3 delta x1
- Unihiker x6
- 二氧化碳传感器 SEN0536 x3
- 环境光传感器 SEN0540 x3
- 气象站 SEN0186 x1
- 485转UART模块 DFR0845 x5
- 温湿度传感器 SEN0334 x1
- Unihiker扩展板 MBT0008 x6
- Power Adapter FIT0639 x6
- SCI x 6
- WIFI 监控摄像头 x1
- 摄像头 x5
- 路由器 x1
- 防水箱体 x6
- 电源转接模块 x 6
功能
- 实时监测大棚内“一米田”内主要环境指标(土壤参数:土壤温湿度、土壤pH、土壤营养盐,环境参数:二氧化碳浓度 、温湿度、光照强度)。
- 苗情监测:图像采集按时自动发送命令远程拍照,自动展示最新作物苗情图片。
- 气象站监测:实时监测大棚外气象情况(风向、风速、温度、气压和雨量)。
- 将监测到的数据在云端显示。
- 离线节点设备在线预警。
方案拓扑图
Unihikerx行空板作为主要的监测核心,并结合了RS485土壤传感器,I2C 温湿度传感器,I2C CO2传感器,I2C 环境光传感器等,采集不同节点的土壤养分,包括温湿度、PH和氮磷钾数据,同时采集空气二氧化碳、温湿度,环境光等的数据,同时还配备了USB摄像头连接Unihiker行空板,对作物的成长进行拍摄。各个节点的Unihiker和Lattepanda都在同一个局域网下,Unihiker通过无线WIFI将数据上传至Lattepanda的本地服务器。
在气象站节点中,Unihiker通过UART通讯收集气象站数据。
重要技术指标
-
土壤传感器:用于检测土壤养分数据
-
Lattepanda 3 delta:用做本地服务器
- 处理器: Intel®赛扬®N5105
- CPU: 2.0~2.9GHz,四核,四线程
- GPU: Intel®UHD显卡(频率:450 - 800MHz)
- 内存:LPDDR4 8GB 2933MHz
- 存储:64GB eMMC V5.1
-
Unihiker:用做每个节点的主控
- CPU: 国产 4核 1.2GHz
- 内存: 512MB DDR3
- 硬盘: 16GB eMMC
- 内置操作系统:Debian
- Wi-Fi: 2.4G
- 蓝牙: 4.0
- 实体按键:Home按键,A/B按键
- 幕:2.8寸240*320 TFT彩屏
- 供电: Type-C 5V供电
- 工作电压: 3.3V
- 最大工作电流: 2000mA
- 接口:
> USB Type-C 1
> USB TYPE-A 1
> microSD卡接口 1
> 3Pin I/O 4 (其中支持3路PWM 2路ADC)
> 4Pin I2C 2
流量和存储估算
按每个节点每5分钟上传一次数据(含图片数据),每个节点数据约为1kb,每张JPG图片约为30kb,图片文件可以直接保存在Lattepanda上,节点数据保存在服务器的Siot数据库中,如果有额外内存需求,可以考虑Lattepanda增加内存条。
硬件连接
1. 以节点1土培生菜为例,节点1-节点5接线相同
USB摄像头和行空板的USB连接。
RS485土壤传感器,485转UART模块(DFR0845)和Uihiker扩展板(MBT0008)连接方式如下表。485转UART模块和土壤传感器之间使用接线柱连接较牢固,485转UART模块和Uihiker扩展板使用杜邦线连接,建议使用热熔胶二次固定,防止电线松动。
SCI,传感器(SEN0536,SEN0540 ,SEN0334)和Uihiker连接方式如图:
2.气象站节点
气象站(SEN0186)和 Uihiker扩展板(MBT0008)连接方式如下表,使用杜邦线连接,建议使用热熔胶二次固定,防止电线松动。
3. 供电:
使用FIT0639和电源转接模块,给Uihiker和扩展板(MBT0008)整体供电。
在LP服务器搭建环境:
安装服务器程序Siot
- 下载windows版本的SIoT解压,双击start SIoT.bat即可启动SIoT,启动之后会弹出命令窗口启动服务器。
- 在浏览器输入 127.0.0.1:8080 即可打开网页端口,登录账号为siot,密码为dfrobot,打开后可以新建Topic或查看消息。
安装Web应用框架Flask
-
windows安装python3.7,下载网页:
-
Python安装flask库
- 访问项目网址下载代码(见第六节),并运行Flask程序
cd flask-demo
flask-start.py
- 网址输入访问http://127.0.0.1:5000/index(需要先启动Siot V2,才可以使用这个Flask Web),在这个网页中,你可以通过上方的按钮选择查看节点的传感器数据。
路由器设置
在lattepanda网页登录路由器页面设置,固定Lattepanda、六个Unihiker的IP。
代码:
1. Unihiker 以节点1 土培生菜为例
(请开启Unihiker开机自启动设置)
# -*- coding: utf-8 -*-
import time
from dfrobot_rp2040_sci import *
from pinpong.board import Board, UART
import serial
import time
import siot
import os
from unihiker import GUI
import requests
import base64
import cv2
Board("").begin() #初始化,选择板型,不输入板型则进行自动识别
SCI1 = DFRobot_RP2040_SCI_IIC(addr=0x21)
u_gui=GUI()
#硬串口1 P0-RX P3-TX
uart1 = UART()
# ser = serial.Serial("/dev/ttyUSB0",115200,timeout=0.5)
#初始化串口 baud_rate 波特率, bits 数据位数(8/9) parity奇偶校验(0 无校验/1 奇校验/2 偶校验) stop 停止位(1/2)
uart1.init(baud_rate = 9600, bits=8, parity=0, stop = 1)
soil_tem_text=u_gui.draw_text(text="soil temperature:NAN",x=0,y=0,font_size=16, color="#0000FF")
soil_hum_text=u_gui.draw_text (text="soil humidity:NAN",x=0,y=30,font_size=16, color="#0000FF")
soil_ph_text=u_gui.draw_text(text="soil ph:NAN",x=0,y=60,font_size=16, color="#0000FF")
soil_N_text=u_gui.draw_text(text="soil N:NAN",x=0,y=90,font_size=16, color="#0000FF")
soil_P_text=u_gui.draw_text(text="soil P:NAN",x=0,y=120,font_size=16, color="#0000FF")
soil_K_text=u_gui.draw_text(text="soil K:NAN",x=0,y=150,font_size=16, color="#0000FF")
CO2_text = u_gui.draw_text(text="CO2:NAN",x=0,y=180,font_size=16, color="#0000FF")
status_text = u_gui.draw_text(text="01_status:NAN",x=0,y=270,font_size=16, color="#0000FF")
air_tem_text=u_gui.draw_text(text="air temperature:NAN",x=0,y=210,font_size=16, color="#0000FF")
air_hum_text=u_gui.draw_text(text="air humidity:NAN",x=0,y=240,font_size=16, color="#0000FF")
#lux_text=u_gui.draw_text(text="light lux:NAN",x=0,y=270,font_size=16, color="#0000FF")
while SCI1.begin() != 0:
print("Initialization Sensor Universal Adapter Board failed.")
time.sleep(1)
print("Initialization Sensor Universal Adapter Board done.")
#发送给传感器的指令
buf = [0x02, 0x03, 0x00, 0x00, 0x00, 0x0A, 0xC5,0xFE]
#返回指令 04传感器地址;03功能码;14数据长度;'00', 'e7'温度;'00', '00'湿度;00 00 空白;'00', '28'ph;'00', '00', '00', '00', '00', '00'氮磷钾;'00', '00', '00', '00'空白;'25', '80'波特率9600;25,b2校验和
#['04', '03', '14', '00', 'e7', '00', '00', '00', '00', '00', '28', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '25', '80', '25', 'b2']
def calc_crc(string):
#print(string)
#data = bytearray.fromhex(string)
data = ['{:02x}'.format(i) for i in string]
#print(data)
data = " ".join(data)
data = data.replace('0x','')
global data2
data2 = data
print(data2)
data = bytearray.fromhex(data)
crc = 0xFFFF
for pos in data:
crc ^= pos
for i in range(8):
if ((crc & 1) != 0):
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return hex(((crc & 0xff) << 8) + (crc >> 8))
def send_photos():
photos_path = '/root/photos'
photos_path_list = os.listdir(photos_path)
photos_path_list.sort(reverse=False)
photos_quan = len(photos_path_list)
photos_count = photos_quan+1
print("count:"+str(photos_count))
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320) #设置摄像头图像宽度
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240) #设置摄像头图像高度
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) #设置OpenCV内部的图像缓存,可以极大提高图像的实时性。
ret, frame = cap.read()
if ret == True:
cv2.imwrite(photos_path+'/Frame'+ str(photos_count) +'.jpg', frame)
print("save photo!")
cap.release()
with open(photos_path+'/Frame'+ str(photos_count) +'.jpg',"rb") as f:
# b64encode是编码,b64decode是解码
data = base64.b64encode(f.read())
src = "data:image/{ext};base64,{data}".format(ext='jpg', data=str(data))
#print(src)
#print(len(src))
siot.publish_save(topic="siot/节点1/image", data=src)
siot.publish_save(topic="siot/image", data=src)
print("photos send ok")
f.close()
status_text.config(text="04_status: photo send ok",x=0,y=290)
photos_count = int(photos_count) + 1
flag = 1
count=0
while True:
air_tem_value=SCI1.get_value0("Temp_Air")
air_tem_t = "Temp_Air: "+str(air_tem_value)+"℃"
air_tem_text.config(text=air_tem_t,x=0,y=210)
air_hum_value=SCI1.get_value0("Humi_Air")
air_hum__t = "Humi_Air: "+str(air_hum_value)+"%RH"
air_hum_text.config(text=air_hum__t,x=0,y=240)
print("-----------write buf-----------")
uart1.write(buf)
time.sleep(1)
count=0
while uart1.any()==0:
print("any:"+str(count))
count=count+1
if count>10:
break
time.sleep(0.1)
while uart1.any()>0:
print("while2:"+str(uart1.any()))
#print(uart1.read(uart1.any()))
time.sleep(0.01)
if uart1.read(1)[0] == 0x02:
print("11")
time.sleep(0.01)
if uart1.read(1)[0] == 0x03:
time.sleep(0.01)
print("while2:"+str(uart1.any()))
data = uart1.read(23)
data.insert(0,0x02)
data.insert(1,0x03)
#print(data)
crc = calc_crc((data))
#print(data[11],data(12))
print("crc="+str(crc))
if crc == '0x0':
print(data2)
data3 = data2.split()
#print(str(data3[10])+str(data3[11]))
soil_tem = int(str(data3[3])+str(data3[4]),16)/10
soil_ph = int(str(data3[9])+str(data3[10]),16)/10
soil_hum = int(str(data3[5])+str(data3[6]),16)/10
soil_N = int(str(data3[11])+str(data3[12]),16)
soil_P = int(str(data3[13])+str(data3[14]),16)
soil_K = int(str(data3[15])+str(data3[16]),16)
soil_ph_t = "soil_ph:"+ str(soil_ph)
soil_ph_text.config(text= soil_ph_t ,x=0,y=60)
soil_hum_t = "soil_hum: "+str(soil_hum)+"%"
soil_hum_text.config(text= soil_hum_t ,x=0,y=30)
soil_tem_t = "soil_tem:"+str(soil_tem)+"℃"
soil_tem_text.config(text= soil_tem_t ,x=0,y=0)
soil_N_t = "soil_N: "+str(soil_N)+"mg/kg"
soil_N_text.config(text= soil_N_t ,x=0,y=90)
soil_P_t = "soil_P: "+str(soil_P)+"mg/kg"
soil_P_text.config(text= soil_P_t ,x=0,y=120)
soil_K_t = "soil_K: "+str(soil_K)+"mg/kg"
soil_K_text.config(text= soil_K_t ,x=0,y=150)
try :
my_variable = requests.get("http://10.1.2.3/wifi/status")
print(my_variable.text)
status = my_variable.text.split('"')[11]
print("wifi: "+status)
status_text.config(text="01_wifi:"+status,x=0,y=270)
siot.init(client_id="unihiker01",server="10.168.1.100",port=1883,user="siot",password="dfrobot")
siot.connect()
siot.loop()
siot.getsubscribe(topic="siot/节点1/土壤温度")
siot.getsubscribe(topic="siot/节点1/土壤湿度")
siot.getsubscribe(topic="siot/节点1/土壤pH")
siot.getsubscribe(topic="siot/节点1/土壤氮")
siot.getsubscribe(topic="siot/节点1/土壤磷")
siot.getsubscribe(topic="siot/节点1/土壤钾")
#siot.getsubscribe(topic="siot/节点1/二氧化碳")
siot.getsubscribe(topic="siot/节点1/空气温度")
siot.getsubscribe(topic="siot/节点1/空气湿度")
siot.getsubscribe(topic="siot/image")
siot.getsubscribe(topic="siot/节点1/image")
siot.publish_save(topic="siot/节点1/土壤温度", data=soil_tem)
siot.publish_save(topic="siot/节点1/土壤pH", data=soil_ph)
siot.publish_save(topic="siot/节点1/土壤湿度", data=soil_hum)
siot.publish_save(topic="siot/节点1/土壤氮", data=soil_N)
siot.publish_save(topic="siot/节点1/土壤磷", data=soil_P)
siot.publish_save(topic="siot/节点1/土壤钾", data=soil_K)
siot.publish_save(topic="siot/节点1/空气温度", data=air_tem_value)
siot.publish_save(topic="siot/节点1/空气湿度", data=air_hum_value)
if flag == 50:
flag = 0
send_photos()
print("send ok")
siot.stop()
status_text.config(text="01_status: data send ok",x=0,y=270)
time.sleep(3600)
except :
print("wifi正在重连!")
status_text.config(text="wifi正在重连!",x=0,y=270)
my_variable = requests.get("http://10.1.2.3/wifi/connect?ssid=dfrobot&password=dfrobot2017") # ssid和password后面改为需要连接的wifi名字密码
print(my_variable.text)
time.sleep(60)
print("查看WiFi连接情况:")
my_variable = requests.get("http://10.1.2.3/wifi/status")
print(my_variable.text)
status = my_variable.text.split('"')[11]
print(status)
status_text.config(text="01_wifi:"+status,x=0,y=270)
2. Unihiker 气象站
# -*- coding: utf-8 -*-
import time
from pinpong.board import Board, UART
import siot
import os
Board("UNIHIKER").begin() #初始化,选择板型,不输入板型则进行自动识别
#行空板硬串口1 P0-RX P3-TX
uart1 = UART()
#初始化串口 baud_rate 波特率, bits 数据位数(8/9) parity奇偶校验(0 无校验/1 奇校验/2 偶校验) stop 停止位(1/2)
uart1.init(baud_rate = 9600, bits=8, parity=0, stop = 1)
#uart1.init() #气象站波特率为9600
siot.init(client_id="hostclient01",server="10.1.2.3",port=1883,user="siot",password="dfrobot")
while True:
databuffer = ""
#print(len("c000s000g000t082r000p000h48b10022*3C"))默认字长
buf = uart1.readline()
#uart1.write(buf)
#b = type(buf)
#print(b)
if buf is None:
print("recv None")
else:
for i in buf:
#print(i)
a = chr(i)
databuffer = databuffer + a
length = len(databuffer)
print(databuffer)
if length == 38: # 如果数据长度为38
print("databuffer:",databuffer) # 打印显示数据串
'''解析获取其中的风向数据'''
try:
WindDirection = int(databuffer[1:4])
except:
WindDirection = 0
if 0 <= WindDirection and WindDirection < 22.5 or 337.5<=WindDirection and WindDirection < 360:
WindDirection_dir = 'S'
if 22.5 <= WindDirection and WindDirection < 67.5:
WindDirection_dir = 'SW'
if 67.5 <= WindDirection and WindDirection < 112.5:
WindDirection_dir = 'W'
if 112.5 <= WindDirection and WindDirection < 157.5:
WindDirection_dir = 'NW'
if 157.5 <= WindDirection and WindDirection < 202.5:
WindDirection_dir = 'N'
if 202.5 <= WindDirection and WindDirection < 247.5:
WindDirection_dir = 'NE'
if 247.5 <= WindDirection and WindDirection < 292.5:
WindDirection_dir = 'E'
if 292.5 <= WindDirection and WindDirection < 337.5:
WindDirection_dir = 'SE'
print("WindDirection:" +str(WindDirection) +" degree","WindDirection_dir:"+WindDirection_dir)
'''解析获取其中的风速数据'''# 1英里每小时=1609.34米/3600秒=0.44703889m/s
# 前一分钟的平均风速
try:
WindSpeedAverage = round(0.44704 * float(databuffer[5:8]),1)
except:
WindSpeedAverage = 0
print("Average Wind Speed (One Minute):" + str(WindSpeedAverage) + "m/s ")
# 前五分钟的最大风速
try:
WindSpeedMax = round(0.44704 * float(databuffer[9:12]),1)
except:
WindSpeedMax = 0
print("Max Wind Speed (Five Minutes):" + str(WindSpeedMax) + "m/s")
'''解析其中的温度数据'''# 摄氏度=(华氏度-32)*5/9
try:
Temperature = round((float(databuffer[13:16]) - 32.00) * 5.00 / 9.00,2)
except:
Temperature = 0
print("Temperature:" + str(Temperature)+ "℃ ")
# print("Temperature:" + "{:.2f}".format(Temperature)+ "C ")
'''解析其中的湿度数据'''
try:
Humidity = round(float(databuffer[25:27]) ,1)
except:
Humidity = 0
print("Humidity:" + str(Humidity) +"% ")
'''解析其中的气压数据'''
try:
BarPressure = round(float(databuffer[28:33])/ 10.00,1)
except:
BarPressure = 0
print("BarPressure:" + str(BarPressure) + "hPa")
else: # 如果长度不是38,那么就令数据为空
databuffer = ""
time.sleep(0.5)
3. Flask服务器程序
# -*- coding: UTF-8 -*-
from flask import Flask,Response,render_template,request
flask_app = Flask(__name__)
# 事件回调函数
def rec_route_funca():
print("b click")
return "rount_func"
def rec_route_funcb():
print("b click")
return "b"
def rec_index():
return render_template("test.html")
@flask_app.route('/index',methods=['GET','POST'])
def route_index():
return rec_index()
flask_app.run(host='0.0.0.0', port=5000, threaded=True)
4. 服务器程序(保存图片,查询节点在线状态)
# -*- coding: UTF-8 -*-
# MindPlus
# Python
import time
import siot
import os
ip = ['ping 10.168.1.114','ping 10.168.1.117','ping 10.168.1.118','ping 10.168.1.122','ping 10.168.1.115','ping 10.168.1.112']
# 事件回调函数
def on_message_callback(client, userdata, msg):
global P1
global N1
global K1
global P5
global N5
global K5
global sendata1
global sendata5
global sendataw
global sendata
global weather_hum
global weather_tem
global indoor_hum
global indoor_tem
if (msg.topic.find("表格")!=-1):
pass
else:
if (msg.topic.find("节点1/土壤氮")!=-1):
N1 = msg.payload.decode()
print(msg.topic)
print(N1)
if (msg.topic.find("节点1/土壤磷")!=-1):
P1 = msg.payload.decode()
print(msg.topic)
print(P1)
if (msg.topic.find("节点1/土壤钾")!=-1):
K1 = msg.payload.decode()
print(msg.topic)
print(K1)
status = ((not (P1 == 0)) and (not (N1 == 0)))
if ((not (status == 0)) and (not (K1 == 0))):
sendata1 = N1 +","+ P1 +","+ K1
print(sendata1)
siot.publish_save(topic="siot/节点1/氮磷钾总和表格", data=sendata1)
if (msg.topic.find("节点5/土壤氮")!=-1):
N5 = msg.payload.decode()
print(msg.topic)
print(N5)
if (msg.topic.find("节点5/土壤磷")!=-1):
P5 = msg.payload.decode()
print(msg.topic)
print(P5)
if (msg.topic.find("节点5/土壤钾")!=-1):
K5 = msg.payload.decode()
print(msg.topic)
print(K5)
status = ((not (P5 == 0)) and (not (N5 == 0)))
if ((not (status == 0)) and (not (K5 == 0))):
sendata5 = N5 +","+ P5 +","+ K5
print(sendata5)
siot.publish_save(topic="siot/节点5/氮磷钾总和表格", data=sendata5)
if (msg.topic.find("气象站/温度")!=-1):
weather_tem = msg.payload.decode()
print((str(msg.topic) + str(weather_tem)))
if (msg.topic.find("气象站/湿度")!=-1):
weather_hum = msg.payload.decode()
print((str(msg.topic) + str(weather_hum)))
if ((not (weather_hum == 0)) and (not (weather_tem == 0))):
sendataw = weather_hum+ "," + weather_tem
print(sendataw)
siot.publish_save(topic="siot/气象站/温湿度表格", data=sendataw)
if (msg.topic.find("节点1/温度")!=-1):
weather_tem = msg.payload.decode()
print((str(msg.topic) + str(weather_tem)))
if (msg.topic.find("节点1/湿度")!=-1):
weather_hum = msg.payload.decode()
print((str(msg.topic) + str(weather_hum)))
if ((not (indoor_hum == 0)) and (not (indoor_tem == 0))):
sendata = indoor_hum+ "," + indoor_tem
print(sendata)
siot.publish_save(topic="siot/节点1/温湿度表格", data=sendata)
siot.init(client_id="",server="10.168.1.100",port=1883,user="siot",password="dfrobot")
siot.set_callback(on_message_callback)
siot.connect()
siot.loop()
P1 = 0
N1 = 0
K1 = 0
P5 = 0
N5 = 0
K5 = 0
weather_hum = 0
weather_tem = 0
indoor_tem = 0
indoor_hum = 0
siot.getsubscribe(topic="siot/节点1/温湿度表格")
siot.getsubscribe(topic="siot/气象站/温湿度表格")
siot.getsubscribe(topic="siot/气象站/温度")
siot.getsubscribe(topic="siot/气象站/湿度")
siot.getsubscribe(topic="siot/节点1/温度")
siot.getsubscribe(topic="siot/节点1/湿度")
siot.getsubscribe(topic="siot/devicestatus")
siot.getsubscribe(topic="siot/节点1/氮磷钾总和表格")
siot.getsubscribe(topic="siot/节点1/土壤氮")
siot.getsubscribe(topic="siot/节点1/土壤磷")
siot.getsubscribe(topic="siot/节点1/土壤钾")
siot.getsubscribe(topic="siot/节点5/氮磷钾总和表格")
siot.getsubscribe(topic="siot/节点5/土壤氮")
siot.getsubscribe(topic="siot/节点5/土壤磷")
siot.getsubscribe(topic="siot/节点5/土壤钾")
while True:
outdev = 0
online = 0
for i in ip:
result = os.popen(i)
status = result.read()
loc = "unreachable" in status
print(loc)
if loc != True:
loc = status.find('Lost')
loss_data = status[loc+7]
if loss_data == '4':
outdev = outdev+1
else:
online = online+1
else:
outdev = outdev+1
output = str(online) + ','+ str(outdev)
print("online,outdev:")
print(output)
siot.publish_save(topic="siot/devicestatus", data=output)
print("send ok")
time.sleep(100)
访问项目下载完整程序:https://github.com/polamaxu/AgriculturalSmartSystem
总结
该物联网农业监测系统能够稳定地检测土壤养分,包括温湿度、PH和氮磷钾数据,同时还能监测户外和大棚的作物生长环境。通过无线WIFI,将大棚环境和室外气象站的传感器数据和图片实时上传云端,提供土壤养分、环境光照、环境参数的实时分析,为家庭精准农业提供必要的数据支持。
FAQ
- Unihiker 一直显示wifi正在重连?
观察屏幕是否循环显示wifi connected->wifi 正在重连。如果有,请重启lattepanda上的siot服务器。如果没有出现,请检查路由器是否正常。
- Unihiker 显示报错:runtimeerror:analog map retrieal time out.
返回Unihiker 菜单页面,重新运行程序,如果还无法解决,可以刷处理器固件,方法:https://www.Unihiker.com/wiki/faq
- 如何远程监控各个行空板的屏幕?
使用windows自带的远程软件remote desktop connection,填入行空的ip来访问
- 如何远程控制查看各个行空板的程序?
使用mobaXterm.exe,选择行空对应的ip就可以打开行空板的系统运行命令行界面,账号是siot,密码是dfrobot。