Eeprom что это в роутере

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

EEPROM для Wi-Fi используется для хранения различных параметров настройки сети, таких как SSID (имя Wi-Fi сети), пароль, IP-адреса и другие параметры. Когда устройство включается, оно загружает эти настройки из EEPROM, что позволяет ему подключаться к Wi-Fi сети автоматически.

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

EEPROM является неотъемлемой частью Wi-Fi устройств и играет ключевую роль в их функционировании. Благодаря EEPROM, устройства могут хранить и получать данные даже при отключении питания, что обеспечивает удобство использования и повышает безопасность передачи данных.

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

Содержание

  1. Что такое EEPROM для Wi-Fi и зачем он нужен?
  2. Важность EEPROM в Wi-Fi устройствах
  3. Преимущества использования EEPROM в Wi-Fi

Что такое EEPROM для Wi-Fi и зачем он нужен?

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

В EEPROM могут храниться такие данные, как имя сети (SSID), пароль, тип шифрования, IP-адреса и другие настройки. Когда устройство включается, оно считывает эти данные из EEPROM и автоматически настраивает себя для подключения к Wi-Fi сети.

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

Кроме того, использование EEPROM позволяет избежать необходимости каждый раз вводить сетевые настройки вручную при подключении к Wi-Fi. Это удобно, особенно если устройство используется в разных wi-fi сетях или при перемещении в другие места.

В целом, EEPROM для Wi-Fi – это важный компонент, который обеспечивает удобство и надежность подключения к Wi-Fi сетям. Благодаря этому чипу, устройства могут автоматически сохранять и использовать настройки Wi-Fi сети, что позволяет избежать лишней конфигурации и сократить время настройки сетевого соединения.

Важность EEPROM в Wi-Fi устройствах

Одной из основных причин важности EEPROM в Wi-Fi устройствах является сохранение настроек. Когда вы настраиваете свое устройство Wi-Fi, вы задаете такие параметры, как имя сети, пароль, IP-адрес и так далее. Эти настройки сохраняются в EEPROM, чтобы они не были потеряны после перезагрузки устройства или отключения питания. Благодаря данным из EEPROM, ваше устройство сможет автоматически подключаться к Wi-Fi сети при каждом включении.

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

Кроме того, EEPROM позволяет обновлять программное обеспечение (прошивку) устройства. Благодаря ей, можно загружать новые версии программного обеспечения, исправлять ошибки, улучшать производительность и добавлять новые функции без необходимости менять саму плату устройства. Программное обеспечение хранится в EEPROM и может быть обновлено по мере необходимости, что упрощает обслуживание и увеличивает долговечность Wi-Fi устройств.

В итоге, EEPROM играет важную роль в Wi-Fi устройствах, обеспечивая сохранение настроек, нужных данных и возможность обновления программного обеспечения. Благодаря EEPROM, ваше устройство может сохранять свои настройки, подключаться к сети автоматически и обновляться вместе с изменяющимися требованиями сети и пользователей.

Преимущества использования EEPROM в Wi-Fi

Применение EEPROM (Electrically Erasable Programmable Read-Only Memory) в Wi-Fi имеет ряд значительных преимуществ:

1. Хранение настроек Wi-Fi: EEPROM позволяет сохранять настройки Wi-Fi, такие как SSID (Service Set Identifier) и пароль. Это позволяет устройствам подключаться к определенным сетям без необходимости каждый раз вводить данные заново.

2. Быстрое восстановление соединения: благодаря удобству хранения настроек Wi-Fi в EEPROM, устройства могут быстро восстанавливать соединение с известными сетями после перезагрузки или обрыва связи. Это особенно полезно в случае использования Wi-Fi во встраиваемых системах и мобильных устройствах, где необходимо минимизировать время простоя.

3. Гибкость при настройке: EEPROM обеспечивает гибкость при настройке Wi-Fi, поскольку можно изменить сохраненные настройки в любой момент. Это позволяет пользователям легко переключаться между разными сетями Wi-Fi или обновлять настройки без необходимости перепрошивки устройства.

4. Экономия ресурсов: используя EEPROM для хранения настроек Wi-Fi, устройства могут снизить потребление ресурсов, такие как энергию и процессорное время, поскольку нет необходимости каждый раз вводить настройки при подключении к сети. Это особенно важно для устройств с ограниченными ресурсами, как например микроконтроллеры.

5. Защита данных: EEPROM позволяет защитить настройки Wi-Fi от несанкционированного доступа или изменения. Это особенно важно в случае, когда настройки включают в себя пароли или другую конфиденциальную информацию.

Использование EEPROM в Wi-Fi обеспечивает удобство, гибкость и экономию ресурсов, что делает его незаменимым компонентом для многих Wi-Fi устройств.

In this tutorial, we’ll learn about ESP8266 manual WiFi configuration with EEPROM without a hard-code network certificate. ESP8266 WiFi-Manager library is a way to manually configure the wifi but, we will not use it here. If you want to know about how to use WiFi-Manager library please refer my previous tutorial: –WiFiManager with ESP8266 – Autoconnect – without having to hard-code.

ESP8266 Manual Wifi Configuration with EEPROM without Hard-Code

 In this section you can learn how to configure WiFi settings on an ESP8266 board without hard-code network credentials (SSID and password) into your code. Here we will use an EEPROM library that allows you to connect your ESP8266 to various access points (APs) and upload new code to your board without hard-coding.

PCBWays

 Related posts:

  • WiFiManager with ESP8266 – Autoconnect – without having to hard-code
  • ESP32 with WiFiManager to Manage SSID and password (No Hard Coding)
  • Different Types of Memory on Microcontroller -RAM, EEPROM, Flash.
  • How to use Arduino’s EEPROM memory? LED state after the power off.
  • ESP32 Wi-Fi Manager using AsyncWebServer library
  • ESP32 WebSocket Server using Arduino IDE
  • ESP32 Asynchronous Web Server using Arduino IDE
  • ESP32/ESP8266 – Passing Input Data on HTML Form Web Server

ESP8266 Manual Wifi Configuration with EEPROM

 Nowadays all IoT products work on this platform. Initially, they all act as hotspots. Then you need to connect your phone or PC to that hotspot. Once the hotspot is connected, then go to the special IP address (192.168.4.1) and then enter the WiFi certificate, and then it is automatically connected to the home router or WiFi. So, most IoT devices are built using ESP 8266 Wifi which helps you set network configuration (for example SSID, and WiFi router password) through a form and store it in your memory.

ESP8266 is a popular Wi-Fi module used in many Internet of Things (IoT) projects. Normally, when configuring the ESP8266 to connect to a Wi-Fi network, the network credentials such as SSID (network name) and password are hardcoded directly into the code. This means that whenever you want to change the network, you need to reprogram the module with new credentials.

How do ESP8266 WiFi Configuration Works?

the working of ESP8266 Wifi Configuration with EEPROM in three sections:

  1. Initial Configuration:
    • Initially, when the ESP8266 module is powered on, it checks the EEPROM memory for stored Wi-Fi credentials.
    • If valid credentials are found, the module tries to connect to the specified network.
    • If the credentials are not found or invalid, the module enters a configuration mode.
  2. Configuration Mode:
    • In the configuration mode, the ESP8266 creates its own Wi-Fi network, acting as an access point (AP).
    • You need to connect a device (e.g., smartphone, laptop) to this network.
    • Once connected, you can access a web interface hosted by the ESP8266 to configure thesettings.
    • The ESP8266 serves a web page where you can input the SSID and password of the desired Wi-Fi network.
    • Upon submitting the new credentials, the ESP8266 stores them in the EEPROM memory.
  3. Connecting to the Configured Network:
    • After storing the new Wi-Fi credentials, the ESP8266 restarts and attempts to connect to the configured network.
    • It reads the credentials from the EEPROM and uses them to establish a connection with the network.
    • If the connection is successful, the ESP8266 can perform its intended tasks, such as sending sensor data to a server or accessing other resources on the network.

However, there is an alternative method called “WiFi Configuration with EEPROM” that allows you to store the Wi-Fi credentials in the module’s EEPROM (Electrically Erasable Programmable Read-Only Memory) instead of hardcoding them in the code.

ESP8266 Manual Wifi Configuration with EEPROM without Hard-Code

Here’s how it works:

  • Initially, you need to program the ESP8266 module with a sketch that contains code to read the network credentials from the EEPROM.
  • When the module is powered on, it reads the network credentials stored in the EEPROM memory.
  • If the credentials are available and valid, the ESP8266 connects to the Wi-Fi network using the read credentials.
  • If the credentials are not available or invalid (for example, when the module is powered on for the first time), the ESP8266 enters a configuration mode where it creates its own Wi-Fi network. You can connect to this network using a device (like a smartphone) and provide the new network credentials through a web interface.
  • Once the new credentials are received, the ESP8266 updates the EEPROM with the new values and connects to the provided Wi-Fi network.
  • This approach allows you to change the Wi-Fi network without reprogramming the module. You can simply connect to the ESP8266’s configuration network and update the credentials through a web interface.

By using EEPROM to store the credentials, you can easily modify them without modifying the code itself. It provides flexibility and convenience, especially in scenarios where the ESP8266 needs to connect to different Wi-Fi networks dynamically.

Concept of Project:

As discussed at the start we will be using ESP8266 internal flash memory to store local network credentials with help simple html page that will accept SSID and Password information and will pass to defined variable that will get stored in EEPROM permanently on submitting using HTML form.

As like in our previous tutorials for the ESP8266 WIFI manager we will be configure our ESP8266 in Access point (Station mode) and client mode based on availability and connectivity with saved credentials for SSID and password of the network.

ccessing a network is real challenge comes with ESP8266 with hard coded credentials, when there is change in network or credential. And only way to connect with network to change the Arduino program with new network credentials. Using Wi-fi manager with the EEPROM library there is no need to hard code the SSID and password of the local network in our Arduino sketch. User will be able to connect any new network (Access Points) with ESP32 board automatically instead manually entering the network credentials every time.

On accessing webpage with IP address 192.168.4.1 the ESP32 Wi-Fi manager web page will open to set the SSID, the password to connect to back as an webserver. On submitting the Wi-Fi credentials it will get saved in their respective location on EEPROM memory.

Arduino sketch:

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
 
//Variables
int i = 0;
int statusCode;
//const char* ssid = "text";
const char* passphrase = "text";
const char* ssid = "JioFiberhome3G";
const char* password = "Micro@123";
String st;
String content;
 
 
//Function Decalration
bool testWifi(void);
void launchWeb(void);
void setupAP(void);
 
//Establishing Local server at port 80 whenever required
ESP8266WebServer server(80);
 
void setup()
{
 
  Serial.begin(115200); //Initialising if(DEBUG)Serial Monitor
  Serial.println();
  Serial.println("Disconnecting previously connected WiFi");
  WiFi.disconnect();
  EEPROM.begin(512); //Initialasing EEPROM
  delay(10);
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.println();
  Serial.println();
  Serial.println("Startup");
 
  //---------------------------------------- Read EEPROM for SSID and pass
  Serial.println("Reading EEPROM ssid");
 
  String esid;
  for (int i = 0; i < 32; ++i)
  {
    esid += char(EEPROM.read(i));
  }
  Serial.println();
  Serial.print("SSID: ");
  Serial.println(esid);
  Serial.println("Reading EEPROM pass");
 
  String epass = "";
  for (int i = 32; i < 96; ++i)
  {
    epass += char(EEPROM.read(i));
  }
  Serial.print("PASS: ");
  Serial.println(epass);
 
 
  WiFi.begin(esid.c_str(), epass.c_str());
  if (testWifi())
  {
    Serial.println("Succesfully Connected!!!");
    return;
  }
  else
  {
    Serial.println("Turning the HotSpot On");
    launchWeb();
    setupAP();// Setup HotSpot
  }
 
  Serial.println();
  Serial.println("Waiting.");
  
  while ((WiFi.status() != WL_CONNECTED))
  {
    Serial.print(".");
    delay(100);
    server.handleClient();
  }
 
}
void loop() {
  if ((WiFi.status() == WL_CONNECTED))
  {
 
    for (int i = 0; i < 10; i++)
    {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(1000);
      digitalWrite(LED_BUILTIN, LOW);
      delay(1000);
    }
 
  }
  else
  {
  }
 
}
 
 
//-------- Fuctions used for WiFi credentials saving and connecting to it which you do not need to change 
bool testWifi(void)
{
  int c = 0;
  Serial.println("Waiting for Wifi to connect");
  while ( c < 20 ) {
    if (WiFi.status() == WL_CONNECTED)
    {
      return true;
    }
    delay(500);
    Serial.print("*");
    c++;
  }
  Serial.println("");
  Serial.println("Connect timed out, opening AP");
  return false;
}
 
void launchWeb()
{
  Serial.println("");
  if (WiFi.status() == WL_CONNECTED)
    Serial.println("WiFi connected");
  Serial.print("Local IP: ");
  Serial.println(WiFi.localIP());
  Serial.print("SoftAP IP: ");
  Serial.println(WiFi.softAPIP());
  createWebServer();
  // Start the server
  server.begin();
  Serial.println("Server started");
}
 
void setupAP(void)
{
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);
  int n = WiFi.scanNetworks();
  Serial.println("scan done");
  if (n == 0)
    Serial.println("no networks found");
  else
  {
    Serial.print(n);
    Serial.println(" networks found");
    for (int i = 0; i < n; ++i)
    {
      // Print SSID and RSSI for each network found
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));
      Serial.print(")");
      Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? " " : "*");
      delay(10);
    }
  }
  Serial.println("");
  st = "<ol>";
  for (int i = 0; i < n; ++i)
  {
    // Print SSID and RSSI for each network found
    st += "<li>";
    st += WiFi.SSID(i);
    st += " (";
    st += WiFi.RSSI(i);
 
    st += ")";
    st += (WiFi.encryptionType(i) == ENC_TYPE_NONE) ? " " : "*";
    st += "</li>";
  }
  st += "</ol>";
  delay(100);
  WiFi.softAP("JioFiberhome5G", "Micro@123");
  Serial.println("softap");
  launchWeb();
  Serial.println("over");
}
 
void createWebServer()
{
 {
    server.on("/", []() {
 
      IPAddress ip = WiFi.softAPIP();
      String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
      content = "<!DOCTYPE HTML>\r\n<html>Hello from ESP8266 at ";
      content += "<form action=\"/scan\" method=\"POST\"><input type=\"submit\" value=\"scan\"></form>";
      content += ipStr;
      content += "<p>";
      content += st;
      content += "</p><form method='get' action='setting'><label>SSID: </label><input name='ssid' length=32><input name='pass' length=64><input type='submit'></form>";
      content += "</html>";
      server.send(200, "text/html", content);
    });
    server.on("/scan", []() {
      //setupAP();
      IPAddress ip = WiFi.softAPIP();
      String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
 
      content = "<!DOCTYPE HTML>\r\n<html>go back";
      server.send(200, "text/html", content);
    });
 
    server.on("/setting", []() {
      String qsid = server.arg("ssid");
      String qpass = server.arg("pass");
      if (qsid.length() > 0 && qpass.length() > 0) {
        Serial.println("clearing eeprom");
        for (int i = 0; i < 96; ++i) {
          EEPROM.write(i, 0);
        }
        Serial.println(qsid);
        Serial.println("");
        Serial.println(qpass);
        Serial.println("");
 
        Serial.println("writing eeprom ssid:");
        for (int i = 0; i < qsid.length(); ++i)
        {
          EEPROM.write(i, qsid[i]);
          Serial.print("Wrote: ");
          Serial.println(qsid[i]);
        }
        Serial.println("writing eeprom pass:");
        for (int i = 0; i < qpass.length(); ++i)
        {
          EEPROM.write(32 + i, qpass[i]);
          Serial.print("Wrote: ");
          Serial.println(qpass[i]);
        }
        EEPROM.commit();
 
        content = "{\"Success\":\"saved to eeprom... reset to boot into new wifi\"}";
        statusCode = 200;
        ESP.reset();
      } else {
        content = "{\"Error\":\"404 not found\"}";
        statusCode = 404;
        Serial.println("Sending 404");
      }
      server.sendHeader("Access-Control-Allow-Origin", "*");
      server.send(statusCode, "application/json", content);
 
    });
  } 
}

Program Code Explanation

In this ESP8266 WiFi configuration project, ESP8266 with EEPROM has 3 sections for WiFi configuration work. The first one (EEPROM Conundrum), where we will see how to write data in ESP8266 memory.

ESP8266 can be problematic to write values ​​other than integers to memory. The main reason is that the standard EEPROM library does not work because ESP8266 does not have EEPROM. The library we use only emulates EEPROM but in real life, we ​​are writing in flash memory.

#ifdef ESP8266
#include <ESP8266WiFi.h> // Pins for board ESP8266 Wemos-NodeMCU
#include <ESP8266WebServer.h>
ESP8266WebServer serverAP(80);
#define accessPointButtonPin D6 // Connect a button to this pin
#else
#include <WiFi.h>
#include <WebServer.h>
#define accessPointButtonPin 4 // Connect a button to this pin
WebServer serverAP(80); // the Access Point Server
#endif

#include <EEPROM.h> // include EEPROM lib to handle ESP32 flash memory

Everything we write in ESP8266 memory must be striped, and we can write them one letter at a time. First, we declare the string variable st and content and some complete geek and const four *.

We will then announce the following three different actions:

 
//Function Decalration
bool testWifi(void);
void launchWeb(void);
void setupAP(void);
 
//Establishing Local server at port 80 whenever required
ESP8266WebServer server(80);
 
void setup()
{

In ESP8266 Wifi Configuration with EEPROM without Hard-Code. First, start the EEPROM function with EEPROM.begin. And then also set the storage size. Next cycle through string value.

void setupAP(void)
{
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);
  int n = WiFi.scanNetworks();
  Serial.println("scan done");
  if (n == 0)
    Serial.println("no networks found");
  else
  {
    Serial.print(n);
    Serial.println(" networks found");
    for (int i = 0; i < n; ++i)
    {
      // Print SSID and RSSI for each network found
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));
      Serial.print(")");
      Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? " " : "*");
      delay(10);
    }
  }
  Serial.println("");
  st = "<ol>";
  for (int i = 0; i < n; ++i)
  {
    // Print SSID and RSSI for each network found
    st += "<li>";
    st += WiFi.SSID(i);
    st += " (";
    st += WiFi.RSSI(i);
 
    st += ")";
    st += (WiFi.encryptionType(i) == ENC_TYPE_NONE) ? " " : "*";
    st += "</li>";
  }
  st += "</ol>";
  delay(100);
  WiFi.softAP("JioFiberhome5G", "Micro@123");
  Serial.println("softap");
  launchWeb();
  Serial.println("over");

Initially, ESP8266 is set up in station mode and tries to connect to previously saved access points from EEPROM memory. If a correct access point network is found, it will be connected to it.

ESP8266 Manual Wifi Configuration with EEPROM without Hard-Code

In this section, you can enter your code as you wish. in HTML form

String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
      content = "<!DOCTYPE HTML>\r\n<html>Hello from ESP8266 at ";
      content += "<form action=\"/scan\" method=\"POST\"><input type=\"submit\" value=\"scan\"></form>";
      content += ipStr;
      content += "<p>";
      content += st;
      content += "</p><form method='get' action='setting'><label>SSID: </label><input name='ssid' length=32><input name='pass' length=64><input type='submit'></form>";
      content += "</html>";
      server.send(200, "text/html", content);
    });
    server.on("/scan", []() {
      //setupAP();
      IPAddress ip = WiFi.softAPIP();
      String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
 
      content = "<!DOCTYPE HTML>\r\n<html>go back";
      server.send(200, "text/html", content);
    });
 
    server.on("/setting", []() {
      String qsid = server.arg("ssid");
      String qpass = server.arg("pass");
      if (qsid.length() > 0 && qpass.length() > 0) {
        Serial.println("clearing eeprom");
        for (int i = 0; i < 96; ++i) {
          EEPROM.write(i, 0);
        }
        Serial.println(qsid);
        Serial.println("");
        Serial.println(qpass);
        Serial.println("");
 
        Serial.println("writing eeprom ssid:");
        for (int i = 0; i < qsid.length(); ++i)
        {
          EEPROM.write(i, qsid[i]);
          Serial.print("Wrote: ");
          Serial.println(qsid[i]);
        }
        Serial.println("writing eeprom pass:");
        for (int i = 0; i < qpass.length(); ++i)
        {
          EEPROM.write(32 + i, qpass[i]);
          Serial.print("Wrote: ");
          Serial.println(qpass[i]);
        }
        EEPROM.commit();
 
        content = "{\"Success\":\"saved to eeprom... reset to boot into new wifi\"}";
        statusCode = 200;
        ESP.reset();
      } else {
        content = "{\"Error\":\"404 not found\"}";
        statusCode = 404;
        Serial.println("Sending 404");
      }
      server.sendHeader("Access-Control-Allow-Origin", "*");

Result:

On successful upload you will get below response from serial monitor that our ESP board sets to Access point with IP address 192.168.4.1

ESP8266 Manual Wifi Configuration with EEPROM without Hard-Code

Check the WIFI access point with network ID “xxxxxxxxx” and connect with network . As we have not assigned the password to AP so we can connect with network without password.

Once the new SSID and password are set, ESP reboots and tries to connect. If it establishes a connection, the process ends successfully. Otherwise, it will again be established as an access point.

ESP8266 Manual Wifi Configuration with EEPROM without Hard-Code

On submitting you can verify that what SSID and password  saved in EEPROM using serial monitor. 

ESP8266 Manual Wifi Configuration with EEPROM without Hard-Code

Шить будем на прошивку Padavan, так как официальная стоковая прошивка ограничена в возможностях настройки и вообще не тру.

Ссылки

Тема прошивки роутера на 4PDA
Обзор на этот роутер на сайте Mysku
Купить такой роутер на Aliexpress за 1500 рублей
Официальный сайт скрипта PROMETHEUS

Первый запуск

Перед прошивкой нужно пройти первоначальную настройку роутера, которая автоматически запускается при первом подключении и переходу по адресу 192.168.31.1.

Первоначальная настройка Xiaomi Mi WiFi mini

Ставим английскую стоковую прошивку

Для того,чтобы поставить кастомную прошивку нам понадобится любая стоковая версии 2.x.x, но не старше 2.9.х.

Качаем стоковую английскую прошивку 2.8.91

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

Самый первый раздел сверху, выбираем файл прошивки и жмем большую синюю кнопку.

Последнее китайское предупреждение, если поставить галочку, то перед прошивкой очистятся все настройки роутера.

Дожидаемся окончания процесса прошивки.

Все благополучно закончилось, жмём большую синюю кнопку.

Установка PROMETHEUS

PROMETHEUS — это скрипт для обслуживания и перепрошивки роутеров

Если у вас 64-битная операционная система, качаем и устанавливаем VMware Workstation Player. Если 32-битная, то Oracle VM VirtualBox

Скачиваем виртуальный образ с Яндекс.Диска, советую сразу качать сборку FULL, так как в мини-образе при запуске все равно будет докачиваться всякая фигня.

Открываем VMware Workstation Player или Oracle VM VirtualBox, выбираем скачанный и распакованный образ и запускаем его.

Кастомная прошивка

Вибираем в запущенном прометее модель роутера, у нас это mi-mini (1)

Появится главное окно скрипта

Для начала выбираем хак SSH (0), введите IP-адрес роутера (192.168.31.1) и пароль от админки. Запустится процесс патчинга. После окончания процесса, сохраните пароль доступа к SSH на всякий случай.

Далее собираем Toolchain (3) из главного меню скрипта. После сборки нажимаем любую кнопку и слева вверху видим статус Toolchain: OK.

Выбираем пункт 4 для того чтобы приступить к сборке прошивки.

Заходим в пункт два и выбираем все скины.

Возвращаемся по кнопке Q обратно в меню прошивки и выбираем пункт сборки прошивки (3), запустится процесс сборки.

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

Прошивка роутера

Подключаем наш роутер по шнурку (LAN).

В меню прошивки нажимаем выбираем пункт «Прошить Firmware» (4)

Отвечаем утвердительно на вопрос о бэкапе

Ждем окончания процесса прошивки и отвечаем утвердительно на вопрос о перезагрузке.

Заходим на прошитый роутер по адресу 192.168.1.1, логин и пароль admin/admin. Переходим в «Дополнительно -> Администрирование -> Сервисы» и включаем доступ по SSH.

Теперь прошивку нужно пропатчить, коротко о том, зачем это нужно:

Китайцы накосячили в EEPROM, из-за чего на прошивке XRMWRT было неправильное отображение RSSI (уровень мощности принимаемого сигнала). В версии начиная с билд 096 это исправлено, но осталась проблема с уровнем приёма сигнала с периферийных устройств из-за неправильной настройки коэффициента усиления

Открываем главное меню скрипта и запускаем патч EEPROM (6). Появляется ошибка подключения, нужно изменить параметры подключения к роутеру. Выбираем обновить данные (2) и вводим IP-адрес 192.168.1.1, логин admin и пароль admin.

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

Всё! Роутер успешно прошит и пропатчен.

По-умолчанию, адрес роутера будет 192.168.1.1, пароль и логин admin. Сеть Wi-Fi — asus, пароль от неё 1234567890.

Время на прочтение
13 мин

Количество просмотров 10K

Введение

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

А именно поговорим о том, как хранить параметры, которые необходимо писать в EEPROM постоянно.

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

Особенность таких параметров заключается в том, что их нельзя писать просто так в одно и то же место EEPROM, вы просто израсходуете все циклы записи EEPROM. Например, если, необходимо писать время работы один раз в 1 минуту, то нетрудно посчитать, что с EEPROM в 1 000 000 циклов записей, вы загубите его меньше чем за 2 года. А что такое 2 года, если обычное измерительное устройство имеет время поверки 3 и даже 5 лет.

Кроме того, не все EEPROM имеют 1 000 000 циклов записей, многие дешевые EEPROM все еще производятся по старым технологиям с количеством записей 100 000. А если учесть, что 1 000 000 циклов указывается только при идеальных условиях, а скажем при высоких температурах это число может снизиться вдвое, то ваша EEPROM способно оказаться самым ненадежным элементом уже в первый год работы устройства.

Поэтому давайте попробуем решить эту проблему, и сделать так, чтобы обращение к параметрам было столь же простым как в прошлой статье, но при этом EEPROM хватало бы на 30 лет, ну или на 100 (чисто теоретически).

Итак, в прошлой статье, я с трудом показал, как сделать, так, чтобы с параметрами в EEPROM можно было работать интуитивно понятно, не задумываясь, где они лежат и как осуществляется доступ к ним

Напомню:

ReturnCode returnCode = NvVarList::Init();  //инициализируем все наши параметры из EEPROM
returnCode = myStrData.Set(tString6{ "Hello" }); //Записываем Hello в EEPOM myStrData.
auto test = myStrData.Get();                //Считываем текущее значение параметра 

myFloatData.Set(37.2F);    //Записываем 37.2 в EEPROM.
myUint32Data.Set(0x30313233);

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

Все очень просто, существует огромный пласт измерительных устройств, которые используют полевые протоколы такие как HART, FF или PF, где пользовательские команды очень атомарные. Например, в HART протоколе есть отдельные команды — запись единиц изменения, запись верхнего диапазона, запись времени демпфирования, калибровка нуля, запись адрес опроса и т.д. Каждая такая команда должна записать один параметр, при этом успеть подготовить ответ и ответить. Таких параметров может быть до 500 — 600, а в небольших устройствах их около 200.

Если использовать способ, который предложил пользователь @HiSER- это будет означать, что для перезаписи одного параметра размером в 1 byte, я должен буду переписать всю EEPROM. А если алгоритм контроля целостности подразумевает хранение копии параметров, то для 200 параметров со средней длиной в 4 байта, мне нужно будет переписать 1600 байт EEPROM, а если параметров 500, то и все 4000.

Малопотребляющие устройства или устройства, питающиеся от от токовой петли 4-20мА должны потреблять, ну скажем 3 мА, и при этом они должны иметь еще достаточно энергии для питания модема полевого интерфейса, графического индикатора, да еще и BLE в придачу. Запись в EEPROM очень энергозатратная операция. В таких устройствах писать нужно мало и быстро, чтобы средний ток потребления был не высоким.

Очевидно, что необходимо, сделать так, чтобы микроконтроллер ел как можно меньше. Самый простой способ, это уменьшить частоту тактирования, скажем до 500 КГц, или 1 Мгц (Сразу оговорюсь, в надежных применениях использование режима низкого потребления запрещено, поэтому микроконтроллер все время должен работать на одной частоте). На такой частоте, простая передача 4000 байт по SPI займет около 70 мс, прибавим к этому задержку на сохранение данных в страницу (в среднем 7мс на страницу), обратное вычитывание, и вообще обработку запроса микроконтроллером и получим около 3 секунд, на то, чтобы записать один параметр.

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

Но вернемся к нашей основной проблеме — мы хотим постоянно писать параметры.

Как работать с EEPROM, чтобы не износить её

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

Как я уже сказал, число записей в EEPROM ограничено. Это число варьируется, и может быть 100 000, а может и 1 000 000. Так как же быть, если я хочу записать параметр 10 000 000 раз? И здесь мы должны понять, как внутри EEPROM устроен доступ к ячейкам памяти.

Итак, в общем случае вся EEPROM разделена на страницы. Страницы изолированы друг от друга. Страницы могут быть разного размера, для небольших EEPROM это, скажем, 16, 32 или 64 байта. Каждый раз когда вы записываете данные по какому-то адресу, EEPROM копирует все содержимое страницы, в которой находятся эти данные, во внутренний буфер. Затем меняет данные, которые вы передали в этом буфере и записывает весь буфер обратно. Т.е. по факту, если вы поменяли 1 байт в странице, вы переписываете всю страницу. Но из-за того, что страницы изолированы друг от друга остальные страницы не трогаются.

Таким образом, если вы записали 1 000 000 раз в одну страницу, вы можете перейти на другую страницу и записать туда еще 1 000 000 раз, потом в другую и так далее. Т.е. весь алгоритм сводится к тому, чтобы писать параметр не в одну страницу, а каждый раз сдвигаться в следующую страницу. Можно закольцевать эти действия и после 10 раз, снова писать в исходную страницу. Таким образом, вы просто отводите под параметр 10 страниц, вместо 1.

Да придется пожертвовать память, но как сделать по другому, я пока не знаю. Если есть какие мысли — пишите в комментариях.

Анализ требований и дизайн

Итак, мы почти поняли что хотим. Но давайте немного формализуем это. Для начала, назовем наши параметры, которые нужно писать постоянно — AntiWearNvData (антиизносные данные). Мы хотим, чтобы обращение к ним было такое же простое и юзер френдли, как и к кешируемым параметрам из предыдущей статьи.

// читываем из EEPROM все параметры и инициализируем их копии в ОЗУ
ReturnCode returnCode = NvVarList::Init();       
returnCode = myStrData.Set(tString6{ "Hello" }); //Записываем Hello в EEPROM myStrData.
auto test = myStrData.Get();                     //Считываем текущее значение параметра

myFloatData.Set(37.2F);                          //Записываем 37.2 в EEPROM.
myUint32Data.Set(0x30313233);

myFloatAntiWearData.Set(10.0F);          //Записали в параметр 10.0F в EEPROM первый раз
myFloatAntiWearData.Set(11.0F);
myFloatAntiWearData.Set(12.0F);
myFloatAntiWearData.Set(13.0F);
...
// Записываем этот же параметр в EEPROM 11 000 000 раз. 
myFloatAntiWearData.Set(11'000'000.0F);  
myUint32AntiWearData.Set(10U);              // Тоже самое с int
myStrAntiWearData.Set(tString6{ "Hello" }); // со строкой и так далее

Все требования можно сформулировать следующим образом:

  • Пользователь должен задать параметры EEPROM и время обновления параметра

    • На этапе компиляции нужно посчитать количество необходимых страниц (записей), чтобы уложиться в необходимое время работы EEPROM. Для этого нужно знать:

      • Количество циклов перезаписи

      • Размер страницы

      • Время обновления параметра

      • Время жизни устройства

Хотя конечно, можно было дать возможность пользователю самому задавать количество записей, но что-то я хочу, чтобы все считалось само на этапе компиляции.

  • Каждая наша переменная(параметр) должна иметь уникальный начальный адрес в EEPROM

    • Мы не хотим сами руками задавать адрес, он должен высчитываться на этапе компиляции

  • При каждой следующей записи, адрес параметра должен изменяться, так, чтобы данные не писались по одному и тому же адресу

    • Это также должно делаться автоматически, но уже в runtime, никаких дополнительных действий в пользовательском коде мы делать не хотим.

  • Мы не хотим постоянно лазить в EEPROM, когда пользователь хочет прочитать параметр

    • Обычно EEPROM подключается через I2C и SPI, передача данных по этим интерфейсам тоже отнимает время, поэтому лучше кэшировать параметры в ОЗУ, и возвращать сразу копию из кеша.

    • При инициализации мы должны найти самую последнюю запись, её считать и закешировать.

  • За целостность должен отвечать драйвер.

    • За алгоритм проверки целостности отвечает драйвер, если при чтении он обнаружил несоответствие он должен вернуть ошибку. В нашем случае, пусть в качестве алгоритма целостности будет простое хранение копии параметра. Сам драйвер описывать не буду, но приведу пример кода.

Ну кажется это все наши хотелки. Как и в прошлой статье давайте прикинем дизайн класса, который будет описывать такой параметр и удовлетворять нашим требованиям:

Класс AntiWearNvData будет похож на, CachedNvData из прошлой статьи, но с небольшими изменениям. При каждой записи в EEPROM, нам нужно постоянно сдвигать адрес записи, поэтому необходимо хранить индекс, который будет указывать на номер текущей записи. Этот индекс должен записываться в EEPROM вместе с параметром, чтобы после инициализации можно было найти запись с самым большим индексом — эта запись и будет самой актуальной. Индекс можно сделать uint32_t точно хватит на 30 лет — даже при 100 000 циклах записи.

И вот наш класс:

Посмотрим на то, как реализуются наши требования таким дизайном.

Пользователь должен задать параметры EEPROM и время обновления параметр

В отличии от CachedNvData Из предыдущей статьи здесь появился параметр updateTime. На основе этого параметра можно посчитать сколько записей необходимо для того, чтобы уложиться в ожидаемое время жизни EEPROM. Сами параметры EEPROM можно задать в отдельном заголовочнике. Например, так:

using tSeconds = std::uint32_t;

constexpr std::uint32_t eepromWriteCycles = 1'000'000U;
constexpr std::uint32_t eepromPageSize = 32U;
// Хотим чтобы EEPROM жила 10 лет
constexpr tSeconds eepromLifeTime = 3600U * 24U * 365U * 10U;

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

template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
  private:
      struct tAntiWear
      {
         T data = defaultValue;
          std::uint32_t index = 0U;
      };
  
      inline static tAntiWear nvItem;
  public:
      // Умножил на 2 чтобы зарезервировать место под копию. 
      // Но по хорошему надо это убрать в список или драйвер
      static constexpr auto recordSize = sizeof(nvItem) * 2U;
      // предполагаем, что параметр не занимает больше страницы и
      // все они выравнены по границам страницы, но ничто не запрещает
      // сделать более сложный расчет необходимого количества записей,
      // для параметров, занимающих больше страницы. Такие ограничения для упрощения.
      static_assert(eepromPageSize/recordSize != 0, "Too big parameter");
      static constexpr size_t recordCounts =  (eepromPageSize/recordSize) * 
                                               eepromLifeTime / 
                                               (eepromWriteCycles * updateTime);

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

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

  • По нему мы будет рассчитывать следующий адрес записи

  • Для того, чтобы после выключения/включения датчика найти последнюю запись, считать её и проинициализировать значением по адресу этой записи кеширумое значение в ОЗУ.

Для этого заведена специальная структура tAntiWear. Её то мы и будем сохранять при вызове метода Set(...), который, кроме непосредственно записи, еще сдвигает индекс текущей записи на 1.

template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
 public:
  ReturnCode Set(const T& value) const
  {
    tAntiWear tempData = {.data = value, .index = nvItem.index};
    //На основе текущего индекса расчитывем текущий адрес записи в EEPROM
    const auto calculatedAddress = GetCalculatedAdress(nvItem.index);

    ReturnCode returnCode = nvDriver.Set(calculatedAddress, 
                                         reinterpret_cast<const tNvData*>(&tempData), 
                                         sizeof(tAntiWear));

    //Если запись прошла успешно, то обновляем кэшируемую копию параметра, 
    //а также смещаем индекс на 1, для следующей записи
    if (!returnCode)
    {
      nvItem.data = value;
      nvItem.index ++;
    }
      return returnCode;
  }
...
};

Давайте посмотрим как реализован метод расчета текущего адреса записи:

template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
...
private:
  static size_t GetCalculatedAdress(std::uint32_t ind)
  {
    constexpr auto startAddress = GetAddress();
    //собственно весь алгоритм расчета сводится к прибавленипю к стартовому адресу
    //смещения текущей записи, которое находится по текущему индексу
    //как только индекс будет кратен расчитанному количеству, необходимо начать писать
    //с начального адреса - такой кольцевой буфер в EEPROM.
    size_t result = startAddress + recordSize * ((ind % recordCounts));
    assert(result < std::size(EEPROM));
    return result;
  }

  constexpr static auto GetAddress()
  {
    return NvList::template GetAddress<const AntiWearNvData<NvList, T, defaultValue, updateTime, nvDriver>>();
  }
};

Мы не хотим постоянно лазить в EEPROM, когда пользователь хочет прочитать параметр

Метод Get() — крайне простой, он просто возвращает копию из ОЗУ

template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
public:
   T Get() const
    {
        return nvItem.data;
    }
};

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

template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
public:
  static ReturnCode Init()
  {
    const auto ind = FindLastRecordPosition();
    constexpr auto startAddress = GetAddress();
    const auto calculatedAddress =  startAddress + recordSize * ind;

    return nvDriver.Get(calculatedAddress, reinterpret_cast<tNvData*>(&nvItem), sizeof(tAntiWear));
  }
...
private:
  static std::uint32_t FindLastRecordPosition()
  {
    // Метод поиска индекса приводить не буду, оставлю на откуп читателям
    // Здесь нужно считать все записи парамтера и найти параметр с самым 
    // большим индексом, пока предположим, что запись с самым большим индексом 
    // находится на позиции 0.
    return  0U;
   }
};

В общем-то и все класс готов, полный код класса:

Полный код класса

template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
 public:
    ReturnCode Set(const T& value) const
    {
        tAntiWear tempData = {.data = value, .index = nvItem.index};
        // К размеру типа прибавляем 4 байта индекса и умножаем на номер индекса записи.
        // Умножаем на 2, чтобы драйвер мог хранить копиию записи для проверки целостности
        const auto calculatedAddress = GetCalculatedAdress(nvItem.index);

        ReturnCode returnCode = nvDriver.Set(calculatedAddress, reinterpret_cast<const tNvData*>(&tempData), sizeof(tAntiWear));
        //  std::cout << "Write at address: " << calculatedAddress << std::endl;
        //Если запись прошла успешно, то обновляем кэшируемую копию параметра, а также смещаем индекс на 1, для следующей записи
        if (!returnCode)
        {
          nvItem.data = value;
          //если колчиство записей превысило нужное количество, обнуляем индекс, начинаем писать с начального адреса
          nvItem.index ++;
        }

        return returnCode;
    }

    static ReturnCode Init()
    {
        const auto ind = FindLastRecordPosition();
        constexpr auto startAddress = GetAddress();
        const auto calculatedAddress =  startAddress + recordSize * ind;

        return nvDriver.Get(calculatedAddress, reinterpret_cast<tNvData*>(&nvItem), sizeof(tAntiWear));
    }

    T Get() const
    {
        return nvItem.data;
    }

    static ReturnCode SetToDefault()
    {
        ReturnCode returnCode = nvDriver.Set(GetCalculatedAdress(nvItem.index), reinterpret_cast<const tNvData*>(&defaultValue), sizeof(T));
        return returnCode;
    }
 private:

   static size_t GetCalculatedAdress(std::uint32_t ind)
   {
       constexpr auto startAddress = GetAddress();
       size_t result = startAddress + recordSize * ((ind % recordCounts));
       assert(result < std::size(EEPROM));
       return result;
   }
   static std::uint32_t FindLastRecordPosition()
   {
       // Здесь нужно считать все записи парамтера и найти параметр с самым большим индексом, пока предположим,
       // что запись с самым большим индексом находится на позиции 1 - Там записано число 15 с индексом 5.
       return  1U;
   }
   constexpr static auto GetAddress()
   {
     return NvList::template GetAddress<const AntiWearNvData<NvList, T, defaultValue, updateTime, nvDriver>>();
   }

   struct tAntiWear
   {
    T data = defaultValue;
    std::uint32_t index = 0U;
   };

   inline static tAntiWear nvItem;

  public:
      static constexpr auto recordSize = sizeof(nvItem) * 2U;
      static_assert(eepromPageSize/recordSize != 0, "Too big parameter");
      static constexpr size_t recordCounts =  (eepromPageSize/recordSize) * eepromLifeTime / (eepromWriteCycles * updateTime);

};

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

Я немного переделал список, так как IAR компилятор все еще не понимает много фишек из С++17, и собственно теперь список принимает только типы, а не ссылки на параметры. Кроме того, теперь у него появились методы SetToDefault и Init. Первый нужен, например, чтобы сбросить все параметры в их начальное значение. А второй, чтобы проинициализировать кешируемые в ОЗУ копии.

template<const tNvAddress startAddress, typename ...TNvVars>
struct NvVarListBase
{
static ReturnCode SetToDefault()
{
return ( ... || TNvVars::SetToDefault());
}

    static ReturnCode Init()
    {
        return ( ... || TNvVars::Init());
    }
    template<typename T>
    constexpr static size_t GetAddress()
    {
        return startAddress + GetAddressOffset<T, TNvVars...>();
    }

 private:

    template <typename QueriedType, typename T, typename ...Ts>
    constexpr static size_t GetAddressOffset()
    {
        auto result = 0;
        if constexpr (!std::is_same<T, QueriedType>::value)
        {
            //можно дописать алгоритм, чтобы все параметры были выравенны по странице.
            result = T::recordSize * T::recordCounts + GetAddressOffset<QueriedType, Ts...>();
        }
        return result;
    }
};

Также в CachedNvData я добавил параметр recordSize и recordCounts = 1. Чтобы расчет адреса параметра был унифицирован для разного типа параметров.

Результат

Собственно все, теперь мы можем регистрировать в списке любые параметры:

struct NvVarList;
constexpr NvDriver nvDriver;

using tString6 = std::array<char, 6U>;

inline constexpr float myFloatDataDefaultValue = 10.0f;
inline constexpr tString6 myStrDefaultValue = { "Popit" };
inline constexpr std::uint32_t myUint32DefaultValue = 0x30313233;
inline constexpr std::uint16_t myUin16DeafultValue = 0xDEAD;

constexpr CachedNvData<NvVarList, float, myFloatDataDefaultValue, nvDriver> myFloatData;
constexpr CachedNvData<NvVarList, tString6, myStrDefaultValue, nvDriver> myStrData;
constexpr CachedNvData<NvVarList, std::uint32_t, myUint32DefaultValue, nvDriver> myUint32Data;
constexpr AntiWearNvData<NvVarList, std::uint32_t, myUint32DefaultValue, 60U, nvDriver> myUint32AntiWearData;
constexpr AntiWearNvData<NvVarList, float, myFloatDataDefaultValue, 60U, nvDriver> myFloatAntiWearData;

struct SomeSubsystem
{
   static constexpr auto test = CachedNvData < NvVarList, std::uint16_t, myUin16DeafultValue,  nvDriver>();
};

//*** Register the Shadowed Nv param in the list *****************************
struct NvVarList : public NvVarListBase<0,
                                        decltype(myStrData),
                                        decltype(myFloatData),
                                        decltype(SomeSubsystem::test),
                                        decltype(myUint32Data),
                                        decltype(myFloatAntiWearData),
                                        decltype(myUint32AntiWearData)
                                       >
{
};

Замечу, что пользователю параметров нужно только объявить параметр и список, а вся портянка с кодом, до этого, пишется один раз. Используются параметры точно также как и CachedNvData.

int main()
{
   NvVarList::SetToDefault();
   ReturnCode returnCode = NvVarList::Init();

    myFloatData.Set(37.2F);
    myStrData.Set(tString6{"Hello"});

    myFloatAntiWearData.Set(10.0F);
    myFloatAntiWearData.Set(11.0F);
    myFloatAntiWearData.Set(12.0F);
    myFloatAntiWearData.Set(13.0F);
    myFloatAntiWearData.Set(14.0F);

    myUint32AntiWearData.Set(10U);
    myUint32AntiWearData.Set(11U);
    myUint32AntiWearData.Set(12U);
    myUint32AntiWearData.Set(13U);
    myUint32AntiWearData.Set(14U);
    myUint32AntiWearData.Set(15U);

    return 1;
}

Что произойдет в этом примере, когда мы будем писать 10,11,12…15 в наш параметр. Каждый раз при записи, адрес параметра будет смещаться на размер параметра + размер индекса + размер копии параметра и индекса. Как только количество записей превысит максимальное количество, параметр начнет писаться с начального адреса.

На картинке снизу как раз видно, что число 15 с индексом 5 записалось с начального адреса, а 10 теперь нет вообще.

В данном случае после сброса питания, при инициализации, будет найдена запись с индексом 5 и значением 15 и это значение и индекс будут записаны в кэшируемую копию нашего параметра.

Вот и все, надеюсь в этой статье цель получилось пояснить более детально, спасибо за то, что прочитали до конца.

Как обычно код с примером.

#include <ESP8266WiFi.h>

#include <ESP8266HTTPClient.h>

#include <ESP8266WebServer.h>

#include <EEPROM.h>

//Variables

int i = 0;

int statusCode;

const char* ssid = «text»;

const char* passphrase = «text»;

String st;

String content;

//Function Decalration

bool testWifi(void);

void launchWeb(void);

void setupAP(void);

//Establishing Local server at port 80 whenever required

ESP8266WebServer server(80);

void setup()

{

  Serial.begin(115200); //Initialising if(DEBUG)Serial Monitor

  Serial.println();

  Serial.println(«Disconnecting previously connected WiFi»);

  WiFi.disconnect();

  EEPROM.begin(512); //Initialasing EEPROM

  delay(10);

  pinMode(LED_BUILTIN, OUTPUT);

  Serial.println();

  Serial.println();

  Serial.println(«Startup»);

  //—————————————- Read EEPROM for SSID and pass

  Serial.println(«Reading EEPROM ssid»);

  String esid;

  for (int i = 0; i < 32; ++i)

  {

    esid += char(EEPROM.read(i));

  }

  Serial.println();

  Serial.print(«SSID: «);

  Serial.println(esid);

  Serial.println(«Reading EEPROM pass»);

  String epass = «»;

  for (int i = 32; i < 96; ++i)

  {

    epass += char(EEPROM.read(i));

  }

  Serial.print(«PASS: «);

  Serial.println(epass);

  WiFi.begin(esid.c_str(), epass.c_str());

  if (testWifi())

  {

    Serial.println(«Succesfully Connected!!!»);

    return;

  }

  else

  {

    Serial.println(«Turning the HotSpot On»);

    launchWeb();

    setupAP();// Setup HotSpot

  }

  Serial.println();

  Serial.println(«Waiting.»);

  while ((WiFi.status() != WL_CONNECTED))

  {

    Serial.print(«.»);

    delay(100);

    server.handleClient();

  }

}

void loop() {

  if ((WiFi.status() == WL_CONNECTED))

  {

    for (int i = 0; i < 10; i++)

    {

      digitalWrite(LED_BUILTIN, HIGH);

      delay(1000);

      digitalWrite(LED_BUILTIN, LOW);

      delay(1000);

    }

  }

  else

  {

  }

}

//——— Fuctions used for WiFi credentials saving and connecting to it which you do not need to change

bool testWifi(void)

{

  int c = 0;

  Serial.println(«Waiting for Wifi to connect»);

  while ( c < 20 ) {

    if (WiFi.status() == WL_CONNECTED)

    {

      return true;

    }

    delay(500);

    Serial.print(«*»);

    c++;

  }

  Serial.println(«»);

  Serial.println(«Connect timed out, opening AP»);

  return false;

}

void launchWeb()

{

  Serial.println(«»);

  if (WiFi.status() == WL_CONNECTED)

    Serial.println(«WiFi connected»);

  Serial.print(«Local IP: «);

  Serial.println(WiFi.localIP());

  Serial.print(«SoftAP IP: «);

  Serial.println(WiFi.softAPIP());

  createWebServer();

  // Start the server

  server.begin();

  Serial.println(«Server started»);

}

void setupAP(void)

{

  WiFi.mode(WIFI_STA);

  WiFi.disconnect();

  delay(100);

  int n = WiFi.scanNetworks();

  Serial.println(«scan done»);

  if (n == 0)

    Serial.println(«no networks found»);

  else

  {

    Serial.print(n);

    Serial.println(» networks found»);

    for (int i = 0; i < n; ++i)

    {

      // Print SSID and RSSI for each network found

      Serial.print(i + 1);

      Serial.print(«: «);

      Serial.print(WiFi.SSID(i));

      Serial.print(» («);

      Serial.print(WiFi.RSSI(i));

      Serial.print(«)»);

      Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? » « : «*»);

      delay(10);

    }

  }

  Serial.println(«»);

  st = «<ol>»;

  for (int i = 0; i < n; ++i)

  {

    // Print SSID and RSSI for each network found

    st += «<li>»;

    st += WiFi.SSID(i);

    st += » («;

    st += WiFi.RSSI(i);

    st += «)»;

    st += (WiFi.encryptionType(i) == ENC_TYPE_NONE) ? » « : «*»;

    st += «</li>»;

  }

  st += «</ol>»;

  delay(100);

  WiFi.softAP(«how2electronics», «»);

  Serial.println(«softap»);

  launchWeb();

  Serial.println(«over»);

}

void createWebServer()

{

{

    server.on(«/», []() {

      IPAddress ip = WiFi.softAPIP();

      String ipStr = String(ip[0]) + ‘.’ + String(ip[1]) + ‘.’ + String(ip[2]) + ‘.’ + String(ip[3]);

      content = «<!DOCTYPE HTML>\r\n<html>Hello from ESP8266 at «;

      content += «<form action=\»/scan\» method=\»POST\»><input type=\»submit\» value=\»scan\»></form>»;

      content += ipStr;

      content += «<p>»;

      content += st;

      content += «</p><form method=’get’ action=’setting’><label>SSID: </label><input name=’ssid’ length=32><input name=’pass’ length=64><input type=’submit’></form>»;

      content += «</html>»;

      server.send(200, «text/html», content);

    });

    server.on(«/scan», []() {

      //setupAP();

      IPAddress ip = WiFi.softAPIP();

      String ipStr = String(ip[0]) + ‘.’ + String(ip[1]) + ‘.’ + String(ip[2]) + ‘.’ + String(ip[3]);

      content = «<!DOCTYPE HTML>\r\n<html>go back»;

      server.send(200, «text/html», content);

    });

    server.on(«/setting», []() {

      String qsid = server.arg(«ssid»);

      String qpass = server.arg(«pass»);

      if (qsid.length() > 0 && qpass.length() > 0) {

        Serial.println(«clearing eeprom»);

        for (int i = 0; i < 96; ++i) {

          EEPROM.write(i, 0);

        }

        Serial.println(qsid);

        Serial.println(«»);

        Serial.println(qpass);

        Serial.println(«»);

        Serial.println(«writing eeprom ssid:»);

        for (int i = 0; i < qsid.length(); ++i)

        {

          EEPROM.write(i, qsid[i]);

          Serial.print(«Wrote: «);

          Serial.println(qsid[i]);

        }

        Serial.println(«writing eeprom pass:»);

        for (int i = 0; i < qpass.length(); ++i)

        {

          EEPROM.write(32 + i, qpass[i]);

          Serial.print(«Wrote: «);

          Serial.println(qpass[i]);

        }

        EEPROM.commit();

        content = «{\»Success\»:\»saved to eeprom… reset to boot into new wifi\»}»;

        statusCode = 200;

        ESP.reset();

      } else {

        content = «{\»Error\»:\»404 not found\»}»;

        statusCode = 404;

        Serial.println(«Sending 404»);

      }

      server.sendHeader(«Access-Control-Allow-Origin», «*»);

      server.send(statusCode, «application/json», content);

    });

  }

}

  • Dwnet technologies suzhou corporation роутер
  • Fibertool беспроводной роутер ft air duo g
  • Eci b focus ethernet роутер
  • Dwl 2100ap прошивка в роутере
  • Echolife hg8245h цена роутер huawei