Introduction

This is a project I have been planning for a while. I want to know if my caravan is moving (when it shouldn't be!) and also to know where it is any time. I purchased an mkr gsm1400 some time ago and all I have done so far is check it worked by loading the blink script. So I thought it was time to put it to use.

Project plan
I want to use the gsm1400 together with a neo-6m to get gps position and send location based text messages.   I want to locate it in my caravan so I  need to make a case for everything to fit in.

 

Project Steps

connect everything up

write a script

test it works

test the power consumption

make a case

locate in caravan

test it still works.

 

Parts list

Arduino GSM MKR1400

GY-GPS6MV2 board

usb cable to connect to pc

1500Mah Lipo battery

SIM card

8 screws

piece of ribbon for battery holder

Parts added as afterthoughts

Perf board 90mm x 70mm

female headers

Oled display

RGB LED

 

Software required

Arduino IDE

 

Connecting everything up

First insert the Sim Card.

Then connect the GPS. The GPS I am using has 4 pins VCC, GND, TX and RX.

Here is the connection diagram.

Connection diagram

Script plan

So with everything connected up I want to write the script. The script needs to perform the following functions:

  • Gsm1400 to initialise
  • get gps location.
  • store this location as the home position
  • send location to pre planned mobile phone
  • wait for a response from phone
  • check correct phone was used
  • react to responses the planned responses are;

STORE tells gsm1400 to monitor current position and report changes

HALT tells gsm1400 to stop monitoring because I’m towing the caravan.

TEST tells gsm1400 to send current location

  • if gps position changes send location to planned phone

 

Loading Libraries

Before you can write the script you need to load the necessary libraries. I opted for the TinyGPSplus library which can be found on github here.

https://github.com/mikalhart/TinyGPSPlus

Download the zip file.

Then in Arduino IDE Sketch > Include Library > Add ZIP Library

 

Find your download and click open

 

The library will be installed and you can check this by looking at the examples under the File menu

 

Next you need to install the GSM1400 libraries and this can be found under Tools>Manage Libraries..

Search for MKRGSM and click on install.

When it is finished the version number and INSTALLED will appear alongside the author.

The script

With the libraries installed select the board -

 

We now also have the sample sketches for MKR1400 available. I opened up and tested the sketches ReceiveSMS and SendSMS and they worked really well.

 

I also tested the TinyGPSplus BasicExample and this also worked well.

I stripped down the three samples and this formed the basis of my sketch which I have annotated as best I can. In some places I am aware the coding could be improved and I would appreciate any pointers to this end. I noticed that the latitude and longitude coordinates constantly fluctuate and I did some analysis of the values. I found that the values constantly change even if the gps isn't moving. So just storing the latitude and longitude then checking again later for a match wouldn't work. So I decided to store the latitude and longitude but only alert if either the latitude and longitude position changed by more than .0003.

So the sketch will start the gsm then from the gps. It will get and store the current position and send a text message containing the stored location. It will then monitor the gps position and if it has changed, as discussed above, then it it will send a text message every 2 minutes with the latest gps position. The script is also monitoring the sms feed and is waiting to receive one of 3 messages; Store, Halt or Test.  Texting Halt to the GSM phone number will pause monitoring of the gps location and would be used when you want to move the caravan. Texting Store will take the current gps location and change it the stored location and gps monitoring will continue. A text message containing Test will get Cara-duino to send the current position. Once the status is Moving a Halt message must be sent before sending Store again.

Here's the code ... there are lots of Serial.print lines which are only there for error proofing. Now that I am happy it is working I could remove these lines.

#include <TinyGPS++.h>  // GPS library
#include <MKRGSM.h>     //GSM libary
#include <ArduinoLowPower.h>
#include <Adafruit_SSD1306.h> //Oled display library
#include <Wire.h>             //I2C library
// The TinyGPS++ object
TinyGPSPlus gps;    //Create a GPS object
Adafruit_SSD1306 display(4);
// initialize the GSM library instance
GSM gsmAccess;
GSM_SMS sms;
//variables to control text message responses
//bool responded = true; //should be initialised to false set to true to avoid unnecessary sms
bool msgreceived = false; //not currently used
char admin_phone[14] = {'+', '4', '4', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; // type in only number that can be used to send a message
char senderNumber[14]; // to check against admin  number
int mysatnum; // number of satellites
float mylong; //longitude
float mylat; //latitude
String smspos; //Text message to send to admin
// connection state
bool connected = false; // GSM status
float StoredLat; //latitude of stored position
float StoredLong; //longitude of stored position
String myStatus = "wait for gps"; //status of caravan
char MsgRcd = 'S'; //Last message received
char PrvMsgRcd; // previous message received used to revert status  when test received
unsigned long currentmillis = 0; //used for mydelay
unsigned long previousmillis = 0; //used for mydelay
unsigned long myinterval = 0; //used for mydelay
int lastupdate = 0;
String oledMessage1; //oled display messages
String oledMessage2; //oled display messages
String oledMessage3; //oled display messages
int redpin = 3;
int greenpin = 4;
int bluepin = 5;


void getpos() {
  // keep reading serial1 until gps.time.minute no longer matches last recorded update time
  // means that gps has to get an update to move out of ths loop or if no update from GPS
  //for 3 minutes then keep reading until update received
  while (Serial1.available() && (gps.time.minute() == lastupdate )  || Serial1.available() && (gps.time.minute() - lastupdate >= 3)) {
    gps.encode(Serial1.read());
  }
  //update variables
  lastupdate = gps.time.minute(); //last recorded GPs update time
  mysatnum = gps.satellites.value();
  mylong = gps.location.lng();
  mylat = gps.location.lat();
  smspos = myStatus + "  " + String(mylat, 6) + "  " + String(mylong, 6);
  Serial.println("getpos executed");
  myPrint();
}
// send sms to admin phone
void mySmsSend () {
  //    sms.beginSMS(admin_phone);
  //    sms.print(smspos);
  //    sms.endSMS();
  smspos = "";
  Serial.println("mySmsSend complete!");
}
//  used mostly for error trapping
void myPrint() {
  Serial.print (gps.time.hour());
  Serial.print(":");
  Serial.print (gps.time.minute());
  Serial.print("  ");
  Serial.print(StoredLat, 6);
  Serial.print("  ");
  Serial.println(StoredLong, 6);
  Serial.print(mylat, 6);
  Serial.print("  ");
  Serial.print(mylong, 6);
  Serial.print("  ");
  Serial .print(gps.location.isValid());
  Serial.print("  ");
  Serial.println(mysatnum);
  Serial.print(smspos);
  Serial.print("  ");
  Serial.print("Last message receeived = ");
  Serial.println(MsgRcd);
  Serial.print("checksum failures ");
  Serial.println(gps.failedChecksum());
  oledMessage1 = String(mylat, 6) + " " + String(mylong, 6);
  oledMessage2 = smspos;
  oledMessage3 = myStatus + " " + MsgRcd;
  oled();
}
// when SMS = S this routine used to store location
void storepos() {
  StoredLat = mylat;
  StoredLong = mylong;
  Serial.print(StoredLat);
  Serial.println(" Storepos executed");
}
// check stored location matches current location has a degree of adjustment to allow for gps fluctuations // 
//  messages sent to the phone are full GPSs so exact location is known.
void checkpos() {
  if (StoredLat >= (mylat + .0003) || StoredLat <= (mylat - .0003) || StoredLong >= (mylong + .0003) || StoredLong <= (mylong - .0003) ) {
    myStatus = "Moving";
  }
  else {
    myStatus = "Static";
  }
  Serial.println("checkpos executed");
  Serial.println(myStatus);
}
//check sms messages
void checksms() {
  connected = false;
  while (!connected) {
    if (gsmAccess.begin() == GSM_READY) {
      connected = true;
      Serial.println("Connected");
    } else {
      Serial.println("Not connected");
      delay(1000);
    }
  }
  // If there are any SMSs available
  Serial.println("checking for SMS");
  if (sms.available()) {
    Serial.println("Message Received");
    // Get remote number
    sms.remoteNumber(senderNumber, 14);
    Serial.print(senderNumber);
    Serial.println("|");
    Serial.print(admin_phone);
    Serial.println("|");
    // test remote number matches admin phone
    bool test = false;
    int x = 0;
    for (x = 0; x < 13; x++) {
      Serial.print(senderNumber[x]);
      Serial.print(" ");
      Serial.println(admin_phone[x]);
      if (senderNumber[x] == admin_phone[x]) {
        test = true;
      }
      else {
        test = false;
        break;
      }
    }
    Serial.println(test);
    // Test nessage is valid
    if (test == true) {
      PrvMsgRcd = MsgRcd;
      MsgRcd = char(sms.peek());
      if (MsgRcd == 'T' || MsgRcd == 'H' || MsgRcd == 'S') {
        Serial.println("Message received okay");
      }
      else {
        MsgRcd = PrvMsgRcd;
        Serial.println("Message ignored");
      }
      sms.flush();
      test = false;
      Serial.print("Messaged received ");
      Serial.println(MsgRcd);
    }
    else
    {
      sms.flush();
    }
  }
}
//delay routine because c delay seems to interfere with serial1
void mydelay() {
  currentmillis = millis();
  previousmillis = millis();
  while ((unsigned long)(currentmillis - previousmillis) <= myinterval) {
    currentmillis = millis();
  }
}
void oled() {
  // Clear the buffer.
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println(oledMessage1);
  display.println(oledMessage2);
  display.println(oledMessage3);
  display.display();
}

void setup() {
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
  display.clearDisplay();
  pinMode(redpin, OUTPUT);
  pinMode(greenpin, OUTPUT);
  pinMode(bluepin, OUTPUT);
  analogWrite(redpin, 255);
  analogWrite(greenpin, 0);
  analogWrite(bluepin, 0);
  delay(1000);

  Serial.begin(115200); // connect usbserial
  Serial1.begin(9600);  // connect serial1 on the tx and rx pins
  //  while (!Serial) {// wait for serial port to connect. Serial required for error proofing only. Serial doesn't work
  //   ;               // when battery connected so these lines have to be commented out
  //  }                // otherwise caraduino will forever stay in this loop waiting for serial.
  // connect to GSM service
  while (!connected) {
    if (gsmAccess.begin() == GSM_READY) {
      connected = true;
      Serial.println("Connected");
    } else {
      Serial.println("Not connected");
      delay(1000);
    }
  }
  Serial.println("GSM initialized");

  Serial.println("Reached initial gps store");
  // get position
  while (mylat == 0) {
    gps.encode(Serial1.read());
    Serial.println("getting first fix");
    getpos();
  }

  storepos(); //store position
  Serial.print("Stored pos");
  Serial.print(StoredLat);
  Serial.print(" ");
  Serial.println(StoredLong);
  lastupdate = gps.time.minute();
  smspos = "Stored  " + String(StoredLat) + " " + String(StoredLong);
  Serial.println(smspos);
  analogWrite(redpin, 0);
  analogWrite(greenpin, 255);
  analogWrite(bluepin, 0);
}

void loop() {
  switch (MsgRcd) {
    case 'H': //Halt means stop monitoring position while moving from one location to another
      smspos = "Halt message received. Position monitoring has stopped";
      mySmsSend();
      while (MsgRcd == 'H') {
        Serial.println( "Halt mode checking SMS");
        oledMessage1 = "Halt mode";
        oledMessage2 = "   ";
        oledMessage3 = "   ";
        oled();
        analogWrite(redpin, 0);
        analogWrite(greenpin, 0);
        analogWrite(bluepin, 255);
        myinterval = 30000;
        mydelay();
        checksms();
        myPrint();
      }
      break;
    case 'S': //Store means remember position and monitor
      analogWrite(redpin, 0);
      analogWrite(greenpin, 255);
      analogWrite(bluepin, 0);
      if (PrvMsgRcd != 'S') {
        getpos();
        storepos();
        smspos = "Stored  " + String(StoredLat, 6)  + " " + String(StoredLong, 6);
        Serial.println(smspos);
        mySmsSend();
        checkpos();
      }
      // just monitor position and check sms
      while (myStatus == "Static" && MsgRcd == 'S') {
        checksms();
        getpos();
        checkpos();
        Serial.println("Static and Stored checking for SMS");
        myinterval = 30000;
        mydelay();
      }
      // if checkpos determines caravan is moving send text messages with current position
      while (myStatus == "Moving" && MsgRcd == 'S') {
        getpos();
        smspos = myStatus + " " + mylat + " " + mylong;
        mySmsSend ();
        oledMessage1 = String(mylat, 6) + " " + String(mylong, 6);
        oledMessage2 = smspos;
        oledMessage3 = myStatus + " " + MsgRcd;
        oled();
        smspos = "";
        myinterval = 120000;
        mydelay();// wait 2 minutes before checking again
        checksms();
        Serial.println("Static and Moving checking for SMS");
      }
      break;


    case 'T': // send current location
      getpos();
      smspos = "Test requested " + String(mylat, 6) + " " + String(mylong, 6);
      mySmsSend();
      smspos = "";
      MsgRcd = PrvMsgRcd; // revert back to previous status
      break;
  }
}

 

Some points to note. Firstly the battery lasts no time at all so I haven't used lowpower because it just wouldn't make enough difference to have a battery for anything more than back up if main power was lost.  So any kind of constant use would need a much bigger battery. Fortunately my caravan has a bigger battery and also a solar panel so I just need to work out how to connect it to my caravan.

The second point is that once a battery is connected the usbserial stops working which makes it very hard to error trap so this led me to modify my design by adding an OLED into the design.

For this I used the Adafruit SSD1306 library and I cut down their examples to the barebones which amounts to only a few lines of code which you can see in the void OLED() routine inside .

Library can be found here.

https://github.com/adafruit/Adafruit_SSD1306

 

 

With the sketch working I wanted to get away from the jumper cables and have something more stable. So I started work on attaching everything to a piece of perf board. First try wasn't very elegant

See photo.

First attempt: top sidefirst attempt: bottom sidefirst attempt assembled

 

As you can see it isn't pretty and also it needed solder both sides which is nigh on impossible.

This is my second attempt which I think is much better and it also includes the RGB LED as an status indicator..

Second attempt Second attempt underneath Second attempt top

 

Enclosure

So now I need an enclosure. I thought about disguising it as a Bluetooth speaker but in the end I want to hide the device out of sight if I can, so I opted for a plain box.

I drew it on FREECAD and printed it.

Enclosure

I have made the box bigger than required because I am thinking about how I could incorporate a pir so I have left enough room for whatever I come up with. I am hiding the OLED display under the lid (I will probably remove it and just leave the socket empty for future use as this will save energy). I realised that this would mean there was no visual indiction as to what was going on inside the box so I have made a hole in the lid to incorporate an LED. This means a slight change to the sketch and the circuit.

So the sketch change involved making the LED turn to red  while waiting for connection, Change to green once working and change to blue while in halt mode.

Here is the circuit board installed in the enclosure. One problem with my enclosure design is that the gap between the battery and the usb socket is too small and only one of my usb power supplies fits. I have redesigned the enclosure slightly larger but haven't had time to print it.

 

 

So this is it working

 


And finally it working in its enclosure

 

I have tested it in the car going shopping but because of the lockdown I haven't been able to go to the caravan to test it. So I don't know what kind of impact the caravan will have on the signals or where I will have to site it in the caravan to make it work.

I think it works quite nicely but time will tell.

 

 

 

Future Considerations

I guess I shouldn't hint at the fact that my project isn't perfect. Yes it works and I think it works well. However like most things it could be improved or at least upgraded.

First upgrade I think should be the PIR mentioned earlier and I think this would be a good early warning if a text message was sent if the PIR is triggered.

Secondly I was thinking that it is impossible to hitch up a caravan without changing its angle so a gyroscope could be a good addition.

I also wondered about number spoofing. I really think this is unlikely with my caravan as its value isn't that high but some of the more expensive units might make a thief think a bit harder. That said, firstly they would need to know my number which is possible I suppose because I get calls from the accident solicitors but they would also need the Cara-duino number which can be kept secret. If they somehow got the number, one solution would be to modify the script to include a password. This got me thinking a bit more and I wondered if the sketch should warn if a valid text is received from an unauthorised number. Fairly simple to do but again number spoofing could trick that. I don't know how number spoofing is done and how hard it is do I don't really know if this should even be a consideration.