0% found this document useful (0 votes)
82 views

As511 DB48 DW0

The document discusses transferring a dataword from an Arduino Mega equipped with an LCD display and buttons to a Siemens Simatic S5 PLC. It describes using an AS511 protocol to transfer the data over a serial connection, which can be monitored using third-party software to confirm communications. The code included initializes libraries, defines variables for the LCD, buttons, and data transfer. It also handles reading buttons, updating the displayed setpoint value, and transferring the value to the PLC when the select button is pressed.

Uploaded by

Edo Ross
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
82 views

As511 DB48 DW0

The document discusses transferring a dataword from an Arduino Mega equipped with an LCD display and buttons to a Siemens Simatic S5 PLC. It describes using an AS511 protocol to transfer the data over a serial connection, which can be monitored using third-party software to confirm communications. The code included initializes libraries, defines variables for the LCD, buttons, and data transfer. It also handles reading buttons, updating the displayed setpoint value, and transferring the value to the PLC when the select button is pressed.

Uploaded by

Edo Ross
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

/* Siemens Simatic S5 PLC transfer of a dataword from an Arduino Mega equipped with an Adafruit 2*16 LCD display with

5 integrated buttons.
* The transfer protocol used is based on the document as511protocol_description by Luca Gallina dated March 17, 2004.
* Also, by modifying the "standard" TTY/RS232 converter found by our friend Google, a third computer can monitor the serial communications
* between the programming unit (I.E the computer running S5) and the PLC AS511 PG port by using for example an excellent freeware called
* Realterm, to confirm proper communications to/from the PLC.
* All information shared within, as is and with no strings attached whatsoever, is already available for download elsewhere on the WWW.
* Standard disclaimer applies, I.E use at your own risk. If TSHTF = Don't come crying to me.
*
*/

#include <Wire.h> // Standard Adafruit libraries etc as found on the interwebs.


#include <Adafruit_RGBLCDShield.h> // I'm sure there's variables etc that which aren't used at all
#include <utility/Adafruit_MCP23017.h> // in this sketch, but I'm too lazy to clean it up.
#include <EEPROM.h> // Also, WYSIWYG. Done in a haste with no structure whatsoever,
#include <SPI.h> // littered with makeshift variables, loops, etc etc.
#include <SD.h> // I'm sure hackers would have a field day boiling down this code
#include <LiquidCrystal.h> // to a few intricate loops using arrays and such, good for them.
// Besides, if it ain't broken, don't try to fix it, right....? ;)
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();

#define WHITE 0x7

int back = 0;
int old = 0;
int uld = 0;
int cur = 0;
int inhib = 0;
int buff[255];
int x = 0;
int y = 0;
int lsb = EEPROM.read(50);
int msb = EEPROM.read(55);
int outp = 0;
int setp = 0;
int contr[19] = {16, 6, 2, 22, 16, 3, 16, 6, 2, 9, 16, 16, 6, 16, 6, 2, 18, 16, 3};
int xsum = 0;
long timeout = 0;
bool brejk = false;
bool AS511_tx_rx = false;
bool up = false;
bool down = false;
bool left = false;
bool right = false;
bool kok = false;

void MAX(){
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Max value =");
lcd.setCursor(0, 1);
lcd.print("32767");
lcd.noCursor(); }

void basic0(){
lcd.setCursor(0, 0);
lcd.print("Setpoint");}

void basic1(){
lcd.setCursor(0, 1);
lcd.print("Input");}

void plcnofun(){
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("PLC not");
lcd.setCursor(0, 1);
lcd.print("responding");
delay(250);
lcd.clear();
delay(150); }

void again(){
lcd.clear();
basic0();
if (outp <=9){ cur = 15; }
if (outp <=99 && outp >= 10){ cur = 14; }
if (outp <=999 && outp >= 100){ cur = 13; }
if (outp <=9999 && outp >= 1000){ cur = 12; }
if (outp <=32767 && outp >= 10000){ cur = 11; }
lcd.setCursor(cur,0);
lcd.print(outp);
basic1();
if (setp <=9){ cur = 15; }
if (setp <=99 && setp >= 10){ cur = 14; }
if (setp <=999 && setp >= 100){ cur = 13; }
if (setp <=9999 && setp >= 1000){ cur = 12; }
if (setp <=32767 && setp >= 10000){ cur = 11; }
lcd.setCursor(cur,1);
lcd.print(setp);
}

void setup() {

Serial.begin(9600,SERIAL_8E1); // Serial protocol for AS511 = 9600 Baud, 8 databits,


Serial3.begin(9600,SERIAL_8E1); // 1 stopbit, even parity.
lcd.begin(16, 2);
int time = millis();
lcd.setBacklight(WHITE); // Power on startup delay, just to make sure the PLC
lcd.setCursor(0, 0); // is fit for fight. This of course indicates that the
lcd.print("Some company ad"); // Arduino and the PLC are powered up simultaneously.
lcd.setCursor(0, 1); // Also, for any practical measures the startup delay
lcd.print("or whatever...."); // could just as well be 10 seconds long, or whatever
delay(3000); // fits the bill.
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Additional");
lcd.setCursor(0, 1);
lcd.print("information");
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Short tutorial");
lcd.setCursor(0, 1);
lcd.print("perhaps...");
lcd.noCursor();
delay(3000);
lcd.clear();
basic0();
if (msb == 255){ msb = 0; lsb=1; setp = 1; outp = 1; } // Sets minimum setpoint to 1 in case the onboard (Mega)
outp = 256 * msb + lsb; // EEPROM is empty, which most probably likely is the case
setp = outp; // whenever you use a brand new Mega.
if (outp <=9){ cur = 15; }
if (outp <=99 && outp >= 10){ cur = 14; }
if (outp <=999 && outp >= 100){ cur = 13; }
if (outp <=9999 && outp >= 1000){ cur = 12; }
if (outp <=32767 && outp >= 10000){ cur = 11; }
lcd.setCursor(cur,0);
lcd.print(outp);
basic1();
lcd.setCursor(cur,1);
lcd.print(setp);
cur = 15;

uint8_t i=0;

void loop() {

uint8_t buttons = lcd.readButtons();

if (buttons && !AS511_tx_rx) { // LCD button reading and data preparing/manipulating


// of the setpoint, which is adapted to fit the range
if (buttons & BUTTON_UP) { // from 0 to 32767, which in turn is the range the
// application in the PLC is working with (in this case).
lcd.noCursor();
up = true;
if (inhib < 1) {
if (cur == 15) { if ((setp + 1) <= 32767) { setp = setp + 1; }}
if (cur == 14) { if ((setp + 10) <= 32767) { setp = setp + 10; }}
if (cur == 13) { if ((setp + 100) <= 32767) { setp = setp + 100; }}
if (cur == 12) { if ((setp + 1000) <= 32767) { setp = setp + 1000; }}
if (cur == 11) { if ((setp + 10000) <= 32767) { setp = setp + 10000; }}
if (setp >=32767) { setp = 32767; }
if (setp <0) { setp = 32767; }
if (setp >=0 and setp <=9) { lcd.setCursor(11,1); lcd.print(" "); lcd.setCursor(15,1); }
if (setp >=10 and setp <=99) { lcd.setCursor(11,1); lcd.print(" "); lcd.setCursor(14,1); }
if (setp >=100 and setp <=999) { lcd.setCursor(11,1); lcd.print(" "); lcd.setCursor(13,1); }
if (setp >=1000 and setp <=9999) { lcd.setCursor(11,1); lcd.print(" "); lcd.setCursor(12,1); }
if (setp >=10000 and setp <=32767) { lcd.setCursor(11,1); }
lcd.print(setp);
inhib = 1; }
}
if (buttons & BUTTON_DOWN) {

lcd.noCursor();
down = true;
if (inhib < 1) {
if (cur == 15) { if ((setp - 1) > 0) { setp = setp - 1; }}
if (cur == 14) { if ((setp - 10) > 0) {setp = setp - 10; }}
if (cur == 13) { if ((setp - 100) > 0) {setp = setp - 100; }}
if (cur == 12) { if ((setp - 1000) > 0) {setp = setp - 1000; }}
if (cur == 11) { if ((setp - 10000) > 0) {setp = setp - 10000; }}
if (setp >=0 and setp <=9) { lcd.setCursor(11,1); lcd.print(" "); lcd.setCursor(15,1); }
if (setp >=10 and setp <=99) { lcd.setCursor(11,1); lcd.print(" "); lcd.setCursor(14,1); }
if (setp >=100 and setp <=999) { lcd.setCursor(11,1); lcd.print(" "); lcd.setCursor(13,1); }
if (setp >=1000 and setp <=9999) { lcd.setCursor(11,1); lcd.print(" "); lcd.setCursor(12,1); }
if (setp >=10000 and setp <=32767) { lcd.setCursor(11,1); }
if (setp <=0) { setp = 1; lcd.setCursor (15,1); lcd.print(setp); }
if (setp > 0) { lcd.print(setp); }
inhib = 1; }
}

if (buttons & BUTTON_LEFT) {

lcd.noCursor();
left = true;
if (inhib < 1) {
if (cur > 11) {
cur = cur - 1; }
else { MAX();
delay(2000);
again();
cur = 11;
lcd.cursor();
lcd.setCursor(cur,1);
}
inhib = 1; }
}

if (buttons & BUTTON_RIGHT) {

lcd.noCursor();
right = true;
if (inhib < 1) {
if (cur < 15) {
cur = cur + 1; }
inhib = 1; }
}

if (buttons & BUTTON_SELECT & (up || down || left || right || kok)) {

lcd.noCursor(); // Pressing Select initiates transfer of the setpoint to the


PLC.
lcd.setCursor(cur,1);
left = false;
right = false;
up = false;
down = false;
kok = false;
back = outp;
uld = setp;
EEPROM.write(50,lsb); // The setpoint is stored in the onboard EEPROM from which
EEPROM.write(55,msb); // it's also fetched during powerup.
AS511_tx_rx = true;
inhib = 1;

}
}

else { inhib = 0;
if (up || down || left || right) { lcd.cursor(); lcd.setCursor(cur,1); }
}

if (AS511_tx_rx) { // As the data to the PLC is sent using serial port 3 we can
// use the native serial monitor to check both what we send
// and what we receive to/from the PLC.

if (x == 0){
Serial3.write(2); // Send #02 (STX) to the PLC. This is a general AS511 code used
Serial.println("PG: 02"); // to prepare the PLC for what we're about to do, which can be to
x=x+1; } // either read or write some data to/from it, or to initiate some
// command or what have you.
// In this particular application we want to use the LCD's
// buttons to change the value of a setpoint and then send
// it to the PLC.
// The setpoint is written to DB48/DW0 in the PLC.

if (x == 1){
if (Serial3.available() >=2 ) { // Get confirmation from the PLC, we're expecting to receive
for (int y=0; y<2; y++) { // #10 (DLE) and #06 (ACK) as a token of "message received and
buff[y] = Serial3.read(); } // understood" from the PLC, basically indicating that the PLC is
Serial.print("AG: "); // ready to do what we ask of it.
for (int y=0; y<2; y++) {
if (buff[y]<=9){ Serial.print("0");}
Serial.print(buff[y], HEX);
Serial.print(" ");}
Serial.println();
x=x+1; timeout = 0;} // All the receive sequences (data coming from the PLC to the
// Arduino) have a timeout safeguard preventing the Arduino from
// freezing.
else { timeout=timeout + 1;
if (timeout >= 50){ brejk=true; x=17; }
}
}

if (x == 2){
Serial3.write(8); // Send #08 (B_OWRITE) to the PLC. This tells the PLC that
Serial.println("PG: 08"); // we're about to overwrite, in this case, DB48/DW0, which
x=x+1; } // is where the PLC in this application reads it's data from
// with regards to whatever changes in operation we want the
// PLC to execute.

if (x == 3){
if (Serial3.available() >=1 ) { // Expecting to receive #02 (STX) from the PLC.
buff[2] = Serial3.read();
Serial.print("AG: ");
if (buff[2]<=9){ Serial.print("0");}
Serial.print(buff[2], HEX);
Serial.println();
x=x+1; timeout = 0;}

else { timeout=timeout + 1;
if (timeout >= 50){ brejk=true; x=17; }
}
}
if (x == 4){
Serial3.write(16); // Send #10 (DLE) & #06 (ACK) to the PLC, all according
Serial3.write(6); // to the AS511 protocol.
Serial.println("PG: 10 06");
x=x+1; }

if (x == 5){
if (Serial3.available() >=3 ) { // Expecting #16, #10 and #03 from the PLC.
for (int y=3; y<6; y++) {
buff[y] = Serial3.read(); }
Serial.print("AG: ");
for (int y=3; y<6; y++) {
if (buff[y]<=9){ Serial.print("0"); }
Serial.print(buff[y], HEX);
Serial.print(" ");}
Serial.println();
x=x+1; timeout = 0;}

else { timeout=timeout + 1;
if (timeout >= 50){ brejk=true; x=17; }
}
}

if (x == 6){
if (brejk){ x=17; }
Serial3.write(16); // Preparing the PLC for overwriting the data in DB48/DW0.
Serial3.write(6);
Serial3.write(1);
Serial3.write(48); // Pointing at DB48.
Serial3.write(0);
Serial3.write(6);
Serial3.write(16);
Serial3.write(3);
x=x+1;
Serial.println("PG: 10 06 01 30 00 06 10 03"); }

if (x == 7){
if (Serial3.available() >=3 ) { // Expecting #10, #06 and #02 from the PLC.
for (int y=6; y<9; y++) {
buff[y] = Serial3.read(); }
Serial.print("AG: ");
for (int y=6; y<9; y++) {
if (buff[y]<=9){ Serial.print("0"); }
Serial.print(buff[y], HEX);
Serial.print(" ");}
Serial.println();
x=x+1; timeout = 0;}

else { timeout=timeout + 1;
if (timeout >= 50){ brejk=true; x=17; }
}
}

if (x == 8){
Serial3.write(16); // Another #10 & #06 to the PLC.
Serial3.write(6);
Serial.println("PG: 10 06");
x=x+1; }

if (x == 9){
if (Serial3.available() >=3 ) { // Expecting #09, #10 & #03 from the PLC.
for (int y=9; y<12; y++) {
buff[y] = Serial3.read(); }
Serial.print("AG: ");
for (int y=9; y<11; y++) {
if (buff[y]<=9){ Serial.print("0"); }
Serial.print(buff[y], HEX);
Serial.print(" ");}
Serial.println();
x=x+1; timeout = 0;}

else { timeout=timeout + 1;
if (timeout >= 50){ brejk=true; x=17; }
}
}

if (x == 10){
Serial3.write(16); // Send #10, #06 & #02 to the PLC.
Serial3.write(6);
Serial3.write(2);
Serial.println("PG: 10 06 02");
x=x+1; }

if (x == 11){
if (Serial3.available() >=2 ) { // Expecting #10 & #06 from the PLC.
for (int y=11; y<13; y++) {
buff[y] = Serial3.read(); }
Serial.print("AG: ");
for (int y=11; y<13; y++) {
if (buff[y]<=9){ Serial.print("0"); }
Serial.print(buff[y], HEX);
Serial.print(" ");}
Serial.println();
x=x+1; timeout = 0;}

else { timeout=timeout + 1;
if (timeout >= 50){ brejk=true; x=17; }
}
}

if (x == 12){

Serial3.write(0); // Here's the actual data writing sequence of DB48/DW0.


Serial3.write(112); // As DB48 only consists of 1 dataword (I.E DW0) the total
Serial3.write(112); // length of DB48 is none the less 6 bytes long.
Serial3.write(1);
Serial3.write(48); // Pointing at DB48 again.
Serial3.write(128);
Serial3.write(0);
Serial3.write(0);
Serial3.write(0);
Serial3.write(0);
Serial3.write(6);
msb = int(setp/256);
lsb = int(setp - msb*256);
Serial3.write(msb); // This is the new DL0 written to DB48......
if (msb == 16){ Serial3.write(msb); }
Serial3.write(lsb); // ......and here's DR0, together they form DW0.
if (lsb == 16){ Serial3.write(lsb); } // In case the value of lsb and/or msb is equal to
Serial3.write(16); // #10 (decimal 16) we have to sent 2 consecutive
Serial3.write(4); // values, I.E we have to send #10 twice, as that's
x=x+1; // part of how the AS511 protocol handles the specific
// value of #10 (decimal 16). Go figure.

Serial.print("PG: 00 70 70 01 30 80 00 00 00 00 06 ");
if (msb <= 15){ Serial.print("0"); }
Serial.print(msb, HEX);
Serial.print(" ");
if (msb == 16){
Serial.print(msb, HEX);
Serial.print(" ");}
if (lsb <= 15){ Serial.print("0");}
Serial.print(lsb, HEX);
Serial.print(" ");
if (lsb == 16){
Serial.print(lsb, HEX);
Serial.print(" "); }
Serial.println("10 04"); }

if (x == 13){
if (Serial3.available() >=3 ) { // Expecting #10, #06 & #02 from the PLC.
for (int y=13; y<16; y++) {
buff[y] = Serial3.read(); }
Serial.print("AG: ");
for (int y=13; y<16; y++) {
if (buff[y]<=9){ Serial.print("0"); }
Serial.print(buff[y], HEX);
Serial.print(" ");}
Serial.println();
x=x+1; timeout = 0;}

else { timeout=timeout + 1;
if (timeout >= 50){ brejk=true; x=17; }
}
}

if (x == 14){
Serial3.write(16); // Another #10 & #06 to the PLC.
Serial3.write(6);
Serial.println("PG: 10 06");
x=x+1; }

if (x == 15){
if (Serial3.available() >=3 ) { // Expecting #12, #10 & #03 from the PLC.
for (int y=16; y<19; y++) {
buff[y] = Serial3.read(); }
Serial.print("AG: ");
for (int y=16; y<19; y++) {
if (buff[y]<=9){ Serial.print("0"); }
Serial.print(buff[y], HEX);
Serial.print(" ");}
Serial.println();
x=x+1; timeout = 0;

xsum = 0;
for (int y=0; y<19; y++){ // XOR checking what was reveived from the PLC versus
xsum = xsum + (buff[y] ^ contr[y]);} // what is expected to be received from the PLC during
// a successful B_OWRITE sequence.
if (xsum > 0){ brejk = true; }}

else { timeout=timeout + 1;
if (timeout >= 50){ brejk=true; x=17; }
}
}

if (x == 16){
Serial3.write(16); // Send the last #10 & #06 to the PLC, this ends the TX/RX
Serial3.write(6); // sequence for B_OWRITE for this time.
Serial.println("PG: 10 06"); // As the B_OWRITE sequence was successfull then we print
x=x+1; // the new setpoint on the LCD display, let's the user know
outp = setp; // that the PLC now has a new setpoint to work with.
lcd.noCursor();
if (setp >=0 and setp <=9) { lcd.setCursor(11,0); lcd.print(" "); lcd.setCursor(15,0); }
if (setp >=10 and setp <=99) { lcd.setCursor(11,0); lcd.print(" "); lcd.setCursor(14,0); }
if (setp >=100 and setp <=999) { lcd.setCursor(11,0); lcd.print(" "); lcd.setCursor(13,0); }
if (setp >=1000 and setp <=9999) { lcd.setCursor(11,0); lcd.print(" "); lcd.setCursor(12,0); }
if (setp >=10000 and setp <=32767) { lcd.setCursor(11,0); }
if (setp <=0) { lcd.setCursor (11,0); lcd.print(" 0"); }
if (setp > 0) { lcd.print(setp); }}
msb = int(outp/256);
lsb = int(outp - msb*256);
EEPROM.write(50,lsb);
EEPROM.write(55,msb);

if ((x == 17) && brejk){


x=0; // If, for whatever reason, the B_OWRITE sequence failed to
old = cur; // write new data to DB48/DW0, let the user know this by
AS511_tx_rx = false; // printing an error message on the LCD display.
for(int y=0; y<3; y++) { // Also, revert the setpoint to the old value that which it
plcnofun(); } // had prior to pressing the Select button, that way the user
outp = back; // not only sees the error message but also sees that the new
setp = uld; // setpoint hasn't actually made it to DB48/DW0 and that the
msb = int(outp/256); // old setpoint is still in use in the PLC, I.E DB48/DW0 hasn't
lsb = int(outp - msb*256); // changed any at all.
EEPROM.write(50,lsb);
EEPROM.write(55,msb);
lcd.clear();
basic0();
if (outp <=9){ cur = 15; }
if (outp <=99 && outp >= 10){ cur = 14; }
if (outp <=999 && outp >= 100){ cur = 13; }
if (outp <=9999 && outp >= 1000){ cur = 12; }
if (outp <=32767 && outp >= 10000){ cur = 11; }
lcd.setCursor(cur,0);
lcd.print(outp);
basic1();
if (setp <=9){ cur = 15; }
if (setp <=99 && setp >= 10){ cur = 14; }
if (setp <=999 && setp >= 100){ cur = 13; }
if (setp <=9999 && setp >= 1000){ cur = 12; }
if (setp <=32767 && setp >= 10000){ cur = 11; }
lcd.setCursor(cur,1);
lcd.print(setp);
cur = old;
lcd.cursor();
lcd.setCursor(cur,1);
brejk = false;
kok = true;
timeout = 0; }

if (x == 17){
x=0;
AS511_tx_rx = false;
timeout = 0;
delay(50);
}
}
}

You might also like