watches / vector / main.c

/** \file
 * Main program for all of the 30-days watches.
 *
 * The mundane bits involved with power management, setup, etc are
 * handled here.  The bootloader mode is also done here.
 *
 * The watch program only needs to implement:
 *
 * init (called by wakeup)
 * draw
 *
 */
#include "main.h"
#include "draw.h"


static uint32_t not_charging;
static uint8_t shutdown_scheduled;


/** At wake up, sample the boot time in ms and use it to scale
 * the ms timestamp to a time since midnight
 */
static uint32_t ms_since_midnight;
static uint32_t epoch_ms;

/* Track how long the button has been held down for */
static uint8_t do_load;
static uint32_t button_down_ms;




static void
do_empty_loop(void)
{
	static uint8_t last_sec;
	struct pulse_time_tm now;
	pulse_get_time_date(&now);
	if (now.tm_sec == last_sec)
		return;
	last_sec = now.tm_sec;

	printf("%02d:%02d:%02d\n", now.tm_hour, now.tm_min, now.tm_sec);
}



void
main_app_loop(void)
{
	// Limit the update rate to 30 fps == 33 ms/frame
	const uint64_t millis = pulse_get_millis();
	if (button_down_ms && millis - button_down_ms > 1000)
	{
		do_load = !do_load;
		button_down_ms = 0;
		return;
	}
	if (do_load)
	{
		do_empty_loop();
		return;
	}

	// Draw the charging indicator
	color24_t charge_light = not_charging
		? (color24_t){10,10,10}
		: (color24_t){0x80,0,0};

	fill(charge_light, 0, 8, 2, 4);

        // If the not-charging state has not been toggled in the last
        // 30 seconds, schedule a power down soon.
        if (not_charging && millis - not_charging > 60000)
	{
		if (!shutdown_scheduled)
			pulse_update_power_down_timer(1);
		shutdown_scheduled = 1;
		return;
	}

	// Compute the ms since midnight
	uint32_t cur_ms = ms_since_midnight + (millis - epoch_ms);
	draw(cur_ms);
}


static void
wakeup(void *arg)
{
	pulse_oled_set_brightness(100);
	pulse_blank_canvas();
	not_charging = pulse_get_millis();
	shutdown_scheduled = 0;

	// Compute the number of mseconds since midnight
	struct pulse_time_tm now;
	pulse_get_time_date(&now);
	uint32_t sec_since_midnight = 0
		+ now.tm_sec
		+ now.tm_min * 60
		+ now.tm_hour * 3600;
	ms_since_midnight = sec_since_midnight * 1000;
	epoch_ms = pulse_get_millis();
	do_load = 0;
	button_down_ms = 0;

	init();
}


void
main_app_init(void)
{
	// Register a callback for the woke_from_button event
	pulse_register_callback(
		ACTION_WOKE_FROM_BUTTON,
		&wakeup
	);

	wakeup(0);
}


void
main_app_handle_button_down(void)
{
	// Track when the button was pressed and draw a small
	// marker to indicate it since my button is twitchy.
	button_down_ms = pulse_get_millis();

	// Reset the shutdown timer when they hit the button
	if (not_charging)
		not_charging = pulse_get_millis();

	fill(COLOR_BLUE, VSCREEN_WIDTH - 2, VSCREEN_HEIGHT/2 - 2, 2, 4);
}


void
main_app_handle_button_up(void)
{
	button_down_ms = 0;
	fill(COLOR_BLACK, VSCREEN_WIDTH - 2, VSCREEN_HEIGHT/2 - 2, 2, 4);
}


void
main_app_handle_hardware_update(
	enum PulseHardwareEvent event
)
{
	switch (event)
	{
	case BATTERY_CHARGING:
		// Erase the last non-charging time to avoid flakey power
		not_charging = 0;
		break;

	case BATTERY_NOT_CHARGING:
		// Record the time that the state was toggled.
		not_charging = pulse_get_millis();
		break;

	default:
		break;
	}
}


void
main_app_handle_doz(void)
{
	// Fade out
	for (int i = 100; i >= 0; i -= 6)
	{
		pulse_oled_set_brightness(i);
		pulse_mdelay(60);
	}
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.