From bdf9d4dd161721add7cc281c15fe4236e77e9009 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Wed, 26 Jun 2019 15:46:07 +0100
Subject: [PATCH] Extensively refactor to reduce resource usage.

Looks like LMIC is still an issue though :-/
---
 .gitmodules                      |  3 +++
 iot/libraries/Arduino-MemoryFree |  1 +
 iot/main/gps.cpp                 | 46 +++++++++++++++++++++-----------
 iot/main/gps.h                   | 19 +++++++------
 iot/main/main.ino                | 37 +++++++++++++------------
 iot/main/peripheral.cpp          |  8 +++---
 iot/main/peripheral.h            |  8 +++---
 iot/main/power.cpp               | 10 -------
 iot/main/power.h                 |  9 -------
 iot/main/settings.h              | 10 +++++--
 iot/main/storage.cpp             | 19 ++++++-------
 iot/main/storage.h               |  2 +-
 iot/main/transmission.cpp        | 33 ++++++++---------------
 13 files changed, 101 insertions(+), 104 deletions(-)
 create mode 160000 iot/libraries/Arduino-MemoryFree

diff --git a/.gitmodules b/.gitmodules
index c410298..9bf6efd 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,3 +10,6 @@
 [submodule "iot/libraries/arduino-lmic"]
 	path = iot/libraries/arduino-lmic
 	url = https://github.com/matthijskooijman/arduino-lmic.git
+[submodule "iot/libraries/Arduino-MemoryFree"]
+	path = iot/libraries/Arduino-MemoryFree
+	url = https://github.com/mpflaga/Arduino-MemoryFree.git
diff --git a/iot/libraries/Arduino-MemoryFree b/iot/libraries/Arduino-MemoryFree
new file mode 160000
index 0000000..aed5cc5
--- /dev/null
+++ b/iot/libraries/Arduino-MemoryFree
@@ -0,0 +1 @@
+Subproject commit aed5cc5e28f8f01afc41a625151c184a8e94e32e
diff --git a/iot/main/gps.cpp b/iot/main/gps.cpp
index 4f49d58..77b0d5a 100644
--- a/iot/main/gps.cpp
+++ b/iot/main/gps.cpp
@@ -1,33 +1,35 @@
 #include "gps.h"
 #include "settings.h"
 
-// The GPS message decoder
-TinyGPSPlus	gps;
 // The serial connection to the GPS device
-SoftwareSerial serial_gps(PIN_GPS_RX, PIN_GPS_TX);
+SoftwareSerial* serial_gps;
 
 void gps_begin() {
+	serial_gps = new SoftwareSerial(PIN_GPS_RX, PIN_GPS_TX);
 	// Initialise the software-based serial connection to the GPS device
-	serial_gps.begin(BAUD_GPS);
+	serial_gps->begin(BAUD_GPS);
 }
 
