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!
Virgilio Enrique Aray Arteaga
Published © GPL3+

Wise Shower Driven by Alexa Skill

This Alexa-controlled device aims to save water by planning the time and amount of water to use in each shower.

AdvancedWork in progress12 hours5,259
Wise Shower Driven by Alexa Skill

Things used in this project

Hardware components

Echo Dot
Amazon Alexa Echo Dot
×1
Arduino Mega 2560
Arduino Mega 2560
×1
ESP8266 ESP-01
Espressif ESP8266 ESP-01
×1
2.8 TFT Touch Shield for Arduino with Resistive Touch Screen ID
×1
YF-S201 Hall Effect Water Flow Meter / Sensor
×1
DS18B20 Programmable Resolution 1-Wire Digital Thermometer
Maxim Integrated DS18B20 Programmable Resolution 1-Wire Digital Thermometer
×1
Plastic Water Solenoid Valve – 12V – 1/2″ NominaL
×1
2 Relay Module, 5V, 10A,
×1
Solderless Breadboard Half Size
Solderless Breadboard Half Size
×1
YwRobot Breadboard Power Supply
×1

Software apps and online services

AWS Lambda
Amazon Web Services AWS Lambda
Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
AWS IAM
Amazon Web Services AWS IAM
AWS IoT
Amazon Web Services AWS IoT
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Schematics

ALEXA WISE SHOWER

Schematic

Schematic Fritzing.org

Schematic Fritzing.org

Code

ESP8266 CODE

Arduino
MQTT, WEBSOCKET, you have to flash your esp8266 with this code
/*
Prototipo de Alexa Wise Shower
Code by Virgilio Aray Arteaga
February 2018
This skecht is an adaptation of the original code that can be found at the following address
https://github.com/odelot/aws-mqtt-websockets
*/
//-----------------------------------------------
#include <Arduino.h>
#include <Stream.h>

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

//AWS
#include "sha256.h"
#include "Utils.h"

//WEBSockets
#include <Hash.h>
#include <WebSocketsClient.h>

//MQTT PAHO
#include <SPI.h>
#include <IPStack.h>
#include <Countdown.h>
#include <MQTTClient.h>

//AWS MQTT Websocket
#include "Client.h"
#include "AWSWebSocketClient.h"
#include "CircularByteBuffer.h"

//AWS IOT config, change these:
char wifi_ssid[]       = "your network";
char wifi_password[]   = "your wifi password";
char aws_endpoint[]    = "?????????.iot.us-east-1.amazonaws.com";
char aws_key[]         = "???????????";    // your IAM credentials
char aws_secret[]      = "??????????????????";   // your IAM credentials
char aws_region[]      = "us-east-1";
char* aws_topic        = "$aws/things/ducha/shadow/update";
int port = 443;

//MQTT config
//---------------------------------------//
const int maxMQTTpackageSize = 1024;     //  VERY IMPORTANT
//---------------------------------------//
const int maxMQTTMessageHandlers = 4;    //  VERY IMPORTANT  
//---------------------------------------//

ESP8266WiFiMulti WiFiMulti;

AWSWebSocketClient awsWSclient(1000);

IPStack ipstack(awsWSclient);
MQTT::Client<IPStack, Countdown, maxMQTTpackageSize, maxMQTTMessageHandlers> *client = NULL;

//# of connections
long connection = 0;
//generate random mqtt clientID
char* generateClientID () {
  char* cID = new char[23]();
  for (int i=0; i<22; i+=1)
    cID[i]=(char)random(1, 256);
  return cID;
}
//count messages arrived
int arrivedcount = 0;
char buf[500];
int ParImpar=1;
int OnOff=1;
String Respuesta ;
//
// Here the response of the AWS IOT
// 
//callback to handle mqtt messages
//
//
void messageArrived(MQTT::MessageData& md)
{
  MQTT::Message &message = md.message;
/*
  //Serial.print("Message ");
  //Serial.print(++arrivedcount);
  //Serial.print(" arrived: qos ");
  //Serial.print(message.qos);
  //Serial.print(", retained ");
  //Serial.print(message.retained);
  //Serial.print(", dup ");
  //Serial.print(message.dup);
  //Serial.print(", packetid ");
  //Serial.println(message.id);
  //Serial.print("Payload ");
*/  
  char* msg = new char[message.payloadlen+1]();
  memcpy (msg,message.payload,message.payloadlen);
//  //Serial.println(msg);
  Respuesta=msg;  // "REspuesta" is a global var will be contain the paiload from aws iot
  delete msg;
}
//
//
//

//connects to websocket layer and mqtt layer
bool connect () {

    if (client == NULL) {
      client = new MQTT::Client<IPStack, Countdown, maxMQTTpackageSize, maxMQTTMessageHandlers>(ipstack);
    } else {

      if (client->isConnected ()) {    
        client->disconnect ();
      }  
      delete client;
      client = new MQTT::Client<IPStack, Countdown, maxMQTTpackageSize, maxMQTTMessageHandlers>(ipstack);
    }


    //delay is not necessary... it just help us to get a "trustful" heap space value
    delay (1000);
    //Serial.print (millis ());
    //Serial.print (" - conn: ");
    //Serial.print (++connection);
    //Serial.print (" - (");
    //Serial.print (ESP.getFreeHeap ());
    //Serial.println (")");

   int rc = ipstack.connect(aws_endpoint, port);
    if (rc != 1)
    {
      //Serial.println("error connection to the websocket server");
      return false;
    } else {
      //Serial.println("websocket layer connected");
    }
    //Serial.println("MQTT connecting");
    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
    data.MQTTVersion = 3;
    char* clientID = generateClientID ();
    data.clientID.cstring = clientID;
    rc = client->connect(data);
    delete[] clientID;
    if (rc != 0)
    {
      //Serial.print("error connection to MQTT server");
      //Serial.println(rc);
      return false;
    }
    //Serial.println("MQTT connected");
    return true;
}

//subscribe to a mqtt topic
void subscribe () {
   //subscript to a topic
    int rc = client->subscribe(aws_topic, MQTT::QOS0, messageArrived);
    if (rc != 0) {
      //Serial.print("rc from MQTT subscribe is ");
      //Serial.println(rc);
      return;
    }
    //Serial.println("MQTT subscribed");
}

//send a message to a mqtt topic
void sendmessage () {
    //send a message
    MQTT::Message message;
    //char buf[100];
    //strcpy(buf, "{\"state\":{\"reported\":{\"on\": false}, \"desired\":{\"on\": false}}}");
    //strcpy(buf, "{\"state\":{\"reported\":{\"interruptor\":\"on\"}}}");
    message.qos = MQTT::QOS0;
    message.retained = false;
    message.dup = false;
    message.payload = (void*)buf;
    message.payloadlen = strlen(buf)+1;
    int rc = client->publish(aws_topic, message); 
    //Serial.println(rc);
}


void setup() {
    //
    //  Serial To comunicate with arduino mega
    //
    Serial.begin (19200,SERIAL_8N1);
    ////delay (20000);
    ////Serial.setDebugOutput(1);

    //fill with ssid and wifi password
    WiFiMulti.addAP(wifi_ssid, wifi_password);
    //Serial.println ("connecting to wifi");
    while(WiFiMulti.run() != WL_CONNECTED) {
        delay(100);
        //Serial.print (".");
    }
    //Serial.println ("\nconnected");

    //fill AWS parameters    
    awsWSclient.setAWSRegion(aws_region);
    awsWSclient.setAWSDomain(aws_endpoint);
    awsWSclient.setAWSKeyID(aws_key);
    awsWSclient.setAWSSecretKey(aws_secret);
    awsWSclient.setUseSSL(true);

    Respuesta = "nothing" ;// this global var will be contain the paiload from aws iot
    
    //Serial.println ("marca 1");

    if (connect ()){
          //Serial.println ("marca 2");

      aws_topic  = "$aws/things/ducha/shadow/get/accepted";
      subscribe ();
      aws_topic  = "$aws/things/ducha/shadow/update";
      subscribe ();
    }
    //Serial.println ("marca 3");

}

void loop() {
  //
  // the "esp8266" will be consulted by mqtt the status of the "thing", 
  // when the "delta" section appears it means that there is an instruction 
  // in the data that will be sent to the MEGA
  //
  //
  //keep the mqtt up and running
  if (awsWSclient.connected ()) {    
      client->yield();
  } else {
    //handle reconnection
    if (connect ()){
      aws_topic  = "$aws/things/ducha/shadow/get/accepted";
      subscribe ();
      aws_topic  = "$aws/things/ducha/shadow/update";
      subscribe ();
    }
  }
  //
  //  THIS IS FOR QUERY AND SET THE THINGS SHADOW  
  //
  aws_topic  = "$aws/things/ducha/shadow/get";
  strcpy(buf, "{}");  // dont need sent any parameter  or payload
  sendmessage ();
  //
  // wait for anwser, now its posible that variable "Respuesta" got the paidload
  //
  String valorcomando   = "nochange";
  String valorminutos   = "5" ; 
  String valorlitros    = "5" ; 
  String valorcalentador= "off" ; 
  String sPaidLoad      = "{}";
  String valortap       = "off";
  //
  if (Respuesta.indexOf("delta")>0) {    // got a valid message from aws iot
                                         // when "delta" is present meaning have to change "reported"
      valorcomando=RetornaValor("desired","ducha");
      //Serial.println(valorcomando);
      if (valorcomando=="\"setup\"") {
        valorminutos   =RetornaValor("desired","time"); 
        valorlitros    =RetornaValor("desired","water"); 
        valorcalentador=RetornaValor("desired","heater"); 
        // Send parameters to ARDUINO MEGA
        Serial.println(valorcomando+","+valorminutos+","+valorlitros+","+valorcalentador+",off");
        // Update reported value
        sPaidLoad="{\"state\":{\"reported\":{\"ducha\":"+valorcomando+",\"time\":"+valorminutos+",\"water\":"+valorlitros+",\"heater\":"+valorcalentador+"}}}";
      }
      if (valorcomando=="\"runing\"") {  // open o close the water
        valortap       =RetornaValor("desired","tap");
        valorcalentador=RetornaValor("desired","heater"); 
        Serial.println(valorcomando+",0,0,"+valorcalentador+","+valortap);
        sPaidLoad="{\"state\":{\"reported\":{\"ducha\":"+valorcomando+",\"heater\":"+valorcalentador+",\"tap\":"+valortap+"}}}";
      }
      if (valorcomando=="\"finished\"") {
        valorminutos   =RetornaValor("desired","time"); 
        valorlitros    =RetornaValor("desired","water"); 
        valorcalentador=RetornaValor("desired","heater"); 
        valortap       =RetornaValor("desired","tap"); 
        Serial.println(valorcomando+","+valorminutos+","+valorlitros+","+"off"+","+"off");
        // Update reported value
        sPaidLoad="{\"state\":{\"reported\":{\"ducha\":"+valorcomando+",\"time\":"+valorminutos+",\"water\":"+valorlitros+",\"heater\":\"off\",\"tap\":\"off\"}}}";
      }
      if (sPaidLoad!="{}") {
        const char *buf1 = sPaidLoad.c_str();                   
        strcpy(buf,buf1);
        //Serial.println(buf);
        aws_topic  = "$aws/things/ducha/shadow/update";
        sendmessage ();  // send message to update de "reported" value
      }
  }
  //
  delay(500);
  //
 }
