Мини-кейс 2: Умная лампа

Умная лампа - RGB-матрица с слайдерами выбора оттенка, насыщенности и яркости.

Перед выполнением прочтите

Информация про модуль RGB LED матрицы от Амперки: http://wiki.amperka.ru/%D0%BF%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82%D1%8B:troyka-rgb-matrix

Информация о библиотеке Adafruit_NeoPixel: https://learn.adafruit.com/adafruit-neopixel-uberguide/arduino-library-use

Создание мини-проекта

Сделаем мини-проект с RGB модулем, который управляется через интерфейс RainMaker. Представим, что наш RGB модуль будет умной лампочкой, которая может менять цвет.

  1. Нужно собрать устройство. К GPIO 17 подключить модуль RGB матрицы Troyka контактом D1. Распиновка платы на родительской странице "О ESP32". Питание для него от пина 3.3v.

    Светодиодная RGB матрица 4×4 (Troyka-модуль)
  2. Создаём новый файл в Arduino IDE с кодом:

    #include <RMaker.h>
    #include <WiFi.h>
    #include <WiFiProv.h>
    #include <Adafruit_NeoPixel.h> // Библиотека для работы с RGB-матрицей
    
    #define SERVICE_NAME "PROV_2" // Имя устройства при подключении по bluetooth, нужно для настройки, когда плата ещё не настроена на WI-FI точку
    #define POP "abcd2" // ? Пароль для устройства при подключении по bluetooth
    
    #define BOOT_BTN_PIN 0 // GPIO пин кнопки BOOT
    
    #define RGB_MATRIX_PIN 17 // GPIO пина, к которому подключена RGB-матрица
    #define MATRIX_LED_DEFAULT_STATE false // Состояние матрицы при старте
    
    #define MATRIX_LED_COUNT 16 // Количество светодиодов в матрице
    
    #define DEFAULT_LED_HUE 0 // Значение слайдера hue для LED по умолчанию
    #define DEFAULT_LED_SATURATION 255 // Значение слайдера насышенности для LED по умолчанию
    #define DEFAULT_LED_BRIGHTNESS 64 // Значение слайдера яркости для LED по умолчанию
    
    Adafruit_NeoPixel ledMatrix = Adafruit_NeoPixel(MATRIX_LED_COUNT, RGB_MATRIX_PIN, NEO_GRB + NEO_KHZ800); // Создаём объект класса Adafruit_NeoPixel
    
    int matrix_led_gpio = RGB_MATRIX_PIN; // Переменная вашего светодиода
    bool matrix_led_state = MATRIX_LED_DEFAULT_STATE; // Переменная для хранения состояния вашего rgb-модуля
    int led_hue = DEFAULT_LED_HUE; // Значение оттенка цвета вашей лампы
    int led_saturation = DEFAULT_LED_SATURATION; // Значение насыщенности вашей ламы
    int led_brightness = DEFAULT_LED_BRIGHTNESS; // Значение яркости вашей ламы
    
    // Фреймворк предоставляет некоторые стандартные типы устройств, такие как выключатель, лампочка, вентилятор, датчик температуры.
    // Но вы также можете определить пользовательские устройства, используя объект базового класса 'Device', как показано здесь
    Device my_device("SmartLED", ESP_RMAKER_DEVICE_OTHER, &matrix_led_gpio);
    
    void sysProvEvent(arduino_event_t *sys_event)
    {
        switch (sys_event->event_id) {      
            case ARDUINO_EVENT_PROV_START:
              // Для ESP32
              Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on BLE\n", SERVICE_NAME, POP);
              printQR(SERVICE_NAME, POP, "ble");   
              // Для ESP32S2
              //Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on SoftAP\n", service_name, pop);
              //printQR(service_name, pop, "softap");   
              break;
        }
    }
    
    // Функция обработки изменений
    void write_callback(Device *device, Param *param, const param_val_t val, void *priv_data, write_ctx_t *ctx)
    {
      const char *device_name = device->getDeviceName();
      const char *param_name = param->getParamName();
    
      if(strcmp(param_name, ESP_RMAKER_DEF_POWER_NAME) == 0) { // Если параметр с именем Power
        matrix_led_state = val.val.b; // Записываем значение состояния вашего светодиода в переменную, обратите внимание, что b - это тип bool
        Serial.printf("Received param %s = %s for %s\n", param_name, (matrix_led_state? "true" : "false"), device_name);
        if (!matrix_led_state) {
          ledMatrix.clear(); // Очистить матрицу
          ledMatrix.show(); // Отправить изменения на матрицу
        } else {
          uint32_t rgbcolor = ledMatrix.ColorHSV(led_hue, led_saturation, led_brightness);
          ledMatrix.fill(rgbcolor); // Закрасить матрицу
          ledMatrix.show(); // Отправить изменения на матрицу
        }
        param->updateAndReport(val);
      } else if (strcmp(param_name, ESP_RMAKER_DEF_HUE_NAME) == 0) { // Если параметр с именем Hue
        led_hue = map(val.val.i, 0, 360, 0, 65535); // Записываем значение в переменную, i - это тип int и перевести в нужный дапазон по инфе с библиотеки
        if (matrix_led_state) { // Если тумблер матрицы вкл, тогда установим новый hue
          uint32_t rgbcolor = ledMatrix.ColorHSV(led_hue, led_saturation, led_brightness);
          ledMatrix.fill(rgbcolor); // Закрасить матрицу
          ledMatrix.show(); // Отправить изменения на матрицу
        }
        Serial.printf("Received param %s = %d for %s\n", param_name, led_hue, device_name);
        param->updateAndReport(val);
      } else if (strcmp(param_name, ESP_RMAKER_DEF_SATURATION_NAME) == 0) { // Если параметр с именем Saturation
        led_saturation = val.val.i; // Записываем значение яркости вашей лампы в переменную, i - это тип int
        if (matrix_led_state) { // Если тумблер матрицы вкл, тогда установим новую яркость
          uint32_t rgbcolor = ledMatrix.ColorHSV(led_hue, led_saturation, led_brightness);
          ledMatrix.fill(rgbcolor); // Закрасить матрицу
          ledMatrix.show(); // Отправить изменения на матрицу
        }
        Serial.printf("Received param %s = %d for %s\n", param_name, led_saturation, device_name);
        param->updateAndReport(val);
      } else if (strcmp(param_name, ESP_RMAKER_DEF_BRIGHTNESS_NAME) == 0) { // Если параметр с именем Brightness
        led_brightness = val.val.i; // Записываем значение яркости вашей лампы в переменную, i - это тип int
        if (led_brightness == 0) { // На слайдере установлено 0
            matrix_led_state = false; // Установить переменную состояния светодиода на выключен
            ledMatrix.clear(); // Очистить матрицу
            ledMatrix.show(); // Отправить изменения на матрицу
            my_device.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, false); // Изменяем состояние тумблера на выкл в RainMaker
            Serial.printf("Toggle State to false.\n");
        } else if (matrix_led_state) { // Если тумблер лампы вкл, тогда установим новую яркость
          uint32_t rgbcolor = ledMatrix.ColorHSV(led_hue, led_saturation, led_brightness);
          ledMatrix.fill(rgbcolor); // Изменить заливку
          ledMatrix.show(); // Отправить изменения на матрицу
        }
        Serial.printf("Received param %s = %d for %s\n", param_name, led_brightness, device_name);
        param->updateAndReport(val);
      }
    }
    
    void setup()
    {
      Serial.begin(115200); // Инициализируем скорость Serial
      pinMode(BOOT_BTN_PIN, INPUT); // Настраиваем пин кнопки BOOT на INPUT
    
      ledMatrix.begin(); // Инициализация RGB-матрицы
      if (!MATRIX_LED_DEFAULT_STATE) { // Если матрица изначально должна быть выключена
        ledMatrix.clear(); // Очистить матрицу
        ledMatrix.show(); // Отправить изменения на матрицу
      }
      
      Node my_node; // Объявляем Node
      my_node = RMaker.initNode("ESP RainMaker Node"); // Инициализируем Node
    
      // Создаём устройство для управления светодиодом
      my_device.addNameParam(); // Устнаваливаем имя устройства
      my_device.addPowerParam(MATRIX_LED_DEFAULT_STATE); // Устанавливаем стандатное значение
      my_device.assignPrimaryParam(my_device.getParamByName(ESP_RMAKER_DEF_POWER_NAME)); // Устанавливаем имя параметра Power
    
      // Создаём и добавляем слайдер hue
      Param hue_param(ESP_RMAKER_DEF_HUE_NAME, ESP_RMAKER_PARAM_HUE, value(DEFAULT_LED_HUE), PROP_FLAG_READ | PROP_FLAG_WRITE);  // Указываем название создаваемого параметра, тип параметра, стандартное значение и задаём права
      hue_param.addBounds(value(0), value(360), value(1)); // Знач-е диапазона от 0 до 360, в данном случае праметр только для отображения, т.к. значение будет приходить всё-равно от 0 до 360. Если не указать будет от 0 и до 0
      hue_param.addUIType(ESP_RMAKER_UI_HUE_SLIDER); // Устанавливаем тип отображения как HUE-слайдер
      my_device.addParam(hue_param); // Добавляем параметр устройству
    
      // Создаём и добавляем слайдер saturation
      Param saturation_param(ESP_RMAKER_DEF_SATURATION_NAME, ESP_RMAKER_PARAM_SPEED, value(DEFAULT_LED_SATURATION), PROP_FLAG_READ | PROP_FLAG_WRITE);  // Указываем название создаваемого параметра, тип параметра, стандартное значение и задаём права
      saturation_param.addBounds(value(0), value(255), value(1)); // Значение от 0 до 255
      saturation_param.addUIType(ESP_RMAKER_UI_SLIDER); // Устанавливаем тип отображения как слайдер
      my_device.addParam(saturation_param); // Добавляем параметр устройству 
    
      // Создаём и добавляем слайдер brightness
      Param brightness_param(ESP_RMAKER_DEF_BRIGHTNESS_NAME, ESP_RMAKER_PARAM_SPEED, value(DEFAULT_LED_BRIGHTNESS), PROP_FLAG_READ | PROP_FLAG_WRITE);  // Указываем название создаваемого параметра, тип параметра, стандартное значение и задаём права
      brightness_param.addBounds(value(0), value(255), value(1)); // Значение от 0 до 255
      brightness_param.addUIType(ESP_RMAKER_UI_SLIDER); // Устанавливаем тип отображения как слайдер
      my_device.addParam(brightness_param); // Добавляем параметр устройству
      
      my_device.addCb(write_callback); // Устанавливаем функцию на обработчик
      my_node.addDevice(my_device); // Добавить девайс на ноду
    
      // Опционально
      RMaker.enableOTA(OTA_USING_PARAMS); // Включить беспроводное обновление
      RMaker.setTimeZone("Europe/Moscow"); // Установить часовой пояс https://rainmaker.espressif.com/docs/time-service.html
      RMaker.enableTZService(); // Кроме того, включите службу часового пояса и позвольте приложениям телефона установить соответствующий часовой пояс
    
      RMaker.enableSchedule(); // Включить расписание для нашего устройства
      
      RMaker.start(); // Запуск сервиса на устройстве
    
      WiFi.onEvent(sysProvEvent);
      WiFiProv.beginProvision(NETWORK_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_1, POP, SERVICE_NAME);
    }
    
    void loop()
    {
      // Обрабатываем нажатие на кнопка BOOT
      if(digitalRead(BOOT_BTN_PIN) == LOW) {
          delay(100); // Для исключения дребезка на тактовой кнопке
          int startTime = millis(); // Время на номент нажатия кнопки
          while(digitalRead(BOOT_BTN_PIN) == LOW) delay(50); // Цикл, который создаёт паузу для ожидания отжатия кнопки
          int endTime = millis(); // Время на момент отжатия кнопки
          int pressTime = endTime - startTime; // Время, на которое кнопка была нажата
          // Условия по времени нажатия
          if (pressTime > 10000) { // Если кнопка была зажата на более 10 секунд, тогда сбросить всё
            Serial.printf("Reset to factory.\n");
            RMakerFactoryReset(2);
          } else if (pressTime > 3000) { // Иначе если кнопка была зажата больше чем на 3 секунды, но меньше 10, тогда сбросить настройки WI-FI
            Serial.printf("Reset Wi-Fi.\n");
            RMakerWiFiReset(2);
          } else { // В остальных случаях
            // ToDo
         }
      }
      delay(10);
    }

    Изучите код, прочитайте комментарии к коду!

  3. Добавьте библиотеку Adafruit_NeoPixel. Вы можете это сделать через пункт "Управление библиотками" или скачать из интернета и добавить как zip библиотеку.

  4. В инструментах вы должны выбрать:

    • Плата: ESP32 Arduino > ESP32 Dev Module;

    • Partition Scheme: RainMaker.

    И выберите порт платы. Загрузите прошивку. Никаких пинов для перевода контроллера в режим прошивки зажимать не нужно.

  5. Далее дайдите в монитор порта, установить скорость 115200. Выведется информация о том, как подключить плату к сервису. Там должна быть строка с QR-кодом и ссылка. Скопируйте ссылку и вставьте в адресную строку браузера.

    На этом этапе могут возникнуть две известные проблемы...

    • Плата пишет что-то на английском, но не выдаёт ссылку, тогда нужно сбросить настройки WI-FI, зажав BOOT на 3 секунды и отпустив. * Если плата уже была до этого настроена, тогда она будет выводить после прошивки текст на английском, QR-код уже не выведется. Это нормально, но пользоваться дальше можно!

    • Плата пишет всякие кракозябры, хотя скорость последовательного порта выставлена верно. Вам нужно сбросить настройки прошивки. Для этого зажмите кнопку BOOT на плате на 10 секунд. По истечению времени отпустите, плата перезапустит код и в монитор порта текст станет читабельный и выведется ссылка.

  6. Откройте приложение RainMaker. В приложении RainMaker нажать на кнопку с иконкой ПЛЮС, запуститься сканер QR-кода.

  7. Отсканировать сканером QR-код с страницы с браузера.

  8. В приложении RainMaker выбрать точку доступа для устройства, дать пароль и подождать подключения и всяких других настроек.

  9. На главной странице RainMaker появится или обновится ваш девайс.

Last updated

Was this helpful?