Как запомнить значения переменных после перезагрузки платы Arduino Uno R3

Есть ли способ где-нибудь сохранить значения необходимых переменных (может быть в файле, который обновляется программой при различных событиях), который можно будет прочитать при запуске программы после перезагрузки платы Arduino?

Я пытаюсь управлять своими приборами переменного тока с помощью ИК-пульта дистанционного управления. Где я хочу, чтобы Arduino помнил, что было включено и что выключено в моей комнате после сбоя питания.

Использование EEPROM кажется довольно хорошим решением, хотя оно имеет ограничение в 100 000 операций записи, что заставляет меня искать другой выход.

Использование внешнего EEPROM, который не имеет такого ограничения или имеет достаточно большой размер, — это действительно хорошая идея, которую я подал от fuenfundachtzig, спасибо за эту замечательную идею. Но я не знаю, как это реализовать в своем проекте. Любой ресурс для сбора знаний об использовании внешнего EEPROM будет высоко оценен.

, 👍9

Обсуждение

Если вас беспокоит только перезагрузка, а не выключение и выключение питания, то вы можете использовать ОЗУ — вам просто нужно не допускать очистки кода инициализации. Конечно, если вам нужно пережить и циклическое включение питания, вам понадобится EEPROM., @Chris Stratton

На самом деле меня очень беспокоит цикличность питания. Я пытаюсь управлять своими приборами переменного тока с помощью ИК-пульта дистанционного управления. Я хочу, чтобы Arduino помнил, что было включено и что выключено в моей комнате после сбоя питания., @Samik Chattopadhyay


7 ответов


2

Сохраните их во встроенной EEPROM.

  • http://www.arduino.cc/en/Reference/EEPROM
  • http://www.arduino.cc/en/Tutorial/EEPROMWrite
  • http://www.arduino.cc/en/Tutorial/EEPROMRead
,

Память EEPROM имеет указанный срок службы 100 000 циклов записи/стирания, поэтому использование EEPROM может быть ограничением, поскольку программа часто автоматически записывает в нее значения, хотя считывает их довольно редко., @Samik Chattopadhyay


7

Внутренняя оперативная память Arduino будет сброшена при повторном включении чипа, поэтому, если вы хотите сохранить свои данные, вам необходимо сохранить их в EEPROM.

Если вас беспокоит ограниченность циклов записи/стирания, вам следует оценить, как часто данные будут обновляться (т. е. записываться в EEPROM) и как долго вы планируете срок службы создаваемого вами устройства. Если 100 000 циклов по-прежнему являются проблемой, у вас есть несколько вариантов:

  • Храните переменные в ОЗУ и записывайте их в EEPROM, скажем, каждые 5 минут (тогда 100 000 циклов составляют примерно один год непрерывного использования, пока EEPROM может выйти из строя).
  • Используйте внешнюю EEPROM (в гнезде), которую можно легко заменить при необходимости.
  • Если заменить его невозможно, рассмотрите возможность использования вместо него FRAM (преимущество по сравнению с EEPROM: много больше циклов стирания/записи, но и очень дорого, поэтому не подходит для больших объемов данных, но определенно для нескольких переменных).

Однако обратите внимание, что ваше описание похоже на типичный вариант использования пользовательских настроек, которые вы не хотите потерять при перезагрузке. Для них внутренняя EEPROM — подходящее место: храните их только тогда, когда они изменены, и вы в безопасности — пользователь не будет легко менять их 100 000 раз...

,

Обычно я использую для подобных целей внешнюю EEPROM; что-то вроде AT24C02 отлично подходит для большинства проектов (это 256 байт). Я просто предпочитаю внешнюю память, поскольку она рассчитана на большее количество циклов чтения/записи и потому что вы можете заменить ее, если она больше не работает., @Stefa168

Баран не очищается. Глобальные переменные устанавливаются в ноль в соответствии со стандартом «c». Неиспользуемая оперативная память сохраняет свое значение во время сброса., @Jot

Да, ты прав. Я думал о сбое питания, который приводит к потере данных в оперативной памяти (и что приводит к необходимости повторной инициализации)., @fuenfundachtzig

как насчет использования устройства чтения SD-карт. это не так уж и дорого, и SD-карта проживет много времени., @m3nda

Кажется, у adafruit есть FRAM за 5 долларов. Я что-то пропустил?, @rfii

Прибл. 5,5 лет?, @fuenfundachtzig

Я очень опоздал на эту вечеринку... показатель 100 000 (типичных) операций записи перед сбоем в EEPROM - разве это не считается побитно? Это означает, что каждый бит в EEPROM может быть записан как «1» примерно 100 000 раз, прежде чем ожидается сбой. V записывать НИЧЕГО КУДА в EEPROM? В первом случае можно использовать громоздкую систему отслеживания количества записей, добавляя счетчик записей к каждому блоку данных вместе с указателями на текущий блок. Я сказал «громоздкий», но это может работать как каталог на диске. Громоздко., @user3481644


3

Пространство EEPROM, как уже упоминалось, будет работать, если требуется энергонезависимость. Однако вы не знаете, должен ли он быть энергонезависимым или нет. Если нет, то вы можете использовать __attribute__, чтобы определить пространство переменных, которое не будет инициализироваться. Поскольку SRAM на ATmega не очищается при перезагрузке или выключении питания. Компилятор по умолчанию инициализирует их. Где это можно отключить для каждой переменной, как в примере ниже:

//неинициализированное значение
union configUnion{
  uint8_t    byte[6]; // соответствие приведенной ниже структуре...
  struct {
    uint16_t value1;
    uint16_t value2;
    uint16_t chksum;
  } val ;
} config  __attribute__ ((section (".noinit")));

void setup() {
  uint16_t sum; 
  //Инициализируем последовательный порт и ждем открытия порта:
  Serial.begin(9600);
  while (!Serial) {
    ; // ждем подключения последовательного порта. Требуется только для Леонардо.
  }

  // печатает заголовок с конечным переносом строки
  Serial.print("Out of Reset -");
  sum = getchksum();
  printValues();

  if (sum != config.val.chksum) {
    config.val.chksum = sum;
    Serial.print("chksum is incorrect setting config.val.chksum = 0x"); Serial.println(config.val.chksum, HEX);
  }

  config.val.value1++;
  config.val.value2++;
  Serial.print("setup new values - ");
  printValues();
  config.val.chksum = getchksum();
  Serial.print("updating chksum config.val.chksum = 0x"); Serial.println(config.val.chksum, HEX);
}

int counter = 0;

void loop() {
  if (counter < 200) {
    Serial.print("after a while - ");
    printValues();
    Serial.println();
    while (true) {
      continue;
    }
  }
  counter++;
}

void printValues() {
  Serial.print(" value1 = 0x"); Serial.print(config.val.value1, HEX);
  Serial.print(", value2 = 0x"); Serial.print(config.val.value2, HEX);
  Serial.print(", sum = 0x"); Serial.print(getchksum(), HEX);
  Serial.print(", chksum = 0x"); Serial.println(config.val.chksum, HEX);
}

uint16_t getchksum() {
  int sum = 0;
  for (int position = 0; position < (sizeof(config) - sizeof(config.val.chksum)); position++) {
    sum = sum + config.byte[position];
  }
  return sum;
}

Вывод приведенного выше кода приведен ниже. Обратите внимание, что первый вывод был результатом включения UNO с нажатой кнопкой Reset, а затем отпущенной после запуска окна Monitor. В противном случае печать «неверной настройки» была бы пропущена до того, как окно монитора можно было бы запустить.

Out of Reset - value1 = 0xBFED, value2 = 0xD2F9, sum = 0x377, chksum = 0xF457
chksum is incorrect setting config.val.chksum = 0x377
setup new values -  value1 = 0xBFEE, value2 = 0xD2FA, sum = 0x379, chksum = 0x377
updating chksum config.val.chksum = 0x379
after a while -  value1 = 0xBFEE, value2 = 0xD2FA, sum = 0x379, chksum = 0x379

Out of Reset - value1 = 0xBFEE, value2 = 0xD2FA, sum = 0x379, chksum = 0x379
setup new values -  value1 = 0xBFEF, value2 = 0xD2FB, sum = 0x37B, chksum = 0x379
updating chksum config.val.chksum = 0x37B
after a while -  value1 = 0xBFEF, value2 = 0xD2FB, sum = 0x37B, chksum = 0x37B

Out of Reset - value1 = 0xBFEF, value2 = 0xD2FB, sum = 0x37B, chksum = 0x37B
setup new values -  value1 = 0xBFF0, value2 = 0xD2FC, sum = 0x37D, chksum = 0x37B
updating chksum config.val.chksum = 0x37D
after a while -  value1 = 0xBFF0, value2 = 0xD2FC, sum = 0x37D, chksum = 0x37D

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

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

,

3

Если вам действительно необходимо часто обновлять NVRAM, загляните в FRAM (сегнетоэлектрическое оперативное запоминающее устройство), срок записи которого составляет 10^13 циклов (это ~ 318 000 лет, если вы пишете один раз в секунду!). Их размер составляет 8 КБ, и они быстрее, чем Flash или EEPROM с частотой 20 МГц (SPI).

Adafruit (в США) продает их на специальной плате по цене около 6$ за штуку. Или вы можете купить голые чипы у обычных поставщиков примерно за 1,30 доллара, но не DIP, а только в упаковках SOP8.

,

0

Если вы готовы к внекристальному решению, купить/создать устройство чтения SD-карт несложно.

,

Пожалуйста, постарайтесь дать более подробный ответ: предоставьте ссылки на такие устройства чтения SD-карт, опубликуйте пример кода о том, как читать/записывать из него переменные, и упомяните, чем это лучше, чем EEPROM., @jfpoilpret

Хорошее предложение. SD использует обычный интерфейс SPI. Итак, все, что вам нужно, это преобразование логического уровня. Кроме того, вы подключаете их напрямую к Arduino. Просто припаяйте несколько проводов к адаптеру micro-SD-SD., @Gerben

+jfpoilpret ИМХО, устройства чтения SD-карт распространены повсеместно и не нуждаются в примерах или цитировании. Код зависит от марки считывателя, поэтому нет смысла предоставлять произвольный код. Я уже упоминал, что он лучше EEPROM, его не так уж сложно купить или собрать., @linhartr22


0

Я очень опоздал на эту вечеринку... Я только что купил UNO WiFi и собираюсь создать простой протокол для сохранения данных в хранилище данных на другом устройстве, подключенном к Wi-Fi. Вероятно, мы будем использовать HTTP с простыми конечными точками и рассматривать удаленное хранилище как непрерывный блок памяти:

GET IP:port/addr/len
PUT IP:port/addr data

Я также мог бы создавать именованные блоки (например, файлы):

GET IP:port/name
PUT IP:port/name data

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

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

,

Итак, ваш ответ: «Добавить Wi-Fi и сохранить значения на удаленном сервере»? разве это не сложно, если проект не использует Wi-Fi ни для чего другого?, @Juraj

У тебя EEPROM не работает?, @Nick Gammon

@Юрай - Да, это мой ответ, и да, это сложно, если ваш проект не использует Wi-Fi ни для чего другого, но, как я уже говорил в своем ответе, Wi-Fi - мое лучшее решение, и я предложил его как еще один возможный ответ на в определенных ситуациях см. мой ответ Нику Гэммону, @user3481644

@NickGammon - Решение EEPROM - не лучшее мое решение, поскольку я не могу предсказать, сколько обновлений сохраненных данных, как в обновлениях, так и в новых данных, поэтому в конечном итоге я могу использовать все данные EEPROM и/или выполнить в них слишком много записей, эффективно уничтожая для меня возможности ООН., @user3481644

Все - ОП заявил: «Есть ли способ где-нибудь сохранить значения необходимых переменных» - в моем ответе акцент на «где-то». «Где-то» включает в себя локальную сеть, о чем и говорит мой ответ., @user3481644

Перечитывая ваш ответ, мне кажется, что это новый вопрос. Возможно, вы захотите сделать это одним. Вопрос задан о запоминании состояний выключателя света после сбоя питания. Учитывая, что Uno имеет 1024 байта EEPROM, а для запоминания состояния включения/выключения необходим только один бит, вы можете хранить 8096 состояний. Или выполните балансировку нагрузки, чтобы после 100 000 операций записи в EEPROM вы переключились на новый банк байтов. Я предполагаю, что у них в доме нет лампочек 8096., @Nick Gammon

Ваше предложение использовать для этой цели веб-сервер приведет к длительной задержке (около 5 минут), пока маршрутизатор и веб-сервер перезагрузятся и будут готовы отвечать на запросы. Если бы отключилось электричество, действительно ли вы захотели бы подождать несколько минут, прежде чем свет снова включится? Если вы пытаетесь решить другую проблему, чем *Я хочу, чтобы Arduino запомнил, что было включено и что выключено в моей комнате после сбоя питания*, я предлагаю вам задать свой собственный новый вопрос., @Nick Gammon

@NickGammon Я использую Arduino с очень упрощенной реализацией HTTP, чтобы клиент был знаком с API. Я получил UNO версии 3 с платой SD/RTC другого производителя, используя простой сервис HTTP, который я создал для другого проекта. Услуга доступна за считанные секунды. С маршрутизатором другая проблема, но я рассматриваю простые устройства, они не будут ничем подвергаться воздействию, поэтому даже коммутатор подойдет., @user3481644

@NickGammon Вся эта история с банками в EEPROM — это много шуток. У меня более 75 устройств, многие из них являются переменными, поэтому для них мне понадобится 7 бит (0–99/128). Мне также нужна таблица для сопоставления устройств со слотами в банке памяти. Многие устройства будут «горячими» и обновляться гораздо чаще, поэтому из-за одного-двух устройств я могу потерять целый банк. А затем проводится тестирование управления банком, которое облегчается за счет простого перемещения банка при каждой записи вместо подсчета и т. д. Однако я не знаю, что нужно покупать новый Arduino, если один из них выйдет из строя., @user3481644

@NickGammon ... И ... Мой вопрос выходит за рамки моего вопроса. У меня есть аналогичные проекты (необходимы данные) для моего дома на колесах. Одно приложение будет иметь записи размером около 50 байт каждая и неизвестное количество записей. Другое приложение будет иметь записи размером около 120 байт каждая, и их могут быть сотни, и любую из них можно будет обновить., @user3481644

@NickGammon Не буду на этом останавливаться, но в моем доме с освещением/устройствами маршрутизатор и сервер будут подключены к ИБП (SPS) примерно на 12 часов работы, а освещение/устройства - нет., @user3481644

Вы делаете несколько хороших замечаний, но вы меняете вопрос с того, чего хотел ОП: «Я хочу, чтобы Arduino помнил, что было включено и что выключено в моей комнате после сбоя питания» на **ваши** требования, которые кажутся несколько разными. Если вы хотите задать новый вопрос с этими несколько более сложными требованиями, мы могли бы обсудить его дальше., @Nick Gammon

@NickGammon Мой «морф» объяснял мой проект и то, как ОП может использовать мой метод для сохранения данных. Всем остальным следовало бы просто оставить все как есть - я НИКОГДА не просил о помощи., @user3481644


0

Меня это тоже озадачило. Я просто хотел использовать встроенную кнопку, чтобы вызвать команду «подсчитать целое число». независимо от каких-либо процедур настройки. Для меня ответ mpflaga дал мне правильное направление. Но данный пример был слишком сложен для моих нужд. Итак, вот минимальный пример целочисленного значения, которое сохраняется после сброса при обратном подсчете. (Но НЕ потеря мощности, так как она находится в оперативной памяти!) Наслаждайтесь!

uint8_t c __attribute__ ((section (".noinit")));

void setup() {
  Serial.begin(9600);
  Serial.print("Just startet. c = ");
  Serial.print(c);
  if(c>7){c=0;}else{c++;}
  Serial.println(" - Waiting for RESET.");
}

void loop() {
  // Делаем больше вещей.
}
,