This commit is contained in:
Alex
2021-02-22 21:50:58 +03:00
parent d5f70a4646
commit 93490cf06c
15 changed files with 648 additions and 139 deletions

View File

@@ -1,20 +1,36 @@
// 0.10 /*
// исправлена обработка ключа Версия 0.11b
// добавлена совместимость с nodemcu Добавлен редактор палитр
// поворот матрицы Исправлены мелкие баги в эффектах
// обновление прошивок для разных схем Переподключение к роутеру после сброса сети
// исправлен цвет огня Настройка ориентации матрицы из приложения
// индикация обновления при запуске Переработан эффект "Частицы"
Добавлена скорость огня
Переключение на новый/выбранный режим при редактировании
Отправка времени из сервиса (для АР)
Выключение по таймеру теперь плавное
Добавлен рассвет
// мигает 8: TODO:
// красным - не смог подключиться к АР плавная смена режимов
// зелёным - смог подключиться к АР Аккуратнее со светомузыкой!
// жёлтым - создал свою АП 4 клика вкл выкл смену?
// бирюзовым - успешно обновился на новую версию Mqtt?
// синим - обновился на ту же версию Базовый пак
// розовым - сброс всех настроек (первый запуск) Предложения Серёги крутского
Убрать аплод?
Огонь 2018/2020?
Взять огонь отсюда https://community.alexgyver.ru/threads/wifi-lampa-budilnik-obsuzhdenie-proshivki-ot-gunner47.2418/page-72#post-33652
Вернуть искры
Эффект погода https://it4it.club/topic/40-esp8266-i-parsing-pogodyi-s-openweathermap/
Эффект часы
*/
// Generic ESP8266, 4MB (FS:2MB OTA) // ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ!
// ДЛЯ КОМПИЛЯЦИИ ПРОШИВКИ ПОД NODEMCU/WEMOS/ESP01/ESP12 ВЫБИРАТЬ
// Инструменты/Плата Generic ESP8266
// Инструменты/Flash Size 4MB (FS:2MB OTA)
// При прошивке с других прошивок лампы поставить: Инструменты/Erase Flash/All Flash Contents
// ESP core 2.7.4+ http://arduino.esp8266.com/stable/package_esp8266com_index.json // ESP core 2.7.4+ http://arduino.esp8266.com/stable/package_esp8266com_index.json
// FastLED 3.4.0+ https://github.com/FastLED/FastLED/releases // FastLED 3.4.0+ https://github.com/FastLED/FastLED/releases
@@ -49,11 +65,12 @@ const char AP_NameChar[] = "GyverLamp2";
const char WiFiPassword[] = "12345678"; const char WiFiPassword[] = "12345678";
// ------------ Прочее ------------- // ------------ Прочее -------------
#define GL_VERSION 010 #define GL_VERSION 011 // код версии прошивки
#define EE_TOUT 30000 // таймаут сохранения епром после изменения, мс #define EE_TOUT 30000 // таймаут сохранения епром после изменения, мс
#define DEBUG_SERIAL // закомментируй чтобы выключить отладку (скорость 115200) //#define DEBUG_SERIAL // закомментируй чтобы выключить отладку (скорость 115200)
#define EE_KEY 44 // ключ сброса WiFi (измени для сброса всех настроек) #define EE_KEY 50 // ключ сброса WiFi (измени для сброса всех настроек)
#define NTP_UPD_PRD 5 // период обновления времени с NTP сервера, минут #define NTP_UPD_PRD 5 // период обновления времени с NTP сервера, минут
//#define SKIP_WIFI // пропустить подключение к вафле (для отладки)
// ------------ БИЛДЕР ------------- // ------------ БИЛДЕР -------------
//#define MAX_LEDS 1200 //#define MAX_LEDS 1200
@@ -82,11 +99,13 @@ const char WiFiPassword[] = "12345678";
#include <WiFiUdp.h> // общение по UDP #include <WiFiUdp.h> // общение по UDP
#include <EEPROM.h> // епром #include <EEPROM.h> // епром
#include "ESP8266httpUpdate.h" // OTA #include "ESP8266httpUpdate.h" // OTA
#include "mString.h" // стринг билдер
// ------------------- ДАТА -------------------- // ------------------- ДАТА --------------------
Config cfg; Config cfg;
Preset preset[MAX_PRESETS]; Preset preset[MAX_PRESETS];
Dawn dawn; Dawn dawn;
Palette pal;
WiFiServer server(80); WiFiServer server(80);
WiFiUDP Udp; WiFiUDP Udp;
WiFiUDP ntpUDP; WiFiUDP ntpUDP;
@@ -94,43 +113,48 @@ NTPClient ntp(ntpUDP);
CRGB leds[MAX_LEDS]; CRGB leds[MAX_LEDS];
Time now; Time now;
Button btn(BTN_PIN); Button btn(BTN_PIN);
timerMillis EEtmr(EE_TOUT), turnoffTmr; timerMillis EEtmr(EE_TOUT), turnoffTmr, connTmr(120000), dawnTmr;
TimeRandom trnd; TimeRandom trnd;
VolAnalyzer vol(A0), low, high; VolAnalyzer vol(A0), low, high;
FastFilter phot; FastFilter phot;
byte btnClicks = 0, brTicks = 0; byte btnClicks = 0, brTicks = 0;
unsigned char matrixValue[11][16]; unsigned char matrixValue[11][16];
bool gotNTP = false; bool gotNTP = false, gotTime = false;
void blink8(CRGB color); void blink8(CRGB color);
// ------------------- SETUP -------------------- // ------------------- SETUP --------------------
void setup() { void setup() {
delay(800); delay(2000); // ждём старта есп
memset(matrixValue, 0, sizeof(matrixValue)); memset(matrixValue, 0, sizeof(matrixValue));
#ifdef DEBUG_SERIAL #ifdef DEBUG_SERIAL
Serial.begin(115200); Serial.begin(115200);
DEBUGLN(); DEBUGLN();
#endif #endif
EEPROM.begin(512); // старт епром EEPROM.begin(512); // старт епром
startStrip(); // старт ленты startStrip(); // старт ленты
btn.setLevel(digitalRead(BTN_PIN)); // смотрим что за кнопка btn.setLevel(digitalRead(BTN_PIN)); // смотрим что за кнопка
EE_startup(); // читаем епром EE_startup(); // читаем епром
#ifndef SKIP_WIFI
checkUpdate(); // индикация было ли обновление checkUpdate(); // индикация было ли обновление
showRGB(); // показываем ргб showRGB(); // показываем ргб
checkGroup(); // показываем или меняем адрес checkGroup(); // показываем или меняем адрес
checkButton(); // проверяем кнопку на удержание checkButton(); // проверяем кнопку на удержание
startWiFi(); // старт вайфай startWiFi(); // старт вайфай
setupTime(); // выставляем время setupTime(); // выставляем время
#endif
setupADC(); // настраиваем анализ setupADC(); // настраиваем анализ
presetRotation(true); // форсировать смену режима presetRotation(true); // форсировать смену режима
} }
void loop() { void loop() {
timeTicker(); // обновляем время timeTicker(); // обновляем время
#ifndef SKIP_WIFI
tryReconnect(); // пробуем переподключиться если WiFi упал
yield(); yield();
parsing(); // ловим данные parsing(); // ловим данные
yield(); yield();
#endif
checkEEupdate(); // сохраняем епром checkEEupdate(); // сохраняем епром
presetRotation(0); // смена режимов по расписанию presetRotation(0); // смена режимов по расписанию
effectsRoutine(); // мигаем effectsRoutine(); // мигаем

View File

@@ -3,7 +3,7 @@ class Time {
byte sec = 0; byte sec = 0;
byte min = 0; byte min = 0;
byte hour = 0; byte hour = 0;
byte day = 0; // пн 0, вт 2.. вс 6 byte day = 0;
int ms = 0; int ms = 0;
uint32_t weekMs = 0; uint32_t weekMs = 0;
uint32_t weekS = 0; uint32_t weekS = 0;

View File

@@ -17,8 +17,7 @@ void button() {
DEBUGLN(btnClicks); DEBUGLN(btnClicks);
switch (btnClicks) { switch (btnClicks) {
case 1: case 1:
setPower(!cfg.state); controlHandler(!cfg.state);
sendToSlaves(0, cfg.state);
break; break;
case 2: case 2:
changePreset(1); changePreset(1);

View File

@@ -48,18 +48,21 @@ const char *OTAfile[] = {
"module_1200.bin", "module_1200.bin",
}; };
const char *NTPservers[] = { const char NTPserver[] = "pool.ntp.org";
"pool.ntp.org", //"pool.ntp.org"
"europe.pool.ntp.org", //"europe.pool.ntp.org"
"ntp1.stratum2.ru", //"ntp1.stratum2.ru"
"ntp2.stratum2.ru", //"ntp2.stratum2.ru"
"ntp.msk-ix.ru", //"ntp.msk-ix.ru"
struct Palette {
byte size = 1;
byte strip[16 * 3];
}; };
#define CFG_SIZE 13 #define CFG_SIZE 13
struct Config { struct Config {
byte GMT = 3; // часовой пояс +13 byte GMT = 3; // часовой пояс +13
byte NTP = 1; // 1..5 ВЫЧЕСТЬ 1
byte bright = 100; // яркость byte bright = 100; // яркость
byte adcMode = 1; // режим ацп (1 выкл, 2 ярк, 3 муз) byte adcMode = 1; // режим ацп (1 выкл, 2 ярк, 3 муз)
byte minBright = 0; // мин яркость byte minBright = 0; // мин яркость
@@ -71,9 +74,11 @@ struct Config {
byte maxCur = 5; // макс ток (мА/100) byte maxCur = 5; // макс ток (мА/100)
byte workFrom = 0; // часы работы (0,1.. 23) byte workFrom = 0; // часы работы (0,1.. 23)
byte workTo = 0; // часы работы (0,1.. 23) byte workTo = 0; // часы работы (0,1.. 23)
int16_t length = 100; // длина ленты byte matrix = 1; // тип матрицы 1.. 8
int16_t width = 1; // ширина матрицы
byte mTurn = 0; int16_t length = 100; // длина ленты
int16_t width = 1; // ширина матрицы
uint32_t cityID = 1; // city ID
byte state = 1; // состояние 0 выкл, 1 вкл byte state = 1; // состояние 0 выкл, 1 вкл
byte group = 1; // группа девайса (1-10) byte group = 1; // группа девайса (1-10)
@@ -82,8 +87,8 @@ struct Config {
byte presetAmount = 1; // количество режимов byte presetAmount = 1; // количество режимов
byte manualOff = 0; // выключали вручную? byte manualOff = 0; // выключали вручную?
int8_t curPreset = 0; // текущий режим int8_t curPreset = 0; // текущий режим
int16_t minLight = 0; // мин освещённость int16_t minLight = 0; // мин освещённость
int16_t maxLight = 1023; // макс освещённость int16_t maxLight = 1023;// макс освещённость
char ssid[32]; // логин wifi char ssid[32]; // логин wifi
char pass[32]; // пароль wifi char pass[32]; // пароль wifi
byte version = GL_VERSION; byte version = GL_VERSION;

View File

@@ -1,6 +1,7 @@
bool EEcfgFlag = false; bool EEcfgFlag = false;
bool EEdawnFlag = false; bool EEdawnFlag = false;
bool EEpresetFlag = false; bool EEpresetFlag = false;
bool EEpalFlag = false;
void EE_startup() { void EE_startup() {
// старт епром // старт епром
@@ -8,18 +9,23 @@ void EE_startup() {
EEPROM.write(511, EE_KEY); EEPROM.write(511, EE_KEY);
EEPROM.put(0, cfg); EEPROM.put(0, cfg);
EEPROM.put(sizeof(cfg), dawn); EEPROM.put(sizeof(cfg), dawn);
EEPROM.put(sizeof(cfg) + sizeof(dawn), preset); EEPROM.put(sizeof(cfg) + sizeof(dawn), pal);
EEPROM.put(sizeof(cfg) + sizeof(dawn) + sizeof(pal), preset);
EEPROM.commit(); EEPROM.commit();
blink8(CRGB::Pink); blink8(CRGB::Pink);
DEBUGLN("First start"); DEBUGLN("First start");
} }
EEPROM.get(0, cfg); EEPROM.get(0, cfg);
EEPROM.get(sizeof(cfg), dawn); EEPROM.get(sizeof(cfg), dawn);
EEPROM.get(sizeof(cfg) + sizeof(dawn), preset); EEPROM.get(sizeof(cfg) + sizeof(dawn), pal);
EEPROM.get(sizeof(cfg) + sizeof(dawn) + sizeof(pal), preset);
DEBUG("EEPR size: ");
DEBUGLN(sizeof(cfg) + sizeof(dawn) + sizeof(pal) + sizeof(preset));
// запускаем всё // запускаем всё
//trnd.setChannel(cfg.group);
FastLED.setMaxPowerInVoltsAndMilliamps(STRIP_VOLT, cfg.maxCur * 100); FastLED.setMaxPowerInVoltsAndMilliamps(STRIP_VOLT, cfg.maxCur * 100);
updPal();
} }
void EE_updateCfg() { void EE_updateCfg() {
@@ -34,6 +40,10 @@ void EE_updatePreset() {
EEpresetFlag = true; EEpresetFlag = true;
EEtmr.restart(); EEtmr.restart();
} }
void EE_updatePal() {
EEpalFlag = true;
EEtmr.restart();
}
void checkEEupdate() { void checkEEupdate() {
if (EEtmr.isReady()) { if (EEtmr.isReady()) {
if (EEcfgFlag || EEdawnFlag || EEpresetFlag) { if (EEcfgFlag || EEdawnFlag || EEpresetFlag) {
@@ -47,11 +57,16 @@ void checkEEupdate() {
EEPROM.put(sizeof(cfg), dawn); EEPROM.put(sizeof(cfg), dawn);
DEBUGLN("save dawn"); DEBUGLN("save dawn");
} }
if (EEpalFlag) {
EEpalFlag = false;
EEPROM.put(sizeof(cfg) + sizeof(dawn), pal);
DEBUGLN("save pal");
}
if (EEpresetFlag) { if (EEpresetFlag) {
EEpresetFlag = false; EEpresetFlag = false;
EEPROM.put(sizeof(cfg) + sizeof(dawn), preset); EEPROM.put(sizeof(cfg) + sizeof(dawn) + sizeof(pal), preset);
DEBUGLN("save preset"); DEBUGLN("save preset");
} }
EEPROM.commit(); EEPROM.commit();
} }
EEtmr.stop(); EEtmr.stop();

View File

@@ -1,12 +1,30 @@
void effectsRoutine() { void effectsRoutine() {
static timerMillis effTmr(30, true); static timerMillis effTmr(30, true);
static byte prevEff = 255; static byte prevEff = 255;
if (dawnTmr.running()) {
if (effTmr.isReady()) {
fill_solid(leds, MAX_LEDS, ColorFromPalette(HeatColors_p, dawnTmr.getLength8(), scaleFF(dawnTmr.getLength8(), dawn.bright), LINEARBLEND));
FastLED.show();
}
if (dawnTmr.isReady()) dawnTmr.stop();
return;
}
if (cfg.state && effTmr.isReady()) { if (cfg.state && effTmr.isReady()) {
int thisLength = getLength(); int thisLength = getLength();
byte thisScale = getScale(); byte thisScale = getScale();
int thisWidth = (cfg.deviceType > 1) ? cfg.width : 1; int thisWidth = (cfg.deviceType > 1) ? cfg.width : 1;
byte thisBright = getBright();
if (turnoffTmr.running()) thisBright = scaleFF(thisBright, 255 - turnoffTmr.getLength8());
if (turnoffTmr.isReady()) {
turnoffTmr.stop();
setPower(0);
return;
}
FastLED.setBrightness(thisBright);
FastLED.setBrightness(getBright());
if (prevEff != CUR_PRES.effect) { if (prevEff != CUR_PRES.effect) {
FastLED.clear(); FastLED.clear();
prevEff = CUR_PRES.effect; prevEff = CUR_PRES.effect;
@@ -18,10 +36,10 @@ void effectsRoutine() {
FOR_j(0, cfg.length) { FOR_j(0, cfg.length) {
FOR_i(0, cfg.width) { FOR_i(0, cfg.width) {
leds[getPix(i, j)] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], leds[getPix(i, j)] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1],
inoise8( scalePal(inoise8(
i * (thisScale / 5) - cfg.width * (thisScale / 5) / 2, i * (thisScale / 5) - cfg.width * (thisScale / 5) / 2,
j * (thisScale / 5) - cfg.length * (thisScale / 5) / 2, j * (thisScale / 5) - cfg.length * (thisScale / 5) / 2,
(now.weekMs >> 1) * CUR_PRES.speed / 255), (now.weekMs >> 1) * CUR_PRES.speed / 255)),
255, LINEARBLEND); 255, LINEARBLEND);
} }
} }
@@ -29,12 +47,13 @@ void effectsRoutine() {
} else { } else {
FOR_i(0, cfg.length) { FOR_i(0, cfg.length) {
leds[i] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], leds[i] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1],
inoise8(i * (thisScale / 5) - cfg.length * (thisScale / 5) / 2, scalePal(inoise8(i * (thisScale / 5) - cfg.length * (thisScale / 5) / 2,
(now.weekMs >> 1) * CUR_PRES.speed / 255), (now.weekMs >> 1) * CUR_PRES.speed / 255)),
255, LINEARBLEND); 255, LINEARBLEND);
} }
} }
break; break;
case 2: // ==================================== ЦВЕТ ==================================== case 2: // ==================================== ЦВЕТ ====================================
{ {
fill_solid(leds, cfg.length * thisWidth, CHSV(CUR_PRES.color, thisScale, CUR_PRES.min)); fill_solid(leds, cfg.length * thisWidth, CHSV(CUR_PRES.color, thisScale, CUR_PRES.min));
@@ -47,11 +66,12 @@ void effectsRoutine() {
} }
} }
break; break;
case 3: // ================================= СМЕНА ЦВЕТА ================================= case 3: // ================================= СМЕНА ЦВЕТА =================================
{ {
CRGB thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], (now.weekMs >> 5) * CUR_PRES.speed / 255, CUR_PRES.min, LINEARBLEND); CRGB thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scalePal((now.weekMs >> 5) * CUR_PRES.speed / 255), CUR_PRES.min, LINEARBLEND);
fill_solid(leds, cfg.length * thisWidth, thisColor); fill_solid(leds, cfg.length * thisWidth, thisColor);
thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], (now.weekMs >> 5) * CUR_PRES.speed / 255, CUR_PRES.max, LINEARBLEND); thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scalePal((now.weekMs >> 5) * CUR_PRES.speed / 255), CUR_PRES.max, LINEARBLEND);
if (CUR_PRES.fromCenter) { if (CUR_PRES.fromCenter) {
fillStrip(cfg.length / 2, cfg.length / 2 + thisLength / 2, thisColor); fillStrip(cfg.length / 2, cfg.length / 2 + thisLength / 2, thisColor);
fillStrip(cfg.length / 2 - thisLength / 2, cfg.length / 2, thisColor); fillStrip(cfg.length / 2 - thisLength / 2, cfg.length / 2, thisColor);
@@ -60,6 +80,7 @@ void effectsRoutine() {
} }
} }
break; break;
case 4: // ================================== ГРАДИЕНТ ================================== case 4: // ================================== ГРАДИЕНТ ==================================
if (CUR_PRES.fromCenter) { if (CUR_PRES.fromCenter) {
FOR_i(cfg.length / 2, cfg.length) { FOR_i(cfg.length / 2, cfg.length) {
@@ -67,7 +88,7 @@ void effectsRoutine() {
if (CUR_PRES.soundReact == GL_REACT_LEN) bright = (i < cfg.length / 2 + thisLength / 2) ? (CUR_PRES.max) : (CUR_PRES.min); if (CUR_PRES.soundReact == GL_REACT_LEN) bright = (i < cfg.length / 2 + thisLength / 2) ? (CUR_PRES.max) : (CUR_PRES.min);
CRGB thisColor = ColorFromPalette( CRGB thisColor = ColorFromPalette(
paletteArr[CUR_PRES.palette - 1], // (x*1.9 + 25) / 255 - быстрый мап 0..255 в 0.1..2 paletteArr[CUR_PRES.palette - 1], // (x*1.9 + 25) / 255 - быстрый мап 0..255 в 0.1..2
(i * (thisScale * 1.9 + 25) / cfg.length) + ((now.weekMs >> 3) * (CUR_PRES.speed - 128) / 128), scalePal((i * (thisScale * 1.9 + 25) / cfg.length) + ((now.weekMs >> 3) * (CUR_PRES.speed - 128) / 128)),
bright, LINEARBLEND); bright, LINEARBLEND);
if (cfg.deviceType > 1) fillRow(i, thisColor); if (cfg.deviceType > 1) fillRow(i, thisColor);
else leds[i] = thisColor; else leds[i] = thisColor;
@@ -81,16 +102,41 @@ void effectsRoutine() {
if (CUR_PRES.soundReact == GL_REACT_LEN) bright = (i < thisLength) ? (CUR_PRES.max) : (CUR_PRES.min); if (CUR_PRES.soundReact == GL_REACT_LEN) bright = (i < thisLength) ? (CUR_PRES.max) : (CUR_PRES.min);
CRGB thisColor = ColorFromPalette( CRGB thisColor = ColorFromPalette(
paletteArr[CUR_PRES.palette - 1], // (x*1.9 + 25) / 255 - быстрый мап 0..255 в 0.1..2 paletteArr[CUR_PRES.palette - 1], // (x*1.9 + 25) / 255 - быстрый мап 0..255 в 0.1..2
(i * (thisScale * 1.9 + 25) / cfg.length) + ((now.weekMs >> 3) * (CUR_PRES.speed - 128) / 128), scalePal((i * (thisScale * 1.9 + 25) / cfg.length) + ((now.weekMs >> 3) * (CUR_PRES.speed - 128) / 128)),
bright, LINEARBLEND); bright, LINEARBLEND);
if (cfg.deviceType > 1) fillRow(i, thisColor); if (cfg.deviceType > 1) fillRow(i, thisColor);
else leds[i] = thisColor; else leds[i] = thisColor;
} }
} }
break; break;
case 5: // =================================== ЧАСТИЦЫ =================================== case 5: // =================================== ЧАСТИЦЫ ===================================
FOR_i(0, cfg.length * cfg.width) leds[i].fadeToBlackBy(70); FOR_i(0, cfg.length * cfg.width) leds[i].fadeToBlackBy(70);
if (cfg.deviceType > 1) { {
uint16_t rndVal = 0;
FOR_i(0, thisScale / 8 + 1) {
rndVal = rndVal * 2053 + 13849; // random2053 алгоритм
int homeX = inoise16(i * 100000000ul + (now.weekMs << 3) * CUR_PRES.speed / 255);
homeX = map(homeX, 10000, 55000, 0, cfg.length);
int offsX = inoise8(i * 2500 + (now.weekMs >> 1) * CUR_PRES.speed / 255) - 128;
offsX = cfg.length / 2 * offsX / 128;
int thisX = homeX + offsX;
if (cfg.deviceType > 1) {
int homeY = inoise16(i * 100000000ul + 2000000000ul + (now.weekMs << 3) * CUR_PRES.speed / 255);
homeY = map(homeY, 10000, 55000, 0, cfg.width);
int offsY = inoise8(i * 2500 + 30000 + (now.weekMs >> 1) * CUR_PRES.speed / 255) - 128;
offsY = cfg.length / 2 * offsY / 128;
int thisY = homeY + offsY;
setPix(thisX, thisY, CHSV(CUR_PRES.rnd ? rndVal : CUR_PRES.color, 255, 255));
} else {
setLED(thisX, CHSV(CUR_PRES.rnd ? rndVal : CUR_PRES.color, 255, 255));
}
}
}
/*
FOR_i(0, cfg.length * cfg.width) leds[i].fadeToBlackBy(70);
if (cfg.deviceType > 1) {
uint16_t rndVal = 0; uint16_t rndVal = 0;
FOR_i(0, thisScale / 8) { FOR_i(0, thisScale / 8) {
int thisY = inoise16(i * 100000000ul + (now.weekMs << 6) * CUR_PRES.speed / 255); int thisY = inoise16(i * 100000000ul + (now.weekMs << 6) * CUR_PRES.speed / 255);
@@ -102,7 +148,7 @@ void effectsRoutine() {
if (thisY >= 0 && thisY < cfg.length && thisX >= 0 && thisX < cfg.width) if (thisY >= 0 && thisY < cfg.length && thisX >= 0 && thisX < cfg.width)
leds[getPix(thisX, thisY)] = CHSV(CUR_PRES.rnd ? rndVal : CUR_PRES.color, 255, 255); leds[getPix(thisX, thisY)] = CHSV(CUR_PRES.rnd ? rndVal : CUR_PRES.color, 255, 255);
} }
} else { } else {
uint16_t rndVal = 0; uint16_t rndVal = 0;
FOR_i(0, thisScale / 8) { FOR_i(0, thisScale / 8) {
int thisPos = inoise16(i * 100000000ul + (now.weekMs << 6) * CUR_PRES.speed / 255); int thisPos = inoise16(i * 100000000ul + (now.weekMs << 6) * CUR_PRES.speed / 255);
@@ -110,12 +156,13 @@ void effectsRoutine() {
rndVal = rndVal * 2053 + 13849; // random2053 алгоритм rndVal = rndVal * 2053 + 13849; // random2053 алгоритм
if (thisPos >= 0 && thisPos < cfg.length) leds[thisPos] = CHSV(CUR_PRES.rnd ? rndVal : CUR_PRES.color, 255, 255); if (thisPos >= 0 && thisPos < cfg.length) leds[thisPos] = CHSV(CUR_PRES.rnd ? rndVal : CUR_PRES.color, 255, 255);
} }
} }*/
break; break;
case 6: // ==================================== ОГОНЬ ==================================== case 6: // ==================================== ОГОНЬ ====================================
{ {
if (cfg.deviceType > 1) { // 2D огонь if (cfg.deviceType > 1) { // 2D огонь
fireRoutine(); fireRoutine(CUR_PRES.speed / 2);
} else { // 1D огонь } else { // 1D огонь
static byte heat[MAX_LEDS]; static byte heat[MAX_LEDS];
CRGBPalette16 gPal; CRGBPalette16 gPal;
@@ -138,16 +185,21 @@ void effectsRoutine() {
} }
} }
break; break;
case 7: // ================================== КОНФЕТТИ ================================== case 7: // ================================== КОНФЕТТИ ==================================
FOR_i(0, (thisScale >> 3) + 1) { FOR_i(0, (thisScale >> 3) + 1) {
byte x = random(0, cfg.length * cfg.width); int x = random(0, cfg.length * cfg.width);
if (leds[x] == CRGB(0, 0, 0)) leds[x] = CHSV(CUR_PRES.rnd ? random(0, 255) : CUR_PRES.color, 255, 255); if (leds[x] == CRGB(0, 0, 0)) leds[x] = CHSV(CUR_PRES.rnd ? random(0, 255) : CUR_PRES.color, 255, 255);
} }
FOR_i(0, cfg.length * cfg.width) { FOR_i(0, cfg.length * cfg.width) {
if (leds[i].r >= 10 || leds[i].g >= 10 || leds[i].b >= 10) leds[i].fadeToBlackBy(CUR_PRES.speed / 2); if (leds[i].r >= 10 || leds[i].g >= 10 || leds[i].b >= 10) leds[i].fadeToBlackBy(CUR_PRES.speed / 2 + 1);
else leds[i] = 0; else leds[i] = 0;
} }
break; break;
case 8: // ================================== ПОГОДА ==================================
break;
} }
// выводим нажатия кнопки // выводим нажатия кнопки
@@ -203,6 +255,13 @@ void fillRow(int row, CRGB color) {
FOR_i(cfg.width * row, cfg.width * (row + 1)) leds[i] = color; FOR_i(cfg.width * row, cfg.width * (row + 1)) leds[i] = color;
} }
void updPal() {
for (int i = 0; i < 16; i++) {
paletteArr[0][i] = CRGB(pal.strip[i * 3], pal.strip[i * 3 + 1], pal.strip[i * 3 + 2]);
}
if (pal.size < 16) paletteArr[0][pal.size] = paletteArr[0][0];
}
void blink8(CRGB color) { void blink8(CRGB color) {
FOR_i(0, 3) { FOR_i(0, 3) {
fill_solid(leds, 8, color); fill_solid(leds, 8, color);
@@ -214,15 +273,37 @@ void blink8(CRGB color) {
} }
} }
byte scalePal(byte val) {
if (CUR_PRES.palette == 1) val = val * pal.size / 16;
return val;
}
void setPix(int x, int y, CRGB color) {
if (y >= 0 && y < cfg.length && x >= 0 && x < cfg.width) leds[getPix(x, y)] = color;
}
void setLED(int x, CRGB color) {
if (x >= 0 && x < cfg.length) leds[x] = color;
}
// получить номер пикселя в ленте по координатам // получить номер пикселя в ленте по координатам
uint16_t getPix(int x, int y) { uint16_t getPix(int x, int y) {
if (cfg.mTurn) { int matrixW;
byte b = x; if (cfg.matrix == 2 || cfg.matrix == 4 || cfg.matrix == 6 || cfg.matrix == 8) matrixW = cfg.length;
x = y; else matrixW = cfg.width;
y = b; int thisX, thisY;
switch (cfg.matrix) {
case 1: thisX = x; thisY = y; break;
case 2: thisX = y; thisY = x; break;
case 3: thisX = x; thisY = (cfg.length - y - 1); break;
case 4: thisX = (cfg.length - y - 1); thisY = x; break;
case 5: thisX = (cfg.width - x - 1); thisY = (cfg.length - y - 1); break;
case 6: thisX = (cfg.length - y - 1); thisY = (cfg.width - x - 1); break;
case 7: thisX = (cfg.width - x - 1); thisY = y; break;
case 8: thisX = y; thisY = (cfg.width - x - 1); break;
} }
if ( !(y & 1) || (cfg.deviceType - 2) ) return (y * cfg.width + x); // если чётная строка
else return (y * cfg.width + cfg.width - x - 1); // если нечётная строка if ( !(thisY & 1) || (cfg.deviceType - 2) ) return (thisY * matrixW + thisX); // чётная строка
else return (thisY * matrixW + matrixW - thisX - 1); // нечётная строка
} }
/* /*
целочисленный мап целочисленный мап

View File

@@ -27,10 +27,15 @@ const unsigned char hueMask[11][16] PROGMEM = {
byte fireLine[100]; byte fireLine[100];
void fireRoutine() { void fireRoutine(byte speed) {
shiftUp(); static byte count = 0;
FOR_i(0, cfg.width) fireLine[i] = random(64, 255); if (count >= 100) {
drawFrame(30); shiftUp();
FOR_i(0, cfg.width) fireLine[i] = random(64, 255);
count = 0;
}
drawFrame(count);
count += speed;
} }
void shiftUp() { void shiftUp() {

View File

@@ -0,0 +1,315 @@
// TODO
// защита от переполнения
char* mUtoa(uint32_t value, char *buffer, bool clear = 1);
char* mLtoa(int32_t value, char *buffer, bool clear = 1);
char* mFtoa(double value, int8_t decimals, char *buffer);
char* mUtoa(uint32_t value, char *buffer, bool clear) {
buffer += 11;
if (clear) *--buffer = 0;
do {
*--buffer = value % 10 + '0';
value /= 10;
} while (value != 0);
return buffer;
}
char* mLtoa(int32_t value, char *buffer, bool clear) {
bool minus = value < 0;
if (minus) value = -value;
buffer = mUtoa(value, buffer, clear);
if (minus) *--buffer = '-';
return buffer;
}
char* mFtoa(double value, int8_t decimals, char *buffer) {
int32_t mant = (int32_t)value;
value -= mant;
uint32_t exp = 1;
while (decimals--) exp *= 10;
exp *= (float)value;
/*buffer += 9;
buffer = mUtoa(exp, buffer);
--buffer = '.';
buffer -= 11;
buffer = mLtoa(mant, buffer, 0);*/
buffer = ltoa(mant, buffer, DEC);
byte len = strlen(buffer);
*(buffer + len++) = '.';
ltoa(exp, buffer + len++, DEC);
return buffer;
}
class mString {
public:
int size = 0;
char* buf;
// system*this = buf;
uint16_t length() {
return strlen(buf);
}
void clear() {
buf[0] = 0;
}
// constructor
mString(char* buffer, int newSize) {
//*this = buf;
buf = buffer;
size = newSize;
}
/*mString (const char c) {
init();
add(c);
}
mString (const char* data) {
init();
add(data);
}
mString (const __FlashStringHelper *data) {
init();
add(data);
}
mString (uint32_t value) {
init();
add(value);
}
mString (int32_t value) {
init();
add(value);
}
mString (uint16_t value) {
init();
add(value);
}
mString (int16_t value) {
init();
add(value);
}
mString (uint8_t value) {
init();
add(value);
}
mString (int8_t value) {
init();
add(value);
}
mString (double value, byte dec = 2) {
init();
add(value, dec);
}*/
// add
mString& add(const char c) {
byte len = length();
buf[len++] = c;
buf[len++] = 0;
return *this;
}
mString& add(const char* data) {
/*byte len = length();
do {
buf[len] = *(data++);
} while (buf[len++] != 0);*/
strcpy(buf + length(), data);
return *this;
}
mString& add(const __FlashStringHelper *data) {
PGM_P p = reinterpret_cast<PGM_P>(data);
strcpy_P(buf + length(), p);
return *this;
/*do {
buf[len] = (char)pgm_read_byte_near(p++);
} while (buf[len++] != 0);
*/
}
mString& add(uint32_t value) {
//char buf[11];
//return add(mUtoa(value, buf));
utoa(value, buf + length(), DEC);
return *this;
}
mString& add(uint16_t value) {
return add((uint32_t)value);
}
mString& add(uint8_t value) {
return add((uint32_t)value);
}
mString& add(int32_t value) {
//char buf[11];
//return add(mLtoa(value, buf));
ltoa(value, buf + length(), DEC);
return *this;
}
mString& add(int16_t value) {
return add((int32_t)value);
}
mString& add(int8_t value) {
return add((int32_t)value);
}
mString& add(double value, int8_t dec = 2) {
char buf[20];
return add(mFtoa(value, dec, buf));
//dtostrf(value, dec, DEC, buf+length());
//return *this;
}
// add +=
mString& operator += (const char c) {
return add(c);
}
mString& operator += (const char* data) {
return add(data);
}
mString& operator += (const __FlashStringHelper *data) {
return add(data);
}
mString& operator += (uint32_t value) {
return add(value);
}
mString& operator += (int32_t value) {
return add(value);
}
mString& operator += (uint16_t value) {
return add(value);
}
mString& operator += (int16_t value) {
return add(value);
}
mString& operator += (uint8_t value) {
return add(value);
}
mString& operator += (int8_t value) {
return add(value);
}
mString& operator += (double value) {
return add(value);
}
// assign
mString& operator = (const char c) {
clear();
return add(c);
}
mString& operator = (const char* data) {
clear();
return add(data);
}
mString& operator = (const __FlashStringHelper *data) {
clear();
return add(data);
}
mString& operator = (uint32_t value) {
clear();
return add(value);
}
mString& operator = (int32_t value) {
clear();
return add(value);
}
mString& operator = (uint16_t value) {
clear();
return add(value);
}
mString& operator = (int16_t value) {
clear();
return add(value);
}
mString& operator = (uint8_t value) {
clear();
return add(value);
}
mString& operator = (int8_t value) {
clear();
return add(value);
}
mString& operator = (double value) {
clear();
return add(value);
}
// compare
bool operator == (const char c) {
return (buf[0] == c && buf[1] == 0);
}
bool operator == (const char* data) {
return !strcmp(buf, data);
}
bool operator == (uint32_t value) {
char valBuf[11];
return !strcmp(buf, utoa(value, valBuf, DEC));
}
bool operator == (int32_t value) {
char valBuf[11];
return !strcmp(buf, ltoa(value, valBuf, DEC));
}
bool operator == (float value) {
char valBuf[20];
return !strcmp(buf, mFtoa(value, 2, valBuf));
}
char operator [] (uint16_t index) const {
return (index < size ? buf[index] : 0);
}
char& operator [] (uint16_t index) {
return buf[index];
}
// convert & parse
uint32_t toInt() {
return atoi(buf);
}
float toFloat() {
return atof(buf);
}
const char* c_str() {
return buf;
}
bool startsWith(const char *data) {
return strlen(data) == strspn(buf, data);
}
int indexOf(char ch, uint16_t fromIndex = 0) {
if (fromIndex >= length()) return -1;
const char* temp = strchr(buf + fromIndex, ch);
if (temp == NULL) return -1;
return temp - buf;
}
int parseBytes(byte* data, int len, char div = ',', char ter = NULL) {
int b = 0, c = 0;
data[b] = 0;
while (true) {
if (buf[c] == div) {
b++;
c++;
if (b == len) return b;
data[b] = 0;
continue;
}
if (buf[c] == ter || b == len) return b + 1;
data[b] *= 10;
data[b] += buf[c] - '0';
c++;
}
}
int parseInts(int* data, int len, char div = ',', char ter = NULL) {
int b = 0, c = 0;
data[b] = 0;
while (true) {
if (buf[c] == div) {
b++;
c++;
if (b == len) return b;
data[b] = 0;
continue;
}
if (buf[c] == ter || b == len) return b + 1;
data[b] *= 10;
data[b] += buf[c] - '0';
c++;
}
}
private:
};

View File

@@ -1,6 +1,8 @@
#include <FastLED.h> // лента #include <FastLED.h> // лента
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ // http://soliton.vm.bytemark.co.uk/pub/cpt-city/
CRGBPalette16 customPal;
DEFINE_GRADIENT_PALETTE( Fire_gp ) { DEFINE_GRADIENT_PALETTE( Fire_gp ) {
0, 0, 0, 0, 0, 0, 0, 0,
128, 255, 0, 0, 128, 255, 0, 0,
@@ -221,6 +223,7 @@ DEFINE_GRADIENT_PALETTE ( aurora_gp ) {
}; };
CRGBPalette16 paletteArr[] = { CRGBPalette16 paletteArr[] = {
customPal,
HeatColors_p, HeatColors_p,
Fire_gp, Fire_gp,
LavaColors_p, LavaColors_p,

View File

@@ -9,35 +9,47 @@ void parsing() {
buf[n] = NULL; buf[n] = NULL;
DEBUGLN(buf); // пакет вида <ключ>,<канал>,<тип>,<дата1>,<дата2>... DEBUGLN(buf); // пакет вида <ключ>,<канал>,<тип>,<дата1>,<дата2>...
mString pars(buf, sizeof(buf));
if (!pars.startsWith(GL_KEY)) return; // не наш ключ
byte keyLen = strlen(GL_KEY);
byte keyLen = strchr(buf, ',') - buf; // indexof byte data[MAX_PRESETS * PRES_SIZE + 5];
if (strncmp(buf, GL_KEY, keyLen)) return; // не наш ключ
byte data[MAX_PRESETS * PRES_SIZE + keyLen];
memset(data, 0, MAX_PRESETS * PRES_SIZE + keyLen); memset(data, 0, MAX_PRESETS * PRES_SIZE + keyLen);
int count = 0; int count = 0;
char *str, *p = buf + keyLen; // сдвиг до даты char *str, *p = buf + keyLen; // сдвиг до даты
char *ssid, *pass; char *ssid, *pass;
uint32_t city = 0;
uint16_t stripL, stripW;
while ((str = strtok_r(p, ",", &p)) != NULL) { while ((str = strtok_r(p, ",", &p)) != NULL) {
data[count++] = atoi(str); uint32_t thisInt = atoi(str);
if (count == 4) ssid = str; data[count++] = (byte)thisInt;
if (count == 5) pass = str; if (data[1] == 0) {
if (count == 4) ssid = str;
if (count == 5) pass = str;
}
if (data[1] == 1) {
if (count == 16) stripL = thisInt;
if (count == 17) stripW = thisInt;
if (count == 18) city = thisInt;
}
} }
// широковещательный запрос времени для local устройств в сети AP лампы // широковещательный запрос времени для local устройств в сети AP лампы
if (data[0] == 0 && cfg.WiFimode && !gotNTP) { if (data[0] == 0 && cfg.WiFimode && !gotNTP) {
now.hour = data[1]; now.day = data[1];
now.min = data[2]; now.hour = data[2];
now.min = data[3];
now.sec = data[4];
now.setMs(0); now.setMs(0);
} }
if (data[0] != cfg.group) return; // не наш адрес, выходим if (data[0] != cfg.group) return; // не наш адрес, выходим
switch (data[1]) { // тип 0 - control, 1 - config, 2 - effects, 3 - dawn switch (data[1]) { // тип 0 - control, 1 - config, 2 - effects, 3 - dawn, 4 - from master, 5 - palette
case 0: DEBUGLN("Control"); case 0: DEBUGLN("Control");
switch (data[2]) { switch (data[2]) {
case 0: setPower(0); break; // выкл case 0: controlHandler(0); break; // выкл
case 1: setPower(1); break; // вкл case 1: controlHandler(1); break; // вкл
case 2: cfg.minLight = phot.getRaw(); break; // мин яркость case 2: cfg.minLight = phot.getRaw(); break; // мин яркость
case 3: cfg.maxLight = phot.getRaw(); break; // макс яркость case 3: cfg.maxLight = phot.getRaw(); break; // макс яркость
case 4: changePreset(-1); break; // пред пресет case 4: changePreset(-1); break; // пред пресет
@@ -54,12 +66,16 @@ void parsing() {
case 12: if (gotNTP) { // OTA обновление, если есть интернет case 12: if (gotNTP) { // OTA обновление, если есть интернет
cfg.update = 1; cfg.update = 1;
EE_updCfg(); EE_updCfg();
delay(100);
FastLED.clear(); FastLED.clear();
FastLED.show(); FastLED.show();
char OTA[60]; char OTA[60];
strcpy(OTA, OTAhost); mString ota(OTA, 60);
strcpy(OTA + strlen(OTAhost), OTAfile[data[3]]); ota.clear();
ota += OTAhost;
ota += OTAfile[data[3]];
DEBUG("Update to ");
DEBUGLN(OTA);
delay(100);
ESPhttpUpdate.update(OTA); ESPhttpUpdate.update(OTA);
} break; } break;
case 13: // выключить через case 13: // выключить через
@@ -77,15 +93,14 @@ void parsing() {
FOR_i(0, CFG_SIZE) { FOR_i(0, CFG_SIZE) {
*((byte*)&cfg + i) = data[i + 2]; // загоняем в структуру *((byte*)&cfg + i) = data[i + 2]; // загоняем в структуру
} }
cfg.mTurn = data[21]; cfg.length = stripL;
cfg.length = data[17] | (data[16] << 8); // склеиваем cfg.width = stripW;
cfg.width = data[20] | (data[19] << 8); // склеиваем cfg.cityID = city;
if (cfg.length > MAX_LEDS) cfg.length = MAX_LEDS; if (cfg.length > MAX_LEDS) cfg.length = MAX_LEDS;
if (cfg.deviceType == GL_TYPE_STRIP) cfg.width = 1; if (cfg.deviceType == GL_TYPE_STRIP) cfg.width = 1;
if (cfg.length * cfg.width > MAX_LEDS) cfg.width = MAX_LEDS / cfg.length; if (cfg.length * cfg.width > MAX_LEDS) cfg.width = MAX_LEDS / cfg.length;
ntp.setTimeOffset((cfg.GMT - 13) * 3600); ntp.setTimeOffset((cfg.GMT - 13) * 3600);
ntp.setPoolServerName(NTPservers[cfg.NTP - 1]);
FastLED.setMaxPowerInVoltsAndMilliamps(STRIP_VOLT, cfg.maxCur * 100); FastLED.setMaxPowerInVoltsAndMilliamps(STRIP_VOLT, cfg.maxCur * 100);
if (cfg.adcMode == GL_ADC_BRI) switchToPhot(); if (cfg.adcMode == GL_ADC_BRI) switchToPhot();
else if (cfg.adcMode == GL_ADC_MIC) switchToMic(); else if (cfg.adcMode == GL_ADC_MIC) switchToMic();
@@ -100,6 +115,7 @@ void parsing() {
*((byte*)&preset + j * PRES_SIZE + i) = data[j * PRES_SIZE + i + 3]; // загоняем в структуру *((byte*)&preset + j * PRES_SIZE + i) = data[j * PRES_SIZE + i + 3]; // загоняем в структуру
} }
} }
if (!cfg.rotation) setPreset(data[cfg.presetAmount * PRES_SIZE + 3] - 1);
EE_updatePreset(); EE_updatePreset();
presetRotation(true); // форсировать смену режима presetRotation(true); // форсировать смену режима
break; break;
@@ -121,9 +137,25 @@ void parsing() {
EE_updateCfg(); EE_updateCfg();
} }
break; break;
case 5: DEBUGLN("Palette");
FOR_i(0, 1 + 16 * 3) {
*((byte*)&pal + i) = data[i + 2]; // загоняем в структуру
}
updPal();
EE_updatePal();
break;
case 6: DEBUGLN("Time");
if (!cfg.WiFimode) { // если мы AP
now.day = data[2];
now.hour = data[3];
now.min = data[4];
}
gotTime = true;
break;
} }
FastLED.clear(); // на всякий случай FastLED.clear(); // на всякий случай
} }
} }
@@ -131,18 +163,19 @@ void sendToSlaves(byte data1, byte data2) {
if (cfg.role == GL_MASTER) { if (cfg.role == GL_MASTER) {
IPAddress ip = WiFi.localIP(); IPAddress ip = WiFi.localIP();
ip[3] = 255; ip[3] = 255;
char reply[20] = GL_KEY;
byte keylen = strlen(GL_KEY);
reply[keylen++] = ',';
reply[keylen++] = cfg.group + '0';
reply[keylen++] = ',';
reply[keylen++] = '4';
reply[keylen++] = ',';
reply[keylen++] = data1 + '0';
reply[keylen++] = ',';
itoa(data2, reply + (keylen++), DEC);
DEBUG("Sending: "); char reply[20];
mString packet(reply, sizeof(reply));
packet.clear();
packet += GL_KEY;
packet += ',';
packet += cfg.group;
packet += ",4,";
packet += data1;
packet += ',';
packet += data2;
DEBUG("Sending to Slaves: ");
DEBUGLN(reply); DEBUGLN(reply);
FOR_i(0, 3) { FOR_i(0, 3) {

View File

@@ -30,14 +30,29 @@ void setPreset(byte pres) {
} }
} }
void setPower(bool state) { void controlHandler(bool state) {
if (turnoffTmr.running()) {
turnoffTmr.stop();
DEBUGLN("stop off timer");
return;
}
if (dawnTmr.running()) {
dawnTmr.stop();
DEBUGLN("stop dawn timer");
return;
}
if (state) cfg.manualOff = 0; if (state) cfg.manualOff = 0;
if (cfg.state && !state) cfg.manualOff = 1; if (cfg.state && !state) cfg.manualOff = 1;
setPower(state);
}
void setPower(bool state) {
cfg.state = state; cfg.state = state;
if (!state) { if (!state) {
delay(100); // чтобы пролететь мин. частоту обновления delay(100); // чтобы пролететь мин. частоту обновления
FastLED.clear(); FastLED.clear();
FastLED.show(); FastLED.show();
} }
sendToSlaves(0, cfg.state);
DEBUGLN(state ? "Power on" : "Power off"); DEBUGLN(state ? "Power on" : "Power off");
} }

View File

@@ -135,6 +135,7 @@ void setupLocal() {
delay(50); delay(50);
} }
if (connect) { if (connect) {
connTmr.stop();
blink8(CRGB::Green); blink8(CRGB::Green);
server.begin(); server.begin();
DEBUG("Connected! Local IP: "); DEBUG("Connected! Local IP: ");
@@ -147,6 +148,7 @@ void setupLocal() {
failCount++; failCount++;
tmr = millis(); tmr = millis();
if (failCount >= 3) { if (failCount >= 3) {
connTmr.restart(); // попробуем позже
setupAP(); setupAP();
return; return;
/*DEBUGLN("Reboot to AP!"); /*DEBUGLN("Reboot to AP!");
@@ -172,5 +174,13 @@ void checkUpdate() {
DEBUG("Update to current"); DEBUG("Update to current");
} }
cfg.update = 0; cfg.update = 0;
EE_updCfg();
}
}
void tryReconnect() {
if (connTmr.isReady()) {
DEBUGLN("Reconnect");
setupLocal();
} }
} }

View File

@@ -1,7 +1,7 @@
void setupTime() { void setupTime() {
ntp.setUpdateInterval(NTP_UPD_PRD / 2 * 60000ul); // меньше в два раза, ибо апдейт вручную ntp.setUpdateInterval(NTP_UPD_PRD / 2 * 60000ul); // меньше в два раза, ибо апдейт вручную
ntp.setTimeOffset((cfg.GMT - 13) * 3600); ntp.setTimeOffset((cfg.GMT - 13) * 3600);
ntp.setPoolServerName(NTPservers[cfg.NTP - 1]); ntp.setPoolServerName(NTPserver);
if (cfg.WiFimode) { if (cfg.WiFimode) {
// если подключены - запрашиваем время с сервера // если подключены - запрашиваем время с сервера
ntp.begin(); ntp.begin();
@@ -16,8 +16,7 @@ void timeTicker() {
updateTime(); // обновляем время updateTime(); // обновляем время
sendTimeToSlaves(); // отправляем время слейвам sendTimeToSlaves(); // отправляем время слейвам
trnd.update(now.hour, now.min, now.sec); // обновляем рандомайзер trnd.update(now.hour, now.min, now.sec); // обновляем рандомайзер
if (gotNTP) checkWorkTime(); // проверяем расписание, если подключены к Интернет if (gotNTP || gotTime) checkWorkTime(); // проверяем расписание, если знаем время
checkTurnoff(); // проверяем таймер отключения
} }
} }
@@ -26,68 +25,68 @@ void updateTime() {
now.sec = ntp.getSeconds(); now.sec = ntp.getSeconds();
now.min = ntp.getMinutes(); now.min = ntp.getMinutes();
now.hour = ntp.getHours(); now.hour = ntp.getHours();
now.day = ntp.getDay(); now.day = ntp.getDay(); // вс 0, сб 6
now.day = (now.day == 0) ? 6 : (now.day - 1); // перевод из вс0 в пн0
now.weekMs = now.getWeekS() * 1000ul + ntp.getMillis(); now.weekMs = now.getWeekS() * 1000ul + ntp.getMillis();
now.setMs(ntp.getMillis()); now.setMs(ntp.getMillis());
if (now.min % NTP_UPD_PRD == 0 && now.sec == 0) { if (now.min % NTP_UPD_PRD == 0 && now.sec == 0) {
// берём время с интернета каждую NTP_UPD_PRD минуту, ставим флаг что данные с NTP получены, значит мы онлайн // берём время с интернета каждую NTP_UPD_PRD минуту, ставим флаг что данные с NTP получены, значит мы онлайн
if (ntp.update() && !gotNTP) gotNTP = true; if (ntp.update() && !gotNTP) gotNTP = true;
} }
checkDawn();
} else { // если нет } else { // если нет
now.tick(); // тикаем своим счётчиком now.tick(); // тикаем своим счётчиком
} }
} }
void sendTimeToSlaves() { void sendTimeToSlaves() {
if (!cfg.WiFimode) { // если мы AP if (!cfg.WiFimode) { // если мы AP
static byte prevSec = 0; static byte prevSec = 0;
if (prevSec != now.sec) { // новая секунда if (prevSec != now.sec) { // новая секунда
prevSec = now.sec; prevSec = now.sec;
if (now.min % 1 == 0 && now.sec == 0) sendTime(); // ровно каждые 5 мин отправляем время if (now.min % 5 == 0 && now.sec == 0) sendTime(); // ровно каждые 5 мин отправляем время
} }
} }
} }
void checkTurnoff() { void checkDawn() {
if (turnoffTmr.isReady()) { if (now.sec == 0 && dawn.state[now.day] && !dawnTmr.running()) { // рассвет включен но не запущен
turnoffTmr.stop(); int dawnMinute = dawn.hour[now.day] * 60 + dawn.minute[now.day] - dawn.time;
setPower(0); if (dawnMinute < 0) dawnMinute += 1440;
if (dawnMinute == now.hour * 60 + now.min) {
DEBUGLN("dawn start");
dawnTmr.setInterval(dawn.time * 60000ul);
dawnTmr.restart();
}
} }
} }
void checkWorkTime() { void checkWorkTime() {
if (!isWorkTime(now.hour, cfg.workFrom, cfg.workTo)) { static byte prevState = 2; // для первого запуска
if (cfg.state) { byte curState = isWorkTime(now.hour, cfg.workFrom, cfg.workTo);
cfg.state = false; if (prevState != curState) { // переключение расписания
FastLED.clear(); prevState = curState;
FastLED.show(); if (curState && !cfg.state && !cfg.manualOff) setPower(1); // нужно включить, а лампа выключена и не выключалась вручную
} if (!curState && cfg.state) setPower(0); // нужно выключить, а лампа включена
} else {
if (!cfg.state && !cfg.manualOff) {
cfg.state = true;
}
} }
} }
void sendTime() { void sendTime() {
IPAddress ip = WiFi.localIP(); IPAddress ip = WiFi.localIP();
ip[3] = 255; ip[3] = 255;
char reply[20] = GL_KEY; char reply[25] = GL_KEY;
byte keylen = strlen(GL_KEY); mString packet(reply, sizeof(reply));
reply[keylen++] = ','; packet.clear();
reply[keylen++] = 0 + '0'; packet += GL_KEY;
reply[keylen++] = ','; packet += ',';
char hours[4]; packet += 0;
itoa(now.hour, hours, DEC); packet += ',';
strncpy(reply + keylen, hours, 3); packet += now.day;
keylen += strlen(hours); packet += ',';
reply[keylen++] = ','; packet += now.hour;
char mins[4]; packet += ',';
itoa(now.min, mins, DEC); packet += now.min;
strncpy(reply + keylen, mins, 3); packet += ',';
keylen += strlen(mins); packet += now.sec;
reply[keylen++] = NULL;
DEBUG("Sending time: "); DEBUG("Sending time: ");
DEBUGLN(reply); DEBUGLN(reply);

View File

@@ -12,7 +12,6 @@ class timerMillis {
} }
boolean isReady() { boolean isReady() {
if (_active && millis() - _tmr >= _interval) { if (_active && millis() - _tmr >= _interval) {
//_tmr += _interval;
reset(); reset();
return true; return true;
} }
@@ -28,6 +27,12 @@ class timerMillis {
void stop() { void stop() {
_active = false; _active = false;
} }
bool running() {
return _active;
}
byte getLength8() {
return (millis() - _tmr) * 255ul / _interval;
}
private: private:
uint32_t _tmr = 0; uint32_t _tmr = 0;