Help with Project, Please

Hi,

I’ve come to a dead end with my project and would appreciate you help.
I build a generator controller using two atmega 328p, One as Main and 2nd as the slave. The 2nd Atmega is responsible for calculating KWH generated and so on… This data is send via I2c to the Main which then saves the kWH to EEPROM (on the MAin MCU) on when the Generator is switch off.

The generator running indication retrieved by means off an AUX relay which inputs to the Atmega a LOW signal while running and HIGH when off. All works perfectly when the gen is running. Data is send constantly to the Main and the kwh is updated correctly.

The Slave MCU resets the kwh cache every time the gen is started as to begin fresh at 0.00kwh.

My problem comes in when the gen is switched off…What is supposed to happen is that the Slave MCU must send the data to the MCU before it clears the data. But it seems that contact bounce in the Aux Gen relay messes this up for me as this worked perfectly when I simulated it on the breadboard during development.

Here is my code for the SLAVE and the MAin (only part where data is read) Thanks for reading through my story :slight_smile:

Slave

#include "EmonLib.h"             // Include Emon Library
#include<Wire.h>                 //for I2C


EnergyMonitor emon1;             // Create an instance

char KW[5];
char AMP[7];
char PF[4];
char VOLT[6];
char KWH [4];

float amp = 0.00;
float pf = 0.00;
float volts = 0.00;
float kw = 0.00;
float kWh = 0.00 ;            // Energy in kWh

unsigned long last_time = 0;
unsigned long current_time = 0;
int lastState;            //for kwh logging
int currentState;            //for kwh logging

const int inputPin = 9;
const int debounceDelay = 2000;

void setup()
{
  //Serial.begin(9600);
  pinMode(inputPin, INPUT);// PIN FOR GEN AC SENSE PIN          

  emon1.voltage(1, 259.75, 1.3);  // Voltage: input pin, calibration, phase_shift
  emon1.current(2 , 60);       // Current: input pin, calibration.
  Wire.begin(2);
  Wire.onRequest(Request);

}

void loop()
{

  emon1.calcVI(20, 2000);        // Calculate all. No.of half wavelengths (crossings), time-out
  float realPower       = emon1.realPower;        //extract Real Power into variable
  float apparentPower   = emon1.apparentPower;    //extract Apparent Power into variable
  float powerFActor     = emon1.powerFactor;      //extract Power Factor into Variable
  float supplyVoltage   = emon1.Vrms;             //extract Vrms into Variable
  float Irms            = emon1.Irms;             //extract Irms into Variable

  last_time = current_time;
  current_time = millis();
  kWh = kWh +  realPower * ((( current_time - last_time) / 3600000.0) / 1000) ; // calculating energy in kWatt-Hour

  kw = realPower / 1000;
  amp = Irms / 1000;
  pf = powerFActor;
  volts = supplyVoltage;

  if (debounce(inputPin))
  {
    kWh = 0.00;
  }


}

boolean debounce(int pin)
{
  boolean state;
  boolean previousState;
  previousState = digitalRead(pin); // store switch state
  for (int counter = 0; counter < debounceDelay; counter++)
  {
    delay(1); // wait for 1 millisecond
    state = digitalRead(pin); // read the pin
    if ( state != previousState)
    {
      counter = 0; // reset the counter if the state changes
      previousState = state; // and save the current state
    }
  }
  // here when the switch state has been stable longer than the debounce period
  return state;
}

void Request()
{
  if (1 == 1)
  {
    dtostrf(kWh, 4, 2, KWH);
    Wire.write(KWH); // appx 5 bytes
    Wire.write(",");
    delay(10);
    dtostrf(volts, 6, 2, VOLT);
    Wire.write(VOLT); // appx 6 bytes
    Wire.write(",");
    delay(10);
    dtostrf(pf, 4, 2, PF);
    Wire.write(PF); // appx 4 bytes
    Wire.write(",");
    delay(10);
    dtostrf(amp, 7, 5, AMP);
    Wire.write(AMP); // appx 6 bytes
    Wire.write(",");
    delay(10);
    dtostrf(kw, 5, 3, KW);
    Wire.write(KW); // appx 8 bytes
    Wire.write("\n");


  }
  else
  { /*Do Nothing*/
  }

}

MAIN MCU

Wire.requestFrom(2, 30);    // request 35 bytes from slave device #2 one more than string lenght
  String string, string1, string2, string3, string4, string5;
  do   // slave may send less than requested
  {
    char c = Wire.read(); // receive a byte as character

    string = string + c; //Keep saving whatever is comming

    string1 = string.substring(0, 4); //slpit String - kwh
    string2 = string.substring(5, 11); // Split - v
    string3 = string.substring(12, 16); //slpit - pf
    string4 = string.substring(17, 24); // Split - A
    string5 = string.substring(25, 30); //slpit - KW


  } while (Wire.available());

  float KVA = string5.toFloat();
  float PF = string3.toFloat();
  float VOLT = string2.toFloat();
  float AMP = string4.toFloat();
  float KWH = string1.toFloat();  //will be received

  int VA = KVA * 1000;
  float A = AMP * 1000.00;



  float LASTKWH = 0.00;
  float TENKKWH = 0.00;
  float TOTKWH = 0.00;

  EEPROM.get(eeAddress13, LASTKWH);
  EEPROM.get(eeAddress14, TENKKWH);
  EEPROM.get(eeAddress15, TOTKWH);

  byte currentState = digitalRead(GEN_AC);
  if (currentState == LOW && lastState == HIGH) {
    //DO NOTHING//
  }

  if (currentState == HIGH && lastState == LOW) {
    LASTKWH = KWH;
    TENKKWH = TENKKWH + LASTKWH;
    TOTKWH = TOTKWH + LASTKWH;

    EEPROM.put(eeAddress13, LASTKWH);
    EEPROM.put(eeAddress14, TENKKWH);
    EEPROM.put(eeAddress15, TOTKWH);
  }
  lastState = currentState;

  EEPROM.get(eeAddress13, LASTKWH);
  EEPROM.get(eeAddress14, TENKKWH);
  EEPROM.get(eeAddress15, TOTKWH);

  Serial.print("t30.txt=\"");  //last run kwh
  Serial.print(LASTKWH, 2); // This is the value you want to send to that object and atribute mentioned before.
  Serial.print("\"");  // Since we are sending text, and not a number, we need to send double quote before and after the actual text.
  Serial.write(0xff);  // We always have to send this three lines after each command sent to the nextion display.
  Serial.write(0xff);
  Serial.write(0xff);

}

Is there a pull-up or pull-down resistor to prevent the Arduino input pin floating when the relay contacts are open?

6v6gt:
Is there a pull-up or pull-down resistor to prevent the Arduino input pin floating when the relay contacts are open?

Hi, Yes I originally had some issues with it since I used internal pull ups which were to weak. I added a 2.2k external pull-up and a 0.22uF cap between ground and pin which sorted my issue on that.

Maybe I posted this in the wrong Forum section, please advise the correct section.

Thanks

I'd say it is in the correct part of the forum.

I'm just trying to work out the intercommunication between the two I2C devices when the relay contacts open (go HIGH). The slave watches this condition but must wait for the master to contact it, at which point it transfers the information back to the master. Is that correct ?

I guess that your debouncing, which contains delays and appears to last two seconds, is the problem. Can't you use millis() to determine if the "OFF" condition has been stable for a number of milliseconds before determining that the generator has shut down ?

Edit


Something like:

Pseudocode.

If ( pin is HIGH and  last transition was more that 2000 milliseconds ago) state = OFF ;
else state = ON ;

6v6gt:
I'd say it is in the correct part of the forum.

I'm just trying to work out the intercommunication between the two I2C devices when the relay contacts open (go HIGH). The slave watches this condition but must wait for the master to contact it, at which point it transfers the information back to the master. Is that correct ?

I guess that your debouncing, which contains delays and appears to last two seconds, is the problem. Can't you use millis() to determine if the "OFF" condition has been stable for a number of milliseconds before determining that the generator has shut down ?

Edit


Something like:

Pseudocode.

If ( pin is HIGH and  last transition was more that 2000 milliseconds ago) state = OFF ;
else state = ON ;

The gen status is wired to the Slave only to reset the kWh variable back to 0.00kWh when Gen started, this is to prevent the kWh variable and get rid of the any rubbish due to ADC instabilities when the Gen was off.
When the gen is running other values including the live kwh measurements is send perfectly via I2C from the SLAVE to Master. The master also checks when the Gen switches of to save the last kWh value to it's eeprom for later recall.
but it seems as if the Slave resets its data to soon, before the Master can receives it.
I've tried varies codes, I think the code below (SLAVE) is similar what you are talking about, but can't get it to work either.

void loop()
{
  emon1.calcVI(20, 2000);        // Calculate all. No.of half wavelengths (crossings), time-out
  //emon1.serialprint();           // Print out all variables (realpower, apparent power, Vrms, Irms, power factor)
  float realPower       = emon1.realPower;        //extract Real Power into variable
  float apparentPower   = emon1.apparentPower;    //extract Apparent Power into variable
  float powerFActor     = emon1.powerFactor;      //extract Power Factor into Variable
  float supplyVoltage   = emon1.Vrms;             //extract Vrms into Variable
  float Irms            = emon1.Irms;             //extract Irms into Variable
  last_time = current_time;
  current_time = millis();
  kWh = kWh +  realPower * ((( current_time - last_time) / 3600000.0) / 1000) ; // calculating energy in kWatt-Hour
  kw = realPower / 1000;
  amp = Irms / 1000;
  pf = powerFActor;
  volts = supplyVoltage;
//Serial.println(kWh);
//Serial.println(digitalRead(GEN_AC));
byte currentState = digitalRead(GEN_AC);
    if (currentState == LOW && lastState == HIGH) {  //if gen is ON and was OFF
      if (millis() - time > 1000)  //Has one second passed?
      {
        kWh = 0.00;
        time = millis();           //and reset time.
      }
    }
    if (currentState == HIGH && lastState == LOW) {  //if gen is OFF and was ON
      //DO NOTHING//
    }
    lastState = currentState;
    
}
void Request()
{
  if (1 == 1)
  {
    dtostrf(kWh, 4, 2, KWH);
    Wire.write(KWH); // appx 5 bytes
    Wire.write(",");
    delay(10);
    dtostrf(volts, 6, 2, VOLT);
    Wire.write(VOLT); // appx 6 bytes
    Wire.write(",");
    delay(10);
    dtostrf(pf, 4, 2, PF);
    Wire.write(PF); // appx 4 bytes
    Wire.write(",");
    delay(10);
    dtostrf(amp, 7, 5, AMP);
    Wire.write(AMP); // appx 6 bytes
    Wire.write(",");
    delay(10);
    dtostrf(kw, 5, 3, KW);
    Wire.write(KW); // appx 8 bytes
    Wire.write("\n");
    
  }
  else
  { /*Do Nothing*/
  }
}

I can't see all your code, but there appears to be at least these issues:

  1. Detection of a stable OFF state from the generator
  2. Slave resetting data before it has been successfully transferred to the master.
  3. delay() statements should almost certainly not be used in I2C call back routines.

The slave, as it is configured, cannot simply transmit data to the master. The master must request it and the slave satisfies that request.

I'd probably not delete the KWh counter on the slave until the next time the master contacts the slave following the slave's transmission of the KWh counter after a generator stop. Maybe something like:

master contacts slave
slave responds
master contacts slave
slave responds
master contacts slave
slave responds
generator stops
master contacts slave
slave responds
master contacts slave
slave resets kwh counter
slave responds

6v6gt:
I can't see all your code, but there appears to be at least these issues:

  1. Detection of a stable OFF state from the generator
  2. Slave resetting data before it has been successfully transferred to the master.
  3. delay() statements should almost certainly not be used in I2C call back routines.

The slave, as it is configured, cannot simply transmit data to the master. The master must request it and the slave satisfies that request.

I'd probably not delete the KWh counter on the slave until the next time the master contacts the slave following the slave's transmission of the KWh counter after a generator stop. Maybe something like:

master contacts slave
slave responds
master contacts slave
slave responds
master contacts slave
slave responds
generator stops
master contacts slave
slave responds
master contacts slave
slave resets kwh counter
slave responds

The Master requests the information, and the slave sends it. The kwh data gets deleted before it can be requested again, and when the Master request it, it comes through as 0.00 or what ever is has after it resets.

Wire.requestFrom(2, 30);    // request 35 bytes from slave device #2 one more than string lenght
  String string, string1, string2, string3, string4, string5;
  do   // slave may send less than requested
  {
    char c = Wire.read(); // receive a byte as character
    string = string + c; //Keep saving whatever is comming
    string1 = string.substring(0, 4); //slpit String - kwh
    string2 = string.substring(5, 11); // Split - v
    string3 = string.substring(12, 16); //slpit - pf
    string4 = string.substring(17, 24); // Split - A
    string5 = string.substring(25, 30); //slpit - KW
  } while (Wire.available());
  float KVA = string5.toFloat();
  float PF = string3.toFloat();
  float VOLT = string2.toFloat();
  float AMP = string4.toFloat();
  float KWH = string1.toFloat();  //will be received
  int VA = KVA * 1000;
  float A = AMP * 1000.00;

snippit from Master, I can send you my complete codes but it is 800 lines

paulw2:
snippit from Master, I can send you my complete codes but it is 800 lines

Just use the attach function to include the .ino files in a post then you don't hit the 9000 char limit.

6v6gt:
Just use the attach function to include the .ino files in a post then you don't hit the 9000 char limit.

never done an attachment, do I need to upload to dropbox first and paste a link?

No, when you click reply, you'll see a link for Attachments and other options.

Hi, Here is the .ino files. Please bear in mind I’m not a programmer, the comments is outdated and sometimes copy pasted from donar code. sorry about that, still in process of fixing it

nO_holidaymode_PCB.ino (33.9 KB)

SLAVEyy.ino (3 KB)

Just to save me searching through 100's of lines of code, can you briefly explain

a) the timing schedule of the master and how often it contacts the slave.
b) how does the generator get switched off. Under control of this system or independently ?
c) do you have schematic diagram which shows both master and slave and the connections to the generator.

From the slave code, it appears to clear the KWh counter after 10 seconds without any check to see if the master has issued a call to collect that data.

6v6gt:
Just to save me searching through 100's of lines of code, can you briefly explain

a) the timing schedule of the master and how often it contacts the slave.
b) how does the generator get switched off. Under control of this system or independently ?
c) do you have schematic diagram which shows both master and slave and the connections to the generator.

From the slave code, it appears to clear the KWh counter after 10 seconds without any check to see if the master has issued a call to collect that data.

Just to save me searching through 100's of lines of code, can you briefly explain

a) the timing schedule of the master and how often it contacts the slave.
The master calls the slave whenever the program is free (from Line 497)
b) how does the generator get switched off. Under control of this system or independently ?
The Master determines this by Checking a AUX relay (AC) for return of Mains AC, it then enters a program and waits 10sec before it switches of the gen. (from Line 268)

c) do you have schematic diagram which shows both master and slave and the connections to the generator.
attached

From the slave code, it appears to clear the KWh counter after 10 seconds without any check to see if the master has issued a call to collect that data.
My intention with this is , once the Gen started, the SLAVE waits 10sec and then resets the variable kwh to start at 0. 10 sec is a bit long, its just for testing.

The Aux relay that signals gen start/stop is an 230Vac relay that is energised by the Generator. On startup of the gen when volts is still not high enough to operate coil, the contacts chatter alot for 300ms or so before it switches, the same with when you switch of the gen, as the voltage drops, the contacts chatters before it falls out.

That is the main cause of my issues, what I'm trying to do is implement a software debounce that can ignore the chattet of contacts until it is stable.

Ok. I’m just focusing on the schematic at the moment.

  1. I don’t see the hardware debouncing which you mentioned in post #2.
  2. The voltage divider for measure appears to apply more than 5volts to the pin.
  3. The leds across the relay coils appear to be reversed and the purpose of the zener diodes there is not clear. I've not seen a zener as a flyback diode before.
  4. There are no explicit I2C pull-up resistors. Are you relying on the ones say in a ds3231 module (HD1)?
  5. What is the distance between the master and slave ? If these are physically close together, can you explain what the reason is for distributing the functions over two ATmega328p devices.

