'movement' -> 'legacy' to signal things we still need to bring in
This commit is contained in:
477
legacy/watch_faces/sensor/accelerometer_data_acquisition_face.c
Normal file
477
legacy/watch_faces/sensor/accelerometer_data_acquisition_face.c
Normal file
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Joey Castillo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "accelerometer_data_acquisition_face.h"
|
||||
#include "watch_utility.h"
|
||||
#include "lis2dw.h"
|
||||
#include "spiflash.h"
|
||||
|
||||
#define ACCELEROMETER_RANGE LIS2DW_RANGE_4_G
|
||||
#define ACCELEROMETER_LPMODE LIS2DW_LP_MODE_2
|
||||
#define ACCELEROMETER_FILTER LIS2DW_BANDWIDTH_FILTER_DIV2
|
||||
#define ACCELEROMETER_LOW_NOISE true
|
||||
#define SECONDS_TO_RECORD 15
|
||||
|
||||
static const char activity_types[][3] = {
|
||||
"TE", // Testing
|
||||
"ID", // Idle
|
||||
"OF", // Off-wrist
|
||||
"SL", // Sleeping
|
||||
"WH", // Washing Hands
|
||||
"WA", // Walking
|
||||
"WB", // Walking with Beverage
|
||||
"JO", // Jogging
|
||||
"RU", // Running
|
||||
"BI", // Biking
|
||||
"HI", // Hiking
|
||||
"EL", // Elliptical
|
||||
"SU", // Stairs Up
|
||||
"SD", // Stairs Down
|
||||
"WL", // Weight Lifting
|
||||
};
|
||||
|
||||
static void update(accelerometer_data_acquisition_state_t *state);
|
||||
static void update_settings(accelerometer_data_acquisition_state_t *state);
|
||||
static void advance_current_setting(accelerometer_data_acquisition_state_t *state);
|
||||
static void start_reading(accelerometer_data_acquisition_state_t *state);
|
||||
static void continue_reading(accelerometer_data_acquisition_state_t *state);
|
||||
static void finish_reading(accelerometer_data_acquisition_state_t *state);
|
||||
static bool wait_for_flash_ready(void);
|
||||
static int16_t get_next_available_page(void);
|
||||
static void write_buffer_to_page(uint8_t *buf, uint16_t page);
|
||||
static void write_page(accelerometer_data_acquisition_state_t *state);
|
||||
static void log_data_point(accelerometer_data_acquisition_state_t *state, lis2dw_reading_t reading, uint8_t centiseconds);
|
||||
|
||||
void accelerometer_data_acquisition_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)*context_ptr;
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(accelerometer_data_acquisition_state_t));
|
||||
memset(*context_ptr, 0, sizeof(accelerometer_data_acquisition_state_t));
|
||||
state = (accelerometer_data_acquisition_state_t *)*context_ptr;
|
||||
state->beep_with_countdown = true;
|
||||
state->countdown_length = 3;
|
||||
}
|
||||
spi_flash_init();
|
||||
wait_for_flash_ready();
|
||||
uint8_t buf[256] = {0xFF};
|
||||
spi_flash_read_data(0, buf, 256);
|
||||
if (buf[0] & 0xF0) {
|
||||
// mark first four pages as used
|
||||
buf[0] = 0x0F;
|
||||
wait_for_flash_ready();
|
||||
HAL_GPIO_A3_clr();
|
||||
spi_flash_command(CMD_ENABLE_WRITE);
|
||||
wait_for_flash_ready();
|
||||
spi_flash_write_data(0, buf, 256);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void accelerometer_data_acquisition_face_activate(void *context) {
|
||||
accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context;
|
||||
state->next_available_page = get_next_available_page();
|
||||
}
|
||||
|
||||
bool accelerometer_data_acquisition_face_loop(movement_event_t event, void *context) {
|
||||
accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
switch (state->mode) {
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
|
||||
update(state);
|
||||
if (state->repeat_ticks > 0) {
|
||||
state->repeat_ticks--;
|
||||
if (state->repeat_ticks == 0) {
|
||||
state->countdown_ticks = state->countdown_length;
|
||||
state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN:
|
||||
if (state->next_available_page < 0) {
|
||||
state->countdown_ticks = 0;
|
||||
state->repeat_ticks = 0;
|
||||
state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
|
||||
}
|
||||
if (state->countdown_ticks > 0) {
|
||||
state->countdown_ticks--;
|
||||
printf("countdown: %d\n", state->countdown_ticks);
|
||||
if (state->countdown_ticks == 0) {
|
||||
// at zero, begin reading
|
||||
state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING;
|
||||
state->reading_ticks = SECONDS_TO_RECORD + 1;
|
||||
// also beep if the user asked for it
|
||||
if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
|
||||
start_reading(state);
|
||||
} else if (state->countdown_ticks < 3) {
|
||||
// beep for last two ticks before reading
|
||||
if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C5, 75);
|
||||
}
|
||||
}
|
||||
update(state);
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING:
|
||||
if (state->reading_ticks > 0) {
|
||||
state->reading_ticks--;
|
||||
if (state->reading_ticks > 0) {
|
||||
continue_reading(state);
|
||||
} else {
|
||||
finish_reading(state);
|
||||
state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C4, 125);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 50);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C4, 125);
|
||||
}
|
||||
}
|
||||
update(state);
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS:
|
||||
update_settings(state);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
switch (state->mode) {
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
|
||||
state->activity_type_index = (state->activity_type_index + 1) % (sizeof(activity_types) / sizeof(activity_types[0]));
|
||||
update(state);
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS:
|
||||
state->settings_page++;
|
||||
if (state->settings_page > ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT) {
|
||||
state->settings_page = 0;
|
||||
state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
|
||||
update(state);
|
||||
} else {
|
||||
update_settings(state);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
movement_illuminate_led();
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
printf("Alarm up! Mode is %d\n", state->mode);
|
||||
switch (state->mode) {
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
|
||||
state->countdown_ticks = state->countdown_length;
|
||||
printf("Setting countdown ticks to %d\n", state->countdown_ticks);
|
||||
state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN;
|
||||
printf("and mode to %d\n", state->mode);
|
||||
update(state);
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN:
|
||||
// cancel countdown
|
||||
state->countdown_ticks = 0;
|
||||
state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
|
||||
update(state);
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS:
|
||||
advance_current_setting(state);
|
||||
update_settings(state);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
printf("Alarm long\n");
|
||||
if (state->mode == ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE) {
|
||||
state->repeat_ticks = 0;
|
||||
state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS;
|
||||
update_settings(state);
|
||||
}
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
// don't light up every time light is hit
|
||||
break;
|
||||
default:
|
||||
movement_default_loop_handler(event);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void accelerometer_data_acquisition_face_resign(void *context) {
|
||||
accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context;
|
||||
if (state->reading_ticks) {
|
||||
state->reading_ticks = 0;
|
||||
finish_reading(state);
|
||||
}
|
||||
state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE;
|
||||
state->settings_page = 0;
|
||||
state->countdown_ticks = 0;
|
||||
state->repeat_ticks = 0;
|
||||
state->reading_ticks = 0;
|
||||
}
|
||||
|
||||
static void update(accelerometer_data_acquisition_state_t *state) {
|
||||
char buf[14];
|
||||
uint8_t ticks = 0;
|
||||
switch (state->mode) {
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE:
|
||||
ticks = state->countdown_length;
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN:
|
||||
ticks = state->countdown_ticks;
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING:
|
||||
ticks = state->reading_ticks;
|
||||
break;
|
||||
default:
|
||||
ticks = 0;
|
||||
break;
|
||||
}
|
||||
sprintf(buf, "%s%2dre%2d#o",
|
||||
activity_types[state->activity_type_index],
|
||||
ticks,
|
||||
(8192 - state->next_available_page) / 82);
|
||||
watch_display_string(buf, 0);
|
||||
|
||||
watch_set_colon();
|
||||
|
||||
// special case: display full if full, <1% if nearly full
|
||||
if (state->next_available_page < 0) watch_display_string(" FUL", 6);
|
||||
else if (state->next_available_page > 8110) watch_display_string("<1", 6);
|
||||
|
||||
// Bell if beep enabled
|
||||
if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
|
||||
// Signal if sensing
|
||||
if (state->reading_ticks) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
|
||||
// LAP if repeating
|
||||
if (state->repeat_ticks) watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_LAP);
|
||||
|
||||
}
|
||||
|
||||
static void update_settings(accelerometer_data_acquisition_state_t *state) {
|
||||
char buf[13];
|
||||
watch_clear_colon();
|
||||
if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
switch (state->settings_page) {
|
||||
case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND:
|
||||
sprintf(buf, "SO Beep %c", state->beep_with_countdown ? 'Y' : 'N');
|
||||
watch_display_string(buf, 0);
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY:
|
||||
sprintf(buf, "DL %2d SeC", state->countdown_length);
|
||||
watch_display_string(buf, 0);
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT:
|
||||
if (state->repeat_interval == 0) {
|
||||
watch_display_string("rE none ", 0);
|
||||
} else {
|
||||
sprintf(buf, "rE %2dn&in", state->repeat_interval / 60);
|
||||
watch_display_string(buf, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void advance_current_setting(accelerometer_data_acquisition_state_t *state) {
|
||||
switch (state->settings_page) {
|
||||
case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND:
|
||||
state->beep_with_countdown = !state->beep_with_countdown;
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY:
|
||||
// this is so lazy, i'm sorry
|
||||
if (state->countdown_length == 1) state->countdown_length = 3;
|
||||
else if (state->countdown_length == 3) state->countdown_length = 10;
|
||||
else if (state->countdown_length == 10) state->countdown_length = 30;
|
||||
else state->countdown_length = 1;
|
||||
break;
|
||||
case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT:
|
||||
if (state->repeat_interval == 0) state->repeat_interval = 60;
|
||||
else if (state->repeat_interval == 60) state->repeat_interval = 600;
|
||||
else if (state->repeat_interval == 600) state->repeat_interval = 1800;
|
||||
else if (state->repeat_interval == 1800) state->repeat_interval = 3600;
|
||||
else state->repeat_interval = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int16_t get_next_available_page(void) {
|
||||
uint8_t buf[256] = {0};
|
||||
|
||||
uint16_t page = 0;
|
||||
for(int16_t i = 0; i < 4; i++) {
|
||||
wait_for_flash_ready();
|
||||
spi_flash_read_data(i * 256, buf, 256);
|
||||
for(int16_t j = 0; j < 256; j++) {
|
||||
if(buf[j] == 0) {
|
||||
page += 8;
|
||||
} else {
|
||||
page += __builtin_clz(((uint32_t)buf[j]) << 24);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (page >= 8192) return -1;
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
static void write_buffer_to_page(uint8_t *buf, uint16_t page) {
|
||||
uint32_t address = 256 * page;
|
||||
|
||||
wait_for_flash_ready();
|
||||
HAL_GPIO_A3_clr();
|
||||
spi_flash_command(CMD_ENABLE_WRITE);
|
||||
wait_for_flash_ready();
|
||||
HAL_GPIO_A3_clr();
|
||||
spi_flash_write_data(address, buf, 256);
|
||||
wait_for_flash_ready();
|
||||
|
||||
uint8_t buf2[256];
|
||||
HAL_GPIO_A3_clr();
|
||||
spi_flash_read_data(address, buf2, 256);
|
||||
wait_for_flash_ready();
|
||||
|
||||
uint8_t used_pages[256] = {0xFF};
|
||||
uint16_t address_to_mark_used = page / 8;
|
||||
uint8_t header_page = address_to_mark_used / 256;
|
||||
uint8_t used_byte = 0x7F >> (page % 8);
|
||||
uint8_t offset_in_buf = address_to_mark_used % 256;
|
||||
|
||||
printf("\twrite 256 bytes to address %ld, page %d.\n", address, page);
|
||||
for(int i = 0; i < 256; i++) {
|
||||
if (buf[i] != buf2[i]) {
|
||||
printf("\tData mismatch detected at offset %d: %d != %d.\n", i, buf[i], buf2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
HAL_GPIO_A3_clr();
|
||||
spi_flash_read_data(header_page * 256, used_pages, 256);
|
||||
used_pages[offset_in_buf] = used_byte;
|
||||
HAL_GPIO_A3_clr();
|
||||
spi_flash_command(CMD_ENABLE_WRITE);
|
||||
wait_for_flash_ready();
|
||||
HAL_GPIO_A3_clr();
|
||||
spi_flash_write_data(header_page * 256, used_pages, 256);
|
||||
wait_for_flash_ready();
|
||||
}
|
||||
|
||||
static bool wait_for_flash_ready(void) {
|
||||
HAL_GPIO_A3_clr();
|
||||
bool ok = true;
|
||||
uint8_t read_status_response[1] = {0x00};
|
||||
do {
|
||||
ok = spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1);
|
||||
} while ((read_status_response[0] & 0x3) != 0);
|
||||
delay_ms(1); // why do i need this?
|
||||
HAL_GPIO_A3_set();
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void write_page(accelerometer_data_acquisition_state_t *state) {
|
||||
if (state->next_available_page > 0) {
|
||||
write_buffer_to_page((uint8_t *)(state->records), state->next_available_page);
|
||||
wait_for_flash_ready();
|
||||
state->next_available_page++;
|
||||
}
|
||||
state->pos = 0;
|
||||
memset(state->records, 0xFF, sizeof(state->records));
|
||||
}
|
||||
|
||||
static void log_data_point(accelerometer_data_acquisition_state_t *state, lis2dw_reading_t reading, uint8_t centiseconds) {
|
||||
accelerometer_data_acquisition_record_t record;
|
||||
record.data.x.record_type = ACCELEROMETER_DATA_ACQUISITION_DATA;
|
||||
record.data.y.lpmode = ACCELEROMETER_LPMODE;
|
||||
record.data.z.filter = ACCELEROMETER_FILTER;
|
||||
record.data.x.accel = (reading.x >> 2) + 8192;
|
||||
record.data.y.accel = (reading.y >> 2) + 8192;
|
||||
record.data.z.accel = (reading.z >> 2) + 8192;
|
||||
record.data.counter = 100 * (SECONDS_TO_RECORD - state->reading_ticks + 1) + centiseconds;
|
||||
printf("logged data point for %d\n", record.data.counter);
|
||||
state->records[state->pos++] = record;
|
||||
if (state->pos >= 32) {
|
||||
write_page(state);
|
||||
}
|
||||
}
|
||||
|
||||
static void start_reading(accelerometer_data_acquisition_state_t *state) {
|
||||
printf("Start reading\n");
|
||||
watch_enable_i2c();
|
||||
lis2dw_begin();
|
||||
lis2dw_set_data_rate(LIS2DW_DATA_RATE_25_HZ);
|
||||
lis2dw_set_range(ACCELEROMETER_RANGE);
|
||||
lis2dw_set_low_power_mode(ACCELEROMETER_LPMODE);
|
||||
lis2dw_set_bandwidth_filtering(ACCELEROMETER_FILTER);
|
||||
if (ACCELEROMETER_LOW_NOISE) lis2dw_set_low_noise_mode(true);
|
||||
lis2dw_enable_fifo();
|
||||
|
||||
accelerometer_data_acquisition_record_t record;
|
||||
watch_date_time_t date_time = watch_rtc_get_date_time();
|
||||
state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
|
||||
record.header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_HEADER;
|
||||
record.header.info.range = ACCELEROMETER_RANGE;
|
||||
record.header.info.temperature = lis2dw_get_temperature();
|
||||
record.header.char1 = activity_types[state->activity_type_index][0];
|
||||
record.header.char2 = activity_types[state->activity_type_index][1];
|
||||
record.header.timestamp = state->starting_timestamp;
|
||||
|
||||
state->records[state->pos++] = record;
|
||||
lis2dw_fifo_t fifo;
|
||||
lis2dw_read_fifo(&fifo); // dump the fifo, this starts a fresh round of data in continue_reading
|
||||
}
|
||||
|
||||
static void continue_reading(accelerometer_data_acquisition_state_t *state) {
|
||||
printf("Continue reading\n");
|
||||
lis2dw_fifo_t fifo;
|
||||
lis2dw_read_fifo(&fifo);
|
||||
|
||||
fifo.count = min(fifo.count, 25); // hacky, but we need a consistent data rate; if we got a 26th data point, chuck it.
|
||||
uint8_t offset = 4 * (25 - fifo.count); // also hacky: we're sometimes short at the start. align to beginning of next second.
|
||||
// TODO: use the threshold interrupt for this, will mean we get consistent 25 Hz data as the accelerometer sees it.
|
||||
|
||||
for(int i = 0; i < fifo.count; i++) {
|
||||
log_data_point(state, fifo.readings[i], i * 4 + offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void finish_reading(accelerometer_data_acquisition_state_t *state) {
|
||||
printf("Finish reading\n");
|
||||
if (state->pos != 0) {
|
||||
write_page(state);
|
||||
}
|
||||
lis2dw_set_data_rate(LIS2DW_DATA_RATE_POWERDOWN);
|
||||
watch_disable_i2c();
|
||||
|
||||
state->repeat_ticks = state->repeat_interval;
|
||||
}
|
||||
117
legacy/watch_faces/sensor/accelerometer_data_acquisition_face.h
Normal file
117
legacy/watch_faces/sensor/accelerometer_data_acquisition_face.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Joey Castillo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ACCELEROMETER_DATA_ACQUISITION_FACE_H_
|
||||
#define ACCELEROMETER_DATA_ACQUISITION_FACE_H_
|
||||
|
||||
/*
|
||||
* ACCELEROMETER DATA ACQUISITION
|
||||
*
|
||||
* TODO: Add description here, including controls.
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
#define ACCELEROMETER_DATA_ACQUISITION_INVALID ((uint64_t)(0b11)) // all bits are 1 when the flash is erased
|
||||
#define ACCELEROMETER_DATA_ACQUISITION_HEADER ((uint64_t)(0b10))
|
||||
#define ACCELEROMETER_DATA_ACQUISITION_DATA ((uint64_t)(0b01))
|
||||
#define ACCELEROMETER_DATA_ACQUISITION_DELETED ((uint64_t)(0b00)) // You can always write a 0 to any 1 bit
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
struct {
|
||||
uint16_t record_type : 2; // see above, helps us identify record types when reading back
|
||||
uint16_t range : 2; // accelerometer range (see lis2dw_range_t)
|
||||
uint16_t temperature : 12; // raw value from the temperature sensor
|
||||
} info;
|
||||
uint8_t char1 : 8; // First character of the activity type
|
||||
uint8_t char2 : 8; // Second character of the activity type
|
||||
uint32_t timestamp : 32; // UNIX timestamp for the measurement
|
||||
} header;
|
||||
struct {
|
||||
struct {
|
||||
uint16_t record_type : 2; // duplicate; this is the same field as info above
|
||||
uint16_t accel : 14; // X acceleration value, raw, offset by 8192
|
||||
} x;
|
||||
struct {
|
||||
uint16_t lpmode : 2; // low power mode (see lis2dw_low_power_mode_t)
|
||||
uint16_t accel : 14; // Y acceleration value, raw, offset by 8192
|
||||
} y;
|
||||
struct {
|
||||
uint16_t filter : 2; // bandwidth filtering selection (see lis2dw_bandwidth_filtering_mode_t)
|
||||
uint16_t accel : 14; // Z acceleration value, raw, offset by 8192
|
||||
} z;
|
||||
uint32_t counter : 16; // number of centiseconds since timestamp in header
|
||||
} data;
|
||||
uint64_t value;
|
||||
} accelerometer_data_acquisition_record_t;
|
||||
|
||||
typedef enum {
|
||||
ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE,
|
||||
ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN,
|
||||
ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING,
|
||||
ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS,
|
||||
} accelerometer_data_acquisition_mode_t;
|
||||
|
||||
typedef enum {
|
||||
ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND,
|
||||
ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY,
|
||||
ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT,
|
||||
// ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_NAME,
|
||||
} accelerometer_data_acquisition_settings_page_t;
|
||||
|
||||
typedef struct {
|
||||
// mode
|
||||
accelerometer_data_acquisition_mode_t mode;
|
||||
accelerometer_data_acquisition_settings_page_t settings_page;
|
||||
// current settings
|
||||
uint8_t activity_type_index; // active activity type
|
||||
bool beep_with_countdown; // should we beep at the countdown
|
||||
uint8_t countdown_length; // how many seconds to count down
|
||||
uint16_t repeat_interval; // how many seconds to wait for a repeat
|
||||
// info about the flash chip
|
||||
int16_t next_available_page;
|
||||
// transient properties
|
||||
uint8_t countdown_ticks;
|
||||
uint8_t repeat_ticks;
|
||||
uint8_t reading_ticks;
|
||||
uint32_t starting_timestamp;
|
||||
accelerometer_data_acquisition_record_t records[32];
|
||||
uint16_t pos;
|
||||
} accelerometer_data_acquisition_state_t;
|
||||
|
||||
void accelerometer_data_acquisition_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void accelerometer_data_acquisition_face_activate(void *context);
|
||||
bool accelerometer_data_acquisition_face_loop(movement_event_t event, void *context);
|
||||
void accelerometer_data_acquisition_face_resign(void *context);
|
||||
|
||||
#define accelerometer_data_acquisition_face ((const watch_face_t){ \
|
||||
accelerometer_data_acquisition_face_setup, \
|
||||
accelerometer_data_acquisition_face_activate, \
|
||||
accelerometer_data_acquisition_face_loop, \
|
||||
accelerometer_data_acquisition_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // ACCELEROMETER_DATA_ACQUISITION_FACE_H_
|
||||
151
legacy/watch_faces/sensor/alarm_thermometer_face.c
Normal file
151
legacy/watch_faces/sensor/alarm_thermometer_face.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Christian Buschau
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "alarm_thermometer_face.h"
|
||||
#include "thermistor_driver.h"
|
||||
|
||||
static float _alarm_thermometer_face_update(bool in_fahrenheit) {
|
||||
thermistor_driver_enable();
|
||||
float temperature_c = thermistor_driver_get_temperature();
|
||||
char buf[14];
|
||||
if (in_fahrenheit) {
|
||||
sprintf(buf, "%4.1f#F", temperature_c * 1.8 + 32.0);
|
||||
} else {
|
||||
sprintf(buf, "%4.1f#C", temperature_c);
|
||||
}
|
||||
watch_display_string(buf, 4);
|
||||
thermistor_driver_disable();
|
||||
return temperature_c;
|
||||
}
|
||||
|
||||
static void _alarm_thermometer_face_clear(int last[]) {
|
||||
for (size_t i = 0; i < LAST_SIZE; i++) {
|
||||
last[i] = INT_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
void alarm_thermometer_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(alarm_thermometer_state_t));
|
||||
memset(*context_ptr, 0, sizeof(alarm_thermometer_state_t));
|
||||
}
|
||||
}
|
||||
|
||||
void alarm_thermometer_face_activate(void *context) {
|
||||
alarm_thermometer_state_t *state = (alarm_thermometer_state_t *)context;
|
||||
state->mode = MODE_NORMAL;
|
||||
_alarm_thermometer_face_clear(state->last);
|
||||
watch_display_string("AT", 0);
|
||||
}
|
||||
|
||||
bool alarm_thermometer_face_loop(movement_event_t event, void *context) {
|
||||
alarm_thermometer_state_t *state = (alarm_thermometer_state_t *)context;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
_alarm_thermometer_face_update(movement_use_imperial_units());
|
||||
break;
|
||||
case EVENT_TICK:
|
||||
if (watch_rtc_get_date_time().unit.second % 5 == 0) {
|
||||
switch (state->mode) {
|
||||
case MODE_NORMAL:
|
||||
_alarm_thermometer_face_update(movement_use_imperial_units());
|
||||
break;
|
||||
case MODE_ALARM:
|
||||
for (size_t i = LAST_SIZE - 1; i > 0; i--) {
|
||||
state->last[i] = state->last[i - 1];
|
||||
}
|
||||
state->last[0] = roundf(_alarm_thermometer_face_update(movement_use_imperial_units()) * 10.0f);
|
||||
bool constant = true;
|
||||
for (size_t i = 1; i < LAST_SIZE; i++) {
|
||||
if (state->last[i - 1] != state->last[i]) {
|
||||
constant = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (constant) {
|
||||
state->mode = MODE_FREEZE;
|
||||
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
movement_play_alarm();
|
||||
}
|
||||
break;
|
||||
case MODE_FREEZE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
switch (state->mode) {
|
||||
case MODE_NORMAL:
|
||||
state->mode = MODE_ALARM;
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
_alarm_thermometer_face_clear(state->last);
|
||||
break;
|
||||
case MODE_FREEZE:
|
||||
state->mode = MODE_NORMAL;
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
break;
|
||||
case MODE_ALARM:
|
||||
state->mode = MODE_NORMAL;
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
_alarm_thermometer_face_update(movement_use_imperial_units());
|
||||
break;
|
||||
}
|
||||
if (movement_button_should_sound()) {
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
|
||||
}
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
if (state->mode != MODE_FREEZE) {
|
||||
movement_set_use_imperial_units(!movement_use_imperial_units());
|
||||
_alarm_thermometer_face_update(movement_use_imperial_units());
|
||||
}
|
||||
break;
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
if (!watch_sleep_animation_is_running()) {
|
||||
state->mode = MODE_NORMAL;
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
watch_start_sleep_animation(1000);
|
||||
}
|
||||
if (watch_rtc_get_date_time().unit.minute % 5 == 0) {
|
||||
_alarm_thermometer_face_update(movement_use_imperial_units());
|
||||
watch_display_string(" ", 8);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void alarm_thermometer_face_resign(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
74
legacy/watch_faces/sensor/alarm_thermometer_face.h
Normal file
74
legacy/watch_faces/sensor/alarm_thermometer_face.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Christian Buschau
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ALARM_THERMOMETER_FACE_H_
|
||||
#define ALARM_THERMOMETER_FACE_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include "movement.h"
|
||||
|
||||
/*
|
||||
* ALARM THERMOMETER
|
||||
*
|
||||
* This watch face shows the current temperature in degrees Celsius. Press and
|
||||
* hold the alarm button to toggle between Celsius and Fahrenheit. Press and
|
||||
* release the alarm button to start a "timer". The watch will sound an alarm
|
||||
* when the temperature remains constant for at least 30 seconds and the
|
||||
* temperature will stop updating until you press the alarm button. You can
|
||||
* cancel the alarm by pressing the button again. If the temperature doesn't
|
||||
* remain constant until the low energy timeout is reached, the alarm will stop.
|
||||
* This is useful to measure e.g. the room temperature. If you lay off your
|
||||
* watch from your wrist, it will take some time until it cools down, and will
|
||||
* notify you when the measurement is constant enough.
|
||||
* THIS WATCH FACE IS NOT INTENDED TO DIAGNOSE, TREAT, CURE OR PREVENT ANY
|
||||
* DISEASE.
|
||||
*/
|
||||
|
||||
#define LAST_SIZE 6
|
||||
|
||||
typedef enum {
|
||||
MODE_NORMAL,
|
||||
MODE_ALARM,
|
||||
MODE_FREEZE
|
||||
} alarm_thermometer_mode_t;
|
||||
|
||||
typedef struct {
|
||||
int last[LAST_SIZE];
|
||||
alarm_thermometer_mode_t mode;
|
||||
} alarm_thermometer_state_t;
|
||||
|
||||
void alarm_thermometer_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void alarm_thermometer_face_activate(void *context);
|
||||
bool alarm_thermometer_face_loop(movement_event_t event, void *context);
|
||||
void alarm_thermometer_face_resign(void *context);
|
||||
|
||||
#define alarm_thermometer_face ((const watch_face_t){ \
|
||||
alarm_thermometer_face_setup, \
|
||||
alarm_thermometer_face_activate, \
|
||||
alarm_thermometer_face_loop, \
|
||||
alarm_thermometer_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // ALARM_THERMOMETER_FACE_H_
|
||||
171
legacy/watch_faces/sensor/lightmeter_face.c
Normal file
171
legacy/watch_faces/sensor/lightmeter_face.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 CC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "lightmeter_face.h"
|
||||
#include "watch_utility.h"
|
||||
#include "watch_slcd.h"
|
||||
|
||||
uint16_t lightmeter_mod(uint16_t m, uint16_t n) { return (m%n + n)%n; }
|
||||
|
||||
void lightmeter_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(lightmeter_state_t));
|
||||
lightmeter_state_t *state = (lightmeter_state_t*) *context_ptr;
|
||||
state->waiting_for_conversion = 0;
|
||||
state->lux = 0.0;
|
||||
state->mode = 0;
|
||||
state->iso = LIGHTMETER_ISO_100;
|
||||
state->ap = LIGHTMETER_AP_4P0;
|
||||
}
|
||||
}
|
||||
|
||||
void lightmeter_face_activate(void *context) {
|
||||
lightmeter_state_t *state = (lightmeter_state_t*) context;
|
||||
state->waiting_for_conversion = 0;
|
||||
lightmeter_show_ev(state); // Print most current reading
|
||||
watch_enable_i2c();
|
||||
return;
|
||||
}
|
||||
|
||||
void lightmeter_show_ev(lightmeter_state_t *state) {
|
||||
|
||||
float ev = max(min(
|
||||
log2(state->lux) +
|
||||
lightmeter_isos[state->iso].ev +
|
||||
LIGHTMETER_CALIBRATION,
|
||||
99), -9);
|
||||
int evt = round(2*ev); // Truncated EV
|
||||
|
||||
// Print EV
|
||||
char strbuff[7];
|
||||
watch_clear_all_indicators();
|
||||
watch_display_string("EV ", 0);
|
||||
|
||||
sprintf(strbuff, "%2i", (uint16_t) abs(evt/2)); // Print whole part of EV
|
||||
watch_display_string(strbuff, 2);
|
||||
if(evt%2) watch_set_indicator(WATCH_INDICATOR_LAP); // Indicate half stop
|
||||
if(ev<0) watch_set_pixel(1,9); // Indicate negative EV
|
||||
|
||||
// Handle lux mode
|
||||
if(state->mode == 1) {
|
||||
sprintf(strbuff, "%6.0f", min(state->lux, 999999.0));
|
||||
watch_display_string(strbuff, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find and print best shutter speed
|
||||
uint16_t bestsh = 0;
|
||||
float besterr = 1.0/0.0;
|
||||
float errbuf = 1.0/0.0;
|
||||
float comp_ev = ev + lightmeter_aps[state->ap].ev;
|
||||
for(uint16_t ind = 2; ind < LIGHTMETER_N_SHS; ind++) {
|
||||
errbuf = comp_ev + lightmeter_shs[ind].ev;
|
||||
if( fabs(errbuf) < fabs(besterr)) {
|
||||
besterr = errbuf;
|
||||
bestsh = ind;
|
||||
}
|
||||
}
|
||||
if(besterr >= 0.5) watch_display_string(lightmeter_shs[LIGHTMETER_SH_HIGH].str, 4);
|
||||
else if(besterr <= -0.5) watch_display_string(lightmeter_shs[LIGHTMETER_SH_LOW].str, 4);
|
||||
else watch_display_string(lightmeter_shs[bestsh].str, 4);
|
||||
|
||||
// Print aperture
|
||||
watch_display_string(lightmeter_aps[state->ap].str, 7);
|
||||
return;
|
||||
}
|
||||
|
||||
bool lightmeter_face_loop(movement_event_t event, void *context) {
|
||||
lightmeter_state_t *state = (lightmeter_state_t*) context;
|
||||
|
||||
opt3001_Config_t c;
|
||||
switch (event.event_type) {
|
||||
case EVENT_TICK:
|
||||
if(state->waiting_for_conversion) { // Check if measurement is ready...
|
||||
c = opt3001_readConfig(lightmeter_addr);
|
||||
if(c.ConversionReady) {
|
||||
state->waiting_for_conversion = 0;
|
||||
opt3001_t result = opt3001_readResult(lightmeter_addr);
|
||||
state->lux = result.lux;
|
||||
lightmeter_show_ev(state);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EVENT_ALARM_BUTTON_UP: // Increment aperture
|
||||
state->ap = lightmeter_mod(state->ap+1, LIGHTMETER_N_APS);
|
||||
|
||||
lightmeter_show_ev(state);
|
||||
break;
|
||||
|
||||
case EVENT_LIGHT_BUTTON_UP: // Decrement aperture
|
||||
if(state->ap == 0) state->ap = LIGHTMETER_N_APS-1;
|
||||
else state->ap = lightmeter_mod(state->ap-1, LIGHTMETER_N_APS);
|
||||
|
||||
lightmeter_show_ev(state);
|
||||
break;
|
||||
|
||||
case EVENT_LIGHT_LONG_PRESS: // Cycle ISO
|
||||
state->iso = lightmeter_mod(state->iso+1, LIGHTMETER_N_ISOS);
|
||||
|
||||
watch_clear_all_indicators();
|
||||
watch_display_string("EV ", 0);
|
||||
watch_display_string(lightmeter_isos[state->iso].str, 4);
|
||||
break;
|
||||
|
||||
case EVENT_ALARM_LONG_PRESS: // Take measurement
|
||||
opt3001_writeConfig(lightmeter_addr, lightmeter_takeNewReading);
|
||||
state->waiting_for_conversion = 1;
|
||||
|
||||
watch_clear_all_indicators();
|
||||
watch_display_string("EV ", 0);
|
||||
watch_display_string(lightmeter_isos[state->iso].str, 4);
|
||||
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
break;
|
||||
|
||||
case EVENT_MODE_LONG_PRESS: // Toggle mode
|
||||
state->mode = !state->mode;
|
||||
lightmeter_show_ev(state);
|
||||
break;
|
||||
|
||||
case EVENT_TIMEOUT:
|
||||
movement_move_to_face(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
movement_default_loop_handler(event);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void lightmeter_face_resign(void *context) {
|
||||
(void) context;
|
||||
opt3001_writeConfig(lightmeter_addr, lightmeter_off);
|
||||
watch_disable_i2c();
|
||||
return;
|
||||
}
|
||||
191
legacy/watch_faces/sensor/lightmeter_face.h
Normal file
191
legacy/watch_faces/sensor/lightmeter_face.h
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 CC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LIGHTMETER_FACE_H_
|
||||
#define LIGHTMETER_FACE_H_
|
||||
|
||||
/*
|
||||
* Aperture-priority Light Meter Face
|
||||
*
|
||||
* Tested with the "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001" flexboard.
|
||||
* This flexboard could use a revision:
|
||||
*
|
||||
* - The thermistor components should be moved west a mm or flipped to the backside
|
||||
* to avoid stressing the flexboard against the processor so much.
|
||||
* - The 'no connect' pad falls off easily.
|
||||
*
|
||||
* Controls:
|
||||
*
|
||||
* - Trigger a measurement by long-pressing Alarm.
|
||||
* Sensor integration is happening when the Signal indicator is on.
|
||||
*
|
||||
* - ISO setting can be cycled by long-pressing Light.
|
||||
* During integration the current ISO setting will be displayed.
|
||||
*
|
||||
* - EV measurement in the top right: "LAP" indicates "half stop".
|
||||
* So "LAP -1" means EV = -1.5. Likewise "LAP 13" means EV = +13.5
|
||||
*
|
||||
* - Aperture in the bottom right: the last 3 main digits are the f-stop.
|
||||
* Adjust this number in half-stop increments using Alarm = +1/2 and Light = -1/2.
|
||||
*
|
||||
* - Best shutter speed in the bottom left: the first 3 digits are the shutter speed.
|
||||
* Some special chars are needed here: "-" = seconds, "h" = extra half second, "K" = thousands.
|
||||
* "HI" or "LO" if there's no shutter in the dictionary within 0.5 stops of correct exposure.
|
||||
*
|
||||
* - Mode long-press changes the main digits to show raw sensor lux measurements.
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
#include "opt3001.h"
|
||||
|
||||
#define LIGHTMETER_CALIBRATION 2.58
|
||||
typedef struct {
|
||||
char * str;
|
||||
float ev;
|
||||
} lightmeter_ev_t;
|
||||
|
||||
static const lightmeter_ev_t lightmeter_isos[] = {
|
||||
{" i 25", -2},
|
||||
{" i 50", -1},
|
||||
{" i 100", 0},
|
||||
{" i 160", 0.68},
|
||||
{" i 200", 1},
|
||||
{" i 400", 2},
|
||||
{" i 800", 3},
|
||||
{" i1600", 4}};
|
||||
typedef enum {
|
||||
LIGHTMETER_ISO_25, LIGHTMETER_ISO_50, LIGHTMETER_ISO_100, LIGHTMETER_ISO_160, LIGHTMETER_ISO_200, LIGHTMETER_ISO_400, LIGHTMETER_ISO_800, LIGHTMETER_ISO_1600,
|
||||
LIGHTMETER_N_ISOS
|
||||
} lightmeter_iso_t;
|
||||
|
||||
static const lightmeter_ev_t lightmeter_aps[] = {
|
||||
{"1.4", 0},
|
||||
{"1.8", -0.5},
|
||||
{"2.0", -1},
|
||||
{"2.4", -1.5},
|
||||
{"2.8", -2},
|
||||
{"3.3", -2.5},
|
||||
{"4.0", -3},
|
||||
{"4.8", -3.5},
|
||||
{"5.6", -4},
|
||||
{"6.7", -4.5},
|
||||
{"8.0", -5},
|
||||
{"9.5", -5.5},
|
||||
{"11.", -6},
|
||||
{"13.", -6.5},
|
||||
{"16.", -7},
|
||||
{"19.", -7.5},
|
||||
{"22.", -8}};
|
||||
typedef enum {
|
||||
LIGHTMETER_AP_1P4, LIGHTMETER_AP_1P8, LIGHTMETER_AP_2P0, LIGHTMETER_AP_2P4, LIGHTMETER_AP_2P8, LIGHTMETER_AP_3P3, LIGHTMETER_AP_4P0, LIGHTMETER_AP_4P8, LIGHTMETER_AP_5P6, LIGHTMETER_AP_6P7, LIGHTMETER_AP_8P0, LIGHTMETER_AP_9P5,
|
||||
LIGHTMETER_AP_11, LIGHTMETER_AP_13, LIGHTMETER_AP_16, LIGHTMETER_AP_19, LIGHTMETER_AP_22,
|
||||
LIGHTMETER_N_APS
|
||||
} lightmeter_ap_t;
|
||||
|
||||
static const lightmeter_ev_t lightmeter_shs[] = {
|
||||
{"LO-", 99},
|
||||
{"HI ", -99},
|
||||
{"30-", 5.0},
|
||||
{"20-", 4.5},
|
||||
{"15-", 4.0},
|
||||
{"11-", 3.5},
|
||||
{"8- ", 3.0},
|
||||
{"6- ", 2.5},
|
||||
{"4- ", 2.0},
|
||||
{"3- ", 1.5},
|
||||
{"2- ", 1.0},
|
||||
{"1h-", 0.5},
|
||||
{"1 ", 0.0},
|
||||
{"1h ", -0.5},
|
||||
{"2 ", -1.0},
|
||||
{"3 ", -1.5},
|
||||
{"4 ", -2.0},
|
||||
{"6 ", -2.5},
|
||||
{"8 ", -3.0},
|
||||
{"12 ", -3.5},
|
||||
{"15 ", -4.0},
|
||||
{"20 ", -4.5},
|
||||
{"30 ", -5.0},
|
||||
{"45 ", -5.5},
|
||||
{"60 ", -6.0},
|
||||
{"90 ", -6.5},
|
||||
{"125", -7.0},
|
||||
{"180", -7.5},
|
||||
{"250", -8.0},
|
||||
{"350", -8.5},
|
||||
{"500", -9.0},
|
||||
{"750", -9.5},
|
||||
{"1K ", -10.0},
|
||||
{"1K5", -10.5},
|
||||
{"2K ", -11.0},
|
||||
{"3K ", -11.5},
|
||||
{"4K ", -12.0},
|
||||
{"6K ", -12.5},
|
||||
{"8K ", -13.0}};
|
||||
typedef enum {
|
||||
LIGHTMETER_SH_LOW, LIGHTMETER_SH_HIGH,
|
||||
LIGHTMETER_SH_30S, LIGHTMETER_SH_20S, LIGHTMETER_SH_15S, LIGHTMETER_SH_11S, LIGHTMETER_SH_8S, LIGHTMETER_SH_6S, LIGHTMETER_SH_3S, LIGHTMETER_SH_4S, LIGHTMETER_SH_2S, LIGHTMETER_SH_1HS,
|
||||
LIGHTMETER_SH_1, LIGHTMETER_SH_1H, LIGHTMETER_SH_2, LIGHTMETER_SH_3, LIGHTMETER_SH_4, LIGHTMETER_SH_6, LIGHTMETER_SH_8, LIGHTMETER_SH_12, LIGHTMETER_SH_15, LIGHTMETER_SH_20, LIGHTMETER_SH_30, LIGHTMETER_SH_45, LIGHTMETER_SH_60, LIGHTMETER_SH_90, LIGHTMETER_SH_125, LIGHTMETER_SH_180, LIGHTMETER_SH_250, LIGHTMETER_SH_350, LIGHTMETER_SH_500, LIGHTMETER_SH_750,
|
||||
LIGHTMETER_SH_1K, LIGHTMETER_SH_1K5, LIGHTMETER_SH_2K, LIGHTMETER_SH_3K, LIGHTMETER_SH_4K, LIGHTMETER_SH_6K, LIGHTMETER_SH_8K,
|
||||
LIGHTMETER_N_SHS
|
||||
} lightmeter_sh_t;
|
||||
|
||||
typedef struct {
|
||||
lightmeter_iso_t iso;
|
||||
lightmeter_ap_t ap;
|
||||
bool waiting_for_conversion;
|
||||
float lux;
|
||||
int mode;
|
||||
} lightmeter_state_t;
|
||||
|
||||
static const opt3001_Config_t lightmeter_takeNewReading = {
|
||||
.RangeNumber = 0B1100,
|
||||
.ConversionTime = 0B1,
|
||||
.Latch = 0B1,
|
||||
.ModeOfConversionOperation = 0B01
|
||||
};
|
||||
|
||||
static const opt3001_Config_t lightmeter_off = {
|
||||
.ModeOfConversionOperation = 0B00
|
||||
};
|
||||
|
||||
uint16_t lightmeter_mod(uint16_t m, uint16_t n);
|
||||
|
||||
void lightmeter_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void lightmeter_face_activate(void *context);
|
||||
void lightmeter_show_ev(lightmeter_state_t *state);
|
||||
bool lightmeter_face_loop(movement_event_t event, void *context);
|
||||
void lightmeter_face_resign(void *context);
|
||||
|
||||
static const uint8_t lightmeter_addr = 0x44;
|
||||
|
||||
#define lightmeter_face ((const watch_face_t){ \
|
||||
lightmeter_face_setup, \
|
||||
lightmeter_face_activate, \
|
||||
lightmeter_face_loop, \
|
||||
lightmeter_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // LIGHTMETER_FACE_H_
|
||||
155
legacy/watch_faces/sensor/minmax_face.c
Normal file
155
legacy/watch_faces/sensor/minmax_face.c
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Mark Blyth
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "minmax_face.h"
|
||||
#include "thermistor_driver.h"
|
||||
#include "watch.h"
|
||||
|
||||
|
||||
static float _get_displayed_temperature_c(minmax_state_t *state){
|
||||
float min_temp = 1000;
|
||||
float max_temp = -1000;
|
||||
for(int i = 0; i < LOGGING_DATA_POINTS; i++){
|
||||
if(state->hourly_maxs[i] > max_temp){
|
||||
max_temp = state->hourly_maxs[i];
|
||||
}
|
||||
if(state->hourly_mins[i] < min_temp){
|
||||
min_temp = state->hourly_mins[i];
|
||||
}
|
||||
}
|
||||
if(state->show_min) return min_temp;
|
||||
return max_temp;
|
||||
}
|
||||
|
||||
|
||||
static void _minmax_face_log_data(minmax_state_t *logger_state) {
|
||||
thermistor_driver_enable();
|
||||
size_t pos = (size_t) watch_rtc_get_date_time().unit.hour;
|
||||
float temp_c = thermistor_driver_get_temperature();
|
||||
// If no data yet, initialise with current temperature
|
||||
if(!logger_state->have_logged){
|
||||
logger_state->have_logged = true;
|
||||
for(int i=0; i<LOGGING_DATA_POINTS; i++){
|
||||
logger_state->hourly_mins[i] = temp_c;
|
||||
logger_state->hourly_maxs[i] = temp_c;
|
||||
}
|
||||
}
|
||||
// On new hour, update lists to current temperature
|
||||
else if(watch_rtc_get_date_time().unit.minute < 2){
|
||||
logger_state->hourly_mins[pos] = temp_c;
|
||||
logger_state->hourly_maxs[pos] = temp_c;
|
||||
}
|
||||
// Log hourly highs and lows
|
||||
else if(logger_state->hourly_mins[pos] > temp_c){
|
||||
logger_state->hourly_mins[pos] = temp_c;
|
||||
}
|
||||
else if(logger_state->hourly_maxs[pos] < temp_c){
|
||||
logger_state->hourly_maxs[pos] = temp_c;
|
||||
}
|
||||
thermistor_driver_disable();
|
||||
}
|
||||
|
||||
static void _minmax_face_update_display(float temperature_c, bool in_fahrenheit) {
|
||||
char buf[14];
|
||||
if (in_fahrenheit) {
|
||||
sprintf(buf, "%4.0f#F", temperature_c * 1.8 + 32.0);
|
||||
} else {
|
||||
sprintf(buf, "%4.0f#C", temperature_c);
|
||||
}
|
||||
watch_display_string(buf, 4);
|
||||
}
|
||||
|
||||
|
||||
void minmax_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(minmax_state_t));
|
||||
memset(*context_ptr, 0, sizeof(minmax_state_t));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void minmax_face_activate(void *context) {
|
||||
minmax_state_t *state = (minmax_state_t *)context;
|
||||
state->show_min = true;
|
||||
watch_display_string("MN", 0); // Start with minimum temp
|
||||
}
|
||||
|
||||
bool minmax_face_loop(movement_event_t event, void *context) {
|
||||
minmax_state_t *state = (minmax_state_t *)context;
|
||||
float temp_c;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
temp_c = _get_displayed_temperature_c(state);
|
||||
_minmax_face_update_display(temp_c, movement_use_imperial_units());
|
||||
break;
|
||||
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
movement_set_use_imperial_units(!movement_use_imperial_units());
|
||||
temp_c = _get_displayed_temperature_c(state);
|
||||
_minmax_face_update_display(temp_c, movement_use_imperial_units());
|
||||
break;
|
||||
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
state->show_min = !state->show_min;
|
||||
if(state->show_min){
|
||||
watch_display_string("MN", 0);
|
||||
} else {
|
||||
watch_display_string("MX", 0);
|
||||
}
|
||||
temp_c = _get_displayed_temperature_c(state);
|
||||
_minmax_face_update_display(temp_c, movement_use_imperial_units());
|
||||
break;
|
||||
|
||||
case EVENT_TIMEOUT:
|
||||
movement_move_to_face(0);
|
||||
break;
|
||||
case EVENT_BACKGROUND_TASK:
|
||||
_minmax_face_log_data(state);
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void minmax_face_resign(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
|
||||
|
||||
movement_watch_face_advisory_t minmax_face_advise(void *context) {
|
||||
(void) context;
|
||||
|
||||
// this will get called at the top of each minute; always request bg task
|
||||
movement_watch_face_advisory_t retval = {
|
||||
.wants_background_task = 1,
|
||||
};
|
||||
|
||||
return retval;
|
||||
}
|
||||
69
legacy/watch_faces/sensor/minmax_face.h
Normal file
69
legacy/watch_faces/sensor/minmax_face.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Mark Blyth
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MINMAX_FACE_H_
|
||||
#define MINMAX_FACE_H_
|
||||
|
||||
#include "movement.h"
|
||||
#include "watch.h"
|
||||
|
||||
#define LOGGING_DATA_POINTS (24)
|
||||
|
||||
/*
|
||||
* Log for the min. and max. temperature over the last 24h.
|
||||
*
|
||||
* Temperature is logged once a minute, every minute. Results are
|
||||
* stored, noting the highest and lowest temperatures observed within
|
||||
* any given hour. The watch face then displays the minimum or maximum
|
||||
* temperature recorded over the last 24h.
|
||||
*
|
||||
* A long press of the light button changes units between Celsius and
|
||||
* Fahrenheit. Pressing the alarm button switches between displaying the
|
||||
* minimum and maximum observed temperatures. If no buttons are pressed,
|
||||
* the watch face will eventually time out and return home.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
bool show_min;
|
||||
bool have_logged;
|
||||
float hourly_mins[LOGGING_DATA_POINTS];
|
||||
float hourly_maxs[LOGGING_DATA_POINTS];
|
||||
} minmax_state_t;
|
||||
|
||||
void minmax_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void minmax_face_activate(void *context);
|
||||
bool minmax_face_loop(movement_event_t event, void *context);
|
||||
void minmax_face_resign(void *context);
|
||||
movement_watch_face_advisory_t minmax_face_advise(void *context);
|
||||
|
||||
#define minmax_face ((const watch_face_t){ \
|
||||
minmax_face_setup, \
|
||||
minmax_face_activate, \
|
||||
minmax_face_loop, \
|
||||
minmax_face_resign, \
|
||||
minmax_face_advise, \
|
||||
})
|
||||
|
||||
#endif // MINMAX_FACE_H_
|
||||
|
||||
79
legacy/watch_faces/sensor/thermistor_testing_face.c
Normal file
79
legacy/watch_faces/sensor/thermistor_testing_face.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Joey Castillo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "thermistor_testing_face.h"
|
||||
#include "thermistor_driver.h"
|
||||
#include "watch.h"
|
||||
|
||||
static void _thermistor_testing_face_update_display(bool in_fahrenheit) {
|
||||
thermistor_driver_enable();
|
||||
float temperature_c = thermistor_driver_get_temperature();
|
||||
char buf[14];
|
||||
if (in_fahrenheit) {
|
||||
sprintf(buf, "%4.1f#F", temperature_c * 1.8 + 32.0);
|
||||
} else {
|
||||
sprintf(buf, "%4.1f#C", temperature_c);
|
||||
}
|
||||
watch_display_string(buf, 4);
|
||||
thermistor_driver_disable();
|
||||
}
|
||||
|
||||
void thermistor_testing_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
(void) context_ptr;
|
||||
// force one setting: never enter low energy mode.
|
||||
// I'm using this watch face to test the temperature sensor boards; there's no need for it.
|
||||
movement_set_low_energy_timeout(0);
|
||||
}
|
||||
|
||||
void thermistor_testing_face_activate(void *context) {
|
||||
(void) context;
|
||||
watch_display_string("TE", 0);
|
||||
movement_request_tick_frequency(8);
|
||||
}
|
||||
|
||||
bool thermistor_testing_face_loop(movement_event_t event, void *context) {
|
||||
(void) context;
|
||||
switch (event.event_type) {
|
||||
case EVENT_ALARM_BUTTON_DOWN:
|
||||
movement_set_use_imperial_units(!movement_use_imperial_units());
|
||||
_thermistor_testing_face_update_display(movement_use_imperial_units());
|
||||
break;
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
_thermistor_testing_face_update_display(movement_use_imperial_units());
|
||||
break;
|
||||
default:
|
||||
movement_default_loop_handler(event);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void thermistor_testing_face_resign(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
54
legacy/watch_faces/sensor/thermistor_testing_face.h
Normal file
54
legacy/watch_faces/sensor/thermistor_testing_face.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Joey Castillo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef THERMISTOR_TESTING_FACE_H_
|
||||
#define THERMISTOR_TESTING_FACE_H_
|
||||
|
||||
/*
|
||||
* THERMISTOR TESTING FACE
|
||||
*
|
||||
* This watch face is designed for testing temperature sensor boards.
|
||||
* It displays temperature readings at a relatively fast rate of 8 Hz,
|
||||
* and disables low energy mode so my testing device doesn't sleep.
|
||||
* You more than likely want to use temperature_display_face instead.
|
||||
*
|
||||
* Press ALARM to toggle display of metric vs. imperial units.
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
void thermistor_testing_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void thermistor_testing_face_activate(void *context);
|
||||
bool thermistor_testing_face_loop(movement_event_t event, void *context);
|
||||
void thermistor_testing_face_resign(void *context);
|
||||
|
||||
#define thermistor_testing_face ((const watch_face_t){ \
|
||||
thermistor_testing_face_setup, \
|
||||
thermistor_testing_face_activate, \
|
||||
thermistor_testing_face_loop, \
|
||||
thermistor_testing_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // THERMISTOR_TESTING_FACE_H_
|
||||
Reference in New Issue
Block a user