資料介紹
描述
像越來(lái)越多的人一樣,我在 COVID-19 封鎖期間迷上了我的植物。不幸的是,額外的愛(ài)和感情似乎阻礙了我最喜歡的植物之一在今年春天長(zhǎng)出任何新葉子。現(xiàn)在一個(gè)普通人可能會(huì)給植物澆水,把它放在陽(yáng)光下,然后等待——但在制造商社區(qū),我們喜歡數(shù)據(jù)和過(guò)度設(shè)計(jì)的解決方案。所以,我從舊硬件盒中取出一些傳感器,開(kāi)始制作植物監(jiān)視器!
最初,這將是一個(gè)閉環(huán)、離線(xiàn)解決方案,當(dāng)土壤濕度低于某個(gè)待確定值時(shí)給植物澆水——最終更多地成為一種數(shù)據(jù)收集工具,用于查看本地網(wǎng)絡(luò)上的傳感器數(shù)據(jù)。我以前從未使用過(guò)任何與網(wǎng)絡(luò)相關(guān)的東西,所以這是一個(gè)將簡(jiǎn)單的想法從傳感器帶到前端的好機(jī)會(huì)。
該項(xiàng)目包含三個(gè)主要部分——Arduino、中間件和前端。確切的代碼和文件結(jié)構(gòu)在鏈接的 GitHub 存儲(chǔ)庫(kù)中,但我將嘗試概述下面的代碼庫(kù)。
阿杜諾
Arduino 部分是我最熟悉的部分,因此啟動(dòng)并運(yùn)行它大約需要 15 分鐘。連接示意圖如下所示。

