Arduino + pantalla OLED (SSD1306)

Recientemente me ha llegado una pantalla OLED de 128×64 pixels, preparada para arduino (permite la comunicación mediante SPI), comprada en ebay por unos 10€. Por si aún no puede ser mas sencilla la comunicación por hardware (gracias a estas placas ya ensambladas, sino a ver quien se traga la cantidad de información técnica para utilizar la pantalla: http://www.gemmarduino.net/PM1/00100-101/SSD1306.pdf ), existe una librería para arduino, que también nos simplifica mucho el código. Se trata de la librería U8glib (Universal Graphics Library for 8bits Embedded Systems) que soporta una serie de dispositivos de este tipo, entre ellos el SSD1306 (aquí la lista completa y el constructor de la clase a utilizar).

Para empezar vamos a tener que descargar la librería U8glib y añadirla al directorio de libraries del IDE de arduino. Luego arrancamos el IDE y ya podremos utilizarla (esto es fácil y no voy a entrar en detalle).

Os dejo un simple sketch como ejemplo, que permite ver algunas de las funcionalidades de esta librería y de paso probar como funciona la pantalla OLED. Para mi ejemplo he tenido que utilizar la clase U8GLIB_SSD1306_128X64 que utilizar comunicación SPI por software y no por hardware. Esto dependerá un poco del circuito que compréis.

#include <U8glib.h>

#define cs 2     // CS
#define a0 3     // DC
#define reset 4  // RST
#define sck 5    // D0
#define mosi 6   // D1

int x, y;

//SPI Comunicación por SW (sck, mosi, cs, a0 , reset)
U8GLIB_SSD1306_128X64 u8g(sck, mosi, cs, a0, reset);

void setup() {
  x = 50;
  y = 5;
}

void loop() {
  // picture loop
  u8g.firstPage();
  do {
   draw("Hello mundo!!!!");
   pie(x, y, "by akirasan 2k12");

   u8g.drawBox(10,22,20,30);

   u8g.drawCircle(50, 50, 14);

   u8g.drawFrame(110,30,15,10);

   u8g.drawPixel(14,23);
   u8g.drawPixel(24,43);
   u8g.drawPixel(125,53);
   u8g.drawPixel(0,0);
   u8g.drawPixel(127,0);
   u8g.drawPixel(127,63);
   u8g.drawPixel(0,63);

  } while( u8g.nextPage() );
  if (y <= 62) {
    y++;
  }

  // rebuild the picture after some delay
  delay(5);
}

void draw(char* text) {
  // graphic commands to redraw the complete screen should be placed here
  u8g.setFont(u8g_font_unifont);
  u8g.drawStr( 0, 20, text);
}

void pie(int x, int y, char* text) {
  // graphic commands to redraw the complete screen should be placed here
  u8g.setFont(u8g_font_04b_03);
  u8g.drawStr( x, y, text);
}

El resultado de este programita es el siguiente:

awesomeness.openphoto

Muy fácil, verdad??,…pues existe otra librería llamada http://code.google.com/p/m2tklib/ que soporta la U8glib y permite realizar GUI (graphical user interface), es decir, menús y control de entrada de datos para interactuar. Aún no la he probado, pero seguro que tiene que ser igualmente sencillo (un posible post en el futuro).

Existen otros tipos de pantalla OLED y evidentemente circuitos con pines diferentes. En este caso en concreto, voy a explicar cual es el pineado necesario y su relación con el constructor de la clase de la librería U8glib. Estos son los pines que trae este circuito:

awesomeness.openphoto

awesomeness.openphoto

Esta es la relación de pines (OLED) y parámetros (U8glib) y pines digitales (arduino) (sin contar con los pines de alimentación GND y V (a +3.3V) ):

 
PIN OLED Parámetro clase
U8GLIB_SSD1306_128X64
PIN arduino
CS cs 2
DC a0 3
RST reset 4
D0 sck 5
D1 mosi 6
CS cs 2

PP: Me he pedido otra pantalla de color blanco,… :)

Reparando Arduino nano

Una de las ventajas que tiene la plataforma arduino es que al ser opensource se puede disponer del esquema y poder encontrar posibles fallos e incluso repararlos. Como me ha pasado a mí,…tanto trastear con el arduino es lo que tiene,…así que tocó hacer un pedido en http://es.rs-online.com/web/ (que por cierto muy bien) y soldador en mano cambiar, en mi caso, el MBR0520.

Proyecto GPS+D90+Arduino (Parte 4 y “última”)

En esta última parte únicamente se trata de pasar el prototipo a una placa Arduino mini pro, la cual permite reducir el tamaño y poderlo encapsular mejor:

Aquí algunas fotos de las primeras pruebas de campo con el sistema “a la vista”:

He reciclado el conector del disparador para que me haga de caja. Para ello hay que desmontarlo todo y con la “dremel” vaciar el contenido de plástico para poder hacer hueco.

He tenido que sacrificar los tres led’s con indicaciones (aunque no descarto ponerlos en algún momento mediante LED’s SMD) y únicamente me he quedado con el indicador del estado del módulo bluetooth, que realmente “me da información“.

Le he incorporado un pequeño pulsador para poder realizar el reset, en caso que se quede sin señal o cualquier otra cosa. Así no tengo que quitarlo y volverlo a conectar (ya que la alimentación de la cámara es constante). La idea es colocarle un pequeño interruptor que permita apagar/encender y no tener que desmontarlo.

Para fijar el conector he utilizado, por primera vez, Sugru. Que aunque es muy versátil  y fácil de utilizar,…tiene su “que”. Las pruebas de campo fueron muy bien aquí os dejo alguna foto del cacharro y alguna de las fotos geoposicionadas directamente desde la cámara.

Fotos geoposicionadas en Panoramio:

PD: Tal vez haga una revisión 2 del proyecto (que he bautizado como qtrArduD90) con GPS incorporado y batería propia.

Proyecto GPS+D90+Arduino (Parte 3)

Tercera parte  del proyecto: Ya tengo el engendrillo montado y funcionando en modo prototipo!!!. El arduino está programado para recibir y transformar la información que recibe vía bluetooth el GPS (Holux M-241) y envía por el conector a la cámara (Nikon D90). Antes de pasar a la teoría aburrida del código, aquí os dejo alguna foto:

Le he puesto tres led’s para conocer el estado (rojo = estado del bluetooth, verde = operativo, amarillo = proceso de configuración). En el código veréis referencias y funciones para tratar los leds:

En esta parte prácticamente es todo programación. He colocado bastantes comentarios en el sketch y el código está muy modularizado para simplificar y entender las acciones. Como podéis ver, en las funciones estandars setup() y loop(), es muy simple:

void setup()
{
  configurar_pines();
  check_leds();            //secuencia de chequeo visual de los leds
  pinMode(ledR,INPUT);     //redefinimos pin del led rojo para que funcione como led del estado del bluetooth
  configurar_bluetooth();
  onled(ledV);
}

void loop()
{
  while (bt.available()){
    i = 0;
    buffer[i] = (char)bt.read();
    if (buffer[i]=='$'){        //inicio sentencia
      leer_comando();           //lee hasta la "," i=","
      validar_comando();        //verifica si es $GPGGA o $GPRMC
      if (_GPGGA || _GPRMC){
        procesar_comando();     //comando válido
        i = 0;
      }
    }//fin inicio sentencia
  }//fin bt.available
}//fin loop

…pero, hay un par de puntos que hay que tener en cuenta:

  1. Configurar el módulo de bluetooth para conectarse al GPS, es decir, que cuando arranquemos el bluetooth sea capaz de emparejarse con el GPS.
  2. Tratar las sentencias NMEA que envía el GPS para que las reconozca la Nikon D90. Hay un par de temillas a tener en cuenta :)

Configurar módulo bluetooth

Antes de utilizarlo, es recomendable conectarlo vía adaptador USB-UART(TTL) y entrar en modo AT para configurar la velocidad por defecto, que es de 9600 a 4800. Velocidad con la que trabajaremos, ya que la Nikon D90 utiliza esta velocidad para recibir los datos. Esta parte es simple, únicamente necesitas un adaptador serial-USB y conectarte al puerto COM. Puedes utilizar el mismo monitor de serial que viene en el IDE de Arduino o utilizar el Putty u otros,…(recordad que para entrar en modo AT hay que seguir lo que se comenta en el siguiente párrafo para entrar en modo AT). Comandos AT que se pueden utilizar. Necesitaréis algo como esto:

Este módulo conversor con chip CP2102 es bastante barato y en ebay lo podéis encontrar por unos 3$ (con gastos incluidos).

Para que el módulo BT (bluetooth) se conecte al GPS hay que inicializarlo en modo AT, es decir, que acepte los comandos de configuración AT. Para ello hay que poner en HIGH (tensión) el pin 34 (o el PIO11) y luego poner encender el módulo. Esto hará que el módulo BT acepte comandos AT.

Básicamente le enviaremos los comandos de fijar a rol master, fijamos el password o pincode (del módulo GPS), emparejamiento y vinculación:

void iniciar_BT_modoAT(){
  // Entrar en modo AT - pin34_HIGH + power
  flashled(ledA,2);
  digitalWrite(bt_PowerPin, HIGH);
  delay(4000);
  flashled(ledA,1);
  digitalWrite(bt_KEYPin, HIGH);
  delay(1000);
  bt.listen();
}

void finalizar_BT_modoAT(){
  // Quitamos power al pin34
  digitalWrite(bt_KEYPin, LOW);
}

void configurar_bluetooth()
{
  String result, bt_pincode, bt_mac;

  flashled(ledA,3);
  Serial.println("Inicializando modulo BT...");

  iniciar_BT_modoAT();

  // Entramos en modo master============================== se supone que debe estar en role=1
  flashled(ledA,2);
  Serial.println("-----------------role");
  enviar_comandoAT("AT+ROLE=1", &result);
  Serial.println(result);

  // Fijamos el pin==============================
  flashled(ledA,2);
  bt_pincode = BT_pincode;
  Serial.println("-----------------password");
  enviar_comandoAT("AT+PSWD="+bt_pincode, &result);
  Serial.println(result);

  // Fijamos el emparejamiento==============================
  flashled(ledA,2);
  bt_mac = BT_MAC;
  Serial.println("-----------------pair");
  enviar_comandoAT("AT+PAIR="+bt_mac+",20", &result);
  Serial.println(result);

  // Vinculamos al GPS==============================
  flashled(ledA,2);
  Serial.println("-----------------link");
  enviar_comandoAT("AT+LINK="+bt_mac, &result);
  Serial.println(result);

  finalizar_BT_modoAT();
}

void enviar_comandoAT(String cmd, String *resultado)
{
  String result = "";

  Serial.print("\n-----llamamos al comando "+cmd+" --> ");

  for (int i2=0; i2<10 ; i2++){
    while (bt.available()){
      Serial.print((char)bt.read());
    }
  }

  bt.print(cmd+"\r\n");
  delay(3000);

  if (bt.available()){
    while(bt.available())
    {
      result.concat((char)bt.read());
    }
  }
  Serial.print("------resultado:"+result);
  *resultado = result;
}

En el sketch está indicado, pero hay que tener en cuenta un detalle con el número de la MAC del dispositivo al que nos tenemos que conectar, es decir, hay que adaptar su formato. Por ejemplo una MAC del estilo 00:1b:c1:06:1b:6b se debe enviar así 1B,C1,61B6B.

Adaptación de sentencias NMEA

De todas las sentencias NMEA que envía el GPS, la Nikon D90 únicamente reconoce (o simplemente utiliza) dos: $GPGGA y $GPRMC. En mi caso, de estas sentencias, la $GPGGA requiere además una pequeña adaptación para que la D90 la entienda (tal vez sería bueno una actualización de firmware de la cámara,…). Así que ojo con el módulo de GPS que utilizáis, porque tal vez no sea necesaria esta adaptación,…o si,…hay que verificarlo. Para ello conéctalo por un puerto COM y monitoriza sus sentencias NMEA.

Ejemplo de una trama en formato NMEA:

$GPGGA,152848.000,4199.9999,N,00209.9999,E,1,7,1.46,10.8,M,51.3,M,,*6B
$GPGSA,A,3,02,10,07,13,05,04,23,,,,,,1.72,1.46,0.90*0A
$GPGSV,3,1,12,10,79,346,22,07,67,128,16,13,51,047,27,04,50,216,28*72
$GPGSV,3,2,12,02,48,279,33,08,43,179,,23,23,063,30,05,21,304,31*7C
$GPGSV,3,3,12,16,08,044,,26,03,250,,20,02,120,,49,,,*4B
$GPRMC,152848.000,A,4199.9999,N,00209.9999,E,0.31,0.27,020212,,,A*67
$GPVTG,0.27,T,,M,0.31,N,0.57,K,A*38

Sentencia $GPGGA. Dos son las modificaciones que con el GPS Holux M-241 hay que hacer:

  1. Número de satélites
  2. El número de satélites tiene que ser de dos dígitos, por lo que si recibimos un número inferior a 10, tendremos que añadir un 0 (cero) por delante. Por ejemplo, esta sentencia:

    $GPGGA,152848.000,4199.9999,N,00209.9999,E,1,7,1.46,10.8,M,51.3,M,,*6B

    Se tiene que sustituir por:

    $GPGGA,152848.000,4199.9999,N,00209.9999,E,1,07,1.46,10.8,M,51.3,M,,*6B

    Esta info la descubrí por propia experiencia la cual confirmé en esta web http://we.easyelectronics.ru/upgrade-repair/analog-nikon-gps.html (ojo!!! que el contenido está en ruso).

  3. Altitud
  4. Algo parecido al anterior punto le pasa al número que marca la altitud en metros. Hay que hacer lo mismo, rellenar con 0 (ceros) hasta completar un número con cuatro digitos. Un ejemplo:

    $GPGGA,152848.000,4199.9999,N,00209.9999,E,1,07,1.46,10.8,M,51.3,M,,*6B

    Debe quedar así:

    $GPGGA,152848.000,4199.9999,N,00209.9999,E,1,07,1.46,0010.8,M,51.3,M,,*6B

La parte del código que se encarga de hacer toda esta adaptación es esta:

void enviar_comando_gprmc(){
  int i2 = 7;
  //leer y enviar hasta final del comando --> previo al inicio del nuevo $
  Serial.print("$GPRMC,");    //enviamos lo que tenemos en el buffer
  while (bt.peek()!='$'){
    while (bt.available() && bt.peek()!='$'){
      Serial.print((char)bt.read());
    }
  }
}

void enviar_comando_gpgga(){
  int cs;
  int i2;
  leer_comando_entero();
  adaptar_satelites();      //  ",7," --> ",07," i=,
  adaptar_altitud();        //  ",148.0," --> ",00148.0,"
  cs=getCheckSum(buffer);
  Serial.write(buffer);
  Serial.print(cs,HEX);
  Serial.println("");
}

void adaptar_satelites(){
  int n=0;
  int i=0;
  while (n<8)              //llegamos hasta el final del comando numero #8
  {
    i++;
    if (buffer[i]==',') n++;
  }
  if (buffer[i-2]==','){
    for (n=strlen(buffer);n>i-2;n--){
      buffer[n+1] = buffer[n];
    }
    buffer[i-1]='0';
  }
}

void adaptar_altitud(){
  int n=0;
  int i=0;
  int k=0;
  int dig=6;

  while (n<10)   //llegamos hasta el comando n9 -->(k)XXXXXX(i) k=, inicial y i=, final
  {
    i++;
    if (n==8) k=i;
    if (buffer[i]==',') n++;
  }

  if (buffer[k+1]=='-'){
    k++;
    dig--;
  }

  int offset = dig-((i-k)-1);            //la longitud del dígito es de 6 o 5 dependiendo del signo - o +

  for (n=strlen(buffer);n>k;n--){        //desplazamos todo n posiciones segun offset
    buffer[n+offset] = buffer[n];
  }
  for (n=k+1;n<=k+offset;n++) buffer[n]='0';
}

Ojo!!! que en ocasiones también pueden venir altitudes negativas. Normalmente cuando no hay suficientes satélites para establecer una posición correcta (el código ya lo contempla).

Lo malo” de modificar la información que se envía de esta sentencia, es que el número de control o checksum (para verificar que la info es correcta), no sirve y hay que recalcularlo nuevamente. Para ello hay que utilizar un XOR desde el inicio de la sentencia, “$” hasta el “*“, y luego colocarlo en formato hexadecimal. Esta parte del código no es mía, lo saqué de aquí:
int getCheckSum(char *string) {
  int i2;
  int XOR;
  int c2;
  // Calculate checksum ignoring any $'s in the string
  for (XOR = 0, i2 = 0; i2 < strlen(string); i2++) {
    c2 = (unsigned char)string[i2];
    if (c2 == '*') break;
    if (c2 != '$') XOR ^= c2;
  }
  return XOR;
}

Un vez programado, prototipado y probado, ya solo queda traspasarlo y empaquetarlo todo debidamente, ponerle un pulsador de reset exterior y un interruptor de encendido. Para ello voy a utilizar un Arduino mini pro versión de 3.3V a 8Mhz (ya veremos si funciona!!! y es capaz de procesar toda la información correctamente y sin retrasos, ya que sino habrá perdidas en los datos).

Para acabar aquí os dejo el link para descargar el sketch, está realizado con la versión Arduino 1.0 (tenedlo en cuenta para la compilación). También agradecer en esta parte del proyecto la colaboración de fefago , por su aporte de información y comentarios ;) .

Links de referencia de esta parte:

http://www.cutedigi.com/pub/Bluetooth/BMX_Bluetooth_quanxin.pdf

http://www.gpsinformation.org/dale/nmea.htm

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1293745670

http://timzaman.wordpress.com/code-c-arduino/checksum-xor-cpp/

http://air.imag.fr/mediawiki/index.php/Wireless_Bluetooth_RS232_TTL_Transceiver_Module

Proyecto GPS+D90+Arduino (Parte 2)

Comenzamos la segunda parte del proyecto con “un pequeño contratiempo“. Ya me ha llegado el módulo Bluetooth para conectarlo con el Arduino. Se trata de un BT BC41713 de CSR (aquí podréis ver las especificaciones). Me ha salido por unos 7€ en ebay. Esto bluetooth viene configurado inicialmente como slave, lo que quiere decir que cualquier otro dispositivo (master) se puede conectar utilizando el pin que tiene por defecto, “1234″. Como mi idea es que este módulo se conecte al GPS (que es slave) tengo que “convertirlo” en master. Para ello hay que poner a HIGH el pin34 (llamado POI11 o en mi caso/módulo KEY) y mediante comandos AT (AT+ROLE=1) ponerlo como master.

…bueno esto era lo que yo pensaba, hasta que lo he probado y he visto que mi módulo no aceptaba este comando, ni otros comandos AT. Arrebuscando información por internet me he dado cuenta de un inconveniente: el chipi-chipi viene con un firmware llamado HC-06 o Linvor V1.5, el cual acepta muy pocos comandos AT. Básicamente estos:

Comando
Respuesta
Nota
AT
OK
Conexión lista!!!
AT+VERSION
Linvor1.5
Versión del firmware (firmware maldito!!!)
AT+BAUDx
OKyyyy
Configurar velocidad de datos donde x puede ser uno de esto números:
  • 1 para 1200 bps
  • 2     2400 bps
  • 3     4800 bps
  • 4     9600 bps
  • 5    19200 bps
  • 6    38400 bps
  • 7    57600 bps
  • 8   115200 bps
  • 9   230400 bps
  • A   460800 bps
  • B   921600 bps
  • C  1382400 bps
AT+NAMEString
OKsetname
Cambiar el nombre emitido por bluetooth
AT+PINxxxx
OKsetpin
Pincode, por defecto 1234

…y lo mas grave no permite el modo master :( . En este post tenéis info sobre el HC-06 o Linvor V1.5 (este hardware puede aceptar varios firmware, incluso existe un IDE para poder desarrollarlos uno mismo,…).

Llegados a este punto, solo me quedaba dos opciones: comprar un nuevo módulo master en ebay (10$) o tostar un firmware nuevo sobre el cacharro. Así que siguiendo el siguiente esquema, descargando el software BlueSuiteCasira 1.24 y recuperando un viejo PC con puerto paralelo LPT y WindowsXP,…he actualizado el firmware!!!,…

Evidentemente, y para un único uso, no he hecho ni una sola soldadura (bueno las del puerto LPT reaprovechado que he encontrado por casa). Así es como me ha quedado el “engendro” temporal. Lo increíble es que ha funcionado “a la primera“:

Ahora ya tengo el módulo tal y como yo quería. Siguiente paso,…programar el Arduino con los componentes pinchados!!!

Si queréis mas información aquí os dejo los links que he utilizado yo. Tendréis mas detalle (paso a paso de la aplicación para flashear, aunque no tiene mucho secreto) y un poco mas de teoría:

http://byron76.blogspot.com/

http://curiosidadesford.blogspot.com/2011/01/modulo-bluetooth-bluetooth-module.html

http://www.neoteo.com/modulo-bluetooth-hc-06-android

Proyecto GPS+D90+Arduino (Parte 1)

He comenzado un proyecto (y voy a tratar de acabar,…) en el cual, la idea básica es: conectar un GPS Holux M241 vía bluetooth a un módulo Arduino Nano v3.0 que está conectado por cable al conector de GPS de mi Nikon D90, con la finalidad de que la cámara geoposicione de forma automática las fotos. Vamos, que guarde en el Exif, info del punto donde fue tomada la foto. Este es el concepto.

En esta primera parte he conectado el Arduino por cable a la Nikon D90 y he simulado la entrada de datos del GPS en formato NMEA. Para ello hará falta: un Arduino (en mi caso un Nano v3.0, pero cualquier versión vale), un pequeño programa (sketch) para que Arduino simule la salida de datos y un conector para la entrada GPS de la cámara (ahora explicaré como conseguirlo).

El conector GPS para la Nikon D90: Se puede conseguir muy barato en eBay (por unos 5$-6$) un disparador remoto por cable (modelo MC-DC2). De este cable únicamente nos servirá el conector, el cual hay que volverlo a pinear correctamente. En esta web tenéis el pineado necesario (es de donde he sacado algo de información). Esta es la pinta que tiene el mío (evidentemente sin el envoltorio):

conector

Bien, una vez pineado el conector de forma correcta, hay que centrarse en el Arduino. Os dejo el programa que he generado como ejemplo para simular la entrada de datos GPS a la cámara:

 #include <SoftwareSerial.h>
 */
 GPS test D90
 SoftwareSerial(rxPin, txPin)
 rxPin = 2
 txPin = 3
 */
 SoftwareSerial mySerial(2, 3);
 void setup()
 {
 mySerial.begin(4800);
 pinMode(2,INPUT);
 pinMode(3,OUTPUT);
 pinMode(13,OUTPUT);
 }
 void loop()
 {
 digitalWrite(13,HIGH);
 mySerial.println("$GPGGA,154654,4428.2011,N,00440.5161,W,0,00,,-00044.7,M,051.6,M,,6B");
 mySerial.println("$GPGSA,A,1,,,,,,,,,,,,,,,1E");
 mySerial.println("$GPGSV,3,1,10,02,50,290,00");
 mySerial.println("$GPGGA,154655,4328.1874,N,00340.5185,W,1,03,08.5,-00044.7,M,051.6,M,,*79");
 mySerial.println("$GPGSA,A,2,13,23,25,,,,,,,,,,08.5,08.5,00.9*0E");
 mySerial.println("$GPGSV,3,1,10,02,50,290,26,04,60,210,26,08,33,173,29,10,21,296,00*7E");
 mySerial.println("$GPGSV,3,2,10,13,58,044,34,16,03,035,00,20,02,109,00,23,26,057,34*7B");
 mySerial.println("$GPGSV,3,3,10,25,24,045,35,27,56,145,27,,,,,,,,*7D");
 mySerial.println("$GPRMC,154655,A,4428.1874,N,00440.5185,W,000.7,000.0,050407,,,A*6C");
 digitalWrite(13,LOW);
 }

Ahora ya solo queda conectar el cable a la salida del Arduino y a la entrada GPS de la D90, dar corriente al Arduino y encender la cámara y verificar que correctamente está recibiendo los datos:

D90+GPS+Arduino

Si disparas alguna foto y consulta sus datos, verás que tiene la información del GPS.

Bueno hasta aquí la primera parte. Estoy a la espera de que me llegue el módulo bluetooth para pincharlo a la placa Arduino y ver de emparejar el GPS Holux (aunque evidentemente se podría utilizar un movil con GPS, porque no?)