Сохранение переменной arduino в eeprom

Описание памяти EEPROM

Ардуино предоставляет своим пользователям три типа встроенной памяти устройств: стационарное ОЗУ (оперативно-запоминающее устройство или SRAM — static random access memory) – необходимо для записи и хранения данных в процессе использования; флеш-карты – для сохранения уже записанных схем; EEPROM – для хранения и последующего использования данных.

По теме:

На ОЗУ все данные стираются, как только происходит перезагрузка устройства либо отключается питание. Вторые две сохраняют всю информацию до перезаписи и позволяют извлекать ее при необходимости. Флеш-накопители достаточно распространены в настоящее время. Подробнее стоит рассмотреть память EEPROM.

Аббревиатура расшифровывается, как Electrically Erasable Programmable Read-Only Memory и в переводе на русский дословно означает – электрически стираемая программируемая память только для чтения. Производитель гарантирует сохранность информации на несколько десятилетий вперед после последнего отключения питания (обычно приводят срок в 20 лет, зависит от скорости снижения заряда устройства).

При этом нужно знать, что возможность перезаписи на устройство ограничена и составляет не более 100 000 раз. Поэтому рекомендуют аккуратно и внимательно относиться к вносимым данным и не допускать перезаписи лишний раз.

Объем памяти, в сравнении с современными носителями, очень небольшой и разный для различных микроконтроллеров. Например, для:

  • ATmega328 – 1кБ
  • ATmega168 и ATmega8 – 512 байт,
  • ATmega2560 и ATmega1280 – 4 кБ.

Так устроено потому, что каждый микроконтроллер предназначен для определенного объема задач, имеет разное количество выводов для подключения, соответственно, необходим разный объем памяти. При этом такого количества достаточно для обычно создаваемых проектов.

Для записи на EEPROM требуется значительное количество времени – около 3 мс. Если в момент записи отключается питание, данные не сохраняются вовсе либо могут быть записаны ошибочно. Требуется всегда дополнительно проверять внесенную информацию, чтобы избежать сбоев во время работы. Считывание данных происходит гораздо быстрее, ресурс памяти от этого не снижается.

Библиотека

Работа с памятью EEPROM осуществляется с помощью библиотеки, которая была специально создана для Ардуино. Главными являются способность к записи и чтению данных. Библиотека активируется командой #include EEPROM.h.

Далее используются простые команды:

  • для записи – EEPROM.write(address, data);
  • для чтения – EEPROM.read(address).

В данных скетчах: address – аргумент с данными ячейки, куда вносятся данные второго аргумента data; при считывании используется один аргумент address, который показывает, откуда следует читать информацию.

Функция Назначение
read(address) считывает 1 байт из EEPROM; address – адрес, откуда считываются данные (ячейка, начиная с 0);
write(address, value) записывает в память значение value (1 байт, число от 0 до 255) по адресу address;
update(address, value) заменяет значение value по адресу address, если её старое содержимое отличается от нового;
get(address, data) считывает данные data указанного типа из памяти по адресу address;
put(address, data) записывает данные data указанного типа в память по адресу address;
EEPROM позволяет использовать идентификатор «EEPROM» как массив, чтобы записывать данные в память и считывать их из памяти.

Запись целых чисел

Запись целых чисел в энергонезависимую память EEPROM осуществить достаточно просто. Внесение чисел происходит с запуском функции EEPROM.write(). В скобках указываются необходимые данные. При этом числа от 0 до 255 и числа свыше 255 записываются по-разному. Первые вносятся просто – их объем занимает 1 байт, то есть одну ячейку. Для записи вторых необходимо использовать операторов highByte() высший байт и lowByte() низший байт.

Число делится на байты и записывается отдельно по ячейкам. Например, число 789 запишется в две ячейки: в первую пойдет множитель 3, а во вторую – недостающее значение. В итоге получается необходимое значение:

3 * 256 + 21 = 789

Для «воссоединения» большого целого числа применяется функция word(): int val = word(hi, low). Нужно читывать, что максимальное целое число для записи – 65536 (то есть 2 в степени 16). В ячейках, в которых еще не было иных записей, на мониторе будут стоять цифры 255 в каждой.

Запись чисел с плавающей запятой и строк

Числа с плавающей запятой и строк – это форма записи действительных чисел, где они представляются из мантиссы и показателя степени. Запись таких чисел в энергонезависимую память EEPROM производится с активацией функции EEPROM.put(), считывание, соответственно, – EEPROM.get().

При программировании числовые значения с плавающей запятой обозначаются, как float, стоит отметить, что это не команда, а именно число. Тип Char (символьный тип) – используется для обозначения строк. Процесс записи чисел на мониторе запускается при помощи setup(), считывание – с помощью loop().

В процессе на экране монитора могут появиться значения ovf, что значит «переполнено», и nan, что значит «отсутствует числовое значение». Это говорит о том, что записанная в ячейку информация не может быть воспроизведена, как число с плавающей точкой. Такой ситуации не возникнет, если достоверно знать, в какой ячейке какой тип информации записан.

Примеры проектов и скетчей

Пример №1

Скетч запишет до 16 символов с последовательного порта и в цикле выведет 16 символов из EEPROM. Благодаря Arduino IDE данные записываются в EEPROM и контролируется содержимое энергонезависимой памяти.

// проверка работы EEPROM #include <EEPROM.h> int i, d; void setup() { Serial.begin(9600); // инициализируем порт, скорость 9600 } void loop() { // чтение EEPROM и вывод 16 данных в последовательный порт Serial.println(); Serial.print(«EEPROM= «); i= 0; while(i < 16) { Serial.print((char)EEPROM.read(i)); i++; } // проверка есть ли данные для записи if ( Serial.available() != 0 ) { delay(50); // ожидание окончания приема данных // запись в EEPROM i= 0; while(i < 20) { d= Serial.read(); if (d == -1) d= ‘ ‘; // если символы закончились, заполнение пробелами EEPROM.write(i, (byte)d); // запись EEPROM i++; } } delay(500); }

Пример №2

Для большего понимания мы можем создать небольшой скетч, который поможет в понимании работы с энергонезависимой памятью. Считаем все ячейки этой памяти. Если ячейка не пустая — вывод в последовательный порт. После чего заполняем ячейки пробелами. Потом вводим текст через монитор последовательного порта. Его записываем в EEPROM, и при последующем включении считываем.

#include <EEPROM.h> int address = 0; // адрес eeprom int read_value = 0; // считываемые с eeprom данные char serial_in_data; // данные последовательного порта int led = 6; // линия 6 для светодиода int i; void setup() { pinMode(led, OUTPUT); // линия 6 настраивается на выход Serial.begin(9600); // скорость передачи по последовательному порту 9600 Serial.println(); Serial.println(«PREVIOUS TEXT IN EEPROM :-«); for(address = 0; address < 1024; address ++) // считываем всю память EEPROM { read_value = EEPROM.read(address); Serial.write(read_value); } Serial.println(); Serial.println(«WRITE THE NEW TEXT : «); for(address = 0; address < 1024; address ++) // заполняем всю память EEPROM пробелами EEPROM.write(address, ‘ ‘); for(address = 0; address < 1024; ) // записываем пришедшие с последовательного порта данные в память EEPROM { if(Serial.available()) { serial_in_data = Serial.read(); Serial.write(serial_in_data); EEPROM.write(address, serial_in_data); address ++; digitalWrite(led, HIGH); delay(100); digitalWrite(led, LOW); } } } void loop() { //—- мигаем светодиодом каждую секунду ——// digitalWrite(led, HIGH); delay(1000); digitalWrite(led, LOW); delay(1000); }

Пример №3

Запись в память два целых числа, чтение их из EEPROM и вывод в последовательный порт. Числа от 0 до 255 занимают 1 байт памяти, с помощью функции EEPROM.write() записываются в нужную ячейку. Для чисел больше 255 их нужно делить на байты с помощью highByte() и lowByte() и записывать каждый байт в свою ячейку. Максимальное число при этом – 65536 (или 216).

#include <EEPROM.h> // подключаем библиотеку EEPROM void setup() { int smallNum = 123; // целое число от 0 до 255 EEPROM.write(0, smallNum); // запись числа в ячейку 0 int bigNum = 789; // число > 255 разбиваем на 2 байта (макс. 65536) byte hi = highByte(bigNum); // старший байт byte low = lowByte(bigNum); // младший байт EEPROM.write(1, hi); // записываем в ячейку 1 старший байт EEPROM.write(2, low); // записываем в ячейку 2 младший байт Serial.begin(9600); // инициализация послед. порта } void loop() { for (int addr=0; addr<1024; addr++) { // для всех ячеек памяти (для Arduino UNO 1024) byte val = EEPROM.read(addr); // считываем 1 байт по адресу ячейки Serial.print(addr); // выводим адрес в послед. порт Serial.print(«\t»); // табуляция Serial.println(val); // выводим значение в послед. порт } delay(60000); // задержка 1 мин }

Пример №4

Запись чисел с плавающей запятой и строк — метод EEPROM.put(). Чтение – EEPROM.get().

#include <EEPROM.h> // подключаем библиотеку void setup() { int addr = 0; // адрес float f = 3.1415926f; // число с плавающей точкой (типа float) EEPROM.put(addr, f); // записали число f по адресу addr addr += sizeof(float); // вычисляем следующую свободную ячейку памяти char name = «Hello, SolTau.ru!»; // создаём массив символов EEPROM.put(addr, name); // записываем массив в EEPROM Serial.begin(9600); // инициализация послед. порта } void loop() { for (int addr=0; addr<1024; addr++) { // для всех ячеек памяти (1024Б=1кБ) Serial.print(addr); // выводим адрес в послед. порт Serial.print(«\t»); // табуляция float f; // переменная для хранения значений типа float EEPROM.get(addr, f); // получаем значение типа float по адресу addr Serial.print(f, 5); // выводим с точностью 5 знаков после запятой Serial.print(«\t»); // табуляция char c; // переменная для хранения массива из 20 символов EEPROM.get(addr, c); // считываем массив символов по адресу addr Serial.println(c); // выводим массив в порт } delay(60000); // ждём 1 минуту }

Пример №5

Использование EEPROM как массива.

#include <EEPROM.h> void setup() { EEPROM = 11; // записываем 1-ю ячейку EEPROM = 121; // записываем 2-ю ячейку EEPROM = 141; // записываем 3-ю ячейку EEPROM = 236; // записываем 4-ю ячейку Serial.begin(9600); } void loop() { for (int addr=0; addr<1024; addr++) { Serial.print(addr); Serial.print(«\t»); int n = EEPROM; // считываем ячейку по адресу addr Serial.println(n); // выводим в порт } delay(60000); }

Работа с EEPROM

Как упоминалось ранее, ресурс памяти EEPROM ограничен. Для продления срока службы энергонезависимой памяти, вместо функции write() запись, лучше применять функцию update обновление. При этом перезапись ведется только для тех ячеек, где значение отличается от вновь записываемого.

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

Такая память на Ардуино стандартно хранит самое важное для работы контроллера и устройства. К примеру, если на такой базе создается регулятор температуры и исходные данные окажутся ошибочными, устройство будет работать «неадекватно» существующим условиям – сильно занижать или завышать температуру.

Существует несколько ситуаций, когда память EEPROM содержит неправильные данные:

  1. При первоначальной активации – еще не было ни одной записи.
  2. В момент неконтролируемого отключения питания – часть или все данные не запишутся или запишутся некорректно.
  3. После завершения возможных циклов перезаписи данных.

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

Для контроля целостности информации используют контрольный код системы. Он создается по образцу записи первоначальных данных и, при проверке, он вновь просчитывает данные. Если результат отличается – это ошибка. Самым распространенным вариантом такой проверки является контрольная сумма – выполняется обычная математическая операция по сложению всех значений ячеек.

Опытные программисты добавляют к этому коду дополнительное «исключающее ИЛИ», например, E5h. В случае если все значения равны нулю, а система по ошибке обнулила исходные данные – такая хитрость выявит ошибку.

Таковы основные принципы работы с энергонезависимой памятью EEPROM для микроконтроллеров Arduino. Для определенных проектов стоит использовать только этот вид памяти. Он имеет как свои плюсы, так и свои недостатки. Для освоения методов записи и чтения лучше начать с простых задач.

25.07.2018 | Публикации |

Был модуль WI-FI. Было реле. В доме установлен роутер со статическим IP. Захотелось отображать температуру дома на экране телефона и в добавок включать и выключать подсветку в квартире (Удаленно управлять приборами).
В итоге получилось следующее: Захожу с телефона на свой IP адрес, на экране отображается температура и влажность в квартире + несколько кнопок для управления реле.

Без контроля состояния приборов. Код программы рабочий и предназначен для личного последующего творчества. Готового продукта веб сервера тут нет.
Датчик температуры все тот же DHT-11. Реле собрано на печатной плате с транзисторным ключем (c945) (Для скачивания оставьте комментарий) и управляется напряжением 5В. Коммутирует 270В — 6А.
В моем случае реле коммутировало дополнительное освещение в доме которое изображено на фото ниже.

По плинтусу с кабель каналом протянуты провода и выведено по квартире для ночного освещения. Питание от блока питания 9В, что бы ночью глаза не резало и видно было.
На рисунке ниже изображен весь используемый комплект.

Модуль WI-FI ESP8266 не нуждается в дополнительном обвесе. Единый минус в том что необходимо питание 3.3В и потребление модуля 250 милиампер. Стандартный стабилизатор на ардуине 3.3В не смог обеспечить стабильное питание WI-FI модулю. В прочем моя Arduina китайская. Поэтому пришлось использовать отдельный стабилизатор напряжения 78033.
Я не писатель поэтому если какая то информация не раскрыта, но она необходима — спрашивайте в комментариях.
Про свой WI-FI модуль могу сказать так же что он китайский и в нем очень плохая прошивка. Которую можно заменить подключив модуль к преходнику USB-TTL и заменить. В китайской прошивке нет метода для программной перезагрузки модуля. Т.е если мой модуль зависает приходится передергивать для него питание. Это огромнейший минус в постройке веб сервера.
перейдем к коду программы для ардуины для построения Веб сервера с задуманными плюшками. Код взять с разных сайтов, более менее изменен под свои нужды и проверен. Добавлены комментарии для того что бы не описывать все словами и большим текстом в статье.
Wi-FI модуль ESP8266 общается при помощи 2х линий Rx Tx.
Ниже полный код веб сервера в который советуется вникнуть.
Домашний веб сервер на Ардуино
Домашний веб сервер на Ардуино
Cкачать код программы:
4870_1injner_web_server_arduino.ino
Пояснения: Для отладки расскоментируйте все строки // monitor.print. Посмотреть работу сервера можно в терминале IDE Arduino.

Как работает код выше вкратце:
Происходит включение ардуины, ардуина инициализирует интерфейсы Serial для компьютера и Serial1 для модуля wi-fi. Далее Ардуина отправляет в модуль данные для коннекта с нужным SSID в воздухе, и подключается к нему получая в ответ IP адрес. Для любопытства в таблице DHCP роутера обнаружится новое устройство без имени (Имя модулю так же можно задать). Как только IP выделен, ардуина начинает ждать данные от Wi-Fi модуля и распознавать, выполняя нужные действия.
Если подключения не произошло, то ардуина повторно без остановки пытается подключится к WI-FI.
Как выглядит веб сервер визуально с компьютера можно увидеть ниже на рисунке.

С телефона тоже работает.

Впрочем, все написано на скорую руку и не изменялось долгое время, и все что возможно выполнить в html и Js можно воплотить на странице сервера для своих нужд. Цвет, авто обновление, красивые кнопки и многое другое. Полный полет фантазии.
Опрос датчика температуры происходит при каждом обновлении страницы. Для датчика нужна библиотека которая находится в соседнем посту про DHT-11.
Реле включается одним пином переводя его в HIGHT или LOW.
В заключение скажу то что сервер в данном виде каким он есть работал у меня больше 3х недель без ресетов. Минусами является то что если часто обновлять страницу то сервер можно положить, а вернее модуль wi-fi, но с хорошим модулем ESP8266 это не проблема, программный автоматический сброс и все пройдет.
В скором времени напишу как сделать систему контролируемую напрямую через WiFi модуль ESP8266 для управления объектами и контроля данных.
Дополнение:
Для доступа через интернет, не забывайте прописать статический IP для MAC ESP8266 в роутере. И настроить пере направление трафика для порта 80 на данный IP.

Не всегда есть возможность полностью отладить устройство и прошить с нужными настройками. Это может быть связано с разными факторами: иногда подстройка производится только после установки девайса, иногда просто со временем нужно внести корректировки в работу и т.д. Для таких случаев можно использовать подстроечные резисторы, но это не всегда удобно, например, если придется менять сразу десяток конфигураций. В таких случаях можно воспользоваться, встроенной в arduino, энергонезависимой памятью – EEPROM. Ей не страшно отсутствие питания или перезагрузки устройства, данные останутся в памяти.
Я как-то писал статью про подстроечный резистор и обещал продолжение. Пришло время сдержать обещание и немного расширить функционал из старой статьи.

Энергонезависимая память EEPROM и arduino

EEPROM – это запоминающее устройство, которое позволяет хранить данные десятки лет с отключенным питанием. Перезаписывать данные можно довольно много раз, но не бесконечно. В одних источниках пишут, что EEPROM позволяет осуществлять до нескольких миллионов циклов перезаписи, а где-то пишут, что не больше ста тысяч. В любом случае, этого количества с лихвой должно хватить, если использовать с умом.
Arduino уже имеет встроенную EEPROM и управлять ею очень просто. В зависимости от версии arduino, объем памяти отличается:
— Платы с микроконтроллерами ATmega8 и ATmega168 используют EEPROM с объемом памяти в 512 байт.
— Платы с ATmega328 используют EEPROM с объемом 1 кб
— Платы на ATmega1280 и ATmega2560 имеют EEPROM с объемом в 4 кб
Для хранения настроек и других не больших массивов данных, даже 512 байт будет достаточно. Но если вы решите хранить какие-то огромные значения, не помещающиеся даже в 4 кб, то всегда можно купить дополнительную микросхему EEPROM и подключить ее к arduino. Такие микросхемы способны хранить, куда больший объем, чем встроенные. Главное при выборе обращать внимание на совместимость с arduino и способность работать с 8-битным микроконтроллером. Например, можно использовать 24LC256.

Скетч записи и чтения данных из EEPROM

Для работы EEPROM в arduino IDE уже есть встроенная библиотека EEPROM.h, ее и будем использовать. Вся память разбита на ячейки по 1 байту, соответственно в каждую ячейку можно записать не больше одного байта, если хранить целые числа, то можно записывать значения от 0 до 255 без сложностей. Этим мы и займемся в скетче – используем одну ячейку памяти и сначала сохраним в нее число от 0 до 4, а потом прочитаем содержимое.
Если вам необходимо хранить значения, превышающие размер в 1 байт, то их придется разбивать по байтам и записывать в разные ячейки по частям.
Обратите внимание: по умолчанию в пустой EEPROM в каждой ячейке хранятся значения 255. Но делать проверку на пустоту с помощью сравнения с числом 255 не стоит. Поскольку не всегда можно полагаться на заводские настройки. И перед использованием лучше прогнать все ячейки в цикле и записать в них свои значения для обнуления, чтобы быть полностью уверенным в содержимом.
Далее остается только привести скетч с подробными комментариями:

#include <EEPROM.h> // библиотека для работы с EEPROM int pin_rezistor = A0; int value = 0; int count_leds = 0; // пин светодиода int ledPins = {4, 5, 6, 7,8}; boolean editOn = false; // состояние светодиода // пин кнопки int btn_1 = 2; boolean lastBtn_1 = false; // предыдущее состояние кнопки boolean currentBtn_1 = false; // текущее состояние кнопки void setup() { // режимы работы пинов pinMode(pin_rezistor, INPUT); pinMode (btn_1, INPUT); for(int i=0; i<=4; i++){ pinMode (ledPins, OUTPUT); } } void loop() { if(editOn){ value = analogRead(pin_rezistor); count_leds = map(value, 0, 1000, 0, 4); }else{ count_leds = EEPROM_int_read(0); } // получение состояния кнопки currentBtn_1 = isBtnStatus(lastBtn_1); // если кнопка была нажата 5 миллисекунд и более if (lastBtn_1 == false && currentBtn_1 == true) { // меняем состояние светодиода editOn = !editOn; // если выключился режим настройки if(!editOn){ EEPROM.write(0, count_leds); } } // сохраняем состояние кнопки lastBtn_1 = currentBtn_1; // все гасим for(int i=0; i<=4; i++){ digitalWrite(ledPins, LOW); } // включаем нужное количество for(int i=0; i<=count_leds; i++){ digitalWrite(ledPins, HIGH); } delay(50); } // функция для определения состояния кнопки boolean isBtnStatus(boolean last) { // считываем состояние кнопки boolean current = digitalRead(btn_1); // сравниваем текущее состояние кнопки со старым if (last != current) { // ждем 5 миллисекунд delay(5); // считываем состояние кнопки еще раз current = digitalRead(btn_1); } // возвращаем состояние кнопки return current; } // чтение int EEPROM_int_read(int addr) { byte raw; for(byte i = 0; i < 2; i++) raw = EEPROM.read(addr+i); int &num = (int&)raw; return num; } // запись void EEPROM_int_write(int addr, int num) { byte raw; (int&)raw = num; for(byte i = 0; i < 2; i++) EEPROM.write(addr+i, raw); }

Послесловие

Я заказал себе в стране Великого Дракона десяток 24LC256, чтобы можно было поэкспериментировать с внешней EEPROM. Как посылка приедет и я «наиграюсь» с микросхемой, то постараюсь написать статью и поделиться опытом.

В уроке рассказывается, как работать с внутренним EEPROM платы Ардуино, о контроле целостности данных при хранении и передаче.

Предыдущий урок Список уроков Следующий урок

Внутреннее EEPROM в системе Ардуино.

EEPROM это энергонезависимая память. Расшифровывается как электрически стираемое программируемое постоянное запоминающее устройство. Применяется для хранения данных, которые должны быть сохранены при выключении питания.

