資料介紹
描述
環境監測應用程序對于生成有關我們周圍環境質量的信息是必不可少的,包括環境是變好還是變壞,或者保持不變。環境監測應用程序生成的數據類型在決策制定中很有用,從長遠來看可以作為歷史數據。在這個項目中,我構建了一個環境監控系統,該系統從附加的傳感器收集數據并將其發布到 MQTT 端點,該端點可以被許多最終用戶訂閱。
硬件設置
我們使用的是帶有板載 ESP8285 WiFi 芯片的 Seeed Wio RP2040 模塊。整體外形非常緊湊,適合 DIY 可穿戴/便攜式項目。
Wio RP2040 模塊的齒形安裝孔間距與面包板不兼容,因此我們需要使用原型板焊接接頭以使其與面包板兼容。
此外,我們需要如下圖所示連接一個微型 USB 分接頭,以便為模塊供電和閃爍。
Grove 溫度/濕度/壓力和氣體傳感器通過 I2C 連接連接到模塊,如圖所示。
Grove Sensor Wio RP2040模塊
地線 --------- 地線
VCC。--------- 3.3V 輸出
SDA --------- GPIO 6 (I2C1 SDA)
SCL。--------- GPIO 27 (I2C1 SCL)
MicroPython 固件
支持的 MicroPython 固件可以從這里下載。我們可以按照以下步驟將下載的firmware-v1.15.1.uf2 USB Flashing Format文件上傳到Wio RP2040 Module。
- 將 BOOT 連接到 GND(參見上面的示意圖)并將 Micro USB 分線器插入計算機的 USB 端口。它將安裝為稱為 RPI-RP2 的大容量存儲設備。
- 斷開 BOOT 和 GND
- 將固件-v1.15.1.uf2 文件拖放到 RPI-RP2 卷上。
刷新二進制文件后,Wio RP2040 模塊將重新啟動,程序將開始運行。我們可以通過串行控制臺(CoolTerm/screen/minicom/Putty)以 115200 的波特率連接到 MicroPython REPL。
設置 MQTT 代理
由于我們想從任何可訪問的位置遠程訪問傳感器數據,我們使用虛擬專用服務器 (VPS) 來安裝 MQTT 代理。您可以使用任何本地 Linux 機器(例如,Raspberry Pi)來實現相同的目的。請按照以下步驟安裝和設置代理和客戶端。
1. 在 Linux (Ubuntu) 上安裝 Mosquitto MQTT 代理和客戶端
$ sudo apt install mosquitto mosquitto-clients
2.設置密碼
$ sudo mosquitto_passwd -c /etc/mosquitto/passwd mypassword
3. 使用以下內容編輯配置文件 /etc/mosquitto/conf.d/default.conf。
allow_anonymous false
password_file /etc/mosquitto/passwd
4.重啟守護進程
$ sudo systemctl restart mosquitto
或者,如果您想讓它更安全,請按照此處的說明進行操作。處理 SSL 連接。
代碼
請按照此處鏈接中的說明安裝 Thonny IDE 并將以下兩個文件上傳到模塊。此外,在 main.py 中更改 WiFi 憑據和 MQTT 代理端點/憑據。
文件:main.py
from time import sleep
import network
import mqtt
from machine import Pin, I2C, ADC, UART, SPI, PWM
from bme680 import *
i2c = I2C(1, scl=Pin(27), sda=Pin(6))
bme = BME680_I2C(i2c=i2c, address=0x76)
N1 = network.WLAN_SPI(network.STA_IF)
N1.active(True)
N1.connect("myssid", "mypsk")
sleep(1)
SERVER = 'mqtt_endpoint_url'
USER = 'myuser'
PWD = 'mypassword'
TOPIC = 'sensors/data'
cl = mqtt.MQTTClient(USER, SERVER, mqtt_port = 1883, mqtt_user=USER, mqtt_password=PWD)
sleep(1)
if N1.isconnected():
print("connecting...")
print(cl.connect())
print("connected")
sleep(1)
while True:
try:
temperature = str(round(bme.temperature, 2))
humidity = str(round(bme.humidity, 2))
pressure = str(round(bme.pressure, 2))
gas = str(round(bme.gas/1000, 2))
data = temperature + " " + humidity + " " + pressure + " " + gas
cl.publish(TOPIC, data)
except OSError as e:
print('Failed to read sensor.')
sleep(10)
文件:bme680.py(修改自 Adafruit BME680 庫)
import time
import math
from micropython import const
from ubinascii import hexlify as hex
try:
import struct
except ImportError:
import ustruct as struct
_BME680_CHIPID = const(0x61)
_BME680_REG_CHIPID = const(0xD0)
_BME680_BME680_COEFF_ADDR1 = const(0x89)
_BME680_BME680_COEFF_ADDR2 = const(0xE1)
_BME680_BME680_RES_HEAT_0 = const(0x5A)
_BME680_BME680_GAS_WAIT_0 = const(0x64)
_BME680_REG_SOFTRESET = const(0xE0)
_BME680_REG_CTRL_GAS = const(0x71)
_BME680_REG_CTRL_HUM = const(0x72)
_BME280_REG_STATUS = const(0xF3)
_BME680_REG_CTRL_MEAS = const(0x74)
_BME680_REG_CONFIG = const(0x75)
_BME680_REG_PAGE_SELECT = const(0x73)
_BME680_REG_MEAS_STATUS = const(0x1D)
_BME680_REG_PDATA = const(0x1F)
_BME680_REG_TDATA = const(0x22)
_BME680_REG_HDATA = const(0x25)
_BME680_SAMPLERATES = (0, 1, 2, 4, 8, 16)
_BME680_FILTERSIZES = (0, 1, 3, 7, 15, 31, 63, 127)
_BME680_RUNGAS = const(0x10)
_LOOKUP_TABLE_1 = (2147483647.0, 2147483647.0, 2147483647.0, 2147483647.0, 2147483647.0,
2126008810.0, 2147483647.0, 2130303777.0, 2147483647.0, 2147483647.0,
2143188679.0, 2136746228.0, 2147483647.0, 2126008810.0, 2147483647.0,
2147483647.0)
_LOOKUP_TABLE_2 = (4096000000.0, 2048000000.0, 1024000000.0, 512000000.0, 255744255.0, 127110228.0,
64000000.0, 32258064.0, 16016016.0, 8000000.0, 4000000.0, 2000000.0, 1000000.0,
500000.0, 250000.0, 125000.0)
def _read24(arr):
ret = 0.0
for b in arr:
ret *= 256.0
ret += float(b & 0xFF)
return ret
class Adafruit_BME680:
def __init__(self, *, refresh_rate=10):
self._write(_BME680_REG_SOFTRESET, [0xB6])
time.sleep(0.005)
chip_id = self._read_byte(_BME680_REG_CHIPID)
if chip_id != _BME680_CHIPID:
raise RuntimeError('Failed 0x%x' % chip_id)
self._read_calibration()
self._write(_BME680_BME680_RES_HEAT_0, [0x73])
self._write(_BME680_BME680_GAS_WAIT_0, [0x65])
self.sea_level_pressure = 1013.25
self._pressure_oversample = 0b011
self._temp_oversample = 0b100
self._humidity_oversample = 0b010
self._filter = 0b010
self._adc_pres = None
self._adc_temp = None
self._adc_hum = None
self._adc_gas = None
self._gas_range = None
self._t_fine = None
self._last_reading = 0
self._min_refresh_time = 1000 / refresh_rate
@property
def pressure_oversample(self):
return _BME680_SAMPLERATES[self._pressure_oversample]
@pressure_oversample.setter
def pressure_oversample(self, sample_rate):
if sample_rate in _BME680_SAMPLERATES:
self._pressure_oversample = _BME680_SAMPLERATES.index(sample_rate)
else:
raise RuntimeError("Invalid")
@property
def humidity_oversample(self):
return _BME680_SAMPLERATES[self._humidity_oversample]
@humidity_oversample.setter
def humidity_oversample(self, sample_rate):
if sample_rate in _BME680_SAMPLERATES:
self._humidity_oversample = _BME680_SAMPLERATES.index(sample_rate)
else:
raise RuntimeError("Invalid")
@property
def temperature_oversample(self):
return _BME680_SAMPLERATES[self._temp_oversample]
@temperature_oversample.setter
def temperature_oversample(self, sample_rate):
if sample_rate in _BME680_SAMPLERATES:
self._temp_oversample = _BME680_SAMPLERATES.index(sample_rate)
else:
raise RuntimeError("Invalid")
@property
def filter_size(self):
return _BME680_FILTERSIZES[self._filter]
@filter_size.setter
def filter_size(self, size):
if size in _BME680_FILTERSIZES:
self._filter = _BME680_FILTERSIZES[size]
else:
raise RuntimeError("Invalid")
@property
def temperature(self):
self._perform_reading()
calc_temp = (((self._t_fine * 5) + 128) / 256)
return calc_temp / 100
@property
def pressure(self):
self._perform_reading()
var1 = (self._t_fine / 2) - 64000
var2 = ((var1 / 4) * (var1 / 4)) / 2048
var2 = (var2 * self._pressure_calibration[5]) / 4
var2 = var2 + (var1 * self._pressure_calibration[4] * 2)
var2 = (var2 / 4) + (self._pressure_calibration[3] * 65536)
var1 = (((((var1 / 4) * (var1 / 4)) / 8192) *
(self._pressure_calibration[2] * 32) / 8) +
((self._pressure_calibration[1] * var1) / 2))
var1 = var1 / 262144
var1 = ((32768 + var1) * self._pressure_calibration[0]) / 32768
calc_pres = 1048576 - self._adc_pres
calc_pres = (calc_pres - (var2 / 4096)) * 3125
calc_pres = (calc_pres / var1) * 2
var1 = (self._pressure_calibration[8] * (((calc_pres / 8) * (calc_pres / 8)) / 8192)) / 4096
var2 = ((calc_pres / 4) * self._pressure_calibration[7]) / 8192
var3 = (((calc_pres / 256) ** 3) * self._pressure_calibration[9]) / 131072
calc_pres += ((var1 + var2 + var3 + (self._pressure_calibration[6] * 128)) / 16)
return calc_pres/100
@property
def humidity(self):
self._perform_reading()
temp_scaled = ((self._t_fine * 5) + 128) / 256
var1 = ((self._adc_hum - (self._humidity_calibration[0] * 16)) -
((temp_scaled * self._humidity_calibration[2]) / 200))
var2 = (self._humidity_calibration[1] *
(((temp_scaled * self._humidity_calibration[3]) / 100) +
(((temp_scaled * ((temp_scaled * self._humidity_calibration[4]) / 100)) /
64) / 100) + 16384)) / 1024
var3 = var1 * var2
var4 = self._humidity_calibration[5] * 128
var4 = (var4 + ((temp_scaled * self._humidity_calibration[6]) / 100)) / 16
var5 = ((var3 / 16384) * (var3 / 16384)) / 1024
var6 = (var4 * var5) / 2
calc_hum = (((var3 + var6) / 1024) * 1000) / 4096
calc_hum /= 1000
if calc_hum > 100:
calc_hum = 100
if calc_hum < 0:
calc_hum = 0
return calc_hum
@property
def altitude(self):
pressure = self.pressure
return 44330 * (1.0 - math.pow(pressure / self.sea_level_pressure, 0.1903))
@property
def gas(self):
self._perform_reading()
var1 = ((1340 + (5 * self._sw_err)) * (_LOOKUP_TABLE_1[self._gas_range])) / 65536
var2 = ((self._adc_gas * 32768) - 16777216) + var1
var3 = (_LOOKUP_TABLE_2[self._gas_range] * var1) / 512
calc_gas_res = (var3 + (var2 / 2)) / var2
return int(calc_gas_res)
def _perform_reading(self):
if (time.ticks_diff(self._last_reading, time.ticks_ms()) * time.ticks_diff(0, 1)
< self._min_refresh_time):
return
self._write(_BME680_REG_CONFIG, [self._filter << 2])
self._write(_BME680_REG_CTRL_MEAS,
[(self._temp_oversample << 5)|(self._pressure_oversample << 2)])
self._write(_BME680_REG_CTRL_HUM, [self._humidity_oversample])
self._write(_BME680_REG_CTRL_GAS, [_BME680_RUNGAS])
ctrl = self._read_byte(_BME680_REG_CTRL_MEAS)
ctrl = (ctrl & 0xFC) | 0x01
self._write(_BME680_REG_CTRL_MEAS, [ctrl])
new_data = False
while not new_data:
data = self._read(_BME680_REG_MEAS_STATUS, 15)
new_data = data[0] & 0x80 != 0
time.sleep(0.005)
self._last_reading = time.ticks_ms()
self._adc_pres = _read24(data[2:5]) / 16
self._adc_temp = _read24(data[5:8]) / 16
self._adc_hum = struct.unpack('>H', bytes(data[8:10]))[0]
self._adc_gas = int(struct.unpack('>H', bytes(data[13:15]))[0] / 64)
self._gas_range = data[14] & 0x0F
var1 = (self._adc_temp / 8) - (self._temp_calibration[0] * 2)
var2 = (var1 * self._temp_calibration[1]) / 2048
var3 = ((var1 / 2) * (var1 / 2)) / 4096
var3 = (var3 * self._temp_calibration[2] * 16) / 16384
self._t_fine = int(var2 + var3)
def _read_calibration(self):
coeff = self._read(_BME680_BME680_COEFF_ADDR1, 25)
coeff += self._read(_BME680_BME680_COEFF_ADDR2, 16)
coeff = list(struct.unpack(', bytes(coeff[1:39])))
coeff = [float(i) for i in coeff]
self._temp_calibration = [coeff[x] for x in [23, 0, 1]]
self._pressure_calibration = [coeff[x] for x in [3, 4, 5, 7, 8, 10, 9, 12, 13, 14]]
self._humidity_calibration = [coeff[x] for x in [17, 16, 18, 19, 20, 21, 22]]
self._gas_calibration = [coeff[x] for x in [25, 24, 26]]
self._humidity_calibration[1] *= 16
self._humidity_calibration[1] += self._humidity_calibration[0] % 16
self._humidity_calibration[0] /= 16
self._heat_range = (self._read_byte(0x02) & 0x30) / 16
self._heat_val = self._read_byte(0x00)
self._sw_err = (self._read_byte(0x04) & 0xF0) / 16
def _read_byte(self, register):
return self._read(register, 1)[0]
def _read(self, register, length):
raise NotImplementedError()
def _write(self, register, values):
raise NotImplementedError()
class BME680_I2C(Adafruit_BME680):
def __init__(self, i2c, address=0x77, debug=False, *, refresh_rate=10):
self._i2c = i2c
self._address = address
self._debug = debug
super().__init__(refresh_rate=refresh_rate)
def _read(self, register, length):
result = bytearray(length)
self._i2c.readfrom_mem_into(self._address, register & 0xff, result)
if self._debug:
print("\t${:x} read ".format(register), " ".join(["{:02x}".format(i) for i in result]))
return result
def _write(self, register, values):
if self._debug:
print("\t${:x} write".format(register), " ".join(["{:02x}".format(i) for i in values]))
for value in values:
self._i2c.writeto_mem(self._address, register, bytearray([value & 0xFF]))
register += 1
一旦上述代碼上傳成功并且所有憑據都OK,Wio RP2040 模塊將開始以 10 秒的間隔通過 MQTT 發布傳感器數據。
使用 Jupyter Notebook 訂閱和可視化實時數據
我們可以在任何支持 Python3 的計算機上使用 MQTT 客戶端訂閱傳感器數據。我們需要使用 pip 安裝以下 python 模塊。
$ pip3 install paho-mqtt jupyter jupyterplot
請從 Github 存儲庫下載 notebooksubscriber.ipynb并運行以下命令。
$ python3 jupyter notebook
打開subscriber.ipynb 并運行它。如果所有憑據都正常,Wio RP2040 模塊正在發布,并且 MQTT 代理正在運行,您將在 Jupyter Notebook 中看到實時數據,如下所示。
結論
Wio RP2040 模塊是一款功能強大、體積小巧且價格實惠的物聯網設備,可用于數據收集和遠程監控。可以使用電池或移動電源輕松操作此設置。所有代碼都可以在代碼部分給出的 Github repo 鏈接中找到。感謝 Seeed Studio 為我提供 Wio RP2040 模塊和 Grove 環境傳感器 (BME680)。
- Seeed Wio終端開源分享
- 將您的Adafruit Feather RP2040連接到物聯網
- 如何在Windows上設置Pico RP2040
- RP2040 Raspberry Pi Pico遇見LoRa
- Arduino Raspberry Pi Pico/RP2040以太網:W5100S EVB Pico
- 將MicroPython從Raspberry pi(RP2040)移植到Wiznet5K
- 基于RP2040板的SUMP/OLS兼容邏輯分析儀固件
- 使用RP2040和KMK固件的40%正交線性鍵盤
- 使用RP2040和W5100S的無套接字功能
- AI WB2和XIAO RP2040組合開源
- RP2040模塊MCU開源分享
- BBkey80:由XIAO RP2040驅動的GL516兼容鍵盤
- RP2040迷你板開源分享
- RP2040 MicroMOD處理器板開源分享
- ProMicro-RP2040印刷電路板
- 光伏電站環境監測儀能帶來哪些效益? 251次閱讀
- 無線終端ZWS云應用(二)—DTU 云的環境監測行業應用 279次閱讀
- 5分鐘內將Raspberry Pi Pico變成簡單的示波器+波形發生器 2317次閱讀
- 基于啟明6M5開發板的無線環境監測小車系統設計 989次閱讀
- 基于RP2040的ToF雷射測距裝置設計 1173次閱讀
- 當Arduino遇上樹莓派:Nano RP2040連接板 7268次閱讀
- 基于RP2040設計的發光魔法LED立方體 2329次閱讀
- Nano RP2040連接板的應用程序 5019次閱讀
- 基于樹莓派產品 Raspberry Pi微控制器板的優缺點 2983次閱讀
- 基于RP2040的Pico可用作PIO的邏輯分析儀 1w次閱讀
- 環境監測傳感器的工作原理及設計 6393次閱讀
- 城市網格化大氣環境監測系統介紹 5630次閱讀
- 一種DSP和nRF24L01的無線環境監測系統的設計 2507次閱讀
- 微波輻射技術在環境監測的應用分析 1813次閱讀
- 基于ZigBee溫室環境監測系統電路設計 9243次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數據手冊
- 1.06 MB | 532次下載 | 免費
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費
- 3TC358743XBG評估板參考手冊
- 1.36 MB | 330次下載 | 免費
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費
- 5元宇宙深度解析—未來的未來-風口還是泡沫
- 6.40 MB | 227次下載 | 免費
- 6迪文DGUS開發指南
- 31.67 MB | 194次下載 | 免費
- 7元宇宙底層硬件系列報告
- 13.42 MB | 182次下載 | 免費
- 8FP5207XR-G1中文應用手冊
- 1.09 MB | 178次下載 | 免費
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 2555集成電路應用800例(新編版)
- 0.00 MB | 33566次下載 | 免費
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費
- 4開關電源設計實例指南
- 未知 | 21549次下載 | 免費
- 5電氣工程師手冊免費下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費
- 6數字電路基礎pdf(下載)
- 未知 | 13750次下載 | 免費
- 7電子制作實例集錦 下載
- 未知 | 8113次下載 | 免費
- 8《LED驅動電路設計》 溫德爾著
- 0.00 MB | 6656次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費
- 2protel99se軟件下載(可英文版轉中文版)
- 78.1 MB | 537798次下載 | 免費
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費
- 6電路仿真軟件multisim 10.0免費下載
- 340992 | 191187次下載 | 免費
- 7十天學會AVR單片機與C語言視頻教程 下載
- 158M | 183279次下載 | 免費
- 8proe5.0野火版下載(中文版免費下載)
- 未知 | 138040次下載 | 免費
評論
查看更多