6. Penampil LCD + Serial + IoT

Aplikasi 6 ini merupakan pengembangan dari Aplikasi 5, di mana selain bisa mengubah data Outseal Nano/Mega lewat komunikasi serial, dan menampilkannya di LCD, juga bisa terhubung dengan layanan Internet of Things, yang dalam contoh di sini menggunakan layanan Arduino IoT Cloud.

Agar lebih mudah, maka rangkaian Outseal Nano dan Outseal Mega akan menggunakan rangkaian yang sama seperti di Aplikasi 5. Hanya di Aplikasi 6 ini, tidak lagi menggunakan Arduino Uno, sebagai gantinya menggunakan ESP8266.

CatatanAda 2 tipe ESP8266 yang banyak digunakan, yaitu tipe ESP8266-01 dan tipe ESP8266-12E. Tipe ESP8266-12E ini sering juga disebut NodeMCU. Masing-masing tipe tersebut memiliki kelebihannya sendiri-sendiri. Oya, mungkin ada rekan-rekan yang bertanya, bagaimana kalau menggunakan ESP32 sebagai ganti ESP8266? Wah, ... tentu saja bisa sekali, karena kemampuan ESP32 lebih tinggi daripada ESP8266, hanya saja, harganya lebih mahal 2-3 kali lipat. Untuk cara penggantiannya, saya jelaskan di bagian akhir tulisan ini. 

Berikut ini langkah-langkahnya:
1. Mula-mula, akan ditunjukkan simulasi dari aplikasi ini. Setelah simulasi bisa berjalan dengan baik, barulah akan diperlihatkan bagaimana pembuatannya dengan hardware yang riil. Untuk itu, mulai dari simulasi, siapkan rangkaian Proteus berikut ini: (lihat kembali Aplikasi 5, Penampil LCD + Serial). 

Gambar 1(a). Rangkaian Outseal Nano dengan komponen ATmega328 sebagai simulasi ESP8266 (File Proteus ini dapat diunduh di bagian akhir tulisan ini dengan nama file OutsealNano_ESP8266.pdsprj)

Gambar 1(b). Rangkaian Outseal Mega dengan komponen ATmega328 sebagai simulasi ESP8266 (File Proteus ini dapat diunduh di bagian akhir tulisan ini dengan nama file OutsealMega_ESP8266.pdsprj)

Catatan: Berhubung Proteus belum menyediakan komponen ESP8266, maka agar dapat disimulasikan, komponen ESP8266 ini direpresentasikan dengan komponen ATmega328. Kemudian agar rangkaian simulasi Proteus ini dapat terhubung dengan Arduino IoT Cloud, maka rekan-rekan harus menyambungkan hardware ESP8266 yang riil ke Port USB di komputer. Antara hardware ESP8266 dengan ATmega328 di Proteus saling berkomunikasi secara serial melalui COMPIM.

2. Setelah rangkaian dibuat, langkah berikutnya adalah memprogram setiap komponen mikrokontroler, berikut ini berturut-turut programnya:

a. Ladder Diagram Outseal Nano (ATmega328p) 
Gambar 2(a). Ladder Diagram untuk Outseal Nano

b. Ladder Diagram Outseal Mega (ATmega128) 
Gambar 2(b). Ladder Diagram untuk Outseal Mega

