#1 2021-04-23 17:31:26

YK
Учасник
Зареєстрований: 2020-02-26
Повідомлень: 27

Солнечный фонарь ATTiny85 & WS2812b + USB зарядка

Солнечный фонарь ATTiny85 & WS2812b + USB зарядка



Снаружи

Изнутри


Возникла идея с сыном сделать простую и в тоже время непростую лампу. которая бы заряжалась от солнца и
автоматически включалась при наступлении темноты. Но чтобы имела 10 эффектов начиная от простого белого свечения ну так далее,
включая эффект свечи, конечно же с запоминанием режима.

Что понадобиться
1. Солнечная панель  https://arduino.ua/prod1353-solnechnaya-panel-6v-520-ma
2. Аккумулятор 18650.
3. Модуль питания https://arduino.ua/prod424-modyl-avtonomnogo-pitaniya-2a-na-18650-s-usb-vihodom
4. Attiny85, я брал Digispark. Так как стоимость не выше голого процессора https://arduino.ua/prod1985-plata-razrabotchika-attiny85-usb-ot-digispark
5. Кнопка включения выключения
6. Кольцо WS2812 на 12 или 16 светодиодов https://arduino.ua/prod1657-kryg-s-12-rgb-svetodiodami-ws2812-s-posledovatelnim-ypravleniem
7. Touch кнопка https://arduino.ua/prod264-ttp223-touch-key-sensornii-datchik-dlya-arduino

В качестве корпуса. Напечатали на 3d принтере след. модель https://www.thingiverse.com/thing:2966281



Схема соединения простая , паяем все к AtTiny


На PIN1 подключаем сигнальный вход ленты
На PIN4 подключаем сигнальный вход Touch
На PIN2 подключаем вход солнечной панели через резистор 1к и заземляем его резистором 6k (сопротивления можнов варьировать)

Подключаем питание и GND на все компоненты
Солнечную панель подключаем на вход модуля питания и GND
Следует уделить внимание модулю  питания, на его входе должен стоять диод шотки. Собственно я к нему и приваял провод.
Если его нет надо обязательно поставить

Все....

Основная фишка это прошивка. Задача след. пока светит солнце процессор спит и потребляет микроамперы. Вся энергия идет на заряд аккумулятора
Как только панель перестает выдавать энергию, процессор просыпаеться и запускает эффекты.
После нескольких дней экспериментов удалось все выжать по максиму.

Чтобы уменьшить энергопотребления были выаяны вся обвязка Digispark. Оставлены только конденсаторы по питанию .
Модуль Digispark был перепрошит обічной прошивкой через клипсу https://arduino.ua/prod2788-klipsa-dlya-programmatora-ch341a-sop8soic8-bios

Это было сделано по нескольким причинам,
1. Уже нет обвязки
2. MicroNucleus съедает более 2 кб памяти, а ее не хватило на все эфекты
3. MicroNucleus запускаеться 30 секунд. Соотв. лампа включаеться выключателе не сразу



Скетч

#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>

//#define CFG_SLEEP_FIXEDBTN
//#define CFG_NO_SLEEP_SWITCHBTN
#define CFG_SLEEP_SWITCHBTN

#include <Adafruit_NeoPixel.h>
#include <avr/power.h>

#define WDTO_INFINITE 255
#define SLEEP_PERIOD WDTO_8S
#define SKIP_WDT_WAKEUPS 14 // x SLEEP_PERIOD + SLEEP_PERIOD (14 ~= 120 sec)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
#ifdef CFG_SLEEP_FIXEDBTN
#define PIN 1
#define PIN_SOLAR 2
#define FIXED_BUTTON 4
#define PIN_BUTTON       -1
#define INCREASE_EFFECT_EVERY_SWITCH
#define FIXED_MODE 0
#elif defined(CFG_NO_SLEEP_SWITCHBTN)
#define PIN 1
#define PIN_SOLAR -1
#define FIXED_BUTTON -1
#define PIN_BUTTON       4

