WioTerminalで水準器

WioTerminalには、初めから加速度センサがついているので、これを使って…と、なると、とりあえず、水準器あたりからですかねぇ~?
というわけで、水準器、作ってみました。
f:id:manpukukoji:20200711095526j:plain
特に、説明もいらないと思いますが、一応、5つボタンでスケールを変えられるようにしてあります。
上下で5段階変更できます。 これで、フルスケールが変わります。
スケール1のときは、真ん中が水平、一番外の丸が90度、に、なるはず…。
プログラムもほとんどライブラリのサンプルのままです。 角度にするのにちょっとだけ計算してます。

#include"LIS3DHTR.h"
#include "TFT_eSPI.h"
#include <math.h>

LIS3DHTR<TwoWire> lis;
TFT_eSPI tft;

void disp_sight() {
  tft.drawCircle(159,119,90,TFT_WHITE);
  tft.drawCircle(159,119,60,TFT_WHITE);
  tft.drawCircle(159,119,30,TFT_WHITE);
  tft.drawLine(159,29,159,209,TFT_WHITE);
  tft.drawLine(69,119,249,119,TFT_WHITE);
  tft.fillCircle(159,119,3,TFT_RED);  

  tft.fillRoundRect(56,215,206,20,10,TFT_DARKGREEN);
  tft.fillRoundRect(295,16,20,206,10,TFT_DARKGREEN);

}
 
void setup() {
  Serial.begin(115200);
//   while (!Serial);
  pinMode(WIO_5S_UP, INPUT_PULLUP);
  pinMode(WIO_5S_DOWN, INPUT_PULLUP);

  lis.begin(Wire1);
 
  if (!lis) {
    Serial.println("ERROR");
    while(1);
  }
  lis.setOutputDataRate(LIS3DHTR_DATARATE_25HZ); //Data output rate
  lis.setFullScaleRange(LIS3DHTR_RANGE_2G); //Scale range set to 2g

  // Display set up
  tft.begin();
  tft.setRotation(3);
  tft.setTextColor(TFT_YELLOW);
  tft.setTextSize(2);
  tft.fillScreen(TFT_BLACK); 
  disp_sight();
  tft.drawString("Angle", 5, 20);
  tft.drawString("X: ", 10, 40);
  tft.drawString("Y: ", 10, 60);
  tft.drawString("Accel.", 5, 120);
  tft.drawString("X: ", 10, 140);
  tft.drawString("Y: ", 10, 160);
  tft.drawString("Z: ", 10, 180);
  tft.drawString("Scale", 238, 20);
}

  int p_plot_x = 159, p_plot_y = 119;
  int scale = 1, p_scale = 5;
   
void loop() {
  float x_values, y_values, z_values;
  int plot_x, plot_y;
  x_values = lis.getAccelerationX();
  y_values = lis.getAccelerationY();
  z_values = lis.getAccelerationZ();
/* 
  Serial.print("X: "); Serial.print(x_values);
  Serial.print(" Y: "); Serial.print(y_values);
  Serial.print(" Z: "); Serial.print(z_values);
  Serial.println();
*/
  tft.fillRect(50,40,72,35,TFT_BLACK);
  tft.fillRect(50,140,72,55,TFT_BLACK);
  tft.drawString(String(180.0/3.1415*asin(x_values)), 50, 40);
  tft.drawString(String(180.0/3.1415*asin(y_values)), 50, 60);
  tft.drawString(String(x_values), 50, 140);
  tft.drawString(String(y_values), 50, 160);
  tft.drawString(String(z_values), 50, 180);
  if (scale != p_scale) {
  tft.fillRect(260,41,25,20,TFT_BLACK);
  tft.drawString(String(scale), 260, 41);
  }

  tft.fillCircle(p_plot_x,p_plot_y,10,TFT_BLACK);
  tft.fillRect(p_plot_x+3,215,6,20,TFT_DARKGREEN);
  tft.fillRect(295,p_plot_y-3,20,6,TFT_DARKGREEN);
  disp_sight();
  plot_x = 159 + scale*(90.0/3.1415 * 2 * asin(y_values));
  if (plot_x > 249) plot_x = 249;
  if (plot_x < 69) plot_x = 69;
  plot_y = 119 - scale*(90.0/3.1415 * 2 * asin(x_values));
  if (plot_y > 209) plot_y = 209;
  if (plot_y < 29) plot_y = 29;
  tft.fillCircle(plot_x,plot_y,10,TFT_YELLOW);
  tft.fillRect(plot_x-3,215,6,20,TFT_WHITE);
  tft.drawLine(plot_x,215,plot_x,235,TFT_BLACK);
  tft.fillRect(295,plot_y-3,20,6,TFT_WHITE);
  tft.drawLine(295,plot_y,315,plot_y,TFT_BLACK);

   if (digitalRead(WIO_5S_UP) == LOW) {
    scale++;
    if (scale > 5) scale = 5;
    while(digitalRead(WIO_5S_UP) == LOW);
   }
   if (digitalRead(WIO_5S_DOWN) == LOW) {
    scale--;
    if (scale <1) scale = 1;
    while(digitalRead(WIO_5S_DOWN) == LOW);
   }

  p_plot_x = plot_x;
  p_plot_y = plot_y;
  
  delay(50);
}

じっと見てると、結構、画面がちらちらして、目が疲れます。

そんなわけで、らびやん さんのLovyanGFX というライブラリを使ってみました。
画面のちらちらは、解消されたようです。 LovyanGFX おそるべし!!
が、いかんせん、加速度の数字そのものが、ちらちらと変化しているものだから、そこんとこはしかたないですね。
値の表示をやめれば、結構、いい感じかも。

#include"LIS3DHTR.h"
#include <LovyanGFX.hpp>
#include <math.h>

#define DARKGREEN24 0x38761DU
#define GREEN24 0x00FF00
#define RED24 0xFF0000U
#define YELLOW24 0xFFFF00U
#define WHITE24 0xFFFFFFU
#define BLACK24 0x000000U

LIS3DHTR<TwoWire> lis;
static LGFX lcd;
//static LGFX_Sprite canvas(&lcd);   
static LGFX_Sprite sight(&lcd);   
static LGFX_Sprite bottom(&lcd);  
static LGFX_Sprite right(&lcd);   
static LGFX_Sprite bubble(&sight);  
static LGFX_Sprite idx_X(&lcd);   
static LGFX_Sprite idx_Y(&lcd);   
static auto transpalette = 0;           
  
void setup() {
  Serial.begin(115200);
//   while (!Serial);
  pinMode(WIO_5S_UP, INPUT_PULLUP);
  pinMode(WIO_5S_DOWN, INPUT_PULLUP);

  lis.begin(Wire1);
 
  if (!lis) {
    Serial.println("ERROR");
    while(1);
  }
  lis.setOutputDataRate(LIS3DHTR_DATARATE_25HZ); //Data output rate
  lis.setFullScaleRange(LIS3DHTR_RANGE_2G); //Scale range set to 2g

  // Display set up
  lcd.init();
  lcd.setRotation(1);
  lcd.setBrightness(128);

//keep memory
  //canvas.createSprite(200, 200);
  sight.createSprite(200, 200);
  bottom.createSprite(206, 20);
  right.createSprite(20, 206);
  bubble.createSprite(20, 20);
  idx_X.createSprite(6, 20);
  idx_Y.createSprite(20, 6);

// create parts
  sight.fillScreen(transpalette);
  bottom.fillScreen(transpalette);
  right.fillScreen(transpalette);
  bubble.fillScreen(transpalette);
  idx_X.fillScreen(transpalette);
  idx_Y.fillScreen(transpalette);

  sight.drawCircle(100,100,90,WHITE24);
  sight.drawCircle(100,100,60,WHITE24);
  sight.drawCircle(100,100,30,WHITE24);
  sight.drawLine(100,10,100,190,WHITE24);
  sight.drawLine(10,100,190,100,WHITE24);
  sight.fillCircle(100,100,3,RED24);

  bottom.fillRoundRect(0,0,206,20,10,DARKGREEN24);
  right.fillRoundRect(0,0,20,206,10,DARKGREEN24);
  bubble.fillCircle(10,10,10,YELLOW24);
  idx_X.fillRect(0,0,6,20,WHITE24);
  idx_X.drawFastVLine(3,0,20,BLACK24);
  idx_Y.fillRect(0,0,20,6,WHITE24);
  idx_Y.drawFastHLine(0,3,20,BLACK24);
  
  lcd.setTextColor(YELLOW24, BLACK24);
  lcd.setTextSize(2);
  sight.pushSprite(59,19);
  
  lcd.drawString("Angle", 5, 20);
  lcd.drawString("X: ", 10, 40);
  lcd.drawString("Y: ", 10, 60);
  lcd.drawString("Accel.", 5, 120);
  lcd.drawString("X: ", 10, 140);
  lcd.drawString("Y: ", 10, 160);
  lcd.drawString("Z: ", 10, 180);
  bottom.pushSprite(56,215);
  right.pushSprite(295,16);
}

  int p_plot_x = 159, p_plot_y = 119;
  float px_values=0.0, py_values=0.0, pz_values=0.0;
  float px_angle=0, py_angle=0; 
  int scale = 1, p_scale = 5;;
  
void loop() {
  float x_values, y_values, z_values, x_angle, y_angle;
  int plot_x, plot_y;
  
  x_values = lis.getAccelerationX();
  y_values = lis.getAccelerationY();
  z_values = lis.getAccelerationZ();

  x_angle = 180.0/3.1415*asin(x_values);
  y_angle = 180.0/3.1415*asin(y_values);

  plot_x = 100 + scale*(90.0/3.1415 * 2 * asin(y_values));
  if (plot_x > 197) plot_x = 197;
  if (plot_x < 3) plot_x = 3;
  plot_y = 100 - scale*(90.0/3.1415 * 2 * asin(x_values));
  if (plot_y > 197) plot_y = 197;
  if (plot_y < 3) plot_y = 3;

  sight.clear();
  sight.drawCircle(100,100,90,WHITE24);
  sight.drawCircle(100,100,60,WHITE24);
  sight.drawCircle(100,100,30,WHITE24);
  sight.drawLine(100,10,100,190,WHITE24);
  sight.drawLine(10,100,190,100,WHITE24);
  sight.fillCircle(100,100,3,RED24);
  sight.fillCircle(plot_x,plot_y,10,YELLOW24);
  sight.pushSprite(59,19);

  bottom.pushSprite(56,215);
  right.pushSprite(295,16);
  idx_X.pushSprite(plot_x-3+60, 215);
  idx_Y.pushSprite(295, plot_y-3+20);

  lcd.setTextColor(BLACK24,BLACK24);
  lcd.drawString(String(px_angle), 50, 40);
  lcd.drawString(String(py_angle), 50, 60);
  lcd.drawString(String(px_values), 50, 140);
  lcd.drawString(String(py_values), 50, 160);
  lcd.drawString(String(pz_values), 50, 180);
  lcd.setTextColor(WHITE24,BLACK24);
  lcd.drawString(String(x_angle), 50, 40);
  lcd.drawString(String(y_angle), 50, 60);
  lcd.drawString(String(x_values), 50, 140);
  lcd.drawString(String(y_values), 50, 160);
  lcd.drawString(String(z_values), 50, 180);
  lcd.drawString("Scl", 260, 20);
  if (scale != p_scale) {
  lcd.fillRect(270,41,25,20,TFT_BLACK);
  lcd.drawString(String(scale), 270, 41);
  }

  p_plot_x = plot_x;
  p_plot_y = plot_y;
  px_values = x_values;
  py_values = y_values;
  pz_values = z_values;
  px_angle = x_angle;
  py_angle = y_angle;

     if (digitalRead(WIO_5S_UP) == LOW) {
    scale++;
    if (scale > 5) scale = 5;
    while(digitalRead(WIO_5S_UP) == LOW);
   }
   if (digitalRead(WIO_5S_DOWN) == LOW) {
    scale--;
    if (scale <1) scale = 1;
    while(digitalRead(WIO_5S_DOWN) == LOW);
   }

  delay(50);
}

じっと、見てると時々 1g を超えたりする。
一応、自宅の重力加速度を調べてみると、9.797と出たので、1g を超えることはないはず…?
ん? 重力異常?
なんか、壁から宇宙人が出てきそう…。
ぐるっと一回りさせて表示させてみたら、0.93g から1.04g まで、ばらついている。
f:id:manpukukoji:20200711105605j:plain
やっぱり、ちゃんと使うには、キャリブレーションとか必要なんでしょうね。

参考サイト
Overview - Seeed Wiki

github.com

重力値推定計算サービス

Wio Terminal + Grove MP3 でMP3プレーヤーにしてみた

Wio Terminal にGrove MP3のモジュールをつないで、MP3プレーヤーにしてみました。
f:id:manpukukoji:20200708154406j:plain
Wio Terminalは、ボタンやディスプレーが充実しているので、こういう用途には、便利ですね。
まぁ、僕の場合、バッテリーを何とかしないとならんのですが…。
WioとGrove接続は、いたって簡単で、Wioの3.3VとGrovenoVcc, それぞれのGNDどうし、あとは、シリアルのRxとTxをクロスして接続するだけです。
ソフトの方は、Seeedのライブラリやサンプルを参考にすれば、特に問題なさそうです。
基本的な機能しか用意していませんが、Seeedのライブラリになっていない機能もMP3のKT403にはあるようなので、時間をかければ、もう少し凝ったこともできそうです。
ですが、時々、急に機嫌が悪くなったりするので、もしかしたら、結構バグがあるかもしれません。 まぁ、このまま作る人もないでしょうけど、ご迷惑をおかけしたら、ごめんちゃい。 自己責任でよろしく…と、いうことで…。
MP3ファイルをSDカードに保存するときには、少し、癖があって、決められたファルダ構成でないとうまくPlayしてくれません。
フォルダを01、02、03…と分け、その中のファイルの頭に001、002、003…とつけていきます。 ファイル名では、残念ながらコントロールできないようです。
KT403Aとフォルダに関しては、参考サイトで詳しく説明してくれています。

使い方は、

電源を入れると、Play画面が立ち上がり、Aボタンでループモードで再生されます。
5ボタンで、右を押すと次のファイル、左が前のファイル。 上がボリュームアップ、下がボリュームダウンです。また、センターは、ポーズです。
f:id:manpukukoji:20200708154521j:plain
Cボタンを押すと設定画面に切り替わります。
再生のモードが5種類あって、

  • Loop_All  全体を順番に再生しながらループします。
  • Loop_folder  ある特定のフォルダ内をループします。 フォルダの指定が必要です。
  • Play_root  ルートフォルダのファイルを指定して再生します。 ファイルの指定が必要です。
  • Play_MP3  MP3フォルダのファイルを指定して再生します。 ファイルの指定が必要です。
  • Play_Folder_file  特定のフォルダのファイルを指定して再生します。 フォルダとファイルの指定が必要です。

イコライザの機能もあって、NORMAL、POP、ROCK、JAZZ、CLASSIC、BASSの、6種類から選べます。
設定画面では、右、左のボタンで項目を選択します。 上下のボタンで、次の項目選択に移動します。
選択項目は、再生のモードによって変わります。
選択が完了すると、CボタンでPlay画面に戻り、再生が開始されます。
f:id:manpukukoji:20200708154546j:plain

Wio-chan画像の表示

画面が文字ばかりでさみしいので、WioTeriminal-chan の画像を表示するようにしてみましたが、よく考えると、画像は、SDカードから読み込むので、MP3用のSDカードとWio用のSDカードと2枚必要になってしまいます。 とりあえず、1枚しかもっていないので、Wio-chanは、しばらくお休みいただくことにしました。 プログラムのSDカードの初期化のところ ( setup() の初めのところ) を生かせば、画像が表示できるようになりますが、画像は、別途用意しなければなりません。
SeeedのWikiにやり方は、説明されていますけど簡単に書くと、以下のような手順です。
まず、適当なサイズの24ビットのBMP画像を用意します。
次に画像ファイルを変換するパイソンのファイルをダウンロードして(ここbmp_converter.pyというファイル)、どこかのフォルダに置きます。そのフォルダにBMPというフォルダを作って、そこに先ほど用意したBMP画像を置きます。
コマンドプロンプトでパイソンのファイルを置いたフォルダに移動し、"python bmp_converter.py" と、やると8ビットファイルにするか[1]、16ビットファイルにするか[2]聞いてくるので、選択します。
すると、bmpフォルダの中にサブフォルダができて、変換された画像ファイルができています。
画像はSDカードから読み込むので、SDカードのライブラリもインポートしておく必要があります。それと、先ほどのWikiのサイトから、”RawImage.h" というヘッダファイルもダウンロードして、スケッチと同じフォルダに保存しておく必要があるようです。

参考サイト
こちらのサイトでKT403Aについて詳しく説明されています。
www.ne.jp/asahi/shared/o-family/ElecRoom/AVRMCOM/MP3module/MP3module.html
SeeedのWikiは、外せないですよね
Loading Image - Seeed Wiki
こちらは、MP3
GitHub - Seeed-Studio/Seeed_Serial_MP3_Player


スケッチは、以下、参照ください。

#include "KT403A_Player.h"
#include"TFT_eSPI.h"
#include "Seeed_FS.h" //Including SD card library
#include"RawImage.h"  //Including image processing library

TFT_eSPI tft;
#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
    #define COMSerial Serial1
    #define ShowSerial SerialUSB

    KT403A<Uart> Mp3Player;
#endif

int menu = 0, p_menu = 4;
int plmode = 0, p_plmode = 5;
int p_status = 0;
int idx_folder = 1, idx_file = 1;
String mode_S[] = {"Loop_All", "Loop_Folder", "Play_Root" , "Play MP3", "Play_File"};
int Eq = 0;
String Eq_S[] = {"NORMAL", "POP", "ROCK" , "JAZZ", "CLASSIC", "BASS"};
EQUALIZER Eq_e[] = {NORMAL, POP, ROCK , JAZZ, CLASSIC, BASS};

void disp_title() {  
  tft.fillScreen(TFT_BLACK);
  
  if (menu == 0) {
    drawImage<uint8_t>("wio-chan8.bmp", 21, 32);
    tft.setTextColor(TFT_YELLOW);
    tft.drawString("MP3 Player",105, 5);
    tft.drawString("Play : ",5, 35);
    tft.drawString(mode_S[plmode], 89, 35);
    tft.drawString("Equalizer : ",5, 65);
    tft.drawString(Eq_S[Eq], 145, 65);
    //tft.fillTriangle(140,100,180,120,140,140,TFT_YELLOW);
  }
  
  if (menu == 1) {
    tft.drawString("Setting", 117, 5);
    tft.drawString("Play : ", 5, 37);
    tft.drawString(mode_S[plmode], 89, 37);
        
    tft.drawString("Equalizer : ", 5, 120);
    tft.drawString(Eq_S[Eq], 149, 120);

    if( (plmode == 1) or (plmode == 4) ) {
       tft.drawString("Folder# : ", 29, 60);
       tft.drawString(String(idx_folder), 150, 60);
    }
    if( (plmode == 2) or (plmode == 3) or (plmode == 4) ) {
       tft.drawString("File# : ", 52, 85);
       tft.drawString(String(idx_file), 150, 85);
    }
    
  }   //  end of menu 1
}   //  endo of disp_title()

void set_play() {
    int set_item = 0;
    int dummy = 0;
    while(menu == 1) {
      if (menu != p_menu)  disp_title();
      p_menu = menu;
      //play mode
      if(set_item == 0) {   // Play mode setting
        dummy = 1;
        tft.drawFastHLine(89,55,180,TFT_WHITE);
        while (dummy){
          if (digitalRead(WIO_5S_RIGHT) == LOW) {
            plmode++;
            if(plmode > 4) plmode = 0;
            disp_title();
            tft.drawFastHLine(89,55,180,TFT_WHITE);
            while(digitalRead(WIO_5S_RIGHT) == LOW){}
          }
          if (digitalRead(WIO_5S_LEFT) == LOW) {
            plmode--;
            if(plmode < 0) plmode = 4;
            disp_title();
            tft.drawFastHLine(89,55,180,TFT_WHITE);
            while(digitalRead(WIO_5S_LEFT) == LOW){}
          }
          if (digitalRead(WIO_5S_DOWN) == LOW) {
            tft.drawFastHLine(89,55,180,TFT_BLACK);
            if( (plmode == 1) or (plmode == 4) ) {
              set_item = 1;
              dummy = 0;
            }
            else if( (plmode == 2) or (plmode == 3) ) {
              set_item = 2;
              dummy = 0;
            }
            else {
              set_item = 3;
              dummy = 0;
            }
            while(digitalRead(WIO_5S_DOWN) == LOW){}
          }
          if (digitalRead(WIO_5S_UP) == LOW) {
            tft.drawFastHLine(89,55,180,TFT_BLACK);
            set_item = 3;
            dummy = 0;
            while(digitalRead(WIO_5S_UP) == LOW){}
          }
          if (digitalRead(WIO_KEY_C) == LOW) {
            menu = 0;
            dummy = 0;
            while(digitalRead(WIO_5S_UP) == LOW){}
          }
        }   //end of while dummy
     }  //end of set item 0
    
      if(set_item == 1) {   // Folder setting
        dummy = 1;
        tft.drawFastHLine(150,76,36,TFT_WHITE);
        while (dummy){
          if (digitalRead(WIO_5S_RIGHT) == LOW) {
            idx_folder++;
            if(idx_folder > 99) idx_folder = 99;
            disp_title();
            tft.drawFastHLine(150,76,36,TFT_WHITE);
            while(digitalRead(WIO_5S_RIGHT) == LOW){}
          }
          if (digitalRead(WIO_5S_LEFT) == LOW) {
            idx_folder--;
            if(idx_folder < 1) idx_folder = 1;
            disp_title();
            tft.drawFastHLine(150,76,36,TFT_WHITE);
            while(digitalRead(WIO_5S_LEFT) == LOW){}
          }
          if (digitalRead(WIO_5S_DOWN) == LOW) {
            tft.drawFastHLine(150,76,36,TFT_BLACK);
            if (plmode == 1)  {
              set_item = 3;
              dummy = 0;
            }
            else {
              set_item = 2;
              dummy = 0;
            }
            while(digitalRead(WIO_5S_DOWN) == LOW){}
          }
          if (digitalRead(WIO_5S_UP) == LOW) {
            tft.drawFastHLine(150,76,36,TFT_BLACK);
            set_item = 0;
            dummy = 0;
            while(digitalRead(WIO_5S_UP) == LOW){}
          }
          if (digitalRead(WIO_KEY_C) == LOW) {
            menu = 0;
            dummy = 0;
            while(digitalRead(WIO_5S_UP) == LOW){}
          }
        }   //end of while dummy
     }  //end of set item 1

      if(set_item == 2) {   // File setting
        dummy = 1;
        tft.drawFastHLine(150,101,36,TFT_WHITE);
        while (dummy){
          if (digitalRead(WIO_5S_RIGHT) == LOW) {
            idx_file++;
            if(idx_folder > 255) idx_folder = 255;
            disp_title();
            tft.drawFastHLine(150,101,36,TFT_WHITE);
            while(digitalRead(WIO_5S_RIGHT) == LOW){}
          }
          if (digitalRead(WIO_5S_LEFT) == LOW) {
            idx_file--;
            if(idx_folder < 1) idx_folder = 1;
            disp_title();
            tft.drawFastHLine(150,101,36,TFT_WHITE);
            while(digitalRead(WIO_5S_LEFT) == LOW){}
          }
          if (digitalRead(WIO_5S_DOWN) == LOW) {
            tft.drawFastHLine(150,101,36,TFT_BLACK);
              set_item = 3;
              dummy = 0;
            while(digitalRead(WIO_5S_DOWN) == LOW){}
          }
          if (digitalRead(WIO_5S_UP) == LOW) {
            tft.drawFastHLine(150,101,36,TFT_BLACK);
           if( (plmode == 2) or (plmode == 3) ) {
             set_item = 0;
             dummy = 0;
           }
           else {
             set_item = 1;
             dummy = 0;
           }
            while(digitalRead(WIO_5S_UP) == LOW){}
          }
          if (digitalRead(WIO_KEY_C) == LOW) {
            menu = 0;
            dummy = 0;
            while(digitalRead(WIO_5S_UP) == LOW){}
          }
        }   //end of while dummy
     }  //end of set item 2

      if(set_item == 3) {   // Equalizer setting
        dummy = 1;
        tft.drawFastHLine(149,136,84,TFT_WHITE);
        while (dummy){
          if (digitalRead(WIO_5S_RIGHT) == LOW) {
            Eq++;
            if(Eq > 5) Eq = 5;
            disp_title();
            tft.drawFastHLine(149,136,84,TFT_WHITE);
            while(digitalRead(WIO_5S_RIGHT) == LOW){}
          }
          if (digitalRead(WIO_5S_LEFT) == LOW) {
            Eq--;
            if(Eq < 0) Eq = 0;
            disp_title();
            tft.drawFastHLine(149,136,84,TFT_WHITE);
            while(digitalRead(WIO_5S_LEFT) == LOW){}
          }
          if (digitalRead(WIO_5S_DOWN) == LOW) {
            tft.drawFastHLine(149,136,84,TFT_BLACK);
              set_item = 0;
              dummy = 0;
            while(digitalRead(WIO_5S_DOWN) == LOW){}
          }
          if (digitalRead(WIO_5S_UP) == LOW) {
            tft.drawFastHLine(149,136,84,TFT_BLACK);
           if( (plmode == 2) or (plmode == 3) or (plmode == 4) ) {
             set_item = 2;
             dummy = 0;
           }
           else if (plmode == 1){
             set_item = 1;
             dummy = 0;
           }
           else {
             set_item = 0;
             dummy = 0;
           }
            while(digitalRead(WIO_5S_UP) == LOW){}
          }
          if (digitalRead(WIO_KEY_C) == LOW) {
            menu = 0;
            dummy = 0;
            while(digitalRead(WIO_5S_UP) == LOW){}
          }
        }   //end of while dummy
     }  //end of set item 3

    }   //end of menu1 
    disp_title();
}   //end of set_play

void setup() {
/*  
    //Initialise SD card
    if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
        while (1);
    }
*/  
    tft.begin();
    tft.setRotation(3);
    tft.fillScreen(TFT_BLACK); 
    tft.setTextSize(2);

    //ShowSerial.begin(9600);
    COMSerial.begin(9600);
    while (!COMSerial);
    Mp3Player.init(COMSerial);

    pinMode(WIO_KEY_A, INPUT_PULLUP);
    pinMode(WIO_KEY_B, INPUT_PULLUP);
    pinMode(WIO_KEY_C, INPUT_PULLUP);
    
    pinMode(WIO_5S_UP, INPUT_PULLUP);
    pinMode(WIO_5S_DOWN, INPUT_PULLUP);
    pinMode(WIO_5S_LEFT, INPUT_PULLUP);
    pinMode(WIO_5S_RIGHT, INPUT_PULLUP);
    pinMode(WIO_5S_PRESS, INPUT_PULLUP);

}

void loop() {
  if (menu != p_menu) {
    disp_title();
  }
  
  if (menu == 0) {
    
             if (digitalRead(WIO_5S_PRESS) == LOW) {
                ShowSerial.println("Press 5s PRESS");
                Mp3Player.pause_or_play();
                while(digitalRead(WIO_5S_PRESS) == LOW){}
             }  
             if (digitalRead(WIO_KEY_A) == LOW) {
                plmode = 0;
                disp_title();
                Mp3Player.setEqualizer(Eq_e[Eq]);
                Mp3Player.loop(1);
                while(digitalRead(WIO_KEY_A) == LOW){}
             }
             if (digitalRead(WIO_KEY_C) == LOW) {
                menu = 1;
                while(digitalRead(WIO_KEY_C) == LOW){}
             }
             if (digitalRead(WIO_KEY_B) == LOW) {
                Mp3Player.play();
                while(digitalRead(WIO_KEY_B) == LOW){}
             }
            if (digitalRead(WIO_5S_RIGHT) == LOW) {
                Mp3Player.next();
                while(digitalRead(WIO_5S_RIGHT) == LOW){}
            }
            if (digitalRead(WIO_5S_LEFT) == LOW) {
                Mp3Player.previous();
                while(digitalRead(WIO_5S_LEFT) == LOW){}
            }
             if (digitalRead(WIO_5S_UP) == LOW) {
                Mp3Player.volumeUp();
                while(digitalRead(WIO_5S_UP) == LOW){}
            }
            if (digitalRead(WIO_5S_DOWN) == LOW) {
                Mp3Player.volumeDown();
                while(digitalRead(WIO_5S_DOWN) == LOW){}
            }
  }
  else if (menu == 1) {
    p_menu = 0;
    set_play();
    switch (plmode) {
      case 0:   // Loop All
        Mp3Player.setEqualizer(Eq_e[Eq]);
        Mp3Player.loop(1);
        break;
      case 1:   //  Loop Folder
        Mp3Player.setEqualizer(Eq_e[Eq]);
        Mp3Player.loopFolder(idx_folder);
        break;
      case 2:   //  Play index folder (Root)
        Mp3Player.setEqualizer(Eq_e[Eq]);
        Mp3Player.playSongIndex(idx_file);
        break;
      case 3:   //  Play index folder (Root)
        Mp3Player.setEqualizer(Eq_e[Eq]);
        Mp3Player.playSongMP3(idx_file);
        break;
      case 4:   //  Play index folder (Root)
        Mp3Player.setEqualizer(Eq_e[Eq]);
        Mp3Player.playSongSpecify(idx_folder,idx_file);
        break;
    } // end of switch
        
   }  //end of menu 1
    p_menu = menu;
    delay(200);
}//end of loop

Wio TerminalにGPSをつないでみた。(その2)

前回、Wio TerminalにGPSをつないで、とりあえず、緯度、経度、高度を表示しました。
それをいじくってるうちに、もう少し、いろんな機能を試してみたくなってきました。
f:id:manpukukoji:20200703093211j:plain
まず、日付を表示できるようにして、ログをとれるようにし、ポイントをマークできるようにしました。(1か所だけですが…)
ログをとるためには、まず、SDカードが入っていないとだめなので、スイッチを入れた時点でSDカードが入っていないと画面の右下隅に[SD]と表示するようにしました。
そして、WioのAボタン(右端)を押すと、画面右上に、[L] と表示されロギングが始まります。
データは、日付、時間、緯度、経度、高度がカンマ区切りでテキストファイルで保存されます。
また、Bボタン(真ん中)を押すと、画面右上に、[M] と表示され、現在地がメモリに記憶されて、そこまでの距離と方角が2ページ目に表示されるようになっています。

f:id:manpukukoji:20200703094434j:plain
ページ1

Cボタン(左端)を押すとページが切り替わって、2ページ目が表示されます。
2ページ目は、現在の移動のスピードと方位、設定されている記録ポイントの緯度、経度、記録ポイントまでの距離と方角が表示されます。

f:id:manpukukoji:20200703094820j:plain
ページ2

さらに、Cボタンを押すと、3ページ目が表示されます。
3ページ目は、GPS衛星の位置を表示します。

f:id:manpukukoji:20200703095018j:plain
ページ3

とりあえず、いまのところは、ここまで。
まだ、いろいろと追加したい機能があるのだけど、それは、おいおい追加していこうかと思っています。
追加追加でつぎはき拡張していったので、プログラムは、めちゃめちゃ、ぐっちゃぐっちゃしてますが、一応、以下のようになってます。

#include <SoftwareSerial.h>
#include <TinyGPS++.h>
#include "TFT_eSPI.h"
#include <SPI.h>
#include <Seeed_FS.h>
#include "SD/Seeed_SD.h"
 
SoftwareSerial mySerial(2, 3); // RX, TX
TinyGPSPlus gps;
TFT_eSPI tft;

TinyGPSCustom ExtLat(gps, "GPGGA", 3);  //N for Latitude
TinyGPSCustom ExtLng(gps, "GPGGA", 5);  //E for Longitude

const float pi = 3.1415;
int menu = 0, p_menu = 3;
int logging = 0, sat_n = 0;
double dist_LAT = 34.9722899, dist_LONG = 138.3868869;

String p_hour, p_lat, p_lng, p_alt, p_sat, p_date;
String p_my_speed, p_my_course, p_dist_dTo, p_dist_cTo;
String pdist_LAT, pdist_LONG;
String sdist_LAT = String(dist_LAT);
String sdist_LONG = String(dist_LONG);

//for Satellites position  ****************************
static const int MAX_SATELLITES = 40;

TinyGPSCustom totalGPGSVMessages(gps, "GPGSV", 1); // $GPGSV sentence, first element
TinyGPSCustom messageNumber(gps, "GPGSV", 2);      // $GPGSV sentence, second element
TinyGPSCustom satsInView(gps, "GPGSV", 3);         // $GPGSV sentence, third element
TinyGPSCustom satNumber[4]; // to be initialized later
TinyGPSCustom elevation[4];
TinyGPSCustom azimuth[4];
TinyGPSCustom snr[4];

struct
{
  bool active;
  int elevation;
  int azimuth;
  int snr;
  int dsp;
} sats[MAX_SATELLITES];

int Log_f = 0;
String N_date, hour0;
unsigned long p_time;
int N_y, N_m, N_d;
String hr1, min1, sec1;
String F_name;
bool sd;

// ************************

void disp_title(){
  if (menu == 0) {
    tft.fillScreen(TFT_BLACK); 
    tft.setTextSize(3);
    tft.drawString("GPS",120,3);
    tft.setTextSize(2);

    tft.drawString("Date",30,42);
    tft.drawString("Time",30,74);
    tft.drawString("LAT",30,106);
    tft.drawString("LONG",30,138);
    tft.drawString("ALT",30,170);
    tft.drawString("Satellites",30,202);
    if(sd != true) {
        tft.drawChar(295,223,'S',TFT_WHITE, TFT_RED,2);
        tft.drawChar(307,223,'D',TFT_WHITE, TFT_RED,2);     
    }
    p_hour = " "; p_lat = " "; p_lng = " "; p_alt = " "; p_sat = " "; p_date = " ";
    if(Log_f == 1)
        tft.drawChar(295,3,'L',TFT_YELLOW, TFT_BLACK,2);
  }
  else if(menu == 1) {
    tft.fillScreen(TFT_BLACK); 
    tft.setTextSize(3);
    tft.drawString("GPS",120,3);
    tft.setTextSize(2);

    tft.drawString("Speed",30,64);
    tft.drawString("Course",30,88);
    tft.drawString("Destination",30,112);
    tft.drawString("LAT",40,136);
    tft.drawString("LONG",40,160);
    tft.drawString("Distance",40,184);
    tft.drawString("Course",40,208);
    tft.fillRect(130,136,120,16,TFT_BLACK);
    tft.drawString(sdist_LAT,130,136);
    tft.fillRect(130,160,120,16,TFT_BLACK);
    tft.drawString(sdist_LONG,130,160);
    if(sd != true) {
        tft.drawChar(295,223,'S',TFT_WHITE, TFT_RED,2);
        tft.drawChar(307,223,'D',TFT_WHITE, TFT_RED,2);     
    }

    p_my_speed = " "; p_my_course = " "; p_dist_dTo = " "; p_dist_cTo = " ";
    if(Log_f == 1)
        tft.drawChar(295,3,'L',TFT_YELLOW, TFT_BLACK,2);    
  }

  else if(menu == 2) {
    tft.fillScreen(TFT_BLACK); 
    tft.setTextSize(2);

    tft.drawCircle(160,120,120,TFT_WHITE);
    tft.drawCircle(160,120,80,TFT_WHITE);
    tft.drawCircle(160,120,60,TFT_WHITE);
    tft.drawCircle(160,120,40,TFT_WHITE);
    tft.drawLine(160,1,160,239,TFT_WHITE);
    tft.drawLine(40,120,280,120,TFT_WHITE);
    tft.fillCircle(160,120,3,TFT_RED);
    tft.drawChar(153,0,'N',TFT_ORANGE, TFT_BLACK,3);
    tft.drawChar(153,219,'S',TFT_ORANGE, TFT_BLACK,3);
    tft.drawChar(24,111,'W',TFT_ORANGE, TFT_BLACK,3);
    tft.drawChar(280,111,'E',TFT_ORANGE, TFT_BLACK,3);
    if(sd != true) {
        tft.drawChar(295,223,'S',TFT_WHITE, TFT_RED,2);
        tft.drawChar(307,223,'D',TFT_WHITE, TFT_RED,2);     
    }

    for (int i=0; i<MAX_SATELLITES; ++i)
        sats[i-1].dsp = 0;
        if(Log_f == 1)
            tft.drawChar(295,3,'L',TFT_YELLOW, TFT_BLACK,2);
    }

}
//  ===================================================================
void setup() {
  // Open serial communications and wait for port to open:
    Serial.begin(57600);
    mySerial.begin(9600);

    pinMode(WIO_KEY_A, INPUT_PULLUP);
    pinMode(WIO_KEY_B, INPUT_PULLUP);
    pinMode(WIO_KEY_C, INPUT_PULLUP);

    Serial.print("Initializing SD card...");
    if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
      Serial.println("initialization failed!");
      //while (1);
      sd = false;
      tft.drawChar(295,223,'S',TFT_WHITE, TFT_RED,2);
      tft.drawChar(307,223,'D',TFT_WHITE, TFT_RED,2);     
    }
    else {
        Serial.println("initialization done.");
        sd = true;
    }
    tft.begin();
    tft.setRotation(3);

    tft.fillScreen(TFT_BLACK); //Black background    
    tft.setTextColor(TFT_YELLOW);

    //for satellites position
    // Initialize all the uninitialized TinyGPSCustom objects
    for (int i=0; i<4; ++i)
    {
      satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // offsets 4, 8, 12, 16
      elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // offsets 5, 9, 13, 17
      azimuth[i].begin(  gps, "GPGSV", 6 + 4 * i); // offsets 6, 10, 14, 18
      snr[i].begin(      gps, "GPGSV", 7 + 4 * i); // offsets 7, 11, 15, 19
    }
}

//  ============================================================================
void loop() { 
    long N_mjd;
    
    while (mySerial.available() > 0) {
      char c = mySerial.read();
      //Serial.print(c);
      gps.encode(c);
    }

    if (((millis()-p_time) >3000) and (Log_f ==1)) {
      File myFile = SD.open(F_name,FILE_APPEND);
      if(myFile) {
            myFile.print(N_date);
            Serial.print(N_date);
            myFile.print(",");
            Serial.print(" , ");
            myFile.print(hour0);
            Serial.print(hour0);
            myFile.print(",");
            Serial.print(" , ");
            myFile.print(gps.location.lat(),6);
            Serial.print(gps.location.lat(),6);
            myFile.print(",");
            Serial.print(" , ");
            myFile.print(gps.location.lng(),6);
            Serial.print(gps.location.lng(),6);
            myFile.print(",");
            Serial.print(" , ");
            myFile.println(gps.altitude.meters());
            Serial.println(gps.altitude.meters());
            myFile.close();
      }
      p_time = millis();
    }
    
    double lat0 = gps.location.lat();
    double lat1 = (lat0 -int(lat0))*60;
    double lat2 = (lat1 - int(lat1))*60;
    String lat3 = String(int(lat0))+':' + String(int(lat1))+':'+String(lat2) + ' ' + String(ExtLat.value());

    double lng0 = gps.location.lng();
    double lng1 = (lng0 -int(lng0))*60;
    double lng2 = (lng1 - int(lng1))*60;
    String lng3 = String(int(lng0))+':' + String(int(lng1))+':'+String(lng2) + ' ' + String(ExtLng.value());

    int hr = gps.time.hour()+9;
    if (hr>24) hr -= 24;
    String hr0 = '0'+String(hr);
     hr1 = hr0.substring(hr0.length()-2); 
    String min0 = '0'+String(gps.time.minute());
     min1 = min0.substring(min0.length()-2); 
    String sec0 = '0'+String(gps.time.second());
     sec1 = sec0.substring(sec0.length()-2);
           hour0 =  hr1+':'+min1+':'+sec1;

//Calculate date 
    if (gps.date.isUpdated()) {
        int y = gps.date.year();
        int m = gps.date.month();
        int d = gps.date.day();
        if(m<3){y--; m+=12;}
        long mjd = int(365.25*y)+int(y/400)-int(y/100)+int(30.59*(m-2))+d-678912;
        if(gps.time.hour()>14) 
            N_mjd = 58580+(mjd-51412);
        else
            N_mjd = 58579+(mjd-51412);
        long n = N_mjd+678881;
        long a = 4*n+3+3*(4*(n+1)/146097+1);
        long b = 5*((a % 1461)/4)+2;
        N_y = int(a/1461);
        N_m = int(b/153+3);
        N_d = int((b % 153)/5+1);
        if (N_m>12){N_y++; N_m-=12;}
        N_date = String(N_y)+'/'+('0'+String(N_m)).substring(('0'+String(N_m)).length()-2)+'/'+('0'+String(N_d)).substring(('0'+String(N_d)).length()-2);
    }

  if(menu == 0) {   //menu 0: Location -----------------------------
    if(menu != p_menu) {
      disp_title();
      p_menu = menu;
    }
        
    if (N_date != p_date) {    //Date
      tft.fillRect(100,42,120,16,TFT_BLACK);
      tft.drawString(N_date,100,42);
      p_date = N_date;
    }
    
    if (hour0 != p_hour) {    //Time
      tft.fillRect(100,74,120,16,TFT_BLACK);
      tft.drawString(hour0,100,74);
      p_hour = hour0;
    }
    
    if (lat3 != p_lat) {      //Latitude
      tft.fillRect(100,106,132,16,TFT_BLACK);
      tft.drawString(lat3,100,106);
      p_lat = lat3;
    }
    
    if (lng3 != p_lng) {      //Longitude
      tft.fillRect(100,138,156,16,TFT_BLACK);
      tft.drawString(lng3,100,138);
      p_lng = lng3;
    }

    if (String(gps.altitude.meters()) != p_alt) { //Altimeter
      tft.fillRect(100,170,60,16,TFT_BLACK);
      tft.drawString(String(gps.altitude.meters()),100,170);
      p_alt = String(gps.altitude.meters());
    }
    
    if (String(gps.satellites.value()) != p_sat) {  //N of Satellites
      tft.fillRect(160,202,32,16,TFT_BLACK);
      tft.drawString(String(gps.satellites.value()),160,202);
      p_sat = String(gps.satellites.value());
    }
  } // end of menu=0

  else if (menu == 1) {   //menu1: Speed  ---------------------------
      if(menu != p_menu) {
        disp_title();
        p_menu = menu;
      }
      String my_speed = String(gps.speed.mps());
      String my_course = String(gps.course.deg());
      double dist_dTo =
        TinyGPSPlus::distanceBetween(
          gps.location.lat(),
          gps.location.lng(),
          dist_LAT, 
          dist_LONG);
      double dist_cTo =
        TinyGPSPlus::courseTo(
          gps.location.lat(),
          gps.location.lng(),
          dist_LAT, 
          dist_LONG);
      String s_distanceTo = String(dist_dTo);
      String s_courseTo = String(dist_cTo);

      //Display Speed & Course
      if (my_speed != p_my_speed) {  //Speed
        tft.fillRect(130,64,48,16,TFT_BLACK);
        tft.drawString(my_speed,130,64);
        p_my_speed = my_speed;
      }
      
      if (my_course != p_my_course) {  //Course
        tft.fillRect(130,88,72,16,TFT_BLACK);
        tft.drawString(my_course,130,88);
        p_my_course = my_course;
      }
      
      if (sdist_LAT != pdist_LAT) {  //distination latitude
        tft.fillRect(130,136,120,16,TFT_BLACK);
        tft.drawString(sdist_LAT,130,136);
        pdist_LAT = sdist_LAT;
      }
      
      if (sdist_LONG != pdist_LONG) {  //distination longitude
        tft.fillRect(130,160,120,16,TFT_BLACK);
        tft.drawString(sdist_LONG,130,160);
        pdist_LONG = sdist_LONG;
      }
      
      if (s_distanceTo != p_dist_dTo) {  //distance to the mark point
        tft.fillRect(154,184,84,16,TFT_BLACK);
        tft.drawString(s_distanceTo,154,184);
        p_dist_dTo = s_distanceTo;
      }
      
      if (s_courseTo != p_dist_cTo) {  //Course to the mark point
        tft.fillRect(154,208,72,16,TFT_BLACK);
        tft.drawString(s_courseTo,154,208);
        p_dist_cTo = s_courseTo;
      }            
  } //end of menu = 1

  if (menu == 2) {   //menu 2: Satellites  ---------------------------
      if(menu != p_menu) {
        disp_title();
        p_menu = menu;
      }

    if (totalGPGSVMessages.isUpdated())
    {
      for (int i=0; i<4; ++i) {
        int no = atoi(satNumber[i].value());
        if (no >= 1 && no <= MAX_SATELLITES)
        {
          sats[no-1].elevation = atoi(elevation[i].value());
          sats[no-1].azimuth = atoi(azimuth[i].value());
          sats[no-1].snr = atoi(snr[i].value());
          sats[no-1].active = true;
        }
      }

      int totalMessages = atoi(totalGPGSVMessages.value());
      int currentMessage = atoi(messageNumber.value());
      if (totalMessages == currentMessage)
      {
        for (int i=0; i<MAX_SATELLITES; ++i)
        {
          if (sats[i].active)
          {            
            int p_X = 160 + (120*cos((sats[i].elevation/180.0)*pi)) * (sin((sats[i].azimuth)/360.0*2.0*pi));
            int p_Y = 120 - (120*cos((sats[i].elevation/180.0)*pi)) * (cos((sats[i].azimuth)/360.0*2.0*pi));
            tft.fillCircle( p_X, p_Y, 5,TFT_BLUE);
            if (sats[i].dsp == 0) {
                tft.drawString(String(i+1),p_X+3,p_Y+3);
                sats[i].dsp = 1;
            }
          }
        }
               
        for (int i=0; i<MAX_SATELLITES; ++i)
          sats[i].active = false;
      }
    }    
  }   //end of menu = 2
  
//  Button check
  if (digitalRead(WIO_KEY_C) == LOW) {  //  Page(menu) change
      menu++;
      if(menu > 2) menu = 0;
      while(digitalRead(WIO_KEY_C) == LOW){}
  }

  if (digitalRead(WIO_KEY_A) == LOW) {  //  Log to SD card
      if ((Log_f == 0) and (sd == true)) {
          Log_f = 1;
          tft.drawChar(295,3,'L',TFT_YELLOW, TFT_BLACK,2);
          p_time = millis();
          if(N_date !="" and hour0 !="") {
            F_name = String(N_y)+String(N_m)+String(N_d)+"_"+hr1+min1+sec1+".txt";
            Serial.println(F_name);
          }
          
      }
      else if ((Log_f == 1) and (sd == true)){
          Log_f = 0;
          tft.drawChar(295,3,'L',TFT_BLACK, TFT_BLACK,2);
          Serial.println(F_name+" closed");
      }
      while(digitalRead(WIO_KEY_A) == LOW){}
  }
  
  if (digitalRead(WIO_KEY_B) == LOW) {  //  Mark current location
      dist_LAT = gps.location.lat();
      dist_LONG = gps.location.lng();
      tft.drawChar(308,3,'M',TFT_YELLOW, TFT_BLACK,2);
      while(digitalRead(WIO_KEY_B) == LOW){}
  }

}   //  end of loop

Wio Terminal のファイルアクセスがうまくいかん!!

[追記]
この記事を書いたのが、ほぼ、1年前になるけど、どうやら、Seeedのライブラリがアップデートされているようで、新しい、ライブラリでやると、なんとか、問題なく使えるようになっているみたいだ。
新しいライブラリは、以下のリンクで。
github.com

このライブラリをインクルードして、以前の

#include <SPI.h>
#include <Seeed_FS.h>
#include <SD/Seeed_SD.h>

のところを

"#include <Seeed_Arduino_FS.h>"

に、変更してやれば、思うように使えるようになった。

変更前のライブラリでも、公開されている利用例もあるので、この問題が、自分だけだったのか、他の方にも生じていたのかよくわからないけど、一応、自分が、忘れないための記録として残しておくことにします。
なんだか、すっきりしないけど、まぁ、いいか…。
一応、1年前の記事も、以下に置いとくことにします。

*********************************************************

SDカードのファイルのRead / Write がうまくいかない。
Wiki のサンプルをまんまコピーしてもハングしてしまう。
これって、僕だけ?
皆さん、問題なく動いてるのかなぁ…??
コンパイルもアップロードも問題なくて、エラーにも遭遇しないのだけど、ファイルはできてるけど、ファイルにデータは、かけてなくて、読めない状態。
いろいろ試してみると、どうも、File 変数の宣言と実際のファイルオープンを分けるとうまくいかなくて、宣言と同時にオープンすると動くみたい。
でも、そうなると、グローバルでできないので、いろいろ、不便だなぁ~、なんとかならんかなぁ~…。

ちなみに、読み込みだけで見た場合、これは、できる。

#include <SPI.h>
#include <Seeed_FS.h>
#include <SD/Seeed_SD.h>
 
 //File  myFile;
 
void setup() {
  Serial.begin(115200);
  while (!Serial) {
  }
  Serial.print("Initializing SD card...");
  if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");
  
  // re-open the file for reading:
  File myFile = SD.open("test.txt", FILE_READ);      //  <-------ここのとこ
  if (myFile) {
    Serial.println("test.txt:");
 
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}
 
void loop() {
  // nothing happens after setup
}

でも、これはできない。

#include <SPI.h>
#include <Seeed_FS.h>
#include <SD/Seeed_SD.h>
 
 File  myFile;      //  <-------ここのとこ
 
void setup() {
  Serial.begin(115200);
  while (!Serial) {
  }
  Serial.print("Initializing SD card...");
  if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");
  
  // re-open the file for reading:
 myFile = SD.open("test.txt", FILE_READ);    //  <-------ここのとこ
  if (myFile) {
    Serial.println("test.txt:");
 
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}
 
void loop() {
  // nothing happens after setup
}

で、これもできなかった。

#include <SPI.h>
#include <Seeed_FS.h>
#include <SD/Seeed_SD.h>
 
void setup() {
  Serial.begin(115200);
  while (!Serial) {
  }
  Serial.print("Initializing SD card...");
  if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");
  
  // re-open the file for reading:
  File  myFile;               //  <-------ここのとこ
  myFile = SD.open("test.txt", FILE_READ);  //  <-------ここのとこ
  if (myFile) {
    Serial.println("test.txt:");
 
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}
 
void loop() {
  // nothing happens after setup
}

なんでじゃろ????

Wio TerminalにGPSをつないでみた。

Wio TerminalにGPSモジュールをつないでみた。

f:id:manpukukoji:20200626204250j:plain
GPS
GPSモジュールは、秋月で売ってる GMS7-CR6 ってやつ。
ずいぶん前に買って、RasPiとか、なんかいろんなものにつないで遊んでたんだけど、今回、Wioにつないでみたら、なんか、日付がおかしい???
調べてみたら、ロールオーバーとかなんとかで、去年あたりにGPSの時刻情報がリセットされたらしい。
このままじゃ、日付は、そのままじゃちゃんと表示されない。
まぁ、日付は、それほど重要じゃなさそうなのと、計算が面倒なので、このまま、日付なしで行っちゃえ!
WioとGPSモジュールは、シリアル通信で接続される。
ちょっと前に、GROVEのMP3モジュールで遊んでた時に、ここ(https://wiki.seeedstudio.com/Grove-MP3_v2.0/)に、”SAMDは、ソフトシリアル使えないよ。"( The SAMD does not support software serial. )、みたいにことが書いてあったので、ありゃ、そうかいな…?と思ってたんだけど、シリアルモニタでモニタしながらGPSからメッセージとるのにシリアルが2つ必要になっちゃうので、まぁ、物は試しでソフトシリアルで接続してみたら、すんなりいっちゃった。
と、いうわけで、3.3VとGNDとD2ピンの3本でつながった。
f:id:manpukukoji:20200627160311j:plain
シリアル通信でNMEAのメッセージが取れるようになったところで、今度は、メッセージをカンマ区切りで切り分けなければならない。 四苦八苦してデータを切り分けてたんだけど、これを、ちゃちゃっとやっつけてくれるライブラリがあった。
こちら
TinyGPS++ | Arduiniana

データは、取れるようになったので、あとは、表示だけ。
ディスプレイに表示するだけだけど、意外と表示に時間がかかるようで、こっちにあまり時間を取られると、シリアル通信の方がおかしくなる。 間に合わなくなって、うまく、タイミングがとれなくなるのかな?
とりあえず、必要な緯度経度と時刻、あとは、なんちゃって高度と衛星の数を表示させた。
もう少し、アイデア次第でいろいろ遊べそうです。

#include <SoftwareSerial.h>
#include <TinyGPS++.h>
#include"TFT_eSPI.h"

SoftwareSerial mySerial(2, 3); // RX, TX
TinyGPSPlus gps;
TFT_eSPI tft;

TinyGPSCustom ExtLat(gps, "GPGGA", 3);  //N for Latitude
TinyGPSCustom ExtLng(gps, "GPGGA", 5);  //E for Longitude

String p_hour, p_lat, p_lng, p_alt, p_sat;

void setup() {
  // Open serial communications and wait for port to open:
    Serial.begin(57600);
    mySerial.begin(9600);

    tft.begin();
    tft.setRotation(3);

    tft.fillScreen(TFT_BLACK); //Black background    
    tft.setTextColor(TFT_YELLOW);

    tft.setTextSize(3);
    tft.drawString("GPS",120,2);
    tft.setTextSize(2);
    tft.drawString("Time",30,64);
    tft.drawString("LAT",30,96);
    tft.drawString("LONG",30,128);
    tft.drawString("ALT",30,160);
    tft.drawString("Satellites",30,192);

}

void loop() { // run over and over

    while (mySerial.available() > 0) {
      char c = mySerial.read();
      //Serial.print(c);
      gps.encode(c);
    }

    double lat0 = gps.location.lat();
    double lat1 = (lat0 -int(lat0))*60;
    double lat2 = (lat1 - int(lat1))*60;
    String lat3 = String(int(lat0))+':' + String(int(lat1))+':'+String(lat2) + ' ' + String(ExtLat.value());

    double lng0 = gps.location.lng();
    double lng1 = (lng0 -int(lng0))*60;
    double lng2 = (lng1 - int(lng1))*60;
    String lng3 = String(int(lng0))+':' + String(int(lng1))+':'+String(lng2) + ' ' + String(ExtLng.value());

    int hr = gps.time.hour()+9;
    if (hr>24) hr -= 24;
    String hr0 = '0'+String(hr);
    String hr1 = hr0.substring(hr0.length()-2); 
    String min0 = '0'+String(gps.time.minute());
    String min1 = min0.substring(min0.length()-2); 
    String sec0 = '0'+String(gps.time.second());
    String sec1 = sec0.substring(sec0.length()-2);
    String hour0 =  hr1+':'+min1+':'+sec1;

    if (hour0 != p_hour) {    //Time
      tft.fillRect(100,64,120,16,TFT_BLACK);
      tft.drawString(hour0,100,64);
      p_hour = hour0;
    }
    
    if (lat3 != p_lat) {      //Latitude
      tft.fillRect(100,96,132,16,TFT_BLACK);
      tft.drawString(lat3,100,96);
      p_lat = lat3;
    }
    
    if (lng3 != p_lng) {      //Longitude
      tft.fillRect(100,128,156,16,TFT_BLACK);
      tft.drawString(lng3,100,128);
      p_lng = lng3;
    }

    if (String(gps.altitude.meters()) != p_alt) { //Altimeter
      tft.fillRect(100,160,60,16,TFT_BLACK);
      tft.drawString(String(gps.altitude.meters()),100,160);
      p_alt = String(gps.altitude.meters());
    }
    
    if (String(gps.satellites.value()) != p_sat) {  //N of Satellites
      tft.fillRect(160,192,32,16,TFT_BLACK);
      tft.drawString(String(gps.satellites.value()),160,192);
      p_sat = String(gps.satellites.value());
    }

}

Wio Terminalで温度計

Wio TerminalのADCを使って温度計にしてみた。

f:id:manpukukoji:20200625143037j:plain
温度計
使ったセンサは、LM35DZ。
以前に秋月電子で買っていたものだけど、今見たらもう、置いてないみたい。
まぁ、センサは何でも良いのだけど、LM35の場合、使用電圧が4Vからになっていた。
Wioの5Vから引っ張ってきてAD変換してみたら、なんか、45度とかになる???
どうやら、Wioの基準電圧は、電源電圧にしても5Vではなく、3.3Vみたいですね。
3.3Vで計算しなおしたら、それっぽい値になりました。
配線は、電源とGNDとアナログ端子の3本だけ。 電圧は、3.3Vでも5Vでもどっちでも同じ値が出てました。アナログ端子は、A3を使いました。
f:id:manpukukoji:20200627134811p:plain
温度センサ
折れ線グラフのライブラリを用意してくれているので、これを使って、折れ線グラフを表示させて、データーのサンプリングのインターバルを5wayスイッチで1秒から30秒まで変えられるようにしてみた。

#include"TFT_eSPI.h"
#include"seeed_line_chart.h" //include the library

TFT_eSPI tft;

#define LED BCM21

#define max_size 50 //maximum size of data
doubles data; //Initilising a doubles type to store data
TFT_eSprite spr = TFT_eSprite(&tft);  // Sprite 

int interval = 1000;

void blink() {  //LED blink every sampling
  digitalWrite(LED,HIGH);
  delay(50);
  digitalWrite(LED,LOW);
  delay(50);
}

void interval_up() {  //Interval counter increase
    interval += 1000;
    if(interval>30000) interval = 30000;
    tft.fillRect(300,200,50,30,TFT_BLACK);
    tft.setTextColor(TFT_NAVY);          
    tft.setTextSize(2);                   
    tft.drawString(String(interval/1000), 300, 215); 
}

void interval_down() {  //Interval counter decrease
    interval -= 1000;
    if(interval<1000) interval = 1000;  
    tft.fillRect(300,200,50,30,TFT_BLACK);
    tft.setTextColor(TFT_NAVY);          
    tft.setTextSize(2);                   
    tft.drawString(String(interval/1000), 300, 215); 
}

void setup(){
  pinMode(A3, INPUT);
  pinMode(LED,OUTPUT);

  //Interrupt
  pinMode(WIO_5S_UP, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(WIO_5S_UP), interval_up, FALLING);
  pinMode(WIO_5S_DOWN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(WIO_5S_DOWN), interval_down, FALLING);
  
  tft.begin();
  tft.setRotation(3);
  spr.createSprite(TFT_HEIGHT,200);

  tft.fillScreen(TFT_BLACK); 
 
}
 
void loop(){
  
  int value = analogRead(A3); //read analog data on the pin A3
  blink();
  double tmp = value*3.3/1023.0/0.01; //calculate temparature

    spr.fillSprite(TFT_WHITE);
    if (data.size() == max_size) {
        data.pop();//remove the first read variable
    }
    data.push(tmp); //store in data

    auto header =  text(0, 0)
                .value("Temparature")
                .align(center)
                .valign(vcenter)
                .width(tft.width())
                .thickness(2);
 
    header.height(header.font_height() * 2);
    header.draw(); //Header height is the twice the height of the font

   auto content = line_chart(20, header.height()); //(x,y) where the line graph begins
         content
                .height(200 - header.height() * 1.5) //actual height of the line chart
                .width(tft.width() - content.x() * 2) //actual width of the line chart
                .based_on(0.0) //Starting point of y-axis, must be a float
                .show_circle(false) //drawing a cirle at each point, default is on.
                .value(data) //passing through the data to line graph
                .color(TFT_PURPLE) //Setting the color for the line
                .draw();
 
    spr.pushSprite(0, 0);
    
  // display current temarature
  tft.setTextColor(TFT_YELLOW);          
  tft.setTextSize(2);                   
  tft.drawString("Current Temp. : ", 10, 215); 
  tft.fillRect(200,200,100,30,TFT_BLACK);
  tft.setTextSize(3);                   
  tft.drawString(String(tmp), 200, 208); 
  tft.fillRect(300,200,50,30,TFT_BLACK);
  tft.setTextColor(TFT_NAVY);          
  tft.setTextSize(2);                   
  tft.drawString(String(interval/1000), 300, 215); 

    delay(interval);
}

Wio Terminalに実験用の拡張基板を用意してみた。

最近手に入れたWio Terminal 

f:id:manpukukoji:20200624172320j:plain
WioTerminal
なかなか人気のようで、すぐに売り切れになるみたい…。でも、供給もすぐされるようで、”買おうかな?”と思っていると、売り切れになってしまい、”あちゃ、しばらく様子を見るか”と思っていると、またすぐに販売が開始され、あわてて、’ポチッ’と、やると、しばらくして、また、売り切れ・・・。を、繰り返しているもよう。
ディスプレイやボタンも初めから用意されているので、何かと便利に遊べそうなのだけど、I/Oの端子が後ろに出ているので、組み込みには良いのだろうけど、いろいろ、試してみるには、ちょいと、不便。
で、とりあえず、ソケットをそのまま引っ張り出して拡張基板みたいなのを作ってみた。
f:id:manpukukoji:20200624185651j:plain
ただもう、ひたすら、40本のピンヘッダとソケットの間を結線するだけの根気勝負だけ、と、思いきや、しっかり失敗してしまった。
左右のソケット列が逆になっちゃった。 まぁ、やり直しも面倒なのでこのままいくことにしよう。
ついでに、外部電源接続できるようにしておいた。
電源端子ができたので、消費電流を見てみたら、Lチカさせてる状態で、100mAくらい流れてた。 ちなみに、スイッチ切っても0.15mAくらい流れている。
せっかくなので、ボタンとディスプレイを使ったLチカを書いてみた。
f:id:manpukukoji:20200624223032j:plain
ボタンLチカ

#include"TFT_eSPI.h"
TFT_eSPI tft;

#define LED BCM21

 int flg;
void setup(){
  pinMode(LED, OUTPUT);
  pinMode(WIO_KEY_C, INPUT_PULLUP);
  
  tft.begin();
  tft.setRotation(3);
 
  tft.fillScreen(TFT_BLACK); 
}
 
void loop(){ 
     if (digitalRead(WIO_KEY_C) == LOW) {
      if (flg == 0) {flg = 1; }
      else {flg = 0;}
    }

    if (flg == 1) {
      digitalWrite(LED,HIGH);
      tft.fillCircle(160,120,50,TFT_YELLOW);
       delay(1000);
      digitalWrite(LED,LOW);
      tft.fillCircle(160,120,50,TFT_BLACK);
    }
    else {
      digitalWrite(LED,LOW);
      tft.fillCircle(160,120,30,TFT_BLACK);
   }
    delay(1000);
}