c. KodeProgram_SimulasiESP8266:
Catatan: Kode program untuk simulasi ESP8266 ini sama seperti kode program di Aplikasi ke-4, untuk Arduino Uno:
  1. #include <ModbusRTU.h>
  2. #include <Wire.h>
  3. #include <LiquidCrystal_I2C.h>
  4. #include <SoftwareSerial.h>
  5. SoftwareSerial S(3, 2);
  6. LiquidCrystal_I2C lcd(0x27, 16, 2);
  7. ModbusRTU mb;
  8. String inputString = "";
  9. bool stringComplete = false;
  10. bool case1 = false;
  11. bool case2 = false;
  12. bool case3 = false;
  13. bool case4 = false;
  14. bool case5 = false;
  15. bool case6 = false;
  16. bool coil[20];
  17. bool idis[20];
  18. uint16_t hreg[10];
  19. uint16_t ireg[10];
  20. uint16_t cacah;
  21. unsigned long skr = 0;
  22. void setup() {
  23.   Serial.begin(9600);
  24.   lcd.init();
  25.   lcd.backlight();
  26.   S.begin(9600);
  27.   mb.begin(&S);
  28.   mb.master();
  29.   mb.readIsts(1, 0, idis, 16);  //hanya untuk trigger
  30.   while (mb.slave()) {
  31.     mb.task();
  32.     delay(10);
  33.   }
  34.   delay(1000);
  35.   Serial.println("Silahkan kirim data");
  36. }
  37. void loop() {
  38.   if (stringComplete) {
  39.     Serial.print("> ");
  40.     Serial.print(inputString);
  41.     lcd.setCursor(0, 0);
  42.     lcd.print(">               ");
  43.     lcd.setCursor(1, 0);
  44.     lcd.print(inputString);
  45.     String d1 = getValue(inputString, ',', 0);
  46.     String d2 = getValue(inputString, ',', 1);
  47.     String d3 = getValue(inputString, ',', 2);
  48.     String d4 = getValue(inputString, ',', 3);
  49.     uint8_t id = d1.toInt();
  50.     uint8_t kode = d2.toInt();
  51.     uint16_t alamat = d3.toInt();
  52.     uint16_t nilai = d4.toInt();
  53.     switch (kode) {
  54.       case 1:
  55.         mb.readCoil(id, alamat, coil, nilai);
  56.         case1 = true;
  57.         break;
  58.       case 2:
  59.         mb.readIsts(id, alamat, idis, nilai);
  60.         case2 = true;
  61.         break;
  62.       case 3:
  63.         mb.readHreg(id, alamat, hreg, nilai);
  64.         case3 = true;
  65.         break;
  66.       case 4:
  67.         mb.readIreg(id, alamat, ireg, nilai);
  68.         case4 = true;
  69.         break;
  70.       case 5:
  71.         mb.writeCoil(id, alamat, nilai < 1);
  72.         case5 = true;
  73.         break;
  74.       case 6:
  75.         mb.writeHreg(id, alamat, nilai);
  76.         case6 = true;
  77.         break;
  78.     }
  79.     while (mb.slave()) {
  80.       mb.task();
  81.       delay(10);
  82.     }
  83.     if (case1) {
  84.       Serial.print(" = ");
  85.       lcd.setCursor(0, 1);
  86.       lcd.print("=               ");
  87.       lcd.setCursor(1, 1);
  88.       for (int i = 0; i < nilai; i++) {
  89.         Serial.print(coil[i] ? "0" : "1");
  90.         lcd.print(coil[i] ? "0" : "1");
  91.       }
  92.       Serial.println();
  93.       case1 = false;
  94.     }
  95.     if (case2) {
  96.       Serial.print(" = ");
  97.       lcd.setCursor(0, 1);
  98.       lcd.print("=               ");
  99.       lcd.setCursor(1, 1);
  100.       for (int i = 0; i < nilai; i++) {
  101.         Serial.print(idis[i] ? "0" : "1");
  102.         lcd.print(idis[i] ? "0" : "1");
  103.       }
  104.       Serial.println();
  105.       case2 = false;
  106.     }
  107.     if (case3) {
  108.       Serial.print(" = ");
  109.       lcd.setCursor(0, 1);
  110.       lcd.print("=               ");
  111.       lcd.setCursor(1, 1);
  112.       for (int i = 0; i < (nilai - 1); i++) {
  113.         Serial.print(hreg[i]);
  114.         Serial.print(',');
  115.         lcd.print(hreg[i]);
  116.         lcd.print(',');
  117.       }
  118.       Serial.println(hreg[nilai - 1]);
  119.       lcd.print(hreg[nilai - 1]);
  120.       case3 = false;
  121.     }
  122.     if (case4) {
  123.       Serial.print(" = ");
  124.       lcd.setCursor(0, 1);
  125.       lcd.print("=               ");
  126.       lcd.setCursor(1, 1);
  127.       for (int i = 0; i < (nilai - 1); i++) {
  128.         Serial.print(ireg[i]);
  129.         Serial.print(',');
  130.         lcd.print(ireg[i]);
  131.         lcd.print(',');
  132.       }
  133.       Serial.println(ireg[nilai - 1]);
  134.       lcd.print(ireg[nilai - 1]);
  135.       case4 = false;
  136.     }
  137.     if (case5) {
  138.       mb.readCoil(id, alamat, coil, 1);
  139.       while (mb.slave()) {
  140.         mb.task();
  141.         delay(10);
  142.       }
  143.       Serial.print(" = ");
  144.       Serial.println(coil[0] ? "0" : "1");
  145.       lcd.setCursor(0, 1);
  146.       lcd.print("=               ");
  147.       lcd.setCursor(1, 1);
  148.       lcd.print(coil[0] ? "0" : "1");
  149.       case5 = false;
  150.     }
  151.     if (case6) {
  152.       mb.readHreg(id, alamat, hreg, 1);
  153.       while (mb.slave()) {
  154.         mb.task();
  155.         delay(10);
  156.       }
  157.       Serial.print(" = ");
  158.       Serial.println(hreg[0]);
  159.       lcd.setCursor(0, 1);
  160.       lcd.print("=               ");
  161.       lcd.setCursor(1, 1);
  162.       lcd.print(hreg[0]);
  163.       case6 = false;
  164.     }
  165.     inputString = "";
  166.     stringComplete = false;
  167.   }
  168.   if (millis() - skr > 1000) {
  169.     skr = millis();
  170.     tampilLCD();
  171.     cacah++;
  172.     if (cacah > 5) cacah = 0;
  173.   }
  174. }
  175. void serialEvent() {
  176.   while (Serial.available()) {
  177.     char inChar = (char)Serial.read();
  178.     inputString += inChar;
  179.     if (inChar == 13) {
  180.       stringComplete = true;
  181.       skr = millis();
  182.     }
  183.   }
  184. }
  185. String getValue(String data, char separator, int index) {
  186.   int found = 0;
  187.   int strIndex[] = { 0, -1 };
  188.   int maxIndex = data.length() - 1;
  189.   for (int i = 0; i <= maxIndex && found <= index; i++) {
  190.     if (data.charAt(i) == separator || i == maxIndex) {
  191.       found++;
  192.       strIndex[0] = strIndex[1] + 1;
  193.       strIndex[1] = (i == maxIndex) ? i + 1 : i;
  194.     }
  195.   }
  196.   return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
  197. }
  198. void tampilLCD() {
  199.   switch (cacah) {
  200.     case 0:
  201.       inputString = "1,2,0,8";
  202.       stringComplete = true;
  203.       break;
  204.     case 1:
  205.       inputString = "1,2,8,8";
  206.       stringComplete = true;
  207.       break;
  208.     case 2:
  209.       inputString = "1,1,0,8";
  210.       stringComplete = true;
  211.       break;
  212.     case 3:
  213.       inputString = "1,1,8,8";
  214.       stringComplete = true;
  215.       break;
  216.     case 4:
  217.       inputString = "1,4,0,2";
  218.       stringComplete = true;
  219.       break;
  220.     case 5:
  221.       inputString = "1,3,0,2";
  222.       stringComplete = true;
  223.       break;
  224.   }
  225. }

d. KodeProgram_HardwareESP8266
Catatan: Kode program untuk hardware ESP8266 ini akan menghubungkan rangkaian di Proteus dengan Arduino IoT Cloud. 
  1. #include "arduino_secrets.h"
  2. #include "thingProperties.h"
  3. void setup() {
  4.   Serial.begin(9600);
  5.   delay(1500);
  6.   initProperties();
  7.   ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  8.   setDebugMessageLevel(2);
  9.   ArduinoCloud.printDebugInfo();
  10. }
  11. void loop() {
  12.   ArduinoCloud.update();
  13.   if (Serial.available() > 0) {
  14.     modbus = Serial.readStringUntil('\n');
  15.   }
  16. }
  17. void onModbusChange()  {
  18.   Serial.println(modbus);
  19. }

3. Silahkan meng-upload program di atas, kecuali untuk KodeProgram_HardwareESP8266, jangan di-upload dulu, karena kode program ini memerlukan secret key, yang hanya dibangkitkan ketika membuat Thing di Arduino IoT Cloud. Untuk itu, ikuti langkah-langkah dalam bentuk gambar berikut ini untuk membuat Thing dan Dashboard di Arduino IoT Cloud:

Langkah 1. buka halaman web cloud.arduino.cc, klik Get Started for free

Langkah 2. Klik Sign In apabila sudah memiliki akun, apabila belum, klik Create One

Langkah 3. Isi username atau email dan password, klik Sign In

Langkah 4. Di daftar Menu, klik Things, klik Create Thing  

Langkah 5. Di halaman Things, klik Untitled, Rename menjadi Outseal, kemudian klik Add 

Langkah 6. Muncul jendela Add variable, isi Name = Modbus, pilih Character String, klik Add Variable

Langkah 7. Klik Select Device

Langkah 8. Klik Set Up New Device

Langkah 9. Pilih Third party device

Langkah 10. Dalam contoh di sini, saya menggunakan NodeMCU ESP8266 (apabila rekan-rekan menggunakan hardware lain, silahkan memilih sesuai tipe hardware tersebut)

Langkah 11. Muncul nama device, klik Next

Langkah 12. Di halaman ini, kode Secret key diberikan, silahkan klik pada download the PDF, kemudian beri centang pada tulisan I saved ..., kemudian klik Continue

Langkah 13. Maka Setup Device telah selesai, klik Done

Langkah 14. Di halaman Thing, terlihat nama Device muncul, hanya statusnya masih Offline, berikutnya, klik tombol Configure 

Langkah 15. Isi nama dan password hotspot WiFi, dan isi Secret key dari file PDF yang baru saja diunduh di langkah no. 12 di atas, klik Save

Langkah 16. Setelah Variabel ditambahkan, Device dipilih, dan Network diisi, berikutnya, klik Tab Sketch

Langkah 17. Muncul halaman Sketch, klik pada Open full editor

Langkah 18. Di halaman Web Editor Arduino, hapus program di Tab Outseal_xxx.ino, dan isi dengan KodeProgram_HardwareESP8266 di atas

Langkah 19. Upload program dengan menekan tombol panah ke samping, namun sebelumnya, pastikan Port COM dan Board yang dipilih sudah benar, dengan menekan tombol panah ke bawah di kolom Board, atur hingga ttanda silang pada kolom Board hilang

Catatan: Apabila Port COM tidak muncul, pastikan rekan-rekan menginstal Arduino Create Agent, yang langkah instalasinya bisa dilihat di sini: Install the Arduino Create Agent – Arduino Help Center

Langkah 20. Setelah upload berhasil, klik Monitor untuk mengetahui apakah koneksi ke Arduino IoT Cloud berhasil atau tidak (tanda koneksi berhasil, muncul tulisan "Connected to Arduino IoT Cloud")

Langkah 21. Terlihat status device masih Offline, tulisan "Connected to Arduino IoT Cloud" masih belum muncul

Langkah 22. Berhubung status device masih Offline, maka alternatif solusinya adalah: download Sketch, kemudian upload dengan software Arduino IDE di komputer, klik Sketchbook, klik kanan Sketch yang paling atas, pilih Download Sketch

Langkah 23. Di folder Download, klik kanan file, pilih Extract Here

Langkah 24. Munucl 5 buah file hasil ekstrak, klik 2 kali pada file ino: Outseal..., maka terbuka software IDE Arduino

Langkah 25. Di Tab arduino_secrets.h, isi nama dan password Wi-Fi serta kode secret key

Langkah 26. Setelah secret key terisi, klik Select Board, pilih tipe Board dan Port COM yang sesuai

Langkah 27. Setelah Board dan Port COM dipilih, langkah berikutnya adalah upload program, klik kanan tombol panah ke kanan, tunggu hingga muncul tulisan Done Uploading

Langkah 28. Terlihat proses upload selesai, muncul tulisan Done Uploading

Langkah 29. Buka Serial Monitor, tunggu hingga muncul tulisan "Connected to Arduino IoT Cloud", seperti ditunjukkan pada gambar di atas

Catatan: Apabila tulisan "Connected to Arduino IoT Cloud" masih belum muncul (biasanya masalah ini hanya terjadi pada ESP8266/NodeMCU), maka solusinya adalah downgrade versi library ArduinoIoTCloud ke versi 1.8.0 dan juga Arduino_ConnectionHandler ke versi 0.7.1. Upload program lagi, seharusnya tulisan "Connected to Arduino IoT Cloud" bisa muncul.

Langkah 30. Downgrade library ArduinoIoTCloud ke 1.8.0 dan library Arduino_ConnectionHandler ke 0.7.1

Langkah 31. Setelah tulisan "Connected to Arduino IoT Cloud" muncul, maka status device Online

Langkah 32. Setelah status device Online, langkah selanjutnya buat Dashboard, klik tombol Menu di pojok kiri atas 

Langkah 33. Di daftar menu, klik Dashboards, kemudian klik Create Dashbaord

Langkah 34. Di halaman Dashboard, klik tombol Add, klik Tab Things, klik Outseal

Langkah 35. Di jendela daftar variabel, klik Create Widgets

Langkah 36. Muncul widget Messenger di halaman Dashboard 

4. Setelah status device Online dan Dashboard selesai dibuat, langkah berikutnya adalah menjalankan simulasi Proteus. Namun sebelum menjalankan simulasi Proteus, perlu diatur dulu komponen COMPIM. Klik 2 kali komponen COMPIM, isi Physical Port dengan COM yang digunakan oleh hardware NodeMCU, dan atur Baud rate = 9600.

Gambar 3. Isi Physical Port di komponen COMPIM dengan COM yang digunakan NodeMCU, dan atur Baud rate = 9600

5. Jalankan rangkaian Outseal Nano di Proteus, dan perhatikan widget Messenger di Arduino IoT Cloud. Seharusnya widget Messenger akan menampilkan data yang sama seperti tampilan Virtual Terminal KIRIM. Ketik di kolom paling bawah widget Messengger, sebagai contoh ketik "1,6,0,900", maka data ini akan muncul di Virtual Terminal TERIMA, dan juga instruksi tersebut akan membuat LED di kaki R.7 menyala sedikit redup (intensitas cahaya maksimum apabila diberi nilai 1000).

Gambar 4. Terlihat komunikasi antara rangkaian Outseal Nano di Proteus dengan widget Messenger di Arduino IoT Cloud 

6. Ulangi langkah no. 5 di atas untuk rangkaian Outseal Mega. Seharusnya widget Messenger akan dapat mengirimkan dan menerima data Modbus dari rangkaian Outseal Mega, memonitor kondisi input digital (16 Switch) dan input analog (2 Potensio), serta mengontrol output digital (16 LED) dan analog pwm (2 LED di kaki R.6 dan R.7).

Gambar 5. Terlihat komunikasi antara rangkaian Outseal Mega di Proteus dengan widget Messenger di Arduino IoT Cloud

7. Setelah simulasi berjalan dengan baik, langkah berikutnya adalah mengimplementasikan ke hardware riil. Di sini saya menggunakan Arduino Nano sebagai simulasi Outseal Nano, dan sebuah NodeMCU untuk menghubungkan Arduino Nano tersebut ke Arduino IoT Cloud. Berikut ini gambar pengawatan rangkaiannya:

Gambar 6. Pengawatan rangkaian hardware Outseal Nano (disimulasikan dengan Arduino Nano) dengan Penampil LCD+Serial+Arduino IoT Cloud

8. Untuk rangkaian Gambar 6 di atas, berbeda dengan rangkaian untuk simulasi Gambar 1(a), di mana di rangkaian Gambar 6 ini, sudah tidak lagi menggunakan komponen ATmega328, tetapi komponen tersebut telah dilebur ke dalam NodeMCU. Jadi program NodeMCU untuk rangkaian Gambar 6 ini menggabungkan KodeProgram_SimulasiESP8266 dan KodeProgram_HardwareESP8266. Berikut ini kode program penggabungan tersebut:

