ARDUINO Попеременный вывод значений куда либо.

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
По мотивам Больших Часов на Светодиодной Ленте (БЧСЛ).

Тестировать буду на atmrga168, выводить данные в монитор порта. Просто так делать вывод не очень интересно. Поэтому 1-ое что сделаю добавлю в код свою любимую функцию подсчета кол-ва циклов loop , которые проходят за 1 секунду. Код получился вот такой:
C++:
void setup() {
  Serial.begin(115200);
}

void loop() {
  ShowFPS();
}

void ShowFPS()
{
  static uint32_t tm_m = millis();
  static uint32_t cnt_m = 0;
  cnt_m++;
  if ((millis() - tm_m) > 1000)
  {
    Serial.print("loop per sec: "); Serial.println(cnt_m);
    cnt_m = 0;
    tm_m = millis();
  }
}
10:48:46.085 -> loop per sec: 265059
10:48:47.068 -> loop per sec: 265330
10:48:48.087 -> loop per sec: 265058
10:48:49.071 -> loop per sec: 265330
10:48:50.086 -> loop per sec: 265058
10:48:51.071 -> loop per sec: 265330
10:48:52.088 -> loop per sec: 265058
10:48:53.069 -> loop per sec: 265330
10:48:54.085 -> loop per sec: 265329

Пустой код крутится 265 тысяч раз в секунду.

Выводить в монитор порта будем время. Что бы не городить огород просто переведем значение millis в часы , минуты и секунды, прошедшие с момента включения.
Все это оформим функцией , которая будет возвращать строку.
Функция вывода строки в монитор порта будет отдельной, нам с ней еще надо будет как то поработать:
C++:
String timeToString()[CODE lang="cpp" ]
{
  char temp[10];
  uint32_t nt=millis()/1000; //получили секунды
  int8_t s=nt%60;
  int8_t m=nt/60%60;
  int8_t h=nt/3600%24;
  snprintf(temp, 10, "%02d:%02d:%02d", h,m,s);
  return String(temp);
}

void displayTime(String s)
{
  Serial.println(s);
}

void loop() {
  displayTime(timeToString());
  ShowFPS();
}
11:06:50.702 -> 00:05:40
11:06:50.702 -> 00:05:40
11:06:50.702 -> 00:05:40
11:06:50.702 -> 00:05:40
11:06:50.702 -> 00:05:40
11:06:50.702 -> 00:05:40
11:06:50.702 -> loop per sec: 1179
11:06:50.702 -> 00:05:40
11:06:50.702 -> 00:05:40
11:06:50.702 -> 00:05:40
11:06:50.702 -> 00:05:40
11:06:50.702 -> 00:05:40
11:06:50.702 -> 00:05:40
11:06:50.735 -> 00:05:40
В результате имеем офигительное замедление работы ( где то в 250 раз!) и много избыточной информации.

Первое, что следует сделать, это ограничить вывод информации , если она не изменилась. Тут можно пойти 2-мя путями
1) Повесить эту обязанность на displayTime примерно вот так:
C++:
void displayTime(String s)
{
  static String old_s="";
  if (old_s!=s)
    {
    Serial.println(s);
    old_s=s;
    }
}
Мы запоминаем строку, которую вывели в последний раз и новую строку сравниваем с тем, что запомнили, вывод только в случае если эти строки не равны.
И сразу стало в 2 раза быстрее. Но в 100 раз хуже чем было сначала.
Плюс этого варианта в том, что он универсальный и будет работать на любую строку , которую мы хотим вывести куда -либо.
Минус в том, что размер строки может быть очень большим и хранить все это придется в оперативной памяти. А ее всегда мало.
Да и работает весьма медленно.
2) Второй вариант ввести в программу флаг, который будет говорить о том, что что то на выходе поменялось.
А так же возводить его, когда секунды поменялись, в данном примере этого достаточно , в более сложном может потребоваться сравнивать и минуты.
C++:
bool hasChange=false;
...
String timeToString()
{
  char temp[10];
  uint32_t nt = millis() / 1000; //получили секунды
  static int8_t old_s=255;
  int8_t s = nt % 60;
  if (s!=old_s)
    {
    old_s=s;
    hasChange=true;
    }
  else return"";
  int8_t m = nt / 60 % 60;
  int8_t h = nt / 3600 % 24;
  snprintf(temp, 10, "%02d:%02d:%02d", h, m, s);
  return String(temp);
}


...

void displayTime(String s)
{
  if (!hasChange) return;
  Serial.println(s);
hasChange=false;
}
11:22:58.778 -> 00:00:04
11:22:58.778 -> loop per sec: 9794
11:22:59.757 -> 00:00:05
11:22:59.790 -> loop per sec: 9802
Результат еще лучше, но все же далек от начального. Теперь посмотрим функцию получения времени, мы обрабатываем ее почти 10 тысяч раз в секунду, а выводим с точностью 1 секунда. Значит период преобразование можно смело снизить скажем до 10 раз в секунду. Точность вывовда будет в районе 0.1 секунды, что нам выше крыши, можно и еще снизить. пробуем, добавив в функцию преобразования времени в число такие (стандартные для меня) строки:
C++:
  static uint32_t tmr1=millis();
  if (millis()-tmr1<100) return "";
  tmr1=millis();
И получим:
11:29:23.065 -> loop per sec: 31995
11:29:24.047 -> 00:00:09
11:29:24.047 -> loop per sec: 31963
11:29:25.030 -> 00:00:10
Что уже не плохо, и дает достаточно времени на обработку остальных функций , энкодера , кнопок.
Увеличив интервал опроса до 500 мы почти не изменим скорость вывода т.к. через раз будет изменение секунд.

Теперь добавим новую функцию, вывод температуры.
Сам датчик подключаться не будет, просто будем брать температуру равной 25 градусам.
Сразу ограничим получение температуры 1-ой секундой:
String TemperToString()
{
static uint32_t tmr1=millis();
if (millis()-tmr1<1000) return "";
tmr1=millis();

hasChange=true;
return "25°";
}
И добавим еще пару таких заглушек, для уличной температуры и для давления:
C++:
String TemperOutToString()
{
  static uint32_t tmr1=millis();
  if (millis()-tmr1<1000) return "";
  tmr1=millis();

  hasChange=true;
  return "-5°";
}

String PressToString()
{
  static uint32_t tmr1=millis();
  if (millis()-tmr1<1000) return "";
  tmr1=millis();

  hasChange=true;
  return "745mm";
}
Теперь надо как то сделать вывод всех этих данных по очереди с определенной длительностью
Для этого в голове (или константами) пронумеруем данные:
0 - часы, хотим выводить их 4 секунды
1 - температура дома, 2 сек
2 - температура улицы, 2 сек
3 - давление, 2 секунды

И сделаем под это дело массив:
uint8_t periodDisplay[]={4,2,2,2};
и массив порядка вывода:
uint8_t orderDisplay[]={0,1,2,3};
Введем переменную, которая будет говорить нам о том, что сейчас выводим:
uint8_t mode=0;

