Telemetría y data logger with Arduino Part II


Para el proyecto, tal y como describí en la parte I del artículo, cuento con unas radios 433Mhz de 3DR que funcionan a la perfección. Adjuntan un programa de conexión muy interesante y cómo y además operan valores TTL, con lo que se conectan directamente al puerto serie de arduino.

Para comprobar la conexión utilicé el los programas y el LCD descritos en un post anterior

Llegado este punto, y siendo breve, dudé entre efectuar los calculos en tierra o en el rover, pero puesto que uno de los principales usos de mi sistema será el de logger para un posterior proceso, he decidido que los haré en el rover y los datos serán enviados procesados a la base, la cual solo mostrará los mismos, y en una versión futura, será capaz de enviar ordenes al rover.

Siguiendo con el proyecto, el GPS EM-411 (el mismo que el EM-406 pero cumpliendo RoHS y con los pines rx/tx traspuestos), también ha llegado. Me ha soprendido lo completo que es por su ridiculo coste ($24) y lo recomiendo siempre y cuando la cadencia (1Hz) sea suficiente. Gracias a que monta un chip SiRFIII es compatible con un programa de configuración que resulta extremadamente cómodo para configurar el mismo: SiRFDEMO.exe (buscarlo así en google)


For the project, as described in Part I of the article, I have two 433Mhz radios by 3DR. The radios comes with a very interesting connection program and also operate at TTL values​​, which are connected directly to the serial port of Arduino.

To check the connection i use a sketch and LCD such is described here

At this point, and be brief, I hesitated between the data processing on land or in the rover, but since one of the main uses of my system will be the logger for further process, I have decided to do in the rover and processed data will be sent to the base, which will show them, and in a future version will be able to send commands from land to rover

Following the project, the GPS EM-411 (the same as the EM-406 but complying with RoHS and pin rx / tx transposed), has also arrived. I was surprised by how complete is the ridiculous cost ($24) and I recommend it as long as the rate (1Hz) is sufficient. Because mounting a SiRFIII chip supports a configuration program that is extremely easy to configure the same: SiRFDEMO.exe (Google it)

sirfdemo.exe
Programa de configuración Sirfdemo

Tiene dos posibles configuraciones muy interesantes, admite programar los parámetros NMEA que necesitamos mostrar (hay algunos como la dilución o los numeros de los satélites que se están mostrando que no nos son necesarios) y la cadencia de los mismos, así como la posibilidad de activar el protocolo SiRF en lugar de NMEA el cual sin aún haberlo estudiado, a priori parece interesante.

Sirfdemo NMEA Config
Dialogo de configuración de los comandos NMEA que enviará el EM-411 así como la cadencia

Una vez configurado el gps con los parámetros básicos y necesarios (para comenzar con el comando GGA nos será suficiente)

Para las pruebas iniciales he querido simplificarlo todo, y el programa montado el Rover, que se puede ver en la foto compuesto por un arduino mega, un pack de baterías ni-mh, el gps y la radio, simplemente recogía los datos de gps y los enviaba por la radio. De hecho, si hubiese cableado el gps directamente a la radio el efecto hubiera sido el mismo, pero quería meter el arduino para posteriormente intercalar más datos como el sensor pitot y un termómetro unica y exclusivamente por que lo tengo en un cajón aburrido.

Rover y Base
Rover y Base

Como el unico objetivo era transmitir los datos desde el Rover a Base y testear el alcance de las antenas, el proceso lo he ejecutado en el Base.
He utilizado la librería TinyGPS que aparentemente funciona bastante bien, y los programas usados han sido los siguientes:


ROVER:

void setup(){
  Serial1.begin(4800);
  Serial2.begin(57600);
}

void loop(){
  while(Serial1.available()){
    Serial2.write(Serial1.read());
  }
}

Y para el Base, simplemente he usado un ejemplo que traia la librería, modificado ligeramente:

#include <TinyGPS.h>

/* This sample code demonstrates the normal use of a TinyGPS object.
   It requires the use of SoftwareSerial, and assumes that you have a
   4800-baud serial GPS device hooked up on pins 3(rx) and 4(tx).
*/

