#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h> // For rand()

#define NEO_PIN PB0           // Use PB0 (Pin 0)
#define NEO_PORT PORTB
#define NEO_DDR DDRB
#define PIXEL_COUNT 16         // Number of NeoPixels (adjust as needed)

uint8_t pixelData[PIXEL_COUNT * 3]; // Array to store RGB data for all pixels

// Function to send one byte to the NeoPixel
void NEO_sendByte(uint8_t byte) {
    for (uint8_t bit = 8; bit; bit--) {
        asm volatile(
            "sbi  %[port], %[pin]   \n\t"  // Set pin HIGH
            "sbrs %[byte], 7        \n\t"  // Skip next if MSB is 1
            "cbi  %[port], %[pin]   \n\t"  // Set pin LOW if MSB is 0
            "rjmp .+0               \n\t"  // 2-cycle delay
            "add  %[byte], %[byte]  \n\t"  // Shift byte left by 1
            "cbi  %[port], %[pin]   \n\t"  // Set pin LOW
            :
            : [port] "I" (_SFR_IO_ADDR(NEO_PORT)),
              [pin]  "I" (NEO_PIN),
              [byte] "r" (byte)
        );
    }
}

// Function to send a pixel color in GRB format
void sendPixelColor(uint8_t green, uint8_t red, uint8_t blue) {
    NEO_sendByte(green);
    NEO_sendByte(red);
    NEO_sendByte(blue);
}

// Function to send all pixel data
void sendPixels() {
    for (uint8_t i = 0; i < PIXEL_COUNT; i++) {
        sendPixelColor(pixelData[i * 3], pixelData[i * 3 + 1], pixelData[i * 3 + 2]);
    }
}

// Initialize all pixels to black
void clearPixels() {
    for (uint8_t i = 0; i < PIXEL_COUNT * 3; i++) {
        pixelData[i] = 0x00;
    }
}

// Add random color to a specific pixel
void setPixelRandom(uint8_t index) {
    pixelData[index * 3] = (uint8_t)(rand() % 256);     // Random green
    pixelData[index * 3 + 1] = (uint8_t)(rand() % 256); // Random red
    pixelData[index * 3 + 2] = (uint8_t)(rand() % 256); // Random blue
}

int main(void) {    
    // Set NEO_PIN as output
    NEO_DDR |= (1 << NEO_PIN);
    
    clearPixels(); // Start with all pixels black
    uint8_t currentPixel = 0;
    uint8_t allFilled = 0; // Flag to indicate all pixels have been assigned colors
    
    while (1) {
        if (!allFilled) {
            // Add a random color to the next pixel
            setPixelRandom(currentPixel);
            currentPixel++;
            if (currentPixel >= PIXEL_COUNT) {
                currentPixel = 0; // change to allFilled = 1 to randomise pixel filling
            }
        } else {
            // Randomly change one pixel at a time
            setPixelRandom(rand() % PIXEL_COUNT);
        }
        
        sendPixels();     // Send updated colors to NeoPixels
        _delay_ms(500);   // Adjust delay as needed for animation speed
    }
}
ATTINY8520PU