Внесем некоторые правки в цикл loop, введем статическую переменную для хранения времени выполнения текущего режима, а так же выбор того, что надо выводить:
C++:
void loop() {
  static uint32_t ch_tmr = millis();

  if ((millis() - ch_tmr) < (periodDisplay[mode] * 1000))
  {
    switch (mode) {
      case 0:
        displayTime(timeToString());
        break;
      case 1:
        displayTime(TemperToString());
        break;
      case 2:
        displayTime(TemperOutToString());
        break;
      case 3:
        displayTime(PressToString());
        break;
      default:
        mode = 0;
        break;
    }
  }
  else
  {
    ch_tmr = millis();
    mode++;
  }

  ShowFPS();
}
Такой код уже работает:
11:59:57.667 -> 00:03:03
11:59:57.837 -> loop per sec: 27434
11:59:58.648 -> 25°
11:59:58.852 -> loop per sec: 27481
11:59:59.667 -> 25°
11:59:59.837 -> loop per sec: 27610
12:00:00.650 -> -5°
12:00:00.853 -> loop per sec: 27574
12:00:01.633 -> -5°
12:00:01.836 -> loop per sec: 27278
12:00:02.649 -> 745mm
12:00:02.851 -> loop per sec: 27261
12:00:03.664 -> 745mm
12:00:03.832 -> loop per sec: 27046
12:00:04.643 -> 00:03:10
12:00:04.845 -> loop per sec: 27136
12:00:05.658 -> 00:03:11
12:00:05.828 -> loop per sec: 27405
12:00:06.639 -> 00:03:12
12:00:06.843 -> loop per sec: 27434
12:00:07.655 -> 00:03:13
Но у него есть маленькая, досадная ошибка. В один из моментов времени переменная mode становиться равной 4 и periodDisplay[mode] берет данные с луны. Что может очень навредить лунатикам и жителям планеты ардуино. Решить это можно проверкой переполнения после строки mode++; а можно добавить всего один байт в массив periodDisplay:
uint8_t periodDisplay[] = {4, 2, 2, 2,0};
А т.к. давление и температура будет вряд ли меняться быстрее градуса в 2 секунды, то можно увеличить их интервал обновления, на примере темпрературы:
C++:
String TemperToString()
{
  static uint32_t tmr1 = millis()-periodDisplay[1]*1000;
  if (millis() - tmr1 < (periodDisplay[1]*1000)) return "";
  tmr1 = millis();

  hasChange = true;
  return "25°";
}
В начале функции изменена инициализация начального значения таймера, что бы при старте он сразу выполнил свою функцию, без нее 1-ый цикл будет сбойный.
Вот результат выполнения кода:
12:12:48.856 -> 00:01:31
12:12:49.840 -> 00:01:32
12:12:50.857 -> 00:01:33
12:12:51.839 -> 25°
12:12:53.840 -> -5°
12:12:55.844 -> 745mm
12:12:57.847 -> 00:01:40
12:12:58.831 -> 00:01:41
12:12:59.850 -> 00:01:42
12:13:00.835 -> 00:01:43
12:13:01.820 -> 25°
12:13:03.817 -> -5°
12:13:05.813 -> -5°
12:13:05.813 -> 745mm
12:13:07.843 -> 745mm
12:13:07.843 -> 00:01:50
Как видно из него иногда проскакивают двойные значения температуры и давления, это связано с неким дребезгом миллис. Избавиться можно изменив период проверки, несколько увеличив его изменив в строке:
if (millis() - tmr1 < (periodDisplay[1]*1000)) return "";
1000 на скажем 1100. Результат показывать не буду, но поверьте, так работает как надо.

Но увлекшись выводом я совсем забыл про orderDisplay. Он нам нужен для выставления желаемого нам порядка прохождения. Внесем сл. изменения в цикл loop:
1) выбор switch идет уже по данным из массива orderDisplay
2) текущую проверку интервала идет сложнее, по данным из массива из массива. Да, вот так.
3) Все таки сделал проверку режима на выход за пределы границы массива orderDisplay, т.к. концепция немного усложнилось. Лишний 0 из массива интревалов. убрал

C++:
void loop() {
  static uint32_t ch_tmr = millis();

  if ((millis() - ch_tmr) < (periodDisplay[orderDisplay[mode]] * 1000)) //текущуая проверка интервала
  {
    switch (orderDisplay[mode]) { // выбор
      case 0:
        displayTime(timeToString());
        break;
      case 1:
        displayTime(TemperToString());
        break;
      case 2:
        displayTime(TemperOutToString());
        break;
      case 3:
        displayTime(PressToString());
        break;
    }
  }
  else
  {
    ch_tmr = millis();
    mode++;
    if (sizeof(orderDisplay)==mode) mode=0;
  }

//  ShowFPS();
}
Что это дает и для чего такие сложности? Изменив массив порядка вывода к примеру на такой:
uint8_t orderDisplay[] = {0, 1, 0, 2, 0, 3};
Получим следующий вывод данных:
12:27:07.450 -> 00:03:31
12:27:08.436 -> 00:03:32
12:27:09.453 -> 00:03:33
12:27:10.434 -> 745mm
12:27:12.436 -> 00:03:36
12:27:13.421 -> 00:03:37
12:27:14.441 -> 00:03:38
12:27:15.425 -> 00:03:39
12:27:16.411 -> 25°
12:27:18.413 -> 00:03:42
12:27:19.431 -> 00:03:43
12:27:20.414 -> 00:03:44
12:27:21.434 -> 00:03:45
12:27:22.418 -> -5°
12:27:24.419 -> 00:03:48
12:27:25.434 -> 00:03:49
12:27:26.415 -> 00:03:50
12:27:27.433 -> 00:03:51
12:27:28.415 -> 745mm
12:27:30.410 -> 00:03:54
12:27:31.424 -> 00:03:55
12:27:32.405 -> 00:03:56
Как видите данные температур и давления чередуются с данными времени
А если сделать
uint8_t orderDisplay[] = {0, 1, 0, 3};
то внешняя температура вообще не будет отображаться.
Цикл loop крутиться 27 тысяч раз в секунду, что вполне хватит для стабильной работы Гайвер-Энкодера и Гайвер-Батона без прерываний.

И последний момент. Мигание точек.
Лично я бы их вывел вообще отдельным пунктом. Но можно и в функцию получения времени. Сейчас функция получения времени вызывается 2 раза в секунду, что нужно для "быстрого" мигания точек. Примерный алгоритм реализации:
Что бы иметь возможность управлять точками не только из функции получения времени заводим глобальную переменную bool showDot=false;
Добавляю 2-ую функцию (что бы не терять 1-ый вариант, а так совсем не обязательно):
C++:
String timeToStringDots()
{
  static uint32_t tmr1 = millis();
  if (millis() - tmr1 < 500) return "";
  tmr1 = millis();

  char temp[10];
  uint32_t nt = millis() / 1000; //получили секунды
  static int8_t old_s = 255;
  int8_t s = nt % 60;
  showDot = !showDot;
  hasChange = true;

  int8_t m = nt / 60 % 60;
  int8_t h = nt / 3600 % 24;
  if (showDot)
    snprintf(temp, 10, "%02d:%02d:%02d", h, m, s);
  else
    snprintf(temp, 10, "%02d %02d %02d", h, m, s);
  return String(temp);
}
....
//кусок цикла loop
    switch (orderDisplay[mode]) {
      case 0:
        displayTime(timeToStringDots());
        break;
      case 1:
// конец куска
И в результате вывод будет такой:
12:38:07.646 -> 00:00:00
12:38:08.120 -> 00 00 01
12:38:08.627 -> 00:00:01
12:38:09.135 -> 00 00 02
12:38:09.611 -> 00:00:02
12:38:10.118 -> 00 00 03
12:38:10.627 -> 00:00:03
12:38:11.338 -> 25°
12:38:13.133 -> 00 00 06
12:38:13.608 -> 00:00:06
12:38:14.116 -> 00 00 07
12:38:14.626 -> 00:00:07
12:38:15.135 -> 00 00 08
12:38:15.607 -> 00:00:08
12:38:16.115 -> 00 00 09
12:38:16.625 -> 00:00:09
12:38:17.303 -> -5°
На этом утреннюю писанину считаю законченной. Код можно оптимизировать как душе угодно, избавиться от String например. Использовать в своих целях (любых, как коммерческих так и нет) со ссылкой на автора разумеется.
Материал выдан для обучения. Итоговый код в прикреплении.

UPD. Для небольшого разнообразия сделал температуру и давление случайными при каждом получении.
 

Вложения

Изменено:
  • Лойс +1
Реакции: foka44 и ASM

ASM

★★★★★✩✩
26 Окт 2018
1,609
317
Надо будет поизучать, текста много)
Благодарим)
 

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632
void displayTime(String s) { static String old_s=""; if (old_s!=s) { Serial.println(s); old_s=s; } }
А так, на сколько быстро?
C++:
// Функция реагирует на изменения двух последних символов в строке, в принципе ее можно натравить на любые позиции символов
void displayTime(String s)
{
  static byte chs;
  if (((int)s.charAt(s.length()-1)^(int)s.charAt(s.length()) != chs)
    {
    Serial.println(s);
    chs=((int)tmp.charAt(s.length()-1)^(int)s.charAt(s.length());
    }
}
 
Изменено:
  • Лойс +1
Реакции: Старик Похабыч

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Не знаю, можно делать эксперименты и засекать время. Я таким занимаюсь когда все уже готово и надо как то оптимизировать код, где то под конец. Для данного конкретного случая такой вариант по идее должен быть хорош, но он не учитывает мигание точек, т.е. надо брать 3 символа. Можно просто смотреть на то, что поменялись точки и все, для данной задачи этого достаточно, но могут быть и другие задачи.
Я остановился на всей строке, т.к. что бы я выше не менял, не добавлял - будет работать, т.е. я пожертвовал скоростью в угоду универсальности и упрощения кода. Иногда надо наоборот.
 

bort707

★★★★★★✩
21 Сен 2020
3,058
910
Вот этот момент не понял:
Но у него есть маленькая, досадная ошибка. В один из моментов времени переменная mode становиться равной 4 и periodDisplay[mode] берет данные с луны. Что может очень навредить лунатикам и жителям планеты ардуино. Решить это можно проверкой переполнения после строки mode++; а можно добавить всего один байт в массив periodDisplay:
uint8_t periodDisplay[] = {4, 2, 2, 2,0};
и как тебе поможет добавление еще одного элемента в массив? От необходимости нормировать mode по основанию 4 это никак не избавляет, а то ведь оно может стать и 5 и 6 ... Или это на случай превышения синусом значения 1 ? :) по принципу "как бы чего не вышло"?
Такой строчки разве не достаточно?
C++:
if (++mode > 3) mode =0;
она все равно у тебя в коде где-то должна быть...
 
Изменено:

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Это промежуточный вариант был, я писал на ходу как есть. Так вот доп элемент помогал не выйти за рамки массива, не залезть на чужую память. Далее я изменил принцип и от этого элемента избавился.
 

bort707

★★★★★★✩
21 Сен 2020
3,058
910
не знаю... по-моему в учебном тексте таким "элементам" не место... Новички же копировать начнут...
Решение "добавим элемент к массиву" - некрасивое и бессмысленное в плане логики кода, как мне кажется
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Ну почему же. Последствия ошибочных решений тоже должны быть как то объяснены и понимаемы. И есть конечный пример.
Многие по незнанию такого наворотят, а потом столькими палками подопрут и работает. Так вот "0" - это палка, подпирающая забор. Я ее потом убрал.
 

bort707

★★★★★★✩
21 Сен 2020
3,058
910
И второе... раз уж это поучениье для новичков. Из функции обработки мигания точек вывод времени лучше убрать. Я имею в виду в примере. Он там по смыслу никак не нужен, кроме этого все заканчивается вот таким знаменательным кодом:
C++:
if (showDot)
    snprintf(temp, 10, "%02d:%02d:%02d", h, m, s);
  else
    snprintf(temp, 10, "%02d %02d %02d", h, m, s);
боролись-боролись за скорость. а сами добавляем условия. которые ничего не выбирают :)
 

bort707

★★★★★★✩
21 Сен 2020
3,058
910
Многие по незнанию такого наворотят, а потом столькими палками подопрут и работает. Так вот "0" - это палка, подпирающая забор. Я ее потом убрал.
Ок. возможно...
только, в итоге, твой творческий процесс остался за кадром, а в топике остался странный выверт, ничем, на мной взгляд, не обоснованный... Я бы его убрал.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Боролся я за бессмысленное повторение операций. Есть смысл выводить на экран данные 120 раз в секунду, если содержимое меняется 2 раза в секунду ?

Конструкция с showDot мигает точками. Что значит ничего не выбирает ?

в итоге, твой творческий процесс остался за кадром
Ну вот в комментах прояснили. Но судя по всем никто ничего не понял :( Т.к. вопросов нет вообще
 

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632

@bort707,
Они там реально разные. :)

@Старик Похабыч, я бы вообще вот так сделал
mode++;
massive[mode%3];
И пущай себе тикает до переполнения.

И еще, раз уж так память важна. Для коротких интервалов до 255мс, типа дебонс кнопки, или счетчика кликов

uint8_t timer = (uint8_t) millis ();

if ((uint8_t) millis () - timer >= period) {}

Ну и uint16_t для интервалов побольше. А то так на всех по 4байта на таймер не напасешся.
 
Изменено:
  • Лойс +1
Реакции: Старик Похабыч

bort707

★★★★★★✩
21 Сен 2020
3,058
910
Ну и uint16_t для интервалов побольше. А то так на всех по 4байта на таймер не напасешся.
практика показывает. что экономия на спичках (2 байта на таймерах) смысла не имеет. Нередко рядом с подобным счетчиком, бережливо обьявленным в uint16 - видишь массив константных строк байт на 300, которые автор и не подумал убрать в ПРОГМЕМ :)

Я в своих программах все таймеры по умолчанию обьявляю uint32. , В подавлющем большинстве скетчей нет смысла ловить каждый байт - обычно памяти хватает с запасом.Зато не надо помнить, что там обьявлено и хватит ли размера. Ловить переполнение переменной в процессе работы программы - одна из самых сложных ошибок.
Если возникнет тот редкий случай. что для впихивания программы в память будет не хвтать десятка байт - вот тогда и буду уменьшать размер таймеров, если будет нужно.
 
  • Лойс +1
Реакции: Старик Похабыч

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632

@bort707,
И еще, раз уж так память важна.
А так то я не настаиваю. ;)
Вообще-то, в одном моем проекте 28 таймеров тикают, из них только четыре в uint16_t не помещаются. И четыре вполне в uint8_t помещаются (дебонс кнопок, охранного периметра, и ответ модема).
Так то 112байт против 60-ти. Неплохой коробок спичек набегает. :)
 
Изменено:

ASM

★★★★★✩✩
26 Окт 2018
1,609
317
uint8_t periodDisplay[]={4,2,2,2};
приветствую)
возникла необходимость засунуть эту строку в структуру, но при этом стала выбивать ошибку
Код:
error: too many initializers for 'uint8_t [0] {aka unsigned char [0]}'
uint8_t periodDisplay[] = {6,2,2,2,2};
мне нужно periodDisplay сохранять в eeprom через GyverPortal, но uint же не засунешь никуда)
Код:
error: invalid user-defined conversion from 'uint8_t [5] {aka unsigned char [5]}' to 'const String&' [-fpermissive]
     GP.TEXT("periodDisplay", "", periodDisplay);
или так)
Код:
error: call of overloaded 'String(uint8_t [5])' is ambiguous
     GP.TEXT("periodDisplay", "", String (periodDisplay));
если со структурой никак не получится, могу просто на periodDisplay выделить отдельно EEManager mem5(periodDisplay);
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Что то ничего не понял :D
Структура должна четко определять свой объем памяти. Т.е. вот так можно:
C++:
struct WiFiConfig
{
  uint8_t ssid[4] = {1,2,3,4};  
  char www_username[10] = DEF_LOGIN;
  char www_password[10] = DEF_PSWD;
};
а если размер массива не определить, то будет ошибка
 

ASM

★★★★★✩✩
26 Окт 2018
1,609
317
а если размер массива не определить, то будет ошибка
а если размер будет меняться, кому-то не нужны будут все режимы, а поставит 1-2 то проблем не будет с моими пятью?) [5]

в html два варианта input text и input number, вот куда-то надо спрятать этот
uint8_t ssid[4] = {1,2,3,4};
вариант выше чуть позже проверю)
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Ты хочешь задавать это все в html и закидывать в память ?
Возьми на подобии char www_username[10] = DEF_LOGIN; Только побольше размер,под максимум.
С html получаешь строку вида "1,2,3,4,5", парсишь ее и уже в массив структуры кидаешь уже числа, заканчивая все это числом, которое не используется. Ну я так вижу.
 

ASM

★★★★★✩✩
26 Окт 2018
1,609
317
Ты хочешь задавать это все в html и закидывать в память ?
да
вариант с uint8_t periodDisplay[5] = {6,2,2,2,2}; отлично скомпилировался)
теперь надо как-то засунуть сюда
C++:
GP.TEXT("periodDisplay", "", String (c.periodDisplay));
пробую преобразовать в строку, получаю
Код:
call of overloaded 'String(uint8_t [5])' is ambiguous
а если использовать без стринг
C++:
GP.TEXT("periodDisplay", "", c.periodDisplay);
то получаю
Код:
error: invalid user-defined conversion from 'uint8_t [5] {aka unsigned char [5]}' to 'const String&' [-fpermissive]
 

ASM

★★★★★✩✩
26 Окт 2018
1,609
317
вот такой вариант испытал
C++:
unsigned char periodDisplay[5] = {6,2,2,2,2};
GP.TEXT("periodDisplay", "", c.periodDisplay.T_CSTR);
Код:
request for member 'T_CSTR' in 'c.Clock::periodDisplay', which is of non-class type 'unsigned char [5]'
а что если преобразовать так?
C++:
byte a,b,c,d,e;
unsigned char periodDisplay[5] = {a,b,c,d,e};
если с интервалами вроде понятно, выключатели организовать, то с последовательностью как будет (orderDisplay[] = {0,1,2,3,4};), что-то не понятно...
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
GP.TEXT("periodDisplay", "", c.periodDisplay.T_CSTR) требует 3 параметра, и все строковые как я понимаю.
Напиши функцию, которая будет возвращать строку получая на входе массив с числами внутри.