カランのブログ

ソフトウェアエンジニア / 台湾人 / 福岡生活

今のモード ライト

本稿はシリーズの3番目の記事です:

  1. センサー紹介 - DHT11とMH-Z14A
  2. データ通信 - UART
  3. Arduinoの落とし穴
  4. (未公開)WiFi編:デバッグ時間を節約するために、ESP32開発ボードを追加購入しました。WiFiとBluetooth機能が組み込まれています。
  5. (未公開)MQTT編:データを他のデバイスに送信するために、軽量で小さな通信プロトコルであるMQTTを使用しました。
  6. (未公開)Grafana / Web編:データをデータベースに保存するので、見栄えの良い方法で表示します。ここでは、Grafana + PromethusおよびSvelteを使用してデータを表示しています。

この記事では、Arduinoやプログラミング言語に不慣れな場合に遭遇するいくつかの落とし穴について説明します。

CPUのデータ読み取り順序の違い(ビッグエンディアンとリトルエンディアン)

CPUの命令セットアーキテクチャによって、データの読み取り順序も異なる場合があります。これはエンディアンネス(バイトオーダ)と呼ばれます。ビッグエンディアンは最上位バイトから読み取る方法であり、リトルエンディアンは最下位バイトから読み取る方法です。以下の図を見ればより明確になるでしょう:

ビッグエンディアンとリトルエンディアンの違い

Arduinoではリトルエンディアンがデータの読み取り順序として採用されています。ここでその回答を見つけることができます。メモリに関しては、上記で説明した以外のデータの読み取り順序の違いもビッグエンディアンとリトルエンディアンで表現することができます。

メモリは管理のために便宜上固定サイズに分割されるため、連続するデータを格納する際に空間が不足する場合は、次の空間にデータを配置します。値を取得するときにそれらを合算します。

この問題が発生したのは、ppmunsigned shortポインターに直接設定したためです。これにより、正しい数値が自動的に計算され、ビットシフトを手動で書く必要がなくなるはずです。以下の例のようになります:

struct Co2Result {
  byte startByte;
  byte command;
  byte high;
  byte low;
  unsigned short ppm;
};
Co2Result *result = malloc(sizeof(Co2Result));
result->startByte = response[0];
result->command = response[1];
result->high = response[2];
result->low = response[3];
result->ppm = &result->high;

shortのサイズは2バイトなので、result->highをポインターに指定すると、2バイト後ろから読み取ることになります。これはresult->high<<8 + result->lowと同等の効果があるはずです。

shortポインターのテスト結果

テストの結果、非常に奇妙な数値が表示されることがわかりました。これはビットの読み取り順序の違いによるものです。ビットシフトを使用すると、正しい数値を取得できます:

result->ppm = (result->high << 8) + result->low;

Cのデータ構造はプラットフォームによって異なる場合がある

例えば、intは2バイトまたは4バイトになる場合がありますし、通常のlongは8バイトまたは4バイトになる場合があります。Javaなどの言語では、データ型が異なるプラットフォームで異なる結果になることはありません。なぜなら、データ型の定義はプログラミング言語自体の実装によるものだからです。したがって、C/C++では、異なるサイズのデータ型をプラットフォームに応じて定義できるuint8_tのような型が使用されます。

Cを書く人にとっては馴染みのあることかもしれませんが、データ構造のサイズを求めるときは、各プラットフォームでデータ型のサイズが固定されているとは思わず、sizeofを使用して計算するべきです。

byteデータ型を活用する

Arduinoでは、byteというデータ型が提供されています。内部的にはunsigned charと同じですが、uint8_tcharintの違いがよくわからないことがあります。ここで明確にします。

  1. byteunsigned charは同じです。どちらも変数に8ビットのメモリを割り当てます。唯一の違いは、unsigned charは0〜255の範囲(unsigned)の数字をより広く感じることができるということです。Arduinoでは、byteを一貫性のために推奨しています。
  2. uint8_tbyteunsigned charは実際にはほぼ同じ概念であり、エイリアスのようなものです。C/C++では、異なるプラットフォームではintを8ビットまたは16ビットと定義する場合があるため、より正確な意味を表現するために通常はuint8_tを使用します。

ヘッダーファイルを使って関数を分割する

Arduinoプロジェクトでは、コードの構造は比較的小さいため、すべての機能をloopsetupに詰め込んでもコードが読みにくくなることはありません。しかし、回路が少し複雑になると、すべての実装を1つのファイルに詰め込むと乱雑になります。

Arduino IDEの拡張子は.inoですが、本質的にはC++であり、C++の構文をサポートしていますが、標準ライブラリは使用できません。モジュールを作成し、Arduinoの関数を使用する場合は、Arduino.hを追加するだけです。

#include "Arduino.h"
#include <string.h>

また、vscodeを使用している場合、Arduinoの組み込みライブラリがincludeできないことに気付くかもしれません。直接インポートするとコンパイルは通るかもしれませんが、エディタ上に赤いラインが表示されます。この場合、.vscode/c_cpp_properties.jsonを追加して、vscodeが正しいヘッダーファイルを見つけるようにします。以下は例です(macOSバージョンの例ですが、LinuxやWindowsではArduinoのインストール場所を探す必要があるかもしれません):

{
  "configurations": [
    {
      "name": "Mac",
      "includePath": [
        "${workspaceFolder}/**",
        "/Applications/Arduino.app/Contents/Java/hardware/**",
        "/Applications/Arduino.app/Contents/Java/hardware/arduino/**"
      ],
      "defines": [],
      "macFrameworkPath": [
        "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
      ],
      "compilerPath": "/usr/bin/clang",
      "cStandard": "c11",
      "cppStandard": "c++17",
      "intelliSenseMode": "clang-x64"
    }
  ],
  "version": 4
}

その他

  • noInterrupet()cli()は同じです。Arduino.hでは次のように定義されています。
#define interrupts() sei()
#define noInterrupts() cli()
  • Arduinoでstd:stringを直接使用すると問題が発生する場合があります。Arduinoでは、stdを使用しない方が良いです。Arduinoでは、C++の機能はすべて使用できません。フォーラムのスレッドを参照してください。Arduinoでstdライブラリを動作させるための取り組みが行われているようですが、進捗状況は不明です。

この実験の結果、Arduinoを使用してより低レベルな知識を学ぶことは良い方法であることがわかりました。組み込みのSerial Portをデバッグに使用でき、USB接続してIDEをインストールすればすぐにコードをアップロードでき、GitHubでバックエンドの実装も見つけることができます。同時に、C/C++の知識を補完する時間を見つけたいと思いました(大学の講義でしか触れたことがありません)。

また、この記事はArduinoについてあまり詳しくないため、情報を補完し、説明を改善するために最善の努力をしましたが、不完全な部分や誤りがあるかもしれません。ご指摘いただければ幸いです。

次の記事

2020年唐豊台理科大学卒業式スピーチノート

前の記事

從咖啡遊牧地圖(Cafe Nomad)看商機

この文章が役に立つと思うなら、下のリンクで応援してくれると大変嬉しいです✨

Buy me a coffee

作者

Kalan 頭像照片,在淡水拍攝,淺藍背景

愷開 | Kalan

Kalan です。台湾出身で、2019年に日本へ転職し、福岡に住んでいます。フロントエンド開発に精通しているだけでなく、IoT、アプリ開発、バックエンド、電子工作などの分野にも挑戦しています。 最近、エレキギターを始めました。ブログを通じて、より多くの人と交流できればと思っています。気軽に絡んでください