文字列に複数の変数を印刷するにはどうすればよいですか?


46

端末に出力したい変数がいくつかあるとしたら、それらを文字列で印刷する最も簡単な方法は何ですか?

現在、私は次のようなことをしています:

Serial.print("Var 1:");Serial.println(var1);
Serial.print(" Var 2:");Serial.println(var2);
Serial.print(" Var 3:");Serial.println(var3);

これを行うためのより良い方法はありますか?


アイデア、それがうまくいくかどうかは知りませんが、本のいくつかの変形例である...ここでも、これはArduinoの上でサポートされている場合、私は知らない:stackoverflow.com/questions/804288/...
apnorton

回答:


37

ardprintfprintfシリアル接続でシミュレートする関数です。この関数(下部にあります)は、関数が必要なファイルの先頭に貼り付けることができます。競合は発生しません。

に似たものと呼ぶことができますprintf。この例の動作をご覧ください。

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  int l=2;
  char *j = "test";
  long k = 123456789;
  char s = 'g';
  float f = 2.3;

  ardprintf("test %d %l %c %s %f", l, k, s, j, f);

  delay(5000);

}

予想される出力は次のとおりです。

test 2 123456789 g test 2.30

関数のプロトタイプは次のとおりです。

int ardprintf(char *, ...);

関数呼び出しで検出された引数の数を返します。

これは関数定義です:

#ifndef ARDPRINTF
#define ARDPRINTF
#define ARDBUFFER 16
#include <stdarg.h>
#include <Arduino.h>

int ardprintf(char *str, ...)
{
  int i, count=0, j=0, flag=0;
  char temp[ARDBUFFER+1];
  for(i=0; str[i]!='\0';i++)  if(str[i]=='%')  count++;

  va_list argv;
  va_start(argv, count);
  for(i=0,j=0; str[i]!='\0';i++)
  {
    if(str[i]=='%')
    {
      temp[j] = '\0';
      Serial.print(temp);
      j=0;
      temp[0] = '\0';

      switch(str[++i])
      {
        case 'd': Serial.print(va_arg(argv, int));
                  break;
        case 'l': Serial.print(va_arg(argv, long));
                  break;
        case 'f': Serial.print(va_arg(argv, double));
                  break;
        case 'c': Serial.print((char)va_arg(argv, int));
                  break;
        case 's': Serial.print(va_arg(argv, char *));
                  break;
        default:  ;
      };
    }
    else 
    {
      temp[j] = str[i];
      j = (j+1)%ARDBUFFER;
      if(j==0) 
      {
        temp[ARDBUFFER] = '\0';
        Serial.print(temp);
        temp[0]='\0';
      }
    }
  };
  Serial.println();
  return count + 1;
}
#undef ARDBUFFER
#endif

** %文字を印刷するには、%%。*を使用します


現在、Github gistsで利用可能です。


3
いいアイデアだ。もっとミニマリストになりそうだと思ったので、バッファリングせずにこのバージョンを書き直した。興味のある人は誰でも要点を確認できます:gist.github.com/EleotleCram/eb586037e2976a8d9884
eleotlecram

13

通常、質問に2つの答えを出すことはありませんが今日はこれを見つけただけです。ここでは、バッファーなしでprintfを使用できます。

// Function that printf and related will use to print
int serial_putchar(char c, FILE* f) {
    if (c == '\n') serial_putchar('\r', f);
    return Serial.write(c) == 1? 0 : 1;
}

FILE serial_stdout;

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

    // Set up stdout
    fdev_setup_stream(&serial_stdout, serial_putchar, NULL, _FDEV_SETUP_WRITE);
    stdout = &serial_stdout;

    printf("My favorite number is %6d!\n", 12);
}

void loop() {
  static long counter = 0;
  if (millis()%300==0){
    printf("millis(): %ld\tcounter: %ld (%02X)\n", millis(), counter, counter++);
    delay(1);    
  }
}

これにはまだ浮動小数点の制限があります。

編集:私はこれについて少しテストを行うと思った、それは非常にうまく機能します。書式設定された出力を使用して、より良いテストをループに追加しました。


ああ、それはクールだ。printfはsprintfよりもずっと安全です。書式文字列を無料で提供します。これは素晴らしいことです。クールなトリック。ありがとう。(投票済み)
ダンカンC 14

1つの質問:あなたのserial_putchar関数で、なぜreturnステートメントを作成しreturn !Serial.write(c);ないのですか?ブール値の戻り値の意味を逆にするための三項演算子よりもきれいではありませんか?
ダンカンC 14

それは良い点であり、私はそれが好きです。コードは私のものではなく、見つけたとおりに貼り付けました。
マディバード14

serial_putchar機能をありがとう。それは御treat走になります。:-)あなたは、修正することができ、浮動小数点の制限を
グリーンオンライン

4

これはおそらく良くない、ただ違うだけです。出力にStringオブジェクトを使用できます。これらのオブジェクトは連結を可能にし、自動タイプキャストをサポートします。

Serial.begin(9600);
String label = "Var";
const byte nValues = 3;
int var[nValues] = {36, 72, 49};

for (int i = 0; i < nValues; i++) {
    String stuff = label + i + ": ";
    Serial.println(stuff + var[i]);
}

4
明らかに、メモリの制限に注意することが重要です。1か所で多くの連結やその他の文字列操作を行うと、驚くほどのスペースが使用される可能性があります。
ピーターブルームフィールド

@ PeterR.Bloomfield絶対に本当です!それが、私がこの変種がより良くないと言った理由です;)
クラウス-ディーターワルシャチャ

4

私は通常、タブを使用して、シリアルで物事をうまく並べました。私のように物事を揃えることで、変数の特定の変化に気づきながら、できるだけ速くarduinoを起動できます。

次のようなものを試してください:

Serial.println("Var 1:\tVar 2tVar 3:");
Serial.print("\t");
Serial.print(var1);
Serial.print("\t");
Serial.print(var2);
Serial.print("\t");
Serial.print(var3);
Serial.println();

またはこのようなもの:

Serial.print("Var 1:");Serial.println(var1);
Serial.print("\tVar 2:");Serial.println(var2);
Serial.print("\tVar 3:");Serial.println(var3);

正直なところ、私は同じこと( "\ t"と "\ n")を行い、通常、コードオブジェクトが文字列を大きくすることを避けます。
クラウスディエターワルシャチャ

1
@KlausWarzecha、素敵な列にあるので、変数名を与えることはめったにありません。また、それが簡単にこの構文に一致しないランダムなプリントアウトを見るために作る
Steven10172

4

私はこれをデバッグにのみ使用しますが:

int a = 10;
int b = 20;
Serial.println("a = " + String(a) + " and b = " + String(b));

String $とは何ですか?
ジュラジ

LMFTFM(私に修正させてください)。
linhartr22

2

私はArduinoの世界の初心者ですが、最近はこれが単なるC ++であることがわかりました(例外や多態性はありません)。ただし、テンプレートは引き続き使用できます。したがって、私の解決策は次のテンプレートを使用することです。

void myprint(void)
{
  Serial.println("");
}

template<typename ...Args>
void myprint(const uint64_t & val, Args && ...args)
{
  serialPrintUint64(val);
  myprint(args...);
}

template<typename T, typename ...Args>
void myprint(const T & t, Args && ...args)
{
  Serial.print(t);
  myprint(args...);
}

....

// somewhere in your code
myprint("type: ", results.decode_type, 
        "\t value: ", results.value, 
        "\t addr: ", results.address,
        "\t cmd: ", results.command);

ここでの良いところは、ここで余分なメモリと余分な処理を使用しないことです。


1

私は通常(苦痛なことに)の複数行に固執しますSerial.printが、複雑になったらに戻りsprintfます。使用可能なバッファを用意する必要があるという点で迷惑です。

使い方は次のように簡単(??)です。

char buffer[35]; // you have to be aware of how long your data can be
                 // not forgetting unprintable and null term chars
sprintf(buffer,"var1:%i\tvar2:%i\tvar3:%i",var1,var2,var3);
Serial.println(buffer);

ただし、警告の言葉ですが、(デフォルトでは)浮動小数点型をサポートしていません。


1
sprintfは恐ろしい憎悪です。タイプセーフではなく、バッファのオーバーランなどが簡単です。1960年代のツールです。....それは私があまりにもそれを使用する、と述べたが、それは心の弱い人のためではない
ダンカンC

オーバーランを回避するには、snprintf ...を使用します。ほとんどのModer IDE(Arduino IDEではありません)は、提供された変数タイプに対して文字列形式をチェックし、警告をスローします。
ネクストハック

1

Streaming.h代わりに、を使用

Serial.print("Var 1:");Serial.println(var1);
Serial.print(" Var 2:");Serial.println(var2);
Serial.print(" Var 3:");Serial.println(var3);

書ける

Serial << "Var 1:" << var1) << " Var 2:" << var2 << " Var 3:" << var3 << endl;

<<in Streaming.hin の定義は、それを一連の通常のSerial.print()呼び出しに変換します。つまり、<<コードサイズを増やすことなく実装された構文糖衣です。

Streaming.hインストールしていない場合はStreaming5.ziparduiniana.orgから入手してください。ライブラリディレクトリ(例:)で解凍します~/sketchbook/libraries。ストリーム演算子として#include <Streaming.h>使用するスケッチ内に線を追加します<<

ベース変換指定子_HEX、_DEC、_OCT、および_BINに加えて、_FLOAT関数(小数点以下の桁数)およびが提供されますendl。たとえば、緯度と経度の値を「座標は-23.123、135.4567」のような形式で印刷するには、次のように記述できます。

Serial << "Your coordinates are " << _FLOAT(latitude,3) << ", " << _FLOAT(longitude,4) << endl;

これは次のように書くこともできます

Serial << F("Your coordinates are ") << _FLOAT(latitude,3) << ", " << _FLOAT(longitude,4) << endl;

長い文字列をRAMに持ち込む代わりにPROGMEMに保持します。

注:Streaming.h 文字列は作成されません。<<引数のテキストをストリームに配信するだけです。arduinianaでPStringクラスは、代わりにストリーミングされた出力の文字列が望ましいまたは必要な場合、ストリームの入力から文字列を構築することができます。


1

使用法は、変数のデータ型によって異なります。

彼らがそうであるならばint、それはそうである%dか、%i 彼らがそうであるならばstring、それはそうでしょう%s

printfのラッパー

要件に基づいて制限を変更できます

#include <stdarg.h>
void p(char *fmt, ... ){
    char buf[128]; // resulting string limited to 128 chars
    va_list args;
    va_start (args, fmt );
    vsnprintf(buf, 128, fmt, args);
    va_end (args);
    Serial.print(buf); // Output result to Serial
}

ソース:https : //playground.arduino.cc/Main/Printf

使用例:

p("Var 1:%s\nVar 2:%s\nVar 3:%s\n", var1, var2, var3); // strings
p("Var 1:%d\nVar 2:%d\nVar 3:%d\n", var1, var2, var3); // numbers

ESP8266

Serialフレームワークの組み込みクラス。追加のライブラリや機能は必要ありません。

// strings
Serial.printf("Var 1:%s\nVar 2:%s\nVar 3:%s\n", var1, var2, var3);
// numbers
Serial.printf("Var 1:%d\nVar 2:%d\nVar 3:%d\n", var1, var2, var3);

printf形式のリファレンスページでの書式設定のヒントの詳細:http : //www.cplusplus.com/reference/cstdio/printf/

\n 改行のエスケープシーケンスです。

エスケープシーケンスは、文字列リテラルおよび文字リテラル内の特定の特殊文字を表すために使用されます。

ソース:http : //en.cppreference.com/w/cpp/language/escape

[編集] -@Jurajが述べたように、ほとんどのAVRモジュールでは利用できません。そこで、ESP8266の記述と一般的なAVRモジュールのprintfラッパーを追加しました


本当じゃない。シリアルクラスはありません。printf関数は、Printクラスになりますが、それは最も使用されるAVRパッケージではありません
ユライ

@Jurajはあなたが正しいです、私はそれを持っているESP8266でのみテストしました(リンク)、それはarduinoコアからだと思った。それに応じて私の答えを更新します
レミ

p関数については、可能であれば、もう1つ下票を追加します。
ジュラジュ

これは古い質問であり、2014年に何が利用可能だったのかわからないため、古い回答を判断することはできません。
ジュラジ


-1

http://playground.arduino.cc/Main/Printf 私はこれが私のmega2560上で正常に動作している観測しました

それだけでうまくいったので、vsnprintf_PやPROGMEMは不要です...

#include "Arduino.h"
void local_printf(const char *format, ...)
{
static char line[80];
va_list args;
va_start(args, format);
int len = vsnprintf(line, sizeof(line), format, args);
va_end(args);
for (char *p = &line[0]; *p; p++) {
    if (*p == '\n') {
        Serial.write('\r');
    }
    Serial.write(*p);
}
if (len >= sizeof(line))
    Serial.write('$');
}

void setup()
{
Serial.begin(115200);
local_printf("%s:%d: %s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
}

void loop()
{
static int count=0;
local_printf("%s:%d: %s %d\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, count++);
delay(1*1000);
}

// src/main.c:24: void setup()
// src/main.c:30: void loop() 0
// src/main.c:30: void loop() 1

1
なぜ自分自身を使用printf()するのではなく、なぜこれをやりたいのでしょうか?
エドガーボネット

-3
int Money_amount = 55;

Serial.print(String("New amount: $" + Money_amount));

端末に表示されます:

New amount: $55

1
+演算子を使用してintをc-stringに連結することはできません。
gre_gor
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.