TinyGPS gps;

static void gpsdump(TinyGPS &gps);
static bool feedgps();
static void print_float(float val, float invalid, int len, int prec);
static void print_int(unsigned long val, unsigned long invalid, int len);
static void print_date(TinyGPS &gps);
static void print_str(const char *str, int len);

void setup()
{
  Serial.begin(115200);
  Serial1.begin(57600);
  
  Serial.print("Testing TinyGPS library v. "); Serial.println(TinyGPS::library_version());
  Serial.println("by Mikal Hart");
  Serial.println();
  Serial.print("Sizeof(gpsobject) = "); Serial.println(sizeof(TinyGPS));
  Serial.println();
  Serial.println("Sats HDOP Latitude Longitude Fix  Date       Time       Date Alt     Course Speed Card  Distance Course Card  Chars Sentences Checksum");
  Serial.println("          (deg)    (deg)     Age                        Age  (m)     --- from GPS ----  ---- to London  ----  RX    RX        Fail");
  Serial.println("--------------------------------------------------------------------------------------------------------------------------------------");
}

void loop()
{
  bool newdata = false;
  unsigned long start = millis();
  
  // Every second we print an update
  while (millis() - start < 1000)
  {
    if (feedgps())
      newdata = true;
  }
  
  gpsdump(gps);
}

static void gpsdump(TinyGPS &gps)
{
  float flat, flon;
  unsigned long age, date, time, chars = 0;
  unsigned short sentences = 0, failed = 0;
  static const float LONDON_LAT = 51.508131, LONDON_LON = -0.128002;
  
  print_int(gps.satellites(), TinyGPS::GPS_INVALID_SATELLITES, 5);
  print_int(gps.hdop(), TinyGPS::GPS_INVALID_HDOP, 5);
  gps.f_get_position(&flat, &flon, &age);
  print_float(flat, TinyGPS::GPS_INVALID_F_ANGLE, 9, 5);
  print_float(flon, TinyGPS::GPS_INVALID_F_ANGLE, 10, 5);
  print_int(age, TinyGPS::GPS_INVALID_AGE, 5);

  print_date(gps);

  print_float(gps.f_altitude(), TinyGPS::GPS_INVALID_F_ALTITUDE, 8, 2);
  print_float(gps.f_course(), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  print_float(gps.f_speed_kmph(), TinyGPS::GPS_INVALID_F_SPEED, 6, 2);
  print_str(gps.f_course() == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(gps.f_course()), 6);
  print_int(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0UL : (unsigned long)TinyGPS::distance_between(flat, flon, LONDON_LAT, LONDON_LON) / 1000, 0xFFFFFFFF, 9);
  print_float(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : TinyGPS::course_to(flat, flon, 51.508131, -0.128002), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  print_str(flat == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(TinyGPS::course_to(flat, flon, LONDON_LAT, LONDON_LON)), 6);

  gps.stats(&chars, &sentences, &failed);
  print_int(chars, 0xFFFFFFFF, 6);
  print_int(sentences, 0xFFFFFFFF, 10);
  print_int(failed, 0xFFFFFFFF, 9);
  Serial.println();
}

static void print_int(unsigned long val, unsigned long invalid, int len)
{
  char sz[32];
  if (val == invalid)
    strcpy(sz, "*******");
  else
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
  feedgps();
}

static void print_float(float val, float invalid, int len, int prec)
{
  char sz[32];
  if (val == invalid)
  {
    strcpy(sz, "*******");
    sz[len] = 0;
        if (len > 0) 
          sz[len-1] = ' ';
    for (int i=7; i<len; ++i)
        sz[i] = ' ';
    Serial.print(sz);
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1);
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(" ");
  }
  feedgps();
}

static void print_date(TinyGPS &gps)
{
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned long age;
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  if (age == TinyGPS::GPS_INVALID_AGE)
    Serial.print("*******    *******    ");
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d %02d:%02d:%02d   ",
        month, day, year, hour, minute, second);
    Serial.print(sz);
  }
  print_int(age, TinyGPS::GPS_INVALID_AGE, 5);
  feedgps();
}

static void print_str(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  feedgps();
}

static bool feedgps()
{
  while (Serial1.available())
  {
    if (gps.encode(Serial1.read()))
      return true;
  }
  return false;
}

Y la salida generada por le puerto serie del Base ha sido la siguiente:


Testing TinyGPS library v. 12
by Mikal Hart
Sizeof(gpsobject) = 115

Sats HDOP Latitude Longitude Fix Date Time Date Alt Course Speed Card Distance Course Card Chars Sentences Checksum
(deg) (deg) Age Age (m) --- from GPS ---- ---- to London ---- RX RX Fail
--------------------------------------------------------------------------------------------------------------------------------------
**** **** ******* ******* **** ******* ******* **** ******* ****** ***** *** 0 0.00 *** 136 0 0
**** **** ******* ******* **** ******* ******* **** ******* ****** ***** *** 0 0.00 *** 490 0 0
**** **** ******* ******* **** ******* ******* **** ******* ****** ***** *** 0 0.00 *** 608 0 0
**** **** ******* ******* **** ******* ******* **** ******* ****** ***** *** 0 0.00 *** 744 0 0
7 220 40.53405 -3.90062 557 09/18/2012 18:48:37 580 727.90 143.65 0.30 SE 1254 12.09 NNE 76308 444 1
7 220 40.53405 -3.90062 558 09/18/2012 18:48:38 594 727.80 144.91 0.22 SE 1254 12.09 NNE 76510 446 1
7 220 40.53405 -3.90062 558 09/18/2012 18:48:39 573 727.60 138.75 0.26 SE 1254 12.09 NNE 76712 448 1
7 220 40.53405 -3.90062 139 09/18/2012 18:48:40 143 727.50 138.09 0.28 SE 1254 12.09 NNE 77092 450 1
7 220 40.53405 -3.90062 596 09/18/2012 18:48:41 611 727.40 142.05 0.26 SE 1254 12.09 NNE 77294 452 1
7 220 40.53405 -3.90062 599 09/18/2012 18:48:42 613 727.20 146.51 0.26 SSE 1254 12.09 NNE 77496 454 1
7 220 40.53405 -3.90062 596 09/18/2012 18:48:43 642 727.10 140.14 0.28 SE 1254 12.09 NNE 77698 456 1
7 220 40.53405 -3.90062 620 09/18/2012 18:48:44 645 727.00 137.82 0.26 SE 1254 12.09 NNE 77900 458 1
7 220 40.53405 -3.90062 222 09/18/2012 18:48:45 255 726.90 141.64 0.26 SE 1254 12.09 NNE 78280 460 1
7 220 40.53405 -3.90061 640 09/18/2012 18:48:46 670 726.70 139.36 0.24 SE 1254 12.09 NNE 78482 462 1
7 220 40.53405 -3.90061 650 09/18/2012 18:48:47 683 726.60 140.66 0.24 SE 1254 12.09 NNE 78696 464 1
7 220 40.53405 -3.90061 661 09/18/2012 18:48:48 687 726.50 146.14 0.28 SE 1254 12.09 NNE 78887 466 1

Con esto he probado una librería capaz de procesar NMEA y la comunicación a una distancía de 2km de mi casa, suficiente ya que en el campo de vuelo 1km ya es suficiente como para no ver el avión.


sirfdemo.exe
Programa de configuración Sirfdemo

It has two possible configurations, supports to program the NMEA parameters we need to show (there are some as dilution or numbers of the satellites are showing that we are needed) and the rate thereof, and the possibility of activating the SIRF protocol instead NMEA. I have not studied yet, but it looks interesting.

Sirfdemo NMEA Config
Dialogo de configuración de los comandos NMEA que enviará el EM-411 así como la cadencia

After configuring the GPS with basic and necessary parameters (to start with the GGA command will be enought)

For initial testing I wanted to simplify everything, and mounted the Rover program, which you can see in the picture consists of a arduino mega, a battery pack ni-mh, GPS and radio, just collected GPS data sent them by radio. In fact, if he had wired the GPS directly to the radio the effect would be the same, but I wanted to put the arduino to collate more data later as pitot sensor and thermometer exclusively by that I have it in a box boring.

Rover y Base
Rover y Base

As the only purpose was to transmit the data from the Base to Rover to test the range of the antennas, I’ve run the process on the Base.
I used the TinyGPS library wich works quite well, Here are the sketchs i did use:


ROVER:

void setup(){
  Serial1.begin(4800);
  Serial2.begin(57600);
}

void loop(){
  while(Serial1.available()){
    Serial2.write(Serial1.read());
  }
}

And for the Base, just an library example slightly modified:

#include <TinyGPS.h>

/* This sample code demonstrates the normal use of a TinyGPS object.
   It requires the use of SoftwareSerial, and assumes that you have a
   4800-baud serial GPS device hooked up on pins 3(rx) and 4(tx).
*/

TinyGPS gps;

static void gpsdump(TinyGPS &gps);
static bool feedgps();
static void print_float(float val, float invalid, int len, int prec);
static void print_int(unsigned long val, unsigned long invalid, int len);
static void print_date(TinyGPS &gps);
static void print_str(const char *str, int len);

void setup()
{
  Serial.begin(115200);
  Serial1.begin(57600);
  
  Serial.print("Testing TinyGPS library v. "); Serial.println(TinyGPS::library_version());
  Serial.println("by Mikal Hart");
  Serial.println();
  Serial.print("Sizeof(gpsobject) = "); Serial.println(sizeof(TinyGPS));
  Serial.println();
  Serial.println("Sats HDOP Latitude Longitude Fix  Date       Time       Date Alt     Course Speed Card  Distance Course Card  Chars Sentences Checksum");
  Serial.println("          (deg)    (deg)     Age                        Age  (m)     --- from GPS ----  ---- to London  ----  RX    RX        Fail");
  Serial.println("--------------------------------------------------------------------------------------------------------------------------------------");
}

void loop()
{
  bool newdata = false;
  unsigned long start = millis();
  
  // Every second we print an update
  while (millis() - start < 1000)
  {
    if (feedgps())
      newdata = true;
  }
  
  gpsdump(gps);
}

static void gpsdump(TinyGPS &gps)
{
  float flat, flon;
  unsigned long age, date, time, chars = 0;
  unsigned short sentences = 0, failed = 0;
  static const float LONDON_LAT = 51.508131, LONDON_LON = -0.128002;
  
  print_int(gps.satellites(), TinyGPS::GPS_INVALID_SATELLITES, 5);
  print_int(gps.hdop(), TinyGPS::GPS_INVALID_HDOP, 5);
  gps.f_get_position(&flat, &flon, &age);
  print_float(flat, TinyGPS::GPS_INVALID_F_ANGLE, 9, 5);
  print_float(flon, TinyGPS::GPS_INVALID_F_ANGLE, 10, 5);
  print_int(age, TinyGPS::GPS_INVALID_AGE, 5);

  print_date(gps);

  print_float(gps.f_altitude(), TinyGPS::GPS_INVALID_F_ALTITUDE, 8, 2);
  print_float(gps.f_course(), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  print_float(gps.f_speed_kmph(), TinyGPS::GPS_INVALID_F_SPEED, 6, 2);
  print_str(gps.f_course() == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(gps.f_course()), 6);
  print_int(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0UL : (unsigned long)TinyGPS::distance_between(flat, flon, LONDON_LAT, LONDON_LON) / 1000, 0xFFFFFFFF, 9);
  print_float(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : TinyGPS::course_to(flat, flon, 51.508131, -0.128002), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  print_str(flat == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(TinyGPS::course_to(flat, flon, LONDON_LAT, LONDON_LON)), 6);

  gps.stats(&chars, &sentences, &failed);
  print_int(chars, 0xFFFFFFFF, 6);
  print_int(sentences, 0xFFFFFFFF, 10);
  print_int(failed, 0xFFFFFFFF, 9);
  Serial.println();
}

static void print_int(unsigned long val, unsigned long invalid, int len)
{
  char sz[32];
  if (val == invalid)
    strcpy(sz, "*******");
  else
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
  feedgps();
}

static void print_float(float val, float invalid, int len, int prec)
{
  char sz[32];
  if (val == invalid)
  {
    strcpy(sz, "*******");
    sz[len] = 0;
        if (len > 0) 
          sz[len-1] = ' ';
    for (int i=7; i<len; ++i)
        sz[i] = ' ';
    Serial.print(sz);
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1);
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(" ");
  }
  feedgps();
}

static void print_date(TinyGPS &gps)
{
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned long age;
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  if (age == TinyGPS::GPS_INVALID_AGE)
    Serial.print("*******    *******    ");
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d %02d:%02d:%02d   ",
        month, day, year, hour, minute, second);
    Serial.print(sz);
  }
  print_int(age, TinyGPS::GPS_INVALID_AGE, 5);
  feedgps();
}

static void print_str(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  feedgps();
}

static bool feedgps()
{
  while (Serial1.available())
  {
    if (gps.encode(Serial1.read()))
      return true;
  }
  return false;
}

Here is the output generated by the Base Serial:


Testing TinyGPS library v. 12
by Mikal Hart
Sizeof(gpsobject) = 115

Sats HDOP Latitude Longitude Fix Date Time Date Alt Course Speed Card Distance Course Card Chars Sentences Checksum
(deg) (deg) Age Age (m) --- from GPS ---- ---- to London ---- RX RX Fail
--------------------------------------------------------------------------------------------------------------------------------------
**** **** ******* ******* **** ******* ******* **** ******* ****** ***** *** 0 0.00 *** 136 0 0
**** **** ******* ******* **** ******* ******* **** ******* ****** ***** *** 0 0.00 *** 490 0 0
**** **** ******* ******* **** ******* ******* **** ******* ****** ***** *** 0 0.00 *** 608 0 0
**** **** ******* ******* **** ******* ******* **** ******* ****** ***** *** 0 0.00 *** 744 0 0
7 220 40.53405 -3.90062 557 09/18/2012 18:48:37 580 727.90 143.65 0.30 SE 1254 12.09 NNE 76308 444 1
7 220 40.53405 -3.90062 558 09/18/2012 18:48:38 594 727.80 144.91 0.22 SE 1254 12.09 NNE 76510 446 1
7 220 40.53405 -3.90062 558 09/18/2012 18:48:39 573 727.60 138.75 0.26 SE 1254 12.09 NNE 76712 448 1
7 220 40.53405 -3.90062 139 09/18/2012 18:48:40 143 727.50 138.09 0.28 SE 1254 12.09 NNE 77092 450 1
7 220 40.53405 -3.90062 596 09/18/2012 18:48:41 611 727.40 142.05 0.26 SE 1254 12.09 NNE 77294 452 1
7 220 40.53405 -3.90062 599 09/18/2012 18:48:42 613 727.20 146.51 0.26 SSE 1254 12.09 NNE 77496 454 1
7 220 40.53405 -3.90062 596 09/18/2012 18:48:43 642 727.10 140.14 0.28 SE 1254 12.09 NNE 77698 456 1
7 220 40.53405 -3.90062 620 09/18/2012 18:48:44 645 727.00 137.82 0.26 SE 1254 12.09 NNE 77900 458 1
7 220 40.53405 -3.90062 222 09/18/2012 18:48:45 255 726.90 141.64 0.26 SE 1254 12.09 NNE 78280 460 1
7 220 40.53405 -3.90061 640 09/18/2012 18:48:46 670 726.70 139.36 0.24 SE 1254 12.09 NNE 78482 462 1
7 220 40.53405 -3.90061 650 09/18/2012 18:48:47 683 726.60 140.66 0.24 SE 1254 12.09 NNE 78696 464 1
7 220 40.53405 -3.90061 661 09/18/2012 18:48:48 687 726.50 146.14 0.28 SE 1254 12.09 NNE 78887 466 1

With this I have tested a library NMEA capable of processing and communication at a distance of 2km from my house, enough as the airfield is 1km longer enough to not see the plane.


2 comentarios de “Telemetría y data logger with Arduino Part II”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *