Code for an Arduino Automatic Transfer Switch

For shure.

Remember that YT videos are not the best place to learn.

You acquire new knowledge by doing things yourself.

Thinking for yourself makes you smart, not the stuff from the tube.

How do you do that?

Think of a project and learn how to do it with a textbook and Arduino tutorials.

And if you have questions about programming and hardware, this ingenious form is there with its members.

Have a nice day and enjoy coding in C++.

Most newcomer underestimate how much knowledge is nescessary to write code.

This is a very vague description which could be (not must be) interpreted as a hidden way of saying

"can somebody write the full functional code for me"

The best way to get really help is to ask specific questions.
As I have written in a former post. Communication becomes very effective and fast
if you give precise descriptions.

If you don't know yet what the important details are the "precise" question is

"what details do you need to analyse the problem?"

I have written a code that is mimimum near to full functional. This is the code that you tried but described

So to make it faster.
Your question could be
"what details do you need to analyse why it did not work?"

Answer "it is very likely that you got a compiler-error because you did not install the library SafeString".

Next step would be that you google for

Arduino-IDE installing a library

Which leads you here

Then you add the SafeString library and try to compile again.

If you still get a compiler-error

Which is again a

very precise

description of what did happen.

precision is crucial in programming.

again an analogy to truck-driving
if you would say to an extra-terrestrian beeing

"Fill up the tank"

this would be a unprecise description

and a lot of knowledge is nescessary to do it right

  • where is the tank-cap (usually)?
  • how does a tank cap look like to find it by walking around the truck?
  • where do I have to drive to get the fuel?
  • what sort of fuel must I fill in?
  • how do I use this thing that pours out the fuel?
  • how can I make it filling up automatically?
  • to know that you have to pay for it!
  • how do you pay for it?

All things that are totally common to you.
But ask a 2 year old child if she/he knows how to

"Fill up the tank"
the 2 year ld child has to learn it.

Taking extra 15 minutes to write down a very precise description speeds up finishing the project because you safe minimum 45 minutes for asking back different things and to post the details you thought you left out for faster posting.

best regards Stefan

1 Like

I'm looking to educate myself, not have someone write my code for me. I put a description of my problems in an outline format, including my code which as you can see isn't a quick down and dirty sketch. Have you ever thought that maybe I don't know the exact question I should ask? As I stated previously I'm rather new at this. Maybe you could ask me questions which might help me gain more knowledge so I can give you better information. I appreciate all the links and installing a library was one of the first things I learned. I might not be very good at the Arduino yet but I'll get there, I have been a supervisor / trainer as I used to be a police officer. I have taken people who know next to nothing about the job that they were hired for and made them experts. I didn't chastise them for what they didn't know. You are obviously very good at this and I would love to learn from you, but if you don't want to teach me then I'll just thank you for what you've help me with this far. I hope you have a wonderful evening and thank you for your help.

Did the compiling of the code that I posted in post #11 successfully compile
or
did you get a compiler-error?

If you got a compiler-error please provide the compiler-log like described here

If you had no compiler-error

As you worked as a trainer you had your trainee next to you for direct communication.
In a user-forum you have to replace the direct comunication with a very precise description of what you have observed when testing a new code-version.

So what happends if you load my code-version from post # 11 (do you recognise the details? I'm repeating the number of the post instead of writing just about "the code" which would be unprecise)

Does the code compile yes or no? etc. etc.

best regards Stefan

Hi @musicman608,

I think one of the obstacles, maybe the main one, in getting a confident answer to your question is that some, like me, are unfamiliar with ATS devices and their wiring and usage. I could study them and become familiar with how to wire them up (safely at 240 V) and how to use them in automatic mode. But naturally most here would expect you to do the legwork to avoid that.

So I suggest you ‘minimalise’ your requirement. Express it in terms of LEDs and pin states. Breadboard that as a first step and see if it gives you and us a clearer understanding.

All of which roughly echoes Stefan's more detailed suggestions in post #11.

Also, I may have missed the key point of whether there are any low voltage DC outputs from the ATS indicating its states? If not I assume that you will be sensing those from the mains voltage terminals?

Hi Stefan,
Yes the code compiled and uploaded but the LED's and the LCD display just give a quick blink every 2 seconds. When I switch it to generator power (a 6 volt battery to simulate) it displays Q A for a few seconds then goes back to blinking. I'm definitely intrigued though, I'm going to study your code and see if I can do anything with it. I wish I could send a video of what the hardware is doing. Looks like you have a different way of doing this and since I'm kind of (extremely) slow I'll need to look at the code and try to break it down to see what's happening. Getting ready to head out for work. Thanks again for everything.

Glenn

Also, I downloaded the LCD library that you googled. Do you want me to install it? I used the NewLiquidCrystal library.

Meant for Stefan, not me?

As the names of these libraries are all so similar the only thing that makes clear which library it is is the Link to GitHub to exact that particular library.

As you can see in the above posts I used this peculiar parameter "POSITIVE" for googling the library and still found a
different library than the one you used

For your small code I guess each I2C-library does the job.
Though using the quasi-standard I2C-lib makes it easier for others to test your code.
Or Using the hd44780-library. This library requires to add a second include line which depends on the used microcontroller.

@david_2018 : can you provide a compiling example
for the hd44780-library
for Arduino nano

which means all

include-lines are written and fit to Arduino-nano

best regards Stefan

Good evening Stefan,

The only thing that was wrong in your code was the following line which caused my LCD display to just blink once then go blank.

LiquidCrystal_I2C lcd(i2c_addr, en, rw, rs, d4, d5, d6, d7, bl, POSITIVE);
//LiquidCrystal_I2C lcd(i2c_addr, 20, 4); //, en, rw, rs, d4, d5, d6, d7, bl, POSITIVE); StefanL38

I changed only that line back and it's right back where I started, but with better code. So it seems that every 2 seconds it goes to a different IF statement but never stays in the state that it's supposed to be in. Example, the temperature and humidity are always accurate but the Mains and Generator status randomly change from On to Off, the SSR comes on randomly, and the LED changes colors. At first I thought maybe I had too many IF statements but now I think I just may have written them wrong. I invest as much time as I'm able to but if you could take another look at my code and give me some more guidance I'd be forever in your debt.

Your if-conditions are plain simple. You could have 50 or 100 moe if-conditions without any problem. Even much more complex if-conditions.

One possible reason for the randomly changing states could be 60 Hz flickering of your inputs.

Please post a schematic that shows all details how you connected the arduino-inputs
mainsDetect which is IO-pin 4
and
genDetect which is IO-pin 6

The randomly changing between LOW and HIGH can be caused by the AC-voltage
that - how the name says - is Alternating Current. Changing its polarity 120 times per second.

If you have connected the mains / generator-voltage without a bridge rectifier and a smoothing capacitor it might be that the input changes between LOW and HIGH 120 times per second.

best regards Stefan

Here is a code-version with the exact same switchting-logic
but I added serial printing for debugging
upload this code-version and test it.

Copy what is printed to the serial monitor and post it as a code-section

@ptillisch
can the IDE 2.X copy all content from the serial monitor?
or
is there still this bug with not copying the complete content?

best regards Stefan

I will absolutely do that but it will have to wait until tomorrow as I'm back out on the road right now. I get home tomorrow night so I will do as you said. I did however connect a different power supply other than the PC and it seemed to clean it up a lot. So I'm wondering if the Nano is not pumping out enough current what I'm doing. Instead of hooking the fans directly to the Nano I'm thinking of using relays instead. Probably use a separate power source as well so I don't tax the Nano too much.

What does that mean if you write "it seemed to clean it up a lot." ?

What differencies did you obeserve in the code-behaviour?

Trying to supply other things than low-current LEDs directly from the Arduino-Nano's
5V-pin is general a bad idea.

Did you ever measure with a digital multimeter how much current your fans are drawing?
If you draw too much current it can happen that the onboard-voltage-regulator of the Arduino-Nano goes into thermal shutdown. Which would cause the Nano to restart if the onboard-voltage-regulator has cooled down enough to switch on again.

In any case I recommend to use a beefy enough extra-power-supply to supply things like fans, standard LEDs (each LED drawing 20mA), relays etc.

You can use relays This will work.
If the fans are supplied with DC 5V or DC 12V you can use MOS-FET-modules to switch the fans on/off.

best regards Stefan

So I'm narrowing it down to a power issue. I have the nano doing way too much, like switching the SSR on and off as well as some LED's. It's also powering the LCD as well as the DHT11 sensor. Before I look further at the code I'm going to get everything I can onto a "beefy" :wink: power supply. I'll let you know how it goes.

