8 Port relay module + ACS712, delay time not turning on for right amount of time

I am turning off relays for 3 seconds and turning on relays for 3 seconds.

The problem is the relay was staying on for roughly 11.5 seconds (I measured with a stopwatch).
I removed the delay time in the smoothing code (void ReadCurrent()) for the ACS 712 current sensor.
That changed the on time to 6 seconds.

I recall reading somewhere on the forum that a delay time for smoothing on the ACS712 sensor is necessary to get proper readings. Maybe it's not necessary in my case since I'm measuring during the on cycle.

I've purposely put a delay in the code and divided each delay by four to measure current during four instances of the on cycle.

I need this for solenoids because their current consumption drops and heat rises over time in long cycles.

I don't see where the additional 3 seconds are being added from.
Does anyone have any ideas?
Thanks.

/*
  testing sketch for the ACS712 current sensor module

  PARAMETERS:
  vcc: 4.5 - 5.5 v
  icc: 13 - 15 ma
  zero current output voltage: vcc × 0.5 (2.5 v)

  SENSITIVITY:
  +- 5 a: 185 mv/a (+-13513 mamper @ 5v)
  +-20 a: 100 mv/a (+-25000 mamper @ 5v)
  +-30 a:  66 mv/a (+-37878 mamper @ 5v)

  theoretical resolution for 5v analog pin:
  (arduino resolution: 4.8828 mv)
   5a: 26.4ma
  20a: 48.8ma
  30a: 74.0ma

  error @ 25c: +-1.5 %
*/

#include <LiquidCrystal.h>

int mVperAmp = 185; // 185 (5A), 100 (20A), 66 (30A) mvperAmp value used, using ACS712 sensors
int testVoltage = 24;
const int numReadings = 64;

const int AIN1 = A1;
const int AIN2 = A2;
const int AIN3 = A3;
const int AIN4 = A4;
const int AIN5 = A5;
const int AIN6 = A6;
const int AIN7 = A7;
const int AIN8 = A8;

unsigned int RawValue1 = 0; // can hold up to 64 10-bit A/D readings
unsigned int RawValue2 = 0;
unsigned int RawValue3 = 0;
unsigned int RawValue4 = 0;
unsigned int RawValue5 = 0;
unsigned int RawValue6 = 0;
unsigned int RawValue7 = 0;
unsigned int RawValue8 = 0;

float ACSoffset1 = 2414.467;
float ACSoffset2 = 2424.242;
float ACSoffset3 = 2399.804;
float ACSoffset4 = 2409.580;
float ACSoffset5 = 2419.355;
float ACSoffset6 = 2414.467;
float ACSoffset7 = 2429.130;
float ACSoffset8 = 2409.580;

float Voltage1 = 0;
float Voltage2 = 0;
float Voltage3 = 0;
float Voltage4 = 0;
float Voltage5 = 0;
float Voltage6 = 0;
float Voltage7 = 0;
float Voltage8 = 0;

float Amps1 = 0;
float Amps2 = 0;
float Amps3 = 0;
float Amps4 = 0;
float Amps5 = 0;
float Amps6 = 0;
float Amps7 = 0;
float Amps8 = 0;

float Power1 = 0;
float Power2 = 0;
float Power3 = 0;
float Power4 = 0;
float Power5 = 0;
float Power6 = 0;
float Power7 = 0;
float Power8 = 0;

const int rs = 8, en = 9, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7); // initialize library and associate LCD pins with Arduino pins

unsigned long TOn = 3000; // delay time to turn relay on/off (60,000 ms = 1 minute)
unsigned long TOff = 3000; 

void setup () {
  lcd.begin(16, 2); // LCD's columns and rows: 16 columns, 2 rows
  lcd.setCursor(0, 0); // Printing position on lcd screen is column 0, row 0
  Serial.begin(9600);

  lcd.print("On:");
  lcd.print(TOn/60000);
  lcd.print("m");
  lcd.print(",");
  lcd.print("Off:");
  lcd.print(TOff/60000);
  lcd.print("m");

  DDRA = B11111111; // set PORTA (digital 22-29) to outputs

  int maxnumber = 600; // set cycle count

  for (int count = 0; count < maxnumber;) //Counter will stop counting after certain amount of cycles
  {
    lcd.setCursor(0, 1); //clears lcd before incrementing to next number
    count++;
    lcd.print("Cycle Count:");
    lcd.print(count);

    PORTA = B00000000;

    ReadCurrent();

    delay(TOn/4); //relay on time
    
    ReadCurrent();

    delay(TOn/4);

    ReadCurrent();

    delay(TOn/4);

    ReadCurrent();

    delay(TOn/4);

    ReadCurrent();
    
    PORTA = B11111111;
    
    delay(TOff); //relay off time
  }
  DDRA = B00000000;
}

void loop(void){
  }

void ReadCurrent()
{
for (int x = 0; x < numReadings; x++) // 64 analogue readings for averaging
  {
    RawValue1 = RawValue1 + analogRead(AIN1); // add each A/D reading to a total
    RawValue2 = RawValue2 + analogRead(AIN2);
    RawValue3 = RawValue3 + analogRead(AIN3);
    RawValue4 = RawValue4 + analogRead(AIN4);
    RawValue5 = RawValue5 + analogRead(AIN5);
    RawValue6 = RawValue6 + analogRead(AIN6);
    RawValue7 = RawValue7 + analogRead(AIN7);
    RawValue8 = RawValue8 + analogRead(AIN8);
    delay(2); // wait 2 milliseconds before the next loop, for the analog-to-digital converter to settle, after the last reading
  }
  
  Voltage1 = ((RawValue1 / numReadings) / 1023.0) * 5000; // Gets you mV
  Voltage2 = ((RawValue2 / numReadings) / 1023.0) * 5000;
  Voltage3 = ((RawValue3 / numReadings) / 1023.0) * 5000;
  Voltage4 = ((RawValue4 / numReadings) / 1023.0) * 5000;
  Voltage5 = ((RawValue5 / numReadings) / 1023.0) * 5000;
  Voltage6 = ((RawValue6 / numReadings) / 1023.0) * 5000;
  Voltage7 = ((RawValue7 / numReadings) / 1023.0) * 5000;
  Voltage8 = ((RawValue8 / numReadings) / 1023.0) * 5000;
  
  Amps1 = ((ACSoffset1 - Voltage1) / mVperAmp);
  Amps2 = ((ACSoffset2 - Voltage2) / mVperAmp);
  Amps3 = ((ACSoffset3 - Voltage3) / mVperAmp);
  Amps4 = ((ACSoffset4 - Voltage4) / mVperAmp);
  Amps5 = ((ACSoffset5 - Voltage5) / mVperAmp);
  Amps6 = ((ACSoffset6 - Voltage6) / mVperAmp);
  Amps7 = ((ACSoffset7 - Voltage7) / mVperAmp);
  Amps8 = ((ACSoffset8 - Voltage8) / mVperAmp);

  Power1 = Amps1 * testVoltage;
  Power2 = Amps2 * testVoltage;
  Power3 = Amps3 * testVoltage;
  Power4 = Amps4 * testVoltage;
  Power5 = Amps5 * testVoltage;
  Power6 = Amps6 * testVoltage;
  Power7 = Amps7 * testVoltage;
  Power8 = Amps8 * testVoltage;  

  Serial.print("Raw Value 1 = " ); // shows pre-scaled value
  Serial.print(RawValue1 / numReadings);
  Serial.print("\t mV 1 = "); // shows the voltage measured
  Serial.print(Voltage1, 3); // the '3' after voltage allows you to display 3 digits after decimal point
  Serial.print("\t Amps 1 = ");
  if (Amps1 >= 0) 
  Serial.print(" ");
  Serial.print(Amps1, 3);
  Serial.print("\t Power1 = ");
  Serial.print (Power1, 3);
  Serial.println();

  Serial.print("Raw Value 2 = " );
  Serial.print(RawValue2 / numReadings);
  Serial.print("\t mV 2 = ");
  Serial.print(Voltage2, 3);
  Serial.print("\t Amps 2 = ");
  if (Amps2 >= 0) 
  Serial.print(" ");
  Serial.print(Amps2, 3);
  Serial.print("\t Power2 = ");
  Serial.print(Power2, 3);
  Serial.println();

  Serial.print("Raw Value 3 = " );
  Serial.print(RawValue3 / numReadings);
  Serial.print("\t mV 3 = ");
  Serial.print(Voltage3, 3);
  Serial.print("\t Amps 3 = ");
  if (Amps3 >= 0) 
  Serial.print(" ");
  Serial.print(Amps3, 3);
  Serial.print("\t Power3 = ");
  Serial.print(Power3, 3);
  Serial.println();

  Serial.print("Raw Value 4 = " );
  Serial.print(RawValue4 / numReadings);
  Serial.print("\t mV 4 = ");
  Serial.print(Voltage4, 3);
  Serial.print("\t Amps 4 = ");
  if (Amps4 >= 0) 
  Serial.print(" ");
  Serial.print(Amps4, 3);
  Serial.print("\t Power4 = ");
  Serial.print(Power4, 3);
  Serial.println();

  Serial.print("Raw Value 5 = " );
  Serial.print(RawValue5 / numReadings);
  Serial.print("\t mV 5 = ");
  Serial.print(Voltage5, 3);
  Serial.print("\t Amps 5 = ");
  if (Amps5 >= 0) 
  Serial.print(" "); 
  Serial.print(Amps5, 3);
  Serial.print("\t Power5 = ");
  Serial.print(Power5, 3);
  Serial.println();

  Serial.print("Raw Value 6 = " );
  Serial.print(RawValue6 / numReadings);
  Serial.print("\t mV 6 = ");
  Serial.print(Voltage6, 3);
  Serial.print("\t Amps 6 = ");
  if (Amps6 >= 0) 
  Serial.print(" "); 
  Serial.print(Amps6, 3);
  Serial.print("\t Power6 = ");
  Serial.print(Power6, 3);
  Serial.println();

  Serial.print("Raw Value 7 = " );
  Serial.print(RawValue7 / numReadings);
  Serial.print("\t mV 7 = ");
  Serial.print(Voltage7, 3);
  Serial.print("\t Amps 7 = "); 
  if (Amps7 >= 0) 
  Serial.print(" "); 
  Serial.print(Amps7, 3);
  Serial.print("\t Power7 = ");
  Serial.print(Power7, 3);
  Serial.println();

  Serial.print("Raw Value 8 = " );
  Serial.print(RawValue8 / numReadings);
  Serial.print("\t mV 8 = ");
  Serial.print(Voltage8, 3);
  Serial.print("\t Amps 8 = ");
  if (Amps8 >= 0) 
  Serial.print(" "); 
  Serial.print(Amps8, 3);
  Serial.print("\t Power8 = ");
  Serial.print(Power8, 3);
  Serial.println();

  Serial.println();
  
  RawValue1 = 0; // reset value
  RawValue2 = 0;
  RawValue3 = 0;
  RawValue4 = 0;
  RawValue5 = 0;
  RawValue6 = 0;
  RawValue7 = 0;
  RawValue8 = 0;
  
  //delay(1000);
}

What Arduino are you using?

Arduino MEGA 2560

const int AIN1 = A1;
const int AIN2 = A2;
const int AIN3 = A3;
const int AIN4 = A4;
const int AIN5 = A5;
const int AIN6 = A6;
const int AIN7 = A7;
const int AIN8 = A8;

Why on earth define then at all if all you do is add the 'IN' bit?

Have a look at arrays :wink: That would reduce the code by at least 1/4th and including looking it up saved you halve the time :wink:

Talking about time, delay() will stop EVERYTHING dead in it's tracks. Use millis() or micros() (see Blink without delay) to be able to do other stuff (like, getting the timing straight).

Also, I would suggest to stop using direct port manipulation and just use Arduino functions for the outputs. A heck of a lot easier and more problem free.

Ever thought of using arrays?

Probably takes 3 seconds to print all that out...

I'll get around to commenting, in the meantime, have a look into the Streaming library.

Ok I'll check out the streaming library.

Not too familiar with arrays but I'll start researching about them.

With regards to port manipulation
My application needed to turn on all solenoids at the same time.
I can't recall but last time I had trouble trying to turn them on at the same time with
digital pin callouts. Maybe it was just my lack of enough coding knowledge.

Also in this certain scenario, timing is not that critical.
But in time sensitive applications I read port manipulation is faster for turning things on/off
Maybe useful for others using solid state relays?

Don't know how long I can look at this without rewriting it. My finger's getting tired scrolling up and down. You really need to embrace arrays.

In any event, let's pick at it. First your print statements, example

	Serial.print("Raw Value 2 = " );
	Serial.print(RawValue2 / numReadings);
	Serial.print("\t mV 2 = ");
	Serial.print(Voltage2, 3);
	Serial.print("\t Amps 2 = ");
	if (Amps2 >= 0)
	Serial.print(" ");
	Serial.print(Amps2, 3);
	Serial.print("\t Power2 = ");
	Serial.print(Power2, 3);
	Serial.println();

You are aware that the if (Amps2 >= 0) only pertains to Serial.print(" ") ?

The basics of arrays are dead simple

//this
something1 = 42;
//becomes
something[1] = 42;

//but enables you to do something with all 10 somethings by doing
for(byte i = 0; i < 10; i++){
  something[i] = 42;
}

And with solenoids (which are slow and mechanical aka slow) I doubt it's noticeable if one solenoid is turned on 1ms later. Port manipulation does offer faster responses at the cost of you knowing exactly what you do. But like I said, controlling something mechanical like a solenoid does not need to be ns precise. :wink: And even with a solid state application you need some pretty specific need if 1ms or 100uS will matter that much :slight_smile:

Before we go any further, is it okay to change your code to this:

/*
testing sketch for the ACS712 current sensor module

PARAMETERS:
vcc: 4.5 - 5.5 v
icc: 13 - 15 ma
zero current output voltage: vcc × 0.5 (2.5 v)

SENSITIVITY:
+- 5 a: 185 mv/a (+-13513 mamper @ 5v)
+-20 a: 100 mv/a (+-25000 mamper @ 5v)
+-30 a:  66 mv/a (+-37878 mamper @ 5v)

theoretical resolution for 5v analog pin:
(arduino resolution: 4.8828 mv)
5a: 26.4ma
20a: 48.8ma
30a: 74.0ma

error @ 25c: +-1.5 %
*/

#include <LiquidCrystal.h>
#include <Streaming.h>

int mVperAmp = 185; // 185 (5A), 100 (20A), 66 (30A) mvperAmp value used, using ACS712 sensors
int testVoltage = 24;
const int numReadings = 64;

const byte AIN[] = {0, A1, A2, A3, A4, A5, A6, A7, A8};

uint16_t raw_value[9] = {0};

float acs_offset[] = {0, 2414.467, 2424.242, 2399.804, 2409.580, 2419.355, 2414.467, 2429.130, 2409.580};

float voltage[9] = {0};

float amps[9] = {0};

float power[9] = {0};

const int rs = 8, en = 9, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7); // initialize library and associate LCD pins with Arduino pins

//unsigned long TOn = 3000; // delay time to turn relay on/off (60,000 ms = 1 minute)
uint16_t    TOn     = 3000; // delay time to turn relay on/off (60,000 ms = 1 minute)
uint16_t    qton    = 750;
uint16_t    TOff    = 3000;
//unsigned long TOff = 3000;

void setup () 
{
    lcd.begin(16, 2); // LCD's columns and rows: 16 columns, 2 rows
    lcd.setCursor(0, 0); // Printing position on lcd screen is column 0, row 0
    Serial.begin(9600);

    lcd << "On:" << (TOn/60000) << "m, Off:" << (TOff/60000) << "m" ;
    DDRA = 0B11111111; // set PORTA (digital 22-29) to outputs

    int maxnumber = 600; // set cycle count

    for (int count = 0; count < maxnumber;) //Counter will stop counting after certain amount of cycles
    {
        lcd.setCursor(0, 1); //clears lcd before incrementing to next number
        count++;
        lcd << "Cycle Count:" << count;
        PORTA = 0B00000000;

        for (byte i = 1; i <= 4; i++)
        {
            ReadCurrent();
            delay(qton);
        }
        ReadCurrent();

        PORTA = 0B11111111;

        delay(TOff); //relay off time
    }
    DDRA = 0B00000000;
}

void loop(void)
{
}

void ReadCurrent()
{
    for (int x = 0; x < numReadings; x++) // 64 analogue readings for averaging
    {
        for (byte i = 1; i <= 8; i++) (raw_value[i] += analogRead(AIN[i]));
        delay(2); // wait 2 milliseconds before the next loop, for the analog-to-digital converter to settle, after the last reading
    }

    for (byte i = 1; i <= 8; i++) (voltage[i] = (((raw_value[i] / numReadings) / 1023) * 5000));

    for (byte i = 1; i <= 8; i++) (amps[i] = ((acs_offset[i] - voltage[i]) / mVperAmp));

    for (byte i = 1; i <= 8; i++) (power[i] = (amps[i] * testVoltage));

    for (byte i = 1; i <= 8; i++)
    {
        Serial << "Raw value " << i << " = " << (raw_value[i] / numReadings) << "\tmV " << i << " = " << _FLOAT(voltage[i],3) << "\tAmps " << i << " = ";
        if (amps[i] > 0) Serial.print(" ");
        Serial << _FLOAT(amps[i],3) << "\tPower1 = " << _FLOAT(power[i], 3) << "\n";
        raw_value[i] = 0;
    }
    Serial.println();

    //delay(1000);
}

It's 2590 bytes lighter (code) and uses ~300 less RAM plus its a lot easier to read (I think).

If you want to turn on ports together on a '328P:
PORTB = 0x00; // or 0xff D8-9-10-11-12-13
PORTC = 0x00; // or 0xff D14-15-16-17-18-19
PORTD = 0x00; // or 0xff D0-1-2-3-4-5-6-7

CrossRoads, see posts 1 and 2.

Yeah, sorry, I should have done some more reading before posting.
Lot of delay()s in that code.

Ok I tried out your code.
I like.
Definitely looks cleaner.
Thanks.

I just changed this to:

for (byte i = 1; i <= 8; i++) (voltage[i] = ((raw_value[i] / numReadings) / 1023) * 5000);

from:

for (byte i = 1; i <= 8; i++) (voltage[i] = (((raw_value[i] / numReadings) / 1023) * 5000));

Can't figure out why results are so different.
The timing is still 6 seconds.

These are the readouts I get with nothing hooked up:

New code:

Raw value 1 = 513	mV 1 = 0.000	Amps 1 =  13.051	Power1 = 313.228
Raw value 2 = 523	mV 2 = 0.000	Amps 2 =  13.104	Power1 = 314.496
Raw value 3 = 510	mV 3 = 0.000	Amps 3 =  12.972	Power1 = 311.326
Raw value 4 = 505	mV 4 = 0.000	Amps 4 =  13.025	Power1 = 312.594
Raw value 5 = 513	mV 5 = 0.000	Amps 5 =  13.078	Power1 = 313.862
Raw value 6 = 516	mV 6 = 0.000	Amps 6 =  13.051	Power1 = 313.228
Raw value 7 = 524	mV 7 = 0.000	Amps 7 =  13.130	Power1 = 315.130
Raw value 8 = 518	mV 8 = 0.000	Amps 8 =  13.025	Power1 = 312.594

Old code:

Raw Value 1 = 508	 mV 1 = 2482.894	 Amps 1 = -0.370	 Power1 = -8.877
Raw Value 2 = 519	 mV 2 = 2536.657	 Amps 2 = -0.608	 Power2 = -14.584
Raw Value 3 = 506	 mV 3 = 2473.118	 Amps 3 = -0.396	 Power3 = -9.511
Raw Value 4 = 498	 mV 4 = 2434.018	 Amps 4 = -0.132	 Power4 = -3.170
Raw Value 5 = 508	 mV 5 = 2482.894	 Amps 5 = -0.343	 Power5 = -8.243
Raw Value 6 = 506	 mV 6 = 2473.118	 Amps 6 = -0.317	 Power6 = -7.609
Raw Value 7 = 519	 mV 7 = 2536.657	 Amps 7 = -0.581	 Power7 = -13.949
Raw Value 8 = 515	 mV 8 = 2517.106	 Amps 8 = -0.581	 Power8 = -13.949

Will this become problematic?:

uint16_t

If I use anything longer than 65535 ms for timing?

I took this out, is it redundant???:

  delay(qton);
        }
        ReadCurrent(); //took this out
   for (int count = 0; count < maxnumber;) //Counter will stop counting after certain amount of cycles
    {
        lcd.setCursor(0, 1); //clears lcd before incrementing to next number
        count++;
        lcd << "Cycle Count:" << count;
        PORTA = 0B00000000;

        for (byte i = 1; i <= 4; i++)
        {
            ReadCurrent();
            delay(qton);
        }
        ReadCurrent();

        PORTA = 0B11111111;

        delay(TOff); //relay off time
    }

Also I'm going to take a look at the millis() examples like BlinkwithoutDelay.
Not as easy as delay for me to use but I'll try to figure it out.

Unfortunately your output means nothing to me as I have no idea what the numbers should look like. Try taking out all the delays and see what happens.

Hi knightridar, what is the purpose of your program, what does it do? :confused: :slight_smile:

for (byte i = 1; i <= 8; i++) (voltage[i] = (((raw_value[i] / numReadings) / 1023) * 5000));

If you want a more correct reading (and faster calculation as a bonus), change 1023 to 1024 :wink:

And if you want to gain even more, change the order of calculation so all the multiplication is done first:

for (byte i = 1; i <= 8; i++) (voltage[i] = (raw_value[i] * 5000UL) / numReadings / 1024);

And a quick note, float is NOT simply a decimal point. Float is only accurate up to 6 digits, uses 32-bit and is slow to calculate. Fixed point is much better. For example, if you want to store €1,37, don't store it as a float of 1.37 but as 137 in a integer and remember it's stored in cents. When you print it, simply add a decimal :wink: