add proper firmware flashing screen

This commit is contained in:
cottongin
2026-01-27 13:16:20 -05:00
parent 7349fbb208
commit a04388fd6c
8 changed files with 357 additions and 0 deletions

65
src/images/LockIcon.h Normal file
View File

@@ -0,0 +1,65 @@
#pragma once
#include <cstdint>
// Lock icon dimensions (original orientation - shackle on top)
static constexpr int LOCK_ICON_WIDTH = 32;
static constexpr int LOCK_ICON_HEIGHT = 40;
// 32x40 pixel padlock icon (1-bit bitmap, MSB first)
// 0 = black pixel, 1 = white pixel
// Original orientation: shackle on top, body below
// Use drawImageRotated() to rotate as needed for different screen orientations
static const uint8_t LockIcon[] = {
// Row 0-1: Empty space above shackle
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
// Row 2-3: Shackle top curve
0x00, 0x0F, 0xF0, 0x00, // ....####....
0x00, 0x3F, 0xFC, 0x00, // ..########..
// Row 4-5: Shackle upper sides
0x00, 0x78, 0x1E, 0x00, // .####..####.
0x00, 0xE0, 0x07, 0x00, // ###......###
// Row 6-9: Extended shackle legs (longer for better visual)
0x00, 0xC0, 0x03, 0x00, // ##........##
0x01, 0xC0, 0x03, 0x80, // ###......###
0x01, 0x80, 0x01, 0x80, // ##........##
0x01, 0x80, 0x01, 0x80, // ##........##
// Row 10-13: Shackle legs continue into body
0x01, 0x80, 0x01, 0x80, // ##........##
0x01, 0x80, 0x01, 0x80, // ##........##
0x01, 0x80, 0x01, 0x80, // ##........##
0x01, 0x80, 0x01, 0x80, // ##........##
// Row 14-15: Body top
0x0F, 0xFF, 0xFF, 0xF0, // ############
0x1F, 0xFF, 0xFF, 0xF8, // ##############
// Row 16-17: Body top edge
0x3F, 0xFF, 0xFF, 0xFC, // ################
0x3F, 0xFF, 0xFF, 0xFC, // ################
// Row 18-29: Solid body (no keyhole)
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
// Row 30-33: Body lower section
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
0x3F, 0xFF, 0xFF, 0xFC,
// Row 34-35: Body bottom edge
0x3F, 0xFF, 0xFF, 0xFC,
0x1F, 0xFF, 0xFF, 0xF8,
// Row 36-37: Body bottom
0x0F, 0xFF, 0xFF, 0xF0,
0x00, 0x00, 0x00, 0x00,
// Row 38-39: Empty space below
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};

View File

@@ -26,6 +26,7 @@
#include "activities/settings/SettingsActivity.h"
#include "activities/util/FullScreenMessageActivity.h"
#include "fontIds.h"
#include "images/LockIcon.h"
#define SPI_FQ 40000000
// Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults)
@@ -128,6 +129,118 @@ void logMemoryState(const char* tag, const char* context) {
#define logMemoryState(tag, context) ((void)0)
#endif
// Flash command detection - receives "FLASH\n" from pre_flash.py script
static String flashCmdBuffer;
void checkForFlashCommand() {
while (Serial.available()) {
char c = Serial.read();
if (c == '\n') {
if (flashCmdBuffer.startsWith("FLASH")) {
// Extract version if provided (format: "FLASH:x.y.z")
String newVersion = "";
if (flashCmdBuffer.length() > 6 && flashCmdBuffer.charAt(5) == ':') {
newVersion = flashCmdBuffer.substring(6);
}
Serial.printf("[%lu] [FLS] Flash command received (version: %s), displaying message\n", millis(),
newVersion.length() > 0 ? newVersion.c_str() : "unknown");
// Display flash message with inverted colors (black background, white text)
renderer.clearScreen(0x00); // Black background
renderer.drawCenteredText(NOTOSANS_14_FONT_ID, 200, "Flashing firmware...", false, EpdFontFamily::BOLD);
// Show new version under title if provided
if (newVersion.length() > 0) {
String versionText = "v" + newVersion;
renderer.drawCenteredText(UI_10_FONT_ID, 260, versionText.c_str(), false);
}
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Do not disconnect USB", false);
// Get screen dimensions and orientation for positioning
const int screenW = renderer.getScreenWidth();
const int screenH = renderer.getScreenHeight();
// Show current version in bottom-left corner (orientation-aware)
// "Bottom-left" is relative to the current orientation
constexpr int versionMargin = 10;
const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION);
int versionX, versionY;
switch (renderer.getOrientation()) {
case GfxRenderer::Portrait: // Bottom-left is actual bottom-left
versionX = versionMargin;
versionY = screenH - 30;
break;
case GfxRenderer::PortraitInverted: // Bottom-left is actual top-right
versionX = screenW - textWidth - versionMargin;
versionY = 20;
break;
case GfxRenderer::LandscapeClockwise: // Bottom-left is actual bottom-right
versionX = screenW - textWidth - versionMargin;
versionY = screenH - 30;
break;
case GfxRenderer::LandscapeCounterClockwise: // Bottom-left is actual bottom-left
versionX = versionMargin;
versionY = screenH - 30;
break;
}
renderer.drawText(SMALL_FONT_ID, versionX, versionY, CROSSPOINT_VERSION, false);
// Position and rotate lock icon based on current orientation (USB port location)
// USB port locations: Portrait=bottom-left, PortraitInverted=top-right,
// LandscapeCW=top-left, LandscapeCCW=bottom-right
// Position offsets: edge margin + half-width offset to center on USB port
constexpr int edgeMargin = 28; // Distance from screen edge
constexpr int halfWidth = LOCK_ICON_WIDTH / 2; // 16px offset for centering
int iconX, iconY;
GfxRenderer::ImageRotation rotation;
int outW, outH; // Note: 90/270 rotation swaps output dimensions
switch (renderer.getOrientation()) {
case GfxRenderer::Portrait: // USB at bottom-left, shackle points right
rotation = GfxRenderer::ROTATE_90;
outW = LOCK_ICON_HEIGHT;
outH = LOCK_ICON_WIDTH;
iconX = edgeMargin;
iconY = screenH - outH - edgeMargin - halfWidth;
break;
case GfxRenderer::PortraitInverted: // USB at top-right, shackle points left
rotation = GfxRenderer::ROTATE_270;
outW = LOCK_ICON_HEIGHT;
outH = LOCK_ICON_WIDTH;
iconX = screenW - outW - edgeMargin;
iconY = edgeMargin + halfWidth;
break;
case GfxRenderer::LandscapeClockwise: // USB at top-left, shackle points down
rotation = GfxRenderer::ROTATE_180;
outW = LOCK_ICON_WIDTH;
outH = LOCK_ICON_HEIGHT;
iconX = edgeMargin + halfWidth;
iconY = edgeMargin;
break;
case GfxRenderer::LandscapeCounterClockwise: // USB at bottom-right, shackle points up
rotation = GfxRenderer::ROTATE_0;
outW = LOCK_ICON_WIDTH;
outH = LOCK_ICON_HEIGHT;
iconX = screenW - outW - edgeMargin - halfWidth;
iconY = screenH - outH - edgeMargin;
break;
}
renderer.drawImageRotated(LockIcon, iconX, iconY, LOCK_ICON_WIDTH, LOCK_ICON_HEIGHT, rotation);
renderer.displayBuffer(EInkDisplay::FAST_REFRESH);
}
flashCmdBuffer = "";
} else if (c != '\r') {
flashCmdBuffer += c;
// Prevent buffer overflow from random serial data (increased for version info)
if (flashCmdBuffer.length() > 30) {
flashCmdBuffer = "";
}
}
}
}
void exitActivity() {
if (currentActivity) {
logMemoryState("MAIN", "Before onExit");
@@ -391,6 +504,9 @@ void setup() {
}
void loop() {
// Check for flash command from pre_flash.py script (must be first to catch before upload)
checkForFlashCommand();
static unsigned long maxLoopDuration = 0;
const unsigned long loopStartTime = millis();
static unsigned long lastMemPrint = 0;