山行記録のための行動ログを取りたい⑥ ブザー・OLED

スポンサーリンク
スポンサーリンク

正常動作してるかの確認機能

こちらの山行時

塔ノ岳・丹沢山・蛭ヶ岳
塔ノ岳・丹沢山・蛭ヶ岳 大倉バス停からいってこいしてきたが、死ぬかと思った。ナイトハイクで2:15ぐらいに出発、5:30塔ノ岳、7:00丹沢山、9:00蛭ヶ岳という感じだったらしい。風びゅんびゅんでクソ寒く、蛭ヶ岳まで行くか迷ったが無事帰っ...

ロガーを持っていったものの、正常にログが取れていなかった。13時間半も歩いたのにすごく悲しかった(実際後で調査したら気づいてもその場で修復できるようなものではなかったが)ので、ログの記録に異常が出た場合には気づけるようにしたい。XIAOの拡張ボードにはブザー、OLED、ボタンがついているので、これでどうにかならんだろか。

スポンサーリンク

ブザー

Seeeduino XIAO Expansion Boardにはもとからブザーがついている。3ピンめがけてコード書けば良い。

勢い余ってサンプル使ってこんなん作った。

ヤマハ「ぷりんと楽譜」
ヤマハぷりんと楽譜の群馬電機公認「呼び込み君」BGM 楽譜配信中!特集一覧ページです。欲しい楽譜を1曲からネットで簡単購入!ピアノ、エレクトーン、ギター、バンドスコア、合唱など定番楽譜をはじめ様々な楽譜を25万点以上取り揃え!コンビニや楽器...

が、実際のところブザーでのお知らせってどうなんだろう。ダメだった時にブザー鳴らすにしても、それがすぐ直せないようなものだったらずっと鳴ってる。それを避けるために何分かに一回とかにしても気づかなかったらその間のログはない。逆にログが取れてるときに間欠的に鳴らすとしてもずーっと聞いてたら気が狂ってきそう。クマ避けになる?wまぁこの方法はやめとこう。

スポンサーリンク

OLED

じゃあOLEDだったら気づくの?と言われるとそういうわけではない。だから音が嫌なんだな俺は。とりあえず公式で提示されているサンプルを

Expansion Board Base for XIAO | Seeed Studio Wiki
Seeed Studio XIAO Expansion board
GitHub - olikraus/U8g2_Arduino: U8glib V2 library for Arduino
U8glib V2 library for Arduino. Contribute to olikraus/U8g2_Arduino development by creating an account on GitHub.

U8g2というライブラリ中のU8x8というライブラリを使用している。

U8x8

U8g2 also includes U8x8 library. Features for U8g2 and U8x8 are:

  • U8g2
    • Includes all graphics procedures (line/box/circle draw).
    • Supports many fonts. (Almost) no restriction on the font height.
    • Requires some memory in the microcontroller to render the display.
  • U8x8
    • Text output only (character) device.
    • Only fonts allowed with fixed size per character (8×8 pixel).
    • Writes directly to the display. No buffer in the microcontroller required.
Home
U8glib library for monochrome displays, version 2 - olikraus/u8g2

8×8ピクセルのフォント(またはその倍数?)しか使えないらしい。ディスプレイの解像度は128×64なので、横は16文字まで入る。そうなると「2023/03/18 23:59:59」が

こうなる。入らない。あと日本語を使おうとしても、フォントが無い(多分)ため表示ができない。ちゅーことでU8g2の方でやってみよう。

U8g2

リファレンスがかなり詳細なので、読み込めば大抵の疑問は解決するのではなかろうか。

u8g2setupcpp
U8glib library for monochrome displays, version 2 - olikraus/u8g2
u8g2reference
U8glib library for monochrome displays, version 2 - olikraus/u8g2
#include <Arduino.h>
#include <SPI.h>
#include <U8g2lib.h>

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, PIN_WIRE_SCL, PIN_WIRE_SDA);

void setup(void) {
  u8g2.begin();
}

