Schlagwort-Archive: GPS

[ICStation.com] BitBastelei #261 – GPS-Tracker mit Neo-6/7-Module und ESP8266

[ICStation.com] BitBastelei #261 - GPS-Tracker mit Neo-6/7-Module und ESP8266

(111 MB) 00:40:34

2017-11-05 11:00 🛈

In der letzten Woche haben wir uns angesehen, wie Satellitennavigationssysteme wie GPS funktionieren, nun wollen wir diese auch in der Praxis nutzen. Da die Berechnungen komplex sind ist es auch hier eine gute Idee spezialisierte ICs zu verwenden, welche eine einfache Schnittstelle für Mikrocontroller bereitstellen. ICStation.com bietet hierzu ein Modul auf Basis des mächtigen U-Blox Neo M6/M7 an, welches über UART angesprochen werden kann. Schauen wir mal auf das Modul, welche Optionen die Konfigurationssoftware bietet, den Aufbau des NMEA-Protokolls und bauen am Ende einen GPS-Trakcer auf Basis des ESP8266.

Links

Code

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <NMEAGPS.h>
#include <SoftwareSerial.h>

/**
 * Required libs (see manager):
 *   - SoftwareSerial
 *   - NeoGPS
 */

#define RTCMEMORYSTART 65

extern "C" {
#include "user_interface.h"
}

//WiFi
const char* ssid = "freifunk-myk.de";
const char* pass = "";

//URL of ownTracks Recorder
const char* owntracksURL = "http://adlerweb-vm-owntracks.ffmyk:8083/pub";

//User and Device for ownTracks
const char* owntracksUser   = "esp8266-1";
const char* owntracksDevice = "esp8266-1";

//Sleep between GNSS checks in minutes
//const unsigned int sleep_off = 5;
const unsigned int sleep_off = 1;

//Time for GNSS warm-up in seconds
//const unsigned int sleep_GNSS = 30;
const unsigned int sleep_GNSS = 10;

//Time to wait for 3D-Fix before reverting to 2D in seconds
const unsigned int sleep_3d  = 30;

//Time to wait for 2D-Fix before reverting to RF in seconds
const unsigned int sleep_2d  = 60;

//RF Geolocation Todo https://github.com/m0xpd/ESP8266-GeoLocation/blob/master/ESP8266_GeoLocate_2.ino

//Geo-deviation in deg-fract(?)
const unsigned int fence = 5000;

//Force update after x minutes
const unsigned int rf_force = 720;

//Time to wait for RF-Connect before giving up in seconds
const unsigned int rf_timeout = 60;

//GNSS Power Switch
#define GNSS_PWR_PIN D4
#define GNSS_PWR_ON  LOW

//Software UART for GNSS (RX, TX)
SoftwareSerial GNSSPort(D7, D8);
#define GNSS_BAUD 115200

//Hardware UART for Debug
#define DEBUG_PORT Serial
#define DEBUG_BAUD 115200

//Write all data when received
#define DEBUG_DUMP 0

NMEAGPS GNSS;
gps_fix fix;
int32_t lon=0, lat=0;
float vel=0,alt=0,cog=0;

bool delayGNSS(uint32_t dly) {
  return delayGNSS(dly, false);
}

bool delayGNSS(int32_t dly, bool direct) {
  unsigned long mstop = millis()+dly;
  bool ret = false;

  if(DEBUG_DUMP > 1) DEBUG_PORT.print(F("\nS:"));
  if(DEBUG_DUMP > 1) DEBUG_PORT.print(dly);

  do {
    if (GNSS.available( GNSSPort )) {
      fix = GNSS.read();
      ret = true;
      if(DEBUG_DUMP > 1) DEBUG_PORT.print('!');
    }
  }while(!ret && mstop > millis());

  if (fix.valid.heading) cog = fix.heading();
  if (fix.valid.speed) vel = fix.speed_kph();
  if (fix.valid.altitude) alt = fix.altitude();
  if (fix.valid.location) {
    lon = fix.longitudeL();
    lat = fix.latitudeL();
  }
  

  if(!ret) return false;
  if(!direct) {
    if(DEBUG_DUMP > 1) DEBUG_PORT.print('+');
    if(DEBUG_DUMP > 1) DEBUG_PORT.print(mstop-millis());
    delay((mstop-millis()));
  }
  return true;
}

bool getGNSS(void) {
  bool ret;
  if(delayGNSS(100, true)) {
    if(DEBUG_DUMP > 0) debugDump();
    return true;
  }
  return false;
}

bool wait_3d(void) {
  DEBUG_PORT.print(F("Waiting 3D Fix..."));
  for (unsigned int sec = 0; sec < sleep_3d; sec++) {
    if(getGNSS() && lon != 0 && lat != 0 && alt != 0) {
      DEBUG_PORT.println(F("OK"));
      return true;
    }
    Serial.print('.');
    yield();
    delayGNSS(1000);
  }
  DEBUG_PORT.println(F("NOPE"));
  return false;
}

bool wait_2d(void) {
  DEBUG_PORT.print(F("Waiting 2D Fix..."));
  for (unsigned int sec = 0; sec < sleep_2d; sec++) {
    if(getGNSS() && lon != 0 && lat != 0) {
      DEBUG_PORT.println(F("OK"));
      return true;
    }
    Serial.print('.');
    yield();
    delayGNSS(1000);
  }
  DEBUG_PORT.println(F("NOPE"));
  return false;
}

byte getBattery(void) {
  //@TODO not yet implemented
  return 100;
}

String getGeoDecimal(int32_t location) {
  unsigned long tmp1, tmp2;
  String out = "";

  if (location < 0) {
    out += '-';
    location = 0 - location;
  }

  tmp1 = location / 10000000;
  tmp2 = location - (tmp1 * 10000000);

  out += tmp1;
  out += ".";
  out += tmp2;

  return out;
}

bool getFence(void) {
  int32_t check = 0;
  DEBUG_PORT.print(F("FENCE - "));

  //Lat
  DEBUG_PORT.print(F("LAT:"));
  system_rtc_mem_read(RTCMEMORYSTART + 1, (int32_t *)check, 4);
  DEBUG_PORT.print(check);
  DEBUG_PORT.print('/');
  DEBUG_PORT.print(lat);
  if (check + fence > lat || check - fence < lat) {
    DEBUG_PORT.println('!');
    return true;
  }
  //Lon
  DEBUG_PORT.print(F(" - LON:"));
  system_rtc_mem_read(RTCMEMORYSTART + 2, (int32_t *)check, 4);
  DEBUG_PORT.print(check);
  DEBUG_PORT.print('/');
  DEBUG_PORT.print(lon);
  if (check + fence > lon || check - fence < lon) {
    DEBUG_PORT.println('!');
    return true;
  }

  DEBUG_PORT.println('_');
  return false;
}

NeoGPS::clock_t getTimestamp(void) {
  if (!fix.valid.date || !fix.valid.time) return 0;

  //This contains the seconds starting from the start of this century
  NeoGPS::clock_t seconds = fix.dateTime;

  //Guessing we're still 20xx this is the unix timestamp for 01.01.2000 00:00:00
  seconds += 946684800;

  return seconds;
}

bool rfConnect(void) {
  unsigned int rftimer;
  
  if(WiFi.status() == WL_CONNECTED) return true;
  
  DEBUG_PORT.print(F("connecting to "));
  DEBUG_PORT.println(ssid);
  if(pass == "") {
    WiFi.begin(ssid);
  }else{
    WiFi.begin(ssid, pass);
  }
  while (WiFi.status() != WL_CONNECTED && rftimer < rf_timeout) {
    delay(1000);
    DEBUG_PORT.print(".");
    rftimer++;
    yield();
  }
  if (WiFi.status() != WL_CONNECTED) {
    DEBUG_PORT.println(F("failed"));
    return false;
  }
  DEBUG_PORT.println(F("connected"));
  DEBUG_PORT.print(F("IP address: "));
  DEBUG_PORT.println(WiFi.localIP());
  return true;
}

void rfDisconnect(void) {
  /*WiFi.disconnect();
  WiFi.mode(WIFI_OFF);
  WiFi.forceSleepBegin();*/
}

bool rfSend(void) {
  String data;
  HTTPClient http;

  if (!rfConnect()) return false;
  if (lat == 0 || lon == 0) return false;

  http.begin(owntracksURL);

  http.addHeader(F("X-Limit-U"), owntracksUser);
  http.addHeader(F("X-Limit-D"), owntracksDevice);
  http.addHeader(F("User-Agent"), F("Adlerweb-ESP-Tracker"));
  http.addHeader(F("Content-Type"), F("application/json"));

  data  = "{\"_type\":\"location\",\"tid\":\"01\",\"conn\":\"m\",\"_cp\":true,\"batt\":";
  data += getBattery();
  data += ",\"lat\":";

  data += getGeoDecimal(lat);
  data += ",\"lon\":";
  data += getGeoDecimal(lon);

  if (cog != 0) {
    data += ",\"cog\":";
    data += cog;
  }

  if (vel != 0) {
    data += ",\"vel\":";
    data += vel;
  }

  if (alt != 0) {
    data += ",\"alt\":";
    data += alt;
  }

  data += ",\"tst\":";
  data += getTimestamp();
  data += "}";

  DEBUG_PORT.println(F("---"));
  DEBUG_PORT.println(data);
  DEBUG_PORT.println(F("---"));
  DEBUG_PORT.println(F("Send"));
  DEBUG_PORT.flush();
  http.POST(data);
  DEBUG_PORT.println(".");
  DEBUG_PORT.flush();
  http.writeToStream(&Serial);
  DEBUG_PORT.println(".");
  DEBUG_PORT.flush();
  http.end();
  DEBUG_PORT.println(F("OK"));
  DEBUG_PORT.flush();

  rfDisconnect();

  return true;
}

