0

$USD

$USD
PROJECTS ESP32

How to make a ESP32 Fall Detector

DFRobot Mar 26 2019 1590

An improvement from my previous fall detector. It can send an email by sensing a fall or with the simple press of a button.


Things used in this project

Hardware components

DFRobot ESP32 ESP-WROOM Module
Silicon Labs CP2102 USB to UART Bridge
MCP73831 Li-Ion Charger IC
LM317BD2T Adjustable Regulator
0805 4.7uF Capacitor
0805 100nF Capacitor
0805 1uF Capacitor
WS2812b LED
1206 LED
Micro USB Connector
0805 470 ohm Resistor
0805 2k ohm Resistor
0805 510 ohm Resistor
0805 300 ohm Resistor
0805 10k ohm Resistor
0805 270 ohm Resistor
6mm x 6mm Pushbutton
SMD 6mm x 6mm Tall Pushbutton

Software apps and online services

Autodesk EagleCAD
Autodesk Fusion 360

Hand tools and fabrication machines
3D Printer (generic)
Soldering iron (generic)
Digital Microscope

Story

Back in August of 2017, I imagined a device that could alert users if one of their loved ones experienced a fall or pressed a “panic” button. It used an ESP8266 and was assembled on a piece of perf-board. It had a single LED that would indicate if a fall had occurred. The device also featured a very basic LiPo charging circuit that had no indicators.



New Idea

Since my last fall detector was so rudimentary, I wanted to make drastic improvements. The first one was making it USB programmable, so I used a CP2102 USB to UART converter IC to handle the USB to UART serial connection.


I also wanted there to be more indications of the operations, so I added an LED for charging, one for power, and two for the USB status. I chose to use an ESP32 due to its increased power and Bluetooth connectivity, which can allow for future expansion, such as an accompanying app.


PCB Design

All these new features would require a lot of additional circuitry, and a simple piece of perf-board would not cut it. This required a PCB, which I designed in EagleCAD. I began by laying out the connections with their schematic editor. Then I moved onto making the actual board and traces.



Soldering

This was the most difficult part because of the fine-pitched pins. The hardest component to solder was the CP2102, which comes in a QFN-28 package. Each pin is just.5mm apart, and without a stencil, this was fairly tricky to attach. I solved this problem by applying a generous amount of liquid flux to the pads and then running a small amount of solder over the pins.



Usage

The device works by checking the acceleration measured by the MPU6050 at set intervals. Once it detects a fall, it sends an email to a set contact. I have found out that the battery lasts about three days, so it must be charged regularly. There is also a button that is connected to a hardware interrupt that can send an email when pressed.


Code

ESP32 Code MainC/C++

#include <Wire.h>
#include <SimpleTimer.h>

#include <Arduino.h>

#include <WiFi.h>
#include <WiFiMulti.h>

#include <HTTPClient.h>
#include "sendemail.h"

const int MPU_addr=0x68;
int16_t AcX, AcY, AcZ, TmP,GyX,GyY,GyZ;
float AcX_calc, AcY_calc, AcZ_calc;
uint32_t lastTime;

// WiFi network info.
const char* SSID = "ssid";
const char* PASS = "wifi pass";

SendEmail e("stmp.gmail.com", 465, "[email protected]", "password", 5000, true);

#define LED_PIN 14
#define BUTTON_PIN 27

void check_imu(){
  readIMU();
  Serial.print("AcX: "); Serial.print(AcX); Serial.print("g | AcY: "); Serial.print(AcY); Serial.print("g | AcZ: "); Serial.print(AcZ);
  Serial.println("g");
  if(abs(AcX_calc)> 22000 || abs(AcY)> 22000|| abs(AcZ) > 27000){
    Serial.println("Fall detected");
    int button_val = 1;
    lastTime = millis();
    button_val = digitalRead(BUTTON_PIN);
    while(millis()-lastTime<5000){
      button_val = digitalRead(BUTTON_PIN);
      delay(10); //Debounce
      Serial.println(button_val);
      if(!button_val) break;
    }
    if(!button_val){
      Serial.println("Didn't do anything, button was pressed");
      delay(200);
    }
    else{
      alarm();
    }
    delay(50);
  }
  
}

void setup() {
	WiFi.begin(SSID, PASS);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  Wire.begin(4,5);
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
  Serial.begin(115200);
  Serial.println("Wrote to IMU");
  
}

void loop() {
  int button_state = digitalRead(BUTTON_PIN);
  if(!button_state){
    alarm();
    delay(500);
  }
  check_imu();
}

void readIMU(){
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  TmP=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
}

void alarm(){
  Serial.println("Alarm sent!");
  e.send("<[email protected]>", "<[email protected]>", "ALERT: Fall Detected", "Fall has been detected");
}

sendemail.cppC/C++

#include "sendemail.h"

SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const bool ssl) :
    host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), client((ssl) ? new WiFiClientSecure() : new WiFiClient())
{

}

String SendEmail::readClient()
{
  String r = client->readStringUntil('\n');
  r.trim();
  while (client->available()) r += client->readString();
  return r;
}

bool SendEmail::send(const String& from, const String& to, const String& subject, const String& msg)
{
  if (!host.length())
  {
    return false;
  }
  client->stop();
  client->setTimeout(timeout);
  // smtp connect
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.print("Connecting: ");
  DEBUG_EMAIL_PORT.print(host);
  DEBUG_EMAIL_PORT.print(":");
  DEBUG_EMAIL_PORT.println(port);
#endif
  if (!client->connect(host.c_str(), port))
  {
    return false;
  }
  String buffer = readClient();
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  if (!buffer.startsWith(F("220")))
  {
    return false;
  }
  buffer = F("EHLO ");
  buffer += client->localIP();
  client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  buffer = readClient();
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  if (!buffer.startsWith(F("250")))
  {
    return false;
  }
  if (user.length()>0  && passwd.length()>0 )
  {
    buffer = F("AUTH LOGIN");
    client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
    buffer = readClient();
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
    if (!buffer.startsWith(F("334")))
    {
      return false;
    }
    base64 b;
    buffer = user;
    buffer = b.encode(buffer);
    client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
    buffer = readClient();
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
    if (!buffer.startsWith(F("334")))
    {
      return false;
    }
    buffer = this->passwd;
    buffer = b.encode(buffer);
    client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
    buffer = readClient();
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
    if (!buffer.startsWith(F("235")))
    {
      return false;
    }
  }
  // smtp send mail
  buffer = F("MAIL FROM: ");
  buffer += from;
  client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  buffer = readClient();
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  if (!buffer.startsWith(F("250")))
  {
    return false;
  }
  buffer = F("RCPT TO: ");
  buffer += to;
  client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  buffer = readClient();
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  if (!buffer.startsWith(F("250")))
  {
    return false;
  }
  buffer = F("DATA");
  client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  buffer = readClient();
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  if (!buffer.startsWith(F("354")))
  {
    return false;
  }
  buffer = F("From: ");
  buffer += from;
  client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  buffer = F("To: ");
  buffer += to;
  client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  buffer = F("Subject: ");
  buffer += subject;
  buffer += F("\r\n");
  client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  buffer = msg;
  client->println(buffer);
  client->println('.');
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  buffer = F("QUIT");
  client->println(buffer);
#ifdef DEBUG_EMAIL_PORT
  DEBUG_EMAIL_PORT.println(buffer);
#endif
  return true;
}

sendemail.hC/C++

#ifndef __SENDEMAIL_H
#define __SENDEMAIL_H

//#define DEBUG_EMAIL_PORT

#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <base64.h>

class SendEmail
{
  private:
    const String host;
    const int port;
    const String user;
    const String passwd;
    const int timeout;
    const bool ssl;
    WiFiClient* client;
    String readClient();
  public:
   SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const bool ssl);
   bool send(const String& from, const String& to, const String& subject, const String& msg);
   ~SendEmail() {client->stop(); delete client;}
};

#endif