1. Seputar Modbus

Ada teman yang bertanya di grup, bagaimana cara mudah menghubungkan 2 atau lebih Arduino dengan sambungan RS-485, dan dengan protokol Modbus RTU. Teman tersebut sudah mencoba beberapa kali namun belum berhasil, dan tidak tahu, manakah bagian yang bermasalah, apakah kabelnya, atau komponennya, atau programnya, atau breakout RS485-nya, atau ada bagian yang masih kurang, semuanya serba misteri.

Sebenarnya saya juga tidak tahu banyak. Hanya saja, untungnya saya bisa "coba-coba" dengan simulasi Proteus. Dengan simulasi Proteus ini, hal yang sulit dan misteri seperti di atas, bisa tersolusikan dengan mudah  dan hemat, hemat waktu dan biaya. 

Untuk membuat penjelasan saya lebih mudah dipahami, saya membuatnya dalam 6 tahapan sebagai berikut:

Berikut ini uraian satu persatu dari keenam tahapan di atas:

========================================================================================

1. Komunikasi Serial antara 2 Arduino Uno/Nano
Perhatikan rangkaian Proteus berikut ini. 

Gambar 1. Komunikasi serial antara 2 Arduino Uno/Nano

Keterangan rangkaian:
  • Rangkaian di atas menggunakan 2 buah rangkaian yang sama, yang terdiri dari sebuah ATmega328, sebuah tombol, sebuah LED dengan resistor 330 ohm, serta sebuah Virtual Terminal. 
  • Komponen ATmega328 mensimulasikan Arduino Uno/Nano (karena Arduino Uno dan Arduino Nano sama-sama menggunakan mikrokontroler ATmega328). Virtual Terminal digunakan di sini untuk menampilkan data yang diterima dari komunikasi serial.
  • Komunikasi serial ditunjukkan dengan sambungan yang saling bersilang (cross) antara kaki TX dengan RX di kedua ATmega328. Dengan bantuan library SoftwareSerial, kaki D8 (PB0) dibuat menjadi kaki RX dan kaki D9 (PB1) dibuat menjadi kaki TX. 
  • Rekan-rekan dapat mengunduh file Proteus Gambar 1 di atas di bagian akhir tulisan ini. 

Dengan rangkaian Gambar 1 di atas, berikut ini kode program untuk kedua ATmega328 (kode program untuk U1 dan U2 sama):

Kode_Program_01:
  1. #include <SoftwareSerial.h>
  2. SoftwareSerial mySerial(8, 9);      // RX, TX
  3. int awal = 0;
  4. void setup() {
  5.   Serial.begin(9600);    
  6.   mySerial.begin(9600);  
  7.   pinMode(2, INPUT_PULLUP);
  8.   pinMode(3, OUTPUT);
  9. }
  10. void loop() {
  11.   int tekan = !digitalRead(2);      //ditekan=1, dilepas=0
  12.   if (tekan != awal) {
  13.     if (tekan) mySerial.write("1"); //jika ditekan, kirim 1
  14.     else mySerial.write("0");       //jika dilepas, kirim 0
  15.   }
  16.   awal = tekan;
  17.   if (mySerial.available()) {
  18.     char nyala = mySerial.read() - '0';  //ubah karakter ASCII ke angka
  19.     if (nyala) digitalWrite(3, HIGH);    //jika baca 1, LED nyala
  20.     else digitalWrite(3, LOW);           //jika baca 0, LED padam
  21.   }
  22. }

Keterangan program:
  • Agar kaki D8 dan D9 bisa digunakan untuk kaki RX dan TX komunikasi serial, gunakan library SoftwareSerial. 
  • Baris 4 - 9 hanya dijalankan sekali di awal, digunakan untuk seting komunikasi serial (baik hardware serial maupun software serial) dan penugasan kaki input output.
  • Baris 10 - 32 dijalankan berulang-kali, dengan urutan aksi:
    • membaca kondisi tombol yang terhubung dengan kaki D2, apabila terjadi perubahan pada kondisi tombol, jika tombol ditekan, maka tampilkan "kirim 1" di Virtual Terminal dan kirim "1" ke software serial (ATmega328 lain); jika tombol dilepas, maka tampilkan "kirim 0" di Virtual Terminal dan kirim "0" ke software serial (ATmega328 lain).
    • membaca data software serial yang masuk, apabila bernilai 1, maka nyalakan LED dan tampilkan "terbaca 1" di Virtual Terminal; apabila bernilai 0, maka padamkan LED dan tampilkan "terbaca 0" di Virtual Terminal. 
  • Rekan-rekan dapat mengunduh file Kode_Program_01 di atas, di bagian akhir tulisan ini. 

Langkah-langkah Simulasi
Berikut ini langkah-langkah yang diperlukan agar rangkaian Proteus Gambar 1 di atas dapat disimulasikan:
1. Kompilasi program di atas. Pastikan muncul lokasi direktori file Hex hasil kompilasi di bagian Output.
2. Copy lokasi file Hex tersebut, dan tempelkan di kolom Program File, di jendela Edit Component ATmega328, baik untuk ATmega328 yang pertama, maupun yang kedua.
3. Di samping mengisi lokasi file Hex di Program File, atur juga parameter CLKDIV8 = Unprogrammed, CKSEL Fuses = Ext. Clock, dan Clock Frequency = 16MHz, seperti ditunjukkan pada gambar berikut.

Gambar 2. Klik 2 kali komponen ATmega328, di Edit Component, isi kolom Program File dengan lokasi file Hex, CLKDIV8 = Unprogrammed, CKSEL Fuses = Ext. Clock, Clock Frequency = 16MHz

4. Jalankan simulasi Proteus dengan menekan tombol Run. Perhatikan, setiap kali tombol ditekan, baik tombol di ATmega328 yang pertama maupun tombol di ATmega328 yang kedua, akan membuat LED yang terhubung ke ATmega328 lain, menjadi nyala, sedangkan bila tombol dilepas, LED akan padam. Selain LED bisa menyala/padam, Virtual Terminal juga menampilkan "kirim 1" ketika tombol ditekan, dan "kirim 0" ketika tombol dilepas. Kemudian ketika LED menyala, Virtual Terminal akan menampilkan "terbaca 1", dan jika LED padam, Virtual Terminal akan menampilkan "terbaca 0". 

Gambar 3. Simulasi komunikasi serial antara 2 Arduino Uno/Nano, ketika tombol ditekan, maka LED yang terhubung ke ATmega328 lain, akan menyala

Dari Gambar 3 di atas, terlihat bahwa komunikasi serial antara 2 buah Arduino Uno/Nano telah berhasil dan berjalan dengan baik. Berikutnya, di Tahap 2, ditambahkan Serial Monitor untuk variasi data input dan output.

======================================================================================== 

2. Komunikasi Serial antara 2 Arduino Uno/Nano + Serial Monitor
Berikut ini langkah-langkahnya:
1. Tambahkan Virtual Terminal di masing-masing ATmega328 seperti rangkaian berikut ini:

Gambar 4. Rangkaian ini sama seperti Gambar 1, dengan tambahan Virtual Terminal VT3 dan VT4

Keterangan rangkaian:
  • Rangkaian Gambar 4 di atas sama seperti Rangkaian Gambar 1, hanya diberi penambahan Virtual Terminal untuk masing-masing ATmega328, yang terhubung dengan kaki D0 (Rx) dan D1 (Tx). 
  • Rekan-rekan dapat mengunduh file Proteus Gambar 4 di atas di bagian akhir tulisan ini. 

2. Berikutnya gunakan kode program berikut ini, baik untuk U1 maupun untuk U2 (kode program untuk U1 dan U2 sama):

