Введение
В комплекте есть как простые одноцветные светодиоды (красный, зелёный, жёлтый, по 5 штук каждого), так и RGB-модуль, объединяющий три светодиода (красный, зелёный, синий) в одном корпусе, что позволяет получать произвольные цвета через смешивание. В статье разберём управление яркостью отдельных светодиодов и цветом RGB-модуля через ШИМ, а также передачу выбранного пользователем цвета из Qt-интерфейса.
Концепция
Яркость светодиода, подключённого к ШИМ-совместимому пину, регулируется через analogWrite(pin, value), где значение от 0 до 255 определяет скважность импульсов, воспринимаемую глазом как пропорциональную яркость свечения. RGB-модуль управляется тем же способом, но независимо для каждого из трёх цветовых каналов — комбинация трёх значений яркости (R, G, B) определяет итоговый воспринимаемый цвет, что напрямую соответствует модели RGB, используемой и в Qt при работе с QColor.
Пример кода
// firmware/src/main.cpp — управление RGB-модулем и отдельными светодиодами
#define RED_PIN 9
#define GREEN_PIN 10
#define BLUE_PIN 11
#define STATUS_LED_PIN 6
void setup()
{
Serial.begin(9600);
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
pinMode(STATUS_LED_PIN, OUTPUT);
delay(100);
Serial.println("READY");
}
void loop()
{
static String inputBuffer = "";
while (Serial.available() > 0) {
char c = Serial.read();
if (c == 'n') {
processCommand(inputBuffer);
inputBuffer = "";
} else if (inputBuffer.length() < 64) {
inputBuffer += c;
}
}
}
void processCommand(String command)
{
command.trim();
if (command.startsWith("RGB:")) {
// Формат команды: RGB:255,128,0
int r, g, b;
sscanf(command.c_str() + 4, "%d,%d,%d", &r, &g, &b);
analogWrite(RED_PIN, constrain(r, 0, 255));
analogWrite(GREEN_PIN, constrain(g, 0, 255));
analogWrite(BLUE_PIN, constrain(b, 0, 255));
Serial.println("ACK:RGB");
} else if (command.startsWith("LED:BRIGHTNESS:")) {
int brightness = constrain(command.substring(15).toInt(), 0, 255);
analogWrite(STATUS_LED_PIN, brightness);
Serial.println("ACK:LED:BRIGHTNESS");
}
}
// rgbcontrolwidget.h — выбор цвета через стандартный QColorDialog
#pragma once
#include <QWidget>
#include <QPushButton>
#include <QColorDialog>
#include "arduinoconnection.h"
class RgbControlWidget : public QWidget
{
Q_OBJECT
public:
explicit RgbControlWidget(ArduinoConnection *connection, QWidget *parent = nullptr)
: QWidget(parent), m_connection(connection)
{
auto *pickColorButton = new QPushButton("Выбрать цвет", this);
auto *layout = new QVBoxLayout(this);
layout->addWidget(pickColorButton);
connect(pickColorButton, &QPushButton::clicked, this, [this]() {
const QColor color = QColorDialog::getColor(Qt::white, this, "Цвет RGB-светодиода");
if (color.isValid()) {
m_connection->sendCommand(QString("RGB:%1,%2,%3")
.arg(color.red()).arg(color.green()).arg(color.blue()));
}
});
}
private:
ArduinoConnection *m_connection;
};
Пояснения к коду
Команда RGB:255,128,0 разбирается через sscanf() — компактный способ извлечь три целых числа из строки за один вызов без последовательного разделения по запятым вручную; результат сразу применяется через три отдельных вызова analogWrite(), каждый со своим ограничением через constrain() для защиты от некорректных значений. RgbControlWidget показывает удобство использования стандартного QColorDialog — пользователь выбирает цвет через привычный, нативный диалог выбора цвета операционной системы, и Qt-приложению остаётся только извлечь компоненты red()/green()/blue() выбранного QColor и переслать их в виде той же текстовой команды формата протокола.
Подводные камни
- Не все три ШИМ-пина RGB-модуля физически совместимы с ШИМ на конкретной модели Arduino — на Arduino Uno только определённые пины (3, 5, 6, 9, 10, 11) поддерживают
analogWrite()с полноценной ШИМ-функциональностью; при использовании не-ШИМ пинаanalogWrite()либо не скомпилируется с ожидаемым поведением, либо даст только цифровое включение/выключение без промежуточных уровней яркости. - Использование
sscanf()без проверки количества успешно распознанных значений (возвращаемое значениеsscanf— число успешно прочитанных полей) — если команда пришла в искажённом виде (например, не все три числа присутствуют),sscanfоставит часть переменныхr,g,bнеинициализированными, что приведёт к использованию случайного «мусорного» значения памяти вместо ожидаемого числа — для надёжности стоит проверять возвращаемое значениеsscanf()перед использованием результатов. - Несовпадение цветовой модели восприятия человеком и линейной шкалы ШИМ — равные шаги изменения значения ШИМ (например, от 10 до 20 против от 200 до 210) воспринимаются глазом не как равные изменения яркости, поскольку восприятие яркости человеком нелинейно; для более визуально равномерного управления яркостью через слайдер в Qt-интерфейсе иногда применяют гамма-коррекцию значения перед отправкой на Arduino, а не передают линейное значение слайдера напрямую.
- Длительная работа нескольких светодиодов на максимальной яркости одновременно увеличивает суммарное энергопотребление, что при питании всей схемы от USB-порта компьютера (ограниченного по току) или маломощного источника может вызвать нестабильность напряжения, влияющую на остальную часть схемы — для проектов с несколькими одновременно ярко горящими светодиодами и другими модулями стоит явно учитывать суммарное потребление тока относительно возможностей источника питания.