Arduinoが出てから、SDカードやI2CやSPIセンサをかなり手軽に扱えるようになったと思う。これ以前であればPICで暗号のようなコードを書いて作るしかなかったので、 かなりの進歩だろう。センサは基本表面実装部品なので、変換基板かモジュールでないと扱いにくいが、加速度センサのADXL345やジャイロセンサのL3GD20はモジュールが広く販売されているので、 簡易的な慣性計測装置(IMU)としては使いやすい。CSV出力よりバイナリ出力の方が2倍程度高速な記録ができる。下記は、SDカードのルートにCFG.TXTとして一行目にサンプリングレート(ms)、 二行目に計測サンプル数を保存してから電源を入れるとその分だけ記録するようなコードである。記録中はLED点滅をするようにしている。CSV出力かバイナリ出力は コンパイル時に決めるようにしているが、もう少し手を入れればCFG.TXTで選択できるようにも出来るだろう。
表面、SDカード、センサ
裏面、I2Cレベル変換
#include <SdFat.h>
#include <I2C.h>
#include <TimerOne.h>
// CSV writer
#define ENABLE_CSV
// debug serial output
// #define FREERAM
// pinout (acc,gyr:I2C) SDA=A4 SCL=A5 (SDcard:SPI) CS=4 SCK=D13 MISO=D12 MOSI=11
// internal Timer1 PWM D9/D10
// not used Timer0 PWM millis() D5/D6 Timer2 D3 D11
// green LED D7
// red LED D3
#define LED_GREEN (7)
#define LED_RED (3)
#define SDCARD_CS (4)
#define ADXL345 (0x53)
#define ADXL345_IDCO 0x00
#define ADXL345_RATE 0x2C
#define ADXL345_RATE_100HZ 0x0A
#define ADXL345_RATE_200HZ 0x0B
#define ADXL345_RATE_400HZ 0x0C
#define ADXL345_RATE_800HZ 0x0D
#define ADXL345_POWR 0x2D
#define ADXL345_FORM 0x31
#define ADXL345_FIFO_CTL 0x38
#define L3GD20 B1101010 // SA0 = GND
//const byte L3GD20 = B1101011;// SA0 = VDD_IO
#define L3GD20_WHOAMI 0x0f
#define L3GD20_CTRL1 0x20
int32_t REC_COUNT = 200;
int32_t SAMPLE_RATE_INTERVAL = 5000;
// 5ms=200Hz 10ms=100Hz 2.5ms=400Hz 1.25ms=800Hz 0.5ms=2kHz
#ifdef ENABLE_CSV
bool CSVMODE = false;
#endif
// size must be < 255
#ifdef ENABLE_CSV
#define ACC_M_SIZE 50
#else
#define ACC_M_SIZE 60
#endif
// CSV 30[min] ~8Mbyte
// CSV writer 200Hz -> 50
// CSV writer 400Hz -> drop frame+
// BIN writer 500Hz -> 60
// BIN writer 800Hz -> 60 drop?
// BIN writer 1kHz -> 60 ~drop+
int16_t _gyr[ACC_M_SIZE][3];
int16_t _acc[ACC_M_SIZE][3];
//File accFile;
SdFat SD;
SdFile accFile;
#ifdef FREERAM
volatile int16_t _ram;
#endif
volatile uint8_t _acc_count8, _int_count8, _int_count8_hb;
volatile uint16_t _acc_ret;
volatile unsigned long time1, time2;
void readRegister(int deviceAddress, int address, volatile int * to)
{
I2c.read(deviceAddress, address, 0x6);
to[0] = I2c.receive(), to[0] |= I2c.receive() << 8;
to[1] = I2c.receive(), to[1] |= I2c.receive() << 8;
to[2] = I2c.receive(), to[2] |= I2c.receive() << 8;
}
void initAcc()
{
I2c.read(ADXL345, ADXL345_IDCO, 1);
_acc_ret = I2c.receive() << 8;
I2c.write(ADXL345, ADXL345_RATE, ADXL345_RATE_200HZ);
I2c.write(ADXL345, ADXL345_FORM, B00001111); // LEFT-JUSTTIFIED FULL_RES 16G
I2c.write(ADXL345, ADXL345_FIFO_CTL, 0x00);
I2c.write(ADXL345, ADXL345_POWR, 0x00);
I2c.write(ADXL345, ADXL345_POWR, 0x10);
I2c.write(ADXL345, ADXL345_POWR, 0x08);
I2c.read(L3GD20, L3GD20_WHOAMI, 1);
_acc_ret |= I2c.receive();
//Serial.print("ADXL345(E5)L3GD2(D4):");
//Serial.println(_acc_count,HEX);//
I2c.write(L3GD20, L3GD20_CTRL1, B01111111);
// |||||||+ X axis enable
// ||||||+- Y axis enable
// |||||+-- Z axis enable
// ||||+--- PD: 0: power down, 1: active
// ||++---- BW1-BW0: cut off 70[Hz]:11 / 25:11
// ++------ DR1-DR0: ODR 190[HZ]:01 / 95:00
}
void measureAcc()
{
int16_t acc[3];
#ifdef FREERAM
if(_ram < freeRam()) _ram = freeRam();
#endif
readRegister(ADXL345, 0x32, acc);
_acc[_acc_count8][0] = acc[0], _acc[_acc_count8][1] = acc[1], _acc[_acc_count8][2] = acc[2];
readRegister(L3GD20, 0x28|0x80, acc);
_gyr[_acc_count8][0] = acc[0], _gyr[_acc_count8][1] = acc[1], _gyr[_acc_count8][2] = acc[2];
// for DEBUG
// _acc[_acc_count][0] = -32768, _acc[_acc_count][1] = -32768, _acc[_acc_count][2] = -32768;
// _gyr[_acc_count][0] = -32768, _gyr[_acc_count][1] = -32768, _gyr[_acc_count][2] = -32768;
_acc_count8 ++;
if(_acc_count8 == ACC_M_SIZE/2) {
_int_count8 ++;
if(_int_count8 == 0) _int_count8_hb ++;
}
else if(_acc_count8 == ACC_M_SIZE) {
_int_count8 ++;
if(_int_count8 == 0) _int_count8_hb ++;
_acc_count8 = 0;
}
}
#ifdef FREERAM
int freeRam()
{
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
#endif
void setup()
{
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_RED, OUTPUT);
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_RED, LOW);
digitalWrite(LED_GREEN, HIGH);
digitalWrite(LED_RED, HIGH);
delay(1000);
#ifdef FREERAM
Serial.begin(9600);
#endif
I2c.begin();
I2c.setSpeed(1);
while(1) {
initAcc();
if(0xE5D4 == _acc_ret) break;
#ifdef FREERAM
Serial.println("SensorError");
#endif
}
digitalWrite(LED_GREEN, LOW);
pinMode(10, OUTPUT);
while(1) {
//if (!SD.begin(SDCARD_CS)) { delay(500); }
//if (!SD.begin(SDCARD_CS, SPI_FULL_SPEED)) {
if (!SD.begin(SDCARD_CS, SPI_HALF_SPEED)) {
#ifdef FREERAM
Serial.println("SDError");
#endif
delay(100);
digitalWrite(LED_RED, LOW);
delay(500);
digitalWrite(LED_RED, HIGH);
}
else break;
}
digitalWrite(LED_RED, LOW);
//Serial.println("OK");
delay(500);
#ifdef FREERAM
_ram = 0;
#endif
_acc_count8 = _int_count8 = _int_count8_hb = 0;
// first line : fs
// second line : seconds
// third line : bin(0) or csv(1)
//if(!(accFile = SD.open("DATA.CFG", FILE_READ))) {
accFile.open("CFG.TXT", O_READ);
if(!accFile.isOpen()) {
//Serial.println("NOCFG");
} else {
char line[25] = {""};
int n;
if(accFile.fgets(line, sizeof(line)) > 0) {
SAMPLE_RATE_INTERVAL = 1000000L / atol(line);
}
if(accFile.fgets(line, sizeof(line)) > 0) {
REC_COUNT = 1000000L / SAMPLE_RATE_INTERVAL * atol(line) / ((long)ACC_M_SIZE / 2L) + 1L;
}
#ifdef ENABLE_CSV
if(accFile.fgets(line, sizeof(line)) > 0) {
if(atoi(line) > 0) CSVMODE = true;
}
#endif
accFile.close();
}
//Serial.print("FS:");Serial.println(1000000L/SAMPLE_RATE_INTERVAL);
//Serial.print("Count:");Serial.println(REC_COUNT);
SD.remove("DATA.LOG");
//accFile = SD.open("DATA.LOG", FILE_WRITE);
accFile.open("DATA.LOG", O_CREAT | O_APPEND | O_WRITE);
accFile.print("FS:");accFile.println(1000000L/SAMPLE_RATE_INTERVAL);
//accFile.print("FS(us):");accFile.println(SAMPLE_RATE_INTERVAL);
accFile.print("ISize:");accFile.println(ACC_M_SIZE/2);
accFile.print("I:");accFile.println(2); // acc+gyr
accFile.print("XYZ:");accFile.println(3);
accFile.print("Byte:");accFile.println(2);
accFile.print("Count:");accFile.println(REC_COUNT);
accFile.close();
SD.remove("DATA.CSV");
SD.remove("DATA.BIN");
#ifdef ENABLE_CSV
if(CSVMODE) {
accFile.open("DATA.CSV", O_CREAT | O_WRITE);
} else {
#endif
accFile.open("DATA.BIN", O_CREAT | O_WRITE);
#ifdef ENABLE_CSV
}
#endif
delay(500);
time1 = millis();
Timer1.initialize();
Timer1.attachInterrupt(measureAcc, SAMPLE_RATE_INTERVAL);
}
void loop()
{
uint8_t _int_count8_copy = 0, _int_count8_hb_copy = 0, _pre_count8 = 0, _pre_count8_hb = 0;
uint32_t _int_count_copy = 0, _drop_count = 0;
#ifdef FREERAM
uint32_t _main_loop_count = 0;
Serial.println("loop():");
#endif
while(1) {
_int_count8_copy = _int_count8;
#ifdef FREERAM
_main_loop_count ++;
#endif
if(_int_count8_copy == _pre_count8) continue;
else {
if(((uint16_t)_int_count8_copy - (uint16_t)_pre_count8) == 1) {
_int_count_copy ++;
} else if((_int_count8_copy == 0)&&(_pre_count8 == 255)) {
_int_count_copy ++;
} else {
_drop_count ++;
#ifdef FREERAM
Serial.println("@");
#endif
}
_pre_count8 = _int_count8_copy;
}
_int_count8_hb_copy = _int_count8_hb;
if((_int_count8_hb_copy - _pre_count8_hb) >= 2) {
_drop_count ++;
#ifdef FREERAM
Serial.println("*");
#endif
}
_pre_count8_hb = _int_count8_hb_copy;
if(_int_count_copy < REC_COUNT+1) {
#ifdef FREERAM
Serial.print(_main_loop_count);
Serial.print("w");
Serial.print(_int_count_copy);
Serial.print("/");
Serial.print(REC_COUNT+1);
_main_loop_count = 0;
#endif
#ifdef ENABLE_CSV
if(CSVMODE) {
int count, count_max;
if(_int_count_copy & 1) { count = 0; count_max = ACC_M_SIZE/2; }
else { count = ACC_M_SIZE/2; count_max = ACC_M_SIZE; }
for(;count < count_max;count ++) {
//String line = "";
//line += String(_acc[cc][0],DEC) + String(",") + String(_acc[cc][1],DEC) + String(",") + String(_acc[cc][2],DEC) + String(",");
//line += String(_gyr[cc][0],DEC) + String(",") + String(_gyr[cc][1],DEC) + String(",") + String(_gyr[cc][2],DEC);
//accFile.println(line);
// 16bit integer -32768/32767 6x6digit+\0+5 commas = 42~3
char line[43];
sprintf(line, "%d,%d,%d,%d,%d,%d", _acc[count][0], _acc[count][1], _acc[count][2], _gyr[count][0], _gyr[count][1], _gyr[count][2]);
accFile.println(line);
#ifdef FREERAM
if(_ram < freeRam()) _ram = freeRam();
#endif
}
} else {
#endif
uint8_t* __at;
__at = (uint8_t*)_acc;
if(_int_count_copy & 0x1 == 0) __at += sizeof(int)*3*ACC_M_SIZE/2;
accFile.write((uint8_t*)_acc,sizeof(int)*3*ACC_M_SIZE/2);
__at = (uint8_t*)_gyr;
if(_int_count_copy & 0x1 == 0) __at += sizeof(int)*3*ACC_M_SIZE/2;
accFile.write((uint8_t*)_gyr,sizeof(int)*3*ACC_M_SIZE/2);
#ifdef FREERAM
if(_ram < freeRam()) _ram = freeRam();
#endif
#ifdef ENABLE_CSV
}
#endif
#ifdef FREERAM
Serial.print(":");
Serial.print(_ram);
Serial.println("]");
#endif
if(_pre_count8 & 0x1) {
digitalWrite(LED_GREEN, HIGH);
} else {
digitalWrite(LED_GREEN, LOW);
}
if(_drop_count > 0) {
digitalWrite(LED_RED, HIGH);
}
} else {
time2 = millis();
#ifdef FREERAM
Serial.print(_main_loop_count);Serial.print("c");Serial.println(_int_count_copy);
Serial.print("Drop:");Serial.println(_drop_count);
_main_loop_count = 0;
#endif
accFile.close();
accFile.open("DATA.LOG", O_APPEND | O_WRITE);
accFile.print("Drop:");accFile.println(_drop_count);
accFile.print("MS:");accFile.println(time2-time1);
accFile.print("SMPL:");accFile.println((long)REC_COUNT*(long)ACC_M_SIZE/(long)2);
accFile.close();
if(_drop_count > 0) {
digitalWrite(LED_RED, HIGH);
}
Timer1.detachInterrupt();
while(1) {
digitalWrite(LED_GREEN, HIGH);
delay(1600);
digitalWrite(LED_GREEN, LOW);
delay(200);
}
}
} // while(1)
}
<rindolf> What should I do now?
<rindolf> I'll work on Text-Qantor.
<rindolf> It's so great not to have a job.
<Zuu> yeah, if someone else pays for the food it sure is :D
<Zuu> also, i dont really understand much of what you just told me
:P
* Zuu puts a stick into the Text-Qantor
<rindolf> Zuu: Qantor == Qantor ain't no TeX/Troff oh really.
<rindolf> It's a typesetting system I'm working on.
* Zuu hates the name
<Zuu> it makes me kinda mad actually :/
<rindolf> Zuu: :-)
<rindolf> Zuu: maybe it will grow on you.
<rindolf> Zuu: some people I know named a browser suckass.
<Zuu> :(
<rindolf> I refused to work on it.
<Zuu> see that's a name!
<rindolf> Zuu: heh.
<Zuu> i didnt mean that btw :)
<Zuu> suckass is kinda... unkind
<rindolf> OK, now I should write an
http://www.shlomifish.org/humour/bits/facts/XSLT/
transformation.
<rindolf> I'll start from something I already have.
<Zuu> But the "X ain't no <something related>" is just a lame naming
convention imho
<Zuu> yeah, work on some XSLT facts :D
<rindolf> Zuu: just call it Qantor then.
<rindolf> Without the mnemonics.
<Zuu> but anyone interrested will learn that it's an abbreviation
<Zuu> just by the fact that it's recursive makes me want to kill
myself a little bit more :P
<rindolf> Zuu: do me a break and kill yourself.
<Zuu> :>
<rindolf> Less Zuus - more grass for evil reindeers like me to feed on.
-- What is Qantor?
-- ##programming, Freenode
Satan condemned Hitler for a million years of writing XSLT.
-- Shlomi Fish
-- XSLT Facts by Shlomi Fish and Friends ( http://www.shlomifish.org/humour/bits/facts/XSLT/ )