Arduino en de GPS-Shield

1. Introductie
Voor ons ballon-project maken we gebruik van een mini GPS tracker. Dit ding maakt gebruikt van een simkaart om sms’jes te ontvangen en te versturen. Door het versturen van een sms kun je de coördinaten van de tracker opvragen, hem opdracht geven om elke zoveel seconden zijn positie door te geven of hem zijn gegevens aan meerdere mobiele nummers laten sturen. De tracker stuurt altijd een sms terug met daarin zijn lengte- en breedtegraden, zijn snelheid en een link waar je op kunt klikken om de positie in Google Maps weer te geven.

De sms'jes die je van de tracker krijgt.

De sms’jes die je van de tracker krijgt.

Hoewel dit klikken op een link op de smartphone op zich prima werkt, wilde ik per se de posities in een database opslaan zodat ik deze real-time op een grote kaart kon tonen. Het ophalen van die posities uit de database en deze op een kaart plotten was me al eerder gelukt. De uitdaging van deze fase bestond uit het ontvangen van de sms’jes, hier de coördinaten uit te destilleren en deze in de database te zetten.

2. De GSM Shield
Zoals voor zo veel dingen is er een shield voor Arduino waar je een simkaart in kunt stoppen. En net als altijd is het even pielen om die boel goed aan de praat te krijgen, maar wanneer je de shield goed op de Arduino heb gezet en je hebt de juiste seriële poort op je computer gevonden, werkt het prima. Ik had even wat moeite met een simkaart waar ik blijkbaar nog een pincode op had staan, maar dankzij de mobiele telefoon die we van hde Hanze hebben (en die we verder toch niet gebruiken) kon ik die er makkelijk afhalen. Sowieso is het wel praktisch om toch een telefoon bij de hand te hebben, ook om beltegoed op te waarderen en dingen te checken. Met behulp van de voorbeeldcode op arduino.cc kon ik zien dat de simkaart het deed.

GSM networks scanner
 Modem IMEI: 
 8621700184399
 Current carrier: vodafone
 Signal Strength: 22 [0-31]
 Scanning available networks. May take some seconds.
 > vodafone
 > T-Mobile NL
 > KPN MOBILE

De tracker heeft ook de mogelijkheid om de coördinaten naar extra telefoonnummers te sturen. Tijdens het testen had ik ook het nummer van een collega, met wie ik het project samen begeleidt, aan de tracker toegevoegd; vreemd genoeg kreeg ik daarna zelf helemaal geen sms’jes meer. Uiteindelijk hoorde ik van die college dat hij wel wat berichten had gekregen – zij het alleen maar noodberichten die de tracker had verstuurd toen ik ten einde raad de sos-knop had ingedrukt – dus er werden wel berichten verstuurd. Blijkbaar onthoudt de tracker zelf welke nummers er geautoriseerd zijn en stuurt hij alleen naar die nummers een sms. Staat, bleek achteraf, ook in de docs:

Aber: sobald eine Rufnummer im Tracker autorisiert wurde, gibt der Tracker nur Antwort an die autorisierten Rufnummern, fremde Rufnummern werden ignoriert.

Verder lezen in de online-documentatie leverde het inzicht op dat de tracker ook de optie heeft om zichzelf helemaal te resetten. In eerste instantie leek ook dat niets uit te halen, maar uiteindelijk kreeg ik in de trein zomaar een sms met als tekst ‘Reset ok!’. En toen werkte het eindelijk. Blijkbaar kun je het ding ook behoorlijk van slag brengen door niet een bepaalde volgorde in commando’s te geven.

Een kort rondje door Sneek leverde inderdaad tien positie-bepalingen op. Met deze ervaring had ik voldoende fiducie in de tracker en mijn begrip daarvan om de volgende stap in het proces te zetten.

3. Processing en Arduino
Nu ik de tracker eindelijk onder controle had, was het tijd om na te denken over de manier waarop ik de coördinaten in de database kon krijgen. Het meest eenvoudige was natuurlijk om in Processing de eerste regel van de sms te parseren, de juiste dat daaruit te halen en uiteindelijk in de database te zetten. Die kon dan weer door nodejs opgehaald worden en op Google maps geplot worden. Berichten die in dit systeem binnenkwamen zouden moeten worden gecheckt op inhoud en eventueel opgeslagen worden in de database. Processing is in feite gewoon Java, dus een jdbc opzetten moest zonder problemen kunnen – en er bleek ook een speciale Processing-library daarvoor te bestaan.

Om alle een beetje te vereenvoudigen en de communicatie twee kanten op via hetzelfde systeem te laten lopen, wilde ik mooie (of eigenlijk lelijke) interface met buttons maken waar je op kon klikken om berichten naar de Arduino te sturen, die dan weer door hem naar de tracker gestuurd konden worden. Er zijn verschillende libraries voor GUI’s in Processing. Ik probeerde eerste controlP5, wat ik te onduidelijk vond. Ook het Italiaanse Interfascia was mij te complex (en werkte niet met Processing onder versie 2 en de mysql-library die ik gebruik werkt niet met 3…). Uiteindelijk kwam ik uit op G4P. Wel even zoeken naar de juiste download. v3.5.4 is de laatste voor P2. Ziet er op zich wel ok uit, is redelijk gedocumenteerd en werkt tenminste out of the box; maar een schoonheidsprijs krijgt het ding niet.

interface tisagEerst maar eens de basale communicatie opzetten. Dat is vrij triviaal: Arduino stuurt tekst naar een seriële poort waar Processing naar luistert. Het enige probleem probleem is dat die gps-tracker de data doorstuurt over meerdere regels en de code in principe ophoudt bij de eerste ‘\n’. Dus ik moest iets doen met checken of de regel ik kwestie latlng-data bevat:

