Slave / Master LCD Display

i have a project where currently, i am using 6 separate Uno R3 to run 2 different relays,
each of the 6 have different timing cycles (schedules / frequencies/ number of loops) for the relays, while these cycles are running, i am printing to a 4x20 LCD i2c Screen that displays line 1 the name of the sketch, line 2 not used, line 3 countdown timer till the next relay activation, line 4 total number of activations. once the total number of cycles (loops) are completed it ends.

this is on 110v wall timer that is set to have power for 12 hrs a day. so every night the power is shut off and it will restart when the power is turned back on.

and when it is time to switch the sketch, i have been switching out the uno. (yes i know i can reprogram the uno, but i do not have a computer close, and would have to unhook, and re-hook up the wires either way.

all of this is working fine!!!!!!, just trying to make life easier.

so i am now going to use the 6 uno's as "Slaves" all feeding to 1 "Master" and the master having the lone LCD Screen a 4x20 i2c (side note: only 1 slave will have power at a time)

i am able to get this to work to trigger the relays, see sketch below, but i am stumped on how to get a slave so send the lcd.print to send from the slave through the master to display on the lcd.

is this even possible????

#include <Wire.h> // for I2C communication
#include <LiquidCrystal_I2C.h> // for LCD display

// Define M Relay Pins////////////////////////////////
int Pin13 = 13; // Input pin from Slave 1 pin 2
int Pin12 = 12; // Input pin from Slave 2 pin 2
int Pin11 = 11; // Input pin from Slave 3 pin 2
int Pin10 = 10; // Input pin from Slave 4 pin 2
int Pin9 = 9;   // Input pin from Slave 5 pin 2
int Pin8 = 8;   // Input pin from Slave 6 pin 2

int Pin0 = A0;  // Output pin to M Relay

// Define S Relay Pins////////////////////////////////
int Pin7 = 7; // Input pin from Slave 1 pin 3
int Pin6 = 6; // Input pin from Slave 2 pin 3
int Pin5 = 5; // Input pin from Slave 3 pin 3
int Pin4 = 4; // Input pin from Slave 4 pin 3
int Pin3 = 3; // Input pin from Slave 5 pin 3
int Pin2 = 2; // Input pin from Slave 6 pin 3

int Pin1 = A1; // Output pin to S Relay

void setup() {
  pinMode(Pin13, INPUT);  // set pin as input
  pinMode(Pin12, INPUT);  // set pin as input
  pinMode(Pin11, INPUT);  // set pin as input
  pinMode(Pin10, INPUT);  // set pin as input
  pinMode(Pin9, INPUT);   // set pin as input
  pinMode(Pin8, INPUT);   // set pin as input
  pinMode(Pin7, INPUT);   // set pin as input
  pinMode(Pin6, INPUT);   // set pin as input
  pinMode(Pin5, INPUT);   // set pin as input
  pinMode(Pin4, INPUT);   // set pin as input
  pinMode(Pin3, INPUT);   // set pin as input
  pinMode(Pin2, INPUT);   // set pin as input
  
  pinMode(Pin0, OUTPUT); // set pin as output
  pinMode(Pin1, OUTPUT); // set pin as output
  }

  void loop() {

  // Check if pins 8 thru 13 is receiving signal
  if (digitalRead(Pin13) == HIGH || digitalRead(Pin12) == HIGH || digitalRead(Pin11) == HIGH
   || digitalRead(Pin10) == HIGH || digitalRead(Pin9) == HIGH || digitalRead(Pin8) == HIGH) {
    
      digitalWrite(A0, HIGH);// Output HIGH signal on A0
  } else {
      digitalWrite(A0, LOW);// Output LOW signal on A0
  }
  
  // Check if pins 2 thru 7 is receiving signal
  if (digitalRead(Pin7) == HIGH || digitalRead(Pin6) == HIGH || digitalRead(Pin5) == HIGH
   || digitalRead(Pin4) == HIGH || digitalRead(Pin3) == HIGH || digitalRead(Pin2) == HIGH) {
    
      digitalWrite(A1, HIGH);// Output HIGH signal on A1
  } else {
      digitalWrite(A1, LOW); // Output LOW signal on A1
  }
}

Why not just use one modern, faster MCU and follow the tutorial here about doing many tasks at once?

It's not really about the multiple things, only 1 slave runs a day, I may need it to run the sketch on slave 3 today, 4 tomorrow, 2 the next day.... Basically I set it the night before, and when the wall timer activates, I turns the Arduino on, it will loop the program, between 1 and 20 times (depends on sketch) and not used again until the timer activates the next day

It really seems like you are making your life more difficult than need be. A single controller can handle 12 relays, all with different cycles quite easily.

But, if you must go down this road, how far away are your slave Uno boards? They could communicate with i2c if they are very close but it sounds like they might not be. It would probably be better to use RS485.

Your test program would benefit greatly from using arrays rather than variables that are basically just pin numbers. If your slave UNOs are without power, the pins coming into the master must be INPUT_PULLUP (or you need external resistors)- or else those pins are just floating and you will not be able to reliably read their state.

Whatever.

sounds like a Arduino Mega or even an ESP32 or RP2040 could handle the whole task
removes the complexity of programming multiple microcontroller communications with possibility of problems due to mechanical connections, wires, etc

Completely agree. I can't see any reason to spread 6 Arduinos around to control 12 relays, use just a single UNO to control all the relays (there are boards with up to with 16!).

Are you physically disconnecting one UNO and replacing it with another, or are you just selecting which is powered, while all the UNOs will be left connected to your master UNO? Having unpowered boards connected means you need to be careful not to phantom power the boards by applying a HIGH voltage to any of its inputs, which will try to power the board through the internal input over-voltage protection diode.

Easier would have been to put three switches on a single UNO, which could be read in setup() to select which of up to eight timing cycles would be run.

1 Like

Physically switching them out

Then master does not need to know which slave is connected, just display whatever it sends.

This may be a better explanation of what i am doing.

The 2 led's represent the 2 solenoids that the relays are controlling.

this is mounted about 2ft from the solenoids, and everything is powered through a transformer.

at the end of the day, everything stays as is, and i only swap the uno out.

so that's why i was thinking the 6 slaves, into one master (uno or Mega) and a rotary switch to power the selected slave.

david_2018 mentioned using switches, what would be the proper terminology to search for, to read up on that.

Below is one of the sketches that i run, the only thing that changes from 1 sketch to the next is the values of the 1st 12 const int's which determine the delays, the number of loops, number of cycles, and the duration each relay is activated, .

in the example below, once powered up

2 hour delay,

Mrelay activated for 5min, 10 min delay, Srelay activated for 5min, 10 min delay:
repeat 5 times

5 hour delay

Mrelay activated for 5min, 10 min delay, Srelay activated for 5min, 10 min delay:
repeat 2 times

4 hour delay

Mrelay activated for 5min, 10 min delay, Srelay activated for 5min, 10 min delay:
repeat 2 times

completed for the day

#include <Wire.h>
#include <LiquidCrystal_I2C.h>


LiquidCrystal_I2C lcd(0x27, 20, 4); // Create an LCD object with the I2C address 


const int FirstDelay = 7200000;  // Define Inital Delay Period Before 1st Phase (in milliseconds)
const int fdloop = 1;         // number of times to loop 1st delay
const int CyclesPhOne = 5;    // Define Cycles in Phase 1

const int SecondDelay = 18000000; // Define Delay between Phase 1 & 2 (in milliseconds)
const int sdloop = 1;         // number of times to loop 2nd delay
const int CyclesPhTwo = 2;    // Define Cycles in Phase 2

const int ThirdDelay = 14400000;  // Define Delay between Phase 2 & 3 (in milliseconds)
const int tdloop = 1;         // number of times to loop 3rd delay
const int CyclesPhThree = 2;  // Define Cycles in Phase 3

const int MRelayDuration = 300000; // Define Length of M Cycle (in milliseconds)
const int SRelayDuration = 300000; // Define Length of S Cycle (in milliseconds)

const int DelayBetween = 600000; // Define Length of Delay between Ming Cycle and S Cycle (in milliseconds)

const int SRelay = 2; // Define Relay Pins
const int MRelay = 3; // Define Relay Pins

int totalcycles = CyclesPhOne + CyclesPhTwo + CyclesPhThree;
int fd = 0; // Loop Counter for First Delay
int sd = 0; // Loop Counter for Second Delay
int td = 0; // Loop Counter for Third Delay
int fc = 0; // Loop Counter for First Cycle
int sc = 0; // Loop Counter for Second Cycle
int tc = 0; // Loop Counter for Third Cycle

void setup() {
  pinMode(13, OUTPUT); // Set pin D13 as an output for LCD power
  digitalWrite(13, HIGH); // Set pin D13 to HIGH (5V)

  lcd.init();
  lcd.backlight(); // Turn on the backlight
  pinMode(SRelay, OUTPUT);
  pinMode(MRelay, OUTPUT);
  digitalWrite(SRelay, LOW); // Ensure SRelay is off initially
  digitalWrite(MRelay, LOW); // Ensure MRelay is off initially
}
//////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
  // Do nothing after completing the cycles

if (fd < (fdloop)) {
   lcd.clear();
      lcd.print("Stage V2");
        for (unsigned long i = (FirstDelay - DelayBetween) / 1000; i > 0; i--) {
    lcd.setCursor(0, 2);
    lcd.print("Daily Cycle Part 1:");
    lcd.setCursor(0, 3);
    lcd.print("Begins In: ");
    lcd.print(i);
    lcd.print(" sec  ");
    delay(1000); // Countdown every second
    
fd += 1;
}
}
else if (fc < (CyclesPhOne)) {

  lcd.clear();
      lcd.print("Stage V2");
        for (unsigned long i = DelayBetween / 1000; i > 0; i--) {
    lcd.setCursor(0, 2);
    lcd.print("M:");
    lcd.setCursor(0, 3);
    lcd.print("Begins In: ");
    lcd.print(i);
    lcd.print(" sec  ");
    delay(1000); // Countdown every second
        }
  
   lcd.clear();
    lcd.print("Stage V2");
    lcd.setCursor(0, 2);
    lcd.print("M Cycle #: ");
    lcd.print(fc + sc + tc + 1);
    lcd.print(" of ");
    lcd.print(totalcycles);
        
    digitalWrite(MRelay, HIGH);  // Turn the Relay on
    delay(MRelayDuration);       // Duration of Ming
    digitalWrite(MRelay, LOW);   // Turn the Relay off
    
    lcd.clear();
    lcd.print("Stage V2");
     for (unsigned long i = DelayBetween / 1000; i > 0; i--) {
    lcd.setCursor(0, 2);
    lcd.print("S:");
    lcd.setCursor(0, 3);
    lcd.print("Begins In: ");
    lcd.print(i);
    lcd.print(" sec  ");
    delay(1000); // Countdown every second
        }

    lcd.clear();
    lcd.print("Stage V2");
    lcd.setCursor(0, 2);
    lcd.print("S Cycle #: ");
    lcd.print(fc + sc + tc + 1);
    lcd.print(" of ");
    lcd.print(totalcycles);
    
    digitalWrite(SRelay, HIGH);  // Turn the Relay on
    delay(SRelayDuration);       // Duration of Ming
    digitalWrite(SRelay, LOW);   // Turn the Relay off
       
fc += 1;
 
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////

else if (sd < (sdloop)) {
   lcd.clear();
      lcd.print("Stage V2");
        for (unsigned long i = (SecondDelay - DelayBetween) / 1000; i > 0; i--) {
    lcd.setCursor(0, 2);
    lcd.print("Daily Cycle Part 2:");
    lcd.setCursor(0, 3);
    lcd.print("Begins In: ");
    lcd.print(i);
    lcd.print(" sec  ");
    delay(1000); // Countdown every second
    
sd += 1;
}
}
else if (sc < (CyclesPhTwo)) {

  lcd.clear();
      lcd.print("Stage V2");
        for (unsigned long i = DelayBetween / 1000; i > 0; i--) {
    lcd.setCursor(0, 2);
    lcd.print("M:");
    lcd.setCursor(0, 3);
    lcd.print("Begins In: ");
    lcd.print(i);
    lcd.print(" sec  ");
    delay(1000); // Countdown every second
        }
  
   lcd.clear();
    lcd.print("Stage V2");
    lcd.setCursor(0, 2);
    lcd.print("M Cycle #: ");
    lcd.print(fc + sc + tc + 1);
    lcd.print(" of ");
    lcd.print(totalcycles);
        
    digitalWrite(MRelay, HIGH);  // Turn the Relay on
    delay(MRelayDuration);       // Duration of Ming
    digitalWrite(MRelay, LOW);   // Turn the Relay off
    
    lcd.clear();
    lcd.print("Stage V2");
     for (unsigned long i = DelayBetween / 1000; i > 0; i--) {
    lcd.setCursor(0, 2);
    lcd.print("S:");
    lcd.setCursor(0, 3);
    lcd.print("Begins In: ");
    lcd.print(i);
    lcd.print(" sec  ");
    delay(1000); // Countdown every second
        }

    lcd.clear();
    lcd.print("Stage V2");
    lcd.setCursor(0, 2);
    lcd.print("S Cycle #: ");
    lcd.print(fc + sc + tc + 1);
    lcd.print(" of ");
    lcd.print(totalcycles);
    
    digitalWrite(SRelay, HIGH);  // Turn the Relay on
    delay(SRelayDuration);       // Duration of Ming
    digitalWrite(SRelay, LOW);   // Turn the Relay off
    
       
sc += 1;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
else if (td < (tdloop)) {
   lcd.clear();
      lcd.print("Stage V2");
        for (unsigned long i = (ThirdDelay - DelayBetween) / 1000; i > 0; i--) {
    lcd.setCursor(0, 2);
    lcd.print("Daily Cycle Part 3:");
    lcd.setCursor(0, 3);
    lcd.print("Begins In: ");
    lcd.print(i);
    lcd.print(" sec  ");
    delay(1000); // Countdown every second
    
td += 1;
}
}

else if (tc < (CyclesPhThree)) {

  lcd.clear();
      lcd.print("Stage V2");
        for (unsigned long i = DelayBetween / 1000; i > 0; i--) {
    lcd.setCursor(0, 2);
    lcd.print("M:");
    lcd.setCursor(0, 3);
    lcd.print("Begins In: ");
    lcd.print(i);
    lcd.print(" sec  ");
    delay(1000); // Countdown every second
        }
  
   lcd.clear();
    lcd.print("Stage V2");
    lcd.setCursor(0, 2);
    lcd.print("M Cycle #: ");
    lcd.print(fc + sc + tc + 1);
    lcd.print(" of ");
    lcd.print(totalcycles);
        
    digitalWrite(MRelay, HIGH);  // Turn the Relay on
    delay(MRelayDuration);       // Duration of Ming
    digitalWrite(MRelay, LOW);   // Turn the Relay off
    
    lcd.clear();
    lcd.print("Stage V2");
     for (unsigned long i = DelayBetween / 1000; i > 0; i--) {
    lcd.setCursor(0, 2);
    lcd.print("S:");
    lcd.setCursor(0, 3);
    lcd.print("Begins In: ");
    lcd.print(i);
    lcd.print(" sec  ");
    delay(1000); // Countdown every second
        }

    lcd.clear();
    lcd.print("Stage V2");
    lcd.setCursor(0, 2);
    lcd.print("S Cycle #: ");
    lcd.print(fc + sc + tc + 1);
    lcd.print(" of ");
    lcd.print(totalcycles);
    
    digitalWrite(SRelay, HIGH);  // Turn the Relay on
    delay(SRelayDuration);       // Duration of Ming
    digitalWrite(SRelay, LOW);   // Turn the Relay off
       
tc += 1;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
else {
  lcd.clear();
  lcd.print("Stage V2");
    lcd.setCursor(0, 2);
  lcd.print("Daily Cycle");
  lcd.setCursor(0, 3);
  lcd.print("Complete");
while (true);
}
}

Rather than hard code all those values into your sketch, get yourself a display that has a few buttons attached, like this one from adafruit: RGB LCD Shield Kit w/ 16x2 Character Display - Only 2 pins used! [NEGATIVE DISPLAY] : ID 714 : Adafruit Industries, Unique & fun DIY electronics and kits

Then, you can store your cycle in EEPROM and use the buttons+display to adjust the schedule as needed without being connected to a PC or swapping out the Uno.

Once you have that done, you have a "universal" controller that you can replicate as many times as you need.

awesome, thanks!!!! just needed a direction to go

Thanks everyone, still a newbie with arduino. Have worked with Python for years, and C+ is kicking my butt right now

Hi @gfiles1977 ,
on a Uno R3, int's are 16 bit..
most of your const int's are over capacity..

good luck.. ~q

Are you really just making timing/functional changes based on day of week, or is it somewhat random where human input to make a selection is actually needed.

If it is actually based on day of week, day of month, or some predictable sequence, you could add a real time clock and use that to adjust which set of active parameters you use as well as when to wake up and start running the cycle.
i.e. eliminate the mechanical timer to power on/off the Arduino and have a single always running Arduino self reconfigure itself based on the day of the week or date, and then have it do things based on the time of day.

--- bill

which the compiler would happily tell you, if you ask it to do so. In the IDE, go to File -> Preferences and set Compiler warings to all.

1 Like

Thanks everyone for the Help.

I re-wrote the sketch, i never thought it would but everything will fit on one Uno/Nano

i will be using a 8 position rotary switch (thanks @david_2018 for pointing me in that direction) to pre select the "program" i want to run, was actually able to add 2 more "programs" to the setup that i had been wanting to add

i set the compiler warnings to ALL and no errors (Thanks @blh64 ) and it using 29% of storage space, and 26% of Dynamic Memory

i just hard coded my values instead of using const int's (thanks @qubits-us )

wont post all the long, long sketch but i defined the pin switches,

#define PIN_SWITCH_1 4
#define PIN_SWITCH_2 5 etc.

added pinMode inputs

pinMode(PIN_SWITCH_1, INPUT);
pinMode(PIN_SWITCH_2, INPUT); etc.

and in my void loop, used IF statements to select the "program"

if (digitalRead(PIN_SWITCH_1) == HIGH){ do something }
if (digitalRead(PIN_SWITCH_2) == HIGH){ do something else} etc.

anyone see anything that may cause issues?

You can simplify things if you use some arrays

const byte switchPin = { 4, 5, 6, 7, 8, 9, 10, 11 };
const int nPins = sizeof (switchPin) / sizeof (switchPin[0]);

void routine1();  /* forward reference to function so we can use it */
void routine2();
void routine3();
void routine4();
void routine5();
void routine6();
void routine7();
void routine8();

void (*funcTable[nPins])() = { routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8 };

...
void setup() {
  for( int i=0; i < nPins; ++i ) {
    pinMode(switchPin[i], INPUT);    /* do you have external pull-up or pull-down resistors? */
  }
  ...
}

void loop() {
  for( int i=0; i < nPins; ++i ) {
    if ( digitalRead(switchPin[i]) == HIGH ) {
      funcTable[i]();
    }
  }
}

void routine1() {
  // actual code to do whatever routine 1 does
}

/* same for all the other routines