Commit 13a0fc03 authored by Lisandro Damián Nicanor Pérez Meyer's avatar Lisandro Damián Nicanor Pérez Meyer
Browse files

Merge branch 'release/0.0.2'

parents d97011fb 0b6c7c24
......@@ -67,11 +67,8 @@
*/
#define MQTT_BROKER_HOSTNAME_SIZE 64
/// NTP sync period: each 12 hours.
#define NTP_SYNC_PERIOD_MS 43200000UL
/// NTP sync timeout while booting the device.
#define NTP_TRY_TIMEOUT_MS 10000
/// NTP sync period: each hour.
#define NTP_SYNC_PERIOD_MS 3600000UL
typedef struct {
char flag;
......
......@@ -158,7 +158,7 @@ INLINE_INHERITED_MEMB = NO
# shortest path that makes the file name unique will be used
# The default value is: YES.
FULL_PATH_NAMES = YES
FULL_PATH_NAMES = NO
# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
# Stripping is only done if one of the specified strings matches the left-hand
......@@ -468,7 +468,7 @@ LOOKUP_CACHE_SIZE = 0
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
NUM_PROC_THREADS = 1
NUM_PROC_THREADS = 0
#---------------------------------------------------------------------------
# Build related configuration options
......@@ -494,7 +494,7 @@ EXTRACT_PRIVATE = YES
# methods of a class will be included in the documentation.
# The default value is: NO.
EXTRACT_PRIV_VIRTUAL = NO
EXTRACT_PRIV_VIRTUAL = YES
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
......
......@@ -6,8 +6,15 @@
* Pouso Marcelo Alejandro <mapouso86@gmail.com>
*/
#include "MqttClient.h"
#define ARDUINOJSON_USE_LONG_LONG 1
#include <ArduinoJson.h>
#include <ESPmDNS.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include "MqttClient.h"
#include "Configuration.h"
#include "printf.h"
......@@ -32,8 +39,13 @@ const char *ca_cert =
"vtYJW2fy/3/nDV6lLKTpWm49WA==\n" "-----END CERTIFICATE-----\n";
#endif
using namespace std::placeholders;
MqttClient::MqttClient():_pubSubClient(_mqttClient)
{
callback = std::bind(&MqttClient::subscriptionCallback, this, _1, _2, _3);
_pubSubClient.setCallback(callback);
_lastNtpSync = 0;
}
bool MqttClient::connectWifi(const char *ssid, const char *pass)
......@@ -130,3 +142,62 @@ bool MqttClient::publish(const char *topic, const char *message)
{
_pubSubClient.publish(topic, message);
}
bool MqttClient::subscribeToHeartbeat()
{
bool subscribed = _pubSubClient.subscribe(HEARTBEAT_TOPIC);
if(subscribed)
printfn("Subscribed to heartbeat");
else
printfn("Not subscribed to heartbeat");
return subscribed;
}
void MqttClient::loop()
{
_pubSubClient.loop();
}
bool MqttClient::getEpoch(time_t &time)
{
struct timeval tv;
auto retval = gettimeofday(&tv, NULL);
if(retval < 0)
return false;
time = tv.tv_sec;
return true;
}
void MqttClient::subscriptionCallback(const char * topic, byte* payload, unsigned int length)
{
if(strncmp(HEARTBEAT_TOPIC, topic, strlen(HEARTBEAT_TOPIC)) != 0)
return;
byte pLoad[length + 1];
memcpy(pLoad, payload, length);
pLoad[length] = '\0';
StaticJsonDocument<64> doc;
DeserializationError error = deserializeJson(doc, pLoad);
if (error)
{
printf("deserializeJson() failed: ");
printfn("%s", error.c_str());
return;
}
_lastNtpSync = doc["time"].as<time_t>();
struct timeval tv;
tv.tv_sec = _lastNtpSync;
settimeofday(&tv, NULL);
if(_pubSubClient.unsubscribe(HEARTBEAT_TOPIC))
printfn("Unsubscribed form heartbeat");
else
printfn("Could not unsubscribe from heartbeat");
}
......@@ -10,19 +10,15 @@
#define MQTT_CLIENT_H
#include "Configuration.h"
#include <inttypes.h>
#include <functional>
#include <Arduino.h>
#include <WiFi.h>
#include <sys/time.h>
#include "src/WiFiClientSecure/src/WiFiClientSecure.h"
#include "src/PubSubClient/src/PubSubClient.h"
class MqttClient {
protected:
#if USE_WIFI_SECURE == true
WiFiClientSecure _mqttClient;
#else
WiFiClient _mqttClient;
#endif
PubSubClient _pubSubClient;
public:
MqttClient();
bool connectWifi(const char *ssid, const char *pass);
......@@ -31,5 +27,24 @@ public:
bool isConnectedBroker();
String getID();
bool publish(const char *topic, const char *message);
bool subscribeToHeartbeat();
void loop();
bool getEpoch(time_t & time);
time_t lastNtpSync() { return _lastNtpSync; }
private:
static constexpr char* HEARTBEAT_TOPIC = "datakeeper/heartbeat";
void subscriptionCallback(const char * topic, byte* payload, unsigned int length);
MQTT_CALLBACK_SIGNATURE;
time_t _lastNtpSync;
#if USE_WIFI_SECURE == true
WiFiClientSecure _mqttClient;
#else
WiFiClient _mqttClient;
#endif
PubSubClient _pubSubClient;
};
#endif
#ifndef ABSTRACTSENSOR_H
#define ABSTRACTSENSOR_H
#define ARDUINOJSON_USE_LONG_LONG 1
#include <ArduinoJson.h>
#include "MqttClient.h"
......
......@@ -8,6 +8,7 @@
#ifndef BLOODPRESSURESENSOR_H
#define BLOODPRESSURESENSOR_H
#define ARDUINOJSON_USE_LONG_LONG 1
#include <ArduinoJson.h>
#include "MqttClient.h"
......
mosimpa-monitor (0.0.1-1) UNRELEASED; urgency=medium
mosimpa-monitor (0.0.2) mosimpa; urgency=medium
* Use the NTP server provided by our server.
* Use the MAC address as MQTT client name, thus allowing multiple devices to
......@@ -12,8 +12,13 @@ mosimpa-monitor (0.0.1-1) UNRELEASED; urgency=medium
* Do not store the IP address on the EEPROM, we use either mDNS or DNS
resolutions.
* If the MQTT broker's hostname resolution fails via mDNS try a DNS query.
* Update Doxyfile.
* Sync time against datakeeper's heartbeat (GitLab: #23).
* Make ArduinoJson accept long long to be used as time, part of
https://gitlab.com/mosimpa/documentation/-/issues/107
* Fix printing MQTT client ID.
-- Lisandro Damián Nicanor Pérez Meyer <perezmeyer@gmail.com> Wed, 02 Dec 2020 15:08:46 -0300
-- Lisandro Damián Nicanor Pérez Meyer <perezmeyer@gmail.com> Mon, 28 Dec 2020 20:40:28 -0300
mosimpa-monitor (0.0.1-0) mosimpa; urgency=medium
......
......@@ -8,6 +8,7 @@
#ifndef HEARTRATESENSOR_H
#define HEARTRATESENSOR_H
#define ARDUINOJSON_USE_LONG_LONG 1
#include <ArduinoJson.h>
#include "MqttClient.h"
......
#include <Arduino.h>
#include <WiFi.h>
#include <ESPmDNS.h>
#include "printf.h"
#include "localtime.h"
/**
* @brief LocalTime::LocalTime Use pool.ntp.org by default.
*/
LocalTime::LocalTime()
{
_ntpServer.clear();
}
/**
* @brief LocalTime::LocalTime
* @param ntpServer NTP server to use as default.
*/
LocalTime::LocalTime(const String & ntpServer) : _ntpServer(ntpServer)
{
}
void LocalTime::setNtpServer(const String &ntpServer)
{
_ntpServer = ntpServer;
}
/**
* @brief LocalTime::syncTime Syncs
* @return true if the time could be synced.
*
* This method might block for up to 5 seconds.
*/
bool LocalTime::syncTime()
{
struct tm timeinfo;
if(_ntpServer.length() == 0)
return false;
if(WiFi.status() != WL_CONNECTED)
return false;
printf("Starting NTP connection to %s ", _ntpServer.c_str());
IPAddress serverIp = MDNS.queryHost(_ntpServer);
if(serverIp != INADDR_NONE)
{
printfn("IP %s", serverIp.toString().c_str());
configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, serverIp.toString().c_str());
}
else
{
printfn("IP could not be determined.");
printfn("Trying with a DNS resolution.");
configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, _ntpServer.c_str());
}
return getLocalTime(&timeinfo, 5000);
}
/**
* @brief LocalTime::getEpoch
* @param epoch A variable in which the current epoch will be returned.
* @return true if successfull.
*
* This method might block for 5000ms.
*/
bool LocalTime::getEpoch(time_t & epoch)
{
struct tm timeinfo;
if(_ntpServer.length() == 0)
return false;
if(!getLocalTime(&timeinfo, 5000))
return false;
epoch = mktime(&timeinfo);
return true;
}
#ifndef LOCALTIME_H
#define LOCALTIME_H
#include <Arduino.h>
/**
* @brief The LocalTime class handles an NTP connection to sync the local
* clock.
*/
class LocalTime
{
public:
LocalTime();
LocalTime(const String & ntpServer);
void setNtpServer(const String & ntpServer);
bool syncTime();
bool getEpoch(time_t &epoch);
private:
/// 10 seconds timeout.
static const unsigned long int TIMEOUT_MS = 10000;
static const long GMT_OFFSET_SEC = -10800; // -3 x 60 x 60
static const int DAYLIGHT_OFFSET_SEC = 3600; // o 0
String _ntpServer;
};
#endif // LOCALTIME_H
......@@ -6,10 +6,11 @@
* Pouso Marcelo Alejandro <mapouso86@gmail.com>
*/
#include "MqttClient.h"
#define ARDUINOJSON_USE_LONG_LONG 1
#include <ArduinoJson.h>
#include <Wire.h>
#include "MqttClient.h"
#include "max32664ahub.h"
MAX32664AHub::MAX32664AHub(uint16_t rstn, uint16_t mfio, TwoWire & wirePort):
......
......@@ -10,6 +10,7 @@
#define MAX32664A_HUB_H
#include <inttypes.h>
#include <Arduino.h>
#define ARDUINOJSON_USE_LONG_LONG 1
#include <ArduinoJson.h>
#include "time.h"
#include <Wire.h>
......
......@@ -9,6 +9,7 @@
/**
* \todo We are sending the password in clear.
*/
#define ARDUINOJSON_USE_LONG_LONG 1
#include <ArduinoJson.h>
#include <Wire.h>
#include <Ticker.h>
......@@ -18,7 +19,6 @@
#include "Configuration.h"
#include "ConfigurationWebServer.h"
#include "MqttClient.h"
#include "localtime.h"
#include "max32664ahub.h"
#include "abstractsensor.h"
#include "temperaturesensor.h"
......@@ -43,17 +43,16 @@ MAX32664AHub spo2AndHeartRateHub(SENSOR_HUB_RESTN, SENSOR_HUB_MFIO,
uint32_t mqttMessageId = 0;
uint8_t emptyHubDataCount = 0;
LocalTime localTime;
unsigned long int lastNtpSync;
enum Leds
{ ledAlive = LED_BUILTIN1, ledNetwork = LED_BUILTIN2, ledSensors =
LED_BUILTIN3
{
LED_ALIVE = LED_BUILTIN1,
LED_NETWORK = LED_BUILTIN2,
LED_SENSORS = LED_BUILTIN3
};
/* ------------------------------------------------------------- */
bool mqttClientInitialize(void);
void mqttClientConnectionStatus(void);
bool mqttClientConnectionStatus(void);
void publishInfoDevice(void);
void readSensorHub(void);
......@@ -79,9 +78,6 @@ void setup()
Configuration.begin();
isDeviceConfigured = Configuration.isSetting();
// Use the MQTT server hostname for retrieving the NTP data.
localTime.setNtpServer(Configuration.getMqttHostname());
configureLeds();
waitForSerialCommand();
configureWifi();
......@@ -99,7 +95,8 @@ void setup()
*/
void loop()
{
static unsigned long int start = millis();
static unsigned long int sensorStart = millis();
static unsigned long int timeSyncStart = sensorStart;
if (isDeviceConfigured == false)
{
......@@ -112,22 +109,36 @@ void loop()
}
else
{
// Controla conexion con el broker.. Si no esta conectado reintenta conexión.
mqttClientConnectionStatus();
if(digitalRead(SENSOR_HUB_MFIO) == LOW)
readSensorHub();
if(millis() - lastNtpSync > NTP_SYNC_PERIOD_MS)
// Controla conexion con el broker.. Si no esta conectado reintenta conexión.
if(!mqttClientConnectionStatus())
return;
if((mqttClient.lastNtpSync() == 0) ||
(millis() - timeSyncStart > NTP_SYNC_PERIOD_MS))
{
if(localTime.syncTime())
lastNtpSync = millis();
mqttClient.subscribeToHeartbeat();
timeSyncStart = millis();
}
// Do not send data if we do not have a valid NTP reference.
if((lastNtpSync != 0) &&
((millis() - start > PUBLISH_THRESHOLD_MAX_MS)) ||
(((millis() - start) > PUBLISH_THRESHOLD_MIN_MS) &&
// At this point we are connected to WiFi and the broker.
// Let the MQTT client do it's job.
mqttClient.loop();
// Do not send data if we do not have a valid time reference.
if(mqttClient.lastNtpSync() == 0)
return;
/*
* Send data when:
* - PUBLISH_THRESHOLD_MAX_MS have passed by.
* - PUBLISH_THRESHOLD_MIN_MS and we have a minimum of confidence in
* the HR measurement.
*/
if(((millis() - sensorStart > PUBLISH_THRESHOLD_MAX_MS)) ||
(((millis() - sensorStart) > PUBLISH_THRESHOLD_MIN_MS) &&
(nextReportBioData.confidence >= MIN_DATA_CONFIDENCE) &&
(nextReportBioData.status == 3)))
{
......@@ -137,9 +148,8 @@ void loop()
readSensorTemperature();
publishSensorsData();
start = millis();
sensorStart = millis();
}
}
}
......@@ -161,16 +171,16 @@ void configureLeds()
{
// led alive
pinMode(LED_BUILTIN1, OUTPUT);
ledOn(true, ledAlive);
ledOn(true, LED_ALIVE);
tickerLed.attach(BLINK_LED_PERIOD, blinkLed1);
// led network status
pinMode(LED_BUILTIN2, OUTPUT);
ledOn(false, ledNetwork);
ledOn(false, LED_NETWORK);
// led sensors status
pinMode(LED_BUILTIN3, OUTPUT);
ledOn(false, ledSensors);
ledOn(false, LED_SENSORS);
}
/**
......@@ -190,22 +200,6 @@ void configureWifi()
isDeviceConfigured = mqttClientInitialize();
if (isDeviceConfigured)
{
auto start = millis();
bool ntpSynced = false;
lastNtpSync = 0;
/*
* It will try to sync the time, if it can't it will be tried again
* later on on the main loop.
*/
do
{
ntpSynced = localTime.syncTime();
} while(!ntpSynced && (start - millis() < NTP_TRY_TIMEOUT_MS));
if(ntpSynced)
lastNtpSync = millis();
publishInfoDevice();
tickerPublishDeviceInfo.attach(PERIOD_PUBLISH_INFO_DEVICE_SEC,
publishInfoDevice);
......@@ -306,7 +300,7 @@ void configureSensorHub()
error_count++;
if (error_count >= SENSOR_CONFIG_ERROR_COUNT)
{
ledOn(true, ledSensors);
ledOn(true, LED_SENSORS);
printfn("*** Sensor ERROR!");
break;
}
......@@ -323,9 +317,6 @@ void configureSensorHub()
printfn("\tCoef C: 0x%x, we sent 0x%x", coefficients[2], SENSOR_HUB_COEF_C);
printfn("Init sensors interruptions and process...");
// TODO: test with mfio interrupt instead of ticker
// attach MFIO pin interrupt for sensor hub - read sensor HUB data
// attachInterrupt(digitalPinToInterrupt(SENSOR_HUB_MFIO), readSensorHub, RISING);
}
}
......@@ -368,7 +359,7 @@ void waitForSerialCommand()
bool mqttClientInitialize(void)
{
String id = mqttClient.getID().c_str();
printfn("ID: %s", id);
printfn("ID: %s", id.c_str());
if(!mqttClient.connectWifi(Configuration.getSSID(), Configuration.getPass()))
return false;
......@@ -386,19 +377,27 @@ bool mqttClientInitialize(void)
* @brief mqttClientConnectionStatus Checks the connection against the MQTT
* broker. It will re try the connection if it's not up.
*/
void mqttClientConnectionStatus(void)
bool mqttClientConnectionStatus(void)
{
if (!mqttClient.isConnectedBroker()) {
ledOn(true, ledNetwork);
if (mqttClient.isConnectedWiFi()) {
if (!mqttClient.isConnectedBroker())
{
ledOn(true, LED_NETWORK);
if(mqttClient.isConnectedWiFi())
{
mqttClient.connectBroker(Configuration.getMqttHostname(),
Configuration.getMqttPort());
} else {
return false;
}
else
{
mqttClientInitialize();
return false;
}
} else {
ledOn(false, ledNetwork);
}
ledOn(false, LED_NETWORK);
return true;
}
/**
......@@ -425,19 +424,6 @@ uint16_t battmV()
return static_cast<uint16_t>(-1.6478e-7*read*read + 3.23246e-3*read + 0.337436);
}
bool getEpoch(time_t & epoch)
{
if(!localTime.getEpoch(epoch))
{
printfn("Can't publish data without valid time.");
lastNtpSync = 0;
return false;
}
return true;
}
/**
* @brief publishInfoDevice Publish data to the MQTT broker.
*/
......@@ -447,7 +433,7 @@ void publishInfoDevice(void)
infoJson.clear();
time_t epoch;
if(!getEpoch(epoch))
if(!mqttClient.getEpoch(epoch))
return;
if (mqttClient.isConnectedBroker()) {
......@@ -505,7 +491,7 @@ void loadSpo2AndHeartRateData(bioData sensorData)
HeartRateSensor::Data heartRateData;
time_t epoch;
if(!getEpoch(epoch))
if(!mqttClient.getEpoch(epoch))
return;
oxygenData.time = epoch;
......@@ -525,7 +511,7 @@ void loadTemperatureData(int16_t temperature)
TemperatureSensor::Data temperatureData;
time_t epoch;
if(!getEpoch(epoch))
if(!mqttClient.getEpoch(epoch))
return;
temperatureData.time = epoch;
......