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
SOFTWARE APPS AND ONLINE SERVICES
· Autodesk EagleCAD
· Autodesk Fusion 360
HAND TOOLS AND FABRICATION MACHINES
· 3D Printer (generic)
· Soldering iron (generic)
· Digital Microscope
Story
I would like to thank DFRobot for sponsoring this project.
Previous Project
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.
Schematics
Board Schematic
CODE ESP32 CODE MAIN
#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.cpp
#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.h
#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
The article was first published in hackster, March 18, 2019
cr: https://www.hackster.io/gatoninja236/esp32-fall-detector-b8ec6f
author: Evan Rust