Increase baudrate in serial transmission over IR

Hello all,

I am currently working in a project to evaluate the performance of IR over skin tissues. I managed to send and properly decode signals without any problem using common IR protocols such as NEC. Now I would like to evaluate the performance of the IR link by sending higher data rates. In order to achieve that, I have etablished an IR link and sent data using UART, modulated at a carrier freq of 38 kHz. I have been following this old thread from , and so far, it has been really useful.

I attach my current hardware setup. It is exactly like the old thread one, just using a newer IR receiver (TSOP98638).

|500x211

I attach also the code for the transmitter and the receiver, which is exactly the same one from the old thread and works perfectly out of the box.

This is the code for the transmitter:

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#define SYSCLOCK 16000000  // main system clock (Hz)
#define PULSECLOCK 38000  // Hz
#define IROUT 11

uint8_t timer2top(unsigned long freq) ;

void setup() {
 Serial.begin(2400) ;

 cbi(TCCR2A,COM2A1) ; // connect OC2A (COM2A0 = 1)
 sbi(TCCR2A,COM2A0) ;

 cbi(TCCR2B,WGM22) ;  // CTC mode for TIMER2
 sbi(TCCR2A,WGM21) ;
 cbi(TCCR2A,WGM20) ;

 TCNT2 = 0 ;

 cbi(ASSR,AS2) ;  // use system clock for timer 2

 OCR2A = 255 ;   // set TOP to 255 for now

 cbi(TCCR2B,CS22) ;  // TIMER2 prescale = 1
 cbi(TCCR2B,CS21) ;
 sbi(TCCR2B,CS20) ;

 cbi(TCCR2B,FOC2A) ;  // clear forced output compare bits
 cbi(TCCR2B,FOC2B) ;

 pinMode(IROUT, OUTPUT) ;  // set OC2A to OUPUT  
 OCR2A = timer2top(PULSECLOCK) ;
 sei() ;
}

// main loop
void loop() {
 Serial.println("Hello, world!") ;  
 delay(5000) ;
}

// return TIMER2 TOP value per given desired frequency (Hz)
uint8_t timer2top(unsigned long freq) {
 return((byte)((unsigned long)SYSCLOCK/2/freq) - 1) ;
}

And for the receiver:

void setup() {
 Serial.begin(2400) ;
}

void loop() {
 int n, i, ch ;

 n = Serial.available() ;
 if (n > 0) {
       i = n ;
   while (i--) {
     ch = Serial.read() ;
     Serial.print((char)ch) ;
   }
 }
 delay(1000) ;
}

With the IR receiver I have, I can achieve a baudrate of 4800 bps. In the old thread implementation, data is not supposed to go faster than 2400 bps.

[u]My question now is:[/u]

What modifications should I do in the code in order to achieve higher data rates? I tried in changing the Serial.begin(2400); for Serial.begin(4800); in the transmitter and in the receiver, but it did not work. Can someone give me some clue about how could I increase this data rate?

Thanks in advance,

Sergio

Setup_schematic.png|1878x794

A limiting factor for IR transmission is the carrier frequency which is 38khz for your receiver. I confess to having forgotten the relationship between the data frequency and the carrier frequency, but 4800 bps is about 1/10th and maybe that is the max.

...R

A limiting factor for IR transmission is the carrier frequency which is 38khz for your receiver.

Indeed, with 38kHz the maximum achievable data rate is only 4800bps.

But actually my question is, knowing the maximum achievable data rate (4800 bps), what changes should I do in the posted code in order to go from the actual data rate (around 1500 bps) to 4800 bps?

To be honest I am a bit lost through this low level orders/syntax and was wondering if someone could shed some light on how to achieve that.

Thank you all,

Sergio

xexunas: But actually my question is, knowing the maximum achievable data rate (4800 bps), what changes should I do in the posted code in order to go from the actual data rate (around 1500 bps) to 4800 bps?

I presume if it is working at all that the carrier frequency must be 38k

So then my question is how do you know it is only sending at 1500 bps ?

And, from what I can see you have the baud rate set at 2400 so it should be working at about 2400 bps.

Some years ago I did build a project based on the same link as you but I can't remember what baud rate I used. And for what I had been doing a low baud rate would probably have been OK

...R

I presume if it is working at all that the carrier frequency must be 38k

Yes, it is. Sorry for not making that clear.

So then my question is how do you know it is only sending at 1500 bps ?

I just made some simple modifications in the main loop of the transmitter:

void loop() {
  if ( i == 0) {
    start_timer = millis();
  }
  if (i < 200) {
    Serial.println("AAAA");
    i++;
  }
  if ( i == 200) {
    time_elapsed = millis() - start_timer;
    Serial.print("Total time elapsed: ");
    Serial.println(time_elapsed);
    Serial.println(" seconds");
    i++;
  }
}

Then, the aprox data rate is (200messages * 32bits)/time_elapsed

Still, my question is: What are the modifications that should be made in the code in order to achieve a data rate around 4800 bps?

Sergio

xexunas: Then, the aprox data rate is (200messages * 32bits)/time_elapsed

You are confusing bytes-per second and bits-per-second.

The bits-per-second speed is set by the baud rate - end of story. The maximum number of bytes-per-second should be close to 1/10th of the baud rate. The 8 bits of a byte are preceded by a start-bit and succeeded by a stop bit. So, a max of about 480 bytes per second at 4800 baud.

You are using Serial.println("AAAA"); which sends 6 bytes - you have forgotten the carriage-return and line-feed characters.

In your test you send 200 * 6 = 1200 bytes - but you have not told us the time taken for that.

You will have another timing inaccuracy because the code in the if ( i == 200) { will be called before the Serial Output Buffer is empty - there could be 64 bytes still in the buffer so that your time might be for 1136 bytes. You should add the line Serial.flush(); before your line time_elapsed = millis() - start_timer;

...R

Thanks a lot for the clarifications @Robin2.

In your test you send 200 * 6 = 1200 bytes - but you have not told us the time taken for that.

The time taken for that doing the modifications in the code you sugested is 4.997 seconds. Then, (200 * 6 * 8 ) / 4.997 = 1.921 kbps.

But still, regarding my original question, does someone knows how could I send data at a higher data rate ?(Not more than 4800 bps due to 38 kHz carrier frequency constraint)

Sergio

xexunas: The time taken for that doing the modifications in the code you sugested is 4.997 seconds. Then, (200 * 6 * 8 ) / 4.997 = 1.921 kbps.

Forget about bits-per-second.

If you can send 1200 bytes in 4.997 secs then the data rate is 240 bytes per second.

And as you have modified the program please post the latest version in your next Reply.

...R

Forget about bits-per-second.

If you can send 1200 bytes in 4.997 secs then the data rate is 240 bytes per second.

Yes, you are right. But I don't really get why matters the magnitude in which one value is expressed. Are not 240 Bytes/sec equivalent to 1.92 kbits/sec ?

And as you have modified the program please post the latest version in your next Reply.

I just added the Serial.flush() line you suggested before. Here it goes:

void loop() {
  if ( i == 0) {
    start_timer = millis();
  }
  if (i < 200) {
    Serial.println("AAAA");
    i++;
  }
  if ( i == 200) {
    Serial.flush();
    time_elapsed = millis() - start_timer;
    Serial.print("Total time elapsed: ");
    Serial.println(time_elapsed);
    Serial.println(" seconds");
    i++;
  }
}

But actually, the matter I need to solve is this:

What modifications should I do in the code in order to achieve higher data rates? I tried in changing the Serial.begin(2400); for Serial.begin(4800); in the transmitter and in the receiver, but it did not work. Can someone give me some clue about how could I increase this data rate?

I can't help if you won't take the trouble to post the complete program. And please use the code button for code and not the quote button

...R

And please use the code button for code and not the quote button

I'm sorry. I have been using the code tags all time, I just missed that last one. Already edited.

I can't help if you won't take the trouble to post the complete program

It's not any trouble. I just wanted to solve a specific question about a specific functionality. I think that the complete program that I have already uploaded in my original post is adequate to understand the goal that I want to achieve.

However if you think it's not, here goes the code we have been talking in our last message exchange.

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#define SYSCLOCK 16000000  // main system clock (Hz)
#define PULSECLOCK 38000  // Hz
#define IROUT 11

uint8_t timer2top(unsigned long freq);
void get_ir_tx_ready();

void setup() {
  Serial.begin(2400);
  get_ir_tx_ready();

  // Delay in order to wait for the receiver
  delay(5000);
}

int i = 0;
long time_elapsed;
long start_timer;

void loop() {
  if ( i == 0) {
    start_timer = millis();
  }
  if (i < 200) {
    Serial.println("AAAA");
    i++;
  }
  if ( i == 200) {
    Serial.flush();
    time_elapsed = millis() - start_timer;
    Serial.print("Total time elapsed: ");
    Serial.println(time_elapsed);
    Serial.println(" seconds");
    i++;
  }
}


// return TIMER2 TOP value per given desired frequency (Hz)
uint8_t timer2top(unsigned long freq) {
  return ((byte)((unsigned long)SYSCLOCK / 2 / freq) - 1);
}


void get_ir_tx_ready() {
  cbi(TCCR2A, COM2A1); // connect OC2A (COM2A0 = 1)
  sbi(TCCR2A, COM2A0);

  cbi(TCCR2B, WGM22); // CTC mode for TIMER2
  sbi(TCCR2A, WGM21);
  cbi(TCCR2A, WGM20);

  TCNT2 = 0;

  cbi(ASSR, AS2); // use system clock for timer 2

  OCR2A = 255;   // set TOP to 255 for now

  cbi(TCCR2B, CS22); // TIMER2 prescale = 1
  cbi(TCCR2B, CS21);
  sbi(TCCR2B, CS20);

  cbi(TCCR2B, FOC2A); // clear forced output compare bits
  cbi(TCCR2B, FOC2B);

  pinMode(IROUT, OUTPUT);  // set OC2A to OUPUT
  OCR2A = timer2top(PULSECLOCK);
  sei();
}

Thanks for your help,

Sergio

xexunas: I think that the complete program that I have already uploaded in my original post is adequate to understand the goal that I want to achieve.

That is not the purpose for asking for the complete revised program. We need to see the exact changes that you made (or did not make) if we are to be able to help. The devil is in the detail.

In the program in Reply #10 the baud rate is set at 2400 and according to your Reply #6 you are transmitting 240 bytes per second so it seems to be performing perfectly.

...R

The devil is in the detail.

Indeed ;).

In the program in Reply #10 the baud rate is set at 2400 and according to your Reply #6 you are transmitting 240 bytes per second so it seems to be performing perfectly.

Yes I know. But what I want to really achieve is: once the code is working, how can I send data at 4800 bps?

Quoting from my original post:

What modifications should I do in the code in order to achieve higher data rates? I tried in changing the Serial.begin(2400); for Serial.begin(4800); in the transmitter and in the receiver, but it did not work. Can someone give me some clue about how could I increase this data rate?

That's the matter I would like to solve, sorry if I was not clear from the begining .

xexunas: Yes I know. But what I want to really achieve is: once the code is working, how can I send data at 4800 bps?

All the discussion about bits per second was very distracting.

Sorry, I don't know how to make it work at 4800 baud. I suspect you need a device that can work at a higher carrier frequency. I won't pretend to be an expert but the datasheet in your link refers to a minimum of 10 cycles per burst and 13 cycles between bursts. 38khz / 23 gives you 1652 bits per second, and you seem to be doing better than that so probably I am interpreting the datasheet incorrectly.

If you study the Atmega 328 datasheet (assuming you are using an Uno) it may be possible to set a baud rate between 2400 and 4800. But even if that can be done I have no idea whether it would work with the IR system.

Of course another way to improve throughput is by reducing the number of bytes that need to be sent - is that possible? So far your discussion has only been about theoretical throughput rather than the actual data that you want to send.

...R

Sorry, I don't know how to make it work at 4800 baud. I suspect you need a device that can work at a higher carrier frequency. I won't pretend to be an expert but the datasheet in your link refers to a minimum of 10 cycles per burst and 13 cycles between bursts. 38khz / 23 gives you 1652 bits per second, and you seem to be doing better than that so probably I am interpreting the datasheet incorrectly.

I am really really far to be an expert, but I asked the people from Vishay (the IR receiver manufacter) and they answered me that with 38kHz, the maximum achievable data rate is 4800bps.

But even if that can be done I have no idea whether it would work with the IR system.

So let's asume that at 4800 bps, the hardware will work. Do you have any clue about what modifications should I do in the code to achieve that? Maybe some references to start looking at?

Of course another way to improve throughput is by reducing the number of bytes that need to be sent - is that possible?

In the setup I am using, I send 4 bytes using Serial.write() instead of Serial.println().

I attach here the full code just in case that may be useful. In this code, I send a random 4 bytes integer via serial (over IR) and via I2C and then wait for an ACK coming from the receiver side.

#include  // I2C library

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#define SYSCLOCK 16000000  // main system clock (Hz)
#define PULSECLOCK 38000  // Hz
#define IROUT 11
#define MAX_32_BIT_VALUE 2147483647
#define PKT_LENGTH 4 //In bytes
#define I2C_ADDR 8
#define PKT_LENGTH 4 //In bytes
#define ACK 0x06 //ASCII ACK character
#define EXPERIMENT_ITERATIONS 1000000

long randNumber;
byte splitted_msg[4];
int i2c_bytes_count;
long iterations_counter = 0;

void setup() {
  Wire.begin();
  Serial.begin(2400);
  get_ir_tx_ready();

  /*
     As analog input pin 0 is unconnected, random analog
     noise will cause the call to randomSeed() to generate
     different seed numbers each time the sketch runs.
     randomSeed() will then shuffle the random function.
  */
  randomSeed(analogRead(0));

  // Delay in order to wait for the receiver
  delay(3000);


  // Send first data frame
  
  randNumber = random(MAX_32_BIT_VALUE);
  send_i2c_pkt(randNumber);

  for (int i = 0; i < PKT_LENGTH; i++) {
    //extract the right-most byte of the shifted variable
    splitted_msg[i] = ((randNumber >> (i * 8)) & 0xFF);
  }
  Serial.write(splitted_msg, PKT_LENGTH);

}


void loop() {
  if (iterations_counter < EXPERIMENT_ITERATIONS) {
    
    // Wait for ACK before sending 
    if ((Serial.available() > 0) && (Serial.read() == ACK)) {
      
      randNumber = random(MAX_32_BIT_VALUE);

      send_i2c_pkt(randNumber);

      for (int i = 0; i < PKT_LENGTH; i++) {
        //extract the right-most byte of the shifted variable
        splitted_msg[i] = ((randNumber >> (i * 8)) & 0xFF);
      }
      Serial.write(splitted_msg, PKT_LENGTH);

      iterations_counter++;
    }
  }
}



void send_i2c_pkt (long i2c_randNumber) {
  i2c_bytes_count = 0;

  while (i2c_bytes_count < PKT_LENGTH) {

    for (int i = 0; i < PKT_LENGTH; i++) {
      //extract the right-most byte of the shifted variable
      splitted_msg[i] = ((i2c_randNumber >> (i * 8)) & 0xFF);
    }

    Wire.beginTransmission(I2C_ADDR);
    Wire.write(splitted_msg[i2c_bytes_count]);
    Wire.endTransmission();
    i2c_bytes_count ++;
  }
}



// return TIMER2 TOP value per given desired frequency (Hz)
uint8_t timer2top(unsigned long freq) {
  return ((byte)((unsigned long)SYSCLOCK / 2 / freq) - 1);
}


// Prepare arduino to send modulated UART data.
void get_ir_tx_ready() {
  cbi(TCCR2A, COM2A1); // connect OC2A (COM2A0 = 1)
  sbi(TCCR2A, COM2A0);

  cbi(TCCR2B, WGM22); // CTC mode for TIMER2
  sbi(TCCR2A, WGM21);
  cbi(TCCR2A, WGM20);

  TCNT2 = 0;

  cbi(ASSR, AS2); // use system clock for timer 2

  OCR2A = 255;   // set TOP to 255 for now

  cbi(TCCR2B, CS22); // TIMER2 prescale = 1
  cbi(TCCR2B, CS21);
  sbi(TCCR2B, CS20);

  cbi(TCCR2B, FOC2A); // clear forced output compare bits
  cbi(TCCR2B, FOC2B);

  pinMode(IROUT, OUTPUT);  // set OC2A to OUPUT
  OCR2A = timer2top(PULSECLOCK);
  sei();
}

xexunas: So let's asume that at 4800 bps, the hardware will work. Do you have any clue about what modifications should I do in the code to achieve that? Maybe some references to start looking at?

All I can think of is Serial.begin(4800);

I was wondering if using 2 stop bits might help - but the Arduino serial reference page is not working right now.

...R

PS ... do you have a frequency meter to verify that the Arduino is actually generating a 38khz carrier. If it is slightly off it might not matter at the lower baud rate.

All I can think of is Serial.begin(4800);

That's what I tried in the begining too, but it doesn't work.

I was wondering if the void setup () part of the code had something to do with it:

void setup() {
 Serial.begin(2400) ;

 cbi(TCCR2A,COM2A1) ; // connect OC2A (COM2A0 = 1)
 sbi(TCCR2A,COM2A0) ;

 cbi(TCCR2B,WGM22) ;  // CTC mode for TIMER2
 sbi(TCCR2A,WGM21) ;
 cbi(TCCR2A,WGM20) ;

 TCNT2 = 0 ;

 cbi(ASSR,AS2) ;  // use system clock for timer 2

 OCR2A = 255 ;   // set TOP to 255 for now

 cbi(TCCR2B,CS22) ;  // TIMER2 prescale = 1
 cbi(TCCR2B,CS21) ;
 sbi(TCCR2B,CS20) ;

 cbi(TCCR2B,FOC2A) ;  // clear forced output compare bits
 cbi(TCCR2B,FOC2B) ;

 pinMode(IROUT, OUTPUT) ;  // set OC2A to OUPUT  
 OCR2A = timer2top(PULSECLOCK) ;
 sei() ;
}

PS ... do you have a frequency meter to verify that the Arduino is actually generating a 38khz carrier. If it is slightly off it might not matter at the lower baud rate.

I don't. Just assuming that I am sending at 38 kHz because the receiver I have (TSOP96838) only decodes messages at 38 kHz.

xexunas: I was wondering if the void setup () part of the code had something to do with it:

[.....]

I don't. Just assuming that I am sending at 38 kHz because the receiver I have (TSOP96838) only decodes messages at 38 kHz.

The code in setup() sets the 38kHz frequency. However it may not be exactly 38kHz because the Arduino clock may not be exactly 16Mhz. The TSOP can work with a small amount of frequency error but its ability to work with a frequency error would probably be a lot less at the higher baud rate.

The code uses an 8-bit counter for timing so the minimum variation would be 1/255 or about 0.4%. Looking at my TSOP4838 datasheet that looks like a large variation - a 1% variation would render the thing almost useless. The code could probably be re-written to use a 16-bit counter which would allow finer variation. But, frankly, I think that experiment would be a waste of time without a reliable frequency verification device. For all I know it is already perfectly at 38kHz.

Bottom line ... I have run out of ideas.

...R

Bottom line ... I have run out of ideas.

No probem, still you solved some doubts I already had. Thanks a lot for your time, let't see if someone can shed some light on the matter.

Sergio

You could try one of the TSOP devices that uses a higher carrier frequency.

...R