This commit is contained in:
Alex
2019-11-14 14:17:02 +03:00
parent 4ae5b205fc
commit 08c6eb6d7b
24 changed files with 1649 additions and 0 deletions

View File

@@ -0,0 +1,293 @@
/*
Скетч к проекту "Многофункциональный RGB светильник"
Страница проекта (схемы, описания): https://alexgyver.ru/GyverLamp/
Исходники на GitHub: https://github.com/AlexGyver/GyverLamp/
Нравится, как написан код? Поддержи автора! https://alexgyver.ru/support_alex/
Автор: AlexGyver, AlexGyver Technologies, 2019
https://AlexGyver.ru/
*/
/*
Версия 1.5.1
- Оптимизировано обращение к серверу времени (нет подвисаний при отсутствии интернета)
- Оптимизация под пины NodeMCU
Версия 1.5.2
- Исправлен незначительный баг с таймером
- Исправлено падение по WDT при выводе IP
- Исправлен баг с переназначением времени будильника
- Исправлено переключение с первого на последний режимы
- Приложение автоматически получает настройки с кнопки
- Бегущая строка с текущим временем во время рассвета
Версия 1.5.3
- Увеличена плавность рассвета
- Поправлен баг с отображением времени рассвета
Версия 1.5.4
- Поправлены глюки во время рассвета
Версия 1.5.5
- Поправлено невыключение света во время рассвета
*/
// Ссылка для менеджера плат:
// http://arduino.esp8266.com/stable/package_esp8266com_index.json
// Для WEMOS выбираем плату LOLIN(WEMOS) D1 R2 & mini
// Для NodeMCU выбираем NodeMCU 1.0 (ESP-12E Module)
// ============= НАСТРОЙКИ =============
// -------- КНОПКА -------
#define USE_BUTTON 1 // 1 - использовать кнопку, 0 - нет
// -------- ВРЕМЯ -------
#define GMT 3 // смещение (москва 3)
#define NTP_ADDRESS "europe.pool.ntp.org" // сервер времени
// -------- РАССВЕТ -------
#define DAWN_BRIGHT 200 // макс. яркость рассвета
#define DAWN_TIMEOUT 1 // сколько рассвет светит после времени будильника, минут
// ---------- МАТРИЦА ---------
#define BRIGHTNESS 40 // стандартная маскимальная яркость (0-255)
#define CURRENT_LIMIT 2000 // лимит по току в миллиамперах, автоматически управляет яркостью (пожалей свой блок питания!) 0 - выключить лимит
#define WIDTH 16 // ширина матрицы
#define HEIGHT 16 // высота матрицы
#define COLOR_ORDER GRB // порядок цветов на ленте. Если цвет отображается некорректно - меняйте. Начать можно с RGB
#define MATRIX_TYPE 0 // тип матрицы: 0 - зигзаг, 1 - параллельная
#define CONNECTION_ANGLE 0 // угол подключения: 0 - левый нижний, 1 - левый верхний, 2 - правый верхний, 3 - правый нижний
#define STRIP_DIRECTION 0 // направление ленты из угла: 0 - вправо, 1 - вверх, 2 - влево, 3 - вниз
// при неправильной настройке матрицы вы получите предупреждение "Wrong matrix parameters! Set to default"
// шпаргалка по настройке матрицы здесь! https://alexgyver.ru/matrix_guide/
// --------- ESP --------
#define ESP_MODE 1
// 0 - точка доступа
// 1 - локальный
byte IP_AP[] = {192, 168, 4, 66}; // статический IP точки доступа (менять только последнюю цифру)
// ----- AP (точка доступа) -------
#define AP_SSID "GyverLamp"
#define AP_PASS "12345678"
#define AP_PORT 8888
// -------- Менеджер WiFi ---------
#define AC_SSID "AutoConnectAP"
#define AC_PASS "12345678"
// ============= ДЛЯ РАЗРАБОТЧИКОВ =============
#define LED_PIN D4 // пин ленты
#define BTN_PIN D2
#define MODE_AMOUNT 18
#define NUM_LEDS WIDTH * HEIGHT
#define SEGMENTS 1 // диодов в одном "пикселе" (для создания матрицы из кусков ленты)
// ---------------- БИБЛИОТЕКИ -----------------
#define FASTLED_INTERRUPT_RETRY_COUNT 0
#define FASTLED_ALLOW_INTERRUPTS 0
#define FASTLED_ESP8266_RAW_PIN_ORDER
#define NTP_INTERVAL 60 * 1000 // обновление (1 минута)
#include "timer2Minim.h"
#include <FastLED.h>
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <WiFiUdp.h>
#include <EEPROM.h>
#include <NTPClient.h>
#include <GyverButton.h>
#include "fonts.h"
// ------------------- ТИПЫ --------------------
CRGB leds[NUM_LEDS];
WiFiServer server(80);
WiFiUDP Udp;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, NTP_ADDRESS, GMT * 3600, NTP_INTERVAL);
timerMinim timeTimer(1000);
timerMinim timeStrTimer(120);
GButton touch(BTN_PIN, LOW_PULL, NORM_OPEN);
// ----------------- ПЕРЕМЕННЫЕ ------------------
const char* autoConnectSSID = AC_SSID;
const char* autoConnectPass = AC_PASS;
const char AP_NameChar[] = AP_SSID;
const char WiFiPassword[] = AP_PASS;
unsigned int localPort = AP_PORT;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1]; //buffer to hold incoming packet
String inputBuffer;
static const byte maxDim = max(WIDTH, HEIGHT);
struct {
byte brightness = 50;
byte speed = 30;
byte scale = 40;
} modes[MODE_AMOUNT];
struct {
boolean state = false;
int time = 0;
} alarm[7];
const byte dawnOffsets[] = {5, 10, 15, 20, 25, 30, 40, 50, 60};
byte dawnMode;
boolean dawnFlag = false;
float thisTime;
boolean manualOff = false;
boolean sendSettings_flag = false;
int8_t currentMode = 0;
boolean loadingFlag = true;
boolean ONflag = true;
uint32_t eepromTimer;
boolean settChanged = false;
// Конфетти, Огонь, Радуга верт., Радуга гориз., Смена цвета,
// Безумие 3D, Облака 3D, Лава 3D, Плазма 3D, Радуга 3D,
// Павлин 3D, Зебра 3D, Лес 3D, Океан 3D,
unsigned char matrixValue[8][16];
String lampIP = "";
byte hrs, mins, secs;
byte days;
String timeStr = "00:00";
void setup() {
ESP.wdtDisable();
//ESP.wdtEnable(WDTO_8S);
// ЛЕНТА
FastLED.addLeds<WS2812B, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)/*.setCorrection( TypicalLEDStrip )*/;
FastLED.setBrightness(BRIGHTNESS);
if (CURRENT_LIMIT > 0) FastLED.setMaxPowerInVoltsAndMilliamps(5, CURRENT_LIMIT);
FastLED.show();
touch.setStepTimeout(100);
touch.setClickTimeout(500);
Serial.begin(115200);
Serial.println();
delay(1000);
// WI-FI
if (ESP_MODE == 0) { // режим точки доступа
WiFi.softAPConfig(IPAddress(IP_AP[0], IP_AP[1], IP_AP[2], IP_AP[3]),
IPAddress(192, 168, 4, 1),
IPAddress(255, 255, 255, 0));
WiFi.softAP(AP_NameChar, WiFiPassword);
IPAddress myIP = WiFi.softAPIP();
Serial.print("Access point Mode");
Serial.print("AP IP address: ");
Serial.println(myIP);
server.begin();
} else { // подключаемся к роутеру
Serial.print("WiFi manager");
WiFiManager wifiManager;
wifiManager.setDebugOutput(false);
#if (USE_BUTTON == 1)
if (digitalRead(BTN_PIN)) wifiManager.resetSettings();
#endif
wifiManager.autoConnect(autoConnectSSID, autoConnectPass);
/*WiFi.config(IPAddress(IP_STA[0], IP_STA[1], IP_STA[2], IP_STA[3]),
IPAddress(192, 168, 1, 1),
IPAddress(255, 255, 255, 0));*/
Serial.print("Connected! IP address: ");
Serial.println(WiFi.localIP());
lampIP = WiFi.localIP().toString();
}
Serial.printf("UDP server on port %d\n", localPort);
Udp.begin(localPort);
// EEPROM
EEPROM.begin(202);
delay(50);
if (EEPROM.read(198) != 20) { // первый запуск
EEPROM.write(198, 20);
EEPROM.commit();
for (byte i = 0; i < MODE_AMOUNT; i++) {
EEPROM.put(3 * i + 40, modes[i]);
EEPROM.commit();
}
for (byte i = 0; i < 7; i++) {
EEPROM.write(5 * i, alarm[i].state); // рассвет
eeWriteInt(5 * i + 1, alarm[i].time);
EEPROM.commit();
}
EEPROM.write(199, 0); // рассвет
EEPROM.write(200, 0); // режим
EEPROM.commit();
}
for (byte i = 0; i < MODE_AMOUNT; i++) {
EEPROM.get(3 * i + 40, modes[i]);
}
for (byte i = 0; i < 7; i++) {
alarm[i].state = EEPROM.read(5 * i);
alarm[i].time = eeGetInt(5 * i + 1);
}
dawnMode = EEPROM.read(199);
currentMode = (int8_t)EEPROM.read(200);
// отправляем настройки
sendSettings();
timeClient.begin();
memset(matrixValue, 0, sizeof(matrixValue));
randomSeed(micros());
// получаем время
byte count = 0;
while (count < 5) {
if (timeClient.update()) {
hrs = timeClient.getHours();
mins = timeClient.getMinutes();
secs = timeClient.getSeconds();
days = timeClient.getDay();
break;
}
count++;
delay(500);
}
updTime();
}
void loop() {
parseUDP();
effectsTick();
eepromTick();
timeTick();
#if (USE_BUTTON == 1)
buttonTick();
#endif
ESP.wdtFeed(); // пнуть собаку
yield(); // ещё раз пнуть собаку
}
void eeWriteInt(int pos, int val) {
byte* p = (byte*) &val;
EEPROM.write(pos, *p);
EEPROM.write(pos + 1, *(p + 1));
EEPROM.write(pos + 2, *(p + 2));
EEPROM.write(pos + 3, *(p + 3));
EEPROM.commit();
}
int eeGetInt(int pos) {
int val;
byte* p = (byte*) &val;
*p = EEPROM.read(pos);
*(p + 1) = EEPROM.read(pos + 1);
*(p + 2) = EEPROM.read(pos + 2);
*(p + 3) = EEPROM.read(pos + 3);
return val;
}

View File

@@ -0,0 +1,84 @@
byte minuteCounter = 0;
CHSV dawnColor;
void timeTick() {
if (ESP_MODE == 1) {
if (timeTimer.isReady()) { // каждую секунду
secs++;
if (secs == 60) {
secs = 0;
mins++;
minuteCounter++;
updTime();
}
if (mins == 60) {
mins = 0;
hrs++;
if (hrs == 24) {
hrs = 0;
days++;
if (days > 6) days = 0;
}
updTime();
}
if (minuteCounter > 30 && WiFi.status() == WL_CONNECTED) { // синхронизация каждые 30 минут
minuteCounter = 0;
if (timeClient.update()) {
hrs = timeClient.getHours();
mins = timeClient.getMinutes();
secs = timeClient.getSeconds();
days = timeClient.getDay();
}
}
if (secs % 3 == 0) checkDawn(); // каждые 3 секунды проверяем рассвет
}
}
if (dawnFlag && timeStrTimer.isReady()) {
fill_solid(leds, NUM_LEDS, dawnColor);
fillString(timeStr, CRGB::Black, false);
delay(1);
ESP.wdtFeed(); // пнуть собаку
yield(); // ещё раз пнуть собаку
FastLED.show();
}
}
void updTime() {
timeStr = String(hrs);
timeStr += ":";
timeStr += (mins < 10) ? "0" : "";
timeStr += String(mins);
}
void checkDawn() {
byte thisDay = days;
if (thisDay == 0) thisDay = 7; // воскресенье это 0
thisDay--;
thisTime = hrs * 60 + mins + (float)secs / 60;
// проверка рассвета
if (alarm[thisDay].state && // день будильника
thisTime >= (alarm[thisDay].time - dawnOffsets[dawnMode]) && // позже начала
thisTime < (alarm[thisDay].time + DAWN_TIMEOUT) ) { // раньше конца + минута
if (!manualOff) {
// величина рассвета 0-255
int dawnPosition = (float)255 * ((float)((float)thisTime - (alarm[thisDay].time - dawnOffsets[dawnMode])) / dawnOffsets[dawnMode]);
dawnPosition = constrain(dawnPosition, 0, 255);
dawnColor = CHSV(map(dawnPosition, 0, 255, 10, 35),
map(dawnPosition, 0, 255, 255, 170),
map(dawnPosition, 0, 255, 10, DAWN_BRIGHT));
FastLED.setBrightness(255);
dawnFlag = true;
}
} else {
if (dawnFlag) {
dawnFlag = false;
manualOff = false;
FastLED.setBrightness(modes[currentMode].brightness);
FastLED.clear();
FastLED.show();
}
}
}

View File

@@ -0,0 +1,75 @@
boolean brightDirection;
void buttonTick() {
touch.tick();
if (touch.isSingle()) {
if (dawnFlag) {
manualOff = true;
dawnFlag = false;
loadingFlag = true;
FastLED.setBrightness(modes[currentMode].brightness);
changePower();
} else {
if (ONflag) {
ONflag = false;
changePower();
} else {
ONflag = true;
changePower();
}
sendSettings_flag = true;
}
}
if (ONflag && touch.isDouble()) {
if (++currentMode >= MODE_AMOUNT) currentMode = 0;
FastLED.setBrightness(modes[currentMode].brightness);
loadingFlag = true;
settChanged = true;
eepromTimer = millis();
FastLED.clear();
delay(1);
sendSettings_flag = true;
}
if (ONflag && touch.isTriple()) {
if (--currentMode < 0) currentMode = MODE_AMOUNT - 1;
FastLED.setBrightness(modes[currentMode].brightness);
loadingFlag = true;
settChanged = true;
eepromTimer = millis();
FastLED.clear();
delay(1);
sendSettings_flag = true;
}
// вывод IP на лампу
if (ONflag && touch.hasClicks()) {
if (touch.getClicks() == 5) {
resetString();
while (!fillString(lampIP, CRGB::Green, true)) {
delay(1);
ESP.wdtFeed(); // пнуть собаку
yield(); // ещё раз пнуть собаку
}
}
}
if (ONflag && touch.isHolded()) {
brightDirection = !brightDirection;
}
if (ONflag && touch.isStep()) {
if (brightDirection) {
if (modes[currentMode].brightness < 10) modes[currentMode].brightness += 1;
else if (modes[currentMode].brightness < 250) modes[currentMode].brightness += 5;
else modes[currentMode].brightness = 255;
} else {
if (modes[currentMode].brightness > 15) modes[currentMode].brightness -= 5;
else if (modes[currentMode].brightness > 1) modes[currentMode].brightness -= 1;
else modes[currentMode].brightness = 1;
}
FastLED.setBrightness(modes[currentMode].brightness);
settChanged = true;
eepromTimer = millis();
sendSettings_flag = true;
}
}

View File

@@ -0,0 +1,25 @@
void saveEEPROM() {
EEPROM.put(3 * currentMode + 40, modes[currentMode]);
EEPROM.commit();
}
void eepromTick() {
if (settChanged && millis() - eepromTimer > 30000) {
settChanged = false;
eepromTimer = millis();
saveEEPROM();
if (EEPROM.read(200) != currentMode) EEPROM.write(200, currentMode);
EEPROM.commit();
}
}
void saveAlarm(byte almNumber) {
EEPROM.write(5 * almNumber, alarm[almNumber].state); // рассвет
eeWriteInt(5 * almNumber + 1, alarm[almNumber].time);
EEPROM.commit();
}
void saveDawnMmode() {
EEPROM.write(199, dawnMode); // рассвет
EEPROM.commit();
}

View File

@@ -0,0 +1,72 @@
uint32_t effTimer;
void effectsTick() {
if (!dawnFlag) {
if (ONflag && millis() - effTimer >= ((currentMode < 5 || currentMode > 13) ? modes[currentMode].speed : 50) ) {
effTimer = millis();
switch (currentMode) {
case 0: sparklesRoutine();
break;
case 1: fireRoutine();
break;
case 2: rainbowVertical();
break;
case 3: rainbowHorizontal();
break;
case 4: colorsRoutine();
break;
case 5: madnessNoise();
break;
case 6: cloudNoise();
break;
case 7: lavaNoise();
break;
case 8: plasmaNoise();
break;
case 9: rainbowNoise();
break;
case 10: rainbowStripeNoise();
break;
case 11: zebraNoise();
break;
case 12: forestNoise();
break;
case 13: oceanNoise();
break;
case 14: colorRoutine();
break;
case 15: snowRoutine();
break;
case 16: matrixRoutine();
break;
case 17: lightersRoutine();
break;
}
FastLED.show();
}
}
}
void changePower() {
if (ONflag) {
effectsTick();
for (int i = 0; i < modes[currentMode].brightness; i += 8) {
FastLED.setBrightness(i);
delay(1);
FastLED.show();
}
FastLED.setBrightness(modes[currentMode].brightness);
delay(2);
FastLED.show();
} else {
effectsTick();
for (int i = modes[currentMode].brightness; i > 8; i -= 8) {
FastLED.setBrightness(i);
delay(1);
FastLED.show();
}
FastLED.clear();
delay(2);
FastLED.show();
}
}

