And I was worried about the solar production when the fog rolled in

800W from a 2.8kW array in that weather :)

Posted in Uncategorized | 1 Comment

Flooring and kitchen cabinets in

20111002-200935.jpg

20111002-201003.jpg

20111002-201015.jpg

Posted in Building | 1 Comment

Gas and laminate flooring installed

Posted in Building | Leave a comment

PV install done!

The PV installation is finally completed and the battery have had their first taste of those juicy solar electrons.
The tech details:

  1. 12 x Yingli 235W modules for 2.82kW of installed PV wired in 4 strings of 3 panels. 100Vmp for each string in summer.
  2. Morningstar 60A MPPT charge controller
  3. 5 PzS 700 700Ah (C5) traction batteries from Sunlight.gr
  4. SMA Sunny Island 5048, 5kW inverter with a shunt installed so that its internal SoC can keep track of the energy coming in through the morningstar controller
  5. 5kW diesel generator from Volvox Engineering (rebadged Vidhata generator from India) with a Deapsea 3110 controller
  6. Mounting structure custom made from aluminium tubing, 60x40x2mm.

Posted in Off grid power, Solar power | Leave a comment

Barley

Hello my pretties

Posted in Land, Plants | 2 Comments

Version 0.1 Battery Monitor

Basic features working well: Measuring SG and temp and calculating the SoC. Displaying on LCD and working over SMS.

Posted in Off grid power, Solar power | Tagged , , | 3 Comments

SG data logger, first steps

The helpful chaps at JSA Photonics sent over a replacement sensor and it’s working rather nicely. Have only run real tests using a single SG value as my battery doesn’t cycle much yet.

Got the dimensions of the cell cap dimensiones slightly wrong when ordering so the sensor’s sitting a bit lower than it should be.

The manual meter reads just below 1.26

And so does the sensor! The Arduino AD converter is only 10 bit, which limits the accuracy of the readings, but anyway 2 decimal places is good enough for specific gravity. Note that it has an integrated temperature sensor too. The output reading is currently not temperature corrected.

Posted in Off grid power, Solar power | 3 Comments

SMA Sunny Island installed

Sunny Island 5048 hooked up to the generator and the 800Ah Forklift battery from Sunlight.gr.

Posted in Uncategorized | Leave a comment

SMS generator control with Arduino

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());
 }  

}
Posted in Off grid power, water | 15 Comments

Exhaust silencer version 2.0

Going to make the expansion chamber smaller, so that it’s deeper underground, then funnel the gases through a maze made out of blocks filled with sand.  There’s a big hole filled with builders sand that forms the center of this structure, with any luck the sand and the irregular direction of the baffled blocks and the fact that it’s all underground will make this super silent.

Perimeter foundations with sandy center:

And a maze of baffles including one at an odd angle to break the sound and try and reduce echoes.  The gap between the blocks is always about three times the size of the surface area of the existing exhaust pipe, so shouldn’t have a problem with back-pressure on the engine.

After waiting for the cement to dry, I filled the holes up with sand, leaving a gap towards the top for the concrete to fill:

Used wooden blanks to form the roof and laid some mesh over it to add some strength to the croncrete

…and covered it all in concrete

Posted in Off grid power | Leave a comment