Driving 20km every day just to turn the generator and water pump on was becoming a drag, so I built an arduino solution with a GPRS shield, relay board, current sensor and real time clock to be able to turn them on remotely and monitor error conditions.
During testing I must have put too much current through one of the Phidgets sensors caused it stopped working properly, would only close, but not open – so switched it out for a 10A solid state version. Have been using the SMS command system for a week now without a hitch, basically I can start the generator, stop it, query it’s status and turn the timer on or off. If the unit detects any error condition, it will immediately shut the generator off and send an SMS alert.
The unit also has a built in warm up and cool down time for the generator and uses the current sensor to detect if the well is dry, i.e. if well is dry, pump stops, so current sensors can detect this and the arduino will shut down the generator. The current sensor will also detect if the generator didn’t come on or if the pump failed to turn on – or if the generator stopped when it should be running. All result in an error SMS being sent.
Had to mount the Arduino on the ceiling as the antenna cable wasn’t long enough
Relay box as close to the thick cabling as possible
Complete source code below, if anyone needs to rip bits out of it. Note that you’ll need to change the phone number, and the password “1234″, this password has nothing to do with the sim card, it’s just a number I’ve picked so that only people who know it can send messages to the generator.
#include "Wire.h" #include#define DS1307_I2C_ADDRESS 0x68 #define maxLength 200 #define PHONE 11111111111 String inString = String(maxLength); int HOUR = 21; int MINUTE_START = 01; int MINUTE_STOP = 55; boolean DEBUG = false; unsigned long starttime = 0; const unsigned long OVERRUN_TIME = 2UL*60UL*60UL*1000UL; const int GEN_START_PIN = 10; const int LOAD_PIN = 11; const int LED = 13; int GEN_START_DELAY = 20000; int GEN_STOP_DELAY = 20000; const int RETRY_LIMIT = 3; const int CURRENT_SENSOR_PIN = 2; const double MINIMUM_LOAD = 0.2; long WARMUP_DELAY = 60000; long LOAD_DELAY = 10000; const int GPRSonModulePin = 2; const double PUMP_LOAD = 4.0; double load = 0.0; boolean errorSent = false; boolean stoppedInWindow = false; boolean timerOn = true; int genState = 0; //0 = off, 1=running int pumpCommandState = 0; int genCommandState = 0; //0 = stop, 1 = start int error = 0; char* errorMsg[] = {"No error","Pump failed to start","Generator failed to start","Generator failed to stop","Generator overrun","Generator still running after stop signal"}; char* msg = ""; void getSerialChars() { while (Serial.available() > 0) { char inChar = Serial.read(); if (inString.length() < maxLength) { inString.append(inChar); } } } // Convert normal decimal numbers to binary coded decimal byte decToBcd(byte val) { return ( (val/10*16) + (val%10) ); } // Convert binary coded decimal to normal decimal numbers byte bcdToDec(byte val) { return ( (val/16*10) + (val%16) ); } void setDateDs1307(byte second, // 0-59 byte minute, // 0-59 byte hour, // 1-23 byte dayOfWeek, // 1-7 byte dayOfMonth, // 1-28/29/30/31 byte month, // 1-12 byte year) // 0-99 { Wire.beginTransmission(DS1307_I2C_ADDRESS); Wire.send(0); Wire.send(decToBcd(second)); // 0 to bit 7 starts the clock Wire.send(decToBcd(minute)); Wire.send(decToBcd(hour)); // If you want 12 hour am/pm you need to set // bit 6 (also need to change readDateDs1307) Wire.send(decToBcd(dayOfWeek)); Wire.send(decToBcd(dayOfMonth)); Wire.send(decToBcd(month)); Wire.send(decToBcd(year)); Wire.endTransmission(); } // Gets the date and time from the ds1307 void getDateDs1307(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) { // Reset the register pointer Wire.beginTransmission(DS1307_I2C_ADDRESS); Wire.send(0); Wire.endTransmission(); Wire.requestFrom(DS1307_I2C_ADDRESS, 7); // A few of these need masks because certain bits are control bits *second = bcdToDec(Wire.receive() & 0x7f); *minute = bcdToDec(Wire.receive()); *hour = bcdToDec(Wire.receive() & 0x3f); // Need to change this if 12 hour am/pm *dayOfWeek = bcdToDec(Wire.receive()); *dayOfMonth = bcdToDec(Wire.receive()); *month = bcdToDec(Wire.receive()); *year = bcdToDec(Wire.receive()); } void switchModule(){ digitalWrite(GPRSonModulePin,HIGH); delay(2500); digitalWrite(GPRSonModulePin,LOW); delay(25000); if (DEBUG) { Serial.println("GPRS module turned on"); } } boolean genInTimerWindow() { byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); if (DEBUG) { Serial.print("Time: "); Serial.print(hour,DEC); Serial.print(":"); Serial.println(minute,DEC); } if (hour != HOUR) { stoppedInWindow = false; } if ((hour == HOUR) && (minute >= MINUTE_START) && (minute <= MINUTE_STOP)) { return true; } else { return false; } } void startGen() { if (DEBUG) { Serial.println("Starting gen"); } digitalWrite(GEN_START_PIN, HIGH); genState = 1; } void stopGen() { if (DEBUG) { Serial.println("Stopping gen"); } digitalWrite(GEN_START_PIN, LOW); genState = 0; } void connectLoad() { if (DEBUG) { Serial.println("Connecting load"); } digitalWrite(LOAD_PIN, HIGH); } void disconnectLoad() { digitalWrite(LOAD_PIN, LOW); if (DEBUG) { Serial.println("Disconnecting load"); } } double readLoad() { int load = analogRead(CURRENT_SENSOR_PIN); return (((double)load)*25.0/1023.0); } void deleteAllMsgs() { delay(1500); Serial.println("AT+CMGD=0,4"); delay(4000); } int smsCommand() { int retval=0; delay(1500); Serial.print("AT+CMGL="); Serial.print(34,BYTE); Serial.print("REC UNREAD"); Serial.println(34,BYTE); delay(3000); if (Serial.available() > 0) { inString = ""; delay(500); getSerialChars(); if (inString.contains("1234#")) { String command = String(20); command = inString.substring(inString.indexOf('#')+1,inString.length()); if (command.contains("start")) retval = 1; if (command.contains("stop")) retval = 2; if (command.contains("clear")) retval = 3; if (command.contains("status")) retval = 4; if (command.contains("timer on")) retval = 5; if (command.contains("timer off")) retval = 6; if (retval != 0) deleteAllMsgs(); } } return retval; } void sendSMSpreamble() { delay(1500); Serial.print("AT+CMGS="); Serial.print(34,BYTE); Serial.print(PHONE); Serial.println(34,BYTE); delay(1500); } void sendSMSappend() { delay(500); Serial.print(0x1A,BYTE); // end of message command 1A (hex) delay(5000); } void sendErrorSMS() { sendSMSpreamble(); Serial.print("Error: "); Serial.print(errorMsg[error]); writeStatus(); sendSMSappend(); } void sendStopSMS() { sendSMSpreamble(); writeStatus(); sendSMSappend(); } void writeStatus() { load = readLoad(); Serial.println(msg); Serial.print("L: "); Serial.println(load); Serial.print("Gen: "); Serial.println(genState); Serial.print("Runtime: "); Serial.print((millis()-starttime)/60000UL); Serial.println(" min"); Serial.print("Timer: "); if (timerOn) { Serial.print(HOUR); Serial.print(":"); Serial.print(MINUTE_START); Serial.print(" - "); Serial.println(MINUTE_STOP); } else { Serial.println("Off"); } } void sendStatusSMS() { sendSMSpreamble(); writeStatus(); sendSMSappend(); } void raiseError() { stopGen(); sendErrorSMS(); } void genStartSequence() { if (DEBUG) { Serial.println("Gen start sequence"); } disconnectLoad(); startGen(); delay(GEN_START_DELAY); load = readLoad(); if (DEBUG) { Serial.println(load); } if (load >= MINIMUM_LOAD) { starttime = millis(); delay(WARMUP_DELAY); connectLoad(); delay(LOAD_DELAY); load = readLoad(); if (load > PUMP_LOAD) { if (DEBUG) { Serial.print("Pump came on"); } } else { error = 1; if (DEBUG) { Serial.print(load); Serial.println(" was less than the pump load."); } } } else { error = 2; if (DEBUG) { Serial.print(load); Serial.println(" was less than the minimum load."); } } } void genStopSequence() { if (DEBUG) { Serial.println("Gen stop sequence"); } disconnectLoad(); delay(WARMUP_DELAY); stopGen(); delay(GEN_STOP_DELAY); load = readLoad(); if (load >= MINIMUM_LOAD) { error = 3; } else { sendStopSMS(); } } void blinkLED() { digitalWrite(LED,HIGH); delay(1000); digitalWrite(LED,LOW); } void setup() { Serial.begin(19200); pinMode(LED, OUTPUT); digitalWrite(LED, HIGH); pinMode(GEN_START_PIN, OUTPUT); digitalWrite(GEN_START_PIN, LOW); pinMode(LOAD_PIN, OUTPUT); digitalWrite(LOAD_PIN, LOW); Wire.begin(); delay(5000); starttime = 0UL; switchModule(); // swith the module ON Serial.println("AT+CMGF=1"); if (DEBUG) { //byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; //getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); //HOUR = hour; //MINUTE_START = minute; //MINUTE_STOP = MINUTE_START+1; WARMUP_DELAY=1000; LOAD_DELAY = 1000; GEN_START_DELAY = 2000; GEN_STOP_DELAY = 2000; } msg = "Module turned on."; //sendStatusSMS(); msg = ""; digitalWrite(LED, LOW); } void loop() { switch (smsCommand()) { case 1: genCommandState = 1; error = 0; timerOn = false; break; case 2: genCommandState = 0; stoppedInWindow = true; msg = "Stop signal from SMS"; break; case 3: error = 0; break; case 4: msg = ""; sendStatusSMS(); break; case 5: timerOn = true; sendStatusSMS(); break; case 6: timerOn = false; sendStatusSMS(); break; } if (error == 0) { errorSent = false; if (genState == 0) { starttime = 0UL; if ((timerOn) && (genInTimerWindow()) && (!stoppedInWindow)) { if (DEBUG) { Serial.println("In time window"); } genCommandState = 1; } if (genCommandState == 1) { genStartSequence(); } } else { if ((timerOn) && !genInTimerWindow()) { genCommandState = 0; msg = "Stopped by timer"; } if (readLoad() < PUMP_LOAD) { msg = "Stop signal from pump"; if (DEBUG) { Serial.println(msg); } genCommandState = 0; stoppedInWindow = true; } unsigned long time = millis(); if ((time - starttime) > OVERRUN_TIME) { error = 4; } if (genCommandState == 0) { genStopSequence(); } } } else { if (!errorSent) { raiseError(); errorSent = true; } } delay(2000); blinkLED(); if (DEBUG) { Serial.print("gen state="); Serial.print(genState); Serial.print(" Load="); Serial.println(readLoad()); } }


Sweet! I didn’t realise your place was so big that you would have to drive 20km from one end to the other. Or is it just that the easiest way to get there involves a 20km detour?
LOL, not that big, but we’re not currently living on the land! We’re living in town 20kms away, house plans are still in progress…. sloooooooowwwww progress…
Perfecto, me funciona, estoy muy agradecido a este codigo. Eres magnifico.
No podia leer los SMS que recibia.
Estoy estudiando el codigo, ya que hay muchas cosas controladas que me gustan. Como por ejemplo que si el SMS recibido puede ser interpretado este SMS es borrado.
Gracias.
Brilliant! Best arduino project I’ve seen and shows some great coding.
We have a campo house on Costa Dorada with diesel genny, solars and wind genny. Working on a logger for energy production and consumption. After that will be a smart pool and hot tub controller to switch the pumps and heaters on when most power is available.
Keep up the good work.
Would be interested to know all the components involved and some relative pricing.
Hi Chris,
It’s been a while and I canabalised some of the parts from what I had lying around, so the below is a rough estimate if you were starting from scratch.
Stephen,
Thanks for the info. I have a remote cabin and this can really come in handy. Will post how it goes.
GREATE info here, I just used houres finding som arduino code for controlling som machinery at the plant I work…So this is very handy.
And I might use it to control som funktions in my old military truck aswell….
can I get your mail…then I will send you updates on the projects.
Hey again!
I tryed out your arduino code (I am a newbee) and it faild in compiling what do I do wrong?
What is your setup:
Arduino uno ?
Arduino Mega?
And the GSM?
Hi
Very good to know ,but instead of ■GPRS shield from Libelium Iwana use Siemens mg35 modem pl. help
I am able to send sms not able to receive in txt ,how to display on lcd using Arduino 328
Thanks
Ham NZ
Don’t know anything about the Siemens modem. The easiest display I’ve worked with is a Serial Display, there are a few on the market. The advantage is that it only uses 2 IO pins instead of the many pins that the more common LCDs use.
Hi Stephen,
thanks for share knowledge, please help me try the script error:
inString.append (inChar);
‘String class’ has no member named ‘append’
if (inString.contains (“ABCD #”)) {
‘String class’ has no member named ‘contains’
what version of the Arduino I do not fit?
my Arduino Mega1280, Arduino software version of the 022 and windows vista
Thank you for helping
Looks like you could be using an old version of the Arduino IDE, try download the new version with the new String class.
I have a Cabin that is off grid and I need a way to delay the generator power to the Cabin until the Generator gets up to full speed. The setup would delay the power to the main distribution panel inside. There is no automatic transfer switch and the generator is wired to the main distribution panel. The Generator is a 7kw propane unit.
Thanks Lee
Hi Lee,
Some generator controllers have this functionality built in. If yours doesn’t have this feature, you could try asking the question on the wind&sun forum where there are many bright people living with generators/solar/wind, etc: