[ตอนที่ 4] ควบคุมบอร์ด AIS DEVIO NB-DEVKIT I ส่ง Payload ตรงไป AIS MEGELLAN โดย Arduino IDE

ลงไม้ลงมือ : “สร้างฟังก์ชันส่ง Payload ตรงไป AIS MEGELLAN โดย Arduino IDE”
บทความ โดย… วิสิทธิ์ เวียงนาค
วันที่ 20 กุมภาพันธ์ 2563

มาถึงตอนนี้ ท่านคงเห็นภาพการทำงานและทดลองใช้ AT Commands ควบคุมบอร์ด AIS DEVIO NB-DEVKIT I เชื่อมต่อเครือข่าย NB-IoT รับส่งข้อมูลสำเร็จกันแล้ว ในบทความนี้จะสาธิตการใช้โปรแกรม Aduino IDE สร้างฟังก์ชัน รับส่ง Payload ตรงไปยัง AIS Magellan (เวอร์ชันแรก) ซึ่งเป็น Platfrom IoT ที่ทาง AIS ได้จัดเตรียมไว้ให้นักพัฒนาใช้

สำหรับท่านใดที่เพิ่งเข้ามาอ่านบทความนี้ แนะนำให้เข้าไปดูบทความก่อนหน้าก่อนนะครับ

เริ่มต้นวางแผนการเขียนโปรแกรม

ก่อนที่จะเข้าสู่โหมดการเขียนโค๊ดกัน มาวางแผนการเขียนโปรแกรมกันก่อน ผู้เขียนจะไม่ใช้ไลบรารี่ที่ทาง AIS จัดเตรียมไว้ให้ แต่จะสร้างฟังก์ชันขึ้นมาใช้งานใหม่ และใช้ AT Commands สั่งตรงจาก ESP32 ผ่าน UART ไปยังโมดูล SIM7020E เพื่อส่ง Paylaod ไปยัง Magellan Platform

รูปแสดง โครงสร้างของโปรแกรม

โปรแกรมหลัก

เป็นการสร้างโปรแกรมหลักบน Arduino IDE เหมือนโปรแกรมทั่วๆ ไป เร่ิมต้นด้วยการเรียกไลบรารี่ขึ้นมาใช้งาน มีอยู่ด้วยกันสองตัวได้แก่ HardwareSerial.h และ Preferences.h ทั้งหมดเป็นไลบรารี่ที่มาพร้อมกับ ESP32 อยู่แล้ว

ที่ฟังก์ชัน Setup() กำหนดให้ Serial monitor มี baud rate ที่ 9600 และสร้าง Hardware Serial พอร์ตที่ 1 ขึ้นมาชื่อว่า NBIOT_SERIAL มี baud rate ที่ 9600 เช่นเดียวกัน กำหนดให้เชื่อมต่อกับขา GPIO 16,17 (Rx,Tx) และเชื่อมต่อโมดูล NBIoT (SIM7020E) ด้วย UART จากนั้นส่งคำสั่ง “AT” เพื่อเริ่มการสื่อสารด้วย AT Commands

ที่ฟังก์ชัน Loop() มีการเรียกใช้ฟังก์ชัน millis() เพื่อตั้งเวลาส่ง Payload ไปยัง Magellan Plaform ตามเวลาที่กำหนด ตามโค๊ดข้างล่างคือส่ง Payload ทุกๆ 60 วินาที (การนำไปใช้งานจริงอาจตั้งเวลามากกว่านี้ก็ได้)

#include <HardwareSerial.h>
#include <Preferences.h>
Preferences preferences;
HardwareSerial NBIOT_SERIAL(1);
unsigned long last1;//********************************************************//#define MYTOKEN "768660c0-5aa2-1111-ae7f-0dec994a4321"//********************************************************//void setup() {Serial.begin(9600);

//Setup NBIOT module
pinMode(26, OUTPUT); //pwkey
pinMode(27, OUTPUT); //reset
digitalWrite(26, LOW);
delay(800);
digitalWrite(26, HIGH);

NBIOT_SERIAL.begin(9600, SERIAL_8N1, 16, 17);
ATCMD("AT");
ATCMD("AT+CSOC=1,2,1");
}void loop() {if (millis() - last1 >= 60000) {
last1 = millis();
ATCMD("AT+CSOCON=0,5683,103.20.205.85"); //Send payload to AIS magellan platform
ATCMD(PAYLOAD());
}
}

สร้างฟังก์ชันใช้งานใหม่

ฟังก์ชันที่สร้างขึ้นมาใหม่ มีดังต่อไปนี้

String ATCMD(String at){
...
}
String STR_TO_HEX(String t){
...
}
String PAYLOAD(){
...
}

ฟังก์ชัน ATCMD(String at)

ฟังก์ชันแรก มีหน้าที่ช่วยส่ง AT Commands จาก Hardware Serial ของ ESP32 ไปยังโมดูล SIM7020E พร้อมทั้งดักรอข้อความตอบกลับมา ถ้ามีจะนำข้อความที่ได้ไปแสดงผลใน Serial Monitor

มีรูปแบบการใช้งานดังนี้
ATCMD(“ประโยคคำสั่งในรูปแบบสตริง”)

String ATCMD(String at) {  String content = "";
char character;
NBIOT_SERIAL.println(at);
delay(1000);
while (NBIOT_SERIAL.available()) {
character = NBIOT_SERIAL.read();
content.concat(character);
}
Serial.println(content);
return content;
}

ฟังก์ชัน STR_TO_HEX(String t)

ฟังก์ชันที่สอง มีหน้าที่ช่วยแปลงข้อความธรรมาให้อยู่ในรูปของสตริงเลขฐาน 16 (String to Hex String) ซึ่งเป็นข้อกำหนดการสื่อสารข้อมูลของ SIM7020E

มีรูปแบบการใช้งานดังนี้
STR_TO_HEX(“ข้อความในรูปแบบสตริง”)

String STR_TO_HEX(String t) {  char data[250];
int a = t.length();
String sText = "";
char tChar[a] = "";
t.toCharArray(tChar, a + 1);
for (int i = 0; i < sizeof(tChar); i++) {
sprintf(data, "%x", tChar[i]);
sText += data;
}
return sText;
}

ฟังก์ชัน PAYLOAD()

ฟังก์ชันที่สาม มีหน้าที่จัดเตรียมข้อความ Payload ตามฟอร์แมตที่ทาง Magellan Platform กำหนด ฟังก์ชันนี้จะสร้างชุดข้อความที่ประกอบไปด้วย Header + Payload และคำสั่งใช้งาน

รูปแบบของ Payload ฟอร์แมตที่ทาง Magellan Platform กำหนดมีดังนี้

42 ……………Confirmable Message with Token
02 ……………Method code POST
0001 …………Message ID
12d6 ……….. Random Token ID
b ……………..CoAP Option Delta Numbers
5 ……………..Payload Length
4e42496f54 …NB-IoT Type
0 ……………..CoAP Option Delta Numbers
d ……………..Option Length (max 12)
17 …………….Option Length
Token …….…..Hex string format
ff ………………Payload Marker
Payload ………Json data in hex string format

String PAYLOAD() {  char msg_id[6];
char random_id[6];
//(1) Update payload counter
preferences.begin("pcounter", false);
unsigned long counter = preferences.getUInt("counter", 1);
counter++;
preferences.putUInt("counter", counter);
preferences.end();
//(2) Convert counter/random to hex string
sprintf(msg_id, "%04x", counter);
sprintf(random_id, "%04x", random(0, 32767));
//(3)Prepare sensor payload
String str1 = "";
str1 += "{";
str1 += "\"id\":";
str1 += String(counter);
str1 += ",";
str1 += "\"temp\":";
str1 += String(random(25, 30));
str1 += ",";
str1 += "\"hum\":";
str1 += String(random(40, 80));
str1 += ",";
str1 += "\"light\":";
str1 += String(random(20, 90));
str1 += "}";
//(4) Prepare payload format
String str2 = "";
str2 += "42";
str2 += "02";
str2 += msg_id;
str2 += random_id;
str2 += "b";
str2 += "5";
str2 += "4e42496f54";
str2 += "0";
str2 += "d";
str2 += "17";
str2 += STR_TO_HEX(MYTOKEN);
str2 += "ff";
str2 += STR_TO_HEX(str1);
//(5) Prepare AT command to payload string
String str3 = "";
str3 += "AT+CSOSEND=0,";
str3 += String(str2.length());
str3 += ",";
str3 += str2;
return str3;
}