void debugDump() {
  DEBUG_PORT.print(F("Status: "));
  switch (fix.status) {
    case 1:
      DEBUG_PORT.println(F("Nährungswert"));
      break;
    case 2:
      DEBUG_PORT.println(F("Nur Zeit"));
      break;
    case 3:
      DEBUG_PORT.println(F("GNSS-Fix"));
      break;
    case 4:
      DEBUG_PORT.println(F("DGNSS-Fix"));
      break;
    default:
      DEBUG_PORT.println(F("Keiner"));
  }
  DEBUG_PORT.print(F("UTC: "));
  DEBUG_PORT.print(fix.dateTime.year);
  DEBUG_PORT.print("-");
  DEBUG_PORT.print(fix.dateTime.month);
  DEBUG_PORT.print("-");
  DEBUG_PORT.print(fix.dateTime.date);
  DEBUG_PORT.print(" ");
  DEBUG_PORT.print(fix.dateTime.hours);
  DEBUG_PORT.print(":");
  DEBUG_PORT.print(fix.dateTime.minutes);
  DEBUG_PORT.print(":");
  DEBUG_PORT.print(fix.dateTime.seconds);
  DEBUG_PORT.print(" - ");
  DEBUG_PORT.print(getTimestamp());
  DEBUG_PORT.print("/");
  //DEBUG_PORT.println(lasttime);

  DEBUG_PORT.print(F("Satellites: "));
  DEBUG_PORT.println(fix.satellites);

  DEBUG_PORT.print(F("Speed: "));
  DEBUG_PORT.println(vel);

  DEBUG_PORT.print(F("Heading: "));
  DEBUG_PORT.println(cog);

  DEBUG_PORT.print(F("Altitude: "));
  DEBUG_PORT.println(alt);

  DEBUG_PORT.print(F("Postition: "));
  DEBUG_PORT.print(lat);
  DEBUG_PORT.print(",");
  DEBUG_PORT.println(lon);

  DEBUG_PORT.print(getGeoDecimal(lat));
  DEBUG_PORT.print(",");
  DEBUG_PORT.println(getGeoDecimal(lon));
}

byte getBootMode(void) {
  byte bootMode = 0;
  system_rtc_mem_read(RTCMEMORYSTART, &bootMode, 1);
  DEBUG_PORT.print("BootMode: ");
  DEBUG_PORT.println(bootMode);
  return bootMode;
}


/////////////


void setup() {
  int32_t dummy = 0;

  pinMode(GNSS_PWR_PIN, OUTPUT);

  DEBUG_PORT.begin(DEBUG_BAUD);
  GNSSPort.begin(GNSS_BAUD);

  delay(2000);

  DEBUG_PORT.println( F("Adlerweb GNSS Tracker v0.1.1") );
  DEBUG_PORT.flush();

  byte bootMode = getBootMode();

  if(bootMode == 0 || bootMode > 5) { //Unknown or first boot
    DEBUG_PORT.println(F("Enabling GNSS Power"));
    digitalWrite(GNSS_PWR_PIN, GNSS_PWR_ON);
    bootMode=1;
    system_rtc_mem_write(RTCMEMORYSTART, &bootMode, 1);
    DEBUG_PORT.print(F("Sleeping for "));
    DEBUG_PORT.print(sleep_GNSS);
    DEBUG_PORT.println(F(" Seconds"));
    DEBUG_PORT.flush();
    ESP.deepSleep(sleep_GNSS * 1e6);
  }

  if(bootMode == 1) { //Waiting for GNSS 3D Fix
    if (wait_3d()) {
      bootMode = 4;
    } else {
      bootMode++;
    }
  }

  if(bootMode == 2) {//Waiting for GNSS 2D Fix
    if (wait_2d()) {
      bootMode = 4;
    } else {
      bootMode++;
    }
  }
  
  if(bootMode == 3) { //Waiting for RF Fix
    //@TODO Not implemented
    bootMode = 5;
  }

  if(bootMode == 4) { //Fix Aquired - sending
    system_rtc_mem_read(RTCMEMORYSTART + 3, &dummy, 4);
    if (getFence() || dummy + (rf_force * 60) <= getTimestamp()) {
      bootMode = 5;
      if (rfSend()) {
        system_rtc_mem_write(RTCMEMORYSTART + 1, &lat, 4);
        system_rtc_mem_write(RTCMEMORYSTART + 2, &lon, 4);
        dummy = getTimestamp();
        system_rtc_mem_write(RTCMEMORYSTART + 3, &dummy, 4);
        bootMode = 0;
      } else {
        DEBUG_PORT.println(F("Transfer failed"));
        bootMode = 0;
      }
    }else{
      Serial.println(F("Position unchanged, no transfer"));
      bootMode = 0;
    }
  }

  if(bootMode == 5) { //No fix
    debugDump();
    bootMode = 0;
  }
  
  system_rtc_mem_write(RTCMEMORYSTART, &bootMode, 1);
}

void loop() {
  DEBUG_PORT.println(F("Disabling GNSS Power"));
  digitalWrite(GNSS_PWR_PIN, !GNSS_PWR_ON);
  DEBUG_PORT.print(F("Sleeping for "));
  DEBUG_PORT.print(sleep_off);
  DEBUG_PORT.println(F(" Minutes"));
  DEBUG_PORT.flush();
  ESP.deepSleep(sleep_off * 60e6);
}

Inhalt

  • 00:35 Das Modul
  • 09:07 Das NMEA-Protokoll
  • 13:33 Test & Konfiguration per U-Center
  • 18:02 GPS mit ESP8266 und Arduino
  • 19:05 Test mit SoftwareSerial
  • 20:47 GPS/WiFi-Tracking mit OwnTracks

Hinweise:

Das GPS-Modul wurde mir von ICStation.com für dieses Video kostenfrei zur Verfügung gestellt.

BitBasics – Satellitennavigationssysteme (GPS, GLONASS, etc)

BitBasics - Satellitennavigationssysteme (GPS, GLONASS, etc)

(43 MB) 00:13:24

2017-10-29 11:00 🛈

Wer wissen will wo er ist, der Fragt meist sein Smartphone – aber wie kann der kleine Kasten diese Frage beantworten? In dieser Folge schauen wir auch die Techniken hinter GPS & Co.

Auf zur Jagd! Mal schaun welchrn Geocache es noch gibt

Was hat Beetlebum (oder eher seine Kommentatoren) jetzt schon wieder angerichtet… Da ist man schon fast mit dem Mappen für OSM durch kommt da das gute, alte Geocaching wieder auf. Ein Blick auf Geocaching-Seiten zeigt auch direkt mal 2 Objekte in meinem Örtchen. Mal schauen, welche es davon noch gibt.

Leiden für die Wissenschaft: Saffig@OpenStreetMap

Google Maps/Earth sind ja schöne Werkzeuge, aber leider nicht ohne Fehler – möchte man die Daten für eigene Projekte verwenden stößt man schnell an die Grenzen der Lizenz. Aber auch simple Routenführung kann schnell zum Problem werden, wenn Google eine Straße vorschlägt, die in Wirklichkeit ein 50cm breiter Durchgang zwischen Häusern ist.

Hier versucht das Projekt „OpenStreetMap“ anzusetzen – ganz im Stile der Wikipedia werden hier die Straßenkarten durch die Community generiert. Dies setzt natürlich ein gewisses Interesse an den Karten voraus – während in Großstädten zum Teil schon Hausnummern erfasst und jede Telefonzelle markiert ist bestand meine Gegend aus einem großen, weißem Loch. Andernach war grob erfasst, Plaidt und Miesenheim bestanden aus 2 Straßen und von Saffig war nur der Ortsname verzeichnet.

Inzwischen hat sich das gebessert – einige Kilometer Fußmarsch mit GPS-Empfänger am Rucksackgurt und Tracking-Software auf dem PDA später bin nicht nur ich um so manche Blase am Fuß, sondern auch OSM um einen guten Batzen Rohdaten reicher. Inzwischen komme ich sogar einigermaßen mit dem grauenvollem Interface des „Profi-Editors“ JOSM zurecht. OK, einiges fehlt noch, aber es ist ein Anfang. Die ersten Straßen sind auch bereits bei osmarender zu bestaunen – die „Mapnik“-Karte der Startseite wird leider nur wöchentlich aktualisiert. Und eins kann ich inzwischen sagen: Die Warnungen der Wiki, dass das Mappen süchtig machen könne scheint berechtigt – einer meiner Arbeitskollegen tobt sich bereits das ganze Wochenende mit seinem Auto in Neuwied aus.

Also – GPS-Empfänger rauskramen/ausleihen und loslaufen – soll nochmal einer sagen, dass man Informatiker nicht ausm Keller locken könnte.