您的溫度傳感器可能有四個(gè)引腳而不是三個(gè)引腳,仍然只有三個(gè)活動(dòng)連接,但請(qǐng)查看特定傳感器的文檔。
DHT11 需要 Arduino 提供的 GND 和 VCC,以及 Arduino 讀取的數(shù)據(jù)信號(hào)。土壤濕度傳感器有點(diǎn)棘手,它需要與 DHT11(GND、VCC、信號(hào))相同的連接,但由于傳感器有效地設(shè)置了電解,它的使用壽命很短。如果你讓它不斷地關(guān)閉直流電壓,其中一條腿(陽(yáng)極)會(huì)迅速氧化,傳感器將開(kāi)始給你提供虛假讀數(shù)。
因此,建議在讀數(shù)時(shí)只為濕度傳感器供電。這很容易通過(guò)將 VCC 信號(hào)連接到 Arduino 的數(shù)字輸出引腳之一,并在您期望讀取之前發(fā)送一個(gè)“HIGH”脈沖來(lái)實(shí)現(xiàn)。在我的情況下,它是從引腳 8 供電的。
獲取傳感器讀數(shù)的軟件如下。確保你有我安裝的 DHT11 傳感器庫(kù)(注意我沒(méi)有使用 Adafruit 電路游樂(lè)場(chǎng)庫(kù),使用的庫(kù)更輕量級(jí)。)
#include
const int DHT_pin = A0;
const int soil_moisture_pin = A1;
const int soil_moisture_trigger = 8;
int soil_moisture = 0;
unsigned long ms_per_s = 1000L;
unsigned long minutes = 60;
unsigned long sampling_time = minutes * ms_per_s * 60;
int dht_sig = 0;
int soil_moisture_sig = 0;
dht DHT;
void setup() {
pinMode(soil_moisture_pin,INPUT);
pinMode(soil_moisture_trigger,OUTPUT);
Serial.begin(9600);
}
void loop() {
dht_sig = DHT.read11(DHT_pin);
digitalWrite(soil_moisture_trigger,HIGH);
delay(10);
soil_moisture_sig = analogRead(soil_moisture_pin);
digitalWrite(soil_moisture_trigger,LOW);
soil_moisture = map(soil_moisture_sig,168,0,0,100);
Serial.print(DHT.temperature);
Serial.print(DHT.humidity);
Serial.print(soil_moisture);
delay(sampling_time);
}
中間件
實(shí)際上,您可以通過(guò)在控制回路旁邊添加一個(gè)水泵來(lái)自動(dòng)為您的植物澆水,從而將項(xiàng)目留在這里,但讓我們繼續(xù)。
現(xiàn)在 Arduino 部分運(yùn)行良好,我們需要在 Raspberry Pi 上設(shè)置代碼以獲取此信息。Pis 唯一真正的工作是從 Arduino 獲取數(shù)據(jù)并將其推送到將在下一部分中制作的服務(wù)器上。如果你想降低項(xiàng)目的成本,你可以簡(jiǎn)單地使用一個(gè)兼容 wifi 的微控制器——老實(shí)說(shuō),使用整個(gè) Raspberry Pi 3b+ 來(lái)完成這個(gè)任務(wù)是多余的。
無(wú)論如何,我們將使用 PySerial 從 Arduino 獲取數(shù)據(jù)。請(qǐng)注意,Arduino 端的串行打印是通過(guò)串行通信發(fā)送數(shù)據(jù)。因此,我們?cè)?Arduino 代碼中打印到串行控制臺(tái)的任何內(nèi)容都可以被 PySerial 讀取。確保您的 Arduino 已插入 Pi 的 USB 端口之一。
首先要做的是創(chuàng)建一個(gè)main.py 文件并通過(guò)執(zhí)行獲取 PySerial
sudo pip install pyserial
在 Pi 的終端中。讓我們開(kāi)始編碼。
打開(kāi)main.py 并導(dǎo)入 pyserial - 注意模塊名稱(chēng)只是“serial”。
import serial
我們現(xiàn)在可以使用以下行設(shè)置串行對(duì)象:
ser = serial.Serial("/dev/ttyACM0",9600)
第一個(gè)參數(shù)是 COM 端口,這對(duì)您來(lái)說(shuō)可能有所不同。9600 是波特率,也是在 Arduino 端設(shè)置的。
PySerial 是 Python 中任何與串行相關(guān)的功能強(qiáng)大的包 - 但我們只會(huì)使用 read 函數(shù)。我們將在從 Arduino 到 Raspberry pi 的每個(gè)數(shù)據(jù)包中發(fā)送三個(gè)數(shù)據(jù)值——溫度是前 5 個(gè)字節(jié),濕度是接下來(lái)的 5 個(gè)字節(jié),土壤濕度是最后兩個(gè)字節(jié)。這可以使用
ser.read()
功能。我們還需要解碼傳入的字節(jié),因此您的代碼如下所示:
import serial
ser = serial.Serial("/dev/ttyACM0",9600)
temperature_r = ser.read(5).decode('utf-8')
humidity_r = ser.read(5).decode('utf-8')
soil_m_r = ser.read(2).decode('utf-8')
關(guān)于 ser.read() 有幾點(diǎn)需要注意。首先,它是阻塞的,這意味著在讀取某些內(nèi)容之前不會(huì)執(zhí)行其他代碼。在等待來(lái)自 Arudino 的數(shù)據(jù)時(shí),我們將利用這一優(yōu)勢(shì),但是,如果軟件沒(méi)有按預(yù)期運(yùn)行,這是一件好事。
現(xiàn)在我們已經(jīng)從傳感器獲取到 Raspberry Pi 的數(shù)據(jù),是時(shí)候?qū)⑵浞诺骄W(wǎng)絡(luò)上了。
燒瓶
Flask 是一個(gè)用于 Python 的 microweb 框架,可用于制作服務(wù)器。不建議將 Flask 用作生產(chǎn)服務(wù)器 - 它也在他們的官方文檔中說(shuō)明了這一點(diǎn),所以如果你打算將基于它的項(xiàng)目商業(yè)化,請(qǐng)三思。
要設(shè)置服務(wù)器,我們需要制作一個(gè)燒瓶應(yīng)用程序。首先,我們需要進(jìn)行一些導(dǎo)入并初始化一個(gè) Flask 對(duì)象:
from flask import Flask, jsonify,render_template,request
app = Flask(__name__)
if __name__ == '__main__':
app.run()
現(xiàn)在該應(yīng)用程序已準(zhǔn)備好讓我們向其中添加一些路線(xiàn)。讓我們從加載頁(yè)面開(kāi)始。
from flask import Flask, jsonify,render_template,request
app = Flask(__name__)
@app.route("/")
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
在這種情況下,index.html是要加載的 HTML 頁(yè)面。此功能將由 Javascript 驅(qū)動(dòng)。
您可以提供app.run()一些參數(shù),在我的例子中,我指定了要在 Raspberry Pi 的 IP 上運(yùn)行的主機(jī)和端口。
app.run(host = '0.0.0.0', port = 5000)
?
把這一切放在一起
我們運(yùn)行系統(tǒng)的三個(gè)部分:Arduino,它是數(shù)據(jù)收集單元,Pyserial,它抓取 Arduino 數(shù)據(jù),以及將數(shù)據(jù)推送到本地網(wǎng)絡(luò)的燒瓶服務(wù)器。
在開(kāi)始集成之前,讓我先回顧一下代碼流程。Arduino 將全天收集數(shù)據(jù),當(dāng)有人登錄本地網(wǎng)絡(luò)時(shí),所有這些數(shù)據(jù)都會(huì)被繪制出來(lái)。為此,我們需要長(zhǎng)時(shí)間存儲(chǔ)數(shù)據(jù),直到請(qǐng)求處于活動(dòng)狀態(tài)。我們可以使用線(xiàn)程來(lái)做到這一點(diǎn)。在這個(gè)線(xiàn)程中,我們要做的就是在一個(gè)while循環(huán)中收集數(shù)據(jù)。由于讀取功能被阻塞,循環(huán)將被卡住,直到 Arduino 首先發(fā)送任何數(shù)據(jù)。線(xiàn)程將在單獨(dú)的函數(shù)中定義,并通過(guò)隊(duì)列將數(shù)據(jù)發(fā)送到燒瓶應(yīng)用程序。代碼現(xiàn)在看起來(lái)像這樣:
from flask import Flask, jsonify,render_template,request
import threading, queue
import serial
from datetime import datetime
ser = serial.Serial("/dev/ttyACM0",9600)
app = Flask(__name__)
q = queue.Queue()
temperature = []
humidity = []
soil_moisture = []
time_ax = []
def data_collection():
while(1):
#set variables
i = 0
#read is blocking so waits till next packet of data is sent
temperature_r = ser.read(5).decode('utf-8')
humidity_r = ser.read(5).decode('utf-8')
soil_m_r = ser.read(2).decode('utf-8')
time_of_reading = datetime.now() #this will be the x axis
#put data in queue
q.put(temperature_r)
q.put(humidity_r)
q.put(soil_m_r)
q.put(time_of_reading.strftime("%H:%M"))
@app.route("/")
def index():
return render_template('index.html')
if __name__ == '__main__':
x = threading.Thread(target=data_collection)
x.start()
app.run(host = '0.0.0.0', port = 5000)
現(xiàn)在我們終于可以定義第二條路由,它將從數(shù)據(jù)收集線(xiàn)程中獲取數(shù)據(jù),并將其發(fā)送到前端。Queue 模塊中的 get 和 put 方法也是阻塞的——所以如果事情不正常,請(qǐng)務(wù)必查看文檔。
from flask import Flask, jsonify,render_template,request
import threading, queue
import serial
from datetime import datetime
ser = serial.Serial("/dev/ttyACM0",9600)
app = Flask(__name__)
q = queue.Queue()
temperature = []
humidity = []
soil_moisture = []
time_ax = []
def data_collection():
while(1):
#set variables
i = 0
#read is blocking so waits till next packet of data is sent
temperature_r = ser.read(5).decode('utf-8')
humidity_r = ser.read(5).decode('utf-8')
soil_m_r = ser.read(2).decode('utf-8')
time_of_reading = datetime.now() #this will be the x axis
#put data in queue
q.put(temperature_r)
q.put(humidity_r)
q.put(soil_m_r)
q.put(time_of_reading.strftime("%H:%M"))
@app.route("/update", methods = ['GET'])
def update_chart():
while not q.empty():
temperature.append(q.get())
humidity.append(q.get())
soil_moisture.append(q.get())
time_ax.append(q.get())
return jsonify(results = [temperature,humidity,soil_moisture,time_ax])
@app.route("/")
def index():
return render_template('index.html')
if __name__ == '__main__':
x = threading.Thread(target=data_collection)
x.start()
app.run(host = '0.0.0.0', port = 5000)
這就是 Python 方面的內(nèi)容。
Javascript
所有后端都準(zhǔn)備好了 - 我們可以使用 chartjs 使用 Javascript 繪制數(shù)據(jù)。首先設(shè)置chartjs環(huán)境。我使用了以下代碼行:
var ctx = $("#temperature_chart");
console.log("test");
var temperature_chart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets : [
{
label: 'temperature',
data: [temperature],
borderColor: [
'#060666',
],
borderWidth: 3,
fill: false,
yAxisID: "temperature"
},
{
label: 'Humidity',
data: [humidity],
borderColor: [
'#d6c73e'
],
fill: false,
yAxisID: "humidity"
},
{
label: 'Soil Moisture',
data: [soil_moisture],
borderColor: [
'#7fced4'
],
fill: false,
yAxisID: "humidity"
}
]
},
options: {
responsive: false,
scales:{
xAxes: [ {
//type: 'time',
display: true,
scaleLabel : {
display: true,
labelString: 'Time (s)'
},
ticks: {
autoSkip: true,
maxTicksLimit: 12
}
}],
yAxes: [ {
id: "temperature",
display: true,
position: 'left',
ticks: {
suggestedMin: 15,
suggestedMax: 30
},
scaleLabel : {
display: true,
labelString: 'Temperature (C)'
}
},
{
id: "humidity",
display: true,
position: 'right',
ticks: {
suggestedMin: 0,
suggestedMax: 100
},
scaleLabel : {
display: true,
labelString: 'Percentage Humidity'
}
}]
}
}
});
最后,為了從 Flask 后端獲取數(shù)據(jù),我們使用以下代碼行:
var x = 0;
var temperature = 0;
var humidity = 0;
var soil_moisture = 0;
var updated_data = $.get('/update');
updated_data.done(function(results){
temperature = results.results[0];
humidity = results.results[1];
soil_moisture = results.results[2];
x = results.results[3];
console.log(temperature);
console.log(humidity);
console.log(soil_moisture);
temperature_chart.data.datasets[0].data = temperature;
temperature_chart.data.datasets[1].data = humidity;
temperature_chart.data.datasets[2].data = soil_moisture;
temperature_chart.data.labels = x;
temperature_chart.update();
});
當(dāng)有人進(jìn)入網(wǎng)頁(yè)時(shí),這會(huì)調(diào)用 Python 腳本中指定的更新路由。
結(jié)果
結(jié)果