อธิบายโค๊ดเพิ่มเติมฟังก์ชัน PAYLOAD()

(1) Update payload counter

Message id

การส่งข้อความแต่ละครั้ง Message id ที่ใช้อ้างอิงไม่ควรจะซ้ำกัน แนวคิดง่ายๆ ในการจัดการกับเรื่องนี้ก็คือ การสร้างและเก็บ Counter ไว้ แล้วบวกขึ้นไปทีละหนึ่ง จากนั้นเก็บข้อมูลไว้ในที่ถาวร หมายความว่าถ้าเกิดเหตุการณ์ไฟดับแล้ว ข้อมูลยังคงอยู่

แล้วจะเก็บ Counter ไว้ที่ไหนดี ?

การเก็บข้อมูลแบบถาวรบนโมดูล ESP32 สามารถเก็บไว้ที่ Flash Memory ภายนอกได้ การทำงานจะทำผ่าน SPI Bus มีขนาดสูงสุดถึง 16 MBytes แต่สำหรับโมดูล ESP-WROOM-32 ที่อยู่บน dev board ให้ Flash Memory มาที่ 4 MBytes

รูปแสดง ขนาดของ Flash Memory โมดูล ESP32
รูปแสดง ตำแหน่งของ Flash Memory โมดูล ESP32

พาทิชันของ Flash Memory ภายนอก ที่สามารถเขียนโปรแกรมเก็บข้อมูลแบบถาวร ได้ก็คือ NVS มีขนาดความจุ 20KBytes ส่วน EEPROM มีขนาดความจุ 4KBytes และ SPIFFS มีขนาดความจุถึง 1468 KBytes นอกนั้นเป็นพื้นที่ของระบบที่ต้องใช้

รูปแสดง Flash memory Partition Table

NVS (Nonvolatile Storage) เป็น SPI Flash Memory ของโมดูล ESP32 มีขนาด 20 KBytes เอาไว้เก็บข้อมูลระบบเช่น ชื่อ SSID MC Address และอื่นๆ ข้อมูลที่เก็บจะเป็นแบบถาวรไม่สูญหายแม้เขียน Sketch ทับลงไป การใช้งานให้นึกเสมอว่าไฟล์จะอยู่รวมกับไฟล์ระบบอื่นๆ ควรระมัดระวังการใช้งาน แนะนำให้เก็บ ข้อมูลที่ไม่ซับซ้อนเช่น รหัสผ่าน ตัวเลขนับจำนวนการบูต หรือข้อความสั้นๆ

ในครั้งนี้ผู้เขียนจะขอเลือกการเก็บข้อมูล Counter แบบถาวรบน Non-Volatile Storage (NVS) ซึ่งมีความจุและทำงานได้เร็วว่า EEPROM การใช้งานบน ESP32 เป็นเรื่องง่ายๆ โดยการเรียกใช้ไลบรารี่ #include <Preferences.h> ที่มีมาให้อยู่แล้ว

ตามโค๊ดข้างล่าง จะสร้างแฟ้ม preferences ขึ้นมาชื่อ “pcounter” จากนั้นสั่งให้อ่านค่าตัวแปรชื่อ “counter” ใน NVS ถ้าไม่พบตัวแปรนี้ ก็ให้สร้างตัวแปรขึ้นมาใหม่แล้วกำหนดค่าเท่ากับ 1 จากนั้นสั่งอัพเดทตัวแปร counter ให้บวกหนึ่งแล้วปิดแฟ้ม preferences

//Update payload counter
preferences.begin(“pcounter”, false);
unsigned long counter = preferences.getUInt(“counter”, 1);
counter++;
preferences.putUInt(“counter”, counter);
preferences.end();

2) Convert counter/random to hex string

เป็นการเปลี่ยนค่า counter_id และ token_id ให้อยู่ในรูปแบบ hex string ก่อนอื่นมาทำความเข้าใจการใช้ฟังก์ชัน sprintf() กันก่อน มันมีไว้สําหรับแปลงรูปแบบข้อความสตริงให้อยู่ในรูปแบบที่กำหนด

มีรูปแบบการใช้งานดังนี้
sprintf(var, format, str);

var หมายถึง ตัวแปรสำหรับเก็บผลลัพธิ์ข้อความสตริงที่แปลงรูปแล้ว

format หมายถึง รูปแบบของข้อความสตริงที่ต้องการ มีดังต่อไปนี้
%%….รูปแบบ %
%b…..เลขฐานสอง
%c…..ตัวอักษรที่สอดคล้องกับ ASCII value
%d…..เลขฐาน 10 แบบมีเครื่องหมาย
%e…..สัญลักษณ์แบบวิทยาศาสตร์ เช่น 1.2e+2
%u….เลขฐาน 10 แบบไม่มีเครื่องหมาย
%f…..เลขทศนิยมแบบมี local settings
%F….เลขทศนิยมแบบไม่มี local settings
%o….เลขฐาน 8
%s….String
%x….เลขฐาน 16 ตัวอักษรเล็ก
%X….เลขฐาน 16 ตัวอักษรใหญ่

str หมายถึงข้อความสตริงที่ต้องการเปลี่ยนรูป

ยกตัวอย่างใช้งาน เช่น

sprintf(msg_id, “%04x”, counter);
sprintf(random_id, “%04x”, random(0, 32767));

ฟังก์ชัน sprinrf() จะเปลี่ยนรูปแบบของตัวเลขด้านบนให้อยู่ในรูปแบบ “%04x” คือ Hex String โดยเติมเลข 0 อยู่นำหน้าเพื่อให้มีตัวอักษรครบทั้ง 4 ตัว

ยกตัวอย่างผลลัพธ์ เช่น 1000 = 3EB
ฟังก์ชันจะเติมเลขศูนย์นำหน้าให้ และแสดงผลลัพธ์ที่ได้เป็น 03EB เป็นต้น

3) Prepare sensor payload

เร่ิมจากการสร้างตัวแปรแบบสตริงชื่อ str1 เก็บข้อมูลในรูปแบบ JSON ประกอบไปด้วย id, temp, hum และ light

การทดสอบในครั้งนี้ยังไม่ได้อ่านค่าเซ็นเซอร์ที่อยู่บนบอร์ดจริง แต่จะใช้วิธีการสร้างตัวเลขแบบสุ่มขึ้นมาแสดง เพื่อให้ท่านได้เห็นภาพการทำงานเท่านั้น

ค่าตัวแปร temp ได้มาจากการสุ่มตัวเลขระหว่าง 25 ถึง 30
ค่าตัวแปร hum ได้มาจากการสุ่มตัวตัวเลขระหว่าง 40 ถึง 80
ค่าตัวแปร light ได้มาจากการสุ่มตัวตัวเลขระหว่าง 20 ถึง 90

ตัวอย่างของตัวแปรแบบสตริง str1 มีดังนี้

{"id":90,"temp":25,"hum":74,"light":61}

4) Prepare payload format

เป็นการสร้างตัวแปรแบบสตริงชื่อ str2 เก็บข้อมูลในรูปแบบ JSON ประกอบไปด้วย Header ที่จำเป็นของ NB-IoT + TOKEN + ค่าเซ็นเซอร์ที่ได้จาการสร้างตัวเลขแบบสุ่มขึ้นมาแสดง

ตัวอย่างตัวแปรแบบสตริง str2 มีดังนี้

420200772eefb54e42496f540d1737363836363063302d356161322d313165382d616537662d306465633939346135393330ff7b226964223a3131392c2274656d70223a32362c2268756d223a36372c226c69676874223a35377d

5) Prepare AT command to payload string

เป็นการสร้างชุดคำสั่งใช้งาน ประกอบไปด้วย AT Command + Header ที่จำเป็นของ NB-IoT + TOKEN + ค่าเซ็นเซอร์ที่สร้างตัวเลขแบบสุ่มขึ้นมาแสดง

ตัวอย่างตัวแปรแบบสตริง str3 มีดังนี้

AT+CSOSEND=0,182,420200772eefb54e42496f540d1737363836363063302d356161322d313165382d616537662d306465633939346135393330ff7b226964223a3131392c2274656d70223a32362c2268756d223a36372c226c69676874223a35377d

เชื่อมต่อเครือข่าย NB-IoT แล้วส่ง Payload

หลังจากที่ได้โหลดโค๊ดจากโปรแกรม Arduino IDE ลงไปในบอร์ดเสร็จเรียบร้อย บอร์ด AIS DEVIO NB-DEVKIT I จะเริ่มทำงานโดยการเชื่อมต่อกับเครือข่าย NB-IoT แล้วส่งคำสั่ง “AT” จาก ESP32 ไปยัง SIM7020E เพื่อเปิดการเริ่มต้นสื่อสาร จากนั้นจะเริ่มส่ง Uplink Payload ไปยัง Magellan แพลตฟอร์ม

AT
OK
AT+CSOC=1,2,1
+CSOC: 1
OK
AT+CSOCON=0,5683,103.20.205.85
OK
AT+CSOSEND=0,182,420200757dc0b54e42496f540d1737363836363063302d356161322d313165382d616537662d306465633939346135393330ff7b226964223a3131372c2274656d70223a32362c2268756d223a35382c226c69676874223a37357d
OK
+CSONMI: 0,20,624100757DC0FF323030AT+CSOCON=0,5683,103.20.205.85
OK
AT+CSOSEND=0,182,42020076727bb54e42496f540d1737363836363063302d356161322d313165382d616537662d306465633939346135393330ff7b226964223a3131382c2274656d70223a32382c2268756d223a37352c226c69676874223a34387d
OK
+CSONMI: 0,20,62410076727BFF323030AT+CSOCON=0,5683,103.20.205.85
OK
AT+CSOSEND=0,182,420200772eefb54e42496f540d1737363836363063302d356161322d313165382d616537662d306465633939346135393330ff7b226964223a3131392c2274656d70223a32362c2268756d223a36372c226c69676874223a35377d
OK

ถ้าการส่ง Payload ไปยัง Magellan Platform สำเสร็จ โมดูล SIM7020E จะแสดงข้อความ “+CSONMI: <ข้อมูล>” ตอบกับมา

มีรายละเอียดตามนี้
+CSONMI=<socket_id>,<data_len>,<data>

อธิบายความหมาย
<socket_id> หมายเลข socket_id
<data_len> ความยาวของตังเลข
<data> ข้อมูลที่ตอบกลับมา

AIS Magellan Platform

ที่หน้าหลักบน Magellan Platform ไปที่เมนู Things -> Thing Detail หลังจากที่มีข้อความส่งมาจากบอร์ด DEVIO NB-DEVKIT I จะพบกับ Status มีสถานะเป็น “Connected” เป็นตัวหนังสือสีเขียว และที่บริเวณ Data จะมีค่า id , temp , hum และ light แสดงออกมา

รูปแสดง หน้าหลักของ Magellan Platform

ไปที่เมนู Dashboard จะพบว่า Widget Graph ที่สร้างรอไว้ เริ่มมีข้อมูลแสดงออกมา

รูปแสดง หน้า Dashboard ของ Magellan Platform

โค๊ดตัวเต็ม

//------------------------------------------//
// NBIoT Dev Board
// 6-Oct-2019, Rev 1.0
// By Mr.Visit W.
// https://www.facebook.com/visit.wiangnak/
//------------------------------------------//
#include <HardwareSerial.h>
#include <Preferences.h>
Preferences preferences;
HardwareSerial NBIOT_SERIAL(1);
unsigned long last1;//********************************************************//#define MYTOKEN "768660c0-5aa2-1111-ae7f-0dec994a4321"//********************************************************//void setup() {Serial.begin(9600);

//Setup NBIOT module
pinMode(26, OUTPUT); //pwkey
pinMode(27, OUTPUT); //reset
digitalWrite(26, LOW);
delay(800);
digitalWrite(26, HIGH);

NBIOT_SERIAL.begin(9600, SERIAL_8N1, 16, 17);
ATCMD("AT");
ATCMD("AT+CSOC=1,2,1");
}void loop() {if (millis() - last1 >= 60000) {
last1 = millis();
ATCMD("AT+CSOCON=0,5683,103.20.205.85"); //Send payload to AIS magellan platform
ATCMD(PAYLOAD());
}
}
//############################################################//String PAYLOAD() { char msg_id[6];
char random_id[6];
//Update payload counter
preferences.begin("pcounter", false);
unsigned long counter = preferences.getUInt("counter", 1);
counter++;
preferences.putUInt("counter", counter);
preferences.end();
//Convert counter/random to hex string
sprintf(msg_id, "%04x", counter);
sprintf(random_id, "%04x", random(0, 32767));
//Prepare sensor payload
String str1 = "";
str1 += "{";
str1 += "\"id\":";
str1 += String(counter);
str1 += ",";
str1 += "\"temp\":";
str1 += String(random(25, 30));
str1 += ",";
str1 += "\"hum\":";
str1 += String(random(40, 80));
str1 += ",";
str1 += "\"light\":";
str1 += String(random(20, 90));
str1 += "}";
//Prepare payload format
String str2 = "";
str2 += "42";
str2 += "02";
str2 += msg_id;
str2 += random_id;
str2 += "b";
str2 += "5";
str2 += "4e42496f54";
str2 += "0";
str2 += "d";
str2 += "17";
str2 += STR_TO_HEX(MYTOKEN);
str2 += "ff";
str2 += STR_TO_HEX(str1);
//Prepare AT command to payload string
String str3 = "";
str3 += "AT+CSOSEND=0,";
str3 += String(str2.length());
str3 += ",";
str3 += str2;
return str3;}//############################################################//String STR_TO_HEX(String t) { char data[250];
int a = t.length();
String sText = "";
char tChar[a] = "";
t.toCharArray(tChar, a + 1);
for (int i = 0; i < sizeof(tChar); i++) {
sprintf(data, "%x", tChar[i]);
sText += data;
}
return sText;
}
//############################################################//String ATCMD(String at) { String content = "";
char character;
NBIOT_SERIAL.println(at);
delay(1000);
while (NBIOT_SERIAL.available()) {
character = NBIOT_SERIAL.read();
content.concat(character);
}
Serial.println(content);
return content;
}

การเลือกใช้วิธีการส่ง AT Commands ควบคุมบอร์ด DEVIO NB-DEVKIT I เพื่อส่ง Uplink Payload ตรงไปยัง Magellan Platform แทนที่จะใช้ไลบรารี่ที่ทาง AIS จัดเตรียมไว้ให้ จะทำให้ท่านเข้าใจถึงกระบวนการทำงานของ NB-IoT เบื้องต้นและการใช้ SIM7020E อย่างเต็มประสิทธิภาพมากขึ้น ท่านสามารถนำไปประยุกต์ใช้งานจริงที่ซับซ้อนได้

ในบทความหน้า [ตอนที่ 5] พัฒนาไลบรารี่บนโปรแกรม Arduino IDE ควบคุมบอร์ด AIS DEVIO NB-DEVKIT I

ผู้เขียนมีความตั้งใจที่จะนำเสนอการใช้ IoT-LoRaWAN-NBIOT สำหรับพัฒนา SMART CITY เพื่อให้ผู้ที่สนใจนำไปพัฒนาต่อยอดสร้างนวตกรรมใหม่ๆ ขอเป็นส่วนหนึ่งเล็กๆ น้อยๆ ช่วยผลักดันประเทศของเราให้เข้าสู่ยุคดิจิตอล Thailand 4.0 อย่างแท้จริง

ติดตามข่าวสารทาง facebook ได้ที่ลิงค์นี้
Facebook กลุ่ม IoT-SmartCity

--

--