Ladder Logic Programming for ESP32 Based SENSOPER Industrial Controllers

by sensoper_controls in Circuits > Microcontrollers

502 Views, 3 Favorites, 0 Comments

Ladder Logic Programming for ESP32 Based SENSOPER Industrial Controllers

smartcontrols1.jpg

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 microcontroller devices. We'll be working with the SENSOPER Smart Controls SC-SE-I8-RO6-TO2 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

So what do you need to get started?

  • SENSOPER Smart Controls SC-SE-I8-RO6-TO2 Device - Check more about the product
  • 24V DC supply
  • Wires for connection
  • USB cable (Type A to Type B mini)
  • PC/Laptop

Operating Scenario

Here, we'll be operating the SENSOPER Smart Controls SC-SE-I8-RO6-TO2 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.

Device GPIO Configuration

GPIO pins detail of Digital Inputs are as follows,

I.0 = 18     I.1 = 39     I.2 = 34      I.3 = 35

I.4 = 19     I.5 = 21     I.6 = 22      I.7 = 23

Here the digital inputs are normally kept on/high (digital logic state "1") by using pull-up resistors inside the device. So when the connection is made the state changes to off/low (digital logic state "0).


Implement the Ladder Logic

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 SENSOPER Smart Controls SC-SE-I8-RO6-TO2 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

To check more about the SENSOPER lineup.

// 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 struct
typedef 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>