This section details the Nayad firmware. The code and all the material of the project are hosted in a Gitlab repository.
The code covers the parts of:
Data is sent to an online platform (Hacking Ecology Sensing) for real-time visualization and stored in the integrated database. At the same time, every hour this data is saved in local memory (system microSD card).
#include <ArduinoJson.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
#include <NAYAD_RTC.h>
#include <nayad_do.h>
#include <pgmspace.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <ThingsBoard.h>
#include <Wire.h>
#include <WiFi.h>
#include <OneWire.h>
// CLOUD SETUP
#define WIFI_AP "vodafoneB9E8"
#define WIFI_PASSWORD "2dWPgwtT@$"
#define TOKEN "lYsyZF3EHK2l32gyYq7o"
char NayadServer[] = "sensing.hackingecology.com";
//WiFi CONFIG
WiFiClient wifiClient;
ThingsBoard sensing(wifiClient);
int status = WL_IDLE_STATUS;
unsigned long lastSend;
//TOUCH CONFIG
int threshold = 44; // check the threshold value using the "touchread" code (at examples)
bool touch1detected = false;
// SD CONFIG
File myFile;
// RTC CONFIG
NAYAD_RTC RTC;
DateTime timeRTC = DateTime(__DATE__, __TIME__);
// TEMPERATURE CONFIG
const int oneWireBus1 = 0;
OneWire oneWire1 (oneWireBus1);
DallasTemperature sensorsTemp (&oneWire1);
float temperature_sensor;
// DO Sensor
Nayad_DO DO = Nayad_DO(25);
float DO_percent, DO_voltage, DO_concent;
uint8_t user_bytes_received = 0;
const uint8_t bufferlen = 32;
char user_data[bufferlen];
void setup() {
Serial.begin(115200);
InitWiFi();
lastSend = 0;
sensorsTemp.begin();
//SD card setup
touchAttachInterrupt(T0, gotTouch1, threshold);
if (!SD.begin()) {
Serial.println("Card Mount Failed");
return;
}
else {
Serial.println("Card is Ready");
return;
}
// DO Setup
Serial.println(F("Use command \"CAL\" to calibrate the circuit to 100% saturation in air\n\"CAL,CLEAR\" clears the calibration"));
if (DO.begin()) {
Serial.println("Loaded EEPROM");
}
//RTC Setup
DateTime compiled = DateTime(__DATE__, __TIME__);
Serial.println("Checking I2C device...");
if (RTC.searchDevice())
{
Serial.println("configuring RTC I2C");
RTC.configure();
if (!RTC.IsDateTimeValid())
{
if (RTC.LastError() != 0)
{
Serial.print("RTC communications error = ");
Serial.println(RTC.LastError());
}
else
{
Serial.println("RTC lost confidence in the DateTime!");
RTC.SetDateTime(compiled);
}
}
else
{
Serial.printf("Found an RTC with valid time\n");
}
timeRTC = RTC.now();
uint32_t nowTS = timeRTC.getTimeStamp();
uint32_t compiledTS = compiled.getTimeStamp();
if (nowTS < compiledTS)
{
Serial.printf("RTC is older than compile time! (Updating DateTime)\n");
RTC.SetDateTime(compiled);
}
else if (nowTS > compiledTS)
{
Serial.printf("RTC is newer than compile time. (this is expected)\n");
}
else if (nowTS == compiledTS)
{
Serial.printf("RTC is the same as compile time! (not expected but all is fine)\n");
}
if (!timeRTC.checkWeek())
{
Serial.printf("Update WEEK\n");
RTC.setWeekDays(dow(timeRTC.year(), timeRTC.month(), timeRTC.day()));
}
}
else
{
Serial.printf("device not found\n");
while (1);
}
}
void loop() {
//Loop for WIFI
if ( !sensing.connected() ) {
reconnect();
}
sensing.loop();
// Loop calibration
if (Serial.available() > 0) {
user_bytes_received = Serial.readBytesUntil(13, user_data, sizeof(user_data));
}
if (user_bytes_received) {
parse_cmd(user_data);
user_bytes_received = 0;
memset(user_data, 0, sizeof(user_data));
}
// Loop sensor data
SensorsDataDO();
}
void SensorsDataDO() {
sensorsTemp.requestTemperatures();
float temperature_sensor = sensorsTemp.getTempCByIndex(0);
{
static unsigned long timepoint = millis();
if (millis() - timepoint > 1000U) //time interval: 1 second
{
DO_percent = DO.read_do_percentage();
DO_voltage = DO.read_voltage();
DO_concent = DO.read_do_concentration(DO_voltage, temperature_sensor);
Serial.print("temperature: ");
Serial.print(temperature_sensor, 2);
Serial.println(" ^C");
Serial.print("Dissolved Oxygen: ");
Serial.print(DO_percent);
Serial.print("%, ");
Serial.print(DO_voltage);
Serial.print(" mV, ");
Serial.print(DO_concent);
Serial.println(" mg/L");
// Loop for RTC
timeRTC = RTC.now();
if (timeRTC.IsValid())
{
Serial.printf("str Data: %s\n", timeRTC.getStrDate().c_str());
Serial.printf("str Hora: %s\n", timeRTC.getStrTime().c_str());
Serial.printf("TS: %u\n", timeRTC.getTimeStamp());
delay(random(1000, 5000));
}
else
{
Serial.printf("Invalid DateTime\n");
}
}
sensing.sendTelemetryFloat("DOperc", DO_percent);
sensing.sendTelemetryFloat("DOv", DO_voltage);
sensing.sendTelemetryFloat("DOconc", DO_concent);
sensing.sendTelemetryFloat("temperature", temperature_sensor);
delay(1000);
// Setup touch and SD card Storage
if (touch1detected) {
touch1detected = false;
Serial.println("Touch 1 detected");
if (SD.exists("/data.csv")) {
Serial.println("Appending line...");
File file = SD.open("/data.csv", FILE_APPEND);
if (!file) {
Serial.println("Failed to open file for appending");
return;
}
if (file) {
file.print(timeRTC.getStrDate().c_str());
file.print(" ");
file.print(timeRTC.getStrTime().c_str());
file.print(",");
file.print(timeRTC.getTimeStamp());
file.print(",");
file.print(temperature_sensor);
file.print(",");
file.println(DO_concent);
delay(1000);
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
file.close();
}
else if (!SD.exists("/data.csv")) {
File file = SD.open("/data.csv", FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file) {
file.println("Time, UNIX, Temperature (ÂșC), DO (mg/L)");
file.print(timeRTC.getStrDate().c_str());
file.print(" ");
file.print(timeRTC.getStrTime().c_str());
file.print(",");
file.print(timeRTC.getTimeStamp());
file.print(",");
file.print(temperature_sensor);
file.print(",");
file.println(DO_concent);
Serial.println("File written");
} else {
Serial.println("Write failed");
}
file.close();
}
}
}
}
// DO calibration
void parse_cmd(char* string) {
strupr(string);
String cmd = String(string);
if (cmd.startsWith("CAL")) {
int index = cmd.indexOf(',');
if (index != -1) {
String param = cmd.substring(index + 1, cmd.length());
if (param.equals("CAL CLEAR")) {
DO.cal_clear();
Serial.println("CALIBRATION CLEARED");
}
}
else {
DO.cal();
Serial.println("DO PROBE CALIBRATED");
}
}
}
void gotTouch1() {
touch1detected = true;
}
//SD card
void writeFile(fs::FS &fs, const char * path, const char * message) {
Serial.printf("Writing file: %s\n", path);
File file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
file.close();
}
void appendFile(fs::FS &fs, const char * path, const char * message) {
Serial.printf("Appending to file: %s\n", path);
File file = fs.open(path, FILE_APPEND);
if (!file) {
Serial.println("Failed to open file for appending");
return;
}
if (file.print(message)) {
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
file.close();
}
void InitWiFi() {
Serial.println("Connecting to AP ...");
// attempt to connect to WiFi network
WiFi.begin(WIFI_AP, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("Connected to AP");
}
void reconnect() {
status = WiFi.status();
if ( status != WL_CONNECTED) {
WiFi.begin(WIFI_AP, WIFI_PASSWORD);
// while (WiFi.status() != WL_CONNECTED) {
// delay(8000);
// Serial.print(".");
//}
Serial.println("Connected to AP");
}
Serial.print("Connecting to Sensing Platform ...");
if ( tb.connect(NayadServer, TOKEN) ) {
Serial.println( "[DONE]" );
} else {
Serial.print( "[FAILED]" );
Serial.println( " : retrying in 5 seconds]" );
// Wait 5 seconds before retrying
delay( 5000 );
}
}