Guillermo Perez Guillen
Published © Apache-2.0

Hand Spray With Person Sensor

Raspberry Pi and Arduino versions of spray to sanitize, refresh, hydrate and soothe the skin of people with disabilities while traveling

IntermediateFull instructions provided8 hours212
Hand Spray With Person Sensor

Things used in this project

Hardware components

Person Sensor
Useful Sensors Person Sensor
×1
Raspberry Pi 4 Model B
Raspberry Pi 4 Model B
×1
Grommet, Gripper
Grommet, Gripper
×1
SG90 Micro-servo motor
SG90 Micro-servo motor
×1
SparkFun Qwiic Cable Kit
SparkFun Qwiic Cable Kit
×1
Qwiic pHat For Raspberry Pi
×1
Arduino UNO
Arduino UNO
×1

Software apps and online services

Raspbian
Raspberry Pi Raspbian
Python
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Hand Spray

Story

Read more

Custom parts and enclosures

Part 1

STL file - Part 1

Part 2

STL file - Part 2

Person Sensor Holder

Part used in the Person Sensor With Arduino UNO section

Schematics

Person Sensor With Raspberry Pi

Schematic Diagram

Person Sensor With Arduino UNO

Schematic Diagram

Code

person_sensor_raspberry.py

Python
This code must be run on the Raspberry Pi
# AUTHOR: GUILLERMO PEREZ GUILLEN
# Example of accessing the Person Sensor from Useful Sensors on a Pi using
# Python. See https://usfl.ink/ps_dev for the full developer guide.

import io
import fcntl
import struct
import time

########## SERVO CONFIGURATION
import RPi.GPIO as GPIO 

servoPIN = 18
GPIO.setmode(GPIO.BCM)
GPIO.setup(servoPIN, GPIO.OUT)

p = GPIO.PWM(servoPIN, 50) # GPIO 18 for PWM with 50Hz
p.start(7.5) # Initialization 5


# The person sensor has the I2C ID of hex 62, or decimal 98.
PERSON_SENSOR_I2C_ADDRESS = 0x62

# We will be reading raw bytes over I2C, and we'll need to decode them into
# data structures. These strings define the format used for the decoding, and
# are derived from the layouts defined in the developer guide.
PERSON_SENSOR_I2C_HEADER_FORMAT = "BBH"
PERSON_SENSOR_I2C_HEADER_BYTE_COUNT = struct.calcsize(
    PERSON_SENSOR_I2C_HEADER_FORMAT)

PERSON_SENSOR_FACE_FORMAT = "BBBBBBbB"
PERSON_SENSOR_FACE_BYTE_COUNT = struct.calcsize(PERSON_SENSOR_FACE_FORMAT)

PERSON_SENSOR_FACE_MAX = 4
PERSON_SENSOR_RESULT_FORMAT = PERSON_SENSOR_I2C_HEADER_FORMAT + \
    "B" + PERSON_SENSOR_FACE_FORMAT * PERSON_SENSOR_FACE_MAX + "H"
PERSON_SENSOR_RESULT_BYTE_COUNT = struct.calcsize(PERSON_SENSOR_RESULT_FORMAT)

# I2C channel 1 is connected to the GPIO pins
I2C_CHANNEL = 1
I2C_PERIPHERAL = 0x703

# How long to pause between sensor polls. 0.2
PERSON_SENSOR_DELAY = 3

i2c_handle = io.open("/dev/i2c-" + str(I2C_CHANNEL), "rb", buffering=0)
fcntl.ioctl(i2c_handle, I2C_PERIPHERAL, PERSON_SENSOR_I2C_ADDRESS)

while True:
    try:
        read_bytes = i2c_handle.read(PERSON_SENSOR_RESULT_BYTE_COUNT)
    except OSError as error:
        print("No person sensor data found")
        print(error)
        time.sleep(PERSON_SENSOR_DELAY)
        continue
    offset = 0
    (pad1, pad2, payload_bytes) = struct.unpack_from(
        PERSON_SENSOR_I2C_HEADER_FORMAT, read_bytes, offset)
    offset = offset + PERSON_SENSOR_I2C_HEADER_BYTE_COUNT

    (num_faces) = struct.unpack_from("B", read_bytes, offset)
    num_faces = int(num_faces[0])
    offset = offset + 1

    faces = []
    for i in range(num_faces):
        (box_confidence, box_left, box_top, box_right, box_bottom, id_confidence, id,
         is_facing) = struct.unpack_from(PERSON_SENSOR_FACE_FORMAT, read_bytes, offset)
        offset = offset + PERSON_SENSOR_FACE_BYTE_COUNT
        face = {
            "box_confidence": box_confidence,
            "box_left": box_left,
            "box_top": box_top,
            "box_right": box_right,
            "box_bottom": box_bottom,
            "id_confidence": id_confidence,
            "id": id,
            "is_facing": is_facing,
        }
        ## TURN SPRAY ON
        if is_facing == 1:
            print("spray ON")
            p.ChangeDutyCycle(12.5)
            time.sleep(0.5)
            p.ChangeDutyCycle(7.5)
            time.sleep(0.5)
            p.ChangeDutyCycle(12.5)
            time.sleep(0.5)            
            p.ChangeDutyCycle(7.5)
            time.sleep(0.5)
        ## TURN SPRAY OFF
        else:
            print("spray OFF")
            p.ChangeDutyCycle(7.5)
            time.sleep(0.5)            
        faces.append(face)
    checksum = struct.unpack_from("H", read_bytes, offset)
    print(num_faces, faces)
    time.sleep(PERSON_SENSOR_DELAY)

person_sensor_arduino.ino

Arduino
This code must be uploaded to the Arduino UNO board
// Author: Guillermo Perez Guillen

#include <Wire.h>
#include <Servo.h> //servo
 
Servo servoMotor; //servo

#include "person_sensor.h"

// How long to wait between reading the sensor. The sensor can be read as
// frequently as you like, but the results only change at about 5FPS, so
// waiting for 200ms is reasonable.
const int32_t SAMPLE_DELAY_MS = 3000; 

void setup() {
  // You need to make sure you call Wire.begin() in setup, or the I2C access
  // below will fail.
  Wire.begin();
  Serial.begin(9600);
  servoMotor.attach(9);  
}

void loop() {
  person_sensor_results_t results = {};
  // Perform a read action on the I2C address of the sensor to get the
  // current face information detected.
  if (!person_sensor_read(&results)) {
    Serial.println("No person sensor results found on the i2c bus");
    delay(SAMPLE_DELAY_MS);
    return;
  }

  Serial.println("********");
  Serial.print(results.num_faces);
  Serial.println(" faces found");
  for (int i = 0; i < results.num_faces; ++i) {
    const person_sensor_face_t* face = &results.faces[i];
    Serial.print("Face #");
    Serial.print(i);
    Serial.print(": ");
    Serial.print(face->box_confidence);
    Serial.print(" confidence, (");
    Serial.print(face->box_left);
    Serial.print(", ");
    Serial.print(face->box_top);
    Serial.print("), (");
    Serial.print(face->box_right);
    Serial.print(", ");
    Serial.print(face->box_bottom);
    Serial.print("), ");
    if (face->is_facing) {
      Serial.println("facing");
    } else {
      Serial.println("not facing");
    }
  }
  //servo
  if(results.num_faces == 1){
    // SPRAY ON
  servoMotor.write(160);
  delay(500);
  servoMotor.write(100);
  delay(500);
  servoMotor.write(160);
  delay(500);    
  servoMotor.write(100);
  delay(500);      
  }
  else{
    // SPRAY OFF
  servoMotor.write(100);
  delay(500);          
  }
  delay(SAMPLE_DELAY_MS);
}

hand-spray-with-person-sensor

Repository

Credits

Guillermo Perez Guillen

Guillermo Perez Guillen

57 projects • 63 followers
Electronics and Communications Engineer (ECE) & Renewable Energy: 14 prizes in Hackster / Hackaday Prize Finalist 2021-22-23

Comments