ArduinoとPC間はUSBデータ通信でデータの転送を行っています。
(詳しくは以下の記事をご参照ください。)
Arduinoからデータを送信する際には『Serial.print()』や『Serial.println()』を使用することが多いと思います。
もちろん文字列を送りたいのならいいのですが、場合によっては複数のデータやオブジェクトを送信したい場合があると思います。
そこで、本記事ではArduinoからWPFアプリへオブジェクトを転送するプログラムを解説していきます。
JSON形式とは
JSON形式とは、データの構造を表すフォーマットです。
データ構造を表すフォーマットと言えば他にも「XML」や「CSV」などがありますね。
そんなJSON形式の特徴を以下に挙げます。
- 可読性が高い
- 階層構造を持つデータをテキストベースで表現可能
- データ量が少なくデータ転送効率が良い
- 多くの開発言語・開発環境でライブラリが存在
特に、オブジェクト指向プログラミングでおなじみの『オブジェクト』を表現するのに長けていますので、複数のデータ型の値を一括りにして取り扱うことができます。
JSON形式の記述方法
JSON形式は以下のルールに従って記述されます。
- オブジェクトは{ }(中かっこ)で囲む
- 『キー : 値』の形で内部データを構成する
- データは,(カンマ)で区切る
- 文字列は“”(ダブルクォーテーション)で囲む
- 配列の場合は[](角かっこ)で囲む
- 配列の各要素は,(カンマ)で区切る
- 使えるデータ型は『文字列』、『数値』、『ブール値』、『null』の4種類
例として、太郎さんを表すオブジェクトと、それをJSON形式で表現したデータを見比べてみましょう。

1 2 3 4 5 6 7 8 9 10 |
{ "name":"Taro", "age":30, "favorite_food":["Orange", "Banana", "Peach"], "wife":{ "name":"Keiko", "age":29, "favorite_food":["Chocolate", "Melon", "Waffle"] } } |
JSON形式での記述の仕方がインデントによってツリー状に見えますので、直感的にもわかりやすいですね。

XMLのようなタグとかもないので可読性が高いというのも
うなずけます
JSON形式のデータを送信するプログラム
JSON形式のデータをArduinoからPCへ送信するプログラムを作ってみました。
ArduinoからTaroさんのオブジェクトを送信し、PC側のアプリで受け取るプログラムです。
Arduino側で一度オブジェクトをJSON形式に変換して、テキストデータとしてPCに転送し、再度PC側のアプリでオブジェクトに戻します。