void loop(void) {
  u8g2.firstPage();
  do {
    u8g2.setFont(u8g2_font_b10_t_japanese2);
    u8g2.drawUTF8(0, 10, "こんにちは, 世界!");
    u8g2.drawUTF8(0, 20, "2023/03/19 23:59:59");
  } while (u8g2.nextPage());
  
}

とりあえず動いたコード。コンストラクタI2Cでなんで動くのと言うのは置いといて、u8g2_font_b10_t_japanese2は日本語が含まれているフォント。英数の場合はdrawStrで文字を表示したがASCII外の文字(でいいのかな?)を表示する場合は、drawUTF8を使う。結果

正直に言うと、日本語の字形はおかしく感じる。「んち」は途中で切れているし、全体的にバランスが悪い。また句読点「、。」などは収録されていないようだ。また、英数と記号だけでも文字幅のピクセルが違うようで全体的な統一感に欠けるかなという印象だった。ただ他にフォントがなさそう。既存フォントを利用できないかなー?

スポンサーリンク

U8g2で外部フォントを使う

で、以前使ったことがある美咲フォントが使えないかなと思った。

8×8ドット日本語フォント「美咲フォント」

U8g2で外部フォントを使う方法を調べたところFAQに記述があった。

u8g2/doc/faq.txt at master · olikraus/u8g2
U8glib library for monochrome displays, version 2 - olikraus/u8g2

こちらを見ながらやっていく。簡単にいうとbdf形式のフォントデータから変換するらしい。ということでまず美咲フォントのX11 BDF 形式を入手してきておく。そんで

u8g2/tools/font/bdfconv at master · olikraus/u8g2
U8glib library for monochrome displays, version 2 - olikraus/u8g2

からbdfconv.exeを入手(Windowsの場合)。適当な同じフォルダに入れておく。今回はC:\tmp\font内にmisaki_gothic.bdfとbdfconv.exeを配置した。そしたら変換用のコマンドを作る。

u8g2/doc/faq.txt at master · olikraus/u8g2
U8glib library for monochrome displays, version 2 - olikraus/u8g2

ここの説明から、ユニコードのどの部分を変換するかの指定をしなければいけない。その助けとなるのが

u8g2 Unifont helper

こちらに必要事項を入力していくとコマンドを発行してくれる。Character rangesの部分は適当にBasic LatinとCJK Symbols and Punctuation、Hiragana、Katakana、CJK Unified Ideographs、Halfwidth and Fullwidth Formsにした。こうしてできたコマンドがこれ。

bdfconv -v -f 1 -m "0-127,12288-12351,12352-12447,12448-12543,19968-40959,65280-65519" misaki_gothic.bdf -o misaki_gothic.c -n misaki_gothic -d misaki_gothic.bdf

これをコマンドプロンプトで実行してみると

同フォルダ内にbdf.tgaとmisaki_gothic.cが作成された!

bdf.tgaはフォントの一覧みたい(画像は一部をpngに変換)。できたcファイルをArduinoで読み込む。Sketch→Add file…でmisaki_gothic.cを指定すればよい。そしたらコード内で使うように変更してく。

#include <Arduino.h>
#include <SPI.h>
#include <U8g2lib.h>
#include "misaki_gothic.c"

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, PIN_WIRE_SCL, PIN_WIRE_SDA);

void setup(void) {
  u8g2.begin();
}

void loop(void) {
  u8g2.firstPage();
  do {
    u8g2.setFont(misaki_gothic);
    u8g2.drawUTF8(0, 8, "Hello, World!");
    u8g2.drawUTF8(0, 16, "こんにちは、世界!");
    u8g2.drawUTF8(0, 24, "2023/03/19 23:59:59");
  } while (u8g2.nextPage());
  
}

includeで追加したファイル名を指定し、setFontでフォント名を指定する。実行してみると

たんと表示されました~。

寿限無とかも表示できるよ。

スポンサーリンク

U8g2で2値画像を表示する

リファレンス読んでたら図形等も表示できることを知った。他にもXBMという形式の2値画像が表示できるようで、このサイトのfaviconを表示してみたくなった。

画像の準備

もともとの画像がこちらで、これを2値化する。これは適当に手作業で

こんな感じ。ディスプレイが黒背景なため四隅を丸くし浮かないようにした。また背景は透過にするのでなく色を置いた。画像で黒が置かれたところが点灯し白色として表示されるため、ネガポジは反転しないと上手く表示されない。そしたらこの画像をXBMに変換する。変換ツールはいろんなのがあるが、

XBMコンバーター — Convertio
XBMファイルを変換する必要がありますか? 当社のオンラインツールはこれをお手伝い致します! 使いやすく、登録なしで100%安心して使用できます。 Convertio — いかなるファイルのどんな問題も解決する高度なオンラインツール。

とか。今回は検索して最初に出てきたやつにした。変換されたファイルをダウンロードする。XBMの実態はテキストファイルなので、VSCとかで開くと

#define dalomo_width 16
#define dalomo_height 16
static char dalomo_bits[] = {
  0xF8, 0x1D, 0x4C, 0x35, 0x18, 0x34, 0x03, 0xD0, 0x06, 0xE0, 0x06, 0x00, 
  0xC1, 0x43, 0xE3, 0xC7, 0x63, 0xC6, 0x43, 0xC2, 0x00, 0x80, 0x85, 0xA1, 
  0x0F, 0xE0, 0x26, 0x6C, 0xB0, 0x0D, 0xF8, 0x1C, };

みたいな表示になっているはず。変数名が長いので適当に直した。これで画像を変換することができた。

スケッチ

これをそのままスケッチに追加してコピーすると

#include <Arduino.h> 
#include <SPI.h> 
#include <U8g2lib.h>
#include "misaki_gothic.c"

#define dalomo_width 16
#define dalomo_height 16
static unsigned char dalomo_bits[] = {
  0xF8,  0x1D,  0x4C,  0x35,  0x18,  0x34,
  0x03,  0xD0,  0x06,  0xE0,  0x06,  0x00,
  0xC1,  0x43,  0xE3,  0xC7,  0x63,  0xC6,
  0x43,  0xC2,  0x00,  0x80,  0x85,  0xA1,
  0x0F,  0xE0,  0x26,  0x6C,  0xB0,  0x0D,
  0xF8,  0x1C,
};

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, PIN_WIRE_SCL, PIN_WIRE_SDA);

void setup(void) {
  u8g2.begin();
}

void loop(void) {
  u8g2.firstPage();
  do {
    u8g2.setFont(misaki_gothic);
    u8g2.drawUTF8(0, 8, "2023/03/19 23:59:59");
    u8g2.drawUTF8(0, 16, "緯度:35.6825");
    u8g2.drawUTF8(0, 24, "経度:139.752778");
    u8g2.drawUTF8(0, 32, "高度:10 m");
    u8g2.drawUTF8(0, 40, "温度:17 度");
    u8g2.drawUTF8(0, 48, "気圧:1013 hPa");

    u8g2.drawXBM(112, 48, dalomo_width, dalomo_height, dalomo_bits);
  } while (u8g2.nextPage());
}

気をつける点としてdalomo_bitsの型をunsigned charに変更しておく。実行すると

表示された!ちょっとガタついてんな。まぁいっか!そしたら行動ロガーに組み込んでいこう。

スポンサーリンク

行動ロガーのスケッチを更新する

#include <SoftwareSerial.h>
#include "TinyGPS++.h"
#include <Dps310.h>
#include <SPI.h>
#include <SD.h>
#include <U8g2lib.h>
#include "misaki_gothic.c"

#define dalomo_width 16
#define dalomo_height 16
static unsigned char dalomo_bits[] = {
  0xF0, 0x0D, 0x4C, 0x35, 0x18, 0x34, 0x02, 0x50, 0x07, 0xE0, 0x06, 0x00, 
  0xC1, 0x43, 0xE3, 0xC7, 0x63, 0xC6, 0x43, 0xC2, 0x00, 0x80, 0x85, 0xA1, 
  0x0F, 0x60, 0x26, 0x6C, 0xB0, 0x0D, 0xF0, 0x1C, };


TinyGPSPlus gps;
SoftwareSerial mySerial(7, 6);

Dps310 Dps310PressureSensor = Dps310();

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0);

const int chipSelect = 2;
String fileName;
File root;

String dataString;

void setup() {
  Serial.begin(57600);
  mySerial.begin(9600);

  u8g2.begin();
  u8g2.setFont(misaki_gothic);

  Dps310PressureSensor.begin(Wire);

  if (!SD.begin(chipSelect)) {
    while (1)
      ;
  }
  root = SD.open("/");
  checkDupFile(root, 0);
}

void loop() {
  float temperature;
  float pressure;
  uint8_t oversampling = 7;
  int16_t ret;
  String ymd;
  String hms;

  u8g2.firstPage();

  while (mySerial.available() > 0) {
    char c = mySerial.read();
    gps.encode(c);
    if (gps.time.isUpdated() || gps.date.isUpdated() || gps.location.isUpdated()) {  //if (gps.location.isUpdated() && gps.time.isValid()) {//

      ymd += gps.date.year();
      ymd += "/";
      if (gps.date.month() < 10) ymd += "0";
      ymd += gps.date.month();
      ymd += "/";
      if (gps.date.day() < 10) ymd += "0";
      ymd += gps.date.day();
      dataString += ymd + ",";

      if (gps.time.hour() < 10) hms += "0";
      hms += gps.time.hour();
      hms += ":";
      if (gps.time.minute() < 10) hms += "0";
      hms += gps.time.minute();
      hms += ":";
      if (gps.time.second() < 10) hms += "0";
      hms += gps.time.second();
      dataString += hms + ",";

      dataString += String(gps.location.lat(), 7);
      dataString += ",";
      dataString += String(gps.location.lng(), 7);
      dataString += ",";
      dataString += String(gps.altitude.meters());
      dataString += ",";

      ret = Dps310PressureSensor.measureTempOnce(temperature, oversampling);

      if (ret != 0) {
        Serial.print("FAIL! ret = ");
        Serial.println(ret);
      } else {
        dataString += String(temperature);
      }

      dataString += ",";

      ret = Dps310PressureSensor.measurePressureOnce(pressure, oversampling);
      if (ret != 0) {
        Serial.print("FAIL! ret = ");
        Serial.println(ret);
      } else {
        dataString += String(pressure);
      }

      File dataFile = SD.open(fileName, FILE_WRITE);
      if (dataFile) {
        dataFile.println(dataString.c_str());
        dataFile.close();
      }

      do {
        u8g2.drawStr(0, 9, (ymd + " " + hms).c_str());
        u8g2.drawUTF8(0, 18, ("緯度 : " + String(gps.location.lat(), 7)).c_str());
        u8g2.drawUTF8(0, 27, ("経度 : " + String(gps.location.lng(), 7)).c_str());
        u8g2.drawUTF8(0, 36, ("高度 : " + String(gps.altitude.meters()) + " m").c_str());
        u8g2.drawUTF8(0, 45, ("温度 : " + String(temperature)).c_str());
        u8g2.drawUTF8(0, 54, ("気圧 : " + String(pressure) + " Pa").c_str());
        u8g2.drawXBM(112, 48, dalomo_width, dalomo_height, dalomo_bits);
      } while (u8g2.nextPage());

      ymd = "";
      hms = "";
      dataString = "";
    }
  }
}

void checkDupFile(File dir, int num) {
  bool existFile = false;
  while (true) {
    File entry = dir.openNextFile();
    if (!entry) {
      if (num == 0) {
        fileName = "LOG0.CSV";
      }
      break;
    }

    num++;
    fileName = "LOG" + String(num) + ".CSV";
  }
}

ほんとにただ追加しただけ。行間はちょっと開けた。

とりまおっけーい!

コメント

タイトルとURLをコピーしました