GoTo Goat – A GPS goat tracker

As goats are naturally born masters of escaping, we soon realized that the electric fence only should be considered as the first line of defense. Usually, the most clever ones finds their way out, and then calls for the others to follow, and generally, their just heading out to a nearby pasture, where the grass is allegedly greener, or home to the barn. On a few occasions though, they have been spooked by wild boars, and ran off into the forest. When I was tracking them out on a small dirt road, and a neighbor called from his forestry tractor, about 3 km out, and asked if maybe my goats where out hiking, I realized that we have a need for a tracking device. The idea of the GoTo Goat device was born, a GPS-module, transmitting coordinates over the cell network.

wp-1474494088463.jpg
The goats where very happy to see me, and followed me home through the forest.

 

 

Features:

Real-time GPS location

Upload coordinates to a cloud service, or your own server over GPRS

Solar powered

Android locator app

Component list

Microcontroller: Arduino nano 6 euro

GPRS module: SIM800L 5 euro

GPS module: Ublox NEO-6M 7 euro

Power supply: Linocell 3X Solar Powerbank 8000 mAh 50 euro

Step-down regulator: AMS1117 adjustable (5V-3.7V) 1 euro

Sim card (Telia) free

Total cost: 19 euro for the components and 50 for the powerbank, but i bought the powerbank locally in Sweden, and there are much cheaper ones around on ebay. With a little more labour put into sourcing, the cost could probably drop to about 30 euro.

The SIM800L

s-l225

This is a very nice little module. Essentially a complete cell phone, except for user interface, speaker and mic. It’s controlled from the arduino by sending AT commands through serial communication. The SIM800L is supposed to be powered by a 3.7V Li cell, as in a cellphone, and that’s why the buck converter is needed in this setup to bring the current down from 5V.

The only functionality used here is the GPRS device for sending over HTTP, but using SMS or DMTF for controlling the unit are possibilities as well as calling the goat for an occasional chat.

 

 

The NEO-6M

u6

 

 

A neat GPS-module supported by the arduino TinyGPS library. Pretty fast on getting a fix, and supplying coordinates once a second.

 

 

 

The Nano

$_1

I choose an Arduino nano for prototyping, since it has it’s own FTDI, a reasonable size and a friendly number of pins. If the size, power consumption and prize needs to be further tweaked, I can’t see any reason not to use a mini pro though. The SIM800L is rated for max 3.7V to 4.1V so the internal voltage regulators will be useless.

 The SIM

Any sim card that supports GPRS will work, but it’s recommended to use one that not expires too soon after charging. As very little traffic volumes are transmitted, it would be a shame if the card needed to be refueled every 3 months or so. Instead, if you can find one that works for several years, and only using the throttled speed when paid data is depleted, that would be completely sufficient.

 

Connections

SIM800L pin NEO-&M pin AMS1117 pin Arduino pin Powerbank
Net – antenna Usb Port2
Vcc Vout
Rst D5
TX D8
RX D7
Gnd Gnd Gnd Gnd Port1 –
Vcc 5V
Rx D4
Tx D3
Vin Port1 +
31583617682_a1031ba862_o
SIM800 and nano in the center, NEO-6M to the right. Here, the SIM800 is powered by a 3.7V battery

Cloud API

I choose thingspeak.com as cloud service to store the coordinates. Mostly because I already use it for other sensors, so I’m already familiar with the API. Virtually any online API would be suitable, as long as you can send in values through a HTTP GET parameter.

Thingspeak will let you send in 3 000 000 updates / year with their free account, so that will limit you to 5.7 updates / minute. I’ll go for max 1/min, probably less. Since you are putting the API key in the arduino code, you don’t want to use a service that is likely to go off the market, or become “premium only”, like Xively did. To be sure that the service stays alive, you could host your own thingspeak platform on a raspberry pi or similar.

Code

The NEO-6M is very simple to interface from the Arduino. I used the kitchensink example from the TinyGPS library, and trimmed it down to the bare necessities.

The SIM800L is more complicated to interface, since you need 2-way communication to ensure that the commands have been successfully performed. I found some example code at Cassiopeia that was very helpful. Even if I do not use DMTF functionality as in the example, their approach for communicating with the SIM800L is very convenient.

As both the NEO-6M, the SIM800L and the arduino IDE Serial monitor are using serial communication for interfacing the arduino, there will be issues regarding which ports to use. I kept the hardware serial (Serial) for debugging purposes, and let only the computer interface with it. For the modules, I assigned two SoftwareSerial ports, sim800 on pin 7 and 8 and ss on pin 3 and 4. To keep the communication channels separated, the arduino is instructed on which one to listen, depending on where input is expected.


#include <SoftwareSerial.h>
#include <TinyGPS++.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
//Thingspeak channel
#define CHANNEL 199082 //Not used
//Thingspeak API write key
#define APIKEY "XXXXXXXXX"//Put your own write key here
//SIM800 TX is connected to Arduino D8
#define SIM800_TX_PIN 8
#define RATE 10000 //GPS sampling rate
//SIM800 RX is connected to Arduino D7
#define SIM800_RX_PIN 7
static const int RXPin = 3, TXPin = 4;
static const uint32_t GPSBaud = 9600;
const int sim_rst = 5;
int errors = 0;
//Create software serial object to communicate with SIM800
SoftwareSerial sim800(SIM800_TX_PIN,SIM800_RX_PIN);
SoftwareSerial ss(RXPin, TXPin);
TinyGPSPlus gps;
unsigned long last = 0UL;
// watchdog interrupt
ISR(WDT_vect)
{
wdt_disable(); // disable watchdog
}
void myWatchdogEnable(const byte interval)
{
MCUSR = 0; // reset various flags
WDTCSR |= 0b00011000; // see docs, set WDCE, WDE
WDTCSR = 0b01000000 | interval; // set WDIE, and appropriate delay

wdt_reset();
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
sleep_mode(); // now goes to Sleep and waits for the interrupt
}
void setup() {
//Begin serial comunication with Arduino and Arduino IDE (Serial Monitor)
Serial.begin(9600);
while(!Serial);

//Beging serial communication with Arduino and SIM800
sim800.begin(9600);
delay(1000);
ss.begin(GPSBaud);
delay(1000);
Serial.println("Setup Complete!");
last = millis()-RATE;
}

void loop() {
errors = 0;
// Dispatch incoming characters from GPS
ss.listen();
while (ss.available() > 0)
gps.encode(ss.read());
if (millis() - last > RATE)
{
if (gps.location.isValid())
{
static const double OFFICE_LAT = 56.000, OFFICE_LON = 14.000; //Hard coded reference coordinates if needed in the future
double distanceToOffice =
TinyGPSPlus::distanceBetween(
gps.location.lat(),
gps.location.lng(),
OFFICE_LAT,
OFFICE_LON);
Serial.println(distanceToOffice);
double courseToOffice =
TinyGPSPlus::courseTo(
gps.location.lat(),
gps.location.lng(),
OFFICE_LAT,
OFFICE_LON);
char tmpCourse[10];
char tmpDistance[10];
char tmpLat[10];
char tmpLng[10];
char tmpSpeed[10];
char params[200];
int batteryLevel;
//Convert GPS data to strings
dtostrf(gps.location.lat(),1,6, tmpLat);
dtostrf(gps.location.lng(),1,6, tmpLng);
dtostrf(distanceToOffice/1000, 1,6, tmpDistance);
dtostrf(courseToOffice,1,6, tmpCourse);
dtostrf(gps.speed.kmph(), 1,6, tmpSpeed);
sim800.listen();//Turn to sim800l channel
disconnectGPRS();//Sometimes, the sim800l gets stuck with GPRS activated, and trying to activate it again will naturally fail.
while(!wakeUpSim800());
if(simOK()){
batteryLevel= getBatteryLevel();
Serial.print("Battery level: ");
Serial.println(batteryLevel);
sprintf(params, "apikey=%s&field1=%s&field2=%s&field3=%s&field6=%d", APIKEY, tmpLat, tmpLng, tmpSpeed, batteryLevel);
Serial.println(params);
//Next three stages are sequential. Error handling means that if the sequence is not completed with less than 5 unsuccessful tries on all stages, there is something wrong, and the sequence is aborted.
while(!initGPRS()&&errors++<5);

while(!initHTTP()&&errors++<5);

while(!putDataToThingspeak(params)&&errors++<5);
errors = 0;
while(!disconnectGPRS()&&errors++<5);
while(!powerDownSim800());
delay(200);
Serial.println("Entering watchdog sleep");
delay(100);
myWatchdogEnable (0b100001); // 8 seconds
myWatchdogEnable (0b100001); // 8 seconds
myWatchdogEnable (0b100001); // 8 seconds
myWatchdogEnable (0b100001); // 8 seconds
myWatchdogEnable (0b100001); // 8 seconds
myWatchdogEnable (0b100001); // 8 seconds
myWatchdogEnable (0b100001); // 8 seconds
myWatchdogEnable (0b100001); // 8 seconds

Serial.println("Resuming operations");
}
ss.listen(); //Turn back to GPS channel
}

if (gps.charsProcessed() < 10)
Serial.println(F("WARNING: No GPS data. Check wiring."));

last = millis();

}
}
boolean wakeUpSim800(){
Serial.println(F("Checking for sim800 module..."));

digitalWrite(sim_rst, LOW); // hardware reset after sleep RST
delay(300);
digitalWrite(sim_rst, HIGH);

// time to startup 3 sec
for (int i = 0; i < 6; i++) {
digitalWrite(13, HIGH); // green LED blink after RESET
delay(250);
digitalWrite(13, LOW);
delay(250);
}

sim800.println("AT"); // check if sim800 module responds
delay(100);
if (sim800.find("OK")) {
Serial.println(F("sim800 module awake"));
return true;
}
else{
Serial.println(F("sim800 module not found"));
return false;
}

}
boolean simOK() { // SIM CHECK OK
Serial.println(F("Checking for SIM card.. "));

sim800.println("AT"); // check if sim800 module responds
delay(100);
if (sim800.find("OK")) {
Serial.println(F("sim800 module found"));

delay(100); // wait for sim800 to settle a bit
sim800.println("AT+CFUN=1"); // operation
if (sim800.find("OK"))
Serial.println(F("Function level 1"));
else
return false;
delay(2000);
sim800.println("AT+CSMINS?"); // check if SIM card inserted
delay(100);
if (sim800.find("CSMINS: 0,0")) {
Serial.println(F("no SIM card found, stop here"));
return false;
}
Serial.println(F("SIM card found")); // continue if SIM card found

Serial.println(F("Allow some time for SIM to register on the network.."));
Serial.println();
delay(1000);
return true;
}
else{
Serial.println(F("sim800 module not found, stop here"));
return false;
}
}

void simReply() { // SIM REPLY
delay(500);
while (sim800.available()) {
char c = sim800.read();
if (c != '\n') Serial.write(c); // replace new line with space
else Serial.print(" ");
delay(5);
}
Serial.println();
}

boolean initGPRS(){
boolean noError = true;
sim800.println("AT+CIPSHUT"); //Ensure GPRS PDP is down before init
simReply();
//delay(1000);
sim800.println("AT+CGATT=1");//Attach to GPRS service
simReply();

sim800.println("AT+SAPBR=3,1,CONTYPE,GPRS");//Set(3) bearer connected(1) to Contype GPRS
if (sim800.find("ERROR"))
noError = false;
sim800.println("AT+CGATT=1");//Attach to GPRS service
if (sim800.find("ERROR"))
noError = false;
sim800.println("AT+SAPBR=3,1,APN,online.telia.se");//Set(3) bearer connected(1) to APN
if (sim800.find("ERROR"))
noError = false;
sim800.println("AT+SAPBR=1,1");//Open connected bearer
if (sim800.find("ERROR"))
noError = false;
Serial.print("initGPRS finished with no errors = ");
Serial.println(noError);
return noError;

}

boolean initHTTP(){
boolean noError = true;
sim800.println("AT+HTTPTERM");
if (sim800.find("ERROR"))
noError = false;
sim800.println("AT+HTTPINIT");
if (sim800.find("ERROR"))
noError = false;
sim800.println("AT+HTTPPARA=CID,1");
if (sim800.find("ERROR"))
noError = false;
Serial.print("initHTTP finished with no errors = ");
Serial.println(noError);
return noError;
}
boolean putDataToThingspeak(char params[200]){
boolean noError = true;
char req[250];
sprintf(req, "AT+HTTPPARA=URL,api.thingspeak.com/update?%s", params);
Serial.println(req);
sim800.println(req);
if (sim800.find("ERROR"))
noError = false;
delay(1000);
sim800.println("AT+HTTPACTION = 0");
if (sim800.find("ERROR"))
noError = false;
Serial.print("putDataToThingspeak finished with no errors = ");
Serial.println(noError);
return noError;
}

boolean disconnectGPRS(){
boolean noError = true;
sim800.println("AT+CIPSHUT");
if (sim800.find("ERROR"))
noError = false;
sim800.println("AT+SAPBR=0,1");
if (sim800.find("ERROR"))
noError = false;
sim800.println("AT+CGATT=0");
if (sim800.find("ERROR"))
noError = false;
sim800.println("AT+CFUN=0");
if (sim800.find("ERROR"))
noError = false;
Serial.print("disconnectGPRS finished with no errors = ");
Serial.println(noError);
return noError;

}
int getBatteryLevel(){
sim800.listen();
sim800.println("AT+CBC"); // battery level
delay(500);
String s = sim800.readStringUntil(',');//Grab the value between the commas
String level = sim800.readStringUntil(',');

return(level.toInt());
}

boolean powerDownSim800(){
boolean noError = true;
sim800.println("AT+CPOWD=1");
if (sim800.find("ERROR"))
noError = false;

Serial.print("SIM800l power down = ");
Serial.println(noError);
return noError;

}

 

Here are the latitude results from thingspeak, for more, go to the GoTo goat section on the IoT page.

Android app

The Android app is still just an embryo, but it will find a goat… You can find it on Play: https://play.google.com/apps/testing/com.framtidabruk.gotogoats

You need to enter your own thingspeak.com Api key and channel ID in the settings before it becomes useful.

3766840962902790605-account_id=1

Arduino for beginners

As you might have noticed we love technology and finding innovative ways to use it around the farm. Nils has a lot of knowledge in programming as well as micro-computers such as Arduino and Raspberry Pi. Claire is an enthusiastic beginner, having participated in courses in Visual Basic, HTML/CSS and Python in the past. Her latest endeavour is to learn more about Arduino which according to the official website: “…is an open-source electronics platform based on easy-to-use hardware and software. Arduino boards are able to read inputs – light on a sensor, a finger on a button, or a Twitter message – and turn it into an output – activating a motor, turning on an LED, publishing something online. You can tell your board what to do by sending a set of instructions to the microcontroller on the board. To do so you use the Arduino programming language (based on Wiring), and the Arduino Software (IDE), based on Processing.”

Happily, we found that there is a place called Blekinge uppfinnareverkstad dedicated to innovations and co-working in a near-by town called Svängsta, and they offer courses in Arduino for beginners -among other things. It is a really cool place, founded in the 80’s in an old factory and is run on a volunteer  basis. Worth a visit (or even membership – 500 SEK for a year) if you are interested in anything from welding to 3D printing.
IMG_20161103_190618The idea is to use Arduinos for some of the many things we want to monitor and automate at the farm – in the milking process, at the dairy – or even to make a goat locator for when the critters decide to break out of their pasture! We will keep you posted…

 

DIY yoghurt maker

Even if we only milk one of our goats for the moment (and she is a low producer that give approximately half of her milk to a very hungry kid when they are together in the pasture), the milk bottles is filling up the fridge faster than we can consume it. We’ve already quit buying milk for drinking, coffee and cooking, so the next product to make ourselves will be yoghurt.

The process is rather simple, take some milk, pasteurize it if you don’t trust your hygiene, add bacterias and keep the temperature at the optimal level for as long as it takes for the bacterias to consume all accessible lactose and lower the pH to uncomfortable levels.

You can do this in your oven, but the temperature control will be crude, which results in runny and uneven yoghurt. There are yoghurt machines, not that expensive, that controls the temperature very well, but they don’t know when to turn themselves off, so you still have to watch it, or set the timer out of your best guesses.

Since my Diy wireless pH-sensor gives me the two variables I need to control the process (temperature and pH), I figured that I only needed a heat source.  Then I found something even better at a second hand store; a portable 12V peltier cooler/heater from Waeco, made in the early 90’s, featuring such elegant solutions as switching between heat and cold by turning the electricity cord, thus switching polarity.

This makes the perfect completely automatic yoghurt maker, since it both keeps the heat at an even level, and when the right pH is achieved, cools the yoghurt down to fridge temperature. Just throw in some milk and culture in a jar (or a teapot) and leave.

The controller is very simple. Since the only functionality needed that the waeco box didn’t handle, was the ability to turn on and off and switch polarity remotely, I connected a L298b motor driver and a NRF24L radio to an Arduino nano. The L298b module is normally used to turn DC motors forward and backward, but that could be applied to the peltier element in the box to make it hot or cold as well. Unfortunately, the L298b was only capable of 4 amps in throughput, and the waeco transformer supplied more even though it was specified for 4, resulting in a very hot chip. The solution was to use 2 L298b in parallell. Power cables as well as signal cables to the arduino.

28977497902_81aa65f9e6_o.jpg

The chips were still hot, but with a cooling fan from a PC chassi, they are now cold and very cool. I connected the fan to the input side to let it consume some power and make it easier for the L298b. That means that the fan is always on, which might be unnecessary.

The Arduino code for the controller is also really simple. I used the mysensors.org sample code for relay and made two adjustments: increasing the numbers om relays to at least 3 (I actually enabled 6, as there might be need for using the second channel on the L298b in the future, but for this functionality, you only need 3) and enabling pwm on the pins that controls on/off. I haven’t used pwm for anything yet, but that will allow me to control the current from the L298b output (speed, heat etc.).

The logic is placed in the home automation system I have running on a Raspberry pi. It is currently Fhem, but any system with support for the Mysensors library will work. Fhem is a quite complex system with lot of forum material in german, but if you are comfortable with both German and Perl, there is no more powerful home automation system in my opinion.

The controller presents itself in Fhem when the gateway is in inclusion mode, and this is the fhem.cfg code that is generated (with som additions):

 

define MYSENSOR_10 MYSENSORS_DEVICE 10
attr MYSENSOR_10 IODev gateway
attr MYSENSOR_10 alias Youghurt maker
attr MYSENSOR_10 mapReading_switch1 1 switch
attr MYSENSOR_10 mapReading_switch2 2 switch
attr MYSENSOR_10 mapReading_switch3 3 switch
attr MYSENSOR_10 mapReading_switch4 4 switch
attr MYSENSOR_10 mapReading_switch5 5 switch
attr MYSENSOR_10 mapReading_switch6 6 switch
attr MYSENSOR_10 mode repeater
attr MYSENSOR_10 room Dashboard,Mysensors
attr MYSENSOR_10 setReading_switch1 on,off
attr MYSENSOR_10 setReading_switch2 on,off
attr MYSENSOR_10 setReading_switch3 on,off
attr MYSENSOR_10 setReading_switch4 on,off
attr MYSENSOR_10 setReading_switch5 on,off
attr MYSENSOR_10 setReading_switch6 on,off
attr MYSENSOR_10 version 2.0.0

We can se that all 6 relays shows up, but I have only found use for three.

The logic doesn’t show up by itself so here I had to do som actual brainwork.

I found some excellent thruth tables here and got the following pin/switch setup:

Switch1/ENA: Main power 1=on, 0=off

Switch2=IN1

Switch3=IN2

Positive direction / heat: switch1 = 1, switch2 = 1, switch3 = 0

Negative direction / cold: switch1  = 1, switch2 = 0, switch3 = 1

Power off: switch1 = 0

 

The yogurt culture I am using prefers a temperature of 43 C and I will let it work until it has reached a pH of 4.20. I’ve hardcoded those levels in my config file for now, but an improvement will be to create a device that changes these values.

I want the machine to:

  • Rise and hold the temperature on 43 C.
  • Do that until pH has dropped to 4.20
  • Then cool it down as much as possible

That is achieved with the following code in fhem.cfg:


define phNotify notify MYSENSOR_118:temperature.* {if (ReadingsVal("MYSENSOR_118", "temperature", 0) > 43 ) { fhem("set MYSENSOR_10 switch1 off") } elsif(ReadingsVal("MYSENSOR_118", "temperature1", 0) > 4.25) {fhem("set MYSENSOR_10 switch1 on;; set MYSENSOR_10 switch2 on;; set MYSENSOR_10 switch3 off")} elsif(ReadingsVal("MYSENSOR_118", "temperature1", 0) < 4.25) {fhem("set MYSENSOR_10 switch1 on;; set MYSENSOR_10 switch2 off;; set MYSENSOR_10 switch3 on")}}

Worth mentioning is that the pH-sensor is called MYSENSOR_118 and its temperature sensor reports as temperature, while its pH-sensor reports as temperature1.

So this is the result. The red line is temperature, starting at fridge temperature at 6C and rising steadily to 43C where it plans out. Meanwhile the green bars representing the pH goes from 6.5 to 4.4 (at the time of the screenshot).

Obviously, the Waeco box isn’t made for heating, rather than keeping a temperature. The slow rise of about 8-9 degrees/hour making it 5 hour until optimal temperature is reached, is not acceptable. Heating the milk before putting it in the box is one easy solution, another is to never cool it down and let it go directly from the udder to the box.

 

Diy wireless pH-meter

In cheese-making, the key to a tasty, and reproducable, cheese, is keeping track of the  exact pH and the temperature in the active culture. You can do this with manual tools, such as a kitchen thermometer and pH-strips, but the measurements will be crude, and depend on your constant presence. That leading to inconsistencies between batches,  and difficulties in tracking errors in the process.

Michel Lepage is cutting the curd. Photo taken at the craft cheese-making course we took at Eldrimner in 2014.
A digital pH-meter is expensive. You can find some from €100, but you wont get built-in temperature correction for less than €250, and wireless goes beyond €350. For continous readings and  the possibility to recalibrate your sensor yourself, instead of sending it to the manufacturer,  add a lot more… I havn’t yet found a device capable of tweeting its readings 😉

My diy pH-sensor is not exactly cheap either. It ticks in at about €150 in material costs. I’ve seen people look pensive when they see the casing, and subconsiously push it closer to the recycling, so a slightly pricier casing than the pet bottle might be an investment. Otherwise, the bill of materials looks like:

Sensor

  • Atlas pH meter kit $149 (EZO version)
  • Arduino mini pro 3.3v $1.90
  • NRF24L01 radio $1
  • DS18B20 waterproof temp sensor $1.63
  • Battery holder $3
  • 2xAA batteries
  • Cables

With todays exchange rates, it translates to around €150.
If you’re setting up a new sensor network, you need a radio gateway and a computer to run the controller software on too

  • Arduino nano $6
  • NRF24L01 radio $1
  • Cables
  • Old computer or raspberry pi $25-$50

      You can order everything from ebay or aliexpress through the Mysensors store, but the items listed there may not always be availible in singel packages. Anyway, you will need more of those radios.

      Shipping is usually free from China (who is paying that?), but the pH-kit comes from the US, so add a few euros for shipping and customs.

      Features:

      • Measures pH-level in fluids and semi-solid compounds.
      • Calculates the correct pH from the latest temperature reading.
      • Measures temperature
      • 30 seconds between samples.
      • Continous measuring, just leave the probe in the milk and watch the readings.
      • Wireless transfer of data to the raspberry pi based controller unit
      • Presents the readings as a datastream or in nice graphs in a web interface. Use your phone or tablet to monitor the process from anywhere.

          Prerequisites:

          Tools

          • FTDI USB programmer, to program the arduino  mini pro and perform calibration. If you use arduino nano instead, you can skip this, but the nano is more expensive and power consuming.
          • Soldering iron, lead, soldering paste.
          • Computer with arduino ide or codebender running.
          • Pliers, knives, screwdrivers and that kind of stuff.

          Skills

          • Basic soldering. The only soldering done in my prototype is on the on/off switch. For a sturdy and reliable device to use in a kitchen environment, I recommend soldering the connections rather than using Dupont cables.
          • Basic programming. You can clone my code from codebender and hope it will work out of the box, but since things changed quickly on the internets of things, you will probably need to change some code to adapt to new version etc. So some understanding of coding will be helpful.
          • Arduino/MCU experiences. I wouldn’t recommend to make this your first microcontroller or Mysensors project. Start out with a simple blinking light and then a temperature sensor to make sure you get the IDE and Mysensors API.
          • Raspberry pi/Linux experiences. You could use a Windows computer as controller and user interface server, but if you’re up to arduino hacking, you might as well use an embedded device right away.