Skip to content

Commit 7621079

Browse files
committed
fix(board): add APOTA to senseBox Eye
1 parent 6a33f0b commit 7621079

File tree

4 files changed

+343
-11
lines changed

4 files changed

+343
-11
lines changed

boards.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41448,7 +41448,7 @@ sensebox_eye.menu.PartitionScheme.tinyuf2=TinyUF2 Compatibility (2MB APP/12MB FF
4144841448
sensebox_eye.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader_tinyuf2
4144941449
sensebox_eye.menu.PartitionScheme.tinyuf2.build.custom_partitions=partitions_tinyuf2
4145041450
sensebox_eye.menu.PartitionScheme.tinyuf2.upload.maximum_size=2097152
41451-
sensebox_eye.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin"
41451+
sensebox_eye.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" 0x210000 "{runtime.platform.path}/variants/{build.variant}/APOTA.bin"
4145241452
sensebox_eye.menu.PartitionScheme.gen4esp32scheme4=Huge App (16MB APP)
4145341453
sensebox_eye.menu.PartitionScheme.gen4esp32scheme4.build.custom_partitions=gen4esp32_16MBapp
4145441454
sensebox_eye.menu.PartitionScheme.gen4esp32scheme4.upload.maximum_size=16646144

variants/sensebox_eye/APOTA.bin

954 KB
Binary file not shown.

variants/sensebox_eye/APOTA.ino

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
// APOTA is a arduino fallback sketch that is written to OTA1_Partition.
2+
// APOTA opens an access point which waits to receive a .binfile on /sketch.
3+
// After succesful upload, the file is written to OTA0_Partition and the microcontroller reboots to the newly uploaded sketch.
4+
5+
#define DISPLAY_ENABLED
6+
7+
#include <WiFi.h>
8+
#include <WebServer.h>
9+
#include <ESPmDNS.h>
10+
#include <WiFiAP.h>
11+
#include <Update.h>
12+
#include <Wire.h>
13+
#ifdef DISPLAY_ENABLED
14+
#define SCREEN_WIDTH 128
15+
#define SCREEN_HEIGHT 64
16+
#define OLED_RESET -1
17+
#include <Adafruit_GFX.h>
18+
#include <Adafruit_SSD1306.h>
19+
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
20+
#include <Adafruit_NeoPixel.h>
21+
Adafruit_NeoPixel rgb_led_1 = Adafruit_NeoPixel(1, 1, NEO_GRB + NEO_KHZ800);
22+
23+
#endif
24+
#include "esp_partition.h"
25+
#include "esp_ota_ops.h"
26+
#include "esp_system.h"
27+
28+
String ssid;
29+
uint8_t mac[6];
30+
31+
// Create an instance of the server
32+
WebServer server(80);
33+
bool displayEnabled;
34+
35+
const int BUTTON_PIN = 0; // GPIO for the button
36+
volatile unsigned long lastPressTime = 0; // Time of last button press
37+
volatile bool doublePressDetected = false; // Flag for double press
38+
const unsigned long doublePressInterval = 500; // Max. time (in ms) between two presses for double press
39+
volatile int pressCount = 0; // Counts the button presses
40+
41+
const unsigned char epd_bitmap_wifi[] PROGMEM = {
42+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43+
0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0xff, 0x00, 0x00,
44+
0x00, 0x3f, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x7c, 0x00, 0x03, 0xe0, 0x00, 0x00, 0xf0, 0x00, 0x01, 0xf0, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x78, 0x00,
45+
0x03, 0xc0, 0x00, 0x00, 0x38, 0x00, 0x07, 0x80, 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, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00,
47+
0x00, 0x1c, 0x00, 0x07, 0x80, 0x00, 0x00, 0x38, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x70, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x70, 0x00, 0x00, 0xc0, 0x00,
48+
0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x00,
49+
0x00, 0x01, 0xe0, 0xf0, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x78, 0x00, 0x00, 0x00, 0x03, 0x80, 0x38, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00,
50+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
51+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
52+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
53+
};
54+
55+
// 'checkmark', 44x44px
56+
const unsigned char epd_bitmap_checkmark[] PROGMEM = {
57+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60+
0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00,
61+
0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xf0, 0x00, 0x00,
62+
0x00, 0x0f, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x0f, 0x83, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xc7, 0x80, 0x00, 0x00, 0x00, 0x03, 0xef, 0x00, 0x00, 0x00,
63+
0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
64+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
68+
};
69+
70+
void IRAM_ATTR handleButtonPress() {
71+
unsigned long currentTime = millis(); // Get current time
72+
73+
// Debounce: If the current press is too close to the last one, ignore it
74+
if (currentTime - lastPressTime > 50) {
75+
pressCount++; // Count the button press
76+
77+
// Check if this is the second press within the double-press interval
78+
if (pressCount == 2 && (currentTime - lastPressTime <= doublePressInterval)) {
79+
doublePressDetected = true; // Double press detected
80+
pressCount = 0; // Reset counter
81+
}
82+
83+
lastPressTime = currentTime; // Update the time of the last press
84+
}
85+
}
86+
87+
// Function to switch the boot partition to OTA1
88+
void setBootPartitionToOTA0() {
89+
const esp_partition_t *ota0_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL);
90+
91+
if (ota0_partition) {
92+
// Set OTA1 as new boot partition
93+
esp_ota_set_boot_partition(ota0_partition);
94+
Serial.println("Boot partition changed to OTA0. Restarting...");
95+
96+
// Restart to boot from the new partition
97+
esp_restart();
98+
} else {
99+
Serial.println("OTA1 partition not found!");
100+
}
101+
}
102+
103+
void setupDisplay() {
104+
displayEnabled = display.begin(SSD1306_SWITCHCAPVCC, 0x3D);
105+
if (displayEnabled) {
106+
display.display();
107+
delay(100);
108+
display.clearDisplay();
109+
}
110+
}
111+
112+
void displayStatusBar(int progress) {
113+
display.clearDisplay();
114+
display.setCursor(24, 8);
115+
display.println("Sketch wird");
116+
display.setCursor(22, 22);
117+
display.println("hochgeladen!");
118+
119+
display.fillRect(0, SCREEN_HEIGHT - 24, SCREEN_WIDTH - 4, 8, BLACK); // Clear status bar area
120+
display.drawRect(0, SCREEN_HEIGHT - 24, SCREEN_WIDTH - 4, 8, WHITE); // Draw border
121+
int filledWidth = (progress * SCREEN_WIDTH - 4) / 100; // Calculate progress width
122+
display.fillRect(1, SCREEN_HEIGHT - 23, filledWidth - 4, 6, WHITE); // Fill progress bar
123+
124+
display.setCursor((SCREEN_WIDTH / 2) - 12, SCREEN_HEIGHT - 10);
125+
display.setTextSize(1);
126+
display.setTextColor(WHITE, BLACK);
127+
display.print(progress);
128+
display.println(" %");
129+
display.display();
130+
}
131+
132+
void displayWelcomeScreen() {
133+
display.clearDisplay();
134+
135+
// Draw WiFi symbol
136+
display.drawBitmap(0, 12, epd_bitmap_wifi, 44, 44, WHITE);
137+
138+
// Display SSID text
139+
display.setCursor(40, 13);
140+
display.setTextSize(1);
141+
display.setTextColor(WHITE, BLACK);
142+
display.println("Verbinde dich"); // "Connect"
143+
display.setCursor(60, 27);
144+
display.println("mit:"); // "with"
145+
146+
// Display SSID
147+
display.setCursor(40, 43);
148+
display.setTextSize(1); // Larger text for SSID
149+
display.print(ssid);
150+
151+
display.display();
152+
}
153+
154+
void displaySuccessScreen() {
155+
display.clearDisplay();
156+
157+
// Draw WiFi symbol
158+
display.drawBitmap(0, 12, epd_bitmap_checkmark, 44, 44, WHITE);
159+
160+
// Display SSID text
161+
display.setCursor(48, 22);
162+
display.setTextSize(1);
163+
display.setTextColor(WHITE, BLACK);
164+
display.println("Erfolgreich"); // "Successfully"
165+
display.setCursor(48, 36);
166+
display.println("hochgeladen!"); // "uploaded!"
167+
168+
display.display();
169+
}
170+
171+
void wipeDisplay() {
172+
display.clearDisplay();
173+
display.println("");
174+
display.display();
175+
}
176+
177+
void setupWiFi() {
178+
WiFi.macAddress(mac);
179+
char macLastFour[5];
180+
snprintf(macLastFour, sizeof(macLastFour), "%02X%02X", mac[4], mac[5]);
181+
ssid = "senseBox:" + String(macLastFour);
182+
183+
// Define the IP address, gateway, and subnet mask
184+
IPAddress local_IP(192, 168, 1, 1); // The new IP address
185+
IPAddress gateway(192, 168, 1, 1); // Gateway address (can be the same as the AP's IP)
186+
IPAddress subnet(255, 255, 255, 0); // Subnet mask
187+
188+
// Set the IP address, gateway, and subnet mask of the access point
189+
WiFi.softAPConfig(local_IP, gateway, subnet);
190+
191+
// Start the access point
192+
WiFi.softAP(ssid.c_str());
193+
}
194+
195+
void setupOTA() {
196+
// Handle updating process
197+
server.on(
198+
"/sketch", HTTP_POST,
199+
[]() {
200+
server.sendHeader("Connection", "close");
201+
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
202+
ESP.restart();
203+
},
204+
[]() {
205+
HTTPUpload &upload = server.upload();
206+
207+
if (upload.status == UPLOAD_FILE_START) {
208+
Serial.setDebugOutput(true);
209+
size_t fsize = UPDATE_SIZE_UNKNOWN;
210+
if (server.clientContentLength() > 0) {
211+
fsize = server.clientContentLength();
212+
}
213+
Serial.printf("Receiving Update: %s, Size: %d\n", upload.filename.c_str(), fsize);
214+
215+
Serial.printf("Update: %s\n", upload.filename.c_str());
216+
if (!Update.begin(fsize)) { //start with max available size
217+
Update.printError(Serial);
218+
}
219+
} else if (upload.status == UPLOAD_FILE_WRITE) {
220+
/* flashing firmware to ESP*/
221+
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
222+
Update.printError(Serial);
223+
} else {
224+
int progress = (Update.progress() * 100) / Update.size();
225+
displayStatusBar(progress); // Update progress on status bar
226+
}
227+
} else if (upload.status == UPLOAD_FILE_END) {
228+
if (Update.end(true)) { //true to set the size to the current progress
229+
displaySuccessScreen();
230+
delay(3000);
231+
wipeDisplay();
232+
} else {
233+
Update.printError(Serial);
234+
}
235+
Serial.setDebugOutput(false);
236+
}
237+
yield();
238+
}
239+
);
240+
}
241+
242+
void setup() {
243+
// Start Serial communication
244+
Serial.begin(115200);
245+
rgb_led_1.begin();
246+
rgb_led_1.setBrightness(30);
247+
rgb_led_1.setPixelColor(0, rgb_led_1.Color(51, 51, 255));
248+
rgb_led_1.show();
249+
250+
// Configure button pin as input
251+
pinMode(BUTTON_PIN, INPUT_PULLUP);
252+
253+
// Interrupt for the button
254+
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), handleButtonPress, FALLING);
255+
256+
#ifdef DISPLAY_ENABLED
257+
setupDisplay();
258+
#endif
259+
setupWiFi();
260+
// Set the ESP32 as an access point
261+
setupOTA();
262+
server.begin();
263+
}
264+
265+
void loop() {
266+
// Handle client requests
267+
server.handleClient();
268+
269+
#ifdef DISPLAY_ENABLED
270+
displayWelcomeScreen();
271+
#endif
272+
273+
if (doublePressDetected) {
274+
Serial.println("Doppeldruck erkannt!"); // "Double press detected!"
275+
setBootPartitionToOTA0();
276+
#ifdef DISPLAY_ENABLED
277+
display.setCursor(0, 0);
278+
display.setTextSize(1);
279+
display.setTextColor(WHITE, BLACK);
280+
display.println("");
281+
display.display();
282+
delay(50);
283+
#endif
284+
// Restart to boot from the new partition
285+
esp_restart();
286+
}
287+
}

variants/sensebox_eye/variant.cpp

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,25 @@
2626
#include "pins_arduino.h"
2727
#include "driver/rmt_tx.h"
2828
#include "esp_log.h"
29+
#include "esp_partition.h"
30+
#include "esp_system.h"
31+
#include "esp_ota_ops.h"
2932

3033
extern "C" {
3134

35+
void blinkLED(uint8_t color[3], rmt_channel_handle_t led_chan, rmt_encoder_handle_t ws2812_encoder, rmt_transmit_config_t tx_config) {
36+
ESP_ERROR_CHECK(rmt_transmit(led_chan, ws2812_encoder, color, sizeof(color), &tx_config));
37+
rmt_tx_wait_all_done(led_chan, portMAX_DELAY);
38+
39+
// Wait a moment
40+
delay(50);
41+
42+
// Turn LED off
43+
uint8_t pixel_off[3] = { 0x00, 0x00, 0x00 };
44+
ESP_ERROR_CHECK(rmt_transmit(led_chan, ws2812_encoder, pixel_off, sizeof(pixel_off), &tx_config));
45+
rmt_tx_wait_all_done(led_chan, portMAX_DELAY);
46+
}
47+
3248
void initVariant(void) {
3349
rmt_channel_handle_t led_chan = NULL;
3450
rmt_tx_channel_config_t tx_chan_config = {};
@@ -52,20 +68,49 @@ void initVariant(void) {
5268

5369
ESP_ERROR_CHECK(rmt_enable(led_chan));
5470

55-
// WS2812 GRB data (green pixel)
56-
uint8_t pixel[3] = { 0x10, 0x00, 0x00 }; // green
5771
rmt_transmit_config_t tx_config = {
5872
.loop_count = 0
5973
};
60-
ESP_ERROR_CHECK(rmt_transmit(led_chan, ws2812_encoder, pixel, sizeof(pixel), &tx_config));
61-
rmt_tx_wait_all_done(led_chan, portMAX_DELAY);
6274

63-
// Wait a moment
64-
delay(50);
75+
uint8_t pixel[3] = { 0x10, 0x00, 0x00 }; // green
76+
blinkLED(pixel, led_chan, ws2812_encoder, tx_config);
6577

66-
// Turn LED off
67-
uint8_t pixel_off[3] = { 0x00, 0x00, 0x00 };
68-
ESP_ERROR_CHECK(rmt_transmit(led_chan, ws2812_encoder, pixel_off, sizeof(pixel_off), &tx_config));
69-
rmt_tx_wait_all_done(led_chan, portMAX_DELAY);
78+
// define button pin
79+
pinMode(0, INPUT_PULLUP);
80+
81+
// keep button pressed
82+
unsigned long pressStartTime = 0;
83+
bool buttonPressed = false;
84+
85+
// Wait 3.5 seconds for the button to be pressed
86+
unsigned long startTime = millis();
87+
88+
// Check if button is pressed
89+
while (millis() - startTime < 3500) {
90+
if (digitalRead(0) == LOW) {
91+
if (!buttonPressed) {
92+
// The button was pressed
93+
buttonPressed = true;
94+
}
95+
} else if (buttonPressed) {
96+
// When the button is pressed and then released, boot into the OTA1 partition
97+
const esp_partition_t *ota1_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, NULL);
98+
99+
if (ota1_partition) {
100+
esp_err_t err = esp_ota_set_boot_partition(ota1_partition);
101+
if (err == ESP_OK) {
102+
uint8_t pixel[3] = { 0x00, 0x00, 0x10 }; // blue
103+
blinkLED(pixel, led_chan, ws2812_encoder, tx_config);
104+
esp_restart(); // restart, to boot OTA1 partition
105+
} else {
106+
uint8_t pixel[3] = { 0x00, 0x10, 0x00 }; // red
107+
blinkLED(pixel, led_chan, ws2812_encoder, tx_config);
108+
ESP_LOGE("OTA", "Error setting OTA1 partition: %s", esp_err_to_name(err));
109+
}
110+
}
111+
// Abort after releasing the button
112+
break;
113+
}
114+
}
70115
}
71116
}

0 commit comments

Comments
 (0)