Помогите избавиться от GOTO и бесконечных циклов в коде

Newby
Offline
Зарегистрирован: 22.03.2014

Всем привет,

Переписываю скетч Гайвера для управления подсветкой Adalight под ESP8266. Столкнулся с тем, что коде обработки входящих данных используется GOTO  и много замкнутых циклов - на время ожидания поступлениях данных на вход - чтение данных осуществляется по-байтово.

 while (!Serial.available()) check_connection();; //пока нет данных идет отсчет таймера на автоотключение подсветки.

Пока лучшее что я придумал - добавить в этих циклах return - для выхода из функции. Алгоритм работает, но это рушит некоторые моменты типа включения\выключения подсветки.. Также есть риск пропустить что-либо во входящем буфере.

Приведу код оригинального скетча:

#include <FastLED.h>
#define NUM_LEDS 176          // число светодиодов в ленте
#define DI_PIN 14         // пин, к которому подключена лента
#define OFF_TIME 10          // время (секунд), через которое лента выключится при пропадаании сигнала

unsigned long off_timer;
#define serialRate 115200  // скорость связи с ПК
uint8_t prefix[] = {'A', 'd', 'a'}, hi, lo, chk, i;  // кодовое слово Ada для связи

CRGB leds[NUM_LEDS];  // создаём ленту
boolean led_state = true;  // флаг состояния ленты


void setup()
{
FastLED.addLeds<WS2812, DI_PIN, GRB>(leds, NUM_LEDS);  // инициализация светодиодов
 Serial.begin(serialRate);
  Serial.print("Ada\n");     // Связаться с компом
}


void loop() {
if (!led_state) led_state = true;
  off_timer = millis();  

  for (i = 0; i < sizeof prefix; ++i) {
waitLoop: while (!Serial.available()) check_connection();; //как тут избавиться от GOTO? i=-1?
    if (prefix[i] == Serial.read()) continue;
    i = 0;
    goto waitLoop;
  }

while (!Serial.available()) check_connection();;
  hi = Serial.read();
  while (!Serial.available()) check_connection();;
  lo = Serial.read();
  while (!Serial.available()) check_connection();;
  chk = Serial.read();
  if (chk != (hi ^ lo ^ 0x55))
  {
    i = 0;
    goto waitLoop;
  }

 memset(leds, 0, NUM_LEDS * sizeof(struct CRGB));
  for (uint8_t i = 0; i < NUM_LEDS; i++) {
    byte r, g, b;
    // читаем данные для каждого цвета
    while (!Serial.available()) check_connection();
    r = Serial.read();
    while (!Serial.available()) check_connection();
    g = Serial.read();
    while (!Serial.available()) check_connection();
    b = Serial.read();
    leds[i].r = r;
    leds[i].g = g;
    leds[i].b = b;
  }
  FastLED.show();  // записываем цвета в ленту
}


void check_connection() {
  if (led_state) {
    if (millis() - off_timer > (OFF_TIME * 1000)) {
      led_state = false;
      FastLED.clear();
      FastLED.show();
    }
  }
}

пробовал расставлять Сириал принты в каждом цикле - они действительно срабатывают периодически, т.е. получается нельзя заменить все одной проверкой на Serial.available()

 

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Newby пишет:

Всем привет,

Переписываю скетч Гайвера для ...

Вот это к нему

Newby
Offline
Зарегистрирован: 22.03.2014

А причем тут он?) он выложил в открытый доступ "как есть", оно работает. А я уже пытаюсь допилить под свои нужды

b707
Offline
Зарегистрирован: 26.05.2017

ну можно поднапрячься и переписать в структурном стиле без ГОТО......

Вместо этого

  for (i = 0; i < sizeof prefix; ++i) {
waitLoop: while (!Serial.available()) check_connection();; //как тут избавиться от GOTO? i=-1?
    if (prefix[i] == Serial.read()) continue;
    i = 0;
    goto waitLoop;
  }

пишем так:

i = 0;
while (i < sizeof(prefix)) {
  if ((Serial.available()) &&  (prefix[i] == Serial.read())) i++;    
  else { i = 0;
         check_connection();
       }
   } 

вроде ничего не упустил

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Выход из цикла без выхода из функции - break

Newby
Offline
Зарегистрирован: 22.03.2014

b707 пишет:

вроде ничего не упустил

 

Там еще один вызов goto в строке 42 - как его лучше обойти? через флаг? типа

static boolean flag_goto=false; в начале функции

Затем обернуть все до "while (i<sizeof(prefix))"  в if(!flag_goto){} 

а на строке 42 делать flag_goto=true; return; 

Или есть более правильное решение?

b707
Offline
Зарегистрирован: 26.05.2017

Newby пишет:

Там еще один вызов goto в строке 42

я только кусок с 26 по 31 строчку переписывал...

Там у гайвера прилично закручено... думать надо. В том коде. что я переписал - тоже не вполне логика сохранена... надо еще править.

SLKH
Offline
Зарегистрирован: 17.08.2015

b707 пишет:

ну можно поднапрячься и переписать в структурном стиле без ГОТО......

Вместо этого

  for (i = 0; i < sizeof prefix; ++i) {
waitLoop: while (!Serial.available()) check_connection();; //как тут избавиться от GOTO? i=-1?
    if (prefix[i] == Serial.read()) continue;
    i = 0;
    goto waitLoop;
  }

пишем так:

i = 0;
while (i < sizeof(prefix)) {
  if ((Serial.available()) &&  (prefix[i] == Serial.read())) i++;    
  else { i = 0;
         check_connection();
       }
   } 

вроде ничего не упустил

А можно спросить: вариант с goto как-то не так работает, как надо? Ради чего бороться с goto в этом отдельно взятом случае? Просто, чтобы не было этого оператора, или же есть какие-то реальные обоснования?

Newby
Offline
Зарегистрирован: 22.03.2014

Ну изначально задача была убрать бесконечные циклы т.к. иначе при программа могла зависнуть внутри этого goto навсегда - в оригинальном проекте где кроме получения данных по Serial и управления лентой ничего и не надо - то для ESP-варианта нужно обеспечивать wifi соединение, проверять подключенных клиентов, обслуживать веб-сервер с настройками и т.п.

Ну и эстетическая составляющая - когда goto перенаправляет внутрь цикла For - это как-то ппц криво.

Вариант предложенный b707 - работает, дополнил его кодом указынным выше и алгоритм работает нормально, Спасибо. Привожу пример реальной функции:

void loop() {
  yld=millis();  //счетчик времени до йилда
  //соединение с ком
  if (CheckConnection()){ //проверка подключений к TCP серверу
    data=true;
    LedLoop();
  } else{  
data=false;
  Other();
  }
}

void Other() {
  
  check_Led(); //проверка на авто отключение лед из оригинального скетча
  
  portal.tick(); //сервер настроек
  
  yield(); 
  yld=millis();
  
}

void LedLoop() { //основной код управления лед
 
  static boolean flag_goto=false;
    if(!flag_goto){


  if (!led_state) {
    led_state = true; Serial.println("Led is ON");
    }//если лента выкл. то включить и  запустить таймер
    off_timer = millis();

  }
int i=0;
  while( i < sizeof(prefix)){
    if (Sclient.available() && (prefix[i] == Sclient.read())){
      i++;
    } else{
      data=false;
      i=0;
      Other(); //проверям пока другой код
      CheckConnection(); //не отвалился ли клиент
    }
  }
  


  while (!Sclient.available()) { data=false; Other();CheckConnection();} 
     
  hi = Sclient.read();

   while (!Sclient.available()) { data=false; Other();CheckConnection();}
  lo = Sclient.read();

 while (!Sclient.available()) { data=false; Other();CheckConnection();}

  chk = Sclient.read();

  if (chk != (hi ^ lo ^ 0x55)) {
    i = 0;
   flag_goto=true; //замена второго вызова goto
   return;
  } else {flag_goto=false;};
  
  chkYeild("Ledloop before memset"); //проверка на вызов йилда

  memset(leds, 0, sets.NUM_LEDS * sizeof(struct CRGB));
  for (uint8_t i = 0; i < sets.NUM_LEDS; i++) {
    byte r, g, b;
    // читаем данные для каждого цвета
    
     while (!Sclient.available()) { data=false; Other();CheckConnection();}
     r = Sclient.read();
chkYeild("Ledloop led r");

     while (!Sclient.available()) { data=false; Other();CheckConnection(); }
    g = Sclient.read();
chkYeild("Ledloop led g");

    while (!Sclient.available()) { data=false; Other();CheckConnection(); }
    b = Sclient.read();

    leds[i].r = r;
    leds[i].g = g;
    leds[i].b = b;
    chkYeild("Ledloop led b");

//Serial.print("led ");Serial.print(i);Serial.print(": ");Serial.print("R");Serial.print(r);Serial.print("G");Serial.print(g);Serial.print("B");Serial.println(b);
  }
  //chkYeild("Ledloop before ledshow");

  FastLED.show();  // записываем цвета в ленту

Serial.print("exe time: ");
Serial.println(millis()-exetm);
exetm=millis();
}

void chkYeild(String s){ //вызываем йилд раз в 20мс и пишем откуда
  if((millis()-yld)>19){
    Serial.print(s);Serial.print(" :");
  Serial.println(millis()-yld);}
  yield();
  yld=millis();
}

 

Время на отрисовку 180лед по wifi не превышает 120мс, чаще 30-50;

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Как-то управление по сериал отличается от wi-fi?

Newby
Offline
Зарегистрирован: 22.03.2014

не совсем понял.. что значит управление?

Управление происходит программой AmbiBox, при подключении через сериал - она шлет данные напрямую в ком-порт устройства. По файваю Ambibox подключается к виртуальному ком-порту и шлет данные туда, а виртуальный ком-порт уже перекидывает данные на TCP сервер ESP, дальше все тоже самое, т.к. протокол данных задает Амбибокс.