完成したデバイス

完成品

. .
皮膚系(外装) 皮膚系(内装) 加速度計

装着の様子

.
皮膚系 加速度計

Processingのグラフ出力

安静時から自分で頭をはたいた場合

Specifications

SCのCalibration

加速度計のスペック

  • このデバイスのスペック
    • 入力
      • サンプリング周波数: 50回
      • サンプル数: 128個
    • 出力
      • サンプリングレート: 0.02秒
      • サンプリング時間: 2.56秒
    • FFT後
      • 周波数の最小単位df: 0.390625秒間隔
      • 横軸の数: 64個
      • 最大Hz: 25Hz
    • 検出できる1番細かい動き: 2.56回/1秒
    • 検出できる1番遅い動き: 25秒に1回
  • 参考: 日立のデバイスのスペック
    • 入力
      • サンプリング周波数: 50回
      • サンプル数: 200個
    • 出力
      • サンプリングレート: 0.02秒
      • サンプリング時間: 4秒
    • FFT後
      • 周波数の最小単位df: 0.25秒間隔
      • 横軸の数: 100個
      • 最大Hz: 25Hz
    • 検出できる1番細かい動き: 4回/1秒
    • 検出できる1番遅い動き: 25秒に1回

スケッチ

加速度計のスケッチ

#include <M5StickC.h>
#include <WiFi.h>
#include <WiFiUDP.h>
#include "time.h"

//FFT
#include "arduinoFFT.h"
#define SAMPLING_FREQUENCY 50
const uint16_t FFTsamples = 256;  // サンプル数は2のべき乗
double vReal[FFTsamples];  // vReal[]にサンプリングしたデーターを入れる
double vImag[FFTsamples];
double vLog[FFTsamples];
arduinoFFT FFT = arduinoFFT(vReal, vImag, FFTsamples, SAMPLING_FREQUENCY);  // FFTオブジェクトを作る
unsigned int sampling_period_us;
//unsigned long microseconds;
float indextoHz;
int fftroop;


//FFT


const char* ssid       = "XXXXXXXXXX";
const char* password   = "XXXXXXXXXX";
const char* saddr      = "XXXXXXXXXX";//ipconfigで確認して設定

double vbat = 0.0;
int8_t bat_charge_p = 0;

#define MODE_A 0 // blank
#define MODE_B 1 // display
uint8_t disp_mode = MODE_B;

#define BTN_A_PIN  37
#define BTN_ON  LOW
#define BTN_OFF HIGH
uint8_t prev_btn_a = BTN_OFF;
uint8_t btn_a      = BTN_OFF;

const char* ntpServer =  "ntp.jst.mfeed.ad.jp";
const int sport = 55998;
const int kport = 5556;

RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateTypeDef RTC_DateStruct;

float accX = 0.0F;
float accY = 0.0F;
float accZ = 0.0F;
float acc;

int peak;
int mil;
int milf;
int mila;

uint8_t mac3[6];

WiFiUDP Udp;
char packetBuffer[800];

