ATtiny402-FireFly-(remake of ATtiny85 fireflys)

I completely re-designed and all new code for realism, my ATtiny85 Fire fly's. I did this with the new ATtiny402. This is a major upgrade. The ATtiny85 Fire fly did win an award. The 2022 #3 best ATtiny85 project. https://all3dp.com/2/best-attiny85-projects/

This new design and code implement the LTC4054 for charging the battery from the solar panel, which only needs a little bit of charge (in my old version I used a TP4056 module). This LTC4054 module is one I designed and the schematics are below. This version doesn't care if its night or not. If it detects motion, it lights. Due to the PIR, it consumes about 14uA. I have other projects that use the AM312 PIR and they will last and run for 6 months without a recharge.. But since this does have a solar panel (only charging at 20-40ma) should run indefinitely . I made the fire fly's the same as before by 3d printing. Then I attached the LED's to the fire flies by using UV glue and also mixed in glow in the dark powder. It just make for a nice effect. I used Yellow LED's in this new version (mainly because the lower voltage of the LED compared to the green LED's from my ATtiny85 version).

Update 7/7/2023: Changed Lipo Battery Charger to 1.6v

Update 9/1/2023: Need to change for dark detection, if battery goes dead then gets charged, for some reason it would not restart.

20230603_070826.mp4

Used PCB from my Meteor light project. Works Great, just can't use Pin 6 (#1 output) due to Timer B being used for fire fly flashing.

Battery management (also have DW01 set up on Battery for protection as well).

PCB available at https://oshpark.com/profiles/shermluge

/* * ATtiny402-analogWrite-firefly-motion.c * * Compiled in Microchip Studio 7 * * Works fantastic, looks real * Change pins to work with ATtiny402-Meteor-lights-PCB * Pins Changed.. Works with PCB now. *  * To do: * Still cannot get it working with the PIR pin2 interrupt. Even tried pin6 no luck * try changing resistor (even though it works with other software). * See if interrupt is working with test points, Maybe lights just not lighting after sleep * (although they wake up after WDT and Sleep) * * UPDATE 5/31/2023: * Fixed Motion issue.. WDT was starting over, resetting the sleepCount variable..  * The motion did not. So added one line in and now the motion works great!!!! * * Created: 5/29/2023 11:05:50 * Author : Sherman Stebbins */ 
//Pins used PA1, PA2, PA7 (PA6 is attached to timer b)#define F_CPU 10000000UL#include <util/delay.h>#include <avr/io.h>#include <avr/interrupt.h>#include <stdlib.h> //rand()#include "analogWrite.h"
#define TCB_TIMEOUT_VALUE 0x000f //change timer speed less is more (0x000f is fast as you can go)#define DOWN 0#define UP 1#define HOLD 2#define PAUSE 3#define MAXSLEEPCOUNT 4#define MAXLEDTHATLIGHT 12
void gotosleep(void);void ledOn(uint8_t led, uint8_t value);void tcbInit(void);uint8_t adcResult(void);
volatile uint8_t upDown = UP;volatile uint8_t count = 0;volatile uint8_t pause = 0;volatile uint8_t sleepCount = 0;volatile uint16_t loopCount = 0;volatile uint8_t rampDownSpeed = 1;volatile uint8_t ledLight = 0;volatile int randLoopCount = 0;volatile int randNum = 0;volatile int randCount = 0;volatile int randSleepCount = 5;volatile unsigned char seedCount __attribute__((section(".noinit"))); // Not cleared on RESET
ISR(TCB0_INT_vect){ TCB0.INTFLAGS = TCB_CAPT_bm; /* Clear the interrupt flag */ //PORTA.OUTTGL = PIN6_bm; /* Toggle PB6 GPIO */ //for Scope (does slow it down a tad)
loopCount++;}
ISR(PORTA_PORT_vect){ // PORTA.INTFLAGS = 0x04; //clear int flag, from Ben Heck.. PORTA.INTFLAGS = PORT_INT2_bm; // got from iotn402.h}
int main(void){ randSleepCount++; CCP=0xD8; //unlock cpu clock CLKCTRL.MCLKCTRLB = 0; // go full speed seedCount++; uint8_t newSeedcount = adcResult(); srand(newSeedcount); if(seedCount>=200){ seedCount=0; } PORTA.DIR = 0b00000000; tcbInit(); //pull up pin 2 and set it to trigger int on falling edge: PORTA.PIN2CTRL = PORT_ISC_RISING_gc; //this was my fix for changing current draw sei(); TCA0_init(); //if you want motion detection: //pull up pin 2 and set it to trigger int on falling edge:// PORTA.PIN3CTRL = PORT_ISC_RISING_gc; //this was my fix for changing current draw SLPCTRL.CTRLA = SLPCTRL_SMODE_PDOWN_gc | SLPCTRL_SEN_bm; //Microchip studio showed this //Set random stuff: randLoopCount = (rand() % 800) +200; randNum = (rand() % 20) +1; randCount = (rand() % 200)+50; randSleepCount = (rand() % MAXLEDTHATLIGHT)+5; ledLight = (rand() % 6); uint8_t randHold =0; while (1) { if(loopCount>randLoopCount){ randLoopCount = (rand() % 800) +200; loopCount=0; switch(upDown){ case UP : // analogWrite(1,count); randNum = (rand() % 20) +1; randCount = (rand() % 200)+50; if(count % randNum && count<randCount){ pause=1; } count++; if(count>254){ upDown=HOLD; count--; } break; case HOLD : randHold = (rand() % 85)+1; for(uint8_t i=0;i<randHold;i++){ _delay_ms(1); } upDown=DOWN; case DOWN : count=count - rampDownSpeed; if(count<=0){ rampDownSpeed = (rand() % 3)+1; count = 0; upDown=PAUSE; sleepCount++; } break; case PAUSE : PORTA.DIR = 0b00000000; uint8_t randPause = (rand() % 150)+10; for(uint8_t i=0;i<randPause;i++){ _delay_ms(1); } ledLight = (rand() % 6); upDown=UP; break; } if(pause){ ledOn(ledLight,1); pause=0; }else{ ledOn(ledLight,count); } } if(sleepCount>randSleepCount){ sleepCount=0;  //this sovlved motion sensor... PORTA.DIR = 0b00000000; gotosleep(); } }}
uint8_t adcResult(void){ PORTA.DIR &= ~(PIN6_bm); uint8_t adcVal = 0; ADC0.CTRLA = 0b00000111; //0b00000100 is resolution at 8bit, 0b00000010 freerun, 0b00000001 enable ADC0.CTRLB = 0x0; //sample at 2 accumulation ADC0.CTRLC = 0b00010000; ADC0.MUXPOS = 0x6; //set to open pin six ADC0.COMMAND = 0x1; //start conversion while(!(ADC0.INTFLAGS & ADC_RESRDY_bm)){;} // _delay_us(10); adcVal = ADC0.RES; ADC0.CTRLA = 0; return adcVal;}void tcbInit(void){ /* Load the Compare or Capture register with the timeout value*/ TCB0.CCMP = TCB_TIMEOUT_VALUE; /* Enable TCB and set CLK_PER divider to 1 (No Prescaling) */ TCB0.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; /* Enable Capture or Timeout interrupt */ TCB0.INTCTRL = TCB_CAPT_bm;
}
void ledOn(uint8_t led, uint8_t value){ PORTA.OUT = 0b00000000; PORTA.DIR = 0b00000000; switch (led){ case 0: PORTA.DIR |= (PIN1_bm|PIN3_bm); PORTA.OUTCLR = PIN3_bm; analogWrite(1,value); break; case 1: PORTA.DIR |= (PIN3_bm|PIN1_bm); PORTA.OUTCLR = PIN1_bm; analogWrite(3,value); break; case 2: PORTA.DIR |= (PIN1_bm|PIN7_bm); PORTA.OUTCLR =PIN7_bm; analogWrite(1,value); break; case 3: PORTA.DIR |= (PIN7_bm|PIN1_bm); PORTA.OUTCLR = PIN1_bm; analogWrite(7,value); break; case 4: PORTA.DIR |= (PIN3_bm|PIN7_bm); PORTA.OUTCLR = PIN7_bm; analogWrite(3,value); break; case 5: PORTA.DIR |= (PIN7_bm|PIN3_bm); PORTA.OUTCLR = PIN3_bm; analogWrite(7,value); break; default: break; }}

void gotosleep(void){ PORTA.DIR = 0;
PORTA.PIN3CTRL = PORT_PULLUPEN_bm; PORTA.PIN1CTRL = PORT_PULLUPEN_bm; PORTA.PIN7CTRL = PORT_PULLUPEN_bm; PORTA.PIN6CTRL = PORT_PULLUPEN_bm;// PORTA.PIN2CTRL = PORT_PULLUPEN_bm; TCB0.CTRLA = 0x00; TCA0.SPLIT.CTRLA = 0x00; TCA0.SPLIT.CTRLB = 0x00; TCA0.SPLIT.CTRLD = 0x00;
// CCP = 0xd8;// WDT.CTRLA = 0xb; PORTA.PIN2CTRL = PORT_ISC_RISING_gc; //this was my fix for changing current draw sei(); SLPCTRL.CTRLA = 0b00000101; //POWERDOWN, SEN sleep enable
asm("sleep"); //sleep //turn off pull ups after sleep PORTA.PIN3CTRL &= ~(PORT_PULLUPEN_bm); PORTA.PIN1CTRL &= ~(PORT_PULLUPEN_bm); PORTA.PIN7CTRL &= ~(PORT_PULLUPEN_bm); PORTA.PIN6CTRL &= ~(PORT_PULLUPEN_bm);// uint8_t newSeed = adcResult(); // srand(newSeed);
//Put flashing light here test: Not tested yet// PORTA.DIR |= PIN6_bm;// PORTA.OUT |= PIN6_bm;// _delay_ms(1000);// PORTA.OUTCLR = PIN6_bm;// PORTA.DIR = 0b00000000; // CCP = 0xd8;// WDT.CTRLA = 0; TCA0_init(); tcbInit(); randSleepCount = (rand() % 6);}