Trouble detecting End of Line using SoftwareSerial on ATTiny85

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

*Moved to Arduino forum.*

 

Hello all,

I've been wrestling with a problem for a few weeks now and have come to a point where I don't know what to try next.

 

Long story short, I'm trying to smoothly control an WS2812 RGB LED through an ATTiny85 with an HC-06 bluetooth module using the Bluetooth Color Picker Android app.

The data output from the app is in the format "#:R,G,B"

 

Example:
0:200,0,0
0:199,0,0
0:198,0,0
etc

 

When inputting these commands manually in a terminal they work fine, but the bluetooth color picker app sends the commands much faster and they don't all trigger my IF statement for checking newline.

The problem I'm having is that I can't reliably detect the end of line character on the ATTiny85 using Software Serial, but the same code (using hardware serial instead) works perfectly fine on my Arduino MEGA ADK.

 

#include <Adafruit_NeoPixel.h>
#define PIN            2
#define NUMPIXELS      2
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

//#include <SoftwareSerial.h>
//SoftwareSerial mySerial(4,3); // (RX,TX) Sets physical pin 2 as TX and physical pin 4 as RX

void setup() {
  Serial.begin(9600);
  Serial.println("Software Serial On");
  pixels.begin(); //Initializes NeoPixel library
}

void loop() {
  //if some data is sent
  while(Serial.available() > 0){
    //Parse serial data into integers, separated by non-digit characters
    int pixel = Serial.parseInt();
    int red = Serial.parseInt();
    int green = Serial.parseInt();
    int blue = Serial.parseInt();
    
    //WORKS on MEGA but not ATTiny85. Software Serial vs Serial?
    if (Serial.read() == '\n'){ //if end of line seen (doesn't work all the time)
      pixels.setPixelColor(pixel, pixels.Color(red,green,blue)); // Should change color with app control
      pixels.show(); // This sends the updated pixel color to the hardware.
    }
  }
}

This code works perfectly on my MEGA, but changing everything to SoftwareSerial and trying it on my ATTiny85 does not work.

 

I'm running the ATTiny85 at 8MHz, which I believe is necessary for SoftwareSerial.

I'm powering the ATTiny85 from the Arduino MEGA's 5V rail. I have not tried 3.3V yet, but intend to.

 

Any advice here would be GREATLY appreciated as I really have no idea what to try next.

 

Thank you,
Mike

Last Edited: Thu. Apr 23, 2015 - 03:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your description sounds like it's the Software Serial that's failing - not the IF test.

 

ie, the Soft Serial just can't cope at the speed the BT sends.

 

Performance is always the Big Issue with Soft UARTs - so this would not be entirely unexpected?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Let's guess the worst case:

"0:255,255,255\n"

 

That looks like about 14 bytes that could arrive in a burst.    SoftwareSerial @ 9600 baud would need at least a 16-byte RX buffer.    32-byte would be better.    You might be busy when another burst arrives.

 

Does SoftwareSerial have a configurable buffer size ?

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From http://www.arduino.cc/en/Reference/SoftwareSerial
"This hardware allows the Atmega chip to receive serial communication even while working on other tasks, as long as there room in the 64 byte serial buffer. "

 

Looks like 64-bytes by default, but "_SS_MAX_RX_BUFF" can be defined with different buffer values.

 

The default of 64-bytes should be plenty, but now I know to try using "mySerial.overflow()" to check for an overflow of the buffer.

This may be happening if SoftwareSerial can't process the serial data fast enough?

 

Could a faster baud rate help with this? Or slower?

 

Thanks for giving me a place to start.

Last Edited: Wed. Apr 22, 2015 - 03:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ThomasTesla wrote:

From http://www.arduino.cc/en/Reference/SoftwareSerial
"This hardware allows the Atmega chip to receive serial communication even while working on other tasks, as long as there room in the 64 byte serial buffer. "

"This hardware" is referring to the hardware UART, which the ATTiny doesn't have. The hardware serial library on Arduino uses a 64-byte buffer.

 

ThomasTesla wrote:

The default of 64-bytes should be plenty, but now I know to try using "mySerial.overflow()" to check for an overflow of the buffer.

This may be happening if SoftwareSerial can't process the serial data fast enough?

What is more likely happening is that SoftwareSerial is missing some start bits because (AFAIUI) it does not process anything in the background while your code is running. Some other "software" serial libraries use timer interrupts to regularly poll the digital I/O in the background so that it's much less likely to miss incoming data. I don't know if any of those libraries will work on the ATTiny.

 

A slower baud rate may help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is parseint an arduino function? Seems like you wouldnt even want to wade into reading the ascii rgb out of the buffer until there is at least 7 chars in the buffer.... enough for a '0', a ':', 2 commas and 3 1 digit chars. Are you sure there is a cr at the end of it? Maybe the phone is just sending 0:1,2,3 nothing?

 

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is parseint an arduino function?

Google says:

 

http://www.arduino.cc/en/Referen...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

parseInt is an arduino function which works on the ATTiny85.

 

So you're suggesting something like this?

if(mySerial.available() > 6){
    do something
    
}

I had tried that once with poor results, but will test again.

 

When hooked up to my Arduino, the serial monitor outputs each color command on a newline. This indicates a '\n' character right? I've also seen '\r\n' being used.

If no newline character was used, the data output would be "0:0,255,01:255,0,0:2:0,0,255" right?
 

 

Thank you,

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I tried slower and slower baud rates. 4800 looked a bit more functional, but then 2400 seemed to be breaking. I was unable to try 1200 due to programming issues.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

2400 bps is about 5ms per char, 1200 about 10. I'll hypothesize that once you wait around to get all the chars for an ascii rgb report, maybe 11 chars, THEN you do something with it that takes about 10ms, and by then you've missed the beginning of the next pkt. This is why the EEs invented hw uarts with interrupts back in the 70s. Get an avr with a uart like an uno and run the serial at 115200 with the 2x bit on? 83us per char instead of 10,000.

 

Imagecraft compiler user

Last Edited: Thu. Apr 23, 2015 - 02:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I really don't want to have to change ICs if I don't have to.

 

Would a possible solution be to modify the bluetooth color picker app to send less or more infrequent data?

 

If there a way in software that I can skip every other packet of data sent?

 

So a worst case data packet would be 13 bytes, and with a 64 byte serial buffer, I could read almost 5 packets before it starts to overflow? I could try increasing the serial buffer size.

 

Would running the ATTiny85 with an external 16MHz crystal help process the data faster?

 

The end goal is to have the MCU change the color of the RGB LEDs in real time with the app.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So you push a button on a touchscreen, phone sends out a bt pkt over and over as long as your finger is down on the touchscreen. You could look at the tx line on the bt receiver with a scope and see how fast the bits are shaking and how fast the pkts are arriving. I seem to recall they send a pkt, wait a while, send a pkt. I think a 'while' is 50ms. Not sure.

 

Imagecraft compiler user

Last Edited: Thu. Apr 23, 2015 - 03:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your 13 bytes are going to take 14ms to send.    You could handle about 70 of these packets in one second @ 9600 baud.

 

I would assume that SoftwareSerial is capable of handling continuous 9600 baud.    I know that sensible Soft UART implementations would be fine.

 

But the obvious thing is to limit your Bluetooth app.   Why send 70 packets when 20 packets are as fast any human might notice?

 

Incidentally,   you can test your SoftwareSerial throughput by generating the data directly with another Arduino or PC.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The app only sends data when the color sliders are being moved. So if the Blue and Green are at 0 and the Red is at 255, and I slide the Red to 0, it would output:

 

0:255,0,0

0:253,0,0

0:252,0,0

0:250,0,0

0:248,0,0

etc

etc

 

The numbers output depend on the speed at which the slider is moved. Sometimes more numbers are skipped and sometimes less. It's not just incremented by +/- 1 all the time.

 

I haven't tried creating or altering a phone app yet. I was hoping to avoid that for the time being.

 

I like that idea David. I will try having my MEGA output the data to the Tiny and see what happens.

 

Thank you,

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ThomasTesla wrote:

If there a way in software that I can skip every other packet of data sent?

 

The problem is that you'll always want to capture the last packet sent or your RGB values won't be the same as set on the app. As there's no way to know if the packet is the last one you have to capture every packet.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Update:

 

I've outsourced a mobile app which ouputs one packet of data in the format:

0:200,0,0\r\n
0:199,0,0\r\n
0:198,0,0

 

I am still having difficulty detecting the end of each line using the following code:

#include <Adafruit_NeoPixel.h>
#define PIN            1
#define NUMPIXELS      3
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

#include <SoftwareSerial.h>
SoftwareSerial mySerial(4,3); // (RX,TX) Sets physical pin 2 as TX and physical pin 3 as RX

//int pixel, red, green, blue = 0;

void setup() {
  mySerial.begin(9600);
  mySerial.println("Software Serial On");
  pixels.begin(); //Initializes NeoPixel library
}

void loop() {
  
  //if some data is sent
  if(mySerial.available() > 6){
    //delay(300); //wait for all data
    //Parse serial data into integers, separated by non-digit characters
    int pixel = mySerial.parseInt();
    int red = mySerial.parseInt();
    int green = mySerial.parseInt();
    int blue = mySerial.parseInt();
    //int test = mySerial.parseInt();
    
    if (mySerial.read() == '\r\n'){
     red = green = blue = 255; //set to white
    }  
  
    pixels.setPixelColor(pixel, pixels.Color(red,green,blue)); // Should change color with app control
    pixels.show(); // This sends the updated pixel color to the hardware.  
  }
  
}

I'm only setting the LEDs to white as a test of my if statement, which so far is not working.

 

I've heard of using some other random character ($%#&!) as my break between color data packets, but I'd prefer to learn how to accurately detect the end of a line, either \r, \n, or \r\n.

Are these considered characters? Will parseInt be able to read them in? Or is a simple if statement and Serial.read enough?

 

Thanks for the help,

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
'\r\n'

is not valid in C. That is TWO characters. You cannot use == for comparing more than one character. Perhaps you mean something like
 

if (strncmp(..read(), 2, "\r\n") == 0) ...

however I don't know what overloads the Serial class has for reading strings?

 

Also your logic does not seem to make sense. You say every line ends in "\r\n" yet you read valid "red", "green", "blue" then if you see "\r\n" you set them all to 255. Doesn't that mean they are always going to be 255, 255, 255 ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ThomasTesla wrote:

I'm only setting the LEDs to white as a test of my if statement, which so far is not working.

 

That was the intent. Seeing as the LEDs do NOT turn white every time, this is not working. The if statement never triggers.

 

I'll try that code segment, thanks.