void sample(int nsamples) {
  int i;
    for (int i = 0; i < nsamples; i++) {
  btn_a = digitalRead(BTN_A_PIN);

  if(prev_btn_a == BTN_OFF && btn_a == BTN_ON){
    if(disp_mode == MODE_A){
    M5.Lcd.setCursor(0, 1, 2);
    M5.Lcd.print("WIFI: ");
    M5.Lcd.println(WiFi.localIP());
    M5.Lcd.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac3[0], mac3[1], mac3[2], mac3[3], mac3[4], mac3[5]);
    disp_mode = MODE_B;
    }else{
    M5.Lcd.fillScreen(BLACK);
      disp_mode = MODE_A;
    }
  }

  prev_btn_a = btn_a;

  // put your main code here, to run repeatedly:

        while(millis() < (mil + sampling_period_us)){
        }
        mil = millis();
        mila = millis();    
        M5.IMU.getAccelData(&accX, &accY, &accZ);
        acc = sqrt(sq(accX)+sq(accY)+sq(accZ));
        Udp.beginPacket(saddr, sport);
        Udp.printf("acc![%02X:%02X:%02X:%02X:%02X:%02X],%04d/%02d/%02d %02d:%02d:%02d,", mac3[0], mac3[1], mac3[2], mac3[3], mac3[4], mac3[5], RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Date, RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds);
        Udp.printf("%10d,%5.2f,,,,,,", mila, acc);
        Udp.print(" ");
        Udp.endPacket();
        Serial.print(mila);
        Serial.print(", ");
        Serial.print(acc);
        Serial.print(", ");
        Serial.print(peak);
        Serial.print(", ");
        float hz;
        hz = (float)peak * indextoHz;
        Serial.println(float(hz));
        vReal[i] = acc;
        vLog[i] = acc;
        vImag[i] = 0;

  M5.Rtc.GetTime(&RTC_TimeStruct);
  M5.Rtc.GetData(&RTC_DateStruct);

  if(disp_mode == MODE_B){
  vbat = M5.Axp.GetVbatData() * 1.1 / 1000;
  bat_charge_p = int8_t((vbat - 3.0) / 1.2 * 100);
  if(bat_charge_p > 100){
    bat_charge_p = 100;
  }else if(bat_charge_p < 0){
    bat_charge_p = 0;
  }
  M5.Lcd.setCursor(0, 34);
  M5.Lcd.printf("C:%3d%% ", bat_charge_p);
  M5.Lcd.printf("%02d-%02d ", RTC_DateStruct.Month, RTC_DateStruct.Date);
  M5.Lcd.printf("%02d:%02d:%02d\n", RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds);
  M5.Lcd.setCursor(0, 50);
  M5.Lcd.printf("X:%5.2f Y:%5.2f Z:%5.2f   ", accX, accY, accZ);
  M5.Lcd.setCursor(0, 66);
  M5.Lcd.printf("ACC:%5.2f  FFT:%.2fHz   ", acc, hz);
  }
    

   }
}

void setup() {
  // put your setup code here, to run once:
  M5.begin();
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  M5.Axp.ScreenBreath(8); // MIN7-MAX12

  M5.IMU.Init();

  pinMode(BTN_A_PIN,  INPUT_PULLUP);
  
  esp_read_mac(mac3, ESP_MAC_WIFI_STA);
  M5.Lcd.setTextSize(1);
  M5.Lcd.setCursor(1, 0, 2);
  M5.Lcd.println("Welcome \n If the connection fails, turn off and on the power again.");
 
  // connect to WiFi
  M5.Lcd.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    M5.Lcd.print(".");

  }
  M5.Lcd.println(" CONNECTED");
  delay(1000);

  // Set ntp time to local
  configTime(9 * 3600, 0, ntpServer);
  // Get local time
  struct tm timeInfo;
  if (getLocalTime(&timeInfo)) {
    // Set RTC time
    RTC_TimeTypeDef TimeStruct;
    TimeStruct.Hours   = timeInfo.tm_hour;
    TimeStruct.Minutes = timeInfo.tm_min;
    TimeStruct.Seconds = timeInfo.tm_sec;
    M5.Rtc.SetTime(&TimeStruct);
 
    RTC_DateTypeDef DateStruct;
    DateStruct.WeekDay = timeInfo.tm_wday;
    DateStruct.Month = timeInfo.tm_mon + 1;
    DateStruct.Date = timeInfo.tm_mday;
    DateStruct.Year = timeInfo.tm_year + 1900;
    M5.Rtc.SetData(&DateStruct);
  }
  M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 1, 2);
    M5.Lcd.print("WIFI: ");
    M5.Lcd.println(WiFi.localIP());
   uint8_t mac3[6];
   esp_read_mac(mac3, ESP_MAC_WIFI_STA);
  M5.Lcd.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac3[0], mac3[1], mac3[2], mac3[3], mac3[4], mac3[5]);
  Udp.begin(kport); 
  
  //FFT
  sampling_period_us = round(1000*(1.0/SAMPLING_FREQUENCY));
    indextoHz = (float)SAMPLING_FREQUENCY / FFTsamples;
    fftroop = 5 / indextoHz + 1;
  //FFT
  mil = millis();
}

void DCRemoval(double *vData, uint16_t samples) {
    double mean = 0;
    for (uint16_t i = 1; i < samples; i++) {
        mean += vData[i];
    }
    mean /= samples;
    for (uint16_t i = 1; i < samples; i++) {
        vData[i] -= mean;
    }
}

void loop() {
    sample(FFTsamples);

    DCRemoval(vReal, FFTsamples);
    FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);  // 窓関数
    FFT.Compute(FFT_FORWARD); // FFT処理(複素数で計算)
    FFT.ComplexToMagnitude(); // 複素数を実数に変換
    int i;
    float peakm = 0;
      for(i = 0; i < fftroop; i++){ //5HZ相当までしか判定しない
      if(peakm<=vReal[i]){
      peakm=vReal[i];
      peak = i;
      }
    }
  //UDP
  milf = millis();
  Udp.beginPacket(saddr, sport);
  Udp.printf("aFFT![%02X:%02X:%02X:%02X:%02X:%02X],%04d/%02d/%02d %02d:%02d:%02d,", mac3[0], mac3[1], mac3[2], mac3[3], mac3[4], mac3[5], RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Date, RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds);
  Udp.printf("%10d,,,,%.2f,%3d,%3d,", milf, peak * indextoHz, SAMPLING_FREQUENCY, FFTsamples);
  Udp.print(" ");
  Udp.endPacket();
}

皮膚系のスケッチ

  • 長野先生に書いていただいたものに若干の修正をした
#include <M5StickC.h>
#include <WiFi.h>
#include <WiFiUDP.h>
#include "time.h"
#include <Adafruit_ADS1015.h>

#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 26 // データ(黄)で使用するポート番号
#define SENSER_BIT   9 // 精度の設定bit
// 0.5, 0.25, 0.125, 0.0625度:9, 10, 11, 12ビット
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

const char* ssid       = "XXXXXXXXXX";
const char* password   = "XXXXXXXXXX";
const char* saddr      = "XXX.XXX.XXX.XXX";//ipconfigで確認して設定

const float slope = 0.028;       //マイクロジーメンス変換のための傾き
const float intercept = -2.038;  //マイクロジーメンス変換のための切片
const int sampling_period = 140; //サンプリングレート指定(ms単位)
int mil;

double vbat = 0.0;
int8_t bat_charge_p = 0;

#define MODE_A 0 // blank
#define MODE_B 1 // display
uint8_t disp_mode = MODE_B;

#define BTN_A_PIN  37
#define BTN_ON  LOW
#define BTN_OFF HIGH
uint8_t prev_btn_a = BTN_OFF;
uint8_t btn_a      = BTN_OFF;

const char* ntpServer =  "ntp.jst.mfeed.ad.jp";
const int sport = 55999;
const int kport = 5557;

RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateTypeDef RTC_DateStruct;

uint8_t mac3[6];

//新GSR
Adafruit_ADS1015 ads;     /* Use thi for the 12-bit version */
float   multiplier = 0.125F;    
//新GSR

WiFiUDP Udp;
char packetBuffer[255];


void setup() {
  // put your setup code here, to run once:
  M5.begin();
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  M5.Axp.ScreenBreath(8); // MIN7-MAX12
  
  sensors.setResolution(SENSER_BIT);
  pinMode(26,INPUT);

  pinMode(BTN_A_PIN,  INPUT_PULLUP);
  
  esp_read_mac(mac3, ESP_MAC_WIFI_STA);
  M5.Lcd.setTextSize(1);
  M5.Lcd.setCursor(1, 0, 2);
  M5.Lcd.println("Welcome \n If the connection fails, turn off and on the power again.");
 
  // connect to WiFi
  M5.Lcd.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    M5.Lcd.print(".");

  }
  M5.Lcd.println(" CONNECTED");
  delay(1000);

  // Set ntp time to local
  configTime(9 * 3600, 0, ntpServer);
  // Get local time
  struct tm timeInfo;
  if (getLocalTime(&timeInfo)) {
    // Set RTC time
    RTC_TimeTypeDef TimeStruct;
    TimeStruct.Hours   = timeInfo.tm_hour;
    TimeStruct.Minutes = timeInfo.tm_min;
    TimeStruct.Seconds = timeInfo.tm_sec;
    M5.Rtc.SetTime(&TimeStruct);
 
    RTC_DateTypeDef DateStruct;
    DateStruct.WeekDay = timeInfo.tm_wday;
    DateStruct.Month = timeInfo.tm_mon + 1;
    DateStruct.Date = timeInfo.tm_mday;
    DateStruct.Year = timeInfo.tm_year + 1900;
    M5.Rtc.SetData(&DateStruct);
  }
  M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 1, 2);
    M5.Lcd.print("WIFI: ");
    M5.Lcd.println(WiFi.localIP());
   uint8_t mac3[6];
   esp_read_mac(mac3, ESP_MAC_WIFI_STA);
  M5.Lcd.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac3[0], mac3[1], mac3[2], mac3[3], mac3[4], mac3[5]);
  Udp.begin(kport); 

//新GSR
  ////////////////////////// ONE>TWO>SIXTEENの順で感度が悪く(データが階段状になりやすく),最高値を振り切りにくい
  ads.setGain(GAIN_ONE);      multiplier= 2.0F;
  ads.begin();
//新GSR

  
}

void loop() {

  btn_a = digitalRead(BTN_A_PIN);

  if(prev_btn_a == BTN_OFF && btn_a == BTN_ON){
    if(disp_mode == MODE_A){
    M5.Lcd.setCursor(0, 1, 2);
    M5.Lcd.print("WIFI: ");
    M5.Lcd.println(WiFi.localIP());
    M5.Lcd.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac3[0], mac3[1], mac3[2], mac3[3], mac3[4], mac3[5]);
    disp_mode = MODE_B;
    }else{
    M5.Lcd.fillScreen(BLACK);
      disp_mode = MODE_A;
    }
  }

  prev_btn_a = btn_a;

  // put your main code here, to run repeatedly:
  M5.Rtc.GetTime(&RTC_TimeStruct);
  M5.Rtc.GetData(&RTC_DateStruct);


//新GSR
  while(millis() < (mil + sampling_period)){  //サンプリングレートの時間が過ぎるまで待つ
  }
  mil = millis();                             //待ち時間終了直後の経過時間取得。これはサンプリングレート判定用なので,processingに送信しない。
  
  //以下データ取得
  int16_t results;  
  int16_t results_t;  
  results = ads.readADC_Differential_0_1();  
//新GSR
  sensors.requestTemperatures();              // 温度取得要求
  float tp = sensors.getTempCByIndex(0);      //温度の取得

  int sctempmil = millis();                   //データ取得後の経過時間取得。processingに送信するデータはこっちの経過時間。

  if(disp_mode == MODE_B){
  vbat = M5.Axp.GetVbatData() * 1.1 / 1000;
  bat_charge_p = int8_t((vbat - 3.0) / 1.2 * 100);
  if(bat_charge_p > 100){
    bat_charge_p = 100;
  }else if(bat_charge_p < 0){
    bat_charge_p = 0;
  }
  M5.Lcd.setCursor(0, 34);
  M5.Lcd.printf("CHARGE: %3d%%", bat_charge_p);
  M5.Lcd.setCursor(0, 50);
  M5.Lcd.printf("%04d-%02d-%02d ", RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Date);
  M5.Lcd.printf("%02d:%02d:%02d\n", RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds);
  M5.Lcd.printf("SC: %.2f TEMP: %4.1f'C   ", results * multiplier * slope + intercept, tp);
    Serial.println(results * multiplier);
  }
  
  //UDP
  Udp.beginPacket(saddr, sport);
  Udp.printf("SCtemp![%02X:%02X:%02X:%02X:%02X:%02X],%04d/%02d/%02d %02d:%02d:%02d,%10d,,%.3f,%4.1f,,,,", mac3[0], mac3[1], mac3[2], mac3[3], mac3[4], mac3[5], RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Date, RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds, sctempmil, results * multiplier * slope + intercept, tp);
  Udp.endPacket();
}

皮膚系(SC,皮膚温)と加速度計(加速度と変換後の周波数)のデータをまとめるProcessingのスケッチ

// import UDP library
import hypermedia.net.*;

String ip = "XXX.XXX.XXX.XXX";  // the remote IP address
int port1        = 55998;    // the destination port
int port2        = 55999;    // the destination port

int mil;
int sctempmil;
int accmilst;
int sctempmilst;
float acc;
float fft;
float sc;
float tp;
int g;

String filename1;

graphMonitor_acc accGraph;
graphMonitor_fft fftGraph;
graphMonitor_ondo ondoGraph;
graphMonitor_sc scGraph;

UDP udp1;  // define the UDP object
UDP udp2;  // define the UDP object

PrintWriter output1;

static final int dat_fft = 4;
static final int dat_acc = 2;
static final int dat_ondo = 3;
static final int dat_sc = 5;


void setup() {
  size(1366,768);
  PFont font = createFont("Arial",48,true);
  textFont(font);
  frameRate(60);
  smooth();
  accGraph  = new graphMonitor_acc("Acc", 794, 50, 500, 300);
  fftGraph  = new graphMonitor_fft("Hz", 122, 50, 500, 300);
  scGraph   = new graphMonitor_sc("SC", 794, 418, 500, 300);
  ondoGraph = new graphMonitor_ondo("Temp", 122, 418, 500, 300);
 
  filename1 = nf(year(),2)+nf(month(),2)+nf(day(),2)+nf(hour(),2)+nf(minute(),2)+nf(second(),2)+"_log1.csv";

  output1 = createWriter(filename1);

  udp1 = new UDP( this, 55998 );
  udp1.listen( true );
  udp2 = new UDP( this, 55999 );
  udp2.listen( true );
  
  accmilst = 0;
  sctempmilst = 0;
}

//process events
void draw() {
  background(245,245,245);
  accGraph.graphDraw_acc(acc);
  fftGraph.graphDraw_fft(fft);
  ondoGraph.graphDraw_ondo(tp);
  scGraph.graphDraw_sc(sc);
  text(int(mil),100,18);
}


void keyPressed() {
    
    String message  = str( key );   // the message to send
    

    message = message+";\n";
    udp1.send( message, ip, port1 );
    udp2.send( message, ip, port2 );

      if( key == 'e' ){
  output1.flush();
  output1.close();
  delay(1000);
  exit();
  }

}


class graphMonitor_acc {
    String TITLE;
    int X_POSITION, Y_POSITION;
    int X_LENGTH, Y_LENGTH;
    float [] y1;
    float maxRange;
    float minRange;
    graphMonitor_acc(String _TITLE, int _X_POSITION, int _Y_POSITION, int _X_LENGTH, int _Y_LENGTH) {
      TITLE = _TITLE;
      X_POSITION = _X_POSITION;
      Y_POSITION = _Y_POSITION;
      X_LENGTH   = _X_LENGTH;
      Y_LENGTH   = _Y_LENGTH;
      y1 = new float[X_LENGTH];
      for (int i = 0; i < X_LENGTH; i++) {
        y1[i] = 0;
      }
    }

    void graphDraw_acc(float _y1) {
      y1[X_LENGTH - 1] = _y1;
      for (int i = 0; i < X_LENGTH - 1; i++) {
        y1[i] = y1[i + 1];
      }
      maxRange = 2;
      for (int i = 0; i < X_LENGTH - 1; i++) {
        maxRange = (abs(y1[i]) > maxRange ? abs(y1[i]) : maxRange);
      }
      minRange = 0.5;
      for (int i = 0; i < X_LENGTH - 1; i++) {
        minRange = (abs(y1[i]) < minRange ? abs(y1[i]) : minRange);
      }

      pushMatrix();

      translate(X_POSITION, Y_POSITION);
      fill(255);
      stroke(130);
      strokeWeight(1);
      rect(0, 0, X_LENGTH, Y_LENGTH);
      line(0, Y_LENGTH / 2, X_LENGTH, Y_LENGTH / 2);
      textSize(25);
      fill(51,51,204);
      textAlign(LEFT, BOTTOM);
      text(TITLE, 20, -5);
      textSize(22);
      textAlign(RIGHT);
      text(nf(maxRange, 0, 1), -5, 18);
      text(nf(minRange, 0, 1), -5, Y_LENGTH);
      translate(0, Y_LENGTH / 2);
      scale(1, -1);
      strokeWeight(3);
      for (int i = 0; i < X_LENGTH - 1; i++) {
        stroke(0, 102, 204);
        line(i, ((y1[i] - minRange) * Y_LENGTH / (maxRange - minRange)) - (Y_LENGTH / 2), i + 1, ((y1[i + 1] - minRange) * Y_LENGTH / (maxRange - minRange)) - (Y_LENGTH / 2));
    }
      popMatrix();
    }
}  


class graphMonitor_fft {
    String TITLE;
    int X_POSITION, Y_POSITION;
    int X_LENGTH, Y_LENGTH;
    float [] y1;
    float maxRange;
    graphMonitor_fft(String _TITLE, int _X_POSITION, int _Y_POSITION, int _X_LENGTH, int _Y_LENGTH) {
      TITLE = _TITLE;
      X_POSITION = _X_POSITION;
      Y_POSITION = _Y_POSITION;
      X_LENGTH   = _X_LENGTH;
      Y_LENGTH   = _Y_LENGTH;
      y1 = new float[X_LENGTH];
      for (int i = 0; i < X_LENGTH; i++) {
        y1[i] = 0;
      }
    }

    void graphDraw_fft(float _y1) {
      y1[X_LENGTH - 1] = _y1;
      for (int i = 0; i < X_LENGTH - 1; i++) {
        y1[i] = y1[i + 1];
      }
      maxRange = 5;
      for (int i = 0; i < X_LENGTH - 1; i++) {
        maxRange = (abs(y1[i]) > maxRange ? abs(y1[i]) : maxRange);
      }

      pushMatrix();

      translate(X_POSITION, Y_POSITION);
      fill(255);
      stroke(130);
      strokeWeight(1);
      rect(0, 0, X_LENGTH, Y_LENGTH);
      line(0, Y_LENGTH / 2, X_LENGTH, Y_LENGTH / 2);
      textSize(25);
      fill(51,51,204);
      textAlign(LEFT, BOTTOM);
      text(TITLE, 20, -5);
      textSize(22);
      textAlign(RIGHT);
      text(nf(maxRange, 0, 1), -5, 18);
      text(0, -5, Y_LENGTH);

      translate(0, Y_LENGTH / 2);
      scale(1, -1);
      strokeWeight(3);
      for (int i = 0; i < X_LENGTH - 1; i++) {
        stroke(0, 102, 204);
        line(i, (y1[i] * Y_LENGTH / maxRange) - (Y_LENGTH / 2), i + 1, (y1[i + 1] * Y_LENGTH / maxRange) - (Y_LENGTH / 2));
      }
      popMatrix();
    }
}  




class graphMonitor_ondo {
    String TITLE;
    int X_POSITION, Y_POSITION;
    int X_LENGTH, Y_LENGTH;
    float [] y1;
    float maxRange;
    float minRange;
    graphMonitor_ondo(String _TITLE, int _X_POSITION, int _Y_POSITION, int _X_LENGTH, int _Y_LENGTH) {
      TITLE = _TITLE;
      X_POSITION = _X_POSITION;
      Y_POSITION = _Y_POSITION;
      X_LENGTH   = _X_LENGTH;
      Y_LENGTH   = _Y_LENGTH;
      y1 = new float[X_LENGTH];
      for (int i = 0; i < X_LENGTH; i++) {
        y1[i] = 0;
      }
    }

    void graphDraw_ondo(float _y1) {
      y1[X_LENGTH - 1] = _y1;
      for (int i = 0; i < X_LENGTH - 1; i++) {
        y1[i] = y1[i + 1];
      }
      maxRange = 37;
      for (int i = 0; i < X_LENGTH - 1; i++) {
        maxRange = (abs(y1[i]) > maxRange ? abs(y1[i]) : maxRange);
      }
      minRange = 30;
      for (int i = 0; i < X_LENGTH - 1; i++) {
        minRange = (abs(y1[i]) < minRange ? abs(y1[i]) : minRange);
    }

      pushMatrix();

      translate(X_POSITION, Y_POSITION);
      fill(255);
      stroke(130);
      strokeWeight(1);
      rect(0, 0, X_LENGTH, Y_LENGTH);
      line(0, Y_LENGTH / 2, X_LENGTH, Y_LENGTH / 2);

      textSize(25);
      fill(51,51,204);
      textAlign(LEFT, BOTTOM);
      text(TITLE, 20, -5);
      textSize(22);
      textAlign(RIGHT);
      text(nf(maxRange, 0, 1), -5, 18);
      text(nf(minRange, 0, 1), -5, Y_LENGTH);
      translate(0, Y_LENGTH / 2);
      scale(1, -1);
      strokeWeight(3);
      for (int i = 0; i < X_LENGTH - 1; i++) {
        stroke(0, 102, 204);
        line(i, ((y1[i] - minRange) * Y_LENGTH / (maxRange - minRange)) - (Y_LENGTH / 2), i + 1, ((y1[i + 1] - minRange) * Y_LENGTH / (maxRange - minRange)) - (Y_LENGTH / 2));

    }
      popMatrix();
    }
}  

class graphMonitor_sc {
    String TITLE;
    int X_POSITION, Y_POSITION;
    int X_LENGTH, Y_LENGTH;
    float [] y1;
    float maxRange;
    graphMonitor_sc(String _TITLE, int _X_POSITION, int _Y_POSITION, int _X_LENGTH, int _Y_LENGTH) {
      TITLE = _TITLE;
      X_POSITION = _X_POSITION;
      Y_POSITION = _Y_POSITION;
      X_LENGTH   = _X_LENGTH;
      Y_LENGTH   = _Y_LENGTH;
      y1 = new float[X_LENGTH];
      for (int i = 0; i < X_LENGTH; i++) {
        y1[i] = 0;
      }
    }

    void graphDraw_sc(float _y1) {
      y1[X_LENGTH - 1] = _y1;
      for (int i = 0; i < X_LENGTH - 1; i++) {
        y1[i] = y1[i + 1];
      }
      maxRange = 30;
      for (int i = 0; i < X_LENGTH - 1; i++) {
        maxRange = (abs(y1[i]) > maxRange ? abs(y1[i]) : maxRange);
      }

      pushMatrix();

      translate(X_POSITION, Y_POSITION);
      fill(255);
      stroke(130);
      strokeWeight(1);
      rect(0, 0, X_LENGTH, Y_LENGTH);
      line(0, Y_LENGTH / 2, X_LENGTH, Y_LENGTH / 2);

      textSize(25);
      fill(51,51,204);
      textAlign(LEFT, BOTTOM);
      text(TITLE, 20, -5);
      textSize(22);
      textAlign(RIGHT);
      text(nf(maxRange, 0, 1), -5, 18);
      text(0, -5, Y_LENGTH);

      translate(0, Y_LENGTH / 2);
      scale(1, -1);
      strokeWeight(3);
      for (int i = 0; i < X_LENGTH - 1; i++) {
        stroke(0, 102, 204);
        line(i, (y1[i] * Y_LENGTH / maxRange) - (Y_LENGTH / 2), i + 1, (y1[i + 1] * Y_LENGTH / maxRange) - (Y_LENGTH / 2));
      }
      popMatrix();
    }
}  

void receive( byte[] data, String ip, int port ) {  // <-- extended handler
  String message = new String( data );
  println( "receive: \""+message+"\" from "+ip+" on port "+port );
  // print the result
  if (port == 5556){
    String dmessage[] = message.split("!", 0);
     if( dmessage[0].length() != 3 ){
          String dummys[] = message.split(",", 0);
          mil = int(float(dummys[2]));
          if (accmilst == 0){
            accmilst = mil;
          }
          mil = mil - accmilst;
          fft = float(dummys[6]);
          message = dummys[0] + "," + dummys[1] + "," + mil + "," + dummys[3] + "," + dummys[4] + "," + dummys[5] + "," + dummys[6] + "," + dummys[7] + "," + dummys[8];
          output1.println(message);
     }else{
          String dummys[] = message.split(",", 0);
          mil = int(float(dummys[2]));
          if (accmilst == 0){
            accmilst = mil;
          }
          mil = mil - accmilst;
          acc = float(dummys[3]);
          message = dummys[0] + "," + dummys[1] + "," + mil + "," + dummys[3] + "," + dummys[4] + "," + dummys[5] + "," + dummys[6] + "," + dummys[7] + "," + dummys[8];
          output1.println(message);
     }
  }
  if (port == 5557){
  String dummys[] = message.split(",", 0);
     sctempmil = int(float(dummys[2]));
     if (sctempmilst == 0){
        sctempmilst = sctempmil - mil - 10;
        }
     sctempmil = sctempmil - sctempmilst;
     sc = float(dummys[4]);
     tp = float(dummys[5]);
     message = dummys[0] + "," + dummys[1] + "," + sctempmil + ",," + sc + "," + tp + ",,,";
     output1.println(message);

  }else{
  }
}

データの処理

Processingで出力したデータをRで加工する

#関数としてまとめてしまう

shape.log<-function(file){
#ファイル名

#ファイルの読み込み
d.1<-read.csv(file,
    header=F)

#余計な列の削除
d.2<-d.1[,2:7]

#列名の定義
colnames(d.2)<-c("T",
    "ET",
    "ACC",
    "SC",
    "TEMP",
    "FFT")

#余分な列の削除
#行数の取得
n<-length(d.2[,1])
omit<-numeric(n)
for(i in 1:n){
    omit[i]<-ifelse(sum(is.na(d.2[i,]))>=5,0,1)
    }
d.2<-d.2[omit==1,]

#新しい行数
n.2<-length(d.2[,1])

#ACCのみのデータを作成
d.ACC<-d.2[!is.na(d.2[,3]),1:3]

#SCとTEMPのみのデータを作成
d.SC.TEMP<-d.2[!is.na(d.2[,4]),c(1,2,4,5)]

#FFTのみのデータを作成
d.FFT<-d.2[!is.na(d.2[,6]),c(1,2,6)]

#各ファイルの書き出し

#フォルダの用意と移動
dirname<-sub("log1.csv","logs",file)
dir.create(dirname)

f.ACC<-paste(dirname,"/",sub("log1","ACC",file),sep="")
write.csv(d.ACC,f.ACC,row.names=F)

f.SC.TEMP<-paste(dirname,"/",sub("log1","SC_TEMP",file),sep="")
write.csv(d.SC.TEMP,f.SC.TEMP,row.names=F)

f.FFT<-paste(dirname,"/",sub("log1","FFT",file),sep="")
write.csv(d.FFT,f.FFT,row.names=F)

f.ALL<-paste(dirname,"/",sub("log1","ALL",file),sep="")
write.csv(d.2,f.ALL,row.names=F)

print(paste("done, see ",dirname))

}

#ファイル名を入力
file<-"20200607183326_log1.csv"

#関数の実行
shape.log(file)

#または
shape.log("20200607183326_log1.csv")

可視化のコード(tentative)

#可視化関数

plot.logs<-function(file){
    
d.1<-read.csv(file,
    header=F)
    
#余計な列の削除
d.2<-d.1[,2:7]
    
#列名の定義
colnames(d.2)<-c("T",
    "ET",
    "ACC",
    "SC",
    "TEMP",
    "FFT")

#余分な列の削除
#行数の取得
n<-length(d.2[,1])
omit<-numeric(n)
for(i in 1:n){
    omit[i]<-ifelse(sum(is.na(d.2[i,]))>=5,0,1)
    }
d.2<-d.2[omit==1,]
    
#可視化
par(mfrow=c(2,2))
plot(d.2[,2]/1000/60,d.2[,3],
    type="l",
    xlab="ET(m)",
    ylab="m/s^2",
    main="ACC",
    col="blue")
plot(d.2[,2]/1000/60,d.2[,6],
    type="h",
    xlab="ET(m)",
    ylab="Hz",
    main="FFT",
    col="blue")
plot(d.2[,2]/1000/60,d.2[,4],
    type="h",
    xlab="ET(m)",
    ylab="μS",
    main="SC",
    col="blue")
plot(d.2[,2]/1000/60,d.2[,5],
    type="h",
    xlab="ET(m)",
    ylab="Degree",
    main="TEMP",
    col="blue")
}

#関数の実行
file<-"20200614132700_log1.csv"
plot.logs(file)

加工したデータをプロットした例

動作確認等

充電池の持続時間

  • 皮膚系: 12:43:00から始めて,17:00:00の時点で電池残量表示が88%
  • 加速度計: 12:42:35から始めて,15:14:20で電池残量表示が42%,16:00:00で電池切れ

動作距離

  • かなり距離を離した状態でも稼働に問題はなく,教室内にwifiを置けば,教室場面でも問題なくデータが取得出来るであろうことが分かった。

SCのデータが最大値を振り切る問題への対応

  • 最も最大値を振り切りにくい「ads.setGain(GAIN_ONE); multiplier= 2.0F;」を採用した。それでも最大値を振り切ることはあるが,それはそれで分析可能であろうと判断した。

皮膚温の表示範囲

  • 「子ども子ども計測ハンドブック」を参考にした。

取得データの確認

  • ルーターの電波の範囲を離れると,データに抜けが生じる(当たり前)。
  • 分析前に,念のためデータの抜けを確認した方がいいと思われる。
  • ラグの発生状況は以下の通りであり,$SD 0.20ms $程度であった。
  • 時系列にプロットすると以下の通り。

その他