movement: add thermistor readout face
This commit is contained in:
		
							parent
							
								
									38a2dff234
								
							
						
					
					
						commit
						b88cd0cd7e
					
				| @ -14,6 +14,7 @@ INCLUDES += \ | |||||||
|   -I../watch_faces/clock/ \
 |   -I../watch_faces/clock/ \
 | ||||||
|   -I../watch_faces/settings/ \
 |   -I../watch_faces/settings/ \
 | ||||||
|   -I../watch_faces/complications/ \
 |   -I../watch_faces/complications/ \
 | ||||||
|  |   -I../watch_faces/thermistor/ \
 | ||||||
| 
 | 
 | ||||||
| # If you add any other source files you wish to compile, add them after ../app.c
 | # If you add any other source files you wish to compile, add them after ../app.c
 | ||||||
| # Note that you will need to add a backslash at the end of any line you wish to continue, i.e.
 | # Note that you will need to add a backslash at the end of any line you wish to continue, i.e.
 | ||||||
| @ -27,6 +28,8 @@ SRCS += \ | |||||||
|   ../watch_faces/settings/preferences_face.c \
 |   ../watch_faces/settings/preferences_face.c \
 | ||||||
|   ../watch_faces/settings/set_time_face.c \
 |   ../watch_faces/settings/set_time_face.c \
 | ||||||
|   ../watch_faces/complications/pulsometer_face.c \
 |   ../watch_faces/complications/pulsometer_face.c \
 | ||||||
|  |   ../watch_faces/thermistor/thermistor_driver.c \
 | ||||||
|  |   ../watch_faces/thermistor/thermistor_readout_face.c \
 | ||||||
| 
 | 
 | ||||||
| # Leave this line at the bottom of the file; it has all the targets for making your project.
 | # Leave this line at the bottom of the file; it has all the targets for making your project.
 | ||||||
| include $(TOP)/rules.mk | include $(TOP)/rules.mk | ||||||
|  | |||||||
| @ -6,14 +6,20 @@ | |||||||
| // TODO: none of this is implemented
 | // TODO: none of this is implemented
 | ||||||
| typedef union { | typedef union { | ||||||
|     struct { |     struct { | ||||||
|         uint32_t reserved : 15; |         uint32_t reserved : 14; | ||||||
|         uint32_t clock_mode_24h : 1;        // determines whether clock should use 12 or 24 hour mode.
 |  | ||||||
|         uint32_t button_should_sound : 1;   // if true, pressing a button emits a sound.
 |         uint32_t button_should_sound : 1;   // if true, pressing a button emits a sound.
 | ||||||
|         uint32_t to_interval : 2;           // an inactivity interval for asking the active face to resign.
 |         uint32_t to_interval : 2;           // an inactivity interval for asking the active face to resign.
 | ||||||
|         uint32_t le_interval : 3;           // 0 to disable low energy mode, or an inactivity interval for going into low energy mode.
 |         uint32_t le_interval : 3;           // 0 to disable low energy mode, or an inactivity interval for going into low energy mode.
 | ||||||
|         uint32_t led_duration : 2;          // how many seconds to shine the LED for (x2), or 0 to disable it.
 |         uint32_t led_duration : 2;          // how many seconds to shine the LED for (x2), or 0 to disable it.
 | ||||||
|         uint32_t led_red_color : 4;         // for general purpose illumination, the red LED value (0-15)
 |         uint32_t led_red_color : 4;         // for general purpose illumination, the red LED value (0-15)
 | ||||||
|         uint32_t led_green_color : 4;       // for general purpose illumination, the green LED value (0-15)
 |         uint32_t led_green_color : 4;       // for general purpose illumination, the green LED value (0-15)
 | ||||||
|  | 
 | ||||||
|  |         // while Movement itself doesn't implement a clock or display units, it may make sense to include some
 | ||||||
|  |         // global settings for watch faces to check. The 12/24 hour preference could inform a clock or a
 | ||||||
|  |         // time-oriented complication like a sunrise/sunset timer, and a simple locale preference could tell an
 | ||||||
|  |         // altimeter to display feet or meters as easily as it tells a thermometer to display degrees in F or C.
 | ||||||
|  |         uint32_t clock_mode_24h : 1;        // indicates whether clock should use 12 or 24 hour mode.
 | ||||||
|  |         uint32_t use_imperial_units : 1;    // indicates whether to use metric units (the default) or imperial.
 | ||||||
|     } bit; |     } bit; | ||||||
|     uint32_t value; |     uint32_t value; | ||||||
| } movement_settings_t; | } movement_settings_t; | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #include "preferences_face.h" | #include "preferences_face.h" | ||||||
| #include "set_time_face.h" | #include "set_time_face.h" | ||||||
| #include "pulsometer_face.h" | #include "pulsometer_face.h" | ||||||
|  | #include "thermistor_readout_face.h" | ||||||
| 
 | 
 | ||||||
| const watch_face_t watch_faces[] = { | const watch_face_t watch_faces[] = { | ||||||
|     simple_clock_face, |     simple_clock_face, | ||||||
|  | |||||||
							
								
								
									
										76
									
								
								movement/watch_faces/thermistor/thermistor_driver.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								movement/watch_faces/thermistor/thermistor_driver.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,76 @@ | |||||||
|  | #include "thermistor_driver.h" | ||||||
|  | #include "watch.h" | ||||||
|  | 
 | ||||||
|  | #define THERMISTOR_B_COEFFICIENT (3950.0) | ||||||
|  | #define THERMISTOR_NOMINAL_TEMPERATURE (25.0) | ||||||
|  | #define THERMISTOR_NOMINAL_RESISTANCE (10000.0) | ||||||
|  | #define THERMISTOR_SERIES_RESISTANCE (10000.0) | ||||||
|  | 
 | ||||||
|  | // TODO: we really need a math library.
 | ||||||
|  | uint32_t msb(uint32_t v); | ||||||
|  | double ln(double y); | ||||||
|  | 
 | ||||||
|  | void thermistor_driver_enable() { | ||||||
|  |     // Enable the ADC peripheral, which we'll use to read the thermistor value.
 | ||||||
|  |     watch_enable_adc(); | ||||||
|  |     // Enable analog circuitry on pin A1, which is tied to the thermistor resistor divider.
 | ||||||
|  |     watch_enable_analog_input(A1); | ||||||
|  |     // Enable digital output on A0, which is the power to the thermistor circuit.
 | ||||||
|  |     watch_enable_digital_output(A0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void thermistor_driver_disable() { | ||||||
|  |     // Enable the ADC peripheral, which we'll use to read the thermistor value.
 | ||||||
|  |     watch_disable_adc(); | ||||||
|  |     // Disable analog circuitry on pin A1 to save power.
 | ||||||
|  |     watch_disable_analog_input(A1); | ||||||
|  |     // Disable A0's output circuitry.
 | ||||||
|  |     watch_disable_digital_output(A0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float thermistor_driver_get_temperature() { | ||||||
|  |     // set A0 high to power the thermistor circuit.
 | ||||||
|  |     watch_set_pin_level(A0, true); | ||||||
|  |     // get the pin level
 | ||||||
|  |     uint16_t val = watch_get_analog_pin_level(A1); | ||||||
|  |     // and then set A0 low to power down the thermistor circuit.
 | ||||||
|  |     watch_set_pin_level(A0, false); | ||||||
|  | 
 | ||||||
|  |     double reading = (double)val; | ||||||
|  |     reading = (1023.0 * THERMISTOR_SERIES_RESISTANCE) / (reading / 64.0); | ||||||
|  |     reading -= THERMISTOR_SERIES_RESISTANCE; | ||||||
|  |     reading = reading / THERMISTOR_NOMINAL_RESISTANCE; | ||||||
|  |     reading = ln(reading); | ||||||
|  |     reading /= THERMISTOR_B_COEFFICIENT; | ||||||
|  |     reading += 1.0 / (THERMISTOR_NOMINAL_TEMPERATURE + 273.15); | ||||||
|  |     reading = 1.0 / reading; | ||||||
|  |     reading -= 273.15; | ||||||
|  | 
 | ||||||
|  |     return reading; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t msb(uint32_t v) { | ||||||
|  |   static const int pos[32] = {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; | ||||||
|  |   v |= v >> 1; | ||||||
|  |   v |= v >> 2; | ||||||
|  |   v |= v >> 4; | ||||||
|  |   v |= v >> 8; | ||||||
|  |   v |= v >> 16; | ||||||
|  |   v = (v >> 1) + 1; | ||||||
|  |   return pos[(v * 0x077CB531UL) >> 27]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double ln(double y) { | ||||||
|  |     int log2; | ||||||
|  |     double divisor, x, result; | ||||||
|  | 
 | ||||||
|  |     log2 = msb((int)y); // See: https://stackoverflow.com/a/4970859/6630230
 | ||||||
|  |     divisor = (double)(1 << log2); | ||||||
|  |     x = y / divisor;    // normalized value between [1.0, 2.0]
 | ||||||
|  | 
 | ||||||
|  |     result = -1.7417939 + (2.8212026 + (-1.4699568 + (0.44717955 - 0.056570851 * x) * x) * x) * x; | ||||||
|  |     result += ((double)log2) * 0.69314718; // ln(2) = 0.69314718
 | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										13
									
								
								movement/watch_faces/thermistor/thermistor_driver.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								movement/watch_faces/thermistor/thermistor_driver.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #ifndef THERMISTOR_DRIVER_H_ | ||||||
|  | #define THERMISTOR_DRIVER_H_ | ||||||
|  | 
 | ||||||
|  | // NOTE: This implementation is specific to one prototype sensor board, OSO-MISC-21-009, but both
 | ||||||
|  | // the sensor board design and this implementation are likely to change. Thermistor functionality
 | ||||||
|  | // may even end up being baked into the Sensor Watch library. This is all by way of saying this
 | ||||||
|  | // code is very temporary and the thermistor screens will likely get a rewrite in the future.
 | ||||||
|  | 
 | ||||||
|  | void thermistor_driver_enable(); | ||||||
|  | void thermistor_driver_disable(); | ||||||
|  | float thermistor_driver_get_temperature(); | ||||||
|  | 
 | ||||||
|  | #endif // THERMISTOR_DRIVER_H_
 | ||||||
							
								
								
									
										73
									
								
								movement/watch_faces/thermistor/thermistor_readout_face.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								movement/watch_faces/thermistor/thermistor_readout_face.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | |||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include "thermistor_readout_face.h" | ||||||
|  | #include "thermistor_driver.h" | ||||||
|  | #include "watch.h" | ||||||
|  | 
 | ||||||
|  | void _thermistor_readout_face_update_display(bool in_fahrenheit) { | ||||||
|  |     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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void thermistor_readout_face_setup(movement_settings_t *settings, void ** context_ptr) { | ||||||
|  |     (void) settings; | ||||||
|  |     (void) context_ptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void thermistor_readout_face_activate(movement_settings_t *settings, void *context) { | ||||||
|  |     (void) settings; | ||||||
|  |     (void) context; | ||||||
|  |     watch_display_string("TE", 0); | ||||||
|  |     thermistor_driver_enable(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool thermistor_readout_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { | ||||||
|  |     (void) context; | ||||||
|  |     watch_date_time date_time = watch_rtc_get_date_time(); | ||||||
|  |     switch (event.event_type) { | ||||||
|  |         case EVENT_MODE_BUTTON_UP: | ||||||
|  |             movement_move_to_next_face(); | ||||||
|  |             break; | ||||||
|  |         case EVENT_LIGHT_BUTTON_DOWN: | ||||||
|  |             movement_illuminate_led(); | ||||||
|  |             break; | ||||||
|  |         case EVENT_ALARM_BUTTON_UP: | ||||||
|  |             settings->bit.use_imperial_units = !settings->bit.use_imperial_units; | ||||||
|  |             _thermistor_readout_face_update_display(settings->bit.use_imperial_units); | ||||||
|  |             break; | ||||||
|  |         case EVENT_ACTIVATE: | ||||||
|  |             // force a measurement to be taken immediately.
 | ||||||
|  |             date_time.unit.second = 0; | ||||||
|  |             // fall through
 | ||||||
|  |         case EVENT_TICK: | ||||||
|  |             if (date_time.unit.second % 5 == 4) { | ||||||
|  |                 // Not 100% on this, but I like the idea of using the signal indicator to indicate that we're sensing data.
 | ||||||
|  |                 // In this case we turn the indicator on a second before the reading is taken, and clear it when we're done.
 | ||||||
|  |                 // In reality the measurement takes a fraction of a second, but this is just to show something is happening.
 | ||||||
|  |                 watch_set_indicator(WATCH_INDICATOR_SIGNAL); | ||||||
|  |             } else if (date_time.unit.second % 5 == 0) { | ||||||
|  |                 _thermistor_readout_face_update_display(settings->bit.use_imperial_units); | ||||||
|  |                 watch_clear_indicator(WATCH_INDICATOR_SIGNAL); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case EVENT_TIMEOUT: | ||||||
|  |             movement_move_to_face(0); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void thermistor_readout_face_resign(movement_settings_t *settings, void *context) { | ||||||
|  |     (void) settings; | ||||||
|  |     (void) context; | ||||||
|  |     thermistor_driver_disable(); | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								movement/watch_faces/thermistor/thermistor_readout_face.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								movement/watch_faces/thermistor/thermistor_readout_face.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | #ifndef THERMISTOR_FACE_H_ | ||||||
|  | #define THERMISTOR_FACE_H_ | ||||||
|  | 
 | ||||||
|  | #include "movement.h" | ||||||
|  | 
 | ||||||
|  | void thermistor_readout_face_setup(movement_settings_t *settings, void ** context_ptr); | ||||||
|  | void thermistor_readout_face_activate(movement_settings_t *settings, void *context); | ||||||
|  | bool thermistor_readout_face_loop(movement_event_t event, movement_settings_t *settings, void *context); | ||||||
|  | void thermistor_readout_face_resign(movement_settings_t *settings, void *context); | ||||||
|  | 
 | ||||||
|  | static const watch_face_t thermistor_readout_face = { | ||||||
|  |     thermistor_readout_face_setup, | ||||||
|  |     thermistor_readout_face_activate, | ||||||
|  |     thermistor_readout_face_loop, | ||||||
|  |     thermistor_readout_face_resign, | ||||||
|  |     NULL | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif // THERMISTOR_FACE_H_
 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user