Wichai Tossamartvorakul
Published © LGPL

Alexa CoCo Sensor monitor

Use Alexa to ask Coco monitor movement, sound and temperature for you. SMS will be sent to your mobile phone if any sensor detect change.

IntermediateFull instructions provided12 hours2,251
Alexa CoCo Sensor monitor

Things used in this project

Hardware components

ESP8266 ESP-01
Espressif ESP8266 ESP-01
If use LoLin ESP you do not need to provide extra 5 V because it's on board
×1
PIR Motion Sensor (generic)
PIR Motion Sensor (generic)
×1
DS18B20 Programmable Resolution 1-Wire Digital Thermometer
Maxim Integrated DS18B20 Programmable Resolution 1-Wire Digital Thermometer
×1
Sound detect
×1

Software apps and online services

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

Story

Read more

Schematics

Circuit Diagram for CoCo

Connect sensor as in the diagram. Be careful for PIR voltage is 5V not 3 V. If you use LoLin ESP it will provided 5 V on board. You need R 4.7 K between signal pin (D5) and 5 V.

Code

Arduino code for Coco sensor monitor

Arduino
// Pigeon Monitor for pub/sub MQTT to AWS IOT

#include <Arduino.h>
#include <Stream.h>
#include <string.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"

// OneWire protocol for DS18B20
#include <OneWire.h>

#define DEBUG
// #undef DEBUG 

// Pin Define

#define DSPIN D5     // what pin we're connected to
#define PIR_MOTION_SENSOR  D4 //Use pin D3 to receive the signal from the module
#define SOUND_DETECT D7 
#define MIC  A0

//Temperature check mode  TEMP_ENABLE =  TEMP_MAX not specify temperature use the previous one
#define TEMP_DISABLE  0
#define TEMP_ENABLE  2
#define TEMP_MAX  2
#define TEMP_MIN  3

#define BUFFER 150   // Buffer for publish topic 

OneWire  ds(DSPIN);  // on pin D5 (a 4.7K resistor is necessary)

//AWS IOT config, change these:
char wifi_ssid[]       = "WifiSSID";
char wifi_password[]   = "WifiPassword";
char aws_endpoint[]    = "IOT_Endpoint.iot.us-east-1.amazonaws.com";
char aws_key[]         = "AWS_KEY";
char aws_secret[]      = "AWS_SECRET";
char aws_region[]      = "us-east-1";
const char* aws_topic  = "$aws/things/Pigeon/shadow/update";
const char* aws_accepted  = "$aws/things/Pigeon/shadow/update/accepted";
int port = 443;

//Buffer for sensor status message and monitor flag
String sensormessage;   
int  MoveFlag = false;
int  SoundFlag = false;
int  TempFlag =  TEMP_DISABLE; 
int  degree = 30;    // Temperature to check default for room temperature in Thailand
int  SensorFlag = false;

//MQTT config
const int maxMQTTpackageSize = 512;
const int maxMQTTMessageHandlers = 1;

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;

void setTempFlag(String str)
{
   int s1,s2,i;
   String s;
 
 //Get TempFlag
// Default set to disable
    TempFlag = TEMP_ENABLE;

    s1 = str.indexOf(",");  //Check for value of Tempflag
    if ( s1 > 0)
    {
        s = str.substring(0,s1);
#ifdef DEBUG       
        Serial.println( s );
#endif        
 
        if (s == "\"max\"")     // Max Temperature check
        {
           TempFlag = TEMP_MAX;
        }
        if (s == "\"min\"")     // Min Temperature check
        {
           TempFlag = TEMP_MIN;
        }
#ifdef DEBUG      
        Serial.print( "TempFlag = " );
        Serial.println (TempFlag);
        Serial.println (str);  
#endif  
 // Get Degree
        s1  = str.indexOf(":\"");
        if ( s1 > 0)
        {
              s = str.substring(s1+2);
         }   
        s2  = s.indexOf("\"");   
        s = s.substring(0,s2); // Skip character :
        degree = s.toInt();
        
#ifdef DEBUG      
        Serial.print(" Degree = ");
        Serial.println( degree,DEC );
#endif        
    }
}

// decode for MQTT 
void decodeMQTT(String str)
{
  String  flag[] = {"MoveFlag","SoundFlag","TempFlag"};
  int s1,s2,i;
 
 for (i = 0; i < 3; i++)
 { 
    s1 = str.indexOf(flag[i]);  //Check for interest flag
    if ( s1 > 0)
    {
        s2 = str.indexOf("}}");
        str = str.substring(s1,s2);
// Sepereate string by separator :
        s1  = str.indexOf(":");
        str = str.substring(s1+1); // Skip character :
// Process by index of array of flag        
        switch (i)
        {
          case 0:         // Monitor Movement
              if (str == "true")
              {
                   MoveFlag = true;
#ifdef DEBUG      
                   Serial.println (" Enable MoveFlag");
#endif        
              }
              else 
              {
                 MoveFlag = false;        
#ifdef DEBUG      
                   Serial.println (" Disable MoveFlag");     
#endif        
              }
              break;
          case 1:         // Monitor Sound
              if (str == "true")
              {
                   SoundFlag = true;
#ifdef DEBUG      
                   Serial.println (" Enable SoundFlag");
#endif        
              }
              else 
              {
                 SoundFlag = false;        
#ifdef DEBUG      
                   Serial.println (" Disable SoundFlag");     
#endif        
              }          
              break;
          case 2:
              setTempFlag (str);
              break;
        }        
    }
 }
}

//callback to handle mqtt messages
void messageArrived(MQTT::MessageData& md)
{

  MQTT::Message &message = md.message;
#ifdef DEBUG      
  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 ");
#endif        
  char* msg = new char[message.payloadlen+1]();
  memcpy (msg,message.payload,message.payloadlen);
#ifdef DEBUG
  Serial.println(msg);
#endif        
// Add handle message arrived here
  decodeMQTT(String(msg));
  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);
#ifdef DEBUG          
    Serial.print (millis ());
    Serial.print (" - conn: ");
    Serial.print (++connection);
    Serial.print (" - (");
    Serial.print (ESP.getFreeHeap ());
    Serial.println (")");
#endif        
   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");
    }

#ifdef DEBUG      
     Serial.println("MQTT connecting");
#endif        
    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)
    {
#ifdef DEBUG      
      Serial.print("error connection to MQTT server");
      Serial.println(rc);
#endif        
      return false;
    }
#ifdef DEBUG    
    Serial.println("MQTT connected");
#endif        
    return true;
}

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

//Publish a message to a mqtt topic
void sendmessage () {
    //send a message
    MQTT::Message message;
    char buf[BUFFER];
    String str;
    if (SensorFlag)
    {
        str  = "{\"state\":{\"reported\":{\"Sensor Activated\": \""+sensormessage+ "\",\"SensorFlag\": true}}}";
    }
    else
    {
        str  = "{\"state\":{\"reported\":{\"Sensor Activated\": \""+sensormessage+ "\",\"SensorFlag\": false}}}";     
    }
    str.toCharArray(buf,BUFFER); // Why cannot use sizeof (str) ?? 
#ifdef DEBUG
     Serial.println(buf);
#endif        
    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);
}


void setup() {
    Serial.begin (115200);
     delay (2000);
    Serial.setDebugOutput(1);
    sensormessage = "Sensor disable";
    SensorFlag = false;

//fill with ssid and wifi password
    WiFiMulti.addAP(wifi_ssid, wifi_password);
#ifdef DEBUG      
    Serial.println ("connecting to wifi");
#endif        
    while(WiFiMulti.run() != WL_CONNECTED) {
        delay(100);
#ifdef DEBUG
        Serial.print (".");
#endif        
    }
#ifdef DEBUG      
     Serial.println ("\nconnected");
#endif        
   //fill AWS parameters    
    awsWSclient.setAWSRegion(aws_region);
    awsWSclient.setAWSDomain(aws_endpoint);
    awsWSclient.setAWSKeyID(aws_key);
    awsWSclient.setAWSSecretKey(aws_secret);
    awsWSclient.setUseSSL(true);
    if (connect ()){
      subscribe ();
      sendmessage ();
    }

 // For Sensor Setup   
#ifdef DEBUG      
    Serial.println("Sensors test!");
#endif        
   pinMode(PIR_MOTION_SENSOR, INPUT);
   pinMode (SOUND_DETECT, INPUT_PULLUP) ;  // output interface D7 Need Pull UP ?
// Attach Interrupt
    attachInterrupt(digitalPinToInterrupt(SOUND_DETECT),sensorCheck, CHANGE);
    attachInterrupt(digitalPinToInterrupt(PIR_MOTION_SENSOR),sensorCheck, CHANGE);
}


void loop() {
  //keep the mqtt up and running
   static float  celcius, fahrenheit, previous;
  
  if (awsWSclient.connected ()) {    
      client->yield();
  } else {
    //handle reconnection
    if (connect ()){
      subscribe ();      // Subscribe to ???
    }
  }
  if (TempFlag)  // Not disable > 0
  {
        celcius = getTemperature();
             
        if (celcius != 0.0)
        {
#ifdef DEBUG
        Serial.print("Temperature = ");
        Serial.println (celcius);
#endif 
            switch (TempFlag)
            {
              case TEMP_MAX:
                  if ( celcius > degree)
                  {
                      sensormessage = "Temperature more than "+ String(degree) + " Celcius.  It's now " +String(celcius) +" Celcius ";
                      SensorFlag = true;
                      TempFlag = TEMP_DISABLE;  // Avoid repeat checking
                  }
                  break;

              case TEMP_MIN:  
                  if ( celcius < degree)
                  {
                      sensormessage = "Temperature less than  "+ String(degree)+ " Celcius.  It's now " +String(celcius) +" Celcius ";
                      SensorFlag = true;
                      TempFlag = TEMP_DISABLE;  // Avoid repeat checking
                  } 
                  break;
              default:  
                  if ( celcius > degree)
                  {
                      sensormessage = "Temperature more than  "+ String(degree);
                      SensorFlag = true;
                      TempFlag = TEMP_DISABLE;  // Avoid repeat checking
                  }
                  break;    
            }       
#ifdef DEBUG      
                fahrenheit = celcius* 1.8 + 32.0;
                Serial.print("Temperature = ");
                Serial.print(celcius);
                Serial.print(" Celsius, ");
                Serial.print(fahrenheit);
                Serial.println(" Fahrenheit");
#endif        
        }      
  }
  
   if (SensorFlag)
   {
#ifdef DEBUG
    Serial.println ("Sensor message = " +sensormessage);
#endif    
    sendmessage();
    sensormessage = "Sensor Disable";   //Clear sensor message
    SensorFlag = false;   
   }
}


float getTemperature()
{
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;

  if ( !ds.search(addr)) {
//  Serial.println("No more addresses.");
//  Serial.println();
    ds.reset_search();
    delay(250);
    return 0.0;  // No more address
 }

  if (OneWire::crc8(addr, 7) != addr[7]) {
#ifdef DEBUG      
        Serial.println("CRC is not valid!");
#endif        
        return 0.0;  // Error
  }
//  Serial.println();
 
 //the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
//    Serial.println("  Chip = DS18S20");  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
//     Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
 //     Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
#ifdef DEBUG    
      Serial.println("Device is not a DS18x20 family device.");
#endif        
      return  0.0; // Error
  } 

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end
// sensorCheck();
  delay(500);     // maybe 750ms is enough, maybe not
 // we might do a ds.depower() here, but the reset will take care of it.
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

//  Serial.print("  Data = ");
//  Serial.print(present, HEX);
//  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
//   Serial.print(data[i], HEX);
//   Serial.print(" ");
  }
  
//  Serial.print(" CRC=");
//  Serial.print(OneWire::crc8(data, 8), HEX);
//  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  return celsius;
}

// ISP service routine 
void sensorCheck ()
{  int val,sensorValue,moveDetect;
   if (MoveFlag)
   {
         moveDetect = digitalRead(PIR_MOTION_SENSOR);
         if(moveDetect == HIGH)//if the sensor value is HIGH?
         {
           sensormessage = " -Intruder Detect ";
           SensorFlag = true;
           MoveFlag = false;   // Disable checking to avoid repeat warning
         } 
         else
         {
           sensormessage = "No sensor detect ";         
         }        
   }  
     
   if (SoundFlag)
   {
        val = digitalRead (SOUND_DETECT) ;  // digital interface will be assigned a value of 3 to read val
//       sensorValue = analogRead (MIC);    // Not use at this moment
        if (val == HIGH)  // When the sound detection module detects a signal, LED flashes
        {
           sensormessage = " -Sound Detect ";
           SensorFlag = true;
           SoundFlag = false;  //Disable sound checking to avoid repeat warning 
        }
        else
        {
           sensormessage = "No sensor detect ";         
        }
   } 
   
#ifdef DEBUG
    Serial.println("ISP called sensormessage = "+sensormessage);
#endif       
     
 }
         

  
   

Credits

Wichai Tossamartvorakul

Wichai Tossamartvorakul

6 projects • 4 followers
I am Embedded system engineer. Currently, I am interest in IOT, AI and Mobile Application development

Comments

Add projectSign up / Login