Помогите избавиться от GOTO и бесконечных циклов в коде
- Войдите на сайт для отправки комментариев
Всем привет,
Переписываю скетч Гайвера для управления подсветкой 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()
Всем привет,
Переписываю скетч Гайвера для ...
Вот это к нему
А причем тут он?) он выложил в открытый доступ "как есть", оно работает. А я уже пытаюсь допилить под свои нужды
ну можно поднапрячься и переписать в структурном стиле без ГОТО......
Вместо этого
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(); } }вроде ничего не упустил
Выход из цикла без выхода из функции - break
вроде ничего не упустил
Там еще один вызов goto в строке 42 - как его лучше обойти? через флаг? типа
static boolean flag_goto=false; в начале функции
Затем обернуть все до "while (i<sizeof(prefix))" в if(!flag_goto){}
а на строке 42 делать flag_goto=true; return;
Или есть более правильное решение?
Там еще один вызов goto в строке 42
я только кусок с 26 по 31 строчку переписывал...
Там у гайвера прилично закручено... думать надо. В том коде. что я переписал - тоже не вполне логика сохранена... надо еще править.
ну можно поднапрячься и переписать в структурном стиле без ГОТО......
Вместо этого
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 навсегда - в оригинальном проекте где кроме получения данных по 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;
Как-то управление по сериал отличается от wi-fi?
не совсем понял.. что значит управление?
Управление происходит программой AmbiBox, при подключении через сериал - она шлет данные напрямую в ком-порт устройства. По файваю Ambibox подключается к виртуальному ком-порту и шлет данные туда, а виртуальный ком-порт уже перекидывает данные на TCP сервер ESP, дальше все тоже самое, т.к. протокол данных задает Амбибокс.