まずArduino側のプログラムを解説していきます。
なお、本サイトの他の記事と同じくボードは『Arduino Nano Every』を使用しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <Arduino.h> #include <Arduino_JSON.h> void setup(void) { Serial.begin(9600); } void loop(void) { JSONVar human_dic; JSONVar wife_dic; String message; // Wifeオブジェクトを生成する wife_dic[ "name" ] = "Keiko"; wife_dic[ "age" ] = 29; wife_dic[ "favorite_food" ][ 0 ] = "Chocolate"; wife_dic[ "favorite_food" ][ 1 ] = "Melon"; wife_dic[ "favorite_food" ][ 2 ] = "Waffle"; // humanオブジェクトを生成する human_dic[ "name" ] = "Taro"; human_dic[ "age" ] = 30; human_dic[ "favorite_food" ][ 0 ] = "Orange"; human_dic[ "favorite_food" ][ 1 ] = "Banana"; human_dic[ "favorite_food" ][ 2 ] = "Peach"; human_dic[ "wife" ] = wife_dic; message = JSON.stringify( human_dic ); // ディクショナリをJSON形式に変換する Serial.println( message ); // メッセージを送信する delay(1000); // 1000ms待つ } |
WPF(PC)側のプログラムです。
なお、今回もPrismを利用してMVVMで作成しています。MVVMの詳細やPrismのインストール方法はこちらへ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<Window x:Class="SerialCommJSON.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" Title="{Binding Title}" Height="100" Width="525"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="3*" /> <ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" /> </Grid.ColumnDefinitions> <StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="10,0,0,0"> <Label Grid.Column="0" Content="Arduinoシリアル通信テストアプリ(JSON)" FontSize="16" FontWeight="Bold" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center" /> <!-- PortListの内容をItemsSourceにバインディングし、選択中のアイテムはSelectedPortに反映 --> <ComboBox HorizontalAlignment="Left" Width="180" ItemsSource="{Binding PortList}" SelectedItem="{Binding SelectedPort}" /> </StackPanel> <!-- バインディングによって接続ボタンが押下されたらConnectCommandが発行される --> <Button Grid.Column="1" Command="{Binding ConnectCommand}" Content="接続" FontSize="14" FontWeight="Bold" Margin="5" /> <!-- バインディングによって切断ボタンが押下されたらDisonnectCommandが発行される --> <Button Grid.Column="2" Command="{Binding DisconnectCommand}" Content="切断" FontSize="14" FontWeight="Bold" Margin="5" /> </Grid> </Window> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
using Prism.Commands; using Prism.Mvvm; using SerialCommJSON.Models; using System.Collections.ObjectModel; namespace SerialCommJSON.ViewModels { public class MainWindowViewModel : BindableBase { private SerialPortWrapper m_SerialPortWrapper = null; // View側のCommandを受け取るためのDelegateCommand public DelegateCommand ConnectCommand { get; } public DelegateCommand DisconnectCommand { get; } // COMポートリスト public ObservableCollection<string> PortList { get { return m_SerialPortWrapper.PortList; } set { m_SerialPortWrapper.PortList = value; } } // 現在選択されているCOMポート名(文字列) public string SelectedPort { get { return m_SerialPortWrapper.SelectedPort; } set { m_SerialPortWrapper.SelectedPort = value; } } // コンストラクタ public MainWindowViewModel( ) { // Modelのインスタンスを生成 m_SerialPortWrapper = new SerialPortWrapper( ); // Model側でPropertyChangedイベントが発生したら、ViewModelでRaisePropertyChanged()を呼び出す // なお、Model側とViewModelでプロパティ名を一致させたため、イベント発生元のModelでのプロパティ名(e.PropertyName)を // そのままRaisePropertyChanged()に入れられる m_SerialPortWrapper.PropertyChanged += ( sender, e ) => RaisePropertyChanged( e.PropertyName ); // View側でCommandが発行されたら、ViewModelのprivateメソッドで処理する ConnectCommand = new DelegateCommand( Connect_Click ); DisconnectCommand = new DelegateCommand( Disconnect_Click ); } // ConnectCommand発行時の処理 private void Connect_Click( ) { // ModelのConnect()を呼び出す m_SerialPortWrapper.Connect( ); } // DisconnectCommand発行時の処理 private void Disconnect_Click( ) { // ModelのDisconnect()を呼び出す m_SerialPortWrapper.Disconnect( ); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
using Prism.Mvvm; using System; using System.Collections.ObjectModel; using System.IO.Ports; using System.Linq; using System.Text; using System.Text.Json; using System.Windows; namespace SerialCommJSON.Models { public class SerialPortWrapper : BindableBase { private SerialPort m_SerialPort = null; // USBデータ通信で使用するCOMポートのリスト private ObservableCollection<string> m_PortList = new(); public ObservableCollection<string> PortList { get { return m_PortList; } set { SetProperty( ref m_PortList, value ); } } // 現在選択しているCOMポート(文字列) private string m_SelectedPort; public string SelectedPort { get { return m_SelectedPort; } set { SetProperty( ref m_SelectedPort, value ); } } // コンストラクタ public SerialPortWrapper( ) { // シリアルポートの初期設定 m_SerialPort = new SerialPort( ); m_SerialPort.BaudRate = 9600; // 9600bpsに設定 m_SerialPort.DataBits = 8; // データビットは8bits m_SerialPort.Parity = Parity.None; // パリティなし m_SerialPort.Encoding = Encoding.UTF8; // 文字コードはUTF-8 m_SerialPort.WriteTimeout = 5000; // 書き込みタイムアウト:5000ms m_SerialPort.ReadTimeout = 5000; // 読み込みタイムアウト:5000ms // USBデータ通信の受信イベントの処理を記述(処理は別スレッドで行われる) m_SerialPort.DataReceived += ( sender, e ) => { try { Human human; // 受信データを読み出す string message = m_SerialPort.ReadLine(); // 受信データをHumanクラスのオブジェクトに変換する human = JsonSerializer.Deserialize<Human>( message ); } catch ( Exception ex ) // 例外処理 { // もし例外が発生したらメッセージボックスで表示する MessageBox.Show( ex.Message ); } }; // 現在有効なシリアル通信のCOMポートを取得してPortListに追加する foreach ( var port in SerialPort.GetPortNames( ) ) { PortList.Add( port ); } // PortListの最初の項目がComboBoxに表示されるよう設定する SelectedPort = PortList.FirstOrDefault( ); } // 接続コマンドが発行されたときの処理 public void Connect( ) { try { // SelectedPortをUSBデータ通信のポートに設定する m_SerialPort.PortName = SelectedPort; // ポートを開く m_SerialPort.Open( ); } catch ( Exception ex ) // 例外処理 { MessageBox.Show( ex.Message ); } } // 切断コマンドが発行されたときの処理 public void Disconnect( ) { try { // ポートを閉じる m_SerialPort.Close( ); } catch ( Exception ex ) // 例外処理 { MessageBox.Show( ex.Message ); } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
using System.Text.Json.Serialization; namespace SerialCommJSON.Models { // Humanのエンティティクラス public class Human { [JsonPropertyName( "name" )] public string Name { get; set; } [JsonPropertyName( "age" )] public uint Age { get; set; } [JsonPropertyName( "favorite_food" )] public string[ ] FavoriteFood { get; set; } [JsonPropertyName( "wife" )] public Wife Wife { get; set; } } // Wifeのエンティティクラス public class Wife { [JsonPropertyName( "name" )] public string Name { get; set; } [JsonPropertyName( "age" )] public uint Age { get; set; } [JsonPropertyName( "favorite_food" )] public string[ ] FavoriteFood { get; set; } } } |
.NET Frameworkではシリアル通信で使用する「SerialPort」クラスがいつでも使用できる状態にありましたが、後継の.NETではNuGetパッケージマネージャーでパッケージをインストールしないと使えません。
そのため、まずはSerialPortクラスが入っているSystem.IO.Portsパッケージをインストールしてください。
『参照』を押し、検索ボックスに「system.io.ports」と入力します。
検索結果にある『System.IO.Ports』を選択し、右側にある現在のプロジェクト名のチェックボックスにチェックを入れます。
そのうえで『インストール』を押します。
これでSystem.IO.Portsのインストールは完了です。