Skip to content

Commit f9f4838

Browse files
committed
MCU8MASS-458 Add GPS tracker example
1 parent ef41192 commit f9f4838

File tree

2 files changed

+257
-1
lines changed

2 files changed

+257
-1
lines changed
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
#include <Adafruit_GPS.h>
2+
#include <Arduino.h>
3+
#include <http_client.h>
4+
#include <led_ctrl.h>
5+
#include <log.h>
6+
#include <low_power.h>
7+
#include <lte.h>
8+
#include <mcp9808.h>
9+
#include <mqtt_client.h>
10+
#include <sequans_controller.h>
11+
#include <veml3328.h>
12+
13+
#define GPSSerial Serial2
14+
15+
#define HTTP_DOMAIN "161.35.147.136"
16+
17+
/**
18+
* @brief Connected refers to the LTE network.
19+
*/
20+
enum class State { NOT_CONNECTED, CONNECTED, CONNECTED_WITH_FIX };
21+
22+
/**
23+
* @brief Interface for the GPS.
24+
*/
25+
Adafruit_GPS GPS(&GPSSerial);
26+
27+
/**
28+
* @brief Due to no float string support, these variables are used with dtostrf
29+
* such that the float values for latitude and longitude are properly
30+
* converted before being sent to the server.
31+
*/
32+
char latitude[16] = "0";
33+
char longitude[16] = "0";
34+
char time[24] = "0";
35+
36+
/**
37+
* @brief Keeps track of the state.
38+
*/
39+
static State state = State::NOT_CONNECTED;
40+
41+
/**
42+
* @brief Whether or not we've parsed one GPS entry. Prevents sending zeros
43+
* whilst having fix after boot.
44+
*/
45+
static bool has_parsed = false;
46+
47+
/**
48+
* @brief Starts the GPS modem and sets up the configuration.
49+
*/
50+
static void initializeGPS(void) {
51+
52+
GPSSerial.swap(1);
53+
54+
GPS.begin(9600);
55+
56+
// Enable RMC & GGA output messages
57+
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
58+
59+
// Set the update rate to 1Hz
60+
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
61+
62+
// Disable updates on antenna status
63+
GPS.sendCommand(PGCMD_NOANTENNA);
64+
65+
delay(1000);
66+
67+
// Ask for firmware version
68+
GPSSerial.println(PMTK_Q_RELEASE);
69+
}
70+
71+
/**
72+
* @brief Sets up the HTTP client with the given domain
73+
*/
74+
static void initializeHTTP(void) {
75+
if (!HttpClient.configure(HTTP_DOMAIN, 8080, false)) {
76+
Log.info("Failed to configure HTTP client");
77+
} else {
78+
Log.info("Configured HTTP");
79+
}
80+
}
81+
82+
/**
83+
* @brief Sends a payload with latitude, longitude and the timestamp.
84+
*/
85+
static void sendData(void) {
86+
87+
char data[80] = "";
88+
89+
sprintf(data,
90+
"{\"lat\":\"%s\",\"lon\":\"%s\",\"time\": \"%s\"}",
91+
latitude,
92+
longitude,
93+
time);
94+
95+
HttpResponse response = HttpClient.post("/data", data);
96+
97+
Log.infof("POST - status code: %u, data size: %u\r\n",
98+
response.status_code,
99+
response.data_size);
100+
101+
if (response.status_code != 0) {
102+
String body = HttpClient.readBody(512);
103+
104+
if (body != "") {
105+
Log.infof("Response: %s\r\n", body.c_str());
106+
}
107+
}
108+
}
109+
110+
/**
111+
* @brief Connects to the network operator. Will block until connection is
112+
* achieved.
113+
*/
114+
static void connectToNetwork() {
115+
116+
// If we already are connected, don't do anything
117+
if (!Lte.isConnected()) {
118+
while (!Lte.begin()) {}
119+
Log.infof("Connected to operator: %s\r\n", Lte.getOperator().c_str());
120+
}
121+
122+
state = State::CONNECTED;
123+
}
124+
125+
/**
126+
* @brief Checks for new GPS messages and parses the NMEA messages if any.
127+
*/
128+
static void parseGPSMessages() {
129+
130+
// Read the incoming messages, needn't do anything with them yet as that is
131+
// taken care of by the newNMEAReceived() function.
132+
if (GPS.available()) {
133+
GPS.read();
134+
}
135+
136+
if (GPS.newNMEAreceived()) {
137+
if (!GPS.parse(GPS.lastNMEA())) {
138+
// If we fail to parse the NMEA, wait for the next one
139+
return;
140+
} else {
141+
142+
Log.rawf("Timestamp: %d/%d/20%d %d:%d:%d GMT+0 \r\n",
143+
GPS.day,
144+
GPS.month,
145+
GPS.year,
146+
GPS.hour,
147+
GPS.minute,
148+
GPS.seconds);
149+
150+
Log.rawf("Fix: %d, quality: %d\r\n", GPS.fix, GPS.fixquality);
151+
152+
if (GPS.fix) {
153+
154+
// Need to convert all floats to strings
155+
dtostrf(GPS.latitudeDegrees, 2, 4, latitude);
156+
dtostrf(GPS.longitudeDegrees, 2, 4, longitude);
157+
158+
sprintf(time,
159+
"%d/%d/20%d %d:%d:%d",
160+
GPS.day,
161+
GPS.month,
162+
GPS.year,
163+
GPS.hour,
164+
GPS.minute,
165+
GPS.seconds);
166+
167+
Log.rawf("Location: %s N, %s E\r\n", latitude, longitude);
168+
Log.rawf("Satellites: %d\r\n", GPS.satellites);
169+
170+
Log.rawf("\r\n");
171+
172+
has_parsed = true;
173+
}
174+
}
175+
}
176+
}
177+
178+
void setup() {
179+
LedCtrl.begin();
180+
LedCtrl.startupCycle();
181+
182+
Log.begin(115200);
183+
Log.info("Starting AVR-IoT Cellular Adafruit GPS example");
184+
185+
// We configure the low power module for power down configuration, where
186+
// the LTE modem and the CPU will be powered down
187+
LowPower.configurePowerDown();
188+
189+
// Make sure sensors are turned off
190+
Veml3328.begin();
191+
Mcp9808.begin();
192+
Veml3328.shutdown();
193+
Mcp9808.shutdown();
194+
195+
initializeGPS();
196+
initializeHTTP();
197+
}
198+
199+
void loop() {
200+
201+
parseGPSMessages();
202+
203+
switch (state) {
204+
case State::NOT_CONNECTED:
205+
connectToNetwork();
206+
break;
207+
208+
case State::CONNECTED:
209+
if (!Lte.isConnected()) {
210+
state = State::NOT_CONNECTED;
211+
} else if (GPS.fix) {
212+
state = State::CONNECTED_WITH_FIX;
213+
LedCtrl.on(Led::CON);
214+
215+
// Decrease update rate once we have fix to save power
216+
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ);
217+
}
218+
219+
break;
220+
221+
case State::CONNECTED_WITH_FIX:
222+
if (!Lte.isConnected()) {
223+
state = State::NOT_CONNECTED;
224+
} else if (!GPS.fix) {
225+
226+
// Lost fix, set update rate back to 1 Hz
227+
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
228+
229+
state = State::CONNECTED;
230+
LedCtrl.off(Led::CON);
231+
}
232+
233+
if (Lte.isConnected() && GPS.fix && has_parsed) {
234+
sendData();
235+
236+
// Reset state before we power down, which will turn of the modem
237+
state = State::NOT_CONNECTED;
238+
LedCtrl.off(Led::CON);
239+
240+
Log.info("Entering low power");
241+
GPS.sendCommand(PMTK_STANDBY);
242+
delay(1000); // Allow some time to print messages before we sleep
243+
244+
LowPower.powerDown(60);
245+
246+
Log.info("Woke up!");
247+
GPS.sendCommand(PMTK_AWAKE);
248+
249+
// Set that we need an update for the position
250+
has_parsed = false;
251+
}
252+
253+
break;
254+
}
255+
}

src/http_client.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ static HttpResponse sendData(const char *endpoint,
123123
start_character != HTTP_SEND_START_CHARACTER);
124124

125125
if (start_character != HTTP_SEND_START_CHARACTER) {
126-
Log.error("Timed out waiting for modem to be ready for HTTP payload");
126+
Log.error("Timed out waiting for modem to be ready for HTTP payload. "
127+
"Is the server active?");
127128
return httpResponse;
128129
}
129130

0 commit comments

Comments
 (0)