Часы на адресной светодиодной ленте WS2812B (Arduino)(2)

Ранее в статье http://rcl-radio.ru/?p=110997 рассматривался пример создания часов на адресной ленте WS2812b с использованием часов реального времени DS3231. В этой статье будет рассмотрен пример аналогичных часов но с использованием GPS модуля NEO-6M — http://rcl-radio.ru/?p=129191.

GPS модуль необходим для получения точного времени со спутников GPS. Для поиска спутников модулю NEO-6M может понадобится время от 5 до 30 минут после подачи питания на модуль GPS.

Назначение контактов модуля NEO-6M:

  • GND —  заземляющий вывод питания
  • TxD — вывод для передачи данных
  • RxD — вывод для получения данных
  • VCC — вывод питания 3,3 В

В часах предусмотрена регулировка яркости адресной ленты и изменения цвета свечения.

Схема часов

Кнопки:

  • MODE — позволяет менять цвет свечения адресной ленты
  • UP —  кнопка позволяет увеличивать яркость свечения адресной ленты
  • DOWN — кнопка позволяет уменьшать яркость свечения адресной ленты

Сборка часов

Материал на который наклеена адресная лента для создания часов может быть различный, адресную ленту необходимо разрезать на 28 отрезков по три пикселя для сегментов индикаторов , и 2 отрезка по два пикселя для разделительных точек.

Порядок наклеивания отрезков адресной ленты на основание показан на рисунках:

При установке большой яркости свечения адресной ленты, необходимо использовать отдельный от Arduino источник питания 5 В, так ток потребления адресной ленты может превысить 2 А. 

Как ранее было написано, что GPS модулю после подачи на него питания необходимо некоторое время чтобы осуществить поиск спутников. Пока спутники не найдены, часы работать не будут, а в мониторе порта должна появится следующая информация:

Это значит что GPS модуль активен и передает информация на плату Arduino, но спутники еще не найдены.

Как только модуль GPS найдет спутники то информация в мониторе порта поменяется и часы начнут показывать время:

При настройке часов необходимо внести изменения в скетч:

#define time_offset 21600 // смещение от UTC 1 час = 3600 (зависит от часового пояса, для примера смещение времени для Омска 6 часов, то есть 21600 секунд )

#define BRIG_MAX 50 // настойка ограничения максимальной яркости. Напряжение 5 В подаваемое на адресную ленту может подаваться от платы Arduino, от маломощного источника питания,  но при этом яркость ленты не должна превышать 10 единиц.

#define RX      2
#define TX      3
#define L_BIT    PORTB &= ~(1<<PB0)
#define H_BIT    PORTB |=  (1<<PB0)    
#define LED_MAX 88
#define BRIG_MAX 50
#define time_offset   21600  // смещение от UTC 1 час = 3600
 
#include <EEPROM.h>
#include <TinyGPS++.h>      // https://github.com/mikalhart/TinyGPSPlus/archive/refs/heads/master.zip
#include <TimeLib.h>        // https://github.com/PaulStoffregen/Time/archive/master.zip
#ifndef HAVE_HWSERIAL1
#include <SoftwareSerial.h>                         
  SoftwareSerial Serial1(RX, TX);
#endif
  TinyGPSPlus gps; 
 
  byte rr[LED_MAX],gg[LED_MAX],bb[LED_MAX],r,g,b;
  int ii,up,down,brig,mm,hh;
  byte r_led=0,g_led=1,b_led=0;
  byte mode;
  bool w,w1,w2;
  unsigned long tic,times,times0,times1;
  byte last_second, Second, Minute, Hour, Day, Month;
  int Year;
  bool d;
 
void setup(){
  Serial.begin(9600);                   
  Serial1.begin(9600);  
  DDRB |= (1<<PB0); // INPUT WS2812B (D8 - ARDUINO)
  pinMode(6,INPUT_PULLUP);// MODE
  pinMode(4,INPUT_PULLUP);// INPUT BUTTON UP
  pinMode(5,INPUT_PULLUP);// INPUT BUTTON DOWN
  delay(1000);
  if(EEPROM.read(100)!=0){for(int i1=0;i1<101;i1++){EEPROM.write(i1,1);}}// очистка памяти при первом включении 
  mode = EEPROM.read(0);brig = EEPROM.read(1);
  if(brig>BRIG_MAX){brig=1;}
}
 
void loop(){
/// GPS
  while(Serial1.available()>0)
  if(gps.encode(Serial1.read())){
    times1=millis();
    Serial.print("Time: ");
  if(gps.time.isValid()){
    Minute = gps.time.minute();
    Second = gps.time.second();
    Hour   = gps.time.hour();
    if (Hour<10) Serial.print(F("0"));Serial.print(Hour);Serial.print(":");
    if (Minute<10) Serial.print(F("0"));Serial.print(Minute);Serial.print(":");
    if (Second<10) Serial.print(F("0"));Serial.print(Second);Serial.println();
     }else{Serial.println("Not Available");}
 
    Serial.print("Date: ");
  if(gps.date.isValid()){
    Day   = gps.date.day();
    Month = gps.date.month();
    Year  = gps.date.year();
    Serial.print(Month);Serial.print("/");
    Serial.print(Day);Serial.print("/");
    Serial.println(Year);       
     }else{Serial.println("Not Available");}
  }
 
 
   if(last_second != gps.time.second()){
     d=1;
     rgb_write();
     cli();  led_rgb();sei();res(); 
     delay(300);
     d=0;
     rgb_write();
     cli();  led_rgb();sei();res(); 
     last_second = gps.time.second();
     setTime(Hour, Minute, Second, Day, Month, Year);
     adjustTime(time_offset);
     hh = hour();
     mm = minute();
     Serial.print(hour()/10);Serial.print(hour()%10);Serial.print(":");
     Serial.print(minute()/10);Serial.print(minute()%10);Serial.print(":");
     Serial.print(second()/10);Serial.println(second()%10);
     }
 
  if (millis() > 5000 && gps.charsProcessed() < 10){
    Serial.println(F("No GPS detected: check wiring."));
    while(true);}
 
/////// BUTTON ///////////////////////////////////////////////
  if(digitalRead(6)==LOW){mode++;if(mode>7){mode=0;};delay(300);w=1;w2=1;times0=millis();}
  switch(mode){
    case 0: r_led=1,g_led=1,b_led=1; break;
    case 1: r_led=1,g_led=0,b_led=0; break;
    case 2: r_led=0,g_led=1,b_led=0; break;
    case 3: r_led=0,g_led=0,b_led=1; break;
    case 4: r_led=1,g_led=1,b_led=0; break;
    case 5: r_led=1,g_led=0,b_led=1; break;
    case 6: r_led=0,g_led=1,b_led=1; break;
    case 7: r_led=0,g_led=0,b_led=0; break;
    }
   if(digitalRead(4)==LOW){brig++;if(brig>BRIG_MAX){brig=BRIG_MAX;}delay(300); w=1;w2=1;times0=millis();} 
   if(digitalRead(5)==LOW){brig--;if(brig<1){brig=1;}delay(300);w=1;w2=1;times0=millis();}   
 
if(w2==1){w2=0;rgb_write();cli();led_rgb();sei();res();}
 
 
 
////// EEPROM ///////////////////
 if(millis()-times0>10000&&w==1){EEPROM.write(0,mode);EEPROM.write(1,brig);w=0;}  
 
 
} // end loop
 
void rgb_write(){
  /////// TIME ////////////////////////////////////////////  
   ii = hh*100+mm;
////////// RGB ////////////////////////////////////////////////
  switch(ii/1000%10){   
    case 0: ws(0,1);ws(3,1);ws(6,1);ws(9,0);ws(12,1);ws(15,1);ws(18,1);break;
    case 1: ws(0,0);ws(3,0);ws(6,1);ws(9,0);ws(12,0);ws(15,0);ws(18,1);break;
    case 2: ws(0,0);ws(3,1);ws(6,1);ws(9,1);ws(12,1);ws(15,1);ws(18,0);break;
    case 3: ws(0,0);ws(3,1);ws(6,1);ws(9,1);ws(12,0);ws(15,1);ws(18,1);break;
    case 4: ws(0,1);ws(3,0);ws(6,1);ws(9,1);ws(12,0);ws(15,0);ws(18,1);break;
    case 5: ws(0,1);ws(3,1);ws(6,0);ws(9,1);ws(12,0);ws(15,1);ws(18,1);break;
    case 6: ws(0,1);ws(3,1);ws(6,0);ws(9,1);ws(12,1);ws(15,1);ws(18,1);break;
    case 7: ws(0,0);ws(3,1);ws(6,1);ws(9,0);ws(12,0);ws(15,0);ws(18,1);break;
    case 8: ws(0,1);ws(3,1);ws(6,1);ws(9,1);ws(12,1);ws(15,1);ws(18,1);break;
    case 9: ws(0,1);ws(3,1);ws(6,1);ws(9,1);ws(12,0);ws(15,1);ws(18,1);break;
    } 
  switch(ii/100%10){   
    case 0: ws(21,1);ws(24,1);ws(27,1);ws(30,0);ws(33,1);ws(36,1);ws(39,1);break;
    case 1: ws(21,0);ws(24,0);ws(27,1);ws(30,0);ws(33,0);ws(36,0);ws(39,1);break;
    case 2: ws(21,0);ws(24,1);ws(27,1);ws(30,1);ws(33,1);ws(36,1);ws(39,0);break;
    case 3: ws(21,0);ws(24,1);ws(27,1);ws(30,1);ws(33,0);ws(36,1);ws(39,1);break;
    case 4: ws(21,1);ws(24,0);ws(27,1);ws(30,1);ws(33,0);ws(36,0);ws(39,1);break;
    case 5: ws(21,1);ws(24,1);ws(27,0);ws(30,1);ws(33,0);ws(36,1);ws(39,1);break;
    case 6: ws(21,1);ws(24,1);ws(27,0);ws(30,1);ws(33,1);ws(36,1);ws(39,1);break;
    case 7: ws(21,0);ws(24,1);ws(27,1);ws(30,0);ws(33,0);ws(36,0);ws(39,1);break;
    case 8: ws(21,1);ws(24,1);ws(27,1);ws(30,1);ws(33,1);ws(36,1);ws(39,1);break;
    case 9: ws(21,1);ws(24,1);ws(27,1);ws(30,1);ws(33,0);ws(36,1);ws(39,1);break;
    } 
 
 
    if(d==1){data_led(42, r_led*brig, g_led*brig, b_led*brig);data_led(43, r_led*brig, g_led*brig, b_led*brig);
    data_led(44, r_led*brig, g_led*brig, b_led*brig);data_led(45, r_led*brig, g_led*brig, b_led*brig);}
    else{data_led(42, 0, 0, 0);data_led(43, 0, 0, 0);data_led(44, 0, 0, 0);data_led(45, 0, 0, 0);}
 
 
 
  switch(ii/10%10){   
    case 0: ws(46,1);ws(49,1);ws(52,1);ws(55,0);ws(58,1);ws(61,1);ws(64,1);break;
    case 1: ws(46,0);ws(49,0);ws(52,1);ws(55,0);ws(58,0);ws(61,0);ws(64,1);break;
    case 2: ws(46,0);ws(49,1);ws(52,1);ws(55,1);ws(58,1);ws(61,1);ws(64,0);break;
    case 3: ws(46,0);ws(49,1);ws(52,1);ws(55,1);ws(58,0);ws(61,1);ws(64,1);break;
    case 4: ws(46,1);ws(49,0);ws(52,1);ws(55,1);ws(58,0);ws(61,0);ws(64,1);break;
    case 5: ws(46,1);ws(49,1);ws(52,0);ws(55,1);ws(58,0);ws(61,1);ws(64,1);break;
    case 6: ws(46,1);ws(49,1);ws(52,0);ws(55,1);ws(58,1);ws(61,1);ws(64,1);break;
    case 7: ws(46,0);ws(49,1);ws(52,1);ws(55,0);ws(58,0);ws(61,0);ws(64,1);break;
    case 8: ws(46,1);ws(49,1);ws(52,1);ws(55,1);ws(58,1);ws(61,1);ws(64,1);break;
    case 9: ws(46,1);ws(49,1);ws(52,1);ws(55,1);ws(58,0);ws(61,1);ws(64,1);break;
    }  
  switch(ii%10){   
    case 0: ws(67,1);ws(70,1);ws(73,1);ws(76,0);ws(79,1);ws(82,1);ws(85,1);break;
    case 1: ws(67,0);ws(70,0);ws(73,1);ws(76,0);ws(79,0);ws(82,0);ws(85,1);break;
    case 2: ws(67,0);ws(70,1);ws(73,1);ws(76,1);ws(79,1);ws(82,1);ws(85,0);break;
    case 3: ws(67,0);ws(70,1);ws(73,1);ws(76,1);ws(79,0);ws(82,1);ws(85,1);break;
    case 4: ws(67,1);ws(70,0);ws(73,1);ws(76,1);ws(79,0);ws(82,0);ws(85,1);break;
    case 5: ws(67,1);ws(70,1);ws(73,0);ws(76,1);ws(79,0);ws(82,1);ws(85,1);break;
    case 6: ws(67,1);ws(70,1);ws(73,0);ws(76,1);ws(79,1);ws(82,1);ws(85,1);break;
    case 7: ws(67,0);ws(70,1);ws(73,1);ws(76,0);ws(79,0);ws(82,0);ws(85,1);break;
    case 8: ws(67,1);ws(70,1);ws(73,1);ws(76,1);ws(79,1);ws(82,1);ws(85,1);break;
    case 9: ws(67,1);ws(70,1);ws(73,1);ws(76,1);ws(79,0);ws(82,1);ws(85,1);break;
    }  
  }
 
void ws(int ind, bool datt){
  if(datt==1){data_led(ind, r_led*brig, g_led*brig, b_led*brig);data_led(ind+1, r_led*brig, g_led*brig, b_led*brig);data_led(ind+2, r_led*brig, g_led*brig, b_led*brig);}
  if(datt==0){data_led(ind, 0, 0, 0);data_led(ind+1, 0, 0, 0);data_led(ind+2, 0, 0, 0);}}
 
void bit_0(){H_BIT;asm("nop");asm("nop");   L_BIT;asm("nop");asm("nop");asm("nop");   asm("nop");} 
void bit_1(){H_BIT;asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");   L_BIT;}
void bit_w(bool x){if(x) bit_1(); else bit_0();}
void res(){delayMicroseconds(100);}
void data_led(int num, byte rrr, byte ggg, byte bbb){bb[num]=bbb;rr[num]=rrr;gg[num]=ggg;}
void led_rgb(){
  for(int num_i=0;num_i<LED_MAX;num_i++){ 
bit_w((bb[num_i] & 0x80)>>7);
bit_w((bb[num_i] & 0x40)>>6);
bit_w((bb[num_i] & 0x20)>>5);
bit_w((bb[num_i] & 0x10)>>4);
bit_w((bb[num_i] & 0x08)>>3);
bit_w((bb[num_i] & 0x04)>>2);
bit_w((bb[num_i] & 0x02)>>1);
bit_w((bb[num_i] & 0x01)>>0);
 
bit_w((rr[num_i] & 0x80)>>7);
bit_w((rr[num_i] & 0x40)>>6);
bit_w((rr[num_i] & 0x20)>>5);
bit_w((rr[num_i] & 0x10)>>4);
bit_w((rr[num_i] & 0x08)>>3);
bit_w((rr[num_i] & 0x04)>>2);
bit_w((rr[num_i] & 0x02)>>1);
bit_w((rr[num_i] & 0x01)>>0);
 
bit_w((gg[num_i] & 0x80)>>7);
bit_w((gg[num_i] & 0x40)>>6);
bit_w((gg[num_i] & 0x20)>>5);
bit_w((gg[num_i] & 0x10)>>4);
bit_w((gg[num_i] & 0x08)>>3);
bit_w((gg[num_i] & 0x04)>>2);
bit_w((gg[num_i] & 0x02)>>1);
bit_w((gg[num_i] & 0x01)>>0);
}}

 

Comments

  1. если в сегменте не 3 а например 4 светодиода, то кол. светодиодов будет 116.
    То тогда:
    #define LED_MAX 88, заменить на #define LED_MAX 116,
    и еще какие в скетче, изменения делать?

    1. if(datt==1){data_led(ind, r_led*brig, g_led*brig, b_led*brig);data_led(ind+1, r_led*brig, g_led*brig, b_led*brig);data_led(ind+2, r_led*brig, g_led*brig, b_led*brig);}
      if(datt==0){data_led(ind, 0, 0, 0);data_led(ind+1, 0, 0, 0);data_led(ind+2, 0, 0, 0);}}

      надо добавить 4 элемент

      ind ind+1 ind+2 in+3

      далее во всех строках

      case 0: ws(0,1);ws(3,1);ws(6,1);ws(9,0);ws(12,1);ws(15,1);ws(18,1);break;

      изменить на

      case 0: ws(0,1);ws(4,1);ws(8,1);ws(12,0);ws(16,1);ws(24,1);ws(28,1);break;

Добавить комментарий

Войти с помощью: