太陽能板的天氣氣象站,我在上一篇有提到如何製作ESP8266版本的。這一次,我們教各位NBIoT版本的。
NBIoT版本的我會加上電池自我檢測的功能。另外Line的通知功能,我們要移到Node-RED上,也就是NBIoT將抓取到的環境溫度、溼度、氣壓及海拔數據,透過MQTT上傳到Node-RED的MQTT主題,再由mysql工具及line-notify工具,分別執行寫入雲端資料庫及line通知聊天室的功能。
文章目錄標題
氣象站具備以下功能:
- 氣象站可以測量:溫度、溼度、氣壓、海拔
- 您可以從智能手機、Line或自架網站即時瞭解天氣參數值
- 整個電路以及電源都放在3D列印的外殼中
- 因為是NBIoT版本,需要申請SIM卡做為行動網路訊號收取,不過,也因為是行動網路,所以只要附近有基地台可以收到訊號,就可以運作。
所需的零件和工具
- DSI2598
- TP 4056充電板
- BME280感測器
- 太陽能板
- 單面纖維板
- 鋰離子充電電池及電池座
- 22 AWG電線
- 熱溶膠
- 3D列印PLA線材
使用工具:
- 3D列印機
- 烙鐵
- 熱溶膠槍
- 剪線鉗/剝線鉗
供電系統
本系統是使用在戶外或偏遠地方,要持續運作氣象站,必須有連續的電源,否則系統將無法工作。可以向電路提供連續電源的最佳方法是使用電池,只是一般電池放電幾天之後,電池電量將耗盡。因此,本範例提出了一種太陽能充電電路,讓使用者利用從太陽中得到電能,為電池充電並為DSI2598板供電。本案例是使用18650電池。
電池通過TP4056充電模組從太陽能面板充電。TP4056模組帶有電池保護晶片及不帶保護晶片。建議購買包含電池保護晶片的模組。
關於TP4056電池充電器
TP4056模塊非常適合為3.7V 1 Ah或更高的LiPo單體電池充電。基於TP4056充電器IC和DW01電池保護IC,該模組將提供1000 mA充電電流,然後在充電完成時切斷。另外,當電池電壓降至2.4V以下時,保護IC會切斷負載,以保護電池免受欠壓。它還可以防止過電壓和反極性連接。
測量天氣數據
早期,天氣參數(如環境溫度、濕度和氣壓)是通過單獨的模擬儀器測量:溫度計,濕度計和氣壓計。但如今,市場上充斥著可用於測量各種環境參數的價位低且高效的數位感測器。最好的例子是DHT11,DHT 22,BMP180,BMP280等感測器。
在此項目中,我們將使用BMP 280感測器。
BME 280:
BME280是一種複雜的感測器,可以非常準確地測量氣壓和溫度。BME280是Bosch的下一代感測器,是BMP085 / BMP180 / BMP183的升級產品,具有0.25m的低海拔噪聲和相同的快速轉換時間。
該感測器的優勢在於它可以使用I2C或SPI與微控制器進行通信。為了簡單易接線,建議購買I2C版本。
使用外部天線(3dBi)
DSI2598 開發板內建陶瓷天線,並可以連接外部天線以擴大範圍。在使用外部天線之前,必須將天線信號從內置陶瓷天線重新路由到外部插座,這可以通過旋轉(0603)零歐姆電阻來完成。可以看Alex Eames製作的影片來旋轉零歐姆電阻。然後將天線SMA連接器卡入 DSI2598 迷你天線插槽。
焊接針腳、電路圖架構
本範例太陽能大氣氣象站的電路圖架構圖如上圖所示,各位可以依上圖電路圖焊接。
DSI2598 -> BME 280
3.3 V -> VCC
GND ->GND
D13 -> SCL
D11 -> SDA
D10 -> CSB
D12 -> SDO
TP4056連接
太陽能電池板端子-> +和-靠近微型USB端口
電池端子-> B +和B-
DSI2598的VIN(5V)和GND-> Out +和Out-
電池電量偵測電阻
TP4056 B+ -> 100k 歐姆 -> (DSI2598) A4 -> 100k 歐姆 -> (DSI2598) GND及TP1056 B-
焊接在纖維板上
參考上述電路圖,將引腳母接頭、電路線焊接在纖維板上,再將DSI2598、TP4056、BME280、太陽能板及電池盒安裝上。
氣象站外殼
這裏從作者已經設定好的stl檔直接下載印出來。我使用的3D列印規格如下:
3D印表機:Creality Ender 3 Pro
材料:PLA 1.75mm
主體印12小時、封面蓋子3.5小時
層高設定:0.2
射出頭溫度:預設200度
床溫:預設60度
印出速度:40 mm/s
程式碼編寫
這邊我是參考這篇NBIoT的開發方式,再改成自己要應用的程式碼,主要架構我要想要用NBIoT(DSI2598) + MQTT服務 -> 再導向將資料新增到MySQL資料庫及Line Notify服務通知功能。太陽能大氣氣象站定即將偵測到的相關數據傳送給MQTT Server,再用Function去新增寫入資料到MySQL及Line Notify服務,通知Line設定的聊天室群組。Line Notify相關設定請參考我之前的文章,MySQL的Node-RED使用可以參考ESP8266的天氣氣象站文章。另外,我還加入18650鋰電池電量偵測功能,電池電量偵測系統可以參考這篇文章。
它們的程式碼分別如下所示:
電池電量偵測系統程式碼:
int analogInPin = A4; // Analog input pin
int sensorValue; // Analog Output of Sensor
float calibration = 0.67; // Check Battery voltage using multimeter & add/subtract the value
int bat_percentage;
void setup()
{
Serial.begin(115200);
}
void loop()
{
sensorValue = analogRead(analogInPin);
float voltage = (((sensorValue * 3.3) / 1024) * 2 + calibration); //multiply by two as voltage divider network is 100K & 100K Resistor
bat_percentage = mapfloat(voltage, 2.8, 4.2, 0, 100); //2.8V as Battery Cut off Voltage & 4.2V as Maximum Voltage
if (bat_percentage >= 100)
{
bat_percentage = 100;
}
if (bat_percentage <= 0)
{
bat_percentage = 1;
}
Serial.print("Analog Value = ");
Serial.print(sensorValue);
Serial.print("\t Output Voltage = ");
Serial.print(voltage);
Serial.print("\t Battery Percentage = ");
Serial.println(bat_percentage);
delay(5000);
}
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
BMP280感測器程式碼如下:
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <SPI.h>
#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10
#define SEALEVELPRESSURE_HPA (1013.25)
//Adafruit_BME280 bme; // I2C
Adafruit_BMP280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
unsigned long delayTime;
void setup() {
Serial.begin(115200);
Serial.println(F("BME280 test"));
bool status;
// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin();
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
Serial.println("-- Default Test --");
delayTime = 5000;
Serial.println();
}
void loop() {
printValues();
delay(delayTime);
}
void printValues() {
Serial.print("Temperature = ");
Serial.print(bme.readTemperature());
Serial.println(" *C");
// Convert temperature to Fahrenheit
/*Serial.print("Temperature = ");
Serial.print(1.8 * bme.readTemperature() + 32);
Serial.println(" *F");*/
Serial.print("Pressure = ");
Serial.print(bme.readPressure() / 100.0F);
Serial.println(" hPa");
Serial.print("Approx. Altitude = ");
Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(" m");
Serial.print("Humidity = ");
//Serial.print(bme.readHumidity());
Serial.println(" %");
Serial.println();
}
NBIoT整合兩個程式碼的應用如下:
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BMP280.h>
#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10
#define SEALEVELPRESSURE_HPA (1013.25)
#include "BC26.h"
//Adafruit_BMP280 bmp; // I2C
Adafruit_BMP280 bmp(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
unsigned long delayTime;
/*這一段是偵測電池電量的變數宣告*/
int analogInPin = A4; // Analog input pin
int sensorValue; // Analog Output of Sensor
float calibration = 0.67; // Check Battery voltage using multimeter & add/subtract the value
int bat_percentage;
float voltage;
/*這一段是偵測電池電量的變數宣告*/
void setup() {
Serial.begin(9600);
mySerial.begin(9600);
delay(500);
while(!Serial); // time to get serial running
Serial.println(F("BME280 test"));
BC26_initail(); // 啟動與設定連線的伺服器
delay(5000);
bool status;
// default settings
status = bmp.begin();
if (!status) {
Serial.println("Could not find a valid BMP280 sensor, check wiring, address, sensor ID!");
while (1);
}
Serial.println("-- Default Test --");
delayTime = 1800000;
Serial.println();
int sta_pre = sta;
build_MQTT_connect(server_IP, server_port);
delay(5000);
reading(sta_pre, sta);
connect_MQTT();
delay(5000);
reading(sta_pre, sta);
delay(10000);
}
void loop() {
sendBatteryValue();//傳送偵測到的電池電量值
printBME280Values();//傳送BME280偵測到的值
delay(delayTime);
}
void printBME280Values() {
Serial.println("GetTemp&Humi&Pre&Alt OK! ");
float Temperature = bmp.readTemperature();
Serial.print(Temperature); Serial.println(" °C");
//float Humidity = bmp.readHumidity();
//Serial.println(Humidity); Serial.println(" %");
float Pressure = bmp.readPressure() / 100.0F;
Serial.print(Pressure); Serial.println(" hPa");
float Altitude = bmp.readAltitude(SEALEVELPRESSURE_HPA);
Serial.print(Altitude); Serial.println(" m");
MQTTtopic="nbiot/bmp";
MQTTmessage=String(Temperature)+","+String(Pressure)+","+String(Altitude)
+","+String(sensorValue)+","+String(voltage)+","+String(bat_percentage);
Publish_MQTT(MQTTtopic, MQTTmessage);
Serial.println("PutTemp&Humi&Pre&Alt OK! ");
}
void sendBatteryValue(){
sensorValue = analogRead(analogInPin);
voltage = (((sensorValue * 3.3) / 1024) * 2 + calibration); //multiply by two as voltage divider network is 100K & 100K Resistor
bat_percentage = mapfloat(voltage, 2.8, 4.2, 0, 100); //2.8V as Battery Cut off Voltage & 4.2V as Maximum Voltage
if (bat_percentage >= 100)
{
bat_percentage = 100;
}
if (bat_percentage <= 0)
{
bat_percentage = 1;
}
Serial.print("Analog Value = ");
Serial.print(sensorValue);
Serial.print("\t Output Voltage = ");
Serial.print(voltage);
Serial.print("\t Battery Percentage = ");
Serial.println(bat_percentage);
}
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
Node-RED設定
因為要有新增到MySQL雲端資料庫的功能及Line Notify的功能,所以我們先到右上角部署旁的功能鍵下的設置地方。我們可以看到使用者設置這邊,到Palette的安裝搜尋處,填入關鍵字”node-red-node-mysql”,按下安裝按鍵。安裝完成後,我們可以在儲存的地方看到mysql元件。接著填入”node-red-contrib-line-notify”,按下安裝。安裝完成後,我們可以在Line的地方看到line-notify元件。
接下來我們拉出七個節點,分別是mqtt in、function、mysql、debug、function、line-notify及debug。
節點設定如下:
mqtt in:接收ESP8266端發送MQTT訊息
function:判斷MQTT訊息,並將訊息折分為需要的值;將值填入INSERT INTO 語法中
mysql:連線MySQL資料庫,將收到INSERT INTO語法及值送出,寫入到雲端資料庫中
debug:檢測每一筆從收到MQTT傳送來的值到寫入mysql資料庫中的作業是否成功或失敗
function:判斷MQTT訊息,並將訊息折分為需要的值;將值組合為msg要傳給line-notify傳訊用
line-notify:先到line bot設定好並取得token值,接著就是將收到function傳過來的msg經由line bot傳送給line聊天室
debug:檢測每一筆從收到MQTT傳送來的值到寫入line-notify的作業是否成功或失敗
七個節點的設定及語法如下:
mqtt in 節點最重要的地方就是主題設定要跟NBIoT(DSI2598)傳送的主題相同,這樣才可以收到它傳送來的值。
mysql function 節點功能是收到mqtt的主題的訊息,該訊息包含溫度、溼度、氣壓及海拔四個值,所以這裡就是將它折分為四個參數值,並填入INSERT INOT語法中,再傳給mysql節點。
msyql節點就是設定它跟雲端的mysql資料庫主機連線,讓從function傳過來的INSERT INTO語法可以寫入參數值。
Line Msg function節點功能是收到mqtt的主題的訊息,該訊息包含溫度、氣壓、海拔、電池電量偵測值、電壓及電量百分比等七個值,所以這裡就是將它折分為七個參數值,並填入INSERT INOT語法中,再傳給Line Notify節點。
Line-Notify節點功能是收到function傳送過來的msg後,再送出給Line 聊天室的功能。這邊要先到Line bot設定ok後,取得token值,再將token填入本節點的設定。
最後測試,各位可以參考instructables這位作者提供的APP及上傳到ThingSpeak網站來測試結果,而我這邊則是以測試Line聊天室收到的訊息及雲端資料庫來做測試。
天氣氣象站網站建置
當我們建置好從天氣氣象站的感測器讀取數據上傳到Node-RED後,我們也可以將訊息寫到資料庫中,用網站網頁的方式呈現出來,這樣不管是用電腦或手機都可以隨時查詢氣象站的即時數據。接著,我就來講解如何用WordPress製作這樣的網站內容。
首先,你可以找一台樹莓派或是用Oracle VM VirtualGBox新增一個虛擬主機或是在NAS中新增一台虛擬主機或是直接在Windows電腦上安裝XAMPP….等,反正就是用自己的方式,建置一個網站伺服器的環境,我自己則是自己下載TrueNas灌一台主機,並在該台設備上切出VM,然後在上面架設Nginx Server及安裝Docker及Portainer,這樣就可以在Docker上隨時配置建構多台WordPress網站。若各位學員有興趣,我們之後也可以開設建置這樣的課程。
假設我們已經配置並建構好一個WordPress網站,我這裏以weather.omia.site這個網站來說。
我這裡有安裝三個外掛,Elementor、Insert PHP Code Snippet及Head,Footer and Post Injections這三個外掛。
Elementor是文章編輯器這我就不用講解了,因為只要有玩WordPress的人應該都知道它的存在。
Insert PHP Code Snippet則是想要在文章、頁面或自訂面板中放入PHP程式碼運作,我們就會需要安裝這個外掛來執行。
Head,Footer and Post Injections則是我想放置一段JavaScript程式碼,所以安裝這個外掛工具。
新增PHP程式碼到PHP Code Snippet中
下面是顯示最新的環境溫度、氣壓及海拔訊息的php程式碼
<?php
require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/wp-config.php');
$con = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD,DB_NAME);
//查詢
$sql = "SELECT * FROM IoT_Weather_Station_NBIoT ORDER BY wid DESC LIMIT 1";
$result = mysqli_query($con,$sql);
$colums = mysqli_num_fields($result); //列
echo "<table><tr>";
for($i=1; $i < $colums-1; $i++){
$field_name=mysqli_fetch_field_direct($result,$i);
if($field_name->name == 'temperature'){
echo "<th style='background-color:#FF8633;text-align:center' >溫度</th>";
}else if($field_name->name == 'pressure'){
echo "<th style='background-color:#FF8633;text-align:center' >氣壓</th>";
}else if($field_name->name == 'altitude'){
echo "<th style='background-color:#FF8633;text-align:center' >海拔</th>";
}else if($field_name->name == 'sensorValue'){
echo "<th style='background-color:#FF8633;text-align:center' >感測值</th>";
}else if($field_name->name == 'voltage'){
echo "<th style='background-color:#FF8633;text-align:center' >電壓</th>";
}else if($field_name->name == 'bat_percentage'){
echo "<th style='background-color:#FF8633;text-align:center' >電池容量</th>";
}
}
echo "</tr>";
while($row=mysqli_fetch_row($result)){
echo "<tr>";
for($i=1; $i<$colums-1; $i++){
if($i != 2){
echo "<td style='background-color:#7E7E7E;text-align:center;'><font color='#EFEFEF'>$row[$i]</font></td>";
}
}
echo "</tr>";
}
echo "</table>";
?>
新增該php程式碼後,我們可以看到它的短代碼如下:
然後,我們到首頁的頁面中,將該短碼放到短碼工具中。如下設定:
最後再按下發佈,並將該頁面設定為首頁,這樣我們就可以到網址中看到我們抓取的氣象站數據呈現。
但是我們不希望每30秒或是每5分鐘去按下網頁的Refresh(F5)按鍵來更新網頁的內容,所以這時我們的另一個外掛Head,Footer and Post Injections就發揮作用。安裝完該外掛後,我們可以在設定下找到該外掛的設定。
開啟Header and Footer後,我們只要在插入及標籤之間下面填入下面程式碼,這樣我們可以達到每5分鐘refresh頁面的效果。content=”300″代表300秒也就是5分鐘的意思。
<meta http-equiv="refresh" content="300">
總結
本篇文章分享了有關NBIoT版本如何從零到建置天氣氣象站的步驟,希望各位學員喜歡。
之後,我會再新增ESP32版本的天氣氣象站網站實作課程,敬請期待!

