Traduction initiale

This commit is contained in:
bean 2026-01-10 01:16:39 -05:00
parent d4ae108d9b
commit 624586ec03
15 changed files with 239 additions and 239 deletions

View File

@ -14,7 +14,7 @@ void BootActivity::onEnter() {
renderer.clearScreen();
renderer.drawImage(CrossLarge, (pageWidth + 128) / 2, (pageHeight - 128) / 2, 128, 128);
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "BOOTING");
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "DÉMARRAGE");
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, CROSSPOINT_VERSION);
renderer.displayBuffer();
}

View File

@ -13,7 +13,7 @@
void SleepActivity::onEnter() {
Activity::onEnter();
renderPopup("Entering Sleep...");
renderPopup("Mise en veille...");
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::BLANK) {
return renderBlankSleepScreen();
@ -64,13 +64,13 @@ void SleepActivity::renderCustomSleepScreen() const {
}
if (filename.substr(filename.length() - 4) != ".bmp") {
Serial.printf("[%lu] [SLP] Skipping non-.bmp file name: %s\n", millis(), name);
Serial.printf("[%lu] [SLP] Ignorer les noms de fichiers non-.bmp: %s\n", millis(), name);
file.close();
continue;
}
Bitmap bitmap(file);
if (bitmap.parseHeaders() != BmpReaderError::Ok) {
Serial.printf("[%lu] [SLP] Skipping invalid BMP file: %s\n", millis(), name);
Serial.printf("[%lu] [SLP] Ignorer le fichier BMP invalide: %s\n", millis(), name);
file.close();
continue;
}
@ -84,7 +84,7 @@ void SleepActivity::renderCustomSleepScreen() const {
const auto filename = "/sleep/" + files[randomFileIndex];
FsFile file;
if (SdMan.openFileForRead("SLP", filename, file)) {
Serial.printf("[%lu] [SLP] Randomly loading: /sleep/%s\n", millis(), files[randomFileIndex].c_str());
Serial.printf("[%lu] [SLP] Chargement aléatoire: /sleep/%s\n", millis(), files[randomFileIndex].c_str());
delay(100);
Bitmap bitmap(file);
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
@ -103,7 +103,7 @@ void SleepActivity::renderCustomSleepScreen() const {
if (SdMan.openFileForRead("SLP", "/sleep.bmp", file)) {
Bitmap bitmap(file);
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
Serial.printf("[%lu] [SLP] Loading: /sleep.bmp\n", millis());
Serial.printf("[%lu] [SLP] Chargement: /sleep.bmp\n", millis());
renderBitmapSleepScreen(bitmap);
return;
}
@ -119,7 +119,7 @@ void SleepActivity::renderDefaultSleepScreen() const {
renderer.clearScreen();
renderer.drawImage(CrossLarge, (pageWidth + 128) / 2, (pageHeight - 128) / 2, 128, 128);
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "SLEEPING");
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "EN VEILLE");
// Make sleep screen dark unless light is selected in settings
if (SETTINGS.sleepScreen != CrossPointSettings::SLEEP_SCREEN_MODE::LIGHT) {
@ -135,34 +135,34 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const {
const auto pageHeight = renderer.getScreenHeight();
float cropX = 0, cropY = 0;
Serial.printf("[%lu] [SLP] bitmap %d x %d, screen %d x %d\n", millis(), bitmap.getWidth(), bitmap.getHeight(),
Serial.printf("[%lu] [SLP] bitmap %d x %d, écran %d x %d\n", millis(), bitmap.getWidth(), bitmap.getHeight(),
pageWidth, pageHeight);
if (bitmap.getWidth() > pageWidth || bitmap.getHeight() > pageHeight) {
// image will scale, make sure placement is right
float ratio = static_cast<float>(bitmap.getWidth()) / static_cast<float>(bitmap.getHeight());
const float screenRatio = static_cast<float>(pageWidth) / static_cast<float>(pageHeight);
Serial.printf("[%lu] [SLP] bitmap ratio: %f, screen ratio: %f\n", millis(), ratio, screenRatio);
Serial.printf("[%lu] [SLP] ratio bitmap: %f, ratio écran: %f\n", millis(), ratio, screenRatio);
if (ratio > screenRatio) {
// image wider than viewport ratio, scaled down image needs to be centered vertically
if (SETTINGS.sleepScreenCoverMode == CrossPointSettings::SLEEP_SCREEN_COVER_MODE::CROP) {
cropX = 1.0f - (screenRatio / ratio);
Serial.printf("[%lu] [SLP] Cropping bitmap x: %f\n", millis(), cropX);
Serial.printf("[%lu] [SLP] Recadrage de bitmap x: %f\n", millis(), cropX);
ratio = (1.0f - cropX) * static_cast<float>(bitmap.getWidth()) / static_cast<float>(bitmap.getHeight());
}
x = 0;
y = std::round((static_cast<float>(pageHeight) - static_cast<float>(pageWidth) / ratio) / 2);
Serial.printf("[%lu] [SLP] Centering with ratio %f to y=%d\n", millis(), ratio, y);
Serial.printf("[%lu] [SLP] Centrage avec ratio de %f à y=%d\n", millis(), ratio, y);
} else {
// image taller than viewport ratio, scaled down image needs to be centered horizontally
if (SETTINGS.sleepScreenCoverMode == CrossPointSettings::SLEEP_SCREEN_COVER_MODE::CROP) {
cropY = 1.0f - (ratio / screenRatio);
Serial.printf("[%lu] [SLP] Cropping bitmap y: %f\n", millis(), cropY);
Serial.printf("[%lu] [SLP] Recadrage bitmap y: %f\n", millis(), cropY);
ratio = static_cast<float>(bitmap.getWidth()) / ((1.0f - cropY) * static_cast<float>(bitmap.getHeight()));
}
x = std::round((pageWidth - pageHeight * ratio) / 2);
y = 0;
Serial.printf("[%lu] [SLP] Centering with ratio %f to x=%d\n", millis(), ratio, x);
Serial.printf("[%lu] [SLP] Centrage avec ratio de %f à x=%d\n", millis(), ratio, x);
}
} else {
// center the image
@ -170,7 +170,7 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const {
y = (pageHeight - bitmap.getHeight()) / 2;
}
Serial.printf("[%lu] [SLP] drawing to %d x %d\n", millis(), x, y);
Serial.printf("[%lu] [SLP] dessinage à %d x %d\n", millis(), x, y);
renderer.clearScreen();
renderer.drawBitmap(bitmap, x, y, pageWidth, pageHeight, cropX, cropY);
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
@ -205,12 +205,12 @@ void SleepActivity::renderCoverSleepScreen() const {
// Handle XTC file
Xtc lastXtc(APP_STATE.openEpubPath, "/.crosspoint");
if (!lastXtc.load()) {
Serial.println("[SLP] Failed to load last XTC");
Serial.println("[SLP] Chargement échoué du dernier XTC");
return renderDefaultSleepScreen();
}
if (!lastXtc.generateCoverBmp()) {
Serial.println("[SLP] Failed to generate XTC cover bmp");
Serial.println("[SLP] Génération échoué de bmp de couverture XTC");
return renderDefaultSleepScreen();
}
@ -219,12 +219,12 @@ void SleepActivity::renderCoverSleepScreen() const {
// Handle EPUB file
Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint");
if (!lastEpub.load()) {
Serial.println("[SLP] Failed to load last epub");
Serial.println("[SLP] Chargement échoué du dernier epub");
return renderDefaultSleepScreen();
}
if (!lastEpub.generateCoverBmp()) {
Serial.println("[SLP] Failed to generate cover bmp");
Serial.println("[SLP] Génération échoué de bmp de couverture");
return renderDefaultSleepScreen();
}

View File

@ -34,7 +34,7 @@ void OpdsBookBrowserActivity::onEnter() {
currentPath = OPDS_ROOT_PATH;
selectorIndex = 0;
errorMessage.clear();
statusMessage = "Checking WiFi...";
statusMessage = "Verification du WiFi...";
updateRequired = true;
xTaskCreate(&OpdsBookBrowserActivity::taskTrampoline, "OpdsBookBrowserTask",
@ -70,7 +70,7 @@ void OpdsBookBrowserActivity::loop() {
if (state == BrowserState::ERROR) {
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
state = BrowserState::LOADING;
statusMessage = "Loading...";
statusMessage = "Chargement...";
updateRequired = true;
fetchFeed(currentPath);
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
@ -155,11 +155,11 @@ void OpdsBookBrowserActivity::render() const {
const auto pageWidth = renderer.getScreenWidth();
const auto pageHeight = renderer.getScreenHeight();
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Calibre Library", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Bilbiothèque Calibre", true, EpdFontFamily::BOLD);
if (state == BrowserState::CHECK_WIFI) {
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, statusMessage.c_str());
const auto labels = mappedInput.mapLabels("« Back", "", "", "");
const auto labels = mappedInput.mapLabels("« Retour", "", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();
return;
@ -167,23 +167,23 @@ void OpdsBookBrowserActivity::render() const {
if (state == BrowserState::LOADING) {
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, statusMessage.c_str());
const auto labels = mappedInput.mapLabels("« Back", "", "", "");
const auto labels = mappedInput.mapLabels("« Retour", "", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();
return;
}
if (state == BrowserState::ERROR) {
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 20, "Error:");
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 20, "Erreur:");
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, errorMessage.c_str());
const auto labels = mappedInput.mapLabels("« Back", "Retry", "", "");
const auto labels = mappedInput.mapLabels("« Retour", "Reéssay.", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();
return;
}
if (state == BrowserState::DOWNLOADING) {
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 40, "Downloading...");
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 40, "Téléchargement...");
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 10, statusMessage.c_str());
if (downloadTotal > 0) {
const int barWidth = pageWidth - 100;
@ -198,15 +198,15 @@ void OpdsBookBrowserActivity::render() const {
// Browsing state
// Show appropriate button hint based on selected entry type
const char* confirmLabel = "Open";
const char* confirmLabel = "Ouvrir";
if (!entries.empty() && entries[selectorIndex].type == OpdsEntryType::BOOK) {
confirmLabel = "Download";
confirmLabel = "Télécharger";
}
const auto labels = mappedInput.mapLabels("« Back", confirmLabel, "", "");
const auto labels = mappedInput.mapLabels("« Retour", confirmLabel, "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
if (entries.empty()) {
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, "No entries found");
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, "Aucune entrée trouvée");
renderer.displayBuffer();
return;
}
@ -241,18 +241,18 @@ void OpdsBookBrowserActivity::fetchFeed(const std::string& path) {
const char* serverUrl = SETTINGS.opdsServerUrl;
if (strlen(serverUrl) == 0) {
state = BrowserState::ERROR;
errorMessage = "No server URL configured";
errorMessage = "Aucun URL serveur configuré";
updateRequired = true;
return;
}
std::string url = UrlUtils::buildUrl(serverUrl, path);
Serial.printf("[%lu] [OPDS] Fetching: %s\n", millis(), url.c_str());
Serial.printf("[%lu] [OPDS] Récupération: %s\n", millis(), url.c_str());
std::string content;
if (!HttpDownloader::fetchUrl(url, content)) {
state = BrowserState::ERROR;
errorMessage = "Failed to fetch feed";
errorMessage = "Récupération du flux échouée";
updateRequired = true;
return;
}
@ -260,7 +260,7 @@ void OpdsBookBrowserActivity::fetchFeed(const std::string& path) {
OpdsParser parser;
if (!parser.parse(content.c_str(), content.size())) {
state = BrowserState::ERROR;
errorMessage = "Failed to parse feed";
errorMessage = "Analyse du flux échouée";
updateRequired = true;
return;
}
@ -270,7 +270,7 @@ void OpdsBookBrowserActivity::fetchFeed(const std::string& path) {
if (entries.empty()) {
state = BrowserState::ERROR;
errorMessage = "No entries found";
errorMessage = "Aucune entrée trouvée";
updateRequired = true;
return;
}
@ -285,7 +285,7 @@ void OpdsBookBrowserActivity::navigateToEntry(const OpdsEntry& entry) {
currentPath = entry.href;
state = BrowserState::LOADING;
statusMessage = "Loading...";
statusMessage = "Chargement...";
entries.clear();
selectorIndex = 0;
updateRequired = true;
@ -303,7 +303,7 @@ void OpdsBookBrowserActivity::navigateBack() {
navigationHistory.pop_back();
state = BrowserState::LOADING;
statusMessage = "Loading...";
statusMessage = "Chargement...";
entries.clear();
selectorIndex = 0;
updateRequired = true;
@ -329,7 +329,7 @@ void OpdsBookBrowserActivity::downloadBook(const OpdsEntry& book) {
}
std::string filename = "/" + StringUtils::sanitizeFilename(baseName) + ".epub";
Serial.printf("[%lu] [OPDS] Downloading: %s -> %s\n", millis(), downloadUrl.c_str(), filename.c_str());
Serial.printf("[%lu] [OPDS] Téléchargement: %s -> %s\n", millis(), downloadUrl.c_str(), filename.c_str());
const auto result =
HttpDownloader::downloadToFile(downloadUrl, filename, [this](const size_t downloaded, const size_t total) {
@ -339,12 +339,12 @@ void OpdsBookBrowserActivity::downloadBook(const OpdsEntry& book) {
});
if (result == HttpDownloader::OK) {
Serial.printf("[%lu] [OPDS] Download complete: %s\n", millis(), filename.c_str());
Serial.printf("[%lu] [OPDS] Téléchargement terminé: %s\n", millis(), filename.c_str());
state = BrowserState::BROWSING;
updateRequired = true;
} else {
state = BrowserState::ERROR;
errorMessage = "Download failed";
errorMessage = "Téléchargement échoué";
updateRequired = true;
}
}
@ -353,21 +353,21 @@ void OpdsBookBrowserActivity::checkAndConnectWifi() {
// Already connected?
if (WiFi.status() == WL_CONNECTED) {
state = BrowserState::LOADING;
statusMessage = "Loading...";
statusMessage = "Chargement...";
updateRequired = true;
fetchFeed(currentPath);
return;
}
// Try to connect using saved credentials
statusMessage = "Connecting to WiFi...";
statusMessage = "Connexion au WiFi...";
updateRequired = true;
WIFI_STORE.loadFromFile();
const auto& credentials = WIFI_STORE.getCredentials();
if (credentials.empty()) {
state = BrowserState::ERROR;
errorMessage = "No WiFi credentials saved";
errorMessage = "Aucun identifiants sauvegardés";
updateRequired = true;
return;
}
@ -385,14 +385,14 @@ void OpdsBookBrowserActivity::checkAndConnectWifi() {
}
if (WiFi.status() == WL_CONNECTED) {
Serial.printf("[%lu] [OPDS] WiFi connected: %s\n", millis(), WiFi.localIP().toString().c_str());
Serial.printf("[%lu] [OPDS] WiFi connecté: %s\n", millis(), WiFi.localIP().toString().c_str());
state = BrowserState::LOADING;
statusMessage = "Loading...";
statusMessage = "Chargement...";
updateRequired = true;
fetchFeed(currentPath);
} else {
state = BrowserState::ERROR;
errorMessage = "WiFi connection failed";
errorMessage = "Connexion WiFi échouée";
updateRequired = true;
}
}

View File

@ -277,21 +277,21 @@ void HomeActivity::render() const {
}
renderer.drawCenteredText(UI_10_FONT_ID, bookY + bookHeight - renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2,
"Continue Reading", !bookSelected);
"Continuer la lecture", !bookSelected);
} else {
// No book to continue reading
const int y =
bookY + (bookHeight - renderer.getLineHeight(UI_12_FONT_ID) - renderer.getLineHeight(UI_10_FONT_ID)) / 2;
renderer.drawCenteredText(UI_12_FONT_ID, y, "No open book");
renderer.drawCenteredText(UI_10_FONT_ID, y + renderer.getLineHeight(UI_12_FONT_ID), "Start reading below");
renderer.drawCenteredText(UI_12_FONT_ID, y, "Aucun livre ouvert");
renderer.drawCenteredText(UI_10_FONT_ID, y + renderer.getLineHeight(UI_12_FONT_ID), "Commencer la lecture au-dessous");
}
// --- Bottom menu tiles ---
// Build menu items dynamically
std::vector<const char*> menuItems = {"Browse Files", "File Transfer", "Settings"};
std::vector<const char*> menuItems = {"Parcourir les fichiers", "Transfert de fichiers", "Paramètres"};
if (hasOpdsUrl) {
// Insert Calibre Library after Browse Files
menuItems.insert(menuItems.begin() + 1, "Calibre Library");
menuItems.insert(menuItems.begin() + 1, "Blibliothèque Calibre");
}
const int menuTileWidth = pageWidth - 2 * margin;
@ -329,7 +329,7 @@ void HomeActivity::render() const {
renderer.drawText(UI_10_FONT_ID, textX, textY, label, !selected);
}
const auto labels = mappedInput.mapLabels("", "Confirm", "Up", "Down");
const auto labels = mappedInput.mapLabels("", "Confirmer", "Haut", "Bas");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
const auto batteryX = pageWidth - 25 - renderer.getTextWidth(SMALL_FONT_ID, "100 %");

View File

@ -34,7 +34,7 @@ void CalibreWirelessActivity::onEnter() {
stateMutex = xSemaphoreCreateMutex();
state = WirelessState::DISCOVERING;
statusMessage = "Discovering Calibre...";
statusMessage = "Recherche de Calibre...";
errorMessage.clear();
calibreHostname.clear();
calibreHost.clear();

View File

@ -15,7 +15,7 @@
namespace {
// AP Mode configuration
constexpr const char* AP_SSID = "CrossPoint-Reader";
constexpr const char* AP_SSID = "CrossPoint-Liseuse";
constexpr const char* AP_PASSWORD = nullptr; // Open network for ease of use
constexpr const char* AP_HOSTNAME = "crosspoint";
constexpr uint8_t AP_CHANNEL = 1;
@ -34,7 +34,7 @@ void CrossPointWebServerActivity::taskTrampoline(void* param) {
void CrossPointWebServerActivity::onEnter() {
ActivityWithSubactivity::onEnter();
Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onEnter: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] [MEM] Pile libre à onEnter: %d octets\n", millis(), ESP.getFreeHeap());
renderingMutex = xSemaphoreCreateMutex();
@ -55,7 +55,7 @@ void CrossPointWebServerActivity::onEnter() {
);
// Launch network mode selection subactivity
Serial.printf("[%lu] [WEBACT] Launching NetworkModeSelectionActivity...\n", millis());
Serial.printf("[%lu] [WEBACT] Lancement de NetworkModeSelectionActivity...\n", millis());
enterNewActivity(new NetworkModeSelectionActivity(
renderer, mappedInput, [this](const NetworkMode mode) { onNetworkModeSelected(mode); },
[this]() { onGoBack(); } // Cancel goes back to home
@ -65,7 +65,7 @@ void CrossPointWebServerActivity::onEnter() {
void CrossPointWebServerActivity::onExit() {
ActivityWithSubactivity::onExit();
Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit start: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] [MEM] Pile libre au début de onExit: %d bytes\n", millis(), ESP.getFreeHeap());
state = WebServerActivityState::SHUTTING_DOWN;
@ -77,56 +77,56 @@ void CrossPointWebServerActivity::onExit() {
// Stop DNS server if running (AP mode)
if (dnsServer) {
Serial.printf("[%lu] [WEBACT] Stopping DNS server...\n", millis());
Serial.printf("[%lu] [WEBACT] Arrêt du serveur DNS...\n", millis());
dnsServer->stop();
delete dnsServer;
dnsServer = nullptr;
}
// CRITICAL: Wait for LWIP stack to flush any pending packets
Serial.printf("[%lu] [WEBACT] Waiting 500ms for network stack to flush pending packets...\n", millis());
Serial.printf("[%lu] [WEBACT] Attente de 500ms de pile réseau pour vider les paquets en attente...\n", millis());
delay(500);
// Disconnect WiFi gracefully
if (isApMode) {
Serial.printf("[%lu] [WEBACT] Stopping WiFi AP...\n", millis());
Serial.printf("[%lu] [WEBACT] Arrêt du WiFi AP...\n", millis());
WiFi.softAPdisconnect(true);
} else {
Serial.printf("[%lu] [WEBACT] Disconnecting WiFi (graceful)...\n", millis());
Serial.printf("[%lu] [WEBACT] Déconnexion du WiFi (gracieuse)...\n", millis());
WiFi.disconnect(false); // false = don't erase credentials, send disconnect frame
}
delay(100); // Allow disconnect frame to be sent
Serial.printf("[%lu] [WEBACT] Setting WiFi mode OFF...\n", millis());
Serial.printf("[%lu] [WEBACT] Désactivation du WiFi...\n", millis());
WiFi.mode(WIFI_OFF);
delay(100); // Allow WiFi hardware to fully power down
Serial.printf("[%lu] [WEBACT] [MEM] Free heap after WiFi disconnect: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] [MEM] Pile libre après déconnexion du WiFi: %d octets\n", millis(), ESP.getFreeHeap());
// Acquire mutex before deleting task
Serial.printf("[%lu] [WEBACT] Acquiring rendering mutex before task deletion...\n", millis());
Serial.printf("[%lu] [WEBACT] Acquisition du mutex de rendu avant la suppression de la tâche...\n", millis());
xSemaphoreTake(renderingMutex, portMAX_DELAY);
// Delete the display task
Serial.printf("[%lu] [WEBACT] Deleting display task...\n", millis());
Serial.printf("[%lu] [WEBACT] Suppression de la tâche d'affichage...\n", millis());
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
Serial.printf("[%lu] [WEBACT] Display task deleted\n", millis());
Serial.printf("[%lu] [WEBACT] Tâche d'affichage supprimée\n", millis());
}
// Delete the mutex
Serial.printf("[%lu] [WEBACT] Deleting mutex...\n", millis());
Serial.printf("[%lu] [WEBACT] Suppression du mutex...\n", millis());
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
Serial.printf("[%lu] [WEBACT] Mutex deleted\n", millis());
Serial.printf("[%lu] [WEBACT] Mutex supprimé\n", millis());
Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit end: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] [MEM] Pile libre à la fin de onExit: %d octets\n", millis(), ESP.getFreeHeap());
}
void CrossPointWebServerActivity::onNetworkModeSelected(const NetworkMode mode) {
Serial.printf("[%lu] [WEBACT] Network mode selected: %s\n", millis(),
mode == NetworkMode::JOIN_NETWORK ? "Join Network" : "Create Hotspot");
Serial.printf("[%lu] [WEBACT] Mode de réseau selectionné: %s\n", millis(),
mode == NetworkMode::JOIN_NETWORK ? "Réjoindre le réseau" : "Créer un point d'accès");
networkMode = mode;
isApMode = (mode == NetworkMode::CREATE_HOTSPOT);
@ -136,11 +136,11 @@ void CrossPointWebServerActivity::onNetworkModeSelected(const NetworkMode mode)
if (mode == NetworkMode::JOIN_NETWORK) {
// STA mode - launch WiFi selection
Serial.printf("[%lu] [WEBACT] Turning on WiFi (STA mode)...\n", millis());
Serial.printf("[%lu] [WEBACT] Lancement du WiFi (Mode STA)...\n", millis());
WiFi.mode(WIFI_STA);
state = WebServerActivityState::WIFI_SELECTION;
Serial.printf("[%lu] [WEBACT] Launching WifiSelectionActivity...\n", millis());
Serial.printf("[%lu] [WEBACT] Lancement de WifiSelectionActivity...\n", millis());
enterNewActivity(new WifiSelectionActivity(renderer, mappedInput,
[this](const bool connected) { onWifiSelectionComplete(connected); }));
} else {
@ -152,7 +152,7 @@ void CrossPointWebServerActivity::onNetworkModeSelected(const NetworkMode mode)
}
void CrossPointWebServerActivity::onWifiSelectionComplete(const bool connected) {
Serial.printf("[%lu] [WEBACT] WifiSelectionActivity completed, connected=%d\n", millis(), connected);
Serial.printf("[%lu] [WEBACT] WifiSelectionActivity terminé, connecté=%d\n", millis(), connected);
if (connected) {
// Get connection info before exiting subactivity
@ -164,7 +164,7 @@ void CrossPointWebServerActivity::onWifiSelectionComplete(const bool connected)
// Start mDNS for hostname resolution
if (MDNS.begin(AP_HOSTNAME)) {
Serial.printf("[%lu] [WEBACT] mDNS started: http://%s.local/\n", millis(), AP_HOSTNAME);
Serial.printf("[%lu] [WEBACT] mDNS lancé: http://%s.local/\n", millis(), AP_HOSTNAME);
}
// Start the web server
@ -180,8 +180,8 @@ void CrossPointWebServerActivity::onWifiSelectionComplete(const bool connected)
}
void CrossPointWebServerActivity::startAccessPoint() {
Serial.printf("[%lu] [WEBACT] Starting Access Point mode...\n", millis());
Serial.printf("[%lu] [WEBACT] [MEM] Free heap before AP start: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] Lancement du mode Access Point...\n", millis());
Serial.printf("[%lu] [WEBACT] [MEM] Pile libre avant le début de AP: %d octets\n", millis(), ESP.getFreeHeap());
// Configure and start the AP
WiFi.mode(WIFI_AP);
@ -197,7 +197,7 @@ void CrossPointWebServerActivity::startAccessPoint() {
}
if (!apStarted) {
Serial.printf("[%lu] [WEBACT] ERROR: Failed to start Access Point!\n", millis());
Serial.printf("[%lu] [WEBACT] ERROR: Lancement de Access Point échoué!\n", millis());
onGoBack();
return;
}
@ -211,15 +211,15 @@ void CrossPointWebServerActivity::startAccessPoint() {
connectedIP = ipStr;
connectedSSID = AP_SSID;
Serial.printf("[%lu] [WEBACT] Access Point started!\n", millis());
Serial.printf("[%lu] [WEBACT] Access Point lancé!\n", millis());
Serial.printf("[%lu] [WEBACT] SSID: %s\n", millis(), AP_SSID);
Serial.printf("[%lu] [WEBACT] IP: %s\n", millis(), connectedIP.c_str());
// Start mDNS for hostname resolution
if (MDNS.begin(AP_HOSTNAME)) {
Serial.printf("[%lu] [WEBACT] mDNS started: http://%s.local/\n", millis(), AP_HOSTNAME);
Serial.printf("[%lu] [WEBACT] mDNS lancé: http://%s.local/\n", millis(), AP_HOSTNAME);
} else {
Serial.printf("[%lu] [WEBACT] WARNING: mDNS failed to start\n", millis());
Serial.printf("[%lu] [WEBACT] ATTENTION: lancement de mDNS échoué\n", millis());
}
// Start DNS server for captive portal behavior
@ -227,16 +227,16 @@ void CrossPointWebServerActivity::startAccessPoint() {
dnsServer = new DNSServer();
dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
dnsServer->start(DNS_PORT, "*", apIP);
Serial.printf("[%lu] [WEBACT] DNS server started for captive portal\n", millis());
Serial.printf("[%lu] [WEBACT] Serveur DNS lancé pour portail captif\n", millis());
Serial.printf("[%lu] [WEBACT] [MEM] Free heap after AP start: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] [MEM] Pile libre après lancement de AP: %d octets\n", millis(), ESP.getFreeHeap());
// Start the web server
startWebServer();
}
void CrossPointWebServerActivity::startWebServer() {
Serial.printf("[%lu] [WEBACT] Starting web server...\n", millis());
Serial.printf("[%lu] [WEBACT] Lancement du server web...\n", millis());
// Create the web server instance
webServer.reset(new CrossPointWebServer());
@ -244,16 +244,16 @@ void CrossPointWebServerActivity::startWebServer() {
if (webServer->isRunning()) {
state = WebServerActivityState::SERVER_RUNNING;
Serial.printf("[%lu] [WEBACT] Web server started successfully\n", millis());
Serial.printf("[%lu] [WEBACT] Serveur web lancé avec succès!\n", millis());
// Force an immediate render since we're transitioning from a subactivity
// that had its own rendering task. We need to make sure our display is shown.
xSemaphoreTake(renderingMutex, portMAX_DELAY);
render();
xSemaphoreGive(renderingMutex);
Serial.printf("[%lu] [WEBACT] Rendered File Transfer screen\n", millis());
Serial.printf("[%lu] [WEBACT] Écran File Transfer rendu\n", millis());
} else {
Serial.printf("[%lu] [WEBACT] ERROR: Failed to start web server!\n", millis());
Serial.printf("[%lu] [WEBACT] ERREUR: Lancement du serveur web échoué!\n", millis());
webServer.reset();
// Go back on error
onGoBack();
@ -262,9 +262,9 @@ void CrossPointWebServerActivity::startWebServer() {
void CrossPointWebServerActivity::stopWebServer() {
if (webServer && webServer->isRunning()) {
Serial.printf("[%lu] [WEBACT] Stopping web server...\n", millis());
Serial.printf("[%lu] [WEBACT] Arrêt du serveur web...\n", millis());
webServer->stop();
Serial.printf("[%lu] [WEBACT] Web server stopped\n", millis());
Serial.printf("[%lu] [WEBACT] Serveur web arrêté\n", millis());
}
webServer.reset();
}
@ -290,7 +290,7 @@ void CrossPointWebServerActivity::loop() {
// Log if there's a significant gap between handleClient calls (>100ms)
if (lastHandleClientTime > 0 && timeSinceLastHandleClient > 100) {
Serial.printf("[%lu] [WEBACT] WARNING: %lu ms gap since last handleClient\n", millis(),
Serial.printf("[%lu] [WEBACT] ATTENTION: %lu ms intervalle depuis le dernier handleClient\n", millis(),
timeSinceLastHandleClient);
}
@ -334,7 +334,7 @@ void CrossPointWebServerActivity::render() const {
} else if (state == WebServerActivityState::AP_STARTING) {
renderer.clearScreen();
const auto pageHeight = renderer.getScreenHeight();
renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2 - 20, "Starting Hotspot...", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2 - 20, "Lancement du point d'accès...", true, EpdFontFamily::BOLD);
renderer.displayBuffer();
}
}
@ -344,7 +344,7 @@ void drawQRCode(const GfxRenderer& renderer, const int x, const int y, const std
// The structure to manage the QR code
QRCode qrcode;
uint8_t qrcodeBytes[qrcode_getBufferSize(4)];
Serial.printf("[%lu] [WEBACT] QR Code (%lu): %s\n", millis(), data.length(), data.c_str());
Serial.printf("[%lu] [WEBACT] Code QR (%lu): %s\n", millis(), data.length(), data.c_str());
qrcode_initText(&qrcode, qrcodeBytes, 4, ECC_LOW, data.c_str());
const uint8_t px = 6; // pixels per module
@ -365,21 +365,21 @@ void CrossPointWebServerActivity::renderServerRunning() const {
// Use consistent line spacing
constexpr int LINE_SPACING = 28; // Space between lines
renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Transfert de fichiers", true, EpdFontFamily::BOLD);
if (isApMode) {
// AP mode display - center the content block
int startY = 55;
renderer.drawCenteredText(UI_10_FONT_ID, startY, "Hotspot Mode", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_10_FONT_ID, startY, "Mode de point d'accès", true, EpdFontFamily::BOLD);
std::string ssidInfo = "Network: " + connectedSSID;
std::string ssidInfo = "Réseau: " + connectedSSID;
renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING, ssidInfo.c_str());
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 2, "Connect your device to this WiFi network");
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 2, "Connectez votre appareil à ce réseau");
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 3,
"or scan QR code with your phone to connect to Wifi.");
"ou scannez le code QR avec votre cellulaire pour connecter au WiFi.");
// Show QR code for URL
const std::string wifiConfig = std::string("WIFI:S:") + connectedSSID + ";;";
drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 4, wifiConfig);
@ -390,24 +390,24 @@ void CrossPointWebServerActivity::renderServerRunning() const {
renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING * 3, hostnameUrl.c_str(), true, EpdFontFamily::BOLD);
// Show IP address as fallback
std::string ipUrl = "or http://" + connectedIP + "/";
std::string ipUrl = "ou http://" + connectedIP + "/";
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 4, ipUrl.c_str());
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 5, "Open this URL in your browser");
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 5, "Ouvrez cet URL dans votre navigateur");
// Show QR code for URL
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 6, "or scan QR code with your phone:");
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 6, "ou scannez le code QR avec votre cellulaire:");
drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 7, hostnameUrl);
} else {
// STA mode display (original behavior)
const int startY = 65;
std::string ssidInfo = "Network: " + connectedSSID;
std::string ssidInfo = "Réseau: " + connectedSSID;
if (ssidInfo.length() > 28) {
ssidInfo.replace(25, ssidInfo.length() - 25, "...");
}
renderer.drawCenteredText(UI_10_FONT_ID, startY, ssidInfo.c_str());
std::string ipInfo = "IP Address: " + connectedIP;
std::string ipInfo = "Adresse IP: " + connectedIP;
renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING, ipInfo.c_str());
// Show web server URL prominently
@ -415,16 +415,16 @@ void CrossPointWebServerActivity::renderServerRunning() const {
renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING * 2, webInfo.c_str(), true, EpdFontFamily::BOLD);
// Also show hostname URL
std::string hostnameUrl = std::string("or http://") + AP_HOSTNAME + ".local/";
std::string hostnameUrl = std::string("ou http://") + AP_HOSTNAME + ".local/";
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 3, hostnameUrl.c_str());
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 4, "Open this URL in your browser");
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 4, "Ouvrez cet URL dans votre navigateur");
// Show QR code for URL
drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 6, webInfo);
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 5, "or scan QR code with your phone:");
renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 5, "ou scannez le code QR avec votre cellulaire:");
}
const auto labels = mappedInput.mapLabels("« Exit", "", "", "");
const auto labels = mappedInput.mapLabels("« Sortie", "", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
}

View File

@ -7,9 +7,9 @@
namespace {
constexpr int MENU_ITEM_COUNT = 2;
const char* MENU_ITEMS[MENU_ITEM_COUNT] = {"Join a Network", "Create Hotspot"};
const char* MENU_DESCRIPTIONS[MENU_ITEM_COUNT] = {"Connect to an existing WiFi network",
"Create a WiFi network others can join"};
const char* MENU_ITEMS[MENU_ITEM_COUNT] = {"Réjoindre un réseau", "Créer un point d'accès"};
const char* MENU_DESCRIPTIONS[MENU_ITEM_COUNT] = {"Connecter à un réseau WiFi existant",
"Créez un réseau Wi-Fi auquel d'autres peuvent se connecter"};
} // namespace
void NetworkModeSelectionActivity::taskTrampoline(void* param) {
@ -97,10 +97,10 @@ void NetworkModeSelectionActivity::render() const {
const auto pageHeight = renderer.getScreenHeight();
// Draw header
renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Transfert de fichiers", true, EpdFontFamily::BOLD);
// Draw subtitle
renderer.drawCenteredText(UI_10_FONT_ID, 50, "How would you like to connect?");
renderer.drawCenteredText(UI_10_FONT_ID, 50, "Comment voulez-vous connecter?");
// Draw menu items centered on screen
constexpr int itemHeight = 50; // Height for each menu item (including description)
@ -122,7 +122,7 @@ void NetworkModeSelectionActivity::render() const {
}
// Draw help text at bottom
const auto labels = mappedInput.mapLabels("« Back", "Select", "", "");
const auto labels = mappedInput.mapLabels("« Retour", "Select.", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();

View File

@ -54,36 +54,36 @@ void WifiSelectionActivity::onEnter() {
void WifiSelectionActivity::onExit() {
Activity::onExit();
Serial.printf("[%lu] [WIFI] [MEM] Free heap at onExit start: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WIFI] [MEM] Pile libre au début de onExit: %d octets\n", millis(), ESP.getFreeHeap());
// Stop any ongoing WiFi scan
Serial.printf("[%lu] [WIFI] Deleting WiFi scan...\n", millis());
Serial.printf("[%lu] [WIFI] Suppression du scan WiFi...\n", millis());
WiFi.scanDelete();
Serial.printf("[%lu] [WIFI] [MEM] Free heap after scanDelete: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WIFI] [MEM] Pile libre après scanDelete: %d octets\n", millis(), ESP.getFreeHeap());
// Note: We do NOT disconnect WiFi here - the parent activity (CrossPointWebServerActivity)
// manages WiFi connection state. We just clean up the scan and task.
// Acquire mutex before deleting task to ensure task isn't using it
// This prevents hangs/crashes if the task holds the mutex when deleted
Serial.printf("[%lu] [WIFI] Acquiring rendering mutex before task deletion...\n", millis());
Serial.printf("[%lu] [WIFI] Acquisition du mutex de rendu avant la suppression de la tâche...\n", millis());
xSemaphoreTake(renderingMutex, portMAX_DELAY);
// Delete the display task (we now hold the mutex, so task is blocked if it needs it)
Serial.printf("[%lu] [WIFI] Deleting display task...\n", millis());
Serial.printf("[%lu] [WIFI] Suppression de la tâche d'affichage...\n", millis());
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
Serial.printf("[%lu] [WIFI] Display task deleted\n", millis());
Serial.printf("[%lu] [WIFI] Tâche d'affichage supprimée\n", millis());
}
// Now safe to delete the mutex since we own it
Serial.printf("[%lu] [WIFI] Deleting mutex...\n", millis());
Serial.printf("[%lu] [WIFI] Suppression de mutex...\n", millis());
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
Serial.printf("[%lu] [WIFI] Mutex deleted\n", millis());
Serial.printf("[%lu] [WIFI] Mutex supprimé\n", millis());
Serial.printf("[%lu] [WIFI] [MEM] Free heap at onExit end: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WIFI] [MEM] Pile libre à la fin de onExit: %d octets\n", millis(), ESP.getFreeHeap());
}
void WifiSelectionActivity::startWifiScan() {
@ -179,7 +179,7 @@ void WifiSelectionActivity::selectNetwork(const int index) {
// Use saved password - connect directly
enteredPassword = savedCred->password;
usedSavedPassword = true;
Serial.printf("[%lu] [WiFi] Using saved password for %s, length: %zu\n", millis(), selectedSSID.c_str(),
Serial.printf("[%lu] [WiFi] Utilisation du mot de passe sauvegardé pour %s, longueur: %zu\n", millis(), selectedSSID.c_str(),
enteredPassword.size());
attemptConnection();
return;
@ -191,7 +191,7 @@ void WifiSelectionActivity::selectNetwork(const int index) {
// Don't allow screen updates while changing activity
xSemaphoreTake(renderingMutex, portMAX_DELAY);
enterNewActivity(new KeyboardEntryActivity(
renderer, mappedInput, "Enter WiFi Password",
renderer, mappedInput, "Entrez le mot de passe du WiFi",
"", // No initial text
50, // Y position
64, // Max password length
@ -251,16 +251,16 @@ void WifiSelectionActivity::checkConnectionStatus() {
updateRequired = true;
} else {
// Using saved password or open network - complete immediately
Serial.printf("[%lu] [WIFI] Connected with saved/open credentials, completing immediately\n", millis());
Serial.printf("[%lu] [WIFI] Connecté avec les identifiants sauvegardés/ouverts, complétion immédiate\n", millis());
onComplete(true);
}
return;
}
if (status == WL_CONNECT_FAILED || status == WL_NO_SSID_AVAIL) {
connectionError = "Connection failed";
connectionError = "Connexion échouée";
if (status == WL_NO_SSID_AVAIL) {
connectionError = "Network not found";
connectionError = "Réseau introuvable";
}
state = WifiSelectionState::CONNECTION_FAILED;
updateRequired = true;
@ -505,14 +505,14 @@ void WifiSelectionActivity::renderNetworkList() const {
const auto pageHeight = renderer.getScreenHeight();
// Draw header
renderer.drawCenteredText(UI_12_FONT_ID, 15, "WiFi Networks", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Réseaux WiFi", true, EpdFontFamily::BOLD);
if (networks.empty()) {
// No networks found or scan failed
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
const auto top = (pageHeight - height) / 2;
renderer.drawCenteredText(UI_10_FONT_ID, top, "No networks found");
renderer.drawCenteredText(SMALL_FONT_ID, top + height + 10, "Press OK to scan again");
renderer.drawCenteredText(UI_10_FONT_ID, top, "Aucun réseau trouvé");
renderer.drawCenteredText(SMALL_FONT_ID, top + height + 10, "Appuyez OK pour scanner à nouveau");
} else {
// Calculate how many networks we can display
constexpr int startY = 60;
@ -568,13 +568,13 @@ void WifiSelectionActivity::renderNetworkList() const {
// Show network count
char countStr[32];
snprintf(countStr, sizeof(countStr), "%zu networks found", networks.size());
snprintf(countStr, sizeof(countStr), "%zu réseaux trouvés", networks.size());
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 90, countStr);
}
// Draw help text
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Encrypted | + = Saved");
const auto labels = mappedInput.mapLabels("« Back", "Connect", "", "");
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Crypté | + = Sauvegardé");
const auto labels = mappedInput.mapLabels("« Retour", "Connecter", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
}
@ -584,9 +584,9 @@ void WifiSelectionActivity::renderConnecting() const {
const auto top = (pageHeight - height) / 2;
if (state == WifiSelectionState::SCANNING) {
renderer.drawCenteredText(UI_10_FONT_ID, top, "Scanning...");
renderer.drawCenteredText(UI_10_FONT_ID, top, "Recherche...");
} else {
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connecting...", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connexion...", true, EpdFontFamily::BOLD);
std::string ssidInfo = "to " + selectedSSID;
if (ssidInfo.length() > 25) {
@ -601,18 +601,18 @@ void WifiSelectionActivity::renderConnected() const {
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
const auto top = (pageHeight - height * 4) / 2;
renderer.drawCenteredText(UI_12_FONT_ID, top - 30, "Connected!", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, top - 30, "Connecté!", true, EpdFontFamily::BOLD);
std::string ssidInfo = "Network: " + selectedSSID;
std::string ssidInfo = "Réseau: " + selectedSSID;
if (ssidInfo.length() > 28) {
ssidInfo.replace(25, ssidInfo.length() - 25, "...");
}
renderer.drawCenteredText(UI_10_FONT_ID, top + 10, ssidInfo.c_str());
const std::string ipInfo = "IP Address: " + connectedIP;
const std::string ipInfo = "Adresse IP: " + connectedIP;
renderer.drawCenteredText(UI_10_FONT_ID, top + 40, ipInfo.c_str());
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, "Press any button to continue");
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, "Appuyez sur n'importe quel bouton pour continuer");
}
void WifiSelectionActivity::renderSavePrompt() const {
@ -621,15 +621,15 @@ void WifiSelectionActivity::renderSavePrompt() const {
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
const auto top = (pageHeight - height * 3) / 2;
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connected!", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connecté!", true, EpdFontFamily::BOLD);
std::string ssidInfo = "Network: " + selectedSSID;
std::string ssidInfo = "Réseaux: " + selectedSSID;
if (ssidInfo.length() > 28) {
ssidInfo.replace(25, ssidInfo.length() - 25, "...");
}
renderer.drawCenteredText(UI_10_FONT_ID, top, ssidInfo.c_str());
renderer.drawCenteredText(UI_10_FONT_ID, top + 40, "Save password for next time?");
renderer.drawCenteredText(UI_10_FONT_ID, top + 40, "Enregistrer le mot de passe pour la prochaine fois?");
// Draw Yes/No buttons
const int buttonY = top + 80;
@ -640,19 +640,19 @@ void WifiSelectionActivity::renderSavePrompt() const {
// Draw "Yes" button
if (savePromptSelection == 0) {
renderer.drawText(UI_10_FONT_ID, startX, buttonY, "[Yes]");
renderer.drawText(UI_10_FONT_ID, startX, buttonY, "[Oui]");
} else {
renderer.drawText(UI_10_FONT_ID, startX + 4, buttonY, "Yes");
renderer.drawText(UI_10_FONT_ID, startX + 4, buttonY, "Oui");
}
// Draw "No" button
if (savePromptSelection == 1) {
renderer.drawText(UI_10_FONT_ID, startX + buttonWidth + buttonSpacing, buttonY, "[No]");
renderer.drawText(UI_10_FONT_ID, startX + buttonWidth + buttonSpacing, buttonY, "[Non]");
} else {
renderer.drawText(UI_10_FONT_ID, startX + buttonWidth + buttonSpacing + 4, buttonY, "No");
renderer.drawText(UI_10_FONT_ID, startX + buttonWidth + buttonSpacing + 4, buttonY, "Non");
}
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, "LEFT/RIGHT: Select | OK: Confirm");
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, "GAUCHE/DROITE: Selectionner | OK: Confirmer");
}
void WifiSelectionActivity::renderConnectionFailed() const {
@ -660,9 +660,9 @@ void WifiSelectionActivity::renderConnectionFailed() const {
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
const auto top = (pageHeight - height * 2) / 2;
renderer.drawCenteredText(UI_12_FONT_ID, top - 20, "Connection Failed", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, top - 20, "Connexion échouée", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_10_FONT_ID, top + 20, connectionError.c_str());
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, "Press any button to continue");
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, "Appuyez sur n'importe quel bouton pour continuer");
}
void WifiSelectionActivity::renderForgetPrompt() const {
@ -671,15 +671,15 @@ void WifiSelectionActivity::renderForgetPrompt() const {
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
const auto top = (pageHeight - height * 3) / 2;
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Forget Network?", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Oublier le réseau?", true, EpdFontFamily::BOLD);
std::string ssidInfo = "Network: " + selectedSSID;
std::string ssidInfo = "Réseau: " + selectedSSID;
if (ssidInfo.length() > 28) {
ssidInfo.replace(25, ssidInfo.length() - 25, "...");
}
renderer.drawCenteredText(UI_10_FONT_ID, top, ssidInfo.c_str());
renderer.drawCenteredText(UI_10_FONT_ID, top + 40, "Remove saved password?");
renderer.drawCenteredText(UI_10_FONT_ID, top + 40, "Supprimer le mot de passe sauvegardé?");
// Draw Yes/No buttons
const int buttonY = top + 80;
@ -690,17 +690,17 @@ void WifiSelectionActivity::renderForgetPrompt() const {
// Draw "Yes" button
if (forgetPromptSelection == 0) {
renderer.drawText(UI_10_FONT_ID, startX, buttonY, "[Yes]");
renderer.drawText(UI_10_FONT_ID, startX, buttonY, "[Oui]");
} else {
renderer.drawText(UI_10_FONT_ID, startX + 4, buttonY, "Yes");
renderer.drawText(UI_10_FONT_ID, startX + 4, buttonY, "Oui");
}
// Draw "No" button
if (forgetPromptSelection == 1) {
renderer.drawText(UI_10_FONT_ID, startX + buttonWidth + buttonSpacing, buttonY, "[No]");
renderer.drawText(UI_10_FONT_ID, startX + buttonWidth + buttonSpacing, buttonY, "[Non]");
} else {
renderer.drawText(UI_10_FONT_ID, startX + buttonWidth + buttonSpacing + 4, buttonY, "No");
renderer.drawText(UI_10_FONT_ID, startX + buttonWidth + buttonSpacing + 4, buttonY, "Non");
}
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, "LEFT/RIGHT: Select | OK: Confirm");
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, "GAUCHE/DROITE: Selectionner | OK: Confirmer");
}

View File

@ -59,7 +59,7 @@ void EpubReaderActivity::onEnter() {
if (f.read(data, 4) == 4) {
currentSpineIndex = data[0] + (data[1] << 8);
nextPageNumber = data[2] + (data[3] << 8);
Serial.printf("[%lu] [ERS] Loaded cache: %d, %d\n", millis(), currentSpineIndex, nextPageNumber);
Serial.printf("[%lu] [ERS] Cache chargée: %d, %d\n", millis(), currentSpineIndex, nextPageNumber);
}
f.close();
}
@ -69,7 +69,7 @@ void EpubReaderActivity::onEnter() {
int textSpineIndex = epub->getSpineIndexForTextReference();
if (textSpineIndex != 0) {
currentSpineIndex = textSpineIndex;
Serial.printf("[%lu] [ERS] Opened for first time, navigating to text reference at index %d\n", millis(),
Serial.printf("[%lu] [ERS] Ouvert pour la première fois, navigation à la référence textuel à l'indice %d\n", millis(),
textSpineIndex);
}
}
@ -242,7 +242,7 @@ void EpubReaderActivity::renderScreen() {
// Show end of book screen
if (currentSpineIndex == epub->getSpineItemsCount()) {
renderer.clearScreen();
renderer.drawCenteredText(UI_12_FONT_ID, 300, "End of book", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Fin du livre", true, EpdFontFamily::BOLD);
renderer.displayBuffer();
return;
}
@ -258,7 +258,7 @@ void EpubReaderActivity::renderScreen() {
if (!section) {
const auto filepath = epub->getSpineItem(currentSpineIndex).href;
Serial.printf("[%lu] [ERS] Loading file: %s, index: %d\n", millis(), filepath.c_str(), currentSpineIndex);
Serial.printf("[%lu] [ERS] Chargement du fichier: %s, indice: %d\n", millis(), filepath.c_str(), currentSpineIndex);
section = std::unique_ptr<Section>(new Section(epub, currentSpineIndex, renderer));
const uint16_t viewportWidth = renderer.getScreenWidth() - orientedMarginLeft - orientedMarginRight;
@ -267,13 +267,13 @@ void EpubReaderActivity::renderScreen() {
if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight)) {
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
Serial.printf("[%lu] [ERS] Aucune cache trouvée, création...\n", millis());
// Progress bar dimensions
constexpr int barWidth = 200;
constexpr int barHeight = 10;
constexpr int boxMargin = 20;
const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, "Indexing...");
const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, "Indexage...");
const int boxWidthWithBar = (barWidth > textWidth ? barWidth : textWidth) + boxMargin * 2;
const int boxWidthNoBar = textWidth + boxMargin * 2;
const int boxHeightWithBar = renderer.getLineHeight(UI_12_FONT_ID) + barHeight + boxMargin * 3;
@ -287,7 +287,7 @@ void EpubReaderActivity::renderScreen() {
// Always show "Indexing..." text first
{
renderer.fillRect(boxXNoBar, boxY, boxWidthNoBar, boxHeightNoBar, false);
renderer.drawText(UI_12_FONT_ID, boxXNoBar + boxMargin, boxY + boxMargin, "Indexing...");
renderer.drawText(UI_12_FONT_ID, boxXNoBar + boxMargin, boxY + boxMargin, "Indexage...");
renderer.drawRect(boxXNoBar + 5, boxY + 5, boxWidthNoBar - 10, boxHeightNoBar - 10);
renderer.displayBuffer();
pagesUntilFullRefresh = 0;
@ -296,7 +296,7 @@ void EpubReaderActivity::renderScreen() {
// Setup callback - only called for chapters >= 50KB, redraws with progress bar
auto progressSetup = [this, boxXWithBar, boxWidthWithBar, boxHeightWithBar, barX, barY] {
renderer.fillRect(boxXWithBar, boxY, boxWidthWithBar, boxHeightWithBar, false);
renderer.drawText(UI_12_FONT_ID, boxXWithBar + boxMargin, boxY + boxMargin, "Indexing...");
renderer.drawText(UI_12_FONT_ID, boxXWithBar + boxMargin, boxY + boxMargin, "Indexage...");
renderer.drawRect(boxXWithBar + 5, boxY + 5, boxWidthWithBar - 10, boxHeightWithBar - 10);
renderer.drawRect(barX, barY, barWidth, barHeight);
renderer.displayBuffer();
@ -312,12 +312,12 @@ void EpubReaderActivity::renderScreen() {
if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, progressSetup, progressCallback)) {
Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis());
Serial.printf("[%lu] [ERS] Persistence des données de page au SD échouée\n", millis());
section.reset();
return;
}
} else {
Serial.printf("[%lu] [ERS] Cache found, skipping build...\n", millis());
Serial.printf("[%lu] [ERS] Cache trouvée, sauter sa construction...\n", millis());
}
if (nextPageNumber == UINT16_MAX) {
@ -330,16 +330,16 @@ void EpubReaderActivity::renderScreen() {
renderer.clearScreen();
if (section->pageCount == 0) {
Serial.printf("[%lu] [ERS] No pages to render\n", millis());
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Empty chapter", true, EpdFontFamily::BOLD);
Serial.printf("[%lu] [ERS] Aucune page à afficher\n", millis());
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Chapitre vide", true, EpdFontFamily::BOLD);
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
renderer.displayBuffer();
return;
}
if (section->currentPage < 0 || section->currentPage >= section->pageCount) {
Serial.printf("[%lu] [ERS] Page out of bounds: %d (max %d)\n", millis(), section->currentPage, section->pageCount);
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Out of bounds", true, EpdFontFamily::BOLD);
Serial.printf("[%lu] [ERS] Page hors limites: %d (max %d)\n", millis(), section->currentPage, section->pageCount);
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Hors limites", true, EpdFontFamily::BOLD);
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
renderer.displayBuffer();
return;
@ -348,14 +348,14 @@ void EpubReaderActivity::renderScreen() {
{
auto p = section->loadPageFromSectionFile();
if (!p) {
Serial.printf("[%lu] [ERS] Failed to load page from SD - clearing section cache\n", millis());
Serial.printf("[%lu] [ERS] Chargment de page du SD échoué - suppression de la cache de section\n", millis());
section->clearCache();
section.reset();
return renderScreen();
}
const auto start = millis();
renderContents(std::move(p), orientedMarginTop, orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
Serial.printf("[%lu] [ERS] Rendered page in %dms\n", millis(), millis() - start);
Serial.printf("[%lu] [ERS] Page rendue en %dms\n", millis(), millis() - start);
}
FsFile f;
@ -451,8 +451,8 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in
std::string title;
int titleWidth;
if (tocIndex == -1) {
title = "Unnamed";
titleWidth = renderer.getTextWidth(SMALL_FONT_ID, "Unnamed");
title = "Sans nom";
titleWidth = renderer.getTextWidth(SMALL_FONT_ID, "Sans nom");
} else {
const auto tocItem = epub->getTocItem(tocIndex);
title = tocItem.title;

View File

@ -180,14 +180,14 @@ void FileSelectionActivity::render() const {
renderer.clearScreen();
const auto pageWidth = renderer.getScreenWidth();
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Books", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Livres", true, EpdFontFamily::BOLD);
// Help text
const auto labels = mappedInput.mapLabels("« Home", "Open", "", "");
const auto labels = mappedInput.mapLabels("« Accueil", "Ouvrir", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
if (files.empty()) {
renderer.drawText(UI_10_FONT_ID, 20, 60, "No books found");
renderer.drawText(UI_10_FONT_ID, 20, 60, "Aucun livre trouvé");
renderer.displayBuffer();
return;
}

View File

@ -22,7 +22,7 @@ bool ReaderActivity::isXtcFile(const std::string& path) {
std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) {
if (!SdMan.exists(path.c_str())) {
Serial.printf("[%lu] [ ] File does not exist: %s\n", millis(), path.c_str());
Serial.printf("[%lu] [ ] Le fichier n'existe pas: %s\n", millis(), path.c_str());
return nullptr;
}
@ -31,13 +31,13 @@ std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) {
return epub;
}
Serial.printf("[%lu] [ ] Failed to load epub\n", millis());
Serial.printf("[%lu] [ ] Chargement de l'epub échoué\n", millis());
return nullptr;
}
std::unique_ptr<Xtc> ReaderActivity::loadXtc(const std::string& path) {
if (!SdMan.exists(path.c_str())) {
Serial.printf("[%lu] [ ] File does not exist: %s\n", millis(), path.c_str());
Serial.printf("[%lu] [ ] Le fichier n'existe pas: %s\n", millis(), path.c_str());
return nullptr;
}
@ -46,14 +46,14 @@ std::unique_ptr<Xtc> ReaderActivity::loadXtc(const std::string& path) {
return xtc;
}
Serial.printf("[%lu] [ ] Failed to load XTC\n", millis());
Serial.printf("[%lu] [ ] Chargement du XTC échoué\n", millis());
return nullptr;
}
void ReaderActivity::onSelectBookFile(const std::string& path) {
currentBookPath = path; // Track current book path
exitActivity();
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Loading..."));
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Chargement..."));
if (isXtcFile(path)) {
// Load XTC file
@ -62,7 +62,7 @@ void ReaderActivity::onSelectBookFile(const std::string& path) {
onGoToXtcReader(std::move(xtc));
} else {
exitActivity();
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load XTC",
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Chargement du XTC échoué",
EpdFontFamily::REGULAR, EInkDisplay::HALF_REFRESH));
delay(2000);
onGoToFileSelection();
@ -74,7 +74,7 @@ void ReaderActivity::onSelectBookFile(const std::string& path) {
onGoToEpubReader(std::move(epub));
} else {
exitActivity();
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load epub",
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Chargement de l'epub échoué",
EpdFontFamily::REGULAR, EInkDisplay::HALF_REFRESH));
delay(2000);
onGoToFileSelection();

View File

@ -251,7 +251,7 @@ void XtcReaderActivity::renderPage() {
pixelCounts[getPixelValue(x, y)]++;
}
}
Serial.printf("[%lu] [XTR] Pixel distribution: White=%lu, DarkGrey=%lu, LightGrey=%lu, Black=%lu\n", millis(),
Serial.printf("[%lu] [XTR] Distribution des pixels: Blanc=%lu, GrisFoncé=%lu, GrisPâle=%lu, Noir=%lu\n", millis(),
pixelCounts[0], pixelCounts[1], pixelCounts[2], pixelCounts[3]);
// Pass 1: BW buffer - draw all non-white pixels as black
@ -315,7 +315,7 @@ void XtcReaderActivity::renderPage() {
free(pageBuffer);
Serial.printf("[%lu] [XTR] Rendered page %lu/%lu (2-bit grayscale)\n", millis(), currentPage + 1,
Serial.printf("[%lu] [XTR] Page rendue %lu/%lu en niveaux de gris (2-bits)\n", millis(), currentPage + 1,
xtc->getPageCount());
return;
} else {
@ -352,7 +352,7 @@ void XtcReaderActivity::renderPage() {
pagesUntilFullRefresh--;
}
Serial.printf("[%lu] [XTR] Rendered page %lu/%lu (%u-bit)\n", millis(), currentPage + 1, xtc->getPageCount(),
Serial.printf("[%lu] [XTR] Page rendue %lu/%lu (%u-bits)\n", millis(), currentPage + 1, xtc->getPageCount(),
bitDepth);
}
@ -375,7 +375,7 @@ void XtcReaderActivity::loadProgress() {
uint8_t data[4];
if (f.read(data, 4) == 4) {
currentPage = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
Serial.printf("[%lu] [XTR] Loaded progress: page %lu\n", millis(), currentPage);
Serial.printf("[%lu] [XTR] Progrès chargé: page %lu\n", millis(), currentPage);
// Validate page number
if (currentPage >= xtc->getPageCount()) {

View File

@ -14,7 +14,7 @@
namespace {
constexpr int MENU_ITEMS = 2;
const char* menuNames[MENU_ITEMS] = {"Calibre Web URL", "Connect as Wireless Device"};
const char* menuNames[MENU_ITEMS] = {"URL web Calibre", "Connecter en tant qu'un appareil sans fil"};
} // namespace
void CalibreSettingsActivity::taskTrampoline(void* param) {
@ -83,7 +83,7 @@ void CalibreSettingsActivity::handleSelection() {
// Calibre Web URL
exitActivity();
enterNewActivity(new KeyboardEntryActivity(
renderer, mappedInput, "Calibre Web URL", SETTINGS.opdsServerUrl, 10,
renderer, mappedInput, "URL web Calibre", SETTINGS.opdsServerUrl, 10,
127, // maxLength
false, // not password
[this](const std::string& url) {
@ -155,14 +155,14 @@ void CalibreSettingsActivity::render() {
// Draw status for URL setting
if (i == 0) {
const char* status = (strlen(SETTINGS.opdsServerUrl) > 0) ? "[Set]" : "[Not Set]";
const char* status = (strlen(SETTINGS.opdsServerUrl) > 0) ? "[Défini]" : "[Indéfini]";
const auto width = renderer.getTextWidth(UI_10_FONT_ID, status);
renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, settingY, status, !isSelected);
}
}
// Draw button hints
const auto labels = mappedInput.mapLabels("« Back", "Select", "", "");
const auto labels = mappedInput.mapLabels("« Retour", "Select.", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();

View File

@ -17,12 +17,12 @@ void OtaUpdateActivity::onWifiSelectionComplete(const bool success) {
exitActivity();
if (!success) {
Serial.printf("[%lu] [OTA] WiFi connection failed, exiting\n", millis());
Serial.printf("[%lu] [OTA] Connexion au WiFi a échouée, arrêt en cours\n", millis());
goBack();
return;
}
Serial.printf("[%lu] [OTA] WiFi connected, checking for update\n", millis());
Serial.printf("[%lu] [OTA] WiFi connecté, recherche de mises à jour\n", millis());
xSemaphoreTake(renderingMutex, portMAX_DELAY);
state = CHECKING_FOR_UPDATE;
@ -31,7 +31,7 @@ void OtaUpdateActivity::onWifiSelectionComplete(const bool success) {
vTaskDelay(10 / portTICK_PERIOD_MS);
const auto res = updater.checkForUpdate();
if (res != OtaUpdater::OK) {
Serial.printf("[%lu] [OTA] Update check failed: %d\n", millis(), res);
Serial.printf("[%lu] [OTA] Recherce de mises à jour échouée: %d\n", millis(), res);
xSemaphoreTake(renderingMutex, portMAX_DELAY);
state = FAILED;
xSemaphoreGive(renderingMutex);
@ -40,7 +40,7 @@ void OtaUpdateActivity::onWifiSelectionComplete(const bool success) {
}
if (!updater.isUpdateNewer()) {
Serial.printf("[%lu] [OTA] No new update available\n", millis());
Serial.printf("[%lu] [OTA] Aucune nouvelle mise à jour disponible\n", millis());
xSemaphoreTake(renderingMutex, portMAX_DELAY);
state = NO_UPDATE;
xSemaphoreGive(renderingMutex);
@ -67,11 +67,11 @@ void OtaUpdateActivity::onEnter() {
);
// Turn on WiFi immediately
Serial.printf("[%lu] [OTA] Turning on WiFi...\n", millis());
Serial.printf("[%lu] [OTA] Activation du WiFi...\n", millis());
WiFi.mode(WIFI_STA);
// Launch WiFi selection subactivity
Serial.printf("[%lu] [OTA] Launching WifiSelectionActivity...\n", millis());
Serial.printf("[%lu] [OTA] Lancement de WifiSelectionActivity...\n", millis());
enterNewActivity(new WifiSelectionActivity(renderer, mappedInput,
[this](const bool connected) { onWifiSelectionComplete(connected); }));
}
@ -115,7 +115,7 @@ void OtaUpdateActivity::render() {
float updaterProgress = 0;
if (state == UPDATE_IN_PROGRESS) {
Serial.printf("[%lu] [OTA] Update progress: %d / %d\n", millis(), updater.processedSize, updater.totalSize);
Serial.printf("[%lu] [OTA] Progrès de la mise à jour : %d / %d\n", millis(), updater.processedSize, updater.totalSize);
updaterProgress = static_cast<float>(updater.processedSize) / static_cast<float>(updater.totalSize);
// Only update every 2% at the most
if (static_cast<int>(updaterProgress * 50) == lastUpdaterPercentage / 2) {
@ -127,27 +127,27 @@ void OtaUpdateActivity::render() {
const auto pageWidth = renderer.getScreenWidth();
renderer.clearScreen();
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Update", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Mise à jour", true, EpdFontFamily::BOLD);
if (state == CHECKING_FOR_UPDATE) {
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Checking for update...", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Recherche de mises à jour...", true, EpdFontFamily::BOLD);
renderer.displayBuffer();
return;
}
if (state == WAITING_CONFIRMATION) {
renderer.drawCenteredText(UI_10_FONT_ID, 200, "New update available!", true, EpdFontFamily::BOLD);
renderer.drawText(UI_10_FONT_ID, 20, 250, "Current Version: " CROSSPOINT_VERSION);
renderer.drawText(UI_10_FONT_ID, 20, 270, ("New Version: " + updater.getLatestVersion()).c_str());
renderer.drawCenteredText(UI_10_FONT_ID, 200, "Une mise à jour est disponible!", true, EpdFontFamily::BOLD);
renderer.drawText(UI_10_FONT_ID, 20, 250, "Version actuelle: " CROSSPOINT_VERSION);
renderer.drawText(UI_10_FONT_ID, 20, 270, ("Nouvelle version: " + updater.getLatestVersion()).c_str());
const auto labels = mappedInput.mapLabels("Cancel", "Update", "", "");
const auto labels = mappedInput.mapLabels("Annuler", "M-à-j", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();
return;
}
if (state == UPDATE_IN_PROGRESS) {
renderer.drawCenteredText(UI_10_FONT_ID, 310, "Updating...", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_10_FONT_ID, 310, "Mise à jour...", true, EpdFontFamily::BOLD);
renderer.drawRect(20, 350, pageWidth - 40, 50);
renderer.fillRect(24, 354, static_cast<int>(updaterProgress * static_cast<float>(pageWidth - 44)), 42);
renderer.drawCenteredText(UI_10_FONT_ID, 420,
@ -160,20 +160,20 @@ void OtaUpdateActivity::render() {
}
if (state == NO_UPDATE) {
renderer.drawCenteredText(UI_10_FONT_ID, 300, "No update available", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Aucune novelle mise à jour disponible", true, EpdFontFamily::BOLD);
renderer.displayBuffer();
return;
}
if (state == FAILED) {
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update failed", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Mise à jour échouée", true, EpdFontFamily::BOLD);
renderer.displayBuffer();
return;
}
if (state == FINISHED) {
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Update complete", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_10_FONT_ID, 350, "Press and hold power button to turn back on");
renderer.drawCenteredText(UI_10_FONT_ID, 300, "Mise à jour terminée", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_10_FONT_ID, 350, "Appuyez longuement sur le bouton d'alimentation pour rallumer");
renderer.displayBuffer();
state = SHUTTING_DOWN;
return;
@ -188,7 +188,7 @@ void OtaUpdateActivity::loop() {
if (state == WAITING_CONFIRMATION) {
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
Serial.printf("[%lu] [OTA] New update available, starting download...\n", millis());
Serial.printf("[%lu] [OTA] Nouvelle mise à jour disponible, téléchargement commencé...\n", millis());
xSemaphoreTake(renderingMutex, portMAX_DELAY);
state = UPDATE_IN_PROGRESS;
xSemaphoreGive(renderingMutex);
@ -197,7 +197,7 @@ void OtaUpdateActivity::loop() {
const auto res = updater.installUpdate([this](const size_t, const size_t) { updateRequired = true; });
if (res != OtaUpdater::OK) {
Serial.printf("[%lu] [OTA] Update failed: %d\n", millis(), res);
Serial.printf("[%lu] [OTA] Mise à jour échouée: %d\n", millis(), res);
xSemaphoreTake(renderingMutex, portMAX_DELAY);
state = FAILED;
xSemaphoreGive(renderingMutex);

View File

@ -16,31 +16,31 @@ namespace {
constexpr int settingsCount = 18;
const SettingInfo settingsList[settingsCount] = {
// Should match with SLEEP_SCREEN_MODE
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop"}),
SettingInfo::Enum("Status Bar", &CrossPointSettings::statusBar, {"None", "No Progress", "Full"}),
SettingInfo::Toggle("Extra Paragraph Spacing", &CrossPointSettings::extraParagraphSpacing),
SettingInfo::Toggle("Text Anti-Aliasing", &CrossPointSettings::textAntiAliasing),
SettingInfo::Toggle("Short Power Button Click", &CrossPointSettings::shortPwrBtn),
SettingInfo::Enum("Reading Orientation", &CrossPointSettings::orientation,
{"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}),
SettingInfo::Enum("Front Button Layout", &CrossPointSettings::frontButtonLayout,
{"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm", "Lft, Bck, Cnfrm, Rght"}),
SettingInfo::Enum("Side Button Layout (reader)", &CrossPointSettings::sideButtonLayout,
{"Prev, Next", "Next, Prev"}),
SettingInfo::Enum("Reader Font Family", &CrossPointSettings::fontFamily,
SettingInfo::Enum("Écran de veille", &CrossPointSettings::sleepScreen, {"Sombre", "Clair", "Personnalisé", "Couverture", "Aucun"}),
SettingInfo::Enum("Mode de couverture de veille", &CrossPointSettings::sleepScreenCoverMode, {"Étirée", "Recadrée"}),
SettingInfo::Enum("Barre d'état", &CrossPointSettings::statusBar, {"Aucune", "Sans progrès", "Pleine"}),
SettingInfo::Toggle("Espace supplé. entre paragraphes", &CrossPointSettings::extraParagraphSpacing),
SettingInfo::Toggle("Anticrénelage du texte", &CrossPointSettings::textAntiAliasing),
SettingInfo::Toggle("Clic court du bouton alim.", &CrossPointSettings::shortPwrBtn),
SettingInfo::Enum("Orientation de lecture", &CrossPointSettings::orientation,
{"Portrait", "Paysage à droite", "Inversée", "Paysage à gauche"}),
SettingInfo::Enum("Disposition des boutons au front", &CrossPointSettings::frontButtonLayout,
{"Rtr, Cnfrmr, Gche, Drte", "Gche, Drte, Rtr, Cnfrmr", "Gche, Rtr, Cnfrmr, Drte"}),
SettingInfo::Enum("Disposition des boutons à droite (lis.)", &CrossPointSettings::sideButtonLayout,
{"Précédent, Prochaine", "Prochaine, Précédent"}),
SettingInfo::Enum("Police", &CrossPointSettings::fontFamily,
{"Bookerly", "Noto Sans", "Open Dyslexic"}),
SettingInfo::Enum("Reader Font Size", &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}),
SettingInfo::Enum("Reader Line Spacing", &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}),
SettingInfo::Value("Reader Screen Margin", &CrossPointSettings::screenMargin, {5, 40, 5}),
SettingInfo::Enum("Reader Paragraph Alignment", &CrossPointSettings::paragraphAlignment,
{"Justify", "Left", "Center", "Right"}),
SettingInfo::Enum("Time to Sleep", &CrossPointSettings::sleepTimeout,
SettingInfo::Enum("Taille de police", &CrossPointSettings::fontSize, {"Petit", "Medium", "Large", "Très Large"}),
SettingInfo::Enum("Espacement des lignes", &CrossPointSettings::lineSpacing, {"Fin", "Normal", "Large"}),
SettingInfo::Value("Marge d'écran", &CrossPointSettings::screenMargin, {5, 40, 5}),
SettingInfo::Enum("Alignement des lignes", &CrossPointSettings::paragraphAlignment,
{"Justif", "Gauche", "Centré", "Droite"}),
SettingInfo::Enum("Temps de veille", &CrossPointSettings::sleepTimeout,
{"1 min", "5 min", "10 min", "15 min", "30 min"}),
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
SettingInfo::Enum("Fréquence de rafraîche.", &CrossPointSettings::refreshFrequency,
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}),
SettingInfo::Action("Calibre Settings"),
SettingInfo::Action("Check for updates")};
SettingInfo::Action("Paramètres Calibre"),
SettingInfo::Action("Vérifier les mises à jour")};
} // namespace
void SettingsActivity::taskTrampoline(void* param) {
@ -137,7 +137,7 @@ void SettingsActivity::toggleCurrentSetting() {
SETTINGS.*(setting.valuePtr) = currentValue + setting.valueRange.step;
}
} else if (setting.type == SettingType::ACTION) {
if (strcmp(setting.name, "Calibre Settings") == 0) {
if (strcmp(setting.name, "Paramètres Calibre") == 0) {
xSemaphoreTake(renderingMutex, portMAX_DELAY);
exitActivity();
enterNewActivity(new CalibreSettingsActivity(renderer, mappedInput, [this] {
@ -145,7 +145,7 @@ void SettingsActivity::toggleCurrentSetting() {
updateRequired = true;
}));
xSemaphoreGive(renderingMutex);
} else if (strcmp(setting.name, "Check for updates") == 0) {
} else if (strcmp(setting.name, "Verifier les mises à jour") == 0) {
xSemaphoreTake(renderingMutex, portMAX_DELAY);
exitActivity();
enterNewActivity(new OtaUpdateActivity(renderer, mappedInput, [this] {
@ -182,7 +182,7 @@ void SettingsActivity::render() const {
const auto pageHeight = renderer.getScreenHeight();
// Draw header
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Settings", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Paramètres", true, EpdFontFamily::BOLD);
// Draw selection
renderer.fillRect(0, 60 + selectedSettingIndex * 30 - 2, pageWidth - 1, 30);
@ -198,7 +198,7 @@ void SettingsActivity::render() const {
std::string valueText = "";
if (settingsList[i].type == SettingType::TOGGLE && settingsList[i].valuePtr != nullptr) {
const bool value = SETTINGS.*(settingsList[i].valuePtr);
valueText = value ? "ON" : "OFF";
valueText = value ? "OUI" : "NON";
} else if (settingsList[i].type == SettingType::ENUM && settingsList[i].valuePtr != nullptr) {
const uint8_t value = SETTINGS.*(settingsList[i].valuePtr);
valueText = settingsList[i].enumValues[value];
@ -214,7 +214,7 @@ void SettingsActivity::render() const {
pageHeight - 60, CROSSPOINT_VERSION);
// Draw help text
const auto labels = mappedInput.mapLabels("« Save", "Toggle", "", "");
const auto labels = mappedInput.mapLabels("« Enreg.", "Basculer", "", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
// Always use standard refresh for settings screen