Vår video fick silver i årets FACEnetwork (Farmhouse and Artisan Cheese & Dairy Producers European Network) videotävling!! Årets tema var bland annat att lyfta bra idéer och smarta lösningar. Som klippt och skuret för oss tänkte vi och började filma snuttar från vår vardag för att få ihop till en liten film om hur vi använder teknik för att driva en hållbar verksamhet. Ni får jättegärna gå in och kika på resultatet!
Internet of Cheese
Den senaste månaden har vi haft mycket finbesök här på gården! Land Lantbruks reporter kom och hälsade på för att skriva en artikel om vårt bidrag till Teknikutmaningen. Sen kom P4 Blekinge för att ställa lite mer frågor om hur automationerna hjälper oss hålla elkostnaderna nere och till sist fick vi en kamera på plats när SVT Nyheter var nyfikna på samma ämne.
Vi tycker det är så roligt att våra automationer och tekniska lösningar uppmärksammas! Det här är något vi jobbat med redan innan elpriserna började skena men givetvis är det mer aktuellt nu än någonsin. Vår vision är att driva en hållbar verksamhet och eftersom vi har en bakgrund inom IT faller det sig naturligt att det är just tekniska lösningar vi jobbar med för att uppnå det. För er som är nyfikna har vi länkat till några av de medier vi synts i på sistone:
Getmejeriet i Svängsta sänker sina elkostnader – med hemmabyggt it-system
2022-10-22 SVT Nyheter
Getmejeriet kapar elräkningen med hjälp av ”internet of cheese”
2022-10-10 Sveriges Radio p4 Blekinge
Automatik startar osttillverkningen när elpriset är som lägst
2022-09-30 Land Lantbruk Teknikutmaningen 2022
Teknikutmaningen 2022
Automatik startar osttillverkningen när elpriset är som lägst
”För att spara på elkostnaden har Claire och Nils Sjöström byggt ett automatiskt system som startar osttillverkningen på natten när priset på el är som lägst. Allt som behövdes var en dator och några reläer. Nu tävlar de i Teknikutmaningen med sitt bidrag ”Internet of Cheese”. Klicka här för att läsa hela vårt bidrag till Land Lantbruks Teknikutmaningen 2022.
Ost gjord på solljus
Vi kommer inte höja våra priser i år! Tack vare solceller, effektiv energiförbrukning och att vi har nästan hela produktionskedjan på gården.
Det fina med ostystning är att det naturligt följer samma kurva som solljuset och därför är det optimalt att driva mejeriet med energi från solceller. När midsommarsolen står som högst, har getterna som mest mjölk och vi ystar på för glatta livet. Sen när soltimmarna minskar, ja då minskar också mjölkmängden och därmed ystningen och energiförbrukningen.
På vintern använder vi bara el till det allra nödvändigaste. Getterna går på sparlåga i väntan på killingarnas ankomst och istället för mjölkning och ystning passar vi på att riva, fixa, rensa och andra projekt som inte hinns med under högsäsongen. Sen till våren när dagarna ljusnar, ja då drar allt igång igen. Hagarna grönskar, mjölken flödar och solen skiner rätt ner i osten!
We built a dairy!
For quite a while we weren’t sure if we should try and make our goat-and-cheese-farm dream come true by building a dairy on location or if we should try to rent a space in a commercial kitchen of some sort. The opportunity arose when Claire’s stepfather decided to bring a container with building materials from China for the house project he is working on, and let us have the container in return for storing the stuff in it on our farm for some time. We then decided that we should convert the 20 foot container to our very own farm dairy!
The container in place in front of one of our barns:
We poured concrete on the floor and made a drain:
Then we cut out holes for windows and doors, put up walls and a ceiling:
We poured more fine concrete as a final layer and had some ”help” decorating:
Then we put up ceramic tiles on the walls, painted the ceiling:
In between there was a lot of work on things such as painting the floor with epoxi, connecting water and sewage, installing ventilation, and of course, putting a roof with a tilt on top of the container:
Finally it was time to move in all the equipment. This is Nils using our 300 l Rademaker cheese-making vat for the first time!
Claire in front of the cheese vat wearing signature hat and apron with our logo:
The whole project took us about 6 months from start to finish, while working with our day-jobs as well as taking care of the goats, etc. Did I mention we also delivered 27 goat babies..?
All in all, building our farm dairy was a really cool project which we are super proud of, and plan to put to good use as well as develop and expand during the years to come.
The farms of capital. Part I
The post-industrial crossroads
During the last century, the farm as a societal phenomenon, as well as economical factor, has transformed. A typical farm 100 years ago, would today be considered small scale or homestead. The activities on a farm 100 years ago could span over almost the entire agricultural field, including animal husbandry for several species, vegetable, crop and fodder cultivation, forestry, fishing and fruit foraging, as well as processing the staples into refined and conserved food. These value creating processes was distributed horizontally over several production chains, as well as vertically from raw material and staple production, to refined food and manufactured goods. An economically flexible model, since the cost of switching line of production when one was doing bad, mostly was service based as taken in the learning of a new skill, or hiring another specialist. The modern industrialized farms are most often highly specialized on a few activities, chosen from an economical perspective. If there are grasslands, cow or sheep meat would be a good choice. If there are sedimentary soils, growing crops would be suitable. However, managing farms from an industrial perspective has proven risky, as not only the weather presents unforseeable risks, but also the global market, dependencies on logistics, dependencies on fertilizer and fuel prices and interests on the capital invested in the machine park. The solution to many of these problems has been to scale up and specialize even more, all inspired from other industrial areas. But as the same logistic network that is needed to transport your cattle to a distant meat packaging plant, also can transport meat from a place where it is cheaper to produce, the competition eventually becomes harder. A phenomenon that has greatly reduced the amount of small farms.
That’s all logical. This curve is to be found in all areas affected by industrialization, let be during different periods.The agricultural sector just tends to be a little slower than i.e. manufacturing and raw material sectors, that accomplished the same a few decades earlier.
However, the industrial age is long in the past now. The digital era, the age of information and knowledge, has introduced itself in area after area, leading to transformation of traditional methods, or extinction. Will the huge, specialized and economically vulnerable farms destroy each other ecologically and economically in the competition, and how will our food be produced then?
How much wood would the woodchuck chuck, if the woodchuck could chuck wood
One of the many new activities that farm life has brought us is taking in wood from our small but lovely forest. We have yet to optimize this process (having discussed alternatives such as using a horse to drag logs from the forest to the far, as well as buying a quad and bringing in chopped wood after having chopped it in the forest).
For now, we are happy to view this labor as exercise with a purpose – keeping the house warm! There is a primal pleasure in sawing down a tree, sawing it into logs that are the right length for our furnace, chopping the logs into firewood, and then stacking the firewood in neat boxes that are sheltered from the rain. The actual chucking of that same firewood into the furnace takes place at least 6 months from when the tree was cut down – usually longer, the drier the wood is the better!
We primarily take down trees that have broken after a storm, as well as trees that are growing on the very edge of the lake and are sick from too much water. We have a variety of species – a lot of spruce but also more sturdy kinds of trees such as aspen and beech. An added bonus of taking down spruces is that the goats love eating the needles and peeling the bark off the branches, especially now in wintertime when fresh greens are scarce.
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.
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
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
A neat GPS-module supported by the arduino TinyGPS library. Pretty fast on getting a fix, and supplying coordinates once a second.
The Nano
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 + |
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.
Working 9 to 5
Since we both have ”office jobs” it was a priority for us to get a good working space sorted. One of the smaller rooms downstairs seemed like a good option, and since it had a funky smell (hence giving the room the not-so-flattering name ”kissrummet”/”the pee-room”) as well as the floor being way crooked, it felt even more like the best place to start off the interior remodeling. We took out the greenish/brown plastic floor and uncovered some pretty old floorboards and isolating materials which we replaced and built up a little to make the floor more even. Finally we were able to put down a nice new oak floor! After that it was only the small matter of replacing the wallpapers, painting the ceiling, frames, window and radiator and putting up new lamps… We did not get around to finishing the room until October but we were mighty happy to be able to move in our desk!
This is what the room looked like before:
Work in progress… Raoul ”helping” as usual!
And this is what it looks like now:
Getting into the closet
The walk-in closet adjacent to our bedroom was not initially a priority to renovate, but one day Claire got fed up with crawling around on the floor looking for a pair of socks in the cupboard she had been using to store clothes in since we moved here. ”It will improve our quality of life!”, she exclaimed, and so the project began. We removed the old plastic carpet, took out the frames and steamed/scraped the old wallpapers off. Also, one of the walls needed to be replaced and the chimney-wall needed a good scraping/cleaning as well as a new coat of paint.The first idea that came to mind was a nice, red, carpet, so we built around that, adding a colorful wallpaper and golden frames as a final touch. Since the ceiling is quite low we were not able to install any of the standard wardrobe-systems found at IKEA, and in fact did not want to cover up too much of the nice walls. Instead, we found some regular cube-shaped shelves of the ”Kallax”-model, as well as a curtain-rod for clothes on hangers.
This is what it looked like before:
..and the result – a luxurious little room, where the only regret is that there is not more time to be spent choosing clothes and hanging out in there: