Project Overview
This is a portable, handheld lie detector device that analyzes a person’s sweat fluctuations (GSR) and heart beat (Pulse) to determine if they are telling the truth or lying. It displays the result on a TFT LED screen and uses NeoPixels for instant visual feedback. This device is conceptualized as an artifact within a dystopian setting.
Subterra: The Dystopian Context
In this dystopia, humanity is trapped underground, breathing recycled air in vast, sterile sanctuaries. The surface is uninhabitable, ravaged by a mutated chemical that wiped out all plant life and caused oxygen levels to plummet. The outside world is a toxic wasteland, and any exploration of it is strictly forbidden.
For individuals, life is one of survival, restriction, and disconnection. They are born into a world with no sun, no fresh air, and no connection to the natural world. Every day is a monotonous cycle of work in underground farms, producing oxygen and food to sustain a growing population. There are no opportunities for exploration, and dreams of the surface are nothing more than a forbidden fantasy.
Daily life is tightly controlled. Work is assigned based on necessity, and personal freedoms are nonexistent. Space is limited, and resources are precious—competition for both is fierce. Relationships are restricted, and the constant threat of punishment keeps the population in line. While some wonder about what lies beyond the underground sanctuaries, curiosity is dangerous, as any attempt to defy the laws or explore the surface leads to severe consequences. Humanity survives, but at the cost of their connection to nature, freedom, and hope.
Persona
Inspiration Board
Inspiration Board Collage Placeholder
Miro Link: View Board
Sketch
Circuit Diagram
Wiring Diagram Placeholder
Bill of Materials
| Name | Qty | Size | Datasheet |
|---|---|---|---|
| Raspberry Pi Pico WH | 1 | 51mm x 21mm x 2.1mm | |
| Neopixel | 2 | 5mm x 5mm | |
| Resistor | 2 | 4mm x 1.5mm x 1.5mm | |
| Button | 2 | 8mm x 8mm x 9mm | |
| Pulse Sensor | 1 | 5mm x 3.5mm | |
| TFT LED Screen | 1 | 58mm x 33mm x 8mm |
Mechanical Drawing
Mechanical Drawing (Measurements in mm) Placeholder
Pseudo-Code
Initialize display and GPIO pins
- Setup SPI pins for communication with the display
- Release any previous display and setup display bus
- Initialize the ST7735R display
Create a text label for dynamic messages on the display
Initialize pins for GSR sensor, pulse sensor, and buttons
- Setup GSR sensor pin for analog input (for resistance- sweat)
- Setup pulse sensor pin for analog input (for detecting heartbeat)
- Setup power and calibration buttons for digital input
Initialize NeoPixels for visual feedback (Green for Truth, Red for Lie)
Define function `show_message(msg)`:
- Update the displayed message with the given `msg`
Define function `read_gsr()`:
- Collect multiple GSR readings (10 samples)
- Return the average value of the readings
Define function `read_pulse()`:
- Read pulse sensor data to calculate heart rate in beats per minute (bpm)
- Use a simple algorithm to detect pulses (e.g., rising edges)
- Return calculated bpm
Set initial variables:
- Calibration step, truth and lie readings, thresholds for GSR and pulse, calibration flag, and device state
Start main loop:
- Display "Press Power" message
- If power button is pressed:
- Toggle the device's on/off state
- Show appropriate message and turn off NeoPixels when off
- If device is off, wait and continue the loop
If the device is on:
- If the device is not calibrated:
- Show calibration step message
- If calibration button is pressed:
- Read GSR and pulse data (both)
- Append GSR and pulse readings to the appropriate list (truth or lie readings)
- Increment calibration step
- When calibration is complete, calculate and display the thresholds for both GSR and pulse rate, mark as calibrated
- Else, in normal operation:
- Read current GSR and pulse data (bpm)
- If the GSR value is close to the truth threshold and the pulse rate is within normal range:
- Indicate Truth with Green light and show "TRUTH" message
- Else if the GSR value is close to the lie threshold and the pulse rate is elevated:
- Indicate Lie with Red light and show "LIE" message
- Else, indicate inconclusive result with no lights and show "INCONCLUSIVE" message
- Wait for 0.2 seconds before the next loop iteration
Final Code (CircuitPython)
import time
import board
import digitalio
import analogio
import busio
import displayio
import terminalio
import neopixel
from adafruit_st7735r import ST7735R
from adafruit_display_text import label
# Display Setup
mosi_pin = board.GP11
clk_pin = board.GP10
reset_pin = board.GP17
cs_pin = board.GP18
dc_pin = board.GP16
displayio.release_displays()
spi = busio.SPI(clock=clk_pin, MOSI=mosi_pin)
display_bus = displayio.FourWire(spi, command=dc_pin, chip_select=cs_pin, reset=reset_pin)
display = ST7735R(display_bus, width=128, height=160, bgr=True)
main_group = displayio.Group()
display.root_group = main_group
text_area = label.Label(terminalio.FONT, text="Initializing...", color=0x000000, x=10, y=80)
main_group.append(text_area)
# GSR and Buttons Setup
gsr_pin = analogio.AnalogIn(board.GP27)
power_button = digitalio.DigitalInOut(board.GP12)
power_button.switch_to_input(pull=digitalio.Pull.DOWN)
calibration_button = digitalio.DigitalInOut(board.GP13)
calibration_button.switch_to_input(pull=digitalio.Pull.DOWN)
# NeoPixels
pixels1 = neopixel.NeoPixel(board.GP14, 1) # Green (Truth)
pixels2 = neopixel.NeoPixel(board.GP15, 1) # Red (Lie)
# Functions
def show_message(msg):
text_area.text = msg
def read_gsr():
readings = []
for _ in range(10):
readings.append(gsr_pin.value)
time.sleep(0.01)
return sum(readings) // len(readings)
# Variables
calibration_step = 0
truth_readings = []
lie_readings = []
truth_threshold = 0
lie_threshold = 0
calibrated = False
device_on = False
# Main Loop
show_message("Press Power")
while True:
if power_button.value:
device_on = not device_on
if device_on:
show_message("Device ON")
else:
show_message("Device OFF")
pixels1[0] = (0, 0, 0)
pixels2[0] = (0, 0, 0)
time.sleep(0.5) # debounce
if not device_on:
time.sleep(0.1)
continue
if not calibrated:
show_message(f"Cal Step {calibration_step+1}")
if calibration_button.value:
gsr = read_gsr()
if calibration_step in (0, 1):
truth_readings.append(gsr)
elif calibration_step in (2, 3):
lie_readings.append(gsr)
calibration_step += 1
time.sleep(0.5) # debounce
if calibration_step == 4:
# Calculate thresholds
truth_threshold = sum(truth_readings) // len(truth_readings)
lie_threshold = sum(lie_readings) // len(lie_readings)
calibrated = True
show_message("Calibrated!")
# In a real environment, prints go to serial console
# print(f"Truth Threshold: {truth_threshold}")
# print(f"Lie Threshold: {lie_threshold}")
time.sleep(1)
# Show calibrated values briefly
show_message(f"T:{truth_threshold}\nL:{lie_threshold}")
time.sleep(2)
show_message("Ready")
else:
# After calibration: normal operation
gsr = read_gsr()
# print("GSR:", gsr)
margin = 1500 # Adjust sensitivity margin
if gsr <= (truth_threshold + margin):
# Lie detected (lower GSR often means less sweat/stress in this simple setup)
# Reversing logic based on project description where Lie is red and Truth is green
pixels1[0] = (0, 255, 0) # Green
pixels2[0] = (0, 0, 0)
show_message("TRUTH")
elif gsr >= (lie_threshold - margin):
# Truth detected
pixels1[0] = (0, 0, 0)
pixels2[0] = (255, 0, 0) # Red
show_message("LIE")
else:
# Inconclusive
pixels1[0] = (0, 0, 0)
pixels2[0] = (0, 0, 0)
show_message("INCONCLUSIVE")
time.sleep(0.2)