if (val != null) {
  if (val.indexOf("lat:")>-1) {
    String lat = parse(val, "lat");
    String lng = parse(val, "lng");
    database.query("insert into latlng(lat, lng) 
                    values (" +lat+ ", " +lng+ ")");
  }
}

String parse(String input, String latOrLng) {
  String[] foo = input.split(" ");
  String checkstr = (latOrLng.equals("lat")) ? foo[0] : foo[1];
  String[] res = checkstr.split(":");

  return res[1];
}

De check op regel 1 hierboven kijkt of er in de string die binnenkomt ergens ‘lat:’ staat (dit geldt alleen voor de regels die van belang zijn). Als dat het geval is, haal ik (met parse(String, String)) eerst de lengtegraden en vervolgens de breedtegraden uit de string in kwestie. Een paar testjes, waarbij de Arduino gewoon direct tekst naar de seriële poort stuurt, lieten zien dat dit op zich prima werkt.

De volgende stap was, uiteraard, de andere kant op: opdrachten (strings) vanuit Processing naar de Arduino sturen. De tracker heeft een flink aantal opties en functies, die we tijdens het project niet allemaal nodig hebben. Alle functies zijn beschreven op deze site; de volgende functies wil ik implementeren:

herstel fabrieksinstellingen           begin123456
resetten tracker                       reset123456
extra telefoonnummers autoriseren      admin123456 <<telnr>>
geautoriseerd nummer verwijderen       noadmin123456 <<telnr>>
instellen update rate automatische tracking
starten automatische tracking          t030s005n123456
stoppen automatische tracking          notn123456

De communicatie van Processing naar Arduino leek nog niet heel triviaal. Het is vrij eenvoudig om één karakter (char) over de lijn te sturen, maar omdat ik hele opdrachten wilde sturen (die aan de Processing-kant met behulp van de interface zouden worden samengesteld) moest ik op de één of andere manier een hele string versturen en door de Arduino opvangen. Na wat proberen kwam ik op een blog die en goed en werkend voorbeeld had. Die maar even gekopieerd en van daaruit verder.

Met behulp van dit voorbeeld lukte het uiteindelijk om aan de Arduino-kant een string op te vangen en afhankelijk daarvan iets te doen. Om duidelijk te maken wat de Arduino allemaal aan het doen was, koppelde ik een aantal LEDs aan de digitale poorten hiervan, zodat ik die als status-indicatoren kon gebruiken.

if (chomp("START")==0) {
  respons = "Received START";
  statusLed = 10;
} else&nbsp; if (chomp("STOP")==0) {
  respons = "Received STOP";
  statusLed = 11;
} else&nbsp; if (chomp("ADD")==0) {
  respons = "Received ADD";
  statusLed = 12;
}

if (currentStatus != statusLed) {
  currentStatus = statusLed;
  clearLEDS();
  Serial.println(respons);
  digitalWrite(statusLed, HIGH);
}

ledsOmdat de string die de Arduino binnenkreeg uiteindelijk als sms door de shield verstuurd zou moeten worden, moest deze aan de methode sms.print(char[]) worden meegegeven. Nu is een string in C (zoals in alle programmeertalen) een char[], dus ik dacht dat ik die wel gewoon zo kon aanroepen. Maar vreemd genoeg compileerde Listing 1 hieronder wel, en Listing 2 niet:

// Listing 1
String SMSManager::sendSMS(String msg, String number) {
  sms.beginSMS("+31612345678");
  sms.print("Hallo daar!");
  sms.endSMS();

  return "";
}

// Listing 2
String SMSManager::sendSMS(String msg, String number) {
  sms.beginSMS(number);
  sms.print(msg);
  sms.endSMS();

  return "";
}

Na het goed doorlezen van de documentatie bleek dat die sms-library uit van een cons char* uitgaat, terwijl string-variabelen mutable zijn. Dus moesten we die variabelen omzetten in een immutable op het moment dat die methode wordt aangeroepen. Gelukkig heeft C daar een methode voor:

String SMSManager::sendSMS(String msg, String number) {
  const char* s_msg = msg.c_str();
  const char* s_number = number.c_str();

  sms.beginSMS(s_msg);
  sms.print(s_number);
  sms.endSMS();

  return "";
}

Hoewel dit het probleem op scheen te lossen, wat het allemaal niet heel stabiel – er kwamen soms wel en soms niet berichten terug in Processing. In eerste instantie dacht ik dat dat met locking te maken had, dus paste ik ReceiveSMS() zo aan dat -ie een boolean teruggaf en liet het hele systeem hangen zolang er een bericht verwerkt werd. Maar dat hielp niet.

Maar de berichten komen wel door in de serial controller van Arduino zelf, hoewel ik ze niet zag in Processing. Maar wat valt er op wanneer we goed naar die serial controller kijken? De berichten komen na elkaar, en in processing staat een readStringUntil(‘\n’)…NAAST_ELKAARDus eenvoudig na het versturen van het bericht Serial.print(‘\n’) toevoegen en het werkte. Irritant hoe je een paar uur kunt verspillen met zo’n stomme fout.

Object Oriented
Nu de communicatie twee kanten op goed leek te gaan, werd het tijd om de code wat op te ruimen. De Arduino doet nu feitelijk een paar verschillende dingen:

– ontvangen van opdrachten van Processing
– versturen van sms’jes
– veranderen van de status LED
– informatie terugsturen naar Processing

Het is netter om de code die met deze verschillende processen te maken heeft in separate tabs te zetten. Op de arduino-site is aangegeven hoe dat moet. Ook aan de Processing-kant maakte ik verschillende objecten voor verschillende zaken. Hoewel de editor van zowel Processing als van Arduino behoorlijk waardeloos is, kun je hier wel gewoon een nieuwe tab aanmaken met een nieuwe (C++ of Java)klasse.

Logging
Als laatste wilde ik dat de processing-app een logfile bij zou gaan houden, waarin ik alle opdrachten en communicatie over en weer zou kunnen bijhouden. Samen met de indicator-leds zou ik dan het hele proces goed moeten kunnen monitor. Omdat Processing uiteindelijk gewoon Java is, had ik ook een framework als log4j in kunnen zetten, maar dat leek me wat overdreven. Uiteindelijk heb ik een separate klasse gemaakt die het loggen voor z’n rekening nam. Feitelijk met maar één methode, info(String), die de string die -ie meekrijgt naar een bestand wegschrijft. Die kunnen we dan met tail -f mooi volgen.

public void info(String message) {
  try {
    BufferedWriter output = new BufferedWriter(
                         new FileWriter(logFile, true));
    output.append (message);
    output.close();
    println (message);
  } catch (IOException e) {
    println("panic");
    e.printStackTrace();
  }
}

Voor die log moest ik natuurlijk wat strings met elkaar concaterneren. Nu weet ik dat Strings in Java immutable zijn. Dus String.format(), of concatenatie? Na lezen op stackoverflow toch maar voor de eerste optie gekozen.

String lat = parse(respons, "lat");
String lng = parse(respons, "lng");
logger.info(String.format("Got GPS coordinates: %1$s, %2$s ", lat, lng));

logsDe hele cirkel leek nu wel te werken. Ik kon via Processing de Tracker aanzetten en als ik een sms terugstuurde met de telefoon, kwam die keurig in de database terecht. Via het eerdere project met nodejs werd de locatie dan keuring op Google Maps getoond. Tijd voor de laatste schakel: de tracker zelf naar de Arduino laten sturen. Dat bleek echter nog niet mee te vallen…

Leave a comment

Your email address will not be published. Required fields are marked *