Ladder Logic Programming for ESP32 Based Industrial Controllers | Norvi IIOT

by NORVI Controllers in Circuits > Microcontrollers

11492 Views, 7 Favorites, 0 Comments

Ladder Logic Programming for ESP32 Based Industrial Controllers | Norvi IIOT

ladder_iot.png

Ladder logic is one of the most widely used PLC programming languages in the industry. It is a graphical programming language for PLCs that uses symbols that are similar to those used in wiring diagrams. It's simple to understand and use, and has been adopted since the early days of Programmable Logic Controllers.

In this instructable, we'll be looking at the open-source project by Leonardo Fernandes that generates Arduino from code from Ladder Logic, for ESP32 based micro-controller devices. We'll be working with the Norvi IIOT-AE01-R device and making it work for the given conditions by developing the ladder logic using the IoT ladder editor by Leonardo Fernandes.

To understand more about ladder logic programming and the ladder editor - https://github.com/leofds/iot-ladder-editor/blob/main/README_ladder_language.md

Check this tutorial to know more about how to work with Norvi IIOT-AE01-R.

So what do you need to get started?

Operating Scenario

Here, we'll be operating the Norvi controller for the following scenario.

  • If the digital input 1 is triggered for 2 sec or continued, input 1 Latch ON.
  • If the digital input 2 is triggered for 2 sec or continued, input 2 Latch ON.
  • If the digital input 3 is triggered for 2 sec or continued, input 3 Latch ON.
  • when an input 1/ input 2 / input 3 is triggered, relay output 1/ relay output 2/ relay output 3 to turn ON respectively.
  • When input 4 is ON for 1 sec or more, output 1 to turn OFF and output 2 & 3 to flash 2 sec ON and 2 seconds OFF.

GPIOs of the Device

Check steps 4 and 5 in this instructable.

Building the Ladder Logic

ladder_snip.PNG

How to use the software?

To understand the Ladder Language read README_ladder_language.md.

  • Go to Project> Properties>Pin mapping and set the inputs & outputs of the Norvi IIOT AE01-R device correctly.
  • Use drag and drop to place instructions on the Rung and associate input/output variables to instructions.
  • Go to Project>Build to generate the output code.
  • The output file plc.ino will be created in the folder "current folder"/out/plc.
  • Double click to open Arduino IDE.
  • Put the ESP32 board in programming mode.
  • Click on the Update icon on the Arduino IDE to load the program on the ESP32 board.
  • After success, reset the ESP32 board to run the program - Check this tutorial for uploading the code & wiring the device.

Generated Arduino code for the above ladder logic.

// IoT Ladder Editor (0.0.2)//// Copyright (C) 2021  Leonardo Fernandes//// <a href="https://github.com/leofds/iot-ladder-editor" rel="nofollow"> https://github.com/leofds/iot-ladder-editor</a>//// Project: ladder_project#if CONFIG_FREERTOS_UNICORE#define ARDUINO_RUNNING_CORE 0#else#define ARDUINO_RUNNING_CORE 1#endif#define PIN_Q01 14#define PIN_Q02 12#define PIN_Q03 13#define PIN_Q04 15#define PIN_Q05 2#define PIN_Q06 33#define PIN_Q07 26#define PIN_Q08 27#define PIN_I01 18#define PIN_I02 39#define PIN_I03 34#define PIN_I04 35#define PIN_I05 19#define PIN_I06 21#define PIN_I07 22#define PIN_I08 23// Timer structtypedef struct {  int32_t PRE;  int32_t AC;  int32_t B;  int32_t DN;  int32_t EN;  uint64_t TT;} LD_TIMER;union {  uint32_t p[2];  uint64_t v;} LD_TIME;uint64_t getTime(){  return LD_TIME.v;}uint8_t LD_I1 = 0;uint8_t LD_Q1 = 0;uint8_t LD_I2 = 0;uint8_t LD_Q2 = 0;uint8_t LD_I3 = 0;uint8_t LD_Q3 = 0;uint8_t LD_I4 = 0;uint8_t LD_Q4 = 0;LD_TIMER LD_T1;LD_TIMER LD_T2;LD_TIMER LD_T3;LD_TIMER LD_T4;LD_TIMER LD_T5;LD_TIMER LD_T6;LD_TIMER LD_T7;void refreshTime64bit(){  unsigned long now = millis();  if(now < LD_TIME.p[0]){    LD_TIME.p[1]++;  }  LD_TIME.p[0] = now;}void readInputs(){  LD_I1 = digitalRead(PIN_I01);  LD_I2 = digitalRead(PIN_I02);  LD_I3 = digitalRead(PIN_I03);  LD_I4 = digitalRead(PIN_I04);}void writeOutputs(){  digitalWrite(PIN_Q01, LD_Q1);  digitalWrite(PIN_Q02, LD_Q2);  digitalWrite(PIN_Q03, LD_Q3);  digitalWrite(PIN_Q04, LD_Q4);}void rung001(void){  uint8_t _LD_S0;  uint64_t _LD_T1;  uint64_t _LD_T2;  _LD_S0 = 1;  if(LD_I1){    _LD_S0 = 0;  }  LD_T1.EN = _LD_S0;  if(!_LD_S0){    LD_T1.DN = 0;    LD_T1.AC = 0;    LD_T1.TT =  getTime();  }else{    if(!LD_T1.DN){      _LD_T1 =  getTime();      _LD_T2 = _LD_T1 - LD_T1.TT;      if(_LD_T2 >= LD_T1.B){        LD_T1.TT = _LD_T1;        LD_T1.AC = LD_T1.AC + 1;        if(LD_T1.AC >= LD_T1.PRE){          LD_T1.DN = 1;        }      }    }  }  _LD_S0 = LD_T1.DN;  if(_LD_S0){    if(LD_T1.PRE < LD_I1){      _LD_S0 = 0;    }  }  if(_LD_S0){    LD_Q1 = 1;  }}void rung002(void){  uint8_t _LD_S0;  uint64_t _LD_T3;  uint64_t _LD_T4;  _LD_S0 = 1;  if(LD_I2){    _LD_S0 = 0;  }  LD_T2.EN = _LD_S0;  if(!_LD_S0){    LD_T2.DN = 0;    LD_T2.AC = 0;    LD_T2.TT =  getTime();  }else{    if(!LD_T2.DN){      _LD_T3 =  getTime();      _LD_T4 = _LD_T3 - LD_T2.TT;      if(_LD_T4 >= LD_T2.B){        LD_T2.TT = _LD_T3;        LD_T2.AC = LD_T2.AC + 1;        if(LD_T2.AC >= LD_T2.PRE){          LD_T2.DN = 1;        }      }    }  }  _LD_S0 = LD_T2.DN;  if(_LD_S0){    if(LD_T2.PRE < LD_I2){      _LD_S0 = 0;    }  }  if(_LD_S0){    LD_Q2 = 1;  }}void rung003(void){  uint8_t _LD_S0;  uint64_t _LD_T5;  uint64_t _LD_T6;  _LD_S0 = 1;  if(LD_I3){    _LD_S0 = 0;  }  LD_T3.EN = _LD_S0;  if(!_LD_S0){    LD_T3.DN = 0;    LD_T3.AC = 0;    LD_T3.TT =  getTime();  }else{    if(!LD_T3.DN){      _LD_T5 =  getTime();      _LD_T6 = _LD_T5 - LD_T3.TT;      if(_LD_T6 >= LD_T3.B){        LD_T3.TT = _LD_T5;        LD_T3.AC = LD_T3.AC + 1;        if(LD_T3.AC >= LD_T3.PRE){          LD_T3.DN = 1;        }      }    }  }  _LD_S0 = LD_T3.DN;  if(_LD_S0){    if(LD_T3.PRE < LD_I3){      _LD_S0 = 0;    }  }  if(_LD_S0){    LD_Q3 = 1;  }}void rung004(void){  uint8_t _LD_S0;  uint64_t _LD_T7;  uint64_t _LD_T8;  _LD_S0 = 1;  if(LD_I4){    _LD_S0 = 0;  }  LD_T4.EN = _LD_S0;  if(!_LD_S0){    LD_T4.DN = 0;    LD_T4.AC = 0;    LD_T4.TT =  getTime();  }else{    if(!LD_T4.DN){      _LD_T7 =  getTime();      _LD_T8 = _LD_T7 - LD_T4.TT;      if(_LD_T8 >= LD_T4.B){        LD_T4.TT = _LD_T7;        LD_T4.AC = LD_T4.AC + 1;        if(LD_T4.AC >= LD_T4.PRE){          LD_T4.DN = 1;        }      }    }  }  _LD_S0 = LD_T4.DN;  if(_LD_S0){    if(LD_T4.PRE < LD_I4){      _LD_S0 = 0;    }  }  if(_LD_S0){    LD_Q4 = 1;  }  if(_LD_S0){    LD_Q1 = 0;  }}void rung005(void){  uint8_t _LD_S0;  uint64_t _LD_T9;  uint64_t _LD_T10;  uint64_t _LD_T11;  uint64_t _LD_T12;  int32_t _LD_S1;  uint64_t _LD_T13;  uint64_t _LD_T14;  _LD_S0 = 1;  if(LD_I4){    _LD_S0 = 0;  }  LD_T5.EN = _LD_S0;  if(!_LD_S0){    LD_T5.DN = 0;    LD_T5.AC = 0;    LD_T5.TT =  getTime();  }else{    if(!LD_T5.DN){      _LD_T9 =  getTime();      _LD_T10 = _LD_T9 - LD_T5.TT;      if(_LD_T10 >= LD_T5.B){        LD_T5.TT = _LD_T9;        LD_T5.AC = LD_T5.AC + 1;        if(LD_T5.AC >= LD_T5.PRE){          LD_T5.DN = 1;        }      }    }  }  _LD_S0 = LD_T5.DN;  if(_LD_S0){    if(LD_T5.PRE < LD_I4){      _LD_S0 = 0;    }  }  LD_T6.EN = _LD_S0;  if(!_LD_S0){    LD_T6.DN = 0;    LD_T6.AC = 0;    LD_T6.TT =  getTime();  }else{    if(!LD_T6.DN){      _LD_T11 =  getTime();      _LD_T12 = _LD_T11 - LD_T6.TT;      if(_LD_T12 >= LD_T6.B){        LD_T6.TT = _LD_T11;        LD_T6.AC = LD_T6.AC + 1;        if(LD_T6.AC >= LD_T6.PRE){          LD_T6.DN = 1;        }      }    }  }  _LD_S0 = LD_T6.DN;  _LD_S1 = _LD_S0;  if(_LD_S0){    LD_Q3 = 0;  }  if(_LD_S1){    LD_Q2 = 0;  }  if(_LD_S1){    _LD_S0 = 1;  }  LD_T7.EN = _LD_S0;  if(!_LD_S0){    LD_T7.DN = 0;    LD_T7.AC = 0;    LD_T7.TT =  getTime();  }else{    if(!LD_T7.DN){      _LD_T13 =  getTime();      _LD_T14 = _LD_T13 - LD_T7.TT;      if(_LD_T14 >= LD_T7.B){        LD_T7.TT = _LD_T13;        LD_T7.AC = LD_T7.AC + 1;        if(LD_T7.AC >= LD_T7.PRE){          LD_T7.DN = 1;        }      }    }  }  _LD_S0 = LD_T7.DN;  _LD_S1 = _LD_S0;  if(_LD_S0){    LD_Q2 = 1;  }  if(_LD_S1){    LD_Q3 = 1;  }  if(_LD_S1){    _LD_S0 = 1;  }  if(_LD_S0){    LD_T6.DN = 0;    LD_T6.AC = 0;    LD_T6.EN = 0;    LD_T6.TT =  getTime();  }}void rung006(void){  uint8_t _LD_S0;  _LD_S0 = 1;}void initContext(void){  LD_T1.EN = 0;  LD_T1.AC = 0;  LD_T1.PRE = 1;  LD_T1.B = 2000;  LD_T1.DN = 0;  LD_T1.TT =  getTime();  LD_T2.EN = 0;  LD_T2.AC = 0;  LD_T2.PRE = 1;  LD_T2.B = 2000;  LD_T2.DN = 0;  LD_T2.TT =  getTime();  LD_T3.EN = 0;  LD_T3.AC = 0;  LD_T3.PRE = 1;  LD_T3.B = 2000;  LD_T3.DN = 0;  LD_T3.TT =  getTime();  LD_T4.EN = 0;  LD_T4.AC = 0;  LD_T4.PRE = 1;  LD_T4.B = 1000;  LD_T4.DN = 0;  LD_T4.TT =  getTime();  LD_T7.EN = 0;  LD_T7.AC = 0;  LD_T7.PRE = 1;  LD_T7.B = 2000;  LD_T7.DN = 0;  LD_T7.TT =  getTime();  LD_T6.EN = 0;  LD_T6.AC = 0;  LD_T6.PRE = 1;  LD_T6.B = 2000;  LD_T6.DN = 0;  LD_T6.TT =  getTime();  LD_T5.EN = 0;  LD_T5.AC = 0;  LD_T5.PRE = 1;  LD_T5.B = 1000;  LD_T5.DN = 0;  LD_T5.TT =  getTime();}void init(){  LD_TIME.v = 0;  refreshTime64bit();  pinMode(PIN_I01, INPUT);  pinMode(PIN_I02, INPUT);  pinMode(PIN_I03, INPUT);  pinMode(PIN_I04, INPUT);  pinMode(PIN_Q01, OUTPUT);  pinMode(PIN_Q02, OUTPUT);  pinMode(PIN_Q03, OUTPUT);  pinMode(PIN_Q04, OUTPUT);}void TaskScan(void *pvParameters){  for(;;){    vTaskDelay(1);    readInputs();    refreshTime64bit();    rung001();    rung002();    rung003();    rung004();    rung005();    rung006();    writeOutputs();  }}void setup() {  Serial.begin(115200);  init();  initContext();  xTaskCreatePinnedToCore(TaskScan,"TaskScan",4096,NULL,2,NULL,ARDUINO_RUNNING_CORE);}void loop() {}<br>

To check more about the Norvi lineup - www.norvi.lk