Hi Stefan,
So it's definitely a power issue. I switched to a bigger 5V power supply and everything started doing what it is supposed to, except for the LED. It starts off green when the mains is on. When i remove the mains the blue LED flashed as it should. When I add the generator power the LED turns purple meaning the red and blue LED's switch on. If I press the reset button the Arduino reboots and then the blue LED switches off leaving only the red LED on as it should be. Everything looks right in the code so I'm not sure exactly what's happening. At this point I may just have to deal with it the way it is because the SSR does what it's supposed to. If you have any idea that would be wonderful. If not then just let me know and I'll close out the post. Truly, thank you for ALL of your help.

Glenn

Hi @musicman608

I am sorry in the posting above I forgot to post the new code-version that does the serial printing for debugging.

here it is:

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName,timeInterval) \
  { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  }

#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

#define dbgcf(myFixedText, variableName) \
  { \
    static float lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *


#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <SafeString.h> // StefanL38 new library

#define Type DHT11
//Define LCD pinout
const int en = 2, rw = 1, rs = 0, d4 = 4, d5 = 5, d6 = 6, d7 = 7, bl = 3;

//Define I2C Address
const int i2c_addr = 0x27;
LiquidCrystal_I2C lcd(i2c_addr, en, rw, rs, d4, d5, d6, d7, bl, POSITIVE);
//LiquidCrystal_I2C lcd(i2c_addr, 20, 4); //, en, rw, rs, d4, d5, d6, d7, bl, POSITIVE);

int mainsLED = 2;
int genLED = 3;
int mainsDetect = 4;
int mainsPin = 5;
int genDetect = 6;
int genPin = 7;
int sensePin = 8;
int naLED = 9;
int intakeFan = 10;
int genMnLEDred = 11;
int exhaustFan = 12;
int dt = 1000;
int genDelay = 30000;
int mainsDelay = 1000;
int mainsStatus;
int genStatus;
float humidity;
float tempC;
float tempF;
DHT HT(sensePin, Type);

byte mainsDetectState;
byte genDetectState;

// StefanL38 additional variables
unsigned long myLcdTimer;
unsigned long myBlinkTimer;
unsigned long myGenOffTimer;

cSF(MainsOnOffState_SS, 8);
cSF(GenOnOffState_SS, 8);

byte myState;

const byte sm_normalOperation  = 0;
const byte sm_waitForGenOn     = 1;
const byte sm_waitForMainsOn   = 2;
const byte sm_waitBeforeGenOff = 3;


const char myStateNames[][24] = {
  "sm_normalOperation",
  "sm_waitForGenOn",
  "sm_waitForMainsOn",
  "sm_waitBeforeGenOff"
};


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");

  lcd.begin(20, 4);
  HT.begin();
  pinMode(mainsLED, OUTPUT);
  pinMode(genLED, OUTPUT);
  pinMode(mainsDetect, INPUT);
  pinMode(mainsPin, OUTPUT);
  pinMode(genDetect, INPUT);
  pinMode(genPin, OUTPUT);
  pinMode(naLED, OUTPUT);
  pinMode(genMnLEDred, OUTPUT);
  pinMode(intakeFan, OUTPUT);
  pinMode(exhaustFan, OUTPUT);
  myLcdTimer = millis(); // initialise timing-variable
}



void loop() {
  readSensor();
  switchOnOffFans();

  //instead of delay(2000);
  // check if 2000 milliseconds of time have passed by
  // since last time this happened
  if ( TimePeriodIsOver(myLcdTimer, 2000) ) {
    // only when REALLY 2000 milliseconds have passed by
    updateLcd();
    printToSerial();
  }

  checkMainsGeneratorStatus();
  myGeneratorStateMachine();
  printIfStateChanged();

  blinkIfMainsAndGenOff();
}


void readSensor() {
  humidity = HT.readHumidity();
  tempC = HT.readTemperature();
  tempF = HT.readTemperature(true);
}


void switchOnOffFans() {

  if ((tempF) >= 69.00) {
    digitalWrite(genMnLEDred, HIGH);
    digitalWrite(intakeFan, HIGH);
    digitalWrite(exhaustFan, HIGH);
  }
  else {
    digitalWrite(genMnLEDred, LOW);
    digitalWrite(intakeFan, LOW);
    digitalWrite(exhaustFan, LOW);
  }
}


void updateLcd() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Temperature: ");
  lcd.print(tempF);
  lcd.print(" F");

  lcd.setCursor(0, 1);
  lcd.print("Humidity:    ");
  lcd.print(humidity);
  lcd.print(" % ");

  lcd.setCursor(0, 2);
  lcd.print("Mains Status:  ");
  lcd.print(MainsOnOffState_SS);

  lcd.setCursor(0, 3);
  lcd.print("Gentr Status:  ");
  lcd.print(GenOnOffState_SS);
}


void printToSerial() {
  mainsStatus = digitalRead(mainsDetect);
  genStatus = digitalRead(genDetect);

  Serial.print(" Gen Status ");
  //Serial.println(genStatus);
  Serial.println(GenOnOffState_SS);

  Serial.print(" Mains Status ");
  //Serial.println(mainsStatus);
  Serial.println(MainsOnOffState_SS);

  Serial.print("Humidity: ");
  Serial.print(humidity);
  Serial.println(" % ");

  Serial.print(" Temperature ");
  Serial.print(tempF);
  Serial.println(" F ");
}


void checkMainsGeneratorStatus() {

  mainsDetectState = digitalRead(mainsDetect);
  dbgc("01", mainsDetectState);

  genDetectState   = digitalRead(genDetect);
  dbgc("02", genDetectState);

  if ( mainsDetectState == HIGH) {
    //lcd.print("On");
    MainsOnOffState_SS = "On";
    digitalWrite(mainsLED, HIGH);
    digitalWrite(mainsPin, HIGH);
  }
  else {
    //lcd.print("Off");
    MainsOnOffState_SS = "Off";
    digitalWrite(mainsLED, LOW);
    digitalWrite(mainsPin, LOW);
  }


  if (genDetectState == HIGH) {
    //lcd.print("On");
    GenOnOffState_SS = "On";
    digitalWrite(genLED, HIGH);
    digitalWrite(genPin, HIGH);
  }
  else {
    //lcd.print("Off");
    GenOnOffState_SS = "Off";
    digitalWrite(genLED, LOW);
    digitalWrite(genPin, LOW);
  }
}


void blinkIfMainsAndGenOff() {
  if ( (mainsDetectState == LOW) && (genDetectState == LOW) ) {
    // check if "dt" milliseconds have passed by
    if ( TimePeriodIsOver(myBlinkTimer, dt) ) {
      // only when "dt" milliseconds REALLY have passed by
      digitalWrite(naLED, !digitalRead(naLED) ); // invert state of IO-pin naLED with the not-operator "!"
    }
  }
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}


void myGeneratorStateMachine() {

  switch (myState) {

    case sm_normalOperation:
      // check if mains-power is lost
      if ( digitalRead(mainsDetect) == LOW)  {
        myState = sm_waitForGenOn;
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH

    case sm_waitForGenOn:
      // check if generator produces power
      if ( digitalRead(genDetect) == HIGH)  {
        myState = sm_waitForMainsOn;
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH

    case sm_waitForMainsOn:
      // check if mains-power is back ON again
      if ( digitalRead(mainsDetect) == HIGH)  {
        // when mains-power is really ON
        myGenOffTimer = millis();       // initialise timing-variable
        myState = sm_waitBeforeGenOff;  // start waiting
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH


    case sm_waitBeforeGenOff:
      // check if 10000 milliseconds have passed by
      if ( TimePeriodIsOver(myGenOffTimer, 10000) ) {
        // when REALLY 10000 milliseconds have passed by (with mains-power on)
        digitalWrite(genPin, LOW);
        myState = sm_normalOperation;
      }

      // check if mains-power is lost
      if ( digitalRead(mainsDetect) == LOW)  {
        // when mains-power is lost
        myState = sm_waitForGenOn; // continue with waiting for generator on
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH

  } // END-OF-SWITCH
}


void printIfStateChanged() {
  static byte last_myState;

  // check if variable myState has changed
  if (last_myState != myState) {
    // when variable myState REALLY has changed
    Serial.print("myState changed from ");
    Serial.print(myStateNames[last_myState]);
    Serial.print(" to ");
    Serial.println(myStateNames[myState]);
    last_myState = myState; // update variable last_myState
  }
}

It is the same logic as the code before but does serial printing.
So if you keep your arduino connected to your computer
and open the serial monitor in the serial monitor there can be seen what the code is doing.

Especially state-changes of the state-machine and changes of the mainsdetect and gendetect were printed

As you wrote about SSRs.
What is switched with this SSR? Solid State Relay?

your ventilators or your main-power?
Do these SSR's have a galvanic isolated input?

How much amperes can these SSR switch?

best regards Stefan

2 Likes

Good morning (at least it is here lol.)

Here's the SSR I'm using.

It has a 4-32 volt input and a 480VAC output. The label says it can handle 100 amps but I'm skeptical so the absolute max amperage going through it will be 50 amps at 220 volts. I've been looking at videos and suck trying to find out if there's a specific duty cycle but so far nothing.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.