Kode_Program_02:
  1. #include <SoftwareSerial.h>
  2. SoftwareSerial mySerial(8, 9);  // RX, TX
  3. int awal = 0;
  4. void setup() {
  5.   Serial.begin(9600);
  6.   mySerial.begin(9600);
  7.   pinMode(2, INPUT_PULLUP);
  8.   pinMode(3, OUTPUT);
  9. }
  10. void loop() {
  11.   int tekan = !digitalRead(2);  //ditekan=1, dilepas=0
  12.   if (tekan != awal) {
  13.     if (tekan) mySerial.write("1");  //jika ditekan, kirim 1
  14.     else mySerial.write("0");        //jika dilepas, kirim 0
  15.   }
  16.   awal = tekan;
  17.   if (mySerial.available()) {
  18.     char nyala = mySerial.read() - '0';  //ubah karakter ASCII ke angka
  19.     if (nyala) {
  20.       digitalWrite(3, HIGH);  //jika baca 1, LED nyala
  21.       Serial.println("ON");
  22.     } else {
  23.       digitalWrite(3, LOW);  //jika baca 0, LED padam
  24.       Serial.println("OFF");
  25.     }
  26.   }
  27. }
  28. void serialEvent() {
  29.   while (Serial.available()) {
  30.     char ketik = Serial.read() - '0';  //ubah karakter ASCII ke angka
  31.     if (ketik) mySerial.write("1");    //jika ketik 1, kirim 1
  32.     else mySerial.write("0");          //jika ketik 0, kirim 0
  33.   }
  34. }
Keterangan program:
  • Kode_Program_02 di atas sama seperti Kode_Program_01, hanya ada penambahan instruksi untuk menampilkan data di hardware serial, yaitu Serial.println di baris 21 dan 24, dan juga instruksi untuk menerima data di hardware serial, yaitu void serialEvent() di baris 28 - 34. 
  • Setiap kali angka 1 diketikkan di Virtual Terminal (VT3 atau VT4), maka LED di ATmega328 yang lain akan menyala, sebaliknya ketika diketikkan angka 0, LED tersebut padam (Catatan: pengetikan dilakukan tanpa menekan Enter).
  • Rekan-rekan dapat mengunduh file Kode_Program_02 di atas, di bagian akhir tulisan ini. 
3. Berikutnya, jalankan simulasi Proteus.

Gambar 5. Simulasi komunikasi serial antara 2 Arduino Uno/Nano + Serial Monitor VT3 dan VT4, dimana ketika 1 diketikkan di VT3, LED di U2 menyala, ketika diketikkan 0, LED tersebut padam, begitu pula ketika 1 diketikkan di VT4, LED di U1 menyala, ketika diketikkan 0, LED tersebut padam

Dari Gambar 5 di atas, terlihat bahwa komunikasi serial antara 2 buah Arduino Uno/Nano + Serial Monitor telah berhasil dan berjalan dengan baik. Berikutnya, di Tahap 3, ditunjukkan komunikasi Modbus RTU antara 2 buah Arduino Uno/Nano.

========================================================================================

3. Komunikasi Modbus RTU antara 2 Arduino Uno/Nano
Berikut ini langkah-langkahnya:
1. Gunakan rangkaian yang sama seperti Gambar 1 di atas.
2. Buat kode program berikut ini. Ada 2 buah program, program pertama (Kode_Program_03) untuk Master, sedangkan program kedua (Kode_Program_04) untuk Slave. 

Kode_Program_03:
  1. #include <ModbusRTU.h>
  2. #include <SoftwareSerial.h>
  3. SoftwareSerial mySerial(8, 9);  // RX, TX
  4. ModbusRTU mb;
  5. unsigned long skr = 0;
  6. bool coil[2];
  7. int awal = 0;
  8. void setup() {
  9.   Serial.begin(9600);
  10.   mySerial.begin(9600);
  11.   mb.begin(&mySerial);
  12.   mb.setBaudrate(9600);
  13.   mb.master();
  14.   pinMode(2, INPUT_PULLUP);
  15.   pinMode(3, OUTPUT);
  16. }
  17. void loop() {
  18.   int tekan = !digitalRead(2);  //ditekan=1, dilepas=0
  19.   if (tekan != awal) {
  20.     if (tekan) mb.writeCoil(1, 0, true);  //jika ditekan, kirim T
  21.     else mb.writeCoil(1, 0, false);       //jika dilepas, kirim F
  22.     kirimRequest();
  23.   }
  24.   awal = tekan;
  25.   if (millis() - skr > 100) {
  26.     mb.readCoil(1, 1, coil, 1);  //baca coil di ID=1, indek=1
  27.     kirimRequest();
  28.     if (coil[0]) digitalWrite(3, HIGH);  //jika 1, LED nyala
  29.     else digitalWrite(3, LOW);           //jika 0, LED padam
  30.     skr = millis();
  31.   }
  32. }
  33. void kirimRequest() {
  34.   while (mb.slave()) {
  35.     mb.task();
  36.     delay(10);
  37.   }
  38. }

Kode_Program_04:
  1. #include <ModbusRTU.h>
  2. #include <SoftwareSerial.h>
  3. SoftwareSerial mySerial(8, 9);  // RX, TX
  4. ModbusRTU mb;
  5. unsigned long skr = 0;
  6. int awal = 0;
  7. void setup() {
  8.   Serial.begin(9600);
  9.   mySerial.begin(9600);
  10.   mb.begin(&mySerial);
  11.   mb.setBaudrate(9600);
  12.   mb.slave(1);    //ID Slave = 1
  13.   mb.addCoil(0);  //alamat Coil=0 untuk LED Slave
  14.   mb.addCoil(1);  //alamat Coil=1 untuk LED Master
  15.   pinMode(2, INPUT_PULLUP);
  16.   pinMode(3, OUTPUT);
  17. }
  18. void loop() {
  19.   mb.task();
  20.   int tekan = !digitalRead(2);  //ditekan=1, dilepas=0
  21.   if (tekan != awal) {
  22.     if (tekan) mb.Coil(1, true);  //jika ditekan, Coil[1] = T
  23.     else mb.Coil(1, false);       //jika dilepas, Coil[1] = F
  24.   }
  25.   awal = tekan;
  26.   if (millis() - skr > 100) {
  27.     if (mb.Coil(0)) digitalWrite(3, HIGH);  //jika 1, LED nyala
  28.     else digitalWrite(3, LOW);               //jika 0, LED padam
  29.     skr = millis();
  30.   }
  31. }
Keterangan Program:
  • Kode program di atas memerlukan library modbus-esp8266.
  • Kode_Program_03 untuk komponen ATmega328 U1, sedangkan Kode_Program_04 untuk komponen ATmega328 U2.
  • Nilai untuk LED yang terhubung dengan U1 (Master) dan LED yang terhubung dengan U2 (Slave), semuanya ditempatkan di memori Slave, khususnya di memori Coil, dengan alamat 0 untuk LED di Slave, dan alamat 1 untuk LED di Master (lihat baris 13 - 14 di Kode_Program_04).
  • Master mengirimkan permintaan untuk membaca data Coil di alamat 1, dan menggunakan nilainya untuk mengatur nyala LED di Master. Data Coil di alamat 1 tersebut diisi dengan kondisi tombol di Slave. Apabila tombol di Slave ditekan, maka Coil tersebut bernilai True, apabila tombol dilepas, maka Coil tersebut bernilai False.
  • Master mengirimkan permintaan untuk menulis/mengubah data Coil di alamat 0, berdasarkan tombol di Master; apabila tombol ditekan maka nilai Coil = True, apabila tombol dilepas maka nilai Coil = False. Data Coil di alamat 0 ini nantinya digunakan Slave untuk menghidup/matikan LED yang terhubung dengan Slave tersebut.
  • Kode_Program_03 dan Kode_Program_04 dapat diunduh di bagian akhir tulisan ini.
Catatan: Ada 4 tipe memori yang digunakan pada protokol Modbus, yaitu Coil, Input Diskrit, Input Register dan Holding Register. Tabel 1 berikut ini menunjukkan keterangan masing-masing memori dengan alokasi alamat, sifat aksesnya dan ukuran datanya.

Tabel 1. Tipe Memori Data di Protokol Modbus 
(dikutip dari buku Panduan dasar Outseal PLC oleh Bpk. Agung Bakhtiar) 

3. Kompilasi program pertama (program Master), isikan lokasi file Hex hasil kompilasi di komponen ATmega328 U1. Berikutnya kompilasi program kedua (program Slave), isikan lokasi file Hex hasil kompilasi di komponen ATmega328 U2.

4. Jalankan simulasi Proteus dengan menekan tombol Run. Perhatikan, setiap kali tombol di ATmega328 U1 ditekan, LED di ATmega328 U2 menyala, dan padam ketika tombol tersebut dilepas. Begitu pula, ketika tombol di ATmega328 U2 ditekan, LED di ATmega328 U1 menyala, dan padam ketika tombol tersebut ditekan.

Gambar 6. Hasil simulasi komunikasi Modbus RTU antara Master (U1) dan Slave (U2), di mana tombol di U1 akan menghidup/matikan LED di U2, dan tombol di U2 akan menghidup/matikan LED di U1.

Catatan: Terlihat tampilan data di kedua Virtual Terminal menampilkan karakter yang aneh, hal ini dikarenakan data pada protokol Modbus RTU adalah biner, bukan karakter ASCII. Dengan data biner ini, transfer data menjadi lebih cepat, karena ukuran data menjadi lebih kecil bila dibandingkan data berbentuk ASCII (sebagai gambarannya, 1 karakter ASCII = 8 bit biner).

Dari Gambar 6 di atas, terlihat bahwa komunikasi Modbus RTU antara 2 buah Arduino Uno/Nano telah berhasil dan berjalan dengan baik. Berikutnya, di Tahap 4, ditunjukkan komunikasi Modbus RTU antara 2 buah Arduino Uno/Nano dengan penambahan Serial Monitor untuk variasi input dan tampilan output yang lebih jelas.

========================================================================================

4. Komunikasi Modbus RTU antara 2 Arduino Uno/Nano + Serial Monitor
Berikut ini langkah-langkahnya:
1. Gunakan rangkaian yang sama seperti Gambar 4 di atas.
2. Buat kode program berikut ini. Ada 2 buah program, program pertama (Kode_Program_05) untuk Master, sedangkan program kedua (Kode_Program_06) untuk Slave. 

Kode_Program_05:
  1. #include <ModbusRTU.h>
  2. #include <SoftwareSerial.h>
  3. SoftwareSerial mySerial(8, 9);  // RX, TX
  4. ModbusRTU mb;
  5. unsigned long skr = 0;
  6. bool coil[2];
  7. bool c = false;
  8. int awal = 0;
  9. void setup() {
  10.   Serial.begin(9600);
  11.   mySerial.begin(9600);
  12.   mb.begin(&mySerial);
  13.   mb.setBaudrate(9600);
  14.   mb.master();
  15.   pinMode(2, INPUT_PULLUP);
  16.   pinMode(3, OUTPUT);
  17.   mb.writeCoil(1, 0, false);
  18.   kirimRequest();
  19.   delay(1000);
  20. }
  21. void loop() {
  22.   int tekan = !digitalRead(2);  //ditekan=1, dilepas=0
  23.   if (tekan != awal) {
  24.     if (tekan) {
  25.       mb.writeCoil(1, 0, true);  //jika ditekan, kirim T
  26.       kirimRequest();
  27.       Serial.println("LED U2 ON");
  28.     } else {
  29.       mb.writeCoil(1, 0, false);  //jika dilepas, kirim F
  30.       kirimRequest();
  31.       Serial.println("LED U2 OFF");
  32.     }
  33.   }
  34.   awal = tekan;
  35.   if (millis() - skr > 100) {
  36.     mb.readCoil(1, 1, coil, 1);  //baca coil di ID=1, indek=1
  37.     kirimRequest();
  38.     if (coil[0] != c) {
  39.       if (coil[0]) {
  40.         digitalWrite(3, HIGH);  //jika 1, LED nyala
  41.         Serial.println("LED U1 ON");
  42.       } else {
  43.         digitalWrite(3, LOW);  //jika 0, LED padam
  44.         Serial.println("LED U1 OFF");
  45.       }
  46.     }
  47.     c = coil[0];
  48.     skr = millis();
  49.   }
  50. }
  51. void kirimRequest() {
  52.   while (mb.slave()) {
  53.     mb.task();
  54.     delay(10);
  55.   }
  56. }
  57. void serialEvent() {
  58.   while (Serial.available()) {
  59.     int ketik = Serial.parseInt();
  60.     if (Serial.read() == char(13)) {
  61.       if (ketik) {
  62.         mb.writeCoil(1, 0, true);  //jika diketik 1, kirim T
  63.         kirimRequest();
  64.         Serial.println("LED U2 ON");
  65.       } else {
  66.         mb.writeCoil(1, 0, false);  //jika diketik 0, kirim F
  67.         kirimRequest();
  68.         Serial.println("LED U2 OFF");
  69.       }
  70.     }
  71.   }
  72. }

Kode_Program_06:
  1. #include <ModbusRTU.h>
  2. #include <SoftwareSerial.h>
  3. SoftwareSerial mySerial(8, 9);  // RX, TX
  4. ModbusRTU mb;
  5. unsigned long skr = 0;
  6. int awal = 0;
  7. bool coil = false;
  8. void setup() {
  9.   Serial.begin(9600);
  10.   mySerial.begin(9600);
  11.   mb.begin(&mySerial);
  12.   mb.setBaudrate(9600);
  13.   mb.slave(1);    //ID Slave = 1
  14.   mb.addCoil(0);  //alamat Coil=0 untuk LED Slave
  15.   mb.addCoil(1);  //alamat Coil=1 untuk LED Master
  16.   pinMode(2, INPUT_PULLUP);
  17.   pinMode(3, OUTPUT);
  18. }
  19. void loop() {
  20.   mb.task();
  21.   int tekan = !digitalRead(2);  //ditekan=1, dilepas=0
  22.   if (tekan != awal) {
  23.     if (tekan) {
  24.       mb.Coil(1, true);  //jika ditekan, Coil[1] = T
  25.       Serial.println("LED U1 ON");
  26.     } else {
  27.       mb.Coil(1, false);  //jika dilepas, Coil[1] = F
  28.       Serial.println("LED U1 OFF");
  29.     }
  30.   }
  31.   awal = tekan;
  32.   if (millis() - skr > 100) {
  33.     if (mb.Coil(0) != coil) {
  34.       if (mb.Coil(0)) {
  35.         digitalWrite(3, HIGH);  //jika 1, LED nyala
  36.         Serial.println("LED U2 ON");
  37.       } else {
  38.         digitalWrite(3, LOW);  //jika 0, LED padam
  39.         Serial.println("LED U2 OFF");
  40.       }
  41.     }
  42.     coil = mb.Coil(0);
  43.     skr = millis();
  44.   }
  45. }

Keterangan Program:
  • Kode program di atas memerlukan library modbus-esp8266.
  • Kode_Program_05 untuk komponen ATmega328 U1, sedangkan Kode_Program_06 untuk komponen ATmega328 U2.
  • Virtual Terminal (sama seperti Serial Monitor di Arduino IDE) akan menampilkan data kondisi LED, baik LED di U1 maupun LED di U2. Untuk LED di U1, kondisinya ditampilkan di Virtual Terminal VT3, sedangkan LED di U2, kondisinya ditampilkan di Virtual Terminal VT4.
  • Untuk menyalakan LED di U2, selain dengan menekan tombol di U1, juga bisa dilakukan dengan mengetikkan angka 1 diikuti Enter di Virtual Terminal VT3, sedangkan untuk mematikan LED di U2 tersebut, selain dengan melepas tombol di U1, juga bisa dilakukan dengan mengetikkan angka 0 diikuti Enter di Virtual Terminal VT3.
  • Virtual Terminal VT4 tidak bisa digunakan untuk mengontrol LED di U1, karena U2 sebagai Slave, yang hanya bisa merespon permintaan dari Master. Jadi hanya Virtual Terminal VT3 saja yang bisa digunakan untuk mengontrol LED, karena VT3 terhubung dengan Master.
  • Kode_Program_05 dan Kode_Program_06 dapat diunduh di bagian akhir tulisan ini.
3. Kompilasi program pertama (program Master), isikan lokasi file Hex hasil kompilasi di komponen ATmega328 U1. Berikutnya kompilasi program kedua (program Slave), isikan lokasi file Hex hasil kompilasi di komponen ATmega328 U2.
4. Jalankan simulasi Proteus dengan menekan tombol Run. Perhatikan beberapa hal berikut ini:
  • Setiap kali tombol di U1 ditekan, LED di U2 menyala dan VT4 menampilkan tulisan "LED U2 ON", dan ketika tombol di U1 tersebut dilepas, LED di U2 padam dan VT4 menampilkan tulisan "LED U2 OFF". 
  • Begitu pula, ketika tombol di U2 ditekan, LED di U1 menyala dan VT3 menampilkan tulisan "LED U1 ON", dan ketika tombol di U2 tersebut dilepas, LED di U1 padam dan VT3 menampilkan tulisan "LED U1 OFF". 
  • Selain dengan menekan tombol di U1, LED U2 bisa dinyalakan dengan mengetikkan angka 1 diikuti Enter di VT3, dan dipadamkan dengan mengetikkan angka 0 diikuti Enter di VT3.
Gambar 7. Hasil simulasi komunikasi Modbus RTU antara Master (U1) dan Slave (U2), di mana VT3 dan VT4 ditambahkan untuk menampilkan kondisi LED di U1 dan U2, dan juga untuk mengontrol LED (khusus VT3 yang terhubung dengan Master)

Dari Gambar 7 di atas, terlihat bahwa Virtual Terminal bisa menampilkan kondisi LED, baik LED di U1 maupun LED di U2, dan khusus untuk VT3, bisa juga digunakan untuk mengontrol LED di U2, dengan cara mengetikkan angka 1 diikuti Enter untuk menyalakan LED, dan mengetikan angka 0 diikuti Enter untuk memadamkan LED di U2 tersebut. Berikutnya, di Tahap 5, akan ditunjukkan komunikasi Modbus RTU antara 3 buah Arduino Uno/Nano. Untuk komunikasi 3 buah Arduino, kita tidak bisa menghubungkan RX dengan TX pasangannya, karena akan membuat 1 jalur RX tersambung dengan 2 jalur TX, yang mana akan membuat komunikasi menjadi gagal.

Untuk komunikasi lebih dari 2 Arduino/perangkat, ada 2 aturan yang diperlukan, yaitu:
1. Harus ada yang mengawali komunikasi.
2. Hanya membolehkan 1 Arduino/perangkat yang mengirim data, sedangkan yang lain menerima.

Kedua aturan tersebut dihadirkan pada protokol Modbus, yang membagi perangkat dalam 2 peran, yaitu Master dan Slave. Hanya 1 Master yang diperbolehkan pada satu waktu dalam satu jaringan komunikasi, sedangkan Slave bisa lebih dari 1 perangkat. Master akan selalu mengawali komunikasi dengan mengirimkan permintaan ke Slave dengan nomor ID tertentu, dan menunggu Slave tersebut memberi respon. Permintaan bisa berupa instruksi membaca data atau mengubah data di Slave tersebut. Apabila Master tidak menerima respon dari Slave, Master akan mengulang kembali permintaan tersebut, dan hanya akan berhenti setelah Slave memberi respon. Sekalipun hal ini menyebabkan proses menjadi lambat, namun dengan cara ini, komunikasi data pada protokol Modbus terjamin kehadalannya.

Untuk komunikasi lebih dari 2 Arduino/perangkat, selain penerapan protokol Modbus RTU, diperlukan sambungan RS-485. Dengan sambungan RS-485 ini, memaksa jalur komunikasi hanya membolehkan satu perangkat saja yang mengirimkan data di satu waktu, sedangkan semua perangkat yang lain hanya bisa menerima. Sambungan RS-485 menggunakan IC MAX485 atau SN75176, yang berfungsi untuk mengendalikan buka tutup saluran kirim dan terima, sehingga perangkat akan dapat mengirim data ke jalur komunikasi atau menerima data dari jalur komunikasi secara bergantian. 

Catatan: Sambungan RS-485 hanya diterapkan pada komunikasi untuk lebih dari 2 Arduino/perangkat. Apabila hanya melibatkan 2 Arduino/perangkat saja, pada protokol Modbus RTU, cukup hanya dengan menyambung secara bersilang seperti Gambar 7 di atas, yaitu TX bertemu RX dan sebaliknya. Sambungan saling silang antara 2 perangkat ini biasa disebut juga sebagai sambungan RS-232.

Sambungan RS-485 menggunakan jalur komunikasi yang disebut jalur diferensial. Jalur diferensial ini terdiri dari 2 buah jalur, dengan nilai data berdasarkan beda tegangan antara jalur A dengan jalur B. Apabila tegangan di jalur A lebih tinggi dibandingkan tegangan di jalur B, maka nilai datanya adalah 0, sedangkan bila tegangan di jalur A lebih rendah dibandingkan tegangan di jalur B, maka nilai datanya adalah 1. Dengan menggunakan beda atau selisih tegangan antara 2 jalur ini memungkinkan pengiriman data yang lebih jauh bila dibandingkan dengan pengiriman data berdasarkan level logika. Hal ini dikarenakan dengan semakin jauh jarak pengiriman, semakin besar juga hambatan kabel, yang membuat drop (penurunan) tegangan menjadi semakin besar. Apabila menggunakan level logika, maka bisa jadi logika 1 tidak bisa tercapai karena drop tegangan yang semakin besar seiring dengan panjang kabelnya. Sebaliknya bila menggunakan beda tegangan antara 2 jalur, drop tegangan yang terjadi tidak banyak berpengaruh, karena kedua kabel mengalami drop tegangan yang sama, sehingga selisihnya selalu sama.

========================================================================================

5. Komunikasi Modbus RTU antara 3 Arduino Uno/Nano + Serial Monitor

Berikut ini langkah-langkahnya:
1. Gunakan rangkaian berikut ini.

Gambar 8. Rangkaian untuk komunikasi 3 buah Arduino Uno/Nano dengan sambungan RS-485

Keterangan rangkaian:
  • Rangkaian Gambar 8 di atas menghubungkan 3 buah Arduino Uno/Nano (disimulasikan dengan ATmega328) melalui sambungan RS-485, dengan kaki RX TX menggunakan software serial, yang ditempatkan di kaki D8 (RX) dan kaki D9 (TX).
  • Kaki hardware serial (D0 dan D1) digunakan untuk menampilkan kondisi LED di masing-masing Arduino dengan bantuan Virtual Terminal. Khusus untuk Virtual Terminal VT1, tidak hanya menampilkan kondisi LED, tetapi juga bisa digunakan untuk mengontrol LED di U2 dan LED di U3, sebagai alternatif pengganti tombol yang terhubung di kaki D2 dan D3.
  • Rekan-rekan dapat mengunduh file Proteus Gambar 8 di atas di bagian akhir tulisan ini. 

2. Buat kode program berikut ini. Ada 3 buah program, program pertama (Kode_Program_07) untuk Master, program kedua (Kode_Program_08) untuk Slave1, dan program ketiga (Kode_Program 09) untuk Slave2.

Kode_Program_07:
  1. #include <ModbusRTU.h>
  2. #include <SoftwareSerial.h>
  3. SoftwareSerial mySerial(8, 9);  // RX, TX
  4. ModbusRTU mb;
  5. unsigned long skr = 0;
  6. bool coil1[2];
  7. bool coil2[2];
  8. bool c1 = false;
  9. bool c2 = false;
  10. int awal1 = 0;
  11. int awal2 = 0;
  12. void setup() {
  13.   Serial.begin(9600);
  14.   mySerial.begin(9600);
  15.   mb.begin(&mySerial);
  16.   mb.setBaudrate(9600);
  17.   mb.master();
  18.   pinMode(2, INPUT_PULLUP);
  19.   pinMode(3, INPUT_PULLUP);
  20.   pinMode(6, OUTPUT);
  21.   pinMode(7, OUTPUT);
  22.   mb.writeCoil(1, 0, false);
  23.   kirimRequest();
  24.   delay(1000);
  25. }
  26. void loop() {
  27.   int tekan1 = !digitalRead(2);  //ditekan=1, dilepas=0
  28.   if (tekan1 != awal1) {
  29.     if (tekan1) {
  30.       mb.writeCoil(1, 0, true);  //jika ditekan, kirim T
  31.       kirimRequest();
  32.       Serial.println("LED U2 ON");
  33.     } else {
  34.       mb.writeCoil(1, 0, false);  //jika dilepas, kirim F
  35.       kirimRequest();
  36.       Serial.println("LED U2 OFF");
  37.     }
  38.   }
  39.   awal1 = tekan1;
  40.   int tekan2 = !digitalRead(3);  //ditekan=1, dilepas=0
  41.   if (tekan2 != awal2) {
  42.     if (tekan2) {
  43.       mb.writeCoil(2, 0, true);  //jika ditekan, kirim T
  44.       kirimRequest();
  45.       Serial.println("LED U3 ON");
  46.     } else {
  47.       mb.writeCoil(2, 0, false);  //jika dilepas, kirim F
  48.       kirimRequest();
  49.       Serial.println("LED U3 OFF");
  50.     }
  51.   }
  52.   awal2 = tekan2;
  53.   if (millis() - skr > 100) {
  54.     mb.readCoil(1, 1, coil1, 1);  //baca coil di ID=1, indek=1
  55.     kirimRequest();
  56.     if (coil1[0] != c1) {
  57.       if (coil1[0]) {
  58.         digitalWrite(6, HIGH);  //jika 1, LED nyala
  59.         Serial.println("TOMBOL U2 ON");
  60.       } else {
  61.         digitalWrite(6, LOW);  //jika 0, LED padam
  62.         Serial.println("TOMBOL U2 OFF");
  63.       }
  64.     }
  65.     c1 = coil1[0];
  66.     mb.readCoil(2, 1, coil2, 1);  //baca coil di ID=1, indek=1
  67.     kirimRequest();
  68.     if (coil2[0] != c2) {
  69.       if (coil2[0]) {
  70.         digitalWrite(7, HIGH);  //jika 1, LED nyala
  71.         Serial.println("TOMBOL U3 ON");
  72.       } else {
  73.         digitalWrite(7, LOW);  //jika 0, LED padam
  74.         Serial.println("TOMBOL U3 OFF");
  75.       }
  76.     }
  77.     c2 = coil2[0];
  78.     skr = millis();
  79.   }
  80. }
  81. void kirimRequest() {
  82.   while (mb.slave()) {
  83.     mb.task();
  84.     delay(10);
  85.   }
  86. }
  87. void serialEvent() {
  88.   while (Serial.available()) {
  89.     char ketik1 = Serial.read();
  90.     int ketik2 = Serial.parseInt();
  91.     if (Serial.read() == char(13)) {
  92.       if (ketik1 == 'a') {
  93.         if (ketik2) {
  94.           mb.writeCoil(1, 0, true);  //jika diketik 1, kirim T
  95.           kirimRequest();
  96.           Serial.println("LED U2 ON");
  97.         } else {
  98.           mb.writeCoil(1, 0, false);  //jika diketik 0, kirim F
  99.           kirimRequest();
  100.           Serial.println("LED U2 OFF");
  101.         }
  102.       } else if (ketik1 == 'b') {
  103.         if (ketik2) {
  104.           mb.writeCoil(2, 0, true);  //jika diketik 1, kirim T
  105.           kirimRequest();
  106.           Serial.println("LED U3 ON");
  107.         } else {
  108.           mb.writeCoil(2, 0, false);  //jika diketik 0, kirim F
  109.           kirimRequest();
  110.           Serial.println("LED U3 OFF");
  111.         }
  112.       }
  113.     }
  114.   }
  115. }

Kode_Program_08:
  1. #include <ModbusRTU.h>
  2. #include <SoftwareSerial.h>
  3. SoftwareSerial mySerial(8, 9);  // RX, TX
  4. ModbusRTU mb;
  5. unsigned long skr = 0;
  6. int awal = 0;
  7. bool coil = false;
  8. void setup() {
  9.   Serial.begin(9600);
  10.   mySerial.begin(9600);
  11.   mb.begin(&mySerial);
  12.   mb.setBaudrate(9600);
  13.   mb.slave(1);    //ID Slave = 1
  14.   mb.addCoil(0);  //alamat Coil=0 untuk LED Slave
  15.   mb.addCoil(1);  //alamat Coil=1 untuk LED Master
  16.   pinMode(2, INPUT_PULLUP);
  17.   pinMode(6, OUTPUT);
  18. }
  19. void loop() {
  20.   mb.task();
  21.   int tekan = !digitalRead(2);  //ditekan=1, dilepas=0
  22.   if (tekan != awal) {
  23.     if (tekan) {
  24.       mb.Coil(1, true);  //jika ditekan, Coil[1] = T
  25.       Serial.println("LED1 U1 ON");
  26.     } else {
  27.       mb.Coil(1, false);  //jika dilepas, Coil[1] = F
  28.       Serial.println("LED1 U1 OFF");
  29.     }
  30.   }
  31.   awal = tekan;
  32.   if (millis() - skr > 100) {
  33.     if (mb.Coil(0) != coil) {
  34.       if (mb.Coil(0)) {
  35.         digitalWrite(6, HIGH);  //jika 1, LED nyala
  36.         Serial.println("LED U2 ON");
  37.       } else {
  38.         digitalWrite(6, LOW);  //jika 0, LED padam
  39.         Serial.println("LED U2 OFF");
  40.       }
  41.     }
  42.     coil = mb.Coil(0);
  43.     skr = millis();
  44.   }
  45. }

Kode_Program_09:
  1. #include <ModbusRTU.h>
  2. #include <SoftwareSerial.h>
  3. SoftwareSerial mySerial(8, 9);  // RX, TX
  4. ModbusRTU mb;
  5. unsigned long skr = 0;
  6. int awal = 0;
  7. bool coil = false;
  8. void setup() {
  9.   Serial.begin(9600);
  10.   mySerial.begin(9600);
  11.   mb.begin(&mySerial);
  12.   mb.setBaudrate(9600);
  13.   mb.slave(2);    //ID Slave = 1
  14.   mb.addCoil(0);  //alamat Coil=0 untuk LED Slave
  15.   mb.addCoil(1);  //alamat Coil=1 untuk LED Master
  16.   pinMode(2, INPUT_PULLUP);
  17.   pinMode(6, OUTPUT);
  18. }
  19. void loop() {
  20.   mb.task();
  21.   int tekan = !digitalRead(2);  //ditekan=1, dilepas=0
  22.   if (tekan != awal) {
  23.     if (tekan) {
  24.       mb.Coil(1, true);  //jika ditekan, Coil[1] = T
  25.       Serial.println("LED2 U1 ON");
  26.     } else {
  27.       mb.Coil(1, false);  //jika dilepas, Coil[1] = F
  28.       Serial.println("LED2 U1 OFF");
  29.     }
  30.   }
  31.   awal = tekan;
  32.   if (millis() - skr > 100) {
  33.     if (mb.Coil(0) != coil) {
  34.       if (mb.Coil(0)) {
  35.         digitalWrite(6, HIGH);  //jika 1, LED nyala
  36.         Serial.println("LED U3 ON");
  37.       } else {
  38.         digitalWrite(6, LOW);  //jika 0, LED padam
  39.         Serial.println("LED U3 OFF");
  40.       }
  41.     }
  42.     coil = mb.Coil(0);
  43.     skr = millis();
  44.   }
  45. }

