Commit 4e41cafd authored by Patrick's avatar Patrick

Merge branch 'master' of

parents dd2bea42 85277970
![Purple Mallard](logo/purple_mallard.png)
An Arduino & red-peg based water-level ultrasound sensor in an IP rated case, battery, solar power and remote logging to a teal-mallet server via GSM Shield
# Changelog
* TODO: Platformio code for gsm-ultrasound needs porting to compile in the
Arduino IDE without library install.
## 20th November 2017
* Sending changed to fractional meters instead of millimeters.
* Settings moved to the top of the code, and server and entity_id separated out.
* Offset distance added for adjusting the level.
## 19th November 2017
6 Prototypes prepared for in field testing, this is equivalent to TRL5. As
an initial test, the main target for this is to find as many problems
with the devices or design assumptions by placing the devices into a working
environment, and by them being used by people outside of the design process.
### Operation
The prototypes are set to record the distance reading from the ultrasound sensor
every 15 minutes, at 0, 15, 30 and 45 minutes past the hour. They convert the
distance reading into a simulated level reading (simulating a 15m sensor to bed
maximum) and record that stage measurement in millimetres with a date and time
stamp as comma separated values (CSV) file on the internal microSD card.
Measurements are stored as one file per day, with all the days of the month per
directory (`YYYYMM/DD.csv`). This folder usage limits the requirement to hold or
calculate a *current measurements* file name, and efficiently stores the files
to limit the number per folder so as not to exceed the limits of the FAT
partition format. With this nested structure, the file limits of the FAT format
should be able to store 21 years of information.
Measurements are sent out once 10 have been collected. While it would be preferable
to send out less frequently — as the power usage of the GSM modem is the greatest
part of the whole device usage — the sending rate is limited by the memory
capacity of the microcontroller chosen. This sending rate means measurements
sent remotely may be up to 2.5 hours old.
The device is powered on by opening the case and connecting the battery connector.
The device should then be closed fully and mounted for data collection. The
solar panel should be connected to the trailing lead from the case and mounted
at 45⁰ in a clear, south facing direction. At expected consumption rate of 4.5W/day,
and with the solar panel collecting an estimated 9W/day, the unit *should* be
able to run continuously. Without any solar charge, the 5000mAh battery is
capable of running the device for 4.5 days before it will shut down.
To power off the device fully, disconnect the solar charger or any DC charger
supply; then disconnect the battery.
It is possible to recharge the internal battery by connecting a 5–6V 2.1mm,
centre positive, DC supply to the jack socket on the solar charge board with the
case open. *Do not exceed 6V to this board.* The DC jack on the Arduino board
is not connected.
### Issues
The intention of these test units (numbered `0001``0006`) is to find as many
errors, problems and failures in operation and handling as possible. While it is
desirable to know of any successful operation, it is extremely valuable to
record any problems at this stage, so they can be fed back into product development.
Known issues include:
* There is no record of battery voltage, so it is not possible to know
remotely if a device goes offline if it's been caused by battery depletion.
* The use of the GSM modem is the major power usage in the device, even in
low power mode, the modem continuously uses more power than the sensor does
when it is in use.
* Having both GSM and SD recording uses a large amount of the available
microcontroller RAM. It remains to be seen how important the local data
collection is in practice, and if it can be removed in favour of remote only.
* The solar collection is a noticeable cost in the unit. It is possible that
with lower power requirements for sending — such as with an LPWAN solution —
that it may be possible to run for extended periods without requiring an
external energy supply.
* While every effort has been made to ensure the solar collection meets the
energy usage requirements of the unit, the amount of energy collected is highly
dependent on the weather, location, orientation and view of the sky at the
installation site.
......@@ -9,13 +9,22 @@ teal_mallet_gsm TM;
#include "SdFat.h"
SdFat SD;
File myFile;
#define SD_CS_PIN SD_SS
/* settings: */
#define MAX_DEPTH 15000 // sensor to bed depth in mm
#define OFFSET 0 // gauge offset in mm
#define ORDINAL_INTERVAL 15 // ordinal minutes (0,15,30,45)
#define NUM_MMNT 11 // measurement buffer length
#define NUM_TO_SEND 10 // how many measurements should be gathered before sending
#define SERVER_NAME ((char*)"")
#define ENTITY_ID ((char*)"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
/* end settings */
// set > 0 to get debug serial output
#define DEBUG_MODE 0
#define DEBUG_MODE 1
// choose processor sleep mode (1 = on)
#define SLEEP_MODE_ON 1
......@@ -24,7 +33,7 @@ File myFile;
const char level_param[] PROGMEM = "Level";
const char level_quali[] PROGMEM = "15 min";
const char level_dtype[] PROGMEM = "STAGE";
const char level_units[] PROGMEM = "mm";
const char level_units[] PROGMEM = "m";
const char level_perid[] PROGMEM = "INSTANT";
// then add the strings to an array (for multiple datastreams), also in progmem
......@@ -45,9 +54,6 @@ state_machine_e g_current_state = SLEEP_IDLE;
// readings are taken on ordinals (0/15/30/45)
t_SensorData last_reading_time;
#define NUM_MMNT 11 // measurement buffer length
// then create a pre-sized array of these structs to hold a set of timestamped measurements
uint8_t num_mmnt = NUM_MMNT;
t_measurement measurements[NUM_MMNT] = {};
......@@ -58,7 +64,6 @@ uint8_t readings_tail = 0;
// sends happen after a fixed number of ordinals
t_SensorData last_sending_time;
// how many readings should be collected for sending (<NUM_MMNT)
#define NUM_TO_SEND 10
#error "Can't hold all the readings, reduce NUM_TO_SEND below NUM_MMNT"
......@@ -66,7 +71,7 @@ t_SensorData last_sending_time;
void setup() {
TM.begin(((char*)""), ((char*)"aaaaaaaa-aaaa-aaa-aaa-aaaaaaaaaaaa"));
......@@ -174,11 +179,11 @@ void sleep_idle() {
void take_measurement() {
// create the distance variable to hold the stage reading
long temp = -1; // start with OOR
float temp = -1.0; // start with OOR
t_SensorData current_reading = get_reading(ANA);
if (current_reading.sensor == ANA) {
// if it's the correct reading, calculate the output
temp = MAX_DEPTH - RP.distance(current_reading, MB7366);
temp = float(MAX_DEPTH - RP.distance(current_reading, MB7366) - OFFSET) / 1000.0;
#if DEBUG_MODE > 1
Serial.print(F("take mmnt at: "));
......@@ -186,12 +191,12 @@ void take_measurement() {
Serial.print(F("STAGE: "));
#if DEBUG_MODE > 0
Serial.println(temp, 3);
// stage calculation should always return positive
if (temp >= 0) {
if (temp >= 0.0) {
// add the reading to the buffer
readings_head = (readings_head + 1) % NUM_MMNT;
#if DEBUG_MODE > 0
......@@ -237,7 +242,7 @@ void take_measurement() {
myFile =, FILE_WRITE);
if (file_exists == false) {
// write headers
......@@ -255,7 +260,7 @@ void take_measurement() {
myFile.print("00"); // zero seconds
myFile.print(temp); // stage in mm
myFile.print(temp); // stage in m
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment