Four small computers, five ultrasonic sensors, a GPS, and a 2G modem. Built by Said Mohaddes Sadeqi at a blind support centre before 2022, worn by blind and visually impaired people there since 2022, and honest about what it did not do.
NODES, ONE CHIP EACH
4
ULTRASONIC SENSORS
5
KITS WE BUILT AND GAVE OUT
Tens
TEXT HOME, NO INTERNET
2G
What it was
One job per chip.
Ultrasonic ranging needs microsecond timing. A GSM text can block for many seconds while the tower decides. Put both on one processor and a slow text can freeze the obstacle loop at the worst moment. So version 1 split the work across four independent AVR microcontrollers with no shared clock. That physical boundary is the whole architecture.
Node AIMPLEMENTED
Glasses
UPPER RANGING
DETECTS
Obstacles at chest height and above: branches, signs, the underside of a staircase.
FEEDBACK
A tone in the headphone, with an optional buzz.
PARTS
One HC-SR04 ultrasonic sensor, a 3.5 mm jack, an Arduino Mini class AVR.
LOOP
Hard real time. Ten readings a second. Alert inside 70 cm.
Node BIMPLEMENTED
Shoes
LOWER RANGING
DETECTS
Ground hazards below the knee, left and right: curbs, holes, steps.
FEEDBACK
A vibration motor on each shoe, and an LED that switches on in the dark so others can see the wearer.
PARTS
Two HC-SR04 sensors, two coin motors, a light sensor and an LED, one AVR.
LOOP
Hard real time. Ten readings a second. Alert inside 60 cm on each side.
Node CIN DEV
Gloves
HAND SCANNING
DETECTS
Whatever the hand points at within arm's reach, 3 to 40 cm: a doorway, a table edge, a handrail.
FEEDBACK
A very short, low buzz in the palm.
PARTS
One HC-SR04 sensor, one coin motor, one AVR. The glove itself was never fabricated.
LOOP
Hard real time. Twenty readings a second. Alert inside 40 cm.
Node DIMPLEMENTED
Belt or pack
LOCATION TELEMETRY
DETECTS
A GPS fix, and an incoming text or call from an allowed family number.
FEEDBACK
A text message back with the coordinates. No broadband, no Wi-Fi, no smartphone.
PARTS
A NEO-6M GPS, a SIM800 GSM modem, a 2G SIM, one AVR.
LOOP
Soft real time. Seconds, set by the cell tower, on its own chip so it can never stall the ranging.
How far it went
The first generation in the field. It was built to be low cost from the start. We built and gave out tens of kits ourselves. Because the design was open, hundreds more were built by the people who needed them, with our help sourcing parts.
The bill of materials
From the first generation repository: every part and how many. Costs are not shown while the second generation is in testing.
#
Item
Quantity
Notes
1
AVR microcontroller, Arduino Mini class
4
One independent processor per node
2
Ultrasonic sensor, HC-SR04 class
5
Node A: 1, Node B: 2, Node C: 1, spare: 1
3
GPS module, NEO-6M class
1
Node D only
4
GSM module, SIM800 class
1
Node D only, 2G chipset
5
2G SIM card
1
Pre-activated, regional provider
6
LED lights, 3 mm, assorted
1 pack
Node B visibility marker
7
LDR module
2
Node B ambient light trigger
8
Vibration motor, ERM coin
4
Nodes A, B (two), and C
9
Cells, AA, 1.5 V
4
One per node; 4 to 6 hours was the target
10
Footwear chassis (shoes)
1 pair
Node B mount
11
Eyewear chassis (glasses)
1
Node A mount
12
Jumper wires
3 packs
Breadboard and node interconnect
13
Breadboard, 830 hole
2
Prototyping
14
USB power adapter, micro USB
1
Bench testing and flashing
15
Headphone jack, 3.5 mm, with cable
1
Node A audio output
How it was built
Flash, tune, test, then the safety milestone.
The essentials of the founder's build guide, in his words. The four pinouts follow it, one panel each.
Build, Flash & Configure Guide
Step-by-step instructions for assembling, programming, and testing the Third Eye Kit.
Prerequisites
Arduino IDE 1.8+ or arduino-cli
AVR board support, install via Board Manager in Arduino IDE
Microcontroller drivers, CP2102 / CH340 USB-to-serial (for your specific board)
Test setup, bench space, USB power, soldering iron (for permanent builds)
Step 1: Select Your Node
Each of the four nodes flashes to its own independent microcontroller. There is no combined build, the physical isolation boundary (see Technical Spec, §2.2) means ranging and telemetry run on separate processors.
If you're flashing Node D, do this before deployment:
5.1 Verify 2G Coverage
AT+CREG? # Query registration status
+CREG: 0,1 # 0 = unsolicited, 1 = registered on home network
5.2 Set Authorized Number(s)
Edit the firmware allowlist:
// Authorized phone numbers that can request location
const char* AUTHORIZED[] = {
"+93799123456", // Family member 1
"+93799654321" // Family member 2
};
const int AUTH_COUNT = 2;
5.3 Verify SIM Activation
Before field use, confirm the SIM is pre-activated on a 2G network:
Contact regional telecom provider
Verify account balance and 2G coverage in deployment area
Test with a basic 2G phone first (if possible)
Step 6: Bench-Test Each Node
6.1 Ranging Verification (Nodes A, B, C)
Power on the node
Sweep an obstacle (hand, book, wall) slowly across the sensor cone
Observe feedback:
Haptic motor vibrates when distance < ALERT_CM
Audio tone / buzzer activates simultaneously
Adjust ALERT_CM if feedback distance feels too close or too far
Repeat with different obstacles (soft fabric, hard surface, angled), note any dead zones
6.2 Power Verification
Measure battery voltage before and after 10 minutes of continuous operation
Confirm no voltage sag below the sensor operating range (typically 5V ±0.5V)
If voltage drops below 4.5V, replace cells
6.3 Telemetry Verification (Node D)
Open Serial Monitor, Tools → Serial Monitor (9600 baud)
Power on Node D with SIM inserted
Watch for:
GPS: waiting for fix...
GPS: fixed (lat, lon)
GSM: registered on 2G
Ready for location requests.
Trigger a location request by calling/SMSing the device from an authorized number
Expect reply within 30 seconds with GPS coordinates
Step 7: Pre-Deployment Safety Milestone
Checklist Before Field Use
Known Limitations (Do Not Deploy Into)
Crowd/traffic scenarios, single-threshold ultrasonic logic not designed for dense, dynamic obstacles
Heavy rain or saltwater spray, ultrasonic sensors degrade in moisture (waterproofing [IN DEV])
Reflective or sound-absorbing surfaces, soft fabrics and some glass confuse ultrasonic cone
Extended high heat, battery fade not characterized above 50°C
Troubleshooting
Symptom: Sensor Reads Nothing (Always 0 cm)
Possible causes:
Wiring error, confirm TRIG/ECHO pins against node pinout
Power starved, sensor may need 5V, not 3.3V; check microcontroller supply
If duration is always 0, the echo pin is not receiving a signal.
Symptom: Feedback Fires Constantly
Possible causes:
ALERT_CM set too high (e.g., 200 cm), adjust downward
Sensor is miscalibrated, may be reading ghosts from reflections
Power sag, sensor readings become erratic as voltage drops
Diagnostic:
Print distance readings to Serial Monitor
Look for stable readings with obstacle at known distance
If readings are scattered, suspect power or wiring
Symptom: Audio Output Distorted or Missing
Possible causes:
Headphone jack not fully inserted
PWM pin (D11 for Node A) not capable of audio-rate modulation
Volume too loud, clipping the signal
Diagnostic:
Test with different headphones
Reduce PWM duty cycle (lower volume)
Verify D11 is a PWM-capable pin on your board
The four pinouts
Straight from the repository. Two of them flag open questions in the original wiring; those notes are left in.
node-a-pinout.txtGlasses: sensor on D9 and D10, audio on D11.62 LINESDownload
Third Eye Kit, Node A: Upper Ranging (Glasses)
================================================
MICROCONTROLLER: Arduino Mini Pro (3.3V or 5V)
SENSOR WIRING (HC-SR04 Ultrasonic):
────────────────────────────────────
Sensor Pin | Arduino Pin | Function
─────────────────────────────────────
VCC | +5V | Power (5V)
GND | GND | Ground
TRIG | D9 | Trigger pulse
ECHO | D10 | Echo measurement
AUDIO OUTPUT (3.5mm Headphone Jack):
──────────────────────────────────────
Jack Pin | Arduino Pin | Function
─────────────────────────────────────
Left | D11 | Audio signal (PWM)
Right | D11 | Audio signal (PWM)
Ground | GND | Ground
OPTIONAL HAPTIC (Vibration Motor):
───────────────────────────────────
Motor Pin | Arduino Pin | Function
─────────────────────────────────────
Positive | D6 | Control signal
Negative | GND | Ground
POWER:
──────
Lithium cell (1.5V) or AA battery
Connected to Arduino RAW pin or USB power during development
FIRMWARE CONFIGURATION (node_a.ino):
────────────────────────────────────
#define TRIG_PIN 9 // Trigger pin
#define ECHO_PIN 10 // Echo pin
#define AUDIO_PIN 11 // Audio feedback (PWM)
#define ALERT_CM 70 // Alert distance in cm (TUNE THIS PER USER)
TUNING NOTES:
─────────────
- ALERT_CM typically ranges 50 to 90 cm depending on user height and walking speed
- Start at 70 cm and adjust based on field testing
- Document final value in build log
TESTING:
────────
1. Upload firmware to Arduino Mini
2. Open Serial Monitor (9600 baud)
3. Slowly move obstacle (hand, book) across sensor cone
4. Verify audio tone plays when distance < ALERT_CM
5. Adjust ALERT_CM until feedback distance feels natural for user
TROUBLESHOOTING:
────────────────
- No audio output: Check headphone jack connection, verify D11 is PWM-capable
- Distance always 0: Check HC-SR04 power and pin connections
- Erratic readings: Sensor may be damaged; try spare HC-SR04
node-b-pinout.txtShoes: two sensors, two motors, the light sensor on A0, the LED on D7.92 LINESDownload
Third Eye Kit, Node B: Lower Ranging (Shoes)
==============================================
MICROCONTROLLER: Arduino Mini Pro (5V)
LEFT SHOE SENSOR WIRING (HC-SR04 Ultrasonic):
────────────────────────────────────────────
Sensor Pin | Arduino Pin | Function
─────────────────────────────────────
VCC | +5V | Power (5V) [see NOTE below]
GND | GND | Ground
TRIG | D9 | Trigger pulse
ECHO | D10 | Echo measurement
RIGHT SHOE SENSOR WIRING (HC-SR04 Ultrasonic):
───────────────────────────────────────────────
Sensor Pin | Arduino Pin | Function
─────────────────────────────────────
VCC | +5V | Power (5V) [see NOTE below]
GND | GND | Ground
TRIG | D3 | Trigger pulse
ECHO | D4 | Echo measurement
HAPTIC FEEDBACK (Vibration Motors, left + right):
──────────────────────────────────────────────────
Motor Pin | Arduino Pin | Function
─────────────────────────────────────────
Left Positive | D5 | Control signal (PWM)
Left Negative | GND | Ground
Right Positive | D6 | Control signal (PWM)
Right Negative | GND | Ground
PASSIVE VISIBILITY (LDR + LED):
─────────────────────────────────
Component | Arduino Pin | Function
─────────────────────────────────────
LDR | A0 | Ambient light sensing (analog input)
LED | D7 | Passive visibility (digital output)
GND | GND | Ground (shared)
POWER:
──────
5V source (Arduino Mini Pro 5V variant per node_b.ino)
Connected to Arduino RAW pin or USB power during development
FIRMWARE CONFIGURATION (node_b.ino):
────────────────────────────────────
#define LEFT_TRIG 9 // Left shoe trigger pin
#define LEFT_ECHO 10 // Left shoe echo pin
#define RIGHT_TRIG 3 // Right shoe trigger pin
#define RIGHT_ECHO 4 // Right shoe echo pin
#define LEFT_HAPTIC 5 // Left haptic motor (PWM)
#define RIGHT_HAPTIC 6 // Right haptic motor (PWM)
#define LDR_PIN A0 // Ambient light sensor (analog)
#define LED_PIN 7 // Passive visibility LED
#define ALERT_CM_LEFT 60 // Left alert distance in cm (TUNE PER USER)
#define ALERT_CM_RIGHT 60 // Right alert distance in cm (TUNE PER USER)
#define LED_THRESHOLD 300 // Ambient light threshold (0-1023 ADC)
TUNING NOTES:
─────────────
- ALERT_CM_LEFT / ALERT_CM_RIGHT typically range 50 to 60 cm depending on user's stride
- Start at 60 cm and adjust based on field testing
- LED_THRESHOLD (300) controls the ambient-light level at which the passive
visibility LED turns on; adjust per typical deployment lighting
- Document final values in build log
TESTING:
────────
1. Upload firmware to Arduino Mini
2. Open Serial Monitor (9600 baud)
3. Sweep an obstacle (hand, book, curb edge) under each shoe sensor cone
4. Verify LEFT/RIGHT haptic motor activates when distance < ALERT_CM_LEFT/RIGHT
5. Cover the LDR to confirm the passive LED turns on below LED_THRESHOLD
6. Adjust ALERT_CM_LEFT/RIGHT until feedback distance feels natural for user
TROUBLESHOOTING:
─────────────────
- No haptic feedback: check LEFT_HAPTIC (D5) / RIGHT_HAPTIC (D6) wiring and motor driver
- Distance always 0: check HC-SR04 power and TRIG/ECHO pin connections (D9/D10 left, D3/D4 right)
- LED never lights: verify LDR wiring on A0 and LED_THRESHOLD against ambient conditions
- Erratic readings: sensor may be damaged; try spare HC-SR04
NOTE:
─────
- BUILD.md's Node B pinout section documents TRIG/ECHO/haptic/LDR/LED pin
assignments but does not restate HC-SR04 VCC/GND wiring for this node.
The VCC (+5V) / GND rows above follow the same HC-SR04 wiring pattern
documented for Node A (node-a-pinout.txt) and the 5V platform stated in
node_b.ino's header comment, not independently confirmed per-node in
BUILD.md. Verify against your board before flashing.
node-c-pinout.txtGloves: sensor on D9 and D10, motor on D5. Flags a 3.3 V supply question.69 LINESDownload
Third Eye Kit, Node C: Manual/Tactile Scanning (Gloves)
==========================================================
MICROCONTROLLER: Arduino Mini Pro (3.3V)
STATUS: [IN DEV], Glove enclosure not yet fabricated (per node_c.ino)
SENSOR WIRING (HC-SR04 Ultrasonic, hand-held):
────────────────────────────────────────────────
Sensor Pin | Arduino Pin | Function
─────────────────────────────────────
VCC | +5V | Power (5V) [see NOTE below]
GND | GND | Ground
TRIG | D9 | Trigger pulse
ECHO | D10 | Echo measurement
HAPTIC (Vibration Motor, low intensity):
───────────────────────────────────────────
Motor Pin | Arduino Pin | Function
─────────────────────────────────────
Positive | D5 | Control signal
Negative | GND | Ground
POWER:
──────
Platform per node_c.ino header comment: Arduino Mini Pro (3.3V)
Connected to Arduino RAW pin or USB power during development
FIRMWARE CONFIGURATION (node_c.ino):
────────────────────────────────────
#define TRIG_PIN 9 // Trigger pin
#define ECHO_PIN 10 // Echo pin
#define HAPTIC_PIN 5 // Haptic motor (low intensity)
#define ALERT_CM 40 // Alert distance in cm (arm's reach)
TUNING NOTES:
─────────────
- ALERT_CM (40 cm) targets arm's-reach detection for hand-guided exploration
- BUILD.md does not give a documented tuning range for Node C (unlike Nodes
A/B); adjust based on field testing and document the final value in the
build log
- Sample rate is higher than A/B (50 ms loop delay in node_c.ino) for more
responsive hand-guided scanning
TESTING:
────────
1. Upload firmware to Arduino Mini
2. Open Serial Monitor (9600 baud)
3. Slowly move obstacle (hand, book) across sensor cone
4. Verify haptic pulse fires when distance < ALERT_CM
5. Adjust ALERT_CM until feedback distance feels natural for hand-guided use
TROUBLESHOOTING:
─────────────────
- No haptic feedback: check HAPTIC_PIN (D5) wiring and motor driver
- Distance always 0: check HC-SR04 power and pin connections (D9 TRIG / D10 ECHO)
- Erratic readings: sensor may be damaged; try spare HC-SR04
NOTE:
─────
- BUILD.md's Node C pinout section documents TRIG/ECHO/haptic pin
assignments but does not state the HC-SR04's supply voltage for this node.
The VCC (+5V) row above follows the same HC-SR04 wiring pattern documented
for Node A (node-a-pinout.txt), since HC-SR04 modules are commonly rated
for 5V. This is a real ambiguity worth flagging: node_c.ino's own header
comment states the platform is "Arduino Mini Pro (3.3V)" only (not "3.3V
or 5V" like Node A), so powering a 5V-rated sensor on a 3.3V-only board
may require a regulator or level-shifting that is not documented anywhere
in this repo. Verify actual sensor voltage tolerance before building.
node-d-pinout.txtTelemetry: GPS on the hardware serial, GSM on D2 and D3, power key on D8.70 LINESDownload
Third Eye Kit, Node D: Location Telemetry (Belt/Pack)
=========================================================
MICROCONTROLLER: Arduino Mini Pro (5V)
GPS MODULE WIRING (NEO-6M):
──────────────────────────────
Module Pin | Arduino Pin | Function
─────────────────────────────────────
RX | D0 | Hardware Serial [see NOTE below]
TX | D1 | Hardware Serial [see NOTE below]
VCC | +3.3V | Power
GND | GND | Ground
GSM MODULE WIRING (SIM800-class):
────────────────────────────────────
Module Pin | Arduino Pin | Function
─────────────────────────────────────
RX | D2 | SoftwareSerial RX
TX | D3 | SoftwareSerial TX
VCC | +4.2V (regulated) | Power
GND | GND | Ground
KEY | D8 | Power control (module on/off)
POWER:
──────
Lithium cell (per node_d.ino header comment)
GSM module requires a separately regulated +4.2V supply (per BUILD.md);
do not power SIM800-class modules directly from an unregulated cell.
FIRMWARE CONFIGURATION (node_d.ino):
────────────────────────────────────
#define GPS_BAUD 9600 // GPS (NEO-6M) baud rate, hardware Serial
#define GSM_RX_PIN 2 // GSM SoftwareSerial RX
#define GSM_TX_PIN 3 // GSM SoftwareSerial TX
#define GSM_BAUD 9600 // GSM (SIM800) baud rate
#define GSM_KEY_PIN 8 // GSM module power-key control
PROVISIONING NOTES:
────────────────────
- Authorized phone numbers are configured in firmware, not wiring, see
BUILD.md Step 5.2 (AUTHORIZED[] allowlist) before field deployment
- Verify 2G coverage in the deployment area before relying on this node
(BUILD.md Step 5.1 / 5.3)
TESTING:
────────
1. Upload firmware to Arduino Mini
2. Open Serial Monitor (9600 baud)
3. Power on Node D with SIM inserted
4. Watch for GPS fix and "GSM: registered on 2G" status
5. Trigger a location request by calling/SMSing from an authorized number
6. Expect a reply within 30 seconds with GPS coordinates
TROUBLESHOOTING:
─────────────────
- No GSM response: check GSM_KEY_PIN (D8) toggling and module power supply (+4.2V)
- No GPS fix: check VCC (+3.3V)/GND wiring and antenna placement outdoors
- SMS not sent: verify AUTHORIZED[] contains the requesting number
NOTE:
─────
- Both node_d.ino's inline comment ("GPS Module (NEO-6M), RX = pin 0
(hardware Serial), TX = pin 1") and BUILD.md describe the GPS module on
D0/D1 (the AVR's single hardware UART). node_d.ino also calls
Serial.begin(9600) on that same hardware UART for USB debug logging.
Neither BUILD.md nor node_d.ino documents how GPS traffic and USB debug
logging share that one hardware serial port without colliding, this is
a genuine gap in the existing docs/source, not resolved here.
The firmware
Five files, plain Arduino C.
One shared header does the ranging. Each node's sketch owns its pins, its threshold, and its feedback. Read them here, or download them from this site.
ranging.hShared by three nodes. A 10 microsecond trigger, an echo pulse timed with pulseIn, and pulse width over 58 gives centimeters. Returns -1 on a timeout or an impossible reading.48 LINESDownload
/**
* ranging.h, Shared ultrasonic time-of-flight ranging
*
* getDistance() is identical across Node A (glasses), Node B (shoes),
* and Node C (gloves), all three use a single HC-SR04-class sensor
* read per call. Extracted here so the three nodes stay in sync.
*
* Round-trip formula: distance (cm) = pulseWidth (µs) / 58
* (Speed of sound: ~343 m/s = 0.0343 cm/µs = 1 cm per 29 µs round-trip)
*/
#pragma once
// Sensor timeout (prevent infinite waits)
#define SENSOR_TIMEOUT 30000 // 30 ms in microseconds
/**
* getDistance(), Measure distance using ultrasonic time-of-flight
*
* Returns distance in cm, or -1 on timeout / out-of-range reading
* (valid range: 2 cm to 400 cm, per HC-SR04 datasheet).
*/
inline float getDistance(int trigPin, int echoPin) {
// Send 10 µs trigger pulse
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Measure echo pulse width
long pulseDuration = pulseIn(echoPin, HIGH, SENSOR_TIMEOUT);
// Invalid measurement (sensor not responding or timeout)
if (pulseDuration == 0) {
return -1; // Return negative to indicate error
}
// Convert pulse duration to distance
float distance = pulseDuration / 58.0;
// Clamp to reasonable range (2 cm to 400 cm)
if (distance < 2 || distance > 400) {
return -1;
}
return distance;
}
node_a.inoGlasses. Reads the sensor ten times a second and plays a 440 Hz tone in the headphone when something is inside 70 cm.86 LINESDownload
/**
* Third Eye Kit, Node A: Upper Ranging (Glasses/Headwear)
*
* Detects obstacles at/above chest height.
* Feedback: Local audio cue through headphone jack.
* Status: [IMPLEMENTED]
*
* Platform: Arduino Mini Pro (3.3V or 5V variant)
* Sensor: HC-SR04 Ultrasonic
* Power: Lithium cell (1.5V × 1) or AA battery
*/
#include "../common/ranging.h"
// Pin Configuration
#define TRIG_PIN 9 // Trigger pin for HC-SR04
#define ECHO_PIN 10 // Echo pin for HC-SR04
#define AUDIO_PIN 11 // Audio feedback (PWM-capable)
// Configuration (tunable per user and environment)
#define ALERT_CM 70 // Alert distance in centimeters (~70 cm for head-height obstacle)
// Audio feedback configuration
#define AUDIO_FREQ 440 // Hz (A4 note)
#define AUDIO_DURATION 200 // ms
// Forward declarations
void playAudio(int pin, int freq, int duration);
void setup() {
Serial.begin(9600);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(AUDIO_PIN, OUTPUT);
// Optional: indicator LED to show power-on
digitalWrite(AUDIO_PIN, LOW);
Serial.println("Node A: Upper Ranging System");
Serial.print("Alert distance: ");
Serial.print(ALERT_CM);
Serial.println(" cm");
Serial.println("Waiting for obstacles...");
}
void loop() {
// Get distance from ultrasonic sensor
float distance = getDistance(TRIG_PIN, ECHO_PIN);
// Check if obstacle is within alert range
if (distance > 0 && distance < ALERT_CM) {
// Obstacle detected, play audio alert
playAudio(AUDIO_PIN, AUDIO_FREQ, AUDIO_DURATION);
Serial.print("ALERT: Obstacle at ");
Serial.print(distance);
Serial.println(" cm");
} else if (distance > 0) {
// Optional: log distance periodically for diagnostics
// Serial.print("Distance: "); Serial.println(distance);
}
delay(100); // Sampling interval (10 Hz)
}
/**
* playAudio(), Generate audio tone feedback via PWM
*
* Note: Simple square-wave tone. For better audio quality,
* use hardware-based tone generation or pre-recorded samples.
*/
void playAudio(int pin, int freq, int duration) {
int period = 1000000 / freq; // Period in microseconds
int halfPeriod = period / 2;
long endTime = millis() + duration;
while (millis() < endTime) {
digitalWrite(pin, HIGH);
delayMicroseconds(halfPeriod);
digitalWrite(pin, LOW);
delayMicroseconds(halfPeriod);
}
digitalWrite(pin, LOW); // Ensure pin is low after audio stops
}
node_b.inoShoes. Two sensors, two motors, alert at 60 cm on each side. Reads the light sensor and switches the LED on in the dark.96 LINESDownload
/**
* Third Eye Kit, Node B: Lower Ranging (Shoes)
*
* Dual ultrasonic sensors for left and right foot.
* Detects ground-plane hazards (curbs, holes, steps).
* Feedback: Haptic vibration motors + passive LED visibility.
* Status: [IMPLEMENTED]
*
* Platform: Arduino Mini Pro (5V)
* Sensors: 2 × HC-SR04 Ultrasonic
* Actuators: 2 × Vibration motors, LDR + LED
*/
#include "../common/ranging.h"
// Left Shoe Sensor
#define LEFT_TRIG 9
#define LEFT_ECHO 10
// Right Shoe Sensor
#define RIGHT_TRIG 3
#define RIGHT_ECHO 4
// Haptic & LED
#define LEFT_HAPTIC 5 // PWM
#define RIGHT_HAPTIC 6 // PWM
#define LDR_PIN A0 // Analog input
#define LED_PIN 7 // Digital output
// Configuration
#define ALERT_CM_LEFT 60 // cm, tune per user stride
#define ALERT_CM_RIGHT 60 // cm
#define LED_THRESHOLD 300 // Ambient light threshold (0-1023 ADC)
void activateHaptic(int pin, int duration);
void setup() {
Serial.begin(9600);
// Sensor pins
pinMode(LEFT_TRIG, OUTPUT);
pinMode(LEFT_ECHO, INPUT);
pinMode(RIGHT_TRIG, OUTPUT);
pinMode(RIGHT_ECHO, INPUT);
// Haptic & LED
pinMode(LEFT_HAPTIC, OUTPUT);
pinMode(RIGHT_HAPTIC, OUTPUT);
pinMode(LED_PIN, OUTPUT);
Serial.println("Node B: Lower Ranging System (Dual Shoe)");
Serial.print("Left alert: ");
Serial.print(ALERT_CM_LEFT);
Serial.println(" cm");
Serial.print("Right alert: ");
Serial.print(ALERT_CM_RIGHT);
Serial.println(" cm");
}
void loop() {
float leftDist = getDistance(LEFT_TRIG, LEFT_ECHO);
float rightDist = getDistance(RIGHT_TRIG, RIGHT_ECHO);
// Left shoe hazard detection
if (leftDist > 0 && leftDist < ALERT_CM_LEFT) {
activateHaptic(LEFT_HAPTIC, 100);
Serial.print("LEFT: ");
Serial.print(leftDist);
Serial.println(" cm");
}
// Right shoe hazard detection
if (rightDist > 0 && rightDist < ALERT_CM_RIGHT) {
activateHaptic(RIGHT_HAPTIC, 100);
Serial.print("RIGHT: ");
Serial.print(rightDist);
Serial.println(" cm");
}
// Ambient light detection for passive visibility (LDR + LED)
int ambientLight = analogRead(LDR_PIN);
if (ambientLight < LED_THRESHOLD) {
digitalWrite(LED_PIN, HIGH); // Turn on LED in darkness
} else {
digitalWrite(LED_PIN, LOW);
}
delay(100); // Sampling interval (10 Hz)
}
void activateHaptic(int pin, int duration) {
digitalWrite(pin, HIGH);
delay(duration);
digitalWrite(pin, LOW);
}
node_c.inoGloves, in development. A 40 cm reach and a very short palm pulse, sampled twenty times a second.48 LINESDownload
/**
* Third Eye Kit, Node C: Manual/Tactile Scanning (Gloves)
*
* Near-field obstacle detection for hand-guided exploration.
* Feedback: Low-intensity haptic.
* Status: [IN DEV], Glove enclosure not yet fabricated.
*
* Platform: Arduino Mini Pro (3.3V)
* Sensor: 1 × HC-SR04 Ultrasonic
* Actuator: 1 × Vibration motor
*/
#include "../common/ranging.h"
#define TRIG_PIN 9
#define ECHO_PIN 10
#define HAPTIC_PIN 5
// Near-field range (arm's reach)
#define ALERT_CM 40 // cm, detect objects within arm's reach
void setup() {
Serial.begin(9600);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(HAPTIC_PIN, OUTPUT);
Serial.println("Node C: Manual/Tactile Scanning [IN DEV]");
Serial.println("Glove enclosure design pending.");
}
void loop() {
float distance = getDistance(TRIG_PIN, ECHO_PIN);
if (distance > 0 && distance < ALERT_CM) {
// Low-intensity haptic pulse
digitalWrite(HAPTIC_PIN, HIGH);
delayMicroseconds(50); // Very short pulse for subtle feedback
digitalWrite(HAPTIC_PIN, LOW);
Serial.print("Object at ");
Serial.print(distance);
Serial.println(" cm (hand range)");
}
delay(50); // Higher sample rate for hand-guided exploration
}
node_d.inoTelemetry. Powers up the SIM800, sets SMS text mode, polls for messages from allowed numbers, and sends the coordinates back. As published, the GPS parsing is a comment: the file calls itself a skeleton.185 LINESDownload
/**
* Third Eye Kit, Node D: Location Telemetry (Belt/Pack)
*
* GPS positioning + GSM location reporting to authorized family members.
* Feedback: Outbound SMS with coordinates.
* Status: [IMPLEMENTED], Call-triggered; see consent notes below.
*
* Platform: Arduino Mini Pro (5V)
* Modules: NEO-6M GPS + SIM800L GSM
* Power: Lithium cell
*
* PRIVACY & CONSENT:
* This node responds to inbound requests from authorized numbers.
* See docs/CONSENT_ARCHITECTURE.md for design rationale and limitations.
* Current v2 baseline: answers inbound requests.
* Roadmap: wearer-controlled toggle to milestone all location sharing.
*/
#include <SoftwareSerial.h>
// GPS Module (NEO-6M)
// RX = pin 0 (hardware Serial), TX = pin 1
#define GPS_BAUD 9600
// GSM Module (SIM800L)
#define GSM_RX_PIN 2
#define GSM_TX_PIN 3
#define GSM_BAUD 9600
#define GSM_KEY_PIN 8
SoftwareSerial gsmSerial(GSM_RX_PIN, GSM_TX_PIN); // RX, TX
// Authorized phone numbers (one-time configuration)
const char* AUTHORIZED[] = {
"+93799123456", // Family member 1 (update with real number)
"+93799654321" // Family member 2 (update with real number)
};
const int AUTH_COUNT = 2;
// Global state
float lastLat = 0, lastLon = 0;
bool gpsFixed = false;
void setup() {
Serial.begin(9600); // Debug serial (USB)
gsmSerial.begin(GSM_BAUD); // GSM module
pinMode(GSM_KEY_PIN, OUTPUT);
digitalWrite(GSM_KEY_PIN, LOW);
Serial.println("Node D: Location Telemetry System");
Serial.println("Initializing GPS and GSM modules...");
// Power on GSM module
initializeGSM();
// Note: Real implementation would initialize GPS and wait for first fix
// This is a skeleton; full implementation requires GPS NMEA parsing
}
void loop() {
// Poll for incoming SMS from authorized numbers
checkIncomingSMS();
// Update GPS periodically (every 30 seconds in production)
static unsigned long lastGPSUpdate = 0;
if (millis() - lastGPSUpdate > 30000) {
updateGPSPosition();
lastGPSUpdate = millis();
}
delay(100);
}
/**
* initializeGSM(), Power on and configure GSM module
*/
void initializeGSM() {
// Toggle power pin (at least 1 second)
digitalWrite(GSM_KEY_PIN, HIGH);
delay(1000);
digitalWrite(GSM_KEY_PIN, LOW);
delay(2000); // Wait for module to boot
// Check if module responds
sendAT("AT");
// Expected: "OK"
// Disable command echo
sendAT("ATE0");
// Set SMS text mode
sendAT("AT+CMGF=1");
Serial.println("GSM module initialized.");
}
/**
* checkIncomingSMS(), Poll for incoming SMS
*/
void checkIncomingSMS() {
// In production: parse unsolicited +CMT notifications
// Simplified skeleton: check periodically for new messages
sendAT("AT+CMGL=\"REC UNREAD\"");
// If message received from authorized number, reply with location
// See CONSENT_ARCHITECTURE.md for privacy design rationale
}
/**
* updateGPSPosition(), Read GPS coordinates
*/
void updateGPSPosition() {
// In production: parse NMEA frames from NEO-6M
// Example: $GPRMC,hhmmss.ss,status,lat,N/S,lon,E/W,...
// Simplified skeleton; real implementation uses full NMEA parser
// For now, log that GPS update was attempted
Serial.println("GPS: Checking for fix...");
// When fix is obtained, send to authorized requester
// sendLocationSMS("+93799123456", lastLat, lastLon);
}
/**
* sendLocationSMS(), Send GPS coordinates via SMS
*/
void sendLocationSMS(const char* phoneNumber, float lat, float lon) {
// Compose SMS with coordinates
String message = "Location: ";
message += lat;
message += ",";
message += lon;
// Send SMS command
String command = "AT+CMGS=\"";
command += phoneNumber;
command += "\"";
sendAT(command);
// Send message content followed by Ctrl+Z (0x1A)
gsmSerial.print(message);
gsmSerial.write(0x1A);
Serial.print("Location sent to ");
Serial.println(phoneNumber);
}
/**
* sendAT(), Send AT command and read response
*/
void sendAT(const char* command) {
gsmSerial.println(command);
// Wait for response (simplified; real implementation has timeout)
delay(500);
// Read response
while (gsmSerial.available()) {
Serial.write(gsmSerial.read());
}
}
// ============================================================================
// CONSENT DESIGN RATIONALE
// ============================================================================
//
// The v2 prototype answers inbound requests from authorized numbers.
// This is convenient but NOT a full consent milestone.
//
// Limitations (v2):
// - Wearer cannot disable sharing while carrying the device
// - Abusive caller can query location repeatedly
// - Family member could disable without wearer's knowledge (if device is accessible)
//
// Roadmap [PLANNED]:
// - Physical toggle on device body (wearer controls)
// - Firmware allowlist (only pre-authorized numbers can request)
// - Rate limiting (prevent continuous location tracking)
// - No remote provisioning (no way to add numbers without physical access)
//
// See docs/CONSENT_ARCHITECTURE.md for full design.
The repository marks Node D as implemented and lists its GPS fix and 2G text as validated in the field. The sketch it publishes is a skeleton whose GPS parsing is a comment. We show the file as published and claim no more than the repository does.
What it got
It worked, and the record says exactly how much.
The core loop, sense then alert, was demonstrated on hardware with blind and visually impaired people at the blind support centre, and their feedback set the alert distances. The record says exactly how far it went: tens of first generation kits built and given out by the team, hundreds more built by the people who needed them from the open design, tested in Herat and Turkey, and a TEKNOFEST 2022 finalist with Best Presentation.
Used in the field
Near-range detection of static, unstructured obstacles, above and below.
Haptic and local audio alerts together, at a tunable threshold of about 50 to 70 cm.
Two feedback channels that survive loud streets and markets.
A standalone GPS fix and a 2G location text, with no broadband, Wi-Fi, or phone.
A passive after-dark marker: the LED comes on by itself when the light drops.
Not validated
An announced response to a sensor or power failure. The baseline fails silent; a fault layer was planned for a later revision.
A full consent milestone held by the wearer. The baseline answers any allowed number.
Rain and dust. No waterproofing, no ingress rating.
Crowds and traffic. One threshold and one ultrasonic cone were never meant for them.
Soft fabrics, some glass, and sound-absorbing surfaces.
Long-term use: nobody measured whether people kept wearing it.
Any formal safety certification.
What the record does not contain
The repository publishes no counts. No number of testers, no collision rate, no session logs, no retention figure. It lists the capabilities that were demonstrated and the ones that were not, and it calls that list a promise, not a disclaimer. We claim exactly that much.
The record, dated
From the support centre to the second generation.
Where the first generation came from, who tested it, and when both generations were published. Every date links to its source.
BEFORE 2022, INTO USE IN 2022
Born at a blind support centre.
Said Mohaddes Sadeqi builds the first Third Eye Kit at the blind support centre where he was after losing his sight: ultrasonic ranging at the glasses and the shoes, warnings by sound and vibration, and a 2G text home to family with the wearer's location. He brings it to his school and enters TEKNOFEST 2022, Black Sea, in the Barrier-Free Living category under the team name ABRT01: finalist, and Best Presentation.
Anadolu Agency reports on a student's third eye project for blind people. Faktor, in Bosnia and Herzegovina, also reports on the founder and the device he built.
Two hardware revisions, demonstrated with blind and visually impaired people at the centre, whose feedback set the alert distances, feedback types, and fit. The first generation repository's ledger says what was and was not tried: near range alerts with blind users; crowds, traffic, and safety certification not.
The teww.org domain is registered on 14 October 2025 (public registry record; a date from outside the engineering record). The site carries the first-generation write-up and the second-generation spec.
The first-generation repository is created on GitHub on 4 July 2026: spec, firmware, pinouts, bill of materials. The second-generation repository follows on 5 July 2026 (repository dates, from outside the engineering record): the dossier, the safety file, the four schematics. MIT for the code, CC BY 4.0 for the documents.
The reference spec is frozen and the parts are on the bench. The brain tier moves from the Jetson to the wearer's own phone, on a chest mount, integrated with the microcontrollers across the isolation boundary. Bench drawings for the belt, the collar strip, and the ankle bands, the parts list, the placement study, and this engineering record. Firmware and assembly are the current work; the assist frame is planned after the tests.
Testing with more advanced testers taught the lesson that made version 2.
A voice that tells you where to go is too slow, and never precise enough, for a real road. The testers who already moved well did not want to wait for a description. They wanted to feel the world as it happened, the way a hand feels a wall.
That moved the center of the project. Almost everyone builds the robot eyes, a camera and a model that decides what it saw. Almost nobody builds the human input channel: how the information gets into the person fast enough to act on. Version 2 is that channel. A language of vibration across the body, driven by direct measurement, at the speed of a reflex.
Carried into V2, 1
One job per chip became the isolation boundary: safety below the line, everything else above it.
Carried into V2, 2
The fail-silent baseline became rule 4: fail transparent, and nothing fails silently.
Carried into V2, 3
The consent gap became a rule for the phone: nothing leaves the phone by default, and cloud services are optional and opt in (docs/07).
Carried into V2, 4
The text home moved into the phone: the location text home rides the wearer's own phone in the second generation, with no modem, no SIM, and no airtime to pay for (docs/07).
All of version 1 lives in the founder's repository, MIT for the firmware and the hardware, CC BY 4.0 for the documents. The repository numbers its own iterations v1 and v2; on this site the whole ultrasonic kit is version 1.