I’ll look again at the debouncing.

I've adapted a debounce routine which requires a stable state of xMs before it accepts a transition and does not use delay(). Just add the block of code to your loop() and add the statement to reset the kWh counter then test it. Static variables, in case you don't know, are initialised only once and then retain any value they have been set to between calls of the routine which contain them. I hope you have some success with it.

// Debounce Routine
// respects changes which have been stable for > xMs

uint8_t GEN_AC = 2 ;


void setup() {
  Serial.begin (115200 ) ;
  pinMode( GEN_AC, INPUT_PULLUP ) ;
  Serial.println("starting. . .") ;
}


void loop() {

  // ==========
  const uint32_t stabilisationTimeMs = 1500UL ;  // set as required
  static bool oldPinState = HIGH ;
  static uint32_t lastPinTransitionAtMs = millis() ;
  static bool applicationState = HIGH ; // resolved after pin stable for xMs
  
  bool pinState = digitalRead( GEN_AC ) ;
  if ( pinState != oldPinState ) {
    lastPinTransitionAtMs = millis() ;
    oldPinState = pinState ;
  }

  if ( millis() - lastPinTransitionAtMs > stabilisationTimeMs ) {
    // stable situation for > X ms
    if ( applicationState == HIGH && pinState == LOW ) {
      // transition HIGH to LOW
      Serial.println("transition HIGH to LOW") ;
      applicationState = LOW ;
    }
    else if ( applicationState == LOW && pinState == HIGH ) {
      // transition LOW to HIGH
      Serial.println("transition LOW to HIGH") ;
      Serial.println("reset kWh here") ;
      applicationState = HIGH ;
    }
  }
  // ==========

  
}

6v6gt:
Ok. I’m just focusing on the schematic at the moment.

  1. I don’t see the hardware debouncing which you mentioned in post #2.
  2. The voltage divider for measure appears to apply more than 5volts to the pin.
  3. The leds across the relay coils appear to be reversed and the purpose of the zener diodes there is not clear. I've not seen a zener as a flyback diode before.
  4. There are no explicit I2C pull-up resistors. Are you relying on the ones say in a ds3231 module (HD1)?
  5. What is the distance between the master and slave ? If these are physically close together, can you explain what the reason is for distributing the functions over two ATmega328p devices.

I’ll look again at the debouncing.

Ok. I'm just focusing on the schematic at the moment.

  1. I don't see the hardware debouncing which you mentioned in post #2.
    -Sorry I still had to add it in the schematic, I just quickly soldered it to the board without updating.
  2. The voltage divider for measure appears to apply more than 5volts to the pin.
    -Correct, never updated
  3. The leds across the relay coils appear to be reversed and the purpose of the zener diodes there is not clear.
    -LED Corrected, never updated. The Zener there to protect against switching surges of the relay coil.
  4. There are no explicit I2C pull-up resistors. Are you relying on the ones say in a ds3231 module (HD1)?
    -It works fine without it, didn't think it was necessary, what do you think?
  5. What is the distance between the master and slave ? If these are physically close together, can you explain what the reason is for distributing the functions over two ATmega328p devices.
    -They are next to each other on the same PCB, Dynamic memory is already at 75% usage on the Master 328p, hence a split approach.

I've attached an updated schematic. The original schematic for the PCB is drawn on PCB express and not so clear, hence I've redone it on EAsy Eda.

Schematic revised

If memory was the issue for going to a distributed architecture - may be you could consider an alternative path with an arduino Mega (mini version if space is a concern)?

How often do you update the EEPROM? 100,000 write capability can come quickly

J-M-L:
If memory was the issue for going to a distributed architecture - may be you could consider an alternative path with an arduino Mega (mini version if space is a concern)?

How often do you update the EEPROM? 100,000 write capability can come quickly

I'm thinking about using a Atmega 1284p if I can't get this working. Programming isn't my best skill and what I know is what I read up on the internet and got from Forums like this. I rely heavily on Libraries and my programs can get cluster phobic if I try to fit everything to one program :smiley: It is only writing once every gen start cycle, so stricly speaking it will last 100 000 gen start cycles.