Go Down

Topic: Neopixel and SD card (Read 6198 times) previous topic - next topic

Grumpy_Mike

Code: [Select]
pixels.setPixelColor(5, (200,200,200));
No that's not right how about
Code: [Select]
pixels.setPixelColor(5, pixels.Color (200,200,200));

mooop

#16
Apr 28, 2017, 01:37 pm Last Edit: Apr 28, 2017, 01:43 pm by mooop
Any option or help is greatly appreciated.

I posted here: https://arduino.stackexchange.com/questions/37617/led-strip-and-sd-card

This guy is advising me to go for this: https://www.adafruit.com/product/1141

Except I am not positive this will actually solve my problem. Is there not an option with a regular sd card reader? I have this one: here

EDIT: Sorry GrumpyMike didn't see your message. No this is not changing anything, either way the data led on the arduino doesn't even light up. It's just not sending anything, I believe the SPI.h allocates all the pins making them unusable for something else than the sd card.
There's gotta be a way to cope with that.

Grumpy_Mike

#17
Apr 28, 2017, 11:05 pm Last Edit: Apr 28, 2017, 11:07 pm by Grumpy_Mike
Quote
I believe the SPI.h allocates all the pins making them unusable for something else than the sd card.
No that is not true. I have used an SD card and A/D bit banged on pins and two things on the I2C bus, so it is possible to use other pins with the SD card.

Your reply said:-
Quote
I had the same problem with using the SD reader with a SPI SRAM. It turns out that the very cheap SD reader cards do not like multiple SPI components on the same port.
This is nothing like the problem you described, you have not got two devices on the SPI bus. Getting that card will not help you.

What Arduino are you using? You are using 5K as an input buffer, you might be running out of memory.

mooop

I am using the latest available Arduino Uno.

The animations I store on the sd card are stored as follows:

FRAME delimiter
400 rgb values as follows: (250,250,250)
END FRAME delimiter

There can be up to 500 frames per animation, and I need a lot of animations (dozens). My micro sd is 16gb so the issue is not the storage memory, but as you say maybe the input buffer.

In which case, what would the solution be?

Thanks for all your help.

Grumpy_Mike

Quote
I am using the latest available Arduino Uno.
The Uno only has 2K of memory. You use 1.5K for your led array and then want 5.1K as a buffer for the SD card. Can you see why it is not working?
You need to cut down on the buffers. Why do you need to read the whole of the frame array in and in ASCII? Do not have an SD buffer, simply have the frame data in hex in the SD card, read the SD card and then store it in the pixel buffer ( pixel.set ). You might get away with that but even then it will be touch and go.

If you get a Mega that has 8K of memory, if you need more then you will have to go for an Arduino Zero.

mooop

#20
Apr 30, 2017, 03:13 pm Last Edit: Apr 30, 2017, 03:23 pm by mooop
I see.

How do you calculate how much buffer is needed though? How do you know I need 1.5k for my led array, and where does the 5.1k come from? I can see why it is not working because 5.1 is greater than 2, but I don't see how you get these numbers. So if you can enlighten me here :)
If there a way to calculate the needed sram for one frame of 400 rgb values in hex?

Second, how do you store the frame data in hex?

And finally, on this thread here they seem to mention you can change the buffer size in some conf file of the arduino zero. Is this accurate?
The Arduino Zero has 32Kb in sram indeed, so that could be enough, but do you use the same way you use an Arduino Uno? Just upload and go?

EDIT: Ok so, hex does indeed take less storage space, but I don't know if it will fit in the buffer.
For one led value, made of maximum 3 numbers of maximum 3 digits, with the delimiters, I need 13 characters, 13 x 400=5.2K.
With the hex and the delimiters, I need 7 x 400=2.8K. So we are still too short here. So my only option is to get the Zero or Mega?

hex: A5C1BE,
rgb: (165,193,190)

mooop

Also my global variables already use 1020 bytes (49%) of the SRAM. So it's definitely not enough. My guess is that I need to upgrade to the zero or mega. Even with sketch optimization, I'll be way above 2k.

Grumpy_Mike

Quote
How do you calculate how much buffer is needed though? How do you know I need 1.5k for my led array,
Each LED needs three bytes to store how bright it is, so 400 LEDs times 3 = 1200 bytes which is 1200/1024 = 1.1718K to be precise.

char inputString[ 5000 ] is 5000 bytes = 5000/1024 = 4.882K to be precise. ( not sure where 5.1K came from now I do the sums again )

Quote
If there a way to calculate the needed sram for one frame of 400 rgb values in hex?
It is 400 LEDs times 3 = 1200 bytes

Quote
And finally, on this thread here they seem to mention you can change the buffer size in some conf file of the arduino zero. Is this accurate?
Yes it is accurate but has absolutely nothing to do with what you are doing or want to do. This is the buffer for serial input, nothing to do with a buffer for an SD card.


Quote
For one led value, made of maximum 3 numbers of maximum 3 digits, with the delimiters,
You don't need delimiters at all. You don't need to read it all in first before you transfer it to your LED buffer. So you have no need for any SD buffer at all.

mooop

#23
Apr 30, 2017, 09:38 pm Last Edit: Apr 30, 2017, 09:43 pm by mooop
That inputstring variable was an old one I don't use anymore. But my variables still use 49% of available SRAM for some reason.

Here is my testing sketch at the moment. It reads one frame of an animation stored on the SD card and stops.

Code: [Select]


#include <Time.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif
#include <SD.h>
#include <SPI.h>


Sd2Card card;
SdVolume volume;
SdFile root;

const int chipSelect = 4;

#define PIN            6
#define NUMPIXELS      400

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

boolean stringComplete = false;  // whether the string is complete
boolean readarray=false;
String inString = "";
int c=0;
int cc=0;
int led=0;
int i=0;
int j=0;
int pixel_n=0;
unsigned long start;
unsigned long endd;

//char* ledd[]={"", "", ""};
int ledd[3]={0,0,0};

File myFile;

void setup() {


  pixels.begin(); // This initializes the NeoPixel library.
  pixels.show();

    Serial.begin(9600);
  SD.begin(4);

  myFile = SD.open("test.txt");

  while (myFile.available()) {
    
      char inChar = myFile.read();
      
      if (isDigit(inChar)) {
        inString += (char)inChar;
      }

      if (inChar == '(') {
      led=led+1;

    }
    
    if (inChar == ',') {
      ledd[c]=inString.toInt();
      inString="";
      c=c+1;
    }

    if (inChar == ')') {

      ledd[c]=inString.toInt();
      inString="";
      
      Serial.println(ledd[0]);
      Serial.println(ledd[1]);
      Serial.println(ledd[2]);
    
      Serial.println("END OF LED ");
      Serial.println(led);
      c=0;
      
    }

    if (inChar=='*') {
      Serial.write("NEXT FRAME");

      myFile.close();

      break;
    }  
  }

        delay(500);

      SPI.end();
      Serial.println("Ok we're done");
      delay(2000);

      pixels.setPixelColor(5, pixels.Color(200,200,200)); // THIS WILL NOT WORK
      pixels.show(); // THIS EITHER
      Serial.println("done");
  
}

void loop() {

}



I don't understand how I don't need a buffer. I am reading 400 values stored on the SD card, and only then I update the pixels. How do I send the value without reading it? Sorry I'm confused here.

Also, how do I not need delimiters? The only was I see is by reading char by char and send the value every 6 characters, but that's still 6 x 400=2.4K, and my global variables already use 49% of 2K. I would store the data like this: 4d220d4d220d4d220d4d220d4d220d4d220d4d220d4d220d4d220d but I still need the buffer no?

Thank you for your help and patience, it is much appreciated.

EDIT:

Quote
Each LED needs three bytes to store how bright it is, so 400 LEDs times 3 = 1200 bytes which is 1200/1024 = 1.1718K to be precise.
I need colors though. For me, this is all the data I need for one LED: 4d220d. No?

Grumpy_Mike

Quote
I don't understand how I don't need a buffer.
You read one byte from the file and then you put it in the strip array. You repeat this until you have read 400 * 3 bytes. You do not read all the frame into memory and then transfer it to the strip array.

Quote
I need colors though. For me, this is all the data I need for one LED: 4d220d. No?
Also, how do I not need delimiters?
Yes just three bytes no delimiter because you always have three bytes, you don't need anything else to tell you that all the data has been read.


Quote
But my variables still use 49% of available SRAM for some reason.
Because that is only an estimate, it will not give you an accurate figure.

mooop

Quote
You read one byte from the file and then you put it in the strip array.
What is the strip array?

Quote
Yes just three bytes no delimiter because you always have three bytes, you don't need anything else to tell you that all the data has been read.
I'm really confused. Hex color 4d220d is 6 bytes, how do I only need 3, and which array do I store it into?

The way I was thinking of doing this is:

Code: [Select]
while c<400:

while char_count<6:

str value+=str char
char_count+=1

char_count=1
setpixelcolor(c, value)
c+=1

pixels.show()


What way are you thinking of doing this?

Grumpy_Mike

#26
May 01, 2017, 03:59 pm Last Edit: May 01, 2017, 05:05 pm by Grumpy_Mike Reason: Change code so it compiles
Quote
What is the strip array?
The array that is set up when you do this:-
Adafruit_NeoPixel pixels = Adafruit_NeoPixel ....

Quote
I'm really confused. Hex color 4d220d is 6 bytes,
No it is not it is three bytes 0x4d, 0x22, 0x0d. A byte is 8 bits. It takes two characters to express one byte in hexadecimal but the storage is only one byte.

Your sample code seems to be in Python not C.
Quote
What way are you thinking of doing this?
Open a file and get the file pointer and store it in a variable called fileptr
then something like this:-

Code: [Select]

byte red,green,blue;
int count = 0;
while(count < 400){
  red = fileptr.read();
  green = fileptr.read();
  blue = fileptr.read();
pixels.setPixelColor(count, red,green,blue);
count ++;
}
pixels.show();


Edit this compiles.

mooop

It just doesn't work man, this is really annoying.

Just including the libraries messes it all up. This is the official simple test from the Adafruit Neopixel library examples. It works fine, and if I add the 2 includes SD.h and SPI.h, it doesn't work anymore.

Code: [Select]
// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// released under the GPLv3 license to match the rest of the AdaFruit NeoPixel library

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

#include <SPI.h> // When I add these 2 lines,
#include <SD.h> // it stops working, nothing lights up

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1
#define PIN            6

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS      16

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int delayval = 500; // delay for half a second

void setup() {
  // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
  if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
  // End of trinket special code

  pixels.begin(); // This initializes the NeoPixel library.
}

void loop() {

  // For a set of NeoPixels the first NeoPixel is 0, second is 1, all the way up to the count of pixels minus one.

  for(int i=0;i<NUMPIXELS;i++){

    // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(0,150,0)); // Moderately bright green color.

    pixels.show(); // This sends the updated pixel color to the hardware.

    delay(delayval); // Delay for a period of time (in milliseconds).

  }
}


However there is slight change: the Arduino "L" LED does light up. But the LED strip doesn't.

mooop

I found a working code on instructables for exactly my setup, except with 256 LEDs.

I read the frame files and it worked just fine. Then, I modified the frames, added 144 pixels to make them 400 pixels, modified the N_PIXEL variable (number of pixels) in the sketch, and it doesn't work anymore. So it is a memory issue.

Here is the working code with 200 pixels:

Code: [Select]
// NeoBoard

#include <SPI.h>
#include <SD.h>
#include <Adafruit_NeoPixel.h>

String inString = "";    // string to hold input
int char_counter=1;

int N_PIXELS = 200; // Number of pixels

void setup() {
  Serial.begin(9600);
  SD.begin(4);
}

void loop() {
  loadImages("Galaga0.txt");
  loadImages("Galaga01.txt");
}

void loadImages(String imagee) {
  File entry = SD.open(imagee);
  while (true) {
    //File entry =  dir.openNextFile();
    if (!entry) {break;}
    if (!entry.isDirectory() && entry.size() != 4096){
      //Serial.print(entry.name()); Serial.print(" - "); Serial.println(entry.size(), DEC);
      int bright = analogRead(1);
      //Serial.println(bright);
      int dim = map(bright, 0, 1023, 0, 255); //dimmer function
     
      Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_PIXELS, 6, NEO_GRB + NEO_KHZ800);
      strip.begin();
      strip.setBrightness(dim); //insert dimmer function
      int counter = 0; //start at 0

      while(counter<N_PIXELS){
        int inChar = entry.read();
        inString += (char)inChar;
        if (char_counter==6) {
          Serial.println(inString);
          Serial.println(counter);
         
          strip.setPixelColor(counter, strip.Color(0,150,0));
          inString = "";
          counter++;
          char_counter=0;
        }
        char_counter++;
      }
      strip.show();
      delay(5000); // This is how fast the arduino will cycle through the image
    }
    entry.close();
    char_counter=0;
  }
  //dir.close();
}


And if I change N_PIXELS to 400, it doesn't work. Here is what a frame file looks like:

Code: [Select]
29b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b299... 400 times

So is my only option to buy a Mega or Zero?

Grumpy_Mike

Quote
I found a working code on instructables
Instructables are crap, they are mainly written by idiots.

This particular idiot is using String which has problems with memory usage and tends to crash. It also loads in a heap of text files so it needs a large buffer

Here is some short code that will do what I said, I left the print in the code to see what it is doing. The data file is just filled up with some random hex numbers.
Code: [Select]

// Simple reading neopixels from an SD card by Mike Cook
// released under the GPLv3 license to match the rest of the AdaFruit NeoPixel library

#include <Adafruit_NeoPixel.h>

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN            6

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS      6

#include <SPI.h>
#include <SD.h>

File fileptr;
 
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int delayval = 500; // delay for half a second

void setup() {
  Serial.begin(9600);
  pixels.begin(); // This initializes the NeoPixel library.
  wipe();
  pixels.show();
   if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
    fileptr = SD.open("test.PAT");
  if (fileptr) {
    Serial.println("test.PAT open");
    // fileptr.close();
  }

}

void nextPattern(){
byte red,green,blue;
int count = 0;
while(count < 6){
    Serial.print("LED ");
    Serial.print(count);
    Serial.print(" - ");
  red = fileptr.read();
    Serial.print(red); // for seeing what is happening comment out later
    Serial.print(",");
  green = fileptr.read();
    Serial.print(green);
    Serial.print(",");
  blue = fileptr.read();
    Serial.print(blue);
    Serial.println("");
pixels.setPixelColor(count, red,green,blue);
count ++;
}
pixels.show();
}

void loop() {
  for(int i=0;i<92;i++){
    Serial.println();
    Serial.print("Pattern ");
    Serial.println(i);
    nextPattern();
    delay(delayval);
  }
  fileptr.close();
  Serial.println("file closed");
  wipe();
  pixels.show();
  Serial.println("Finished patterns press reset to repeat");
  while(true) { } // hang here with the file closed
}

void wipe(){
       for(int i=0;i<pixels.numPixels();i++){
       pixels.setPixelColor(i, pixels.Color(0,0,0));
       }
  }


 
This is the print out in the serial monitor

Code: [Select]

Pattern 89
LED 0 - 48,48,48
LED 1 - 48,48,10
LED 2 - 48,48,48
LED 3 - 48,48,48
LED 4 - 10,48,48
LED 5 - 48,48,48

Pattern 90
LED 0 - 48,10,48
LED 1 - 48,48,48
LED 2 - 48,48,10
LED 3 - 48,48,48
LED 4 - 48,48,48
LED 5 - 10,48,48

Pattern 91
LED 0 - 48,48,48
LED 1 - 48,10,48
LED 2 - 48,48,48
LED 3 - 48,48,10
LED 4 - 48,48,48
LED 5 - 48,48,48
file closed
Finished patterns press reset to repeat



Attached is the test pattern file to put on the SD card.
Change the file type from .TXT to .PAT because the forum won't let me upload strange extensions.
P.S. I just made up the .PAT short for pattern.

Go Up