正常動作してるかの確認機能
こちらの山行時
ロガーを持っていったものの、正常にログが取れていなかった。13時間半も歩いたのにすごく悲しかった(実際後で調査したら気づいてもその場で修復できるようなものではなかったが)ので、ログの記録に異常が出た場合には気づけるようにしたい。XIAOの拡張ボードにはブザー、OLED、ボタンがついているので、これでどうにかならんだろか。
ブザー
Seeeduino XIAO Expansion Boardにはもとからブザーがついている。3ピンめがけてコード書けば良い。
勢い余ってサンプル使ってこんなん作った。
が、実際のところブザーでのお知らせってどうなんだろう。ダメだった時にブザー鳴らすにしても、それがすぐ直せないようなものだったらずっと鳴ってる。それを避けるために何分かに一回とかにしても気づかなかったらその間のログはない。逆にログが取れてるときに間欠的に鳴らすとしてもずーっと聞いてたら気が狂ってきそう。クマ避けになる?wまぁこの方法はやめとこう。
OLED
じゃあOLEDだったら気づくの?と言われるとそういうわけではない。だから音が嫌なんだな俺は。とりあえず公式で提示されているサンプルを
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.
8×8ピクセルのフォント(またはその倍数?)しか使えないらしい。ディスプレイの解像度は128×64なので、横は16文字まで入る。そうなると「2023/03/18 23:59:59」が
こうなる。入らない。あと日本語を使おうとしても、フォントが無い(多分)ため表示ができない。ちゅーことでU8g2の方でやってみよう。
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で外部フォントを使う
で、以前使ったことがある美咲フォントが使えないかなと思った。
U8g2で外部フォントを使う方法を調べたところFAQに記述があった。
こちらを見ながらやっていく。簡単にいうとbdf形式のフォントデータから変換するらしい。ということでまず美咲フォントのX11 BDF 形式を入手してきておく。そんで
からbdfconv.exeを入手(Windowsの場合)。適当な同じフォルダに入れておく。今回はC:\tmp\font内にmisaki_gothic.bdfとbdfconv.exeを配置した。そしたら変換用のコマンドを作る。
ここの説明から、ユニコードのどの部分を変換するかの指定をしなければいけない。その助けとなるのが
こちらに必要事項を入力していくとコマンドを発行してくれる。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の実態はテキストファイルなので、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";
}
}
ほんとにただ追加しただけ。行間はちょっと開けた。
とりまおっけーい!
コメント