Project

Solarigraphy camera

The purpose of this project is to automatize the capture of the Solar Analemma, the curve that describes the position of the sun in the sky when it is observed at the same time of the day (time zone), every day of the year, from the same place.

Group Katxarraroak

Solarigraphy camera

Solargraphy is a concept and a photographic practice based on the observation of the sun’s path in the sky (different in every place on earth) and its effect on the landscape. It is captured by a specific procedure that combines pinhole photography, digital processing and webcasting. Typically, undeveloped photographic paper, a pinhole camera and a scanner are used in order to create images of the sun’s daily path across the sky. They are taken with a long exposure, ranging from several hours to many months. Since the beginning, there has been constant sharing, broadcasting and collaborating by enthusiasts around the world, fueled by the means of the internet.

The purpose of this project is to automatize the capture of the Solar Analemma, the curve that describes the position of the sun in the sky when it is observed at the same time of the day (time zone), every day of the year, from the same place. Analemma forms a curve that is usually roughly shaped as an eight (8) or a lemniscate shape.

Capturing the Analemma manually requires taking a picture at the same time every day of the year. It is a task often infeasible since we might not always be available to do so. The automated camera is therefore a very useful tool for this practice since it allows us to be able to take pictures always at the same time.

During this project we have made an Arduino controlled camera using an RTC (real time clock) for time synchronization. Equipped with a battery and a solar panel for a total autonomous system, it does not need external power sources. Thus, it allows us to install it in places of difficult access and it is suitable for capturing the sun without obstructions. The camera shutter (of pinhole type) is controlled by a 9gr servo that moves the piece that covers the camera hole.

Step by step

  • Arduino Nano
  • RTC DS3231 modul
  • SparkFun SunnyBuddy
  • Solar panel
  • Lipo 1S 2000 mAh batery
  • Servo 9gr
  • LED 5mm
  • 220 Ohm Resistence
  • Connection cable
/*
   Solarigraphy camera control
   David Pello 2019

   It controls the shutter opening of a solarigraphy camera
    using a servo and a RTC DS3231.

   It is designed for Arduino Nano.

*/

#include 
#include 
#include 
#include 
#include 

// Soft reset
#define RESTART asm("jmp 0x0000")

// secuencias
#define TOMA_SOL2
#define TOMA_SOL3

// tiempos
#define HORA_APERTURA_SOL1         16
#define MIN_APERTURA_SOL1          0
#define HORA_CIERRE_SOL1           16
#define MIN_CIERRE_SOL1            1

#define HORA_APERTURA_SOL2         17
#define MIN_APERTURA_SOL2          0
#define HORA_CIERRE_SOL2           17
#define MIN_CIERRE_SOL2            1

#define HORA_APERTURA_SOL3         18
#define MIN_APERTURA_SOL3          0
#define HORA_CIERRE_SOL3           18
#define MIN_CIERRE_SOL3            1

#define HORA_APERTURA_PAISAJE     9
#define MIN_APERTURA_PAISAJE      0
#define HORA_CIERRE_PAISAJE       10
#define MIN_CIERRE_PAISAJE        55

#define ANGULO_APERTURA           30
#define ANGULO_CIERRE             0

#define PIN_ACT_OBTURADOR         3  // Arduino D3
#define PIN_CONFIGURACION_I       6 // D6
#define PIN_CONFIGURACION_O       7 // D7

#define SLEEPTIME                 30


// ESTADOS
#define ESPERA_SOL1               0
#define ABRE_SOL1                 1
#define ESPERA_SOL2               2
#define ABRE_SOL2                 3
#define ESPERA_SOL3               4
#define ABRE_SOL3                 5
#define ESPERA_PAISAJE            6
#define ABRE_PAISAJE              7

// variables

DS3231 Clock;
Servo obturador;
byte anyo;
byte mes;
byte dia;
byte dia_semana;
byte hora;
byte minuto;
bool h12, pm, siglo;
byte estado;
struct rst_info* reset_info;
int init_mode;
byte ADay, AHour, AMinute, ASecond, ABits;
bool ADy, A12h, Apm;


void setup() {
  delay(1);

  // Iniciamos el I2C
  Wire.begin();
  // Aseguramos que estamos en modo de 24h
  Clock.setClockMode(false);

  // Empezar de cero
  siglo = false;

  // LED
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  // IO
  pinMode(PIN_CONFIGURACION_I, INPUT_PULLUP);
  pinMode(PIN_CONFIGURACION_O, OUTPUT);
  // set to LOW
  digitalWrite(PIN_CONFIGURACION_O, LOW);

  // Y el puerto serie
  Serial.begin(115200);
  Serial.println();
  Serial.println("Iniciando...");

  // Vemos si tenemos que reiniciar o no.
  init_mode = digitalRead(PIN_CONFIGURACION_I);
  Serial.print("Configuración Leida: ");
  Serial.println(init_mode);
  if (init_mode) {
    // parpadeo
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    
    Serial.println("Continuando...");
    // Cargamos el modo actual desde la eeprom
    estado = EEPROM.read(0);
    Serial.print("Estado leido: "); Serial.println(estado);
    // Leemos el RTC
    mes = Clock.getMonth(siglo);
    dia_semana = Clock.getDoW();
    dia = Clock.getDate();
    hora = Clock.getHour(h12, pm);
    minuto = Clock.getMinute();

    Serial.println(); Serial.print("---- ");
    Serial.print(mes); Serial.print("/"); Serial.print(dia); Serial.print(" ");
    Serial.print(hora); Serial.print(":"); Serial.println(minuto);

    if (Clock.checkAlarmEnabled(1)) {
      Serial.println("nAlarm 1 enabled");
    }

    Serial.print("Alarm 1: ");
    Clock.getA1Time(ADay, AHour, AMinute, ASecond, ABits, ADy, A12h, Apm);
    Serial.print(ADay, DEC);
    if (ADy) {
      Serial.print(" DoW");
    } else {
      Serial.print(" Date");
    }
    Serial.print(' ');
    Serial.print(AHour, DEC);
    Serial.print(' ');
    Serial.print(AMinute, DEC);
    Serial.print(' ');
    Serial.print(ASecond, DEC);
    Serial.print(' ');
    if (A12h) {
      if (Apm) {
        Serial.print('pm ');
      } else {
        Serial.print('am ');
      }
    }
    Serial.print(' ');
    Serial.println(ABits, BIN);


  } else {
    Serial.println("Iniciando desde cero...");

    // parpadeo largo
    for (int i = 0; i < 20; i++) { digitalWrite(LED_BUILTIN, HIGH); delay(100); digitalWrite(LED_BUILTIN, LOW); delay(100); } // iniciamos estado = ESPERA_SOL1; EEPROM.write(0, estado); // obturador cerrado obturador.attach(PIN_ACT_OBTURADOR); obturador.write(ANGULO_CIERRE); delay(250); obturador.detach(); // Leemos el RTC mes = Clock.getMonth(siglo); dia_semana = Clock.getDoW(); hora = Clock.getHour(h12, pm); minuto = Clock.getMinute(); Serial.println(); Serial.print("---- "); Serial.print(hora); Serial.print(":"); Serial.println(minuto); // Ponemos la alarma (0x8 = 1000b = A4 A3 A2 A1 = horas,minutos,segundos da igual el día) Clock.setA1Time(0, HORA_APERTURA_SOL1, MIN_APERTURA_SOL1, 0, 0x8, false, false, false); // Y la activamos Clock.turnOnAlarm(1); } } void loop() { // reiniciamos el i2c // Wire.begin(); // Comprobamos alarma if (Clock.checkIfAlarm(1)) { Serial.println("Evento de alarma RTC!"); // Dependiendo del estado actual... switch (estado) { case ESPERA_SOL1: // Abrimos obturador estado = ABRE_SOL1; EEPROM.write(0, estado); obturador.attach(PIN_ACT_OBTURADOR); obturador.write(ANGULO_APERTURA); delay(250); obturador.detach(); Serial.println("Abriendo SOL1"); // ponemos la nueva alarma dia_semana = Clock.getDoW(); Clock.setA1Time(0, HORA_CIERRE_SOL1, MIN_CIERRE_SOL1, 0, 0x8, false, false, false); Clock.turnOnAlarm(1); break; case ABRE_SOL1: // Cerramos obturador // cambiamos el estado dependiendo de si hay más aperturas de sol #ifndef TOMA_SOL2 estado = ESPERA_PAISAJE; EEPROM.write(0, estado); EEPROM.commit(); obturador.attach(PIN_ACT_OBTURADOR); obturador.write(ANGULO_CIERRE); delay(250); obturador.detach(); Serial.println("Cerrando SOL1 -> Espera Paisaje");

        // ponemos la nueva alarma
        dia_semana = Clock.getDoW();
        Clock.setA1Time(0, HORA_APERTURA_PAISAJE,
                        MIN_APERTURA_PAISAJE, 0, 0x8, false, false, false);
        Clock.turnOnAlarm(1);
#else
        estado = ESPERA_SOL2;
        EEPROM.write(0, estado);
        obturador.attach(PIN_ACT_OBTURADOR);
        obturador.write(ANGULO_CIERRE);
        delay(250);
        obturador.detach();
        Serial.println("Cerrando SOL1 -> Espera SOL2");

        // ponemos la nueva alarma
        dia_semana = Clock.getDoW();
        Clock.setA1Time(0, HORA_APERTURA_SOL2, MIN_APERTURA_SOL2,
                        0, 0x8, false, false, false);
        Clock.turnOnAlarm(1);
#endif
        break;
      case ESPERA_SOL2:
        // Abrimos obturador
        estado = ABRE_SOL2;
        EEPROM.write(0, estado);
        obturador.attach(PIN_ACT_OBTURADOR);
        obturador.write(ANGULO_APERTURA);
        delay(250);
        obturador.detach();
        Serial.println("Abriendo SOL2");

        // ponemos la nueva alarma
        dia_semana = Clock.getDoW();
        Clock.setA1Time(0, HORA_CIERRE_SOL2, MIN_CIERRE_SOL2,
                        0, 0x8, false, false, false);
        Clock.turnOnAlarm(1);
        break;

      case ABRE_SOL2:
        // Cerramos obturador
        // cambiamos el estado dependiendo de si hay más aperturas de sol
#ifndef TOMA_SOL3
        estado = ESPERA_PAISAJE;
        EEPROM.write(0, estado);
        obturador.attach(PIN_ACT_OBTURADOR);
        obturador.write(ANGULO_CIERRE);
        delay(250);
        obturador.detach();
        Serial.println("Cerrando SOL2... -> Espera paisaje");

        // ponemos la nueva alarma
        dia_semana = Clock.getDoW();
        Clock.setA1Time(0, HORA_APERTURA_PAISAJE,
                        MIN_APERTURA_PAISAJE, 0, 0x0, false, false, false);
        Clock.turnOnAlarm(1);
#else
        estado = ESPERA_SOL3;
        EEPROM.write(0, estado);
        obturador.attach(PIN_ACT_OBTURADOR);
        obturador.write(ANGULO_CIERRE);
        delay(250);
        obturador.detach();
        Serial.println("Cerrando SOL2... -> Espera SOL3");

        // ponemos la nueva alarma
        dia_semana = Clock.getDoW();
        Clock.setA1Time(0, HORA_APERTURA_SOL3, MIN_APERTURA_SOL3,
                        0, 0x8, false, false, false);
        Clock.turnOnAlarm(1);
#endif
        break;

      case ESPERA_SOL3:
        // Abrimos obturador
        estado = ABRE_SOL3;
        EEPROM.write(0, estado);
        obturador.attach(PIN_ACT_OBTURADOR);
        obturador.write(ANGULO_APERTURA);
        delay(250);
        obturador.detach();
        Serial.println("Abriendo SOL3");

        // ponemos la nueva alarma
        dia_semana = Clock.getDoW();
        Clock.setA1Time(0, HORA_CIERRE_SOL3, MIN_CIERRE_SOL3,
                        0, 0x8, false, false, false);
        Clock.turnOnAlarm(1);
        break;

      case ABRE_SOL3:
        // Cerramos obturador
        estado = ESPERA_PAISAJE;
        EEPROM.write(0, estado);
        obturador.attach(PIN_ACT_OBTURADOR);
        obturador.write(ANGULO_CIERRE);
        delay(250);
        obturador.detach();
        Serial.println("Cerrando SOL3... -> Espera Paisaje");

        // ponemos la nueva alarma
        dia_semana = Clock.getDoW();
        Clock.setA1Time(0, HORA_APERTURA_PAISAJE, MIN_APERTURA_PAISAJE,
                        0, 0x8, false, false, false);
        Clock.turnOnAlarm(1);
        break;

      case ESPERA_PAISAJE:
        // Abrimos obturador
        estado = ABRE_PAISAJE;
        EEPROM.write(0, estado);
        obturador.attach(PIN_ACT_OBTURADOR);
        obturador.write(ANGULO_APERTURA);
        delay(250);
        obturador.detach();
        Serial.println("Abriendo Paisaje");

        // ponemos la nueva alarma
        dia_semana = Clock.getDoW();
        Clock.setA1Time(0, HORA_CIERRE_PAISAJE, MIN_CIERRE_PAISAJE,
                        0, 0x8, false, false, false);
        Clock.turnOnAlarm(1);
        break;

      case ABRE_PAISAJE:
        // Cerramos obturador
        estado = ESPERA_SOL1;
        EEPROM.write(0, estado);
        obturador.attach(PIN_ACT_OBTURADOR);
        obturador.write(ANGULO_CIERRE);
        delay(250);
        obturador.detach();
        Serial.println("Cerrando Paisaje -> Espera SOL1");

        // ponemos la nueva alarma
        dia_semana = Clock.getDoW();
        Clock.setA1Time(0, HORA_APERTURA_SOL1, MIN_APERTURA_SOL1,
                        0, 0x8, false, false, false);
        Clock.turnOnAlarm(1);
        break;
    }

    // Apagamos el i2c
    //Wire.end();

  }

  // a dormir (bajo consumo)
  Serial.flush(); // wait for serial
  for (int i = 0 ;  i  <  15 ; i++)
    LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF);

  RESTART;
}