#elif  defined(CFG_SLEEP_SWITCHBTN)
#define PIN 1
#define PIN_SOLAR 2
#define FIXED_BUTTON -1
#define PIN_BUTTON       4
#endif

#ifndef NUM_PIXzs
#define NUM_PIX 12
#endif

#define EEPROM_ADDR_EFFECT_NUM 0x10


  
#define MODES_COUNT      11
#define SWITCH_MODE_DELAY_MS 5000
#define SOLAR_CHECK_DELAY_MS 1000
#define SWITCH_ON_BARIER 20




Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIX, PIN, NEO_GRB + NEO_KHZ800);

bool isOn=true;
bool isButtonPress=false;
int current_mode=0;
long next_switch_ms=0;
long next_checksolar_ms=SOLAR_CHECK_DELAY_MS;
bool isAutoModesSwitch=false;
#ifdef FIXED_MODE 
bool isFixedMode=false;
#endif
void showStrip() 
{

   strip.show();

 

}
//-----------------------------------------------------------------------------------------
void setAll(byte red, byte green, byte blue) 
{
  for(int i = 0; i < NUM_PIX; i++) 
  {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}
//-----------------------------------------------------------------------------------------
void setPixel(int Pixel, byte red, byte green, byte blue) 
{
   // NeoPixel
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));



}
void setup() 
{

  if(PIN_SOLAR>=0)
    pinMode(PIN_SOLAR,INPUT);
  if(PIN_BUTTON>=0)
    pinMode(PIN_BUTTON,INPUT);
#ifdef FIXED_MODE 
  if(FIXED_BUTTON>=0){
    pinMode(FIXED_BUTTON, INPUT_PULLUP);
    delay(20);
    isFixedMode=digitalRead(FIXED_BUTTON)==LOW;
    current_mode=FIXED_MODE;
  }
#endif
#ifdef FIXED_MODE 
if(!isFixedMode){
#endif
  byte state=eeprom_read_byte((uint8_t*)EEPROM_ADDR_EFFECT_NUM);
  if(state>0 && state<=MODES_COUNT){
   
    current_mode=state-1;
  }
  else{ 
    eeprom_write_byte((uint8_t*)EEPROM_ADDR_EFFECT_NUM, (uint8_t)(current_mode+1));
  }
#ifdef INCREASE_EFFECT_EVERY_SWITCH
   eeprom_write_byte((uint8_t*)EEPROM_ADDR_EFFECT_NUM, (uint8_t)(((current_mode-1)<MODES_COUNT?current_mode+1:0)+1));
#ifdef FIXED_MODE
}
#endif
#endif

  strip.begin();
  strip.setBrightness(255);
  strip.show(); 

 attachInterrupts();
 
}
ISR(PCINT0_vect)
{
  if(PIN_SOLAR>=0)
    isOn= (digitalRead(PIN_SOLAR)==LOW);
 if(PIN_BUTTON>=0 && !isButtonPress)  //give chance to process
   isButtonPress= (digitalRead(PIN_BUTTON)==HIGH);

//isButtonPress=!isOn;
}
ISR(WDT_vect)
{
  
}
void loop() 
{
 
  if(!isOn){
    setAll(0,0,0);
    delay(100);
 //   digitalWrite(PIN,LOW);
    sleep();
    return;
  }


  switch(current_mode){
    case 0:  Fire(40, 120, 200);break;
    case 1:  SnowSparkle(0x10, 0x10, 0x10, 20, random(100, 1000)); break;
    case 2:  RGBLoop(); break;
    case 3:   setAll(255,255,255); showStrip();delay(100); break;
    case 4:  Strobe(0x25, 0x20, 0x25, 10, 50, 1000); break; 
    case 5:  CylonBounce(0xff, 0, 0, 4, 10, 50); break;
    case 6:  Twinkle(0xff, 0, 0, 10, 100, false); break;
    case 7:  TwinkleRandom(20, 100, false); break;
    case 8:  RunningLights(0,0xFF, 0xFF, 50); break;
    case 9: rainbowCycle(20); break;
    case 10: theaterChase(0xff,0,0,50); break;
    default:
    break;
  }
  if(isButtonPress){
   // isAutoModesSwitch=!isAutoModesSwitch;
   current_mode++;
   if (current_mode >=MODES_COUNT) 
    current_mode = 0;
    eeprom_write_byte((uint8_t*)EEPROM_ADDR_EFFECT_NUM, (uint8_t)(current_mode+1));
    isButtonPress=false;
  }
  if(isAutoModesSwitch && next_switch_ms<millis()){
    current_mode++;
    if (current_mode > MODES_COUNT) current_mode = 0; 
    eeprom_write_byte((uint8_t*)EEPROM_ADDR_EFFECT_NUM, (uint8_t)(current_mode+1));
    next_switch_ms=millis()+SWITCH_MODE_DELAY_MS;
  }
 
  
}
void wakeUp(){

}
void attachInterrupts(){

    cli();
  // Disable interrupts during setup

  if(PIN_BUTTON>=0)
    PCMSK |= _BV( PIN_BUTTON);    // Enable interrupt handler (ISR) FOR BUTTON
  if(PIN_SOLAR>=0)
    PCMSK |= _BV(PIN_SOLAR);    // Enable interrupt handler (ISR) FOR SOLAR
  GIMSK |= _BV(PCIE);             // Enable PCINT interrupt in the general interrupt mask
 
  sei(); 
}
void detachInterupts(){
  if(PIN_BUTTON>=0)
    PCMSK &= ~_BV( PIN_BUTTON);    // Disable interrupt handler (ISR) FOR BUTTON
  if(PIN_SOLAR>=0)
    PCMSK &= ~_BV(PIN_SOLAR);    // Disable interrupt handler (ISR) FOR SOLAR
 
}
void sleep()
{
//  GIMSK = _BV(PCIE); // Включить Pin Change прерывания
//  if (SLEEP_PERIOD == WDTO_INFINITE )
//    PCMSK |= _BV(PIN_SOLAR); 
  
    ADCSRA &= ~_BV(ADEN);                   // ADC off
    ACSR |= _BV(ACD); // Disable analog comparator
    /*
  if (SLEEP_PERIOD != WDTO_INFINITE) {
    wdt_enable(SLEEP_PERIOD); // установить таймер
    WDTCR |= _BV(WDIE); //switch on timer interrupts
  }
  */
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); //  Power-down
  

  sleep_enable(); // 
 
  sleep_cpu();   
   cli();
    sleep_disable(); 
     ADCSRA &= _BV(ADEN); 
    ACSR |= _BV(ACD);
    attachInterrupts();
   sei();
 

  wakeUp();
 // ADCSRA |= _BV(ADEN); // on ADC
  
}

//-----------------------------------------------------------------------------------------

void SnowSparkle(byte red, byte green, byte blue, int SparkleDelay, int SpeedDelay) 
{
  setAll(red,green,blue);
 
  int Pixel = random(NUM_PIX);
  setPixel(Pixel,0xff,0xff,0xff);
  showStrip();
  delay(SparkleDelay);
  setPixel(Pixel,red,green,blue);
  showStrip();
  delay(SpeedDelay);
}
void Fire(int Cooling, int Sparking, int SpeedDelay) 
{
  static byte heat[NUM_PIX];
  int cooldown;
 
  // Step 1.  Cool down every cell a little
  for( int i = 0; i < NUM_PIX; i++) 
  {
    cooldown = random(0, ((Cooling * 10) / NUM_PIX) + 2);
   
    if(cooldown>heat[i]) 
    {
      heat[i]=0;
    } 
    else 
    {
      heat[i]=heat[i]-cooldown;
    }
  }
 
  // Step 2.  Heat from each cell drifts 'up' and diffuses a little
  for( int k= NUM_PIX - 1; k >= 2; k--) 
  {
    heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
  }
   
  // Step 3.  Randomly ignite new 'sparks' near the bottom
  if( random(255) < Sparking ) 
  {
    int y = random(7);
    heat[y] = heat[y] + random(160,255);
    //heat[y] = random(160,255);
  }

  // Step 4.  Convert heat to LED colors
  for( int j = 0; j < NUM_PIX; j++) 
  {
    setPixelHeatColor(j, heat[j] );
  }

  showStrip();
  delay(SpeedDelay);
}
void RunningLights(byte red, byte green, byte blue, int WaveDelay) 
{
  int Position=0;
 
  for(int i=0; i<NUM_PIX*2; i++)
  {
      Position++; // = 0; //Position + Rate;
      for(int i=0; i<NUM_PIX; i++) 
      {
        // sine wave, 3 offset waves make a rainbow!
        //float level = sin(i+Position) * 127 + 128;
        //setPixel(i,level,0,0);
        //float level = sin(i+Position) * 127 + 128;
        setPixel(i,((sin(i+Position) * 127 + 128)/255)*red,
                   ((sin(i+Position) * 127 + 128)/255)*green,
                   ((sin(i+Position) * 127 + 128)/255)*blue);
      }
     
      showStrip();
      delay(WaveDelay);
  }
}
void CylonBounce(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay)
{
  for(int i = 0; i < NUM_PIX-EyeSize-2; i++) 
  {
    setAll(0,0,0);
    setPixel(i, red/10, green/10, blue/10);
    for(int j = 1; j <= EyeSize; j++) 
    {
      setPixel(i+j, red, green, blue);
    }
    setPixel(i+EyeSize+1, red/10, green/10, blue/10);
    showStrip();
    delay(SpeedDelay);
  }

  delay(ReturnDelay);

  for(int i = NUM_PIX-EyeSize-2; i > 0; i--) 
  {
    setAll(0,0,0);
    setPixel(i, red/10, green/10, blue/10);
    for(int j = 1; j <= EyeSize; j++) 
    {
      setPixel(i+j, red, green, blue);
    }
    setPixel(i+EyeSize+1, red/10, green/10, blue/10);
    showStrip();
    delay(SpeedDelay);
  }
 
  delay(ReturnDelay);
}
void Strobe(byte red, byte green, byte blue, int StrobeCount, int FlashDelay, int EndPause)
{
  for(int j = 0; j < StrobeCount; j++) 
  {
    setAll(red,green,blue);
    showStrip();
    delay(FlashDelay);
    setAll(0,0,0);
    showStrip();
    delay(FlashDelay);
  }
 delay(EndPause);
}
void Twinkle(byte red, byte green, byte blue, int Count, int SpeedDelay, boolean OnlyOne) 
{
  setAll(0,0,0);
 
  for (int i=0; i<Count; i++) 
  {
     setPixel(random(NUM_PIX),red,green,blue);
     showStrip();
     delay(SpeedDelay);
     if(OnlyOne) 
     {
       setAll(0,0,0);
     }
   }
 
  delay(SpeedDelay);
}
byte * Wheel(byte WheelPos) 
{
  static byte c[3];
  if(WheelPos < 85) 
  {
   c[0]=WheelPos * 3;
   c[1]=255 - WheelPos * 3;
   c[2]=0;
  } else if(WheelPos < 170) 
  {
   WheelPos -= 85;
   c[0]=255 - WheelPos * 3;
   c[1]=0;
   c[2]=WheelPos * 3;
  } 
  else 
  {
   WheelPos -= 170;
   c[0]=0;
   c[1]=WheelPos * 3;
   c[2]=255 - WheelPos * 3;
  }
  return c;
}
void rainbowCycle(int SpeedDelay) 
{
  byte *c;
  uint16_t i, j;

  for(j=0; j<256*5; j++) 
  { // 5 cycles of all colors on wheel
    for(i=0; i< NUM_PIX; i++) 
    {
      c=Wheel(((i * 256 / NUM_PIX) + j) & 255);
      setPixel(i, *c, *(c+1), *(c+2));
    }
    showStrip();
    delay(SpeedDelay);
  }
}
void theaterChase(byte red, byte green, byte blue, int SpeedDelay) 
{
  for (int j=0; j<10; j++) 
  {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) 
    {
      for (int i=0; i < NUM_PIX; i=i+3) 
      {
        setPixel(i+q, red, green, blue);    //turn every third pixel on
      }
      showStrip();
     
      delay(SpeedDelay);
     
      for (int i=0; i < NUM_PIX; i=i+3) 
      {
        setPixel(i+q, 0,0,0);        //turn every third pixel off
      }
    }
  }
}
//-----------------------------------------------------------------------------------------
void TwinkleRandom(int Count, int SpeedDelay, boolean OnlyOne) 
{
  setAll(0,0,0);
 
  for (int i=0; i<Count; i++) 
  {
     setPixel(random(NUM_PIX),random(0,255),random(0,255),random(0,255));
     showStrip();
     delay(SpeedDelay);
     if(OnlyOne) 
     {
       setAll(0,0,0);
     }
   }

  delay(SpeedDelay);
}
void RGBLoop()
{
  for(int j = 0; j < 3; j++ ) 
  {
    // Fade IN
    for(int k = 0; k < 256; k++) 
    {
      switch(j) 
      {
        case 0: setAll(k,0,0); break;
        case 1: setAll(0,k,0); break;
        case 2: setAll(0,0,k); break;
      }
      showStrip();
      delay(3);
    }
    // Fade OUT
    for(int k = 255; k >= 0; k--) 
    {
      switch(j) 
      {
        case 0: setAll(k,0,0); break;
        case 1: setAll(0,k,0); break;
        case 2: setAll(0,0,k); break;
      }
      showStrip();
      delay(3);
    }
  }
}
//-----------------------------------------------------------------------------------------
void setPixelHeatColor (int Pixel, byte temperature) 
{
  // Scale 'heat' down from 0-255 to 0-191
  byte t192 = round((temperature/255.0)*191);
 
  // calculate ramp up from
  byte heatramp = t192 & 0x3F; // 0..63
 // heatramp <<= 2; // scale up to 0..252
 setPixel(Pixel, 0, 0, 0);
  // figure out which third of the spectrum we're in:
  if( t192 > 0x80) 
  {                     // hottest
    setPixel(Pixel, 255, 255, heatramp);
  } else if( t192 > 0x40 ) 
  {             // middle
    setPixel(Pixel, 255, heatramp, 0);
  } 
  else 
  {                               // coolest
    setPixel(Pixel, heatramp, 0, 0);
  }
}

Неактивний

#2 2021-04-24 13:13:32

MikeM
Учасник
З Киев
Зареєстрований: 2017-11-03
Повідомлень: 160

Re: Солнечный фонарь ATTiny85 & WS2812b + USB зарядка

YK пише:

... идея с сыном сделать ...

Каков возраст сына и что именно в этом проекте делал он?

Неактивний

#3 2021-04-24 21:25:07

akapulko
Гість

Re: Солнечный фонарь ATTiny85 & WS2812b + USB зарядка

Если предполагается зарядка от солнца, зачем прорезь под USB?

#4 2021-05-04 08:14:36

YK
Учасник
Зареєстрований: 2020-02-26
Повідомлень: 27

Re: Солнечный фонарь ATTiny85 & WS2812b + USB зарядка

Это стандартный модуль питания. От USB можно заряжать разные гаджеты как Power Bank

Неактивний

Швидке повідомлення

Введіть повідомлення і натисніть Надіслати

Підвал форуму