質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.52%

  • C

    4370questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • Arduino

    660questions

    Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

シリアル通信のデータの先頭を把握したい ヘッダのうまい使い方

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,515

bobby2128

score 34

前提・実現したいこと

Arduinoを複数用いて、シリアル通信をしています。ArduinoNANO側でRTCモジュールから時間情報を取得し、それをArduinoM0Proへ送信しています。

現状では、電源を二台同時に付けると、時間情報が正確に伝わるのですが(シリアルモニタ上で確認済み)、タイミングがずれると、以下に示すようにほしい形に正確にデータが通信できません。

原因としては、現状のコードではシリアル通信の順番に構わず、来た順番にデータをアウトプットしてしまうので、これに順番を守らせるコードを付け足したいのですが、いいアイデアが浮かびません。

便利な手法やアイデアなどございましたら、ぜひご教授お願いいたします。

※プログラムは長さの関係で本質問と関係性の低い箇所は省略させていただいております。

発生している問題・エラーメッセージ


タイミングが正しいとき

2017.03.17,Tue
4:44:07 @ 26.43 °C


タイミングが正しくないとき

02.16.44,
2643:2017:03 @ 00.07 °C

該当のソースコード

送信側

void loop(){

  DateTime now=RTC.now();   // aktuelle Zeit abrufen 
  show_time_and_date(now);  // Datum und Uhrzeit ausgeben

  sensors.requestTemperatures();                 // Temperatursensor auslesen
  show_temperature(sensors.getTempCByIndex(0));  // Temperatur ausgeben

  delay(1000); // 30 Sekunden warten bis zur nächsten Ausgabe
}

// Wochentag ermitteln
String get_day_of_week(uint8_t dow){ 

  String dows="  ";
  switch(dow){
   case 0: dows="Sun"; break;
   case 1: dows="Mon"; break;
   case 2: dows="Tue"; break;
   case 3: dows="Wed"; break;
   case 4: dows="Thu"; break;
   case 5: dows="Fri"; break;
   case 6: dows="Sat"; break;
  }
  return dows;
}

// Datum und Uhrzeit ausgeben
void show_time_and_date(DateTime datetime){

///////////////////////////////////////////////
  int byou=datetime.second();
  int fun=datetime.minute();
  int zikan=datetime.hour();
  int hi=datetime.day();
  int tuki=datetime.month();
  int nen=datetime.year();
  int youbi=datetime.dayOfWeek();

  sendNenData(nen);
  sendTukiData(tuki);
  sendHiData(hi);
  sendYoubiData(youbi);
  sendZikanData(zikan);
  sendFunData(fun);
  sendByouData(byou);
}

void sendNenData(int value){
  Serial.write('H'); // ヘッダの送信
  Serial.write(lowByte(value)); // 下位バイトの送信
  Serial.write(highByte(value)); // 上位バイトの送信
}
void sendTukiData(int value){
  Serial.write('H'); // ヘッダの送信
  Serial.write(lowByte(value)); // 下位バイトの送信
  Serial.write(highByte(value)); // 上位バイトの送信
}
void sendHiData(int value){
  Serial.write('H'); // ヘッダの送信
  Serial.write(lowByte(value)); // 下位バイトの送信
  Serial.write(highByte(value)); // 上位バイトの送信
}
void sendYoubiData(int value){
  Serial.write('H'); // ヘッダの送信
  Serial.write(lowByte(value)); // 下位バイトの送信
  Serial.write(highByte(value)); // 上位バイトの送信
}
void sendZikanData(int value){
  Serial.write('H'); // ヘッダの送信
  Serial.write(lowByte(value)); // 下位バイトの送信
  Serial.write(highByte(value)); // 上位バイトの送信
}
void sendFunData(int value){
  Serial.write('H'); // ヘッダの送信
  Serial.write(lowByte(value)); // 下位バイトの送信
  Serial.write(highByte(value)); // 上位バイトの送信
}
void sendByouData(int value){
  Serial.write('H'); // ヘッダの送信
  Serial.write(lowByte(value)); // 下位バイトの送信
  Serial.write(highByte(value)); // 上位バイトの送信
}

受信側

