martes, 28 de abril de 2015

Calefacción solar de bajo coste. Empezando a programar el Arduino (I)


Muy buenas.

Ha llegado el día de desvelar el secreto mejor guardado de este proyecto... 

¡ el código del programa que se ejecuta en Arduino !

Como ya deberéis saber si habéis llegado hasta aquí, el lenguaje de programación que se utiliza para realizar programas en Arduino es el C.

Vamos a ir empezando desgranando las rutinas o funciones más importantes del mismo.

Cuando arranca el Arduino, cuando lo encendemos y se carga el programa en su memoria, lo primero que hace es ejecutar una función que forzosamente se ha de llamar "setup". En dicha función realizamos tareas de inicialización de variables que luego utilizaremos durante la ejecución del programa.

void setup() {
  delay(500);  
// allow some time (50 -250 ms) after powerup and sketch start, for the Wiznet W5100 Reset IC to release and come out of reset.
  // start serial port:
  Serial.begin(9600);

  pinMode(relayPin, OUTPUT);  
  pinMode(relayPinPump2, OUTPUT); 
  pinMode(relayPinFan, OUTPUT); 

  digitalWrite(relayPin, HIGH);  
  digitalWrite(relayPinPump2, HIGH);  
  digitalWrite(relayPinFan, HIGH);  

  pinMode(SS_SD_CARD, OUTPUT);
  pinMode(SS_ETHERNET, OUTPUT);
  digitalWrite(SS_SD_CARD, HIGH);   // SD Card not active
  digitalWrite(SS_ETHERNET, LOW);   // Ethernet active

  analogReference(DEFAULT);
  DEBUG_PRINT("Version 5.2 2014-12-04 21:00.\n");
  DEBUG_PRINTLN("Emoncms client starting...");
  // wait 1 second for initialization:
  delay(1000);
  if (!Ethernet.begin(mac)) 
  {
    // if DHCP fails, start with a hard-coded address:
    DEBUG_PRINTLN("Failed to get an IP address using DHCP, forcing manually");
    Ethernet.begin(mac, ip, dns, gw, subnet);
  }
  printStatus();
  delay(1000);

  int trys=0;
  while(!getNtpTime() && trys < 10) 
  {
    trys++;
  }
  if(trys<10)
  {
    ntpUpdated = true;
    DEBUG_PRINTLN("NTP server update success");  
    digitalClockDisplay();
  }
  else
  {
    ntpUpdated = false;
    DEBUG_PRINTLN("NTP server update failed");
  }

  readSSS();

  DEBUG_PRINT("Sensor0\t" );
  DEBUG_PRINT("Sensor1\t" );
  DEBUG_PRINT("Sensor2\t" );
  DEBUG_PRINT("Sensor3\t" );
  DEBUG_PRINT("Sensor4\t" );
  DEBUG_PRINT("Sensor5\t" );
  DEBUG_PRINT("Relay1\t" );
  DEBUG_PRINTLN("Relay2" );
  DEBUG_PRINTLN("End of setup");

}


Dichas tareas son, principalmente, las siguientes:
  1. Se inicializa el puerto serie de comunicaciones (para poder ver en el PC determinados mensajes a modo de traza).
  2. Se establecen determinados pines del Arduino en modo "salida", ya que van a actuar sobre elementos externos, fundamentalmente relés, aunque también sobre aquellos pines que interactúan comunicándose con la placa Ethernet que montamos sobre el Arduino.
  3. Tras poner la tensión de referencia analógica por defecto e imprimir un mensaje (esto es a lo que me refería antes con el puerto serie) que veríamos en el "monitor serial" si tuviésemos conectado el Arduino al PC, se espera un segundo para dar tiempo a que la placa ethernet se inicialice y después se arranca con la función "begin".
  4. Dejamos otro segundo para que todo vaya correctamente y llamamos a la función "getNtpTime" (que veremos más adelante), que lo que hace es intentar conectarse por internet a un servidor proveedor de hora y fecha. Establecemos 10 reintentos, y en una variable denominada "ntpUpdated", indicamos si lo hemos logrado (verdadero o falso).
  5. Se llama a la función ReadSSS, que lo que hace es leer (ya veremos como) un valor de una "feed" (ya vermos tambien que es) de emoncms.org, que nos dira el estado del sistema (Set System Status-> SSS).
  6. Tras esto, mandamos a consola una especie de cabecera, para ir anotando debajo las lecturas de los diferentes sensores posteriormente.

La función "getNtpTime" es la siguiente (hay muchos ejemplos en Internet). No voy a entrar en detalle sobre esta función, que se apoya en otra más llamada "sendNTPpacket". Simplemente funciona y ya está...
También pongo el código de la función "digitalClockDisplay" que manda al monitor Serial el valor de hora y fecha leído.

unsigned long getNtpTime()
{

  Udp.begin(localPort);
  sendNTPpacket(timeServer); // send an NTP packet to a time server

    // wait to see if a reply is available
  delay(1000);
  if ( Udp.parsePacket() ) {
    // We've received a packet, read the data from it
    Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);

    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord &lt;&lt; 16 | lowWord;
    DEBUG_PRINT("Seconds since Jan 1 1900 = " );
    DEBUG_PRINTLN(secsSince1900);              

    // now convert NTP time into everyday time:
    DEBUG_PRINT("Unix time = ");
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;    
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;
    // print Unix time:
    DEBUG_PRINTLN(epoch);                              

    // print the hour, minute and second:
    DEBUG_PRINT("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
    DEBUG_PRINT((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)

    epoch += timeZoneOffset; // Adding seconds for local time

    DEBUG_PRINT(':');
    if ( ((epoch % 3600) / 60) &lt; 10 ) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      DEBUG_PRINT('0');
    }
    DEBUG_PRINT((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
    DEBUG_PRINT(':');
    if ( (epoch % 60) &lt; 10 ) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      DEBUG_PRINT('0');
    }
    DEBUG_PRINTLN(epoch %60); // print the second

    //setTime((epoch  % 86400L) / 3600,(epoch % 3600) / 60,epoch % 60,28,1,2014);
    ///////////////////////////////////////////////////////////
    // Quitar anterior setTime y dejar este:
    // (de: http://www.openreefs.com/ntpServer )
    unsigned long epoch2;
    epoch2 = highWord &lt;&lt; 16 | lowWord;
    epoch2 = epoch2 - 2208988800 + timeZoneOffset;
    setTime(epoch2);
    ///////////////////////////////////////////////////////////


    Udp.stop();
    return OK;
  }
  else
  {
    DEBUG_PRINTLN("ERROR: No NTP packet received");
    return KO;
  }
}

unsigned long sendNTPpacket(IPAddress&amp; address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay &amp; Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:     
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket();
}

void digitalClockDisplay(){
  // digital clock display of the time
  DEBUG_PRINT(hour());
  printDigits(minute());
  printDigits(second());
  DEBUG_PRINT(" ");
  DEBUG_PRINT(day());
  DEBUG_PRINT(" ");
  DEBUG_PRINT(month());
  DEBUG_PRINT(" ");
  DEBUG_PRINT(year());
  DEBUG_PRINTLN();
}


¡¡¡ Ala, a practicar !!!

Con esto ya se puede hacer un programita sencillo que se conecte a Internet para leer la hora, pero no os lo voy a dar todo hecho...

2 comentarios:

  1. muchas gracias por el codigo,puestos a pedir podrias esplicar como se comunica con emoncms.
    por cierto utilizo tu pagina de emons para saber como esta el tiempo si que tal a hecho ,si ha sido nublado o soleado ,es una pasada.

    ResponderEliminar
  2. A ver Fram Mart... un poco de paciencia. Lo voy a publicar TOOOOOOODOOOOOO. Pero esto lo hago en ratos libres y sin cobrar, así que sólo pido un poco de paciencia,¿ok ? Pues eso... Por cierto, mientras esperas podías investigar un poco como hacer lo que me preguntas, no ? Eso es lo que hemos hecho los demás...
    Graciaaaaaasssss...

    ResponderEliminar