diff --git a/Documentation/pycardium/utime.rst b/Documentation/pycardium/utime.rst
index 3fdbf4eff26f8e55f739d6039d4a09d8a5732f20..7b62db053c187880bd0a25451d6ec90a52d6b4ca 100644
--- a/Documentation/pycardium/utime.rst
+++ b/Documentation/pycardium/utime.rst
@@ -25,16 +25,18 @@ alarm.
 
 .. py:function:: time()
 
-   Return the current timestamp in seconds since 2000-01-01 00:00.
+   Return the current timestamp in seconds since 2000-01-01 00:00 in
+   the local timezone.
 
 .. py:function:: set_time(secs)
 
-   Sets the time to ``secs`` seconds since 2000-01-01 00:00.
+   Sets the time to ``secs`` seconds since 2000-01-01 00:00 in the local
+   timezone.
    
 .. py:function:: set_unix_time(secs)
 
    Sets the time to ``secs`` seconds since 1970-01-01 00:00 UTC.
-   This corresponds a regular Unix timestamp which can be obtained
+   This corresponds to a regular Unix timestamp which can be obtained
    by running ``date +%s`` in a command line or ``int(time.time())``
    in Python.
 
diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h
index 87b24e8975dff763119aa4ffe6ce336e1cb75212..69899aaeb9b7e52a848b61b3e9451dd75f14708e 100644
--- a/epicardium/epicardium.h
+++ b/epicardium/epicardium.h
@@ -32,6 +32,7 @@ typedef _Bool bool;
 #define API_SYSTEM_EXIT             0x1
 #define API_SYSTEM_EXEC             0x2
 #define API_SYSTEM_RESET            0x3
+#define API_BATTERY_VOLTAGE         0x4
 
 #define API_INTERRUPT_ENABLE        0xA
 #define API_INTERRUPT_DISABLE       0xB
@@ -210,6 +211,16 @@ API(API_SYSTEM_EXEC, int __epic_exec(char *name));
  */
 API(API_SYSTEM_RESET, void epic_system_reset(void));
 
+/**
+ * Battery Voltage
+ * ===============
+ */
+
+/**
+ * Read the current battery voltage.
+ */
+API(API_BATTERY_VOLTAGE, int epic_read_battery_voltage(float *result));
+
 /**
  * UART/Serial Interface
  * =====================
diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h
index 3555ab52e7c8ed66707459f2b0b4c90bf123dadd..484e951f5c9b9c1e98b181717d4bf8740e1767b5 100644
--- a/epicardium/modules/modules.h
+++ b/epicardium/modules/modules.h
@@ -32,11 +32,32 @@ void vLedTask(void *pvParameters);
 int personal_state_enabled();
 
 /* ---------- PMIC --------------------------------------------------------- */
-/* In 1/10s */
-#define PMIC_PRESS_SLEEP           20
-#define PMIC_PRESS_POWEROFF        40
 void vPmicTask(void *pvParameters);
 
+/* Critical battery voltage */
+#define BATTERY_CRITICAL   3.40f
+
+enum pmic_amux_signal {
+	PMIC_AMUX_CHGIN_U     = 0x1,
+	PMIC_AMUX_CHGIN_I     = 0x2,
+	PMIC_AMUX_BATT_U      = 0x3,
+	PMIC_AMUX_BATT_CHG_I  = 0x4,
+	PMIC_AMUX_BATT_DIS_I  = 0x5,
+	PMIC_AMUX_BATT_NULL_I = 0x6,
+	PMIC_AMUX_THM_U       = 0x7,
+	PMIC_AMUX_TBIAS_U     = 0x8,
+	PMIC_AMUX_AGND_U      = 0x9,
+	PMIC_AMUX_SYS_U       = 0xA,
+	_PMIC_AMUX_MAX,
+};
+
+/*
+ * Read a value from the PMIC's AMUX.  The result is already converted into its
+ * proper unit.  See the MAX77650 datasheet for details.
+ */
+int pmic_read_amux(enum pmic_amux_signal sig, float *result);
+
+
 /* ---------- BLE ---------------------------------------------------------- */
 void vBleTask(void *pvParameters);
 bool ble_shall_start(void);
diff --git a/epicardium/modules/pmic.c b/epicardium/modules/pmic.c
index c02691dc25350723b04bd0059183bcf0522396ae..48a311c983cedd2ff2b1b71a4b4d4ddd9583336c 100644
--- a/epicardium/modules/pmic.c
+++ b/epicardium/modules/pmic.c
@@ -1,41 +1,263 @@
-#include <stdio.h>
+#include "epicardium.h"
+#include "modules/modules.h"
+#include "modules/log.h"
 
-#include "max32665.h"
-#include "gcr_regs.h"
+#include "card10.h"
 #include "pmic.h"
 #include "MAX77650-Arduino-Library.h"
-#include "card10.h"
+
+#include "max32665.h"
+#include "mxc_sys.h"
+#include "mxc_pins.h"
+#include "adc.h"
 
 #include "FreeRTOS.h"
 #include "task.h"
+#include "timers.h"
 
-#include "epicardium.h"
-#include "modules.h"
-#include "modules/log.h"
+#include <stdio.h>
 
 /* Task ID for the pmic handler */
 static TaskHandle_t pmic_task_id = NULL;
 
+enum {
+	/* An irq was received, probably the power button */
+	PMIC_NOTIFY_IRQ = 1,
+	/* The timer has ticked and we should check the battery voltage again */
+	PMIC_NOTIFY_MONITOR = 2,
+};
+
 void pmic_interrupt_callback(void *_)
 {
 	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
 	if (pmic_task_id != NULL) {
-		vTaskNotifyGiveFromISR(pmic_task_id, &xHigherPriorityTaskWoken);
+		xTaskNotifyFromISR(
+			pmic_task_id,
+			PMIC_NOTIFY_IRQ,
+			eSetBits,
+			&xHigherPriorityTaskWoken
+		);
 		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
 	}
 }
 
+int pmic_read_amux(enum pmic_amux_signal sig, float *result)
+{
+	int ret = 0;
+
+	if (sig > _PMIC_AMUX_MAX) {
+		return -EINVAL;
+	}
+
+	ret = hwlock_acquire(HWLOCK_ADC, pdMS_TO_TICKS(100));
+	if (ret < 0) {
+		return ret;
+	}
+	ret = hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100));
+	if (ret < 0) {
+		return ret;
+	}
+
+	/* Select the correct channel for this measurement.  */
+	MAX77650_setMUX_SEL(sig);
+
+	/*
+	 * According to the datasheet, the voltage will stabilize within 0.3us.
+	 * Just to be sure, we'll wait a little longer.  In the meantime,
+	 * release the I2C mutex.
+	 */
+	hwlock_release(HWLOCK_I2C);
+	vTaskDelay(pdMS_TO_TICKS(5));
+	ret = hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100));
+	if (ret < 0) {
+		return ret;
+	}
+
+	uint16_t adc_data;
+	ADC_StartConvert(ADC_CH_0, 0, 0);
+	ADC_GetData(&adc_data);
+
+	/* Turn MUX back to neutral so it does not waste power.  */
+	MAX77650_setMUX_SEL(sig);
+
+	/* Convert ADC measurement to SI Volts */
+	float adc_voltage = (float)adc_data / 1023.0f * 1.22f;
+
+	/*
+	 * Convert value according to PMIC formulas (Table 7)
+	 */
+	switch (sig) {
+	case PMIC_AMUX_CHGIN_U:
+		*result = adc_voltage / 0.167f;
+		break;
+	case PMIC_AMUX_CHGIN_I:
+		*result = adc_voltage / 2.632f;
+		break;
+	case PMIC_AMUX_BATT_U:
+		*result = adc_voltage / 0.272f;
+		break;
+	case PMIC_AMUX_BATT_CHG_I:
+		*result = adc_voltage / 1.25f;
+		break;
+	case PMIC_AMUX_BATT_NULL_I:
+	case PMIC_AMUX_THM_U:
+	case PMIC_AMUX_TBIAS_U:
+	case PMIC_AMUX_AGND_U:
+		*result = adc_voltage;
+		break;
+	case PMIC_AMUX_SYS_U:
+		*result = adc_voltage / 0.26f;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	hwlock_release(HWLOCK_I2C);
+	hwlock_release(HWLOCK_ADC);
+	return ret;
+}
+
+/*
+ * Read the interrupt flag register and handle all interrupts which the PMIC has
+ * sent.  In most cases this will be the buttons.
+ */
+static void
+pmic_poll_interrupts(TickType_t *button_start_tick, TickType_t duration)
+{
+	while (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(500)) < 0) {
+		LOG_WARN("pmic", "Failed to acquire I2C. Retrying ...");
+		xTaskNotify(pmic_task_id, PMIC_NOTIFY_IRQ, eSetBits);
+		return;
+	}
+
+	uint8_t int_flag = MAX77650_getINT_GLBL();
+	hwlock_release(HWLOCK_I2C);
+
+	if (int_flag & MAX77650_INT_nEN_F) {
+		/* Button was pressed */
+		*button_start_tick = xTaskGetTickCount();
+	}
+	if (int_flag & MAX77650_INT_nEN_R) {
+		/* Button was released */
+		*button_start_tick = 0;
+		if (duration < pdMS_TO_TICKS(400)) {
+			return_to_menu();
+		} else {
+			LOG_WARN("pmic", "Resetting ...");
+			card10_reset();
+		}
+	}
+
+	/* TODO: Remove when all interrupts are handled */
+	if (int_flag & ~(MAX77650_INT_nEN_F | MAX77650_INT_nEN_R)) {
+		LOG_WARN("pmic", "Unhandled PMIC Interrupt: %x", int_flag);
+	}
+}
+
+__attribute__((noreturn)) static void pmic_die(float u_batt)
+{
+	/* Stop core 1 */
+	core1_stop();
+
+	/* Grab the screen */
+	disp_forcelock();
+
+	/* Draw an error screen */
+	epic_disp_clear(0x0000);
+
+	epic_disp_print(0, 0, " Battery", 0xffff, 0x0000);
+	epic_disp_print(0, 20, " critical", 0xffff, 0x0000);
+	epic_disp_print(0, 40, "  !!!!", 0xffff, 0x0000);
+	epic_disp_update();
+
+	/* Vibrate violently */
+	epic_vibra_set(true);
+
+	/* Wait a bit */
+	for (int i = 0; i < 50000000; i++)
+		__NOP();
+
+	LOG_WARN("pmic", "Poweroff");
+	MAX77650_setSFT_RST(0x2);
+
+	while (1)
+		__WFI();
+}
+
+/*
+ * Check the battery voltage.  If it drops too low, turn card10 off.
+ */
+static void pmic_check_battery()
+{
+	float u_batt;
+	int res;
+
+	res = pmic_read_amux(PMIC_AMUX_BATT_U, &u_batt);
+	if (res < 0) {
+		LOG_ERR("pmic", "Failed reading battery voltage: %d", res);
+		return;
+	}
+
+	LOG_DEBUG(
+		"pmic",
+		"Battery is at %d.%03d V",
+		(int)u_batt,
+		(int)(u_batt * 1000.0) % 1000
+	);
+
+	if (u_batt < BATTERY_CRITICAL) {
+		pmic_die(u_batt);
+	}
+}
+
+/*
+ * API-call for battery voltage
+ */
+int epic_read_battery_voltage(float *result)
+{
+	return pmic_read_amux(PMIC_AMUX_BATT_U, result);
+}
+
+static StaticTimer_t pmic_timer_data;
+static void vPmicTimerCb(TimerHandle_t xTimer)
+{
+	/*
+	 * Tell the PMIC task to check the battery again.
+	 */
+	xTaskNotify(pmic_task_id, PMIC_NOTIFY_MONITOR, eSetBits);
+}
+
 void vPmicTask(void *pvParameters)
 {
 	pmic_task_id = xTaskGetCurrentTaskHandle();
 
+	ADC_Init(0x9, NULL);
+	GPIO_Config(&gpio_cfg_adc0);
+
 	TickType_t button_start_tick = 0;
 
+	pmic_check_battery();
+
+	TimerHandle_t pmic_timer = xTimerCreateStatic(
+		"PMIC Timer",
+		pdMS_TO_TICKS(60 * 1000),
+		pdTRUE,
+		NULL,
+		vPmicTimerCb,
+		&pmic_timer_data
+	);
+	if (pmic_timer == NULL) {
+		LOG_CRIT("pmic", "Could not create timer.");
+		vTaskDelay(portMAX_DELAY);
+	}
+	xTimerStart(pmic_timer, 0);
+
 	while (1) {
+		uint32_t reason;
 		if (button_start_tick == 0) {
-			ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
+			reason = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
 		} else {
-			ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(100));
+			reason = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(100));
 		}
 
 		TickType_t duration = xTaskGetTickCount() - button_start_tick;
@@ -45,36 +267,12 @@ void vPmicTask(void *pvParameters)
 			MAX77650_setSFT_RST(0x2);
 		}
 
-		while (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(500)) < 0) {
-			LOG_WARN("pmic", "Failed to acquire I2C. Retrying ...");
-			vTaskDelay(pdMS_TO_TICKS(500));
-		}
-
-		uint8_t int_flag = MAX77650_getINT_GLBL();
-		hwlock_release(HWLOCK_I2C);
-
-		if (int_flag & MAX77650_INT_nEN_F) {
-			/* Button was pressed */
-			button_start_tick = xTaskGetTickCount();
-		}
-		if (int_flag & MAX77650_INT_nEN_R) {
-			/* Button was released */
-			button_start_tick = 0;
-			if (duration < pdMS_TO_TICKS(400)) {
-				return_to_menu();
-			} else {
-				LOG_WARN("pmic", "Resetting ...");
-				card10_reset();
-			}
+		if (reason & PMIC_NOTIFY_IRQ) {
+			pmic_poll_interrupts(&button_start_tick, duration);
 		}
 
-		/* TODO: Remove when all interrupts are handled */
-		if (int_flag & ~(MAX77650_INT_nEN_F | MAX77650_INT_nEN_R)) {
-			LOG_WARN(
-				"pmic",
-				"Unhandled PMIC Interrupt: %x",
-				int_flag
-			);
+		if (reason & PMIC_NOTIFY_MONITOR) {
+			pmic_check_battery();
 		}
 	}
 }
diff --git a/pycardium/modules/os.c b/pycardium/modules/os.c
index 385cc9de659270c1d21764f3c956a2f3da9ec16a..c017e417e75a0dea115af340bb1b5a2b85c672e9 100644
--- a/pycardium/modules/os.c
+++ b/pycardium/modules/os.c
@@ -123,6 +123,18 @@ static mp_obj_t mp_os_rename(mp_obj_t py_oldp, mp_obj_t py_newp)
 }
 static MP_DEFINE_CONST_FUN_OBJ_2(rename_obj, mp_os_rename);
 
+static mp_obj_t mp_os_read_battery()
+{
+	float result;
+	int res = epic_read_battery_voltage(&result);
+	if (res < 0) {
+		mp_raise_OSError(-res);
+	}
+
+	return mp_obj_new_float(result);
+}
+static MP_DEFINE_CONST_FUN_OBJ_0(read_battery_obj, mp_os_read_battery);
+
 static const mp_rom_map_elem_t os_module_globals_table[] = {
 	{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_os) },
 	{ MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&exit_obj) },
@@ -132,6 +144,7 @@ static const mp_rom_map_elem_t os_module_globals_table[] = {
 	{ MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&unlink_obj) },
 	{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mkdir_obj) },
 	{ MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&rename_obj) },
+	{ MP_ROM_QSTR(MP_QSTR_read_battery), MP_ROM_PTR(&read_battery_obj) },
 };
 
 static MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table);
diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h
index acf598f1b82c81e31722934c881d7d1a3217d9b3..f78afd25ed5aba9b38ef3b29d845ef5d573e47a6 100644
--- a/pycardium/modules/qstrdefs.h
+++ b/pycardium/modules/qstrdefs.h
@@ -105,6 +105,7 @@ Q(listdir)
 Q(unlink)
 Q(mkdir)
 Q(rename)
+Q(read_battery)
 
 /* gpio */
 Q(gpio)
diff --git a/pycardium/modules/utime.c b/pycardium/modules/utime.c
index a3a1173f67ed8f8f5b291be96f333dad0aa5d796..ef9b3da65d2af7a55799ec879857469117859291 100644
--- a/pycardium/modules/utime.c
+++ b/pycardium/modules/utime.c
@@ -19,8 +19,8 @@
 
 static mp_obj_t time_set_time(mp_obj_t secs)
 {
-	uint64_t timestamp =
-		mp_obj_get_int(secs) * 1000ULL + EPOCH_OFFSET * 1000ULL;
+	uint64_t timestamp = mp_obj_get_int(secs) * 1000ULL +
+			     EPOCH_OFFSET * 1000ULL + TZONE_OFFSET;
 	epic_rtc_set_milliseconds(timestamp);
 	return mp_const_none;
 }