// ===================================================================================
// Project:   TinyNeoController - NeoPixel Controller based on ATtiny13A
// Version:   v1.0
// Year:      2021
// Author:    Stefan Wagner
// Github:    https://github.com/wagiminator
// EasyEDA:   https://easyeda.com/wagiminator
// License:   http://creativecommons.org/licenses/by-sa/3.0/
// ===================================================================================
//
// Description:
// ------------
// An ATtiny13 is more than sufficient to control almost any number of NeoPixels
// via an IR remote. The NeoController was originally developed as a tester for
// 800kHz NeoPixel strings. Since there was still so much flash left in the
// ATtiny13, an IR receiver was integrated so that some parameters can be
// controlled with an IR remote control. In this way, it is also suitable as a
// simple and cheap remote-controlled control unit for NeoPixels. Due to its
// small size, it can be soldered directly to the LED strip without any problems.
// The power supply via a USB-C connection enables currents of up to 3A. There is
// still more than a third of the flash memory left for additional ideas.
//
// References:
// -----------
// The Neopixel implementation is based on NeoCandle.
// https://github.com/wagiminator/ATtiny85-TinyCandle
//
// The IR receiver implementation (NEC protocol) is based on TinyDecoder
// https://github.com/wagiminator/ATtiny13-TinyDecoder
//
// Wiring:
// -------
//                                +-\/-+
//             --- RST ADC0 PB5  1|°   |8  Vcc
//   NEOPIXELS ------- ADC3 PB3  2|    |7  PB2 ADC1 -------- 
// IR RECEIVER ------- ADC2 PB4  3|    |6  PB1 AIN1 OC0B --- 
//                          GND  4|    |5  PB0 AIN0 OC0A --- 
//                                +----+
//
// Compilation Settings:
// ---------------------
// Controller:  ATtiny13A
// Core:        MicroCore (https://github.com/MCUdude/MicroCore)
// Clockspeed:  9.6 MHz internal
// BOD:         BOD disabled
// Timing:      Micros disabled
//
// Leave the rest on default settings. Don't forget to "Burn bootloader"!
// No Arduino core functions or libraries are used. Use the makefile if 
// you want to compile without Arduino IDE.
//
// Fuse settings: -U lfuse:w:0x3a:m -U hfuse:w:0xff:m


// ===================================================================================
// Libraries and Definitions
// ===================================================================================

// Libraries
#include <avr/io.h>           // for GPIO
#include <avr/sleep.h>        // for sleep functions
#include <avr/interrupt.h>    // for interrupts
#include <util/delay.h>       // for delays
#include <stdlib.h>  // For rand()

// Pin definitions
#define NEO_PIN       PB3     // Pin for neopixels

// NeoPixel parameter
#define NEO_GRB               // type of pixel: NEO_GRB, NEO_RGB or NEO_RGBW
#define NEO_PIXELS    150     // number of pixels in the string (max 255)
uint8_t pixel_data[NEO_PIXELS * 3];  // 3 bytes per pixel (RGB)

// Global variables
uint8_t NEO_brightness = 0;   // 0..2

// ===================================================================================
// Neopixel Implementation for 9.6 MHz MCU Clock and 800 kHz Pixels
// ===================================================================================

// NeoPixel parameter and macros
#define NEO_init()    DDRB |= (1<<NEO_PIN)      // set pixel DATA pin as output
#define NEO_latch()   _delay_us(281)            // delay to show shifted colors

// Send a byte to the pixels string
void NEO_sendByte(uint8_t byte) {               // CLK  comment
  for(uint8_t bit=8; bit; bit--) asm volatile(  //  3   8 bits, MSB first
    "sbi  %[port], %[pin]   \n\t"               //  2   DATA HIGH
    "sbrs %[byte], 7        \n\t"               // 1-2  if "1"-bit skip next instruction
    "cbi  %[port], %[pin]   \n\t"               //  2   "0"-bit: DATA LOW after 3 cycles
    "rjmp .+0               \n\t"               //  2   delay 2 cycles
    "add  %[byte], %[byte]  \n\t"               //  1   byte <<= 1
    "cbi  %[port], %[pin]   \n\t"               //  2   "1"-bit: DATA LOW after 7 cycles
    ::
    [port]  "I"   (_SFR_IO_ADDR(PORTB)),
    [pin]   "I"   (NEO_PIN),
    [byte]  "r"   (byte)
  );
}

// Write color to a single pixel
void NEO_writeColor(uint8_t r, uint8_t g, uint8_t b) {
  #if defined (NEO_GRB)
    NEO_sendByte(g); NEO_sendByte(r); NEO_sendByte(b);
  #elif defined (NEO_RGB)
    NEO_sendByte(r); NEO_sendByte(g); NEO_sendByte(b);
  #elif defined (NEO_RGBW)
    NEO_sendByte(r); NEO_sendByte(g); NEO_sendByte(b); NEO_sendByte(0);
  #else
    #error Wrong or missing NeoPixel type definition!
  #endif
}

// Switch off all pixels
void NEO_clear(void) {
  for(uint8_t i = NEO_PIXELS; i; i--) NEO_writeColor(0, 0, 0);
}

// Write hue value (0..191) to a single pixel
void NEO_writeHue(uint8_t hue) {
  uint8_t phase = hue >> 6;
  uint8_t step  = (hue & 63) << NEO_brightness;
  uint8_t nstep = (63 << NEO_brightness) - step;
  switch(phase) {
    case 0:   NEO_writeColor(nstep,  step,     0); break;
    case 1:   NEO_writeColor(    0, nstep,  step); break;
    case 2:   NEO_writeColor( step,     0, nstep); break;
    default:  break;
  }
}

// ===================================================================================
// Standby Implementation
// ===================================================================================

// Go to standby mode
void standby(void) {
  NEO_clear();                                // turn off NeoPixels
  while(1) {
    GIFR  |= (1<<PCIF);                       // clear any outstanding interrupts
    sei();                                    // enable interrupts
    sleep_mode();                             // sleep until IR interrupt
    cli();                                    // disable interrupts
  }
}

// Pin change interrupt service routine
EMPTY_INTERRUPT(PCINT0_vect);                 // just wake up from sleep

// ===================================================================================
// Main Function
// ===================================================================================

int main(void) {
  // Local variables
  uint8_t start = 0;
  uint8_t speed = 3;
  uint8_t dense = 2;

  // Disable unused peripherals and prepare sleep mode to save power
  ACSR   =  (1<<ACD);                         // disable analog comperator
  PRR    =  (1<<PRADC);                       // shut down ADC
  GIMSK |=  (1<<PCIE);                        // enable pin change interrupts
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);        // set sleep mode to power down

  // Setup
  NEO_init();                                 // init Neopixels

  // Loop
 while(1) {
    // Set random color for each pixel
    for(uint8_t i = 0; i < NEO_PIXELS; i++) {
      // Generate random RGB values (range 0-255)
      pixel_data[i * 3]     = rand() %100+ 156;  // Random Red value
      pixel_data[i * 3 + 1] = rand() % 200;  // Random Green value
      pixel_data[i * 3 + 2] = rand() % 15;  // Random Blue value
    }

    // Send all pixel data to the NeoPixels
    for(uint8_t i = 0; i < NEO_PIXELS; i++) {
      NEO_writeColor(pixel_data[i * 3], pixel_data[i * 3 + 1], pixel_data[i * 3 + 2]);
    }

    // Latch the data to the NeoPixels
    NEO_latch();  // Update all the NeoPixels with the color data
    //delay(50);  // Small delay (50 milliseconds)
  }

}
ATTINY8520PU
tiny:PB5
tiny:PB3
tiny:PB4
tiny:GND
tiny:PB0
tiny:PB1
tiny:PB2
tiny:VCC
ring1:GND
ring1:VCC
ring1:DIN
ring1:DOUT