Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions usermods/pov_display/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
##POV Display usermod

this usermod adds a new effect called "POV Image".

To get it working:
- resize your image, the height should be same number of pixels as your led strip.
- rotate your image 90 degrees clockwise (height is now width...)
- upload a bmp image to the ESP filesystem using "/edit" url.
- select "POV Image" effect.
- set the segment name with the absolute path of the image (ie: "/myimage.bmp").
- rotate the segment at approximately 20 RPM.
- enjoy the show!
131 changes: 131 additions & 0 deletions usermods/pov_display/bmpimage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#include "bmpimage.h"
#define BUF_SIZE 64000

byte _buffer[BUF_SIZE];

uint16_t read16(File &f) {
uint16_t result;
f.read((uint8_t *)&result,2);
return result;
}

uint32_t read32(File &f) {
uint32_t result;
f.read((uint8_t *)&result,4);
return result;
}

bool BMPimage::init(const char * fn) {
File bmpFile;
int bmpDepth;
//first, check if filename exists
if (!WLED_FS.exists(fn)) {
return false;
}

bmpFile = WLED_FS.open(fn);
if (!bmpFile) {
_valid=false;
return false;
}

//so, the file exists and is opened
// Parse BMP header
uint16_t header = read16(bmpFile);
if(header != 0x4D42) { // BMP signature
_valid=false;
bmpFile.close();
return false;
}

//read and ingnore file size
read32(bmpFile);
(void)read32(bmpFile); // Read & ignore creator bytes
_imageOffset = read32(bmpFile); // Start of image data
// Read DIB header
read32(bmpFile);
_width = read32(bmpFile);
_height = read32(bmpFile);
if(read16(bmpFile) != 1) { // # planes -- must be '1'
_valid=false;
bmpFile.close();
return false;
}
bmpDepth = read16(bmpFile); // bits per pixel
if((bmpDepth != 24) || (read32(bmpFile) != 0)) { // 0 = uncompressed {
_width=0;
_valid=false;
bmpFile.close();
return false;
}
// If _height is negative, image is in top-down order.
// This is not canon but has been observed in the wild.
if(_height < 0) {
_height = -_height;
}
//now, we have successfully got all the basics
// BMP rows are padded (if needed) to 4-byte boundary
_rowSize = (_width * 3 + 3) & ~3;
//check image size - if it is too large, it will be unusable
if (_rowSize*_height>BUF_SIZE) {
_valid=false;
bmpFile.close();
return false;
}

bmpFile.close();
strcpy(filename,fn);
return true;
}

void BMPimage::clear(){
strcpy(filename, "");
_width=0;
_height=0;
_rowSize=0;
_imageOffset=0;
_loaded=false;
_valid=false;
}

bool BMPimage::load(){
uint16_t size= _rowSize * _height;
if (BUF_SIZE < size ){
return false;
}

File bmpFile = WLED_FS.open(filename);
if (!bmpFile) {
return false;
}
bmpFile.seek(_imageOffset);
bmpFile.read(_buffer, size);
bmpFile.close();
_loaded = true;
return true;
}

byte* BMPimage::line(uint16_t n){
if (_loaded) {
return (_buffer+n*_rowSize);
} else {
return NULL;
}
}

uint32_t BMPimage::pixelColor(uint16_t x, uint16_t y){
uint32_t pos;
byte b,g,r; //colors
if (! _loaded) {
return 0;
}
if ( (x>=_width) || (y>=_height) ) {
return 0;
}
pos=y*_rowSize + 3*x;
//get colors. Note that in BMP files, they go in BGR order
b= _buffer[pos++];
g= _buffer[pos++];
r= _buffer[pos];
return (r<<16|g<<8|b);
}
50 changes: 50 additions & 0 deletions usermods/pov_display/bmpimage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#ifndef _BMPIMAGE_H
#define _BMPIMAGE_H
#include "Arduino.h"
#include "wled.h"

/*
* This class describes a bitmap image. Each object refers to a bmp file on
* filesystem fatfs.
* To initialize, call init(), passign to it name of a bitmap file
* at the root of fatfs filesystem:
*
* BMPimage myImage;
* myImage.init("logo.bmp");
*
* For performance reasons, before actually usign the image, you need to load
* it from filesystem to RAM:
* myImage.load();
* All load() operations use the same reserved buffer in RAM, so you can only
* have one file loaded at a time. Before loading a new file, always unload the
* previous one:
* myImage.unload();
*/

class BMPimage {
public:
int height() {return _height; }
int width() {return _width; }
int rowSize() {return _rowSize;}
bool isLoaded() {return _loaded; }
bool load();
void unload() {_loaded=false; }
byte * line(uint16_t n);
uint32_t pixelColor(uint16_t x,uint16_t y);
bool init(const char* fn);
void clear();
char * getFilename() {return filename;};

private:
char filename[WLED_MAX_SEGNAME_LEN+1]="";
int _width=0;
int _height=0;
int _rowSize=0;
int _imageOffset=0;
bool _loaded=false;
bool _valid=false;
};

extern byte _buffer[];

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@
"name:": "pov_display",
"build": { "libArchive": false},
"dependencies": {
"bitbank2/PNGdec":"^1.0.3"
}
}
38 changes: 38 additions & 0 deletions usermods/pov_display/pov.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "pov.h"

POV::POV() {}

void POV::showLine(byte * line, uint16_t size){
uint16_t i,pos;
uint8_t r,g,b;
for (i=0; i<SEGLEN; i++) {
if (i<size) {
pos=3*i;
//using bgr order
b=line[pos++];
g=line[pos++];
r=line[pos];
SEGMENT.setPixelColor(i, CRGB(r,g,b));
} else {
SEGMENT.setPixelColor(i, CRGB::Black);
}
}
strip.show();
lastLineUpdate=micros();
}

bool POV::loadImage(char * filename){
if(!image.init(filename)) return false;
if(!image.load()) return false;
currentLine=0;
return true;
}

int16_t POV::showNextLine(){
if (!image.isLoaded()) return 0;
//move to next line
showLine(image.line(currentLine), image.width());
currentLine++;
if (currentLine == image.height()) {currentLine=0;}
return currentLine;
}
42 changes: 42 additions & 0 deletions usermods/pov_display/pov.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef _POV_H
#define _POV_H
#include "bmpimage.h"


class POV {
public:
POV();

/* Shows one line. line should be pointer to array which holds pixel colors
* (3 bytes per pixel, in BGR order). Note: 3, not 4!!!
* size should be size of array (number of pixels, not number of bytes)
*/
void showLine(byte * line, uint16_t size);

/* Reads from file an image and making it current image */
bool loadImage(char * filename);

/* Show next line of active image
Retunrs the index of next line to be shown (not yet shown!)
If it retunrs 0, it means we have completed showing the image and
next call will start again
*/
int16_t showNextLine();

//time since strip was last updated, in micro sec
uint32_t timeSinceUpdate() {return (micros()-lastLineUpdate);}


BMPimage * currentImage() {return &image;}

char * getFilename() {return image.getFilename();}

private:
BMPimage image;
int16_t currentLine=0; //next line to be shown
uint32_t lastLineUpdate=0; //time in microseconds
};



#endif
Loading
Loading