1

simplified my goal is to display a transitional Background and some shapes. This shapes are not moving hard rasterized by 1 pixel, more like .1 each cycle. So I need a way to interpolate the movement between the surrounding Neopixels.

Background works fine, so it's not relevant for now.

For the shapes the first try was to show a smooth movement of a square hardcoded. This works suitable, even its not bug free. I put the Code of the first attempt at the very end.

After that I wanted to implement an bitmap mask to show something more complex . That's where I am at currently. First of this attempt is to assume the whole mask is completly filled (again hardcoded).

The LEDs mostly keep completely on or off.

How do I get them fade them in an out at the outer sides?

I use an AI Thinker ESP 32 and 200 WS2812Bs.

Please ask any more infos that are required. Of course if you spot that my approach is unsuitable to begin with, pls tell me.

Infos for the Code: don't get lost on the "circle" variables. At the eary steps I showed a circle and soon realized its easier to step back to a Circle while working at the interpolation.

My Code with the bitmap

#include <Adafruit_NeoPixel.h>

#define PIN_NEO_PIXEL  13
#define NUM_PIXELS     400
#define num_row 20
#define num_col 20
#define circle_size_body 4
#define circle_size_border 5
#define circle_size_corona 7
#define DELAY_INTERVAL  100

float circle_PosX = 10;
float circle_PosY = 10;
int circle_direction = 1;
enum eIntersectingPart { None, Body, Border, Corona };

Adafruit_NeoPixel NeoPixel(NUM_PIXELS, PIN_NEO_PIXEL, NEO_GRB + NEO_KHZ800);

void setup()
{
  NeoPixel.setBrightness(100);
  NeoPixel.begin();
  Serial.begin(115200);           // set up Serial library at 9600 bps
}

float backgroundShift = 0;
float worldSpeed = .05;

void loop()
{
  if(circle_PosY <= 0 && circle_direction < 0
     || circle_PosY >= 10 && circle_direction > 0)
    circle_direction *= -1;
  circle_PosY += worldSpeed * circle_direction;
  circle_PosX += worldSpeed * circle_direction;
    Serial.print("circle_PosX ");
    Serial.print(circle_PosX);
    Serial.print(", circle_PosY ");
    Serial.print(circle_PosY);
    NeoPixel.clear();
    //Hintergrund setzen
    // setBackgroundColor();
        
    //Kreis malen
    drawCircle();
  
  // Serial.print("bgShift: ");
  // Serial.flush();
  NeoPixel.show(); // send the updated pixel colors to the NeoPixel hardware.
  // Serial.println(backgroundShift);
  backgroundShift += worldSpeed;
  delay(100);     // off time
}

uint32_t setBackgroundColor() {
    
  for(int led = 0; led < NUM_PIXELS; led++) {
    int coordinates[2];
    coordinateByLed(coordinates, led);
    
    byte ambientColors[3] = { 0, 0, 255 };
    int ambientCcolorS = (int)( sin( 0.3 * coordinates[0] + backgroundShift) * 127.0 );
    int ambientCcolorC = (int)( cos( 0.3 * coordinates[1] + backgroundShift) * 128.0);
    int amberC = (ambientCcolorS + ambientCcolorC + 255) / 2;
    
    NeoPixel.setPixelColor(
        led,
        NeoPixel.Color(amberC, amberC ,255));
  }
}

