#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "beep.h" #include "ds1302.h" #include "graphics.h" #include "pins.h" #include "st7789.h" #define LOGGING #define HALL_SENSOR_DEBOUNCE_DELAY_us 10000 #define BUTTON_DEBOUNCE_DELAY_us 3000 #define BUTTON_HOLD_TIME_ms 1000 #define TICKS_PER_REVOLUTION 6 #define SPEED_MEASUREMENT_INTERVAL_ms 100 #define SPEED_MEASUREMENT_WINDOW_SIZE 16 #define SPEED_MEASUREMENT_WINDOW_INTERVAL (SPEED_MEASUREMENT_INTERVAL_ms * SPEED_MEASUREMENT_WINDOW_SIZE) enum GUIScreen { MAIN_SCREEN, SETTINGS_SCREEN, }; enum DisplayMode { DISPLAY_MODE_SPEED, DISPLAY_MODE_DISTANCE_FROM_POWER_ON, DISPLAY_MODE_TOTAL_DISTANCE, }; struct GUIState { uint screen; union { struct { uint displayMode; bool displayModeChanged; } mainScreen; struct { //... } settingsScreen; } screenState; uint8_t drawnTimeHours; uint8_t drawnTimeMinutes; uint16_t drawnTimeTextWidth; }; void on_gpio_interrupt(uint pin, uint32_t eventMask); int64_t hall_sensor_debounce_callback(alarm_id_t alarmId, void *userData); int64_t button_debounce_callback(alarm_id_t alarmId, void *userData); int64_t button_hold_callback(alarm_id_t alarmId, void *userData); bool speed_measure_timer_callback(repeating_timer_t *rt); void on_button_short_press(uint button); void on_button_long_press(uint button); void update_lcd(); void gui_init(struct GUIState *state); void init_button_states(); void save_to_rtc(); bool load_from_rtc(); void reset(); const uint8_t FRONT_BUTTON_PINS[4] = {PIN_BUTTON_0, PIN_BUTTON_1, PIN_BUTTON_2, PIN_BUTTON_3}; const uint16_t BRIGHTNESS_CONVERT_TABLE_X[] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; const uint16_t BRIGHTNESS_CONVERT_TABLE_Y[] = {0, 7, 12, 21, 37, 64, 111, 194, 338, 588, 1024}; const size_t BRIGHTNESS_CONVERT_LABLE_LEN = 11; const uint millimetersPerRevolution = 1540; const uint cmphPerTpw = 360000 * millimetersPerRevolution / SPEED_MEASUREMENT_WINDOW_INTERVAL / TICKS_PER_REVOLUTION; // cm/hour per Ticks/window const uint cmPerTick = millimetersPerRevolution / TICKS_PER_REVOLUTION / 10; // cm per tick int16_t convert_brightness(int16_t input) { for (int i = 0; i < BRIGHTNESS_CONVERT_LABLE_LEN - 1; ++i) { if (input < BRIGHTNESS_CONVERT_TABLE_X[i + 1]) { int16_t x0 = BRIGHTNESS_CONVERT_TABLE_X[i]; int16_t x1 = BRIGHTNESS_CONVERT_TABLE_X[i + 1]; int16_t y0 = BRIGHTNESS_CONVERT_TABLE_Y[i]; int16_t y1 = BRIGHTNESS_CONVERT_TABLE_Y[i + 1]; return y0 + (y1 - y0) * (input - x0) / (x1 - x0); } } return -1; } // for debouncing alarm_id_t hallSensorDebounceAlarm = 0; bool hallSensorLastState = 0; bool hallSensorPrevState = 0; struct { alarm_id_t debounceAlarm, holdAlarm; bool currentState, prevState, isHeld; } buttonStates[4]; // for debug (and for the future) absolute_time_t lastWheelTickTime = 0; // for speed measurement uint8_t intervalWheelTicks[SPEED_MEASUREMENT_WINDOW_SIZE]; uint8_t currentInterval = 0; uint16_t ticksPerWindow = 0; // critical_section_t intervalWheelTicksCritSec; repeating_timer_t speedMeasureTimer; // odometer counters uint totalWheelTicks = 0; uint wheelTicksFromPowerOn = 0; DateTime currentDatetime; // unused currently // repeating_timer_t audioPlaybackTimer; struct GUIState guiState; const uint16_t *find_splash_image_offset(const uint16_t *start) { const uint16_t marker[3] = {0x6f66, 0x626f, 0x7261}; for (int offs = 0; offs < 2 * 1024 * 1024; ++offs) { if (start[offs + 0] == marker[0] && start[offs + 1] == marker[1] && start[offs + 2] == marker[2]) { return start + offs; } } return NULL; } int main() { #ifdef LOGGING stdio_usb_init(); #endif // display init const struct st7789_config displayConfig = { .spi = PICO_DEFAULT_SPI_INSTANCE(), .gpio_din = PIN_LCD_SDA, .gpio_clk = PIN_LCD_SCL, .gpio_cs = PIN_LCD_CS, .gpio_dc = PIN_LCD_DATA_COMMAND, .gpio_rst = PIN_LCD_RESET, .gpio_bl = PIN_LCD_BACKLIGHT, }; st7789_init(&displayConfig, LCD_WIDTH + LCD_OFFSET_X, LCD_HEIGHT + LCD_OFFSET_Y); // splash image (just for fun) const uint16_t *splashImage = (const uint16_t *)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + 1581056); if (splashImage) { st7789_caset(LCD_OFFSET_X, LCD_OFFSET_X + 317); st7789_raset(LCD_OFFSET_Y, LCD_OFFSET_Y + 171); st7789_write(splashImage, 318 * 172 * sizeof(uint16_t)); } else { rom_reset_usb_boot(0, 0); } sleep_ms(1000); #ifdef LOGGING if (psram_is_available()) { printf("PSRAM available. detecting size... "); size_t psramSize = psram_detect_size(); printf("%u\n", psramSize); } #endif // critical_section_init(&intervalWheelTicksCritSec); add_repeating_timer_ms(SPEED_MEASUREMENT_INTERVAL_ms, speed_measure_timer_callback, NULL, &speedMeasureTimer); gpio_set_irq_callback(on_gpio_interrupt); gpio_init(PIN_BEEPER); gpio_set_dir(PIN_BEEPER, GPIO_OUT); // hall sensor gpio_init(PIN_MAIN_HALL_SENSOR); gpio_pull_up(PIN_MAIN_HALL_SENSOR); gpio_set_irq_enabled(PIN_MAIN_HALL_SENSOR, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true); // on-board LED gpio_init(25); gpio_set_dir(25, true); // front buttons init_button_states(); gpio_init_mask(0b1111 << 15); for (int pin = 15; pin <= 18; ++pin) { gpio_pull_up(pin); gpio_set_irq_enabled(pin, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true); } irq_set_enabled(IO_IRQ_BANK0, 1); ds1302_init(); DateTime tmp = {26, 9, 9, 15, 10, 0, DOW_WED}; // ds1302_set_datetime(&tmp); load_from_rtc(); gpio_set_function(PIN_LCD_BACKLIGHT, GPIO_FUNC_PWM); pwm_set_wrap(BACKLIGHT_PWM_SLICE, 1024); pwm_set_chan_level(BACKLIGHT_PWM_SLICE, BACKLIGHT_PWM_CHANNEL, 1024); pwm_set_enabled(BACKLIGHT_PWM_SLICE, true); gpio_set_function(7, GPIO_FUNC_PWM); pwm_set_wrap(3, 256); pwm_set_chan_level(3, PWM_CHAN_B, 0); pwm_set_enabled(3, true); gui_init(&guiState); absolute_time_t nextLcdUpdateTime = delayed_by_ms(get_absolute_time(), 50); absolute_time_t nextClockUpdateTime = get_absolute_time(); absolute_time_t nextSaveToRtcTime = delayed_by_ms(get_absolute_time(), 10000); while (true) { absolute_time_t t = get_absolute_time(); if (t >= nextClockUpdateTime) { nextClockUpdateTime = delayed_by_ms(t, 1000); ds1302_get_datetime(¤tDatetime); printf("time is %i %i %i %i %i %i %i\n", currentDatetime.year, currentDatetime.month, currentDatetime.day, currentDatetime.dow, currentDatetime.hour, currentDatetime.minute, currentDatetime.second); } if (t >= nextLcdUpdateTime) { nextLcdUpdateTime = delayed_by_ms(t, 50); update_lcd(); if (gpio_get(PIN_BUTTON_0) == 0 && gpio_get(PIN_BUTTON_3) == 0) { rom_reset_usb_boot(0, 0); } } if (t >= nextSaveToRtcTime) { nextSaveToRtcTime = delayed_by_ms(t, 10000); save_to_rtc(); } sleep_until(nextLcdUpdateTime); } return 0; } void on_gpio_interrupt(uint pin, uint32_t eventMask) { if (pin == PIN_MAIN_HALL_SENSOR) { if (eventMask & GPIO_IRQ_EDGE_RISE) hallSensorLastState = 0; if (eventMask & GPIO_IRQ_EDGE_FALL) hallSensorLastState = 1; if (hallSensorDebounceAlarm) cancel_alarm(hallSensorDebounceAlarm); hallSensorDebounceAlarm = add_alarm_in_us(HALL_SENSOR_DEBOUNCE_DELAY_us, hall_sensor_debounce_callback, NULL, false); gpio_acknowledge_irq(pin, eventMask); } else if (pin == PIN_BUTTON_0 || pin == PIN_BUTTON_1 || pin == PIN_BUTTON_2 || pin == PIN_BUTTON_3) { uint buttonIndex; switch (pin) { case PIN_BUTTON_0: buttonIndex = 0; break; case PIN_BUTTON_1: buttonIndex = 1; break; case PIN_BUTTON_2: buttonIndex = 2; break; case PIN_BUTTON_3: buttonIndex = 3; } auto buttonState = &buttonStates[buttonIndex]; // if (eventMask & GPIO_IRQ_EDGE_RISE) // buttonState->currentState = 0; // if (eventMask & GPIO_IRQ_EDGE_FALL) // buttonState->currentState = 1; // buttonState->currentState = !gpio_get(pin); if (buttonState->debounceAlarm != -1) cancel_alarm(buttonState->debounceAlarm); buttonState->debounceAlarm = add_alarm_in_us(BUTTON_DEBOUNCE_DELAY_us, button_debounce_callback, (void *)buttonIndex, false); gpio_acknowledge_irq(pin, eventMask); } } int64_t hall_sensor_debounce_callback(alarm_id_t alarmId, void *userData) { (void)alarmId; (void)userData; if (hallSensorLastState != hallSensorPrevState) { absolute_time_t now = get_absolute_time(); lastWheelTickTime = now; ++intervalWheelTicks[currentInterval]; ++totalWheelTicks; ++wheelTicksFromPowerOn; gpio_put(25, !gpio_get(25)); hallSensorPrevState = hallSensorLastState; } return 0; } int64_t button_debounce_callback(alarm_id_t alarmId, void *userData) { (void)alarmId; uint buttonIndex = (uint)userData; auto buttonState = &buttonStates[buttonIndex]; bool currentState = !gpio_get(FRONT_BUTTON_PINS[buttonIndex]); if (currentState != buttonState->prevState) { buttonState->prevState = currentState; if (buttonState->holdAlarm != -1) { cancel_alarm(buttonState->holdAlarm); } if (currentState == 1) { buttonState->holdAlarm = add_alarm_in_ms(BUTTON_HOLD_TIME_ms, button_hold_callback, (void *)buttonIndex, false); } else { buttonState->holdAlarm = -1; if (!buttonState->isHeld) on_button_short_press(buttonIndex); else buttonState->isHeld = false; } } return 0; } int64_t button_hold_callback(alarm_id_t alarmId, void *userData) { (void)alarmId; uint buttonIndex = (uint)userData; auto buttonState = &buttonStates[buttonIndex]; buttonState->holdAlarm = -1; buttonState->isHeld = true; on_button_long_press(buttonIndex); return 0; } void on_button_short_press(uint button) { // beep(60, 900); #ifdef LOGGING printf("button %i short press\n", button); #endif if (button == 3 && guiState.screen == MAIN_SCREEN) { uint oldDispMode = guiState.screenState.mainScreen.displayMode; if (oldDispMode == DISPLAY_MODE_SPEED) guiState.screenState.mainScreen.displayMode = DISPLAY_MODE_DISTANCE_FROM_POWER_ON; else if (oldDispMode == DISPLAY_MODE_DISTANCE_FROM_POWER_ON) guiState.screenState.mainScreen.displayMode = DISPLAY_MODE_TOTAL_DISTANCE; else guiState.screenState.mainScreen.displayMode = DISPLAY_MODE_SPEED; guiState.screenState.mainScreen.displayModeChanged = true; } } void on_button_long_press(uint button) { // beep(60, 1273); #ifdef LOGGING printf("button %i long press\n"); #endif if (button == 2 && buttonStates[3].isHeld || button == 3 && buttonStates[2].isHeld) { // if buttons 2 & 3 pressed and held simultaneously... reset(); } } bool speed_measure_timer_callback(repeating_timer_t *rt) { (void)rt; uint16_t sum = 0; /* printf("window: "); for (int i = 0; i < 16; ++i) { sum += intervalWheelTicks[i]; if (currentInterval == i) printf("[%i] ", intervalWheelTicks[i]); else printf("%i ", intervalWheelTicks[i]); } printf("\n"); */ for (int i = 0; i < 16; ++i) { sum += intervalWheelTicks[i]; } currentInterval = (currentInterval + 1) % 16; intervalWheelTicks[currentInterval] = 0; ticksPerWindow = sum; // printf("Total %i \r", sum); return true; } uint16_t color565_lerp(uint16_t a, uint16_t b, uint alphaPercent) { uint alphaComplement = 100 - alphaPercent; uint16_t red = ((b >> 11) * alphaPercent + (a >> 11) * alphaComplement) / 100; uint16_t green = (((b >> 5) & 0x3F) * alphaPercent + ((a >> 5) & 0x3F) * alphaComplement) / 100; uint16_t blue = ((b & 0x1F) * alphaPercent + (a & 0x1F) * alphaComplement) / 100; return blue | (green << 5) | (red << 11); } // speed is in "2.1" decimal fixed-point, in km/h void draw_speed(uint value) { #define DIGITS_START_X 20 #define DIGITS_START_Y 37 #define DIGIT_WIDTH 64 #define DIGIT_SPACING 8 if (value > 999) value = 999; const uint16_t speedToColorX[] = {0, 50, 100, 150, 200, 300}; const uint16_t speedToColorY[] = {0x04ff, 0x27fb, 0x6fe4, 0xdfe4, 0xfe84, 0xfd04}; const size_t speedToColorPointCount = 6; uint16_t color = speedToColorY[speedToColorPointCount - 1]; for (int i = 1; i < speedToColorPointCount; ++i) { if (value <= speedToColorX[i]) { color = color565_lerp(speedToColorY[i - 1], speedToColorY[i], (value - speedToColorX[i - 1]) * 100 / (speedToColorX[i] - speedToColorX[i - 1])); break; } } lcd_draw_digit(value / 100, DIGITS_START_X, DIGITS_START_Y, color, 0x528a); lcd_draw_digit((value / 10) % 10, DIGITS_START_X + DIGIT_WIDTH + DIGIT_SPACING, DIGITS_START_Y, color, 0x528a); lcd_draw_decimal_point(DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING, DIGITS_START_Y + 101, color); lcd_draw_digit(value % 10, DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + 5, DIGITS_START_Y, color, 0x528a); #undef DIGITS_START_X #undef DIGITS_START_Y #undef DIGIT_WIDTH #undef DIGIT_SPACING } // distance is in "2.3" decimal fixed-point, in kilometers void draw_short_term_distance(uint value) { #define DIGITS_START_X 20 #define DIGITS_START_Y 37 #define SMALL_DIGITS_Y_OFFSET 28 #define XSMALL_DIGITS_Y_OFFSET 60 #define DIGIT_WIDTH 64 #define SMALL_DIGIT_WIDTH 48 #define XSMALL_DIGIT_WIDTH 30 #define DIGIT_SPACING 8 #define SMALL_DIGIT_SPACING 6 #define XSMALL_DIGIT_SPACING 4 if (value > 99999) value = 99999; lcd_draw_digit(value / 10000, DIGITS_START_X, DIGITS_START_Y, 0xffff, 0x528a); lcd_draw_digit((value / 1000) % 10, DIGITS_START_X + DIGIT_WIDTH + DIGIT_SPACING, DIGITS_START_Y, 0xffff, 0x528a); lcd_draw_decimal_point(DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING, DIGITS_START_Y + 101, 0xffff); lcd_draw_small_digit((value / 100) % 10, DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + 10, DIGITS_START_Y + SMALL_DIGITS_Y_OFFSET, 0xffff, 0x528a); lcd_draw_small_digit((value / 10) % 10, DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + SMALL_DIGIT_WIDTH + SMALL_DIGIT_SPACING + 10, DIGITS_START_Y + SMALL_DIGITS_Y_OFFSET, 0xffff, 0x528a); lcd_draw_xsmall_digit(value % 10, DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + SMALL_DIGIT_WIDTH * 2 + SMALL_DIGIT_SPACING * 2 + 10, DIGITS_START_Y + XSMALL_DIGITS_Y_OFFSET, 0xffff, 0x528a); #undef DIGITS_START_X #undef DIGITS_START_Y #undef DIGIT_WIDTH #undef DIGIT_SPACING #undef XSMALL_DIGITS_Y_OFFSET } // long term distance is in "4.2" decimal fixed-point, in kilometers void draw_long_term_distance(uint value) { #define DIGITS_START_X 15 #define DIGITS_START_Y 50 #define XSMALL_DIGITS_Y_OFFSET 32 #define XSMALL_DIGIT_WIDTH 30 #define XSMALL_DIGIT_SPACING 4 if (value > 999999) value = 999999; lcd_draw_small_digit(value / 100000, DIGITS_START_X, DIGITS_START_Y, 0xffff, 0x528a); lcd_draw_small_digit((value / 10000) % 10, DIGITS_START_X + SMALL_DIGIT_WIDTH + SMALL_DIGIT_SPACING, DIGITS_START_Y, 0xffff, 0x528a); lcd_draw_small_digit((value / 1000) % 10, DIGITS_START_X + SMALL_DIGIT_WIDTH * 2 + SMALL_DIGIT_SPACING * 2, DIGITS_START_Y, 0xffff, 0x528a); lcd_draw_small_digit((value / 100) % 10, DIGITS_START_X + SMALL_DIGIT_WIDTH * 3 + SMALL_DIGIT_SPACING * 3, DIGITS_START_Y, 0xffff, 0x528a); lcd_draw_decimal_point(DIGITS_START_X + SMALL_DIGIT_WIDTH * 4 + SMALL_DIGIT_SPACING * 4 - 6, DIGITS_START_Y + 76, 0xffff); lcd_draw_xsmall_digit((value / 10) % 10, DIGITS_START_X + SMALL_DIGIT_WIDTH * 4 + SMALL_DIGIT_SPACING * 4 + 10, DIGITS_START_Y + XSMALL_DIGITS_Y_OFFSET, 0xffff, 0x528a); lcd_draw_xsmall_digit(value % 10, DIGITS_START_X + SMALL_DIGIT_WIDTH * 4 + SMALL_DIGIT_SPACING * 4 + XSMALL_DIGIT_WIDTH + XSMALL_DIGIT_SPACING + 10, DIGITS_START_Y + XSMALL_DIGITS_Y_OFFSET, 0xffff, 0x528a); #undef DIGITS_START_X #undef DIGITS_START_Y #undef XSMALL_DIGITS_Y_OFFSET #undef XSMALL_DIGIT_WIDTH #undef XSMALL_DIGIT_SPACING } void update_lcd() { if (guiState.screen == MAIN_SCREEN) { bool forceRedrawClock = guiState.screenState.mainScreen.displayModeChanged; if (guiState.screenState.mainScreen.displayModeChanged) { // lcd_fill_rectangle(49, 27, 213, 113, 0x0000); st7789_fill(0x0000); guiState.screenState.mainScreen.displayModeChanged = false; } if (guiState.screenState.mainScreen.displayMode == DISPLAY_MODE_SPEED) { uint convertedSpeed = ticksPerWindow * cmphPerTpw / 10000; draw_speed(convertedSpeed); lcd_write_string("Скорость", 10, 0, 0xffff, 0x0000); lcd_write_string("км/ч", 241, 126, 0xffff, 0x0000); } else if (guiState.screenState.mainScreen.displayMode == DISPLAY_MODE_DISTANCE_FROM_POWER_ON) { uint convertedDistance = wheelTicksFromPowerOn * cmPerTick / 100; draw_short_term_distance(convertedDistance); lcd_write_string("Текущий путь", 10, 0, 0xffff, 0x0000); lcd_write_string("км", 280, 37, 0xffff, 0x0000); } else if (guiState.screenState.mainScreen.displayMode == DISPLAY_MODE_TOTAL_DISTANCE) { uint convertedDistance = totalWheelTicks * cmPerTick / 1000; draw_long_term_distance(convertedDistance); lcd_write_string("Общий путь", 10, 0, 0xffff, 0x0000); lcd_write_string("км", 280, 37, 0xffff, 0x0000); } if (forceRedrawClock || guiState.drawnTimeHours != currentDatetime.hour || guiState.drawnTimeMinutes != currentDatetime.minute) { lcd_fill_rectangle(305 - guiState.drawnTimeTextWidth, 0, guiState.drawnTimeTextWidth, 24, 0x0000); guiState.drawnTimeHours = currentDatetime.hour; guiState.drawnTimeMinutes = currentDatetime.minute; char timeDisplay[6]; memset(timeDisplay, ' ', sizeof(timeDisplay)); timeDisplay[5] = 0; sprintf(timeDisplay, "%i:%02i", currentDatetime.hour, currentDatetime.minute); lcd_write_string(timeDisplay, 305 - lcd_string_width(timeDisplay), 0, 0xffff, 0x0000); guiState.drawnTimeTextWidth = lcd_string_width(timeDisplay); } } else if (guiState.screen == SETTINGS_SCREEN) { } } void gui_init(struct GUIState *state) { state->screen = 0; state->screenState.mainScreen.displayMode = DISPLAY_MODE_DISTANCE_FROM_POWER_ON; state->screenState.mainScreen.displayModeChanged = true; state->drawnTimeTextWidth = 0; state->drawnTimeHours = 24; state->drawnTimeMinutes = 60; } void init_button_states() { for (int i = 0; i < 4; ++i) { buttonStates[i].debounceAlarm = -100; buttonStates[i].holdAlarm = -1; buttonStates[i].currentState = 0; buttonStates[i].prevState = 0; buttonStates[i].isHeld = 0; } } void reset() { pwm_set_chan_level(BACKLIGHT_PWM_SLICE, BACKLIGHT_PWM_CHANNEL, 0); // turn off the screen (*((volatile uint32_t *)(PPB_BASE + 0x0ED0C))) = 0x5FA0004; // weird trick to actually reset the controller } void save_to_rtc() { printf("saving distance to RTC... "); static bool savedBefore = false; static uint savedDistance; uint8_t buffer[31]; memset(buffer, 0, 31); uint newDistance = totalWheelTicks * cmPerTick / 10; if (savedBefore && newDistance == savedDistance) { printf("skipped saving\n"); return; } *(uint *)(buffer + 0) = newDistance; uint16_t checksum = 0; for (int i = 0; i < 29; ++i) { checksum += buffer[i]; } *(uint16_t *)(buffer + 29) = checksum; uint8_t checkBuffer[31]; int limit = 10; do { printf("\nattempting to write...\n"); ds1302_set_write_protection(0); // for (int i = 0; i < 31; ++i) { // ds1302_set_ram_byte(0xC0 + i * 2, buffer + i); // sleep_us(2); // } ds1302_write_ram_bulk(buffer, 31); for (int i = 0; i < 30; ++i) printf("%02x ", buffer[i]); printf("%02x\n", buffer[30]); ds1302_read_ram_bulk(checkBuffer, 31); printf("attempting to read back...\n"); for (int i = 0; i < 30; ++i) printf("%02x ", checkBuffer[i]); printf("%02x\n", checkBuffer[30]); if (limit-- == 0) break; } while (memcmp(buffer, checkBuffer, 31) != 0); #ifdef LOGGING for (int i = 0; i < 30; ++i) printf("%02x ", buffer[i]); printf("%02x\n", buffer[30]); #endif savedDistance = newDistance; savedBefore = true; #ifdef LOGGING printf("saved\n"); #endif } bool load_from_rtc() { uint8_t buffer[31]; ds1302_read_ram_bulk(buffer, 31); #ifdef LOGGING for (int i = 0; i < 30; ++i) printf("%02x ", buffer[i]); printf("%02x\n", buffer[30]); #endif uint16_t checksum = *(uint16_t *)(buffer + 29); uint16_t actualChecksum = 0; for (int i = 0; i < 29; ++i) { actualChecksum += buffer[i]; } if (actualChecksum != checksum) { #ifdef LOGGING printf("discarding invalid data in RTC memory\n"); #endif return false; } uint savedDistance = *(uint *)(buffer + 0); totalWheelTicks = savedDistance * 10 / cmPerTick; #ifdef LOGGING printf("successfully loaded data from RTC memory\n"); #endif return true; }