// // Arduino Digital Light Wand + SD + LCD V1.07 (LPD8806) // by Is0-Mick 2012 // // Added support for external control switches, for UP and Down to select the filename. // // // This version can use dual strips for double the resolution (change dualstrip = false to true) // // This version supports direct reading of a 24bit BMP from the SD card. // You need to rotate the image 90 degrees right, due to lack of memory / speed to process // this on the fly... // // Added Gamma Table from ada fruit code which gives better conversion of 24 bit to 21 bit // instead of just bitshifting values. // Also added version number to display on start up. // // Fixed a strange problem with 1 green pixel being left on if the last line of the image // or last pixel was not black. Apparently seems to be a known hardware / library bug... // // Tidied up code // // Fixed Backlight Problem // // Fixed the SD init problem (removing card, trying to send a file (which fails) now re-inits the card. // Backlight now fades out, instead of just switching off. // // Changed one of the original values of the shields key values, as it was thinking you were pressing a different // button sometimes, so changed the 535 value to 600 which seems to fix that :) // // Added code for an external switch to show the bitmap so you don't have fiddle with the small button on the shield // Pins are defined as aux button. I pulled one low as a GND, and the other is the trigger.(pins 44,45 currently) #include #include #include #include #define BACKLIGHT 10 #define SDssPin 10 //SD card CS pin int frameDelay = 10; // default for the frame delay LiquidCrystal lcd(A0, A1, A2, A3, A4, A5, A6); //Init the LCD int dataPin = 3; int clockPin = 2; byte gamma(byte x); #define STRIP_LENGTH 32 boolean dualstrip = false;//set to true for dual strips LPD8806 strip = LPD8806(STRIP_LENGTH, dataPin, clockPin); //BacklightControl to save battery Life boolean BackLightTimer = false; int BackLightTimeout = 2000; int BackLightTemp = BackLightTimeout; //Stuff for the Keypad //int adc_key_val[5] ={ // 30, 150, 360, 535, 760 }; int adc_key_val[5] ={ 30, 150, 360, 600, 760 }; int NUM_KEYS = 5; int adc_key_in; int key=-1; int oldkey=-1; //AuxButton is a seperate button to send the image. Connect wires to these pins to use it. int AuxButton = 9; int AuxButtonGND = 8; //Up Button is a seperate button to move up the filenames. Connect wires to these pins to use it. int UpButton =7; int UpButtonGND = 6; //Down Button is a seperate button to move down the filenames. Connect wires to these pins to use it. int DownButton =5; int DownButtonGND =4; File root; File dataFile; String m_CurrentFilename = ""; int m_FileIndex = 0; int m_NumberOfFiles = 0; String m_FileNames[200]; //yep this is bad, but unless you are going to have over 200 images on your lightwand.. long buffer[STRIP_LENGTH]; void setup() { Serial.begin(115200); pinMode(AuxButton, INPUT); digitalWrite(AuxButton,HIGH); pinMode(AuxButtonGND, OUTPUT); digitalWrite(AuxButtonGND,LOW); Serial.print("AuxButton"); pinMode(UpButton, INPUT); digitalWrite(UpButton,HIGH); pinMode(UpButtonGND, OUTPUT); digitalWrite(UpButtonGND,LOW); Serial.print("UpButton"); pinMode(DownButton, INPUT); digitalWrite(DownButton,HIGH); pinMode(DownButtonGND, OUTPUT); digitalWrite(DownButtonGND,LOW); Serial.print("DownButton"); setupLEDs(); setupLCDdisplay(); setupSDcard(); BackLightOn(); } void setupLEDs() { strip.begin(); strip.show(); } void setupLCDdisplay() { Serial.begin(115200); Serial.print("*** DLW V.07 ***"); Serial.print("Initializing..."); delay(1000); } void setupSDcard() { Serial.begin(115200); pinMode(SDssPin, OUTPUT); while (!SD.begin(SDssPin)) { BackLightOn(); Serial.print("SD init failed!"); } Serial.print("SD init done."); delay(1000); root = SD.open("/"); Serial.print("Scanning files"); delay(500); GetFileNamesFromSD(root); isort(m_FileNames, m_NumberOfFiles); m_CurrentFilename = m_FileNames[0]; DisplayCurrentFilename(); } int ReadKeypad() { adc_key_in = analogRead(0); // read the value from the sensor digitalWrite(13, HIGH); key = get_key(adc_key_in); // convert into key press //Serial.print("key read = "); //Serial.println(adc_key_in,DEC); if (key != oldkey) // if keypress is detected { delay(50); // wait for debounce time adc_key_in = analogRead(0); // read the value from the sensor key = get_key(adc_key_in); // convert into key press if (key != oldkey) { oldkey = key; if (key >=0){ return key; } } } return key; } // Convert ADC value to key number int get_key(unsigned int input) { int k; for (k = 0; k < NUM_KEYS; k++) { if (input < adc_key_val[k]) { return k; } } if (k >= NUM_KEYS) k = -1; // No valid key pressed return k; } //The Main menu starts here... void loop() { int keypress = ReadKeypad(); if (( keypress == 1) || (digitalRead(UpButton) == LOW)) //up key (step up through the filenames) { BackLightOn(); if (m_FileIndex > 0) { m_FileIndex--; } else { m_FileIndex = m_NumberOfFiles -1; //wrap round to the last file } DisplayCurrentFilename(); delay(500); } if (( keypress == 2) || (digitalRead(DownButton) == LOW)) //down key (step down through the filenames) { BackLightOn(); if (m_FileIndex < m_NumberOfFiles -1) { m_FileIndex++; } else { m_FileIndex = 0;//wrap round to the 1st file again } DisplayCurrentFilename(); delay(500); } //Serial.print(digitalRead(AuxButton),DEC);//for displaying the key values if ((keypress == 4) || (digitalRead(AuxButton) == LOW))//select key (send out the selected file) { SendFile(m_CurrentFilename); } if(keypress == 0) //right key (frame delay +) { BackLightOn(); if (frameDelay < 200) { frameDelay+=5; } ShowFrameDelay(); } if(keypress == 3)//left key (frame delay -) { BackLightOn(); if (frameDelay > 5) { frameDelay-=5; } ShowFrameDelay(); } if (BackLightTimer == true) BackLightTime(); } void BackLightOn() { analogWrite(BACKLIGHT,255); BackLightTimer = true; BackLightTemp = BackLightTimeout; } void BackLightTime() { if ((BackLightTemp <= 255) && (BackLightTemp >= 0)) { analogWrite(BACKLIGHT,BackLightTemp); delay(1); } if (BackLightTemp <= 0) { BackLightTimer = false; BackLightTemp = BackLightTimeout; analogWrite(BACKLIGHT,0); } else { BackLightTemp --; delay(1); } } void ShowFrameDelay() { lcd.clear(); lcd.print("Frame delay:"); lcd.setCursor(0,1); lcd.print(frameDelay); delay(500); DisplayCurrentFilename(); } void SendFile(String Filename) { lcd.clear(); lcd.print("Sending File"); lcd.setCursor(0, 1); lcd.print(Filename); char temp[14]; Filename.toCharArray(temp,14); dataFile = SD.open(temp); // if the file is available send it to the LED's if (dataFile) { ReadTheFile(); dataFile.close(); ClearStrip(100); } else { lcd.clear(); lcd.print(" Error reading"); lcd.setCursor(4, 1); lcd.print("file"); BackLightOn(); delay(1000); lcd.clear(); setupSDcard();//try to re-init the SD card...(this was failing, but a fix can be done below) //In the SD.CPP in the BEGIN class which starts // boolean SDClass::begin(uint8_t csPin) { // //it needs the line below to be added // // if (root.isOpen()) root.close(); // allows repeated calls // // Just before the line // return card.init(SPI_HALF_SPEED, csPin) && // // return; } DisplayCurrentFilename(); } void DisplayCurrentFilename() { m_CurrentFilename = m_FileNames[m_FileIndex]; lcd.clear(); lcd.print(m_CurrentFilename); } void GetFileNamesFromSD(File dir) { int fileCount = 0; String CurrentFilename = ""; while(1) { File entry = dir.openNextFile(); if (! entry) { // no more files m_NumberOfFiles = fileCount; break; } else { if (entry.isDirectory()) { //GetNextFileName(root); } else { CurrentFilename = entry.name(); if (CurrentFilename.endsWith(".bmp") || CurrentFilename.endsWith(".BMP") )//find files with our extension only { m_FileNames[fileCount] = entry.name(); fileCount++; } } } } } void latchanddelay(int dur) { strip.show(); delay(dur); } void ClearStrip(int duration) { int x; for(x=0;x STRIP_LENGTH) { displayWidth = STRIP_LENGTH;//only display the number of led's we have } /* compute the line length */ uint32_t lineLength = imgWidth * 3; if ((lineLength % 4) != 0) lineLength = (lineLength / 4 + 1) * 4; Serial.println("Line Length"); Serial.println(lineLength,DEC); int x = 0; for(int y=imgHeight;y>0 ;y--) { int bufpos=0; if ( dualstrip == true) { int pos = 0; for(int x=0;x <((displayWidth)/2) ;x ++) { uint32_t offset = (MYBMP_BF_OFF_BITS + (((y-1)* lineLength) + (pos*3))) ; //Serial.println("x = "); //Serial.println(x,DEC); dataFile.seek(offset); int g=gamma(readByte()); int b=gamma(readByte()); int r=gamma(readByte()); strip.setPixelColor(x,r,g,b); g=gamma(readByte()); b=gamma(readByte()); r=gamma(readByte()); strip.setPixelColor((STRIP_LENGTH-x)-1,r,g,b); pos+=2; } latchanddelay(frameDelay/2); } else { for(int x=0;x = 0) && (j < filenames[k]); k--) { filenames[k + 1] = filenames[k]; } filenames[k + 1] = j; } } // Gamma correction compensates for our eyes' nonlinear perception of // intensity. It's the LAST step before a pixel value is stored, and // allows intermediate rendering/processing to occur in linear space. // The table contains 256 elements (8 bit input), though the outputs are // only 7 bits (0 to 127). This is normal and intentional by design: it // allows all the rendering code to operate in the more familiar unsigned // 8-bit colorspace (used in a lot of existing graphics code), and better // preserves accuracy where repeated color blending operations occur. // Only the final end product is converted to 7 bits, the native format // for the LPD8806 LED driver. Gamma correction and 7-bit decimation // thus occur in a single operation. const unsigned char gammaTable[] PROGMEM = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 20, 20, 21, 21, 21, 22, 22, 23, 23, 24, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 37, 38, 38, 39, 40, 40, 41, 41, 42, 43, 43, 44, 45, 45, 46, 47, 47, 48, 49, 50, 50, 51, 52, 52, 53, 54, 55, 55, 56, 57, 58, 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 67, 68, 69, 70, 71, 72, 73, 74, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,104,105,106,107,108, 109,110,111,113,114,115,116,117,118,120,121,122,123,125,126,127 }; inline byte gamma(byte x) { return pgm_read_byte(&gammaTable[x]); }