Spickzettel für ESP32

Installation in der Arduino-IDE

[Quelle]

Hardware ESP32 Dev Module

GPIO-Belegung zum ESP32-WROOM

Pins 30-Pin-Variante (schwarze Pinleiste)

Pins 36-Pin-Variante (gelbe Pinleiste)

Standard-Belegung ESP32

LOLIN32 mit LiIon

ESP32-CAM

Wemos S2 Mini

ESP-Chips

HC2v1: ESP32 Chip model = ESP32-D0WDQ5 Rev 1
HC2v2: ESP32 Chip model = ESP32-D0WD Rev 1

RAM im ESP32

PSRAM

Bootup-Messages

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
             


 

Programmierung des ESP32 Dev Module

Bootloader

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]

Pin lesen

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).

Pin schreiben

#define ledPin 2
digitalWrite(ledPin, HIGH);

Auf dem ESP32 Dev Module V1 ist an IO2 eine blaue LED angeschlossen.

A/D-Wandler

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.

Serielle Schnittstelle

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]

CAN-Schnittstelle

ESP32-CAN-Bibliothek: [Quelle] als .zip herunterladen und nach "Arduino\libraries" entpacken oder mittels Sketch > Include Library > Add .ZIP Library importieren

Self-Test-Mode

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;
}

I2C-Schnittstelle

#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);

SPI-Schnittstelle

siehe Arduino->Beispiele->SPI

Preferences / NVS

#include <Preferences.h>
Preferences prefs;
prefs.begin(„nvs“, false);
prefs.putUChar(„addr“, 65);
byte b = prefs.getUChar(„addr“, 0);
prefs.remove(„nvs“);

[Quelle]

EEPROM

#include <EEPROM.h>
EEPROM.begin(EEPROM_SIZE);
EEPROM.write(address, value);
EEPROM.commit();
EEPROM.read(address);

[Quelle]

OLED-Display

SSD1306 128x64 I2C monochrom

SSD1351 128x160 SPI monochrom

SSD1353 128x160 SPI RGB15

Adafruit SSD1351 RGB

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    

Ucglib

U8glib

#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() );} 

SSD1353 mit u8glib

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 vs. SSD1353

  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

Ucglib

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
 
     

Fonts

In der Fontbezeichnung sind verschiedene Dinge kodiert [Quelle]:

Web-Update

[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>";

RMT

UNI/O

Nach einem POR/BOR muss VOR dem ersten Standby-Pulse ein L-H-Übergang erfolgt sein!

WiFi

Zum Debugging bietet es sich an, die WiFi-Events zu beobachten:

void WiFiEvent(WiFiEvent_t event, system_event_info_t info){...}

WiFi.onEvent(WiFiEvent);

WiFi.begin()

Verbindet die Station mit DHCP.

auch interessant:

WiFi.mode()

ESP-NOW

Bluetooth Classic

ESP-IDF

Arduino

Bluetooth LE

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 ***
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setValue("Hi,other ESP32 here is your data");

***Beispiel bidirektionaler Austausch ***
pTxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_NOTIFY
);
pTxCharacteristic->addDescriptor(new BLE2902());

BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE
);
pRxCharacteristic->setCallbacks(new MyCallbacks());

pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06);
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
// bzw. pServer->getAdvertising()->start();

BLEDevice::init("");

 

 

Connect:
Server
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};

void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};

 
Kommunikation:
Server <> Client
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();

if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++)
Serial.print(rxValue[i]);

Serial.println();
Serial.println("*********");
}
}
};

...
pTxCharacteristic->setValue(&txValue, 1);
pTxCharacteristic->notify();

 
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.
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {

BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;

} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks

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.
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
}else if(doScan){
BLEDevice::getScan()->start(0); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino
}

esptool

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"

Assembler-Code erzeugen

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

Aufbau des Firmware-Images

https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/firmware-image-format.html

Partitionsaufbau bei Arduino

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

Bootloader

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.

NVS-Partition lesen

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_partition_parse.html

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

NVS verschlüsseln

Selbstverschlüsselung der IDF

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:

NVS entschlüsseln

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_partition_gen.html

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.

ESPFUSE.py

https://docs.espressif.com/projects/esptool/en/latest/esp32/espefuse/index.html

Flash-Verschlüsselung

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.

Einschränkungen nach Verschlüsselung

https://docs.espressif.com/projects/espressif-esp-idf/en/latest/security/flash-encryption.html#using-encrypted-flash

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:

Verschlüsselung einrichten

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.

Reflash mit verschlüsselter Firmware-Datei (Development- und Release-Mode)

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.

Reflash mit unverschlüsselter Firmware-Datei (Development-Mode)

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.

Deaktivieren der Verschlüsselung

geht nur 3mal, solange noch freie Bits in FLASH_CRYPT_CNT sind: FLASH_CRYPT_CNT (7Bit) auf geradzahlige Bitanzahl setzen (0x00, 0x03, 0xF, 0x3F)

Vorgehen beim manuellen Einrichten der Verschlüsselung für Release

Vorgehen: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/host-based-security-workflows.html#enable-flash-encryption-externally

alternativ ohne Vorverschlüsselung: (NVS bleibt unverschlüsselt erhalten)

Vorgehen bei Dev

  1. Fuses mal lesen (muss 115200 sein!): espefuse.py --chip esp32 -b 115200 -p COM7 --before no_reset summary
  2. Key erzeugen: espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin
  3. Key brennen: espefuse.py --chip esp32 -p COM7 -b 115200 --before no_reset burn_key flash_encryption [--no-protect-key] my_flash_encryption_key.bin
    mit --no-protect-key kann der Lese-/Schreibschutz des Keys verhindert werden
  4. die bestehende Partitionstabelle kann/muss beibehalten werden: Bootloader (0x1000-0x7FFF) und Partitionstabelle (0x8000-0x8FFF) müssen bleiben, die partitions.csv muss übereinstimmen
    ggfs. die nvs_keys anlegen für zukünftige Nutzung
  5. der Bootloader muss mit der esp-idf erstellt werden:
    1. idf.py menuconfig
      1. Custom Partition Table: partitions.csv @ 0x8000
      2. Bootloader config / "Enable app rollback support"
      3. ggfs. "Check Flash Encryption enabled on app startup" deaktivieren
      4. "Enable flash encryption on boot" in Developer bzw. Release
      5. Component config / Log output / "Use ANSI terminal colors" deaktivieren
      6. Component config / NVS / "Enable NVS encryption" deaktivieren [CONFIG_NVS_ENCRYPTION]
    2. idf.py build
  6. Test Plain (ohne Bootloader): esptool.py -p COM7 -b 460800 --before no_reset --after no_reset --chip esp32 write_flash --flash_mode dio --flash_size 4MB --flash_freq 80m 0xe000 build\ota_data_initial.bin 0x10000 build\hello_world.bin
  7. Do It: esptool.py -p COM7 -b 460800 --before no_reset --after no_reset --chip esp32 write_flash --flash_mode dio --flash_size 4MB --flash_freq 80m 0x1000 build\bootloader\bootloader.bin 0xe000 build\ota_data_initial.bin 0x10000 build\hello_world.bin

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.

Verschlüsseltes NVS

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).

Arduino-Bibliotheken vs. ESP-IDF

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

ESPTOOL erstellen

https://www.esp32.com/viewtopic.php?t=1029


Erstellt 14.04.2019, zuletzt geändert 18.03.2024 23:04:02, Zugriffszähler Besuche. © Christian Enders

Home | Nach oben | D-Link DUB-C2 | TechSolo TN-270 | Hama PCI-USB 2.0 18882 | Promise FastTrak100 TX2 | Mustek 1200 CP | Plustek OpticFilm 7200 | Canon MX925 | HP P1110 | Digitus DN-7003GT | Digitus DN-7003GS | Digitus DN-7006GR | Option Fusion | WLAN-Adapter | USB RS232 Wandler | ESP32 | BlackMagic DeckLink | KNC DVB-C+ | MD-9717