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

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску
Нет описания правки
Нет описания правки
 
(не показаны 2 промежуточные версии 2 участников)
Строка 1: Строка 1:
{{ESP32 панель перехода}}
{{ESP32 панель перехода}}
{{Перевод от Сubewriter}}
{{Перевод от Сubewriter}}
{{Myagkij-редактор}}
{{Myagkij-редактор}}
{{Черновик}}


=Как запомнить последнее состояние контакта=
=Как запомнить последнее состояние контакта=
Строка 26: Строка 23:
Чтобы записать данные на [[flash-память]], необходимо воспользоваться функцией [[Arduino:Библиотеки/EEPROM/write()|EEPROM.write()]], где параметрами служат место (адрес) для сохранения данных и значение (байтовая переменная), которое нужно сохранить:
Чтобы записать данные на [[flash-память]], необходимо воспользоваться функцией [[Arduino:Библиотеки/EEPROM/write()|EEPROM.write()]], где параметрами служат место (адрес) для сохранения данных и значение (байтовая переменная), которое нужно сохранить:


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
EEPROM.write(address, value);
EEPROM.write(address, value);
</syntaxhighlight>
</syntaxhighlight>
Строка 32: Строка 29:
К примеру, чтобы записать значение «9» на адрес «0», нужно вписать следующее:
К примеру, чтобы записать значение «9» на адрес «0», нужно вписать следующее:


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
EEPROM.write(0, 9);
EEPROM.write(0, 9);
</syntaxhighlight>
</syntaxhighlight>
Строка 38: Строка 35:
После этого нужно вызвать...
После этого нужно вызвать...


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
EEPROM.commit();
EEPROM.commit();
</syntaxhighlight>
</syntaxhighlight>
Строка 46: Строка 43:
Чтобы прочесть байт из flash-памяти, воспользуйтесь функцией [[Arduino:Библиотеки/EEPROM/read()|EEPROM.read()]]. Параметром для нее служит адрес байта, который нужно прочесть.
Чтобы прочесть байт из flash-памяти, воспользуйтесь функцией [[Arduino:Библиотеки/EEPROM/read()|EEPROM.read()]]. Параметром для нее служит адрес байта, который нужно прочесть.


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
EEPROM.read(address);
EEPROM.read(address);
</syntaxhighlight>
</syntaxhighlight>
Строка 52: Строка 49:
К примеру, чтобы прочесть байт, хранящийся по адресу «0», нужно вписать следующее:
К примеру, чтобы прочесть байт, хранящийся по адресу «0», нужно вписать следующее:


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
EEPROM.read(0);
EEPROM.read(0);
</syntaxhighlight>
</syntaxhighlight>
Строка 83: Строка 80:
Во-первых, нам нужно подключить [[Arduino:Библиотеки/EEPROM|библиотеку EEPROM]].
Во-первых, нам нужно подключить [[Arduino:Библиотеки/EEPROM|библиотеку EEPROM]].


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
#include <EEPROM.h>
#include <EEPROM.h>
</syntaxhighlight>
</syntaxhighlight>
Строка 89: Строка 86:
Затем задаем размер памяти. Это количество байтов flash-памяти, к которым вы хотите получить доступ в этом скетче. В данном случае мы просто сохраняем состояние [[светодиод]]а, поэтому нам достаточно [[EEPROM]]-памяти размера «1».
Затем задаем размер памяти. Это количество байтов flash-памяти, к которым вы хотите получить доступ в этом скетче. В данном случае мы просто сохраняем состояние [[светодиод]]а, поэтому нам достаточно [[EEPROM]]-памяти размера «1».


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
#define EEPROM_SIZE 1
#define EEPROM_SIZE 1
</syntaxhighlight>
</syntaxhighlight>
Строка 95: Строка 92:
Нам также нужно задать несколько переменных, требующихся для работы этого скетча.
Нам также нужно задать несколько переменных, требующихся для работы этого скетча.


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
// константы – это неизменяемые значения;
// константы – это неизменяемые значения;
// воспользуемся ими, чтобы задать номера контактов:
// воспользуемся ими, чтобы задать номера контактов:
Строка 124: Строка 121:
В блоке [[Arduino:Справочник языка Arduino/setup()|setup()]] инициализируем [[EEPROM-память]] заданного размера.
В блоке [[Arduino:Справочник языка Arduino/setup()|setup()]] инициализируем [[EEPROM-память]] заданного размера.


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(EEPROM_SIZE);
</syntaxhighlight>
</syntaxhighlight>
Строка 130: Строка 127:
Чтобы начать этот код с последнего состояния [[светодиод]]а, его нужно прочесть уже в блоке [[Arduino:Справочник языка Arduino/setup()|setup()]]. Оно хранится на flash-памяти по адресу '''«0»'''.
Чтобы начать этот код с последнего состояния [[светодиод]]а, его нужно прочесть уже в блоке [[Arduino:Справочник языка Arduino/setup()|setup()]]. Оно хранится на flash-памяти по адресу '''«0»'''.


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
ledState = EEPROM.read(0);
ledState = EEPROM.read(0);
</syntaxhighlight>
</syntaxhighlight>
Строка 136: Строка 133:
Затем нам нужно просто включить или выключить [[светодиод]] согласно значению, считанному с flash-памяти.  
Затем нам нужно просто включить или выключить [[светодиод]] согласно значению, считанному с flash-памяти.  


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
digitalWrite(ledPin, ledState);
digitalWrite(ledPin, ledState);
</syntaxhighlight>
</syntaxhighlight>
Строка 142: Строка 139:
Фрагмент ниже из блока [[Arduino:Справочник языка Arduino/loop()|loop()]] проверяет, нажата ли кнопка, и меняет значение в переменной '''«ledState»''' при каждом нажатии на кнопку. Чтобы случайно не переключить контакт из-за дребезга, здесь используется таймер. Этот фрагмент основан на скетче для [[IDE Arduino]], предназначенном для считывания значения с кнопки и предотвращения дребезга.
Фрагмент ниже из блока [[Arduino:Справочник языка Arduino/loop()|loop()]] проверяет, нажата ли кнопка, и меняет значение в переменной '''«ledState»''' при каждом нажатии на кнопку. Чтобы случайно не переключить контакт из-за дребезга, здесь используется таймер. Этот фрагмент основан на скетче для [[IDE Arduino]], предназначенном для считывания значения с кнопки и предотвращения дребезга.


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
   // считываем состояние переключателя в локальную переменную:
   // считываем состояние переключателя в локальную переменную:
   int reading = digitalRead(buttonPin);
   int reading = digitalRead(buttonPin);
Строка 180: Строка 177:
Здесь мы проверяем, отличается ли значение GPIO-контакта от значения в переменной '''«ledState»'''.
Здесь мы проверяем, отличается ли значение GPIO-контакта от значения в переменной '''«ledState»'''.


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
if (digitalRead(ledPin)!= ledState) {
if (digitalRead(ledPin)!= ledState) {
</syntaxhighlight>
</syntaxhighlight>
Строка 186: Строка 183:
Если отличается, меняем состояние [[светодиод]]а при помощи функции [[Arduino:Справочник языка Arduino/Функции/Цифровой ввод/вывод/digitalWrite()|digitalWrite()]].
Если отличается, меняем состояние [[светодиод]]а при помощи функции [[Arduino:Справочник языка Arduino/Функции/Цифровой ввод/вывод/digitalWrite()|digitalWrite()]].


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
digitalWrite(ledPin, ledState);
digitalWrite(ledPin, ledState);
</syntaxhighlight>
</syntaxhighlight>
Строка 192: Строка 189:
И затем сохраняем текущее состояние на [[flash-память]]. Используем для этого функцию [[Arduino:Библиотеки/EEPROM/write()|EEPROM.write()]], а в параметрах у нее указываем адрес памяти ('''«0»'''), и сохраняемое значение ('''«ledState»''').
И затем сохраняем текущее состояние на [[flash-память]]. Используем для этого функцию [[Arduino:Библиотеки/EEPROM/write()|EEPROM.write()]], а в параметрах у нее указываем адрес памяти ('''«0»'''), и сохраняемое значение ('''«ledState»''').


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
EEPROM.write(0, ledState);
EEPROM.write(0, ledState);
</syntaxhighlight>
</syntaxhighlight>
Строка 198: Строка 195:
Наконец, используем EEPROM.commit(), чтобы изменения вступили в силу.
Наконец, используем EEPROM.commit(), чтобы изменения вступили в силу.


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
EEPROM.commit();
EEPROM.commit();
</syntaxhighlight>
</syntaxhighlight>
Строка 226: Строка 223:
==Код==
==Код==


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
// подключаем библиотеку для считывания и записи на flash-память:  
// подключаем библиотеку для считывания и записи на flash-память:  
#include <EEPROM.h>
#include <EEPROM.h>
Строка 326: Строка 323:


=См.также=
=См.также=
{{ads}}


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


<references />
<references />
{{Навигационная таблица/Портал/ESP32}}


[[Категория:ESP32]]
[[Категория:ESP32]]

Текущая версия от 09:19, 18 июня 2023

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


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

В этом примере мы научимся сохранять и считывать данные из 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-память"
  }
}

См.также

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