Digital Data Wireless Transceiver (Laser)

Achindra Bhatnagar
Achindra
Published in
5 min readFeb 5, 2018

In my Digital Communications class, I am trying to help my students learn digital communication/computer networks by discovering the problems of the domain and solve them. In the last class we built a Morse code transmitter/receiver using Laser and LDR.

Today, I extended this to transfer digital data; data encoded in stream of 0 and 1. In Morse code, ‘dot’ and ‘dash’ are equivalent to 0 and 1 of digital data. However, we have explicit signal for 0/dot. Absence of signal is not interpreted as a zero. This makes the channel costlier. I could achieve ~18 words per minute (PARIS is generally used as a reference word for this calculation).

square_wave

Since my transmitter is a laser diode, switched off state can be considered to be a 0 while switched on state can be considered as 1. However, this poses a challenge as how to differentiate a set of 0 representing data from 0 representing silence (absence of data transmission).

Since we are presented with this problem, I added a start message header/pattern (1011) and an end message header/pattern (1101). I continuously sample data on my LDR receiver and add read value 0/1 to my data. Then, I check for end pattern and if it is found, I check for start header before my 8 bit data.

1011<8_bit_data>1101

H: 1011010010001101
E: 1011
010001011101
L: 1011
010011001101
L: 1011010011001101
O: 1011
010011111101

I could decode this pattern on my receiver.

DigitalData_Top.PNG

However, this simple pattern to encapsulate a message is not foolproof. It can easily be cracked by a rogue data series.

DigitalData_Error.PNG

So how do I verify that the data received is actually the data sent from the other end?

I added a small signature to the end of data; number of set bits. Since my data is 8 bits, I added another 4 bits to represent max 8 bits that could be set in the data.

1011<8_bit_data><4_sign_bits>1101

H: 10110100100000101101
E: 1011
0100010100111101
L: 1011
0100110000111101
L: 101101001100
00111101
O: 1011
0100111101011101

I could decode this pattern as well. Result, I could filter some garbage out

DigitalData_Signed.PNG

I do have data loss, but less garbage now. So how can I improve this further? Can I fix transmission errors? Can I make better use of sign bits? Loads of possible improvements, but we shall solve when we the problem hit us in face and we are convinced that the problem is worth solving. May be next class…

Transmitter:

#define PULSE 50#define START_MSG one(); zero(); one(); one();
#define END_MSG one(); one(); zero(); one();
#define MSG_PACK(x) START_MSG x END_MSGvoid setup() {
pinMode(13, OUTPUT);
}
void loop() {
// <8_Data_Bits><4_Sign_Bits>
//H: 2 set bits
MSG_PACK(zero(); one(); zero(); zero(); one(); zero(); zero(); zero(); \
zero(); zero(); one(); zero(); )
//E: 3 set bits
MSG_PACK(zero(); one(); zero(); zero(); zero(); one(); zero(); one(); \
zero(); zero(); one(); one(); )
//L: 3 set bits
MSG_PACK(zero(); one(); zero(); zero(); one(); one(); zero(); zero(); \
zero(); zero(); one(); one(); )
//L: 3 set bits
MSG_PACK(zero(); one(); zero(); zero(); one(); one(); zero(); zero(); \
zero(); zero(); one(); one(); )
//O: 5 set bits
MSG_PACK(zero(); one(); zero(); zero(); one(); one(); one(); one(); \
zero(); one(); zero(); one(); )
//CR: 3 set bits
MSG_PACK(zero(); zero(); zero(); zero(); one(); one(); zero(); one(); \
zero(); zero(); one(); one(); )
//LF: 2 set bits
MSG_PACK(zero(); zero(); zero(); zero(); one(); zero(); one(); zero(); \
zero(); zero(); one(); zero(); )
}
void zero()
{
digitalWrite(13, LOW);
delay(PULSE);
}
void one()
{
digitalWrite(13, HIGH);
delay(PULSE);
}

Receiver:

#define SOM 0xB
#define EOM 0xD
#define PULSE 50
unsigned long data = 0;void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
int val = analogRead(4);
unsigned long sign = 0;
data = (data << 1);
if(val >= 10){
data |= 1;
}else {
data |= 0;
}
//check tail signature
if(EOM == (data&EOM))
{
//check head signature
if(SOM == ((data >> 16)&0xF)){
sign = (data>>4)&0xF; // 4 bits of signature
data = (data>>8)&0xFF;// 8 bits of data
if(sign == getSetBits(data))
{
Serial.print((char)data);
}
//Diagnostics
//if(data==72||data==69||data==76||data==79||data==13||data==10){
//data
//digitalWrite(13, HIGH);
//}else{
//error
//digitalWrite(12, HIGH);
//}
data = 0;
}
}
delay(PULSE);
//digitalWrite(13, LOW);
//digitalWrite(12, LOW);
}
unsigned long getSetBits(unsigned long n)
{
unsigned long count = 0;
while(n)
{
n &= (n-1);
count++;
}
return count;
}

BTW, I achieved about 1 byte data per second speed with the final code published here. Including headers, it comes to 20-bits per second!

EDIT

To detect errors in data, my signature mechanism of adding number of set bits in data wasn't proving good. I need a better and cheaper solution that does not add a lot of data overhead.

I XOR high nibble and low nibble to generate a 4-bit signature. This signature is less likely to fail since error (bit flip) has to happen at corresponding bits in both high nibble and low nibble.

if(sign == ((data & 0xF) ^ ((data >> 4) & 0xF))){
Serial.print((char)data);
}

The results, as expected are better than before. Very rarely I see a rogue byte pass this test.

DigitalData_XORBits

In ideal condition I do not get errors. I do not see rogue bytes (yet) but I do see packets dropped. How do I ensure that I have received all data in the correct order?

This is the next problem to solve. Build an ACK for each packet so that Sender can confirm what he sent has actually been received. I plan to send back the XOR sign as received. However I do not have another laser diode for the purpose...

--

--