bool firstIteration = false;
void drawCircle() {
    //          x                   y                           Anteil
    byte bitmap[circle_size_corona + 1][circle_size_corona + 1];
    
    float startPositonX = circle_PosX - .5f * (float)circle_size_corona;
    float startPositonY = circle_PosY - .5f * (float)circle_size_corona;
    
    int lowerStartX = floor(startPositonX);
    int upperStartX = ceil(startPositonX);
    int lowerStartY = floor(startPositonY);
    int upperStartY = ceil(startPositonY);
            
    float proportionForLowerX = abs(upperStartX - startPositonX);
    float proportionForUpperX = 1 - proportionForLowerX;
    float proportionForLowerY = abs(upperStartY - startPositonY);
    float proportionForUpperY = 1 - proportionForLowerY;
    
    firstIteration = true;
    
    for(byte x = 0; x < circle_size_corona; x++) {
        for(byte y = 0; y < circle_size_corona; y++) {
            float proportion = 0;
            
            int lowerLedX = lowerStartX + x;
            int lowerLedY = lowerStartY + y;
            int upperLedX = upperStartX + x;
            int upperLedY = upperStartY + y;
            
            if(lowerLedX >= 0 || lowerLedX < num_col
                &&
                lowerLedY >= 0 || lowerLedY < num_row) {
                byte currentX = x;
                byte currentY = y;
                proportion = proportionForLowerX * proportionForLowerY;
                bitmap[currentX][currentY] += (byte)(proportion * 100);
                bitmap[currentX][currentY] = min(bitmap[currentX][currentY], (byte)100);
                
                if(lowerLedX == 6 && lowerLedY == 6) {
                    Serial.print(", x ");
                    Serial.print(x);
                    Serial.print(", y ");
                    Serial.print(y);
                    Serial.print(", partialProportion ");
                    Serial.print(proportion);
                    Serial.print(", newBitmapVal ");
                  byte newBitmapVal = bitmap[currentX][currentY];
                    Serial.print(newBitmapVal);
                    Serial.print(" || ");
                }
            }
            if(upperLedX >= 0 || upperLedX < num_col
                &&
                lowerLedY >= 0 || lowerLedY < num_row) {
                byte currentX = x + 1;
                byte currentY = y;
                proportion = proportionForUpperX * proportionForLowerY;
                bitmap[currentX][currentY] += (byte)(proportion * 100);
                bitmap[currentX][currentY] = min(bitmap[currentX][currentY], (byte)100);
                if(upperLedX == 6 && lowerLedY == 6) {
                    Serial.print(", x ");
                    Serial.print(x);
                    Serial.print(", y ");
                    Serial.print(y);
                    Serial.print(", partialProportion ");
                    Serial.print(proportion);
                    Serial.print(", newBitmapVal ");
                  byte newBitmapVal = bitmap[currentX][currentY];
                    Serial.print(newBitmapVal);
                    Serial.print(" || ");
                }
            }
            if(lowerLedX >= 0 || lowerLedX < num_col
                &&
                upperLedY >= 0 || upperLedY < num_row) {
                byte currentX = x;
                byte currentY = y + 1;
                proportion = proportionForLowerX * proportionForUpperY;
                bitmap[currentX][currentY] += (byte)(proportion * 100);
                bitmap[currentX][currentY] = min(bitmap[currentX][currentY], (byte)100);
                if(lowerLedX == 6 && upperLedY == 6) {
                    Serial.print(", x ");
                    Serial.print(x);
                    Serial.print(", y ");
                    Serial.print(y);
                    Serial.print(", partialProportion ");
                    Serial.print(proportion);
                    Serial.print(", newBitmapVal ");
                  byte newBitmapVal = bitmap[currentX][currentY];
                    Serial.print(newBitmapVal);
                    Serial.print(" || ");
                }
            }
            if(upperLedX >= 0 || upperLedX < num_col
                &&
                upperLedY >= 0 || upperLedY < num_row) {
                byte currentX = x + 1;
                byte currentY = y + 1;
                proportion = proportionForUpperX * proportionForUpperY;
                bitmap[currentX][currentY] += (byte)(proportion * 100);
                bitmap[currentX][currentY] = min(bitmap[currentX][currentY], (byte)100);
                bitmap[currentX][currentY] = min(bitmap[currentX][currentY], (byte)100);
                if(upperLedX == 6 && upperLedY == 6) {
                    Serial.print(", x ");
                    Serial.print(x);
                    Serial.print(", y ");
                    Serial.print(y);
                    Serial.print(", partialProportion ");
                    Serial.print(proportion);
                    Serial.print(", newBitmapVal ");
                  byte newBitmapVal = bitmap[currentX][currentY];
                    Serial.print(newBitmapVal);
                    Serial.print(" || ");
                }
            }
    

            firstIteration = false;
        }
    }
    
    firstIteration = true;
    for(int x = 0; x < circle_size_corona + 1; x++) {
        for(int y = 0; y < circle_size_corona + 1; y++) {
            int lowerLedX = lowerStartX + x;
            int lowerLedY = lowerStartY + y;
            int upperLedX = upperStartX + x;
            int upperLedY = upperStartY + y;
            
            if(
            // firstIteration
                lowerLedX == 6 && lowerLedY == 6
                  ||
                    upperLedX == 6 && lowerLedY == 6) {
                Serial.print(", startPositonX ");
                Serial.print(startPositonX);
                Serial.print(", x ");
                Serial.print(x);
                Serial.print(", y ");
                Serial.print(y);
                Serial.print(", lowerLedX ");
                Serial.print(lowerLedX);
                Serial.print(", lowerLedY ");
                Serial.print(lowerLedY);
            }
            
            if(lowerLedX >= 0 || lowerLedX < num_col
                &&
                lowerLedY >= 0 || lowerLedY < num_row) {
                setLedColorWithProportion(lowerLedX,lowerLedY, .01f * bitmap[x][y]);
            }
            
            firstIteration = false;
        }
    }
    
    Serial.print(" ");
    Serial.println();
}

void setLedColorWithProportion(
    int coordX, int coordY,
    float proportion)
{
    int led = ledByCoordinate(coordX, coordY);
    uint32_t ledColor = NeoPixel.getPixelColor(led);
    uint8_t partialColor = (uint8_t)((proportion ) * 255.0f );
    
    uint8_t white = (ledColor>>24) & 255;
    uint8_t red = (uint8_t)((1.0f - proportion) * ((ledColor>>16) & 255))
        + partialColor;
    uint8_t green = (ledColor>>8) & 255;
    uint8_t blue = ledColor & 255;
    
    if(
    // firstIteration
    coordX == 6 && coordY == 6
    ) {
        Serial.print(", prop ");
        Serial.print(proportion);
        Serial.print(", redValOld ");
        Serial.print(((ledColor>>16) & 255));
        Serial.print(", redValNew ");
        Serial.print(red);
    }
    
    ledColor = white;
    ledColor = (ledColor<<8) + red;
    ledColor = (ledColor<<8) + green;
    ledColor = (ledColor<<8) + blue;
    NeoPixel.setPixelColor(
        led,
        ledColor);
}

int ledByCoordinate(byte column, byte row) {
  return (row  * num_col) + (column);
}

void coordinateByLed(int coordinates[2], int led) {
  coordinates[0] = led % num_col;
  coordinates[1] = led / num_col;
}

neoopixel


First attempt without bitmap

#include <Adafruit_NeoPixel.h>

#define PIN_NEO_PIXEL  13
#define NUM_PIXELS     400
#define num_row 20
#define num_col 20
#define circle_size_body 4
#define circle_size_border 5
#define circle_size_corona 7
#define DELAY_INTERVAL  100

float circle_PosX = 10;
float circle_PosY = 10;
int circle_direction = 1;
enum eIntersectingPart { None, Body, Border, Corona };

Adafruit_NeoPixel NeoPixel(NUM_PIXELS, PIN_NEO_PIXEL, NEO_GRB + NEO_KHZ800);

void setup()
{
  NeoPixel.setBrightness(100);
  NeoPixel.begin();
  Serial.begin(9600);           // set up Serial library at 9600 bps
}

float backgroundShift = 0;
float worldSpeed = .01;

void loop()
{
  if(circle_PosY <= 0 && circle_direction < 0
     || circle_PosY >= 10 && circle_direction > 0)
    circle_direction *= -1;
  circle_PosY += worldSpeed * circle_direction;
  circle_PosX += worldSpeed * circle_direction;
    // Serial.print("circle_PosX ");
    // Serial.print(circle_PosX);
    // Serial.print(", circle_PosY ");
    // Serial.print(circle_PosY);
    NeoPixel.clear();
    //Hintergrund setzen
    // setBackgroundColor();
        
    //Kreis malen
    drawCircle();
  
  // Serial.print("bgShift: ");
  // Serial.flush();
  NeoPixel.show(); // send the updated pixel colors to the NeoPixel hardware.
  // Serial.println(backgroundShift);
  backgroundShift += worldSpeed;
  //delay(100);     // off time
}

uint32_t setBackgroundColor() {
    
  for(int led = 0; led < NUM_PIXELS; led++) {
    int coordinates[2];
    coordinateByLed(coordinates, led);
    
    byte ambientColors[3] = { 0, 0, 255 };
    int ambientCcolorS = (int)( sin( 0.3 * coordinates[0] + backgroundShift) * 127.0 );
    int ambientCcolorC = (int)( cos( 0.3 * coordinates[1] + backgroundShift) * 128.0);
    int amberC = (ambientCcolorS + ambientCcolorC + 255) / 2;
    
    NeoPixel.setPixelColor(
        led,
        NeoPixel.Color(amberC, amberC ,255));
  }
}

bool firstIteration = false;
void drawCircle() {
    float startPositonX = circle_PosX - .5f * (float)circle_size_corona;
    float startPositonY = circle_PosY - .5f * (float)circle_size_corona;
    
    int lowerStartX = floor(startPositonX);
    int upperStartX = ceil(startPositonX);
    int lowerStartY = floor(startPositonY);
    int upperStartY = ceil(startPositonY);
            
    float proportionForLowerX = abs(upperStartX - startPositonX);
    float proportionForUpperX = 1 - proportionForLowerX;
    float proportionForLowerY = abs(upperStartY - startPositonY);
    float proportionForUpperY = 1 - proportionForLowerY;
    
    // int endX = lowerStartX + circle_size_corona;
    // int endY = lowerStartY + circle_size_corona;
    
    firstIteration = true;
    
    for(int x = 0; x < circle_size_corona; x++) {
        for(int y = 0; y < circle_size_corona; y++) {
            int lowerLedX = lowerStartX + x;
            int lowerLedY = lowerStartY + y;
            int upperLedX = upperStartX + x;
            int upperLedY = upperStartY + y;
            
            /*if(firstIteration) {
                Serial.print(", startPositonX ");
                Serial.print(startPositonX);
                Serial.print(", x ");
                Serial.print(x);
                Serial.print(", y ");
                Serial.print(y);
                Serial.print(", lowerLedX ");
                Serial.print(lowerLedX);
                Serial.print(", lowerLedY ");
                Serial.print(lowerLedY);
            }*/
            
            if(lowerLedX >= 0 || lowerLedX < num_col
                &&
                lowerLedY >= 0 || lowerLedY < num_row) {
                    setLedColorWithProportion(lowerLedX,lowerLedY, proportionForLowerX * proportionForLowerY);
            }
            if(upperLedX >= 0 || upperLedX < num_col
                &&
                lowerLedY >= 0 || lowerLedY < num_row) {
                    setLedColorWithProportion(upperLedX,lowerLedY, proportionForUpperX * proportionForLowerY);
            }
            if(lowerLedX >= 0 || lowerLedX < num_col
                &&
                upperLedY >= 0 || upperLedY < num_row) {
                    setLedColorWithProportion(lowerLedX,upperLedY, proportionForLowerX * proportionForUpperY);
            }
            if(upperLedX >= 0 || upperLedX < num_col
                &&
                upperLedY >= 0 || upperLedY < num_row) {
                    setLedColorWithProportion(upperLedX,upperLedY, proportionForUpperX * proportionForUpperY);
            }
                
            firstIteration = false;
        }
    }
    // Serial.print(" ");
    // Serial.println();
}

void setLedColorWithProportion(
    int coordX, int coordY,
    float proportion)
{
    int led = ledByCoordinate(coordX, coordY);
    uint32_t ledColor = NeoPixel.getPixelColor(led);
    uint8_t partialColor = (uint8_t)((proportion ) * 255.0f );
    
    uint8_t white = (ledColor>>24) & 255;
    uint8_t red = (uint8_t)((1.0f - proportion) * ((ledColor>>16) & 255))
        + partialColor;
    uint8_t green = (ledColor>>8) & 255;
    uint8_t blue = ledColor & 255;
    
    ledColor = white;
    ledColor = (ledColor<<8) + red;
    ledColor = (ledColor<<8) + green;
    ledColor = (ledColor<<8) + blue;
    NeoPixel.setPixelColor(
        led,
        ledColor);
}

int ledByCoordinate(byte column, byte row) {
    
    /*if(firstIteration) {
        Serial.print(", column ");
        Serial.print(column);
        Serial.print(", row ");
        Serial.print(row);
        Serial.print(", ergebis ");
        Serial.print(((row  * num_col) + (column)));
    }*/
  return (row  * num_col) + (column);
}

void coordinateByLed(int coordinates[2], int led) {
  coordinates[0] = led % num_col;
  coordinates[1] = led / num_col;
}
4
  • What exactly do you mean with bitmap and mask here? Have you tried interpolating by moving the each component linearly with the position of the rectangle? Where exactly do you have problems?
    – chrisl
    Commented Feb 8, 2023 at 12:35
  • it may be easier to develop code for single row first .... for example, if there are 5 fully lit pixels, and the motion is from left to right, then the 1st pixel must fade out and the 6th pixel has to fade in ... the other 4 pixels remain unchanged
    – jsotola
    Commented Feb 8, 2023 at 16:52
  • 3
    It kind of sounds like you're asking about "anti-aliasing".
    – timemage
    Commented Feb 8, 2023 at 17:36
  • Probably not related to your problem, but setLedColorWithProportion() is a quite unconventional way to do alpha compositing: the foreground object is transparent in all channels but red. The usual expectation is for the opacity to be the same for all channels. Commented Feb 25, 2023 at 13:15

1 Answer 1

1

thanks for your advice. I stepped back to a 1D model, simplified, altered and it worked, with complex models (in 1D XD), a hard coded Array. So finally I went on for 2D and extended the code. Now it works.

Yes, it's kind of anti-aliasing.

For next I am thinking how to display multiple Elements with transparency (caused by anti aliasing or pregiven transparency).

Thats all for now.

For all who may be interested, heres my current Code.

#include <Adafruit_NeoPixel.h>

#define PIN_NEO_PIXEL  13
#define NUM_PIXELS     200
#define num_col 20
#define num_row 10
#define DELAY_INTERVAL  100

float circle_PosX = 6;
float circle_PosY = 6;
int circle_direction = 1;

Adafruit_NeoPixel NeoPixel(NUM_PIXELS, PIN_NEO_PIXEL, NEO_GRB + NEO_KHZ800);

void setup()
{
  NeoPixel.setBrightness(255);
  NeoPixel.begin();
  // Serial.begin(115200);           // set up Serial library at 9600 bps
}

float worldSpeed = .01;

void loop()
{
    if(circle_PosX <= 0 && circle_direction < 0
     || circle_PosX >= 20 && circle_direction > 0
     || circle_PosY <= 0 && circle_direction < 0
     || circle_PosY >= 10 && circle_direction > 0)
    circle_direction *= -1;
    circle_PosX += worldSpeed * circle_direction;
    circle_PosY += worldSpeed * circle_direction;
  
    NeoPixel.clear();
        
    //Kreis malen
    drawCircle();

    NeoPixel.show(); // send the updated pixel colors to the NeoPixel hardware.
    // delayMicroseconds(500);     // off time
    // readInput();
}
uint8_t bitmap[5][5] = {
    {0,0,1,0,0},
    {0,1,0,1,0},
    {1,0,0,0,1},
    {0,1,0,1,0},
    {0,0,1,0,0},
};

void drawCircle() {
    for(uint8_t pixelX = 0; pixelX < 5; pixelX++) {
        for(uint8_t pixelY = 0; pixelY < 5; pixelY++) {
            if(bitmap[pixelY][pixelX] == 1) {
                writeCircleToLEDs(
                    pixelX + circle_PosX,
                    pixelY + circle_PosY);
            }
        }
    }
}

uint8_t writeCircleToLEDs(float circlePosX, float circlePosY) {
    int lowerLedX = floor(circlePosX);
    int upperLedX = ceil(circlePosX);
    int lowerLedY = floor(circlePosY);
    int upperLedY = ceil(circlePosY);
    
    float upperLedXAmount = circlePosX - lowerLedX;
    float lowerLedXAmount = 1.0 - upperLedXAmount;
    float upperLedYAmount = circlePosY - lowerLedY;
    float lowerLedYAmount = 1.0 - upperLedYAmount;

    float amountLowerXLowerY = lowerLedXAmount * lowerLedYAmount;
    float amountUpperXLowerY = upperLedXAmount * lowerLedYAmount;
    float amountLowerXUpperY = lowerLedXAmount * upperLedYAmount;
    float amountUpperXUpperY = upperLedXAmount * upperLedYAmount;
    
    setColorOfLed(lowerLedX, lowerLedY, amountLowerXLowerY);
        
    // if(lowerLedX != upperLedX)
    {
        setColorOfLed(upperLedX, lowerLedY, amountUpperXLowerY);
    }
    // if(lowerLedY != lowerLedX )
    {
        setColorOfLed(lowerLedX, upperLedY, amountLowerXUpperY);
    }
    // if(lowerLedY != upperLedY )
    {
        setColorOfLed(upperLedX, upperLedY, amountUpperXUpperY);
    }
    /*
    Serial.print("circlePosX ");
    Serial.println(circlePosX);
    Serial.print("lowerLedX ");
    Serial.println(lowerLedX);
    Serial.print("lowerLedXAmount ");
    Serial.println(lowerLedXAmount);
    Serial.println("--------------------");*/
    
}

void setColorOfLed(uint8_t ledX, uint8_t ledY, float amount) {
    int ledNumber = ledByCoordinate(ledX, ledY);
    uint32_t  baseColor = NeoPixel.getPixelColor(ledNumber);
    uint8_t newRedColor = ((baseColor >> 16) & 255);
    newRedColor += (255 * amount);
    
    baseColor &= ~(255 << 16);
    baseColor |= (newRedColor << 16);
    
    NeoPixel.setPixelColor(
        ledNumber,
        baseColor);
}

uint8_t ledByCoordinate(uint8_t coordinateX, uint8_t coordinateY) {
    return coordinateX + num_col * coordinateY;
}

uint8_t rowReadPosY = 0;
void readInput() {
    bool changed = false;
    uint8_t rowReadPosX = 0;
    
    while(Serial.available() > 0) {
        char input = Serial.read();
        
        if(!isDigit(input)) continue;
        
        bitmap[rowReadPosY][rowReadPosX] = atoi(&input);
        
        rowReadPosX++;
        if(rowReadPosX >= 5) {
            rowReadPosY = ++rowReadPosY % 5;
            rowReadPosX = 0;
        }
        
        changed = true;
    }
    
    if(changed) {
        Serial.println("Ergebnis: ");
        
        for(uint8_t pixelX = 0; pixelX < 5; pixelX++) {
            for(uint8_t pixelY = 0; pixelY < 5; pixelY++) {
                Serial.print(bitmap[pixelY][pixelX]);
            }
            Serial.println();
        }
        
        Serial.print("Aktueller Cursor: (x,y) {");
        Serial.print(rowReadPosX);
        Serial.print(", ");
        Serial.print(rowReadPosY);
        Serial.println("}");
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.