mirror of
https://github.com/firewalkwithm3/Sensor-Watch.git
synced 2024-11-22 19:20:30 +08:00
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool app_loop(void) {
|
static void print_records_at_page(uint16_t page) {
|
||||||
// delay_ms(5000);
|
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;
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
// 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();
|
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;
|
||||||
}
|
}
|
||||||
pos = 0;
|
switch (range) {
|
||||||
memset(records, 0xFF, sizeof(records));
|
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;
|
||||||
}
|
}
|
||||||
if (records[0].header.temperature >> 14 != ACCELEROMETER_TRAINING_RECORD_INVALID) {
|
records[i].header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_DELETED;
|
||||||
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);
|
// uncomment this to mark all pages deleted
|
||||||
|
// write_buffer_to_page((uint8_t *)records, page);
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
delay_ms(5000);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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…
Reference in a new issue