Доброго времени суток! Уважаемые форумчане, прошу Вашей помощи. Я решил повторить замечательный проект часов с 7-мисегментными цифрами на адресной ленте WS2812b, размещенный на Хабре. Если кому интересно, даю ссылку Крутые часы на адресных диодах / Хабр.
Часы построены на Ардуино нано, имеют модуль часов реального времени, датчик освещенности для корректировки яркости свечения светодиодов и модуль bluetooth для корректировки времени через приложение. В качестве датчика температуры используется DHT22. Функции часов самые простые: постоянное отображение текущего времени и с 29 по 31 секунду – индикация температуры.
Данный проект я реализую для гаража и, в связи с этим, на мой взгляд, часы имеют существенные недостатки. Первое, это то, что не выводится значение отрицательной температуры воздуха и второе - отсутствие индикации значений влажности воздуха. Минимальная индицируемая температура – 0 градусов. При минусовой температуре светодиоды не загораются.
Я пытался связаться с автором проекта по поводу доработки кода, но, к сожалению, пока ничего не получается. Сам в программировании «ноль», единственное на что хватило моих знаний, это изменение кода в скетче числа используемых светодиодов (у меня иное количество, в отличие от проекта) и, соответственно правильная индикация выводимой информации. Также изменил отображение градусов Цельсия, тем самым освободив последнюю цифру на табло для отображения знака минус (для отрицательной температуры). Дальше никак.
Поэтому прошу Вашей помощи!
Мои задачи:
-
Отображение значений отрицательной температуры и индикация знака «минус» (светодиоды с 108 по 112).
-
Отображение значений влажности воздуха сразу после индикации температуры, с 32 по 34 секунду. Индикация на последних двух цифрах табло знака % без косой черты.
Заранее всем спасибо!
// ТЕМПЕРАТУРА - ПИН 7
// ЛЕНТА - ПИН 13
// -------- БИБЛИОТЕКИ ---------
#include <BH1750.h> // библиотека датчика освещённости
// библиотека Wire.h для I2C подключается вместе с датчиком освещённости
#include <iarduino_RTC.h> // библиотека часов
#include "DHT.h" // подключаем библиотеку для датчика температуры
#include <EEPROM.h> // библиотека энергонезависимой памяти
// -------- КОНСТАНТЫ ---------
#define serialTimeout 5000 //5 секунд на ввод в данных в serial порт при запросе
#define ArduinoLED 13 //горящий диод ардуины, потом выключим
#define DHT_Port 7 //порт датчика температуры
#define BH1750address = 0x23; //адрес датчика освещения
// Лента
#define COLOR_DEBTH 3 // Разрешение ленты
#define NUM_LEDS 142 // число светодиодов в кольце/ленте
#define LED_PIN 13 // пин, куда подключен DIN ленты
#define thisdelay 20 //-FX LOOPS DELAY VAR
#define thisstep 10 //-FX LOOPS DELAY VAR
#define thishue 0 //-FX LOOPS DELAY VAR
#define thissat 255 //-FX LOOPS DELAY VAR
#define max_bright 30 // максимальная яркость (0 - 255) на старте
//----------------------//
#include "microLED.h" // Библиотеку подключать после COLOR_DEBTH и ORDER_GRB
// -------- ОБЪЕКТЫ И КЛАССЫ ОБОРУДОВАНИЯ ---------
iarduino_RTC time(RTC_DS3231); // подключаем RTC модуль на базе чипа DS3231, используется аппаратная шина I2C
DHT dht(DHT_Port, DHT22); // сообщаем на каком порту будет датчик и версию датчика температуры
BH1750 lightMeter; // объект датчик освещённости
// -------- ПЕРЕМЕННЫЕ ---------
// Обратная связь через сериал порт (при подключении bluetooth на RX и TX автоматически порт работает там)
String inputString = ""; // строка, в которую будут записываться входящие данные
boolean stringComplete = false; // заполнилась ли строка или нет
// Часы
bool Dot; //объявление переменной состояния точки (разделение часы : минуты)
long ledColor = BLUE; // объявление переменной цвета диодной ленты
byte z = 1; //Индекс? диода для конвертации
word Now;
///-- переменные времени для управления цветом
byte startWorkHours = EEPROM.read(0);
byte startWorkMinutes = EEPROM.read(1);
byte endWorkHours = EEPROM.read(2);
byte endWorkMinutes = EEPROM.read(3);
int brightnessCorrection = EEPROM.read(7); // коэффициент коррекции яркости, в кабинете равен +5
// именно integer, чтобы могло быть отрицательным (поправка на меньшую яркость и больше чем 127)
// --------------- ОБЪЯВЛЕНИЕ ЛЕНТЫ ---------------- //
LEDdata leds[NUM_LEDS];
microLED strip(leds, NUM_LEDS, LED_PIN);
/////////////////////////////////////////////////////////////////////////////////////////////////////////
byte digits[10][7] = {{0, 1, 1, 1, 1, 1, 1}, // Digit 0
{0, 1, 0, 0, 0, 0, 1}, // Digit 1
{1, 1, 1, 0, 1, 1, 0}, // Digit 2
{1, 1, 1, 0, 0, 1, 1}, // Digit 3
{1, 1, 0, 1, 0, 0, 1}, // Digit 4
{1, 0, 1, 1, 0, 1, 1}, // Digit 5
{1, 0, 1, 1, 1, 1, 1}, // Digit 6
{0, 1, 1, 0, 0, 0, 1}, // Digit 7
{1, 1, 1, 1, 1, 1, 1}, // Digit 8
{1, 1, 1, 1, 0, 1, 1}
}; // Digit 9 | 2D Array for numbers on 7 segment
void setup()
{
Serial.begin(9600);
pinMode(ArduinoLED, OUTPUT); //диод на плате ардуины
digitalWrite(ArduinoLED, LOW); //выключаем диорд на плате
delay(1000); // Ждем готовности всех датчиков.
lightMeter.begin();
dht.begin(); // запускаем датчик температуры
strip.setBrightness(max_bright); // ограничить максимальную яркость
inputString.reserve(20); // «бронируем» 20 байт для inputString
BrightnessCheck();
startLed(); // красиво запустить ленту
LedColor();
}
void loop()
{
serialParse();
if (GetSecond() >= 29 && GetSecond() <= 31) {
displayTemp(); //вызов функции отображения температуры
}
if (GetSecond() % 10 == 0) { //регулировка яркости
BrightnessCheck();
}
if (GetSecond() == 0 || GetSecond() == 2 || GetSecond() == 7) { //регулировка цвета
LedColor();
}
else{
displayClock();
}
}
///////// Основные функции
void displayClock() // функция вывода времени
{
int Now = GetTime();
int z = 1;
convert(Now, z);// Get leds array with required configuration
strip.show();
}
// Определение цвета ленты
void LedColor() {
time.gettime();
if (time.Hours == 13 && time.weekday != 0 && time.weekday != 6) { //Обед
ledColor = GREEN;
}
else if (time.Hours == 12 && time.minutes >= 40 ) { //Почти обед
ledColor = LIME;
}
else if (time.Hours > endWorkHours || (time.Hours == endWorkHours && time.minutes >= endWorkMinutes) || time.weekday == 6 || time.weekday == 0) {
ledColor = RED; //Если выходной или нерабочее время
}
else if (time.weekday != 5 && (time.Hours == (endWorkHours - 1) && time.minutes >= endWorkMinutes) || time.Hours == endWorkHours && time.minutes < endWorkMinutes) {
ledColor = OLIVE; //Скоро домой (не пятница)
}
else if (time.Hours < startWorkHours || (time.Hours == startWorkHours && time.minutes < startWorkMinutes)) {
ledColor = MAROON;
}
else if (time.weekday == 5) {
if ((time.Hours == (endWorkHours - 2) && time.minutes >= endWorkMinutes) || (time.Hours == (endWorkHours - 1) && time.minutes < endWorkMinutes)) {
ledColor = OLIVE;
}
else if (time.Hours == (endWorkHours - 1) && time.minutes >= endWorkMinutes) {
ledColor = MAROON; //В пятницу они будут красными не сразу
}
else {
ledColor = BLUE;
}
}
else {
ledColor = BLUE;
}
}
// Настройка яркости по датчику освещённости
void BrightnessCheck() {
word lightValue = lightMeter.readLightLevel();
if (lightValue <= 1) {
strip.setBrightness(2);
}
else if (lightValue > 1 && lightValue <= 200 ) {
strip.setBrightness(lightValue + brightnessCorrection);
}
else if (lightValue > 200) {
strip.setBrightness(210);
}
else if (lightValue == 54612) {
strip.setBrightness(max_bright); // Если датчик отвалился, делаем яркость вручную
}
else {
strip.setBrightness(max_bright);
}
}
// ---------------------------------------------------------------------------------------------- //
// Работа с часами и временем
//функция запроса текущей секунды, результат в переменной "second"
byte GetSecond() {
time.gettime();
byte second = time.seconds;
return (second);
};
word GetTime() { //функция запроса текущего времени, результат в виде целого числа, то есть 10:25 будет 1025
byte hour = time.Hours;
byte minutes = time.minutes;
byte second = time.seconds;
if (second % 2 == 0) {
Dot = false;
}
else {
Dot = true;
};
return (hour * 100 + minutes);
}
void displayTemp() // функция вывода температуры
{
float temp = dht.readTemperature();
int Temp = temp + 0.5;
strip.fill(BLACK);
Dot = false;
byte z = 3;
convert(Temp, z);// Get leds array with required configuration
for (byte i = 71; i < 72; i++)
{
strip.setColor(i, ledColor);
}
for (int i = 72; i < 77; i++)
{
strip.setColor(i, ledColor);
}
for (int i = 82; i < 92; i++)
{
strip.setColor(i, ledColor);
}
for (int i = 92; i <= 141; i++) {
{
strip.setColor(i, BLACK);
}
}
strip.show();// Display leds array
delay(3000);
}
// Понтовое включение часов - пробежка радугой
void startLed() {
strip.fill(BLACK);
strip.show();
byte thisled;
byte ihue;
while (thisled < NUM_LEDS) {
ihue = ihue + thisstep;
strip.setLED(thisled, mHSV(ihue, thissat, 255));
strip.show();
thisled++;
delay(thisdelay);
}
delay(200);
thisled = 0;
while (thisled < NUM_LEDS) {
strip.setLED(thisled, mRGB(0, 0, 0));
strip.show();
thisled++;
delay(thisdelay);
}
};
//////// конвертация цифр
LEDdata convert (int DataIn, byte z) { //функция преобразования любого числа в массив для отображения на ленте, результат - массив leds
int cursor;
if (Dot) {
leds[70] = leds[71] = mCOLOR(ledColor);
}
else {
leds[70] = leds[71] = 0x000000;
};
for (byte i = z; i <= 4; i++) {
int digit = DataIn % 10; // get last digit in time
// Serial.println(digit);
if (i == 1) {
cursor = 107;
for (byte k = 0; k < 7; k++) {
if (digits[digit][k] == 1) {
for (int i = 0; i < 5; i++) {
// leds[cursor] = mCOLOR(ledColor);
strip.setLED(cursor, mCOLOR(ledColor));
cursor ++;
}
}
else {
for (byte i = 0; i < 5; i++) {
//leds[cursor] = 0x000000;
strip.setLED(cursor, mCOLOR(BLACK));
cursor ++;
}
}
}
}
else if (i == 2) {
cursor = 72;
for (byte k = 0; k < 7; k++) {
if (digits[digit][k] == 1) {
for (byte i = 0; i < 5; i++) {
//leds[cursor] = mCOLOR(ledColor);
strip.setLED(cursor, mCOLOR(ledColor));
cursor ++;
}
}
else {
for (byte i = 0; i < 5; i++) {
// leds[cursor] = 0x000000;
strip.setLED(cursor, mCOLOR(BLACK));
cursor ++;
}
}
}
}
else if (i == 3) {
cursor = 35;
for (int k = 0; k < 7; k++) {
if (digits[digit][k] == 1) {
for (int i = 0; i < 5; i++) {
//leds[cursor] = mCOLOR(ledColor);
strip.setLED(cursor, mCOLOR(ledColor));
cursor ++;
}
}
else {
for (byte i = 0; i < 5; i++) {
leds[cursor] = 0x000000;
strip.setLED(cursor, mCOLOR(BLACK));
cursor ++;
}
}
}
}
else if (i == 4) {
cursor = 0;
for (byte k = 0; k < 7; k++) {
if (digits[digit][k] == 1) {
for (byte i = 0; i < 5; i++) {
//leds[cursor] = mCOLOR(ledColor);
strip.setLED(cursor, mCOLOR(ledColor));
cursor ++;
}
}
else {
for (byte i = 0; i < 5; i++) {
//leds[cursor] = 0x000000;
strip.setLED(cursor, mCOLOR(BLACK));
cursor ++;
}
}
}
}
DataIn /= 10;
//Serial.println(digit);
};
};
//////////////////////////////////////
/*
Функция SerialEvent используется всякий раз при получении
новых данных через последовательный порт. Она запускается
при каждом запуске нового цикла, поэтому использование задержки
может затормозить ответ. Позволяет передавать сразу несколько байтов.
*/
//////////////////// Работа с принятием данных в порт
void serialParse() {
// если прибыл символ новой строки, показываем строку:
if (stringComplete) {
if (inputString.equalsIgnoreCase("SetTime\r")) { // Если прислали команду на установку времени
Serial.println(F("Корректировка времени:"));
Serial.println(F("Введите часы:"));
Serial.setTimeout(serialTimeout);
byte newHours = Serial.parseInt(); //новые часы получаем из сериал порт
Serial.println(F("Введите минуты:"));
byte newMinutes = Serial.parseInt(); //новые минуты получаем из сериал порт
time.settime(0, newMinutes, newHours); //пишем в модуль новые минуты и часы, секунды обнуляем, остальное без изменений
Serial.print(F("Время установлено: "));
time.gettime();
Serial.print(time.Hours);
Serial.print(F(":"));
Serial.println(time.minutes);
LedColor();
}
else if (inputString.equalsIgnoreCase("SetDate\r")) {
Serial.println(F("Корректировка даты:"));
Serial.println(F("Введите число:"));
Serial.setTimeout(serialTimeout);
byte newDay = Serial.parseInt(); //получаем из сериал порт новое значение даты
Serial.println(F("Введите месяц:"));
byte newMonth = Serial.parseInt(); //получаем из сериал порт новое значение месяца
Serial.println(F("Введите год (0-99):"));
byte newYear = Serial.parseInt(); //получаем из сериал порт новое значение года
Serial.println(F("Введите день недели (0-6):"));
byte newWeekDay = Serial.parseInt(); //получаем из сериал порт новое значение года
time.settime(-1, -1, -1, newDay, newMonth, newYear, newWeekDay); // Установить дату 09.02.2017, а время и день недели оставить без изменений.
time.gettime();
Serial.print(F("Дата установлена: "));
Serial.println(time.gettime("d-m-Y, D"));
}
else if (inputString.equalsIgnoreCase("info\r")) {
Serial.println();
Serial.println(time.gettime("d-m-Y, H:i:s, D"));
//////////////////// Добавить ручную и автояркость ленты
// Датчик температуры
float h = dht.readHumidity();
float t = dht.readTemperature();
Serial.print(F("Температура: "));
Serial.println(t);
Serial.print(F("Влажность: "));
Serial.println(h);
Serial.print(F("Освещённость: "));
Serial.println(lightMeter.readLightLevel());
Serial.println();
Serial.print(F("Начало работы: "));
Serial.print(startWorkHours);
Serial.print(F(":"));
Serial.println(startWorkMinutes);
Serial.print(F("Окончание работы: "));
Serial.print(endWorkHours);
Serial.print(F(":"));
Serial.println(endWorkMinutes);
Serial.print(F("Корректировка яркости:"));
Serial.println(brightnessCorrection);
}
else if (inputString.equalsIgnoreCase("Demo\r")) {
startLed();
Serial.println();
Serial.print(F("Demo OK"));
Serial.println();
}
else if (inputString.equalsIgnoreCase("Check\r")) {
LedColor();
delay(10);
BrightnessCheck();
delay(10);
displayTemp();
Serial.print(F("Check OK"));
}
else if (inputString.equalsIgnoreCase("SetWorkTimes\r")) { // Если прислали команду на установку времени
Serial.setTimeout(serialTimeout);
Serial.println(F("Начало работы:"));
Serial.println(F("Часы:"));
byte newStartHours = Serial.parseInt();
EEPROM.update(0, newStartHours);
startWorkHours = newStartHours;
Serial.println(F("OK"));
Serial.setTimeout(serialTimeout);
Serial.println(F("Минуты:"));
byte newStartMinutes = Serial.parseInt();
EEPROM.update(1, newStartMinutes);
startWorkMinutes = newStartMinutes;
Serial.println("OK");
Serial.setTimeout(serialTimeout);
Serial.println(F("Конец работы:"));
Serial.println(F("Часы:"));
byte newEndHours = Serial.parseInt();
EEPROM.update(2, newEndHours);
endWorkHours = newEndHours;
Serial.println(F("OK"));
Serial.setTimeout(serialTimeout);
Serial.println(F("Минуты:"));
byte newEndMinutes = Serial.parseInt();
EEPROM.update(3, newEndMinutes);
endWorkMinutes = newEndMinutes;
Serial.println("OK");
}
else if (inputString.equalsIgnoreCase("SetBright\r")) { // Если прислали команду на установку времени
Serial.setTimeout(serialTimeout);
Serial.println(F("Корректировка яркости:"));
Serial.println(F("Введите коэффициент корректировки:"));
brightnessCorrection = Serial.parseInt();
EEPROM.update(7, brightnessCorrection);
Serial.print(F("Коэффициент корректировки:"));
Serial.println(brightnessCorrection);
Serial.println(F("OK"));
}
else {
// Serial.print(F("Невернaя команда: "));
Serial.println(inputString);
}
inputString = ""; //обнуляем строку
stringComplete = false; //ждём нового заполнения
}
};
//----------------------------------------------------------------------------------------------//
////////Получение данных из порта //////// НЕ ТРОГАТЬ
void serialEvent() {
while (Serial.available()) {
// получаем новый байт:
char inChar = (char)Serial.read();
// добавляем его к inputString:
inputString += inChar;
// если получили символ новой строки, оповещаем программу об этом,
// чтобы она могла принять дальнейшие действия.
if (inChar == '\r') {
stringComplete = true;
}
}
};
////// ----- КОНЕЦ ПОЛУЧЕНИЯ ДАННЫХ ИЗ ПОРТА ----///////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Bluetooth подключён на порт 5V
/*
watch.settime(0,51,21,27,10,15,2); Записываем время в модуль: 0 сек, 51 мин, 21 час, 27, октября, 2015 года, вторник
Функция settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]):
Функция записывает время в модуль.
Год указывается без учёта века, в формате 0-99.
Часы указываются в 24-часовом формате, от 0 до 23.
День недели указывается в виде числа: 0-воскресенье, 1-понедельник, 2-вторник ..., 6-суббота.
Если предыдущий параметр надо оставить без изменений, то можно указать отрицательное или заведомо большее значение.
Все параметры, кроме секунд, являются необязательными. Если параметр не указан, значит он не будет изменён.
Пример: watch.settime(-1, 10); // Установить 10 минут, а секунды, часы и дату, оставить без изменений.
Пример: watch.settime(0, 5, 13); // Установить 13 часов, 5 минут, 0 секунд, а дату оставить без изменений.
Пример: watch.settime(-1, -1, -1, 9, 2, 17); // Установить дату 09.02.2017, а время и день недели оставить без изменений.
*/
// В EEPROM записаны часы и минуты начала и окончания рабочего дня
// На питание часов на плату между VСС и GND,
// керамический конденсатор 0,1-1 мкФ
// маркировка 103 или 104