-void gps_fetch() {
-	Serial.print(F("[gps] Getting location: "));
+GPSLocation gps_fetch() {
+	// The GPS message decoder
+	TinyGPSPlus	gps;
+	GPSLocation result;
+	
+	Serial.print(F("[gps] Working: "));
 	uint32_t ms_last_update = millis();
 	// We WILL discover our location - if it's the last thing we do!
 	while(true) {
 		if(millis() > 5000 && gps.charsProcessed() < 10) {
-			Serial.println(F("\n[error] Failed to initialise GPS device."));
+			Serial.println(F("\n[error] GPS device init failed"));
 			while(true) { delay(1); }
 		}
 		
 		// Make sure there's something to read
-		if(serial_gps.available() <= 0) {
+		if(serial_gps->available() <= 0)
 			continue;
-		}
 		
 		// If it failed, go around again
-		if(!gps.encode(serial_gps.read()))
+		if(!gps.encode(serial_gps->read()))
 			continue;
 		
 		// If we haven't acquired a lock yet, go around again
@@ -41,14 +43,26 @@ void gps_fetch() {
 		
 		// Hooray!
 		Serial.println(F("ok"));
-		return;
+		
+		result.lat = gps.location.lat();
+		result.lng = gps.location.lng();
+		#ifdef SD_DEBUG_ENABLED
+		snprintf(result.time, 19, "%04d-%02d-%02d %02d:%02d:%02d",
+			gps.date.year(),
+			gps.date.month(),
+			gps.date.day(),
+			gps.time.hour(),
+			gps.time.minute(),
+			gps.time.second()
+		);
+		#endif
+		return result;
 	}
 }
 
-TinyGPSPlus gps_info() {
-	return gps;
-}
-
 void gps_end() {
-	Serial.println(F("[warning] Putting the GPS device to sleep isn't implemented yet."));
+	serial_gps->end();
+	delete serial_gps;
+	//delete gps;
+	// Serial.println(F("[warning] Putting the GPS device to sleep isn't implemented yet."));
 }
diff --git a/iot/main/gps.h b/iot/main/gps.h
index 85c79b5..a67187b 100644
--- a/iot/main/gps.h
+++ b/iot/main/gps.h
@@ -6,6 +6,16 @@
 
 #include "settings.h"
 
+// A lightweight struct to hold location information.
+// TinyGPS++ actually uses a *ton* of RAM so we can't keep the instance around as a global variable
+struct GPSLocation {
+	float lat;
+	float lng;
+	#ifdef SD_DEBUG_ENABLED
+	char time[20];
+	#endif
+};
+
 /**
  * Initialises the connection to the GPS device.
  */
@@ -15,14 +25,7 @@ void gps_begin();
  * Fetches new data from the GPS module.
  * May take a moment, as the GPS device needs time to acquire a satellite fix.
  */
-void gps_fetch();
-
-/**
- * Fetches the latest information from the GPS device.
- * Call gps_fetch() first.
- * @return TinyGPSLocation	The current location.
- */
-TinyGPSPlus gps_info();
+GPSLocation gps_fetch();
 
 /**
  * Ends the connection to the GPS device and puts it to sleep in order to save 
diff --git a/iot/main/main.ino b/iot/main/main.ino
index 7dcd296..d81f564 100644
--- a/iot/main/main.ino
+++ b/iot/main/main.ino
@@ -2,6 +2,7 @@
 #include <Arduino.h>
 #include <TinyGPS++.h>
 #include <SD.h>
+#include <MemoryFree.h>
 
 #include "random.h"
 // BAD PRACTICE: For some extremely strange reason, the Arduino IDE doesn't pick up random.cpp like it does our other source files - so we've got to explicitly include it here. If we had control over the build process (which we don't), we've use a Makefile here that handled this better.
@@ -15,43 +16,41 @@
 
 void setup() {
 	Serial.begin(BAUD_PC);
-	Serial.println(F("[main] Beginning collection sequence."));
+	Serial.println(F("[main] Starting"));
 	
 	random_begin();
+	Serial.println(freeMemory(), DEC); // 1
 	
 	peripheral_register(PIN_SPI_CS_RFM95);
 	peripheral_register(PIN_SPI_CS_SD);
 	
+	Serial.println(freeMemory(), DEC); // 2
 	
 	gps_begin();
-	TinyGPSPlus gps_data = gps_info();
+	Serial.println(freeMemory(), DEC); // 3
+	GPSLocation gps_data = gps_fetch();
+	Serial.println(freeMemory(), DEC); // 4
 	gps_end();
+	Serial.println(freeMemory(), DEC); // 5
 	
-	Serial.print(F("[main] Location: (")); Serial.print(gps_data.location.lat()); Serial.print(F(", ")); Serial.print(gps_data.location.lng()); Serial.println(F(")"));
+	Serial.print(F("[main] Location ")); Serial.print(gps_data.lat); Serial.print(F(", ")); Serial.println(gps_data.lng);
 	
 	uint32_t id = random_get();
 	
-	Serial.print(F("[main] id: "));
+	Serial.print(F("[main] Id "));
 	Serial.println(id);
 	
 	// Activate microSD card breakout board on the SPI bus
 	peripheral_unsilence(PIN_SPI_CS_SD);
 	
 	store_init();
-	store_reading(id, gps_data.location);
-	char debug_message[64];
-	int chars = snprintf(debug_message, 64, "%d-%d-%d %d:%d:%d | %f %f",
-		gps_data.date.year(),
-		gps_data.date.month(),
-		gps_data.date.day(),
-		gps_data.time.hour(),
-		gps_data.time.minute(),
-		gps_data.time.second(),
-		gps_data.location.lat(),
-		gps_data.location.lng()
-	);
-	store_debug(debug_message, chars);
+	Serial.println(freeMemory(), DEC); // 6
+	store_reading(id, gps_data);
+	#ifdef SD_DEBUG_ENABLED
+	store_debug(gps_data.time, 19);
+	#endif
 	store_close();
+	Serial.println(freeMemory(), DEC); // 7
 	
 	// ------------------------------------------------------------------------
 	
@@ -60,8 +59,8 @@ void setup() {
 	
 	uint8_t message[] = "testing";
 	
-	transmit_init();
-	transmit_send(message, 7);
+	// transmit_init();
+	// transmit_send(message, 7);
 	
 	
 	power_off(); // Doesn't return
diff --git a/iot/main/peripheral.cpp b/iot/main/peripheral.cpp
index f4ecfa2..2899b9c 100644
--- a/iot/main/peripheral.cpp
+++ b/iot/main/peripheral.cpp
@@ -1,20 +1,20 @@
 #include <Arduino.h>
 
-void peripheral_register(int pin_number) {
+void peripheral_register(uint8_t pin_number) {
 	pinMode(OUTPUT, pin_number);
 	// Disable the device by default to avoid issues
 	digitalWrite(pin_number, HIGH);
 }
 
-void peripheral_unsilence(int pin_number) {
+void peripheral_unsilence(uint8_t pin_number) {
 	digitalWrite(pin_number, LOW);
 }
 
-void peripheral_silence(int pin_number) {
+void peripheral_silence(uint8_t pin_number) {
 	digitalWrite(pin_number, HIGH);
 }
 
-void peripheral_switch(int pin_number_old, int pin_number_new) {
+void peripheral_switch(uint8_t pin_number_old, uint8_t pin_number_new) {
 	digitalWrite(pin_number_old, HIGH);
 	digitalWrite(pin_number_new, LOW);
 }
diff --git a/iot/main/peripheral.h b/iot/main/peripheral.h
index d186a72..58fb084 100644
--- a/iot/main/peripheral.h
+++ b/iot/main/peripheral.h
@@ -4,21 +4,21 @@
  * Register a new SPI peripheral.
  * @param	pin_number	The pin number of the device's chip select pin.
  */
-void peripheral_register(int pin_number);
+void peripheral_register(uint8_t pin_number);
 
 /**
  * Allows the device with the given chip select pin to talk on the SPI bus.
  * @param pin_number The pin number of the chip select pin of the device to allow to talk.
  */
-void peripheral_unsilence(int pin_number);
+void peripheral_unsilence(uint8_t pin_number);
 /**
  * Stops the device with the given chip select pin from talking on the SPI bus.
  * @param pin_number The chip-select pin number of the device to stop.
  */
-void peripheral_silence(int pin_number);
+void peripheral_silence(uint8_t pin_number);
 /**
  * Switches the active device from one to another on the SPI bus.
  * @param pin_number_old The chip-select pin number of the old device to switch out from.
  * @param pin_number_new The chip-select pin number of the new device to switch in to.
  */
-void peripheral_switch(int pin_number_old, int pin_number_new);
+void peripheral_switch(uint8_t pin_number_old, uint8_t pin_number_new);
diff --git a/iot/main/power.cpp b/iot/main/power.cpp
index 082e395..d5a281d 100644
--- a/iot/main/power.cpp
+++ b/iot/main/power.cpp
@@ -2,16 +2,6 @@
 
 #include "settings.h"
 
-/*
-void power_gps_wakeup() {
-	Serial.println(F("Warn: GPS wakeup isn't implemented yet."));
-}
-
-void power_gps_standby() {
-	Serial.println(F("Warn: GPS standby isn't implemented yet."));
-}
-*/
-
 void power_off() {
 	Serial.println(F("[power] Shutting down"));
 	// Serial.println(F("[power] Switching GPS module to standby"));
diff --git a/iot/main/power.h b/iot/main/power.h
index 6e4f006..94e8717 100644
--- a/iot/main/power.h
+++ b/iot/main/power.h
@@ -1,13 +1,4 @@
 
-/**
- * Wakes the GPS module up from standby.
- */
-// void power_gps_wakeup();
-/**
- * Puts the GPS module into standby.
- */
-// void power_gps_standby();
-
 /**
  * Signals that our work is complete and that we can turn off now to the 
  * TPL5111.
diff --git a/iot/main/settings.h b/iot/main/settings.h
index 3808602..7cfcdc2 100644
--- a/iot/main/settings.h
+++ b/iot/main/settings.h
@@ -31,6 +31,9 @@
 /// RFM95 ///
 /////////////
 
+// Whether to actually enable LMIC-based transmission or not.
+#define ENABLE_RADIO
+
 // The SPI chip-select pin for the RFM 95
 #define PIN_SPI_CS_RFM95	10
 
@@ -63,7 +66,10 @@
 #define PIN_SPI_CS_SD		3
 
 // The filename on the microSD card to store data in.
-#define SD_FILENAME	"data.tsv"
+#define SD_FILENAME	F("data.tsv")
+
+// Whether to write debug information to the filename below. This could compromise privacy, so it should be commented out for production.
+#define SD_DEBUG_ENABLED
 
 // The filename on the microSD card to store debug information in
-#define SD_FILENAME_DEBUG "debug.log"
+#define SD_FILENAME_DEBUG F("debug.log")
diff --git a/iot/main/storage.cpp b/iot/main/storage.cpp
index 8acbbaa..3a7d312 100644
--- a/iot/main/storage.cpp
+++ b/iot/main/storage.cpp
@@ -3,29 +3,28 @@
 #include <TinyGPS++.h>
 
 #include "settings.h"
-
-File _debug_handle;
+#include "gps.h"
 
 void store_init() {
 	// NOTE: If this doesn't work, then we need to use pinMode() & specify the data pin here instead.
 	if(!SD.begin(PIN_SPI_CS_SD)) {
-		Serial.println(F("Error: Failed to initialise connection to microSD card"));
+		Serial.println(F("Error: MicroSD card init failed"));
 		while(true) { delay(1); } // Pseudo-halt, but uses delay() to ensure we keep passing back control to allow easy re-programming
 	}
 }
 
-void store_reading(uint32_t id, TinyGPSLocation location) {
+void store_reading(uint32_t id, GPSLocation location) {
 	// Open the file to  write the data to. If it doesn't exist it will be created.
 	// Unlike elsewhere, FILE_WRITE opens the file with the cursor starting at the end, so it's basically actually equivalent to FILE_APPEND that we use in other environments. Confusing, I know.
 	File handle = SD.open(SD_FILENAME, FILE_WRITE);
 	
 	handle.print(id);
-	handle.print("\t");
+	handle.print(F("\t"));
 	
-	handle.print(location.lat());
-	handle.print("\t");
+	handle.print(location.lat);
+	handle.print(F("\t"));
 	
-	handle.print(location.lng());
+	handle.print(location.lng);
 	handle.println();
 	
 	handle.close();
@@ -33,7 +32,9 @@ void store_reading(uint32_t id, TinyGPSLocation location) {
 
 void store_debug(char* buffer, int size) {
 	File handle = SD.open(SD_FILENAME_DEBUG, FILE_WRITE);
-	
+	handle.write(buffer, size);
+	handle.println();
+	handle.close();
 }
 
 void store_close() {
diff --git a/iot/main/storage.h b/iot/main/storage.h
index d5386c4..b957de7 100644
--- a/iot/main/storage.h
+++ b/iot/main/storage.h
@@ -7,7 +7,7 @@
 
 void store_init();
 
-void store_reading(uint32_t id, TinyGPSLocation location);
+void store_reading(uint32_t id, GPSLocation location);
 
 void store_debug(char* buffer, int size);
 
diff --git a/iot/main/transmission.cpp b/iot/main/transmission.cpp
index 5f61a6c..ad32f50 100644
--- a/iot/main/transmission.cpp
+++ b/iot/main/transmission.cpp
@@ -5,13 +5,10 @@
 #include "settings.h"
 
 // Global static variable that's used to detect when LMIC has finished doing it's thing
-static bool is_sending_complete = false;
-
-
-static osjob_t sendjob;
+bool is_sending_complete = false;
 
 void transmit_init() {
-	Serial.println(F("Initialising LMIC"));
+	Serial.println(F("[LMIC] Init"));
 	// LMIC init
 	os_init();
 	// Reset the MAC state. Session and pending data transfers will be discarded.
@@ -70,7 +67,7 @@ void transmit_init() {
 bool transmit_send(uint8_t* data, int length) {
 	// Check if there is not a current TX/RX job running
 	if (LMIC.opmode & OP_TXRXPEND) {
-		Serial.println(F("OP_TXRXPEND: There's already a job running, not sending"));
+		Serial.println(F("OP_TXRXPEND: Job running, can't send"));
 		return false;
 	}
 	// Prepare upstream data transmission at the next possible time.
@@ -88,16 +85,9 @@ bool transmit_send(uint8_t* data, int length) {
 	return true;
 }
 
-// These callbacks are only used in over-the-air activation, so they are
-// left empty here (we cannot leave them out completely unless
-// DISABLE_JOIN is set in config.h, otherwise the linker will complain).
-void os_getArtEui (u1_t* buf) { }
-void os_getDevEui (u1_t* buf) { }
-void os_getDevKey (u1_t* buf) { }
-
 void onEvent (ev_t ev) {
-	Serial.print(os_getTime());
-	Serial.print(": ");
+	Serial.print(millis());
+	Serial.print(" ");
 	switch(ev) {
 		case EV_SCAN_TIMEOUT:
 			Serial.println(F("EV_SCAN_TIMEOUT"));
@@ -111,27 +101,26 @@ void onEvent (ev_t ev) {
 		case EV_BEACON_TRACKED:
 			Serial.println(F("EV_BEACON_TRACKED"));
 			break;
-		case EV_JOINING:
+		/*case EV_JOINING:
 			Serial.println(F("EV_JOINING"));
 			break;
 		case EV_JOINED:
 			Serial.println(F("EV_JOINED"));
-			break;
+			break;*/
 		case EV_RFU1:
 			Serial.println(F("EV_RFU1"));
 			break;
-		case EV_JOIN_FAILED:
+		/*case EV_JOIN_FAILED:
 			Serial.println(F("EV_JOIN_FAILED"));
 			break;
 		case EV_REJOIN_FAILED:
 			Serial.println(F("EV_REJOIN_FAILED"));
-			break;
-			break;
+			break;*/
 		case EV_TXCOMPLETE:
-			Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
+			Serial.println(F("EV_TXCOMPLETE (incl. RX window wait)"));
 			if(LMIC.dataLen) {
 				// data received in rx slot after tx
-				Serial.print(F("Data Received: "));
+				Serial.print(F("Received: "));
 				Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
 				Serial.println();
 			}