KodeProgram_Penggabungan:
  1. #include "arduino_secrets.h"
  2. #include "thingProperties.h"
  3. #include <ModbusRTU.h>
  4. #include <Wire.h>
  5. #include <LiquidCrystal_I2C.h>
  6. #include <SoftwareSerial.h>
  7. SoftwareSerial S(D5, D6);
  8. LiquidCrystal_I2C lcd(0x27, 16, 2);
  9. ModbusRTU mb;
  10. String var = "";
  11. String inputString = "";
  12. bool stringComplete = false;
  13. bool case1 = false;
  14. bool case2 = false;
  15. bool case3 = false;
  16. bool case4 = false;
  17. bool case5 = false;
  18. bool case6 = false;
  19. bool coil[20];
  20. bool idis[20];
  21. uint16_t hreg[10];
  22. uint16_t ireg[10];
  23. uint16_t cacah;
  24. unsigned long skr = 0;
  25. void setup() {
  26.   Serial.begin(9600);
  27.   lcd.init();
  28.   lcd.backlight();
  29.   S.begin(9600);
  30.   mb.begin(&S);
  31.   mb.master();
  32.   mb.readIsts(1, 0, idis, 16);  //hanya untuk trigger
  33.   while (mb.slave()) {
  34.     mb.task();
  35.     delay(10);
  36.   }
  37.   delay(1000);
  38.   Serial.println("Silahkan kirim data");
  39.   delay(1500);
  40.   initProperties();
  41.   ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  42.   setDebugMessageLevel(2);
  43.   ArduinoCloud.printDebugInfo();
  44. }
  45. void loop() {
  46.   ArduinoCloud.update();
  47.   if (stringComplete) {
  48.     Serial.print("> ");
  49.     Serial.print(inputString);
  50.     lcd.setCursor(0, 0);
  51.     lcd.print(">               ");
  52.     lcd.setCursor(1, 0);
  53.     lcd.print(inputString);
  54.     modbus = "> " + inputString;
  55.     String d1 = getValue(inputString, ',', 0);
  56.     String d2 = getValue(inputString, ',', 1);
  57.     String d3 = getValue(inputString, ',', 2);
  58.     String d4 = getValue(inputString, ',', 3);
  59.     uint8_t id = d1.toInt();
  60.     uint8_t kode = d2.toInt();
  61.     uint16_t alamat = d3.toInt();
  62.     uint16_t nilai = d4.toInt();
  63.     switch (kode) {
  64.       case 1:
  65.         mb.readCoil(id, alamat, coil, nilai);
  66.         case1 = true;
  67.         break;
  68.       case 2:
  69.         mb.readIsts(id, alamat, idis, nilai);
  70.         case2 = true;
  71.         break;
  72.       case 3:
  73.         mb.readHreg(id, alamat, hreg, nilai);
  74.         case3 = true;
  75.         break;
  76.       case 4:
  77.         mb.readIreg(id, alamat, ireg, nilai);
  78.         case4 = true;
  79.         break;
  80.       case 5:
  81.         mb.writeCoil(id, alamat, nilai < 1);
  82.         case5 = true;
  83.         break;
  84.       case 6:
  85.         mb.writeHreg(id, alamat, nilai);
  86.         case6 = true;
  87.         break;
  88.     }
  89.     while (mb.slave()) {
  90.       mb.task();
  91.       delay(10);
  92.     }
  93.     if (case1) {
  94.       Serial.print("= ");
  95.       lcd.setCursor(0, 1);
  96.       lcd.print("=               ");
  97.       lcd.setCursor(1, 1);
  98.       for (int i = 0; i < nilai; i++) {
  99.         Serial.print(coil[i] ? "0" : "1");
  100.         lcd.print(coil[i] ? "0" : "1");
  101.         var = var + String(coil[i] ? "0" : "1");
  102.       }
  103.       Serial.println();
  104.       modbus = "= " + var;
  105.       case1 = false;
  106.     }
  107.     if (case2) {
  108.       Serial.print("= ");
  109.       lcd.setCursor(0, 1);
  110.       lcd.print("=               ");
  111.       lcd.setCursor(1, 1);
  112.       for (int i = 0; i < nilai; i++) {
  113.         Serial.print(idis[i] ? "0" : "1");
  114.         lcd.print(idis[i] ? "0" : "1");
  115.         var = var + String(idis[i] ? "0" : "1");
  116.       }
  117.       Serial.println();
  118.       modbus = "= " + var;
  119.       case2 = false;
  120.     }
  121.     if (case3) {
  122.       Serial.print("= ");
  123.       lcd.setCursor(0, 1);
  124.       lcd.print("=               ");
  125.       lcd.setCursor(1, 1);
  126.       for (int i = 0; i < (nilai - 1); i++) {
  127.         Serial.print(hreg[i]);
  128.         Serial.print(',');
  129.         lcd.print(hreg[i]);
  130.         lcd.print(',');
  131.         if (i == 0) var = String(hreg[i]);
  132.         else var = var + "," + String(hreg[i]);
  133.       }
  134.       Serial.println(hreg[nilai - 1]);
  135.       lcd.print(hreg[nilai - 1]);
  136.       modbus = "= " + var + "," + String(hreg[nilai - 1]);
  137.       case3 = false;
  138.     }
  139.     if (case4) {
  140.       Serial.print("= ");
  141.       lcd.setCursor(0, 1);
  142.       lcd.print("=               ");
  143.       lcd.setCursor(1, 1);
  144.       for (int i = 0; i < (nilai - 1); i++) {
  145.         Serial.print(ireg[i]);
  146.         Serial.print(',');
  147.         lcd.print(ireg[i]);
  148.         lcd.print(',');
  149.         if (i == 0) var = String(ireg[i]);
  150.         else var = var + "," + String(ireg[i]);
  151.       }
  152.       Serial.println(ireg[nilai - 1]);
  153.       lcd.print(ireg[nilai - 1]);
  154.       modbus = "= " + var + "," + String(ireg[nilai - 1]);
  155.       case4 = false;
  156.     }
  157.     if (case5) {
  158.       mb.readCoil(id, alamat, coil, 1);
  159.       while (mb.slave()) {
  160.         mb.task();
  161.         delay(10);
  162.       }
  163.       Serial.print("= ");
  164.       Serial.println(coil[0] ? "0" : "1");
  165.       lcd.setCursor(0, 1);
  166.       lcd.print("=               ");
  167.       lcd.setCursor(1, 1);
  168.       lcd.print(coil[0] ? "0" : "1");
  169.       modbus = "= " + String(coil[0] ? "0" : "1");
  170.       case5 = false;
  171.     }
  172.     if (case6) {
  173.       mb.readHreg(id, alamat, hreg, 1);
  174.       while (mb.slave()) {
  175.         mb.task();
  176.         delay(10);
  177.       }
  178.       Serial.print("= ");
  179.       Serial.println(hreg[0]);
  180.       lcd.setCursor(0, 1);
  181.       lcd.print("=               ");
  182.       lcd.setCursor(1, 1);
  183.       lcd.print(hreg[0]);
  184.       modbus = "= " + String(hreg[0]);
  185.       case6 = false;
  186.     }
  187.     var = "";
  188.     inputString = "";
  189.     stringComplete = false;
  190.   }
  191.   if (millis() - skr > 5000) {
  192.     skr = millis();
  193.     tampilLCD();
  194.     cacah++;
  195.     if (cacah > 3) cacah = 0;
  196.   }
  197. }
  198. void onModbusChange() {
  199.   inputString = modbus;
  200.   stringComplete = true;
  201.   skr = millis();
  202. }
  203. void serialEvent() {
  204.   while (Serial.available()) {
  205.     char inChar = (char)Serial.read();
  206.     inputString += inChar;
  207.     if (inChar == 13) {
  208.       stringComplete = true;
  209.       skr = millis();
  210.     }
  211.   }
  212. }
  213. String getValue(String data, char separator, int index) {
  214.   int found = 0;
  215.   int strIndex[] = { 0, -1 };
  216.   int maxIndex = data.length() - 1;
  217.   for (int i = 0; i <= maxIndex && found <= index; i++) {
  218.     if (data.charAt(i) == separator || i == maxIndex) {
  219.       found++;
  220.       strIndex[0] = strIndex[1] + 1;
  221.       strIndex[1] = (i == maxIndex) ? i + 1 : i;
  222.     }
  223.   }
  224.   return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
  225. }
  226. void tampilLCD() {
  227.   switch (cacah) {
  228.     case 0:
  229.       inputString = "1,2,0,8";
  230.       modbus = inputString;
  231.       stringComplete = true;
  232.       break;
  233.     case 1:
  234.       inputString = "1,1,0,8";
  235.       modbus = inputString;
  236.       stringComplete = true;
  237.       break;
  238.     case 2:
  239.       inputString = "1,4,0,2";
  240.       modbus = inputString;
  241.       stringComplete = true;
  242.       break;
  243.     case 3:
  244.       inputString = "1,3,0,2";
  245.       modbus = inputString;
  246.       stringComplete = true;
  247.       break;
  248.   }
  249. }
Keterangan program:
  1. Secara garis besar, program untuk NodeMCU di atas akan meminta data Modbus ke Outseal setiap 5 detik, mulai dari data Input Diskrit (Switch), kemudian data Coil (Relay), kemudian data Holding Register (LED PWM), kemudian data Input Register (Potensio). Interval waktu 5 detik tersebut dapat diubah ke angka yang lebih besar dengan mengubah angka 5000 di baris instruksi 191. Disarankan diubah ke angka yang lebih besar agar Outseal tidak terlalu sibuk melayani permintaan NodeMCU ini, sehingga dapat menjalankan tugas lain yang lebih penting.
  2. Outseal akan mengirimkan data tersebut ke NodeMCU, dan oleh NodeMCU, data tersebut ditampilkan di LCD I2C dan juga di Serial Monitor, dan kemudian diteruskan ke widget Messenger di Arduino IoT Cloud.
  3. Selain membaca data dari Outseal, NodeMCU juga dapat mengubah data di Outseal, khususnya untuk data Coil (melalui memori internal B - Binary, untuk mengubah kondisi Relay) dan Holding Register (melalui memori internal I - Integer, untuk mengubah intensitas cahaya LED dengan nilai duty cycle pwm: 0 - 1000), yang datanya bisa dimasukkan melalui widget Messenger, dan juga bisa melalui Serial Monitor.
  4. Berhubung sinyal input di Outseal menggunakan konfigurasi Aktif Low, di mana sinyal Low malah bernilai 1, dan sinyal High malah bernilai 0, maka agar logikanya menjadi benar, ditambahkan instruksi pembalikan seperti ini: idis[i] ? "0" : "1";
  5. Berhubung Modul Relay sebagai output menggunakan konfigurasi Aktif Low, di mana nilai 0 membuat Relay ON, dan 1 malah membuat Relay OFF, maka agar logikanya menjadi benar, ditambahkan instruksi pembalikan seperti ini: coil[0] ? "0" : "1";
  6. Begitu pula untuk pengubahan datanya, agar logikanya menjadi benar, pada instruksi writeCoil dilakukan pembalikan dengan isntruksi seperti ini: mb.writeCoil(id, alamat, nilai < 1);
  7. Selain membaca data dari Outseal, NodeMCU juga dapat mengubah data di Outseal, khususnya untuk data Coil (melalui memori internal B - Binary, untuk mengubah kondisi Relay) dan Holding Register (melalui memori internal I - Integer, untuk mengubah intensitas cahaya LED dengan nilai duty cycle pwm: 0 - 1000), yang datanya bisa dimasukkan melalui widget Messenger, dan juga bisa melalui Serial Monitor.
  8. Program di atas bisa digunakan untuk menampilkan data Outseal Nano dan Outseal Mega yang lebih dari 1 (yang terhubung dengan sambungan RS-485), dengan cara mengatur ID sesuai alamat Modbus Slave, yang diisikan pada Settings di Outseal Studio. 
  9. Silahkan memperbanyak Case pada void tampilLCD() di baris program 226 - 249, sehingga dapat menampilkan data Modbus yang terdiri dari Coil, Input Diskrit, Input Register, dan Holding Register untuk setiap Outseal yang dihubungkan, yang ditampilkan secara berurutan, mulai dari Case1 ke Case berikutnya. Jumlah Case ini diatur dalam variabel cacah, yang di-nol-kan lagi setelah mencapai maksimum (lihat baris 195).
