Skip to content

Instantly share code, notes, and snippets.

@IOT-123
Created January 18, 2018 10:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IOT-123/a03746a54a773312e99234e1d7338773 to your computer and use it in GitHub Desktop.
Save IOT-123/a03746a54a773312e99234e1d7338773 to your computer and use it in GitHub Desktop.
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.
/*
* 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