blog

Generating internets with lasers at home

Building weird stuff is nice. In fact, I build a lot of weird stuff. Recently, my friend Alex and I worked on a little project that seems to be completely useless, but it is interesting regardless: we made a Raspberry Pi communicate with an Arduino only using a laser emitter to send data and an LED to receive it. Curious? I wasn't planning to show it here, but another friend said I should post it, so here it goes. When we think about wireless internet (or radio, satellite TV, etc), if you are like me, you will promptly picture a satellite dish sending signals, and another satellite dish receiving signals: we have a part that sends data, and a part that receives data. Wireless internet uses radio waves to transport the data, but radio waves are not the only way to send data, obviously: we can, for example, use light, like in the case of fiber optics. Using a laser to send beams of light with encoded data is not an absurd idea at all then. So, a laser emitter as the sender should work fine, but how can we make a receiver with just an LED? Well, it turns out that LEDs can also be used as photodiodes to detect light! Here is how it works: normally, we would send current through the anode (+ side) of the LED, to the cathode (- side), making it turn on. If we invert the polarity, the LED will not turn on and the current will be blocked, charging up the capacitance in the cathode (in other words, there LED will behave like a tiny capacitor). If we shine a light into that LED (like a laser), current will be allowed to pass to the anode (proportional to the amount of light), making it act like a photodiode. If you are confused by what I said, allow me to me to simplify it: if you invert an LED, no current will go through it; but if you shine a bright light into it, current will go through it. We can use this to detect light: put an inverted LED in a circuit, and if there is light, current will go through it, and if there is no light, no current will go through it. There is our receptor! ![Raspberry Pi with a laser emitter](https://hcoelho.com/storage/images/blog/laser_rasppi.jpg)
This is the Raspberry Pi with the laser emitter. The circuit does not do anything special, only turns the laser on and off
![Arduino with an LED](https://hcoelho.com/storage/images/blog/laser_arduino.jpg)
This is our signal receptor. Yes. It is an LED directly stuck in the input/output.
Alright. We have the hardware done. Now the software. First, how would we send data? For example, if we wanted to send the letter **a** to the arduino, how would we do it? Binary would be the obvious response. The letter **a** in binary is `01100001`, so we could encode it as **0** being "laser off" and **1** being "laser on". This would not work, unfortunately, because we do not have a clock to pace the bits sent. What I mean by this is that a message like this: ```json 01010101 ``` Would be indiscernible from this: ```json 0011001100110011 ``` Since we can't tell the arduino how fast we are sending the bits, it has no idea if it is receiving one bit or ten. What we needed was a self-clocking encoding: an encoding that does not need a clock for synchronization. The first alternative was Manchester encoding. If you are not familiar with it, here is a quick explanation: Manchester sends a 2 bit message for ever bit we want to send. If we want to send a **0**, we will send a **high** and then a **low** (1 and then 0); if we want to send a **1**, we will send a **low** and then a **high** (0 and then 1). Although it would be nice to use Manchester encoding, we decided to make our own! Our protocol is fairly simple: to send a bit, regardless if it is a 1 or a 0, we turn the laser on and then off. What defines if it is a 1 or 0 is the duration of the *on* and *off* phases - a long *on* phase followed by a short *off* phase means 1, and a short *on* phase with a long *off* phase means 0. Here is how it would look in a graph if we sent the binary `1100`: ```json laser on | _____ _____ __ __ | | | | | | | | | | | | | | | | | | | | | | | | | |__| |__| |_____| |_____ laser off | -------------------------------------- time 1^ 1^ 0^ 0^ ``` The interesting part about this encoding is that every bit starts with a high voltage and end with a low voltage (or high light and low light, in our case), making every easy to know when a bit starts and ends. Now that we have an encoding method, here is our python script for sending the messages with the Raspberry Pi: ```python #!/usr/bin/python3 import pigpio import time pi = pigpio.pi() TIME_SHORT = 20 * 0.001 TIME_LONG = TIME_SHORT * 2 def send(thing): for c in thing: for i in range(8): b = (c >> i) & 1 print("Sending bit {}".format(b)) if b == 1: pi.write(22, 0) time.sleep(TIME_LONG) pi.write(22, 1) time.sleep(TIME_SHORT) else: pi.write(22, 0) time.sleep(TIME_SHORT) pi.write(22, 1) time.sleep(TIME_LONG) chars=input("PLESE SEND CHARS PLS: ").encode('ascii') for c in chars: print("{:02x}".format(c)) send(chars) ``` The idea behind this python script is simple: we take every character, get its representation in binary, and then for every bit we turn the laser on and then off, where the duration of each phase depending if the bit is 1 or 0. And here is our decoder on Arduino: ```c++ #define LED_N_SIDE 2 #define LED_P_SIDE 3 #define J_MIN 3000 #define T_SHORT 20 #define T_LONG (T_SHORT * 2) #define T_WAY (13) void setup() { Serial.begin(9600); } bool getBit() { unsigned int j; // Apply reverse voltage, charge up the pin and led capacitance pinMode(LED_N_SIDE, OUTPUT); pinMode(LED_P_SIDE, OUTPUT); digitalWrite(LED_N_SIDE, HIGH); digitalWrite(LED_P_SIDE, LOW); // Change the pin mode to input and read the charge pinMode(LED_N_SIDE, INPUT); digitalWrite(LED_N_SIDE,LOW); bool didDischarge = false; // We sample the current in the LED for a short period of time, // if the LED discharges, it means there was light shining on it for (j = 0; j < J_MIN; j++) { if (digitalRead(LED_N_SIDE) == 0) didDischarge = true; } return didDischarge; } char getChar() { int i; char c = 0; unsigned long t; for (i = 0; i < 8; i++) { while(!getBit()); t = millis(); while(getBit()); t = millis() - t; if (t >= (T_SHORT + T_WAY)) { c |= 1 << i; } } return c; } void loop() { Serial.println(getChar()); } ``` This code is a bit more complicated, but the idea behind it is simple as well: we sample the current passing through the LED, and if the current goes through, it means there is light shining on it; if it does not go through it, there is no light shining on it. We time how long the "light on" phase lasts and how long the "lights off" phase lasts. If the "lights on" phase is longer than a threshold, then it is a 1, otherwise, it is a 0. Put this in an infinite loop, and we can read characters continuously. Now, here is the question: does it work? ![Laser pointed at the LED](https://hcoelho.com/storage/images/blog/laser_sending.jpg)
It is actually really hard to hit the laser directly on the LED, so I have to keep it really close.
At a whopping 2 bytes per second (17 bits, more accurately), we sent our messages! ![Display showing the received message](https://hcoelho.com/storage/images/blog/laser_result.jpg)
On the right: the python script sending the message. On the center: the message we received in the Arduino.
Yes. Yes it does work!