Navegación GPS con ODROID-GO

ODROID-GO es uno de esos pequeños sistemas extensibles versátiles con los que, dado algo de imaginación, puedes crear proyectos muy útiles e interesantes. Aquí tienes uno de esos proyectos, que integra un sensor GPS para mostrar mapas GPS en un ODROID-GO.

Figura 1 - Mapa offline OSM en Youtube

El código fuente se puede obtener desde el repositorio git: https://github.com/ripper121/OdroidGoOSMGPSOffline

El firmware necesario se puede obtener desde esta ubicación: https://goo.gl/PgYF6C

El menú de teclas es el siguiente:

  • JoyPad = Te mueves
  • A/B = Te acercas/alejas (el nivel de zoom va de 5 a 16, dependiendo de los niveles de zoom de los mosaicos que tengas)
  • Select/Start = Brillo
  • Menu = Reiniciar Movimiento

Generar Mosaicos

Como cabría esperar, las limitaciones de diseño del ODROID-GO no permiten mapas en vivo. Por lo tanto, un mapa debe que estar predefinido y almacenado para su uso posterior. Los mosaicos son esencialmente estos mapas offline.

Los mosaicos se pueden descargar en el sistema MS Windows usando la aplicación TileDownloader.exe de: https://bit.ly/2HDsLRh (EDITOR: el acortador de URL de Google no funciona para este enlace)

El comando de una sola línea (reemplaza las coordenadas específicas de tu ubicación) crea un mosaico y lo recupera:

> TileDownloader.exe -URL \https://a.tile.openstreetmap.org/${z}/${x}/${y}.png 
-z 5 -left -0.489 -botton 51.28 -right 0.236 -top 51.686
Donde se pueden especificar las siguientes opciones:

  • -URL : TileServer
  • -z : Zoom level

y los límites del recuadro de selección son:

  • -left : min-longitude
  • -bottom : min-latitude
  • -right : max-longitude
  • -top : max-latitude

En algunas regiones, se puede usar una coma ',' en lugar del punto '.', para los valores del recuadro de selección

Se pueden usar otros servidores de mosaicos del siguiente enlace: https://wiki.openstreetmap.org/wiki/Tile_servers

También puedes obtener mosaicos adicionales de: https://goo.gl/DRusXL

Los mapas offline también se pueden almacenar en una tarjeta SD como puedes ver:

Figura 2 - ODROID-GO con un video youtube de un mapa GPS OSM offline

Utiliza un nivel de zoom de 5 a 14, con el marcador de posición como círculo rojo.

Este "desplazamiento" es una manipulación de las coordenadas GPS a través del JoyStick. Los siguientes pasos te permiten generar mosaicos (Mapa offline) para tu área:

  • 1. Abre Maperitive.exe (http://maperitive.net/)
  • 2. Mueve el mapa a tu posición favorita
  • 3. MAP->Set Geometry Bounds
  • 4. MAP->Set Printing Bounds

Ahora está fijada el área del mapa que quieres exportar.

  • 5. TOOLS-> Generate Tiles (esto puede llevar algo de tiempo, dependiendo de la resolución del nivel de zoom)
  • 6. Ahora encontrarás algunos archivos PNG en la carpeta Maperitive/tiles
  • 8. Abre Flexxi.exe (https://sourceforge.net/projects/flexxi-image-resizer/)
  • 9. Importa la carpeta Tiles
  • 10. Cambia el tamaño de la imagen a 240x240px (el mejor tamaño para la pantalla GO)
  • 11. Convierte a archivos JPG
  • 12. Guarda
  • 13. Ahora tienes tus mosaicos con el tamaño y formato correctos
  • 14. Copia la carpeta "TILES" (todo en mayúsculas) en el directorio raíz de su tarjeta SD

Abre Arduino IDE y graba el código en tu GO. Este es el Código Arduino:

#include 

#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 240
#define TILE_SIZE 240

bool firstRun = true;
double zoom = 10;
double lat_rad = 50.8225313, lon_deg = 12.7508936;
double tileX = 0, tileY = 0;
double old_lat_rad = 0, old_lon_deg = 0;
double old_zoom = 0;
double old_tileY = 0, old_tileX = 0;
uint8_t brightness = 127;

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(115200);
  GO.begin();
  GO.battery.setProtection(true);
  GO.lcd.clear();
  GO.lcd.setCursor(0, 0);

  if (!SD.begin()) {
    GO.lcd.println("Card Mount Failed");
    Serial.println("Card Mount Failed");
    return;
  }
  uint8_t cardType = SD.cardType();

  if (cardType == CARD_NONE) {
    GO.lcd.println("No SD card attached");
    Serial.println("No SD card attached");
    return;
  }

  Serial.print("SD Card Type: ");
  if (cardType == CARD_MMC) {
    GO.lcd.println("MMC");
    Serial.println("MMC");
  } else if (cardType == CARD_SD) {
    GO.lcd.println("SDSC");
    Serial.println("SDSC");
  } else if (cardType == CARD_SDHC) {
    GO.lcd.println("SDHC");
    Serial.println("SDHC");
  } else {
    GO.lcd.println("UNKNOWN");
    Serial.println("UNKNOWN");
  }

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);
  GO.lcd.printf("SD Card Size: %lluMB\n", cardSize);

  delay(3000);

  GO.lcd.clear();
  GO.lcd.setCursor(0, 0);
}

//setBrightness(uint8_t brightness),
void loop()
{
  GO.update();
  if (GO.JOY_X.isAxisPressed() == 1) {
    lon_deg += 0.0001;
    delay(10);
  }
  if (GO.JOY_X.isAxisPressed() == 2) {
    lon_deg -= 0.0001;
    delay(10);
  }
  if (GO.JOY_Y.isAxisPressed() == 2) {
    lat_rad += 0.0001;
    delay(10);
  }
  if (GO.JOY_Y.isAxisPressed() == 1) {
    lat_rad -= 0.0001;
    delay(10);
  }
  if (GO.BtnA.wasPressed() == 1) {
    zoom++;
  }
  if (GO.BtnB.wasPressed() == 1) {
    zoom--;
  }
  if (GO.BtnSelect.isPressed() == 1) {
    GO.lcd.setBrightness(brightness);
    brightness--;
  }
  if (GO.BtnStart.isPressed() == 1) {
    GO.lcd.setBrightness(brightness);
    brightness++;
  }

  if (lat_rad > 85.05112878)
    lat_rad = 85.05112878;
  if (lat_rad < -85.05112878) lat_rad = -85.05112878; if (lon_deg > 180)
    lon_deg = 180;
  if (lon_deg < -180) lon_deg = -180; if (zoom > 16)
    zoom = 16;
  if (zoom < 5) zoom = 5; if (brightness > 254)
    brightness = 254;
  if (brightness < 1)
    brightness = 1;

  //redraw only when something has changed
  if (old_lat_rad != lat_rad || old_lon_deg != lon_deg || old_zoom != zoom || firstRun) {
    double posX, posY, fractpart, intpart;
    //calculate from coordinates to tile numbers
    tileX = long2tilex(lon_deg, zoom);
    tileY = lat2tiley(lat_rad, zoom);
    //fractional part is the position of the your coordinats in the tile
    posX = modf(tileX , &intpart);
    posY = modf(tileY , &intpart);
    posX = (posX * TILE_SIZE) + (abs(DISPLAY_WIDTH - TILE_SIZE));
    posY = (posY * TILE_SIZE);

    //redraw only when something has changed
    if (uint16_t(old_tileX) != uint16_t(tileX) || uint16_t(old_tileY) != uint16_t(tileY)  || old_zoom != zoom  || firstRun) {
      String path = "/TILES/" + String(uint16_t(zoom)) + "/" + String(uint32_t(tileX)) + "/" + String(uint32_t(tileY)) + ".jpg";
      Serial.println(path);
      if (SD.exists(path)) {
        Serial.println("File found.");
        GO.lcd.clear();
        GO.lcd.setCursor(0, 0);
        //drawJpgFile(fs::FS &fs, const char *path, uint16_t x = 0, uint16_t y = 0, uint16_t maxWidth = 0, uint16_t maxHeight = 0, uint16_t offX = 0, uint16_t offY = 0, jpeg_div_t scale = JPEG_DIV_NONE),
        GO.lcd.drawJpgFile(SD, path.c_str(), (abs(DISPLAY_WIDTH - TILE_SIZE)));
      } else {
        GO.lcd.println("");
        GO.lcd.println("Debug:\nFile not found.");
        Serial.println("File not found.");
      }
      firstRun = false;
    }

    GO.lcd.drawRect(int32_t(posX), int32_t(posY), 4, 4, RED);
    GO.lcd.fillRect(0, 0, abs(DISPLAY_WIDTH - TILE_SIZE), DISPLAY_HEIGHT, BLACK);
    GO.lcd.setCursor(0, 0);
    GO.lcd.println("Battery:");
    GO.lcd.println(String(GO.battery.getPercentage()) + "%");
    GO.lcd.println("Lon_deg:");
    GO.lcd.println(String(lon_deg, 6));
    GO.lcd.println("Lat_rad:");
    GO.lcd.println(String(lat_rad, 6));
    GO.lcd.println("Zoom:");
    GO.lcd.println(String(zoom));

    Serial.println(String(tileX, 6));
    Serial.println(String(tileY, 6));
    Serial.println(String(posX));
    Serial.println(String(posY));
    Serial.println(zoom);
    Serial.println(String(lon_deg, 6));
    Serial.println(String(lat_rad, 6));

    old_tileX = tileX;
    old_tileY = tileY;
  }

  old_lat_rad = lat_rad;
  old_lon_deg = lon_deg;
  old_zoom = zoom;
}

double long2tilex(double lon, double z)
{
  return (double)((lon + 180.0) / 360.0 * pow(2.0, z));
}

double lat2tiley(double lat, double z)
{
  return (double)((1.0 - log( tan(lat * M_PI / 180.0) + 1.0 / cos(lat * M_PI / 180.0)) / M_PI) / 2.0 * pow(2.0, z));
}

double tilex2long(int x, int z)
{
  return x / pow(2.0, z) * 360.0 - 180;
}

double tiley2lat(int y, int z)
{
  double n = M_PI - 2.0 * M_PI * y / pow(2.0, z);
  return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n)));
}

Referencias

https://forum.odroid.com/viewtopic.php?f=162&t=33629 https://youtu.be/-4kA_KhIvus https://github.com/ripper121/OdroidGoOSMGPSOffline https://youtu.be/BQWwTZANGlE

Be the first to comment

Leave a Reply