Problem with digitalWrite on Arduino

For some reason, my code works just fine in that tests are measured and executed, the serial monitor displays the correct strings, and my Blynk app receives and displays readings from 1-RTC and 2-temp sensors, as intended, but for some reason, only the digitalWrite(); commands are not executed. The serial.print(); lines that are bunched up with the respective digitalWrite(); are executed, but not the digital writes.

I don’t know if there is a conflict between Blynk and INPUT_PULLUP, but I doubt it as I see quite a few other Blynk sketches being shared using the input_pullup. Can someone review my code and help me figure out why these digitalWrite(); commands are not executed?


#include <SPI.h>                    // Used by Blynk
#include <Ethernet.h>               // Used by Blynk
#include <BlynkSimpleEthernet.h>    // Used by Blynk
#include <Wire.h>                   // I2c
#include "DHT.h"                    // DHT data wire connected to I/O pin with a 10k pullup resistor to 5V               
#include <SimpleTimer.h>            // used to run functions at preset intervals
#include "RTClib.h"                 // RealTimeClock Library for DS1307 and DS3231

//  DHT Reference Values - CHANGE THESE TO MANAGE YOUR ROOM'S CLIMATE - //
byte hiMaxTemp = 90;   // temp that triggers heat removal fan on
byte lowMaxTemp = 75;  // temp that triggers heat removal fan off
byte hiMinTemp = 70;   // temp that triggers heater on
byte lowMinTemp = 60;  // temp that triggers heater off
byte hiHum = 75;       // High humidity value that triggers dehumidifier on
byte lowHum = 70;      // Low humidity value that triggers dehumidifier off

//Digital Pin Peristaltic Pump Dosing Heads - 00 Series Pins - PWM/12v motors
#define GHfloraGro 2          //Pump 1 - Primary Nutrient               -  nutePumps[0]
#define GHfloraMicro 3        //Pump 2 - Primary Nutrient               -  nutePumps[1]
#define GHfloraBloom 4        //Pump 3 - Primary Nutrient               -  nutePumps[2]
#define GHfloraBlend 5        //Pump 4 - Metabolic Supplement           -  nutePumps[3]
#define GHarmorSi 6           //Pump 5 - Silica Supplement (raises pH)  -  nutePumps[4]
#define GHcaliMagic 7         //Pump 6 - Calcium/Magnesium Supplement   -  nutePumps[5]
#define GHkoolBloom 8         //Pump 7 - Bloom Supplement               -  nutePumps[6]
#define GHphDown 9            //Pump 8 - pH Lowering Agent              -  nutePumps[7]
int nutePumps[8] {GHfloraGro, GHfloraMicro, GHfloraBloom, GHfloraBlend, GHarmorSi, GHcaliMagic, GHkoolBloom, GHphDown};

//Analong Pin Sensor Assignments - Analog Pins
//#define pH A0
//#define PPM A1
//#define waterTemp A2


#define lightA 22             //Relay 1/a  -  Relays[0]
#define lightB 23             //Relay 2/b  -  Relays[1]
#define pumpA 24              //Relay 3/c  -  Relays[2]
#define pumpB 25              //Relay 4/d  -  Relays[3]
#define ROpump 26             //Relay 5/e  -  Relays[4]
#define scrubberFan 27        //Relay 6/f  -  Relays[5]
#define heatVentA 28          //Relay 7/g  -  Relays[6]
#define heatVentB 29          //Relay 8/h  -  Relays[7]
int Relays[8] {lightA, lightB, pumpA, pumpB, ROpump, scrubberFan, heatVentA, heatVentB};
//Digital Pin Relay Assignments - 30 Series Pins - 120VAC~ switching
//#define 30                  //2nd bank of 8 relays
//#define 31
//#define 32
//#define 33
//#define 34
//#define 35
//#define 36
//#define 37
//int Relays2[8] {30, 31, 32, 33, 34, 35, 36, 37};

//Digital Pin Valves[] array elements - 40 Series Pins - Hi/Lo Solenoid Valves
#define source 40             //waterValve[0]
#define upperProbes 41        //waterValve[1]
#define upperNuteRail 42      //waterValve[2]
#define siteReturnA 43        //waterValve[3]
#define siteReturnB 44        //waterValve[4]
#define lowerProbes 45        //waterValve[5]
#define lowerNuteRail 46      //waterValve[6]
#define betweenRails 47       //waterValve[7]
#define waste 48              //waterValve[8]
#define mainAccessA 49        //waterValve[9]
#define feedPlantA 50         //waterValve[10]
#define mainAccessB 51        //waterValve[11]
#define feedPlantB 52         //waterValve[12]
int Valves[13] {source, upperProbes, upperNuteRail, siteReturnA, siteReturnB, lowerProbes, lowerNuteRail, betweenRails, waste, mainAccessA, feedPlantA, mainAccessB, feedPlantB};

#define TURN_ON 0 // TURN_ON and TURN_OFF are defined to account for Active High relays
#define TURN_OFF 1 // Used to switch relay states for on/off of AC devices   

RTC_DS1307 RTC;
float UTCOffset = -5.0;    // Your timezone relative to UTC (http://en.wikipedia.org/wiki/UTC_offset)

#define BLYNK_PRINT Serial
char auth[] = "pasteAuthTokenFromEmailHere";
WidgetLCD lcdA(V2);  //Set LCD widget to Advanced Mode - Widget to display project clock

DHT dhtA(A0, DHT22);     // DHT instance named dhtA, I/O pin and sensor type
DHT dhtB(A1, DHT22);     // DHT instance named dhtB, I/O pin and sensor type

SimpleTimer timer;       // SimpleTimer instance named timer

void setup()
{
  Serial.begin(9600);
  //Serial2.begin() // For other baud rates
  //Serial3.begin() // For other baud rates
  Blynk.begin(auth);
  dhtA.begin();
  dhtB.begin();
  Wire.begin(); 
  RTC.begin();
  RTC.adjust(DateTime(__DATE__, __TIME__));

  //pinMode(nutePumps[8], INPUT_PULLUP);
  //pinMode(Valves[13], INPUT_PULLUP);
  pinMode(Relays[8], INPUT_PULLUP);           // 
  pinMode(Relays[8], OUTPUT);
  pinMode(nutePumps[8], OUTPUT);
  pinMode(Valves[13], OUTPUT);
 
  while (Blynk.connect() == false) {}

  timer.setInterval(10000L, timeRoutine); // 10 second intervals between timed routiness
  timer.setInterval(11100L, climateRoutine); // 11.1 second intervals between climate routines
  //timer.setInterval(xxxxL, testResB);
  //timer.setInterval(xxxxL, testResA);
  //timer.setInterval(xxxxL, testResB);

  delay(2500);
}

void loop()
{
  Blynk.run();
  timer.run();
}

//Functions

