「vsprintf()」で数値出力を
Arduinoのスケッチでの数値出力、皆さん「vsprintf()」を使ってられないんですよね。
液晶への表示で使うのは「lcd.print()」。
単純な文字を出すのには良いんですが、細かな数値フォーマットの指定はできません。
となると、「sprintf()」でいったん文字列に直してから、そのバッファを指定してlcd.print。
ちょっと不便です。
私の場合、いつも使っているのが可変長引数を用いた書式付き文字列変換「vsprintf」。
ピピちゃん温度計・ヒータ制御 の制御スケッチを見てもらえば応用例になるかと思いますが、簡単に解説。
/***** 書式付液晶表示 *****/
void lcdprintf(const char *s, ...)
{
va_list vp; // 可変長引数の処理
char bff[40]; // バッファを確保
va_start(vp, s);
vsprintf(bff, s, vp); // ←★
lcd.print(bff); // Serial.print(bff)にするとシリアル出力
va_end(vp);
}
// ★ vsnprintf(bff, sizeof(bff), s, vp); を使うオーバーランしなくなる
変換した文字列を「lcd.print」で液晶へ表示するのは変わりませんが、「printf」のフォーマット指定をそのまま引数に書いて液晶に文字が出せます。
ピピちゃん温度計では、このようにして表示桁数を整えています。
ゼロサプレスするかしないかもおもいのまま。
/***** 温度表示 *****/
// 0 1 2 3 4 5 6 7 横8文字
// 12℃ H25 現在温度と最高温度
// L22 最低温度
void disptemp(void)
{
lcd.setCursor(0, 0); // 1行目
lcdprintf("%2d%c H%2d", tmp_now, CG_DO, tmp_hi);
lcd.setCursor(5, 1); // 2行目
lcdprintf("L%2d", tmp_lo);
}
// CG_DOは℃のキャラクタ指定コード
/***** 時間表示 *****/
// 100分まで、100時間まで表示を区分
// 99m59 / 99h59 / 99d23 (143940分)
void disptime(void)
{
long ct;
lcd.setCursor(0, 1); // 2行目左端
if(chk_time < 100){ // 100分まで : 99m59s
lcdprintf("%2ldm%02d", chk_time, chk_sec);
}
else if(chk_time < 6000){ // 100時間まで : 99h59m
lcdprintf("%2ldh%02ld", chk_time / 60, chk_time % 60);
}
else{
ct = chk_time / 60; // 100時間以上は時にして
if(ct > 2399) ct = 2399; // 100日までに
lcdprintf("%2ldd%02ld", ct / 24, ct % 24); // 99d23h
}
}
ただし、Arduinoでは「%f」(浮動小数点)の変換はできません。
dtostrf()で文字列に変換しなくちゃなりません。
例えば、Arduinoで作るパルスジェネレーターの高精度化ソフト : ラジオペンチさん
から、簡易パルスジェネレータの製作(ソフト編) : アキバ通いと旅さん
・https://blog-imgs-118.fc2.com/v/a/b/vabenecosi/Simplified_PG_v5r3.txt
この中から・・・
void DispFreqD3(long d) { // 周波数を小数点以下2桁xxxx.xxxHzで表示
if (d < 1000000UL) { // 上に空白があればその数だけスペースを埋める
lcd.print(" ");
}
if (d < 100000UL) {
lcd.print(" ");
}
if (d < 10000) {
lcd.print(" ");
}
lcd.print(d / 1000); // 整数部表示
lcd.print(".");
if ( (d % 1000) < 100) { // 小数点以下1桁目がゼロなら
lcd.print("0"); // ゼロを表示
}
if ( (d % 1000) < 10) { // 小数点以下1-2桁目が両方ゼロなら
lcd.print("0"); // 更にゼロを表示
}
lcd.print(d % 1000); // 小数点以下を表示
lcd.print("Hz");
}
この当たりの処理、「printf」系の関数(sprintf,vsprintf)を使えばもっと可読性良く書けるのじゃないかと思うんですが、いかがでしょうか。
============================
※I2Cインターフェース液晶 8文字x2行文字表示ライブラリ
まとめておきました。
・秋月のI2C液晶表示器 ACM0802C-NLW-BBW-IIC 文字表示ライブラリ
「vsnprintf_P」を使った書式指定表示ルーチンをサンプルに入れておきました。
・2021年7月8日:Arduinoの言語環境は「C++」。 vsprintf()で
| 固定リンク
「電子回路工作」カテゴリの記事
- LTC6101を使った電流検出回路をケースに入れる(2023.11.24)
- 今日の失敗:パーツクリーナーで「ポリスチレン」にダメージ(2023.11.10)
- 2つあるワンショットマルチの時定数比を一定にしたい(2023.10.21)
- LEDドライバー、どうしよう(2023.09.04)
- 人感センサー用オフディレータイマー回路(2023.08.26)
コメント
…printf("%4ld.%03ldHz", d / 1000, d % 1000);
でokかな。
投稿: 居酒屋ガレージ店主(JH3DBO) | 2018年2月20日 (火) 09時53分
記事のリスト、全角スペースを使ってますんで、半角に置換のこと。
投稿: 居酒屋ガレージ店主(JH3DBO) | 2018年2月20日 (火) 09時59分
今晩は。
これ以前からの課題で、書式を簡単に指定出来る sprintf()を使うしかないけど、配列に入れるのが面倒だなーということで、つい1文字づつ表示するコードをゴリゴリ書いていました。
これやるとプログラムが無駄に長くなってみっともないんですよね。
そうですか、vsprintf()と言うのがあるとは全く気付きませんでした。次回何かやる時に使ってみます。情報ありがとうございます。
投稿: ラジオペンチ | 2018年2月20日 (火) 18時39分
"C"での魔法の符号「...」。
これの応用なんですが、「vsprintf」だと、関数内部の文字バッファ、文字数超過の危険がありました。(昔々…)
「vsnprintf」の出現で、安心して使えるようになりました。
投稿: 居酒屋ガレージ店主(JH3DBO) | 2018年2月20日 (火) 19時30分
こんばんは。
ラジオペンチさんのブログのコメントをたどって今こちらにたどり着きました。
全く書かれておられるとおりだと思います。
私がこれまで使ったことがあるのはsprintfまでで、vsprintfなどというものがあることは全く知りませんでした。いわんや、可変引数やvsnprintfをや...。
投稿: vabnecosi | 2018年2月22日 (木) 18時21分