Introduction: Smart Buoy [ GPS, Radio (NRF24) and a SD Card Module ]
This Smart Buoy series charts our (ambitious) attempt to build a scientific buoy that can take meaningful measurements about the sea using off-the-shelf products. This is tutorial two of four — make sure you’re up to date, and if you need a swift introduction to the project, check out our summary.
Part 1: Making wave and temperature measurements
In this tutorial, we show show you how to get GPS data, store it on an SD card and send it somewhere using radio.
We did this so we could keep track of the location of our seaborne Buoy. The radio means that we can watch it remotely and the SD card means that on the off chance something breaks and it goes for a wander, we can download the data it collected during its unplanned excursion — if we’re ever able to retrieve it!
Supplies
Step 1: Getting GPS Data
The smart buoy makes sensor measurements as it sits in the sea, including GPS location and datetime. Take a look at the schematic which shows how we set up the circuit. The GPS module communicates via serial connection, so we’re using the Arduino software serial library as well as the tiny GPS library to communicate with it. These libraries make everything super simple. Let’s take you through the code…
#include <TinyGPS++.h> #include <SoftwareSerial.h> // The TinyGPS++ object TinyGPSPlus gps; // The serial connection to the GPS device SoftwareSerial ss(4, 3); struct dataStruct{ double latitude; double longitude; unsigned long date; unsigned long time; }gpsData; void setup(){ Serial.begin(115200); ss.begin(9600); } void loop(){ while (ss.available() > 0){ if (gps.encode(ss.read())){ getInfo(); printResults(); } } } void getInfo(){ if (gps.location.isValid()){ gpsData.latitude = gps.location.lat(); gpsData.longitude = gps.location.lng(); } else{ Serial.println("Invalid location"); } if (gps.date.isValid()){ gpsData.date = gps.date.value(); } else{ Serial.println("Invalid date"); } if (gps.time.isValid()){ gpsData.time = gps.time.value(); } else{ Serial.println("Invalid time"); } } void printResults(){ Serial.print("Location: "); Serial.print(gpsData.latitude, 6); Serial.print(", "); Serial.print(gpsData.longitude, 6); Serial.print(" Date: "); Serial.print(gpsData.date); Serial.print(" Time: "); Serial.print(gpsData.time); Serial.println(); }
(Check out the video for this code at https://youtu.be/xz1ix76U28E)
Step 2: Sending GPS Data Via Radio
Suppose the buoy is in sea taking measurements, but we want to see the data without getting our feet wet or bringing the buoy ashore. To get the measurements remotely, we’re using a radio module connected to an Arduino on both sides of the communication. In future, we will replace the receiver-side Arduino with a raspberry pi. The radio works similarly with both these interfaces so swapping them over is pretty straightforward.
The radio module communicates using SPI, which requires a few more connections than I2C but is still really easy to use because of the NRF24 library. Using the GPS module for the sensor measurements, we transmit its data from one Arduino to the other. We’re going to connect up the GPS and radio module to the Arduino and on the other side an Arduino with the radio module - have a look at the schematic.
Transmitter
#include <SPI.h> #include <nRF24L01.h> #include <RF24.h> #include <SoftwareSerial.h> #include <TinyGPS++.h> TinyGPSPlus gps; SoftwareSerial ss(4, 3); RF24 radio(8, 7); // CE, CSN struct dataStruct{ double latitude; double longitude; unsigned long date; unsigned long time; }gpsData; void setup() { Serial.begin(115200); ss.begin(9600); Serial.println("Setting up radio"); // Setup transmitter radio radio.begin(); radio.openWritingPipe(0xF0F0F0F0E1LL); radio.setChannel(0x76); radio.setPALevel(RF24_PA_MAX); radio.setDataRate(RF24_250KBPS); radio.stopListening(); radio.enableDynamicPayloads(); radio.powerUp(); Serial.println("Starting to send"); } void loop() { while (ss.available() > 0){ if (gps.encode(ss.read())){ getInfo(); radio.write(&gpsData, sizeof(gpsData)); } } } void getInfo(){ if (gps.location.isValid()){ gpsData.longitude = gps.location.lng(); gpsData.latitude = gps.location.lat(); } else{ gpsData.longitude = 0.0; gpsData.latitude = 0.0; } if (gps.date.isValid()){ gpsData.date = gps.date.value(); } else{ gpsData.date = 0; } if (gps.time.isValid()){ gpsData.time = gps.time.value(); } else{ gpsData.time = 0; } }
RECEIVER
#include <SPI.h> #include <nRF24L01.h> #include <RF24.h> RF24 radio(8, 7); // CE, CSN struct dataStruct{ double latitude; double longitude; unsigned long date; unsigned long time; }gpsData; void setup() { Serial.begin(115200); // Setup receiver radio radio.begin(); radio.openReadingPipe(1, 0xF0F0F0F0E1LL); radio.setChannel(0x76); radio.setPALevel(RF24_PA_MAX); radio.setDataRate(RF24_250KBPS); radio.startListening(); radio.enableDynamicPayloads(); radio.powerUp(); } void loop() { if (radio.available()) { radio.read(&gpsData, sizeof(gpsData)); Serial.print("Location: "); Serial.print(gpsData.latitude, 6); Serial.print(", "); Serial.print(gpsData.longitude, 6); Serial.print(" Date: "); Serial.print(gpsData.date); Serial.print(" Time: "); Serial.print(gpsData.time); Serial.println();} }
(Check out the video for this code at https://youtu.be/xz1ix76U28E)
Attachments
Step 3: Storing Data Using an SD Card Module
The radio module is quite reliable, but sometimes you need a contingency plan in case there is a power cut on the receiver side or if the radio moves out of range. Our contingency plan is a SD card module which allows us to store the data we collect. The quantity of data being collected isn’t that large, so even a small SD card will easily be able to store a day’s worth of data.
#include <SPI.h> #include <SD.h> #include <SoftwareSerial.h> #include <TinyGPS++.h> TinyGPSPlus gps; SoftwareSerial ss(4, 3); struct dataStruct{ double latitude; double longitude; unsigned long date; unsigned long time; }gpsData; void setup() { Serial.begin(115200); ss.begin(9600); if (!SD.begin(5)) { Serial.println("Card failed, or not present"); return; } Serial.println("card initialized."); File dataFile = SD.open("gps_data.csv", FILE_WRITE); if (dataFile) { dataFile.println("Latitude, Longitude, Date, Time"); dataFile.close(); } else{ Serial.println("nope can't open file"); } } void loop() { while (ss.available() > 0){ if (gps.encode(ss.read())){ getInfo(); printResults(); saveInfo(); } } } void getInfo(){ if (gps.location.isValid()){ gpsData.latitude = gps.location.lat(); gpsData.longitude = gps.location.lng(); } else{ Serial.println("Invalid location"); } if (gps.date.isValid()){ gpsData.date = gps.date.value(); } else{ Serial.println("Invalid date"); } if (gps.time.isValid()){ gpsData.time = gps.time.value(); } else{ Serial.println("Invalid time"); } } void printResults(){ Serial.print("Location: "); Serial.print(gpsData.latitude, 6); Serial.print(", "); Serial.print(gpsData.longitude, 6); Serial.print(" Date: "); Serial.print(gpsData.date); Serial.print(" Time: "); Serial.print(gpsData.time); Serial.println(); } void saveInfo(){ File dataFile = SD.open("gps_data.csv", FILE_WRITE); if (dataFile) { dataFile.print(gpsData.latitude); dataFile.print(", "); dataFile.print(gpsData.longitude); dataFile.print(", "); dataFile.print(gpsData.date); dataFile.print(", "); dataFile.println(gpsData.time); dataFile.close(); } else{ Serial.println("nope no datafile"); } }
(We talk through this code in the video https://youtu.be/xz1ix76U28E)
Attachments
Step 4: Sending and Storing GPS Data
Step 5: Thanks!
Part 1: Making Wave And Temperature Measurement
Part 2: GPS NRF24 Radio and SD Card
Part 3: Scheduling Power to the Buoy
Check out T3chFlicks.org for more tech-focused educational content (YouTube, Instagram, Facebook, Twitter).