« このスイッチのメーカーはどこ? 定格は? | トップページ | アンプPM-802ME修理完了 »

2018年2月20日 (火)

「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)を使えばもっと可読性良く書けるのじゃないかと思うんですが、いかがでしょうか。

|

« このスイッチのメーカーはどこ? 定格は? | トップページ | アンプPM-802ME修理完了 »

電子回路工作」カテゴリの記事

コメント

…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分

コメントを書く



(ウェブ上には掲載しません)




トラックバック


この記事へのトラックバック一覧です: 「vsprintf()」で数値出力を:

« このスイッチのメーカーはどこ? 定格は? | トップページ | アンプPM-802ME修理完了 »