I am blind or visually impaired: the story in one minute
Third Eye Kit

Engineering, version 1, first generation

The tested prototype.

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.

#ItemQuantityNotes
1AVR microcontroller, Arduino Mini class4One independent processor per node
2Ultrasonic sensor, HC-SR04 class5Node A: 1, Node B: 2, Node C: 1, spare: 1
3GPS module, NEO-6M class1Node D only
4GSM module, SIM800 class1Node D only, 2G chipset
52G SIM card1Pre-activated, regional provider
6LED lights, 3 mm, assorted1 packNode B visibility marker
7LDR module2Node B ambient light trigger
8Vibration motor, ERM coin4Nodes A, B (two), and C
9Cells, AA, 1.5 V4One per node; 4 to 6 hours was the target
10Footwear chassis (shoes)1 pairNode B mount
11Eyewear chassis (glasses)1Node A mount
12Jumper wires3 packsBreadboard and node interconnect
13Breadboard, 830 hole2Prototyping
14USB power adapter, micro USB1Bench testing and flashing
15Headphone jack, 3.5 mm, with cable1Node 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)
  • Hardware, see Bill of Materials
  • 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.

Choose One:

  • Node A, Upper Ranging (glasses / headwear)
  • Node B, Lower Ranging (shoes, left + right)
  • Node C, Manual/Tactile (gloves), [IN DEV]
  • Node D, Location Telemetry (belt/pack GPS + GSM), [IMPLEMENTED]

Step 3: Tune the Threshold

ALERT_CM is the single most safety-relevant configuration value.

Edit the threshold constant in the firmware before flashing:

// Upper ranging (Node A), head/torso hazards
#define ALERT_CM 70  // cm; adjust per environment and user gait

// Lower ranging (Node B), ground-plane hazards
#define ALERT_CM_LEFT  60   // left shoe; tune to user's stride
#define ALERT_CM_RIGHT 60   // right shoe

Tuning Guide

  • Start: 70 cm for upper ranging, 50 to 60 cm for lower
  • Field-test: obstacle sweep at walking speed; note feedback distance
  • User feedback: ask wearer to adjust threshold until they can react comfortably
  • Document: record final ALERT_CM per node and per user

Step 4: Flash the Microcontroller

Via Arduino IDE

  1. Open firmware/node-X/node_X.ino
  2. Select Board, Tools → Board → (your AVR board, e.g., Arduino Mini)
  3. Select Port, Tools → Port → /dev/tty.usbserial-... (or COM port on Windows)
  4. Upload, Sketch → Upload (or Ctrl+U)

Watch the RX/TX LEDs blink. If the upload succeeds, you'll see:

Sketch uses 2,948 bytes of program storage space.
Maximum is 30,720 bytes; program space (xxx %).

avrdude done. Thank you.

Via Arduino CLI

arduino-cli compile --fqbn arduino:avr:mini firmware/node-a-glasses/
arduino-cli upload -p /dev/tty.usbserial-123456 --fqbn arduino:avr:mini firmware/node-a-glasses/

Step 5: Provision Node D (Telemetry Only)

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)

  1. Power on the node
  2. Sweep an obstacle (hand, book, wall) slowly across the sensor cone
  3. Observe feedback:
    • Haptic motor vibrates when distance < ALERT_CM
    • Audio tone / buzzer activates simultaneously
  4. Adjust ALERT_CM if feedback distance feels too close or too far
  5. 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)

  1. Open Serial Monitor, Tools → Serial Monitor (9600 baud)
  2. Power on Node D with SIM inserted
  3. Watch for:
    GPS: waiting for fix...
    GPS: fixed (lat, lon)
    GSM: registered on 2G
    Ready for location requests.
    
  4. Trigger a location request by calling/SMSing the device from an authorized number
  5. 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:

  1. Wiring error, confirm TRIG/ECHO pins against node pinout
  2. Power starved, sensor may need 5V, not 3.3V; check microcontroller supply
  3. Sensor damaged, replace with spare HC-SR04

Diagnostic:

void setup() {
  Serial.begin(9600);
  pinMode(9, OUTPUT);  // TRIG
  pinMode(10, INPUT);  // ECHO
}
void loop() {
  digitalWrite(9, LOW);
  delayMicroseconds(2);
  digitalWrite(9, HIGH);
  delayMicroseconds(10);
  digitalWrite(9, LOW);
  long duration = pulseIn(10, HIGH, 30000);  // 30 ms timeout
  Serial.println(duration);
  delay(100);
}

If duration is always 0, the echo pin is not receiving a signal.

Symptom: Feedback Fires Constantly

Possible causes:

  1. ALERT_CM set too high (e.g., 200 cm), adjust downward
  2. Sensor is miscalibrated, may be reading ghosts from reflections
  3. 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:

  1. Headphone jack not fully inserted
  2. PWM pin (D11 for Node A) not capable of audio-rate modulation
  3. 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.

  1. 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.

  2. SEPTEMBER 2022

    National coverage.

    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.

  3. 2022 AND AFTER

    Field tests at the blind support centre.

    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.

  4. OCTOBER 2025

    The organization opens its site.

    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.

  5. JULY 2026

    Both generations published.

    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.

  6. NOW

    The second-generation build.

    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.

Where it went

People did not want to wait to be told.

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).

Version 2The build.Read the dossier, the schematics, the parts, the milestones

Source

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.

sadeqisaidmohaddes-star/third-eye-kit