C-more HMI and Arduino Uno R3

I have a C-more HMI (EA9-T7CL-R) that I would like to use to use as a master with the Arduino UNO. I have a zihatec rs485 shield on the Arduino. Obviously I need to understand a great deal more on the structure of the information between the HMI and the Arduino. The code that I have been modifying turns the relays off once when I press the button on the HMI. I was hoping to find a skeleton program to add and remove what is needed to control the relays and other items I would like to add. So far no luck. Any help would be greatly appreciated.

#include <modbus.h>
#include <modbusDevice.h>
#include <modbusRegBank.h>
#include <modbusSlave.h>

/* PINS
Add more registers if needed
Digital input pins 2,3,4,5,6,7
Digital output pins 8,9,12,13
Analog output pins 10,11 (PWM)
Analog input pins 0,1,2,3,4,5
*/


modbusDevice regBank;
modbusSlave slave;

int AI0,AI1,AI2,AI3,AI4,AI5;


void setup()
{   
  regBank.setId(1); ///Set Slave ID

//Add Digital Input registers
  regBank.add(10002);
  regBank.add(10003);
  regBank.add(10004);
  regBank.add(10005);
  regBank.add(10006);
  regBank.add(10007);
// Add Digital Output registers
  regBank.add(8);
  regBank.add(9);
  regBank.add(10);
  regBank.add(11);
  regBank.add(12);
  regBank.add(13);
//Analog input registers
  regBank.add(30001);
  regBank.add(30002);
  regBank.add(30003);
  regBank.add(30004);
  regBank.add(30005);
  regBank.add(30006);
//Analog Output registers
  regBank.add(40010); 
  regBank.add(40011); 

  slave._device = &regBank; 
  slave.setBaud(9600);   
 
  pinMode(2,INPUT);
  pinMode(3,INPUT);
  pinMode(4,INPUT);
  pinMode(5,INPUT);
  pinMode(6,INPUT);
  pinMode(7,INPUT);
  pinMode(8,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  pinMode(12,OUTPUT);
 }
void loop(){

  while(1){   
  //Digital Input
    byte DI2 = digitalRead(2);
    if (DI2 >= 1)regBank.set(10002,1);
    if (DI2 <= 0)regBank.set(10002,0);
    byte DI3 = digitalRead(3);
    if (DI3 >= 1)regBank.set(10003,1);
    if (DI3 <= 0)regBank.set(10003,0);
    byte DI4 = digitalRead(4);
    if (DI4 >= 1)regBank.set(10004,1);
    if (DI4 <= 0)regBank.set(10004,0);
    byte DI5 = digitalRead(5);
    if (DI5 >= 1)regBank.set(10005,1);
    if (DI5 <= 0)regBank.set(10005,0);
    byte DI6 = digitalRead(6);
    if (DI6 >= 1)regBank.set(10006,1);
    if (DI6 <= 0)regBank.set(10006,0);
    byte DI7 = digitalRead(7);
    if (DI7 >= 1)regBank.set(10007,1);
    if (DI7 <= 0)regBank.set(10007,0);
                               
  //Digital output
    int DO8 = regBank.get(8);
      if (DO8 <= 0 && digitalRead(8) == HIGH)digitalWrite(8,LOW);
      if (DO8 >= 1 && digitalRead(8) == LOW)digitalWrite(8,HIGH);
    int DO9 = regBank.get(9);
      if (DO9 <= 0 && digitalRead(9) == HIGH)digitalWrite(9,LOW);
      if (DO9 >= 1 && digitalRead(9) == LOW)digitalWrite(9,HIGH);
    int DO10 = regBank.get(10);
      if (DO10 <= 0 && digitalRead(10) == HIGH)digitalWrite(10,LOW);
      if (DO10 >= 1 && digitalRead(10) == LOW)digitalWrite(10,HIGH);
    int DO11 = regBank.get(11);
      if (DO11 <= 0 && digitalRead(11) == HIGH)digitalWrite(11,LOW);
      if (DO11 >= 1 && digitalRead(11) == LOW)digitalWrite(11,HIGH);
    int DO12 = regBank.get(12);
      if (DO12 <= 0 && digitalRead(12) == HIGH)digitalWrite(12,LOW);
      if (DO12 >= 1 && digitalRead(12) == LOW)digitalWrite(12,HIGH);
    
           
  //Analog input  ***READ Twice deliberately
    AI0 = analogRead(0);
    delay(10);
    AI0 = analogRead(0);
    regBank.set(30001, (word) AI0);
    delay(10);
   
    AI1 = analogRead(1);
    delay(10);
    AI1 = analogRead(1);
    regBank.set(30002, (word) AI1);
    delay(10);
   
    AI2 = analogRead(2);
    delay(10);
    AI2 = analogRead(2);
    regBank.set(30003, (word) AI2);
    delay(10);
   
    AI3 = analogRead(3);
    delay(10);
    AI3 = analogRead(3);
    regBank.set(30004, (word) AI3);
    delay(10);
   
    AI4 = analogRead(4);
    delay(10);
    AI4 = analogRead(4);
    regBank.set(30005, (word) AI4);
    delay(10);
   
    AI5 = analogRead(5);
    delay(10);
    AI5 = analogRead(5);
    regBank.set(30006, (word) AI5);
    delay(10);
       
  //Analog output
    word AO10 = regBank.get(40010);
    analogWrite(10,AO10);
    delay(10);
    word AO11 = regBank.get(40011);
    analogWrite(11,AO11);
    delay(10);
       
  slave.run(); 
  }
}

Please share more details. Info is quite blurry, how you want to control the relays ??? and share your HMI screen i.e. what you have rite now and what you wanna achieve ???

Disclaimer: no experience with modbus or your HMI screen.

If I understand you correctly, you have managed to program your screen to have buttons that you can click and use to switch Arduino pins on and off and read analog and digital pins and you're looking for a more generic approach?

If so, one way is to use a class or struct to 'link' a register and a pin (and possibly a function to do something). It's like an entry in a phone book where a name and a phone number are combined.

sterretje:
Disclaimer: no experience with modbus or your HMI screen.

Me2 :frowning:

As I understand your code, regBank contains the values of the Modbus slave device, implemented by your controller. For now I only see how your existing code can be simplified. E.g. your code

   byte DI7 = digitalRead(7);
   if (DI7 >= 1)regBank.set(10007,1);
   if (DI7 <= 0)regBank.set(10007,0);
                              
 //Digital output
   int DO8 = regBank.get(8);
     if (DO8 <= 0 && digitalRead(8) == HIGH)digitalWrite(8,LOW);
     if (DO8 >= 1 && digitalRead(8) == LOW)digitalWrite(8,HIGH);

can be simplified into

   regBank.set(10007,digitalRead(7));
                              
 //Digital output
     digitalWrite(8,regBank.get(8));

Also drop the while loop inside loop():

void loop(){

  while(1){   //<------------ remove this loop

jackthom41 - What I am trying to achieve is automating a parts washer. The idea is to verify I could establish simple control of the relays that turn the parts washer and pump on and off. Then automate the process by adding a timer (to run the process for a specified time) and control of a heater band with a thermocouple. Gradually add functionality as I understood more.

I attached screen shots of the C-More panel manager and button settings.

I looked through your engineering projects. Quite a long list. Cool stuff in there.

sterretje - do you have an implementation example of class or struct ? This sounds interesting.

DrDiettrich - Nice simplification of the code. By reading or getting the value, you already have what needs to be set. The if statements are unnecessary.

I did some reading on the while loop. The discussions all pointed to the fact that the loop was not needed and actually caused havoc in certain instances.

Thank You for the good suggestions.

To start with, you have a pin that is associated with a register bank. I called the struct PINCONTROL

// struct with info how to control a pin
struct PINCONTROL
{
  word address; // register address
  byte pin;     // pin to control
};

You can add more stuff, e.g. the mode for pinMode and a function to execute on that pin. The latter is done below and uses a so-called function pointer.

// struct with info how to control a pin
struct PINCONTROL
{
  word address; // register address
  byte pin;     // pin to control
  byte mode;    // pinMode

  void (*action)(uint16_t index); // action to perform, function pointer
};

Next you can create an array of PINCONTROL structs; below just a sample based on your code.

// all pins
PINCONTROL pinControllers[] =
{
  {10002, 2, INPUT, NULL},          // digital input
  {10003, 3, INPUT_PULLUP, NULL},   // digital input
  {30001, 2, INPUT, NULL},          // analog input
  {8, 8, OUTPUT, NULL},             // digital output
  {9, 9, OUTPUT, NULL},             // PWM output
  {10, 10, OUTPUT, NULL},           // digital output
};

As we don't have functions yet, the function pointer for the action is set to NULL, the other values should be self-explaining.

Now, in setup, you can iterate over the array and do the setup of the register banks and the pins. To make it easier to know how many pin controllers there are, you can use a macro. You can add the below at the top of your code (e.g. before your includes).

// macro to get number of elements in array of any type
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

And

void setup()
{
  Serial.begin(57600);

  regBank.setId(1); ///Set Slave ID

  // just some debug
  Serial.print(F("There are "));
  Serial.print(NUMELEMENTS(pinControllers));
  Serial.println(F(" pin controllers configured"));

  // loop through the configured pin controllers
  for (uint16_t pCnt = 0; pCnt < NUMELEMENTS(pinControllers); pCnt++)
  {
    // add the address to regBank
    regBank.add(pinControllers[pCnt].address);
    // set the pin mode for the pin
    pinMode(pinControllers[pCnt].pin, pinControllers[pCnt].mode);
  }

  slave._device = &regBank;
  slave.setBaud(9600);

The above demonstrates how you can access the variables in a struct use the dot.

Now you can implement loop() in similar fashion;

void loop()
{
  // loop through the configured pin controllers
  for (uint16_t pCnt = 0; pCnt < NUMELEMENTS(pinControllers); pCnt++)
  {
    // prevent crashes if function was not added
    if (pinControllers[pCnt].action != NULL)
    {
      // execute the function associated with the pin controller
      pinControllers[pCnt].action(pCnt);
    }
    // only for demo purposes
    delay(1000);
  }

  slave.run();
}

Now you need to think about your functions; based on your code I came up with the below set of functions (with self-explaing names); idx is the index that will be used to access an element in the pinControls array.

void readDigitalPin(uint16_t idx);
void writeDigitalPin(uint16_t idx);
void toggleDigitalPin(uint16_t idx);
void readAnalogPin(uint16_t idx);
void writePwmPin(uint16_t idx);

Before you can use a function, the compiler needs to know about its existence. I'm still using an older version of the IDE (1.8.5) that is not intelligent enough to get everything right. What I showed are called function prototypes and need to be placed before the pinControls array.
Below the full code till now.

// macro to get number of elements in any array
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

#include <modbus.h>
#include <modbusDevice.h>
#include <modbusRegBank.h>
#include <modbusSlave.h>

// function prototypes
void readDigitalPin(uint16_t idx);
void writeDigitalPin(uint16_t idx);
void toggleDigitalPin(uint16_t idx);
void readAnalogPin(uint16_t idx);
void writePwmPin(uint16_t idx);

// struct with info how to control a pin
struct PINCONTROL
{
  word address; // register address
  byte pin;     // pin to control
  byte mode;    // pinMode

  void (*action)(uint16_t index); // action to perform
};

// all pins
const PINCONTROL pinControllers[] =
{
  {10002, 2, INPUT, readDigitalPin},
  {10003, 3, INPUT_PULLUP, readDigitalPin},
  {30001, 2, INPUT, readAnalogPin},
  {8, 8, OUTPUT, writeDigitalPin},
  {9, 9, OUTPUT, writePwmPin},
  {10, 10, OUTPUT, toggleDigitalPin},
};

modbusDevice regBank;
modbusSlave slave;

void setup()
{
  Serial.begin(57600);

  regBank.setId(1); ///Set Slave ID

  // just some debug
  Serial.print(F("There are "));
  Serial.print(NUMELEMENTS(pinControllers));
  Serial.println(F(" pin controllers configured"));

  // loop through the configured pin controllers
  for (uint16_t pCnt = 0; pCnt < NUMELEMENTS(pinControllers); pCnt++)
  {
    // add the address to regBank
    regBank.add(pinControllers[pCnt].address);
    // set the pin mode for the pin
    pinMode(pinControllers[pCnt].pin, pinControllers[pCnt].mode);
  }

  slave._device = &regBank;
  slave.setBaud(9600);
}

void loop()
{
  // loop through the configured pin controllers
  for (uint16_t pCnt = 0; pCnt < NUMELEMENTS(pinControllers); pCnt++)
  {
    // prevent crashes if function was not added
    if (pinControllers[pCnt].action != NULL)
    {
      // execute the function associated with the pin controller
      pinControllers[pCnt].action(pCnt);
    }
    // only for demo purposes
    delay(1000);
  }

  slave.run();
}

The above code will compile but you will get linker errors as the action functions have not been implemented. Below are the implementations of the action functions; they should be functional but consider them examples that you can modify. Add it the end of your code (after loop()). The serial output is meant for debugging.

/*
  read digital pin
  In:
    index in pinControls array
*/
void readDigitalPin(uint16_t idx)
{
  Serial.print(F("Reading digital pin "));
  Serial.print(pinControllers[idx].pin);
  Serial.print(F(" and setting regBank "));
  Serial.println(pinControllers[idx].address);

  regBank.set(pinControllers[idx].address, (word)digitalRead(pinControllers[idx].pin));
}

/*
  write digital pin
  In:
    index in pinControls array
*/
void writeDigitalPin(uint16_t idx)
{
  Serial.print(F("Setting digital pin "));
  Serial.print(pinControllers[idx].pin);
  Serial.print(F(" based on regBank "));
  Serial.println(pinControllers[idx].address);

  digitalWrite(pinControllers[idx].pin, regBank.get(pinControllers[idx].address));
}

/*
  toggle digital pin
  In:
    index in pinControls array
*/
void toggleDigitalPin(uint16_t idx)
{
  Serial.print(F("Toggling digital pin "));
  Serial.print(pinControllers[idx].pin);
  Serial.print(F(" based on regBank "));
  Serial.println(pinControllers[idx].address);

  if (regBank.get(pinControllers[idx].address) == 1)
  {
    digitalWrite(pinControllers[idx].pin, !digitalRead(pinControllers[idx].pin));
  }
}

/*
  read analog pin
  In:
    index in pinControls array
*/
void readAnalogPin(uint16_t idx)
{
  Serial.print(F("Reading analog pin "));
  Serial.print(pinControllers[idx].pin);
  Serial.print(F(" and setting regBank "));
  Serial.println(pinControllers[idx].address);

  regBank.set(pinControllers[idx].address, (word)analogRead(pinControllers[idx].pin));
}

/*
  write PWM pin
  In:
    index in pinControls array
*/
void writePwmPin(uint16_t idx)
{
  Serial.print(F("Setting PWM pin "));
  Serial.print(pinControllers[idx].pin);
  Serial.print(F(" based on regBank "));
  Serial.println(pinControllers[idx].address);

  analogWrite(pinControllers[idx].pin, regBank.get(pinControllers[idx].address));
}

Below the full code for your convenience; it compiles but I can not test.

// macro to get number of elements in any array
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

#include <modbus.h>
#include <modbusDevice.h>
#include <modbusRegBank.h>
#include <modbusSlave.h>

// function prototypes
void readDigitalPin(uint16_t idx);
void writeDigitalPin(uint16_t idx);
void toggleDigitalPin(uint16_t idx);
void readAnalogPin(uint16_t idx);
void writePwmPin(uint16_t idx);

// struct with info how to control a pin
struct PINCONTROL
{
  word address; // register address
  byte pin;     // pin to control
  byte mode;    // pinMode

  void (*action)(uint16_t index); // action to perform
};

// all pins
const PINCONTROL pinControllers[] =
{
  {10002, 2, INPUT, readDigitalPin},
  {10003, 3, INPUT_PULLUP, readDigitalPin},
  {30001, 2, INPUT, readAnalogPin},
  {8, 8, OUTPUT, writeDigitalPin},
  {9, 9, OUTPUT, writePwmPin},
  {10, 10, OUTPUT, toggleDigitalPin},
};

modbusDevice regBank;
modbusSlave slave;

void setup()
{
  Serial.begin(57600);

  regBank.setId(1); ///Set Slave ID

  Serial.print(F("There are "));
  Serial.print(NUMELEMENTS(pinControllers));
  Serial.println(F(" pin controllers configured"));

  for (uint16_t pCnt = 0; pCnt < NUMELEMENTS(pinControllers); pCnt++)
  {
    regBank.add(pinControllers[pCnt].address);
    pinMode(pinControllers[pCnt].pin, pinControllers[pCnt].mode);
  }

  slave._device = &regBank;
  slave.setBaud(9600);

}

void loop()
{
  for (uint16_t pCnt = 0; pCnt < NUMELEMENTS(pinControllers); pCnt++)
  {
    if (pinControllers[pCnt].action != NULL)
    {
      pinControllers[pCnt].action(pCnt);
    }
    delay(1000);
  }

  slave.run();
}


/*
  read digital pin
  In:
    index in pinControls array
*/
void readDigitalPin(uint16_t idx)
{
  Serial.print(F("Reading digital pin "));
  Serial.print(pinControllers[idx].pin);
  Serial.print(F(" and setting regBank "));
  Serial.println(pinControllers[idx].address);

  regBank.set(pinControllers[idx].address, (word)digitalRead(pinControllers[idx].pin));
}

/*
  write digital pin
  In:
    index in pinControls array
*/
void writeDigitalPin(uint16_t idx)
{
  Serial.print(F("Setting digital pin "));
  Serial.print(pinControllers[idx].pin);
  Serial.print(F(" based on regBank "));
  Serial.println(pinControllers[idx].address);

  digitalWrite(pinControllers[idx].pin, regBank.get(pinControllers[idx].address));
}

/*
  toggle digital pin
  In:
    index in pinControls array
*/
void toggleDigitalPin(uint16_t idx)
{
  Serial.print(F("Toggling digital pin "));
  Serial.print(pinControllers[idx].pin);
  Serial.print(F(" based on regBank "));
  Serial.println(pinControllers[idx].address);

  if (regBank.get(pinControllers[idx].address) == 1)
  {
    digitalWrite(pinControllers[idx].pin, !digitalRead(pinControllers[idx].pin));
  }
}

/*
  read analog pin
  In:
    index in pinControls array
*/
void readAnalogPin(uint16_t idx)
{
  Serial.print(F("Reading analog pin "));
  Serial.print(pinControllers[idx].pin);
  Serial.print(F(" and setting regBank "));
  Serial.println(pinControllers[idx].address);

  regBank.set(pinControllers[idx].address, (word)analogRead(pinControllers[idx].pin));
}

/*
  write PWM pin
  In:
    index in pinControls array
*/
void writePwmPin(uint16_t idx)
{
  Serial.print(F("Setting PWM pin "));
  Serial.print(pinControllers[idx].pin);
  Serial.print(F(" based on regBank "));
  Serial.println(pinControllers[idx].address);

  analogWrite(pinControllers[idx].pin, regBank.get(pinControllers[idx].address));
}

sterretje - Holy intense Batman. Thank You. I have some work to do.

TBarnes:
I did some reading on the while loop. The discussions all pointed to the fact that the loop was not needed and actually caused havoc in certain instances.

Most certainly. :grinning:

The loop() proper in the code already is a while(1) loop. In general, any other "while()" in the code will be a design mistake as it will constrain the code to a limited region whereas the whole concept of the loop() is that it continuously cycles through each area of the code allowing many tasks to be simultaneously - at least in effect - attended.