如您所見(jiàn),最終結(jié)果是帶有兩個(gè) y 軸的時(shí)間戳圖。該項(xiàng)目總體運(yùn)行良好,在更多的日子里,我能夠看到溫度和濕度的明顯周期性,以及土壤濕度的線(xiàn)性下降趨勢(shì),正如預(yù)期的那樣!這些數(shù)據(jù)是完全原始的,有幾種方法可以使這個(gè)項(xiàng)目變得更好
可能的后續(xù)步驟
此項(xiàng)目中的傳感器可以更換為與 Arduino 或 Raspberry Pi 兼容的任何其他傳感器。最終,您可能想要進(jìn)行一些實(shí)際處理而不是繪制原始數(shù)據(jù) - 因此,將所有數(shù)據(jù)不斷發(fā)送到 csv 可能更容易,而不是將其放入隊(duì)列中以進(jìn)行檢索。這意味著在您的 wifi 中斷或系統(tǒng)停止響應(yīng)的情況下,您可以使用數(shù)據(jù)。
您還可以連接水泵或其他一些可以從本地網(wǎng)絡(luò)控制的執(zhí)行器 - 如果在前端按下按鈕,只需添加另一條路線(xiàn)。
Chartjs 并不是真正用于實(shí)時(shí)繪圖,但您可以通過(guò)另一條輪詢(xún)數(shù)據(jù)的路由輕松地使其每隔幾秒更新一次。然后,您可以在 javascript 中檢索此數(shù)據(jù)并在 setInterval() 函數(shù)中更新圖表。
如果您有任何問(wèn)題,請(qǐng)隨時(shí)與我們聯(lián)系。我將盡我所能回答!
- Pi Skype監(jiān)視器開(kāi)源分享
- 無(wú)線(xiàn)串口監(jiān)視器開(kāi)源分享
- 車(chē)庫(kù)門(mén)監(jiān)視器開(kāi)源分享
- 蜂窩連接的蜂巢監(jiān)視器開(kāi)源分享
- 樹(shù)莓派監(jiān)視器開(kāi)源項(xiàng)目
- 管道監(jiān)視器開(kāi)源設(shè)計(jì)
- CPU和RAM使用監(jiān)視器開(kāi)源分享
- 城市花園監(jiān)視器開(kāi)源分享
- 小馬駒監(jiān)視器開(kāi)源分享
- 健康監(jiān)視器開(kāi)源分享
- OctoPrint打印監(jiān)視器開(kāi)源分享
- 電源監(jiān)視器開(kāi)源項(xiàng)目
- 4至14芯電池組監(jiān)視器的模擬前端芯片OZ9355 72次下載
- MAX971 IR傳感器_監(jiān)視器喚醒主系統(tǒng) 0次下載
- IR傳感器/監(jiān)視器喚醒主系統(tǒng)
- 典型電池監(jiān)視器電路圖分享 1.8k次閱讀
- 簡(jiǎn)單的電池監(jiān)視器電路圖 1.3k次閱讀
- 如何創(chuàng)建自定義監(jiān)視器? 1.2k次閱讀
- 光纖應(yīng)用中的監(jiān)視器校準(zhǔn) 1.9k次閱讀
- 如何最大限度地延長(zhǎng)無(wú)線(xiàn)傳感器的運(yùn)行時(shí)間 1.9k次閱讀
- 如何通過(guò)Raspberry Pi設(shè)置CE電流監(jiān)視器 3.3k次閱讀
- Cypress懷孕監(jiān)視器CY8C38系列的性能特性及應(yīng)用方案 3.1k次閱讀
- KUKA C4如何使用診斷監(jiān)視器? 4.7k次閱讀
- 具監(jiān)視器的800mA單電阻器堅(jiān)固型線(xiàn)性穩(wěn)壓器LT3089 1.4k次閱讀
- 揭秘液晶顯示器和液晶監(jiān)視器的七大不同點(diǎn) 2.4k次閱讀
- 鋰離子電池和穩(wěn)壓器監(jiān)視器電路圖 2k次閱讀
- 基于LTC2991系統(tǒng)監(jiān)視器的相對(duì)濕度測(cè)量 2.5k次閱讀
- 液晶監(jiān)視器的選購(gòu)與保養(yǎng) 1.3k次閱讀
- 120Hz+全高清LCD監(jiān)視器新技術(shù)應(yīng)用解析 1.6k次閱讀
- 光隔離溫度傳感器監(jiān)視電路圖 2.6k次閱讀
下載排行
本周
- 1新一代網(wǎng)絡(luò)可視化(NPB 2.0)
- 3.40 MB | 1次下載 | 免費(fèi)
- 2冷柜-電氣控制系統(tǒng)講解
- 13.68 MB | 1次下載 | 10 積分
- 3MDD品牌三極管MMBT3906數(shù)據(jù)手冊(cè)
- 2.33 MB | 次下載 | 免費(fèi)
- 4MDD品牌三極管S9012數(shù)據(jù)手冊(cè)
- 2.62 MB | 次下載 | 免費(fèi)
- 5LAT1218 如何選擇和設(shè)置外部晶體適配 BlueNRG-X
- 0.60 MB | 次下載 | 3 積分
- 6LAT1216 Blue NRG-1/2 系列芯片 Flash 操作與 BLE 事件的互斥處理
- 0.89 MB | 次下載 | 3 積分
- 7收音環(huán)繞擴(kuò)音機(jī) AVR-1507手冊(cè)
- 2.50 MB | 次下載 | 免費(fèi)
- 8MS1000TA 超聲波測(cè)量模擬前端芯片技術(shù)手冊(cè)
- 0.60 MB | 次下載 | 免費(fèi)
本月
- 1愛(ài)華AIWA HS-J202維修手冊(cè)
- 3.34 MB | 37次下載 | 免費(fèi)
- 2PC5502負(fù)載均流控制電路數(shù)據(jù)手冊(cè)
- 1.63 MB | 23次下載 | 免費(fèi)
- 3NB-IoT芯片廠(chǎng)商的資料說(shuō)明
- 0.31 MB | 22次下載 | 1 積分
- 4UWB653Pro USB口測(cè)距通信定位模塊規(guī)格書(shū)
- 838.47 KB | 5次下載 | 免費(fèi)
- 5蘇泊爾DCL6907(即CHK-S007)單芯片電磁爐原理圖資料
- 0.04 MB | 4次下載 | 1 積分
- 6蘇泊爾DCL6909(即CHK-S009)單芯片電磁爐原理圖資料
- 0.08 MB | 2次下載 | 1 積分
- 7100W準(zhǔn)諧振反激式恒流電源電路圖資料
- 0.09 MB | 2次下載 | 1 積分
- 8FS8025B USB的PD和OC快充協(xié)議電壓誘騙控制器IC技術(shù)手冊(cè)
- 1.81 MB | 1次下載 | 免費(fèi)
總榜
- 1matlab軟件下載入口
- 未知 | 935137次下載 | 10 積分
- 2開(kāi)源硬件-PMP21529.1-4 開(kāi)關(guān)降壓/升壓雙向直流/直流轉(zhuǎn)換器 PCB layout 設(shè)計(jì)
- 1.48MB | 420064次下載 | 10 積分
- 3Altium DXP2002下載入口
- 未知 | 233089次下載 | 10 積分
- 4電路仿真軟件multisim 10.0免費(fèi)下載
- 340992 | 191439次下載 | 10 積分
- 5十天學(xué)會(huì)AVR單片機(jī)與C語(yǔ)言視頻教程 下載
- 158M | 183353次下載 | 10 積分
- 6labview8.5下載
- 未知 | 81602次下載 | 10 積分
- 7Keil工具M(jìn)DK-Arm免費(fèi)下載
- 0.02 MB | 73822次下載 | 10 積分
- 8LabVIEW 8.6下載
- 未知 | 65991次下載 | 10 積分
電子發(fā)燒友App





創(chuàng)作
發(fā)文章
發(fā)帖
提問(wèn)
發(fā)資料
發(fā)視頻
上傳資料賺積分
評(píng)論