Платы Ардуино имеют EEPROM разных объемов в зависимости от типа используемого микроконтроллера.

Микроконтроллер Объем внутреннего EEPROM
ATmega328 1024 байт
ATmega8, ATmega168 512 байт
ATmega1280, ATmega2560 4096 байт

Такого объема памяти вполне достаточно для хранения режимов, технологических параметров, коэффициентов и т.п.

  • Принцип действия EEPROM основан на создании электрического заряда в диэлектрике полупроводниковой структуры. Заряды вечно не хранятся, но разработчики гарантируют 20 лет.
  • EEPROM имеет ограниченное число циклов записи, обычно не менее 100 000.
  • Для записи информации в EEPROM требуется достаточно большое время, порядка 3 мс.
  • Чтение EEPROM происходит без задержки и ресурс работы памяти не уменьшает.

Библиотека EEPROM.

Для работы с энергонезависимой памятью в Ардуино есть библиотека EEPROM. Она имеет две функции для записи и чтении байта в EEPROM.

byte read(int address)

Функция возвращает считанный по адресу address байт.

#include <EEPROM.h>
byte dt; dt= EEPROM.read(15); // чтение байта по адресу 15

void write(int address, byte value)

Записывает байт value по адресу address в EEPROM. Запись выполняется за 3,3 мс. Гарантируется 100 000 циклов записи.

#include <EEPROM.h>
EEPROM.write(15, 0); // запись 0 по адресу 15

Программа проверки записи в EEPROM.

Давайте напишем простую программу проверки работы EEPROM. Программа должна записывать до 16ти символов, принятых с последовательного порта и в цикле выводить 16 символов, считанных из EEPROM. С помощью монитора порта Arduino IDE мы сможем записывать данные в EEPROM и контролировать содержимое энергонезависимой памяти.

// проверка работы EEPROM
#include <EEPROM.h>
int i, d;

void setup() {
Serial.begin(9600); // инициализируем порт, скорость 9600
}

void loop() {
// чтение EEPROM и вывод 16 данных в последовательный порт
Serial.println();
Serial.print(«EEPROM= «);
i= 0; while(i < 16) {
Serial.print((char)EEPROM.read(i));
i++;
}
// проверка есть ли данные для записи
if ( Serial.available() != 0 ) {
delay(50); // ожидание окончания приема данных

// запись в EEPROM
i= 0; while(i < 20) {
d= Serial.read();
if (d == -1) d= ‘ ‘; // если символы закончились, заполнение пробелами
EEPROM.write(i, (byte)d); // запись EEPROM
i++;
}
}
delay(500);
}

Загружаем программу в плату Ардуино, проверяем. Открываем монитор порта, посылаем данные на плату:”Проверка EEPROM”.

Данные записываются в EEPROM и теперь в цикле выводятся в окне монитора. Если мы отключим плату от компьютера и затем снова подключим, то данные будут продолжать посылаться на компьютер. Они сохранены в энергонезависимой памяти и при выключении питания платы не меняются.

Контроль целостности данных.

В EEPROM обычно хранят данные абсолютно необходимые для работы программы. Например, если вы делаете регулятор температуры, то это может быть : заданная температура, коэффициенты регуляторов, параметры защитных функций и т.п. Представьте, что эти параметры будут испорчены, а регулятор продолжит работу. Он может включить нагревательный элемент на полную мощность и выставить недопустимо высокую температуру. При неправильных коэффициентах регулятор может “пойти в разнос”. Перестанут работать защитные функции. Неприятности будут большие.

А ведь ситуация, когда в EEPROM могут оказаться недостоверные данные вполне реальная.

  • При первом включении устройства данные в EEPROM недостоверны. Их просто никто еще не задавал.
  • В момент записи данных в EEPROM может отключиться питание платы и тогда часть данных запишется, а часть не успеет.
  • EEPROM имеет ограниченное число циклов записи, и это свойство также может привести к разрушению данных в этой памяти.

Поэтому крайне важно быть уверенными, что в EEPROM хранятся достоверные данные. В случае, если они ошибочны, программа должна принять определенные действия. Это может быть:

  • использовать копию данных из EEPROM;
  • использовать данные специально созданные для аварийного режима;
  • отключить устройство;
  • сигнализировать об ошибке;
  • и много других вариантов.

Целостность данных это показатель, того что данные не были изменены при хранении, передаче, отображении и т.п. Целостность данных может проверяться в любой ситуации, потенциально опасной для разрушения данных. Обычно контроль достоверности данных производят:

  • В энергонезависимой памяти (EEPROM, FLASH, HDD…).
  • При передаче данных ( последовательные интерфейсы, WiFi, GSM, TCP/IP…).
  • В оперативной памяти для особо важных данных.
  • Некоторые компоненты (например, датчик DS18B20) имеют протокол обмена данными с контролем целостности.

Для контроля целостности к блоку данных добавляется контрольный код. Этот код рассчитывается по определенному алгоритму при записи данных. При проверке данных код заново вычисляется и сравнивается с кодом, сформированным при записи. Если коды не совпадают – данные ошибочны.

Алгоритм расчета контрольного кода определяет вероятность определения ошибки данных. Существует большое число алгоритмов формирования контрольных кодов: циклические коды, различные функции хэширования. Но самым простым способом вычисления контрольного кода является контрольная сумма.

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

Я всегда к вычисленной контрольной сумме применяю операцию “исключающее ИЛИ” с кодом подобным E5h. Это позволяет исключить весьма вероятную ситуацию, когда все данные равны 0. Сумма 0 равна 0. Поэтому если блок данных будет ошибочно обнулен (а это бывает), то простая сумма не вычислит эту ошибку. А когда контрольная сумма для всех 0 равна E5h, то ошибка будет выявлена.

Давайте добавим в предыдущую программу контроль целостности данных.

// проверка работы EEPROM
#include <EEPROM.h>
int i, d;
byte sum; // контрольная сумма

void setup() {
Serial.begin(9600); // инициализируем порт, скорость 9600
}

void loop() {
// вычисление контрольной суммы
sum= 0;
i= 0; while(i < 16) {
sum += EEPROM.read(i);
i++;
}
// проверка контрольной суммы
if ( (sum^0xe5) == EEPROM.read(i)) {
// контрольна сумма правильная
// чтение EEPROM и вывод 16 данных в последовательный порт
Serial.println();
Serial.print(«EEPROM= «);
i= 0; while(i < 16) {
Serial.print((char)EEPROM.read(i));
i++;
}
}
else {
// контрольная сумма неправильная
Serial.println();
Serial.print(«EEPROM= data error»);
}
// проверка есть ли данные для записи
if ( Serial.available() != 0 ) {
delay(50); // ожидание окончания приема данных

// запись в EEPROM
sum= 0;
i= 0; while(i < 16) {
d= Serial.read();
if (d == -1) d= ‘ ‘; // если символы закончились, заполнение пробелами
EEPROM.write(i, (byte)d); // запись EEPROM
sum += (byte)d; // вычисление контрольной суммы
i++;
}
EEPROM.write(i, sum ^ 0xe5); // запись контрольной суммы
}
delay(500);
}

Отмечу только, что программа принимает не 16, а 14 символов, т.к. монитор порта добавляет к каждой строке символы переноса строки \r и \n.

Загрузим программу и запустим монитор.

В окне бегут сообщения об ошибке данных. Мы еще не загрузили в EEPROM никаких данных, и алгоритм распознает эту ситуацию. Пошлем строку, например, ”проверка”. Теперь в окне отображаются данные из EEPROM.

Таким же образом можно защищать целостность данных при передаче по последовательному порту. Любая импульсная помеха может исказить сигнал в кабеле связи и вызвать ошибку данных. Конечно, программа приема данных на компьютере должна поддерживать протокол с контрольной суммой. Монитор порта Arduino IDE этой функции не имеет.

Digitrode

Бывают случаи, когда микроконтроллер должен хранить определенные данные, например, показания датчиков или настроечные параметры, довольно продолжительный период времени. Но после отключения питания память данных микроконтроллера стирается, что не подходит для данной задачи. Поэтому зачастую для такой цели долговременного хранения используют энергонезависимую память EEPROM.

В Arduino также есть EEPROM, поскольку в основе Arduino лежит микроконтроллер ATmega. Это позволяет хранить данные без риска потерять их после выключения питания или перезагрузки платы. В этом материале на примере сохранения текстовой информации будет показано как работать с EEPROM в Arduino.

Сначала нужно упомянуть об ограничениях, присущих EEPROM. Во-первых, размер этой энергонезависимой памяти сильно лимитирован. Он меньше памяти данных и памяти программ, и, как правило, составляет всего несколько килобайт. Но этого должно хватить для не слишком ресурсоемких приложений. Во-вторых, количество циклов чтения/записи у EEPROM также заметно ограниченно и, как правило, не превышает 10000 циклов, поэтому работать с этой памятью нужно аккуратно и рационально и не записывать в нее слишком часто.

Для работы с функциями энергонезависимой памяти в среде Arduino IDE имеется библиотека EEPROM.h. С помощью этой библиотеки работать с EEPROM в Arduino очень просто. Для записи в память служит функция EEPROM.write(address, data). В аргумент address записывается адрес ячейки памяти, в которую будут записаны данные, представляемые вторым аргументом data. Для чтения данных из энергонезависимой памяти служит функция EEPROM.read(address), имеющая всего лишь один аргумент address, указывающий на адрес ячейки памяти, из которой нужно прочитать данные.

Для того, чтобы попрактиковаться с EEPROM в Arduino можно сделать небольшой проект. В этом примере мы сначала считаем все ячейки энергонезависимой памяти. Если в них что-то есть, то это будет выведено в последовательный порт. Далее вся ячейки будут заполнены пробелами. Также будет возможность ввести текст через монитор последовательного порта. Этот текст тоже будет записан в EEPROM, и при последующем включении его можно будет считать. Все эти процедуры выполняются не в основном (бесконечном) цикле программы loop, а в процедуре setup(), чтобы не тратить лишний раз ресурс памяти. В основном цикле только каждую секунду мигает светодиод, подключенный к выводу 6 через токоограничивающий резистор 1 КОм. Код программы Arduino для работы с EEPROM приведен ниже.

#include <EEPROM.h> int address = 0; // адрес eeprom int read_value = 0; // считываемые с eeprom данные char serial_in_data; // данные последовательного порта int led = 6; // линия 6 для светодиода int i; void setup() { pinMode(led, OUTPUT); // линия 6 настраивается на выход Serial.begin(9600); // скорость передачи по последовательному порту 9600 Serial.println(); Serial.println(«PREVIOUS TEXT IN EEPROM :-«); for(address = 0; address < 1024; address ++) // считываем всю память EEPROM { read_value = EEPROM.read(address); Serial.write(read_value); } Serial.println(); Serial.println(«WRITE THE NEW TEXT : «); for(address = 0; address < 1024; address ++) // заполняем всю память EEPROM пробелами EEPROM.write(address, ‘ ‘); for(address = 0; address < 1024; ) // записываем пришедшие с последовательного порта данные в память EEPROM { if(Serial.available()) { serial_in_data = Serial.read(); Serial.write(serial_in_data); EEPROM.write(address, serial_in_data); address ++; digitalWrite(led, HIGH); delay(100); digitalWrite(led, LOW); } } } void loop() { //—- мигаем светодиодом каждую секунду ——// digitalWrite(led, HIGH); delay(1000); digitalWrite(led, LOW); delay(1000); }

Я много думал о том, чтобы найти недорогое решение для подключения и управления Arduino через интернету без использования какого-либо Ethernet экрана или даже любого модуля WI-FI. После исследования я обнаружил, что единственный способ взаимодействия с микроконтроллером Arduino — это его последовательный порт (serial port), поэтому я создал простое оконное приложение C# в качестве хаба (HUB) для работы с последовательным портом для отправки и приема данных на плату.

Это HUB-приложение уже подключено к Интернету через ваш персональный компьютер и помогает отправлять и получать данные между микроконтроллером и облачной базой данных, помимо хранения самих данных в онлайн-базе MySQL.

Шаг 1. Тестируем Ардуино

Прежде всего мне пришлось начать с небольшого примера, который позволяет мне протестировать применение идеи. В этом примере я не подключил какой-либо датчик, я использовал только встроенный светодиод на Arduino, чтобы я мог включать и выключать светодиодный индикатор на выводе 13, отправив буквы «I» и «O» на последовательный порт.

int input; //Будет хранить входящий символ из последовательного порта. int led = 13; // Pin 13 // функция настройки запускается один раз, когда вы нажимаете кнопку сброса или включаете питание int state; void setup() { // инициализировать цифровой вывод LED_BUILTIN в качестве выхода. Serial.begin(9600); pinMode(led, OUTPUT); // Установите контакт 13 как цифровой выход Serial.flush(); } // функция цикла работает снова и снова всегда void loop() { // String input = «»; // while (Serial.available() > 0) // { // input += (char) Serial.read(); // delay(5); // } state = digitalRead(led); if (Serial.available()) { input = Serial.read(); if (input == ‘I’) { //digitalWrite(led, !digitalRead(led)); digitalWrite(led, HIGH); Serial.println(1); delay(1000); } else if (input == ‘O’) { digitalWrite(led, LOW); Serial.println(0); delay(1000); } else if (input == ‘T’) { analogRead(led); Serial.println(0); delay(1000); } } }

Простое управление вашим Arduino через web

Эта статья предназначена для новичков. Здесь будет описано как из web приложения при помощи ajax запросов посылать команды phyton скрипту, который будет передавать их через serial port непосредственно на наш arduino.
Вы приобрели себе Arduino, попробовали несколько примеров, поигрались со скетчами. Но вам этого мало, вы хотите управлять, управлять всем этим добром через интернет. Самый простой способ — это приобрести шилдик с Ethernet-портом и подключить его к Arduino (или приобрести платку с уже встроенным Ethernet ). Но она и стоит дороже и в управлении надо поднатаскаться.
Для работы нам понадобятся:
— HTTP сервер
— интерпретатор python
— Arduino
Тут я опишу где взять первое и второе, и как их подружитьТеперь по порядку. Как HTTP сервер я использую Apache. Установить его не составит труда. Если вы совсем новичок и используете windows, то можете взять пакет Denwer с официального сайта, в его составе есть Apache.

Python (я использовал версию 3.3) можете взять так же с официального сайта и установить. Теперь нам надо подружить наш Apache и python. Самый простой способ — это запускать python как cgi. Для этого открываем файл httpd.conf в папке conf в том месте где вы поставили свой apache (если вы поставили denwer то путь будет примерно следующим: :\usr\local\bin\apache)
Ищем строчку
AddHandler cgi-script .cgi
Добавляем в конце через пробел .py и смотрим, чтоб в начале строки не было знака #. Сохраняем, перезапускам сервер.
Теперь для проверки тесной дружбы pythone и apache можно создать тестовый файлик и положить его в домашнюю папку.
#!/Python33/python.exe print («STATUS: 200 OK\n\n») print («<b>hello world</b>»)
Обратите внимание что первой строкой мы показываем где у нас лежит интерпретатор языка. У меня, например, он лежит по адресу C:/Python33/python.exe. Думаю, разберетесь. Назовите его как хотите и зайдите на него через браузер, например, так: localhost/my_first_test_phyton_file.py. Если увидите «hello world», то все хорошо.
Код основного управляющего скрипта на JavaScript предельно прост:
//Порт к которому подключен Arduino var serialPort = ‘COM5’; //непосредственно управляющая функция var Arduino = function(command, callback){ $.get(‘c.py’,{ c:command, p:serialPort }, callback); }
Единственное что тут надо менять, как вы догадались, это порт, на котором у вас подключен arduino. Его всегда можно посмотреть в windows используя Диспетчер устройств. Мы его будем передавать в наш python скрипт чтоб тот знал на какой serial port отправлять полученные данные.
Теперь, если мы сделаем вызов нашей функции, например: Arduino(123), то скрипт создаст ajax запрос вида с.py?c=123&p=COM5 и пошлет его на наш python скрипт c.py. Рассмотрим, что он из себя представляет:
#!/Python33/python.exe import serial import cgi print («STATUS: 200 OK\n») req = cgi.FieldStorage(); ser = serial.Serial(req.value, 9600, timeout=1) ser.write(bytes(req.value,’latin’)) ser.close() print («ok»)
Фактически он просто принимает значение параметра «с», передает его в serial port «p» и пишет «ok». Дешево и сердито.
Для тех, кто хочет не только отдавать, но и принимать, напишем больше кодаНемного усовершенствуем нашу клиентскую часть.
//непосредственно управляющая функция var Arduino = function(sp, errorCallback) { this.serialPort = sp; this.errorCallback = errorCallback || function(){ console.log(‘Error’); } this.send = function(data, callback){ var callback = callback; var self = this; data = this.serialPort; data = Math.round(Math.random()*1000); //на всякий случай, чтобы браузер не кешировал $.ajax({ url:’c.py’, data:data, success:function(data){ if($.trim(data) == ‘error’){ self.errorCallback(); } else { if(typeof callback == «function») callback(data); } } }); } //передаем this.set = function(command, callback){ this.send({ c:command, r:0 }, callback); } //передаем и ожидаем ответ this.get = function(command, callback){ this.send({ c:command, r:1 //флаг отвечающий за режим «ожидаем ответа» }, callback); } }
Теперь, поскольку мы превратили Arduino в класс, то простейший вызов будет примерно таким:
var myArduino = new Arduino(‘COM5’); myArduino.set(113); //зажигаем светодиод на пине 13 myArduino.get(36,function(data){console.log(data)}); //смотрим состояние пина 6. и выводим его в консоль
Ну и, конечно, надо немного изменить серверную часть:
#!/Python33/python.exe import serial import cgi print («STATUS: 200 OK\n») req = cgi.FieldStorage(); try: ser = serial.Serial(req.value, 9600, timeout=1) except: print(«error») exit() ser.write(bytes(req.value,’latin’)) if int(req.value) == 1: res = »; while not res: res = ser.readline() print(res.decode(‘UTF-8’)) else: print («ok») ser.close()
Тут почти ничего не поменялось, кроме того, что когда сервер в запросе получает параметр r=1 то он ожидает от Arduino ответ.
И мы добавили проверку на то, смог ли наш скрипт открыть serial port. Если нет, то вернет ключевое слово «error»
Теперь давайте рассмотрим скетч для arduino, который все это принимает и обрабатывает:
#include <Servo.h> Servo myservo; void setup() { Serial.begin(9600); } String getParam(){ String re; while (Serial.available()) { re.concat(Serial.read()-48); } return re; } int getPin(String p){ return p.substring(0,2).toInt(); } int getVal(String p){ return p.substring(2,6).toInt(); } // Главный цикл void loop() { while (Serial.available()) { char command = (char)Serial.read(); String param = getParam(); int pin = getPin(param); int p; switch (command) { case ‘0’: //Digital write pinMode(pin,OUTPUT); digitalWrite(pin, LOW); break; case ‘1’: //Digital write pinMode(pin,OUTPUT); digitalWrite(pin, HIGH); break; case ‘2’: //Servo myservo.attach(pin); p = getVal(param); myservo.write(p); break; case ‘3’: //Digital read pinMode(pin,INPUT); Serial.print(digitalRead(pin)); break; case ‘4’: { //Analog read int aPin = A0; switch (pin) { case 1: aPin = A1; break; case 2: aPin = A2; break; case 3: aPin = A3; break; case 4: aPin = A4; break; case 5: aPin = A5; break; } Serial.print(analogRead(aPin)); } break; case ‘5’: //Analog write pinMode(pin,OUTPUT); p = getVal(param); analogWrite(pin, p); break; } } }
По serial port мы будем передавать команды вида: 1234567 где:
— номер команды
— номер пина
— данные для пина, если надо.
Например:
113 — установит пин 13 на вывод и передаст по нему состояние HIGH (то-есть включит).
013 — установит пин 13 на вывод и передаст по нему состояние LOW (то-есть выключит).
209100 — установит пин 9 как управляющий сервоприводом и передаст ему значение 100 через ШИМ модуляцию.
310 — установит пин 10 на ввод и считает с него данные HIGH / LOW и вернет как 1 или 0 соответственно.
Вы запросто можете дописывать и свои команды в switch case блок.
Теперь добавим немного красоты в нашу frontend часть и получим, например, такое
Далее я добавил немного магии юзер-интерфейса. Но его я не буду описывать, все интересующиеся могут взять его из архива с проектом.
Для web-части использовал Bootstrap (исключительно из-за удобства и его «резиновости») и jQuery (для ajax).
Теперь посмотрим как это работает.
Сначала надо указать на каком порту у вас устройство и сколько пинов имеет. Потом выбрать на каком пине у вас что находится, и вперед к управлению.

Из недостатков такого подхода можно отметить относительно медленную скорость обмена данных. Чтоб узнать состояние, например, кнопки надо посылать запросы, но слишком часто это делать нельзя, так как можем упереться в занятый serial port. На веб-сокетах работало бы быстрее, но это уже чуть более продвинутая тема, которую я, если захотите, освещу позже.
Проверялось все под Windows 8 х64. Наверно, есть какие-то особенности реализации всего этого под другие системы, буду рад услышать об этом в комментариях.
Теперь о том, где все это может пригодится: например можно сделать демонстрационный стенд; управлять положением камеры; подключить датчик температуры и прочие приборы и удаленно наблюдать за каким нибудь процессом и т.д.
Архив с проектом
Для запуска на iPad в полный экран я использовал бесплатную программу oneUrl
В тематические хабы не вставил только лишь из за отсутствия кармы.
Это первая моя статья. Буду рад ответить на вопросы.
UPD: По просьбам трудящихся я потестил так же этот метод на MacOS. Особых проблем не возникло. На маке обычно уже стоит по умолчинию python, единственное что надо сделать, это подружить его с apache. Первая строка в c.py будет
#!/usr/bin/python
Так же, возможно у вас не будет установленно расширение для питона pyserial, оно устанавливается простой командой в консоли:
easy_install -U pyserial
Далее следует обратить внимание, что обычно предустановленная версия python достаточно старая и может не работать строка