void loop() {
// 受信バッファに3バイト(ヘッダ+int)以上のデータが着ているか確認
 if ( Serial2.available() >= sizeof('H') + sizeof(int) ) {
// ヘッダの確認
  if( Serial2.read() == 'H'){
   int low = Serial2.read(); // 下位バイトの読み取り
   int high = Serial2.read(); // 上位バイトの読み取り
   recv_data = makeWord(high,low); // 上位バイトと下位バイトを合体させてint型データを復元

   if( recv_data <10 && counter!=3)Serial.print(0);
   if(counter!=3){
     Serial.print(recv_data);
     }
   }  
   counter++;
//Serial.print(counter);
   switch(counter){
     case 1: Serial.print("."); break;
     case 2: Serial.print("."); break;
     case 3: Serial.print(", "); break;
     case 4: 
            if(recv_data==0)Serial.print("Sun");
            if(recv_data==1)Serial.print("Mon");
            if(recv_data==2)Serial.print("Tue");
            if(recv_data==3)Serial.print("Wed");
            if(recv_data==4)Serial.print("Thu");
            if(recv_data==5)Serial.print("Fri");
            if(recv_data==6)Serial.print("Sat");
               Serial.println(""); 
               break;
     case 5: Serial.print(":"); break;
     case 6: Serial.print(":"); break;
     case 7: Serial.println(""); break;
     }


   if(counter==7){
   counter=0;
   }
 }

補足情報(言語/FW/ツール等のバージョンなど)

Arduino IDE 1.8.0
Arduino M0 Pro
Arduino NANO

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

こんにちは。

質問の内容が書かれていないので、ソースは呼んでいません。
タイトルから「シリアル通信のデータの先頭をどうやって判別すればよいのか?」だろうと推測して回答してみます。外してたらごめんなさい。

実はこの問題は結構難しいです。恐らくざっくり2種類しかないと思います。

  1. タイムアウト
    パケットは必ず連続して送信し、一定時間以上データを受信できない時に内部カウンタをクリアします。クリア後に届いた最初のデータが先頭です。
    シリアル通信では、何かエラーが発生した場合に備えてタイムアウトの実装がほぼ必須ですから新たにロジックを追加しなければならないケースは少ないです。

  2. 特殊コード
    ペイロード内に記録しないコードを決めます。バケット先頭にそのようなコードの一つを割り当てます。
    例えば、よく使われるベーシック手順はこの方式です。
    この方式はタイムアウトに頼らない点で優れています。ペイロードに使えない値を伝送できるようにするため、それなりの工夫(エスケープするなど)で高速化することもできます。


【追記】
回答を書いている間に質問の内容が書かれてました。フライングしてしまった。すいません。


【質問を読み直して】
ソースもざっとみました。バイナリー・データを送っているので、先に書いた「2. 特殊コード」方式を使うのは難しいですね。かと言って「1. タイムアウト」もタイマの制御が必要になるのでやはり難しいてす。

バイナリー・データを送らないように改造するのが、恐らく簡単です。
今はint型を2バイトでそのまま送っていますが、例えば4ビットづつを8ビットへ拡張して4バイトで送ることが考えられます。4ビットの値が10未満ならオフセット0x30を加え、10以上ならオフセット0x41を加えるとASCIIコードでの16進数表現になります。そして、それを受信側でデコードすることがかのうです。
このような工夫をして送受信することで、例えばベーシック手順のSYNコードなどをペイロード中に発生しないようにします。

そして、送信側はパケット先頭にSYNコードを付加して送り、受信側はSYNコードを受信できるまでデータを1バイトづつ破棄することが考えられます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/03/07 19:20

    Chironian様
    こんにちはご回答いただきまして誠にありがとうございます。
    「今はint型を2バイトでそのまま送っていますが、例えば4ビットづつを8ビットへ拡張して4バイトで送ることが考えられます。4ビットの値が10未満ならオフセット0x30を加え、10以上ならオフセット0x41を加えるとASCIIコードでの16進数表現になります。そして、それを受信側でデコードすることがかのうです。 」この箇所ができたら理想なのですが、勉強不足のため、何かサンプルコードやURLをご提供いただけますと幸甚に存じます。

    お手数おかけいたしますが、どうかよろしくお願いいたします、

    キャンセル

  • 2017/03/07 19:41 編集

    答えを求めるのではなく、何が分からないか具体的に質問して下さい。

    この部分は中身を深く理解しないとデバッグできませんので、部分的な答えを教えても完成しないと思います。
    また、私の手元には開発環境がないですし、仮にあったとしても結構手間がかかるデバッグですので、私の方でデバッグした完全な答えをお教えすることもできません。

    キャンセル

  • 2017/03/08 11:50

    Chironian様
    こんにちはコメントいただきありがとうございました。
    あれから自身でいろいろ試行錯誤した結果、下記の二つのコンセプトで実行した結果、送信と受信がずれた状態からでもそれを修正し表示するようにうまく動作いたしました。

    ・送信側のデータ送信の最後にdelay関数にて一定時間の間を設ける。
    ・間があった直後に来たデータは先頭であることを受信側で判断する。

    はじめたばかりでうまくいかないことばかりでしたが、アイデアをいただきありがとうございました。

    キャンセル

  • 2017/03/08 13:31

    おお、時間制御方式でうまくいったのですね。それは良かったです。

    キャンセル

同じタグがついた質問を見る

  • C

    4370questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • Arduino

    660questions

    Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。