[Quelle]
HC2v1: ESP32 Chip model = ESP32-D0WDQ5 Rev 1
HC2v2: ESP32 Chip model = ESP32-D0WD Rev 1
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
Der Wert "boot" (GPIO_STRAP_REG) gibt den Zustand der Pins GPIO12, GPIO0, GPIO2, GPIO4, GPIO15, GPIO5 beim Reset an.
Bei normalem Start (mit 1,8V-Flash) ist boot=0x13.
Bei Upload-Mode (IO0=0) ist boot=0x03.
GPIO12 0=3,3V 1=1,8V |
GPIO0 1=App 0=Upload |
GPIO2 | GPIO4 | GPIO15 | GPIO5 SDIO Timing 0 1 |
|
0x13 | 0 | 1 | 0 | 0 | 1 | 1 |
0x03 | 0 | 0 | 0 | 0 | 1 | 1 |
0x17 | 0 | 1 | 0 | 1 | 1 | 1 |
0x07 | 0 | 0 | 0 | 1 | 1 | 1 |
Der Bootloader im ESP32 fragt nach einem Reset den Zustand von GPIO0 ab. Ist dieses Low startet der serielle Flashloader. IO0 muss dazu mind. 1ms nach EN=1 auf 0 gehalten werden. Die auf dem Eval-Board verwendete RTS/DTR-Logik verhindert ein gleichzeitiges Low an EN und IO0. Das esp32load.py schaltet RTS und DTR zu langsam um, sodass IO0 zu spät 0 ist. Abhilfe wäre die Vergrößerung der Reset-Zeitkonstante durch eine größere Kapazität.
[Quelle]
digitalRead(switchPin);
Z.B. ist der BOOT-Switch mit IO0 verbunden (um nach dem Reset vom Bootloader in den Flash-Loader zu wechseln) und kann sofort als Taster im Programm genutzt werden (switchPin=0).
#define ledPin 2 digitalWrite(ledPin, HIGH);
Auf dem ESP32 Dev Module V1 ist an IO2 eine blaue LED angeschlossen.
Das Lesen erfolgt wie üblich über
analogRead(34); // read analog GPIO34
Der ESP32 hat zwei ADCs: ADC1 und ADC2.
CH | ADC1 GPIO |
ADC2* GPIO |
CH# |
---|---|---|---|
0 | 36* | 4 | 10 |
1 | 37* | 0 | 11 |
2 | 38* | 2 | 12 |
3 | 39* | 15 | 13 |
4 | 32 | 13 | 14 |
5 | 33 | 12 | 15 |
6 | 34 | 14 | 16 |
7 | 35 | 27 | 17 |
8 | 25 | 18 | |
9 | 26 | 19 | |
Hall | vdd33 |
*GPIO36 und GPIO39 dürfen bei Verwendung des Halls nicht beschaltet sein.
*GPIO37 und GPIO38 sind beim ESP32-WROOM-32 nicht herausgeführt
*ADC2 ist bei Verwendung von WiFi nicht verfügbar.
Wenn der volle Spannungsbereich gewählt ist (atten=3, d.h. 0..3,3V), dann ist ab einer Spannung von ca. 2450mV (3000 ADC) das ADC-Ergebnis nicht mehr linear. Siehe Datasheet 4.1.2. Der genauere Spannungsbereich wäre dann 150mV..2450mV bei 0dB, bzw. 0mV..2500mV beim ESP32-S2
Der ESP32-S2 hat zwei ADCs: ADC1 und ADC2.
CH | ADC1 GPIO |
ADC2* GPIO |
---|---|---|
0 | 1 | 11 |
1 | 2 | 12 |
2 | 3 | 13 |
3 | 4 | 14 |
4 | 5 | 15 |
5 | 6 | 16 |
6 | 7 | 17 |
7 | 8 | 18 |
8 | 9 | 19 |
9 | 10 | 20 |
pa_pkdet1 | ||
pa_pkdet2 | ||
vdd33 |
ADC2 kann zwar parallel zu WiFi verwendet werden, eine Wandlung kann aber durch die höhere Prio des WiFi abgebrochen werden.
Der ESP32 hat 3 UARTs.
Die erste UART, die auf dem Dev-Modul mit dem CP2102 verbunden ist (über IO1 und IO3), wird wie üblich initialisiert mit
Serial.begin(115200);
Die zwei anderen UARTs werden durch den Konstruktor definiert und mit Angabe der Pin-Nummern
in begin()
initialisiert:
//Definition der beiden Schnittstellen HardwareSerial Serial1(1); HardwareSerial Serial2(2);void setup() { //Starten der Schnittstellen Serial.begin(115200); //Serial1 auf Pin 12 und 13 Serial1.begin(9600,SERIAL_8N1,12,13); //Serial2 auf Pin 22 und 23 Serial2.begin(9600,SERIAL_8N1,22,23); //Startmeldung ausgeben Serial.println(); Serial.println("Bitte eine Eingabe:"); }
[Quelle]
ESP32-CAN-Bibliothek "miwagner": [Quelle] als .zip herunterladen und nach "Arduino\libraries" entpacken oder mittels Sketch > Include Library > Add .ZIP Library importieren
weitere Bibliotheken für MCP2515 [3/2024]:
Im Self-Test-Mode wird kein ACK benötigt, was sich zum Testen bzw. am Bus mit nur einem, nicht immer aktiven Teilnehmer eignet. Um diesen zu aktivieren habe ich folgende Änderungen der ESP32CAN-Lib vorgenommen:
Datei | Änderung/Erweiterung |
---|---|
ESP32CAN.cpp | int ESP32CAN::CANSTM() { return CAN_STM(); } |
ESP32CAN.h | class ESP32CAN { public: int CANSTM(); }; |
CAN.c | int CAN_STM() { // enter reset mode MODULE_CAN->MOD.B.RM = 1; MODULE_CAN->MOD.B.STM = 1; MODULE_CAN->MOD.B.RM = 0; return 0; } |
ESP32CAN | ACAN_ESP32 | |
Methoden | CANInit() CANConfigFilter() CANWriteFrame() CANStop() CANSTM() CANLOM() |
available() receive() getReceivedMessage() tryToSend() |
Konfig | CAN_device_t CAN_cfg [Global] CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t)) |
ACAN_ESP32_Settings settings (DESIRED_BIT_RATE) |
Filter | ESP32Can.CANConfigFilter(&p_filter) | ACAN_ESP32_Filter filter = ACAN_ESP32_Filter::singleStandardFilter (ACAN_ESP32_Filter::data, 0x123, 0x404) |
Init | ESP32Can.CANInit() | ACAN_ESP32::can.begin (settings, filter) |
ACAN_ESP32::can.tryToSend (frame) | ||
xQueueReceive(CAN_cfg.rx_queue, &rx_frame, timeout) | ACAN_ESP32::can.receive (frame) | |
Tx | ISR: Tx => xSemaphoreGiveFromISR(sem_tx_complete) | |
Rx | ISR: Rx => xQueueSendToBackFromISR() |
#include <Wire.h> // I2C Wire.begin(21,22); Wire.setClock(400000); // choose 400 kHz I2C rate Wire.beginTransmission(0x3B); // Initialize the Tx buffer Wire.write(0x0F); // Put WHO_AM_I address in Tx buffer Wire.endTransmission(false); // Send the Tx buffer, but send a restart to keep connection alive Wire.requestFrom(0x3B, 1); // Read two bytes from slave PROM address while (Wire.available()) { data = Wire.read(); } // Put read results in the Rx buffer
#include <Wire.h>
#define SDA1 21
#define SCL1 22
#define SDA2 17
#define SCL2 16
TwoWire I2Cone = TwoWire(0);
TwoWire I2Ctwo = TwoWire(1);
siehe Arduino->Beispiele->SPI
#include <Preferences.h> Preferences prefs; prefs.begin("nvs", false); prefs.putUChar("addr", 65); byte b = prefs.getUChar("addr", 0); prefs.remove("nvs");
[Quelle]
#include <EEPROM.h> EEPROM.begin(EEPROM_SIZE); EEPROM.write(address, value); EEPROM.commit(); EEPROM.read(address);
[Quelle]
SSD1306 128x64 I2C monochrom
SSD1351 128x160 SPI monochrom
SSD1353 128x160 SPI RGB15
Pinout:
u8glib U8glib_Arduino v1.19.1 Wiki |
U8g2_Arduino
v2.26.4 Wiki Arduino-Lib |
Ucglib_Arduino
v1.5.2 ucglib Wiki HAL |
|
SSD1306 | x | x | |
SSD1351 | x | x | |
SSD1353 | x |
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0); // I2C / TWI //U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST); // Fast I2C / TWI //U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NO_ACK); // Display which does not send AC void setup() { // assign default color value if ( u8g.getMode() == U8G_MODE_R3G3B2 ) { u8g.setColorIndex(255); // white } else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) { u8g.setColorIndex(3); // max intensity } else if ( u8g.getMode() == U8G_MODE_BW ) { u8g.setColorIndex(1); // pixel on } else if ( u8g.getMode() == U8G_MODE_HICOLOR ) { u8g.setHiColorByRGB(255,255,255); } void loop(void) { // picture loop u8g.firstPage(); do { draw(); } while( u8g.nextPage() ); }
// Page-Mode (_1_) oder FullyBuffered (_F_)
U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R2, /* clock=*/ 16, /* data=*/ 17, /* reset=*/ U8X8_PIN_NONE); // ESP32 Thing, pure SW emulated I2C
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17); // ESP32 Thing, HW I2C with pin remapping
void setup()
{
u8g2.begin();
}
void loop(void)
{
u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
// im Fully Buffered Mode wird nur eine große Page benötigt
u8g2.clearBuffer(); // clear the internal memory
u8g2.drawStr(0,10,"Hello World!"); // write something to the internal memory
u8g2.sendBuffer(); // transfer internal memory to the display
// im Page-Mode müssen die Befehle für jede Page wiederholt werden
do
{
u8g2.drawStr( 0, 0, "drawLine");
} while( u8g2.nextPage() );}
Die u8glib enthält nur teilweise die Unterstützung für den SSD1353. Hinzugefügt habe ich:
Datei | Änderung/Erweiterung |
---|---|
u8glib.h |
class U8GLIB_SSD1353_160X128_HICOLOR : public U8GLIB { public: U8GLIB_SSD1353_160X128_HICOLOR(uint8_t sck, uint8_t mosi, uint8_t cs, uint8_t a0, uint8_t reset = U8G_PIN_NONE) : U8GLIB(&u8g_dev_ssd1353_160x128_hicolor_sw_spi, sck, mosi, cs, a0, reset) { } U8GLIB_SSD1353_160X128_HICOLOR(uint8_t cs, uint8_t a0, uint8_t reset = U8G_PIN_NONE) : U8GLIB(&u8g_dev_ssd1353_160x128_hicolor_hw_spi, cs, a0, reset) { } }; class U8GLIB_SSD1353_160X128_332 : public U8GLIB { public: U8GLIB_SSD1353_160X128_332(uint8_t sck, uint8_t mosi, uint8_t cs, uint8_t a0, uint8_t reset = U8G_PIN_NONE) : U8GLIB(&u8g_dev_ssd1353_160x128_332_sw_spi, sck, mosi, cs, a0, reset) { } U8GLIB_SSD1353_160X128_HICOLOR(uint8_t cs, uint8_t a0, uint8_t reset = U8G_PIN_NONE) : U8GLIB(&u8g_dev_ssd1353_160x128_332_hw_spi, cs, a0, reset) { } }; |
clib\u8g.h | /* SSD1353 OLED Palmtronics */ extern u8g_dev_t u8g_dev_ssd1353_160x128_332_hw_spi; extern u8g_dev_t u8g_dev_ssd1353_160x128_hicolor_hw_spi; extern u8g_dev_t u8g_dev_ssd1353_160x128_332_sw_spi; extern u8g_dev_t u8g_dev_ssd1353_160x128_hicolor_sw_spi; |
SSD1351 | SSD1353 | ucglib | |
---|---|---|---|
Set Column Address | 0x15 Start End | x | |
Set Row Address | 0x75 Start End | x | |
Write RAM Command | 0x5C | x | |
Read RAM Command | 0x5D | ||
Set Second Precharge speed | N/A | 0x8A AA | |
Set Re-map / Color Depth (Display RAM to Panel) |
0xA0 Bits leicht unterschiedliche Reset-Werte, Bit 3 unterschiedlich |
||
Set Display Start Line / vertical scroll by RAM | 0xA1 Start | ||
Set Display Offset | 0xA2 Offset | ||
Set Display Mode - All OFF | 0xA4 | 0xA6 | |
Set Display Mode - All ON, GS63 | 0xA5 | ||
Set Display Mode - Reset to normal | 0xA6 | 0xA4 | |
Set Display Mode - Inverse | 0xA7 | ||
Function Selection (Vdd, Interface Width) | 0xAB Bits | N/A | |
Dim Mode setting | N/A | 0xAB AA BB CC DD EE | |
Display ON in dim mode | N/A | 0xAC | |
NOP | 0xAD 0xB0 0xD1 |
N/A | |
Sleep Mode On (Display OFF) | 0xAE | x | |
Sleep Mode Off (Display ON) | 0xAF | ||
Set Reset (Phase 1) / Pre-charge (Phase 2) period |
0xB1 AB; vorwärtskompatibel | ||
Display Enhancement | 0xB2 AA BB CC | N/A | |
Front Clock Divider (DivSet)/ Oscillator Frequency |
0xB3 AB - unterschiedliche Wertezuordnung | ||
Set Segment Low Voltage (VSL) | 0xB4 AA BB CC | N/A | |
Set GPIO | 0xB5 Bits | N/A | |
Set Second Precharge Period | 0xB6 AA | 0xB4 AA gleicher Wertebereicch | |
Look Up Table for Gray Scale Pulse width | 0xB8 AA[63], aber unterschiedlicher Wertebereich | ||
Use Built-in Linear LUT [reset= linear] | 0xB9 | ||
Set Pre-charge voltage | 0xBB AA, aber unterschiedlicher Wertebereich | ||
Set VCOMH Voltage | 0xBE AA, aber unterschiedlicher Wertebereich | ||
OTP Write | N/A | 0xC0 AA BB | |
Set Contrast Current for Color A,B,C | 0xC1 AA BB CC | 0x81 AA 0x82 BB 0x83 CC |
|
Master Contrast Current Control | 0xC7 AA | 0x87 AA, gleicher Wertebereich | x |
Set Multiplex Ratio | 0xCA AA | 0xA8 AA | |
Software Reset | N/A | 0xE2 | |
NOP | 0xE3 | ||
Set Command Lock | 0xFD AA, aber unterschiedlicher Wertebereich [AA.2] | ||
Graphic Acceleration | |||
Horizontal Scroll | 0x96 AA BB CC DD EE | 0x27 AA BB CC DD EE, dazu 0xA3 | |
Stop Moving | 0x9E | 0x2E | |
Start Moving | 0x9F | 0x2F | |
Set Vertical Scroll Area | N/A | 0xA3 AA BB | |
Draw Line | N/A | 0x21 D[7] | |
Drawing Rectangle | N/A | 0x22 D[10] | |
Copy | N/A | 0x23 D[6] | |
Dim Window | N/A | 0x24 D[4] | |
Clear Window | N/A | 0x25 D[4] | |
Fill Enable / Disable | N/A | 0x26 AA |
Datei | ||
ucglib.h | class Ucglib_SSD1353_18x160x128_HWSPI : public Ucglib4WireHWSPI { public: Ucglib_SSD1353_18x160x128_HWSPI( uint8_t cd, uint8_t cs = UCG_PIN_VAL_NONE, uint8_t reset = UCG_PIN_VAL_NONE) : Ucglib4WireHWSPI(ucg_dev_ssd1353_18x160x128_ilsoft, ucg_ext_ssd1353_18, /*cd=*/ cd , /*cs=*/ cs, /*reset=*/ reset) { } }; |
|
clib\ucg.h | ucg_int_t ucg_dev_ssd1353_18x160x128_dep(ucg_t *ucg, ucg_int_t msg,
void *data); // ucg_dev_oled_160x128_dep.c ucg_int_t ucg_ext_ssd1353_18(ucg_t *ucg, ucg_int_t msg, void *data); // ucg_dev_ic_ssd1353.c ucg_int_t ucg_dev_ic_ssd1353_18(ucg_t *ucg, ucg_int_t msg, void *data); // ucg_dev_ic_ssd1353.c |
|
In der Fontbezeichnung sind verschiedene Dinge kodiert [Quelle]:
[Quelle]
Das Beispiel "OTAWebUpdater" nutzt jQuery, sodass ein Update nicht möglich ist, wenn der ESP32 als AccessPoint läuft, weil die jquery.min.js nicht aus dem Internet geladen werden kann. Stattdessen kann das AJAX auch direkt ins Javascript der firmwareUploadHTML geschrieben werden - einfach dieses firmwareUploadHTML benutzen:
/* * HTML-Seite Firmware-Upload: "/firmwareUpload" */ //#define HTMLCrLf "\n" #define HTMLCrLf const char* firmwareUploadHTML = "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form' onsubmit='return uploadFile(this);'>" HTMLCrLf "<input type='file' name='update'>" HTMLCrLf "<input type='submit' value='Update'>" HTMLCrLf "</form>" HTMLCrLf "<div id='prg'>progress: 0%</div>" HTMLCrLf "<script>" HTMLCrLf "function uploadFile(form)" HTMLCrLf "{" HTMLCrLf "var xhr = new window.XMLHttpRequest();" HTMLCrLf "xhr.upload.addEventListener('progress', function (evt)" HTMLCrLf "{" HTMLCrLf "if (evt.lengthComputable)" HTMLCrLf "{" HTMLCrLf "var per = evt.loaded / evt.total;" HTMLCrLf "document.getElementById('prg').innerHTML = 'progress: ' + Math.round(per*100) + '%';" HTMLCrLf "}" HTMLCrLf "}, false);" HTMLCrLf "xhr.open('POST', '/update', true);" HTMLCrLf // true für async "var data = new FormData(form);" HTMLCrLf "xhr.onload = function()" HTMLCrLf "{" HTMLCrLf "if (xhr.status === 200)" HTMLCrLf "{" HTMLCrLf "console.log('success!');" HTMLCrLf "document.getElementById('prg').innerHTML = 'Success!';" HTMLCrLf "}" HTMLCrLf "else document.getElementById('prg').innerHTML = 'Upload error!';" HTMLCrLf "};" HTMLCrLf "xhr.send(data);" HTMLCrLf "return false;" HTMLCrLf "}" HTMLCrLf "</script>";
Nach einem POR/BOR muss VOR dem ersten Standby-Pulse ein L-H-Übergang erfolgt sein!
Zum Debugging bietet es sich an, die WiFi-Events zu beobachten:
void WiFiEvent(WiFiEvent_t event, system_event_info_t info){...} WiFi.onEvent(WiFiEvent);
Verbindet die Station mit DHCP.
auch interessant:
ESP-IDF
Arduino
Der Server bietet unter der UUID_RX einen Write an, unter UUID_TX einen Notify.
Der Client kann auf den UUID_RX schreiben, und bekommt einen Notify-Callback vom UUID_TX.
Der Server schreibt auf den UUID_TX (dazu wird der Zeiger auf die TxCharacteristic benötigt), und bekommt einen Write-Callback vom UUID_RX.
PIN: https://github.com/choichangjun/ESP32_arduino/blob/master/ESP32_Arduino_paring_Key.ino
https://esp32-server.de/hm-10-esp32/
BLE Server | BLE Client | |
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" |
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART
service UUID #define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" #define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" |
|
Init | BLEDevice::init("UART Service For ESP32"); BLEServer *pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); BLEService *pService = pServer->createService(SERVICE_UUID);
***Beispiel eine gemeinsame Variable *** ***Beispiel bidirektionaler Austausch *** BLECharacteristic * pRxCharacteristic =
pService->createCharacteristic( pService->start(); |
BLEDevice::init("");
|
Connect: Server |
class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { deviceConnected = true; }; void onDisconnect(BLEServer* pServer) { |
|
Kommunikation: Server <> Client |
class MyCallbacks: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { std::string rxValue = pCharacteristic->getValue(); if (rxValue.length() > 0) { Serial.println(); ... |
|
Scan for Server | // Retrieve a Scanner and set the callback we want to use to be
informed when we // have detected a new device. Specify that we want active scanning and start the // scan to run for 5 seconds. BLEScan* pBLEScan = BLEDevice::getScan(); pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setInterval(1349); pBLEScan->setWindow(449); pBLEScan->setActiveScan(true); pBLEScan->start(5, false); |
|
CallBack: Find Server |
/** * Scan for BLE servers and find the first one that advertises the service we are looking for. */ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { /** * Called for each advertising BLE server. */ void onResult(BLEAdvertisedDevice advertisedDevice) { Serial.print("BLE Advertised Device found: "); Serial.println(advertisedDevice.toString().c_str()); // We have found a device, let us now see if it contains the service
we are looking for. BLEDevice::getScan()->stop(); } // Found our server |
|
Main: Connect to Server |
if (doConnect == true) { if (connectToServer()) { Serial.println("We are now connected to the BLE Server."); } else { Serial.println("We have failed to connect to the server; there is nothin more we will do."); } doConnect = false; } |
|
Kommunikation Client > Server |
if (connected) { String newValue = "Time since boot: " + String(millis()/1000); Serial.println("Setting new characteristic value to \"" + newValue + "\""); // Set the characteristic's value to be the array of bytes that is
actually a string. |
https://docs.espressif.com/projects/esptool/en/latest/esp32/esptool/basic-commands.html
Flash-Erase: esptool.py --chip esp32 p com7 erase_flash
Flash-Program: %LOCALAPPDATA%\Arduino15\packages\esp32\tools\esptool_py\3.0.0/esptool.exe --chip esp32 --port COM7 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0xe000 %LOCALAPPDATA%\Arduino15\packages\esp32\hardware\esp32\1.0.6/tools/partitions/boot_app0.bin 0x1000 %LOCALAPPDATA%\Arduino15\packages\esp32\hardware\esp32\1.0.6/tools/sdk/bin/bootloader_qio_80m.bin 0x10000 CAN-Remote-ESP32.ino.bin 0x8000 CAN-Remote-ESP32.ino.partitions.bin
Flash-Read: esptool.exe --chip esp32-s2 --port COM8 --baud 921600 --before no_reset --after no_reset read_flash 0 0x400000 flash_contents.bin
"%LOCALAPPDATA%\Arduino15\packages\esp32\tools\esptool_py\4.2.1/esptool.exe" --chip esp32s2 --port "COM8" --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x1000 "C:\Users\Christian\AppData\Local\Temp\arduino-sketch-BA776B339A5A10F512B565EB85152DEF/SmartCoffee.ino.bootloader.bin" 0x8000 "C:\Users\Christian\AppData\Local\Temp\arduino-sketch-BA776B339A5A10F512B565EB85152DEF/SmartCoffee.ino.partitions.bin" 0xe000 "C:\Users\Christian\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.6/tools/partitions/boot_app0.bin" 0x10000 "C:\Users\Christian\AppData\Local\Temp\arduino-sketch-BA776B339A5A10F512B565EB85152DEF/SmartCoffee.ino.bin"
"%LOCALAPPDATA%\Arduino15\packages\esp32\tools\esptool_py\4.5.1/esptool.exe"
--chip esp32 --port COM5 --baud 921600 --before default_reset --after hard_reset
write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x1000 "%TEMP%\Temp\arduino_build_843164/ACANLoopBack.ino.bootloader.bin"
0x8000 "%TEMP%\arduino_build_843164/ACANLoopBack.ino.partitions.bin" 0xe000 "%LOCALAPPDATA%\Arduino15\packages\esp32\hardware\esp32\2.0.9/tools/partitions/boot_app0.bin"
0x10000 "%TEMP%\arduino_build_843164/ACANLoopBack.ino.bin"
AppData\Local\Arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\esp-2021r2-patch5-8.4.0\bin\xtensa-esp32-elf-objdump.exe -d Sketch.ino.cpp.o >disasm.s
Die Partitionierung ergibt sich z.T. aus der partitions.csv (im temp):
# Name | Type | SubType | Offset | Size | Flags | Flashvorgang über ISP per esptool.exe | Erklärung |
---|---|---|---|---|---|---|---|
0x1000 | [0x7000] | sdk/bin/bootloader_qio_80m.bin [0x48E0] | Bootloader, max. 28.672 Bytes | ||||
0x8000 | [0x1000] | arduino_build_956369/WiFiClientPSK.ino.partitions.bin [0x1000] | Partition Tables | ||||
nvs | data | nvs | 0x9000 | 0x5000 | |||
otadata | data | ota | 0xe000 | 0x2000 | partitions/boot_app0.bin [0x2000] | ||
app0 | app | ota_0 | 0x10000 | 0x140000 | arduino_build_956369/WiFiClientPSK.ino.bin | ||
app1 | app | ota_1 | 0x150000 | 0x140000 | |||
spiffs | data | spiffs | 0x290000 | 0x170000 |
CSV to Binary: C:\Espressif\frameworks\esp-idf-v5.1\components\partition_table\python gen_esp32part.py input_partitions.csv
binary_partitions.bin
Die ino.partitions.bin wird aus der partitions.csv erzeugt durch
gen_esp32part.exe" -q "arduino_build_956369/partitions.csv"
"arduino_build_956369/WiFiClientPSK.ino.partitions.bin"
Binary to CSV: python gen_esp32part.py binary_partitions.bin input_partitions.csv
Display partition table: python gen_esp32part.py binary_partitions.bin
Image einer Partition speichern: C:\Espressif\frameworks\esp-idf-v5.1\components\partition_table\parttool.py -p COM7 read_partition --partition-type=data --partition-subtype=nvs --output "nvs.bin"
Image der Partitions-Partition speichern: esptool.py --chip esp32 -p COM7 -b 921600 --before no_reset --after no_reset read_flash 0x8000 0x1000 ..\HC2v1_part.bin
Der Platz für den Bootloader ist bei den Standard-Arduino-Libraries beschränkt auf 0x7000 Bytes. Bei unverschlüsseltem Flash kann ein Bootloader-Debug-Level von bis zu 'Info' verwendet werden, beim verschlüsselten Flash ist nur 'No output' möglich.
Ein Bootloader, der ohne "Enable flash encryption on boot" erstellt wurde, also z.B. der von Arduino, funktioniert auch mit aktiver Flash-Encryption.
Der First-Stage-Bootloader im ROM kann bei nicht-durchgebrannter eFuse DISABLE_DL_ENCRYPT ("bootloader encryption enabled") ein Plaintext-Image beim Flashen encrypten (esptool mit --encrypt). D.h. im Development-Mode kann beliebig oft Plaintext geflasht werden, ohne dass es pre-encryptet ist.
C:\Espressif\frameworks\esp-idf-v5.1\components\nvs_flash\nvs_partition_tool\nvs_tool.py -i -d minimal --color never d:\esp32\NVS.bin
In die 'nvs_keys' wird raw geschrieben, also als ob es hardware-encrypted wäre, sodass die Schlüssel beim Lesen durch die Hardware-Flash-Decryption entstehen:
Die (entschlüsselte) 'nvs_keys'-Partition ist der Schlüssel, um die verschlüsselte 'nvs'-Partition zu entschlüsseln:
Mit nvs_partition_gen.py lassen sich NVS-Partitionen aus CSV erstellen, oder NVS-Schlüssel (nvs_keys) erstellen.
https://docs.espressif.com/projects/esptool/en/latest/esp32/espefuse/index.html
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/flash-encryption.html
Folgende Teile des Flashs können verschlüsselt sein [Quelle]: Standard sind 'app'-Type verschlüsselt, 'nvs' ist nicht mit Hardware-Verschlüsselung kompatibel, kann aber mittels "NVS Encryption" verschlüsselt werden: dazu wird die Partition "nvs_keys" benötigt, die Hardware-verschlüsselt ist.
Nur in den Adressbereich gemappter Flash wird ver-/entschlüsselt.
Daten über spi_flash_read() werden nicht entschlüsselt! Das betrifft z.B. SketchMD5()
Daten über esp_partition_read() werden je nach Partitionstabelle entschlüsselt oder nicht.
In Arduino sind diese Funktionen nicht Encryption-fähig:
Für verschlüsselten Flash ist ein anderer Bootloader nötig. Dieser richtet die Verschlüsselung ein, wenn sie nicht aktiviert ist (FLASH_CRYPT_CNT hat geradzahlige Anzahl von gesetzten Bits). Aktiviert wird dies durch "Enable flash encryption". Im Development kann zum Schutz vor unbeabsichtigter Verschlüsselung "Require flash encryption to be already enabled" gesetzt werden (SECURE_FLASH_REQUIRE_ALREADY_ENABLED).
DISABLE_DL_DECRYPT: im Bootloader wird der Flash nicht
entschlüsselt, d.h. der Code wird nicht als Plain gelesen.
DISABLE_DL_ENCRYPT: im Bootloader wird die Möglichkeit deaktiviert während des
Uploads verschlüsselt zu schreiben (--encrypt)
Für den Bootloader ist Platz für 0x7000 Bytes. Durch Verschlüsselung oder Debuglevel kann dieser Platz ggfs. nicht ausreichen.
Ein Key kann erzeugt werden durch espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin - die Qualität wird durch das OS bestimmt.
Der Key wird im ESP32 gespeichert durch espefuse.py --port COM7 burn_key
flash_encryption my_flash_encryption_key.bin
Der Lese-/Schreibschutz für den Kay kann mit --no-protect-key verhindert werden.
Vor dem Flash muss die Firmware verschlüsselt werden, im Release-Mode muss zusätzlich mit --force geflasht werden:
Ggfs. sollte auch die ota-Partition mitgeflasht werden, um sicherzustellen, dass app0 ausgeführt wird.
ACHTUNG: Ist FLASH_CRYPT_CNT nicht gesperrt, kann eine unverschlüsselte
Firmware zum Auslesen aufgespielt werden.
ACHTUNG: Ist DISABLE_DL_ENCRYPT nicht aktiv, kann eine unverschlüsselte
Firmware zum Auslesen aufgespielt werden.
geht nur 3mal, solange noch freie Bits in FLASH_CRYPT_CNT sind: FLASH_CRYPT_CNT (7Bit) auf geradzahlige Bitanzahl setzen (0x00, 0x03, 0xF, 0x3F)
alternativ ohne Vorverschlüsselung: (NVS bleibt unverschlüsselt erhalten)
Für Debug muss die Partitionstabelle wegen des größeren Bootloaders geändert werden, und auch in der menuconfig/partition-offset angegeben werden:
# Name | Type | SubType | Offset | Size | Flags | Flashvorgang über ISP per esptool.exe | Erklärung |
---|---|---|---|---|---|---|---|
0x001000 | 0x00C000 | Bootloader | |||||
0x00D000 | 0x001000 | Partition Tables | |||||
otadata | data | ota | 0x00E000 | 0x002000 | |||
app0 | app | ota_0 | 0x010000 | 0x140000 | |||
app1 | app | ota_1 | 0x150000 | 0x140000 | |||
spiffs | data | spiffs | 0x290000 | 0x16B000 | |||
nvs | data | nvs | 0x3FB000 | 0x005000 |
ACHTUNG: Die ESP-IDF verwendet für die Adresse der Partitionstabelle eine feste Größe (ESP_PARTITION_TABLE_OFFSET), die in den Arduino-Libraries "eingebrannt" ist (CONFIG_PARTITION_TABLE_OFFSET = 0x8000). Liegt sie woanders, wird z.B. die NVS-Partition nicht gefunden. Siehe load_partitions()/partition.c (initArduino()/esp32-hal-misc.c > nvs_flash_init()/nvs_api.cpp > esp_partition_find_first()>load_partitions()/partition.c), in der ESP_PARTITION_TABLE_OFFSET verwendet wird.
Fehlermeldung bei Arduino v1.0.6, mit Partitionstabelle auf 0xD000:
[E][esp32-hal-misc.c:298] initArduino(): Failed to initialize NVS! Error: 261 (ESP_ERR_NOT_FOUND 0x105)
Fehlermeldung bei ESP-IDF v5.1 mit Partitionstabelle kompiliert auf 0x8000, tatsächlich auf 0xD000:
E (755) partition: No MD5 found in partition table
E (756) partition: load_partitions returned 0x105
Fehlermeldung wenn CONFIG_NVS_ENCRYPTION gesetzt ist und die nvs_keys-Partition fehlt:
E (815) nvs: CONFIG_NVS_ENCRYPTION is enabled, but no partition with subtype nvs_keys found in the partition table.
Die 'data'/'nvs'-Partition kann nur "software"-verschlüsselt werden (CONFIG_NVS_ENCRYPTION=y), die Schlüssel dazu werden in der "hardware"-verschlüsselten Partition 'data'/'nvs_keys' aufbewahrt. Der Bootloader-Code ist davon nicht betroffen, nur die NVS-Routinen der App:
C:\Espressif\frameworks\esp-idf-v5.1\components\nvs_flash\src\nvs_api.cpp : nvs_flash_init()
Arduino-Library v1.0.6 (esp-idf v3.3.5) unterstützt kein verschlüsseltes NVS (libnvs_flash.a).
https://github.com/espressif/arduino-esp32/issues/6012 Ganz unten ist der Download der Win32-Version von esptool
Win10x86 mit Arduino v1.8.9
Win10x64 mit Arduino v1.8.12
Arduino-Library Builder https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/lib_builder.html
https://www.esp32.com/viewtopic.php?t=1029