윤 영기(Yoon, Young- Ki)
T) 02-6085-4734
master mail : newton@eqboard.com
back-up mail : beowulf.kr@mail.com
KOREA

  • 로깅(Logging) 프로그램 :
    LoggerEQ 1.0.8부터 지원(2023/08/15)
    Excel Logger
  • 배경
    2015년에 Open source platform으로 개발된 JoyLAB sStudio(Windows용), iStudio(Android용)부터 내장
    Arduino.cc의 UNO를 기반으로 개발됨
  • 변경 사항
    – 화면 제어 방법이 차이가 있어, 센서 정보를 가져오는 부분이 변경
    JoyLAB과 호환되지 않음 (“{ 2,2 }”에 관련된 부분)
    센서 값이 표시될 때까지 설정화면에 대기해야 함
  • 센서에 대한 정보는 이 글에서 설명하겠습니다.

STEM(한국에서는 STEAM = STEM + Art) 교육을 지원하기 위하여 개발되었다.
(최근에는 프로그램 학습(Content)이 추가하는 경우가 있다.)

LoggerEQ는 다음과 같은 제한을 가지고 지원한다.
– 측정 간격 : 30초 ~ 0.05초
– 데이터 수: 300개
– 센서 수 : 4개

어느 MCU에서도 사용할 수 있도록 구성된 프로그램입니다.

필수 수정할 부분

void init_Sensor() {

// 센서 ID는 1~15까지 임의로 설정, 단 같은 센서는 같은 번호로 할 것
Sensor[i].Id = 1;
// 센서 명은 31자리 영문으로 설정
strlcpy(Sensor[i].Name, “Force”, 31);
// 센서 기본 단위는 7자리 영문으로 설정
strlcpy(Sensor[i].Unit, “N”, 7);
// ICON은 소스 파일에서 참조
Sensor[i].Icon = 20;
// 교정 기능을 사용할 경우 1, 교정하지 않을 경우는 0으로
Sensor[i].Calibration = 1;
// 센서 값에 -1을 곱하는 기능으로 센서 값의 증가 방향을 바꿀 때 사용
Sensor[i].SignInversion = 1;
// 센서가 프로그램으로 측정 범위를 변경해야 할 때 사용(1~3단계)
Sensor[i].RangeCount = 1;
// 센서 값의 단위 계열은 소스 파일에서 참조
Sensor[i].SensorGroup = 106;
// 차트에 표시되는 상단 값과 하단 값
Sensor[i].RealLowValue = -80;
Sensor[i].RealHighValue = 80;

void readCurrentValue() {
readSensors = false;
{ // AD 값을 물리적 값으로 변환하여 sensorValue에 넣는다.
sensorValue[0]=analogRead(0);
}
{
sensorValue[1]=analogRead(1);
}

  • Device drive로 인하여 접속 실패하는 경우가 있음
  • 메인 이미지가 다른 경우, 이는 유료 협력사에 제공된 로거 프로그램임
  • MBL 센서를 사용해야 하는가?(20230823)
    STEAM 교육에서 사용하는 센서의 대부분은 sparkfun.com, adafruit.com, seeedstudio.com, dfrobot.com 등에서 공개한 것들을 주로 사용한다.
    대부분이 1개씩 사용하도록 설계가 되어 있어, 전원 문제로 정밀도가 떨어진다.
    반면, MBL센서를 Arduino에서 사용하기 위해서는 Interface shield 또는 Sensor connector가 필요하다.
    실험의 목적이 정량 측정이 아닌 STEM에서는 사용하지 않아도 된다.

전체 소스

const int   sensorLength = 4;
const int   LEDpin = 13;

boolean     runFlag = false;
boolean     readSensors = false;
long        currentTimeCount = 0;
unsigned long samplingTime = 200; // ms
unsigned long timeRef;

/*
ICON(일부만 공개)
    ...
  6  Barometer.png
    ...
  9  CarbonDioxide.png
    ...
  14  Current.png
    ...
  20  Force.png
    ...
  31  Motion.png
    ...
  32  pH.png
    ...
  40  RelativePressure.png
    ...
  45  Temperature.png
    ...
  52  Voltage.png

단위 계열(일부만 공개)
  100: none
  101: Time
  102: Temperature
  103: Pressure
  104: Voltage
  105: Length
  106: Force
    ...
  108: Acceleration
    ...
  114: Current
    ...
  120: Parts Per Notation(농도 표기)
  121: Percent(%)
    ...
*/

typedef struct {
  uint8_t   Id;
  char      Name[32];
  char      Unit[8];
  uint8_t   Icon;           // ICON
  uint8_t   Calibration;
  uint8_t   SignInversion;
  uint8_t   RangeCount;
  uint8_t   SensorGroup;    // 단위 계열
  float     RealLowValue;
  float     RealHighValue;
} typeSensor;
typeSensor  Sensor[sensorLength];
float       sensorValue[sensorLength];

String      usbBuffer = "";

//=======================================================================
void setup() {
  init_Sensor();
  pinMode(LEDpin, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  while(Serial.available() > 0) {
    char ch = Serial.read();
    if (ch == '{') 
      usbBuffer = "";
    else if (ch == '}') {
      parsing();
      usbBuffer = "";
    } else
      usbBuffer += ch;
  }

  if (runFlag) {
    if ((micros() - timeRef) / 1000 >= samplingTime) readSensors = true;
  }

  if(readSensors) readCurrentValue();
}

//=======================================================================
// 센서에 맞추어 수정하십시오
//-----------------------------------------------------------------------
void init_Sensor() {
  Sensor[0].Id = 1;
  strlcpy(Sensor[0].Name, "Force", 31);
  strlcpy(Sensor[0].Unit, "N", 7);
  Sensor[0].Icon = 20;
  Sensor[0].Calibration = 1;
  Sensor[0].SignInversion = 1;
  Sensor[0].RangeCount = 1;
  Sensor[0].SensorGroup = 106;
  Sensor[0].RealLowValue = -80;
  Sensor[0].RealHighValue = 80;

  Sensor[1].Id = 2;
  strlcpy(Sensor[1].Name, "Temperature", 31);
  strlcpy(Sensor[1].Unit, "'C", 7);
  Sensor[1].Icon = 45;
  Sensor[1].Calibration = 0;
  Sensor[1].SignInversion = 0;
  Sensor[1].RangeCount = 1;
  Sensor[1].SensorGroup = 102;
  Sensor[1].RealLowValue = -20;
  Sensor[1].RealHighValue = 120;
  
  Sensor[2].Id = 0;
  strlcpy(Sensor[2].Name, "none", 31);
  strlcpy(Sensor[2].Unit, "n", 7);
  Sensor[2].Icon = 0;
  Sensor[2].Calibration = 0;
  Sensor[2].SignInversion = 0;
  Sensor[2].RangeCount = 1;
  Sensor[2].SensorGroup = 100;
  Sensor[2].RealLowValue = 0;
  Sensor[2].RealHighValue = 100;
  
  Sensor[3].Id = 0;
  strlcpy(Sensor[3].Name, "none", 31);
  strlcpy(Sensor[3].Unit, "n", 7);
  Sensor[3].Icon = 0;
  Sensor[3].Calibration = 0;
  Sensor[3].SignInversion = 0;
  Sensor[3].RangeCount = 1;
  Sensor[3].SensorGroup = 100;
  Sensor[3].RealLowValue = 0;
  Sensor[3].RealHighValue = 100;
}

void readCurrentValue() {
  timeRef = micros();
  readSensors = false;
  {
    sensorValue[0]=analogRead(0);
  }
  {
    sensorValue[1]=analogRead(1);
  }

  Serial.print("{ 26,");
  for(int i = 0; i < sensorLength; i++) {
    if(i > 0) Serial.print(",");
    Serial.print(sensorValue[i]);
  }
  Serial.println(" }");
}

//=======================================================================
// 로깅프로그램 명령어 처리
//-----------------------------------------------------------------------
void parsing() {
  uint8_t i = 0;
  int incomingByte1 = 0;
  int incomingByte2 = 0;
  int incomingByte3 = 0;
  float retSampling = 0;
  float newTime = 0;
  
  incomingByte1 = getPasingValue(usbBuffer, ',', 0).toInt();
  switch(incomingByte1) {
    case 1:
      incomingByte2 = getPasingValue(usbBuffer, ',', 1).toInt();
      switch(incomingByte2) {
        case 1: // 인터페이스 확인
          Serial.println("{ 11,STEAMp.com }");
          Serial.flush();
          break;
        case 2: // 연결할 수 있는 센서의 수
          Serial.print("{ 12,");
          Serial.print(sensorLength);
          Serial.println(" }");
          break;
      }
      break;
    case 2: // send sensor information
      incomingByte2 = getPasingValue(usbBuffer, ',', 1).toInt();
      incomingByte3 = getPasingValue(usbBuffer, ',', 2).toInt();
      switch(incomingByte2) {
        case 1:   // 센서 ID (범위: 1~15)
          Serial.print("{ 13,");
          for(i = 0; i < sensorLength; i++) {
            if(i > 0) Serial.print(",");
            Serial.print(Sensor[i].Id);
          }
          Serial.println(" }");
          break;
        case 5:   // 센서 명칭
          Serial.print("{ 24,");
          Serial.print(incomingByte3);
          Serial.print(",");
          Serial.print(Sensor[incomingByte3].Name);
          Serial.println(" }");
          break;
        case 6:   // 센서 단위
          Serial.print("{ 25,");
          Serial.print(incomingByte3);
          Serial.print(",");
          Serial.print(Sensor[incomingByte3].Unit);
          Serial.println(" }");
          break;
        case 10:  // 센서 단위 계열
          Serial.print("{ 30,");
          Serial.print(incomingByte3);
          Serial.print(",");
          Serial.print(Sensor[incomingByte3].SensorGroup);
          Serial.println(" }");
          break;
        case 11:  // 센서 ICON
          Serial.print("{ 70,");
          Serial.print(incomingByte3);
          Serial.print(",");
          Serial.print(Sensor[incomingByte3].Icon);
          Serial.println(" }");
          break;
        case 12:  // 교정 대상인지?
          Serial.print("{ 71,");
          Serial.print(incomingByte3);
          Serial.print(",");
          Serial.print(Sensor[incomingByte3].Calibration);
          Serial.println(" }");
          break;
        case 13:  // 측정 범위를 선택하는 센서인지?
          Serial.print("{ 72,");
          Serial.print(incomingByte3);
          Serial.print(",");
          Serial.print(Sensor[incomingByte3].RangeCount);
          Serial.println(" }");
          break;
        case 14:  // 측정 값의 증가하는 방향이 상대적인지?
          Serial.print("{ 73,");
          Serial.print(incomingByte3);
          Serial.print(",");
          Serial.print(Sensor[incomingByte3].SignInversion);
          Serial.println(" }");
          break;
        case 16:  // 측정 값 표시 범위
          Serial.print("{ 75,");
          Serial.print(incomingByte3);
          Serial.print(",");
          Serial.print(Sensor[incomingByte3].RealLowValue);
          Serial.print(",");
          Serial.print(Sensor[incomingByte3].RealHighValue);
          Serial.println(" }");
          break;
      }
      break;
    case 3:
      incomingByte2 = getPasingValue(usbBuffer, ',', 1).toInt();
      switch(incomingByte2) {
        case 1:
          newTime = getPasingValue(usbBuffer, ',', 2).toFloat();
          if(0.05<= newTime && newTime <= 30.0) samplingTime = (unsigned long)(newTime * 1000.0);
          Serial.print("{ 15,");
          retSampling = samplingTime/1000.0;
          Serial.print(retSampling, 3);
          Serial.println(" }"); 
          break;
        case 2:
          if (!runFlag) readSensors = true;
          break;
        case 3:
          if (!runFlag) {
            currentTimeCount = 0;
            digitalWrite(LEDpin, true);
            Serial.println("{ 17 }");
            runFlag = true;
          }
          break;
        case 4:
          runFlag = false;
          Serial.println("{ 18 }");
          digitalWrite(LEDpin, false);
          break;
      }
      break;
  }
}

//-----------------------------------------------------------------------
String getPasingValue(String data, char separator, int index) {
  int found = 0;
  int strIndex[] = {0, -1};
  int maxIndex = data.length()-1;

  for(int i=0; i<=maxIndex && found<=index; i++){
    if(data.charAt(i)==separator || i==maxIndex){
        found++;
        strIndex[0] = strIndex[1]+1;
        strIndex[1] = (i == maxIndex) ? i+1 : i;
    }
  }

  return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}
인터페이스 연결하는 방법
센서 교정하는 방법
EQBoard-Ard

By neoy2g

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다