Neopixel and SD card

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 :slight_smile:
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)

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.

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 )

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

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.

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.

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.

#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:

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?

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.

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.

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.

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

What is the strip array?

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:

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?

What is the strip array?

The array that is set up when you do this:-
Adafruit_NeoPixel pixels = Adafruit_NeoPixel ....

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.

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:-

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.

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.

// 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.

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:

// 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:

29b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b29929b299... 400 times

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

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.

// 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

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.

test.txt (1.65 KB)

It looks like the limit on this is about 240 LEDs. You can probably get a bit more by taking out the print statements but it will not approach what you want so yes buy a Mega or a Zero.
Note if you go for a Zero then that uses 3V3 signals so you will have to build a buffer to drive the Neopixels.

Note we have knocked on the head your theory that you can't run an SD card and drive Neopixels.

Ok, I will buy a Mega then, the code is the exact same one right? 8K SRAM should be enough?

Thanks a lot for your help. One last question, when I modify the patterns file by adding 6 different colors:

29B299
B22929
000CFF
FBFF00
35AD4D
1EDADF
000000
000000
000000

The LED don't change colors, they're always in the blue spectrum. Why is that? I'm not mistake by saying that this:

29B299
B22929
000CFF
FBFF00
35AD4D
1EDADF

Is one frame of 6 pixels right? So my first pattern should display these pixels.

when I modify the patterns file by adding 6 different colors:

How are you changing this file?

they're always in the blue spectrum.

The blue numbers are much bigger than the other ones.

Ok so I went to the electronics store, they didn't have any Arduino Mega, only a "replica" which the guy said work great. Regardless of what he said I didn't have time to buy one online, so I got this one, and my SD shield does not seem to work with this one. It keeps telling me that the SD initialization failed. One time it detected the card, but said not FAT partition was found. It works great with the Arduino.

The Arduino Mega 2560 replica characteristics are the exact same, what am I doing wrong?

I am using the example sketch from the SD library:

I am guessing this is due to shitty hardware, could be wrong.

// include the SD library:
#include <SPI.h>
#include <SD.h>

// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;

// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
// MKRZero SD: SDCARD_SS_PIN
const int chipSelect = 4;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.print("\nInitializing SD card...");

  // we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("* is a card inserted?");
    Serial.println("* is your wiring correct?");
    Serial.println("* did you change the chipSelect pin to match your shield or module?");
    return;
  } else {
    Serial.println("Wiring is correct and a card is present.");
  }

  // print the type of card
  Serial.print("\nCard type: ");
  switch (card.type()) {
    case SD_CARD_TYPE_SD1:
      Serial.println("SD1");
      break;
    case SD_CARD_TYPE_SD2:
      Serial.println("SD2");
      break;
    case SD_CARD_TYPE_SDHC:
      Serial.println("SDHC");
      break;
    default:
      Serial.println("Unknown");
  }

  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  if (!volume.init(card)) {
    Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
    return;
  }


  // print the type and size of the first FAT-type volume
  uint32_t volumesize;
  Serial.print("\nVolume type is FAT");
  Serial.println(volume.fatType(), DEC);
  Serial.println();

  volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
  volumesize *= volume.clusterCount();       // we'll have a lot of clusters
  volumesize *= 512;                            // SD card blocks are always 512 bytes
  Serial.print("Volume size (bytes): ");
  Serial.println(volumesize);
  Serial.print("Volume size (Kbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);
  Serial.print("Volume size (Mbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);


  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
  root.openRoot(volume);

  // list all files in the card with date and size
  root.ls(LS_R | LS_DATE | LS_SIZE);
}


void loop(void) {

}

EDIT: CORRECTION. My Arduino is also returning an initialization error now. What the hell happened I didn't change anything? Is it possible that the Mega replica messed with the shield?

EDIT 2: CORRECTION. It is working on the Uno, I got the pins wrong. But it is definitely not working on the Mega replica.

The mega has different pins for the SPI bus than the Arduino. In fact these lines are not on the normal pins but on the header pins. Do a search for the exact details.

I get this once in a while:

Initializing SD card...Wiring is correct and a card is present.

Card type: SD1
Could not find FAT16/FAT32 partition.
Make sure you've formatted the card

But most of the time just could not initialized. How come?

Because the error messages assume you have an SD card connected to the SPI pins. You have not, they are floating pins so they can read back as anything. Hence the error messages make no sense.

I checked online, here is what I found:

MISO on pin 51
MOSI on pin 50
CLK on pin 52
CS on pin 53

I rewired this way, and it does not change anything. I still get card detected but no FAT partition once in 10 times or so.

Because the error messages assume you have an SD card connected to the SPI pins. You have not, they are floating pins so they can read back as anything.

Thanks for your help again. How do I fix this?

Have you changed the software so that the CS pin is pin 53?

I have changed the chipSelect variable.

I found this, but when I try these lines of code I get an error. I guess the library doesn't support that anymore.