ATtiny10 Fire fly's 6 with PWM
Total re write of my ATtiny402 fire fly's with full PWM on 6 led's, So far working quite well. But this is a work in progress. While on it consumes about 1mA. In sleep mode at night, it consumes 29uA for the comparator, which keeps the ATtiny10 off while there is light and charging. The ATtiny in sleep consumes 4.5uA.
Updated 8/8/23: Works Great.. Average consumption of power with red led's and code below is 1.35ma.
Updated 8/9/23: Added the LTC4054 battery charger to the schematic and PCB. Have new PCB's heading from Oshpark. https://oshpark.com/profiles/shermluge
Updated 8/10/23: V1.1 PCB HAS ERROR. BATTERY CONNECTED TO GROUND IS OKAY. BUT DO NOT PLUG IN POSITIVE SIDE TO + PIN. ATTACH IT TO TOP OF R11 WERE IT SAYS 100K. WILL BE DOING V1.2 VERY SOON.
Updated 8/15/23: V1.2 PCB is done and I updated the schematics with the changes as we..
Updated 8/27/23: V1.3 PCB corrected the issue of miss marking V1.2 PCB were the 2m resistor is and 1m (R6 and R9) needed to be swapped. The V1.2 board cans still be used by just swapping those resistors. V1.3 is marked correctly and more clearly.
Updated 05/15/24: V1.7 PCB is most up to date and has capability of using the cheaper MCP6541U (must be the U version) over the more expensive BU7230. There are two jumpers you must pick to have the pin outs correct.
All three Generations of Fire fly's. From left to right: First one ATtiny85, ATtiny402 (with motion sensor), ATtiny10 (Newest version).
ATtiny10 Fire fly's charging with charge indicator on.
Solar panel view.
GIF of some of the fire fly's glowing with Prita checking them out.
V1.0 does not have the battery charger built in.
The V1.2 & V1.3 board has the lipo charger built in. This current one shown is V1.2 PCB NOTE: R6 should be 1m and R9 should be 2m resistors. V1.3 has it correctly. Current board is 1.7.
#include <avr/io.h>#include <avr/interrupt.h>#define F_CPU 1000000UL#include <util/delay.h>
#define INIT 0#define RAMPUP 1#define HOLD 2#define RAMPDOWN 3#define BLANK 4
void ledOff(void);void initTimer0(void);void initTimer1(void);void ledOn(uint8_t ledPort);void goToSleep(uint8_t);
//Maybe make volatile?, Const? Worked making const :)const uint8_t fly[] = {2,1,5,3,4,0,5,4,0,2,1,5,4,1,2,4,1,3,5,3,3,4}; //sudo random.
volatile unsigned char nextFly __attribute__((section(".noinit"))); // Not cleared on RESET//volatile uint8_t nextFly __attribute__((section(".noinit"))); // Not cleared on RESETvolatile unsigned char nextHold __attribute__((section(".noinit"))); // Not cleared on RESETvolatile unsigned char nextSleep __attribute__((section(".noinit"))); // Not cleared on RESET//uint8_t nextFly = 0;//volatile uint8_t nextHold = 0;//volatile uint8_t nextSleep = 0;volatile uint8_t led = 0;volatile uint8_t flyMode = INIT;volatile uint16_t Resolution = 2000;
int main(void){ CCP=0xD8; WDTCSR &= ~(1<<WDE); //must have or won't sleep WDTCSR &= ~(1<<WDIE); uint16_t brightness = 0; ledOn(led); while (1) { switch (flyMode){ case INIT: brightness=1; nextFly++; if(nextFly > sizeof(fly)-1){ nextFly=0; } led = fly[nextFly]; //for testing: //led++; //if (led>5){led=0;} ledOn(led); flyMode = RAMPUP; break; case RAMPUP: if(brightness > 300){ brightness+=20; } if(brightness >= Resolution ){ flyMode = HOLD; break; } _delay_ms(10); OCR0B=brightness; OCR0A=brightness; brightness+=10; break; case HOLD: nextHold++; if(nextHold > 5){ nextHold = 0; }
if(nextHold == 0){ _delay_us(1); Resolution = 4000; }else if(nextHold == 1){ _delay_ms(10); Resolution = 3000; //4000 }else if(nextHold == 2){ //_delay_ms(100); Resolution = 5000; //6000 }else if(nextHold == 3){ //_delay_ms(200); Resolution = 6000; }else if(nextHold == 4){ //_delay_ms(300); Resolution = 6500; //8000 }else{ //_delay_ms(400); Resolution = 3500; } //Resolution = 7000; flyMode = RAMPDOWN; ledOn(led); break; case RAMPDOWN: if(brightness <= 0 ){ brightness=1; // led++; flyMode = BLANK; } OCR0B=brightness; OCR0A=brightness; brightness--; _delay_us(100); break; case BLANK: ledOff(); // _delay_ms(1000); nextSleep++; if(nextSleep>4){ nextSleep=0; }
goToSleep(nextSleep); flyMode = INIT; Resolution = 2000; break; } }}
void initTimer0(void){ //Non inverting: TCCR0A = (1 << COM0A1) | (1 << WGM01); //Toggle OC0A and OC0B on compare match | (1 << COM0A0) | (1 << COM0B1) TCCR0B = (1<<CS01) | (1 << WGM02)| (1 << WGM03); //Clear on compare, use divide by 8 clock //mode:14 1110 Fast PWM ICR0 is top ICR0=Resolution; // OCR0A = 1500;}void initTimer1(void){ //Non inverting: TCCR0A = (1 << COM0B1) | (1 << WGM01); //Toggle OC0A and OC0B on compare match | (1 << COM0A0) | (1 << COM0B1) TCCR0B = (1<<CS01) | (1 << WGM02)| (1 << WGM03); //Clear on compare, use divide by 8 clock //mode:14 1110 Fast PWM ICR0 is top ICR0=Resolution; // OCR0B = 1500;}
void ledOn(uint8_t ledPort){ switch(ledPort){ case 0: DDRB = 0b0011; initTimer0(); //led=1; break; case 1: DDRB = 0b0011; initTimer1(); // led=2; break; case 2: DDRB = 0b0110; PORTB &= ~(1<<2); initTimer1(); // led=3; break; case 3: DDRB = 0b0101; PORTB &= ~(1<<2); initTimer0(); // led=0; break; //4 and 5 can both be set up as inverting or non inverting. I chose non, it looks cool. But then put it back case 4: DDRB = 0b0101; PORTB |= (1<<2); //inverting: TCCR0A = (1 << COM0A1) | (1 << COM0A0) | (1 << WGM01); //Toggle OC0A and OC0B on compare match | (1 << COM0A0) TCCR0B = (1<<CS01) | (1 << WGM02)| (1 << WGM03); //Clear on compare, use unscaled clock ICR0=Resolution; break;
case 5: DDRB = 0b0110; PORTB |= (1<<2); //inverting: TCCR0A = (1 << COM0B1) | (1 << COM0B0) | (1 << WGM01); //Toggle OC0A and OC0B on compare match | (1 << COM0B0) TCCR0B = (1<<CS01) | (1 << WGM02)| (1 << WGM03); //Clear on compare, use unscaled clock ICR0=Resolution; break;
}}
void ledOff(){ PORTB = 0; DDRB = 0; TCCR0A = 0; TCCR0B = 0;}
void goToSleep(uint8_t sleepTime){ SMCR |= (1<<2); //SMCR.SM bit 2 (010) Power-down SMCR |= (1<<0); //SMCR.SE =1; SET JUST before Sleep starts sei() ; //set Global Interrupt Enable - if you don't add this, it will not restart. //__asm__ __volatile__("sleep") ; //enter sleep, waiting for interrupt -- This worked great!!! //below line is wrong. should be 0b01100000 because its a reserved number not a valid time hence sleeping 4 seconds. //CCP = 0xd8; Unknown if needed 20230630 switch (sleepTime){ /*case 0: WDTCSR = 0b01000010; //1000 4 sec 0111 2 sec -- sherm recheck this line!! why 16ms? break;*/ case 0: WDTCSR = 0b01000011; //1000 4 sec 0111 2 sec -- sherm recheck this line!! why 16ms? break; case 1: WDTCSR = 0b01000110; //1000 4 sec 0111 2 sec -- sherm recheck this line!! why 16ms? break; case 2: WDTCSR = 0b01100001; //1000 4 sec 0111 2 sec -- sherm recheck this line!! why 16ms? break; case 3: WDTCSR = 0b01100000; //1000 4 sec 0111 2 sec -- sherm recheck this line!! why 16ms? break; case 4: WDTCSR = 0b01000111; //1000 4 sec 0111 2 sec -- sherm recheck this line!! why 16ms? break; }// WDTCSR = 0b01000111; //1000 4 sec 0111 2 sec -- sherm recheck this line!! why 16ms? __asm__ __volatile__ ( "sleep" "\n\t" :: ); //Sleep instruction to put controller to sleep (found this syntax)}