//
//----------
//
 String RetornaValor(String seccion, String dato) {
  /* 
   * "Respuesta" is a global variable that has the "Payload" sent by AWS in the "topic"
   * format of the response from aws:
   * Payload {"state":{"desired":{"ducha":"setup","time":"10",},"reported":{"time":"15"},"delta":{"time":"10"}}}
   * It has three sections (seccion): "desired", "reported", "delta"
   * Solo me interesa obtener el valor de un dato de una seccion en especial
   * "section" can be "desired" or "reported" or "delta"
   * "dato" can be "ducha", "time", "water", "heater", "tap" or "switch" or anything
   * 
   */
  String StrRetorno ;
  int iIndex1=0;
  int iIndex2=0;
  int iIndex3=0;
  iIndex1=Respuesta.indexOf(seccion);           // posicion  de "desired" en payload
  iIndex2=Respuesta.indexOf("}",iIndex1);       // posicion del primer "}" despues de la seccion
  StrRetorno=Respuesta.substring(iIndex1,iIndex2);  // la seccion que me intersa ej -> {"desired":{"light":"on","interruptor":"on"}
  iIndex1=StrRetorno.indexOf(dato);             // posicion del dato que me intersa
  iIndex2=StrRetorno.indexOf(",",iIndex1);
  iIndex3=StrRetorno.indexOf("}",iIndex1);
  if (iIndex2==-1 && iIndex2>iIndex3){
     StrRetorno=StrRetorno.substring(iIndex1,iIndex3);     //  el dato que me intersa ej -> "interruptor":"on"
  }
  else
  {
     StrRetorno=StrRetorno.substring(iIndex1,iIndex2);     //  el dato que me intersa ej -> "interruptor":"on"
  }
  StrRetorno.replace(dato+"\":"," ");  // elimino [interruptor":] me quedo solo con el valor ->  "on"
  StrRetorno.trim();             // elimino espacios en blanco, me quedo solo con el valor -> "on" 
  return StrRetorno;  // retorna algo como "on" o "off" o "12" o "30"
 }

ARDUINO MEGA CODE

Arduino
You have to load this code in your Arduino Mega
/*
Prototipo de Alexa Wise Shower
Code by Virgilio Aray Arteaga
February 2018

This sketch reads the instructions it receives through serial communication from the ESP8266
but also receives instructions from the touch screen.

Manage the devices that make up the WiseShower:

- electric water heater by relay
- Water valve by relay
- water flow meter
- temperature sensor
*/
//-----------------------------------------------
//Required libraries
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Time.h>
//-----------------------------------------------
//Assignment of digital pins
#define FLUJO_AGUA      20 // Water flow Sensor YF-S201 
#define VALVULA1        37 // Relay SOLENOID WATER VALVE (12 V) (Relay)
#define CALENTADOR      47 // Relay Water Heater Rele           
#define SEN_TEMPERATURA 31 // TEMPERATURE SENSOR DS18B20
//------------------------------------------------  
// Touch Screen stuff
#include <Elegoo_GFX.h>    // Core graphics library
#include <Elegoo_TFTLCD.h> // Hardware-specific library
#include <TouchScreen.h>
//
#define LCD_CS A3 
#define LCD_CD A2 
#define LCD_WR A1 
#define LCD_RD A0 
#define LCD_RESET A4 
//
#define TS_MINX 115 // 204
#define TS_MINY 115 // 195
#define TS_MAXX 950 // 948
#define TS_MAXY 920 // 910
//
#define YP A2  // must be an analog pin, use "An" notation!
#define XM A3  // must be an analog pin, use "An" notation!
#define YM 8   // can be a digital pin
#define XP 9   // can be a digital pin
//
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF
//
Elegoo_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 700);
//  End of touch screen stuff
//---------------------------------------------------
//To measure water flow with sensor YF-S201
volatile long NumPulsos;
void ContarPulsos()
{
  NumPulsos++;
}
//
// Instancia a las clases OneWire y DallasTemperature
OneWire oneWireObjeto(SEN_TEMPERATURA);
DallasTemperature sensorDS18B20(&oneWireObjeto);
//----------------------------------------------------
// Global variables
int SegAnt  = 0;
int NumMenu = 1;  // Menu Level
//
int Minutos = 10; // Default time
int Litros  = 20; // Default liters of water
int Grados  = 40; // Default tempo 
int Calentador = 2; // 0 water heater off, 1 water heater on, 2 water header automatic
String HeaterMode = "OFF";
String FechaAct = "";
String HoraAct  = "";
String sMensaje = "          " ;
//  
//
int RespuestaDucha = 0;
float SegundoInicio= 0.00;
float LitrosUsados = 0.00;
float MinutosUsados = 0.00;
char ComandoDucha = '*';
char ComandoDuchaAnt = '*';
float TempActual=0.00;
const float TempHeaterOff=40.00;
const float TempHeaterOn=35.00;
//----------------------------------------------------
void setup() {
  //
  // -Setup pins
  //
  pinMode(SEN_TEMPERATURA,INPUT); // Temperature sensor
  pinMode(FLUJO_AGUA,INPUT);      // flujo de agua
  pinMode(VALVULA1,OUTPUT);       // relay valvula 1
  pinMode(CALENTADOR,OUTPUT);     // realy heater
  //
  //
  digitalWrite(VALVULA1,HIGH); 
  digitalWrite(CALENTADOR,HIGH);    
  //
  // Initialize Serial Port "1" to communicate with ESP8266
  //
  Serial.begin(19200);   // Comm with IDE
  Serial1.begin(19200);  // Comm with ESP8266
  //
  // InicializE TFT Screen
  //
  tft.reset();
  tft.begin(0x9341);  // direccion
  tft.setRotation(3);   // valores 0 1 2 3
  // Welcome Message on tft
  // ---
  PantallaInicio();  // Welcome Screen
  //
  //  Se requiere la fecha y hora Consulta cada segundo
  //
  // Temporal
  int anio = 1970;
  int mes = 01;
  int dia = 01;
  int hora = 00;
  int minuto = 00;
  int segundo = 00;
  setTime(hora,minuto,segundo,dia,mes,anio);
  //
  //--------------------------------
  // bus 1-Wire  temperature sensor
  sensorDS18B20.begin(); // Temperature  
  //--------------------------------
  // water flow control
  attachInterrupt(digitalPinToInterrupt(FLUJO_AGUA),ContarPulsos,RISING);  // flow meter
  interrupts();
  //-----------------------------------
  //
  //
}

void loop(){


  if (Serial1.available()) {
     //
     // Read info from ESP8266 device
     //
     pinMode(XM, OUTPUT);  // Very Important because
     pinMode(YP, OUTPUT);  // Touch screen change this pins
     //     
     sMensaje = "";
     while (Serial1.available()) {
        sMensaje += (char)Serial1.read();
        //Serial.print((char)Serial1.read());
     }
     Serial.println(sMensaje);
     DivideComando(sMensaje);
     /*FechaAct = sMensaje.substring(0,8);
     HoraAct  = sMensaje.substring(8,14);
     Minutos = sMensaje.substring(14,16).toInt();
     Litros  = sMensaje.substring(16,18).toInt();
     Grados  = sMensaje.substring(18,20).toInt();
     //buttonEnabled = false;
     */
     pinMode(XM, OUTPUT);
     pinMode(YP, OUTPUT);
     //EmpezarDucha();
     //NumMenu=3;
     //
     //
     //
  }  
  //
  // Check if the screen was touched
  //
  TSPoint p = ts.getPoint();  //Get touch point
  //
  //
  if (p.z > ts.pressureThreshhold) {   // User touch the screen
   //
   p.x = map(p.x, TS_MAXX, TS_MINX, 0, 320);  // x coordinate 
   p.y = map(p.y, TS_MAXY, TS_MINY, 0, 240);  // y coordinate
   //This is important, because the libraries are sharing pins
   pinMode(XM, OUTPUT);
   pinMode(YP, OUTPUT);
   //
   //  based on the menu each area of the screen has a function
   //
   switch (NumMenu) {
   case 1:  // firt level menu --> Welcome Screen
     //
     // 
     //
     if(p.x>60 && p.x<260 && p.y>180 && p.y<220)// The user has pressed inside the red rectangle
     {
       PantallaParametros(0); // show parameters
       NumMenu=2;             // go to next level menu
     }
     break;
   case 2: // Second level menu --> change parameters 
     //
     //  Parameters screen
     //  user can change parameter or confirm then
     //
     if (p.x>60 && p.x<260 && p.y>190 && p.y<230)  // user has pressed inside the WHITE rectangle
     {
       EmpezarDucha();    // Open Tap
       NumMenu=3;         // Menu Level 3
     }
     //
     //  Use can touch te up / down arrows to change parameters
     //
     else {
        if (p.x>14 && p.x<3140 && p.y>13 && p.y<190){
           ActualizaParametros(p.x,p.y);
           PantallaParametros(1);
        }
     }     
     break;
   case 3:  // Water is runing  (tap open)
      // 
      //
      // When water is runing
      //
      // Touch Red zone to stop shower and return to level 1 menu
      //
      if (p.x>210 && p.x<210+95 && p.y>95 && p.y<110+110){
         CloseWaterTap();
         PantallaInicio();
         NumMenu=1;         // Menu Level 1         
         break;
      }
      //
      //  touch Yellow zone to hold (close tap)
      //
      if (p.x>110 && p.x<110+95 && p.y>95 && p.y<110+110){
         CloseWaterTap();
         break;
      }
      //
      // Touch Green zone to reopen water tap
      //
      if (p.x>10 && p.x<10+95 && p.y>95 && p.y<110+110){
         OpenWaterTap();
         break;
      }        
      //
      //
      //
      break;
   }
  }
  //
  if (NumMenu==3) {
     ShowStatus() ; // shows the time and liters used at the moment
  }
  //
  delay(100); 
   
}
//
// ----------------------------------------------------------
//
void PantallaInicio()  // Welcome Screen
{
  tft.reset();          // reset screen 
  tft.begin(0x9341);    // direccion of device
  tft.setRotation(3);   // valores 0 1 2 3  
  tft.fillScreen(BLACK);
  //
  tft.drawRect(0,0,319,239,WHITE);  //Draw white frame
  tft.drawRect(2,2,315,235,BLUE);  
  tft.drawRect(4,4,310,230,RED);  
  //
  tft.setCursor(30,30);
  tft.setTextColor(WHITE);
  tft.setTextSize(2);
  tft.print("Aray Innovations LLC");
  tft.setCursor(30,50);
  tft.setTextSize(1);
  tft.print("Virgilio Enrique Aray Arteaga");
  tft.setCursor(30,70);
  tft.setTextSize(0);
  tft.print("February 2018");  
  //
  tft.setCursor(20,100);
  tft.setTextColor(RED);
  tft.setTextSize(4);
  tft.print("Wise Shower");
  tft.setCursor(20,140);
  tft.print("Alexa driven");
  //
  //  Zone to touch to start
  //
  tft.fillRect(60,180, 200, 40, RED);
  tft.drawRect(60,180,200,40,WHITE);
  tft.setCursor(88,190);
  tft.setTextColor(WHITE);
  tft.setTextSize(2);
  tft.print("<<<<START>>>>");
  //
  NumMenu=1;
  //
}
//
// ----------------------------------------------------------
//
void EmpezarDucha() {   // Screen to show minutes and liters used
     NumMenu==3;
     pinMode(XM, OUTPUT);
     pinMode(YP, OUTPUT);
     tft.fillScreen(BLUE);  

     tft.drawRect(1,10,318,32,WHITE);
     tft.drawRect(1,41,318,33,WHITE);
     tft.drawRect(1,73,318,33,WHITE);
     
     tft.drawRect(1,10,106,96,WHITE);
     tft.drawRect(1,10,212,96,WHITE);

      
     tft.setTextColor(WHITE);
     tft.setTextSize(2);
    
     tft.setCursor(4,14);
     tft.print("Minutes");
     tft.setCursor(110,14);
     tft.print("Liters");
     tft.setCursor(216,14);
     tft.print("Heater");
     tft.setCursor(34,50);
     tft.print(Minutos);
     tft.setCursor(140,50);
     tft.print(Litros);
     tft.setCursor(230,50);
     HeaterMode = "OFF";
     if (Calentador==1) HeaterMode = "ON";
     if (Calentador==2) HeaterMode = "AUTO";
     tft.print(HeaterMode);
     tft.fillRect(10,110,95,110,GREEN);
     tft.fillRect(110,110,95,110,YELLOW);
     tft.fillRect(210,110,95,110,RED);
     //
     //  Reset values
     //
     NumPulsos     = 0.0; 
     LitrosUsados  = 0.00;
     MinutosUsados = 0.00;
     TempActual    = 0.00;  
     SegundoInicio = millis() ;  // Point of start to count time
     //
     //
     //
     OpenWaterTap();  // Open the water tap and heater if is necesary
     //
     //
     //
}
//
//
//To show the number of liters and time used
void ShowStatus()
{
   int Min = 0;
   int Seg = 0;
   float SegundoActual = millis();
   float Calculo = 0.00;
   MinutosUsados = ((millis()-SegundoInicio)/1000)/60;
   Min=MinutosUsados;
   Calculo=(MinutosUsados-Min)*100;
   Seg=Calculo*60/100;
   LitrosUsados = (float)NumPulsos/450;
   sensorDS18B20.requestTemperatures();
   //TempActual = sensorDS18B20.getTempFByIndex(0);     //FAHRENHEIT
   TempActual = sensorDS18B20.getTempCByIndex(0);       //CELSIUS
   if (Seg!=SegAnt) {
     pinMode(XM, OUTPUT);
     pinMode(YP, OUTPUT);
     tft.fillRect(34,85,60,20,BLUE);
     tft.fillRect(140,85,60,20,BLUE);
     tft.fillRect(246,85,60,20,BLUE);
     tft.setTextColor(WHITE);
     tft.setTextSize(2);
     tft.setCursor(34,85);
     tft.print(Min);
     tft.print(":");
     tft.print(Seg);
     tft.setCursor(140,85);
     tft.print(LitrosUsados);
     tft.setCursor(246,85);
     tft.print(TempActual);
     SegAnt = Seg;
   }
   
   if (Calentador==2) CheckTemp(); // Heater mode Automatic
   if (MinutosUsados >= Minutos) {
      CloseWaterTap();
      PantallaInicio();
   }
   if (LitrosUsados >= Litros) {
      CloseWaterTap();
      PantallaInicio();
   }
}
//
//
//
void MensajeFB(String sMensaje) {   // To show message in the bottom of touch screen
     tft.fillRect(1,221,318,19,BLUE); 
     tft.drawRect(1,221,318,19,WHITE);
     tft.setTextColor(WHITE);
     tft.setTextSize(2);
     tft.setCursor(4,223);
     tft.print(sMensaje);
}
//
//
//
void ActualizaParametros(int x, int y){
    if (x>14  && x<104 && y>13  && y<61)  Minutos++;
    if (x>114 && x<214 && y>13  && y<61)  Litros++;
    if (x>214 && x<314 && y>13  && y<61)  Calentador++;
    if (x>14  && x<104 && y>113 && y<190) Minutos--;
    if (x>114 && x<214 && y>113 && y<190) Litros--;
    if (x>214 && x<314 && y>113 && y<190) Calentador--;
    if (Minutos<1) Minutos=1;
    if (Litros<1) Litros=1;
    if (Calentador<0) Calentador = 2;
    if (Minutos>30) Minutos=30;
    if (Litros>30) Litros=30;
    if (Calentador>2) Calentador = 0;
}

void PantallaParametros(int Flag) {

    if (Flag==0) { 
      //Erase the screen
      tft.fillScreen(BLUE);
      
      //Draw frame
      //tft.fillRect(0,0,320,240,BLUE);
      tft.setCursor(50,50);
      tft.setTextColor(WHITE);
      tft.setTextSize(2);
  
      tft.drawRect(0,0,160,120,YELLOW);    
      tft.drawRect(160,120,160,120,YELLOW);    
      //
      // Parametros
      //
      BotonUpoDn((14+45),(61-48),"+");
      BotonUpoDn((14+45),(61+47+75),"-");
      BotonUpoDn((114+45),(61-48),"+");
      BotonUpoDn((114+45),(61+47+75),"-");
      BotonUpoDn((214+45),(61-48),"+");
      BotonUpoDn((214+45),(61+47+75),"-");
      BotonStart(60,190,"Open the Tap");
    }
    
    CajaIndicador(13,61,"Minutes",Minutos,Flag);
    CajaIndicador(114,61,"Liters",Litros,Flag);
    CajaIndicador(214,61,"Heater",Grados,Flag);
      
}

void BotonStart(int x, int y, String mensaje){
  tft.fillRect(x, y,200,40, WHITE);
  tft.drawRect(x, y,200,40,RED);
  tft.setCursor(x+22,y+10);
  tft.setTextColor(RED);
  tft.setTextSize(2);
  tft.print(mensaje);  
}  

void CajaIndicador (int x, int y, String mensaje, int valor, int Flag){
    if (Flag==0){
    tft.fillRect(x,y,90,75,RED);
    tft.drawRect(x,y,90,75,WHITE);
    tft.setCursor(x+5,y+5);
    tft.setTextColor(WHITE);
    tft.setTextSize(2);
    tft.print(mensaje);    

    }
    else
    {
    tft.fillRect(x+2,y+30,85,35,RED);
    }
    tft.setTextSize(5);
    tft.setCursor(x+30,y+30);
    tft.setTextColor(WHITE);
    if (mensaje!="Heater") {
       tft.print(valor);     
    } else {
       HeaterMode = "OFF";
       if (Calentador==1) HeaterMode = "ON";
       if (Calentador==2) HeaterMode = "AUT";
       tft.setCursor(x+2,y+30);
       tft.print(HeaterMode); 
    }
}


void BotonUpoDn(int x, int y, String uod) {
    int numero = 45;
    if (uod=="+") 
    {
      numero = 45;
    }
    else
    { 
      numero= -45;
    }
    tft.fillTriangle(x,y,(x-numero),(y+numero),(x+numero),(y+numero),WHITE);
    tft.drawTriangle(x,y,(x-numero),(y+numero),(x+numero),(y+numero),RED);
    if (uod=="+")
    {
       tft.setCursor(x-7,y+15);
    }
    else
    {
       tft.setCursor(x-7,y-30);
    }
    tft.setTextColor(RED);
    tft.setTextSize(3);
    tft.print(uod);    
}
//
//
//
void CloseWaterTap(){
     MensajeFB("Green to open, Red finished");         
     TurnOffValve();
     TurnOffHeater();
}
//
//
//
void OpenWaterTap(){
     MensajeFB("Yellow to hold, Red finished");         
     TurnOnValve();
     if (Calentador==1) TurnOnHeater();  // user request heater on
     if (Calentador==2) CheckTemp();    // Heater mode Automatic
}
//
//
//
void TurnOnValve()
{
  digitalWrite(VALVULA1,LOW); 
}
//
//
//
void TurnOffValve()
{
  digitalWrite(VALVULA1,HIGH); 
  digitalWrite(CALENTADOR,HIGH); 
}
//
//
//
void TurnOnHeater()
{
  digitalWrite(CALENTADOR,LOW); 
}
//
//
//
void TurnOffHeater()
{
  digitalWrite(CALENTADOR,HIGH); 
}
//
//
//
void CheckTemp() {
   //Heater mode Automatic
   if (TempActual>=TempHeaterOff) TurnOffHeater(); // 
   if (TempActual<=TempHeaterOn)  TurnOnHeater(); // 
   //
}
//
//----------
//
void DivideComando(String StrDato) {
  /* 
   */
  int iIndex1 = 0;
  StrDato.replace("\"","");                   // the quotes are taken out
  
  iIndex1=StrDato.indexOf(",");    
  String StrCmd=StrDato.substring(0,iIndex1); // Command 
  StrDato=StrDato.substring(iIndex1+1);         // 
  
  iIndex1=StrDato.indexOf(",");   
  String StrMin=StrDato.substring(0,iIndex1); // Minutes
  StrDato=StrDato.substring(iIndex1+1);         // 
  
  iIndex1=StrDato.indexOf(",");   
  String StrLit=StrDato.substring(0,iIndex1); // Liters 
  StrDato=StrDato.substring(iIndex1+1);         // 
  
  iIndex1=StrDato.indexOf(",");   
  String StrHea=StrDato.substring(0,iIndex1); // Heater
  StrDato=StrDato.substring(iIndex1+1);         // 
  
  iIndex1=StrDato.indexOf(",");   
  String StrTap=StrDato.substring(0,iIndex1); // Tap
  //
  StrCmd.trim();
  StrMin.trim();
  StrLit.trim();
  StrHea.trim();
  StrTap.trim();
  //
  /*Serial.print(StrCmd);
  Serial.print(StrMin);
  Serial.print(StrLit);
  Serial.print(StrHea);
  Serial.print(StrTap);*/
  if (StrCmd=="setup") {
    Minutos    = StrMin.toInt();
    Litros     = StrLit.toInt();
    if (StrHea =="off")  Calentador = 0;
    if (StrHea =="on")   Calentador = 1;
    if (StrHea =="auto") Calentador = 2;
    int DrawFlag = (NumMenu - 1);
    PantallaParametros(DrawFlag);                // show parameters   
    NumMenu=2;
  }
  //
  if (StrCmd=="runing") {
    Serial.println("Tap "+StrTap);
    Serial.print("NumMenu ");
    Serial.println(NumMenu);
    if (StrHea =="on")   Calentador = 1;
    if (StrHea =="off")  Calentador = 0;
    if (StrTap =="on")   
    {
       Serial.print("NumMenu ");
       Serial.println(NumMenu);      
       if (NumMenu<3) {
          EmpezarDucha();
       } 
       else
       {
          OpenWaterTap();
       }
       NumMenu=3;
    }
    if (StrTap =="off") 
    {
       if (NumMenu<3) EmpezarDucha();
       CloseWaterTap();
       NumMenu=3;
    }
  }
  if (StrCmd=="finished") 
  {
    CloseWaterTap();
    PantallaInicio();    
    NumMenu=1;    
  }
  
}

Alexa Skill Kit code

JSON
{
  "languageModel": {
    "types": [
      {
        "name": "heater",
        "values": [
          {
            "id": null,
            "name": {
              "value": "off",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "auto",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "on",
              "synonyms": []
            }
          }
        ]
      }
    ],
    "intents": [
      {
        "name": "AMAZON.CancelIntent",
        "samples": []
      },
      {
        "name": "AMAZON.HelpIntent",
        "samples": []
      },
      {
        "name": "AMAZON.StopIntent",
        "samples": []
      },
      {
        "name": "closethetap",
        "samples": [
          "close the tap"
        ],
        "slots": []
      },
      {
        "name": "finishshower",
        "samples": [
          "I have finished",
          "I have finished tanks",
          "i finished showering"
        ],
        "slots": []
      },
      {
        "name": "openthetap",
        "samples": [
          "Open the tap"
        ],
        "slots": []
      },
      {
        "name": "setupshower",
        "samples": [
          "I'm going to take a shower of {timeinminutes} minutes {waterinliters} liters with the heater {HeaterMode}",
          "i need a shower",
          "I want a shower",
          "shower",
          "{timeinminutes} minutes shower",
          "i want take a shower",
          "I want a shower of {timeinminutes} minutes {waterinliters} liters with the heater {HeaterMode}"
        ],
        "slots": [
          {
            "name": "timeinminutes",
            "type": "AMAZON.NUMBER",
            "samples": [
              "{timeinminutes} minutes",
              "{timeinminutes}",
              "about {timeinminutes}"
            ]
          },
          {
            "name": "waterinliters",
            "type": "AMAZON.NUMBER",
            "samples": [
              "{timeinminutes} minutes {waterinliters} liters heater {HeaterMode}",
              "{waterinliters} liters"
            ]
          },
          {
            "name": "HeaterMode",
            "type": "heater",
            "samples": [
              "{HeaterMode}"
            ]
          }
        ]
      },
      {
        "name": "turnoffheater",
        "samples": [
          "Turn off the heater",
          "heater off"
        ],
        "slots": []
      },
      {
        "name": "turnonheater",
        "samples": [
          "Turn on the water heater",
          "Heater on"
        ],
        "slots": []
      }
    ],
    "invocationName": "wiseshower"
  },
  "prompts": [
    {
      "id": "Confirm.Intent-setupshower",
      "variations": [
        {
          "type": "PlainText",
          "value": "Well so you want a shower of {timeinminutes} minutes with a maximun use de {waterinliters} liters of water and heater in {HeaterMode}, right?"
        }
      ]
    },
    {
      "id": "Elicit.Intent-setupshower.IntentSlot-timeinminutes",
      "variations": [
        {
          "type": "PlainText",
          "value": "How long?"
        },
        {
          "type": "PlainText",
          "value": "How many minutes?"
        }
      ]
    },
    {
      "id": "Confirm.Intent-setupshower.IntentSlot-timeinminutes",
      "variations": [
        {
          "type": "PlainText",
          "value": "{timeinminutes} minutes, ok?"
        }
      ]
    },
    {
      "id": "Elicit.Intent-setupshower.IntentSlot-waterinliters",
      "variations": [
        {
          "type": "PlainText",
          "value": "How many liters?"
        }
      ]
    },
    {
      "id": "Confirm.Intent-setupshower.IntentSlot-waterinliters",
      "variations": [
        {
          "type": "PlainText",
          "value": "{waterinliters} liters of water, ok?"
        }
      ]
    },
    {
      "id": "Elicit.Intent-setupshower.IntentSlot-HeaterMode",
      "variations": [
        {
          "type": "PlainText",
          "value": "How about the water heater?"
        },
        {
          "type": "PlainText",
          "value": "Heater on, off or auto?"
        }
      ]
    },
    {
      "id": "Confirm.Intent-setupshower.IntentSlot-HeaterMode",
      "variations": [
        {
          "type": "PlainText",
          "value": "Water heater {HeaterMode}, right?"
        }
      ]
    }
  ],
  "dialog": {
    "intents": [
      {
        "name": "setupshower",
        "confirmationRequired": true,
        "prompts": {
          "confirmation": "Confirm.Intent-setupshower"
        },
        "slots": [
          {
            "name": "timeinminutes",
            "type": "AMAZON.NUMBER",
            "elicitationRequired": true,
            "confirmationRequired": true,
            "prompts": {
              "elicitation": "Elicit.Intent-setupshower.IntentSlot-timeinminutes",
              "confirmation": "Confirm.Intent-setupshower.IntentSlot-timeinminutes"
            }
          },
          {
            "name": "waterinliters",
            "type": "AMAZON.NUMBER",
            "elicitationRequired": true,
            "confirmationRequired": true,
            "prompts": {
              "elicitation": "Elicit.Intent-setupshower.IntentSlot-waterinliters",
              "confirmation": "Confirm.Intent-setupshower.IntentSlot-waterinliters"
            }
          },
          {
            "name": "HeaterMode",
            "type": "heater",
            "elicitationRequired": true,
            "confirmationRequired": true,
            "prompts": {
              "elicitation": "Elicit.Intent-setupshower.IntentSlot-HeaterMode",
              "confirmation": "Confirm.Intent-setupshower.IntentSlot-HeaterMode"
            }
          }
        ]
      }
    ]
  }
}

Lambda

JSON
Funtion to access to the AWS IOT
No preview (download only).

Credits

Virgilio Enrique Aray Arteaga
2 projects • 4 followers

Comments