Strip.clear() не очищает/отключает полосу NeoPixel после сброса ESP8266.

В приведенном ниже коде, который работает на WEMOS D1 (ESP8266), полоска NeoPixel включает 1 светодиод красного цвета, который перемещается слева направо, а затем справа налево, пока плата пытается подключиться к Wi-Fi в while (WiFi.status() != WL_CONNECTED) цикл.

Если я удержу кнопку «SW», подключенную к контакту D7, и нажму кнопку сброса, код включит 7-сегментный дисплей, однако светодиод на полоске NeoPixel, который светился последним, останется включенным (как будто он «замороженный»). Как только я выхожу из цикла while(configRPM > 0){, все возвращается в норму, и полоса NeoPixel размораживается.

Я думал, что strip.clear() очистит полосу NeoPixel в начале setup(), но, похоже, это не сработало. Я думал, что добавление задержки может исправить это, но это не так. Почему полоска NeoPixel не выключается при перезагрузке платы и как это исправить?

Вот код:

#include <ESP8266WiFi.h> // Подключаем библиотеку ESP8266WiFi.
#include <ELMduino.h> // Подключаем библиотеку ELMduino.
#include <Adafruit_NeoPixel.h> // Подключаем библиотеку Adafruit NeoPixel.
#include <Encoder.h>
#include <TM1637Display.h>
#include <EEPROM.h>

// Определите адреса, по которым вы хотите хранить переменные в EEPROM
#define ADDR_MINIMUMRPM 0
#define ADDR_MAXIMUMRPM (ADDR_MINIMUMRPM + sizeof(int))

#define CLK D1
#define DT D2
#define SW D7

#define CLK_TM1637 D5
#define DIO_TM1637 D6

TM1637Display display(CLK_TM1637, DIO_TM1637);
Encoder encoder(CLK, DT);

int configTriggered = 0;
int buttonReleased = 0;
int configRPM = 0;
int minimumRPM = 2500;
int maximumRPM = 6000;
int currentPosition = minimumRPM - 100;
int oldPosition = -1000;
int newPosition = 0;

// Учетные данные Wi-Fi и сведения о сервере
const char* ssid = "WIFI_OBDII"; // Установите SSID ELM327.
WiFiClient client; // WiFiClient — это класс, определяющий, как создавать объекты, представляющие клиентов Wi-Fi. client — объект этого класса, позволяющий ESP8266 работать как клиент WiFi.
ELM327 myELM327; // Создает объект с именем myELM327 класса ELM327. Этот объект представляет собой экземпляр интерфейса ELM327 OBD-II, позволяющий программе взаимодействовать с ним, используя методы, предоставляемые классом ELM327.

const int neopixel_Count = 16; // Указывает, что в полосе имеется 16 NeoPixel с идентификаторами от 0 до 15.
Adafruit_NeoPixel strip(neopixel_Count, D8, NEO_GRB + NEO_KHZ800); // Инициализирует объект полосы NeoPixel с 16 NeoPixel, подключенными к выводу D2, используя порядок цветов Зеленый-Красный-Синий и частоту передачи данных 800 КГц.

const int potentiometer_Pin = A0; // Устанавливает постоянную переменную potentiometer_Pin в значение A0, которое является идентификатором аналогового контакта

int rpm = 0; // Переменная RPM, которая берется из автомобиля и используется в коде.

void setup() { // Функция настройки для инициализации NeoPixels и установления соединений Wi-Fi и ELM327.
  pinMode(SW, INPUT_PULLUP);
  Serial.begin(9600);
  display.clear();
  
  strip.begin(); // Инициализируем NeoPixels.
  delay(100);
  strip.clear(); // Инициализируем все пиксели как «выключенные».
  
  EEPROM.begin(sizeof(minimumRPM) + sizeof(maximumRPM));
  EEPROM.get(ADDR_MINIMUMRPM, minimumRPM);
  EEPROM.get(ADDR_MAXIMUMRPM, maximumRPM);

  if (digitalRead(SW) == LOW){
    configRPM = 1;
    display.setBrightness(0x0f); // Устанавливаем яркость на максимум
    delay(50);
  }
  
  while(configRPM > 0){
    if (digitalRead(SW) == HIGH) {
      buttonReleased = 1;
      delay(50);
    }
    
    if (digitalRead(SW) == LOW && buttonReleased == 1 && configRPM == 1) {
      buttonReleased = 0;
      configRPM = 2;
      minimumRPM = currentPosition;
      Serial.println("Value saved for minimum RPM is " + String(minimumRPM));
      currentPosition = maximumRPM;
      display.showNumberDec(currentPosition);
      delay(50); // Задержка устранения дребезга
    }

    if (digitalRead(SW) == LOW && buttonReleased == 1 && configRPM == 2) {      
      buttonReleased = 0;
      configRPM = 0;
      maximumRPM = currentPosition;
      Serial.println("Value saved for maximum RPM is " + String(maximumRPM));
      display.clear();

      EEPROM.put(ADDR_MINIMUMRPM, minimumRPM);
      EEPROM.put(ADDR_MAXIMUMRPM, maximumRPM);
      EEPROM.commit();
      EEPROM.end();

      delay(50); // Задержка устранения дребезга
    }

    newPosition = encoder.read();
    
    if (newPosition != oldPosition) {
      if (newPosition > oldPosition) {
        currentPosition += 100; // Увеличение на 100 за каждый поворот по часовой стрелке
      } else {
        currentPosition -= 100; // Уменьшение на 100 за каждый поворот против часовой стрелки
      }
      
      // Ограничиваем текущую позицию в пределах от 0 до 9990
      if (currentPosition < 0) {
        currentPosition = 0;
      } else if (currentPosition > 9900) {
        currentPosition = 9900;
      }
      
      display.showNumberDec(currentPosition);
      oldPosition = newPosition;      
    }
  }
  
  Serial.println("Connecting to " + String(ssid)); // Распечатываем SSID на последовательном мониторе.

  WiFi.mode(WIFI_STA); // Установите режим Wi-Fi как «станция», где ESP8266 выступает в качестве клиента сети.
  WiFi.begin(ssid); // Инициирует процесс подключения к сети Wi-Fi ELM327.

  while (WiFi.status() != WL_CONNECTED) { // Пока статус Wi-Fi ESP8266 не подключен к сети Wi-Fi ELM327, выполните следующий код.
    movePixels("right", neopixel_Count, 100); // Перемещение от самого левого светодиода к самому правому с задержкой в 100 миллисекунд.
    movePixels("left", neopixel_Count, 100); // Переход от самого правого светодиода к самому левому с задержкой в 100 миллисекунд.
    Serial.print("."); // Печать "." в последовательном мониторе.
  }
  
  Serial.println("\nConnected to Wifi. The IP address of the ESP8266 is: " + WiFi.localIP().toString()); // Распечатываем IP-адрес ESP8266.

  if (!client.connect(IPAddress(192, 168, 0, 10), 35000)) { // Эта строка пытается подключить клиентский объект к серверу по IP-адресу 192.168.0.10 и порту 35000. Если соединение не удалось , он выполняет код внутри блока if.
    Serial.println("Connection to ELM327 failed. Resetting and restarting the ESP8266."); // Если соединение не установлено, выведите эту строку.
    ESP.reset(); // полностью сбрасывает микроконтроллер ESP8266, отключая его от любой сети Wi-Fi и перезапуская процесс настройки, определенный в функции setup().
  }
  myELM327.begin(client); // Инициализируем ELM327.
}

void loop() { // Функция цикла для непрерывного чтения RPM и обновления NeoPixels.
  rpm = myELM327.rpm(); // Считаем число оборотов в минуту из ELM327.

  if (myELM327.nb_rx_state == ELM_SUCCESS) { // Проверка состояния связи ELM327. В случае успеха сделайте следующее.
    Serial.print("RPM: " + rpm); // Выводим число оборотов в минуту на последовательный монитор.
    updateNeopixels(rpm); // Обновляем NeoPixels на основе RPM.
  } else if (myELM327.nb_rx_state != ELM_GETTING_MSG) { // Проверяем, не получает ли ESP8266 в данный момент сообщение от ELM327.
    myELM327.printError(); // Выводим ошибку ELM327, если связь не удалась.
  }
}

void updateNeopixels(int rpm) { // Функция для обновления NeoPixels на основе RPM
  strip.clear(); // Очистить все NeoPixels.

  if (rpm >= 2500 && rpm < 5900) { // Если число оборотов в минуту находится между этими двумя значениями, сделайте следующее. Максимальное число оборотов в минуту для этого контура выше, поэтому все светодиоды становятся красными в диапазоне от 5800 до 5900.
    // Указание количества светодиодов слева и справа для создания эффекта схождения.
    int leftIndex = map(rpm, 2500, 5800, 0, neopixel_Count / 2 - 1); // Сопоставьте число оборотов в минуту от 2500 до 5800 с диапазоном от 0 до 7.
    int rightIndex = neopixel_Count - 1 - leftIndex; // Берем leftIndex из 15.

    // Например, если число оборотов в минуту равно 5800, leftIndex = 7 и rightIndex = 8.

    // Сопоставляем RPM с цветами
    int red = map(constrain(rpm, 2500, 5800), 2500, 5800, 0, 255); // Сопоставление числа оборотов в минуту с количеством «красных» светодиодов. Чем выше обороты, тем больше красного.
    int green = map(constrain(rpm, 2500, 5800), 2500, 5800, 255, 0); // Сопоставление числа оборотов в минуту с количеством «зеленого» свечения светодиодов. Чем выше число оборотов в минуту, тем меньше зеленого цвета.

    // Например, если частота вращения равна 5800, красный = 255 и зеленый = 0. Это будет применяться ко всем горящим светодиодам.

    // Левая часть полосы
    for (int i = 0; i <= leftIndex; i++) { // Цикл увеличивает i на 1, пока i <= leftIndex, перемещаясь к правому концу полосы. i++ выполняется после завершения тела цикла.
      strip.setPixelColor(i, strip.Color(red, green, 0)); // Устанавливаем цвет NeoPixel, соответствующий идентификатору i.
    }

    // Правая часть полосы (отражает левую сторону)
    for (int i = neopixel_Count - 1; i >= rightIndex; i--) { // Цикл уменьшается на 1, пока i >= rightIndex, перемещаясь к левому концу полосы. i-- выполняется после завершения тела цикла.
      strip.setPixelColor(i, strip.Color(red, green, 0)); // Устанавливаем цвет NeoPixel, соответствующий идентификатору i.
    }

  } else if (rpm >= 5900) { // Если число оборотов в минуту не менее 5900, сделайте следующее.
    for (int i = 0; i < neopixel_Count; i++) { // Цикл увеличивает i на 1, пока i < neopixel_Count (16), поскольку вы не можете отправить одну команду для изменения цвета всех светодиодов.
      strip.setPixelColor(i, strip.Color(255, 0, 255)); // Установите фиолетовый цвет каждого NeoPixel.
    }
  }
  strip.setBrightness(map(analogRead(potentiometer_Pin), 0, 1023, 0, 255)); // Сопоставьте значение потенциометра с диапазоном яркости (0-255) и соответствующим образом установите яркость полосы. 1023 — максимальное значение, получаемое от аналого-цифрового преобразователя (АЦП).
  strip.show(); // Обновляем полосу NeoPixel.
}

void movePixels(String direction, int numPixels, int delayTime) { // Функция для перемещения NeoPixels слева направо, а затем справа налево.
  for(int i = 0; i < numPixels; i++) { // Цикл увеличивает i на 1, пока i < число пикселей (16).
    int index;
    if (direction == "right") { // Если направление равно 'right'...
      index = i; // Установить индекс в i.
    } else if (direction == "left") { // Если направление равно 'left'...
      index = numPixels - 1 - i; // Установите индекс в numPixels (16) - 1 - i.
    } else { // Обрабатываем ввод неверного направления (ни вправо, ни влево).
      return; // Выход из функции без ответа.
    }
    strip.setPixelColor(index, strip.Color(255, 0, 0)); // Установите красный светодиод, который разделяет идентификатор индекса.
    strip.setBrightness(map(analogRead(potentiometer_Pin), 0, 1023, 0, 255)); // Сопоставьте значение потенциометра с диапазоном яркости (0-255) и соответствующим образом установите яркость полосы. 1023 — максимальное значение, получаемое от аналого-цифрового преобразователя (АЦП).
    strip.show(); // Обновляем полосу NeoPixel.
    delay(delayTime); // Отрегулируйте задержку на основе значения потенциометра.
    strip.setPixelColor(index, 0); // Выключаем светодиод в текущей позиции.
  }
}

, 👍1

Обсуждение

Я думаю, вам нужно вызвать «strip.show()» также при использовании очистки, @chrisl


1 ответ


Лучший ответ:

2

Мы можем взглянуть на функцию clear() внутри библиотеки Adafruit_Neopixel. В Adafruit_Neopixel.cpp вы можете найти строку 3396 (поиск по clear(void) приведет вас туда):

void Adafruit_NeoPixel::clear(void) { memset(pixels, 0, numBytes); }

Это очень короткая функция. memset() присваивает всем байтам в определенном диапазоне указанное значение. Таким образом, все байты в пикселях будут установлены в ноль. Хотя это только внутренняя память библиотеки. Здесь нет никаких действий ввода-вывода, никакого кода, отправляющего данные в Neopixels.

Это означает: clear() действует только на внутреннее представление полосы библиотеки, точно так же, как setPixel() и его родственные элементы. Чтобы фактически отправить данные в Neopixels, вам все равно нужно вызвать strip.show(), который затем берет внутреннее представление полосы библиотеки и отправляет данные через цифровой контакт с Neopixels.

Я хочу побудить людей изучить исходный код библиотек, которые они используют. Когда вы научитесь их читать, вы сможете получать быстрые ответы, даже не понимая фактической полной реализации библиотеки.

,

Большое спасибо за ваш ответ @chrisl, очень ценю!, @Lachlan Etherton