Components and supplies
Arduino Mega 2560
Project description
Code
Monitoring solar street lamps voltage and current
arduino
1/* 22020-08-06 by Marcos Ortega 3Project: https://create.arduino.cc/projecthub/marcosjom/network-from-solar-street-lamps-e39e39 4 5This code constantly reads the input from two voltage and current sensors, 6builds and average about every 100ms, and 10 avgs for each second. 7 8Each second an output is send to the serial port in CSV format, using tabulations as separator, as example: 9 10Bat 14.48 v -0.684 a Pnl 0.00 v 0.293 a 11Bat 14.48 v -0.586 a Pnl 0.00 v 0.342 a 12Bat 14.45 v -0.586 a Pnl 0.00 v 0.293 a 13Bat 14.45 v -0.684 a Pnl 0.00 v 0.244 a 14Bat 14.45 v -0.684 a Pnl 0.06 v 0.244 a 15Bat 14.70 v -0.635 a Pnl 0.06 v 0.195 a 16 17A buzzer beeps every 3 secons. 18 19*/ 20 21//--------- 22//- Header declarations 23//--------- 24 25//Analog read 26 27typedef enum ENAnalogSampleType_ { 28 ENAnalogSampleType_BatteryV = 0, 29 ENAnalogSampleType_BatteryA, 30 ENAnalogSampleType_PanelV, 31 ENAnalogSampleType_PanelA, 32 ENAnalogSampleType_Count 33} ENAnalogSampleType; 34 35typedef struct STNBAnalogClock_ { 36 unsigned long readLastTime = 0; //Last time a sample was read 37 unsigned long readWaitAccum = 0; //Time accumulated since last read 38 unsigned long readsCount = 0; //Ammount of read accumulated 39 int avgsCount = 0; //Ammount of avgs populated 40 int avgsIdxLast = 0; //Latest avg idx on circular queue 41} STNBAnalogClock; 42 43typedef struct STNBAnalogSample_ { 44 int pin = A0; //analog pin 45 unsigned long accum = 0; //current accumulation 46 int avgs[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 47} STNBAnalogSample; 48 49typedef struct STNBAnalogReader_ { 50 STNBAnalogClock aClock; //reads coordinator 51 STNBAnalogSample samples[ENAnalogSampleType_Count]; //samples 52} STNBAnalogReader; 53 54//Buzzer 55 56typedef struct STNBBuzzer_ { 57 const int pin = 52; //pin 58 unsigned long lastActTime = 0; //Last time a beep was made 59 unsigned long waitAccum = 0; //Time accumulated since last beep 60} STNBBuzzer; 61 62//Config 63 64typedef struct STNBCfg_ { 65 //------ 66 //System 67 //------ 68 struct { 69 //Arduino reference voltage 70 const float voltsRef = 5.0f; //(verify yours with a multimeter) 71 } sys; 72 //------ 73 //Voltage sensor 74 //------ 75 struct { 76 //Manual divider 77 struct { 78 const float r1 = 33000.f; //ohms 79 const float r2 = 4700.f; //ohms 80 } divider; 81 //Pre-made module 82 struct { 83 const float r1 = 30000.f; //ohms 84 const float r2 = 7500.f; //ohms 85 } module; 86 } voltage; 87 //------ 88 //Current sensor 89 //------ 90 struct { 91 //Pre-made module 92 struct { 93 //ACS712 ELC-05; +/- 5A; 185 mV/A 94 //ACS712 ELC-20; +/- 20A; 100 mV/A 95 //ACS712 ELC-30; +/- 30A; 66 mV/A 96 const float miliVoltsPerAmp = 100.f; 97 } module; 98 } current; 99} STNBCfg; 100 101//--------- 102//- Global objects 103//--------- 104 105STNBAnalogReader state; 106STNBBuzzer buzzer; 107STNBCfg cfg; 108 109//--------- 110//- Methods forward declaration 111//--------- 112 113//Get the raw avg of all samples collected in the avgs-circular-queue 114int samplesAvg(const STNBAnalogSample* src); 115 116//Get the avg in voltage value collected from the voltage-sensor-module. 117float samplesAvgAsVoltageFromSensor(const STNBAnalogSample* src); 118 119//Get the avg in voltage value collected from the divider made with resistor. 120float samplesAvgAsVoltageFromDivider(const STNBAnalogSample* src); 121 122//Get the avg in current (amps) value collected from the current-sensor-module 123float samplesAvgAsAmps(const STNBAnalogSample* src); 124 125//--------- 126//- Setup 127//--------- 128 129void setup() { 130 //Init serial 131 { 132 Serial.begin(9600); 133 while (!Serial) { 134 ; // wait for serial port to connect. Needed for Native USB only 135 } 136 } 137 //Turn off builtin-led 138 { 139 pinMode(LED_BUILTIN, OUTPUT); 140 digitalWrite(LED_BUILTIN, LOW); 141 } 142 //Buzzer 143 { 144 pinMode(buzzer.pin, OUTPUT);// set digital IO pin pattern, OUTPUT to be output 145 buzzer.lastActTime = micros(); 146 buzzer.waitAccum = 0; 147 } 148 //Init analog reader 149 { 150 state.aClock.readLastTime = micros(); 151 state.aClock.readWaitAccum = 0; 152 state.aClock.readsCount = 0; 153 state.aClock.avgsCount = 0; 154 state.aClock.avgsIdxLast = 0; 155 { 156 int i; for(i = 0; i < ENAnalogSampleType_Count; i++){ 157 STNBAnalogSample* ss = &state.samples[i]; 158 //Pin 159 switch(i){ 160 case ENAnalogSampleType_BatteryV: 161 ss->pin = A1; 162 break; 163 case ENAnalogSampleType_BatteryA: 164 ss->pin = A2; 165 break; 166 case ENAnalogSampleType_PanelV: 167 ss->pin = A3; 168 break; 169 case ENAnalogSampleType_PanelA: 170 ss->pin = A4; 171 break; 172 default: 173 //Error 174 ss->pin = A0; 175 break; 176 } 177 //Zeroes 178 ss->accum = 0; 179 { 180 int i; for(i = 0; i < (sizeof(ss->avgs) / sizeof(ss->avgs[0])); i++){ 181 ss->avgs[i] = 0; 182 } 183 } 184 } 185 } 186 } 187} 188 189//--------- 190//- Loop 191//--------- 192 193void loop() { 194 unsigned long curTime = micros(); 195 //Tick for IR detection 196 { 197 STNBAnalogClock* coord = &state.aClock; 198 //Accumulate time 199 if(coord->readLastTime < curTime){ 200 coord->readWaitAccum += curTime - coord->readLastTime; 201 } else { 202 coord->readWaitAccum = (4294967295 - coord->readLastTime) + curTime; 203 } 204 //Do one-tick action 205 { 206 int i; for(i = 0; i < ENAnalogSampleType_Count; i++){ 207 STNBAnalogSample* ss = &state.samples[i]; 208 const int val = analogRead(ss->pin); 209 ss->accum += val; 210 } 211 coord->readsCount++; 212 } 213 //Process accumulated events 214 while(coord->readWaitAccum > 1000000){ 215 const int avgsArrSz = (sizeof(state.samples[0].avgs) / sizeof(state.samples[0].avgs[0])); 216 { 217 //Move index 218 if(coord->avgsCount != 0){ 219 coord->avgsIdxLast++; 220 if(coord->avgsIdxLast == avgsArrSz){ 221 coord->avgsIdxLast = 0; 222 } 223 } 224 //Populate slots 225 { 226 if(coord->readsCount > 0){ 227 int i; for(i = 0; i < ENAnalogSampleType_Count; i++){ 228 STNBAnalogSample* ss = &state.samples[i]; 229 ss->avgs[coord->avgsIdxLast] = ss->accum / coord->readsCount; 230 } 231 } else { 232 int i; for(i = 0; i < ENAnalogSampleType_Count; i++){ 233 STNBAnalogSample* ss = &state.samples[i]; 234 ss->avgs[coord->avgsIdxLast] = 0; 235 } 236 } 237 } 238 //Increase count 239 if(coord->avgsCount < avgsArrSz){ 240 coord->avgsCount++; 241 } 242 //Calculate avgs and print 243 { 244 if(coord->avgsIdxLast == avgsArrSz - 1){ 245 //Battery 246 { 247 const float avgV = samplesAvgAsVoltageFromSensor(&state.samples[ENAnalogSampleType_BatteryV]); 248 const float avgA = samplesAvgAsAmps(&state.samples[ENAnalogSampleType_BatteryA]); 249 Serial.print("Bat\ "); 250 Serial.print(avgV, 2); Serial.print("\ v\ "); 251 Serial.print(avgA, 3); Serial.print("\ a\ "); 252 } 253 //Panel 254 { 255 const float avgV = samplesAvgAsVoltageFromDivider(&state.samples[ENAnalogSampleType_PanelV]); 256 const float avgA = samplesAvgAsAmps(&state.samples[ENAnalogSampleType_PanelA]); 257 Serial.print("Pnl\ "); 258 Serial.print(avgV, 2); Serial.print("\ v\ "); 259 Serial.print(avgA, 3); Serial.print("\ a\ "); 260 } 261 Serial.print("\ 262"); 263 } 264 } 265 //Reset counters 266 { 267 int i; for(i = 0; i < ENAnalogSampleType_Count; i++){ 268 STNBAnalogSample* ss = &state.samples[i]; 269 ss->accum = 0; 270 } 271 } 272 coord->readsCount = 0; 273 } 274 coord->readWaitAccum -= 100000; 275 } 276 //Keep time 277 coord->readLastTime = curTime; 278 //Beep and delay 279 { 280 //Accumulate time 281 if(buzzer.lastActTime < curTime){ 282 buzzer.waitAccum += curTime - buzzer.lastActTime; 283 } else { 284 buzzer.waitAccum = (4294967295 - buzzer.lastActTime) + curTime; 285 } 286 buzzer.lastActTime = curTime; 287 // 288 if(buzzer.waitAccum > 3000000){ 289 //Delay by beeing 290 int i; for(i = 0;i < 10; i++){ // output a frequency sound 291 digitalWrite(buzzer.pin, HIGH);// sound 292 delay(1); 293 digitalWrite(buzzer.pin, LOW);//not sound 294 delay(1); 295 } 296 buzzer.waitAccum = 0; 297 } else { 298 //dealy one tick next read 299 delay(10); 300 } 301 } 302 } 303} 304 305//Get the raw avg of all samples collected in the avgs-circular-queue 306int samplesAvg(const STNBAnalogSample* src){ 307 int total = 0, count = 0, i; 308 for(i = 0; i < (sizeof(src->avgs) / sizeof(src->avgs[0])); i++){ 309 total += src->avgs[i]; 310 count++; 311 break; 312 } 313 return total / count; 314} 315 316//Get the avg in voltage value collected from the voltage-sensor-module. 317float samplesAvgAsVoltageFromSensor(const STNBAnalogSample* src){ 318 const float value = samplesAvg(src); 319 const float vout = (value * cfg.sys.voltsRef) / 1024.0f; 320 const float vin = vout / ( cfg.voltage.module.r2 / (cfg.voltage.module.r1 + cfg.voltage.module.r2) ); 321 return vin; 322} 323 324//Get the avg in voltage value collected from the divider made with resistor. 325float samplesAvgAsVoltageFromDivider(const STNBAnalogSample* src){ 326 const float value = samplesAvg(src); 327 const float vout = (value * cfg.sys.voltsRef) / 1024.0f; 328 const float vin = vout / ( cfg.voltage.divider.r2 / (cfg.voltage.divider.r1 + cfg.voltage.divider.r2) ); 329 return vin; 330} 331 332//Get the avg in current (amps) value collected from the current-sensor-module 333float samplesAvgAsAmps(const STNBAnalogSample* src){ 334 const int adcValAbs = samplesAvg(src); //From 0 to 1024 335 const int adcValSign = adcValAbs - (1024 / 2); //From -512 to 512 336 const float adcValSignRel = (float)adcValSign / (float)(1024 / 2); //From -1.0f to 1.0f 337 const float adcMiliVolts = adcValSignRel * (cfg.sys.voltsRef * 1000.0f / 2.0f); 338 const float currentValue = adcMiliVolts / cfg.current.module.miliVoltsPerAmp; 339 return currentValue; //+ 0.1465; //adjustment 340} 341 342//------------ 343//End-of-program 344//------------ 345
Downloadable files
Monitoring solar street lamps voltage and current
Monitoring solar street lamps voltage and current
Monitoring solar street lamps voltage and current
Monitoring solar street lamps voltage and current
Comments
Only logged in users can leave comments
Anonymous user
4 years ago
Interesting project. I've often wondered if a simple LDR would serve as intrusion detection, one way to do this would be to power a IR LED(s), most LDRs are sensitive to IR too. Any object moving in the vicinity would trigger a change in resistance which could be use to turn on a lamp, and CCTV.
marcosjom
0 Followers
•1 Projects
2
2
krunchee
a year ago
Awesome idea, I'm looking at setting up a solar-powered motion-activated scarecrow.