-
Notifications
You must be signed in to change notification settings - Fork 0
/
exhilaration_logger.pde
486 lines (417 loc) · 15.4 KB
/
exhilaration_logger.pde
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
// Exhilaration Data Logger
// Date:
// August 2010
// Authors:
// F. Graffagnino
// J. Moreau
// J. Borland
#include "Wire.h"
#include "hrmi_funcs.h"
#include <Ports.h>
#include "PortsBMP085.h"
#include <RF12.h> // needed to avoid a linker error :(
/*
* HRMI_HOST_BAUDRATE should be set to the baudrate the host will use
* to communicate with the Arduino over the serial interface.
*
* HRMI_I2C_ADDR should be set to the I2C address the HRMI is configured
* with.
*/
#define HRMI_HOST_BAUDRATE 57600
#define HRMI_I2C_ADDR 63
#define HRMI_NO_DATA 0x2
// modes
#define MODE_WAITING_FOR_SER_CMD 0
#define MODE_RECORDING 1
#define MODE_WAITING_FOR_RESET 2
#define MODE_MEMORY_FULL 3
// constants
#define SERIAL_CMD_TIMEOUT 60000
#define MEM_SIZE_IN_BYTES 262144
//#define MEM_SIZE_IN_BYTES 256
#define SENSOR_VALS_RECORD_SIZE 10
PortI2C one (1);
PortI2C two (2);
PortI2C three (3);
//BMP085 psensor (one, 0); // ultra low power
BMP085 psensor (one, 1); // standard
//BMP085 psensor (one, 3); // ultra high resolution
DeviceI2C rtc (two, 0x68);
MemoryPlug mem (three);
MemoryStream stream (mem);
BlinkPlug buttons (4);
MilliTimer timer;
int mode;
int numEntries = 0; // Number of HR values to request
int numRspBytes; // Number of Response bytes to read
byte i2cRspArray[34]; // I2C response array, sized to read 32 HR values
byte hrmi_addr = HRMI_I2C_ADDR; // I2C address to use
byte sensor_vals[SENSOR_VALS_RECORD_SIZE];
int green_led_state;
char input_cmd;
static byte bin2bcd (byte val) {
return val + 6 * (val / 10);
}
static byte bcd2bin (byte val) {
return val - 6 * (val >> 4);
}
static void setDate (byte yy, byte mm, byte dd, byte h, byte m, byte s) {
rtc.send();
rtc.write(0);
rtc.write(bin2bcd(s));
rtc.write(bin2bcd(m));
rtc.write(bin2bcd(h));
rtc.write(bin2bcd(0));
rtc.write(bin2bcd(dd));
rtc.write(bin2bcd(mm));
rtc.write(bin2bcd(yy));
rtc.write(0);
rtc.stop();
}
static void getDate (byte* buf) {
rtc.send();
rtc.write(0);
rtc.stop();
rtc.receive();
buf[5] = bcd2bin(rtc.read(0));
buf[4] = bcd2bin(rtc.read(0));
buf[3] = bcd2bin(rtc.read(0));
rtc.read(0);
buf[2] = bcd2bin(rtc.read(0));
buf[1] = bcd2bin(rtc.read(0));
buf[0] = bcd2bin(rtc.read(1));
rtc.stop();
}
/*
* Reads the sensor values and stores them in buffer
*
* buffer[0] = hours
* buffer[1] = minutes
* buffer[2] = seconds
* buffer[3] = heart rate
* buffer[4-5] = raw temperature
* buffer[6-9] = pressure
*/
void read_sensor_values(byte * buffer)
{
// Get the response from the RTC
byte now[6];
getDate(now);
buffer[0] = now[3]; // hours
buffer[1] = now[4]; // minutes
buffer[2] = now[5]; // seconds
// Get the response from the HRMI
// Request a set of heart rate values
numEntries = 1;
hrmiCmdArg(hrmi_addr, 'G', numEntries);
numRspBytes = numEntries + 2;
if (hrmiGetData(hrmi_addr, numRspBytes, i2cRspArray) != -1)
{
// First see, if we have valid data. If so, print it out
if ( i2cRspArray[0] & HRMI_NO_DATA )
{
buffer[3] = 0x00; // zero for invalid data
}
else
{
buffer[3] = i2cRspArray[2]; // valid heart rate
}
}
// Get the response from the pressure/temp sensor
int32_t traw = psensor.measure(BMP085::TEMP);
int32_t praw = psensor.measure(BMP085::PRES);
// omit following code to avoid linking in some complex calculation code
struct { int16_t temp; int32_t pres; } payload;
psensor.calculate(payload.temp, payload.pres);
memcpy(&buffer[4], &payload.temp, 2);
memcpy(&buffer[6], &payload.pres, 4);
}
void print_sensor_record(byte *buffer) {
struct { int16_t temp; int32_t pres; } payload;
float tempF;
Serial.print((int) buffer[0]);
Serial.print(":");
Serial.print((int) buffer[1]);
Serial.print(":");
Serial.print((int) buffer[2]);
Serial.print(", ");
Serial.print((int) buffer[3]);
Serial.print(", ");
memcpy(&payload.temp, &buffer[4], 2);
memcpy(&payload.pres, &buffer[6], 4);
tempF = (payload.temp / 10.0) * 1.8 + 32.0;
Serial.print(tempF);
Serial.print(", ");
Serial.print(payload.pres);
Serial.println();
}
/*
* Reads all values stored in memory and prints them out to the serial port
*
* format:
* hh:mm:ss, heart rate, temperature F, pressure
*/
void print_all_data()
{
int i;
byte buffer[SENSOR_VALS_RECORD_SIZE];
stream.reset();
uint32_t mem_ptr = 0;
uint32_t num_records_returned = 0;
for (mem_ptr = 0; mem_ptr < (MEM_SIZE_IN_BYTES - SENSOR_VALS_RECORD_SIZE); mem_ptr += SENSOR_VALS_RECORD_SIZE)
{
for (i = 0; i < SENSOR_VALS_RECORD_SIZE; i++) {
buffer[i] = stream.get();
}
// check for end of valid data
if (buffer[0] == 238 && buffer[1] == 238 && buffer[2] == 238) {
Serial.println();
Serial.print(num_records_returned);
Serial.println(" records returned.");
break;
} else {
// Check to see if we need to discard the corrupted data from a flush
if (buffer[0] == 255 && buffer[1] == 255 && buffer[2] == 255) {
//Corrupted record. Do nothing for now
} else {
print_sensor_record( buffer );
num_records_returned++;
}
}
}
}
/*
* Dumps all memory without any regard to end of data or validity
*
* format:
* hh:mm:ss, heart rate, temperature F, pressure
*/
void dump_all_data()
{
int i;
byte buffer[SENSOR_VALS_RECORD_SIZE];
stream.reset();
uint32_t mem_ptr = 0;
uint32_t num_records_returned = 0;
for (mem_ptr = 0; mem_ptr < (MEM_SIZE_IN_BYTES - SENSOR_VALS_RECORD_SIZE); mem_ptr += SENSOR_VALS_RECORD_SIZE)
{
for (i = 0; i < SENSOR_VALS_RECORD_SIZE; i++) {
buffer[i] = stream.get();
}
print_sensor_record( buffer );
num_records_returned++;
}
}
void setup () {
Serial.begin(115200);
Serial.println("exhilaration logger");
// initialize into "WAITING_FOR_SERIAL_COMMAND" mode
mode = MODE_WAITING_FOR_SER_CMD;
// Initialize the I2C communication for HRMI
hrmi_open();
hrmiCmdArg(hrmi_addr, 'S', 1);
// init pressure/temp sensor
// may omit following call if calculate() won't be called later on
psensor.getCalibData();
byte now[6];
getDate(now);
Serial.print("Current Date:");
for (byte i = 0; i < 6; ++i) {
Serial.print(' ');
Serial.print((int) now[i]);
}
Serial.println(' ');
// set up timer to wait for an input command
timer.set(SERIAL_CMD_TIMEOUT);
Serial.print("waiting for command for ");
Serial.print(SERIAL_CMD_TIMEOUT);
Serial.println(" seconds ['D' = Dump Memory, 'G' = Get Valid Data, 'R' = Reset memory, 'S' = Set time]");
// turn on green led solid and red off
green_led_state = 1;
buttons.ledOn(1);
buttons.ledOff(2);
}
void loop () {
uint32_t millis_start, loop_duration;
float percentage_complete;
millis_start = millis();
// logic depends on the current mode
switch (mode) {
case MODE_WAITING_FOR_SER_CMD:
case MODE_WAITING_FOR_RESET:
// check to see if timer has not expired
// or we are waiting indefinitely
if ( (mode==MODE_WAITING_FOR_RESET) || !timer.poll() ) {
// timer is still alive. check for commands
if (Serial.available()) {
// we received a command - process it
input_cmd = Serial.read();
switch (input_cmd) {
case 'D':
Serial.println("Dumping all memory...");
dump_all_data();
mode = MODE_WAITING_FOR_RESET;
break;
case 'G':
Serial.println("Returning all logged data...");
print_all_data();
mode = MODE_WAITING_FOR_RESET;
break;
case 'R':
Serial.print("Resetting all memory... ");
stream.reset();
for (uint32_t ii = 0; ii < MEM_SIZE_IN_BYTES; ii++) {
if (ii % 1024 == 0) {
percentage_complete = ((float)ii / (float)MEM_SIZE_IN_BYTES) * 100.0;
Serial.print(ii);
Serial.print(" ");
Serial.print(percentage_complete);
Serial.println("% complete");
}
stream.put(0xEE);
}
stream.flush();
mode = MODE_WAITING_FOR_RESET;
Serial.println("done.");
break;
case 'S':
mode = MODE_WAITING_FOR_RESET;
char input_field_buff[3];
byte bytes_waiting;
// Check to see that there is enough input
bytes_waiting = Serial.available();
if (bytes_waiting < 13) {
Serial.println("Error: Format is \"S YYMMDDhhmmss\"");
Serial.print("Only saw ");
Serial.print(bytes_waiting, DEC);
Serial.println(" characters following the char S");
} else {
// Make sure our string always ends in a null char
input_field_buff[2] = 0;
// Space
Serial.read();
// Year
input_field_buff[0] = Serial.read();
input_field_buff[1] = Serial.read();
byte year = atoi(input_field_buff);
// Month
input_field_buff[0] = Serial.read();
input_field_buff[1] = Serial.read();
byte month = atoi(input_field_buff);
// Day
input_field_buff[0] = Serial.read();
input_field_buff[1] = Serial.read();
byte day = atoi(input_field_buff);
// Hour
input_field_buff[0] = Serial.read();
input_field_buff[1] = Serial.read();
byte hour = atoi(input_field_buff);
// Minute
input_field_buff[0] = Serial.read();
input_field_buff[1] = Serial.read();
byte minute = atoi(input_field_buff);
// Second
input_field_buff[0] = Serial.read();
input_field_buff[1] = Serial.read();
byte second = atoi(input_field_buff);
// Range checking
if ((year > 99) || (year < 0)) {
Serial.println("Error: year out of range");
break;
}
if ((month > 12) || (month < 0)) {
Serial.println("Error: month out of range");
break;
}
if ((day > 31) || (day < 0)) {
Serial.println("Error: day out of range");
break;
}
if ((hour > 23) || (hour < 0)) {
Serial.println("Error: hour out of range");
break;
}
if ((minute > 59) || (minute < 0)) {
Serial.println("Error: minute out of range");
break;
}
if ((second > 59) || (second < 0)) {
Serial.println("Error: second out of range");
break;
}
// Set the date
byte now[6];
getDate(now);
Serial.print("Date before changing:");
for (byte i = 0; i < 6; ++i) {
Serial.print(' ');
Serial.print((int) now[i]);
}
Serial.println(' ');
setDate(year, month, day, hour, minute, second);
getDate(now);
Serial.print("Date after set command: ");
for (byte i = 0; i < 6; ++i) {
Serial.print(' ');
Serial.print((int) now[i]);
}
Serial.println(' ');
Serial.println("Done.");
}
break;
default:
Serial.println("Invalid command. Waiting indefinitely");
mode = MODE_WAITING_FOR_RESET;
break;
}
// Read extra trash characters to reset string before next attempt
while (Serial.available()) {
Serial.read();
}
}
} else {
// timer has expired. transition to recording mode
if (mode != MODE_WAITING_FOR_RESET) {
//reset memory stream to beginning
stream.reset();
Serial.println("entering recording mode...");
mode = MODE_RECORDING;
}
}
break;
case MODE_RECORDING:
// time to do some work
read_sensor_values( sensor_vals );
// print out for debugging
print_sensor_record( sensor_vals );
//walk through each byte and put into the mem stream
for (int xx = 0; xx < SENSOR_VALS_RECORD_SIZE; xx++) {
stream.put(sensor_vals[xx]);
}
stream.flush();
// check to see if we are too close to the end
if ((MEM_SIZE_IN_BYTES - stream.position(1)) < 60) {
stream.flush();
mode = MODE_MEMORY_FULL;
}
// toggle green led state
if (green_led_state) {
buttons.ledOff(1);
green_led_state = 0;
} else {
buttons.ledOn(1);
green_led_state = 1;
}
break;
case MODE_MEMORY_FULL:
buttons.ledOn(2);
break;
default:
// todo - print out error
break;
}
loop_duration = millis() - millis_start;
//Serial.print(loop_duration);
//Serial.println(" millisecond loop duration");
delay(1000 - loop_duration);
}