After assembling the circuit described in the diagram, and making sure that the RTC already has the correct time (using the example of the RTC DS3231 library to set it in time), it will be necessary to program it with the Arduino code listed, and for this we will first have to configure in the code the desired hours for the shots in its first section.

To do this, we will modify the hours, minutes and seconds of each shot in the times section, and we will comment or uncomment the TOMA_SOL2 and TOMA_SOL3 sequences depending on whether we want one, two or three shots (and therefore anomalies as with the example image in the gallery).

Then we will program the arduino with this code and mount the system inside the camera that we are going to use. We recommend using a totally watertight box, possibly a PVC connection box suitable for the size of the socket we want to make (depending on our pinhole, focal length, etc.), and we will glue the servo with a piece that when the servo is in position 0º cover the shutter. We can do some test first with the servo control example included in the arduino environment to see the corresponding positions and the direction of movement.

We will place the solar panel oriented towards the south and with an approximate angle with respect to the horizon at the latitude where we are to maximize its solar exposure.

Before closing the camera, we must restart the code. To do this, we will connect the pins marked in the diagram as I / O to each other and we will power the board, so the program will understand that it has to start from scratch. Now we will disconnect everything, including the I / O pins and we will power the system again, which will now be ready to take the shots automatically. We can check if the system is alive by looking at the schematic LED, which should blink approximately every 30 seconds.