1+ #define DISPLAY_ENABLED
2+
3+ #include < WiFi.h>
4+ #include < WebServer.h>
5+ #include < ESPmDNS.h>
6+ #include < WiFiAP.h>
7+ #include < Update.h>
8+ #include < Wire.h>
9+ #ifdef DISPLAY_ENABLED
10+ #define SCREEN_WIDTH 128
11+ #define SCREEN_HEIGHT 64
12+ #define OLED_RESET -1
13+ #include < Adafruit_GFX.h>
14+ #include < Adafruit_SSD1306.h>
15+ Adafruit_SSD1306 display (SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
16+ #include < Adafruit_NeoPixel.h>
17+ Adafruit_NeoPixel rgb_led_1 = Adafruit_NeoPixel(1 , 1 , NEO_GRB + NEO_KHZ800);
18+
19+
20+ #endif
21+ #include " esp_partition.h"
22+ #include " esp_ota_ops.h"
23+ #include " esp_system.h"
24+
25+ String ssid;
26+ uint8_t mac[6 ];
27+
28+
29+ // Create an instance of the server
30+ WebServer server (80 );
31+ bool displayEnabled;
32+
33+ const int BUTTON_PIN = 0 ; // GPIO für den Button
34+ volatile unsigned long lastPressTime = 0 ; // Zeitpunkt des letzten Drucks
35+ volatile bool doublePressDetected = false ; // Flag für Doppeldruck
36+ const unsigned long doublePressInterval = 500 ; // Max. Zeit (in ms) zwischen zwei Drücken für Doppeldruck
37+ volatile int pressCount = 0 ; // Zählt die Button-Drucke
38+
39+ const unsigned char epd_bitmap_wifi [] PROGMEM = {
40+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
41+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x1f , 0x80 , 0x00 , 0x00 , 0x00 , 0x01 ,
42+ 0xff , 0xf0 , 0x00 , 0x00 , 0x00 , 0x07 , 0xff , 0xfc , 0x00 , 0x00 , 0x00 , 0x1f , 0xe0 , 0xff , 0x00 , 0x00 ,
43+ 0x00 , 0x3f , 0x00 , 0x0f , 0x80 , 0x00 , 0x00 , 0x7c , 0x00 , 0x03 , 0xe0 , 0x00 , 0x00 , 0xf0 , 0x00 , 0x01 ,
44+ 0xf0 , 0x00 , 0x01 , 0xe0 , 0x00 , 0x00 , 0x78 , 0x00 , 0x03 , 0xc0 , 0x00 , 0x00 , 0x38 , 0x00 , 0x07 , 0x80 ,
45+ 0x00 , 0x00 , 0x1c , 0x00 , 0x0f , 0x00 , 0x06 , 0x00 , 0x0e , 0x00 , 0x0e , 0x00 , 0x7f , 0xe0 , 0x0e , 0x00 ,
46+ 0x0c , 0x01 , 0xff , 0xf0 , 0x06 , 0x00 , 0x00 , 0x07 , 0xff , 0xfc , 0x02 , 0x00 , 0x00 , 0x0f , 0x80 , 0x3e ,
47+ 0x00 , 0x00 , 0x00 , 0x1f , 0x00 , 0x0f , 0x00 , 0x00 , 0x00 , 0x1c , 0x00 , 0x07 , 0x80 , 0x00 , 0x00 , 0x38 ,
48+ 0x00 , 0x03 , 0xc0 , 0x00 , 0x00 , 0x70 , 0x00 , 0x01 , 0xc0 , 0x00 , 0x00 , 0x70 , 0x00 , 0x00 , 0xc0 , 0x00 ,
49+ 0x00 , 0x20 , 0x00 , 0x00 , 0x40 , 0x00 , 0x00 , 0x00 , 0x1f , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x7f , 0xc0 ,
50+ 0x00 , 0x00 , 0x00 , 0x00 , 0xff , 0xe0 , 0x00 , 0x00 , 0x00 , 0x01 , 0xe0 , 0xf0 , 0x00 , 0x00 , 0x00 , 0x01 ,
51+ 0xc0 , 0x78 , 0x00 , 0x00 , 0x00 , 0x03 , 0x80 , 0x38 , 0x00 , 0x00 , 0x00 , 0x01 , 0x00 , 0x10 , 0x00 , 0x00 ,
52+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
53+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
54+ 0x06 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x04 , 0x00 , 0x00 , 0x00 ,
55+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
56+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
57+ };
58+
59+ // 'checkmark', 44x44px
60+ const unsigned char epd_bitmap_checkmark [] PROGMEM = {
61+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
62+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
63+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
64+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
65+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x80 , 0x00 , 0x00 , 0x00 ,
66+ 0x00 , 0x03 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x07 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x0f , 0x00 , 0x00 ,
67+ 0x00 , 0x00 , 0x00 , 0x1e , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x3c , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x78 ,
68+ 0x00 , 0x00 , 0x00 , 0x0e , 0x00 , 0xf0 , 0x00 , 0x00 , 0x00 , 0x0f , 0x01 , 0xe0 , 0x00 , 0x00 , 0x00 , 0x0f ,
69+ 0x83 , 0xc0 , 0x00 , 0x00 , 0x00 , 0x07 , 0xc7 , 0x80 , 0x00 , 0x00 , 0x00 , 0x03 , 0xef , 0x00 , 0x00 , 0x00 ,
70+ 0x00 , 0x01 , 0xfe , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xfc , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x78 , 0x00 ,
71+ 0x00 , 0x00 , 0x00 , 0x00 , 0x10 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
72+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
73+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
74+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
75+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
76+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
77+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
78+ };
79+
80+
81+ void IRAM_ATTR handleButtonPress () {
82+ unsigned long currentTime = millis (); // Hole aktuelle Zeit
83+
84+ // Vermeidung von Prellen: Wenn der aktuelle Druck zu nahe am letzten liegt, ignoriere ihn
85+ if (currentTime - lastPressTime > 50 ) {
86+ pressCount++; // Zähle den Button-Druck
87+ lastPressTime = currentTime; // Aktualisiere die Zeit des letzten Drückens
88+
89+ // Überprüfe, ob dies der zweite Druck innerhalb des Double-Press-Intervalls ist
90+ if (pressCount == 2 && (currentTime - lastPressTime <= doublePressInterval)) {
91+ doublePressDetected = true ; // Doppeldruck erkannt
92+ pressCount = 0 ; // Zähler zurücksetzen
93+ }
94+ }
95+ }
96+
97+ // Funktion zum Wechseln der Boot-Partition auf OTA1
98+ void setBootPartitionToOTA0 () {
99+ const esp_partition_t * ota0_partition = esp_partition_find_first (
100+ ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL );
101+
102+ if (ota0_partition) {
103+ // Setze OTA1 als neue Boot-Partition
104+ esp_ota_set_boot_partition (ota0_partition);
105+ Serial.println (" Boot partition changed to OTA0. Restarting..." );
106+
107+ // Neustart, um von der neuen Partition zu booten
108+ esp_restart ();
109+ } else {
110+ Serial.println (" OTA1 partition not found!" );
111+ }
112+ }
113+
114+ void setupDisplay () {
115+ displayEnabled = display.begin (SSD1306_SWITCHCAPVCC, 0x3D );
116+ if (displayEnabled) {
117+ display.display ();
118+ delay (100 );
119+ display.clearDisplay ();
120+ }
121+ }
122+
123+ void displayStatusBar (int progress) {
124+ display.clearDisplay ();
125+ display.setCursor (24 , 8 );
126+ display.println (" Sketch wird" );
127+ display.setCursor (22 , 22 );
128+ display.println (" hochgeladen!" );
129+
130+ display.fillRect (0 , SCREEN_HEIGHT - 24 , SCREEN_WIDTH-4 , 8 , BLACK); // Clear status bar area
131+ display.drawRect (0 , SCREEN_HEIGHT - 24 , SCREEN_WIDTH-4 , 8 , WHITE); // Draw border
132+ int filledWidth = (progress * SCREEN_WIDTH-4 ) / 100 ; // Calculate progress width
133+ display.fillRect (1 , SCREEN_HEIGHT - 23 , filledWidth-4 , 6 , WHITE); // Fill progress bar
134+
135+ display.setCursor ((SCREEN_WIDTH/2 )-12 , SCREEN_HEIGHT - 10 );
136+ display.setTextSize (1 );
137+ display.setTextColor (WHITE, BLACK);
138+ display.print (progress);
139+ display.println (" %" );
140+ display.display ();
141+ }
142+
143+
144+ void displayWelcomeScreen () {
145+ display.clearDisplay ();
146+
147+ // Draw WiFi symbol
148+ display.drawBitmap (0 , 12 , epd_bitmap_wifi, 44 , 44 , WHITE);
149+
150+ // Display SSID text
151+ display.setCursor (40 , 13 );
152+ display.setTextSize (1 );
153+ display.setTextColor (WHITE, BLACK);
154+ display.println (" Verbinde dich" );
155+ display.setCursor (60 , 27 );
156+ display.println (" mit:" );
157+
158+ // Display SSID
159+ display.setCursor (40 , 43 );
160+ display.setTextSize (1 ); // Larger text for SSID
161+ display.print (ssid);
162+
163+ display.display ();
164+ }
165+
166+ void displaySuccessScreen () {
167+ display.clearDisplay ();
168+
169+ // Draw WiFi symbol
170+ display.drawBitmap (0 , 12 , epd_bitmap_checkmark, 44 , 44 , WHITE);
171+
172+ // Display SSID text
173+ display.setCursor (48 , 22 );
174+ display.setTextSize (1 );
175+ display.setTextColor (WHITE, BLACK);
176+ display.println (" Erfolgreich" );
177+ display.setCursor (48 , 36 );
178+ display.println (" hochgeladen!" );
179+
180+ display.display ();
181+ }
182+
183+ void wipeDisplay (){
184+ display.clearDisplay ();
185+ display.println (" " );
186+ display.display ();
187+ }
188+
189+
190+
191+
192+ void setupWiFi () {
193+ WiFi.macAddress (mac);
194+ char macLastFour[5 ];
195+ snprintf (macLastFour, sizeof (macLastFour), " %02X%02X" , mac[4 ], mac[5 ]);
196+ ssid = " senseBox:" + String (macLastFour);
197+
198+ // Definiere die IP-Adresse, Gateway und Subnetzmaske
199+ IPAddress local_IP (192 , 168 , 1 , 1 ); // Die neue IP-Adresse
200+ IPAddress gateway (192 , 168 , 1 , 1 ); // Gateway-Adresse (kann gleich der IP des APs sein)
201+ IPAddress subnet (255 , 255 , 255 , 0 ); // Subnetzmaske
202+
203+ // Setze die IP-Adresse, Gateway und Subnetzmaske des Access Points
204+ WiFi.softAPConfig (local_IP, gateway, subnet);
205+
206+ // Starte den Access Point
207+ WiFi.softAP (ssid.c_str ());
208+ }
209+
210+ void setupOTA () {
211+ // Handle updating process
212+ server.on (
213+ " /sketch" , HTTP_POST, []() {
214+ server.sendHeader (" Connection" , " close" );
215+ server.send (200 , " text/plain" , (Update.hasError ()) ? " FAIL" : " OK" );
216+ ESP.restart ();
217+ },
218+ []() {
219+ HTTPUpload& upload = server.upload ();
220+
221+ if (upload.status == UPLOAD_FILE_START) {
222+ Serial.setDebugOutput (true );
223+ size_t fsize = UPDATE_SIZE_UNKNOWN;
224+ if (server.clientContentLength () > 0 ){
225+ fsize = server.clientContentLength ();
226+ }
227+ Serial.printf (" Receiving Update: %s, Size: %d\n " , upload.filename .c_str (), fsize);
228+
229+ Serial.printf (" Update: %s\n " , upload.filename .c_str ());
230+ if (!Update.begin (fsize)) { // start with max available size
231+ Update.printError (Serial);
232+ }
233+ } else if (upload.status == UPLOAD_FILE_WRITE) {
234+ /* flashing firmware to ESP*/
235+ if (Update.write (upload.buf , upload.currentSize ) != upload.currentSize ) {
236+ Update.printError (Serial);
237+ } else {
238+ int progress = (Update.progress () * 100 ) / Update.size ();
239+ displayStatusBar (progress); // Update progress on status bar
240+ }
241+ } else if (upload.status == UPLOAD_FILE_END) {
242+ if (Update.end (true )) { // true to set the size to the current progress
243+ displaySuccessScreen ();
244+ delay (3000 );
245+ wipeDisplay ();
246+ } else {
247+ Update.printError (Serial);
248+ }
249+ Serial.setDebugOutput (false );
250+ }
251+ yield ();
252+ });
253+ }
254+
255+ void setup () {
256+ // Start Serial communication
257+ Serial.begin (115200 );
258+ rgb_led_1.begin ();
259+ rgb_led_1.setBrightness (30 );
260+ rgb_led_1.setPixelColor (0 , rgb_led_1.Color (51 , 51 , 255 ));
261+ rgb_led_1.show ();
262+
263+ // Button-Pin als Input konfigurieren
264+ pinMode (BUTTON_PIN, INPUT_PULLUP);
265+
266+ // Interrupt für den Button
267+ attachInterrupt (digitalPinToInterrupt (BUTTON_PIN), handleButtonPress, FALLING);
268+
269+ #ifdef DISPLAY_ENABLED
270+ setupDisplay ();
271+ #endif
272+ setupWiFi ();
273+ // Set the ESP32 as an access point
274+ setupOTA ();
275+ server.begin ();
276+ }
277+
278+ void loop () {
279+ // Handle client requests
280+ server.handleClient ();
281+
282+ #ifdef DISPLAY_ENABLED
283+ displayWelcomeScreen ();
284+ #endif
285+
286+ if (doublePressDetected) {
287+ Serial.println (" Doppeldruck erkannt!" );
288+ setBootPartitionToOTA0 ();
289+ #ifdef DISPLAY_ENABLED
290+ display.setCursor (0 ,0 );
291+ display.setTextSize (1 );
292+ display.setTextColor (WHITE,BLACK);
293+ display.println (" " );
294+ display.display ();
295+ delay (50 );
296+ #endif
297+ // Neustart, um von der neuen Partition zu booten
298+ esp_restart ();
299+ }
300+ }
0 commit comments