marcosjom
Published © GPL3+

Network From Solar Street-Lamps

Interconnect solar street-lamps by adding monitoring and communication devices into them.

IntermediateWork in progress4,353
Network From Solar Street-Lamps

Things used in this project

Story

Read more

Schematics

Monitoring solar street lamps voltage and current

Code

Monitoring solar street lamps voltage and current

Arduino
/*
2020-08-06 by Marcos Ortega
Project: https://create.arduino.cc/projecthub/marcosjom/network-from-solar-street-lamps-e39e39

This code constantly reads the input from two voltage and current sensors,
builds and average about every 100ms, and 10 avgs for each second.

Each second an output is send to the serial port in CSV format, using tabulations as separator, as example:

Bat  14.48 v -0.684  a Pnl 0.00  v 0.293 a
Bat 14.48 v -0.586  a Pnl 0.00  v 0.342 a
Bat 14.45 v -0.586  a Pnl 0.00  v 0.293 a
Bat 14.45 v -0.684  a Pnl 0.00  v 0.244 a
Bat 14.45 v -0.684  a Pnl 0.06  v 0.244 a
Bat 14.70 v -0.635  a Pnl 0.06  v 0.195 a

A buzzer beeps every 3 secons.

*/

//---------
//- Header declarations
//---------

//Analog read

typedef enum ENAnalogSampleType_ {
  ENAnalogSampleType_BatteryV = 0,
  ENAnalogSampleType_BatteryA,
  ENAnalogSampleType_PanelV,
  ENAnalogSampleType_PanelA,
  ENAnalogSampleType_Count
} ENAnalogSampleType;

typedef struct STNBAnalogClock_ {
  unsigned long     readLastTime    = 0; //Last time a sample was read
  unsigned long     readWaitAccum   = 0; //Time accumulated since last read
  unsigned long     readsCount      = 0; //Ammount of read accumulated
  int               avgsCount       = 0; //Ammount of avgs populated
  int               avgsIdxLast     = 0; //Latest avg idx on circular queue
} STNBAnalogClock;

typedef struct STNBAnalogSample_ {
  int pin                 = A0; //analog pin
  unsigned long accum     = 0;  //current accumulation
  int avgs[10]            = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
} STNBAnalogSample;

typedef struct STNBAnalogReader_ {
  STNBAnalogClock    aClock; //reads coordinator
  STNBAnalogSample   samples[ENAnalogSampleType_Count]; //samples
} STNBAnalogReader;

//Buzzer

typedef struct STNBBuzzer_ {
  const int pin             = 52; //pin
  unsigned long lastActTime = 0;  //Last time a beep was made
  unsigned long waitAccum   = 0;  //Time accumulated since last beep
} STNBBuzzer;

//Config

typedef struct STNBCfg_ {
  //------
  //System
  //------
  struct {
    //Arduino reference voltage
    const float voltsRef = 5.0f; //(verify yours with a multimeter)
  } sys;
  //------
  //Voltage sensor
  //------
  struct {
    //Manual divider
    struct {
      const float r1 = 33000.f; //ohms
      const float r2 = 4700.f; //ohms
    } divider;
    //Pre-made module
    struct {
      const float r1 = 30000.f; //ohms
      const float r2 =  7500.f; //ohms
    } module;
  } voltage;
  //------
  //Current sensor
  //------
  struct {
    //Pre-made module
    struct {
      //ACS712 ELC-05; +/- 5A; 185 mV/A
      //ACS712 ELC-20; +/- 20A; 100 mV/A
      //ACS712 ELC-30; +/- 30A; 66 mV/A
      const float miliVoltsPerAmp = 100.f;
    } module;
  } current;
} STNBCfg;

//---------
//- Global objects
//---------

STNBAnalogReader state;
STNBBuzzer buzzer;
STNBCfg cfg;

//---------
//- Methods forward declaration
//---------

//Get the raw avg of all samples collected in the avgs-circular-queue
int samplesAvg(const STNBAnalogSample* src);

//Get the avg in voltage value collected from the voltage-sensor-module.
float samplesAvgAsVoltageFromSensor(const STNBAnalogSample* src);

//Get the avg in voltage value collected from the divider made with resistor.
float samplesAvgAsVoltageFromDivider(const STNBAnalogSample* src);

//Get the avg in current (amps) value collected from the current-sensor-module
float samplesAvgAsAmps(const STNBAnalogSample* src);

//---------
//- Setup
//---------

void setup() {
  //Init serial
  {
    Serial.begin(9600);
    while (!Serial) {
      ; // wait for serial port to connect. Needed for Native USB only
    }
  }
  //Turn off builtin-led
  {
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, LOW);
  }
  //Buzzer
  {
    pinMode(buzzer.pin, OUTPUT);// set digital IO pin pattern, OUTPUT to be output 
    buzzer.lastActTime = micros();
    buzzer.waitAccum = 0;
  }
  //Init analog reader
  {
    state.aClock.readLastTime  = micros();
    state.aClock.readWaitAccum = 0;
    state.aClock.readsCount    = 0;
    state.aClock.avgsCount     = 0;
    state.aClock.avgsIdxLast   = 0;
    {
      int i; for(i = 0; i < ENAnalogSampleType_Count; i++){
        STNBAnalogSample* ss = &state.samples[i];
        //Pin
        switch(i){
          case ENAnalogSampleType_BatteryV:
            ss->pin = A1;
            break;
          case ENAnalogSampleType_BatteryA:
            ss->pin = A2;
            break;
          case ENAnalogSampleType_PanelV:
            ss->pin = A3;
            break;
          case ENAnalogSampleType_PanelA:
            ss->pin = A4;
            break;
          default:
            //Error
            ss->pin = A0;
            break;
        }
        //Zeroes
        ss->accum = 0;
        {
          int i; for(i = 0; i < (sizeof(ss->avgs) / sizeof(ss->avgs[0])); i++){
            ss->avgs[i] = 0;
          }
        }
      }
    }
  }
}

//---------
//- Loop
//---------

void loop() {
  unsigned long curTime = micros();
  //Tick for IR detection
  {
    STNBAnalogClock* coord = &state.aClock;
    //Accumulate time
    if(coord->readLastTime < curTime){
      coord->readWaitAccum += curTime - coord->readLastTime;
    } else {
      coord->readWaitAccum = (4294967295 - coord->readLastTime) + curTime;
    }
    //Do one-tick action
    {
      int i; for(i = 0; i < ENAnalogSampleType_Count; i++){
        STNBAnalogSample* ss = &state.samples[i];
        const int val =  analogRead(ss->pin);
        ss->accum += val;
      }
      coord->readsCount++;
    }
    //Process accumulated events
    while(coord->readWaitAccum > 1000000){
      const int avgsArrSz = (sizeof(state.samples[0].avgs) / sizeof(state.samples[0].avgs[0]));
      {
        //Move index
        if(coord->avgsCount != 0){
          coord->avgsIdxLast++;
          if(coord->avgsIdxLast == avgsArrSz){
            coord->avgsIdxLast = 0;
          }
        }
        //Populate slots
        {
          if(coord->readsCount > 0){
            int i; for(i = 0; i < ENAnalogSampleType_Count; i++){
              STNBAnalogSample* ss = &state.samples[i];
              ss->avgs[coord->avgsIdxLast] = ss->accum / coord->readsCount;
            }
          } else {
            int i; for(i = 0; i < ENAnalogSampleType_Count; i++){
              STNBAnalogSample* ss = &state.samples[i];
              ss->avgs[coord->avgsIdxLast] = 0;
            }
          }
        }
        //Increase count
        if(coord->avgsCount < avgsArrSz){
          coord->avgsCount++;
        }
        //Calculate avgs and print
        {
          if(coord->avgsIdxLast == avgsArrSz - 1){
            //Battery
            {
              const float avgV  = samplesAvgAsVoltageFromSensor(&state.samples[ENAnalogSampleType_BatteryV]);
              const float avgA  = samplesAvgAsAmps(&state.samples[ENAnalogSampleType_BatteryA]);
              Serial.print("Bat\t"); 
              Serial.print(avgV, 2); Serial.print("\tv\t");
              Serial.print(avgA, 3); Serial.print("\ta\t");
            }
            //Panel
            {
              const float avgV  = samplesAvgAsVoltageFromDivider(&state.samples[ENAnalogSampleType_PanelV]);
              const float avgA  = samplesAvgAsAmps(&state.samples[ENAnalogSampleType_PanelA]);
              Serial.print("Pnl\t"); 
              Serial.print(avgV, 2); Serial.print("\tv\t");
              Serial.print(avgA, 3); Serial.print("\ta\t");
            }
            Serial.print("\n");
           }
        }
        //Reset counters
        {
          int i; for(i = 0; i < ENAnalogSampleType_Count; i++){
            STNBAnalogSample* ss = &state.samples[i];
            ss->accum = 0;
          }
        }
        coord->readsCount = 0;
      }
      coord->readWaitAccum -= 100000;
    }
    //Keep time
    coord->readLastTime = curTime;
    //Beep and delay
    {
      //Accumulate time
      if(buzzer.lastActTime < curTime){
        buzzer.waitAccum += curTime - buzzer.lastActTime;
      } else {
        buzzer.waitAccum = (4294967295 - buzzer.lastActTime) + curTime;
      }
      buzzer.lastActTime = curTime;
      //
      if(buzzer.waitAccum > 3000000){
        //Delay by beeing
        int i; for(i = 0;i < 10; i++){ // output a frequency sound
          digitalWrite(buzzer.pin, HIGH);// sound
          delay(1);
          digitalWrite(buzzer.pin, LOW);//not sound
          delay(1);
        }
        buzzer.waitAccum = 0;
      } else {
        //dealy one tick next read
        delay(10);
      }
    }
  }
}

//Get the raw avg of all samples collected in the avgs-circular-queue
int samplesAvg(const STNBAnalogSample* src){
  int total = 0, count = 0, i;
  for(i = 0; i < (sizeof(src->avgs) / sizeof(src->avgs[0])); i++){
    total += src->avgs[i];
    count++;
    break;
  }
  return total / count;
}

//Get the avg in voltage value collected from the voltage-sensor-module.
float samplesAvgAsVoltageFromSensor(const STNBAnalogSample* src){
  const float value = samplesAvg(src);
  const float vout  = (value * cfg.sys.voltsRef) / 1024.0f;
  const float vin = vout / ( cfg.voltage.module.r2 / (cfg.voltage.module.r1 + cfg.voltage.module.r2) ); 
  return vin;
}

//Get the avg in voltage value collected from the divider made with resistor.
float samplesAvgAsVoltageFromDivider(const STNBAnalogSample* src){
  const float value = samplesAvg(src);
  const float vout  = (value * cfg.sys.voltsRef) / 1024.0f;
  const float vin = vout / ( cfg.voltage.divider.r2 / (cfg.voltage.divider.r1 + cfg.voltage.divider.r2) ); 
  return vin;
}

//Get the avg in current (amps) value collected from the current-sensor-module
float samplesAvgAsAmps(const STNBAnalogSample* src){
  const int adcValAbs     = samplesAvg(src); //From 0 to 1024
  const int adcValSign    = adcValAbs - (1024 / 2); //From -512 to 512
  const float adcValSignRel = (float)adcValSign / (float)(1024 / 2); //From -1.0f to 1.0f
  const float adcMiliVolts = adcValSignRel * (cfg.sys.voltsRef * 1000.0f / 2.0f);
  const float currentValue = adcMiliVolts /  cfg.current.module.miliVoltsPerAmp;
  return currentValue; //+ 0.1465; //adjustment
}

//------------
//End-of-program
//------------

Credits

marcosjom
1 project • 1 follower

Comments