Hazel
Created August 30, 2024

Object sorter with tactile & audible interface

A moddable object sorting device for visually impaired users

46

Things used in this project

Hardware components

Blues Notecarrier A
Blues Notecarrier A
×1
Blues Notecard WiFi
×1
MG996R Servo motor
×2
Adafruit 16-Channel 12-bit PWM/Servo Driver
×1
Gravity: Offline Language Learning Voice Recognition Sensor
DFRobot Gravity: Offline Language Learning Voice Recognition Sensor
×1
Seeed Studio XIAO ESP32-C3
×1
Grove Shield for Seeeduino XIAO - with embedded battery management chip
Seeed Studio Grove Shield for Seeeduino XIAO - with embedded battery management chip
×1
Pimoroni BH1745 Luminance and Colour Sensor Breakout
×1
Seeed Studio Grove Vision AI v2 Kit
×1
Grove - 4 pin Male Jumper to Grove 4 pin Conversion Cable
Seeed Studio Grove - 4 pin Male Jumper to Grove 4 pin Conversion Cable
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Turntable bearing
50 mm acrylic discs

Story

Read more

Custom parts and enclosures

Main CAD files (SolidWorks + STEP)

Braille tag (black bin)

Editable Onshape file: https://cad.onshape.com/documents/b82d0d3ffe34d4606ccadbba/w/d4debde5c3ee906ef7ebe4e6/e/78a7a53a054969625b33fd81

Braille tag (white bin)

Editable Onshape file: https://cad.onshape.com/documents/b82d0d3ffe34d4606ccadbba/w/d4debde5c3ee906ef7ebe4e6/e/78a7a53a054969625b33fd81

Schematics

Block diagram

Code

Arduino script

C/C++
#include <Seeed_Arduino_SSCMA.h>
#include <Wire.h>
#include "DFRobot_DF2301Q.h"
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// I2C address of the BH1745NUC
#define Addr 0x38

SSCMA AI;

int red = 0;
int green = 0;
int blue = 0;
int cData = 0;

DFRobot_DF2301Q_I2C asr;

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

#define SERVOMIN  150 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  600 // This is the 'maximum' pulse length count (out of 4096)
#define USMIN  600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX  2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

uint16_t pulse_45_deg = map(45, 0, 180, SERVOMIN, SERVOMAX);
uint16_t pulse_90_deg = map(90, 0, 180, SERVOMIN, SERVOMAX);
uint16_t pulse_135_deg = map(135, 0, 180, SERVOMIN, SERVOMAX);

void setup()
{
    //RGB sensor setup
    Wire.begin();
    Serial.begin(9600);

    Wire.beginTransmission(Addr);
    Wire.write(0x41);
    Wire.write(0x00); // Set RGBC measurement time 160 msec
    Wire.endTransmission();
    
    Wire.beginTransmission(Addr);
    Wire.write(0x42);
    Wire.write(0x90); // Set measurement mode is active, gain = 1x
    Wire.endTransmission();
    
    Wire.beginTransmission(Addr);
    Wire.write(0x44);
    Wire.write(0x02); // Set default value
    Wire.endTransmission();
    delay(300);
    
    //AI Cam setup
    AI.begin();

    //Voice sensor setup
    while (!(asr.begin())) {
      Serial.println("No voice sensor");
      delay(3000);
    }
    Serial.println("Begin ok!");
    asr.setVolume(7);  
    asr.setMuteMode(0);
    asr.setWakeTime(20);
    uint8_t wakeTime = 0;
    wakeTime = asr.getWakeTime();
    Serial.print("wakeTime = ");
    Serial.println(wakeTime);

    pwm.begin();    
    pwm.setOscillatorFrequency(27000000);
    pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates

    //Set trapdoor servo to 0 deg
    pwm.setPWM(0, 0, SERVOMIN);

    //Set turntable servo to 90 deg
    pwm.setPWM(2, 0, pulse_90_deg);

}

