【NB-IoT】DSI2598開發板之物聯網建置與應用

作者:柯大

為因應進入萬物連網的物聯網時代到臨,對於裝置省電、長距離傳輸資料的需求,低功耗通訊廣域網路(Low Power Wide Area Network, LPWAN)因應而生,LPWAN由於不要求複雜的影音、大資料傳輸、即時訊息等,是萬物聯網最具體的實現。發展上主要以從使用的無線電頻段分成非授權頻段(Unlicense Band) 的LoRaWAN、Sigfox與使用授權頻段(License Band)由3GPP主導的NB-IoT技術,2017年國內三大電信業已投入物聯網NB-I oT通訊設備佈建及基地台更新。

由於NB-IoT使用電信級網路,所以對於網路品質、訊息安全都有高度的保障,而3GPP在 2017年發表R14版本的技術規範,對NB-IT產品可以更完善地支援大規模連接、減少隨機接入衝突,並增強NB-IoT技術的移動性服務連續性(Mobility and service cont、定位(Positioning)、多點傳送(Multicast)、移動性與inuity)、新的功率消耗模式(New Power Class)、降低功率消耗與延遲性(Power consumption and latency reduction),讓NB-IoT的技術競爭力更為完整。NB-IOT的核心特點如下:

  •  海量連接:每基地台可達10萬裝置連接。
  • 超低功耗:獨特PSM可提供長時電池供電。
  • 深度覆蓋:能實現比 GSM 高20db的覆蓋增益。
  • 低成本:低速率低功耗低頻寬帶來的是低成本優勢。

NB-IoT優勢(Source

資策會數位服務創新研究所(以下簡稱「服創所」),為了協助創新應用開發端經由開發板設計應用產品,可藉由雛型產生過程的設計、電路製作過程,可更快速利用IoT開發板搭配小量製造商媒合,加速量產進程,以國內晶片大廠聯發科技股份有限公司(Mediatek,簡稱「聯發科」) MT2625全頻段NB-IoT晶片催生了「DSI 2598」NB-IoT量產導向的物聯網國產開發板,該開發板結合了聯發科MT2625晶片(由Quectel包裝成BC26/BC6模組)及Microchip(ATMEL) ATMEGA328P MCU晶片,有助於物聯網產品加速導入NB-IoT通訊功能。

Mediatek的MT2625晶片的IoT應用情境(圖片來源:聯發科)

「DSI 2598」NB-IoT開發板提供Arduino Nano ATMEGA328P GPIO、I2C、UART、PWM、SPI、ADC等多項輸出入介面,以簡單易學的Arduino IDE 為開發環境為主,是國內首款Arduino-based NB-IoT開發板,以Arduino IDE為主要的程式開發環境,讓開發者可以更輕易的開發各項物聯網應用雛型。

「DSI 2598」NB-IoT開發板應用架構示意圖 (圖片來源:資策會)

「DSI 2598」NB-IoT開發板的硬體主要以Arduino Nano ATMEGA 328 MCU為主,並將MCU Pin8、Pin9直接連接至MT2625(BC26/BC66)NB-IoT模組的UART介面(Tx/Rx)做為通訊傳輸使用,其餘MCU接腳均大部份接出使用,以提供開發者可以繼承原Arduino Nano I/O使用。

DSI2598開發板硬體資料規格(圖片來源:作者提供)

對照原始ATMEGA 328P可用腳位圖如下:

ATMEGA 328P可用腳位圖(Source

其中D8、D9 Pin 已被佔用為SoftwareSerial UART用做ATMEGA 328連接BC26/B66 NB-IoT模組 UART0。DSI 2598一代將ATMEGA 328P腳位接出圖如下:

第一代DSI 2598腳位功能說明(圖片來源:資策會)

DSI2598二代為了更精簡開發板硬體大小,僅接出SPI:D10、D11、D12、D13,I2C:A4、A5(可用為Analog類比輸入用,GPIO:D2(可用為硬體中斷使用)、D3,另5V、3.3V為便利不同感測器的電壓需求,均接出提供使用,Vin:可外接電池供電(可接3.3V~5V供電)。開發模組搭配方便黏貼於模組內部,不占空間的,訊號強度可達3dB~8dB。

第二代DSI 2598腳位功能說明

資策會服創所為了讓所有開發者可以將NB-IoT的應用快速商品化,特別將「DSI 2598」NB-IoT開發板所有的硬體設計開放資源提供下載,網址:https://t.ly/XGYEV

硬體設計開源資料包括:SMT打件用座標檔、開發版Bom表資料、電路板原理圖、PCB Layout,並提供小量製造商媒合資料於資策會官網 ideas Hatch供參考:https://www.ideas-hatch.com

現在讓我們開始來體驗「DSI 2598」NB-IoT開發板,我們只要按照下列步驟一步一步進行,將可順利使用開發板。

一、安裝Arduino IDE環境及DSI 2598 NB-IoT硬體連接

前置步驟1:安裝Arduino IDS 1.8.5版本。
前置步驟2:安裝CH341SER驅動軟體。
前置步驟3:燒錄模組與DSI2598接線。
前置步驟4:如使用DSI 2598二代開發板,可略過此三項步驟。
前置步驟5:程式碼下載:https://t.ly/DKkB2,下載後解壓DSI2598_ATcommand.ino。

二、開發板基礎設定:模組檢查及頻段設定及APN設定

1. 打開DSI2598_ATcommand.ino,並至Arduino的Tool->Port中找到USB模組的COM PORT編號,可至控制台確認。
2. 選擇Tool->Board->Arduino Nano,然後按下 Upload(Ctrl+U),將程式燒錄上傳至DSI2598。

DSI2598_ATcommand.ino 程式碼如下:

#include <SoftwareSerial.h>
SoftwareSerial mySerial(8, 9);
String data = "";
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
mySerial.begin(9600);
}

void loop() {
// put your main code here, to run repeatedly:
while (mySerial.available()){ //Check if there is an available byte to read,
delay(10); //Delay added to make thing stable
char c = mySerial.read(); //Conduct a serial read
if (c == '\n') {break;} //Exit the loop when the "" is detected after the word
data += c; //Shorthand for data = data + c
}

if (data.length() > 0) { // 判斷data內有值在更換
data.trim();
delay(100);
Serial.println(data);
}
data = "";

while (Serial.available()){ //Check if there is an available byte to read,
delay(10); //Delay added to make thing stable
char c = Serial.read(); //Conduct a serial read
if (c == '\n') {break;} //Exit the loop when the "" is detected after the word
data += c; //Shorthand for data = data + c
}

if (data.length() > 0) { // 判斷data內有值在更換
data.trim();
delay(100);
// Serial.println(data);
mySerial.println(data);
}
data = "";
}

3. 打開Serial monitor,在上方輸入欄中輸入AT指令,可先輸入「AT」,查看模組是否有回覆「OK」。

4. 輸入ATI指令,查看模組廠商、型號及韌體版本。

5. Sim卡狀態查詢:AT+CPIN?
回覆:READY,表示有找到SIM卡。
回覆:ERROR,表示沒有SIM卡。

6. 頻寬設定:AT+QBAND=1,8。
本測試使用的是中華電信NB-IoT的服務,使用的頻段是Band8。

7. 設定啟用APN:AT+QGACT=1,1,”apn”,”internet.iot”。
中華電信NB-IoT服務的AP N :“internet.iot”。
遠傳電信APN:”nbiot”。
台灣大哥大APN:”twm.nbiot”。

8. 設定預設註冊APN:AT+QCGDEFCONT=”IP”,”internet.iot“。

9. 啟動網路:AT+CGATT=1。
回覆:OK,表示正常啟動網路。
回覆:ERROR,表明模組剛開機,SIM卡尚未準備好,請等待片刻。若仍然ERROR,很可能是SIM卡有異常。

10. 儲存設定值:AT&W。
回覆:OK,表示正常啟動網路。

11. 確認是否成功聯網:AT+QLEDMODE=1
此指令的功能是讓二代板的NB標示LED燈開始閃爍,作為是否聯網的標的。
閃爍頻率快:未聯網
閃爍頻率慢:已連網

12. 重新啟動模組:AT+QRST=1。

三、模組連網啟動及狀態查詢測試

1. 啟動NB-IoT模組:AT+CFUN=1。
回覆:+OK。
AT+CFUN=0:表示網路未啟動。
AT+CFUN=11:表示啟動模組網,注意:還沒回覆”OK”前,不能下其他指令,是否入網還要等待+CEREG指令的確認。

2. 查詢模組功能狀態:AT+CFUN?
回覆: +CFUN:0表示模組功能未打開,若返回:+CFUN:1則表示功能已打開,似查看手機是否處於處於飛行或關機狀態。

3. 設定連接基地台啟動:AT+CSON=1。
回覆:+OK。
4. 查詢網路是否啟動:AT+CGATT?。
回覆:+CGATT:1。
0:表示網路未啟動。
1:表示網路已啟動,注意,等於1表示僅打開網路功能,但是否入網還要等待+CEREG指令的確認。

5. 查詢APN網路是否已註冊狀態:AT+CEREG?
回覆:+CEREG= 0,1,表示已經進入APN的網域。
回覆:+CEREG= 0,2,表示已經尚未註冊入APN的網域。
回覆:+CEREG= 0,0,表示沒有SIM卡。

6. 訊號強度查詢:AT+CESQ。
回覆:+CESQ : xx, 0, 255, 255, 255。
xx:0~99,0 :未有訊號,99 :找不到訊號。

7. IP查詢:AT+CGPADDR=1。
回覆:OK:尚未找到IP。
回覆::+CGPADDR: 1,IP(四位):表示已有IP。
說明:若設定期間連上網路會自動回傳IP位址。
+IP : IP位址(四位)

8. PING遠端伺服器:AT+QPING= <contextID>,<host>。
Ping 中華電信DNS主機IP,回覆如下:

9. 查詢目前信號值:AT+CSQ。
回覆:+CSQ: 16,0。
+CSQ格式如下: +CSQ:, ,欄位的含義為:
參數說明:
99:表示網路未知,或者網路未附著。如果模組關閉了自動附著功能,需要啟動模組網路才能獲取到正確的信號值。
0 :表示信號品質為-113dBm或者以下,信號非常差。
1 :表示信號品質為-111dBm。
2~30:對應信號值為-109dBm到-53dBm。
31:對應信號值為-51dBm或者更高。
值:0~7,若等於99表示未知或未附著到網路。
CSQ中的rssi與dBm換算公式如下:dBm = rssi*2 – 113,例如,當rssi等於30時,
對應dBm為-53dBm

其他常用的查詢AT Command

1. 查詢模組功能狀態:AT+CFUN? 回覆: +CFUN:0表示模組功能未打開,若返回:+CFUN:1則表示功能已打開。 類似查看手機是否處於處於飛行或關機狀態。

2. 查詢SIM卡的ICCID碼:AT+QCCID。 回覆:+QCCID: 89886920047000207633。 ICCID是積體電路卡識別碼:Integrate Circuit Card Identity的首字母縮寫,該號碼可以在SIM卡的背面看到,可以由該識別碼來查詢NB物聯網卡的號碼。

3. 查詢模組目前連線頻段:AT+QBAND? 回覆:+QBAND: 8。

4. 查詢國際行動設備識別碼IMEI碼:AT+CGSN=1。 回覆:+CGSN: 861050042335367。

5. 查詢可用基地台:AT+COPS? 回覆:+COPS: 0,2,”46692″,9。 最後一位數要9 0 User-specified GSM access technology 8 User-specified LTE M1 A GB access technology 9 User-specified LTE NB S1 access technology

6. 設定NB-IoT自動省電模式關閉:AT+SM=LOCK。 由於DSI2598之BC26模組設計上為了省電7秒鐘內沒有任何指令操作,出廠預設會自動進入PSM低功耗模式如果自動進入PSM模式,如果不想讓BC26自動進入PSM模式,請發送指令:AT+SM=LOCK,返回OK後,BC26就不會自動進入PSM模式啦!如果還想讓它自動進入低功耗模PSM 的主要目的是降低模組功耗,延長電池的供電時間,測試時可以設定臨時禁止休眠。

DSI2598 NB-IoT 開發板連線平台測試

資策會服創所整合內部的開發人力,推出了IDEAS Chain一站式AIoT快速開發工具平台,提供簡易且安全的物聯網建置解決方案,能加速Maker、新創團隊與開發者完成專案,IDEAS Chain 可以很簡單、快速導入物聯網數據應用平台。以下將延用上一篇介紹DSI5168 開發板所建的專案來進一步做開發應用。

(一)平台使用步驟及建立測試專案:

1.連線至IDEAS Chain網站:https://www.ideaschain.com.tw/www/iot.html

2.註冊或登入帳號並進入專案管理

3.新增專案

4.新增裝置

5.建立裝置名稱及資訊

6. 新增感測器項目

測試範例延用上一篇介紹DSI5168 開發板所建的專案,並建了DSI5168-1裝置及2個為TEMP溫度、HUMI溼度的感測器,如下:

7.建立溫度感測器SID:TEMP

8.建立溼度感測器SID:HUMI

9.取得專案資訊: AK

AK = cJfV9BJID87ldXZk

10.取得裝置ID: DID

DID = 7255458640135639
SID_1 = TEMP
SID_2 = HUMI

(二)測試上傳範例程式(使用TCP/UDP SOCKET建立連線至IDEAS Chain IoT平台)

本範例以DSI2598開發板接上最常用到的DHT22溫溼度感測器,再以Arduino 程式連上NB-IoT網路後,建立一個Socket Client 連線Session ,連上IDEAS Chain IoT平台(標準TCP Port 80),每35秒讀取DHT22溫溼度感測器並上傳至平台(參閱平台API文件)。

1.首先需透過Arduino IDE 下載及安裝程式庫:
“草稿碼”→匯入程式庫→管理程式庫,搜尋DHT22程式庫,找到程式庫安裝。

(圖片來源:柯清長)

(圖片來源:柯清長)

2.DSI2598與感測器接線方式

(圖片來源:柯清長)

DSI2598 BC26/BC66建立TCP/UDP Socket連線及發送封包(payload以Hex值)AT command ,步驟如下:
AT+QSOC=1,1,1 OK, Create a TCP/UDP Socket
AT+QSOCON=0,7425,”118.184.176.34″ OK, 建立連接
AT+QSOSEND=0,5,1234562112 OK, 發送數據payload以Hex值傳送
AT+QSODIS=0 OK, Disconnect Socket
AT+QSOCL=0 OK, Close Socket

DSI2598 BC26/BC66 以TCP/UDP 上傳至IDEAS Chain 的資料API格式如下:

{"a":"AK","d":"DID","s":"SID","v":"data"}

"a":"AK"  :AK專案API KEY
"d":"DID" :DID裝置ID
"s":"SID" :SID感測器ID(使用者創建時自訂)
"v":"data" :data要上傳的資料內容
以TCP/UDP Socket 上傳資料到平台需將Payload 封包轉為Hex值傳送,
{"a":"AK","d":"DID","s":"SID","v":"data"}  一串Hex值 payload

平台TCP/UDP Server IP: 150.117.122.166 , Port:7789 (可由Ping https://www.ideaschain.com.tw/ 取得IP)。

為了避免伺服器後續又臨時有問題而更換IP,改為使用domain name的方式。建議寫入程式中以前可先使用IP查詢網站查詢IP,再將IP網址複製到程式中。或是在程式碼中加入AT+QIDNSGIP=1,”https://www.ideaschain.com.tw/“,直接讓MCU去存取得到的IP結果也行。

3.範例程式

DSI2598_ideaschain-Temp-v2.ino
#include <Arduino.h>
#include <SimpleDHT.h>
// for DHT22,
// +: 5V or 3V
// Out: 3
// -: GND
#include "BC26.h"
int pinDHT22 = 3;
SimpleDHT22 dht22(pinDHT22);
float temperature = 0;
float humidity = 0;
int err = SimpleDHTErrSuccess;

void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
mySerial.begin(9600);
Serial.println("Boot BC26 ");
BC26_initail(); // 啟動與設定連線的伺服器
delay(2000);
}

void loop() {
// put your main code here, to run repeatedly:
if(reset_count >= 3600){ // 30min後系統重開
resetFunc();
}
if ((err = dht22.read2(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
Serial.print("Read DHT22 failed, err="); Serial.println(err);delay(2000);
return;
}
Serial.println("GetTemp&Humi OK! ");
Serial.print(temperature); Serial.print(" *C, ");
Serial.print(humidity); Serial.println(" %");

SID = "TEMP";
data_update(AK, DID, SID, String(temperature));
delay(5000);
SID = "HUMI";
data_update(AK, DID, SID, String(humidity));
delay(5000);
Serial.println("PutTemp&Humi OK! ");
delay(1000*30);
}

BC26.h
#include
SoftwareSerial mySerial(8, 9);

//String AK = "cJfV9BJID87ldXZk";
String AK = "cJfV9BJID87ldXZk"; // 平台的AK金鑰參數
String DID = "7255458640135639"; // 平台的裝置ID參數
String SID = "TEMP"; // 平台的感測器ID參數

String data = ""; // 儲存來自BC26回覆的字串參數
String data_on = ""; // 擷取部分字串參數的儲存參數
int sta = 1; // 迴圈的status參數判斷
int time_count = 0;
int count = 0; // 副程式中來自BC26的回覆次數計數器
//int count1 = 0; // 副程式中的時間計數器
int band = 8; // 設定頻段(B8)
int IP_count1 = 0;
void(* resetFunc) (void) = 0; // 宣告系統重置參數
int reset_count = 0; // 系統重新啟動計時器宣告
String data_tcp_build = "AT+QSOC=1,1,1"; // 建立TCP通訊的AT指令
/*
* AT+QIDNSGIP=1,"ideaschain.com.tw"
OK
+QIURC: "dnsgip",0,1,0
+QIURC: "dnsgip","150.117.122.166
*/
String tcpserver_connect = "AT+QSOCON=0,7789,\"150.117.122.166\"";
String noreply = "no reply, reset all";

void serial_read(){
while (mySerial.available()){ //Check if there is an available byte to read,
delay(10); //Delay added to make thing stable
char c = mySerial.read(); //Conduct a serial read
if (c == '\n') {break;} //Exit the loop when the "" is detected after the word
data += c; //Shorthand for data = data + c
}

if (data.length() > 0) { // 判斷data內有值在更換
data.trim();
delay(100);
Serial.println(data);
if(sta == 1){
count++;
}
if(sta == 1){
if(count >= 1){ // Turn on BC26 status
// Serial.println("Turn on BC26");
sta++;
count = 0;
}
}else if(sta == 2){
if(data == "OK"){ // 設置頻段
sta++;
}
}else if(sta == 3){ // wait for IP status
for(int i = 0; i < 3; i++){
data_on += data[i];
}
if(data_on == "+IP"){
sta++;
}else if(data_on =="+CG"){
sta++;
}
}else if(sta == 4){
for(int i = 0; i < 7; i++){ data_on += data[i]; } if(data_on == "+QSOC=0"){ // 開啟TCP通訊通道 sta++; } }else if(sta == 5){ if(data == "OK"){ // 連線至伺服器 sta++; } }else if(sta > 5){ // 傳輸資料與伺服器端訊息回傳
// +QSONMI
for(int i = 0; i < 8; i++){ data_on += data[i]; } if(data_on == "+QSONMI=" || data_on == "AT+QSOSE"){ sta++; }else if(data[0] == 'O' && data[1] == 'K'){ sta++; } } } data = ""; data_on = ""; } void BC26_reset() { // reset BC26 mySerial.println("AT+QRST=1"); while (sta == 1) { // 等待模組訊息回覆 serial_read(); delay(1*1000); count++; if (count > 10) { // 超過10秒未有回覆,重新啟動系統
count = 0;
Serial.println(noreply);
resetFunc();
}
}
count = 0;
}

void ask_for_IP(){ // 查詢IP狀況
mySerial.println("AT+CGPADDR=1");
while (sta == 3) { // 等待模組訊息回覆
serial_read();
delay(1*1000);
count++;
if (count > 10) { // 每10秒問一次IP狀況
IP_count1++;
mySerial.println("AT+CGPADDR=1");
count = 0;
if(IP_count1 > 6){ // 一分鐘後仍沒找到IP,重新開機
Serial.println(noreply);
resetFunc();
}
}
}
count = 0;
IP_count1 = 0;
}

void set_band(int band){ // 設置頻段
String AT_band = "AT+QBAND=1,";
AT_band.concat(band);
mySerial.println(AT_band);
while (sta == 2) { // 等待模組訊息回覆
serial_read();
delay(1*1000);
count++;
if (count > 10) { // 超過10秒未有回覆,重新啟動系統
count = 0;
Serial.println(noreply);
resetFunc();
}
}
count = 0;
}

void build_TCP_connect(){ // 建立TCP連線通道
mySerial.println(data_tcp_build);
}

void connect_toserver(){ // 與伺服器連接
mySerial.println(tcpserver_connect);
}

String str2hex(String str){ // API字串轉hex格式function
String hex_string = "";
for(int i = 0; i < str.length(); i++){ hex_string += String(str[i], HEX); } return hex_string; } String api_string(String AK, String DID, String SID, String data){ String api_pass = "{\"a\":\""; api_pass.concat(AK); api_pass.concat("\",\"d\":\""); api_pass.concat(DID); api_pass.concat("\",\"s\":\""); api_pass.concat(SID); api_pass.concat("\",\"v\":\""); api_pass.concat(data); api_pass.concat("\"}"); //Serial.print("api_pass: "); //Serial.println(api_pass); return api_pass; } void reading(int sta_pre, int sta){ while(sta_pre == sta){ // 等待模組訊息回覆 serial_read(); delay(1*1000); break; count++; } if(count > 10){ // 超過10秒未有回覆,
count = 0;
Serial.println("no replay, send next data after 2 seconds");
delay(1*1000);
sta_pre++;
}
count = 0;
sta_pre = sta;
}

void data_update(String AK, String DID, String SID, String data){ // 資料上傳function
Serial.println("Sending data......");
int sta_pre = sta;
build_TCP_connect();
reading(sta_pre, sta);
delay(500);
connect_toserver();
reading(sta_pre, sta);
delay(500);
mySerial.print("AT+QSOSEND=0,"+String(api_string(AK, DID, SID, data).length()+2)+",");
mySerial.println(str2hex(api_string(AK, DID, SID, data))+"0d0a");
reading(sta_pre, sta);
delay(500);
mySerial.println("AT+QSODIS=0");
reading(sta_pre, sta);
delay(500);
mySerial.println("AT+QSOCL=0");
reading(sta_pre, sta);
delay(500);
}

void BC26_initail(){
BC26_reset();
delay(1*1000);
set_band(band);
delay(20*1000); // 等待20秒連線
ask_for_IP(); // 查詢IP狀況
delay(5*1000);
}

void long_delay(int hr, int minut, int sec){ // 設定delay時間長度
for(int i = 0; i < (hr*60*60 + minut*60 + sec); i++){
delay(1000);
}
}

平台接收感測資料結果如下:

(圖片來源:柯清長)

(圖片來源:柯清長)

(圖片來源:柯清長)

(圖片來源:柯清長)

(圖片來源:柯清長)

(圖片來源:柯清長)

(圖片來源:柯清長)

(三)測試上傳範例程式(使用MQTT):

本範例為大家示範利用PC Windows MQTT Client 來Subscrible接收DSI2598 Publish之溫溼度資料,另以Android IoT MQTT Panel APP來Subscrible接收DSI2598範例透過MQTT Publish所上傳的資料,DSI2598與感測器接線方式如圖。

1.先下載MQTT Client 軟體MQTTBox: http://workswithweb.com/html/mqttbox/downloads.html

(圖片來源:柯清長)

2.開啟MQTTBox程式,選”Add New MQTT Client”設定MQTT Client Name 及MQTT Broker。

(圖片來源:柯清長)

MQTT Client Name:DSI2598-MQTT (自定)
Protocol:tcp
Host:broker.hivemq.com:1883 (broker.hivemq.com是開放的MQTT Broker)
Port:1883
MQTTBox 建立2個subscriber ,Topic to subscribe 分別如下:
topic: 溫度: DSI2598/Temp 溼度: DSI2598/Humi

(圖片來源:柯清長)

3.範例程式

DSI2598-DHT22-MQTT-V3.ino(範例程式:每30秒上傳)

#include <Arduino.h>
#include <SimpleDHT.h>

// for DHT22,
// +: 5V or 3V
// Out: 3
// -: GND

#include "BC26.h"

int pinDHT22 = 3;
SimpleDHT22 dht22(pinDHT22);
float temperature = 0;
float humidity = 0;
int err = SimpleDHTErrSuccess;

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
mySerial.begin(9600);
delay(500);
Serial.println("Boot BC26 ");
BC26_initail(); // 啟動與設定連線的伺服器
delay(5000);
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() {
if ((err = dht22.read2(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
Serial.print("Read DHT22 failed, err="); Serial.println(err);delay(2000);
return;
}
delay(1000);
Serial.println("GetTemp&Humi OK! ");
Serial.print(temperature); Serial.print(" *C, ");
Serial.println(humidity); Serial.println(" %");
MQTTtopic="DSI2598/Temp";
MQTTmessage=String(temperature);
Publish_MQTT(MQTTtopic, MQTTmessage);
delay(10000);
MQTTtopic="DSI2598/Humi";
MQTTmessage=String(humidity);
Publish_MQTT(MQTTtopic, MQTTmessage);
delay(10000);
Serial.println("PutTemp&Humi OK! ");
delay(1000*15);
}

BC26.h
#include
SoftwareSerial mySerial(8, 9);

String data = ""; // 儲存來自BC26回覆的字串參數
String data_on = ""; // 擷取部分字串參數的儲存參數
int sta = 1; // 迴圈的status參數判斷
int time_count = 0;
int count = 0; // 副程式中來自BC26的回覆次數計數器
int band = 8; // 設定頻段(B8)
int IP_count1 = 0;
void(* resetFunc) (void) = 0; // 宣告系統重置參數
int reset_count = 0; // 系統重新啟動計時器宣告
String data_tempt = "";
String server_IP = "\"broker.hivemq.com\"";
String server_port = "1883";
String user = "\"Client1\"";
String pass = "\"pass\"";
String noreply = "no reply, reset all";
String MQTTtopic;
String MQTTmessage;
void serial_read(){
while (mySerial.available()){ //Check if there is an available byte to read,
delay(10); //Delay added to make thing stable
char c = mySerial.read(); //Conduct a serial read
if (c == '\n') {break;} //Exit the loop when the "" is detected after the word
data += c; //Shorthand for data = data + c
}

if (data.length() > 0) { // 判斷data內有值在更換
data.trim();
delay(100);
Serial.println(data);
if(sta == 1){
count++;
}

if(sta == 1){
if(count >= 1){ // Turn on BC26 status
sta++;
count = 0;
}
}else if(sta == 2){
if(data == "OK"){ // 設置頻段
sta++;
}
}else if(sta == 3){ // wait for IP status
for(int i = 0; i < 3; i++){ data_on += data[i]; } if(data_on == "+IP"){ data_tempt = data_on; sta++; } }else if(sta == 4){ if(data == "OK"){ // 連線至MQTT平台伺服器 sta++; } }else if(sta == 5){ if(data == "OK"){ // 連線至自己的頻道 sta++; } }else if(sta > 5){ // 傳輸資料與伺服器端訊息回傳
// +QSONMI
if(data == "OK"){
sta++;
}
}
}
data = "";
data_on = "";
}

void BC26_reset(){ // reset BC26
mySerial.println("AT+QRST=1");
while (sta == 1) { // 等待模組訊息回覆
serial_read();
delay(1*1000);
count++;
if (count > 10) { // 超過10秒未有回覆,重新啟動系統
count = 0;
Serial.println(noreply);
resetFunc();
}
}
count = 0;
}

void ask_for_IP(){ // 查詢IP狀況
mySerial.println("AT+CGPADDR=1");
while (sta == 3) { // 等待模組訊息回覆
serial_read();
delay(1*1000);
count++;
if (count > 10) { // 每10秒問一次IP狀況
IP_count1++;
mySerial.println("AT+CGPADDR=1");
count = 0;
if(IP_count1 > 6){ // 一分鐘後仍沒找到IP,重新開機
Serial.println(noreply);
resetFunc();
}
}
}
count = 0;
IP_count1 = 0;
}

void set_band(int band){ // 設置頻段
String AT_band = "AT+QBAND=1,";
AT_band.concat(band);
mySerial.println(AT_band);
while (sta == 2) { // 等待模組訊息回覆
serial_read();
delay(1*1000);
count++;
if (count > 10) { // 超過10秒未有回覆,重新啟動系統
count = 0;
Serial.println(noreply);
resetFunc();
}
}
count = 0;
}

void build_MQTT_connect(String IP, String port){ // 建立TCP連線通道
data_tempt = "AT+QMTOPEN=0,";
data_tempt.concat(IP);
data_tempt.concat(",");
data_tempt.concat(port);
mySerial.println(data_tempt);
}

void connect_MQTT() // 建立TCP連線通道
{
data_tempt = "AT+QMTCONN=0,";
data_tempt.concat(user);
//data_tempt.concat(",");
// data_tempt.concat(pass);
mySerial.println(data_tempt);
}

void reading(int sta_pre, int sta){
while(sta_pre == sta){ // 等待模組訊息回覆
serial_read();
delay(1*1000);
break;
count++;
}
if(count > 10){ // 超過10秒未有回覆,
count = 0;
Serial.println("no replay, send next data after 2 seconds");
delay(1*1000);
sta_pre++;
}
count = 0;
sta_pre = sta;
}

void Publish_MQTT(String topic, String message){ // 資料上傳function
Serial.println("Sending data......");
int sta_pre = sta;
// AT指令:AT+QMTPUB=0,0,0,0,"topic","message"
// topic: DSI2598/Temp DSI2598/Humi
// message: tempture huminity
data_tempt = "AT+QMTPUB=0,0,0,0,";
data_tempt.concat(topic);
data_tempt.concat(",");
data_tempt.concat(message);
Serial.println(data_tempt);
mySerial.println(data_tempt);
delay(1000);
reading(sta_pre, sta);
}

void BC26_initail(){
BC26_reset();
delay(1*1000);
set_band(band);
delay(20*1000); // 等待20秒連線
ask_for_IP(); // 查詢IP狀況
delay(2*1000);

}

void long_delay(int hr, int minut, int sec){ // 設定delay時間長度
for(int i = 0; i < (hr*60*60 + minut*60 + sec); i++){
delay(1000);
}
}

MQTTBox資料接收結果如下:

(圖片來源:柯清長)

(圖片來源:柯清長)

MQTT Panel APP資料接收結果如下:

(圖片來源:柯清長)

結語

為了快速、有效的解決物聯網產品在研發及行銷上的需求,並且可以讓物聯網新創團隊更快速完成初期的原型開發,找到早期使用者進行測試,因此,一套能加快開發的物聯網建置環境會對團隊有很大的幫助。資策會服創所除了為物聯網新創團隊打造了易用的快速開發工具平台外,更串聯國產IC大廠,從硬體開發的源頭下手,為團隊提供量產導向、簡易上手的國產IC硬體開發套件。

利用「DSI 2598」開發板來開發物聯網創新服務商品,能實現用NB-IoT來傳輸感測資料,而開發板所提供的Arduino的快速開發特性,及所搭配提供標準化的Arduino涵式庫,是一套完整的物聯網方案開發板,更可快速打造出IoT產品的雛形。(責任編輯:王姵文)

柯大

Author: 柯大

現為MakerPRO技術顧問,有近30年資訊電子產業研發經驗,關心最新技術,並投入不綴。亦熱心指導新興團隊參與技術創意競賽,獲獎無數。 專長包括物聯網軟硬體整合規劃設計、資訊服務系統整合規劃。熟悉各種物聯網開發板,包括Arduino、Edison、Linkit one、Ameba…...等。

Share This Post On

3 Comments

  1. 想請教柯大

    所以 DSI 2598這塊板子是可以連接任何有MQTT協定的 IOT平台 例如中華電信IOT大平台

    Post a Reply
  2. 請教老師了
    把原本的
    String tcpserver_connect = “AT+QSOCON=0,7789,\”150.117.122.166\””;
    替換成
    String tcpserver_connect = “AT+QIDNSGIP=1,”https://www.ideaschain.com.tw/“”;
    本人親測 似乎連上了

    同樣花了很多時間連很多次

    連上後 數值沒在平台上更新出來

    附圖
    https://iforum.ideaschain.com.tw/platform_img/1589089927451.png

    Post a Reply

Submit a Comment

Your email address will not be published. Required fields are marked *