View File

@@ -0,0 +1,333 @@
// ================================= ЭФФЕКТЫ ====================================
// --------------------------------- конфетти ------------------------------------
void sparklesRoutine() {
for (byte i = 0; i < modes[0].scale; i++) {
byte x = random(0, WIDTH);
byte y = random(0, HEIGHT);
if (getPixColorXY(x, y) == 0)
leds[getPixelNumber(x, y)] = CHSV(random(0, 255), 255, 255);
}
fader(70);
}
// функция плавного угасания цвета для всех пикселей
void fader(byte step) {
for (byte i = 0; i < WIDTH; i++) {
for (byte j = 0; j < HEIGHT; j++) {
fadePixel(i, j, step);
}
}
}
void fadePixel(byte i, byte j, byte step) { // новый фейдер
int pixelNum = getPixelNumber(i, j);
if (getPixColor(pixelNum) == 0) return;
if (leds[pixelNum].r >= 30 ||
leds[pixelNum].g >= 30 ||
leds[pixelNum].b >= 30) {
leds[pixelNum].fadeToBlackBy(step);
} else {
leds[pixelNum] = 0;
}
}
// -------------------------------------- огонь ---------------------------------------------
// эффект "огонь"
#define SPARKLES 1 // вылетающие угольки вкл выкл
unsigned char line[WIDTH];
int pcnt = 0;
//these values are substracetd from the generated values to give a shape to the animation
const unsigned char valueMask[8][16] PROGMEM = {
{32 , 0 , 0 , 0 , 0 , 0 , 0 , 32 , 32 , 0 , 0 , 0 , 0 , 0 , 0 , 32 },
{64 , 0 , 0 , 0 , 0 , 0 , 0 , 64 , 64 , 0 , 0 , 0 , 0 , 0 , 0 , 64 },
{96 , 32 , 0 , 0 , 0 , 0 , 32 , 96 , 96 , 32 , 0 , 0 , 0 , 0 , 32 , 96 },
{128, 64 , 32 , 0 , 0 , 32 , 64 , 128, 128, 64 , 32 , 0 , 0 , 32 , 64 , 128},
{160, 96 , 64 , 32 , 32 , 64 , 96 , 160, 160, 96 , 64 , 32 , 32 , 64 , 96 , 160},
{192, 128, 96 , 64 , 64 , 96 , 128, 192, 192, 128, 96 , 64 , 64 , 96 , 128, 192},
{255, 160, 128, 96 , 96 , 128, 160, 255, 255, 160, 128, 96 , 96 , 128, 160, 255},
{255, 192, 160, 128, 128, 160, 192, 255, 255, 192, 160, 128, 128, 160, 192, 255}
};
//these are the hues for the fire,
//should be between 0 (red) to about 25 (yellow)
const unsigned char hueMask[8][16] PROGMEM = {
{1 , 11, 19, 25, 25, 22, 11, 1 , 1 , 11, 19, 25, 25, 22, 11, 1 },
{1 , 8 , 13, 19, 25, 19, 8 , 1 , 1 , 8 , 13, 19, 25, 19, 8 , 1 },
{1 , 8 , 13, 16, 19, 16, 8 , 1 , 1 , 8 , 13, 16, 19, 16, 8 , 1 },
{1 , 5 , 11, 13, 13, 13, 5 , 1 , 1 , 5 , 11, 13, 13, 13, 5 , 1 },
{1 , 5 , 11, 11, 11, 11, 5 , 1 , 1 , 5 , 11, 11, 11, 11, 5 , 1 },
{0 , 1 , 5 , 8 , 8 , 5 , 1 , 0 , 0 , 1 , 5 , 8 , 8 , 5 , 1 , 0 },
{0 , 0 , 1 , 5 , 5 , 1 , 0 , 0 , 0 , 0 , 1 , 5 , 5 , 1 , 0 , 0 },
{0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 }
};
void fireRoutine() {
if (loadingFlag) {
loadingFlag = false;
//FastLED.clear();
generateLine();
}
if (pcnt >= 100) {
shiftUp();
generateLine();
pcnt = 0;
}
drawFrame(pcnt);
pcnt += 30;
}
// Randomly generate the next line (matrix row)
void generateLine() {
for (uint8_t x = 0; x < WIDTH; x++) {
line[x] = random(64, 255);
}
}
void shiftUp() {
for (uint8_t y = HEIGHT - 1; y > 0; y--) {
for (uint8_t x = 0; x < WIDTH; x++) {
uint8_t newX = x;
if (x > 15) newX = x - 15;
if (y > 7) continue;
matrixValue[y][newX] = matrixValue[y - 1][newX];
}
}
for (uint8_t x = 0; x < WIDTH; x++) {
uint8_t newX = x;
if (x > 15) newX = x - 15;
matrixValue[0][newX] = line[newX];
}
}
// draw a frame, interpolating between 2 "key frames"
// @param pcnt percentage of interpolation
void drawFrame(int pcnt) {
int nextv;
//each row interpolates with the one before it
for (unsigned char y = HEIGHT - 1; y > 0; y--) {
for (unsigned char x = 0; x < WIDTH; x++) {
uint8_t newX = x;
if (x > 15) newX = x - 15;
if (y < 8) {
nextv =
(((100.0 - pcnt) * matrixValue[y][newX]
+ pcnt * matrixValue[y - 1][newX]) / 100.0)
- pgm_read_byte(&(valueMask[y][newX]));
CRGB color = CHSV(
modes[1].scale * 2.5 + pgm_read_byte(&(hueMask[y][newX])), // H
255, // S
(uint8_t)max(0, nextv) // V
);
leds[getPixelNumber(x, y)] = color;
} else if (y == 8 && SPARKLES) {
if (random(0, 20) == 0 && getPixColorXY(x, y - 1) != 0) drawPixelXY(x, y, getPixColorXY(x, y - 1));
else drawPixelXY(x, y, 0);
} else if (SPARKLES) {
// старая версия для яркости
if (getPixColorXY(x, y - 1) > 0)
drawPixelXY(x, y, getPixColorXY(x, y - 1));
else drawPixelXY(x, y, 0);
}
}
}
//first row interpolates with the "next" line
for (unsigned char x = 0; x < WIDTH; x++) {
uint8_t newX = x;
if (x > 15) newX = x - 15;
CRGB color = CHSV(
modes[1].scale * 2.5 + pgm_read_byte(&(hueMask[0][newX])), // H
255, // S
(uint8_t)(((100.0 - pcnt) * matrixValue[0][newX] + pcnt * line[newX]) / 100.0) // V
);
leds[getPixelNumber(newX, 0)] = color;
}
}
byte hue;
// ---------------------------------------- радуга ------------------------------------------
void rainbowVertical() {
hue += 2;
for (byte j = 0; j < HEIGHT; j++) {
CHSV thisColor = CHSV((byte)(hue + j * modes[2].scale), 255, 255);
for (byte i = 0; i < WIDTH; i++)
drawPixelXY(i, j, thisColor);
}
}
void rainbowHorizontal() {
hue += 2;
for (byte i = 0; i < WIDTH; i++) {
CHSV thisColor = CHSV((byte)(hue + i * modes[3].scale), 255, 255);
for (byte j = 0; j < HEIGHT; j++)
drawPixelXY(i, j, thisColor); //leds[getPixelNumber(i, j)] = thisColor;
}
}
// ---------------------------------------- ЦВЕТА ------------------------------------------
void colorsRoutine() {
hue += modes[4].scale;
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CHSV(hue, 255, 255);
}
}
// --------------------------------- ЦВЕТ ------------------------------------
void colorRoutine() {
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CHSV(modes[14].scale * 2.5, 255, 255);
}
}
// ------------------------------ снегопад 2.0 --------------------------------
void snowRoutine() {
// сдвигаем всё вниз
for (byte x = 0; x < WIDTH; x++) {
for (byte y = 0; y < HEIGHT - 1; y++) {
drawPixelXY(x, y, getPixColorXY(x, y + 1));
}
}
for (byte x = 0; x < WIDTH; x++) {
// заполняем случайно верхнюю строку
// а также не даём двум блокам по вертикали вместе быть
if (getPixColorXY(x, HEIGHT - 2) == 0 && (random(0, modes[15].scale) == 0))
drawPixelXY(x, HEIGHT - 1, 0xE0FFFF - 0x101010 * random(0, 4));
else
drawPixelXY(x, HEIGHT - 1, 0x000000);
}
}
// ------------------------------ МАТРИЦА ------------------------------
void matrixRoutine() {
for (byte x = 0; x < WIDTH; x++) {
// заполняем случайно верхнюю строку
uint32_t thisColor = getPixColorXY(x, HEIGHT - 1);
if (thisColor == 0)
drawPixelXY(x, HEIGHT - 1, 0x00FF00 * (random(0, modes[16].scale) == 0));
else if (thisColor < 0x002000)
drawPixelXY(x, HEIGHT - 1, 0);
else
drawPixelXY(x, HEIGHT - 1, thisColor - 0x002000);
}
// сдвигаем всё вниз
for (byte x = 0; x < WIDTH; x++) {
for (byte y = 0; y < HEIGHT - 1; y++) {
drawPixelXY(x, y, getPixColorXY(x, y + 1));
}
}
}
// ----------------------------- СВЕТЛЯКИ ------------------------------
#define LIGHTERS_AM 100
int lightersPos[2][LIGHTERS_AM];
int8_t lightersSpeed[2][LIGHTERS_AM];
CHSV lightersColor[LIGHTERS_AM];
byte loopCounter;
int angle[LIGHTERS_AM];
int speedV[LIGHTERS_AM];
int8_t angleSpeed[LIGHTERS_AM];
void lightersRoutine() {
if (loadingFlag) {
loadingFlag = false;
randomSeed(millis());
for (byte i = 0; i < LIGHTERS_AM; i++) {
lightersPos[0][i] = random(0, WIDTH * 10);
lightersPos[1][i] = random(0, HEIGHT * 10);
lightersSpeed[0][i] = random(-10, 10);
lightersSpeed[1][i] = random(-10, 10);
lightersColor[i] = CHSV(random(0, 255), 255, 255);
}
}
FastLED.clear();
if (++loopCounter > 20) loopCounter = 0;
for (byte i = 0; i < modes[17].scale; i++) {
if (loopCounter == 0) { // меняем скорость каждые 255 отрисовок
lightersSpeed[0][i] += random(-3, 4);
lightersSpeed[1][i] += random(-3, 4);
lightersSpeed[0][i] = constrain(lightersSpeed[0][i], -20, 20);
lightersSpeed[1][i] = constrain(lightersSpeed[1][i], -20, 20);
}
lightersPos[0][i] += lightersSpeed[0][i];
lightersPos[1][i] += lightersSpeed[1][i];
if (lightersPos[0][i] < 0) lightersPos[0][i] = (WIDTH - 1) * 10;
if (lightersPos[0][i] >= WIDTH * 10) lightersPos[0][i] = 0;
if (lightersPos[1][i] < 0) {
lightersPos[1][i] = 0;
lightersSpeed[1][i] = -lightersSpeed[1][i];
}
if (lightersPos[1][i] >= (HEIGHT - 1) * 10) {
lightersPos[1][i] = (HEIGHT - 1) * 10;
lightersSpeed[1][i] = -lightersSpeed[1][i];
}
drawPixelXY(lightersPos[0][i] / 10, lightersPos[1][i] / 10, lightersColor[i]);
}
}
/*
void lightersRoutine() {
if (loadingFlag) {
loadingFlag = false;
randomSeed(millis());
for (byte i = 0; i < LIGHTERS_AM; i++) {
lightersPos[0][i] = random(0, WIDTH * 10);
lightersPos[1][i] = random(0, HEIGHT * 10);
lightersColor[i] = CHSV(random(0, 255), 255, 255);
speedV[i] = random(5, 10);
angle[i] = random(0, 360);
angleSpeed[i] = random(-10, 10);
}
}
FastLED.clear();
if (++loopCounter > 20) loopCounter = 0;
for (byte i = 0; i < modes[17].scale; i++) {
if (loopCounter == 0) { // меняем скорость каждые 255 отрисовок
angleSpeed[i] += random(-3, 4);
angleSpeed[i] = constrain(angleSpeed[i], -15, 15);
}
lightersPos[0][i] += speedV[i] * cos(radians(angle[i]));
lightersPos[1][i] += speedV[i] * sin(radians(angle[i]));
if (lightersPos[0][i] < 0) lightersPos[0][i] = (WIDTH - 1) * 10;
if (lightersPos[0][i] >= WIDTH * 10) lightersPos[0][i] = 0;
if (lightersPos[1][i] < 0) {
lightersPos[1][i] = 0;
angle[i] = 360 - angle[i];
} else {
angle[i] += angleSpeed[i];
}
if (lightersPos[1][i] >= (HEIGHT - 1) * 10) {
lightersPos[1][i] = (HEIGHT - 1) * 10;
angle[i] = 360 - angle[i];
} else {
angle[i] += angleSpeed[i];
}
if (angle[i] > 360) angle[i] = 360 - angle[i];
if (angle[i] < 0) angle[i] = 360 + angle[i];
drawPixelXY(lightersPos[0][i] / 10, lightersPos[1][i] / 10, lightersColor[i]);
}
}
*/

View File

@@ -0,0 +1,231 @@
// шрифты для вывода текста
const uint8_t fontHEX[][5] PROGMEM = {
{0x00, 0x00, 0x00, 0x00, 0x00}, // 0x20 32
{0x00, 0x00, 0x6f, 0x00, 0x00}, // ! 0x21 33
{0x00, 0x07, 0x00, 0x07, 0x00}, // " 0x22 34
{0x14, 0x7f, 0x14, 0x7f, 0x14}, // # 0x23 35
{0x00, 0x07, 0x04, 0x1e, 0x00}, // $ 0x24 36
{0x23, 0x13, 0x08, 0x64, 0x62}, // % 0x25 37
{0x36, 0x49, 0x56, 0x20, 0x50}, // & 0x26 38
{0x00, 0x00, 0x07, 0x00, 0x00}, // ' 0x27 39
{0x00, 0x1c, 0x22, 0x41, 0x00}, // ( 0x28 40
{0x00, 0x41, 0x22, 0x1c, 0x00}, // ) 0x29 41
{0x14, 0x08, 0x3e, 0x08, 0x14}, // * 0x2a 42
{0x08, 0x08, 0x3e, 0x08, 0x08}, // + 0x2b 43
{0x00, 0x50, 0x30, 0x00, 0x00}, // , 0x2c 44
{0x08, 0x08, 0x08, 0x08, 0x08}, // - 0x2d 45
{0x00, 0x60, 0x60, 0x00, 0x00}, // . 0x2e 46
{0x20, 0x10, 0x08, 0x04, 0x02}, // / 0x2f 47
{0x3e, 0x51, 0x49, 0x45, 0x3e}, // 0 0x30 48
{0x00, 0x42, 0x7f, 0x40, 0x00}, // 1 0x31 49
{0x42, 0x61, 0x51, 0x49, 0x46}, // 2 0x32 50
{0x21, 0x41, 0x45, 0x4b, 0x31}, // 3 0x33 51
{0x18, 0x14, 0x12, 0x7f, 0x10}, // 4 0x34 52
{0x27, 0x45, 0x45, 0x45, 0x39}, // 5 0x35 53
{0x3c, 0x4a, 0x49, 0x49, 0x30}, // 6 0x36 54
{0x01, 0x71, 0x09, 0x05, 0x03}, // 7 0x37 55
{0x36, 0x49, 0x49, 0x49, 0x36}, // 8 0x38 56
{0x06, 0x49, 0x49, 0x29, 0x1e}, // 9 0x39 57
{0x00, 0x36, 0x36, 0x00, 0x00}, // : 0x3a 58
{0x00, 0x56, 0x36, 0x00, 0x00}, // ; 0x3b 59
{0x08, 0x14, 0x22, 0x41, 0x00}, // < 0x3c 60
{0x14, 0x14, 0x14, 0x14, 0x14}, // = 0x3d 61
{0x00, 0x41, 0x22, 0x14, 0x08}, // > 0x3e 62
{0x02, 0x01, 0x51, 0x09, 0x06}, // ? 0x3f 63
{0x3e, 0x41, 0x5d, 0x49, 0x4e}, // @ 0x40 64
{0x7e, 0x09, 0x09, 0x09, 0x7e}, // A 0x41 65
{0x7f, 0x49, 0x49, 0x49, 0x36}, // B 0x42 66
{0x3e, 0x41, 0x41, 0x41, 0x22}, // C 0x43 67
{0x7f, 0x41, 0x41, 0x41, 0x3e}, // D 0x44 68
{0x7f, 0x49, 0x49, 0x49, 0x41}, // E 0x45 69
{0x7f, 0x09, 0x09, 0x09, 0x01}, // F 0x46 70
{0x3e, 0x41, 0x49, 0x49, 0x7a}, // G 0x47 71
{0x7f, 0x08, 0x08, 0x08, 0x7f}, // H 0x48 72
{0x00, 0x41, 0x7f, 0x41, 0x00}, // I 0x49 73
{0x20, 0x40, 0x41, 0x3f, 0x01}, // J 0x4a 74
{0x7f, 0x08, 0x14, 0x22, 0x41}, // K 0x4b 75
{0x7f, 0x40, 0x40, 0x40, 0x40}, // L 0x4c 76
{0x7f, 0x02, 0x0c, 0x02, 0x7f}, // M 0x4d 77
{0x7f, 0x04, 0x08, 0x10, 0x7f}, // N 0x4e 78
{0x3e, 0x41, 0x41, 0x41, 0x3e}, // O 0x4f 79
{0x7f, 0x09, 0x09, 0x09, 0x06}, // P 0x50 80
{0x3e, 0x41, 0x51, 0x21, 0x5e}, // Q 0x51 81
{0x7f, 0x09, 0x19, 0x29, 0x46}, // R 0x52 82
{0x46, 0x49, 0x49, 0x49, 0x31}, // S 0x53 83
{0x01, 0x01, 0x7f, 0x01, 0x01}, // T 0x54 84
{0x3f, 0x40, 0x40, 0x40, 0x3f}, // U 0x55 85
{0x0f, 0x30, 0x40, 0x30, 0x0f}, // V 0x56 86
{0x3f, 0x40, 0x30, 0x40, 0x3f}, // W 0x57 87
{0x63, 0x14, 0x08, 0x14, 0x63}, // X 0x58 88
{0x07, 0x08, 0x70, 0x08, 0x07}, // Y 0x59 89
{0x61, 0x51, 0x49, 0x45, 0x43}, // Z 0x5a 90
{0x3c, 0x4a, 0x49, 0x29, 0x1e}, // [ 0x5b 91
{0x02, 0x04, 0x08, 0x10, 0x20}, // \ 0x5c 92
{0x00, 0x41, 0x7f, 0x00, 0x00}, // ] 0x5d 93
{0x04, 0x02, 0x01, 0x02, 0x04}, // ^ 0x5e 94
{0x40, 0x40, 0x40, 0x40, 0x40}, // _ 0x5f 95
{0x00, 0x00, 0x03, 0x04, 0x00}, // ` 0x60 96
{0x20, 0x54, 0x54, 0x54, 0x78}, // a 0x61 97
{0x7f, 0x48, 0x44, 0x44, 0x38}, // b 0x62 98
{0x38, 0x44, 0x44, 0x44, 0x20}, // c 0x63 99
{0x38, 0x44, 0x44, 0x48, 0x7f}, // d 0x64 100
{0x38, 0x54, 0x54, 0x54, 0x18}, // e 0x65 101
{0x08, 0x7e, 0x09, 0x01, 0x02}, // f 0x66 102
{0x0c, 0x52, 0x52, 0x52, 0x3e}, // g 0x67 103
{0x7f, 0x08, 0x04, 0x04, 0x78}, // h 0x68 104
{0x00, 0x44, 0x7d, 0x40, 0x00}, // i 0x69 105
{0x20, 0x40, 0x44, 0x3d, 0x00}, // j 0x6a 106
{0x00, 0x7f, 0x10, 0x28, 0x44}, // k 0x6b 107
{0x00, 0x41, 0x7f, 0x40, 0x00}, // l 0x6c 108
{0x7c, 0x04, 0x18, 0x04, 0x78}, // m 0x6d 109
{0x7c, 0x08, 0x04, 0x04, 0x78}, // n 0x6e 110
{0x38, 0x44, 0x44, 0x44, 0x38}, // o 0x6f 111
{0x7c, 0x14, 0x14, 0x14, 0x08}, // p 0x70 112
{0x08, 0x14, 0x14, 0x18, 0x7c}, // q 0x71 113
{0x7c, 0x08, 0x04, 0x04, 0x08}, // r 0x72 114
{0x48, 0x54, 0x54, 0x54, 0x20}, // s 0x73 115
{0x04, 0x3f, 0x44, 0x40, 0x20}, // t 0x74 116
{0x3c, 0x40, 0x40, 0x20, 0x7c}, // u 0x75 117
{0x1c, 0x20, 0x40, 0x20, 0x1c}, // v 0x76 118
{0x3c, 0x40, 0x30, 0x40, 0x3c}, // w 0x77 119
{0x44, 0x28, 0x10, 0x28, 0x44}, // x 0x78 120
{0x0c, 0x50, 0x50, 0x50, 0x3c}, // y 0x79 121
{0x44, 0x64, 0x54, 0x4c, 0x44}, // z 0x7a 122
{0x00, 0x08, 0x36, 0x41, 0x41}, // { 0x7b 123
{0x00, 0x00, 0x7f, 0x00, 0x00}, // | 0x7c 124
{0x41, 0x41, 0x36, 0x08, 0x00}, // } 0x7d 125
{0x04, 0x02, 0x04, 0x08, 0x04}, // ~ 0x7e 126
{0x7E, 0x11, 0x11, 0x11, 0x7E}, //__А (0xC0).
{0x7F, 0x49, 0x49, 0x49, 0x33}, //__Б (0xC1).
{0x7F, 0x49, 0x49, 0x49, 0x36}, //__В (0xC2).
{0x7F, 0x01, 0x01, 0x01, 0x03}, //__Г (0xC3).
{0xE0, 0x51, 0x4F, 0x41, 0xFF}, //__Д (0xC4).
{0x7F, 0x49, 0x49, 0x49, 0x41}, //__Е (0xC5).
{0x77, 0x08, 0x7F, 0x08, 0x77}, //__Ж (0xC6).
{0x41, 0x49, 0x49, 0x49, 0x36}, //__З (0xC7).
{0x7F, 0x10, 0x08, 0x04, 0x7F}, //__И (0xC8).
{0x7C, 0x21, 0x12, 0x09, 0x7C}, //__Й (0xC9).
{0x7F, 0x08, 0x14, 0x22, 0x41}, //__К (0xCA).
{0x20, 0x41, 0x3F, 0x01, 0x7F}, //__Л (0xCB).
{0x7F, 0x02, 0x0C, 0x02, 0x7F}, //__М (0xCC).
{0x7F, 0x08, 0x08, 0x08, 0x7F}, //__Н (0xCD).
{0x3E, 0x41, 0x41, 0x41, 0x3E}, //__О (0xCE).
{0x7F, 0x01, 0x01, 0x01, 0x7F}, //__П (0xCF).
{0x7F, 0x09, 0x09, 0x09, 0x06}, //__Р (0xD0).
{0x3E, 0x41, 0x41, 0x41, 0x22}, //__С (0xD1).
{0x01, 0x01, 0x7F, 0x01, 0x01}, //__Т (0xD2).
{0x47, 0x28, 0x10, 0x08, 0x07}, //__У (0xD3).
{0x1C, 0x22, 0x7F, 0x22, 0x1C}, //__Ф (0xD4).
{0x63, 0x14, 0x08, 0x14, 0x63}, //__Х (0xD5).
{0x7F, 0x40, 0x40, 0x40, 0xFF}, //__Ц (0xD6).
{0x07, 0x08, 0x08, 0x08, 0x7F}, //__Ч (0xD7).
{0x7F, 0x40, 0x7F, 0x40, 0x7F}, //__Ш (0xD8).
{0x7F, 0x40, 0x7F, 0x40, 0xFF}, //__Щ (0xD9).
{0x01, 0x7F, 0x48, 0x48, 0x30}, //__Ъ (0xDA).
{0x7F, 0x48, 0x30, 0x00, 0x7F}, //__Ы (0xDB).
{0x00, 0x7F, 0x48, 0x48, 0x30}, //__Ь (0xDC).
{0x22, 0x41, 0x49, 0x49, 0x3E}, //__Э (0xDD).
{0x7F, 0x08, 0x3E, 0x41, 0x3E}, //__Ю (0xDE).
{0x46, 0x29, 0x19, 0x09, 0x7F}, //__Я (0xDF).
{0x20, 0x54, 0x54, 0x54, 0x78}, //__а (0xE0).
{0x3C, 0x4A, 0x4A, 0x49, 0x31}, //__б (0xE1).
{0x7C, 0x54, 0x54, 0x28, 0x00}, //__в (0xE2).
{0x7C, 0x04, 0x04, 0x0C, 0x00}, //__г (0xE3).
{0xE0, 0x54, 0x4C, 0x44, 0xFC}, //__д (0xE4).
{0x38, 0x54, 0x54, 0x54, 0x18}, //__е (0xE5).
{0x6C, 0x10, 0x7C, 0x10, 0x6C}, //__ж (0xE6).
{0x44, 0x54, 0x54, 0x28, 0x00}, //__з (0xE7).
{0x7C, 0x20, 0x10, 0x08, 0x7C}, //__и (0xE8).
{0x78, 0x42, 0x24, 0x12, 0x78}, //__й (0xE9).
{0x7C, 0x10, 0x28, 0x44, 0x00}, //__к (0xEA).
{0x20, 0x44, 0x3C, 0x04, 0x7C}, //__л (0xEB).
{0x7C, 0x08, 0x10, 0x08, 0x7C}, //__м (0xEC).
{0x7C, 0x10, 0x10, 0x10, 0x7C}, //__н (0xED).
{0x38, 0x44, 0x44, 0x44, 0x38}, //__о (0xEE).
{0x7C, 0x04, 0x04, 0x04, 0x7C}, //__п (0xEF).
{0x7C, 0x14, 0x14, 0x14, 0x08}, //__р (0xF0).
{0x38, 0x44, 0x44, 0x44, 0x00}, //__с (0xF1).
{0x04, 0x04, 0x7C, 0x04, 0x04}, //__т (0xF2).
{0x0C, 0x50, 0x50, 0x50, 0x3C}, //__у (0xF3).
{0x30, 0x48, 0xFE, 0x48, 0x30}, //__ф (0xF4).
{0x44, 0x28, 0x10, 0x28, 0x44}, //__х (0xF5).
{0x7C, 0x40, 0x40, 0x7C, 0xC0}, //__ц (0xF6).
{0x0C, 0x10, 0x10, 0x10, 0x7C}, //__ч (0xF7).
{0x7C, 0x40, 0x7C, 0x40, 0x7C}, //__ш (0xF8).
{0x7C, 0x40, 0x7C, 0x40, 0xFC}, //__щ (0xF9).
{0x04, 0x7C, 0x50, 0x50, 0x20}, //__ъ (0xFA).
{0x7C, 0x50, 0x50, 0x20, 0x7C}, //__ы (0xFB).
{0x7C, 0x50, 0x50, 0x20, 0x00}, //__ь (0xFC).
{0x28, 0x44, 0x54, 0x54, 0x38}, //__э (0xFD).
{0x7C, 0x10, 0x38, 0x44, 0x38}, //__ю (0xFE).
{0x08, 0x54, 0x34, 0x14, 0x7C}, //__я (0xFF).
/*
{0x7e, 0x09, 0x09, 0x09, 0x7e}, // А 192
{0x7F, 0x49, 0x49, 0x49, 0x71}, // Б
{0x7f, 0x49, 0x49, 0x49, 0x36}, // В
{0x7F, 0x01, 0x01, 0x01, 0x01}, // Г
{0x60, 0x3E, 0x21, 0x3F, 0x60}, // Д
{0x7f, 0x49, 0x49, 0x49, 0x41}, // Е
{0x76, 0x08, 0x7F, 0x08, 0x76}, // Ж
{0x21, 0x41, 0x45, 0x4b, 0x31}, // З
{0x7F, 0x20, 0x10, 0x08, 0x7F}, // И
{0x7E, 0x20, 0x11, 0x08, 0x7E}, // Й
{0x7f, 0x08, 0x14, 0x22, 0x41}, // К
{0x70, 0x0E, 0x01, 0x01, 0x7F}, // Л
{0x7f, 0x02, 0x0c, 0x02, 0x7f}, // М
{0x7f, 0x08, 0x08, 0x08, 0x7f}, // Н
{0x3e, 0x41, 0x41, 0x41, 0x3e}, // О
{0x7F, 0x01, 0x01, 0x01, 0x7F}, // П
{0x7f, 0x09, 0x09, 0x09, 0x06}, // Р
{0x3e, 0x41, 0x41, 0x41, 0x22}, // С
{0x01, 0x01, 0x7f, 0x01, 0x01}, // Т
{0x07, 0x48, 0x48, 0x48, 0x7F}, // У
{0x1C, 0x22, 0x7F, 0x22, 0x1C}, // Ф
{0x63, 0x14, 0x08, 0x14, 0x63}, // Х
{0x7F, 0x40, 0x40, 0x7F, 0xC0}, // Ц
{0x07, 0x08, 0x08, 0x08, 0x7F}, // Ч
{0x7F, 0x40, 0x7F, 0x40, 0x7F}, // Ш
{0x7F, 0x40, 0x7F, 0x40, 0xFF}, // Щ
{0x01, 0x7F, 0x48, 0x48, 0x70}, // Ъ
{0x7F, 0x48, 0x70, 0x00, 0x7F}, // Ы
{0x00, 0x7F, 0x48, 0x48, 0x70}, // Ь
{0x22, 0x41, 0x49, 0x49, 0x3E}, // Э
{0x7F, 0x08, 0x3E, 0x41, 0x3E}, // Ю
{0x46, 0x29, 0x19, 0x09, 0x7F}, // Я 223
{0x20, 0x54, 0x54, 0x54, 0x78}, //a 224
{0x3c, 0x4a, 0x4a, 0x49, 0x31}, //б
{0x7c, 0x54, 0x54, 0x28, 0x00}, //в
{0x7c, 0x04, 0x04, 0x04, 0x0c}, //г
{0xe0, 0x54, 0x4c, 0x44, 0xfc}, //д
{0x38, 0x54, 0x54, 0x54, 0x18}, //e
{0x6c, 0x10, 0x7c, 0x10, 0x6c}, //ж
{0x44, 0x44, 0x54, 0x54, 0x28}, //з
{0x7c, 0x20, 0x10, 0x08, 0x7c}, //и
{0x7c, 0x41, 0x22, 0x11, 0x7c}, //й
{0x7c, 0x10, 0x28, 0x44, 0x00}, //к
{0x20, 0x44, 0x3c, 0x04, 0x7c}, //л
{0x7c, 0x08, 0x10, 0x08, 0x7c}, //м
{0x7c, 0x10, 0x10, 0x10, 0x7c}, //н
{0x38, 0x44, 0x44, 0x44, 0x38}, //o
{0x7c, 0x04, 0x04, 0x04, 0x7c}, //п
{0x7C, 0x14, 0x14, 0x14, 0x08}, //p
{0x38, 0x44, 0x44, 0x44, 0x20}, //c
{0x04, 0x04, 0x7c, 0x04, 0x04}, //т
{0x0C, 0x50, 0x50, 0x50, 0x3C}, //у
{0x30, 0x48, 0xfc, 0x48, 0x30}, //ф
{0x44, 0x28, 0x10, 0x28, 0x44}, //x
{0x7c, 0x40, 0x40, 0x40, 0xfc}, //ц
{0x0c, 0x10, 0x10, 0x10, 0x7c}, //ч
{0x7c, 0x40, 0x7c, 0x40, 0x7c}, //ш
{0x7c, 0x40, 0x7c, 0x40, 0xfc}, //щ
{0x04, 0x7c, 0x50, 0x50, 0x20}, //ъ
{0x7c, 0x50, 0x50, 0x20, 0x7c}, //ы
{0x7c, 0x50, 0x50, 0x20, 0x00}, //ь
{0x28, 0x44, 0x54, 0x54, 0x38}, //э
{0x7c, 0x10, 0x38, 0x44, 0x38}, //ю
{0x08, 0x54, 0x34, 0x14, 0x7c}, //я 255
*/
};

View File

@@ -0,0 +1,200 @@
// ******************* НАСТРОЙКИ *****************
// "масштаб" эффектов. Чем меньше, тем крупнее!
#define MADNESS_SCALE 100
#define CLOUD_SCALE 30
#define LAVA_SCALE 50
#define PLASMA_SCALE 30
#define RAINBOW_SCALE 30
#define RAINBOW_S_SCALE 20
#define ZEBRA_SCALE 30
#define FOREST_SCALE 120
#define OCEAN_SCALE 90
// ***************** ДЛЯ РАЗРАБОТЧИКОВ ******************
// The 16 bit version of our coordinates
static uint16_t x;
static uint16_t y;
static uint16_t z;
uint16_t speed = 20; // speed is set dynamically once we've started up
uint16_t scale = 30; // scale is set dynamically once we've started up
// This is the array that we keep our computed noise values in
#define MAX_DIMENSION (max(WIDTH, HEIGHT))
#if (WIDTH > HEIGHT)
uint8_t noise[WIDTH][WIDTH];
#else
uint8_t noise[HEIGHT][HEIGHT];
#endif
CRGBPalette16 currentPalette( PartyColors_p );
uint8_t colorLoop = 1;
uint8_t ihue = 0;
void madnessNoise() {
if (loadingFlag) {
loadingFlag = false;
scale = modes[5].scale;
speed = modes[5].speed;
}
fillnoise8();
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
CRGB thisColor = CHSV(noise[j][i], 255, noise[i][j]);
drawPixelXY(i, j, thisColor); //leds[getPixelNumber(i, j)] = CHSV(noise[j][i], 255, noise[i][j]);
}
}
ihue += 1;
}
void rainbowNoise() {
if (loadingFlag) {
loadingFlag = false;
currentPalette = RainbowColors_p;
scale = modes[9].scale;
speed = modes[9].speed;
colorLoop = 1;
}
fillNoiseLED();
}
void rainbowStripeNoise() {
if (loadingFlag) {
loadingFlag = false;
currentPalette = RainbowStripeColors_p;
scale = modes[10].scale;
speed = modes[10].speed;
colorLoop = 1;
}
fillNoiseLED();
}
void zebraNoise() {
if (loadingFlag) {
loadingFlag = false;
// 'black out' all 16 palette entries...
fill_solid( currentPalette, 16, CRGB::Black);
// and set every fourth one to white.
currentPalette[0] = CRGB::White;
currentPalette[4] = CRGB::White;
currentPalette[8] = CRGB::White;
currentPalette[12] = CRGB::White;
scale = modes[11].scale;
speed = modes[11].speed;
colorLoop = 1;
}
fillNoiseLED();
}
void forestNoise() {
if (loadingFlag) {
loadingFlag = false;
currentPalette = ForestColors_p;
scale = modes[12].scale;
speed = modes[12].speed;
colorLoop = 0;
}
fillNoiseLED();
}
void oceanNoise() {
if (loadingFlag) {
loadingFlag = false;
currentPalette = OceanColors_p;
scale = modes[13].scale;
speed = modes[13].speed;
colorLoop = 0;
}
fillNoiseLED();
}
void plasmaNoise() {
if (loadingFlag) {
loadingFlag = false;
currentPalette = PartyColors_p;
scale = modes[8].scale;
speed = modes[8].speed;
colorLoop = 1;
}
fillNoiseLED();
}
void cloudNoise() {
if (loadingFlag) {
loadingFlag = false;
currentPalette = CloudColors_p;
scale = modes[6].scale;
speed = modes[6].speed;
colorLoop = 0;
}
fillNoiseLED();
}
void lavaNoise() {
if (loadingFlag) {
loadingFlag = false;
currentPalette = LavaColors_p;
scale = modes[7].scale;
speed = modes[7].speed;
colorLoop = 0;
}
fillNoiseLED();
}
// ******************* СЛУЖЕБНЫЕ *******************
void fillNoiseLED() {
uint8_t dataSmoothing = 0;
if ( speed < 50) {
dataSmoothing = 200 - (speed * 4);
}
for (int i = 0; i < MAX_DIMENSION; i++) {
int ioffset = scale * i;
for (int j = 0; j < MAX_DIMENSION; j++) {
int joffset = scale * j;
uint8_t data = inoise8(x + ioffset, y + joffset, z);
data = qsub8(data, 16);
data = qadd8(data, scale8(data, 39));
if ( dataSmoothing ) {
uint8_t olddata = noise[i][j];
uint8_t newdata = scale8( olddata, dataSmoothing) + scale8( data, 256 - dataSmoothing);
data = newdata;
}
noise[i][j] = data;
}
}
z += speed;
// apply slow drift to X and Y, just for visual variation.
x += speed / 8;
y -= speed / 16;
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
uint8_t index = noise[j][i];
uint8_t bri = noise[i][j];
// if this palette is a 'loop', add a slowly-changing base value
if ( colorLoop) {
index += ihue;
}
// brighten up, as the color palette itself often contains the
// light/dark dynamic range desired
if ( bri > 127 ) {
bri = 255;
} else {
bri = dim8_raw( bri * 2);
}
CRGB color = ColorFromPalette( currentPalette, index, bri);
drawPixelXY(i, j, color); //leds[getPixelNumber(i, j)] = color;
}
}
ihue += 1;
}
void fillnoise8() {
for (int i = 0; i < MAX_DIMENSION; i++) {
int ioffset = scale * i;
for (int j = 0; j < MAX_DIMENSION; j++) {
int joffset = scale * j;
noise[i][j] = inoise8(x + ioffset, y + joffset, z);
}
}
z += speed;
}

View File

@@ -0,0 +1,114 @@
void parseUDP() {
int packetSize = Udp.parsePacket();
if (packetSize) {
int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
packetBuffer[n] = 0;
inputBuffer = packetBuffer;
if (inputBuffer.startsWith("DEB")) {
if (sendSettings_flag) sendCurrent();
else inputBuffer = "OK " + timeClient.getFormattedTime();
sendSettings_flag = false;
} else if (inputBuffer.startsWith("GET")) {
sendCurrent();
} else if (inputBuffer.startsWith("EFF")) {
saveEEPROM();
currentMode = (byte)inputBuffer.substring(3).toInt();
loadingFlag = true;
FastLED.clear();
delay(1);
sendCurrent();
FastLED.setBrightness(modes[currentMode].brightness);
} else if (inputBuffer.startsWith("BRI")) {
modes[currentMode].brightness = inputBuffer.substring(3).toInt();
FastLED.setBrightness(modes[currentMode].brightness);
settChanged = true;
eepromTimer = millis();
} else if (inputBuffer.startsWith("SPD")) {
modes[currentMode].speed = inputBuffer.substring(3).toInt();
loadingFlag = true;
settChanged = true;
eepromTimer = millis();
} else if (inputBuffer.startsWith("SCA")) {
modes[currentMode].scale = inputBuffer.substring(3).toInt();
loadingFlag = true;
settChanged = true;
eepromTimer = millis();
} else if (inputBuffer.startsWith("P_ON")) {
ONflag = true;
changePower();
sendCurrent();
} else if (inputBuffer.startsWith("P_OFF")) {
ONflag = false;
changePower();
sendCurrent();
} else if (inputBuffer.startsWith("ALM_SET")) {
byte alarmNum = (char)inputBuffer[7] - '0';
alarmNum -= 1;
if (inputBuffer.indexOf("ON") != -1) {
alarm[alarmNum].state = true;
inputBuffer = "alm #" + String(alarmNum + 1) + " ON";
} else if (inputBuffer.indexOf("OFF") != -1) {
alarm[alarmNum].state = false;
inputBuffer = "alm #" + String(alarmNum + 1) + " OFF";
} else {
int almTime = inputBuffer.substring(8).toInt();
alarm[alarmNum].time = almTime;
byte hour = floor(almTime / 60);
byte minute = almTime - hour * 60;
inputBuffer = "alm #" + String(alarmNum + 1) +
" " + String(hour) +
":" + String(minute);
}
saveAlarm(alarmNum);
manualOff = false;
} else if (inputBuffer.startsWith("ALM_GET")) {
sendAlarms();
} else if (inputBuffer.startsWith("DAWN")) {
dawnMode = inputBuffer.substring(4).toInt() - 1;
saveDawnMmode();
}
char reply[inputBuffer.length() + 1];
inputBuffer.toCharArray(reply, inputBuffer.length() + 1);
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(reply);
Udp.endPacket();
}
}
void sendCurrent() {
inputBuffer = "CURR";
inputBuffer += " ";
inputBuffer += String(currentMode);
inputBuffer += " ";
inputBuffer += String(modes[currentMode].brightness);
inputBuffer += " ";
inputBuffer += String(modes[currentMode].speed);
inputBuffer += " ";
inputBuffer += String(modes[currentMode].scale);
inputBuffer += " ";
inputBuffer += String(ONflag);
}
void sendSettings() {
sendCurrent();
char reply[inputBuffer.length() + 1];
inputBuffer.toCharArray(reply, inputBuffer.length() + 1);
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(reply);
Udp.endPacket();
}
void sendAlarms() {
inputBuffer = "ALMS ";
for (byte i = 0; i < 7; i++) {
inputBuffer += String(alarm[i].state);
inputBuffer += " ";
}
for (byte i = 0; i < 7; i++) {
inputBuffer += String(alarm[i].time);
inputBuffer += " ";
}
inputBuffer += (dawnMode + 1);
}

View File

@@ -0,0 +1,93 @@
// работа с бегущим текстом
// **************** НАСТРОЙКИ ****************
#define TEXT_DIRECTION 1 // 1 - по горизонтали, 0 - по вертикали
#define MIRR_V 0 // отразить текст по вертикали (0 / 1)
#define MIRR_H 0 // отразить текст по горизонтали (0 / 1)
#define TEXT_HEIGHT 3 // высота, на которой бежит текст (от низа матрицы)
#define LET_WIDTH 5 // ширина буквы шрифта
#define LET_HEIGHT 8 // высота буквы шрифта
#define SPACE 1 // пробел
// --------------------- ДЛЯ РАЗРАБОТЧИКОВ ----------------------
int offset = WIDTH;
uint32_t scrollTimer;
void resetString() {
offset = WIDTH;
}
boolean fillString(String text, CRGB textColor, boolean clear) {
if (loadingFlag) {
offset = WIDTH; // перемотка в правый край
loadingFlag = false;
}
if (millis() - scrollTimer >= 100) {
scrollTimer = millis();
if (clear) FastLED.clear();
byte i = 0, j = 0;
while (text[i] != '\0') {
if ((byte)text[i] > 191) { // работаем с русскими буквами!
i++;
} else {
drawLetter(j, text[i], offset + j * (LET_WIDTH + SPACE), textColor);
i++;
j++;
}
}
offset--;
if (offset < -j * (LET_WIDTH + SPACE)) { // строка убежала
offset = WIDTH + 3;
return true;
}
FastLED.show();
}
return false;
}
void drawLetter(uint8_t index, uint8_t letter, int16_t offset, CRGB textColor) {
int8_t start_pos = 0, finish_pos = LET_WIDTH;
if (offset < -LET_WIDTH || offset > WIDTH) return;
if (offset < 0) start_pos = -offset;
if (offset > (WIDTH - LET_WIDTH)) finish_pos = WIDTH - offset;
for (byte i = start_pos; i < finish_pos; i++) {
int thisByte;
if (MIRR_V) thisByte = getFont((byte)letter, LET_WIDTH - 1 - i);
else thisByte = getFont((byte)letter, i);
for (byte j = 0; j < LET_HEIGHT; j++) {
boolean thisBit;
if (MIRR_H) thisBit = thisByte & (1 << j);
else thisBit = thisByte & (1 << (LET_HEIGHT - 1 - j));
// рисуем столбец (i - горизонтальная позиция, j - вертикальная)
if (TEXT_DIRECTION) {
if (thisBit) leds[getPixelNumber(offset + i, TEXT_HEIGHT + j)] = textColor;
//else drawPixelXY(offset + i, TEXT_HEIGHT + j, 0x000000);
} else {
if (thisBit) leds[getPixelNumber(i, offset + TEXT_HEIGHT + j)] = textColor;
//else drawPixelXY(i, offset + TEXT_HEIGHT + j, 0x000000);
}
}
}
}
// ------------- СЛУЖЕБНЫЕ ФУНКЦИИ --------------
// интерпретатор кода символа в массиве fontHEX (для Arduino IDE 1.8.* и выше)
uint8_t getFont(uint8_t font, uint8_t row) {
font = font - '0' + 16; // перевод код символа из таблицы ASCII в номер согласно нумерации массива
if (font <= 90) return pgm_read_byte(&(fontHEX[font][row])); // для английских букв и символов
else if (font >= 112 && font <= 159) { // и пизд*ц ждя русских
return pgm_read_byte(&(fontHEX[font - 17][row]));
} else if (font >= 96 && font <= 111) {
return pgm_read_byte(&(fontHEX[font + 47][row]));
}
}

View File

@@ -0,0 +1,42 @@
// мини-класс таймера, версия 2.0
// использован улучшенный алгоритм таймера на millis
// алгоритм чуть медленнее, но обеспечивает кратные интервалы и защиту от пропусков и переполнений
class timerMinim
{
public:
timerMinim(uint32_t interval); // объявление таймера с указанием интервала
void setInterval(uint32_t interval); // установка интервала работы таймера
boolean isReady(); // возвращает true, когда пришло время. Сбрасывается в false сам (AUTO) или вручную (MANUAL)
void reset(); // ручной сброс таймера на установленный интервал
private:
uint32_t _timer = 0;
uint32_t _interval = 0;
};
timerMinim::timerMinim(uint32_t interval) {
_interval = interval;
_timer = millis();
}
void timerMinim::setInterval(uint32_t interval) {
_interval = (interval == 0) ? 1 : interval; // защита от ввода 0
}
boolean timerMinim::isReady() {
uint32_t thisMls = millis();
if (thisMls - _timer >= _interval) {
do {
_timer += _interval;
if (_timer < _interval) break; // переполнение uint32_t
} while (_timer < thisMls - _interval); // защита от пропуска шага
return true;
} else {
return false;
}
}
void timerMinim::reset() {
_timer = millis();
}

View File

@@ -0,0 +1,87 @@
// служебные функции
// залить все
void fillAll(CRGB color) {
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = color;
}
}
// функция отрисовки точки по координатам X Y
void drawPixelXY(int8_t x, int8_t y, CRGB color) {
if (x < 0 || x > WIDTH - 1 || y < 0 || y > HEIGHT - 1) return;
int thisPixel = getPixelNumber(x, y) * SEGMENTS;
for (byte i = 0; i < SEGMENTS; i++) {
leds[thisPixel + i] = color;
}
}
// функция получения цвета пикселя по его номеру
uint32_t getPixColor(int thisSegm) {
int thisPixel = thisSegm * SEGMENTS;
if (thisPixel < 0 || thisPixel > NUM_LEDS - 1) return 0;
return (((uint32_t)leds[thisPixel].r << 16) | ((long)leds[thisPixel].g << 8 ) | (long)leds[thisPixel].b);
}
// функция получения цвета пикселя в матрице по его координатам
uint32_t getPixColorXY(int8_t x, int8_t y) {
return getPixColor(getPixelNumber(x, y));
}
// **************** НАСТРОЙКА МАТРИЦЫ ****************
#if (CONNECTION_ANGLE == 0 && STRIP_DIRECTION == 0)
#define _WIDTH WIDTH
#define THIS_X x
#define THIS_Y y
#elif (CONNECTION_ANGLE == 0 && STRIP_DIRECTION == 1)
#define _WIDTH HEIGHT
#define THIS_X y
#define THIS_Y x
#elif (CONNECTION_ANGLE == 1 && STRIP_DIRECTION == 0)
#define _WIDTH WIDTH
#define THIS_X x
#define THIS_Y (HEIGHT - y - 1)
#elif (CONNECTION_ANGLE == 1 && STRIP_DIRECTION == 3)
#define _WIDTH HEIGHT
#define THIS_X (HEIGHT - y - 1)
#define THIS_Y x
#elif (CONNECTION_ANGLE == 2 && STRIP_DIRECTION == 2)
#define _WIDTH WIDTH
#define THIS_X (WIDTH - x - 1)
#define THIS_Y (HEIGHT - y - 1)
#elif (CONNECTION_ANGLE == 2 && STRIP_DIRECTION == 3)
#define _WIDTH HEIGHT
#define THIS_X (HEIGHT - y - 1)
#define THIS_Y (WIDTH - x - 1)
#elif (CONNECTION_ANGLE == 3 && STRIP_DIRECTION == 2)
#define _WIDTH WIDTH
#define THIS_X (WIDTH - x - 1)
#define THIS_Y y
#elif (CONNECTION_ANGLE == 3 && STRIP_DIRECTION == 1)
#define _WIDTH HEIGHT
#define THIS_X y
#define THIS_Y (WIDTH - x - 1)
#else
#define _WIDTH WIDTH
#define THIS_X x
#define THIS_Y y
#pragma message "Wrong matrix parameters! Set to default"
#endif
// получить номер пикселя в ленте по координатам
uint16_t getPixelNumber(int8_t x, int8_t y) {
if ((THIS_Y % 2 == 0) || MATRIX_TYPE) { // если чётная строка
return (THIS_Y * _WIDTH + THIS_X);
} else { // если нечётная строка
return (THIS_Y * _WIDTH + _WIDTH - THIS_X - 1);
}
}