working data acquisition face!
This commit is contained in:
		
							parent
							
								
									1ce103b6f6
								
							
						
					
					
						commit
						4cd86bff53
					
				| @ -5,31 +5,41 @@ | |||||||
| #include "watch.h" | #include "watch.h" | ||||||
| #include "watch_utility.h" | #include "watch_utility.h" | ||||||
| #include "spiflash.h" | #include "spiflash.h" | ||||||
|  | #include "lis2dw.h" | ||||||
| 
 | 
 | ||||||
| #define ACCELEROMETER_TRAINING_RECORD_DELETED ((uint64_t)(0b00)) | #define ACCELEROMETER_DATA_ACQUISITION_INVALID ((uint64_t)(0b11))   // all bits are 1 when the flash is erased
 | ||||||
| #define ACCELEROMETER_TRAINING_RECORD_DATA ((uint64_t)(0b01)) | #define ACCELEROMETER_DATA_ACQUISITION_HEADER ((uint64_t)(0b10)) | ||||||
| #define ACCELEROMETER_TRAINING_RECORD_HEADER ((uint64_t)(0b10)) | #define ACCELEROMETER_DATA_ACQUISITION_DATA ((uint64_t)(0b01))     | ||||||
| #define ACCELEROMETER_TRAINING_RECORD_INVALID ((uint64_t)(0b11)) | #define ACCELEROMETER_DATA_ACQUISITION_DELETED ((uint64_t)(0b00))   // You can always write a 0 to any 1 bit
 | ||||||
| 
 | 
 | ||||||
| typedef union { | typedef union { | ||||||
|     struct { |     struct { | ||||||
|         int16_t temperature : 16; |         struct { | ||||||
|         int8_t char1 : 8; |             uint16_t record_type : 2;   // see above, helps us identify record types when reading back
 | ||||||
|         int8_t char2 : 8; |             uint16_t range : 2;         // accelerometer range (see lis2dw_range_t)
 | ||||||
|         int32_t timestamp : 32; |             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; |     } header; | ||||||
|     struct { |     struct { | ||||||
|         int16_t x_accel : 16; |         struct { | ||||||
|         int16_t y_accel : 16; |             uint16_t record_type : 2;   // duplicate; this is the same field as info above
 | ||||||
|         int16_t z_accel : 16; |             uint16_t accel : 14;        // X acceleration value, raw, offset by 16384
 | ||||||
|         int32_t counter : 16; |         } 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 16384
 | ||||||
|  |         } 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 16384
 | ||||||
|  |         } z; | ||||||
|  |         uint32_t counter : 16;          // number of seconds since timestamp in header
 | ||||||
|     } data; |     } data; | ||||||
|     uint64_t value; |     uint64_t value; | ||||||
| } acceleromter_training_record_t; | } accelerometer_data_acquisition_record_t; | ||||||
| 
 |  | ||||||
| static void cb_alarm_pressed(void) { |  | ||||||
|     printf("Alarm button was pressed!\n"); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static bool wait_for_flash_ready(void) { | static bool wait_for_flash_ready(void) { | ||||||
|     watch_set_pin_level(A3, false); |     watch_set_pin_level(A3, false); | ||||||
| @ -43,125 +53,6 @@ static bool wait_for_flash_ready(void) { | |||||||
|     return ok; |     return ok; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void print_records_at_page(uint16_t page) { |  | ||||||
|     if (page < 650) return; |  | ||||||
|     acceleromter_training_record_t records[32]; |  | ||||||
|     static uint32_t timestamp = 0; |  | ||||||
|     static uint16_t temperature = 0; |  | ||||||
|     wait_for_flash_ready(); |  | ||||||
|     spi_flash_read_data(page * 256, (void *)records, 256); |  | ||||||
|     for(int i = 0; i < 32; i++) { |  | ||||||
|         switch (records[i].header.temperature >> 14) { |  | ||||||
|             case ACCELEROMETER_TRAINING_RECORD_DELETED: |  | ||||||
|                 break; |  | ||||||
|             case ACCELEROMETER_TRAINING_RECORD_DATA: |  | ||||||
|                 printf("%ld,%d,%d,%d,%d\n", timestamp + records[i].data.counter, records[i].data.x_accel, records[i].data.y_accel, records[i].data.z_accel, temperature); |  | ||||||
|                 break; |  | ||||||
|             case ACCELEROMETER_TRAINING_RECORD_HEADER: |  | ||||||
|                 printf("=== BEGIN %c%c EVENT AT %d ===\n", records[i].header.char1, records[i].header.char2, records[i].header.timestamp); |  | ||||||
|                 printf("timestamp,x_raw,y_raw,z_raw,temperature\n"); |  | ||||||
|                 timestamp = records[i].header.timestamp; |  | ||||||
|                 temperature = records[i].header.temperature & 0x3FFF; |  | ||||||
|                 break; |  | ||||||
|             case ACCELEROMETER_TRAINING_RECORD_INVALID: |  | ||||||
|                 printf(",,,,\n"); |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void print_records() { |  | ||||||
|     uint8_t buf[256]; |  | ||||||
|     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++) { |  | ||||||
|             uint8_t pages_written = buf[j]; |  | ||||||
|             uint8_t start = 0; |  | ||||||
|             if (i == 0 && j == 0) { |  | ||||||
|                 pages_written <<= 4; |  | ||||||
|                 start = 4; |  | ||||||
|             } |  | ||||||
|             for(int k = start; k < 7; k++) { |  | ||||||
|                 if ((pages_written & 0x80) == 0) { |  | ||||||
|                     print_records_at_page(i * 2048 + j * 8 + k); |  | ||||||
|                 } |  | ||||||
|                 pages_written <<= 1; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void app_init(void) { |  | ||||||
|     delay_ms(5000); |  | ||||||
|     spi_flash_init(); |  | ||||||
|     watch_register_extwake_callback(BTN_ALARM, cb_alarm_pressed, true); |  | ||||||
| 
 |  | ||||||
|     bool erase = false; |  | ||||||
|     if (erase) { |  | ||||||
|         printf("Erasing...\n"); |  | ||||||
|         wait_for_flash_ready(); |  | ||||||
|         watch_set_pin_level(A3, false); |  | ||||||
|         spi_flash_command(CMD_ENABLE_WRITE); |  | ||||||
|         wait_for_flash_ready(); |  | ||||||
|         watch_set_pin_level(A3, false); |  | ||||||
|         spi_flash_command(CMD_CHIP_ERASE); |  | ||||||
|         delay_ms(10000); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     uint8_t buf[256] = {0xFF}; |  | ||||||
| 
 |  | ||||||
|     wait_for_flash_ready(); |  | ||||||
|     spi_flash_read_data(0, buf, 256); |  | ||||||
|     printf("byte 0 was %02x\n", buf[0]); |  | ||||||
|     if (buf[0] & 0xF0) { |  | ||||||
|         buf[0] = 0x0F; |  | ||||||
|         printf("setting it to 0x0F\n"); |  | ||||||
|         wait_for_flash_ready(); |  | ||||||
|         watch_set_pin_level(A3, false); |  | ||||||
|         spi_flash_command(CMD_ENABLE_WRITE); |  | ||||||
|         wait_for_flash_ready(); |  | ||||||
|         spi_flash_write_data(0, buf, 256); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     print_records(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void app_wake_from_backup(void) { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void app_setup(void) { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void app_prepare_for_standby(void) { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void app_wake_from_standby(void) { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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) { | static void write_buffer_to_page(uint8_t *buf, uint16_t page) { | ||||||
|     uint32_t address = 256 * page; |     uint32_t address = 256 * page; | ||||||
| 
 | 
 | ||||||
| @ -184,14 +75,6 @@ static void write_buffer_to_page(uint8_t *buf, uint16_t page) { | |||||||
|     uint8_t used_byte = 0x7F >> (page % 8); |     uint8_t used_byte = 0x7F >> (page % 8); | ||||||
|     uint8_t offset_in_buf = address_to_mark_used % 256; |     uint8_t offset_in_buf = address_to_mark_used % 256; | ||||||
| 
 | 
 | ||||||
|     printf("\tWe wrote 256 bytes to address %ld, which was 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]); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     watch_set_pin_level(A3, false); |     watch_set_pin_level(A3, false); | ||||||
|     spi_flash_read_data(header_page * 256, used_pages, 256); |     spi_flash_read_data(header_page * 256, used_pages, 256); | ||||||
|     used_pages[offset_in_buf] = used_byte; |     used_pages[offset_in_buf] = used_byte; | ||||||
| @ -203,58 +86,134 @@ static void write_buffer_to_page(uint8_t *buf, uint16_t page) { | |||||||
|     wait_for_flash_ready(); |     wait_for_flash_ready(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void print_records_at_page(uint16_t page) { | ||||||
|  |     accelerometer_data_acquisition_record_t records[32]; | ||||||
|  |     static uint64_t timestamp = 0; | ||||||
|  |     // static uint16_t temperature = 0;
 | ||||||
|  |     static lis2dw_range_t range = LIS2DW_RANGE_2_G; | ||||||
|  |     static double lsb_value = 1; | ||||||
|  |     static bool printing_header = false; | ||||||
|  | 
 | ||||||
|  |     wait_for_flash_ready(); | ||||||
|  |     spi_flash_read_data(page * 256, (void *)records, 256); | ||||||
|  |     for(int i = 0; i < 32; i++) { | ||||||
|  |         switch (records[i].header.info.record_type) { | ||||||
|  |             case ACCELEROMETER_DATA_ACQUISITION_HEADER: | ||||||
|  |                 printing_header = true; | ||||||
|  |                 timestamp = records[i].header.timestamp; | ||||||
|  |                 // temperature = records[i].header.info.temperature;
 | ||||||
|  |                 printf("%c%c.sample%lld.", records[i].header.char1, records[i].header.char2, timestamp); | ||||||
|  |                 range = records[i].header.info.range; | ||||||
|  |                 break; | ||||||
|  |             case ACCELEROMETER_DATA_ACQUISITION_DATA: | ||||||
|  |                 if (printing_header) { | ||||||
|  |                     printing_header = false; | ||||||
|  |                     uint8_t filter = 0; | ||||||
|  |                     switch (records[i].data.z.filter) { | ||||||
|  |                         case LIS2DW_BANDWIDTH_FILTER_DIV2: | ||||||
|  |                             filter = 2; | ||||||
|  |                             break; | ||||||
|  |                         case LIS2DW_BANDWIDTH_FILTER_DIV4: | ||||||
|  |                             filter = 4; | ||||||
|  |                             break; | ||||||
|  |                         case LIS2DW_BANDWIDTH_FILTER_DIV10: | ||||||
|  |                             filter = 10; | ||||||
|  |                             break; | ||||||
|  |                         case LIS2DW_BANDWIDTH_FILTER_DIV20: | ||||||
|  |                             filter = 20; | ||||||
|  |                             break; | ||||||
|  |                     } | ||||||
|  |                     switch (range) { | ||||||
|  |                         case LIS2DW_RANGE_16_G: | ||||||
|  |                             lsb_value = (records[i].data.y.lpmode == LIS2DW_LP_MODE_1) ? 7.808 : 1.952; | ||||||
|  |                             break; | ||||||
|  |                         case LIS2DW_RANGE_8_G: | ||||||
|  |                             lsb_value = (records[i].data.y.lpmode == LIS2DW_LP_MODE_1) ? 3.904 : 0.976; | ||||||
|  |                             break; | ||||||
|  |                         case LIS2DW_RANGE_4_G: | ||||||
|  |                             lsb_value = (records[i].data.y.lpmode == LIS2DW_LP_MODE_1) ? 1.952 : 0.488; | ||||||
|  |                             break; | ||||||
|  |                         case LIS2DW_RANGE_2_G: | ||||||
|  |                             lsb_value = (records[i].data.y.lpmode == LIS2DW_LP_MODE_1) ? 0.976 : 0.244; | ||||||
|  |                             break; | ||||||
|  |                     } | ||||||
|  |                     printf("RANGE%d_LP%d_FILT%d.CSV\n", range, records[i].data.y.lpmode + 1, filter); | ||||||
|  |                     printf("timestamp,accX,accY,accZ\n"); | ||||||
|  |                 } | ||||||
|  |                 printf("%lld,%f,%f,%f\n", | ||||||
|  |                         (timestamp * 100 + records[i].data.counter) * 10, | ||||||
|  |                         9.80665 * ((double)(records[i].data.x.accel - 8192)) * lsb_value / 1000, | ||||||
|  |                         9.80665 * ((double)(records[i].data.y.accel - 8192)) * lsb_value / 1000, | ||||||
|  |                         9.80665 * ((double)(records[i].data.z.accel - 8192)) * lsb_value / 1000); | ||||||
|  |                 break; | ||||||
|  |             case ACCELEROMETER_DATA_ACQUISITION_INVALID: | ||||||
|  |             case ACCELEROMETER_DATA_ACQUISITION_DELETED: | ||||||
|  |                 // don't print anything
 | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |         records[i].header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_DELETED; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // uncomment this to mark all pages deleted
 | ||||||
|  |     // write_buffer_to_page((uint8_t *)records, page);
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void print_records() { | ||||||
|  |     uint8_t buf[256]; | ||||||
|  |     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++) { | ||||||
|  |             uint8_t pages_written = buf[j]; | ||||||
|  |             uint8_t start = 0; | ||||||
|  |             if (i == 0 && j == 0) { | ||||||
|  |                 pages_written <<= 4; | ||||||
|  |                 start = 4; | ||||||
|  |             } | ||||||
|  |             for(int k = start; k < 8; k++) { | ||||||
|  |                 if ((pages_written & 0x80) == 0) { | ||||||
|  |                     print_records_at_page(i * 2048 + j * 8 + k); | ||||||
|  |                 } | ||||||
|  |                 pages_written <<= 1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void app_init(void) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void app_wake_from_backup(void) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void app_setup(void) { | ||||||
|  |     spi_flash_init(); | ||||||
|  |     delay_ms(5000); | ||||||
|  | 
 | ||||||
|  |     // bool erase = false;
 | ||||||
|  |     // if (erase) {
 | ||||||
|  |     //     printf("Erasing...\n");
 | ||||||
|  |     //     wait_for_flash_ready();
 | ||||||
|  |     //     watch_set_pin_level(A3, false);
 | ||||||
|  |     //     spi_flash_command(CMD_ENABLE_WRITE);
 | ||||||
|  |     //     wait_for_flash_ready();
 | ||||||
|  |     //     watch_set_pin_level(A3, false);
 | ||||||
|  |     //     spi_flash_command(CMD_CHIP_ERASE);
 | ||||||
|  |     //     delay_ms(10000);
 | ||||||
|  |     // }
 | ||||||
|  |   | ||||||
|  |     print_records(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void app_prepare_for_standby(void) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void app_wake_from_standby(void) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool app_loop(void) { | bool app_loop(void) { | ||||||
|     // delay_ms(5000);
 |     delay_ms(5000); | ||||||
| 
 | 
 | ||||||
|     return; |     return true; | ||||||
|          |  | ||||||
|     // simulate logging 15 seconds of data
 |  | ||||||
|     watch_date_time date_time = watch_rtc_get_date_time(); |  | ||||||
|     acceleromter_training_record_t record; |  | ||||||
| 
 |  | ||||||
|     record.header.temperature = 0xC30; |  | ||||||
|     record.header.temperature |= (ACCELEROMETER_TRAINING_RECORD_HEADER << 14); |  | ||||||
|     record.header.char1 = 'W'; |  | ||||||
|     record.header.char2 = 'A'; |  | ||||||
|     record.header.timestamp = watch_utility_date_time_to_unix_time(date_time, 0);; |  | ||||||
| 
 |  | ||||||
|     acceleromter_training_record_t records[32]; |  | ||||||
|     memset(records, 0xFF, sizeof(records)); |  | ||||||
|     records[0] = record; |  | ||||||
|     uint16_t pos = 1; |  | ||||||
|     uint32_t counter = 0; |  | ||||||
| 
 |  | ||||||
|     printf("logging 15*25 data points for timestamp %ld\n", record.header.timestamp); |  | ||||||
|     for(uint8_t i = 0; i < 15; i++) { |  | ||||||
|         for(uint8_t j = 0; j < 25; j++) { |  | ||||||
|             record.data.x_accel = arc4random() & 0x3FFF; |  | ||||||
|             record.data.x_accel |= ACCELEROMETER_TRAINING_RECORD_DATA << 14; |  | ||||||
|             record.data.y_accel = arc4random() & 0x3FFF; |  | ||||||
|             record.data.z_accel = arc4random() & 0x3FFF; |  | ||||||
|             record.data.counter = i; |  | ||||||
|             records[pos++] = record; |  | ||||||
|             if (pos >= 32) { |  | ||||||
|                 printf("pos overflowed at counter %ld\n", counter); |  | ||||||
|                 int16_t next_available_page = get_next_available_page(); |  | ||||||
|                 if (next_available_page > 0) { |  | ||||||
|                     write_buffer_to_page((uint8_t *)records, next_available_page); |  | ||||||
|                     wait_for_flash_ready(); |  | ||||||
|                 } |  | ||||||
|                 pos = 0; |  | ||||||
|                 memset(records, 0xFF, sizeof(records)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     if (records[0].header.temperature >> 14 != ACCELEROMETER_TRAINING_RECORD_INVALID) { |  | ||||||
|         int16_t next_available_page = get_next_available_page(); |  | ||||||
|         if (next_available_page > 0) { |  | ||||||
|             printf("Partial write\n"); |  | ||||||
|             write_buffer_to_page((uint8_t *)records, next_available_page); |  | ||||||
|             wait_for_flash_ready(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     delay_ms(60000); |  | ||||||
| 
 |  | ||||||
|     return false; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -27,14 +27,16 @@ | |||||||
| #include "accelerometer_data_acquisition_face.h" | #include "accelerometer_data_acquisition_face.h" | ||||||
| #include "watch_utility.h" | #include "watch_utility.h" | ||||||
| #include "lis2dw.h" | #include "lis2dw.h" | ||||||
|  | #include "spiflash.h" | ||||||
| 
 | 
 | ||||||
| #define ACCELEROMETER_RANGE LIS2DW_RANGE_4_G | #define ACCELEROMETER_RANGE LIS2DW_RANGE_4_G | ||||||
| #define ACCELEROMETER_LPMODE LIS2DW_LP_MODE_1 | #define ACCELEROMETER_LPMODE LIS2DW_LP_MODE_2 | ||||||
| #define ACCELEROMETER_FILTER LIS2DW_BANDWIDTH_FILTER_DIV2 | #define ACCELEROMETER_FILTER LIS2DW_BANDWIDTH_FILTER_DIV2 | ||||||
| #define ACCELEROMETER_LOW_NOISE true | #define ACCELEROMETER_LOW_NOISE true | ||||||
| #define SECONDS_TO_RECORD 15 | #define SECONDS_TO_RECORD 15 | ||||||
| 
 | 
 | ||||||
| static const char activity_types[][3] = { | static const char activity_types[][3] = { | ||||||
|  |     "TE",   // Testing
 | ||||||
|     "ID",   // Idle
 |     "ID",   // Idle
 | ||||||
|     "OF",   // Off-wrist
 |     "OF",   // Off-wrist
 | ||||||
|     "SL",   // Sleeping
 |     "SL",   // Sleeping
 | ||||||
| @ -49,7 +51,6 @@ static const char activity_types[][3] = { | |||||||
|     "SU",   // Stairs Up
 |     "SU",   // Stairs Up
 | ||||||
|     "SD",   // Stairs Down
 |     "SD",   // Stairs Down
 | ||||||
|     "WL",   // Weight Lifting
 |     "WL",   // Weight Lifting
 | ||||||
|     "TE",   // Testing
 |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void update(accelerometer_data_acquisition_state_t *state); | static void update(accelerometer_data_acquisition_state_t *state); | ||||||
| @ -58,23 +59,43 @@ static void advance_current_setting(accelerometer_data_acquisition_state_t *stat | |||||||
| static void start_reading(accelerometer_data_acquisition_state_t *state, movement_settings_t *settings); | static void start_reading(accelerometer_data_acquisition_state_t *state, movement_settings_t *settings); | ||||||
| static void continue_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 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(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { | void accelerometer_data_acquisition_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { | ||||||
|     (void) settings; |     (void) settings; | ||||||
|     (void) watch_face_index; |     (void) watch_face_index; | ||||||
|  |     accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)*context_ptr; | ||||||
|     if (*context_ptr == NULL) { |     if (*context_ptr == NULL) { | ||||||
|         *context_ptr = malloc(sizeof(accelerometer_data_acquisition_state_t)); |         *context_ptr = malloc(sizeof(accelerometer_data_acquisition_state_t)); | ||||||
|         memset(*context_ptr, 0, sizeof(accelerometer_data_acquisition_state_t)); |         memset(*context_ptr, 0, sizeof(accelerometer_data_acquisition_state_t)); | ||||||
|         accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)*context_ptr; |         state = (accelerometer_data_acquisition_state_t *)*context_ptr; | ||||||
|         state->beep_with_countdown = true; |         state->beep_with_countdown = true; | ||||||
|         state->countdown_length = 3; |         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(); | ||||||
|  |         watch_set_pin_level(A3, false); | ||||||
|  |         spi_flash_command(CMD_ENABLE_WRITE); | ||||||
|  |         wait_for_flash_ready(); | ||||||
|  |         spi_flash_write_data(0, buf, 256); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void accelerometer_data_acquisition_face_activate(movement_settings_t *settings, void *context) { | void accelerometer_data_acquisition_face_activate(movement_settings_t *settings, void *context) { | ||||||
|     (void) settings; |     (void) settings; | ||||||
|     accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context; |     accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context; | ||||||
|     state->next_available_page = 123; |     state->next_available_page = get_next_available_page(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { | bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { | ||||||
| @ -91,6 +112,7 @@ bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_s | |||||||
|                 case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN: |                 case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN: | ||||||
|                     if (state->countdown_ticks > 0) { |                     if (state->countdown_ticks > 0) { | ||||||
|                         state->countdown_ticks--; |                         state->countdown_ticks--; | ||||||
|  |                         printf("countdown: %d\n", state->countdown_ticks); | ||||||
|                         if (state->countdown_ticks == 0) { |                         if (state->countdown_ticks == 0) { | ||||||
|                             // at zero, begin reading
 |                             // at zero, begin reading
 | ||||||
|                             state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING; |                             state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING; | ||||||
| @ -102,6 +124,16 @@ bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_s | |||||||
|                             // beep for last two ticks before reading
 |                             // beep for last two ticks before reading
 | ||||||
|                             if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C5, 75); |                             if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C5, 75); | ||||||
|                         } |                         } | ||||||
|  |                         if (state->countdown_ticks == 1) { | ||||||
|  |                             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(); | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|                     update(state); |                     update(state); | ||||||
|                     break; |                     break; | ||||||
| @ -187,8 +219,6 @@ bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_s | |||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here,
 |  | ||||||
|     // you should return false since the PWM driver does not operate in standby mode.
 |  | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -231,8 +261,9 @@ static void update(accelerometer_data_acquisition_state_t *state) { | |||||||
| 
 | 
 | ||||||
|     watch_set_colon(); |     watch_set_colon(); | ||||||
| 
 | 
 | ||||||
|     // special case: display full if full
 |     // special case: display full if full, <1% if nearly full
 | ||||||
|     if (state->next_available_page < 0) watch_display_string(" FUL", 6); |     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
 |     // Bell if beep enabled
 | ||||||
|     if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL); |     if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL); | ||||||
| @ -249,7 +280,6 @@ static void update(accelerometer_data_acquisition_state_t *state) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void update_settings(accelerometer_data_acquisition_state_t *state) { | static void update_settings(accelerometer_data_acquisition_state_t *state) { | ||||||
|     printf("TODO: Settings screen\n"); |  | ||||||
|     char buf[12]; |     char buf[12]; | ||||||
|     watch_clear_colon(); |     watch_clear_colon(); | ||||||
|     if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL); |     if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL); | ||||||
| @ -287,18 +317,110 @@ static void advance_current_setting(accelerometer_data_acquisition_state_t *stat | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool deleteme = false; | 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(); | ||||||
|  |     watch_set_pin_level(A3, false); | ||||||
|  |     spi_flash_command(CMD_ENABLE_WRITE); | ||||||
|  |     wait_for_flash_ready(); | ||||||
|  |     watch_set_pin_level(A3, false); | ||||||
|  |     spi_flash_write_data(address, buf, 256); | ||||||
|  |     wait_for_flash_ready(); | ||||||
|  | 
 | ||||||
|  |     uint8_t buf2[256]; | ||||||
|  |     watch_set_pin_level(A3, false); | ||||||
|  |     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]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     watch_set_pin_level(A3, false); | ||||||
|  |     spi_flash_read_data(header_page * 256, used_pages, 256); | ||||||
|  |     used_pages[offset_in_buf] = used_byte; | ||||||
|  |     watch_set_pin_level(A3, false); | ||||||
|  |     spi_flash_command(CMD_ENABLE_WRITE); | ||||||
|  |     wait_for_flash_ready(); | ||||||
|  |     watch_set_pin_level(A3, false); | ||||||
|  |     spi_flash_write_data(header_page * 256, used_pages, 256); | ||||||
|  |     wait_for_flash_ready(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool wait_for_flash_ready(void) { | ||||||
|  |     watch_set_pin_level(A3, false); | ||||||
|  |     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?
 | ||||||
|  |     watch_set_pin_level(A3, true); | ||||||
|  |     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, movement_settings_t *settings) { | static void start_reading(accelerometer_data_acquisition_state_t *state, movement_settings_t *settings) { | ||||||
|     (void) state; |     printf("Start reading\n"); | ||||||
|     watch_enable_i2c(); |     lis2dw_fifo_t fifo; | ||||||
|     lis2dw_begin(); |     lis2dw_read_fifo(&fifo); // dump the fifo, this starts a fresh round of data in continue_reading
 | ||||||
|     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; |     accelerometer_data_acquisition_record_t record; | ||||||
|     watch_date_time date_time = watch_rtc_get_date_time(); |     watch_date_time date_time = watch_rtc_get_date_time(); | ||||||
| @ -310,89 +432,26 @@ static void start_reading(accelerometer_data_acquisition_state_t *state, movemen | |||||||
|     record.header.char2 = activity_types[state->activity_type_index][1]; |     record.header.char2 = activity_types[state->activity_type_index][1]; | ||||||
|     record.header.timestamp = state->starting_timestamp; |     record.header.timestamp = state->starting_timestamp; | ||||||
| 
 | 
 | ||||||
|     uint8_t range = 0; |  | ||||||
| 
 |  | ||||||
|     switch (record.header.info.range) { |  | ||||||
|         case LIS2DW_RANGE_16_G: |  | ||||||
|             range = 16; |  | ||||||
|             break; |  | ||||||
|         case LIS2DW_RANGE_8_G: |  | ||||||
|             range = 8; |  | ||||||
|             break; |  | ||||||
|         case LIS2DW_RANGE_4_G: |  | ||||||
|             range = 4; |  | ||||||
|             break; |  | ||||||
|         case LIS2DW_RANGE_2_G: |  | ||||||
|             range = 2; |  | ||||||
|             break; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     state->records[state->pos++] = record; |     state->records[state->pos++] = record; | ||||||
| 
 |  | ||||||
|     printf("TRAINING_%c%c_%d_RANGE%d_", record.header.char1, record.header.char2, record.header.timestamp, range); |  | ||||||
| 
 |  | ||||||
|     deleteme = true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void _write_page(accelerometer_data_acquisition_state_t *state) { |  | ||||||
|     if (state->next_available_page > 0) { |  | ||||||
|         // write_buffer_to_page((uint8_t *)records, next_available_page);
 |  | ||||||
|         // wait_for_flash_ready();
 |  | ||||||
|     } |  | ||||||
|     // state->next_available_page = get_next_available_page();
 |  | ||||||
|     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) { |  | ||||||
|     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; |  | ||||||
|     record.data.y.accel = reading.y; |  | ||||||
|     record.data.z.accel = reading.z; |  | ||||||
|     record.data.counter = SECONDS_TO_RECORD - state->reading_ticks + 1; |  | ||||||
|     state->records[state->pos++] = record; |  | ||||||
|     if (deleteme) { |  | ||||||
|         deleteme = false; |  | ||||||
|         uint8_t filter = 0; |  | ||||||
|         switch (record.data.z.filter) { |  | ||||||
|             case LIS2DW_BANDWIDTH_FILTER_DIV2: |  | ||||||
|                 filter = 2; |  | ||||||
|                 break; |  | ||||||
|             case LIS2DW_BANDWIDTH_FILTER_DIV4: |  | ||||||
|                 filter = 4; |  | ||||||
|                 break; |  | ||||||
|             case LIS2DW_BANDWIDTH_FILTER_DIV10: |  | ||||||
|                 filter = 10; |  | ||||||
|                 break; |  | ||||||
|             case LIS2DW_BANDWIDTH_FILTER_DIV20: |  | ||||||
|                 filter = 20; |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
|         printf("LP%d_FILT%d.CSV\n", record.data.y.lpmode + 1, filter); |  | ||||||
|     } |  | ||||||
|     printf("%d, %d, %d, %d\n", record.data.counter, record.data.x.accel, record.data.y.accel, record.data.z.accel); |  | ||||||
|     if (state->pos >= 32) { |  | ||||||
|         _write_page(state); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void continue_reading(accelerometer_data_acquisition_state_t *state) { | static void continue_reading(accelerometer_data_acquisition_state_t *state) { | ||||||
|  |     printf("Continue reading\n"); | ||||||
|     lis2dw_fifo_t fifo; |     lis2dw_fifo_t fifo; | ||||||
| 
 |  | ||||||
|     lis2dw_read_fifo(&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.
 | ||||||
|  | 
 | ||||||
|     for(int i = 0; i < fifo.count; i++) { |     for(int i = 0; i < fifo.count; i++) { | ||||||
|         _log_data_point(state, fifo.readings[i]); |         log_data_point(state, fifo.readings[i], i * 4 + offset); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void finish_reading(accelerometer_data_acquisition_state_t *state) { | static void finish_reading(accelerometer_data_acquisition_state_t *state) { | ||||||
|     printf("finishing\n"); |     printf("Finish reading\n"); | ||||||
|     if (state->pos != 0) { |     if (state->pos != 0) { | ||||||
|         _write_page(state); |         write_page(state); | ||||||
|     } |     } | ||||||
|     lis2dw_set_data_rate(LIS2DW_DATA_RATE_POWERDOWN); |     lis2dw_set_data_rate(LIS2DW_DATA_RATE_POWERDOWN); | ||||||
|     watch_disable_i2c(); |     watch_disable_i2c(); | ||||||
|  | |||||||
| @ -34,29 +34,29 @@ | |||||||
| 
 | 
 | ||||||
| typedef union { | typedef union { | ||||||
|     struct { |     struct { | ||||||
|         union { |         struct { | ||||||
|             int16_t record_type : 2;    // see above, helps us identify record types when reading back
 |             uint16_t record_type : 2;   // see above, helps us identify record types when reading back
 | ||||||
|             int16_t range : 2;          // accelerometer range (see lis2dw_range_t)
 |             uint16_t range : 2;         // accelerometer range (see lis2dw_range_t)
 | ||||||
|             int16_t temperature : 12;   // raw value from the temperature sensor
 |             uint16_t temperature : 12;  // raw value from the temperature sensor
 | ||||||
|         } info; |         } info; | ||||||
|         int8_t char1 : 8;               // First character of the activity type
 |         uint8_t char1 : 8;              // First character of the activity type
 | ||||||
|         int8_t char2 : 8;               // Second character of the activity type
 |         uint8_t char2 : 8;              // Second character of the activity type
 | ||||||
|         int32_t timestamp : 32;         // UNIX timestamp for the measurement
 |         uint32_t timestamp : 32;        // UNIX timestamp for the measurement
 | ||||||
|     } header; |     } header; | ||||||
|     struct { |     struct { | ||||||
|         union { |         struct { | ||||||
|             int16_t record_type : 2;    // duplicate; this is the same field as info above
 |             uint16_t record_type : 2;   // duplicate; this is the same field as info above
 | ||||||
|             int16_t accel : 14;         // X acceleration value, raw
 |             uint16_t accel : 14;        // X acceleration value, raw, offset by 16384
 | ||||||
|         } x; |         } x; | ||||||
|         union { |         struct { | ||||||
|             int16_t lpmode : 2;         // low power mode (see lis2dw_low_power_mode_t)
 |             uint16_t lpmode : 2;        // low power mode (see lis2dw_low_power_mode_t)
 | ||||||
|             int16_t accel : 14;         // Y acceleration value, raw
 |             uint16_t accel : 14;        // Y acceleration value, raw, offset by 16384
 | ||||||
|         } y; |         } y; | ||||||
|         union { |         struct { | ||||||
|             int16_t filter : 2;         // bandwidth filtering selection (see lis2dw_bandwidth_filtering_mode_t)
 |             uint16_t filter : 2;        // bandwidth filtering selection (see lis2dw_bandwidth_filtering_mode_t)
 | ||||||
|             int16_t accel : 14;         // Z acceleration value, raw
 |             uint16_t accel : 14;        // Z acceleration value, raw, offset by 16384
 | ||||||
|         } z; |         } z; | ||||||
|         int32_t counter : 16;           // number of seconds since timestamp in header
 |         uint32_t counter : 16;          // number of seconds since timestamp in header
 | ||||||
|     } data; |     } data; | ||||||
|     uint64_t value; |     uint64_t value; | ||||||
| } accelerometer_data_acquisition_record_t; | } accelerometer_data_acquisition_record_t; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user