Sleep Analyzer

Ok…

Finalmente depois de muito batalhar, nao com o Arduino, mas sim com o raio do Excel™ la consegui por isto a funcionar e a dar algo que se veja e dê para perceber o que se passa durante o meu sono de beleza.

Em primeiro lugar e antes de colocar os graficos com uma pequena explicação dos mesmos, o que é que usei para fazer esta montagem.

  1. Arduino Uno
  2. Triple Axis Accelerometer Breakout – MMA7260Q
  3. Uma breadboard
  4. Resmas de Cabos
  5. Fita cola

Ora bem, e com isto tudo foi so ligar utilizando o Portuguese tutorial by Daniel Gonçalves!

Depois como eu queria nao ter o portatil sempre ligado ao lado da cama enquanto durmo, lembrei-me de usar a Ethernet Shield (new version) com um maravilhoso leitor de cartões, que nao funciona se estiverem a usar a parte de rede, mas que so usando o leitor funciona as mil maravilhas, dando-me um excelente ponto de armazenagem para um .csv que guardei, para mais tarde analisar. Tudo isto pode ser alimentado por uma pilha e 9V ou simplesmente com um transformador de um router que tinha la para casa que faz 6V. Acrescentei também no código para o LED 13 piscar quando esta a escrever para o cartão, só por precaução de saber se o gajo esta a logar ou nao, claro que o melhor é mesmo ligar o portatil uma ou duas vezes e fazer testes. Por aqui nao tive problemas…so far so good.

Ainda no código, acrescentei também a lib Time.h que me permite um relógio +/- certo de quando começou a logar ate que o desliguei, digo mais ou menos certo porque como nao tenho rede nem gps, nem nada que se pareça tive que acertar as horas no codigo e fazer um start mais ou menos a hora que tinha programado. Esta no código é a linha SetTime.

Vamos ao codigo:

// A simple data logger for the Arduino analog pins
#define LOG_INTERVAL  5000 // mills between entries
#define SENSOR_COUNT     2 // number of analog pins to log
#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    0 // Wait for serial input in setup()
#define SYNC_INTERVAL 1000 // mills between calls to sync()
uint32_t syncTime = 0;     // time of last sync()
 
#include
#include
#include
#include 
 
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;
 
// Reader
int xpin = 0;                               // x-axis of the accelerometer
int ypin = 1;                               // y-axis
int zpin = 2;                               // z-axis (only on 3-axis models)
long tick = 0;
 
//Time
char hora[]= "";
char minuto[]= "";
char segundo[]= "";
 
// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))
 
void error_P(const char* str) {
  PgmPrint("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1);
}
 
void setup()
{
  pinMode(10, OUTPUT);     // set the SS pin as an output (necessary!)
  pinMode(13, OUTPUT);
  digitalWrite(10, HIGH);                    // but turn off the W5100 chip!
  Serial.begin(9600);
 
  setTime(21,30,00,12,23,40);
 
  // Provide ground and power by using the analog inputs as normal
  // digital pins.  This makes it possible to directly connect the
  // breakout board to the Arduino.  If you use the normal 5V and
  // GND pins on the Arduino, you can remove these lines.
 
  pinMode(xpin,INPUT);
  pinMode(ypin,INPUT);
  pinMode(zpin,INPUT);
 
#if WAIT_TO_START
  Serial.println("Type any character to start");
  while (!Serial.available());
#endif //WAIT_TO_START
 
  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed");
 
  // initialize a FAT volume
  if (!volume.init(&card)) error("volume.init failed");
 
  // open root directory
  if (!root.openRoot(&volume)) error("openRoot failed");
 
  // create a new file
  char name[] = "SLEEP.CSV";
  for (uint8_t i = 0; i < 100; i++) {     name[6] = i/10 + '0';     name[7] = i%10 + '0';     if (file.open(&root, name, O_CREAT | O_EXCL | O_WRITE)) break;   }   if (!file.isOpen()) error ("file.create");   digitalWrite(13, HIGH);   Serial.print("Logging to: ");   Serial.println(name);   // write header   file.writeError = 0;   //file.print("X"); #if ECHO_TO_SERIAL    //Serial.print("X"); #endif //ECHO_TO_SERIAL #if SENSOR_COUNT > 6
#error SENSOR_COUNT too large
#endif //SENSOR_COUNT
 
 // for (uint8_t i = 0; i < SENSOR_COUNT; i++) {
  //  file.print(",X");file.print(i, DEC);
#if ECHO_TO_SERIAL
  //  Serial.print(",Temp");Serial.print(i, DEC);
#endif //ECHO_TO_SERIAL
 // }
 // file.println();
#if ECHO_TO_SERIAL
  //Serial.println();
#endif  //ECHO_TO_SERIAL
 
  if (file.writeError || !file.sync()) {
    error("write header failed");
    digitalWrite(13, LOW);
  }
}
 
void loop(void)
{
 
    // clear print error
  file.writeError = 0;
  //delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));
 
  //Sensor
  int val[3];
  val[0] = analogRead(xpin);
  val[1] = analogRead(ypin);
  val[2] = analogRead(zpin);
  tick = millis();
 
   int horas = (hour());
   int minutos = (minute());
   int segundos = (second());
   uint16_t xvalue = val[0];
   uint16_t yvalue = val[1];
   uint16_t zvalue = val[2];
 
   digitalWrite(13, HIGH);
   file.print(horas);
   file.print(":");
   file.print(minutos);
   file.print(":");
   file.print(segundos);
   file.print(';');
   file.print(xvalue);
   file.print(';');
   file.print(yvalue);
   file.print(';');
   file.print(zvalue);
   file.println(';');
 
   //Debug Processing
   Serial.print(horas);
   Serial.print(minutos);
   Serial.print(segundos);
   Serial.print( "X" );
   Serial.print( val[0] );
   Serial.print( "Y" );
   Serial.print( val[1] );
   Serial.print( "Z" );
   Serial.println( val[2] );
 
   /*
   Serial.print(tick);
   Serial.print(';');
   Serial.print(xvalue);
   Serial.print(';');
   Serial.print(yvalue);
   Serial.print(';');
   Serial.print(zvalue);
   Serial.println(';'); */
 
   delay(500);
 
     if (file.writeError) error("write data failed");
 
  //don't sync too often - requires 2048 bytes of I/O to SD card
  if ((millis() - syncTime) <  SYNC_INTERVAL) return;
  syncTime = millis();
  if (!file.sync()) error("sync failed");
  }

Tendo este codigo carregado no arduino era so uma questao de ir para a cama e colocar o sensor de modo muito profissional de maneira a que pudesse sentir os movimentos de uma pessoa a virar/levantar/etc.

Até aqui parecia tudo bem, nao fosse o primeiro teste que fiz estar a logar poucos dados de cada vez e este sensor nao ser assim tao sensivel, mesmo estando ligado para 1.5g.

Ou seja os dados que vou por aqui sao do segundo teste, ja com umas afinações nomedamente acrescentando as horas, para poder ter uma noçao melhor de tempo. Tambem se pode usar a opçao millis() e depois fazer os calculos, mas como eu gosto de nao ter trabalho preferi por as horas directamente no .csv.

O delay que estou a usar agora é de 50 millisegundos e o cartão de memoria aguenta-se que nem um leão, nao falhando, penso eu, nenhum dado. Mas mesmo assim com tantos dados ainda se torna complicado processar.

O que me leva entao ao processamento:

Depois de muito bater com a cabeça, acabei por fazer, e isto porque a minha parte de scripting nao é das melhores, o seguinte circuito:

  1. Inserir os dados .csv num excel directamente
  • Esta opção funcionava, mas o grafico nao ficava nada de legivel, uma vez que sao para 7 horas sao qualquer coisa como 50000 entradas x 3 colunas…
  1. Meter os dados numa BD Mysql
  • Com um simples Select consegui tirar todos os valores correspondentes a cada 5 minutos, minutos certos, ou seja sem os segundos.
  • select * from analyzer2 where Time like ‘%:05:%’ or Time like ’23:40:00′; <- algo deste genero.
  • Voltar a colocar o .csv gerado pelo Mysql no excel e pumba, temos grafico.
  • Eu sei, eu sei, ha maneiras mais praticas e rapidas de fazer isto, mas ei, funciona não funciona? 😉

O resultado de graficos, foi o que passo a mostrar, o motivo de estarem os graficos separados pelos 3 eixos é precisamente para ser mais facil a mostra dos mesmos.

  • X

  • Y

  • Z

Como podem ver, da amostragem, sem cálculos alguns que se mostra, nota-se nitidamente os padrões de movimentos, vou falar por exemplo no caso do eixo X em que o normal ou seja em repouso é entre os 550 e 570, sendo que tudo o que saia desse valor, é considerado movimento. E pode-se ver facilmente nos tres graficos entre a 1:00 e as 2:30 que houve movimento, porque foi na altura em que rebentou uma GIGANTE trovodada la perto da nossa casa que fez disparar quase os alarmes todos da zona, menos o nosso ;).

E pronto, penso que ja da para ter uma ideia do que da para fazer, mas ainda ha muito por onde precorrer, nomeadamente o tratamento dos dados e a recolha dos mesmos.

NOTA: Apenas uma nota a parte, em principio hoje vou colocar o sensor mesmo num dos meus braços, e assim vamos ter movimentos a seria, porque so o movimento do colchão nao esta a sair grande coisa.

Como sempre sugestões e ideias são sempre bem vindas. E vao aparecendo para ver as novidades sobre este projectos.

===================================

UPDATES:

2010-12-22 – Sleep Analyzer – 2º Prototipo

2010-12-23 – Sleep Analyzer – Os resultados e conclusões!

Follow Me