diff --git a/python_payload/st3m/power.py b/python_payload/st3m/power.py
index e4b059eb36a705bb66b4d550194fc97d7a7f13a9..99a951a3ae8fe7a5fc548e1bb05c077c6211e84d 100644
--- a/python_payload/st3m/power.py
+++ b/python_payload/st3m/power.py
@@ -1,6 +1,9 @@
 import machine
 import time
 import sys_kernel
+from st3m import logging
+
+log = logging.Log(__name__, level=logging.DEBUG)
 
 
 class Power:
@@ -11,9 +14,25 @@ class Power:
     """
 
     def __init__(self) -> None:
-        self._adc_pin = machine.Pin(9, machine.Pin.IN)
+        self._adc_pin = machine.Pin(9, machine.Pin.IN, pull=None)
+        # with no battery will be pulled down from the voltage divider
+        # current too low with 1M for ADC so need to check via GPIO
+        bat_read = 0
+        for i in range(5):
+            time.sleep(0.002)
+            bat_read += self._adc_pin.value()
+        if bat_read > 4:
+            self._has_battery = True
+        else:
+            self._has_battery = False
+
         self._adc = machine.ADC(self._adc_pin, atten=machine.ADC.ATTN_11DB)
         self._battery_voltage = self._battery_voltage_sample()
+        self._prev_battery_percentages = [1, 1, 1]
+        self._battery_percentage = 1
+        # speeding up the process to get an intial settled value because recursion is hard
+        for i in range(5):
+            self._approximate_battery_percentage()
         self._ts = time.ticks_ms()
 
     def _battery_voltage_sample(self) -> float:
@@ -21,9 +40,12 @@ class Power:
 
     def _update(self) -> None:
         ts = time.ticks_ms()
-        if ts >= self._ts + 1000:
+        if time.ticks_diff(ts, self._ts) > 2000:
             # Sampling takes time, don't do it too often
+            log.debug("has battery: " + str(self._has_battery))
+            log.debug("is charging: " + str(self.battery_charging))
             self._battery_voltage = self._battery_voltage_sample()
+            self._battery_percentage = self._approximate_battery_percentage()
             self._ts = ts
 
     @property
@@ -31,6 +53,10 @@ class Power:
         self._update()
         return self._battery_voltage
 
+    @property
+    def has_battery(self) -> bool:
+        return self._has_battery
+
     @property
     def battery_charging(self) -> bool:
         """
@@ -38,34 +64,156 @@ class Power:
         """
         return sys_kernel.battery_charging()
 
+    @property
+    def battery_percentage(self) -> int:
+        self._update()
+        return self._battery_percentage
 
-def approximate_battery_percentage(voltage: float) -> float:
-    """
-    Returns approximate battery percentage ([0,100]) based on battery voltage
-    (in volts).
-    """
-    if voltage > 4.20:
-        return 100
-    piecewise = [
-        (100, 4.20),
-        (90, 4.06),
-        (80, 3.98),
-        (70, 3.92),
-        (60, 3.87),
-        (50, 3.82),
-        (40, 3.79),
-        (30, 3.77),
-        (20, 3.74),
-        (10, 3.68),
-        (5, 3.45),
-        (0, 3.00),
-    ]
-    for (p1, v1), (p2, v2) in zip(piecewise, piecewise[1:]):
-        if voltage > v1 or voltage < v2:
-            continue
-        vr = v1 - v2
-        pr = p1 - p2
-        vd = voltage - v2
-        p = vd / vr
-        return pr * p + p2
-    return 0
+    def _approximate_battery_percentage(self) -> int:
+        """
+        Returns approximate battery percentage ([0,100]) based on battery voltage.
+        """
+        if not self._has_battery:
+            return 100
+
+        percentage = 0
+        num_samples = 10
+        voltage_readings = []
+        for i in range(num_samples):
+            voltage_readings.append(self._battery_voltage_sample())
+
+        voltage_readings.sort()
+        voltage = voltage_readings[int(num_samples / 2)]
+
+        # print(voltage)
+
+        if voltage > 4.120:
+            percentage = 100
+        # LUT created from Joulescope measurement of "official" 2Ah Battery at 650mW discharge at 26°C and decimated from ~42k samples
+        batLUT = [
+            (99, 4.114),
+            (98, 4.109),
+            (97, 4.091),
+            (96, 4.076),
+            (95, 4.061),
+            (94, 4.048),
+            (93, 4.036),
+            (92, 4.024),
+            (91, 4.012),
+            (90, 4.001),
+            (89, 3.989),
+            (88, 3.978),
+            (87, 3.967),
+            (86, 3.956),
+            (85, 3.945),
+            (84, 3.934),
+            (83, 3.923),
+            (82, 3.912),
+            (81, 3.901),
+            (80, 3.890),
+            (79, 3.879),
+            (78, 3.869),
+            (77, 3.858),
+            (76, 3.847),
+            (75, 3.837),
+            (74, 3.827),
+            (73, 3.817),
+            (72, 3.807),
+            (71, 3.797),
+            (70, 3.788),
+            (69, 3.778),
+            (67, 3.769),
+            (66, 3.759),
+            (65, 3.750),
+            (64, 3.741),
+            (63, 3.732),
+            (62, 3.723),
+            (61, 3.715),
+            (60, 3.706),
+            (59, 3.698),
+            (58, 3.690),
+            (57, 3.682),
+            (56, 3.674),
+            (55, 3.666),
+            (54, 3.659),
+            (53, 3.652),
+            (52, 3.645),
+            (51, 3.639),
+            (50, 3.633),
+            (49, 3.627),
+            (48, 3.622),
+            (47, 3.617),
+            (46, 3.609),
+            (45, 3.605),
+            (44, 3.602),
+            (43, 3.598),
+            (42, 3.595),
+            (41, 3.591),
+            (40, 3.588),
+            (39, 3.584),
+            (38, 3.581),
+            (37, 3.576),
+            (36, 3.573),
+            (35, 3.569),
+            (34, 3.566),
+            (33, 3.563),
+            (32, 3.562),
+            (31, 3.556),
+            (30, 3.552),
+            (29, 3.549),
+            (28, 3.545),
+            (27, 3.541),
+            (26, 3.537),
+            (25, 3.533),
+            (24, 3.529),
+            (23, 3.525),
+            (22, 3.520),
+            (21, 3.515),
+            (20, 3.510),
+            (20, 3.505),
+            (19, 3.499),
+            (18, 3.493),
+            (17, 3.486),
+            (16, 3.479),
+            (15, 3.472),
+            (14, 3.464),
+            (13, 3.455),
+            (12, 3.446),
+            (11, 3.437),
+            (10, 3.426),
+            (9, 3.415),
+            (8, 3.404),
+            (7, 3.391),
+            (6, 3.378),
+            (5, 3.365),
+            (4, 3.348),
+            (3, 3.324),
+            (2, 3.286),
+            (1, 3.223),
+            (1, 3.121),
+            (0, 2.958),
+            (0, 0),
+        ]
+
+        for i in range(len(batLUT)):
+            if voltage >= batLUT[i][1]:
+                percentage = batLUT[i][0]
+                break
+
+        self._prev_battery_percentages.pop(0)
+        self._prev_battery_percentages.append(percentage)
+        log.debug("percentage: " + str(percentage) + " %")
+        log.debug("prev: " + str(self._prev_battery_percentages) + " %")
+        percent_list = self._prev_battery_percentages
+
+        # avoid division by zero in weird edge cases
+        if (sum(percent_list) == 0) or (percent_list[0] == 0):
+            return 0
+
+        for i in range(len(percent_list)):
+            if sum(percent_list) / percent_list[0] == len(percent_list):
+                # all values are the same, we settled on a value (might be the same as before but that's ok)
+                return percentage
+            else:
+                # we're still settling on a value, return previously settled value
+                return self._battery_percentage
diff --git a/python_payload/st3m/ui/elements/overlays.py b/python_payload/st3m/ui/elements/overlays.py
index 8260ef8453f13246f11bbb6faa4df6f64f31f825..d3c0db41905788ef225aafb2ddd85ce1f5133e2e 100644
--- a/python_payload/st3m/ui/elements/overlays.py
+++ b/python_payload/st3m/ui/elements/overlays.py
@@ -11,7 +11,7 @@ from st3m.goose import Dict, Enum, List, ABCBase, abstractmethod, Optional
 from st3m.utils import tau
 from st3m.ui.view import ViewManager
 from st3m.input import power
-from st3m.power import approximate_battery_percentage
+import st3m.power
 from ctx import Context
 import st3m.wifi
 
@@ -561,10 +561,10 @@ class BatteryIcon(Icon):
     def __init__(self) -> None:
         super().__init__()
         self._percent = 100.0
-        self._charging = False
+        self._changed = True
 
     def visible(self) -> bool:
-        return True
+        return power.has_battery
 
     def draw(self, ctx: Context) -> None:
         if self._percent > 30:
@@ -583,30 +583,45 @@ class BatteryIcon(Icon):
         ctx.rectangle(100, -30, -20, 60)
         ctx.fill()
 
-        if self._charging:
-            ctx.gray(1)
-            ctx.line_width = 20
-            ctx.move_to(10, -65 - 10)
-            ctx.line_to(-30, 20 - 10)
-            ctx.line_to(30, -20 - 10)
-            ctx.line_to(-10, 65 - 10)
-            ctx.line_to(-20, 35 - 10)
-            ctx.stroke()
-            ctx.move_to(-10, 65 - 10)
-            ctx.line_to(40, 35 - 10)
-            ctx.stroke()
+        ctx.font = ctx.get_font_name(1)
+        ctx.move_to(-72, 32)
+        ctx.font_size = 100
+        ctx.rgb(255, 255, 255).text(str(self._percent))
 
-        self._changed = False
+    def think(self, ins: InputState, delta_ms: int) -> None:
+        self._percent = power.battery_percentage
+
+
+class ChargingIcon(Icon):
+    WIDTH: int = 20
+
+    def __init__(self) -> None:
+        super().__init__()
+        self._charging = power.battery_charging
+        self._changed = True
+
+    def visible(self) -> bool:
+        if not power.has_battery:
+            return False
+        else:
+            return power.battery_charging
+
+    def draw(self, ctx: Context) -> None:
+        ctx.rgb(255, 255, 255)
+        ctx.gray(1)
+        ctx.line_width = 20
+        ctx.move_to(10, -65)
+        ctx.line_to(-30, 20)
+        ctx.line_to(30, -20)
+        ctx.line_to(-10, 65)
+        ctx.line_to(-20, 35)
+        ctx.stroke()
+        ctx.move_to(-10, 65)
+        ctx.line_to(40, 35)
+        ctx.stroke()
 
     def think(self, ins: InputState, delta_ms: int) -> None:
-        percent = approximate_battery_percentage(power.battery_voltage)
-        charging = power.battery_charging
-        if percent != self._percent:
-            self._percent = percent
-            self._changed = True
-        if charging != self._charging:
-            self._charging = charging
-            self._changed = True
+        self._charging = power.battery_charging
 
 
 class IconTray(Overlay):
@@ -618,6 +633,7 @@ class IconTray(Overlay):
 
     def __init__(self) -> None:
         self.icons = [
+            ChargingIcon(),
             BatteryIcon(),
             USBIcon(),
             WifiIcon(),
diff --git a/sim/fakes/machine.py b/sim/fakes/machine.py
index b0efefe90ed5032adf6dcd424539ab9d1a156c2e..34d8eb7610ca7b9f6de23e7189c2b911d2b8b775 100644
--- a/sim/fakes/machine.py
+++ b/sim/fakes/machine.py
@@ -4,9 +4,12 @@ import sys
 class Pin:
     IN = None
 
-    def __init__(self, _1, _2):
+    def __init__(self, _1, _2, pull=None):
         pass
 
+    def value(self):
+        return 0
+
 
 class ADC:
     ATTN_11DB = None