Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!

Menopause Ultimate tracking device - Stay connected

An ultimate low cost device to track symptoms of menopause and keep data in the cloud allowing doctors to have a better patient history.

IntermediateFull instructions provided16 hours692

Things used in this project

Hardware components

ESP32 GREY Development Kit with 9Axis Sensor
M5Stack ESP32 GREY Development Kit with 9Axis Sensor
×1

Software apps and online services

AWS IoT
Amazon Web Services AWS IoT
Flask
grafana
Arduino IDE
Arduino IDE

Story

Read more

Schematics

M5Stack schematics

Code

main.cpp

C/C++
use it under platformio.
#include <M5ez.h>
#include <ezTime.h>
#include "secrets.h"
#include <WiFiClientSecure.h>
#include <MQTTClient.h>
#include <ArduinoJson.h>

const char* wifi_ssid = "SSID";
const char* wifi_password = "password";

// The MQTT topics that this device should publish/subscribe
#define AWS_IOT_PUBLISH_TOPIC   "esp32/pub"
#define AWS_IOT_SUBSCRIBE_TOPIC "esp32/sub"

#define THINGNAME "THINGNAME"

bool aws_conn = false;

WiFiClientSecure net = WiFiClientSecure();
MQTTClient client = MQTTClient(512);

bool startperiod = false;

uint16_t mqtt_loop();

void messageHandler(String &topic, String &payload) {
  Serial.println("incoming: " + topic + " - " + payload);

  StaticJsonDocument<200> doc;
  deserializeJson(doc, payload);
  const char* message = doc["message"];

  Serial.println(message);
  M5.Lcd.setCursor(3, 55);
  M5.Lcd.setTextColor(YELLOW);
  M5.Lcd.setTextSize(2);
  M5.Lcd.println(payload);
}

void setup() {
  #include <themes/default.h>
  #include <themes/dark.h>
  ezt::setDebug(INFO);
	ez.begin();

  if (ez.wifi.indexForSSID(wifi_ssid) == -1) {
    Serial.println("WIFI: not found. Adding.");
    ez.wifi.add(wifi_ssid, wifi_password);
    ez.wifi.writeFlash();
  }
  ez.wifi.begin();

  while ( ! WiFi.isConnected()) { 
    delay(500);
    ez.yield();
  }
  Serial.println("Connected");

  ez.addEvent(mqtt_loop);

  // Configure WiFiClientSecure to use the AWS IoT device credentials
  net.setCACert(AWS_CERT_CA);
  net.setCertificate(AWS_CERT_CRT);
  net.setPrivateKey(AWS_CERT_PRIVATE);

  // Connect to the MQTT broker on the AWS endpoint we defined earlier
  client.begin(AWS_IOT_ENDPOINT, 8883, net);

  // Create a message handler
  client.onMessage(messageHandler);

  Serial.println("Connecting to AWS IOT");

  while (!client.connect(THINGNAME)) {
    Serial.print(".");
    delay(100);
  }

  if(!client.connected()){
    Serial.println("AWS IoT Timeout!");
    return;
  }

  aws_conn = true;

  // Subscribe to a topic
  client.subscribe(AWS_IOT_SUBSCRIBE_TOPIC);

  Serial.println("AWS IoT Connected!");

}
uint8_t setNumberInScreen(){
  uint8_t counter = 1;
  bool refresh = true;
  while (true) {
    String btnpressed = ez.buttons.poll();
    if (btnpressed == "Done") break;
    if (btnpressed == "Up") {
      counter ++;
      if(counter > 10){
        counter = 1;
      }
      refresh = true;
    }else if(btnpressed == "Down"){
      counter --;
      if(counter < 1){
        counter = 10;
      }
      refresh = true;
    }
    if(refresh){
      refresh = false;
      m5.lcd.fillRect (0, ez.canvas.bottom() - 45, TFT_W, 40, ez.theme->background); 
      ez.canvas.pos(20, ez.canvas.bottom() - 45);
      ez.canvas.color(TFT_RED);
      ez.canvas.font(&FreeSansBold18pt7b);
      ez.canvas.print(counter);
      ez.canvas.font(&FreeSans12pt7b);
      ez.canvas.color(TFT_BLACK);
    }
  }
  return counter;
}

bool level_1to10_menu(ezMenu* callingMenu) {
  Serial.print( callingMenu->pickCaption() );
  ez.header.show("Record Level");
  ez.canvas.font(&FreeSans12pt7b);
  ez.canvas.lmargin(20);
  ez.canvas.println("");      
  ez.canvas.println("Record the Level of:");
  ez.canvas.println(callingMenu->pickCaption());
  ez.buttons.show("Up # Down # Done");
  uint8_t value = setNumberInScreen();
  time_t currentTime = now();
  StaticJsonDocument<512> doc;
  doc["userid"] = "user123";
  doc["time"] = currentTime;
  if(!startperiod){
    startperiod = true;
    doc["symptom"]["period"]["startdate"] = currentTime;
  }
  if(callingMenu->pickCaption() == "cramps pain"){
    doc["symptom"]["period"]["crampspain"] = value;   
  }else if(callingMenu->pickCaption() == "day rate"){
    doc["symptom"]["period"]["dayrate"] = value;   
  }else{
    return true;
  }    
  char jsonBuffer[512];
  serializeJson(doc, jsonBuffer);                           // print to client
  client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);
  return false;
}

bool bleeding_Function(ezMenu* callingMenu) {
	Serial.print( callingMenu->pickCaption() );
	if(ez.msgBox("Bleeding Selection", "Confirm Selection: " + callingMenu->pickCaption(), "yes##no") == "yes") {
    time_t currentTime = now();
    StaticJsonDocument<512> doc;
    doc["userid"] = "user123";
    doc["time"] = currentTime;
    if(!startperiod){
      startperiod = true;
      doc["symptom"]["period"]["startdate"] = currentTime;
    }
    doc["symptom"]["period"]["bleeding"] = callingMenu->pickCaption();       // Heavy, Light, Spotting
    char jsonBuffer[512];
    serializeJson(doc, jsonBuffer);                           // print to client
    client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);
    return false;
  }
	return true;
}

void bleeding_menus() {
  ezMenu submenu("Bleeding Type");
  submenu.txtSmall();
  submenu.buttons("up#Back#select##down#");
  submenu.addItem("Heavy", NULL, bleeding_Function);
  submenu.addItem("Light", NULL, bleeding_Function);
  submenu.addItem("Spotting", NULL, bleeding_Function);
  submenu.run();
}

void startdate(){
  if(!startperiod){
    String res = ez.msgBox("Confirm Action", "Report the Start date of Period event?", "Cancel # # Accept"); 
    if(res == "Accept"){
      startperiod = true;
      time_t currentTime = now();
      StaticJsonDocument<512> doc;
      doc["userid"] = "user123";
      doc["time"] = currentTime;
      doc["symptom"]["period"]["startdate"] = currentTime;
      // doc["symptom"]["period"]["enddate"] = 0;
      // doc["symptom"]["period"]["bleeding"] = "heavy";       // heavy, light, spotting
      // doc["symptom"]["period"]["crampspain"] = 7;          // level of cramp pain
      // doc["symptom"]["period"]["dayrate"] = 5;             // the rate of date
      char jsonBuffer[512];
    serializeJson(doc, jsonBuffer);                           // print to client
      client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);

      // Serial.println("Send Start Date Period event");
      Serial.println(jsonBuffer);
      // maybe send data here
    }else{
      // do nothing
    }
  }else{
    ez.msgBox("Error", "You have an open date reported. Please choose end date first!");
  }
}

void enddate(){
  if(startperiod){
    String res = ez.msgBox("Confirm Action", "Report the End date of Period event?", "Cancel # # Accept"); 
    if(res == "Accept"){
      startperiod = false;
      // maybe send data here
      time_t currentTime = now();
      StaticJsonDocument<512> doc;
      doc["userid"] = "user123";
      doc["time"] = currentTime;
      doc["symptom"]["period"]["enddate"] = currentTime;
      char jsonBuffer[512];
      serializeJson(doc, jsonBuffer); // print to client
      client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);
      // Serial.println("Send End Date Period event");
    }else{
      // do nothing
    }
  }else{
    ez.msgBox("Error", "You have not reported the start date of this event!");
  }
}

void period_menus() {
  ezMenu submenu("Record Period Menu");
  submenu.txtSmall();
  submenu.buttons("up#Back#select##down#");
  submenu.addItem("start date", startdate);
  submenu.addItem("end date", enddate);
  submenu.addItem("bleeding", bleeding_menus);
  submenu.addItem("cramps pain", NULL, level_1to10_menu);
  submenu.addItem("day rate", NULL, level_1to10_menu);
  submenu.run();
}

void symptom_menu() {
  ezMenu submenu("Symptoms Menu");
  submenu.txtSmall();
  submenu.buttons("up#Back#select##down#");
  submenu.addItem("Hot flashes");
  submenu.addItem("Period", period_menus);
  submenu.addItem("Mod swings");
  submenu.addItem("Sleep issues");
  submenu.addItem("Exit | Go back to main menu");
  submenu.run();
}

void status_menu(){
  if(aws_conn){
    ez.textBox("Device Status", "Connected to AWS", true);
  }else{
    ez.textBox("Device Status", "Disconnected", true);
  }
}

void profile_menu(){
  String cr = (String)char(13);
  ez.textBox("My Profile", "This info should be set from the cloud data... perhaps stored in Flash", true);
}


uint16_t mqtt_loop(){

  client.loop();
  
  return 50;
}

void loop() {
  ezMenu mainmenu("Menopause App");
  mainmenu.txtSmall();
  mainmenu.addItem("Report Symptom", symptom_menu);
  mainmenu.addItem("View Profile", profile_menu);
  mainmenu.addItem("Status", status_menu);
  mainmenu.upOnFirst("last|up");
  mainmenu.downOnLast("first|down");
  mainmenu.run();
}

Credits

Manuel Alejandro Iglesias Abbatemarco

Manuel Alejandro Iglesias Abbatemarco

17 projects • 77 followers
Embedded Firmware Engineer

Comments