基於ACSip S76S使用LoRa技術的智慧家庭無線-溫濕度偵測方案

关键字 :ACSipS76SLoRa
家庭智慧化是我們一直以來努力的目標,市面上也出現很多智慧的產品,大抵可以分為兩類: 一類是大場作品如華碩、Google、Apple、小米等等; 一類是獨立購買的單一功能產品,如無線門鈴、智慧插座、無線燈控等等。大廠作品的好處是系統整合完整,可以互相支援連動,但是缺點也很明顯,就是『天地萬物﹐朕賜給你的才是你的﹐朕不給﹐你不能搶。 』,就是大廠沒出的你就沒得用。獨立購買單功能的好處是,想要什麼買什麼,但是缺點就是,沒有整合,當設備一多起來,光遙控器就超過你的手指

這個系列就是要來把上面兩類的優點合併起來,將家裡會用到的設備全部無線化,又有統一的介面可以管理既然講到無線,當然要提到最適合DIY應用的LoRa無線技術。LoRa有4大優點
1.使用Sub1GHz頻段,繞射能力強,傳輸距離長,全家都收的到
2.尺寸小巧,而且省電,不用常常換電池
3.市面上唯一可以自行建構網路的IOT技術,適合家庭應用
4.設計圖與code原廠都開放網路可以查詢,建置成本低廉

這次為了簡化開發難度我們使用詮鼎代理群登的LoRa模組S76S。S76S的好處是尺寸小巧,只有11mm x 13mm,而且有開發版,腳位有外拉出來,方便開發。話不多說,先來第一系列

智慧家庭無線化一:溫濕度計無線化

目標:

1. DIY製作包含LoRa模組的溫濕度計
2. 接收小主機要可以使用電腦,手機監看
3. 要可以一個小主機接收多個溫濕度計

使用元件:



一、感應器端硬體設置:
將DHT22第一隻腳接Arduino Uno的5V腳位,第二隻先接到UNO的ping8,在串一個10K的電阻在接到5V,第四隻腳接到地
S76S的3.3V腳位接到UNO的3.3V,TX、RX腳位分別接到UNO的pin0跟pin1,GND接地
接線示意圖如下:


實際接線圖如下:



二、ATMEGA328跟溫濕度資料讀取設定:
使用Arduino的好處是可以有程式庫可以直接呼叫而且還是以物件導向的方式撰寫
首先載入官方的DHT程式庫
#include "DHT.h"
載入後,要先宣告才可以使用,物件宣告有兩個參數,一個是所接的腳位一個是硬體類型,如下:
DHT dht(8, DHT22);
現在只需要接一個DHT22所以只要宣告一個就可以了
如果一個不夠,一樣可以宣告加上去,如下:
DHT dht1(0, DHT22)
DHT dht2(1, DHT22)
      .

再來是取得溫濕度的資料,使用到兩個物件:readTemperature()readHumidity()
使用的方式很簡單就是dht.readTemperature()dht.readHumidity()
也可以用isnan()函式來檢查是否讀取錯誤
收到的溫度預設就是攝氏,如果要華氏的話要加上「true」,readTemperature(true),這樣就會是華氏
另外DHT的文件有說明,讀取的時間為250ms,每次的間隔需要2秒以上

校正函式
在讀取溫濕度資料的時候,大家最擔心的當然是資料不正確,或是不准
因為這顆DHT22是線性的感測器,雖然是線性的,但是這世上沒有完美的線路,一定會有類比誤差或是干擾
標準有兩種方式來修正,一個是線路的方式,透過運算放大器上設計一個偏搭DC電流的調整來做offset的補償跟用一個放大再次大倍率的微調電子來做Scalar的補償
另一種就是我們現在用的簡易的軟體計算修正,利用線性方程式:Y=ax+b的原理來作一個校正函數
float Correction((float raw, float scalar, float offset)
{
return(scalar*raw + offset);
}


三、S76S設定:
S76S選擇使用的是P2P的程式碼,這個程式碼的特點是已經定義好他的群組為1個Master對32個Slave,
每一個Slave佔用的時間為0.5秒,所以詢問完一個群組為16秒鐘

32個Slave的設計是適合智慧家庭的
架構圖如下:

設定如下:
1. 首先接到用USB接到電腦做設定
2. 讀取韌體版本:『GetFWVersion
3. 設定模式,有標準模式(inNormal )跟Ping Pong(inTD)測試模式,我們當然是用標準模式:『SetSystemMode inNormal
4. 使用指令設定為Master或是Slave:『LoraMode SLAVE
5. 使用指令設定TX Power範圍為5-20:『LoRaSetPower 20
6. 使用者令設定SF範圍為8/12:『LoraSetSF 10
    這設定會影響資料的長度
     LoraSlavePld (bytes)
     => payload size : SF8=40 ; SF9=40 ; SF10=15 ; SF11=12 ; SF12=12
     LoraMasterPld(bytes)
     => payload size : SF8=40 ; SF9=40 ; SF10=12 ; SF11=9 ; SF12=9
7. 使用指令設定8個跳頻的頻率:『LoraSetChannelFreq 0 926000000
    0是第一個頻率,因為有8個挑頻,所以是0-7
    頻率的限制範圍為 863000000~928000000
8. 設定位址,這個很重要,不然怎麼知道要給誰:『LoraSetMyAddr 9 9 9
    設定範圍請看說明書
9. 設定啟用就可以開始動作:『LoraStartWork ENABLE

四、ATMEGA328+溫濕度+LoRa:

以上全部設定完成之後,以Arduino端只要做一件事情,就是將DHT收到的資料透過S76S送出去,因為這是溫濕度計,不需要控制,所以只要單向傳送就可以了。

完整的程式碼如附件



五、接收器端硬體設置:

將S76S的3.3V與GND分別接到ESP-32S的pin19與pin6,再將S76S的TX、RX分別接到ESP-32S的pin15與pin16
接線示意圖如下:

實際接線圖如下:


六、S76S設定:

S76S的設定(三),差別是,改成設定為Master,在啟動即可

七、ESP-32S設定:

在ESP-32S的部分,一樣可以利用ARduino編輯程式
實際操作說明:

首先載入WiFi跟UDP程式庫
#include "WiFi.h"
#include

然後設定好要對外連線的Wifi網路

const char * ssid = "***********";
const char * password = "***********";

//網路定義
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 0, 27); //發送後端ip
unsigned int localPort = 6666; // UDP走的Port
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer 緩存區,用來儲存收到與發出的訊息
const int Easycon_PACKET_SIZE = 152;
char packetBuffer[PACKET_SIZE]; //buffer to hold incoming packet,218byte
char ReplyBuffer[160]; // a string to send back,218byte

// 透過UDP接收與傳送訊息
EthernetUDP Udp;

系統啟動時,確保無線網路是否連線成功

WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Failed");
while(1) {
delay(1000);
}

增加NTP網路校時的功能
//NTP Code
IPAddress timeServer(118, 163, 81, 61); // time.stdtime.gov.tw

//-------- NTP code ----------

const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte NTPBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

time_t getNtpTime()
{
while (Udp.parsePacket() > 0) ; // discard any previously received packets
//Serial.println("Transmit NTP Request");
sendNTPpacket(timeServer);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
//Serial.println("Receive NTP Response");
Udp.read(NTPBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)NTPBuffer[40] << 24;
secsSince1900 |= (unsigned long)NTPBuffer[41] << 16;
secsSince1900 |= (unsigned long)NTPBuffer[42] << 8;
secsSince1900 |= (unsigned long)NTPBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;

}
}
//Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// set all bytes in the buffer to 0
//memset(packetBuffer, 0, NTP_PACKET_SIZE);
memset(NTPBuffer, 0x00, sizeof(NTPBuffer));
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
NTPBuffer[0] = 0b11100011; // LI, Version, Mode
NTPBuffer[1] = 0; // Stratum, or type of clock
NTPBuffer[2] = 6; // Polling Interval
NTPBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
NTPBuffer[12] = 49;
NTPBuffer[13] = 0x4E;
NTPBuffer[14] = 49;
NTPBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(timeServer, 123); //NTP requests are to port 123
Udp.write(NTPBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}

//網路回傳資料子函式

void Send(){                    
                Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
                Udp.write(ReplyBuffer);
                Udp.endPacket();
                delay(50);
               memset(ReplyBuffer, 0x00, sizeof(ReplyBuffer)); //清空陣列資料

}

主程式:
在系統啟動時偵測LoRa端是否有資料近來
while (Serial1.available() > 0) //LORA端有資料進來
{

Re += char(Serial1.read());
delay(2);
}

if (Re.length())
{
Re.toCharArray(ReplyBuffer, 160);


Re = "";
Send(); //利用函式傳出

}
                                           展示:
因為這個方案是利用ESP-32S將資料用UDP的方式丟出
所以接收端就非常自由,只要可以接收UDP訊號的設備都可以當作接收介面
在這裡是用IBM開發的Node-Red去做畫面演示

第一步:先將流程圖一一拉出來設定好:
如下圖


第二部就是將資料顯示出來就大功告成了
如下圖


附件檔案列表



场景应用图

sceneryUrl