[GPS] + GPRS to FTP to website

I decided to make the “Sherlock I” which essentially is a GPS monitor that parses the info via GPRS to an FTP server, which converts it to a website with a simple PHP script to a google maps website-thingy, so that i can monitor ‘stuff’ real-time. I tried it a few times and it worked well. Then i got bored and forgot about it. But not before i made a youtube video from it!

Parts List

  • Arduino or equivalent
  • FTP server
  • Sim card with GPRS option (i used my unlimited iPhone one!)
  • GPS sensor with serial interface (I used a FALCOM FSA03)
  • GPRS Hilo/SAGEM module (€40 from Libelium)

Short demonstrationvideo…

Code

  1. /*
  2.  *
  3.  *  Copyright (C) 2010 Tim Zaman
  4.  *  http://www.timzaman.com
  5.  *
  6.  */
  7.  
  8. #define GPS_PIN_1               9       // GPS serial pin RX
  9. #define GPS_PIN_2               8       // GPS serial pin TX
  10. #define CHECKPIN                13      // Pin that lights up when things are checked
  11. #define GPSRATE         4800    // GPS baud rate
  12. #include <softwareSerial.h>
  13. #include <flash.h>
  14. #include <streaming.h>
  15. #define BUFFERSIZE      100      // How many bytes of input to buffer from the GPS?
  16. #define ENDLN           "rn"  // SD
  17.  
  18. int onModulePin = 2;                  // the pin to switch on the module (without press on button)
  19. SoftwareSerial mySerial = SoftwareSerial(GPS_PIN_1, GPS_PIN_2);  //(rx,tx)
  20.  
  21. //GPS var
  22. int numSats = 0;
  23. int fixType = 0;
  24. int time[] = {
  25.   0, 0, 0};
  26. double latitude = 0.0;
  27. double longitude = 0.0;
  28. long altitude = 0;
  29. long maxAlt = 0;
  30. int speed = 0;
  31. int txCount = 0;
  32.  
  33. int ExOnce = 0;
  34. int FalcomCheck = 0;
  35. int LogCheck =0;
  36. unsigned long GpsOffTime = 0;   // Tijd dat GPS uit gaat
  37. unsigned long SmsStart = 0;     // SMS-time
  38.  
  39.  
  40. char buffer[BUFFERSIZE];
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47. void switchModule(){                  // Funtion to switch the module ON
  48.   digitalWrite(onModulePin,HIGH);
  49.   delay(2000);
  50.   digitalWrite(onModulePin,LOW);
  51.   delay(2000);
  52.   Serial.print("AT+CPIN=");
  53.   Serial.print(34,BYTE);
  54.   Serial.print("1234"); //1234= your pincode <<<<<
  55.   Serial.println(34,BYTE);
  56.   delay(2000);
  57. }
  58.  
  59.  
  60.  
  61. void setup(){
  62.  
  63.   pinMode(onModulePin, OUTPUT);
  64.   Serial.begin(19200);              // the GPRS baud rate
  65.   switchModule();                    // swith the module ON
  66.  
  67.   for (int i=0;i<2;i++){             // Wait 20 sec for connection
  68.     delay(10000);
  69.   }
  70.   mySerial.begin(4800);
  71.  
  72.   pinMode(GPS_PIN_1, INPUT);
  73.   pinMode(GPS_PIN_2, OUTPUT);
  74.   pinMode(7, OUTPUT);
  75.   pinMode(13, OUTPUT);
  76.   digitalWrite(7, HIGH);
  77.  
  78.  
  79. }
  80.  
  81.  
  82.  
  83. void loop(){
  84.  
  85.   Serial.flush();
  86.  
  87.   // Get a GGA string from the GPS,
  88.   // check if it's a valid fix, and extract the data
  89.   getNMEA("$GPGGA");
  90.   delay(100);
  91.   numSats = getSats();
  92.   fixType = getFixType();
  93.   // Make sure we have a valid fix
  94.   if (fixType != 0) {
  95.     getTime(time);
  96.     latitude = getLat();
  97.     longitude = getLong();
  98.     altitude = getAlt();
  99.  
  100.     // Keep track of the maximum altitude
  101.  
  102.   }
  103.  
  104.   // Convert lat & long into strings
  105.   char latString[12];
  106.   char longString[12];
  107.   doubleToString(latitude, 4, latString);
  108.   doubleToString(longitude, 4, longString);
  109.  
  110.  
  111.   sprintf(buffer, "%02d:%02d:%02d,%s,%s,%ld", time[0], time[1], time[2], latString, longString, altitude);
  112.   Serial.println(buffer);
  113.  
  114.  
  115.   if (fixType > 0) {
  116.     if (ExOnce==0){
  117.       digitalWrite(13, HIGH);
  118.       //ExOnce=1;
  119.       //sendsms();
  120.       logftp();
  121.      // for (int i=0;i<3;i++){             // Wait 30 sec for connection
  122.      //   delay(10000);
  123.      // }
  124.     }
  125.   }
  126.  
  127.   delay(200);
  128. }
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146. void sendsms(){
  147.   Serial.println("AT+CMGF=1");         // set the SMS mode to text
  148.   delay(500);
  149.   Serial.print("AT+CMGS=");               // send the SMS the number
  150.   Serial.print(34,BYTE);                  // send the " char
  151.   Serial.print("0613653490");              // send the number change ********* by the actual number
  152.   Serial.println(34,BYTE);                // send the " char
  153.   delay(1500);
  154.   Serial.print("Hi Tim, i'm uploading now. X, your computer");     // the SMS body
  155.   delay(500);
  156.   Serial.print(0x1A,BYTE);                // end of message command 1A (hex)
  157.  
  158.   delay(20000);
  159.  
  160. }
  161.  
  162. void logftp(){
  163.   Serial.println("AT&k3");         // Flow activate
  164.  
  165.   delay(1000);
  166.   Serial.print("AT+KCNXCFG=0,");     // connect to GPRS
  167.   Serial.print(34,BYTE);
  168.   Serial.print("GPRS");
  169.   Serial.print(34,BYTE);
  170.   Serial.print(",");
  171.   Serial.print(34,BYTE);
  172.   Serial.print("internet");
  173.   Serial.print(34,BYTE);
  174.   Serial.print(",");
  175.   Serial.print(34,BYTE);
  176.   Serial.print(34,BYTE);
  177.   Serial.print(",");
  178.   Serial.print(34,BYTE);
  179.   Serial.println(34,BYTE);
  180.  
  181.   delay(1000);
  182.   Serial.println("AT+KCNXTIMER=0,60,2,70");   // set timers
  183.  
  184.   delay(1000);
  185.   Serial.println("AT+CGATT=1");               //Network check
  186.  
  187.   delay(1000);
  188.   Serial.print("AT+KFTPCFG=0,");    //FTP config/connect
  189.   Serial.print(34,BYTE);
  190.   Serial.print("ftp.timbobel.nl");  //ftp adres
  191.   Serial.print(34,BYTE);
  192.   Serial.print(",");
  193.   Serial.print(34,BYTE);
  194.   Serial.print("timbobel.nl");     //password
  195.   Serial.print(34,BYTE);
  196.   Serial.print(",");
  197.   Serial.print(34,BYTE);
  198.   Serial.print("*****");
  199.   Serial.print(34,BYTE);
  200.   Serial.println(",21,0");
  201.  
  202.   delay(500);
  203.   Serial.print("AT+KPATTERN=");     //Zet EOF pattern (als stop na data)
  204.   Serial.print(34,BYTE);
  205.   Serial.print("--EOF--Pattern--");
  206.   Serial.println(34,BYTE);
  207.  
  208.   delay(500);
  209.   Serial.print("AT+KFTPSND=0,,");
  210.   Serial.print(34,BYTE);
  211.   Serial.print("log");          //Directory binnen de ftp
  212.   Serial.print(34,BYTE);
  213.   Serial.print(",");
  214.   Serial.print(34,BYTE);
  215.   Serial.print("pol.txt");          //Textfile
  216.   Serial.print(34,BYTE);
  217.   Serial.println(",0");
  218.  
  219.   delay(12000);                       //Wachten tot hij is verbonden...
  220.  
  221.   Serial.print(buffer);        //Hier zit de info in
  222.   Serial.println("--EOF--Pattern--"); //Data verzenden
  223.   delay(12000);
  224.  
  225.   Serial.println("AT+KTCPCLOSE=1,1"); //Verbinding sluiten
  226.   delay(1000);
  227. }
  228.  
  229.  
  230.  
  231.  
  232.  
  233.  
  234.  
  235.  
  236.  
  237.  
  238.  
  239.  
  240.  
  241. // ------- GPS Parsing ----------
  242.  
  243. // Reads a line from the GPS NMEA serial output
  244. // Give up after trying to read 1000 bytes (~2 seconds)
  245. int readLine(void) {
  246.   char c;
  247.   byte bufferIndex = 0;
  248.   boolean startLine = 0;
  249.   byte retries = 0;
  250.   while (retries < 20) {
  251.  
  252.     c= mySerial.read();
  253.  
  254.     if (c == -1) {
  255.       delay(2);
  256.       continue;
  257.     }
  258.     if (c == 'n') continue;
  259.     if (c == '$') startLine = 1;
  260.     if ((bufferIndex == BUFFERSIZE-1) || (c == 'r')) {
  261.       if (startLine) {
  262.         buffer[bufferIndex] = 0;
  263.         return 1;
  264.       }
  265.     }
  266.     if (startLine) buffer[bufferIndex++] = c;
  267.     //}
  268.     else {
  269.       retries++;
  270.       delay(50);
  271.     }
  272.   }
  273.   return 0;
  274. }
  275.  
  276. // Returns a specific field from the buffer
  277. void getField(int getId, char *field, int maxLen) {
  278.   byte bufferIndex = 0;
  279.   byte fieldId = 0;
  280.   byte i = 0;
  281.   while (bufferIndex < sizeof(buffer)) {
  282.     if (fieldId == getId) {
  283.       // End of string, or string overflow
  284.       if (buffer[bufferIndex] == ',' || i > (maxLen - 2)) {
  285.         field[i] = 0;	// Null terminate
  286.         return;
  287.       }
  288.       // Buffer chars to field
  289.       field[i++] = buffer[bufferIndex++];
  290.     }
  291.     else {
  292.       // Advance field on comma
  293.       if (buffer[bufferIndex] == ',') {
  294.         bufferIndex++;						// Advance in buffer
  295.         fieldId++;							// Increase field position counter
  296.       }
  297.       else {
  298.         bufferIndex++;						// Advance in buffer
  299.       }
  300.     }
  301.   }
  302.   // Null terminate incase we didn't already..
  303.   field[i] = 0;
  304. }
  305.  
  306. // Polls for an NMEA sentence of type requested
  307. // Validates checksum, silently retries on failed checksums
  308. int getNMEA(char *getType) {
  309.   char type[7];
  310.   byte retries = 0;
  311.   while (retries < 2) {
  312.     if (readLine() && validateChecksum()) {
  313.       ;
  314.       getField(0, type, sizeof(type));
  315.       if (strcmp(type, getType) == 0) {
  316.  
  317.         return 1;
  318.       }
  319.     }
  320.     else {
  321.       retries++;
  322.  
  323.     }
  324.   }
  325.   Serial.println("Failed to read GPS");
  326.  
  327.   return 0;
  328. }
  329.  
  330. // Validates the checksum on an NMEA string
  331. // Returns 1 on valid checksum, 0 otherwise
  332. int validateChecksum(void) {
  333.   char gotSum[2];
  334.   gotSum[0] = buffer[strlen(buffer) - 2];
  335.   gotSum[1] = buffer[strlen(buffer) - 1];
  336.   // Check that the checksums match up
  337.   if ((16 * atoh(gotSum[0])) + atoh(gotSum[1]) == getCheckSum(buffer)) return 1;
  338.   else return 0;
  339. }
  340.  
  341. // Calculates the checksum for a given string
  342. // returns as integer
  343. int getCheckSum(char *string) {
  344.   int i;
  345.   int XOR;
  346.   int c;
  347.   // Calculate checksum ignoring any $'s in the string
  348.   for (XOR = 0, i = 0; i < strlen(string); i++) {
  349.     c = (unsigned char)string[i];
  350.     if (c == '*') break;
  351.     if (c != '$') XOR ^= c;
  352.   }
  353.   return XOR;
  354. }
  355.  
  356. // Returns the groundspeed in km/h
  357. int getSpeed(void) {
  358.   char field[10];
  359.   getField(7, field, sizeof(field));
  360.   int speed = atoi(field);
  361.   return speed;
  362. }
  363.  
  364. // Return the fix type from a GGA string
  365. int getFixType(void) {
  366.   char field[5];
  367.   getField(6, field, sizeof(field));
  368.   int fixType = atoi(field);
  369.   return fixType;
  370. }
  371.  
  372. // Return the altitude in meters from a GGA string
  373. long getAlt(void) {
  374.   char field[10];
  375.   getField(9, field, sizeof(field));
  376.   long altitude = atol(field);
  377.   return altitude;
  378. }
  379.  
  380. // Returns the number of satellites being tracked from a GGA string
  381. int getSats(void) {
  382.   char field[3];
  383.   getField(7, field, sizeof(field));
  384.   int numSats = atoi(field);
  385.   return numSats;
  386. }
  387.  
  388. // Read the latitude in decimal format from a GGA string
  389. double getLat(void) {
  390.   char field[12];
  391.   getField(2, field, sizeof(field));			// read the latitude
  392.   double latitude = atof(field);					// convert to a double (precise)
  393.   int deg = (int) latitude / 100;				// extract the number of degrees
  394.   double min = latitude - (100 * deg);			// work out the number of minutes
  395.   latitude = deg + (double) min/60.0;			// convert to decimal format
  396.   getField(3, field, sizeof(field));			// get the hemisphere (N/S)
  397.   if (strcmp(field, "S") == 0) latitude *= -1;	// sign the decimal latitude correctly
  398.   return latitude;
  399. }
  400.  
  401. // Read the longitude in decimal format from a GGA string
  402. double getLong(void) {
  403.   char field[12];
  404.   getField(4, field, sizeof(field));			// read the longitude
  405.   double longitude = atof(field);					// convert to a double
  406.   int deg = (int) longitude / 100;				// extract the number of degrees
  407.   double min = longitude - (100 * deg);			// work out the number of minutes
  408.   longitude = deg + (double) min/60.00;			// convert to decimal format
  409.   getField(5, field, sizeof(field));			// get the E/W status
  410.   if (strcmp(field, "W") == 0) longitude *= -1; // sign decimal latitude correctly
  411.   return longitude;
  412. }
  413.  
  414. // Converts UTC time to the correct timezone
  415. void convertTime(int *time) {
  416.   // How many hours off GMT are we?
  417.   float offset = 2;
  418.   long sectime = ((long)(time[0]) * 3600) + (time[1] * 60) + time[2];
  419.   sectime += (offset * 3600.0);
  420.   // Did we wrap around?
  421.   if (sectime < 0) sectime += 86400;
  422.   if (sectime > 86400) sectime -= 86400;
  423.   // Convert back to time
  424.   time[0] = (int)(sectime / 3600);
  425.   time[1] = (int)((sectime % 3600) / 60);
  426.   time[2] = (int)((sectime % 3600) % 60);
  427. }
  428.  
  429. // Parses a time field from a GGA string
  430. void parseTime(char *field, int *time) {
  431.   char tmp[3];
  432.   tmp[2] = 0; // Init tmp and null terminate
  433.   tmp[0] = field[0];
  434.   tmp[1] = field[1];
  435.   time[0] = atoi(tmp); // Hours
  436.   tmp[0] = field[2];
  437.   tmp[1] = field[3];
  438.   time[1] = atoi(tmp); // Minutes
  439.   tmp[0] = field[4];
  440.   tmp[1] = field[5];
  441.   time[2] = atoi(tmp); // Seconds
  442. }
  443.  
  444. // Gets the hours, minutes and seconds from a GGA string
  445. void getTime(int *time) {
  446.   char field[12];
  447.   getField(1, field, sizeof(field));
  448.   parseTime(field, time);
  449.   convertTime(time);
  450. }
  451.  
  452.  
  453. // ------ MISC ----------
  454.  
  455. // Returns a string with a textual representation of a float
  456. void doubleToString(double val, int precision, char *string){
  457.  
  458.   // Print the int part
  459.   sprintf(string, "%d", (int)(val));
  460.   if(precision > 0) {
  461.     // Print the decimal point
  462.     strcat(string, ".");
  463.     unsigned long frac;
  464.     unsigned long mult = 1;
  465.     int padding = precision -1;
  466.     while (precision--) {
  467.       mult *=10;
  468.     }
  469.     if (val >= 0)
  470.       frac = (val - (int)(val)) * mult;
  471.     else
  472.       frac = ((int)(val)- val ) * mult;
  473.     unsigned long frac1 = frac;
  474.     while (frac1 /= 10) {
  475.       padding--;
  476.     }
  477.     while (padding--) {
  478.       strcat(string, "0");
  479.     }
  480.  
  481.     // Convert and print the fraction part
  482.     sprintf(string+strlen(string), "%d", (int)(frac));
  483.   }
  484. }
  485.  
  486.  
  487. // Converts a HEX string to an int
  488. int atoh(char c) {
  489.   if (c >= 'A' && c <= 'F')
  490.     return c - 55;
  491.   else if (c >= 'a' && c <= 'f')
  492.     return c - 87;
  493.   else
  494.     return c - 48;
  495. }

Tim Zaman

MSc Biorobotics. Specialization in computer vision and deep learning. Works at NVIDIA.

You may also like...

2 Responses

  1. Kris says:

    Can you send me the php script used pleaee

    • Tim says:

      you will not believe it but i have not backed that up. Doesn’t really matter, because at the time i had 0 PHP experience, but figured it out. it’s very simple, and, a very good exercise!