ESP32:Примеры/Как запомнить последнее состояние контакта

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Как запомнить последнее состояние контакта

В этом примере мы научимся сохранять и считывать данные из flash-памяти ESP32. В качестве примера мы сохраним в нее последнее состояние GPIO-контакта.

Данные, сохраненные на flash-память, остаются там даже при сбросе ESP32 или отключении питания. Flash-память очень похожа на память EEPROM, потому что обе они – разновидности энергонезависимой памяти.

Сохранение данных на flash-память может пригодиться в следующих случаях:

  • Для сохранения последнего состояния переменной
  • Для сохранения настроек
  • Для сохранения того, сколько раз было активировано устройство
  • Для сохранения любого другого типа данных

У flash-памяти есть одно ограничение – то, сколько раз можно выполнить запись на нее. Считывать данные можно сколько угодно раз, но у большинства устройств максимальное количество записей ограничивается диапазоном от 100 тысяч до 1 млн.

Для считывания и записи данных на flash-память ESP32 при помощи IDE Arduino мы воспользуемся библиотекой EEPROM. Ее использование на ESP32 почти аналогично использованию на Arduino. То есть если вы уже пользовались EEPROM-памятью Arduino, то особых проблем у вас не возникнет.

При помощи библиотеки EEPROM можно использовать до 512 байт flash-памяти ESP32. Это значит, что у вас есть 512 разных адресов, и в каждом из них можно сохранить значение от «0» до «255».

Чтобы записать данные на flash-память, необходимо воспользоваться функцией EEPROM.write(), где параметрами служат место (адрес) для сохранения данных и значение (байтовая переменная), которое нужно сохранить:

EEPROM.write(address, value);

К примеру, чтобы записать значение «9» на адрес «0», нужно вписать следующее:

EEPROM.write(0, 9);

После этого нужно вызвать...

EEPROM.commit();

Это нужно, чтобы сохранить изменения.

Чтобы прочесть байт из flash-памяти, воспользуйтесь функцией EEPROM.read(). Параметром для нее служит адрес байта, который нужно прочесть.

EEPROM.read(address);

К примеру, чтобы прочесть байт, хранящийся по адресу «0», нужно вписать следующее:

EEPROM.read(0);

В результате функция EEPROM.read() вернет значение «9», хранящееся по адресу «0».

Чтобы продемонстрировать сохранение данных на flash-память ESP32, мы сохраним последнее состояние выходного контакта (в нашем случае – светодиода). Представьте следующий сценарий:

  1. Вы управляете лампой при помощи ESP32
  2. Вы задали значение, включающее лампу
  3. У платы ESP32 внезапно отключается питание
  4. Поле включения питания лампа остается выключенной, потому что ESP32 не сохранила последнее состояние выходного контакта, к которому подключена лампа

Разумеется, вы не хотите, чтобы так вышло. Вам нужно, чтобы ESP32 сохранила то, что произошло до выключения питания и вернула последнее состояние того выходного контакта.

Чтобы решить эту проблему, нужно сохранить состояние контакта лампы на flash-память. Затем вам просто нужно добавить условие в начало скетча, которое будет проверять последнее состояние контакта лампы, чтобы соответствующим образом включать/выключать лампу.

На схеме ниже показано, что мы собираемся сделать:

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

Давайте разберемся как работает этот код.

Этот код меняет состояние светодиода при каждом нажатии на кнопку, а также имеет фрагмент с «антидребезгом». Но этот код особенный – он запоминает последнее состояние светодиода даже после сброса платы ESP32 и ее отключения от питания. Итак, давайте разберемся, что нужно сделать, чтобы ESP32 запоминал последнее состояние GPIO-контакта.

Во-первых, нам нужно подключить библиотеку EEPROM.

#include <EEPROM.h>

Затем задаем размер памяти. Это количество байтов flash-памяти, к которым вы хотите получить доступ в этом скетче. В данном случае мы просто сохраняем состояние светодиода, поэтому нам достаточно EEPROM-памяти размера «1».

#define EEPROM_SIZE 1

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

// константы – это неизменяемые значения;
// воспользуемся ими, чтобы задать номера контактов:
const int buttonPin = 4;   // номер контакта для кнопки
const int ledPin = 16;     // номер контакта для светодиода

// переменные – это изменяемые значения;
int ledState = HIGH;        // текущее состояние 
                            // выходного контакта (для светодиода)
int buttonState;            // текущее состояние 
                            // входного контакта (для кнопки)
int lastButtonState = LOW;  // предыдущее состояние 
                            // входного контакта (для кнопки)

// переменные ниже имеют тип «unsigned long»,
// потому что время измеряется в миллисекундах, и значение,
// в котором оно хранится, очень быстро станет настолько большим,
// что его нельзя будет сохранить в типе данных «int»

unsigned long lastDebounceTime = 0; // время, когда в последний раз
                                    // был переключен выходной контакт 
unsigned long debounceDelay = 50;   // задержка для антидребезга;
                                    // если выходной контакт 
                                    // «дребезжит», увеличьте
                                    // это значение

В блоке setup() инициализируем EEPROM-память заданного размера.

EEPROM.begin(EEPROM_SIZE);

Чтобы начать этот код с последнего состояния светодиода, его нужно прочесть уже в блоке setup(). Оно хранится на flash-памяти по адресу «0».

ledState = EEPROM.read(0);

Затем нам нужно просто включить или выключить светодиод согласно значению, считанному с flash-памяти.

digitalWrite(ledPin, ledState);

Фрагмент ниже из блока loop() проверяет, нажата ли кнопка, и меняет значение в переменной «ledState» при каждом нажатии на кнопку. Чтобы случайно не переключить контакт из-за дребезга, здесь используется таймер. Этот фрагмент основан на скетче для IDE Arduino, предназначенном для считывания значения с кнопки и предотвращения дребезга.

  // считываем состояние переключателя в локальную переменную:
  int reading = digitalRead(buttonPin);

  // во фрагменте кода ниже проверяем, была ли нажата кнопка
  // (т.е. изменилось ли входное значение с «LOW» на «HIGH»)
  // и прошло ли достаточно времени с момента нажатия
  // (чтобы исключить вероятность переключения из-за шума):

  // если состояние переключателя изменилось -
  // либо из-за шума, либо из-за нажатия на кнопку... 
  if (reading != lastButtonState) {
    // ...сбрасываем счетчик антидребезга:
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // каким бы ни было считанное значение, 
    // оно держится уже дольше периода антидребезга,
    // поэтому принимаем это значение за текущее состояние контакта:

    // если состояние кнопки изменилось:
    if (reading != buttonState) {
      buttonState = reading;

      // переключаем светодиод, только если 
      // новым состоянием кнопки является «HIGH»:
      if (buttonState == HIGH) {
        ledState = !ledState;
      }
    }
  }

То есть нам нужно просто сохранять состояние светодиода на flash-память при каждом изменении этого состояния.

Здесь мы проверяем, отличается ли значение GPIO-контакта от значения в переменной «ledState».

if (digitalRead(ledPin)!= ledState) {

Если отличается, меняем состояние светодиода при помощи функции digitalWrite().

digitalWrite(ledPin, ledState);

И затем сохраняем текущее состояние на flash-память. Используем для этого функцию EEPROM.write(), а в параметрах у нее указываем адрес памяти («0»), и сохраняемое значение («ledState»).

EEPROM.write(0, ledState);

Наконец, используем EEPROM.commit(), чтобы изменения вступили в силу.

EEPROM.commit();

Итак, в этом примере мы научились сохранять данные на flash-память ESP32 при помощи библиотеки EEPROM. Данные, сохраняемые на flash-память, остаются там даже после сброса ESP32 или отключения ее от питания.

Важно

Перед тем, как строить какую-либо цепь, всегда сверяйтесь с распиновкой свой платы.

Необходимое оборудование

Схема

Примечание

На данной схеме используется плата ESP32S-HiLetgo, если вы используете другую, сверьтесь с вашей распиновкой.

Кнопка подключена к контакту GPIO4 через резистор на 10 кОм, а светодиод – к контакту GPIO16 через резистор 330 Ом.

Код

// подключаем библиотеку для считывания и записи на flash-память: 
#include <EEPROM.h>

// задаем количество байтов, к которым нужно получить доступ:
#define EEPROM_SIZE 1

// константы – это неизменяемые значения;
// воспользуемся ими, чтобы задать номера контактов:
const int buttonPin = 4;   // номер контакта для кнопки
const int ledPin = 16;     // номер контакта для светодиода

// переменные – это изменяемые значения;
int ledState = HIGH;        // текущее состояние 
                            // выходного контакта (для светодиода)
int buttonState;            // текущее состояние 
                            // входного контакта (для кнопки)
int lastButtonState = LOW;  // предыдущее состояние 
                            // входного контакта (для кнопки)

// переменные ниже имеют тип «unsigned long»,
// потому что время измеряется в миллисекундах, и значение,
// в котором оно хранится, очень быстро станет настолько большим,
// что его нельзя будет сохранить в типе данных «int»

unsigned long lastDebounceTime = 0; // время, когда в последний раз
                                    // был переключен выходной контакт 
unsigned long debounceDelay = 50;   // задержка для антидребезга;
                                    // если выходной контакт 
                                    // по-прежнему «дребезжит», 
                                    // увеличьте это значение 

void setup() { 
  Serial.begin(115200);
  
  // инициализируем EEPROM-память заданного ранее размера:
  EEPROM.begin(EEPROM_SIZE);

  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);

  // считываем последнее состояние светодиода из flash-памяти:
  ledState = EEPROM.read(0);
  // задаем контакту светодиода последнее сохраненное состояние:
  digitalWrite(ledPin, ledState);
}

void loop() {
  // считываем состояние переключателя в локальную переменную:
  int reading = digitalRead(buttonPin);

  // во фрагменте ниже проверяем, была ли нажата кнопка
  // (т.е. изменилось ли входное значение с «LOW» на «HIGH»)
  // и прошло ли достаточно времени с момента нажатия
  // (чтобы исключить вероятность переключения из-за шума):

  // если состояние переключателя изменилось -
  // либо из-за шума, либо из-за нажатия на кнопку... 
  if (reading != lastButtonState) {
    // ...сбрасываем счетчик антидребезга:
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // каким бы ни было считанное значение, 
    // оно держится уже дольше периода антидребезга,
    // поэтому принимаем это значение за текущее состояние контакта:

    // если состояние кнопки изменилось:
    if (reading != buttonState) {
      buttonState = reading;

      // переключаем светодиод, только если 
      // новым состоянием кнопки является «HIGH»:
      if (buttonState == HIGH) {
        ledState = !ledState;
      }
    }
  }
  // сохраняем считанное значение;
  // во время следующего прохода через loop()
  // это значение будет храниться в переменной «lastButtonState»:
  lastButtonState = reading;
  
  // если значение в переменной «ledState» 
  // отличается от текущего состояния контакта: 
  if (digitalRead(ledPin)!= ledState) {  
    Serial.println("State changed"); // "Состояние изменилось"
    // меняем состояние светодиода:
    digitalWrite(ledPin, ledState);
    // сохраняем состояние на flash-память:
    EEPROM.write(0, ledState);
    EEPROM.commit();
    Serial.println("State saved in flash memory");
               //  "Состояние сохранено на flash-память"
  }
}

См.также

Внешние ссылки