Преобразование уровня шим в текст и отправка в топик

Доброго дня, Форумчане!

Нужна помощь. Вводные: контроллер света для аквариума. ESP8266, MQTT. Мониторю статус устройства, часы в нём, уровень яркости по одному из 16-ти каналов. В приложение на смартфон прилетают время и статус, а вот уровень яркости никак не хочет отображаться. Думаю что как-то криво преобразуется в текст для отправки. В мониторинге порта все данные вижу: 20:09:04.627 → Время: 20:09 | Яркость: 25% (1006 PWM). В виджете времени, на смарте, вижу 20:09:04, а в виджете яркости пустота. Может кто углядит чего корявого в коде. Думаю проблема в самом конце.

#include <ESP8266WiFi.h>
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <time.h>
#include <PubSubClient.h>

// const char* ssid = "ххх"; // Имя точки доступа Wi-Fi
// const char* password = "ххх"; // Пароль от точки доступа

const char* ssid = "ххх"; // Имя 2-й точки доступа Wi-Fi
const char* password = "ххх"; // Пароль от 2-й точки доступа


const char* mqtt_server = "ххх"; // Имя сервера MQTT
const int mqtt_port = ххх;            // Порт для подключения к серверу MQTT  
const char* mqtt_clientId = "ххх";      // определяем ID
const char* mqtt_user = "ххх";     // Логин от сервера 
const char* mqtt_pass = "ххх";     // Пароль от сервера 

const char* mqttTopicDeviceStatus = "aqa/device/status"; // топик статуса устройства
const char* mqttTopicTime = "aqa/device/time";           // топик времени устройства    
const char* mqttTopicBrightness = "aqa/light/channel";   // топик уровня яркости 

const char* mqttDeviceStatusOn = "online";   // статус устройства 
const char* mqttDeviceStatusOff = "offline"; // статус устройства
const int   mqttDeviceStatusQos = 1;
const bool  mqttDeviceStatusRetained = true;

const char* TZ_INFO = "MSK-6"; // Настройка часового пояса 

struct LightSchedule {  
  int dawnStart = 360;  // контрольная точка времени 6:00
  int dawnEnd   = 600;  // контрольная точка времени 10:00
  int duskStart = 960;  // контрольная точка времени 16:00
  int duskEnd   = 1290; // контрольная точка времени 21:30
} schedule;

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); // подключение pca9685
WiFiClient espClient;
PubSubClient client(espClient);

unsigned long lastTimeMsg = 0;
unsigned long lastBrightMsg = 0;
int lastBrightness = -1;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT); 
  digitalWrite(LED_BUILTIN, LOW);
  Serial.begin(115200);
  Wire.begin(4, 5); 
  pwm.begin();
  pwm.setPWMFreq(1000);

  WiFi.begin(ssid, password); // Подключение к WiFi             
  while (WiFi.status() != WL_CONNECTED) { 
    delay(500);
    Serial.print("."); 
  }
  Serial.println("\nWiFi подключен");
  
  configTime(TZ_INFO, "pool.ntp.org", "time.google.com"); // Настройка времени  
  client.setServer(mqtt_server, mqtt_port);               // Настройка MQTT  
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Попытка MQTT подключения...");
    if (client.connect(mqtt_clientId, mqtt_user, mqtt_pass, mqttTopicDeviceStatus, mqttDeviceStatusQos, mqttDeviceStatusRetained, mqttDeviceStatusOff)) {
      Serial.println("успешно");
      client.publish(mqttTopicDeviceStatus, mqttDeviceStatusOn, mqttDeviceStatusRetained); 
    } else {
      Serial.print("ошибка, rc=");
      Serial.print(client.state());
      Serial.println(" ждем 5 сек...");
      delay(5000);
    }
  }
}

void loop() {
  if (!client.connected()) reconnect();
  client.loop();

  time_t now = time(nullptr);
  struct tm* t = localtime(&now);
  
  if (t->tm_year < 70) return; // Ждем синхронизации

  int currentMin = t->tm_hour * 60 + t->tm_min;
  int brightness = 0;

  if (currentMin >= schedule.dawnStart && currentMin < schedule.dawnEnd) {  // Логика рассвета/заката
    brightness = map(currentMin, schedule.dawnStart, schedule.dawnEnd, 0, 4095);
  } 
  else if (currentMin >= schedule.dawnEnd && currentMin < schedule.duskStart) {
    brightness = 4095;
  } 
  else if (currentMin >= schedule.duskStart && currentMin < schedule.duskEnd) {
    brightness = map(currentMin, schedule.duskStart, schedule.duskEnd, 4095, 0);
  } 
  else {
    brightness = 0;
  }

  for (int ch = 0; ch < 16; ch++) {  // Применяем ко всем 16 каналам
    pwm.setPWM(ch, 0, brightness);
  }

  unsigned long now_ms = millis();

  if (now_ms - lastTimeMsg > 60000) { // Отправка времени в топик (раз в 60 секунд)
    lastTimeMsg = now_ms;
    char timeString[9];
    strftime(timeString, sizeof(timeString), "%H:%M:%S", t);
    client.publish(mqttTopicTime, timeString);
  }

  if (now_ms - lastBrightMsg > 5000) {  // Отправка яркости в в топик (раз в 5 секунд при изменении)
    lastBrightMsg = now_ms;
    if (brightness != lastBrightness) {
      int brightnessPercent = map(brightness, 0, 4095, 0, 100); // преобразование в проценты (0-100)
      
      String payload = String(brightnessPercent); 
      client.publish(mqttTopicBrightness, payload.c_str());
      
      Serial.printf("Время: %02d:%02d | Яркость: %d%% (%d PWM)\n", t->tm_hour, t->tm_min, brightnessPercent, brightness);
      lastBrightness = brightness;
    }
  }
}

Попробуй так:

#include <ESP8266WiFi.h>
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <time.h>
#include <PubSubClient.h>

// const char* ssid = "ххх"; // Имя точки доступа Wi-Fi
// const char* password = "ххх"; // Пароль от точки доступа

const char* ssid = "ххх"; // Имя 2-й точки доступа Wi-Fi
const char* password = "ххх"; // Пароль от 2-й точки доступа


const char* mqtt_server = "ххх"; // Имя сервера MQTT
const int mqtt_port = ххх;            // Порт для подключения к серверу MQTT  
const char* mqtt_clientId = "ххх";      // определяем ID
const char* mqtt_user = "ххх";     // Логин от сервера 
const char* mqtt_pass = "ххх";     // Пароль от сервера 

const char* mqttTopicDeviceStatus = "aqa/device/status"; // топик статуса устройства
const char* mqttTopicTime = "aqa/device/time";           // топик времени устройства    
const char* mqttTopicBrightness = "aqa/light/channel";   // топик уровня яркости 

const char* mqttDeviceStatusOn = "online";   // статус устройства 
const char* mqttDeviceStatusOff = "offline"; // статус устройства
const int   mqttDeviceStatusQos = 1;
const bool  mqttDeviceStatusRetained = true;

const char* TZ_INFO = "MSK-6"; // Настройка часового пояса 

struct LightSchedule {  
  int dawnStart = 360;  // контрольная точка времени 6:00
  int dawnEnd   = 600;  // контрольная точка времени 10:00
  int duskStart = 960;  // контрольная точка времени 16:00
  int duskEnd   = 1290; // контрольная точка времени 21:30
} schedule;

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); // подключение pca9685
WiFiClient espClient;
PubSubClient client(espClient);

unsigned long lastTimeMsg = 0;
unsigned long lastBrightMsg = 0;
int lastBrightness = -1;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT); 
  digitalWrite(LED_BUILTIN, LOW);
  Serial.begin(115200);
  Wire.begin(4, 5); 
  pwm.begin();
  pwm.setPWMFreq(1000);

  WiFi.begin(ssid, password); // Подключение к WiFi             
  while (WiFi.status() != WL_CONNECTED) { 
    delay(500);
    Serial.print("."); 
  }
  Serial.println("\nWiFi подключен");
  
  configTime(TZ_INFO, "pool.ntp.org", "time.google.com"); // Настройка времени  
  client.setServer(mqtt_server, mqtt_port);               // Настройка MQTT  
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Попытка MQTT подключения...");
    if (client.connect(mqtt_clientId, mqtt_user, mqtt_pass, mqttTopicDeviceStatus, mqttDeviceStatusQos, mqttDeviceStatusRetained, mqttDeviceStatusOff)) {
      Serial.println("успешно");
      client.publish(mqttTopicDeviceStatus, mqttDeviceStatusOn, mqttDeviceStatusRetained); 
    } else {
      Serial.print("ошибка, rc=");
      Serial.print(client.state());
      Serial.println(" ждем 5 сек...");
      delay(5000);
    }
  }
}

void loop() {
  if (!client.connected()) reconnect();
  client.loop();

  time_t now = time(nullptr);
  struct tm* t = localtime(&now);
  
  if (t->tm_year < 70) return; // Ждем синхронизации

  int currentMin = t->tm_hour * 60 + t->tm_min;
  int brightness = 0;

  if (currentMin >= schedule.dawnStart && currentMin < schedule.dawnEnd) {  // Логика рассвета/заката
    brightness = map(currentMin, schedule.dawnStart, schedule.dawnEnd, 0, 4095);
  } 
  else if (currentMin >= schedule.dawnEnd && currentMin < schedule.duskStart) {
    brightness = 4095;
  } 
  else if (currentMin >= schedule.duskStart && currentMin < schedule.duskEnd) {
    brightness = map(currentMin, schedule.duskStart, schedule.duskEnd, 4095, 0);
  } 
  else {
    brightness = 0;
  }

  for (int ch = 0; ch < 16; ch++) {  // Применяем ко всем 16 каналам
    pwm.setPWM(ch, 0, brightness);
  }

  unsigned long now_ms = millis();

  if (now_ms - lastTimeMsg > 60000) { // Отправка времени в топик (раз в 60 секунд)
    lastTimeMsg = now_ms;
    char timeString[9];
    strftime(timeString, sizeof(timeString), "%H:%M:%S", t);
    client.publish(mqttTopicTime, timeString);
  }

  if (now_ms - lastBrightMsg > 5000) {  // Отправка яркости в в топик (раз в 5 секунд при изменении)
    lastBrightMsg = now_ms;
    if (brightness != lastBrightness) {
      int brightnessPercent = map(brightness, 0, 4095, 0, 100); // преобразование в проценты (0-100)
      
      String payload = String(brightnessPercent); 

      char charsbuf[payload.length() + 1];
      payload.toCharArray(charsbuf, payload.length());
        
      client.publish(mqttTopicBrightness, charsbuf);
      
      Serial.printf("Время: %02d:%02d | Яркость: %d%% (%d PWM)\n", t->tm_hour, t->tm_min, brightnessPercent, brightness);
      lastBrightness = brightness;
    }
  }
}

Спасибо что откликнулись, но всё равно данные не вижу (

Топик точно верно указан?

Сто раз уже перепроверил, сам не пойму в чём загвоздка. Точно также настроен топик часов, только подписка другая

Сделайте строчку с яркостью глобальной переменной

Точнее нет, не так.

Попробуйте вовсе убрать String:

    if (brightness != lastBrightness) {
    int brightnessPercent = map(brightness, 0, 4095, 0, 100); 
      
    char pwm_str[4]; 
    itoa(brightnessPercent, pwm_str);

    client.publish(mqttTopicBrightness, pwm_str);
}
1 лайк

Она же передается как аргумент. В чем смысл делать глобальной (не стёб, просто не знаю)?

Как аргумент передается только указатель. К моменту, когда коллбек выполнится, содержания уже может и не быть.
Но в данном случае строка времени передается так же, и работает. Значит скорее всего не в этом дело.

1 лайк

Ну собственно это я уже выше предлагал…

посмотрим. Пусть ТС попробует

Добавка - твой вариант видел, не считаю его аналогичным моему..

Согласен, пусть пробует.

ругается:

C:\Users\ARD\Documents\Arduino\Proekt_sveta\2_koda_v_odnom\2_koda_v_odnom.ino: In function ‘void loop()’:
C:\Users\ARD\Documents\Arduino\Proekt_sveta\2_koda_v_odnom\2_koda_v_odnom.ino:125:37: error: too few arguments to function ‘char* itoa(int, char*, int)’
125 | itoa(brightnessPercent, pwm_str);
| ^
In file included from C:\Users\ARD\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.1.2\cores\esp8266/Arduino.h:37,
from C:\Users\ARD\AppData\Local\arduino\sketches\11DD388A342D1B081FC45F15CC348559\sketch\2_koda_v_odnom.ino.cpp:1:
C:\Users\ARD\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.1.2\cores\esp8266/stdlib_noniso.h:35:7: note: declared here
35 | char* itoa (int val, char *s, int radix);
| ^~~~
exit status 1

Compilation error: too few arguments to function ‘char* itoa(int, char*, int)’

Основание забыл:
itoa(brightnessPercent, pwm_str, 10);

1 лайк

Заработало!!!

Огромное спасибо!

ВООМ и МММ - Спасибо за помощь!!!

Теперь осталось самое сложное :smiley: - понять чем мой код отличается от кода @MMM (принципиально). Ведь и там и там в функцию передаётся массив char’ов… :thinking:

Разверни мысль, если не сложно. Я что-то «не догоняю».

это как раз понять не сложно - в моем совсем нет промежуточного обьекта String
Другой вопрос, почему этот обьект не работает…

Послушайте, @Резидент, у вас в ИДЕ предупреждения при компиляции включены?

1 лайк

Да - “все”

Отлично.
При компиляции вашего кода или кода @BOOM никаких предупреждений не вылазит?