Tilt pan solar tracker with I2C/EEPROM configuration and sleep cycle between movements. Sleep cycle duration precision decreases as duration increases, but sufficient for this purpose.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* modified from code | |
* by Mathias Leroy | |
* | |
* V0.2 MODIFICATIONS | |
** I2C SET GET | |
** EEPROM SET GET | |
** REMOVE SERIAL OUTPUT - AFFECTED I2C | |
** ENABLE/DISABLE TRACKING | |
** MOVE SERVOS TO LIMITS VIA I2C | |
** READ CURRENT AVG INTENSITY VIA I2C | |
* V0.3 MODIFICATIONS | |
** SWITCH FOR 2 MODES - TRACK (NO I2C) and CONFIGURE (USES I2C) | |
** SLEEP IN TRACK MODE (VERY LOW PRECISION DUE TO 8 SECOND CHUNKS) | |
** DETACH/ATTACH SERVOS ON SLEEP/WAKE (TRANSISTOR USED EVENTUALLY) | |
** REMOVE CONFIGURABLE INITIAL POSITION (REDUNDANT) | |
** REMOVE CONFIGURABLE WAKE SECONDS (REDUNDANT) | |
** REMOVE CONFIGURABLE ENABLE/DISABLE (REDUNDANT) | |
** REMOVE CONFIGURABLE TRACKER ENABLED (USE HARDWARE SWITCH) | |
** REMOVE VOLTAGE GETTER - WILL USE SEPARATE I2C COMPONENT | |
** ADD SERIAL LOGGING WHEN NOT USING I2C | |
*/ | |
#include <Wire.h> | |
#include <stdio.h> | |
#include <Servo.h> | |
#include <EEPROM.h> | |
#include <LowPower.h> | |
#define EEPROM_VERSION 1 | |
#define I2C_MSG_IN_SIZE 3 | |
#define PIN_LDR_TL A0 | |
#define PIN_LDR_TR A1 | |
#define PIN_LDR_BR A3 | |
#define PIN_LDR_BL A2 | |
#define PIN_SERVO_V 11 | |
#define PIN_SERVO_H 5 | |
#define IDX_I2C_ADDR 0 | |
#define IDX_V_ANGLE_MIN 1 | |
#define IDX_V_ANGLE_MAX 2 | |
#define IDX_V_SENSITIVITY 3 | |
#define IDX_V_STEP 4 | |
#define IDX_H_ANGLE_MIN 5 | |
#define IDX_H_ANGLE_MAX 6 | |
#define IDX_H_SENSITIVITY 7 | |
#define IDX_H_STEP 8 | |
#define IDX_SLEEP_MINUTES 9 | |
#define IDX_V_DAWN_ANGLE 10 | |
#define IDX_H_DAWN_ANGLE 11 | |
#define IDX_DAWN_INTENSITY 12 // average of all LDRS | |
#define IDX_DUSK_INTENSITY 13 // average of all LDRS | |
#define IDX_END_EEPROM_SET 14 | |
#define IDX_CURRENT_INTENSITY 15 // average of all LDRS - used for calculating IDX_DAWN_INTENSITY ambiant non-direct light | |
#define IDX_END_VALUES_GET 16 | |
#define IDX_SIGN_1 17 | |
#define IDX_SIGN_2 18 | |
#define IDX_SIGN_3 19 | |
Servo _servoH; | |
Servo _servoV; | |
byte _i2cVals[20] = {10, 10, 170, 20, 5, 10, 170, 20, 5, 20, 40, 10, 30, 40, 0, 0, 0, 0, 0, 0}; | |
int _servoLoopDelay = 10; | |
int _slowingDelay=0; | |
int _angleH = 90; | |
int _angleV = 90; | |
int _averageTop = 0; | |
int _averageRight = 0; | |
int _averageBottom = 0; | |
int _averageLeft = 0; | |
byte _i2cResponse = 0; | |
bool _inConfigMode = false; | |
void setup() | |
{ | |
Serial.begin(115200); | |
getFromEeprom(); | |
if (inConfigMode()){ | |
Serial.println("Config Mode"); | |
Serial.print("I2C Address: "); | |
Serial.println(_i2cVals[IDX_I2C_ADDR]); | |
Wire.begin(_i2cVals[IDX_I2C_ADDR]); | |
Wire.onReceive(receiveEvent); | |
Wire.onRequest(requestEvent); | |
}else{ | |
Serial.println("Tracking Mode"); | |
delay(5000);// time to get hands out of way if connecting the battery etc. | |
} | |
} | |
void loop() | |
{ | |
getLightValues(); | |
if (!_inConfigMode){ | |
// ToDo: TURN ON TRANSISTOR SWITCH | |
_servoH.attach(PIN_SERVO_H); | |
_servoV.attach(PIN_SERVO_V); | |
for (int i = 0; i < 20; i++){ | |
if (i != 0){ | |
getLightValues(); | |
} | |
moveServos(); | |
} | |
delay(500); | |
_servoH.detach(); | |
_servoV.detach(); | |
// ToDo: TURN OFF TRANSISTOR SWITCH | |
delay(500); | |
asleepFor((_i2cVals[IDX_SLEEP_MINUTES] * 60) / 8); | |
} | |
} | |
//---------------------------------CURRENT MODE | |
bool inConfigMode(){ | |
pinMode(PIN_SERVO_H, INPUT); | |
_inConfigMode = digitalRead(PIN_SERVO_H) == 1; | |
return _inConfigMode; | |
} | |
//---------------------------------EEPROM | |
void getFromEeprom(){ | |
if( | |
EEPROM.read(IDX_SIGN_1) != 'S' || | |
EEPROM.read(IDX_SIGN_2) != 'T' || | |
EEPROM.read(IDX_SIGN_3) != EEPROM_VERSION | |
) EEPROM_write_default_configuration(); | |
EEPROM_read_configuration(); | |
} | |
void EEPROM_write_default_configuration(){ | |
Serial.println("EEPROM_write_default_configuration"); | |
for (int i = 0; i < IDX_END_EEPROM_SET; i++){ | |
EEPROM.update(i, _i2cVals[i]); | |
} | |
EEPROM.update(IDX_SIGN_1,'S'); | |
EEPROM.update(IDX_SIGN_2, 'T'); | |
EEPROM.update(IDX_SIGN_3, EEPROM_VERSION); | |
} | |
void EEPROM_read_configuration(){ | |
Serial.println("EEPROM_read_configuration"); | |
for (int i = 0; i < IDX_END_EEPROM_SET; i++){ | |
_i2cVals[i] = EEPROM.read(i); | |
//Serial.println(String(i) + " = " + _i2cVals[i]); | |
} | |
} | |
//---------------------------------I2C | |
void receiveEvent(int count) { | |
if (count == I2C_MSG_IN_SIZE) | |
{ | |
char cmd = Wire.read(); | |
byte index = Wire.read(); | |
byte value = Wire.read(); | |
switch (cmd) { | |
case 'G': | |
if (index < IDX_END_VALUES_GET){ | |
_i2cResponse = _i2cVals[index]; | |
} | |
break; | |
case 'S': | |
if (index < IDX_END_EEPROM_SET){ | |
_i2cVals[index] = value; | |
EEPROM.update(index, _i2cVals[index]); | |
} | |
break; | |
default: | |
return; | |
} | |
} | |
} | |
void requestEvent() | |
{ | |
Wire.write(_i2cResponse); | |
} | |
//---------------------------------LDRs | |
void getLightValues(){ | |
int valueTopLeft = analogRead(PIN_LDR_TL); | |
int valueTopRight = analogRead(PIN_LDR_TR); | |
int valueBottomRight = analogRead(PIN_LDR_BR); | |
int valueBottomLeft = analogRead(PIN_LDR_BL); | |
_averageTop = ( valueTopLeft + valueTopRight ) / 2; | |
_averageRight = ( valueTopRight + valueBottomRight ) / 2; | |
_averageBottom = ( valueBottomRight + valueBottomLeft ) / 2; | |
_averageLeft = ( valueBottomLeft + valueTopLeft ) / 2; | |
int avgIntensity = (valueTopLeft + valueTopRight + valueBottomRight + valueBottomLeft) / 4; | |
_i2cVals[IDX_CURRENT_INTENSITY] = map(avgIntensity, 0, 1024, 0, 255); | |
} | |
//---------------------------------SERVOS | |
void moveServos(){ | |
Serial.println("moveServos"); | |
if ( (_averageLeft-_averageRight)>_i2cVals[IDX_H_SENSITIVITY] && (_angleH-_i2cVals[IDX_H_STEP])>_i2cVals[IDX_H_ANGLE_MIN] ) { | |
// going left | |
Serial.println("moveServos going left"); | |
delay(_slowingDelay); | |
for (int i=0; i < _i2cVals[IDX_H_STEP]; i++){ | |
_servoH.write(_angleH--); | |
delay(_servoLoopDelay); | |
} | |
} | |
else if ( (_averageRight-_averageLeft)>_i2cVals[IDX_H_SENSITIVITY] && (_angleH+_i2cVals[IDX_H_STEP])<_i2cVals[IDX_H_ANGLE_MAX] ) { | |
// going right | |
Serial.println("moveServos going left"); | |
delay(_slowingDelay); | |
for (int i=0; i < _i2cVals[IDX_H_STEP]; i++){ | |
_servoH.write(_angleH++); | |
delay(_servoLoopDelay); | |
} | |
} | |
else { | |
// doing nothing | |
Serial.println("moveServos doing nothing"); | |
delay(_slowingDelay); | |
} | |
if ( (_averageTop-_averageBottom)>_i2cVals[IDX_V_SENSITIVITY] && (_angleV+_i2cVals[IDX_V_STEP])<_i2cVals[IDX_V_ANGLE_MAX] ) { | |
// going up | |
Serial.println("moveServos going up"); | |
delay(_slowingDelay); | |
for (int i=0; i < _i2cVals[IDX_V_STEP]; i++){ | |
_servoV.write(_angleV++); | |
delay(_servoLoopDelay); | |
} | |
} | |
else if ( (_averageBottom-_averageTop)>_i2cVals[IDX_V_SENSITIVITY] && (_angleV-_i2cVals[IDX_V_STEP])>_i2cVals[IDX_V_ANGLE_MIN]) { | |
// going down | |
Serial.println("moveServos going down"); | |
delay(_slowingDelay); | |
for (int i=0; i < _i2cVals[IDX_V_STEP]; i++){ | |
_servoV.write(_angleV--); | |
delay(_servoLoopDelay); | |
} | |
} | |
else { | |
Serial.println("moveServos doing nothing"); | |
delay(_slowingDelay); | |
} | |
} | |
//---------------------------------SLEEP | |
void asleepFor(unsigned int eightSecondSegments){ | |
Serial.println("asleepFor"); | |
for (unsigned int sleepCounter = eightSecondSegments; sleepCounter > 0; sleepCounter--) | |
{ | |
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment