A beam that breaks
A TK63 Infrared Transmitter opposite the TK64 makes a break-beam. The sketch reads SIGNAL with the emitter off, then on, and keeps only the difference: that is the beam alone, whatever the room's infrared is doing. It learns the clear beam at start-up and reports broken when the difference falls under half of it. Sunlight that pins SIGNAL at the ceiling blinds it.
Two blocks, face to face
The TK63 is an infrared LED at 940 nm, the sensor's own peak, switched by a transistor: SIGNAL HIGH and it shines. Both blocks carry their dome standing straight up off the board, so to make a beam, stand them on edge with the domes facing each other, a few centimetres apart to start.
Wire the TK64 as in the first reading. Wire the TK63 the same way, GND and VCC beside the TK64's, and its SIGNAL to a digital output: GPIO 6 on an ESP32-S3, GPIO 22 on an ESP32, D3 on an Uno, GP17 on a Pico, the pins the TK63's own handbook uses.
Measure the room, then the beam
A single reading with the emitter on cannot tell the beam from a sunny window: the sensor adds up every bit of infrared in view. So each round of the sketch reads twice.
Run it with Nothing in the beam. The first reading, with the TK63 off, is the room. The second, with it on, is the room and the beam. On minus off is the beam alone: the room is in both readings and cancels. Run it again with A hand: the room reading is the same, the beam is mostly gone, and the difference drops under the limit.
The limit is half of what the sketch measured at start-up with the path clear. Half leaves room for the beam to wander a little without a false alarm, and a real obstruction takes far more than half.
What subtraction cannot fix
Run it with Nothing, in sunlight. The room reading is already at the ceiling, so switching the beam on changes nothing, the difference is 0, and the sketch reports a break that is not there. Subtracting cancels a bright room; it cannot rescue a sensor that has run out of room. The same happens, more gently, above the LED's knee, where each microamp moves SIGNAL a tenth as far: a sensor sitting in strong light sees a smaller beam.
So shade the sensor: a short black tube over the dome, pointing at the emitter, cuts the room's infrared far more than the beam's.
Waits and speed
The sketch waits 2 ms after each switch. The sensor itself settles in about a tenth of a millisecond here, worked out from the datasheet's timing, so 2 ms is generous. A round and the 10 ms pause after it take under 20 ms, so anything that stays in the beam longer than that is caught.
The code
No library. Each round switches the TK63 off, waits 2 ms and reads the room; switches it on, waits 2 ms and reads the room and the beam; and keeps on minus off. At start-up it takes 20 rounds with the path clear and remembers the mean. A round that gives under half of that is a broken beam.
/*
Infrared Receiver - a beam that breaks TK64 / /p/tk64
A TK63 Infrared Transmitter sends the beam; this TK64 reads it.
Stand them face to face, a few centimetres apart to start.
Wiring, TK64. Count from the square pad on the TinkerBlock board,
parts up, header at the bottom:
GND -> GND
VCC -> 3V3 on an ESP32, ESP32-S3 or Pico; 5V on an Uno.
Your board's logic voltage: strong infrared takes
SIGNAL to about VCC - 0.4 V.
NC -> nothing (unconnected on the board)
SIGNAL -> GPIO 4 on an ESP32-S3, GPIO 34 on an ESP32,
A0 on an Uno, GP26 on a Raspberry Pi Pico
Wiring, TK63: GND to GND, VCC to the same supply pin as the
TK64's, NC to nothing, and its SIGNAL to GPIO 6 on an ESP32-S3,
GPIO 22 on an ESP32, D3 on an Uno, GP17 on a Pico.
Arduino IDE
Tools > Board your board, e.g. ESP32S3 Dev Module
Tools > Port the one that appears when you plug in
Tools > USB CDC On Boot Enabled (ESP32-S3 only)
No library needed.
*/
// The analog pin the TK64's SIGNAL is wired to.
// Uno: A0. ESP32: 34. ESP32-S3: 4. Pico: 26.
const int IR_PIN = 4;
// The pin the TK63's SIGNAL is wired to. HIGH turns its beam on.
// Uno: 3. ESP32: 22. ESP32-S3: 6. Pico: 17.
const int TX_PIN = 6;
const int SETTLE_MS = 2; // let the sensor settle after a switch
int clearBeam = 0; // the beam, measured with the path clear
bool wasBroken = false;
int readIr() {
long sum = 0;
for (int i = 0; i < 8; i++) {
sum += analogRead(IR_PIN);
}
return sum / 8;
}
// One round: the room alone, then the room and the beam.
int beam() {
digitalWrite(TX_PIN, LOW);
delay(SETTLE_MS);
int off = readIr(); // the room's own infrared
digitalWrite(TX_PIN, HIGH);
delay(SETTLE_MS);
int on = readIr(); // the room and the beam
digitalWrite(TX_PIN, LOW);
return on - off; // the beam alone
}
void setup() {
Serial.begin(115200);
pinMode(TX_PIN, OUTPUT);
delay(500);
// Learn the beam with nothing in the way.
long sum = 0;
for (int i = 0; i < 20; i++) {
sum += beam();
}
clearBeam = sum / 20;
Serial.print("clear beam: ");
Serial.println(clearBeam);
if (clearBeam < 20) {
Serial.println("weak beam: move closer, aim, or shade it");
}
}
void loop() {
// Under half the clear beam: something is in the way.
bool broken = beam() < clearBeam / 2;
if (broken != wasBroken) {
Serial.println(broken ? "beam broken" : "beam clear");
wasBroken = broken;
}
delay(10);
}Keep the path clear while it starts: what it measures then is its idea of clear. TX_PIN switches the TK63 through its own transistor, so any output pin will do. The sketch compiles for an ESP32-S3 and an Uno; on an Uno, IR_PIN is A0 and TX_PIN is 3.
View on GitHub · blocks/tk64-ir-photodiode/arduino/ir_break_beam/ir_break_beam.ino @ v1.5The same round in MicroPython, for an ESP32, an ESP32-S3 or a Pico: off, read, on, read, subtract, then compare with half the clear beam learned at start-up.
"""
Infrared Receiver - a beam that breaks, MicroPython TK64 / /p/tk64
A TK63 Infrared Transmitter sends the beam; this TK64 reads it.
Stand them face to face, a few centimetres apart to start.
Wiring, TK64. Count from the square pad on the TinkerBlock board,
parts up, header at the bottom:
GND -> GND
VCC -> 3V3 (never 5V: strong infrared takes SIGNAL to
about VCC - 0.4 V)
NC -> nothing (unconnected on the board)
SIGNAL -> GPIO 4 on an ESP32-S3, GPIO 34 on an ESP32,
GP26 on a Raspberry Pi Pico
Wiring, TK63: GND to GND, VCC to 3V3, NC to nothing, and its
SIGNAL to GPIO 6 on an ESP32-S3, GPIO 22 on an ESP32, GP17 on a
Pico.
Thonny
Run > Configure interpreter MicroPython (ESP32) or
MicroPython (Raspberry Pi Pico)
Save it to the board as main.py to run it on every power-up.
Nothing to install: machine, sys and time are built in.
"""
import sys
import time
from machine import ADC, Pin
# TK64 SIGNAL, an ADC pin. ESP32: 34. ESP32-S3: 4. Pico: 26.
IR_PIN = 4
# TK63 SIGNAL, HIGH for the beam. ESP32: 22. ESP32-S3: 6. Pico: 17.
TX_PIN = 6
SETTLE_MS = 2 # let the sensor settle
adc = ADC(Pin(IR_PIN))
if sys.platform == "esp32": # ESP32 and ESP32-S3
adc.atten(ADC.ATTN_11DB) # the full range, to about 3.1 V
tx = Pin(TX_PIN, Pin.OUT, value=0)
def read_ir():
return sum(adc.read_u16() for _ in range(8)) // 8
def beam():
"""One round: the room alone, then the room and the beam."""
tx.value(0)
time.sleep_ms(SETTLE_MS)
off = read_ir() # the room's own infrared
tx.value(1)
time.sleep_ms(SETTLE_MS)
on = read_ir() # the room and the beam
tx.value(0)
return on - off # the beam alone
time.sleep_ms(500)
clear = sum(beam() for _ in range(20)) // 20 # nothing in the way
print("clear beam:", clear)
if clear < 300:
print("weak beam: move closer, aim, or shade it")
was_broken = False
while True:
broken = beam() < clear // 2 # under half: something is there
if broken != was_broken:
print("beam broken" if broken else "beam clear")
was_broken = broken
time.sleep_ms(10)There is no Uno here: an Uno cannot run MicroPython. read_u16 counts to 65535, so the weak-beam warning is at 300 counts, about 15 mV. Stop it with Ctrl-C in Thonny's shell; the emitter is left off.
View on GitHub · blocks/tk64-ir-photodiode/micropython/ir_break_beam.py @ v1.5When it does not work
The TK64 is not seeing the TK63. Check the TK63's SIGNAL is on TX_PIN and its VCC and GND are wired, look at its dome through a phone camera for the glow, and stand the blocks closer, domes facing each other. Both look straight up off their boards, so two blocks lying flat side by side do not face each other.
The difference fell under half of what it learned. Either the sensor is at the ceiling (sunlight or a lamp on it: the red LED stays lit), or something moved after start-up. Shade the sensor, then press reset with the path clear so it learns the beam again.
Something lets the beam through or around. Skin lets some infrared through, and at a few centimetres light leaks round a small object. Block it with something opaque and wider, such as a book, or move the blocks further apart so the beam is weaker and a small object takes more of it.
Yes: that is a reflective sensor, and the same rounds work; only the test changes. Side by side, both domes look up; a hand a few centimetres above reflects some of the beam back. The difference is then larger with a hand than without, so report when it rises well above the clear value instead of falling below half of it.
Edit this page — content/books/ir-photodiode/a-beam-that-breaks.mdx
Questions about this product
See what other owners have asked, and read their solutions.
Infrared Receiver
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.