diff --git a/firmware/images/error_laser.bmp b/firmware/images/error_laser.bmp new file mode 100644 index 0000000..b94c42a Binary files /dev/null and b/firmware/images/error_laser.bmp differ diff --git a/firmware/images/error_magnetic.bmp b/firmware/images/error_magnetic.bmp new file mode 100644 index 0000000..f258a1e Binary files /dev/null and b/firmware/images/error_magnetic.bmp differ diff --git a/firmware/images/error_movement.bmp b/firmware/images/error_movement.bmp new file mode 100644 index 0000000..2980dd2 Binary files /dev/null and b/firmware/images/error_movement.bmp differ diff --git a/firmware/measure.py b/firmware/measure.py index 0dd5c1d..b75ccd9 100644 --- a/firmware/measure.py +++ b/firmware/measure.py @@ -117,40 +117,56 @@ async def take_reading(devices: hardware.HardwareBase, disp: display.DisplayBase) -> bool: # take a reading try: - mag, grav, distance = await get_raw_measurement(devices, disp, True) - if cfg.calib is None: - raise NotCalibrated() - # noinspection PyTypeChecker - azimuth, inclination, _ = cfg.calib.get_angles(mag, grav) - distance += cfg.laser_cal - logger.debug(f"Distance: {distance}m") - if cfg.anomaly_strictness is not None: + try: + mag, grav, distance = await get_raw_measurement(devices, disp, True) + if cfg.calib is None: + raise NotCalibrated() # noinspection PyTypeChecker - cfg.calib.raise_if_anomaly(mag, grav, cfg.anomaly_strictness) - except tuple(ERROR_MESSAGES.keys()) as exc: - for key in ERROR_MESSAGES.keys(): - if isinstance(exc, key): - disp.show_big_info(ERROR_MESSAGES[key]) - logger.info(f"Measurement error: {repr(exc)}") - if not isinstance(exc, asyncio.TimeoutError): - # don't wibble the laser if it's timed out, it'll just get more confused - devices.flash_laser(5,0.1) - devices.beep_sad() - await asyncio.sleep(0) - return False - else: - leg = Leg(azimuth, inclination, distance) - readings.store_reading(leg, cfg) - devices.bt.disto.send_data(azimuth, inclination, distance) - if readings.triple_shot(): - devices.flash_laser(2,0.2) - devices.beep_happy() + azimuth, inclination, _ = cfg.calib.get_angles(mag, grav) + distance += cfg.laser_cal + logger.debug(f"Distance: {distance}m") + if cfg.anomaly_strictness is not None: + # noinspection PyTypeChecker + cfg.calib.raise_if_anomaly(mag, grav, cfg.anomaly_strictness) + except tuple(ERROR_MESSAGES.keys()) as exc: + for key in ERROR_MESSAGES.keys(): + if isinstance(exc, key): + # spic17: for error messages happening often during measurement display bitmaps instead of text + # because this saves a lot of memory and thus significantly reduces + # the number of out of memory exceptions + if (key == MagneticAnomalyError) or (key == DipAnomalyError): + disp.show_bitmap_info('error_magnetic') + elif (key == GravityAnomalyError): + disp.show_bitmap_info('error_movement') + elif (key == LaserError): + disp.show_bitmap_info('error_laser') + else: + # all other error messages + disp.show_big_info(ERROR_MESSAGES[key]) + logger.info(f"Measurement error: {repr(exc)}") + if not isinstance(exc, asyncio.TimeoutError): + # don't wibble the laser if it's timed out, it'll just get more confused + devices.flash_laser(5,0.1) + devices.beep_sad() + await asyncio.sleep(0) + return False else: - devices.beep_bip() - await asyncio.sleep(0) - return True - - + leg = Leg(azimuth, inclination, distance) + readings.store_reading(leg, cfg) + devices.bt.disto.send_data(azimuth, inclination, distance) + if readings.triple_shot(): + devices.flash_laser(2,0.2) + devices.beep_happy() + else: + devices.beep_bip() + await asyncio.sleep(0) + return True + except MemoryError: + # spic17: better not do anything not strictly neccessary in these delicate moments after a memory error + # TODO: do something more drastic here after maybe 3 occurences (reboot?) + # because the device may not be able to recover otherwise? + return False + async def take_multiple_readings(devices, disp, fname, prelude, reminder): devices.laser_enable(True) disp.show_info(prelude) diff --git a/firmware/versions/display128x64.py b/firmware/versions/display128x64.py index ae744e0..4d6313b 100644 --- a/firmware/versions/display128x64.py +++ b/firmware/versions/display128x64.py @@ -55,17 +55,26 @@ def __init__(self, oled: BusDisplay, config: Config): @staticmethod def create_big_text_group(big_text: Sequence[str], index_txt): - measurement_group = displayio.Group() - azimuth = label.Label(font_20, text=big_text[0], color=0xffffff, x=1, y=9) - inclination = label.Label(font_20, text=big_text[1], color=0xffffff, x=1, y=31) - distance = label.Label(font_20, text=big_text[2], color=0xffffff, x=1, y=53) - reading_index = label.Label(terminalio.FONT, text=index_txt, color=0xffffff) - reading_index.anchored_position = (127, 32) - reading_index.anchor_point = (1.0, 0.5) - measurement_group.append(azimuth) - measurement_group.append(inclination) - measurement_group.append(distance) - measurement_group.append(reading_index) + # spic17: watchout for memory exceptions here. + try: + measurement_group = displayio.Group() + azimuth = label.Label(font_20, text=big_text[0], color=0xffffff, x=1, y=9) + inclination = label.Label(font_20, text=big_text[1], color=0xffffff, x=1, y=31) + distance = label.Label(font_20, text=big_text[2], color=0xffffff, x=1, y=53) + reading_index = label.Label(terminalio.FONT, text=index_txt, color=0xffffff) + reading_index.anchored_position = (127, 32) + reading_index.anchor_point = (1.0, 0.5) + measurement_group.append(azimuth) + measurement_group.append(inclination) + measurement_group.append(distance) + measurement_group.append(reading_index) + except MemoryError: + # Out of memory error. Catch and display nothing. Oftentimes we will recover afterwards + # (this seems purely a matter of garbage collection mechanisms, which seem not very + # predictable in this embedded system) + # Maybe use 'BitmapLabel' for optimization? + # Maybe call check_mem to try invoking garbage collection? + measurement_group = displayio.Group() return measurement_group def _set_group_with_icons(self, group): @@ -153,8 +162,25 @@ def show_info(self, text, clean=False): def show_big_info(self, text): group = self.create_big_text_group(text.splitlines(), "") - self.show_group(group) - + logger.debug("show_big_info tvo") + try: + self.show_group(group) + except MemoryError: + # spic17: do nothing when memory is spent. We may still recover when gc occurs. + pass + + def show_bitmap_info(self, bitmap_name): + group = displayio.Group() + info_bmp = bitmaps[bitmap_name] + info_tile = displayio.TileGrid(info_bmp, pixel_shader=palette, x=0, y=0) + group.append(info_tile) + try: + self._set_group_with_icons(group) + self.refresh() + except MemoryError: + # spic17: do nothing. Doing anything here may lead to an outer memory error. + # hopefully, we recover - otherwise we are off no worse than when crashing directly. + pass def show_group(self, group: Optional[displayio.Group]): self.oled.root_group = group self.refresh()