in progress

Will be documenting the whole process here.

context

Whether it was because my mom used to always ask about my skin (and whether I was drinking enough water) or because I over-obsessed about my skin, I used to spend hours of my week leaning over my sink, observing and picking at almost every millimeter and pore of my face. I knew it wasn’t good for me. I knew that the more I picked, poked, and squeezed, the larger my pores would get. The worse my skin would get. But it was so hard for me to stop.

Since high school, I’ve been thinking about making something that would stop me from scrutinizing my face. Now, I want to actually build the thing. So here it is.

ideas

Here’s my thought process whenever my face-analyzing starts to happen:

I’ll be at the mirror–washing my hands or face–when I spot something or feel an uneven dot on my face. I’ll then lean close to the mirror (probably within 2 inches) and see if it’s something immediately fixable (ie pick-at-able). Often it is, and so I’ll continue there for a number of minutes, just going at it. I know in my mind that I should stop, but usually I fall into a default state and just keep going.

I think there are a couple of possible inputs and outputs:

Inputs:

  • Proximity sensor. I need to get close to pick at my face and see where the imperfections are. (ultrasound? time of flight? many such possibilities)

Outputs:

  • A mist maker to make my mirror foggy so that I can’t see anything.
  • A speaker and annoying voice reminder. I’m usually in that default state when I’m that close to the mirror. And if the only way to stop the voice is to back up, I won’t be able to pick.
  • A bright light. Maybe I just blind myself if I’m really close to the mirror?
  • A little OLWD/LCD screen to accompany any of the above possibilities. It can keep track of how many times I’ve picked in the last x amount of time.
  • PDLC (polymer dispersed liquid crystal) screen on top of some acrylic (for easy application and removal onto mirrors). This would literally make it so that I can’t clearly see anything. This’ll be a lot easier (and convenient for the user (me)).

V0: Distance sensing, buzzer and PDLC screen output

On this first iteration, I want to focus on building something that will address my core needs: not being able to see my face after picking at my face (getting to close to the mirror).

Electronics:

ToF Sensor Input + Buzzer Output

This was pretty straightforward to build. Just to make sure the overall system would work as expected, I started with Adafruit / packaged versions of everything I needed to start. I found some good Adafruit ToF sensor documentation and worked with the Seeed Xiao RP2040 documentation. This is what things looked

miro1

Code Test

#include "Adafruit_VL53L1X.h"

#define IRQ_PIN 29
#define XSHUT_PIN 0
#define BUZZER_PIN 27  // Passive buzzer on pin 27
#define PDLC_PIN 26

// Constants
#define DISTANCE_THRESHOLD 200  // mm - adjust as needed
#define BEEP_DURATION 300       // ms for each beep
#define BEEP_INTERVAL 200     // ms between buzzer toggles
#define BUZZER_FREQUENCY 2000   // Hz - frequency for the tone

Adafruit_VL53L1X vl53 = Adafruit_VL53L1X(XSHUT_PIN, IRQ_PIN);
bool isTooClose = false;
bool buzzerActive = false;
unsigned long lastBeepTime = 0;

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.println(F("MIRO"));
  
  // Initialize buzzer pin
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(PDLC_PIN, OUTPUT);
  digitalWrite(BUZZER_PIN, LOW);
  digitalWrite(PDLC_PIN, LOW);
  Serial.println("PDLC Relay Test Starting...");
  Serial.println("Relay should activate every 5 seconds");

  Wire.begin();
  if (! vl53.begin(0x29, &Wire)) {
    Serial.print(F("Error on init of VL sensor: "));
    Serial.println(vl53.vl_status);
    while (1)       delay(10);
  }
  Serial.println(F("VL53L1X sensor OK!"));

  Serial.print(F("Sensor ID: 0x"));
  Serial.println(vl53.sensorID(), HEX);

  if (! vl53.startRanging()) {
    Serial.print(F("Couldn't start ranging: "));
    Serial.println(vl53.vl_status);
    while (1)       delay(10);
  }
  Serial.println(F("Ranging started"));

  // Valid timing budgets: 15, 20, 33, 50, 100, 200 and 500ms!
  vl53.setTimingBudget(100);
  Serial.print(F("Timing budget (ms): "));
  Serial.println(vl53.getTimingBudget());

  /*
  vl.VL53L1X_SetDistanceThreshold(100, 300, 3, 1);
  vl.VL53L1X_SetInterruptPolarity(0);
  */
}

// Function to generate a tone on a passive buzzer
void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
  unsigned long period = 1000000 / frequency; // Period in microseconds
  unsigned long startTime = micros();
  unsigned long endTime = startTime + duration * 1000; // Convert ms to us
  
  while (micros() < endTime) {
    digitalWrite(pin, HIGH);
    delayMicroseconds(period / 2);
    digitalWrite(pin, LOW);
    delayMicroseconds(period / 2);
  }
}

void updateBuzzer(unsigned long currentTime) {
  if (isTooClose) {
    // Time to start a new beep?
    if (!buzzerActive && (currentTime - lastBeepTime >= BEEP_INTERVAL)) {
      buzzerActive = true;
      lastBeepTime = currentTime;
    }
    
    // If beep is active but not beyond its duration, generate tone
    if (buzzerActive && (currentTime - lastBeepTime < BEEP_DURATION)) {
      // Generate a short pulse for the tone (this is non-blocking for short pulses)
      digitalWrite(BUZZER_PIN, HIGH);
      delayMicroseconds(1000000 / BUZZER_FREQUENCY / 2);
      digitalWrite(BUZZER_PIN, LOW);
      delayMicroseconds(1000000 / BUZZER_FREQUENCY / 2);
    } else if (buzzerActive && (currentTime - lastBeepTime >= BEEP_DURATION)) {
      // Beep duration is over
      buzzerActive = false;
    }
  } else {
    // Not too close, reset buzzer state
    buzzerActive = false;
    digitalWrite(BUZZER_PIN, LOW);
  }
}


void loop() {
  unsigned long currentTime = millis();
  int16_t distance;

  if (vl53.dataReady()) {
    // new measurement for the taking!
    distance = vl53.distance();
    if (distance == -1) {
      // something went wrong!
      Serial.print(F("Couldn't get distance: "));
      Serial.println(vl53.vl_status);
      return;
    }else {
      // Valid distance reading
      Serial.print(F("Distance: "));
      Serial.print(distance);
      Serial.println(" mm");
      
      // Update proximity state
      isTooClose = (distance < DISTANCE_THRESHOLD);
    }
    vl53.clearInterrupt();
  }
  // Update buzzer with proper tone generation
  updateBuzzer(currentTime);

  if (isTooClose){
    digitalWrite(PDLC_PIN, HIGH);
    delay(100);  // Brief 100ms pulse
    digitalWrite(PDLC_PIN, LOW);
  } else {
    digitalWrite(PDLC_PIN, HIGH);
    delay(100);  // Brief 100ms pulse
    digitalWrite(PDLC_PIN, LOW);
  }
 
}

It worked well! Next step: the PDLC film. There was a very immediate first problem: the PDLC film takes in 48-65V (typically works around 50AC/60Hz). And I need to somehow power this from the seeed, which outputs a max of 5V from the rails, or 3.3V from the digital and analog pins.

My first attempt at a solution was a 5V relay. I’d keep in the batteries, hack open the battery container to reveal where the button switch is, and then solder the relay to those pins such that I could simulate a button press with the output of seeed pin.

PDLC Output

BoM

Here are my possible order lists so far for the bare bones PDLC and buzzer version. I want to get this working before I add on the speaker and OLED screen, just because I feel like this’ll have highest effectiveness (and a. because I’m a bit lazy right now to figure out which speaker specs would go well (I may need to make an analog amplifier for it) and b. to see if I have enough pins for a non-I2C protocol screen). (Also, Alec of the EDS lab kindly gave me a Seeed Xiao RP2040, so I’ll be using that instead in the meantime):

Parts Quantity vers.
Seeed Xiao RP2040 1
PDLC screen (6x8.3in) 2
ToF Sensor (built in breakout board to offload some work for now) 2
electronics

I’m starting with the core parts needed for an MVP. I’m working with the seeed RP2040, the VL53L1X ToF sensor, a passive buzzer, and the PDLC display.

I found some

project design and dev

design