Keterangan Program:
  • Kode program di atas memerlukan library modbus-esp8266.
  • Kode_Program_07 untuk U1 sebagai Master, Kode_Program_08 untuk U2 sebagai Slave1 dan Kode_Program_09 untuk U3 sebagai Slave2.
  • Setiap Virtual Terminal (sama seperti Serial Monitor di Arduino IDE) akan menampilkan data kondisi LED; Untuk kondisi LED di U1 ditampilkan di VT1, kondisi LED di U2 ditampilkan di VT2, dan kondisi LED di U3 ditampilkan di VT3. Khusus VT1, selain menampilkan kondisi LED, juga menampilkan kondisi tombol di U2 dan U3.
  • Untuk menyalakan LED di U2, selain dengan menekan tombol pertama di U1, juga bisa dilakukan dengan mengetikkan "a1" diikuti Enter di Virtual Terminal VT1. Sedangkan untuk mematikan LED di U2 tersebut, selain dengan melepas tombol pertama di U1, juga bisa dilakukan dengan mengetikkan "a0" diikuti Enter di Virtual Terminal VT1.
  • Untuk menyalakan LED di U3, selain dengan menekan tombol kedua di U1, juga bisa dilakukan dengan mengetikkan "b1" diikuti Enter di Virtual Terminal VT1. Sedangkan untuk mematikan LED di U3 tersebut, selain dengan melepas tombol kedua di U1, juga bisa dilakukan dengan mengetikkan "b0" diikuti Enter di Virtual Terminal VT1.
  • Virtual Terminal VT2 dan VT3 tidak bisa digunakan untuk mengontrol LED di U1, karena U2 dan U3 sebagai Slave, yang hanya bisa merespon permintaan dari Master. Jadi hanya Virtual Terminal VT1 saja yang bisa digunakan untuk mengontrol LED, karena VT1 terhubung dengan Master.
  • Kode_Program_07, Kode_Program_08 dan Kode_Program_09 dapat diunduh di bagian akhir tulisan ini.
3. Kompilasi Kode_Program_07, isikan lokasi file Hex hasil kompilasi di komponen ATmega328 U1. Berikutnya, kompilasi Kode_Program_08, isikan lokasi file Hex hasil kompilasi di komponen ATmega328 U2. Terakhir, kompilasi Kode_Program_09, isikan lokasi file Hex hasil kompilasi di komponen ATmega328 U3.

4. Jalankan simulasi Proteus dengan menekan tombol Run. Perhatikan beberapa hal berikut ini: 
  • Setiap kali tombol pertama di U1 ditekan, LED di U2 menyala dan VT1 menampilkan tulisan "LED U2 ON", dan ketika tombol pertama di U1 tersebut dilepas, LED di U2 padam dan VT1 menampilkan tulisan "LED U2 OFF". 
  • Selain dengan menekan tombol pertama di U1, LED di U2 bisa dinyalakan dengan mengetikkan "a1" diikuti Enter di VT1, dan dipadamkan dengan mengetikkan "a0" diikuti Enter di VT1. 
  • Begitu pula, ketika tombol kedua di U1 ditekan, LED di U3 menyala dan VT1 menampilkan tulisan "LED U3 ON", dan ketika tombol kedua di U1 tersebut dilepas, LED di U3 padam dan VT1 menampilkan tulisan "LED U3 OFF". 
  • Selain dengan menekan tombol kedua di U1, LED di U3 bisa dinyalakan dengan mengetikkan "b1" diikuti Enter di VT1, dan dipadamkan dengan mengetikkan "b0" diikuti Enter di VT1. 
  • Kemudian ketika tombol di U2 ditekan, LED pertama di U1 menyala, VT1 menampilkan tulisan "TOMBOL U2 ON" dan VT2 menampilkan tulisan "LED1 U1 ON", dan ketika tombol di U2 tersebut dilepas, LED pertama di U1 padam, VT1 menampilkan tulisan "TOMBOL U2 OFF" dan VT2 menampilkan tulisan "LED1 U1 OFF". 
  • Begitu pula, ketika tombol di U3 ditekan, LED kedua di U1 menyala, VT1 menampilkan tulisan "TOMBOL U3 ON" dan VT3 menampilkan tulisan "LED2 U1 ON", dan ketika tombol di U3 tersebut dilepas, LED kedua di U1 padam, VT1 menampilkan tulisan "TOMBOL U3 OFF" dan VT3 menampilkan tulisan "LED2 U1 OFF". 
Gambar 9. Hasil simulasi komunikasi Modbus RTU antara Master (U1) dengan Slave1 (U2) dan Slave2 (U3), di mana Master dapat membaca kondisi tombol dan LED di setiap Slave, dan dapat mengontrol kondisi LED di setiap Slave, baik melalui tombol maupun melalui Virtual Terminal

Dari Gambar 9 di atas, terlihat bahwa komunikasi Modbus RTU antara Master dengan kedua Slave telah berjalan dengan baik. Berikutnya, di Tahap 6, ditunjukkan penggunaan Outseal Nano dan Outseal Mega sebagai alternatif Modbus Slave.

========================================================================================

6. Komunikasi Modbus RTU antara Arduino Uno/Nano dengan  Outseal Nano dan Outseal Mega + Serial Monitor

Berikut ini langkah-langkahnya:
1. Gunakan rangkaian berikut ini.

Gambar 10. Rangkaian untuk komunikasi Modbus RTU antara Master (U1) dengan 3 Slave yang terdiri dari Arduino Nano (U2), Outseal Nano (U3) dan Outseal Mega (U4)

Keterangan rangkaian:
  • Rangkaian Gambar 10 di atas menghubungkan Master (Arduino Uno/Nano) dengan 3 Slave. Slave pertama diprogram dengan Arduino IDE, sedangkan Slave kedua (Outseal Nano) dan Slave ketiga (Outseal Mega) diprogram dengan Outseal Studio. 
  • Virtual Terminal hanya terhubung dengan Master dan Slave pertama, karena Outseal Nano dan Outseal Mega hanya menyediakan Modbus RTU, di mana datanya berbentuk biner.
  • Penempatan kaki input dan kaki output pada Outseal Nano dan Outseal Mega telah dibuat baku atau tetap. Dalam contoh di sini, tombol di Outseal Nano dan Outseal Mega terhubung ke kaki input S.5, sedangkan untuk LED di Outseal Nano dan Outseal Mega, terhubung ke kaki output R4. 
  • Karena ada 3 Slave, maka di Master tersedia 3 buah tombol dan 3 buah LED, yang digunakan untuk mengontrol LED di ketiga Slave dan menampilkan kondisi tombol di ketiga Slave dalam bentuk indikator LED.
  • Rekan-rekan dapat mengunduh file Proteus Gambar 10 di atas di bagian akhir tulisan ini. 

2. Buat kode program berikut ini. Ada 4 buah program; Kode_Program_10 untuk Master, Kode_Program_11 untuk Slave1, Kode_Program_12 untuk Slave2 dan Kode_Program_13 untuk Slave3.

