ESP8266 alternatív OTA

Fórumok

A partíció 1MB FW 3MB SPIFFS. A Firmware ~900kB
Ehhez szeretnék valami WiFi-s frissítést.
A normál OTA esélytelen. Arra gondoltam ha a rendes FW által AsyncWebServer segítségével fogadott fájl pl "update.bin" akkor lezárja a fájlrendszert és kiírja 0x100000-tól a bin-t a flesbe. Fájlokat buktam, leszarom, az új FW formatálja. Ez egy AsyncElegantOTA firmware lenne. Az eredeti FW setup első sora megnézi az eepromot amiből kiderül hogy frissíteni kell-e, ha igen akkor 0x100000 címen levő kódot futtatja. Lehet egy .ino-t fordítani úgy hogy a 0x100000 címen kezdődjön és setupból indítható legyen?

Hozzászólások

van amikor nem forraszthato ki az esp chip a cuccbol.

Nem az ESP chip-ben van az EEPROM, az egy másik chip.

persze en is tudok olyan esp-t keresni mivel fel sem merul a problema.

Három út van: vagy nagyobb EEPROM vagy kisebb igények vagy másik architektúra. Az ESP8266 esetén a chip működése adott, az OTA működést a chip firmware adja, az kezeli az EEPROM újraírását, ezt nem tudod cserélni.

Esküszöm nem értelek. Ezt én írtam, törli a flasht, az eeprom első hat bájtját megőrzi.

@echo off
COLOR A
echo Full Format ^!^!^!^!^!^!^!^!^!^!^!
setlocal EnableDelayedExpansion
SET /P M=COM port: 
echo -------------------------
echo BACKUP REGISTRATION CODE
esptool.exe --port COM%M% read_flash 0x3FB000 6 code.txt
echo SUCCESS!
echo -------------------------
echo ERASE FLASH
esptool.exe --port COM%M% erase_flash
echo SUCCESS!
echo -------------------------

set "INPUT=code.txt"
set "OUTPUT=eeprom_full.bin"
set "PADBIN=pad_ff.bin"
set "HEX=pad_ff.hex"
set /a TOTAL=4096

for %%I in (%INPUT%) do set /a SIZE=%%~zI

if !SIZE! GEQ !TOTAL! (
    echo %INPUT% is already >= 4096 bytes
    copy /Y %INPUT% %OUTPUT% >nul
    goto :done
)

set /a NEED=!TOTAL! - !SIZE!

set /a LINES=!NEED! / 16
set /a REM=!NEED! %% 16

> %HEX% (
    for /L %%i in (1,1,!LINES!) do echo FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
    if !REM! NEQ 0 (
        set "LINE="
        for /L %%j in (1,1,!REM!) do set "LINE=!LINE!FF "
        echo !LINE!
    )
)

certutil -f -decodehex %HEX% %PADBIN% >nul 2>&1
if errorlevel 1 (
    echo HIBA: certutil nem tudta létrehozni a bináris fájlt.
    goto :error
)

copy /b %INPUT% + %PADBIN% %OUTPUT% >nul

del %PADBIN%
del %HEX%

echo WRITE REGISTRATION CODE
esptool.exe --port COM%M% write_flash 0x3FB000 eeprom_full.bin
del %OUTPUT%
echo SUCCESS!
goto :done

:error
echo ERROR.
exit /b 1

:done
endlocal
echo -------------------------

Na hol az eeprom?

Esküszöm nem értelek.

A fő probléma az, hogy az ESP8266 működését, felépítését és architektúráját se érted.

Na hol az eeprom?

Mindegy, hogy minek hívod a fájlt, a neve nem kerül kiírásra, az csak a gépeden jelent bármit is, viszont a Flash-ban van ugyanúgy és ahogy írtam, a Flash az egy EEPROM altípus... amit te `eeprom_full.bin` néven nevezel és kiírsz a 0x3FB000 területre - az a memory map specifikáció szerint "Unused" régió, oda azt írsz, amit akarsz, és bárhogy nevezheted a fájlt a gépeden, semmit nem jelent.

Szóval újra: a Flash egy egy EEPROM altípus. Az ESP8266 külső EEPROM nélkül nem működik. Az OTA boot folyamatot az ESP8266 belső ROM firmware kezeli, az meg csak úgy tud működni, hogy van egy firmware partíció és ott van két slot. Ha nincs két slot, akkor nincs OTA. Fájlrendszerből nem fog működni a boot, fájlrendszerből úgy tud működni, hogy ugyanúgy van két slot a firmware partícióban és a fájlrendszerről átmásolja valamelyikbe a firmware-t. Olyan nem lesz, amit szeretnél, de ez amúgy le van írva a specifikációkban is.

Nekem az az ic FLASH. EEPROM az a 24cXX

Neked lehet, a 24Cxx az egy konkrét gyártó konkrét egy EEPROM IC típuscsaládja. Ezen kívül van millió fajta EEPROM. Minden olyan IC EEPROM, ami elektromosan törölhető és programozható, ez egy naaaaagy gyűjtőnév. Olvasnivaló a témában: https://en.wikipedia.org/wiki/EEPROM

Az a kérdés tudok-e egy ElegantOTA fw-t így fordítani.

Az ElegantOTA egy pár soros wrapper az ESP8266 saját OTA firmware kódja felett, azzal pedig nem tudsz olyan OTA-t csinálni, hogy "1MB FW 3MB SPIFFS" használatával ~900kB firmware OTA frissíthető legyen. Ez csak úgy megy, ha a firmware partíció 2MB méretű. Maximum olyat tudsz, hogy az firmware partíció aszimmetrikus, és van egy kisebb firmware, ami letölti a nagyobbat, de a legminimálisabb Arduino firmware kb. 300 kB lesz, ESP-IDF esetén ~130 kB, ezzel se fog 900 kB OTA felférni.

Futás közben nem lehet átkonfigolni 2MB/1MB beállításra?

Nem.

Ez így szopás. Ötlet?

https://hup.hu/comment/3225522#comment-3225522

Amúgy, figyelj, szerintem ülj le egy nyugodt sarokba és olvasd el az ESP8266 specifikációkat, meglepően jól leírnak mindent.

Ez a feltöltött cuccot 0x000000 címtól beírja. Ha ez lenne 0x100000 címtől, működhetne?

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP.h>

// WiFi adatok — állítsd be a saját hálózatodra
const char* ssid = "asajah";
const char* pass = "xxxxxxxx";

ESP8266WebServer server(80);

// Cél flash kezdőcím (byte-ban)
const uint32_t FLASH_WRITE_ADDR = 0x200000;
// Flash szektor méret (ESP8266: 4 KB)
const uint32_t SECTOR_SIZE = 4096;

uint32_t currentWriteAddr = FLASH_WRITE_ADDR;
bool *erasedSectors = nullptr;
uint32_t firstSector = 0;
uint32_t erasedSectorCount = 0;

// Ideiglenes puffer a 4-bájtos igazításhoz
#define TMPBUF_SIZE 4096
uint8_t tmpBuf[TMPBUF_SIZE];
uint32_t tmpBufLen = 0;

void ensureSectorErased(uint32_t addr) {
  uint32_t sector = addr / SECTOR_SIZE;
  if (sector < firstSector || sector >= firstSector + erasedSectorCount) return;
  uint32_t idx = sector - firstSector;
  if (!erasedSectors[idx]) {
    Serial.printf("Erase sector %u\n", sector);
    ESP.flashEraseSector((uint16_t)sector);
    erasedSectors[idx] = true;
  }
}

void writeFlashAligned(const uint8_t* data, size_t len) {
  size_t offset = 0;

  // Ha van a tmpBufban adat, előbb azt töltjük fel a teljes 4-bájtos blokkhoz
  if (tmpBufLen > 0) {
    size_t need = 4 - (tmpBufLen % 4);
    if (need > len) need = len;
    memcpy(tmpBuf + tmpBufLen, data, need);
    tmpBufLen += need;
    offset += need;

    if ((tmpBufLen % 4) == 0) {
      // írjuk ki tmpBuf-ot
      ensureSectorErased(currentWriteAddr);
      // ESP.flashWrite vár uint32_t* és méret szavakban (4 bájt)
      ESP.flashWrite(currentWriteAddr, (uint32_t*)tmpBuf, tmpBufLen / 4);
      currentWriteAddr += tmpBufLen;
      tmpBufLen = 0;
    }
  }

  // Most a maradék adatot írjuk szóközönként (4 bájt)
  size_t remain = len - offset;
  size_t fullWords = (remain / 4);
  if (fullWords > 0) {
    size_t byteCount = fullWords * 4;
    // Írás előtt gondoskodunk a szükséges szektor(oka)tól
    uint32_t writeAddr = currentWriteAddr;
    uint32_t endAddr = writeAddr + byteCount;
    // erase szektorok szükség szerint
    uint32_t s = writeAddr / SECTOR_SIZE;
    uint32_t e = (endAddr - 1) / SECTOR_SIZE;
    for (uint32_t sec = s; sec <= e; ++sec) {
      ensureSectorErased(sec * SECTOR_SIZE);
    }
    // végleges írás
    ESP.flashWrite(currentWriteAddr, (uint32_t*)(data + offset), fullWords);
    currentWriteAddr += byteCount;
    offset += byteCount;
  }

  // Végül a maradék (<4 bájt) adatot tmpBufba tesszük
  size_t left = len - offset;
  if (left > 0) {
    memcpy(tmpBuf + tmpBufLen, data + offset, left);
    tmpBufLen += left;
  }
}

void handleUpload() {
  HTTPUpload& upload = server.upload();

  if (upload.status == UPLOAD_FILE_START) {
    Serial.printf("Upload start: %s, size: %u\n", upload.filename.c_str(), (uint)upload.totalSize);

    // Ellenőrizzük a flash méretét
    uint32_t flashSize = ESP.getFlashChipRealSize();
    if (FLASH_WRITE_ADDR >= flashSize) {
      Serial.println("ERROR: write address outside flash!");
      // megszakítás (válasz)
      server.send(500, "text/plain", "Write address outside flash!");
      return;
    }

    // lefoglaljuk a szektor erase állapot tömböt (feltételezzük az upload.totalSize elérhető)
    uint32_t needed = upload.totalSize ? upload.totalSize : (flashSize - FLASH_WRITE_ADDR);
    uint32_t startSec = FLASH_WRITE_ADDR / SECTOR_SIZE;
    uint32_t sectorCount = (needed + SECTOR_SIZE - 1) / SECTOR_SIZE;
    // cap ellenőrzés
    if (FLASH_WRITE_ADDR + needed > flashSize) {
      Serial.println("ERROR: Not enough flash space for file");
      server.send(500, "text/plain", "Not enough flash space for file");
      return;
    }
    firstSector = startSec;
    erasedSectorCount = sectorCount;
    // felszabadítás előző esetben
    if (erasedSectors) free(erasedSectors);
    erasedSectors = (bool*)malloc(sectorCount);
    memset(erasedSectors, 0, sectorCount);

    currentWriteAddr = FLASH_WRITE_ADDR;
    tmpBufLen = 0;

    Serial.printf("Will write to 0x%06X, sectors %u..%u\n", FLASH_WRITE_ADDR, (unsigned)startSec, (unsigned)(startSec + sectorCount - 1));
  } 
  else if (upload.status == UPLOAD_FILE_WRITE) {
    // upload.buf és upload.currentSize áll rendelkezésre
    writeFlashAligned((const uint8_t*)upload.buf, upload.currentSize);
    Serial.printf("Wrote chunk %u bytes, total written so far ~%u\n", (unsigned)upload.currentSize, (unsigned)(currentWriteAddr - FLASH_WRITE_ADDR));
  } 
  else if (upload.status == UPLOAD_FILE_END) {
    // maradék padding: padoljunk 0xFF-el a 4-byte határig (flash erased állapot 0xFF)
    if (tmpBufLen > 0) {
      // pad 0xFF
      while ((tmpBufLen % 4) != 0 && tmpBufLen < TMPBUF_SIZE) {
        tmpBuf[tmpBufLen++] = 0xFF;
      }
      if (tmpBufLen % 4 == 0) {
        ensureSectorErased(currentWriteAddr);
        ESP.flashWrite(currentWriteAddr, (uint32_t*)tmpBuf, tmpBufLen / 4);
        currentWriteAddr += tmpBufLen;
      }
      tmpBufLen = 0;
    }

    Serial.printf("Upload END: %s, total written: %u bytes\n", upload.filename.c_str(), (unsigned)(currentWriteAddr - FLASH_WRITE_ADDR));
    server.send(200, "text/plain", "Upload successful");
    // felszabadítjuk az erase tömböt
    if (erasedSectors) { free(erasedSectors); erasedSectors = nullptr; }
  }
}

void setup() {
  Serial.begin(57600);
  delay(100);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
  Serial.printf("Connecting to WiFi '%s' ...\n", ssid);
  while (WiFi.status() != WL_CONNECTED) {
    delay(200);
    Serial.print('.');
  }
  Serial.println();
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, [](){
    String html = "<html><body><form method='POST' action='/upload' enctype='multipart/form-data'>"
                  "<input type='file' name='file'>"
                  "<input type='submit' value='Upload'>"
                  "</form></body></html>";
    server.send(200, "text/html", html);
  });

  // feltöltés végpont: két callback verziója (on() harmadik paraméterként a befejező callback)
  server.on("/upload", HTTP_POST, [](){
    // normál befejező válasz csak itt, de az upload callback küldi vissza a végső státuszt.
    // Üres. A valós választ az upload státuszban küldjük.
  }, handleUpload);

  server.begin();
  Serial.println("HTTP server started");
}

void loop() {
  server.handleClient();
}

Már értem. Kevered a FLASH-t meg az EEPROM-ot.

Nem, nem keverem, ahogy idéztem is a specifikációból: "Flash memory is a block-erase EEPROM optimized for high density and speed." 

Te kevered:

Nincs is eeprom. Az a flash végén egy kis rész. Nem értem ez hogy jön ide.

Az a "flash végén egy kis rész", az nem EEPROM, mert a Flash egésze egy EEPROM, a "flash végén egy kis rész" az egy szimpla "Unused" memory block az EEPROM-ban, egy 4096 bájt hosszú blokk, amit te valamiért a saját gépeden úgy hívsz, hogy `eeprom_full.bin`. De hívhatod azt bárhogy, lehetne a neve `patyomkin.bin` is, ugyanúgy működne a programod, az ESP8266 ugyanis nem tudja, hogy mi a fájl neve, amiből felmásolsz 4096 bájtot.

a: 24C16

Ilyen nincs az ESP architektúrában használva.

b: EEPROM.begin(512); 

Ez meg az Arduino AVR platformon lévő 4kB méretű EEPROM szoftveres emulációja ESP Arduino környezetben, ezt, ha akarod (EEPROM_start), akkor bárhova be tudod címezni az ESP külső EEPROM memóriában... ez nem egy fizikai külön EEPROM, hanem egy szoftveresen emulált EEPROM, ami pont ugyanúgy viselkedik, mint egy AVR alapú Arduino esetén a fizikai EEPROM.

Hagyjuk, értelek.

Kételkedek benne kissé.

Én nem szenvednék teljes firmware OTA frissítésésel, inkább olyat használnék, ahol tudod változtatni a filerendszeren a kódot. Pl. NodeMCU.

A tudomány és a hit vitája akkor eldőlt, amikor villámhárítót szereltek a templomokra.

Sokkal kevesebbet (és lassabban) tudsz futtatni, ha interpretált kóddal futsz neki, lényegesen jobb lehetőségeid vannak Arduino alapokon és ESP-IDF esetén meg teljesen kinyílik a világ. De ez az OTA lehetőséget nem érinti, azt a chip saját firmware kódja végzi és kötött a működése.

Tisztában vagyok vele, hogy egy interpreter nyelv lassabb, mint a natív fordított. De rengeteg olyan alkalmazás van, ahol ez az interpreter sebesség is bőven elég, nekem pl. eddig megfelelt mindenre: Villanyóra P1 port, szenzorok olvasása, ventilátorok vezérlése, mindezek ESP-01-el, ami a leggyengébb ESP8266 modul.

A tudomány és a hit vitája akkor eldőlt, amikor villámhárítót szereltek a templomokra.