Files
gunner47-GyverLamp/firmware/GyverLamp_v1.4/GyverLamp_v1.4.ino

363 lines
20 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Скетч к проекту "Многофункциональный RGB светильник"
Страница проекта (схемы, описания): https://alexgyver.ru/GyverLamp/
Исходники на GitHub: https://github.com/AlexGyver/GyverLamp/
Нравится, как написан код? Поддержи автора! https://alexgyver.ru/support_alex/
Автор: AlexGyver, AlexGyver Technologies, 2019
https://AlexGyver.ru/
*/
/*
Версия 1.4:
- Исправлен баг при смене режимов
- Исправлены тормоза в режиме точки доступа
--- 08.07.2019
- Исправлены параметры и процесс подключения к WiFi сети (таймаут 7 секунд) и развёртываия WiFi точки доступа (параметры имени/пароля)
- Добавлено "#define USE_NTP" - позволяет запретить обращаться в интернет
- Добавлено "#define ESP_USE_BUTTON - позволяет собирать лампу без физической кнопки, иначе яркость эффектов самопроизвольно растёт до максимальной
- Переработаны параметры IP адресов, STA_STATIC_IP теперь пустой по умолчанию - избавляет от путаницы с IP адресами из неправильных диапазонов
- Добавлено "#define GENERAL_DEBUG" - выводит в Serial некоторые отладочные сообщения
- Добавлено "#define WIFIMAN_DEBUG (true)" - выводит в Serial отладочные сообщения библиотеки WiFiManager
- Добавлена таблица с тест кейсами
- Форматирование кода, комментарии
--- 11.07.2019
- Исправлена ошибка невыключения матрицы после срабатывания будильника, если до будильника матрица была выключенной
- Дополнена таблица с тест кейсами
--- 14.07.2019
- Исправлена ошибка выключения будильника, если перед его срабатыванием был активен эффект "матрица" (или другой эффект, где задействовано мало светодиодов)
- Добавлено управление по воздуху:
-- работает только в режиме WiFi клиента
-- работает при подключенной кнопке (потому что режим прошивки активируется кнопкой)
--- 16.07.2019
- Исправлено регулярное подвисание матрицы на 1-2 секунды при отсутствии подключения к интернету (но при успешном подключении к WiFi)
*/
// Ссылка для менеджера плат:
// http://arduino.esp8266.com/stable/package_esp8266com_index.json
// ============= НАСТРОЙКИ =============
// --- ВРЕМЯ ---------------------------
#define USE_NTP // закомментировать или удалить эту строку, если нужно, чтобы устройство не лезло в интернет
#define GMT (3) // часовой пояс (москва 3)
#define NTP_ADDRESS ("europe.pool.ntp.org") // сервер времени
#define NTP_INTERVAL (30UL * 60UL * 1000UL) // интервал синхронизации времени (30 минут)
// --- РАССВЕТ -------------------------
#define DAWN_BRIGHT (200U) // максимальная яркость рассвета (0-255)
#define DAWN_TIMEOUT (1U) // сколько рассвет светит после времени будильника, минут
// --- МАТРИЦА -------------------------
#define BRIGHTNESS (40U) // стандартная маскимальная яркость (0-255)
#define CURRENT_LIMIT (2000U) // лимит по току в миллиамперах, автоматически управляет яркостью (пожалей свой блок питания!) 0 - выключить лимит
#define WIDTH (16U) // ширина матрицы
#define HEIGHT (16U) // высота матрицы
#define COLOR_ORDER (GRB) // порядок цветов на ленте. Если цвет отображается некорректно - меняйте. Начать можно с RGB
#define MATRIX_TYPE (0U) // тип матрицы: 0 - зигзаг, 1 - параллельная
#define CONNECTION_ANGLE (0U) // угол подключения: 0 - левый нижний, 1 - левый верхний, 2 - правый верхний, 3 - правый нижний
#define STRIP_DIRECTION (0U) // направление ленты из угла: 0 - вправо, 1 - вверх, 2 - влево, 3 - вниз
// при неправильной настройке матрицы вы получите предупреждение "Wrong matrix parameters! Set to default"
// шпаргалка по настройке матрицы здесь! https://alexgyver.ru/matrix_guide/
// --- ESP -----------------------------
#define ESP_MODE (1U) // 0U - WiFi точка доступа, 1U - клиент WiFi (подключение к роутеру)
#define ESP_USE_BUTTON // если строка не закомментирована, должна быть подключена кнопка (иначе ESP может регистрировать "фантомные" нажатия и некорректно устанавливать яркость)
#define ESP_HTTP_PORT (80U) // номер порта, который будет использоваться во время первой утановки имени WiFi сети (и пароля), к которой потом будет подключаться лампа в режиме WiFi клиента (лучше не менять)
#define ESP_UDP_PORT (8888U) // номер порта, который будет "слушать" UDP сервер во время работы лампы как в режиме WiFi точки доступа, так и в режиме WiFi клиента (лучше не менять)
#define ESP_CONN_TIMEOUT (7U) // время в секундах (ДОЛЖНО БЫТЬ МЕНЬШЕ 8, иначе сработает WDT), которое ESP будет пытаться подключиться к WiFi сети, после его истечения автоматически развернёт WiFi точку доступа
#define ESP_CONF_TIMEOUT (300U) // время в секундах, которое ESP будет ждать ввода SSID и пароля WiFi сети роутера в конфигурационном режиме, после его истечения ESP перезагружается
#define GENERAL_DEBUG // если строка не закомментирована, будут выводиться отладочные сообщения
#define WIFIMAN_DEBUG (true) // вывод отладочных сообщений при подключении к WiFi сети: true - выводятся, false - не выводятся; настройка не зависит от GENERAL_DEBUG
#define OTA // если строка не закомментирована, модуль будет ждать два последдовательных запроса пользователя на прошивку по воздуху (см. документацию в "шапке")
#ifdef OTA
#define ESP_OTA_PORT (8266U) // номер порта, который будет "прослушиваться" в ожидании команды прошивки по воздуху
#endif
// --- ESP (WiFi клиент) ---------------
uint8_t STA_STATIC_IP[] ={}; // статический IP адрес: {} - IP адрес определяется роутером; {192, 168, 1, 66} - IP адрес задан явно (если DHCP на роутере не решит иначе); должен быть из того же диапазона адресов, что разадёт роутер
// SSID WiFi сети и пароль будут запрошены WiFi Manager'ом в режиме WiFi точки доступа, нет способа захардкодить их в прошивке
// --- AP (WiFi точка доступа) ---
#define AP_NAME ("GyverLamp") // имя WiFi точки доступа, используется как при запросе SSID и пароля WiFi сети роутера, так и при работе в режиме ESP_MODE = 0
#define AP_PASS ("12345678") // пароль WiFi точки доступа
uint8_t AP_STATIC_IP[] = {192, 168, 4, 1}; // статический IP точки доступа (лучше не менять)
// ============= ДЛЯ РАЗРАБОТЧИКОВ =====
#define LED_PIN (2U) // пин ленты
#define BTN_PIN (4U) // пин кнопки
#define MODE_AMOUNT (18U)
#define NUM_LEDS (WIDTH * HEIGHT)
#define SEGMENTS (1U) // диодов в одном "пикселе" (для создания матрицы из кусков ленты)
// --- БИБЛИОТЕКИ ----------------------
#define FASTLED_INTERRUPT_RETRY_COUNT (0U)
#define FASTLED_ALLOW_INTERRUPTS (0U)
#define FASTLED_ESP8266_RAW_PIN_ORDER
#include "timerMinim.h"
#include <FastLED.h>
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <WiFiUdp.h>
#include <EEPROM.h>
#ifdef ESP_USE_BUTTON
#include <GyverButton.h>
#endif
#ifdef USE_NTP
#include <NTPClient.h>
#endif
#ifdef OTA
#include "OtaManager.h"
#endif
// --- ИНИЦИАЛИЗАЦИЯ ОБЪЕКТОВ ----------
CRGB leds[NUM_LEDS];
WiFiManager wifiManager;
WiFiServer wifiServer(ESP_HTTP_PORT);
WiFiUDP Udp;
#ifdef USE_NTP
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, NTP_ADDRESS, GMT * 3600, NTP_INTERVAL);
#endif
timerMinim timeTimer(3000);
#ifdef ESP_USE_BUTTON
GButton touch(BTN_PIN, LOW_PULL, NORM_OPEN);
#endif
#ifdef OTA
OtaManager otaManager;
OtaPhase OtaManager::OtaFlag = OtaPhase::None;
#endif
// --- ИНИЦИАЛИЗАЦИЯ ПЕРЕМЕННЫХ -------
uint16_t localPort = ESP_UDP_PORT;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1]; // buffer to hold incoming packet
String inputBuffer;
static const uint8_t maxDim = max(WIDTH, HEIGHT);
struct
{
uint8_t brightness = 50;
uint8_t speed = 30;
uint8_t scale = 40;
} modes[MODE_AMOUNT];
struct
{
boolean state = false;
int16_t time = 0;
} alarm[7];
uint8_t dawnOffsets[] = {5, 10, 15, 20, 25, 30, 40, 50, 60};
uint8_t dawnMode;
boolean dawnFlag = false;
long thisTime;
boolean manualOff = 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];
void setup()
{
Serial.begin(115200);
Serial.println();
ESP.wdtDisable();
//ESP.wdtEnable(WDTO_8S);
#ifdef ESP_USE_BUTTON
touch.setStepTimeout(100);
touch.setClickTimeout(500);
buttonTick();
if(touch.state()) // сброс сохранённых SSID и пароля при старте с зажатой кнопкой
{
wifiManager.resetSettings();
#ifdef GENERAL_DEBUG
Serial.println("Настройки WiFiManager сброшены");
#endif
}
#endif
// ЛЕНТА
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.clear();
FastLED.show();
// WI-FI
wifiManager.setDebugOutput(WIFIMAN_DEBUG); // вывод отладочных сообщений
if (ESP_MODE == 0) // режим WiFi точки доступа
{
WiFi.softAPConfig( // wifiManager.startConfigPortal использовать нельзя, т.к. он блокирует вычислительный процесс внутри себя, а затем перезагружает ESP, т.е. предназначен только для ввода SSID и пароля
IPAddress(AP_STATIC_IP[0], AP_STATIC_IP[1], AP_STATIC_IP[2], AP_STATIC_IP[3]), // IP адрес WiFi точки доступа
IPAddress(AP_STATIC_IP[0], AP_STATIC_IP[1], AP_STATIC_IP[2], 1), // первый доступный IP адрес сети
IPAddress(255, 255, 255, 0)); // маска подсети
WiFi.softAP(AP_NAME, AP_PASS);
Serial.println("Режим WiFi точки доступа");
Serial.print("IP адрес: ");
Serial.println(WiFi.softAPIP());
wifiServer.begin();
}
else // режим WiFi клиента (подключаемся к роутеру, если есть сохранённые SSID и пароль, иначе создаём WiFi точку доступа и запрашиваем их)
{
Serial.println("Режим WiFi клиента");
if (WiFi.SSID())
{
Serial.print("Подключение WiFi сети: ");
Serial.println(WiFi.SSID());
}
else
{
Serial.println("WiFi сеть не определена, запуск WiFi точки доступа для настройки параметров подключения к WiFi сети...");
}
if (STA_STATIC_IP)
{
wifiManager.setSTAStaticIPConfig(
IPAddress(STA_STATIC_IP[0], STA_STATIC_IP[1], STA_STATIC_IP[2], STA_STATIC_IP[3]), // статический IP адрес ESP в режиме WiFi клиента
IPAddress(STA_STATIC_IP[0], STA_STATIC_IP[1], STA_STATIC_IP[2], 1), // первый доступный IP адрес сети (справедливо для 99,99% случаев; для сетей меньше чем на 255 адресов нужно вынести в константы)
IPAddress(255, 255, 255, 0)); // маска подсети (справедливо для 99,99% случаев; для сетей меньше чем на 255 адресов нужно вынести в константы)
}
wifiManager.setConnectTimeout(ESP_CONN_TIMEOUT); // установка времени ожидания подключения к WiFi сети, затем старт WiFi точки доступа
wifiManager.setConfigPortalTimeout(ESP_CONF_TIMEOUT); // установка времени работы WiFi точки доступа, затем перезагрузка; отключить watchdog?
wifiManager.autoConnect(AP_NAME, AP_PASS); // пытаемся подключиться к сохранённой ранее WiFi сети; в случае ошибки, будет развёрнута WiFi точка доступа с указанными AP_NAME и паролем на время ESP_CONN_TIMEOUT секунд; http://AP_STATIC_IP:ESP_HTTP_PORT (обычно http://192.168.0.1:80) - страница для ввода SSID и пароля от WiFi сети роутера
if (WiFi.status() != WL_CONNECTED)
{
Serial.printf("Время ожидания ввода SSID и пароля от WiFi сети или подключения к WiFi сети превышено\nПерезагрузка модуля");
#if defined(ESP8266)
ESP.reset();
#else
ESP.restart();
#endif
}
Serial.print("IP адрес: ");
Serial.println(WiFi.localIP());
}
Serial.printf("Порт UDP сервера: %u\n", localPort);
Udp.begin(localPort);
// EEPROM
EEPROM.begin(202);
delay(50);
if (EEPROM.read(198) != 20) // первый запуск
{
EEPROM.write(198, 20);
EEPROM.commit();
for (uint8_t i = 0; i < MODE_AMOUNT; i++)
{
EEPROM.put(3 * i + 40, modes[i]);
EEPROM.commit();
}
for (uint8_t 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 (uint8_t i = 0; i < MODE_AMOUNT; i++)
{
EEPROM.get(3 * i + 40, modes[i]);
}
for (uint8_t 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);
sendCurrent(); // отправляем настройки
char reply[inputBuffer.length() + 1];
inputBuffer.toCharArray(reply, inputBuffer.length() + 1);
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(reply);
Udp.endPacket();
#ifdef USE_NTP
timeClient.begin();
#endif
memset(matrixValue, 0, sizeof(matrixValue));
randomSeed(micros());
}
void loop()
{
parseUDP();
effectsTick();
eepromTick();
#ifdef USE_NTP
timeTick();
#endif
#ifdef ESP_USE_BUTTON
buttonTick();
#endif
#ifdef OTA
otaManager.HandleOtaUpdate(); // ожидание и обработка команды на обновление прошивки по воздуху
#endif
ESP.wdtFeed(); // пнуть собаку
yield(); // обработать все "служебные" задачи: WiFi подключение и т.д.
}
void eeWriteInt(int16_t pos, int16_t val)
{
uint8_t* p = (uint8_t*) &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();
}
int16_t eeGetInt(int16_t pos)
{
int16_t val;
uint8_t* p = (uint8_t*) &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;
}