Kode_Program_10:
  1. #include <ModbusRTU.h>
  2. #include <SoftwareSerial.h>
  3. SoftwareSerial mySerial(8, 9);  // RX, TX
  4. ModbusRTU mb;
  5. unsigned long skr = 0;
  6. bool coil1[2];
  7. bool coil2[2];
  8. bool coil3[2];
  9. bool c1 = false;
  10. bool c2 = false;
  11. bool c3 = false;
  12. int awal1 = 0;
  13. int awal2 = 0;
  14. int awal3 = 0;
  15. void setup() {
  16.   Serial.begin(9600);
  17.   mySerial.begin(9600);
  18.   mb.begin(&mySerial);
  19.   mb.setBaudrate(9600);
  20.   mb.master();
  21.   pinMode(2, INPUT_PULLUP);
  22.   pinMode(3, INPUT_PULLUP);
  23.   pinMode(4, INPUT_PULLUP);
  24.   pinMode(5, OUTPUT);
  25.   pinMode(6, OUTPUT);
  26.   pinMode(7, OUTPUT);
  27.   mb.readCoil(1, 0, coil1, 1);
  28.   kirimRequest();
  29.   delay(1000);
  30. }
  31. void loop() {
  32.   int tekan1 = !digitalRead(2);  //ditekan=1, dilepas=0
  33.   if (tekan1 != awal1) {
  34.     if (tekan1) {
  35.       mb.writeCoil(1, 0, true);  //jika ditekan, kirim T
  36.       kirimRequest();
  37.       Serial.println("LED U2 ON");
  38.     } else {
  39.       mb.writeCoil(1, 0, false);  //jika dilepas, kirim F
  40.       kirimRequest();
  41.       Serial.println("LED U2 OFF");
  42.     }
  43.   }
  44.   awal1 = tekan1;
  45.   int tekan2 = !digitalRead(3);  //ditekan=1, dilepas=0
  46.   if (tekan2 != awal2) {
  47.     if (tekan2) {
  48.       mb.writeCoil(2, 128, true);  //jika ditekan, kirim T
  49.       kirimRequest();
  50.       Serial.println("LED U3 ON");
  51.     } else {
  52.       mb.writeCoil(2, 128, false);  //jika dilepas, kirim F
  53.       kirimRequest();
  54.       Serial.println("LED U3 OFF");
  55.     }
  56.   }
  57.   awal2 = tekan2;
  58.   int tekan3 = !digitalRead(4);  //ditekan=1, dilepas=0
  59.   if (tekan3 != awal3) {
  60.     if (tekan3) {
  61.       mb.writeCoil(3, 128, true);  //jika ditekan, kirim T
  62.       kirimRequest();
  63.       Serial.println("LED U4 ON");
  64.     } else {
  65.       mb.writeCoil(3, 128, false);  //jika dilepas, kirim F
  66.       kirimRequest();
  67.       Serial.println("LED U4 OFF");
  68.     }
  69.   }
  70.   awal3 = tekan3;
  71.   if (millis() - skr > 100) {
  72.     mb.readCoil(1, 1, coil1, 1);  //baca coil di ID=1, indek=1
  73.     kirimRequest();
  74.     if (coil1[0] != c1) {
  75.       if (coil1[0]) {
  76.         digitalWrite(5, HIGH);  //jika 1, LED nyala
  77.         Serial.println("TOMBOL U2 ON");
  78.       } else {
  79.         digitalWrite(5, LOW);  //jika 0, LED padam
  80.         Serial.println("TOMBOL U2 OFF");
  81.       }
  82.     }
  83.     c1 = coil1[0];
  84.     mb.readCoil(2, 129, coil2, 1);  //baca coil di ID=1, indek=1
  85.     kirimRequest();
  86.     if (coil2[0] != c2) {
  87.       if (coil2[0]) {
  88.         digitalWrite(6, HIGH);  //jika 1, LED nyala
  89.         Serial.println("TOMBOL U3 ON");
  90.       } else {
  91.         digitalWrite(6, LOW);  //jika 0, LED padam
  92.         Serial.println("TOMBOL U3 OFF");
  93.       }
  94.     }
  95.     c2 = coil2[0];
  96.     mb.readCoil(3, 129, coil3, 1);  //baca coil di ID=1, indek=1
  97.     kirimRequest();
  98.     if (coil3[0] != c3) {
  99.       if (coil3[0]) {
  100.         digitalWrite(7, HIGH);  //jika 1, LED nyala
  101.         Serial.println("TOMBOL U4 ON");
  102.       } else {
  103.         digitalWrite(7, LOW);  //jika 0, LED padam
  104.         Serial.println("TOMBOL U4 OFF");
  105.       }
  106.     }
  107.     c3 = coil3[0];
  108.     skr = millis();
  109.   }
  110. }
  111. void kirimRequest() {
  112.   while (mb.slave()) {
  113.     mb.task();
  114.     delay(10);
  115.   }
  116. }
  117. void serialEvent() {
  118.   while (Serial.available()) {
  119.     char ketik1 = Serial.read();
  120.     int ketik2 = Serial.parseInt();
  121.     if (Serial.read() == char(13)) {
  122.       if (ketik1 == 'a') {
  123.         if (ketik2) {
  124.           mb.writeCoil(1, 0, true);  //jika diketik 1, kirim T
  125.           kirimRequest();
  126.           Serial.println("LED U2 ON");
  127.         } else {
  128.           mb.writeCoil(1, 0, false);  //jika diketik 0, kirim F
  129.           kirimRequest();
  130.           Serial.println("LED U2 OFF");
  131.         }
  132.       } else if (ketik1 == 'b') {
  133.         if (ketik2) {
  134.           mb.writeCoil(2, 128, true);  //jika diketik 1, kirim T
  135.           kirimRequest();
  136.           Serial.println("LED U3 ON");
  137.         } else {
  138.           mb.writeCoil(2, 128, false);  //jika diketik 0, kirim F
  139.           kirimRequest();
  140.           Serial.println("LED U3 OFF");
  141.         }
  142.       } else if (ketik1 == 'c') {
  143.         if (ketik2) {
  144.           mb.writeCoil(3, 128, true);  //jika diketik 1, kirim T
  145.           kirimRequest();
  146.           Serial.println("LED U4 ON");
  147.         } else {
  148.           mb.writeCoil(3, 128, false);  //jika diketik 0, kirim F
  149.           kirimRequest();
  150.           Serial.println("LED U4 OFF");
  151.         }
  152.       }
  153.     }
  154.   }
  155. }

Kode_Program_11:
  1. #include <ModbusRTU.h>
  2. #include <SoftwareSerial.h>
  3. SoftwareSerial mySerial(8, 9);  // RX, TX
  4. ModbusRTU mb;
  5. unsigned long skr = 0;
  6. int awal = 0;
  7. bool coil = false;
  8. void setup() {
  9.   Serial.begin(9600);
  10.   mySerial.begin(9600);
  11.   mb.begin(&mySerial);
  12.   mb.setBaudrate(9600);
  13.   mb.slave(1);    //ID Slave = 1
  14.   mb.addCoil(0);  //alamat Coil=0 untuk LED Slave
  15.   mb.addCoil(1);  //alamat Coil=1 untuk LED Master
  16.   pinMode(2, INPUT_PULLUP);
  17.   pinMode(6, OUTPUT);
  18. }
  19. void loop() {
  20.   mb.task();
  21.   int tekan = !digitalRead(2);  //ditekan=1, dilepas=0
  22.   if (tekan != awal) {
  23.     if (tekan) {
  24.       mb.Coil(1, true);  //jika ditekan, Coil[1] = T
  25.       Serial.println("LED1 U1 ON");
  26.     } else {
  27.       mb.Coil(1, false);  //jika dilepas, Coil[1] = F
  28.       Serial.println("LED1 U1 OFF");
  29.     }
  30.   }
  31.   awal = tekan;
  32.   if (millis() - skr > 100) {
  33.     if (mb.Coil(0) != coil) {
  34.       if (mb.Coil(0)) {
  35.         digitalWrite(6, HIGH);  //jika 1, LED nyala
  36.         Serial.println("LED U2 ON");
  37.       } else {
  38.         digitalWrite(6, LOW);  //jika 0, LED padam
  39.         Serial.println("LED U2 OFF");
  40.       }
  41.     }
  42.     coil = mb.Coil(0);
  43.     skr = millis();
  44.   }
  45. }

Kode_Program_12:

Gambar 11. Program untuk Outseal Nano di Outseal Studio, pastikan Settings Hardware = Nano V5, Baudrate = 9600 dan Alamat Modbus = 2

Kode_Program_13:

Gambar 12. Program untuk Outseal Mega di Outseal Studio, pastikan Settings Hardware = Mega V3, Baudrate = 9600 dan Alamat Modbus = 3

Keterangan Program:
  • Kode_Program_10 dan Kode_Program_11 di atas memerlukan library modbus-esp8266.
  • Kode_Program_10 hampir sama dengan Kode_Program_07, hanya di sini diberi tambahan untuk pengaturan Slave3.
  • Kode_Program_11 sama dengan Kode_Program_08.
  • Kode_Program_12 dan Kode_Program_13 menggunakan Outseal Studio. Kedua program tersebut sama persis, hanya berbeda pada Settings. Bandingkan Kode_Program_12 dan Kode_Program_13 dengan Kode_Program_11. Tampak kedua program Outseal tersebut lebih sederhana bila dibandingkan dengan Kode_Program_11, sekalipun ketiganya sama-sama program untuk membuat Modbus Slave. Hanya kelemahan di Outseal Studio, tidak tersedia fitur untuk komunikasi serial UART, sehingga datanya tidak bisa ditampilkan di Virtual Terminal.
  • Untuk Kode_Program_12, pastikan Settings di Outseal Studio diatur ke Hardware = Nano V5, Baudrate = 9600, dan Alamat Modbus Slave = 2. Untuk Kode_Program_13, pastikan Settings di Outseal Studio diatur ke Hardware = Mega V3, Baudrate = 9600, dan Alamat Modbus Slave = 3.
  • Tekan tombol Test untuk meng-kompilasi program di Outseal Studio. Apabila kompilasi berhasil, akan muncul tulisan "Tidak terdapat kesalahan", dan hasil kompilasi Outseal Studio ini akan selalu bernama Hasil.hex, dengan lokasi yang selalu sama, yaitu berada di direktori: C:\\Users\nama_User\Local\AppData\Temp\Outseal\Hasil.hex.
  • Kode_Program_10 sampai Kode_Program_13 di atas dapat diunduh di bagian akhir dari tulisan ini.
3. Kompilasi Kode_Program_10, isikan lokasi file Hex hasil kompilasi di komponen ATmega328 U1. Berikutnya, kompilasi Kode_Program_11, isikan lokasi file Hex hasil kompilasi di komponen ATmega328 U2. Kemudian kompilasi Kode_Program_12, isikan lokasi file Hex hasil kompilasi di komponen ATmega328 U3. Terakhir kompilasi Kode_Program_13, isikan lokasi file Hex hasil kompilasi di komponen ATmega128 U4.

4. Jalankan simulasi Proteus dengan menekan tombol Run. Perhatikan beberapa hal berikut ini: 
  • Setiap kali tombol pertama di U1 ditekan, LED di U2 menyala dan VT1 menampilkan tulisan "LED U2 ON", dan ketika tombol pertama di U1 tersebut dilepas, LED di U2 padam dan VT1 menampilkan tulisan "LED U2 OFF". 
  • Selain dengan menekan tombol pertama di U1, LED di U2 bisa dinyalakan dengan mengetikkan "a1" diikuti Enter di VT1, dan dipadamkan dengan mengetikkan "a0" diikuti Enter di VT1. 
  • Begitu pula, ketika tombol kedua di U1 ditekan, LED di U3 menyala dan VT1 menampilkan tulisan "LED U3 ON", dan ketika tombol kedua di U1 tersebut dilepas, LED di U3 padam dan VT1 menampilkan tulisan "LED U3 OFF". 
  • Selain dengan menekan tombol kedua di U1, LED di U3 bisa dinyalakan dengan mengetikkan "b1" diikuti Enter di VT1, dan dipadamkan dengan mengetikkan "b0" diikuti Enter di VT1. 
  • Begitu pula, ketika tombol ketiga di U1 ditekan, LED di U4 menyala dan VT1 menampilkan tulisan "LED U4 ON", dan ketika tombol ketiga di U1 tersebut dilepas, LED di U4 padam dan VT1 menampilkan tulisan "LED U4 OFF". 
  • Selain dengan menekan tombol kedua di U1, LED di U3 bisa dinyalakan dengan mengetikkan "c1" diikuti Enter di VT1, dan dipadamkan dengan mengetikkan "c0" diikuti Enter di VT1. 
  • Ketika tombol di U2 ditekan, LED pertama di U1 menyala, VT1 menampilkan tulisan "TOMBOL U2 ON" dan VT2 menampilkan tulisan "LED1 U1 ON", dan ketika tombol di U2 tersebut dilepas, LED pertama di U1 padam, VT1 menampilkan tulisan "TOMBOL U2 OFF" dan VT2 menampilkan tulisan "LED1 U1 OFF". 
  • Ketika tombol di U3 ditekan, LED kedua di U1 menyala dan VT1 menampilkan tulisan "TOMBOL U3 ON", dan ketika tombol di U3 tersebut dilepas, LED kedua di U1 padam dan VT1 menampilkan tulisan "TOMBOL U3 OFF. 
  • Ketika tombol di U4 ditekan, LED ketiga di U1 menyala dan VT1 menampilkan tulisan "TOMBOL U4 ON", dan ketika tombol di U4 tersebut dilepas, LED ketiga di U1 padam dan VT1 menampilkan tulisan "TOMBOL U4 OFF. 
Gambar 13. Hasil simulasi komunikasi Modbus RTU antara Master (U1) dengan 3 buah Slave (U2, U3 dan U4), di mana Master dapat membaca kondisi tombol dan LED di setiap Slave, dan dapat mengontrol kondisi LED di setiap Slave, baik melalui tombol maupun melalui Virtual Terminal

Dari Gambar 13 di atas, terlihat bahwa komunikasi Modbus RTU antara Master dengan ketiga Slave telah berjalan dengan baik. Sampai di sini pembahasan mengenai komunikasi Modbus RTU sederhana menggunakan program Arduino dan Outseal Studio.

Catatan: Program Ladder Diagram Outseal Studio tampak lebih sederhana dibandingkan dengan program teks Arduino. Bagi rekan-rekan yang menyukai kesederhanaan, saya rekomendasikan menggunakan Outseal Studio. Sebenarnya sudah tersedia juga instruksi untuk Modbus Master, yaitu instruksi MF1 hingga MF16, hanya saja, saya belum menemukan pengaturan waktu penerimaan respon yang tepat, sehingga tidak dapat saya tampilkan di sini.

Catatan Tambahan: Untuk implementasi rangkaian secara riil, sebaiknya gunakan modul MAX485 dengan tipe auto-flow, agar tidak repot menambahkan kontrol pada kaki RE-DE. Untuk lebih jelasnya, Gambar 14 berikut ini menunjukkan 2 modul MAX485 yang ada di pasaran. Pilih modul MAX485 yang sebelah kanan (b), yang memiliki kontrol otomatis RE-DE (kaki RE-DE untuk pengaturan arah data).

Gambar 14. Pilih modul yang b agar tidak repot menambahkan kontrol pada kaki RE-DE


Agar lebih jelas, berikut ini video hasil simulasi dari tahap 1 sampai tahap 6:

Video hasil simulasi tahap 1 sampai tahap 6

No comments:

Post a Comment