Большие часы на адресных светодиодах WS2812B

Большие часы на адресных светодиодах WS2812B
Всем привет, хочу рассказать о том как я попробовал повторить проект, но столкнулся с некоторыми проблемами.

Как-то раз на просторах зарубежного интернета я нашел вот такой проект часов на базе светодиодной ленты WS2812B: Big, Auto Dim, Room Clock, и мне захотелось его повторить. Часы устроены как семи сегментный циферблат и работают на RTC модуле DS3231 . Часы умеют показывать температуру, имеют кнопки для настройки и перевода на зимнее время и обратно, позже я добавил датчик влажности DHT11. Температура уже вместе с влажностью считывалась с этого датчика и выводилась раз в минуту. Хоть и в модуле часов есть температурный датчик, но похоже он показывал температуру не корректно из-за самонагрева (ИМХО).




Untitled Sketch_МП.png
И главное нанести много горячих соплей

IMG_20180907_120755.jpg


Когда все было собрано, повозившись с библиотеками, все заработало. И тут возникло одно большое НО: положения яркости в только в двух позициях меня не устраивало. Тем более на границе значений происходило мерцание. Установка яркости на статичном значении меня тоже не очень устраивала ибо днем при установленном тусклом свете ничего не было видно, а ночью даже со значениями близким к минимуму можно было осветить всю Москву. Поинтересовавшись как в других проектах с этим справляется Alex было решено заменить такой код настройки яркости.

C:
void BrightnessCheck(){
  const byte sensorPin = BRI_PIN; // light sensor pin
  const byte brightnessLow = 1; // Low brightness value
  const byte brightnessHigh = 50; // High brightness value
  int sensorValue = analogRead(sensorPin); // Read sensor
  Serial.print("Sensor is: ");Serial.println(sensorValue);
  sensorValue = map(sensorValue, 0, 255, 1, 100);
  LEDS.setBrightness(sensorValue);
  };
вот такими строчками:
C:
void BrightnessCheck() {

  if (auto_bright) {                         // если включена адаптивная яркость
    if (millis() - bright_timer > 100) {     // каждые 100 мс
      bright_timer = millis();               // сброить таймер
      new_bright = map(analogRead(BRI_PIN), 0, bright_constant, min_bright, max_bright);   // считать показания с фоторезистора, перевести диапазон
      new_bright = constrain(new_bright, min_bright, max_bright);
      new_bright_f = new_bright_f * coef + new_bright * (1 - coef);
      LEDS.setBrightness(new_bright_f);      // установить новую яркость
    }
  }
};

Тут стоит упомянуть что программистом я не являюсь и большинство проблем я пытаюсь решить методом тыка и перебора.

И тут все заработало, моей радости не было придела, пока не наступила ночь. Появилась проблема с отключением ленты при полной темноте. Иногда это было только на минуту, а иногда на целую ночь. Еще есть проблема со слабим мерцанием светодиодов если освещения в комнате было мало (например когда работал телик), но это было редко и длилось это одну минуту.

Что касается кода вот сылка на GitHub. Кое-что я перевел для лучшего понимания.

Давно хотел поделиться готовым и работоспособным проектом в кругах аудитории Алекса, но вышло так что собственно нормальной работоспособностью тут не пахнет. Буду благодарени, если у кто сможет помочь.
 
Изменено:

Комментарии

PareNN

✩✩✩✩✩✩✩
25 Дек 2020
4
0
сколько платите? :)
Наверное придется все самому доделавать! Просто esp8266 намного лучше подходит для часов при стоимости как ардуино нано. Просто посмотрел версию 1.7 бета - написано что 2 датчика температуры, посмотрел код - фик там только 1.
 

bort707

★★★★★★✩
21 Сен 2020
2,864
850
посмотрел версию 1.7 бета - написано что 2 датчика температуры, посмотрел код - фик там только 1.
потенциально там заложено два, а дальше кому сколько надо - столько и делает.

Переделка на 8266 - вещь несложная... когда знаешь. Но в любом случае переписывание кода и его тестирование на другом чипе требует приличного времени, превышающего порог простой помощи на форуме.
Поэтому тут или делаете сами, или заказываете
 

PareNN

✩✩✩✩✩✩✩
25 Дек 2020
4
0
Понимаю что переделка не сложная - если занимаешься постоянно. Алгоритм остается такой же, только переделать чтение-запись в память и получение времени по ntp. Время, а время деньги - понимаю. Выкрою время по возможности попробую сам все сделать, спасибо!
 

PareNN

✩✩✩✩✩✩✩
25 Дек 2020
4
0
Спасибо, значит только со временем останется погулять и с подключением к WIFI роутеру.
 

maksland

★★★✩✩✩✩
13 Янв 2019
442
103
Omsk
8266 одобряю , только RTC оставить тоже надо а то не всегда wifi стабильный может и тем более 24/7 быть
для настройки можно wifi использовать если часы высоко будут
и по wifi синхронизацию делать раз в час например
 

bort707

★★★★★★✩
21 Сен 2020
2,864
850
8266 одобряю , только RTC оставить тоже надо а то не всегда wifi стабильный
RTC в первую очередь нужен для того, чтобы часы не сбивались при отключении питания. А так то внутренний таймер вполне пригоден для отсчета времени между синхронизациями и без RTC
 

maksland

★★★✩✩✩✩
13 Янв 2019
442
103
Omsk
те пока wifi не подрубишь, с телефона например, часы будут показывать всё что угодно :)
у нас в деревне часто свет рубят :(
 

stranic123

✩✩✩✩✩✩✩
17 Июл 2019
4
0
Привет всем! Парни кто может подключить датчик DS18B20 в версии часов 1.7 как я понял там надо что-то дописать в коде который на pin 10 и можно-ли сделать что-бы переход радуги был не каждую минуту а каждый час я очень слаб в программировании.
 

ASM

★★★★✩✩✩
26 Окт 2018
1,485
292
@stranic123,он там же подключен) Неужели запятые сложно поставить, как это безобразие прочитать?)

это как раз можно не переделывать - насколько я помню, для 8266 есть библиотека с эмуляцией EEPROM, основной код можно не трогать
подробности кто нашел?)
собирался на НГ праздниках заняться 8266, но не добрался)
вначале начал смотреть, нужно менять библиотеки, с nano не подходят под 8266)
 
Изменено:

suhorukov-p

✩✩✩✩✩✩✩
16 Фев 2019
47
6
Всем привет!!! Наконец то я добрался до своих часиков))) Перепрошил их на 2 датчика температуры. Часы работаю без глюков,с Сентября 2020 года. температуру показывают как нужно. Коллеги по работе уже к ним привыкли и понимают отлично, что первое показание дома, а второе на улице.

 
  • Лойс +1
Реакции: maksland

maksland

★★★✩✩✩✩
13 Янв 2019
442
103
Omsk
эх... раскрашивать бы температуру по цвету "-" синим а "+" красным
 

ASM

★★★★✩✩✩
26 Окт 2018
1,485
292
Поправленный вариант без новогоднего режима, удалил в нем ненужное)
Часы, уличный датчик на DS18B20, внутри BMP280 для температуры и давления.
C++:
#include <DS3232RTC.h>
#include <FastLED.h>
#include <microDS18B20.h>
MicroDS18B20 sensors(2);
#include <iarduino_Pressure_BMP.h>
iarduino_Pressure_BMP sensor(0x76); //Барометр

#define LEDS_IN_SEGMENT 4     // Задаём сколько у нас светодиодов в сегменте
#define DOTS_NUM 2            // Задаём сколько у нас разделительных точек
#define NUM_LEDS (LEDS_IN_SEGMENT * 28 + DOTS_NUM) // Вычисляем кол-во светодиодов
#define NUM_COLORS 16         // Количество цветов
#define COLOR_CHANGE 0        // Смена цвета ( 0 - никогда, 1 - раз в минуту, 2 - каждые десять минут, 3 - каждый час, 4 - каждые десять часов)
#define max_bright 255        // максимальная яркость (0 - 255)
#define min_bright 10         // минимальная яркость (0 - 255)
#define bright_constant 1023  // константа усиления от внешнего света (0 - 1023), чем МЕНЬШЕ константа, тем "резче" будет прибавляться яркость
#define coef 0.4              // коэффициент фильтра (0.0 - 1.0), чем больше - тем медленнее меняется яркость
int new_bright, new_bright_f;
#define auto_bright 1         // автоматическая подстройка яркости от уровня внешнего освещения (1 - включить, 0 - выключить)
unsigned long bright_timer, off_timer;


#define COLOR_ORDER GRB  // Тип ленты
#define LED_PIN 6  // Пин дата от ленты
#define MIN_PIN 5  // Пин минутной кнопки
#define HUR_PIN 4  // Пин часовой кнопки
#define BRI_PIN A0 // PIN фоторезистора

CRGB leds[NUM_LEDS]; // Define LEDs strip

uint8_t  digits[] = {
  0b00111111,     // Digit 0
  0b00100001,     // Digit 1
  0b01110110,     // Digit 2
  0b01110011,     // Digit 3
  0b01101001,     // Digit 4
  0b01011011,     // Digit 5
  0b01011111,     // Digit 6
  0b00110001,     // Digit 7
  0b01111111,     // Digit 8
  0b01111011,     // Digit 9
  0b01111000,     // Digit * градус  10
  0b00011110,     // Digit C         11
  0b01111100,     // Digit P         12
  0b00000000,     // Blank           13
  0b01000000      // Digit -         14
};

bool Dot = true;  //переменная для точек
//bool TempShow = true;  //переменная для температуры
//bool PressShow = true; //переменная для давления
int last_digit = 0;
byte set_light;
byte brightness;
unsigned long Timer;

//управление цветом
//int ledColor = 0x00FFFF; // Color used (in hex)
//long ledColor = CRGB::Blue; // Color used (in hex)
CRGB ledColor = CRGB::Blue; // Color used (in hex)
//массив цветов, для рандом при включенном режиме cylon(); ledColor =  ColorTable[random(16)];
CRGB ColorTable[NUM_COLORS] = {
  CRGB::Amethyst,
  CRGB::Aqua,
  CRGB::Blue,
  CRGB::Chartreuse,
  CRGB::DarkGreen,
  CRGB::DarkMagenta,
  CRGB::DarkOrange,
  CRGB::DeepPink,
  CRGB::Fuchsia,
  CRGB::Gold,
  CRGB::GreenYellow,
  CRGB::LightCoral,
  CRGB::Tomato,
  CRGB::Salmon,
  CRGB::Red,
  CRGB::Orchid
};

void setup() {
  Serial.begin(9600);
  FastLED.addLeds<WS2812B, LED_PIN, RGB>(leds, NUM_LEDS);
  pinMode(MIN_PIN, INPUT_PULLUP); // Define Minutes adjust button pin
  pinMode(HUR_PIN, INPUT_PULLUP); // Define Hours adjust button pin

  pinMode(A3, OUTPUT); //контакт GND барометра
  digitalWrite(A3, LOW);//подача земли на барометр

  pinMode(9, OUTPUT); //контакт VCC датчика осв
  digitalWrite(9, HIGH);//подача питания на датчик осв
}

int GetTime() {
  tmElements_t Now;
  RTC.read(Now);
  int hour = Now.Hour;
  int minute = Now.Minute;
  int second = Now.Second;
  Serial.print(hour);   Serial.print(":");  // Вывод часов
  Serial.print(minute); Serial.print(":");  // Вывод минут
  Serial.print(second); Serial.println(""); // Вывод секунд
  Dot = second % 2;
  return (hour * 100 + minute);
};

void BrightnessCheck() {
  if (auto_bright) {                         // если включена адаптивная яркость
    if (millis() - bright_timer > 100) {     // каждые 100 мс
      bright_timer = millis();               // сбросить таймер
      new_bright = map(analogRead(BRI_PIN), 0, bright_constant, max_bright, min_bright);   // считать показания с фоторезистора, перевести диапазон
      new_bright = constrain(new_bright, min_bright, max_bright);
      new_bright_f = new_bright_f * coef + new_bright * (1 - coef);
      LEDS.setBrightness(new_bright_f);      // установить новую яркость
    }
  }
};

void TimeToArray(){
  int Now = GetTime(); // Получаем время
  Dots(Dot);
  for (int i = 1; i <= 4; i++) {
    int digit = Now % 10; // Получаем последнюю цифру в времени
    int cursor = NUM_LEDS - i * LEDS_IN_SEGMENT * 7;
    if (i > 2) {
      cursor -= DOTS_NUM;
    }
    if ( i == 4 & digit == 0)Digit(digits[13], cursor); //Если впереди ноль, то выключаем его
    else
      Digit(digits[digit], cursor);                     //Иначе показываем символ
    if ( i == COLOR_CHANGE) {                           //Как часто менять цвет
      if (digit != last_digit) {
        ledColor =  ColorTable[random(NUM_COLORS)];    // Случайный цвет из таблицы
      }
      last_digit = digit;
    }
    Now /= 10;
  };
};

void Dots (boolean Dot_On) {
  leds[56] = (Dot_On) ? ledColor : CRGB (0,0,0);
  leds[57] = (Dot_On) ? ledColor : CRGB (0,0,0);
}

void TempStreetToArray() {
  sensors.requestTemp();
  int celsius = sensors.getTemp();
  Serial.println (celsius);
  Dots(0);
 
  Digit(digits[10], (NUM_LEDS - LEDS_IN_SEGMENT * 7));//градус
 
  int digit = abs (celsius % 10);
  Digit(digits[digit], (NUM_LEDS - LEDS_IN_SEGMENT * 14));
 
  digit = abs (celsius / 10);
  if (digit == 0)Digit(digits[13], (NUM_LEDS - LEDS_IN_SEGMENT * 21 - DOTS_NUM)); //Если впереди ноль, то выключаем его
  else
    Digit(digits[digit], (NUM_LEDS - LEDS_IN_SEGMENT * 21 - 2));
    
  if (sensors.getTemp() <= -1)Digit(digits[14], (NUM_LEDS - LEDS_IN_SEGMENT * 28 - DOTS_NUM));//если < или = -1, то показываем -
  else
    Digit(digits[13], (NUM_LEDS - LEDS_IN_SEGMENT * 28 - DOTS_NUM)); //иначе выключаем

};

void TempToArray() {
  sensor.begin();
  int celsius = sensor.temperature - 1; // датчик за часами, часы на стене, там температура чуть выше
  Serial.println (celsius);
  Dots(0);
  Digit(digits[10], (NUM_LEDS - LEDS_IN_SEGMENT * 7));
  int digit = abs (celsius % 10);
  Digit(digits[digit], (NUM_LEDS - LEDS_IN_SEGMENT * 14));
  digit = celsius / 10;
  if (digit == 0)Digit(digits[13], (NUM_LEDS - LEDS_IN_SEGMENT * 21 - DOTS_NUM)); //Если впереди ноль, то выключаем его
  else
    Digit(digits[digit], (NUM_LEDS - LEDS_IN_SEGMENT * 21 - DOTS_NUM));
  Digit(digits[13], (NUM_LEDS - LEDS_IN_SEGMENT * 28 - DOTS_NUM)); //отключаем
};

void PressToArray() {
  sensor.begin();
  int davlenie = sensor.pressure;
  Serial.println (davlenie);
  Dots(0);
  int digit = davlenie % 10;
  Digit(digits[digit], (NUM_LEDS - LEDS_IN_SEGMENT * 7));
  digit = davlenie % 100 / 10;
  Digit(digits[digit], (NUM_LEDS - LEDS_IN_SEGMENT * 14));
  digit = davlenie / 100;
  Digit(digits[digit], (NUM_LEDS - LEDS_IN_SEGMENT * 21 - DOTS_NUM));
  Digit(digits[13], (NUM_LEDS - LEDS_IN_SEGMENT * 28 - DOTS_NUM)); //отключаем
};

void TimeAdjust() {
  int buttonH = digitalRead(HUR_PIN);
  int buttonM = digitalRead(MIN_PIN);
  if (buttonH == LOW || buttonM == LOW) {
    delay(200);
    tmElements_t Now;
    RTC.read(Now);
    int hour = Now.Hour;
    int minutes = Now.Minute;
    int second = Now.Second;
    if (buttonH == LOW) {
      if (Now.Hour == 23) {
        Now.Hour = 0;
      }
      else {
        Now.Hour += 1;
      };
    } else {
      if (Now.Minute == 59) {
        Now.Minute = 0;
      }
      else {
        Now.Minute += 1;
      };
    };
    RTC.write(Now);
  }
}

void Digit (uint8_t digit, uint8_t cursor) {
  for (uint8_t mask = 0b01000000; mask > 0; mask = mask >> 1) {
    for (uint8_t i = 0; i < LEDS_IN_SEGMENT; i++) {
      leds[cursor] = (digit & mask) ? ledColor : CRGB (0,0,0);
      cursor ++;
    }
  }
}

void loop(){
  static byte key =0;
  BrightnessCheck();
  TimeAdjust();

    if ((millis() - Timer <= 5000) && (key ==0))
      TimeToArray();FastLED.show();key++;
    if ((millis() - Timer <= 7000) && (millis() - Timer > 5000)&& (key ==1))
      TempToArray();FastLED.show();key++;
    if ((millis() - Timer <= 9000) && (millis() - Timer > 7000)&& (key ==2))
      TempStreetToArray();FastLED.show();key++;
    if ((millis() - Timer <= 11000) && (millis() - Timer > 9000)&& (key ==3))
      PressToArray();FastLED.show();key++;
   if ((millis() - Timer <= 13000) && (millis() - Timer > 11000)&& (key ==4))
      Timer = millis();key==0;
}
Поднимаю вопрос с millis, при показе часов мигают как-то не в такт.
Раньше было на задержках, все было норм, но только время нельзя было корректировать в любой момент, пока не пройдут все задержки...
Где может быть проблема?)
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,159
1,267
Москва
А как не так ?
Наверное ты очень часто их отображаешь. Надо ограничить отображение раз в пол секунды.
И еще. зачем проверять яркость 10 раз в секунду ? Так резко темнеет ?

А вообще молодец, добился чего хотел!
 

kym13

★★✩✩✩✩✩
14 Ноя 2020
424
64
Здравствуйте. Собрал часы на скетче из 89 поста на BME280 и модуле фоторезистора с алиэкспресс, всё работает, но как всегда есть "но". Автоматическая регулировка яркости работает наоборот т.е. когда становиться темно яркость прибавляется, когда светло уменьшается. Поскольку я в программировании ноль с минусом, то тупо вырезал из скетча найденого в нете блок который отвечает за регулировку и вставил в скетч из поста 89, а родной закоментировал и стало всё работать как надо. Вместо корпуса использовал рамку для фотографий формата А4.1-1.jpg2-2.jpg3-3.jpg4-4.jpg

Замерил потребляемый ток, от 700 до 980 мА в зависимости от цвета при максимальной яркости. Запитал от 2А зарядки для смартфона.
Мой скетч:
 

Вложения

ASM

★★★★✩✩✩
26 Окт 2018
1,485
292
Автоматическая регулировка яркости работает наоборот
min и max меняешь местами, посмотрел код, закомментировано немного не то)
в конце видео отчетливо заметны точки, которые передержались)
зачем проверять яркость 10 раз в секунду ?
может тогда запихнуть вызов в функцию с датчиками или часами?
 
Изменено:

Старик Похабыч

★★★★★★★
14 Авг 2019
4,159
1,267
Москва
Смотрел раз 5, замедлил до 0.25... ничего отчетливого передержанного не заметил , увы.

Не надо пихать функцию яркости в функцию с датчиками и часами, это логически не верно. Сделай внутри проверку на то, что бы прошло какое то время с последней проверки.
C++:
void test_yarkosti()
{
    static uint32_t last_t=millis();
    if (millis()-last_t)<5000) return;
    last_t=millis();
    /*
    тут остальной код работы с яркостью.
    */
}
 

bort707

★★★★★★✩
21 Сен 2020
2,864
850
а если сделать версию, мигание каждую секунду, что нужно сделать?
точки включили - засекли миллис.
как прошло 1000 ис - выключили. снова засекли...
Опять прошло 1000мс - включили обратно...
и так по кругу.

прости, что не заглядываю в код - я на правки, что туда новички вносят - уже смотреть не могу :)
Только исправят один косяк - обязательно притащат другой :)
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,159
1,267
Москва
Да, понял. глянул код.
Значит так. Точки у тебя рисуются функцией Dots, в зависимости от переменной Dot.
Переменная Dot у тебя вычисляется в GetTime() , принимается как остаток от деления секунд на 2, т.е. 0 или 1, вроде бы все нормально.
GetTime вызывается из TimeToArray(), которая в промежуток между 0-5 секундой вызывается опять же с бешеной частотой.

Что посмотреть. Для начала в функции Dots выводи значение Dot_On в монитор порта. Посмотри отметки времени, меняется ли оно (значение Dot_On ) как нужно, т.е. каждую секунду на противоположное. Если меняется, то скорее всего ошибка с очень частой перерисовкой матрицы. убирай ее.

И еще , ты тут ничего не напутал?
C++:
if ((millis() - Timer <= 5000) && (key ==0))
      TimeToArray();FastLED.show();key++;
    if ((millis() - Timer <= 7000) && (millis() - Timer > 5000)&& (key ==1))
      TempToArray();FastLED.show();key++;
    if ((millis() - Timer <= 9000) && (millis() - Timer > 7000)&& (key ==2))
      TempStreetToArray();FastLED.show();key++;
    if ((millis() - Timer <= 11000) && (millis() - Timer > 9000)&& (key ==3))
      PressToArray();FastLED.show();key++;
   if ((millis() - Timer <= 13000) && (millis() - Timer > 11000)&& (key ==4))
      Timer = millis();key==0;
Получается что key всегда увеличивается и FastLED.show(); происходит 4 раза за цикл ?
 

bort707

★★★★★★✩
21 Сен 2020
2,864
850
Получается что key всегда увеличивается и FastLED.show(); происходит 4 раза за цикл ?
так и надо. первый раз показывается время, второй температура комнатная, третий - температура уличная... Этот кусок кода АСМ помогал писать я и там все (тогда) было верно :)