# Модуль LED Arduino #

> Версия: 2.0.1

---

## Описание:

> Модуль LED Arduino предназначен для управления светодиодной лентой с использованием Arduino. Он обеспечивает подключение к устройству, отправку команд для изменения режимов работы светодиодов, а также обработку событий и ошибок. Модуль поддерживает управления светодиодной лентой для разных проектов. В данный момент проекты, которые поддерживает пакет:
* Neuro-Fluger (используется адресная светодиодная лента)
* LUKOIL (используется многоцветная RGB лента)

## Подключение:

### Подключение устройства Arduino:

1. Соедините Arduino с компьютером по USB.
2. Узнайте имя порта, к которому подключено устройство Arduino (например, "COM3" для Windows | /dev/ttyACM0 для Linux).

### Посмотреть пример работы модуля:

1. Установите пакет: `npm install @neuro-city/led-arduino`
2. Перейдите в директорию с примером работы: `cd ./node_modules/@neuro-city/led-arduino/example`
3. Задать в /configs в конфиге интересующего проекта порт
4. В /example/index.js убедится в том, что конфиг и команды интересующего проекта раскомментированы 
4. Запустите пример: `node index.js`

### Подключить пакет к проекту:

1. Установите пакет: `npm install @neuro-city/led-arduino`
2. Создайте переменную для использования модуля: `const arduinoLed = require('@neuro-city/led-arduino)`;

### Перечень функций пакета:

1. **connectArduino**: Функция connectArduino экспортируется из модуля и предназначена для проверки и установки соединения с Arduino на основе переданных конфигурационных данных. Принимает объект initLedArduino, содержащий параметры конфигурации для Arduino. Проверяет корректность полей этого объекта. В случае, если какое-либо из значений не соответствует ожидаемому формату или диапазону, функция генерирует ошибку с помощью errorEmit и завершает свою работу. 

Каждый проект имеет свои индивидуальные аргументы для конфигурации работы arduino и светодиодной ленты. Поэтому для каждого проекта имеется индивидуальная функция для проверки параметров. Для любого нового поддерживаемого проекта, необходимо добавить новую функцию проверки со своими параметрами.

Если конфигурация прошла проверку успешно, вызывает внутреннюю функцию findPort, которая инициирует поиск порта Arduino и установку соединения. При возникновении ошибки в работе модуля Arduino, функция логирует ошибку с помощью log.error.

2.**findPort**: Функция findPort используется для поиска и подключения к порту, к которому подключено устройство Arduino. Если порт уже указан в конфигурации, функция попытается подключиться к этому порту. Порт указывается в формате "COM3" для Windows или /dev/ttyACM0 (при определенных действиях /dev/arduino) для Linux. В противном случае она просмотрит доступные порты и попытается найти устройство Arduino по его идентификатору. После успешного подключения функция инициализирует светодиодную ленту и готова к воспроизведению.

3. **connecting**: Функция connecting используется для установления соединения с Arduino после успешного нахождения порта. Она устанавливает обработчики событий для открытия, закрытия и получения данных от Arduino. Внутри функции осуществляется обработка полученных данных и управление статусом светодиодной ленты в зависимости от полученных сообщений от Arduino. Функция также обрабатывает событие закрытия порта и пытается переподключиться к устройству через определенный интервал времени в случае разрыва соединения.

В ней в зависимости от проекта вызывается определенная функция **initializationСommand(arduino, arg)** для отправки инициализационного конфига на arduino. Обработчик события `data` в функции connecting при получении сообщения от контроллера «Waiting for config data...» обращается к функции sendConfigInitializeCommand:

```javascript
    if (trimmedMessage.includes('Waiting for configData command...')) {
            initializationСommand(arduino, ledArduinoConfig)
            ledArduinoConfig.status = 'initializng';
            ledArduinoConfig.type = null;     
        }
```
 А функция **idleCommand(arduino, arg)** служит для отправки команды вызова ждущего режима в зависимости от проекта. Обработчик события `data` в функции connecting при получении сообщения от контроллера «Arduino connected!» обращается к функции sendConfigInitializeCommand:

```javascript
    if (trimmedMessage.includes('Arduino connected!')) {    
        idleCommand(arduino, ledArduinoConfig)
        ledArduinoConfig.status = 'in work';
        ledArduinoConfig.type = 'default';
    }
```

4. **sendConfigInitializeCommand**: Функция sendConfigInitializeCommand в зависимости от проекта отправляет команду данных инициализации на Arduino с указанием необходимых параметоров. Они передаются в виде строки.

* **Neuro-Fluger** - команда `configData (<brightness>, <numLeds>)`, где brightness - яркость светодиодной ленты, а numLeds - количество пикселей
* **LUKOIL** - команда `configdata(<data_pin>, <clock_pin>, <hex_color>)`, где data_pin и clock_pin цифровые пины arduino, а hex_color начальный цвет свечения светодиодной ленты в начале работы

Если порт Arduino открыт и доступен (arduino.isOpen), то функция отправляет команду через метод write объекта arduino. При успешной отправке команды, функция генерирует сообщение о том, какая команда была отправлена, с указанием переданных параметров. В случае возникновения ошибки при отправке команды, генерируется сообщение об ошибке. После успешной отправки сообщения от контроллера приходит команда «Arduino connected!»

5. **sendInitializeHEXCommand**: Функция sendInitializeHEXCommand аналогична предыдущей, однако отправляет команду при наличии для вызова сценария ждущего режима свечения светодиодной ленты.  init с указанием цвета в формате HEX (hexColor) и длительности (duration). Параметры команды передаются в виде строки init(${hexColor}, ${duration}).

* **Neuro-Fluger** - команда `init(<hexColor>) (<duration>)`, где hexColor - цвет режима ожидания, а duration - скорость работы сценария
* **LUKOIL** - для данного проекта отсутствует

Аналогично предыдущей функции, проверяется доступность порта Arduino. При успешной отправке команды, генерируется сообщение о том, какая команда была отправлена, с указанием переданных параметров. В случае возникновения ошибки при отправке команды, генерируется сообщение об ошибке.
Обе функции предполагают наличие объекта arduino, представляющего открытый порт Arduino. Если порт не открыт или объект arduino отсутствует, функции генерируют сообщение об ошибке.

## Интерфейс:
* ### Команды:
    * **connectArduino(initLedArduino)** — эта функция устанавливает соединение с устройством Arduino. Принимает индивидуальный объект с параметрами конфигурации для каждого проекта

    * Следующий блок кода экспортирует две функции из модуля:
    
    ```javascript
    module.exports = {
        arduinoLEDConnect
    };
    ```
    
    1. **arduinoLEDConnect**: Эта функция предназначена для установки соединения с устройством Arduino. Она принимает на вход параметры конфигурации Arduino. Внутри функции происходит инициализация соединения с Arduino, а также установка обработчиков событий для открытия, закрытия, ошибок и передачи данных по соединению.

    2. **callLedArduinoCommand**: Эта функция используется для отправки команд Arduino для управления светодиодной лентой выбранного проекта в зависимости от выбранного эффекта и цвета. Она принимает два параметра: `commandType` (тип эффекта) и `args` (параметры команды в виде объекта). Внутри функции осуществляется проверка типа эффекта и отправка соответствующей команды Arduino через функцию `callCommand`.

    * Функция `callLedArduinoCommand` принимает два параметра: `commandType` и `args`. 

    **Neuro-Fluger**

    **LUKOIL** - команды управления светодиодной лентой https://akziagroup.atlassian.net/wiki/spaces/LA/pages/2467037186
    
    ```javascript
    callLedArduinoCommand('blink', {hexColor: '#FF3064', hexColor2: '#60F50A', speed: 50});
    const callLedArduinoCommand = (commandType, args) => {
        if (isArduinoConnected && arduinoLed) {
            arduinoLed.callCommand(commandType, args);
        } else {
            console.warn(`[devices] Команда ${commandType} не может быть выполнена: Arduino не подключено.`);
        }
    };
    ```

    ### Примечание:

    * Все команды к устройству, кроме `connectArduino`, необходимо размещать в колбеке события `open`.

    * На Linux вы также можете прослушивать порт напрямую с помощью команды:

        `echo -n "конфигурационная команда каждого проекта" > /dev/ttyACM0` // При подключении подключении новой Arduino в Linux выдаётся индивидуальный путь к ней, в связи с чем каждой последующей Arduino операционной системой будет присвоен иной путь, см. следующий пункт

    * Поскольку у Arduino фиксированный VID и PID (2341 0043), для удобства вы также можете создать алиас для порта Arduino следующей командой:

        `echo 'ACTION=="add", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0043", SYMLINK+="arduino"' | sudo tee /etc/udev/rules.d/10-local.rules`

    * Так же у Arduino возможен VID и PID (1a86 7523), для удобства вы также можете создать алиас для порта Arduino следующей командой:

        `echo 'ACTION=="add", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="arduino"' | sudo tee /etc/udev/rules.d/11-local.rules`
    
    Из-за конфликта между идентификаторами продуктов (программой чтения с экрана Брайля и чипом Arduino на базе CH340).
 
    Редактировать:
        `/usr/lib/udev/rules.d/85-brltty.rules`
    
    Найдите эту строку и закомментируйте ее: 
        `ENV{PRODUCT}=="1a86/7523/*", ENV{BRLTTY_BRAILLE_DRIVER}="bm", GOTO="brltty_usb_run"` 

    Перезагрузите устройство
    
    После этого действия можно будет указывать путь как: `/dev/arduino`

    * Каждый проект в /configs содержит конфиг каждого проекта. Каждый проект содержит индивидуальные параметры.


    **Neuro-Fluger:**
    ```json
    {
    "ledArduino": {
            "name": "Arduino подсветка светодиодной лентой",
            "value": {
                "enable": {
                    "name": "Включен",
                    "value": true
                },
                "project": {
                    "name": "Название проекта",
                    "value": "neuro-fluger"
                },
                "port": {
                    "name": "Порт",
                    "value": "/dev/arduino"
                },
                "reconnect_Interval_Value": {
                    "name": "Интервал переподключения к Arduino - контроллеру управления LED лентой",
                    "value": 15,
                    "type": "integer",
                    "step": "1"
                },
                "duration": {
                    "name": "Задержка анимаций ленты",
                    "value": 5,
                    "type": "integer",
                    "step": "1"
                },
                "number_LED": {
                    "name": "Количество пикселей светодиодов",
                    "value": 26,
                    "type": "integer",
                    "step": "1"
                },
                "color_Wait": {
                    "name": "Цвет светодиодной ленты режима ожидания команды. Формат hex.",
                    "value": "#ede207"
                },
                "brightness": {
                    "name": "Яркость светодиодной ленты",
                    "value": 244,
                    "type": "integer",
                    "step": "1"
                }
            }
    }
    }   
    ```
    - `enable`: Параметр, определяющий включено ли устройство. В данном примере установлено значение `true`, что указывает на то, что устройство включено.
    - `project`: Название проекта `neuro-fluger`
    - `port`: Параметр, указывающий порт, к которому подключено устройство (например, "COM5").
    - `color_Wait`: Цвет светодиодной ленты в режиме ожидания команды, представленный в формате HEX (например, "#ede207").
    - `brightness`: Яркость светодиодной ленты, представленная числом (в данном примере значение равно 244).
    - `number_LED`: Количество пикселей светодиодов на ленте (в данном примере значение равно 26).
    - `duration`: Задержка анимаций ленты, представленная числом (в данном примере значение равно 50).
    - `reconnect_Interval_Value`: Интервал переподключения к LED ленте в случае потери соединения, представленный числом (в данном примере значение равно 5 секунд).


    **LUKOIL:**
    ```json
    {
    "ledArduino": {
            "name": "Arduino подсветка светодиодной лентой",
            "value": {
                "enable": {
                    "name": "Включен",
                    "value": true
                },
                "project": {
                    "name": "Название проекта",
                    "value": "lukoil"
                },
                "port": {
                    "name": "Порт",
                    "value": "/dev/arduino"
                },
                "reconnect_Interval_Value": {
                    "name": "Интервал переподключения к Arduino - контроллеру управления LED лентой",
                    "value": 15,
                    "type": "integer",
                    "step": "1"
                },
                "enableRGBPin": {
                    "name": "Включить использование RGB пинов на arduino. Тип подключения зависит от используемого драйвера светодиодной ленты RGB.",
                    "value": true
                },
                "dataPin": {
                    "name": "Номер пина data на Arduino",
                    "value": 6,
                    "type": "integer",
                    "step": "1"
                },
                "clockPin": {
                    "name": "Номер пина clock на Arduino",
                    "value": 7,
                    "type": "integer",
                    "step": "1"
                },
                "redPin": {
                    "name": "Номер пина red на Arduino",
                    "value": 3,
                    "type": "integer",
                    "step": "1"
                },
                "greenPin": {
                    "name": "Номер пина green на Arduino",
                    "value": 5,
                    "type": "integer",
                    "step": "1"
                },
                "bluePin": {
                    "name": "Номер пина blue на Arduino",
                    "value": 6,
                    "type": "integer",
                    "step": "1"
                },
                "idleColor": {
                    "name": "Цвет ожидания (idle). Этот цвет будет использоваться по умолчанию и для режима пульсации, если не запущен другой эффект. Формат hex.",
                    "value": "#000000"
                }
            }
        }
    }
    ```
    - `enable`: Параметр, определяющий включено ли устройство. В данном примере установлено значение `true`, что указывает на то, что устройство включено.
    - `project`: Название проекта `lukoil`
    - `port`: Параметр, указывающий порт, к которому подключено устройство (например, "COM5").
    - `enableRGBPin`: Разрешение на использование пинов red green blue на arduino для - true. При false - используется подключение к светодиодной ленте через data clock. Тип подключения зависит от используемого драйвера светодиодной ленты RGB.
    - `dataPin`: Номер пина data на arduino (по умолчанию 6).
    - `clockPin`: Номер пина clock на arduino (по умолчанию 7).
    - `redPin`: номер пина red на arduino (по умолчанию 3).
    - `green`: номер пина green на arduino (по умолчанию 5).
    - `blue`: номер пина blue на arduino (по умолчанию 6).
    - `idleColor`: Цвет светодиодной ленты после инициализации, представленный в формате HEX (например, "#ede207").
    - `reconnect_Interval_Value`: Интервал переподключения к LED ленте в случае потери соединения, представленный числом (в данном примере значение равно 15 секунд).


Пример подключения пакета к клиентскому приложению
```javascript
const arduinoLed = require('../index.js');

// const config = require('../configs/neuroFluger.json') // Конфиг Neuro-Fluger
const config = require('../configs/lukoil.json') // Конфиг LUKOIL

let isArduinoConnected = false;
// Подключение к устройству Arduino
const arduinoLedConnect = () => {
    if (config.ledArduino.value.enable.value) {
        try {
            // Neuro-Fluger Локальные значения цветов и других параметров
            // const ledArduino = {
            //     project: config?.ledArduino?.value?.project?.value, // Название проекта
            //     port: config?.ledArduino?.value?.port?.value, // Пример порта, замените на фактический
            //     color_Wait: config?.ledArduino?.value?.color_Wait?.value, // Локальное значение цвета для режима ожидания
            //     brightness: config?.ledArduino?.value?.brightness?.value, // Локальное значение яркости
            //     duration: config?.ledArduino?.value?.duration?.value, // Локальное задержки
            //     numLeds: config?.ledArduino?.value?.number_LED?.value, // Локальное значение количества LEDs
            //     reconnectIntervalValue: config?.ledArduino?.value?.reconnect_Interval_Value?.value // Локальное значение интервала переподключения
            // }

            // LUKOIL Локальные значения цветов и других параметров
            const ledArduino = {
                project: config?.ledArduino?.value?.project?.value, // Название проекта
                port: config?.ledArduino?.value?.port?.value, // Пример порта, замените на фактический
                idleColor: config?.ledArduino?.value?.idleColor?.value, // Локальное значение цвета для режима ожидания
                enableRGBPin: config?.ledArduino?.value?.enableRGBPin?.value, // Разрешение на использование пинов red green blue
                dataPin: config?.ledArduino?.value?.dataPin?.value, // Локальное значение data на Arduino
                clockPin: config?.ledArduino?.value?.clockPin?.value, // Локальное значение clock на Arduino
                redPin: config?.ledArduino?.value?.redPin?.value, // Локальное значение red на Arduino
                greenPin: config?.ledArduino?.value?.greenPin?.value, // Локальное значение green на Arduino
                bluePin: config?.ledArduino?.value?.bluePin?.value, // Локальное значение blue на Arduino
                reconnectIntervalValue: config?.ledArduino?.value?.reconnect_Interval_Value?.value // Локальное значение интервала переподключения
            }

            console.log('[LED Arduino] Настройки Arduino для работы светодиодной ленты успешно получены: ', ledArduino);

            arduinoLed.connectArduino(ledArduino);

            // Обработка событий
            arduinoLed.on('open', (data) => {
                console.log('[LED Arduino] ', data);   
                isArduinoConnected = true;          
            });

            arduinoLed.on('close', () => {
                console.log('[LED Arduino] Соединение с Arduino разорвано');
                isArduinoConnected = false;
            });

            arduinoLed.on('error', (error) => {
                console.error(`[LED Arduino] Ошибка: ${error}`);
                isArduinoConnected = false;
            });

            arduinoLed.on('data', (data) => {
                console.log(`[LED Arduino] `, data);
            });

            arduinoLed.on('status-changed',(statusObject) => {
                console.log(`[LED Arduino] `,statusObject);
            });
        } catch (error) {
            console.error('[LED Arduino] В работе модуля LED Arduino возникли ошибки:', error);
            isArduinoConnected = false;
        }
    }
};

// Функция отправляет команды в пакет для вызова сценария свечения светодиодной ленты
const callLedArduinoCommand = (commandType, args) => {
    if (isArduinoConnected && arduinoLed) {
        arduinoLed.callCommand(commandType, args);
    } else {
        console.warn(`[devices] Команда ${commandType} не может быть выполнена: Arduino не подключено.`);
    }
};

arduinoLedConnect();

// Отправляем команд с интервалом для Neuro-Fluger
// setInterval(() => { 
//     callLedArduinoCommand('nav', {color:'#60f50a'}); 
// }, 10000);
// setInterval(() => {
//     callLedArduinoCommand('spec', {color: '#ff3064'});  
// }, 20000); 

// Отправляем команды с интервалом для LUKOIL
setTimeout(() => { 
    callLedArduinoCommand('dimmer', {hexColor:'#FF0000', speed: 10}); 
}, 10000);
setTimeout(() => {
    callLedArduinoCommand('blink', {hexColor: '#0000FF', hexColor2: '#60F50A', speed: 50});  
}, 20000); 
setTimeout(() => {
    callLedArduinoCommand('smoothtransition', {hexColor: '#00ff00', hexColor2: '#FFFF00', speed: 10});  
}, 30000); 
setTimeout(() => { 
    callLedArduinoCommand('staticlighting', {hexColor:'#60f50a'}); 
}, 40000);
setTimeout(() => { 
    callLedArduinoCommand('pulse', {hexColor:'#FF0000', duration: 10}); 
}, 50000);
```

* Предыдущий блок кода демонстрирует пример подключения пакета к клиентскому приложению.

* Функция `arduinoLEDConnect` используется для инициализации подключения к устройству Arduino на основе конфигурационных данных.

* В начале функции проверяется параметр `enable` в конфигурации устройства "ledArduino". Если параметр установлен в `true`, то инициализация подключения выполняется.

* Затем создается объект `ledArduino`, в котором содержатся индивидуальные параметры для каждого проекта для подключения к arduino

* Далее выполняется вызов функции `arduinoLed.connectArduino(ledArduino)`, которая осуществляет подключение к устройству Arduino на основе переданных параметров.

* Затем устанавливаются обработчики событий для различных событий, таких как `open`, `close`, `error`, `data` и `status-changed`. Внутри обработчиков событий выводится соответствующая информация о состоянии подключения, ошибках и полученных данных.

* Если параметр `enable` в конфигурации устройства установлен в `false`, то выводится информация о том, что подключение к модулю Arduino отключено в конфиге.
