Go Down

Topic: AtTiny85 and Neopixels (Read 252 times) previous topic - next topic

squadscope

Hey everyone!

I decided to light up my old soccer table, I want them to show to score of each player. Therefore I got an Arduino MEGA2560 controlling other LEDs and break sensors (they interrupt on goal). The MEGA than sends the score to the AtTiny using Serial1 at MEGA and SoftwareSerial at AtTiny.
The AtTiny only receives the values over serial as long as I do not type in the Neopixel code.

Working Code (all NeoPixel stuff commented out):
Code: [Select]

#include <SoftwareSerial.h>
//#include <Adafruit_NeoPixel.h>

#define PIN 1

//Adafruit_NeoPixel strip = Adafruit_NeoPixel(40, PIN, NEO_GRB + NEO_KHZ800);
SoftwareSerial mySerial(3, 4); // RX, TX

byte b = 0;
byte led[10][4] = {{0,1,38,39},{2,3,36,37},{4,5,34,35},{6,7,32,33},{8,9,30,31},{10,11,28,29},{12,13,26,27},{14,15,24,25},{16,17,22,23},{18,19,20,21}}; //mapping LEDs (40 LEDS but only 10 score on soccer table)

//uint32_t c1 = strip.Color(255, 0, 0); //color of not achieved goals
//uint32_t c2 = strip.Color(255, 200, 0); //color of achieved goals

boolean debug = true;

void setup() {
  //strip.setBrightness(255); // Global Brightness
  //strip.begin(); 
  //strip.show(); // Initialize all pixels to 'off'
  mySerial.begin(2400);           // start mySerial for output
  //resetStrip(); //Initiaize all pixels to c1 as defined before
}

void loop() {
  String s = mySerial.readString();   //Reads string by MEGA2560
  if(debug) mySerial.print(s);        //confirmes string by MEGA2560
  if(s.length() == 0) return;         //if length is 0 no string was sent -> return
  b = (byte)s.toInt();                //converts string to byte
  if(debug) mySerial.print(b);        //confirmes byte
   
  if(b == 255){
    //resetStrip();                   //255 is sent by MEGA2560 when the game is over -> strip needs to be resetted for the next game
  }else{
    if(b > sizeof(led)) b = sizeof(led);        //if accidently send a byte higher than the arrays length
    for(byte i = 0; i < b; i++){                //set the score
      for(byte j = sizeof(led[i]); j > 0; j--){
        //strip.setPixelColor(led[i][j], c2);
      }
      //strip.show();
    }
  }
}

/*
void resetStrip(){                    //port of Adafruits strandtest colorWipe - initializes all Pixels to c1
  byte j = strip.numPixels();
  for(byte i = 0; i < j; i++){
    strip.setPixelColor(i, c1);
    strip.show();
    delay(100);
  }
}
*/


'The code I need':
Code: [Select]

#include <SoftwareSerial.h>
#include <Adafruit_NeoPixel.h>

#define PIN 1

Adafruit_NeoPixel strip = Adafruit_NeoPixel(40, PIN, NEO_GRB + NEO_KHZ800);
SoftwareSerial mySerial(3, 4); // RX, TX

byte b = 0;
byte led[10][4] = {{0,1,38,39},{2,3,36,37},{4,5,34,35},{6,7,32,33},{8,9,30,31},{10,11,28,29},{12,13,26,27},{14,15,24,25},{16,17,22,23},{18,19,20,21}}; //mapping LEDs (40 LEDS but only 10 score on soccer table)

uint32_t c1 = strip.Color(255, 0, 0); //color of not achieved goals
uint32_t c2 = strip.Color(255, 200, 0); //color of achieved goals

boolean debug = true;

void setup() {
  mySerial.begin(2400);           // start mySerial for output
 
  strip.setBrightness(255); // Global Brightness
  strip.begin(); 
  strip.show(); // Initialize all pixels to 'off'
  resetStrip(); //Initiaize all pixels to c1 as defined before
}

void loop() {
  String s = mySerial.readString();   //Reads string by MEGA2560
  if(debug) mySerial.print(s);        //confirmes string by MEGA2560
  if(s.length() == 0) return;         //if length is 0 no string was sent -> return
  b = (byte)s.toInt();                //converts string to byte
  if(debug) mySerial.print(b);        //confirmes byte
   
  if(b == 255){
    resetStrip();                   //255 is sent by MEGA2560 when the game is over -> strip needs to be resetted for the next game
  }else{
    if(b > sizeof(led)) b = sizeof(led);        //if accidently send a byte higher than the arrays length
    for(byte i = 0; i < b; i++){                //set the score
      for(byte j = sizeof(led[i]); j > 0; j--){
        strip.setPixelColor(led[i][j], c2);
      }
      strip.show();
    }
  }
}

void resetStrip(){                    //port of Adafruits strandtest colorWipe - initializes all Pixels to c1
  byte j = strip.numPixels();
  for(byte i = 0; i < j; i++){
    strip.setPixelColor(i, c1);
    strip.show();
    delay(100);
  }
}


MEGA code is not needed since serial communication works fine and during the tests I used my FTDI for serial monitoring

I hope you can help me! Thanks guys!! :)

PaulRB

#1
Feb 24, 2018, 05:00 pm Last Edit: Feb 24, 2018, 05:22 pm by PaulRB
The code you are using uses software serial, which will require a lot of work by the CPU. The neopixel library also needs a lot of CPU cycles and disables interrupts to achieve the accurate timing need for the neopixel data signal. So I think the problem here could be that the tiny can't do both at the same time.

Another possibility is that you are running out of ram. The tiny only has 0.5K. Your code uses String objects, which is not wise with so little ram. Try using C style strings instead.

Also try changing this
Code: [Select]

    for(byte i = 0; i < b; i++){                //set the score
      for(byte j = sizeof(led[i]); j > 0; j--){
        strip.setPixelColor(led[i][j], c2);
      }
      strip.show();
    }

to this
Code: [Select]

    for(byte i = 0; i < b; i++){                //set the score
      for(byte j = sizeof(led[i]); j > 0; j--){
        strip.setPixelColor(led[i][j], c2);
      }
    }
    strip.show();

(I moved the strip.show() outside the outer for-loop).

Why do you use the tiny? Why not connect the neopixels direct to the Mega?

squadscope

Okay... :(

Thank you! Do you have any other idea how to send a variable of 1 byte to the attiny?

PaulRB

#3
Feb 24, 2018, 05:27 pm Last Edit: Feb 24, 2018, 05:29 pm by PaulRB
Sorry, I did several updates to my post above, please re-read.

You don't have to send the score in ASCII format. You can send it as a single byte. That might help reduce the time taken to receive the serial data, and the need for all that string manipulation code. The downside of that is that you can't easily test using usb serial and serial monitor.

Smajdalf

The code you are using uses software serial, which will require a lot of work by the CPU. The neopixel library also needs a lot of CPU cycles and disables interrupts to achieve the accurate timing need for the neopixel data signal. So I think the problem here could be that the tiny can't do both at the same time.

Another possibility is that you are running out of ram. The tiny only has 0.5K. Your code uses String objects, which is not wise with so little ram. Try using C style strings instead.
I thought the same.
But 40 Neopixels is only 120 bytes reserved by the library + 40 bytes of OPs array. Even if the String library consumes 300 bytes there is still some space for stack etc. Not much but still viable I would say. Do you thing String may consume even more memory?
Also the timing issues seem not so severe - OP checks Serial and only after he receives data he does the calculations and updates Neopixels. I think there should be no problem unless Mega sends another data while the Neopixels are being updated. The update takes a few milliseconds, the score does not change so quickly... But OP claims no message from Mega is received. With Neopixels blocking Serial I would expect rarely lost data (only when a lot of data is being send while testing).
On 8-bit (i.e. Uno) use "byte" instead of "int" if possible - it is faster and saves resources!

PaulRB

#5
Feb 24, 2018, 08:37 pm Last Edit: Feb 24, 2018, 08:45 pm by PaulRB
String objects probably request dynamically allocated heap memory, and later release it, each time they are used. I don't know how clever the "garbage collection" is on Arduino/attiny but I suspect it's almost non-existent. So although the released heap memory is free to re-use in theory, I would not be surprised if it's not available in practice, because the heap becomes "fragmented". I've never done any tests on this, but lots of forum members with more experience than me discourage use of String objects on atmega328, which has 4 times more ram than a tiny85.

I agree with all your comments, I can't be sure about any of my suggestions. It could be something else entirely, possibly some completely dumb error we haven't spotted but should have by now!

squadscope

#6
Feb 24, 2018, 09:06 pm Last Edit: Feb 24, 2018, 09:13 pm by squadscope
But wouldn't an overflow of RAM only happen after time? The Strings are very short...

The idea of NeoPixel disabling the interrupts would explain why software serial does not work anymore after enabling the neopixel code but before 'using' it... (sorry when it's hard to understand - I'm not a native speaker...)

I build myself a small and dump algorithm to send 1 byte monodirectional to the AtTiny. There is no check if data is transmitted right but it works fine for me..

Master Code:
Code: [Select]
#define PIN 2

void setup() {
  pinMode(PIN, OUTPUT);
  digitalWrite(PIN, LOW);
  
  Serial.begin(2400);
  Serial.println("Serial running!");
}

void loop() {
  byte b = (byte)Serial.readString().toInt();
  Serial.println(b);
  if(b == 255) b--;
  for(byte a = 1; a <= b; a++){
    digitalWrite(PIN, HIGH);
    delayMicroseconds(9851);
  }
  digitalWrite(PIN, LOW);
}


Slave code:
Code: [Select]
#define PIN 4

boolean done = true;

void setup() {
  pinMode(PIN, INPUT);
}

void loop() {
  if(digitalRead(PIN) == HIGH){
    done = false;
    b++;
    delay(10);
  }
}



Edit:
How do I mark this thread as solved? :o

Smajdalf

This is drawback of using someone else's libraries. I think it may be some incompatibility hidden deep inside. IIRC Digispark uses Timer1 for millis() related stuff while Arduino Uno uses Timer0. It is possible Adafruit library changes prescaler of Timer1 - nothing goes wrong on Uno but it breaks timing of the Tiny if it truly uses Timer1. But there is so many things that may go wrong. Getting rid of String class is surely the fastest thing to try and useful in either case.
On 8-bit (i.e. Uno) use "byte" instead of "int" if possible - it is faster and saves resources!

PaulRB

How do I mark this thread as solved? :o
You edit your original post and change the subject text.

But what have you solved and how?

krupski

Hey everyone!

I decided to light up my old soccer table, I want them to show to score of each player. Therefore I got an Arduino MEGA2560 controlling other LEDs and break sensors (they interrupt on goal). The MEGA than sends the score to the AtTiny using Serial1 at MEGA and SoftwareSerial at AtTiny.
The AtTiny only receives the values over serial as long as I do not type in the Neopixel code.

'The code I need':
Code: [Select]

#include <SoftwareSerial.h>
#include <Adafruit_NeoPixel.h>

Ah... another victim of AdaBloat libraries.

Try this:

Code: [Select]
////////////////////////////// WS2812B One Wire Timing ///////////////////////////////
//
//           _______________
//          |               |                        |
//  0 code: | <-- 350ns --> | <------ 900 ns ------> | <-- 1250 ns total (20~ @ 16mHz)
//          |               |________________________|
//
//
//           ________________________
//          |                        |               |
//  1 code: | <------ 900 ns ------> | <-- 350ns --> | <-- 1250 ns total (20~ @ 16mHz)
//          |                        |_______________|
//
//
//
//          |                                              |
//  RESET   | <------------- 50 us or more --------------> | <-- 50 us (800~ @ 16mHz)
//  Code:   |______________________________________________|
//
//
//////////////////////////////////////////////////////////////////////////////////////
// note: data order for ws281x is GRN, RED, BLU (MSB First)
//
//      | <------- GRN -------> | <------- RED -------> | <------- BLU -------> |
// BIT: |23 22 21 20 19 18 17 16|15 14 13 12 11 10  9  8| 7  6  5  4  3  2  1  0|
//////////////////////////////////////////////////////////////////////////////////////

void send0 (void) // send a "0" bit
{
    sbi (LED_OUT, LED_BIT);
    __builtin_avr_delay_cycles ((((F_CPU / 1e9) * 350) + 0.5) - 2);
    cbi (LED_OUT, LED_BIT);
    __builtin_avr_delay_cycles ((((F_CPU / 1e9) * 900) + 0.5) - 2);
}

void send1 (void) // send a "1" bit
{
    sbi (LED_OUT, LED_BIT);
    __builtin_avr_delay_cycles ((((F_CPU / 1e9) * 900) + 0.5) - 2);
    cbi (LED_OUT, LED_BIT);
    __builtin_avr_delay_cycles ((((F_CPU / 1e9) * 350) + 0.5) - 2);
}

void send (uint8_t red, uint8_t grn, uint8_t blu)
{
    // note: data order for ws281x is G,R,B
    uint8_t x;
    x = 8;
    while (x--) {
        ((1UL << x) & grn) ? send1() : send0();
    }
    x = 8;
    while (x--) {
        ((1UL << x) & red) ? send1() : send0();
    }
    x = 8;
    while (x--) {
        ((1UL << x) & blu) ? send1() : send0();
    }
}


Just call "send()" with a value for red, green and blue. It will control the first LED. Next call will shift the first place to the second, and the new RGB color will be set in place 0.





Gentlemen may prefer Blondes, but Real Men prefer Redheads!

Go Up