sending / receiving data via laser between two UNOs through a UART-like protocol

I am fairly new to Arduino development, and I am trying to build a small system from two Arduino UNO boards where I send data from one board to the other.
I use laser as a medium. On the first board, I have some data (say an array for now), that I want to send to the other side. I am using a simple UART-like protocol; I have a start bit, eight data bits, and my idle state. My idle state is high (laser is on), and then I have my start bit (low), then my eight data bits. I set my array to change the state of a pin on the board through a for loop with 100ms delay (bit time) between each bit and the following one.

Here is my sender code:

#define LASERPIN 13

void setup() {
  pinMode (LASERPIN, OUTPUT);
  digitalWrite (LASERPIN, HIGH); //IDLE
  delay(10000);

  uint8_t bits[] = {HIGH, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW}; //to send

  //start bit
  digitalWrite (LASERPIN, LOW);
  delay(100);

  for (int i = 0; i < 8; i++) {
    digitalWrite (LASERPIN, bits[i]);
    delay(100);
  }

  //back to IDLE
  digitalWrite(LASERPIN, HIGH);
  delay(10);
}

void loop() {

}

I know it seems that it doesnt make sense to have what I have in setup(), but I only need my code to run once (sort of a dirty fix).

I don't think there are errors in my sender code, but I attached it just in case.

Now, on the receiver side. I have a photodiode that is connected to a comparator that tests whether i received a high or a low bit. VCC on the opamp is 5 volts, and GND is zero, my reference voltage on it is somewhere around 3.3 volts. The photodiode's voltage when laser is pointed at it is close to 5 volts, and 0.20 volts in ambient conditions.

The mechanism of operation on the receiver side is that I have attached an interrupt to the pin that the opamp's output is connected to, and whenever it detects a falling edge (transition from idle to start bit) it starts a 150ms timer to sample the first bit (after the start bit), then triggers a 100ms timer to sample the remaining bits.

Here is the receiver code:

#define START 2
#define DATA 3

volatile uint8_t i = 0;
volatile uint8_t a[8];
volatile bool reading[8] ;
volatile byte reading1 = 0;

void setup() {

  Serial.begin(9600);
  cli();

  //set timer1 interrupt at 10Hz
  TCCR1A = 0;   // set entire TCCR1A register to 0
  TCCR1B = 0;   // same for TCCR1B
  TCNT1  = 0;   //initialize counter value to 0

  // set compare match register for 1500hz (1.5ms) increments
  OCR1A = 37502;// = (16*10^6) / (6.66666666667*64) - 1

  TCCR1B |= (1 << WGM12);               // turn on CTC mode
  TCCR1B |= (1 << CS11) | (1 << CS10);  // Set CS10 and CS11 bits for 64 prescaler
  // TIMSK1 |= (1 << OCIE1A);             // enable timer compare interrupt

  sei();

  attachInterrupt(digitalPinToInterrupt(START), startISR, FALLING);

  pinMode(START, INPUT);
  pinMode(DATA, INPUT);

}

void loop() {
}

void startISR() {

  detachInterrupt(digitalPinToInterrupt(START));
  // Serial.print("Start ISR entered\n");

  TIMSK1 |= (1 << OCIE1A);    // enable timer compare interrupt
}

ISR(TIMER1_COMPA_vect) {

  OCR1A = 24999; // (1ms) interrupt every 1ms to sample bits

  if (i < 8) {
    //a[i] = digitalRead(DATA);
    a[i] = (digitalRead(DATA));
    i++;

    // Serial.print("data ISR entered \n");
    Serial.print(reading[i]);
    // Serial.println();

  }
  else {
    TIMSK1 |= (0 << OCIE1A);
    // Serial.println();
  }
}

My problem is that whatever I receive on the photodiode, the output on the serial monitor will be 00000008.
I have tried so many things (data types, different approaches to reading, ...etc.) but nothing seems to work.

From an electronics view point, I have made sure everything is working; I have used an oscilloscope to check what signal I'm sending, and it was correct (timing-wise), and I checked the output of the opamp using the scope, and the frame I got was identical to what I have sent.

Could anyone please check my codes and try to explain to me what could have went wrong? I'm suspecting there could be an error in my reading logic; as in the way I am saving the pin values.

A potential error I am suspecting is that timers aren't configured correctly, and I have thought about letting the ISR that reads data toggle a digital pin and have that pin connected to an oscilloscope and see what I get on the screen and measure the time per bit, but I don't have an oscilloscope at my disposal now to verify.

Is there some reason you chose not to use the real serial libraries to send and receive?

Paul

Paul_KD7HB:
Is there some reason you chose not to use the real serial libraries to send and receive?

Yes, its a pretty silly reason. This is for a university project and I am required to do it the way I am doing it now. :confused:

Yes, it is. But,,,,,,, I bet they don't say you can't get the thing working with the real serial ports and then modify, say, the receive code to make it work the way they want, and do the same to the transmit code.

At least you know all the electronics and code and timing can actually work together.

Paul

I would change

 for (int i = 0; i < 8; i++) {
    digitalWrite (LASERPIN, bits[i]);
    delay(100);

to

 for (int i = 0; i < 8; i++) {
    digitalWrite (LASERPIN, bits[i]);
    Serial.print (LASERPIN, bits[i]);
    delay(100);
    Serial.println();

see if what is is what you expect it to be.

I would have made this change if I didn't use an oscilloscope to see what signal my sender Arduino is sending through pin 13. And it was indeed sending the signal I wanted it to send.

A true UART protocol tests the bit position for 0 or 1 right in the middle of the bit timing, or tries to. That allows for the clock being a bit slow or a bit fast. No pun intended. That may be part of your RX problem.

Paul

Paul_KD7HB:
A true UART protocol tests the bit position for 0 or 1 right in the middle of the bit timing, or tries to. That allows for the clock being a bit slow or a bit fast. No pun intended. That may be part of your RX problem.

Paul

Thats true. On the Tx side, I set the time per bit to be 100ms (including the start bit).
On the Rx side, I, first, set a 150ms timer to save the state of the receiving pin after 150ms (100ms of the start bit + 50ms which is 50% of the first data bit after the start bit), then I change the timer's max count so that it takes 100ms to overflow, meaning I take readings every 100ms after the first 150ms (in the middle of every bit's timing).

I am not familiar with the code to process the internal registers, so I cannot comment on that logic. The transmit and receive logic seems to be ok.

The only potential problem may be on the receive side of attaching the interrupt link after turning on the interrupt.

Paul