void climateRoutine()
{
  byte h1 = dhtA.readHumidity();          // f1 and h1 are fahrenheit and humidity readings
  byte f1 = dhtA.readTemperature(true);   // from DHT/A
  byte h2 = dhtB.readHumidity();          // f2 and h2 are fahrenheit and humidity readings 
  byte f2 = dhtB.readTemperature(true);   // from DHT/B

  Blynk.virtualWrite(V0, f1);      //  Set Virtual Pin 0 frequency to PUSH in Blynk app
  Blynk.virtualWrite(V1, h1);      //  Set Virtual Pin 1 frequency to PUSH in Blynk app
  Blynk.virtualWrite(V3, f2);      //  Set Virtual Pin 2 frequency to PUSH in Blynk app
  Blynk.virtualWrite(V4, h2);      //  Set Virtual Pin 3 frequency to PUSH in Blynk app
  Serial.println(f1);
  Serial.println(h1);
  Serial.println(f2);
  Serial.println(h2);
  //   Tests temp & humidity to see if preset values are exceed.
  //   If exceeded, a relay is trigger high to power an exhaust fan or heater.
  //**************************** REMOVING HEAT *************************************//
  if (f1 >= hiMaxTemp)  //if "f1" is greater than or equal to hiMaxTemp,
  {
    digitalWrite(heatVentA, TURN_ON);  // TURN_ON heatVentA (fan).
    Serial.print("\t");
    Serial.println(F("Exhausting the heat from A")); //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  else if (f1 <= lowMaxTemp)  //  or else if "f1" is less than or equal to lowMaxTemp
  {
    digitalWrite(heatVentA, TURN_OFF); //  TURN_OFF relay E.
  }
}

void timeRoutine()
{
  DateTime now = RTC.now();  // reads time at beginning of loop

  byte twelveHour = now.hour() - 12; // Variable used to display 13+ hours in 12 hour format
  byte zeroHour = 12;                // Variable use to convert "0" zero hour to display it as 12:00+
  byte displayHour;
  byte MIN = now.minute();
  byte SEC = now.second();
  char* meridian;

  if (now.hour() == 0)  // First we test if the hour reads "0"
  {
    displayHour = zeroHour;
    meridian = "AM";
  }
  else if (now.hour() >= 13)  // if no, Second we test if the hour reads "13 or more"
  {
    displayHour = twelveHour;
    meridian = "PM";
  }
  else
  {
    displayHour = now.hour();
    meridian = "AM";
  }

  char timeStamp[11];
  char dateStamp[11];
  sprintf(timeStamp, "%02d:%02d:%02d-%02s", displayHour, MIN, SEC, meridian);
  sprintf(dateStamp, "%02d/%02d/%04d", now.month(), now.day(), now.year());
  String ts;
  String ds;
  ts = timeStamp;
  ds = dateStamp;

  lcdA.clear();            //Advanced Mode
  lcdA.print(3, 0, ts);    //Advanced Mode
  lcdA.print(3, 1, ds);    //Advanced Mode
  Serial.println(ts);
  Serial.print(ds);

  //*************** 12/12 BloomA Light - 6AM-6PM**********************************//
  //***** Adjust hours and minutes in accordance with 24 hour time format. *******//
  //***** Create tests where true is ON time and false is OFF time. *************//
  boolean lightAstate = false;
  if (now.hour() >= 6 && now.hour() <= 17) lightAstate = true;
  if (lightAstate == true)
  {
    digitalWrite(lightA, TURN_ON);
    Serial.print("\t");
    Serial.print(F("Bloom A Light On"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  else
  {
    digitalWrite(lightA, TURN_OFF);
    Serial.print("\t");
    Serial.println(F("Bloom A Light OFf"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  //*************** 12/12 BloomB Light - 6PM-6AM**********************************//
  //***** Adjust hours and minutes in accordance with 24 hour time format. *******//
  //***** lightB is lit during the opposing 12 hours to lightA. *****************//
  boolean lightBstate = false;
  if (lightAstate == false) lightBstate = true;
  if (lightBstate == true)
  {
    digitalWrite(lightB, TURN_ON);
    Serial.print("\t");
    Serial.print(F("Bloom B Light On"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  else
  {
    digitalWrite(lightB, TURN_OFF);
    Serial.print("\t");
    Serial.println(F("Bloom B Light OFf"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  //*********************************** FEED TIMEs - pumpA ******************************************//
  // By testing hour once and minute twice, we isolate the "on" time and the duration it is "on". *****//
  // If the Boolean becomes true, D32 is HIGH until Boolean becomes false again. **********************//
  boolean pumpAstate = false;
  if (now.hour() == 9 && now.minute() >= 0 && now.minute() < 10) pumpAstate = true;    //9:00 am - 10mins
  if (now.hour() == 15 && now.minute() >= 30 && now.minute() < 40) pumpAstate = true;  //3:30 pm - 10mins
  if (now.hour() == 22 && now.minute() >= 30 && now.minute() < 33) pumpAstate = true;  //10:30pm - 03mins
  if (pumpAstate == true)
  {
    digitalWrite(pumpA, TURN_ON);
    Serial.print("\t");
    Serial.println(F("Feeding PlantA"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  else
  {
    digitalWrite(pumpA, TURN_OFF);
  }
  //*******************************Feed Times - pumpB *******************************************//
  boolean pumpBstate = false;
  if (now.hour() == 9 && now.minute() >= 0 && now.minute() < 10) pumpBstate = true;    //9:00 am - 10mins
  if (now.hour() == 15 && now.minute() >= 30 && now.minute() < 40) pumpBstate = true;  //3:30 pm - 10mins
  if (now.hour() == 22 && now.minute() >= 30 && now.minute() < 33) pumpBstate = true;  //10:30pm - 03mins
  if (pumpBstate == true)
  {
    digitalWrite(pumpB, TURN_ON);
    Serial.print("\t");
    Serial.println(F("Feeding PlantB"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  else
  {
    digitalWrite(pumpB, TURN_OFF);
  }
}
1 Like

@myggle your sketch states the relays are active HIGH yet you have TURN_ON defined as 0 and 1 for TURN_OFF. Surely this is not right?

0 or LOW sent from a relay pin on the arduino is when the relay closes and AC~ flows to a device. 1 or HIGH causes the relay to release and disrupts the AC~ current from flowing. Is active low the correct way to describe that?

Yes.
Relays can be active high or active low. From my experience normally high.
Are you sure yours are active low?

That sounds like you have your relay wired backwards (as far as the device it is controlling). Most relays have three connections for the control part (many written in chinese, so not too helpful :slight_smile: )

COMMON - Usually the terminal in the middle and typically where the negative or ground of the device being controlled goes.

NORMALLY CLOSED - When set active LOW (and it’s natural resting state) there will be no power to the relay coil, and this will connect to COMMON

NORMALLY OPEN - When set active HIGH, there will be power to the relay coil, allowing this to connect with COMMON

Sainsmart Relay Module Circuit Diagram

The above link is to the circuit diagram. There is a good amount of added language on the bottom of the one pdf that clearly outlines this fact.

Ah, looks like “they” made this relay board with an inverted logic control… i.e. very cheaply designed :wink:

As the linked schematics show, current flows from VCC through the optocoupler and into IN1 and sinks on the digital pin on my Mega. This is how I can achieve full isolation in my circuit. FYI, the relays have their own 5V/4A power supply. So the normally jumpered 3 pin header has the jumper removed and a 5V and GND supply lines connected to the respective pins. The Arduino circuitry doesn’t cross the relay circuitry.

I know. But they could have added an extra inverter circuit to allow the more “accepted” LOW = OFF = NormClose) state. Which is also prefered for better power efficiency with battery powered power setups as well as avoiding the everything-turns-on-for-a-fraction-of-a-second-at-reboot issue.

But as long as you are aware of it (and you are), then all is good - perhaps just add a note in your commenting so when others look at it they don’t assume that is an issue.

From my experience, LOW active :grin:

Ah ha!! so @Costas splurges for the good stuff and @Jamin shops on Chinebay :stuck_out_tongue: (like I am one to talk… :blush:)

1 Like

It’s getting to the point where some products are very difficult to find at reasonable cost except way across the pond. Not saying this is the case here, but as a beginner, I couldn’t pass up 8 bundled relays with opto isolation. My project will account for many relays, so I bought 2 modules.

Aside from the HIGH/LOW debate, can anyone point out any other flaws in my code, or know of a reason why only the digitalWrite commands in my sketch wouldn’t execute? Also, note, I use the following sketch to test the relays themselves, and all toggle in sequence.

/* 8 Relay Module configured for Opto-isolation
   Arduino UNO pins 3 to 10 connected to IN1 to IN8
   Connections: http://i.imgur.com/MDNQGeC.png */

void setup() {
    for (int i = 22; i <= 29; i++) {
    pinMode(i, INPUT_PULLUP);
    pinMode(i, OUTPUT); // defaults HIGH (relays off)
    }

}

void loop() {
    for (int i = 22; i <= 29; i++) {
    digitalWrite(i, LOW); // energize relays until all on
    delay(1000);
  }
    for (int i = 22; i <= 29; i++) {
    digitalWrite(i, HIGH); // de-energize relays until all off
    delay(1000);
  }
}

Well this is strange.

pinMode(i, INPUT_PULLUP);
pinMode(i, OUTPUT); // defaults HIGH (relays off)

You’re telling the IO to be INPUT then OUTPUT. Just call OUTPUT only.
You comment is also incorrect… you want to use a digitalWrite to set the pin HIGH/LOW.

So should be:

pinMode(i, OUTPUT);
digitalWrite(i, HIGH); // assuming active LOW, switch off. 

The next issue is your loop().
Assuming you’re not using this code with Blynk later (cant use delay()) then you just need to make a small change.

void loop() {
  // do all ON
  for (int i = 22; i &lt;= 29; i++) {
    digitalWrite(i, LOW); // energize relays until all on
  }
  // once done all on, wait 1sec
  delay(1000);
  // do all OFF
  for (int i = 22; i &lt;= 29; i++) {
    digitalWrite(i, HIGH); // de-energize relays until all off
  }
  // once done all off, wait 1sec
  delay(1000);
  // repeat
}
1 Like

I found the problem. It was with the way I (didn’t) address the array Relays[8] correctly. I quick fixed it by commenting out the array lines and adding in the variable names in pinMode as OUTPUTs. I will try to fix this after work today and share good or bad, and go from there.

gtg

1 Like

hello!

what @Jamin says, he is absolutely right:
by default all arduino pins are set to input. there is no reason to declare input_pullup then output. or maybe you should elaborate why you did this?

other things:

  1. probably it would be better to use <DHT.h> instead of "DHT.h" at line 5., and the same applies to “RTClib.h”, line 7.

  2. it is a good idea to always define the #defines in UPPERCASE, this way you always know that it is not a variable.

  3. the biggest problem i see is this part (besides setting input then output):

    //pinMode(nutePumps[8], INPUT_PULLUP);
    //pinMode(Valves[13], INPUT_PULLUP);
    pinMode(Relays[8], INPUT_PULLUP); //
    pinMode(Relays[8], OUTPUT);
    pinMode(nutePumps[8], OUTPUT);
    pinMode(Valves[13], OUTPUT);

i assume, that you think these lines will set all elements in the array as outputs.
but this is a nasty bug!

actually, when you write nutePumps[8] or Valves[13], you are not referring to the whole array, just to the 8th or 13th element of the nutePumps and Valves array. which are inexistent, because array indexing begins from 0, not from 1! so the last index of the nutePumps array will be 7, not 8. and the same applies to Valves array.

form my experience, assigning value to an nonexistent index of an array will silently write that value to a different part of the flash, causing severe anomalies and hard to find bugs. http://stackoverflow.com/questions/29431559/difference-between-accessing-non-existent-array-index-and-existing-but-empty-ind

you should definitely read and understand how arrays work: https://www.arduino.cc/en/Reference/Array

  1. you just can not set multiple pinModes using this method:

pinMode(Valves[13], OUTPUT);

even if the respective index is valid, it will set ONLY the 13th pin value to output, not the whole array.

to set the state of multiple pins, you should use a for cycle, something like this:

for (byte light = LIGHTmin; light < LIGHTmax + 1; light++) {  // setup lights as outputs

    pinMode(light, OUTPUT);
    digitalWrite(light, LOW);  // or HIGH, as you prefer
  }

or like this:

for (byte i = 0; i < maxValue + 1; i++) pinMode(myArray[i], INPUT_PULLUP);

EDIT: i just saw that you find the problem, while i was writing this post. but now i will leave it anyway, hope it will be useful for someone.

1 Like

I tried another approach with the array in that I changed it to read;

pinMode(Relays[0, 1, 2, 3, 4, 5, 6, 7], OUTPUT);

I think it will do what I hope it does. It compiles, but I can’t test it till I get my project assembled which will be a couple days.

Info about setting to INPUT_PULLUP first

The use of the INPUT_PULLUP before OUTPUT is to default the pin state to HIGH which for an active low relay means all loops opened (connected devices off), and once into the loop, HIGH and LOW will set the state of the relays. It was also mentioned that I could set the OUTPUT to HIGH.

As far as

"DHT.h" 

vs

<DHT.h>

the adafruit library for the sensor on github uses the " " instead of < > in the example sketch, and this is also true for the RTClib examples. If memory serves me correctly, I think I ran into these problems over a year ago in this same sketch and I was getting compiler errors because some of my libraries were included between < > instead of " ".

DHT Tester example

Regarding the capitalization of my defines. I am still on the uphill side of my learning curve and have not come across this before now. I will read up on it though and make the changes when I understand more so what I’m doing.

i’m afraid you still didn’t understand how arrays / pinMode function works. maybe i’m wrong, but i’m 99.9% certain that will not work this way…

afaik the pinMode function takes exactely 2 arguments, first is the pin number, second is the mode. it will run only once, not for every element in the array. you should use the examples i gave in my previous post. (but of course you can experiment, it wont breake anything)

as for the input pullups before setting to output, i understand what are you talking about, but still not see the logic behind it: first you set the pin to high state, than immediately in the next line you set to output: it will reset anyway, so it will stay in the “input pullup high” state just for one microsecond (maybe), before you are setting to output…

2 Likes

You need a “for loop” to go through the array and assign pinmodes

1 Like