8. Upload program di atas ke hardware NodeMCU, dan susun rangkaian seperti Gambar 6 di atas. Foto berikut ini menunjukkan hasil penerapan program, di mana terlihat komunikasi antara Outseal dengan Arduino IoT Cloud melalui NodeMCU, yang memperlihatkan tampilan LCD dan tampilan widget Messenger menampilkan hasil yang sama.

Gambar 7. Komunikasi antara rangkaian Outseal dan Arduino IoT Cloud melalui NodeMCU berjalan dengan baik, terlihat tampilan LCD I2C dan widget Messenger di Arduino IoT Cloud menampilkan hasil yang sama

9. Sampai di sini langkah-langkah implementasi Penampil LCD + Serial + IoT untuk Outseal Nano dan Outseal Mega. Untuk memudahkan rekan-rekan dalam mempelajari materi di halaman ini, rekan-rekan bisa mengunduh semua file yang diperlukan dalam pembuatan aplikasi di halaman ini di link berikut ini: https://drive.google.com/file/d/185BWxCiCMW7DwoofT0eVnCMQ99XedaRM/view?usp=sharing

10. Agar lebih jelas, berikut ini video pembuatan Penampil LCD + Serial + IoT yang dibuat mengikuti materi di halaman ini. 

Video pembuatan aplikasi Penampil data Outseal di LCD I2C, Serial Monitor dan Arduino IoT Cloud

Catatan: Apabila rekan-rekan menggunakan ESP32 sebagai ganti NodeMCU, maka perhatikan hal berikut ini:
  1. Pastikan saat memilih tipe device di Select Device adalah ESP32, dengan pilihan ESP32 Dev Module.
  2. Untuk ESP32, upload program bisa langsung dari Web Editor Arduino online. Arduino IoT Cloud memberikan dukungan penuh untuk ESP32 ini, sehingga tidak akan terjadi masalah status device tidak bisa Online seperti terjadi pada NodeMCU.
  3. ESP32 memiliki 3 buah Hardware Serial, sehingga tidak memerlukan Software Serial. Untuk itu ubah instruksi pada baris 7 dari KodeProgram_Penggabungan di atas, dari semula SoftwareSerial S(D5,D6); ubah menjadi HardwareSerial S(1); 
  4. Berikutnya ubah juga instruksi di baris 29, dari semula S.begin(9600); ubah menjadi S.begin(9600, SERIAL_8N1, 9, 10); di mana kaki 9 adalah RXD dan kaki 10 adalah TXD. Kedua kaki ini dapat diubah sesuai dengan keinginan. 
  5. Untuk lebih jelasnya mengenai pengubahan kode program untuk ESP32 ini, silahkan unduh file di langkah no. 9 di atas, dan buka KodeProgram_Penggabungan_ESP32.
  6. Kaki SDA dan SCL di ESP32 secara default adalah kaki 21 (SDA) dan 22 (SCL). Hubungkan kaki SDA dan SCL ini ke LCD I2C.
Catatan: Untuk contoh penggunaan 2 Outseal Nano yang terhubung secara RS485 dan dikontrol serta dimonitor dari Arduino IoT Cloud dapat teman-teman lihat di sini: Bermain Arduino IoT Cloud: Aplikasi#3. Outseal IoT

2 comments: