Skip to content

Commit 1d49900

Browse files
authored
Create Track_Your_Treats_FONA808.ino
1 parent 303a55c commit 1d49900

1 file changed

Lines changed: 239 additions & 0 deletions

File tree

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
// Track Your Treats - FONA808 Shield & Adafruit IO Halloween Candy Route Tracker
2+
// Author: Tony DiCola
3+
//
4+
// See the guide at:
5+
// https://learn.adafruit.com/track-your-treats-halloween-candy-gps-tracker/overview
6+
//
7+
// Released under a MIT license:
8+
// https://opensource.org/licenses/MIT
9+
#include <SoftwareSerial.h>
10+
#include "Adafruit_SleepyDog.h"
11+
#include "Adafruit_FONA.h"
12+
#include "Adafruit_MQTT.h"
13+
#include "Adafruit_MQTT_FONA.h"
14+
15+
16+
// Configuration (you need to change at least the APN and AIO username & key values):
17+
18+
#define LED_PIN 6 // Pin connected to an LED that flashes the status of the project.
19+
20+
#define BUTTON_PIN 5 // Pin connected to the button.
21+
22+
#define LOGGING_PERIOD_SEC 15 // Seconds to wait between logging GPS locations.
23+
24+
#define FONA_RX 2 // FONA serial RX pin (pin 2 for shield).
25+
26+
#define FONA_TX 3 // FONA serial TX pin (pin 3 for shield).
27+
28+
#define FONA_RST 4 // FONA reset pin (pin 4 for shield)
29+
30+
#define FONA_APN "" // APN used by cell data service (leave blank if unused).
31+
// Contact your cell service provider to get this value and
32+
// any username or password required too (see below).
33+
34+
#define FONA_USERNAME "" // Username used by cell data service (leave blank if unused).
35+
36+
#define FONA_PASSWORD "" // Password used by cell data service (leave blank if unused).
37+
38+
#define AIO_SERVER "io.adafruit.com" // Adafruit IO server name.
39+
40+
#define AIO_SERVERPORT 1883 // Adafruit IO port.
41+
42+
#define AIO_USERNAME "" // Adafruit IO username (see http://accounts.adafruit.com/).
43+
44+
#define AIO_KEY "" // Adafruit IO key (see settings page at: https://io.adafruit.com/settings).
45+
46+
#define PATH_FEED_NAME "treat-path" // Name of the AIO feed to log regular location updates.
47+
48+
#define GOOD_CANDY_FEED_NAME "treat-good-candy" // Name of the AIO feed to log good candy locations.
49+
50+
#define MAX_TX_FAILURES 3 // Maximum number of publish failures in a row before resetting the whole sketch.
51+
52+
53+
// Global state (you don't need to change this):
54+
SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX); // FONA software serial connection.
55+
Adafruit_FONA fona = Adafruit_FONA(FONA_RST); // FONA library connection.
56+
const char MQTT_SERVER[] PROGMEM = AIO_SERVER; // MQTT server, client, username, password.
57+
const char MQTT_CLIENTID[] PROGMEM = __TIME__ AIO_USERNAME; // (stored in flash memory)
58+
const char MQTT_USERNAME[] PROGMEM = AIO_USERNAME;
59+
const char MQTT_PASSWORD[] PROGMEM = AIO_KEY;
60+
Adafruit_MQTT_FONA mqtt(&fona, MQTT_SERVER, AIO_SERVERPORT, // MQTT connection.
61+
MQTT_CLIENTID, MQTT_USERNAME,
62+
MQTT_PASSWORD);
63+
uint8_t txFailures = 0; // Count of how many publish failures have occured in a row.
64+
uint32_t logCounter = 0; // Counter until next location log is recorded.
65+
66+
// Publishing feed setup (you don't need to change this):
67+
// Note that the path ends in '/csv', this means a comma separated set of values
68+
// can be pushed to the feed, including location data like lat, long, altitude.
69+
const char PATH_FEED[] PROGMEM = AIO_USERNAME "/feeds/" PATH_FEED_NAME "/csv";
70+
Adafruit_MQTT_Publish path = Adafruit_MQTT_Publish(&mqtt, PATH_FEED);
71+
const char GOOD_CANDY_FEED[] PROGMEM = AIO_USERNAME "/feeds/" GOOD_CANDY_FEED_NAME "/csv";
72+
Adafruit_MQTT_Publish goodCandy = Adafruit_MQTT_Publish(&mqtt, GOOD_CANDY_FEED);
73+
74+
75+
// Halt function called when an error occurs. Will print an error and stop execution while
76+
// doing a fast blink of the LED. If the watchdog is enabled it will reset after 8 seconds.
77+
void halt(const __FlashStringHelper *error) {
78+
Serial.println(error);
79+
while (1) {
80+
digitalWrite(LED_PIN, LOW);
81+
delay(100);
82+
digitalWrite(LED_PIN, HIGH);
83+
delay(100);
84+
}
85+
}
86+
87+
// Timer interrupt called every millisecond to keep track of when the location should be logged.
88+
SIGNAL(TIMER0_COMPA_vect) {
89+
// Decrease the count since last location log.
90+
if (logCounter > 0) {
91+
logCounter--;
92+
}
93+
}
94+
95+
// Serialize the lat, long, altitude to a CSV string that can be published to the specified feed.
96+
void logLocation(float latitude, float longitude, float altitude, Adafruit_MQTT_Publish& publishFeed) {
97+
// Initialize a string buffer to hold the data that will be published.
98+
char sendBuffer[120];
99+
memset(sendBuffer, 0, sizeof(sendBuffer));
100+
int index = 0;
101+
102+
// Start with '0,' to set the feed value. The value isn't really used so 0 is used as a placeholder.
103+
sendBuffer[index++] = '0';
104+
sendBuffer[index++] = ',';
105+
106+
// Now set latitude, longitude, altitude separated by commas.
107+
dtostrf(latitude, 2, 6, &sendBuffer[index]);
108+
index += strlen(&sendBuffer[index]);
109+
sendBuffer[index++] = ',';
110+
dtostrf(longitude, 3, 6, &sendBuffer[index]);
111+
index += strlen(&sendBuffer[index]);
112+
sendBuffer[index++] = ',';
113+
dtostrf(altitude, 2, 6, &sendBuffer[index]);
114+
115+
// Finally publish the string to the feed.
116+
Serial.print(F("Publishing: "));
117+
Serial.println(sendBuffer);
118+
if (!publishFeed.publish(sendBuffer)) {
119+
Serial.println(F("Publish failed!"));
120+
txFailures++;
121+
}
122+
else {
123+
Serial.println(F("Publish succeeded!"));
124+
txFailures = 0;
125+
}
126+
}
127+
128+
void setup() {
129+
// Initialize serial output.
130+
Serial.begin(115200);
131+
Serial.println(F("Track your Treats - FONA808 & Adafruit IO"));
132+
133+
// Initialize LED and button.
134+
pinMode(LED_PIN, OUTPUT);
135+
pinMode(BUTTON_PIN, INPUT_PULLUP);
136+
digitalWrite(LED_PIN, LOW);
137+
138+
// Initialize the FONA module
139+
Serial.println(F("Initializing FONA....(may take 10 seconds)"));
140+
fonaSS.begin(4800);
141+
if (!fona.begin(fonaSS)) {
142+
halt(F("Couldn't find FONA"));
143+
}
144+
fonaSS.println("AT+CMEE=2");
145+
Serial.println(F("FONA is OK"));
146+
147+
// Use the watchdog to simplify retry logic and make things more robust.
148+
// Enable this after FONA is intialized because FONA init takes about 8-9 seconds.
149+
Watchdog.enable(8000);
150+
Watchdog.reset();
151+
152+
// Wait for FONA to connect to cell network (up to 8 seconds, then watchdog reset).
153+
Serial.println(F("Checking for network..."));
154+
while (fona.getNetworkStatus() != 1) {
155+
delay(500);
156+
}
157+
158+
// Enable GPS.
159+
fona.enableGPS(true);
160+
161+
// Start the GPRS data connection.
162+
Watchdog.reset();
163+
fona.setGPRSNetworkSettings(F(FONA_APN), F(FONA_USERNAME), F(FONA_PASSWORD));
164+
delay(2000);
165+
Watchdog.reset();
166+
Serial.println(F("Disabling GPRS"));
167+
fona.enableGPRS(false);
168+
delay(2000);
169+
Watchdog.reset();
170+
Serial.println(F("Enabling GPRS"));
171+
if (!fona.enableGPRS(true)) {
172+
halt(F("Failed to turn GPRS on, resetting..."));
173+
}
174+
Serial.println(F("Connected to Cellular!"));
175+
176+
// Wait a little bit to stabilize the connection.
177+
Watchdog.reset();
178+
delay(3000);
179+
180+
// Now make the MQTT connection.
181+
int8_t ret = mqtt.connect();
182+
if (ret != 0) {
183+
Serial.println(mqtt.connectErrorString(ret));
184+
halt(F("MQTT connection failed, resetting..."));
185+
}
186+
Serial.println(F("MQTT Connected!"));
187+
188+
// Configure timer0 compare interrupt to run and decrease the log counter every millisecond.
189+
OCR0A = 0xAF;
190+
TIMSK0 |= _BV(OCIE0A);
191+
}
192+
193+
void loop() {
194+
// Watchdog reset at start of loop--make sure everything below takes less than 8 seconds in normal operation!
195+
Watchdog.reset();
196+
197+
// Reset everything if disconnected or too many transmit failures occured in a row.
198+
if (!fona.TCPconnected() || (txFailures >= MAX_TX_FAILURES)) {
199+
halt(F("Connection lost, resetting..."));
200+
}
201+
202+
// Grab a GPS reading.
203+
float latitude, longitude, speed_kph, heading, altitude;
204+
bool gpsFix = fona.getGPS(&latitude, &longitude, &speed_kph, &heading, &altitude);
205+
206+
// Light the LED solid if there's a GPS fix, otherwise flash it on and off once a second.
207+
if (gpsFix) {
208+
digitalWrite(LED_PIN, HIGH);
209+
}
210+
else {
211+
// No fix, blink the LED once a second and stop further processing.
212+
digitalWrite(LED_PIN, (millis()/1000) % 2);
213+
return;
214+
}
215+
216+
// Check if the button is pressed.
217+
if (digitalRead(BUTTON_PIN) == LOW) {
218+
// Pause a bit to debounce.
219+
delay(100);
220+
if (digitalRead(BUTTON_PIN) == LOW) {
221+
// Button pressed! Log the current location to the good candy feed.
222+
logLocation(latitude, longitude, altitude, goodCandy);
223+
// Then flash the light 5 times to signal the location was recorded.
224+
for (int i=0; i<5; ++i) {
225+
digitalWrite(LED_PIN, HIGH);
226+
delay(250);
227+
digitalWrite(LED_PIN, LOW);
228+
delay(250);
229+
}
230+
}
231+
}
232+
233+
// Periodically log the location.
234+
if (logCounter == 0) {
235+
// Log the current location to the path feed, then reset the counter.
236+
logLocation(latitude, longitude, altitude, path);
237+
logCounter = LOGGING_PERIOD_SEC*1000;
238+
}
239+
}

0 commit comments

Comments
 (0)