commit 9bce215a08e6bc8f5aeccf8fafbcabac36d41c93 Author: Dustin Brunner Date: Sun Mar 28 15:48:30 2021 +0200 Version 1 diff --git a/ATtiny_WS2812_garden_lamp.fzz b/ATtiny_WS2812_garden_lamp.fzz new file mode 100644 index 0000000..cadf560 Binary files /dev/null and b/ATtiny_WS2812_garden_lamp.fzz differ diff --git a/Program_ATtiny.fzz b/Program_ATtiny.fzz new file mode 100644 index 0000000..54df3ff Binary files /dev/null and b/Program_ATtiny.fzz differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e9dcdbd --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# ATtiny 13 WS2812B-LED Fire-Effect Garden Lamp +This is how you can build a Garden Lamp based on an ATtiny13 and some WS2812B (Neopixel) LEDs. +The LEDs are Simulating a nice-looking Fire-Effect. This effect is generated using a random number generator. +It also features a LDR Light-Sensor to turn on the Lamp in the Darkness automatically. +In the code you can set a time after which the lamp automatically turns off, even if it is still dark. + + + +# Software +The software is based on several different code snippets. You can find links to them in the Sources-Tab. +There are a view different configuration options (like on-time, color, ...), which can be done in the `config.h` file. They are well described in this file. + +### Features +1. Fire-Effect turns on when LDR-value gets under `on_val` as defined +2. Second state is entered after the timeout `max_on_time` is reached. The LEDs light in the color, defined as `timeout_...`. +3. If it gets light again the LEDs light in the color, defined as `day_...`. If you want it to be off, set all values to 0. + +## Program the ATtiny +### 1. Upload `ArduinoISP` sketch to the Arduino + You can find it in the Examples folder of the Arduino-IDE + +### 2. wiring + I use a Arduino Nano to program the ATtiny, but also a Arduino UNO will do fine. Hook up the ATtiny like it is shown in the schematic: + + The capacitor is important because it prevents the Arduino from resetting itself while programming the ATtiny. + +### 3. Download ATtiny13 support for the IDE + Add the URL `http://drazzy.com/package_drazzy.com_index.json` to the Additional Boards Manager URLs in the IDE Preferences. After that open the Boards-Manager, search for "DIY Attiny" and install it. Now you should find the Attiny13 and some other Attiny Boards in the Board menu. + +### 4. Uploading + Open the code in the Arduino IDE and select the Attiny13 as the Board. + Select the following settings: + + + These settings are important: + - Processor-Speed to 9.6 Mhz + - Millis, Tone Support to "No Millis, No Tone" + + **After that Press `Burn Bootloader` to make sure that these Settings are applied.** + + Now you can upload the code by simply pressing **Upload**. + + + +# Hardware +## Schematic + + +## PCB Layout + + +## ATtiny-Pinout + +
(Source: https://cdn.sparkfun.com/assets/f/8/f/d/9/52713d5b757b7fc0658b4567.png) + + + +# Sources / Useful Links +You can find more detailed information here: +- Arduino ISP: https://www.instructables.com/Updated-Guide-on-How-to-Program-an-Attiny13-or-13a/ +- Light sensor: https://www.elec-cafe.com/attiny85-light-sensor-switch/ +- Timer Interrupt: https://arduinodiy.wordpress.com/2015/06/22/flashing-an-led-with-attiny13/ +- WS2812B Lib: + - https://blog.podkalicki.com/attiny13-controlling-leds-ws2811ws2812/ + - https://www.instructables.com/Updated-Guide-on-How-to-Program-an-Attiny13-or-13a/ +- Random number gernerator lib: + - https://blog.podkalicki.com/attiny13-pseudo-random-numbers/ + - https://github.com/lpodkalicki/blog/tree/master/avr/attiny13/009_lightweigth_prng_based_on_lfsr +- Fire Effect: + - https://codebender.cc/sketch:271084#Neopixel%20Flames.ino + - WS2812FX-Lib, "Fire-Flicker" Effect: https://github.com/kitesurfer1404/WS2812FX + +
+ +### I hope you like this project! + +
+

This work by Dustin Brunner is licensed under CC BY 4.0

+ +Creative Commons Lizenzvertrag
Dieses Werk von Dustin Brunner ist lizenziert unter einer Creative Commons Namensnennung 4.0 International Lizenz. diff --git a/code/code.ino b/code/code.ino new file mode 100644 index 0000000..ca684c5 --- /dev/null +++ b/code/code.ino @@ -0,0 +1,134 @@ +/* + ATtiny 13 WS2812B-LED Fire-Effect Garden Lamp + Author: dustinbrun + licensed under CC BY 4.0 + + Version 03.2021 + + + Make sure to Set in the Board Settings: + Processor-Speed to 9.6 Mhz + Millis, Tone Support to "No Millis, No Tone" + After that Press "Burn Bootloader" to make sure that these Settings are applied + + + ----------------------------- !!!! Configuration can be done in the "config.h file !!!! ----------------------------------------- + +*/ + +#include "light_ws2812.c" +#include "light_ws2812.h" +#include "random_avr.h" +#include "random_avr.c" +#include "config.h" + + +bool on = true; +bool was_on = true; +volatile unsigned long counter = 0;// interrupt needs volatile variable + +struct pix +{ + uint8_t g; + uint8_t r; + uint8_t b; +}; +pix pixel[PIXEL_NUM]; + + +ISR(TIMER0_OVF_vect) { + /* + Timer Configuration: + Prescaler 1024, CPU-Speed 9,6MHz = 9600000 Hz + + Interrupt is called with a Frequency of: 9600000Hz/1024/256 = 36,6Hz + so every --> 0,027s <--- + + */ + counter++; +} + + +void setup() +{ + + random_init(0xabcd); // initialize 16 bit seed + pinMode(LDR_pin, INPUT); + pinMode(2, OUTPUT); + + //Interrupt Setup + cli(); // Clear interrupts, just to make sure + TCCR0B |= (1 << CS02) | (1 << CS00); // set prescale timer to 1/1024th, set CS02 and CS00 bit in TCCR0B + TIMSK0 |= 1 << TOIE0; // enable timer overflow interrupt, left shift 1 to TOIE0 and OR with TIMSK + sei(); //start timer + +} + +void loop() +{ + + if (analogRead(LDR_pin) < on_val && !was_on) + { + on = true; + was_on = true; + counter = 0; + } + + if (analogRead(LDR_pin) > off_val && was_on) + { + on = false; + was_on = false; + counter = 0; + + for (int i = 0; i < PIXEL_NUM; i++) + { + pixel[i].r = day_red; + pixel[i].g = day_green; + pixel[i].b = day_blue; + } + ws2812_setleds((struct cRGB *)pixel, PIXEL_NUM); + } + + if (counter > max_on_time && on) + { + on = false; + counter = 0; + + for (int i = 0; i < PIXEL_NUM; i++) + { + pixel[i].r = timeout_red; + pixel[i].g = timeout_green; + pixel[i].b = timeout_blue; + } + ws2812_setleds((struct cRGB *)pixel, PIXEL_NUM); + } + + + if (on) + { + + // Flicker, based on our initial RGB values + int flicker = random_avr() % indensity; + int f_red = red - flicker; + int f_green = green - flicker; + int f_blue = blue - flicker; + if (f_red < 0) f_red = 0; + if (f_green < 0) f_green = 0; + if (f_blue < 0) f_blue = 0; + + for (int i = 0; i < PIXEL_NUM; i++) + { + pixel[i].r = f_red; + pixel[i].g = f_green; + pixel[i].b = f_blue; + } + ws2812_setleds((struct cRGB *)pixel, PIXEL_NUM); + + delay(400 - random_avr() % d_delay); + } + else + { + counter = 0; + } + +} diff --git a/code/config.h b/code/config.h new file mode 100644 index 0000000..1c3617d --- /dev/null +++ b/code/config.h @@ -0,0 +1,67 @@ +/* + * ---------------------------------------- + * ------ Configuration Options ----------- + * ---------------------------------------- + */ + + + +int on_val = 400; //LDR Value below that to switch the Light on +int off_val = 500; //LDR Value above that to switch the Light off +unsigned long max_on_time = 666666; // Calculation: ('on_time_in_minutes' * 60) / 0.027 + //Example: 5h (=300min) ON-time: max_on_time = (300 * 60) / 0.027 = 666666 +#define PIXEL_NUM 3 //Ammount of WS2812 LEDs + +/* + * ---------------------------------------- + * ---------- Colour Settings ------------- + * ---------------------------------------- + */ + +//----------- Flickering Effect ----------- +//Default Colours +int red = 255; +int green = 70; +int blue = 20; + +// Purple flame: + // int red = 158, green = 8, blue = 148; +// Green flame: + //int red = 74, green = 150, blue = 12; + +int indensity = 40; // Maximum Flickering offset +int d_delay = 300; // Maximum Delay offset + + +// ----- colour after timeout ---- +// This is the Colour with which the leds light up, after the timeout (max_on_time) is reached until it gets light again +// If you want it to be off, set all values to 0 +int timeout_red = 0; +int timeout_green = 0; +int timeout_blue = 5; + + +// ------ color throughout the day ------- +// This is the Colour with which the leds light up, when the LDR detects daylight +int day_red = 0; +int day_green = 0; +int day_blue = 0; + + + + +/* + * ---------------------------------------- + * ---------------- Pins ------------------ + * ---------------------------------------- + */ + +#define LDR_pin A3 +// Pin Defintion for LEDs in the light_ws2812.h File as "ws2812_pin" variable + + +/* + * ---------------------------------------- + * ---------- End of config --------------- + * ---------------------------------------- + */ diff --git a/code/light_ws2812.c b/code/light_ws2812.c new file mode 100644 index 0000000..b5fbe7f --- /dev/null +++ b/code/light_ws2812.c @@ -0,0 +1,181 @@ +/* +* light weight WS2812 lib V2.0b +* +* Controls WS2811/WS2812/WS2812B RGB-LEDs +* Author: Tim (cpldcpu@gmail.com) +* +* Jan 18th, 2014 v2.0b Initial Version +* Nov 29th, 2015 v2.3 Added SK6812RGBW support +* +* License: GNU GPL v2+ (see License.txt) +*/ + +#include "light_ws2812.h" +#include +#include +#include + +// Setleds for standard RGB +void inline ws2812_setleds(struct cRGB *ledarray, uint16_t leds) +{ + ws2812_setleds_pin(ledarray, leds, _BV(ws2812_pin)); +} + +void inline ws2812_setleds_pin(struct cRGB *ledarray, uint16_t leds, uint8_t pinmask) +{ + ws2812_sendarray_mask((uint8_t *)ledarray, leds + leds + leds, pinmask); + _delay_us(ws2812_resettime); +} + +// Setleds for SK6812RGBW +void inline ws2812_setleds_rgbw(struct cRGBW *ledarray, uint16_t leds) +{ + ws2812_sendarray_mask((uint8_t *)ledarray, leds << 2, _BV(ws2812_pin)); + _delay_us(ws2812_resettime); +} + +void ws2812_sendarray(uint8_t *data, uint16_t datlen) +{ + ws2812_sendarray_mask(data, datlen, _BV(ws2812_pin)); +} + +/* + This routine writes an array of bytes with RGB values to the Dataout pin + using the fast 800kHz clockless WS2811/2812 protocol. +*/ + +// Timing in ns +#define w_zeropulse 350 +#define w_onepulse 900 +#define w_totalperiod 1250 + +// Fixed cycles used by the inner loop +#define w_fixedlow 2 +#define w_fixedhigh 4 +#define w_fixedtotal 8 + +// Insert NOPs to match the timing, if possible +#define w_zerocycles (((F_CPU / 1000) * w_zeropulse) / 1000000) +#define w_onecycles (((F_CPU / 1000) * w_onepulse + 500000) / 1000000) +#define w_totalcycles (((F_CPU / 1000) * w_totalperiod + 500000) / 1000000) + +// w1 - nops between rising edge and falling edge - low +#define w1 (w_zerocycles - w_fixedlow) +// w2 nops between fe low and fe high +#define w2 (w_onecycles - w_fixedhigh - w1) +// w3 nops to complete loop +#define w3 (w_totalcycles - w_fixedtotal - w1 - w2) + +#if w1 > 0 +#define w1_nops w1 +#else +#define w1_nops 0 +#endif + +// The only critical timing parameter is the minimum pulse length of the "0" +// Warn or throw error if this timing can not be met with current F_CPU settings. +#define w_lowtime ((w1_nops + w_fixedlow) * 1000000) / (F_CPU / 1000) +#if w_lowtime > 550 +#error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?" +#elif w_lowtime > 450 +#warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)." +#warning "Please consider a higher clockspeed, if possible" +#endif + +#if w2 > 0 +#define w2_nops w2 +#else +#define w2_nops 0 +#endif + +#if w3 > 0 +#define w3_nops w3 +#else +#define w3_nops 0 +#endif + +#define w_nop1 "nop \n\t" +#define w_nop2 "rjmp .+0 \n\t" +#define w_nop4 w_nop2 w_nop2 +#define w_nop8 w_nop4 w_nop4 +#define w_nop16 w_nop8 w_nop8 + +void inline ws2812_sendarray_mask(uint8_t *data, uint16_t datlen, uint8_t maskhi) +{ + uint8_t curbyte, ctr, masklo; + uint8_t sreg_prev; + + ws2812_DDRREG |= maskhi; // Enable output + + masklo = ~maskhi & ws2812_PORTREG; + maskhi |= ws2812_PORTREG; + + sreg_prev = SREG; + cli(); + + while (datlen--) + { + curbyte = *data++; + + __asm volatile( + " ldi %0,8 \n\t" + "loop%=: \n\t" + " out %2,%3 \n\t" // '1' [01] '0' [01] - re +#if (w1_nops & 1) + w_nop1 +#endif +#if (w1_nops & 2) + w_nop2 +#endif +#if (w1_nops & 4) + w_nop4 +#endif +#if (w1_nops & 8) + w_nop8 +#endif +#if (w1_nops & 16) + w_nop16 +#endif + " sbrs %1,7 \n\t" // '1' [03] '0' [02] + " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low + " lsl %1 \n\t" // '1' [04] '0' [04] +#if (w2_nops & 1) + w_nop1 +#endif +#if (w2_nops & 2) + w_nop2 +#endif +#if (w2_nops & 4) + w_nop4 +#endif +#if (w2_nops & 8) + w_nop8 +#endif +#if (w2_nops & 16) + w_nop16 +#endif + " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high +#if (w3_nops & 1) + w_nop1 +#endif +#if (w3_nops & 2) + w_nop2 +#endif +#if (w3_nops & 4) + w_nop4 +#endif +#if (w3_nops & 8) + w_nop8 +#endif +#if (w3_nops & 16) + w_nop16 +#endif + + " dec %0 \n\t" // '1' [+2] '0' [+2] + " brne loop%=\n\t" // '1' [+3] '0' [+4] + : "=&d"(ctr) + : "r"(curbyte), "I"(_SFR_IO_ADDR(ws2812_PORTREG)), "r"(maskhi), "r"(masklo)); + } + + SREG = sreg_prev; +} diff --git a/code/light_ws2812.h b/code/light_ws2812.h new file mode 100644 index 0000000..e0e19f8 --- /dev/null +++ b/code/light_ws2812.h @@ -0,0 +1,103 @@ +/* + light_ws2812_config.h + + v2.4 - Nov 27, 2016 + + User Configuration file for the light_ws2812_lib + +*/ + +/////////////////////////////////////////////////////////////////////// +// Define I/O pin +/////////////////////////////////////////////////////////////////////// + +#define ws2812_port B // Data port +#define ws2812_pin 0 // Data out pin + +/////////////////////////////////////////////////////////////////////// +// Define Reset time in µs. +// +// This is the time the library spends waiting after writing the data. +// +// WS2813 needs 300 µs reset time +// WS2812 and clones only need 50 µs <<<----- +// +/////////////////////////////////////////////////////////////////////// + +#define ws2812_resettime 50 + + + + + +// ------------------------------------------------------------------------------------------------------------- + +/* WS2812_CONFIG_H_ + * light weight WS2812 lib include + * + * Version 2.3 - Nev 29th 2015 + * Author: Tim (cpldcpu@gmail.com) + * + * Please do not change this file!" + * + * License: GNU GPL v2+ (see License.txt) + * + */ + +#ifndef LIGHT_WS2812_H_ +#define LIGHT_WS2812_H_ + +#include +#include + +/* + * Structure of the LED array + * + * cRGB: RGB for WS2812S/B/C/D, SK6812, SK6812Mini, SK6812WWA, APA104, APA106 + * cRGBW: RGBW for SK6812RGBW + */ + +struct cRGB { uint8_t g; uint8_t r; uint8_t b; }; +struct cRGBW { uint8_t g; uint8_t r; uint8_t b; uint8_t w;}; + + + +/* User Interface + * + * Input: + * ledarray: An array of GRB data describing the LED colors + * number_of_leds: The number of LEDs to write + * pinmask (optional): Bitmask describing the output bin. e.g. _BV(PB0) + * + * The functions will perform the following actions: + * - Set the data-out pin as output + * - Send out the LED data + * - Wait 50µs to reset the LEDs + */ + +void ws2812_setleds (struct cRGB *ledarray, uint16_t number_of_leds); +void ws2812_setleds_pin (struct cRGB *ledarray, uint16_t number_of_leds,uint8_t pinmask); +void ws2812_setleds_rgbw(struct cRGBW *ledarray, uint16_t number_of_leds); + +/* + * Old interface / Internal functions + * + * The functions take a byte-array and send to the data output as WS2812 bitstream. + * The length is the number of bytes to send - three per LED. + */ + +void ws2812_sendarray (uint8_t *array,uint16_t length); +void ws2812_sendarray_mask(uint8_t *array,uint16_t length, uint8_t pinmask); + + +/* + * Internal defines + */ + +#define CONCAT(a, b) a ## b +#define CONCAT_EXP(a, b) CONCAT(a, b) + +#define ws2812_PORTREG CONCAT_EXP(PORT,ws2812_port) +#define ws2812_DDRREG CONCAT_EXP(DDR,ws2812_port) + +#endif /* LIGHT_WS2812_H_ */ diff --git a/code/random_avr.c b/code/random_avr.c new file mode 100644 index 0000000..c02c4cc --- /dev/null +++ b/code/random_avr.c @@ -0,0 +1,33 @@ +/** + Copyright (c) 2017, Łukasz Marcin Podkalicki + Lightweight library of 16 bit random number generator based on LFSR. +*/ + +#include +#include "random_avr.h" + +static uint16_t random_number = 0; + +static uint16_t +lfsr16_next(uint16_t n) +{ + + return (n >> 0x01U) ^ (-(n & 0x01U) & 0xB400U); +} + +void random_init(uint16_t seed) +{ +#ifdef USE_RANDOM_SEED + random_number = lfsr16_next(eeprom_read_word((uint16_t *)RANDOM_SEED_ADDRESS) ^ seed); + eeprom_write_word((uint16_t *)0, random_number); +#else + random_number = seed; +#endif /* !USE_RANDOM_SEED */ +} + +uint16_t +random_avr(void) +{ + + return (random_number = lfsr16_next(random_number)); +} diff --git a/code/random_avr.h b/code/random_avr.h new file mode 100644 index 0000000..a1514e8 --- /dev/null +++ b/code/random_avr.h @@ -0,0 +1,16 @@ +/* + Copyright (c) 2017, Łukasz Marcin Podkalicki + Lightweight library of 16 bit random number generator based on LFSR. +*/ + +#ifndef _RANDOM_H_ +#define _RANDOM_H_ + +#ifdef USE_RANDOM_SEED +#define RANDOM_SEED_ADDRESS 0x00 +#endif /* !USE_RANDOM_SEED */ + +void random_init(uint16_t seed); +uint16_t random_avr(void); + +#endif /* !_RANDOM_H_ */ diff --git a/pictures/ATtiny_WS2812_garden_lamp_Leiterplatte.png b/pictures/ATtiny_WS2812_garden_lamp_Leiterplatte.png new file mode 100644 index 0000000..4316a01 Binary files /dev/null and b/pictures/ATtiny_WS2812_garden_lamp_Leiterplatte.png differ diff --git a/pictures/ATtiny_WS2812_garden_lamp_Schaltplan.png b/pictures/ATtiny_WS2812_garden_lamp_Schaltplan.png new file mode 100644 index 0000000..93037c5 Binary files /dev/null and b/pictures/ATtiny_WS2812_garden_lamp_Schaltplan.png differ diff --git a/pictures/Program_ATtiny_Steckplatine.png b/pictures/Program_ATtiny_Steckplatine.png new file mode 100644 index 0000000..92e220e Binary files /dev/null and b/pictures/Program_ATtiny_Steckplatine.png differ diff --git a/pictures/attiny-pinout.png b/pictures/attiny-pinout.png new file mode 100644 index 0000000..a4725b9 Binary files /dev/null and b/pictures/attiny-pinout.png differ diff --git a/pictures/ide_settings.png b/pictures/ide_settings.png new file mode 100644 index 0000000..ed330d5 Binary files /dev/null and b/pictures/ide_settings.png differ