#include <Arduino.h>
#include <string.h>
 
// ピン接続 | Pin assign
#define GT_DTR 4 // DTR
#define GT_DSR 6 // DSR
 
// モーター用ピン設定 | Motor pin settings
const int speedPin = 9;
const int dir1Pin = 8;
const int dir2Pin = 10;
 
// 変数 | Variable
unsigned long previousMillis = 0;
int currentSliderValue = 0; // 1秒おきのグラフ更新のために数値を記憶しておく変数 | Variable to store the value for the 1-second graph update
 
void setup() {
  // ピン初期設定 | Pin setting
  pinMode(GT_DTR, INPUT);
  pinMode(GT_DSR, OUTPUT);
  digitalWrite(GT_DSR, LOW);
 
  // シリアル通信の開始 | Start serial communication
  Serial.begin(38400);
  Serial.setTimeout(100);
 
  // モーターピンの初期化 | Initialize motor pins
  pinMode(speedPin, OUTPUT);
  pinMode(dir1Pin, OUTPUT);
  pinMode(dir2Pin, OUTPUT);
  
  // 初期状態は前進方向で停止 | Initial state: Stopped in forward direction
  digitalWrite(dir1Pin, HIGH);
  digitalWrite(dir2Pin, LOW);
  analogWrite(speedPin, 0);
 
  delay(1000); // 初期化待ち | Wait for initialization
}
 
void loop() {
  unsigned long currentMillis = millis(); // 現在時刻の取得 | Get current time
 
  // GT-SPデータ受信 | Receive one GT-SP response data
  if (Serial.available()) {
    
    // GT-SPから数値を取得 | Get numeric value from GT-SP
    unsigned long gtspValue = gtsp_signal_read_numeric();
    currentSliderValue = gtspValue; // グラフ用に値を保持しておく | Store the value for the graph
 
    // 1. スライダーの数値(0〜100)を読み取り、DCモーターの回転速度に変換して制御する | Read the slider value (0-100) and convert it to DC motor rotation speed
    int targetSpeed = map(currentSliderValue, 0, 100, 0, 255);
    analogWrite(speedPin, targetSpeed);
 
    // 2. スライダーの数値を更新する (テキストオブジェクトID:3にパーセント表示を送信) | Update the slider value (Send percentage display to Text Object ID: 3)
    gtsp_ObjPrpSet_string(3, 0x40, String(currentSliderValue) + " %");
 
    // 3. 数値の範囲に応じて、テキストオブジェクトの文字色を変更するコマンドを送信する | Send a command to change the text color according to the value range
    unsigned long colorCode;
    if (currentSliderValue == 0) {
      colorCode = 0x00808080; // 閾値: 0 の場合は灰色 | Threshold: 0 (Gray)
    } else if (currentSliderValue <= 50) {
      colorCode = 0x0000FF00; // 閾値: 1から50 の場合は緑色 | Threshold: 1-50 (Green)
    } else if (currentSliderValue <= 80) {
      colorCode = 0x00FFFF00; // 閾値: 51から80 の場合は黄色 | Threshold: 51-80 (Yellow)
    } else {
      colorCode = 0x00FF0000; // 閾値: 81から100 の場合は赤色 | Threshold: 81-100 (Red)
    }
    // 文字色プロパティ(0x50)にカラーコードを送信 | Send color code to font color property (0x50)
    gtsp_ObjPrpSet_val(3, 0x50, 4, colorCode);
  }
 
  // 1秒おきにグラフの描画と色変更を行う処理 | Process to draw the graph and change color every 1 second
  if (currentMillis - previousMillis >= 1000) {
    
    // 4. 1秒ごとに最新の数値を送信し、BLOCK STACKグラフを更新する | Send the latest value every second to update the BLOCK STACK graph
    gtsp_ObjPrpSet_val(0, 0x40, 4, currentSliderValue);
 
    // 5. 数値の範囲に応じて、グラフの色を変更するコマンドを送信する | Send a command to change the graph color according to the value range
    unsigned long graphColorCode;
    if (currentSliderValue == 0) {
      graphColorCode = 0x00808080; // 閾値: 0 の場合は灰色 | Threshold: 0 (Gray)
    } else if (currentSliderValue <= 50) {
      graphColorCode = 0x0000FF00; // 閾値: 1から50 の場合は緑色 | Threshold: 1-50 (Green)
    } else if (currentSliderValue <= 80) {
      graphColorCode = 0x00FFFF00; // 閾値: 51から80 の場合は黄色 | Threshold: 51-80 (Yellow)
    } else {
      graphColorCode = 0x00FF0000; // 閾値: 81から100 の場合は赤色 | Threshold: 81-100 (Red)
    }
    // グラフオブジェクト(ID:0)のレーンカラープロパティに色を送信 | Send color code to the lane color property of Graph Object (ID: 0)
    gtsp_ObjPrpSet_val(0, 0x70, 4, graphColorCode);
 
    previousMillis = currentMillis; // 時間をリセット | Reset the timer
  }
}
 
/********************** GT-SP関数 | Function for GT-SP **********************/
 
////////////////////////////////////////////////////
//  データ受信（数値用） | Receive data from GT-SP (for Numeric)
////////////////////////////////////////////////////
unsigned long gtsp_signal_read_numeric() {
  byte res_dl[4] = "";
  unsigned long dl;
  if (Serial.find("RESb", 4)) {
    Serial.readBytes(res_dl, 4); //データ長抽出 | Read data length
    dl = (unsigned long)(res_dl[0] + (res_dl[1] << 8) + (res_dl[2] << 16) + (res_dl[3] << 24)); //データ長変換 | Convert data length
 
    // 数値を受信するためのバッファ | Buffer for receiving numeric values
    byte res_data_buffer[4];
    // データを読み取り | Read data
    Serial.readBytes(res_data_buffer, dl);
 
    // 数値に変換してリターン | Convert to numeric value and return
    return (unsigned long)(res_data_buffer[0] + (res_data_buffer[1] << 8) + (res_data_buffer[2] << 16) + (res_data_buffer[3] << 24));
  }
 
  // エラー時は0を返す | Return 0 if error occurs
  return 0;
}
 
////////////////////////////////////////////////////
// オブジェクト制御コマンド - プロパティ設定 (数値用) | Object Control Command - Property Setting (for Value)
////////////////////////////////////////////////////
void gtsp_ObjPrpSet_val(int obj, int prp, int ln, unsigned long val) {
  gt_print("CMD");     // コマンドヘッダ | Command header
  gt_put(0xd3);        // オブジェクト-プロパティ設定コマンド | Object-Property Setting command
  gt_put(obj >> 0);    // オブジェクトNo. 下位バイト | Object No. Lower byte
  gt_put(obj >> 8);    // オブジェクトNo. 上位バイト | Object No. Upper byte
  gt_put(prp >> 0);    // プロパティNo. 下位バイト | Property No. Lower byte
  gt_put(prp >> 8);    // プロパティNo. 上位バイト | Property No. Upper byte
  gt_put(ln >> 0);     // データ長 最下位バイト | Data length Least significant byte
  gt_put(ln >> 8);     // データ長 下位バイト | Data length second byte
  gt_put(ln >> 16);    // データ長 上位バイト | Data length third byte
  gt_put(ln >> 24);    // データ長 最上位バイト | Data length Most significant byte
  if (ln >= 1) gt_put(val >> 0);
  if (ln >= 2) gt_put(val >> 8);
  if (ln >= 3) gt_put(val >> 16);
  if (ln >= 4) gt_put(val >> 24);
}
 
////////////////////////////////////////////////////
//  オブジェクト制御コマンド - プロパティ設定 (文字列用) | Object Control Command - Property Setting (for String)
////////////////////////////////////////////////////
void gtsp_ObjPrpSet_string(int obj, int prp, String val )
{
  gt_print("CMD");     //コマンドヘッダ | Command header
  gt_put(0xd3);        //オブジェクト-プロパティ設定コマンド | Object-Property Setting command
  gt_put(obj >> 0);    //オブジェクトNo. 下位バイト | Object No. Lower byte
  gt_put(obj >> 8);    //オブジェクトNo. 上位バイト| Object No. Upper byte
  gt_put(prp >> 0);    //プロパティNo. 下位バイト | Property No. Lower byte 
  gt_put(prp >> 8);    //プロパティNo. 上位バイト| Property No. Upper byte 
  gt_put(val.length() >> 0);    //データ長 最下位バイト | Data length Least significant byte
  gt_put(val.length() >> 8);    //データ長 下位バイト | Data length second byte
  gt_put(val.length() >> 16);   //データ長 上位バイト | Data length third byte
  gt_put(val.length() >> 24);   //データ長 最上位バイト| Data length Most significant byte
  gt_print(val);       //文字列送信 | Send string
}
 
////////////////////////////////////////////////////
// 1byte送信 | Send byte to GT-SP
////////////////////////////////////////////////////
void gt_put(unsigned char onebyte) {
  while (digitalRead(GT_DTR) == HIGH) {} // ビジーチェック | Busy check
  Serial.write(onebyte);
}
 
////////////////////////////////////////////////////
// 文字列送信 | Send String to GT-SP
////////////////////////////////////////////////////
void gt_print(String val) {
  int val_i;
  // 文字列を1文字ずつ送信 | Send string per one byte
  for (val_i = 0; val_i < val.length(); val_i++) {
    while (digitalRead(GT_DTR) == HIGH) {} // ビジーチェック | Busy check
    Serial.print(val.substring(val_i, val_i + 1));
  }
}