Compare commits

..

2 Commits

Author SHA1 Message Date
15b7cb1f4c Add battery run protection: require charger below 10%
All checks were successful
Build / Build (push) Successful in 10s
- Add bq24193_charger_connected() for charger detection
- Block install when battery < 10% and charger not plugged in
- Show clear screen after charger detected before continuing
- User can cancel with + and - to return to Hekate

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 15:12:06 +01:00
4b124a9998 Add + and - simultaneously to cancel and return to Hekate before install
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 15:03:58 +01:00
3 changed files with 93 additions and 7 deletions

View File

@@ -164,6 +164,12 @@ void bq24193_enable_charger()
i2c_send_byte(I2C_1, BQ24193_I2C_ADDR, BQ24193_PORConfig, reg);
}
bool bq24193_charger_connected()
{
u8 status = bq24193_get_reg(BQ24193_Status);
return (status & BQ24193_STATUS_PG_MASK) != 0;
}
void bq24193_fake_battery_removal()
{
// Disable watchdog to keep BATFET disabled.

View File

@@ -19,6 +19,8 @@
#ifndef __BQ24193_H_
#define __BQ24193_H_
#include <utils/types.h>
#define BQ24193_I2C_ADDR 0x6B
// REG 0 masks.
@@ -117,5 +119,6 @@ enum BQ24193_reg_prop {
int bq24193_get_property(enum BQ24193_reg_prop prop, int *value);
void bq24193_enable_charger();
void bq24193_fake_battery_removal();
bool bq24193_charger_connected();
#endif /* __BQ24193_H_ */

View File

@@ -18,6 +18,8 @@
#include <mem/heap.h>
#include <mem/minerva.h>
#include <power/max77620.h>
#include <power/max17050.h>
#include <power/bq24193.h>
#include <soc/bpmp.h>
#include <soc/fuse.h>
#include <soc/hw_init.h>
@@ -376,6 +378,56 @@ void ipl_main(void) {
// Determine installation mode
install_mode_t mode = current.is_installed ? INSTALL_MODE_UPDATE : INSTALL_MODE_CLEAN;
// Battery run protection: below 10% requires charger
#define BATT_LOW_THRESHOLD 10
int batt_raw = 0;
int batt_pct = 100;
if (max17050_get_property(MAX17050_RepSOC, &batt_raw) == 0) {
batt_pct = batt_raw >> 8; // RepSOC: high byte = percent
}
while (batt_pct < BATT_LOW_THRESHOLD && !bq24193_charger_connected()) {
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
print_header();
set_color(COLOR_RED);
gfx_printf("Akkustand zu niedrig! (%d%%)\n\n", batt_pct);
gfx_printf("Unter %d%% Akku darf die Installation nur\n", BATT_LOW_THRESHOLD);
gfx_printf("mit angeschlossenem Ladegeraet durchgefuehrt werden.\n\n");
set_color(COLOR_YELLOW);
gfx_printf("Stecke das Ladegeraet an und warte...\n");
gfx_printf("Oder druecke + und - zum Abbrechen.\n");
set_color(COLOR_WHITE);
jc_init_hw();
while (btn_read() & BTN_POWER) { msleep(50); }
bool user_cancelled = false;
while (batt_pct < BATT_LOW_THRESHOLD && !bq24193_charger_connected() && !user_cancelled) {
jc_gamepad_rpt_t *jc = joycon_poll();
if (jc && jc->plus && jc->minus) {
user_cancelled = true;
break;
}
if (max17050_get_property(MAX17050_RepSOC, &batt_raw) == 0) {
batt_pct = batt_raw >> 8;
}
msleep(200);
}
if (user_cancelled) {
gfx_printf("\nAbgebrochen. Starte Hekate...\n");
msleep(500);
if (file_exists(PAYLOAD_PATH)) {
launch_payload(PAYLOAD_PATH);
} else {
power_set_state(POWER_OFF_REBOOT);
}
return;
}
}
// Charger detected or battery OK - clear the low-battery screen before continuing
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
print_header();
// Show information
set_color(COLOR_CYAN);
gfx_printf("Installationsmodus: %s\n", mode == INSTALL_MODE_UPDATE ? "Update" : "Saubere Installation");
@@ -400,33 +452,58 @@ void ipl_main(void) {
gfx_printf("Druecke A-Taste (rechter Joy-Con) oder Power-Taste,\n");
gfx_printf("um die Installation zu starten...\n");
set_color(COLOR_WHITE);
set_color(COLOR_CYAN);
gfx_printf("Druecke + und - gleichzeitig zum Abbrechen (zurueck zu Hekate).\n");
set_color(COLOR_WHITE);
// Wait for either A button or Power button
// Wait for A/Power to start, or +/- to cancel
bool button_pressed = false;
bool cancelled = false;
// First, wait for power button to be released if it's currently pressed
while (btn_read() & BTN_POWER) {
msleep(50);
}
while (!button_pressed) {
// Check power button - detect press (transition from not pressed to pressed)
while (!button_pressed && !cancelled) {
// Check power button
u8 btn_state = btn_read();
if (btn_state & BTN_POWER) {
button_pressed = true;
break;
}
// Check joycon A button
// Check joycon buttons
jc_gamepad_rpt_t *jc = joycon_poll();
if (jc && jc->a) {
button_pressed = true;
break;
if (jc) {
if (jc->a) {
button_pressed = true;
break;
}
// + and - simultaneously = cancel, return to hekate
if (jc->plus && jc->minus) {
cancelled = true;
break;
}
}
msleep(50); // Small delay to avoid busy-waiting
}
if (cancelled) {
gfx_printf("\n");
set_color(COLOR_YELLOW);
gfx_printf("Abgebrochen. Starte Hekate...\n");
set_color(COLOR_WHITE);
msleep(500);
if (file_exists(PAYLOAD_PATH)) {
launch_payload(PAYLOAD_PATH);
} else {
power_set_state(POWER_OFF_REBOOT);
}
return;
}
// Clear the prompt and start installation
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);