void loop()
{
    uint8_t CMDID = asr.getCMDID();
    if (CMDID != 0) {
        Serial.print("CMDID = ");  //Printing command ID
        Serial.println(CMDID);
    }
    
    if (!AI.invoke())
    {
        Serial.println("scan ok");

        for (int i = 0; i < AI.boxes().size(); i++)
        {
            Serial.print("Box["); Serial.print(i);
            Serial.print("] target="); Serial.print(AI.boxes()[i].target); //Object class (0=paper, 1=rock, 2=scissors)
            Serial.print(", score="); Serial.print(AI.boxes()[i].score);
            Serial.print(", x="); Serial.print(AI.boxes()[i].x);
            Serial.print(", y="); Serial.print(AI.boxes()[i].y);
            Serial.print(", w="); Serial.print(AI.boxes()[i].w);
            Serial.print(", h="); Serial.println(AI.boxes()[i].h);

            //Fist detected by camera?
            if (int(AI.boxes()[i].target) == 1){
              Serial.println("Fist detected");
              int x = AI.boxes()[i].x;
              int y = AI.boxes()[i].y;

              //Fist centred over box?
              if ((x >=80) && (x<= 160)){
                if ((y >=80) && (y<= 160)){
                  Serial.println("Hand centred");
                  asr.playByCMDID(38);//Say "OK"
                  delay(1000);

                  //Scan colours
                  RGBC();

                  //Black sock?
                  if (cData < 15){
                    Serial.println("Black sock");
                    //Rotate turntable servo to 45 deg
                    for (uint16_t pulselen = pulse_90_deg; pulselen > pulse_45_deg; pulselen--) {
                      pwm.setPWM(2, 0, pulselen);
                      delay(5);
                    }
                    delay(500);
                  }
                  //White sock
                  else {
                    Serial.println("White sock");
                    //Rotate turntable servo to 135 deg
                    for (uint16_t pulselen = pulse_90_deg; pulselen < pulse_135_deg; pulselen++) {
                      pwm.setPWM(2, 0, pulselen);
                      delay(5);
                    }
                    delay(500);
                  }

                  //Rotate trapdoor servo to 45 deg, then back to zero
                  for (uint16_t pulselen = SERVOMIN; pulselen < pulse_45_deg; pulselen++) {
                    pwm.setPWM(0, 0, pulselen);
                  }
                  delay(500);
                  for (uint16_t pulselen = pulse_45_deg; pulselen > SERVOMIN; pulselen--) {
                    pwm.setPWM(0, 0, pulselen);
                  }
                  asr.playByCMDID(22); //Say "done"
                  delay(200);
                  AI.invoke();
                }
              }
            }
        }
    }
}

void RGBC()
{
    unsigned int data[8];
    for(int i = 0; i < 8; i++)
    {
        // Start I2C Transmission
        Wire.beginTransmission(Addr);
        // Select data register
        Wire.write((80+i));
        // Stop I2C Transmission
        Wire.endTransmission();
        
        // Request 1 byte of data from the device
        Wire.requestFrom(Addr, 1);
        
        // Read 8 bytes of data
        // Red lsb, Red msb, Green lsb, Green msb, Blue lsb, Blue msb
        // cData lsb, cData msb
        if(Wire.available() == 1)
        {
            data[i] = Wire.read();
        }
        //delay(300);
    }

    // Convert the data
    red = ((data[1] & 0xFF) * 256) + (data[0] & 0xFF);
    green = ((data[3] & 0xFF) * 256) + (data[2] & 0xFF);
    blue = ((data[5] & 0xFF) * 256) + (data[4] & 0xFF);
    cData = ((data[7] & 0xFF) * 256) + (data[6] & 0xFF);
    
    // Output data to serial monitor
    Serial.print("Red Color luminance  : ");
    Serial.println(red);
    Serial.print("Green Color luminance : ");
    Serial.println(green);
    Serial.print("Blue Color luminance : ");
    Serial.println(blue);
    Serial.print("Clear Data Color luminance : ");
    Serial.println(cData);
}

Credits

Hazel

Hazel

3 projects • 4 followers
Thanks to Ayman Abasi.

Comments