STM32およびBluetoothまたはDIYのリモートコントロール

導入する代わりに



良い一日。

今日は、同じ部屋にPCリモートコントロールシステムを構築する試みについてお話します。



自転車を言う人のためにすぐに注意します。 はい、自転車です。 そして、私はそれを構築することに興味がありました。 いくつかの理由があります。 その1つは、購入せずに自分でやりたいという欲求です。



背景



ある日、私の頭は、PCをリモートで制御するというアイデアを思い付きました。つまり、メディアプレーヤーの単純なコマンドに加えて、アプリケーションとシステム全体のステータスの形でフィードバックも取得しました。 このデバイスの最も近いアナログは、スマートフォン用のファッショナブルな電子時計です。 そして、同様の機能に近いものを見つけることができなかったので、私は独学のために、このシステムを自分で組み立てることだけでなく、決定しました。



オプションの1つは、現代の家庭用電化製品のリモートコントロールに馴染みのあるデータ転送方法を使用しました。IRはIRです。 しかし、これはあまり便利ではなく、距離が長いため、問題があります。 実際、すべてがあなたが思うよりも簡単です。私がPDAの「死体」から入手したモジュールは、トランシーバーのように機能するように設計されています。つまり、信号インバーターさえありませんでした。 したがって、デバッグコンソールでUSB <-> UARTコンバーターに直接接続すると、ランダムな文字の途切れないストリームが表示されました。



そのため、慣れ親しんだBTトランシーバーを使用することにしました。 また、テストの1つから残ったESD-200モジュールのビンに膿瘍があります(実際には不快で少し物足りないだけでなく、高価なモジュールに加えて、遠くからパケットが消え始めます)。 退屈しないように、スクリーンをシーメンスM55から取り出しました。 また、任意のコマンドを割り当てることができる6つのボタンのブロックもあります。 STM32F4DISCOVERYデバッグボードを頭脳として選択しました。ボードをデバッグしたりはんだ付けしたりする必要はありません。



一般的なスキーム



注:ここで説明されていることは、主に理論的に存在します。 実際には、これはレイアウトであり、単純に高速化されているため、いくつかのポイントは単純化されています。

Linux(私はGentoo Linuxを持っています。他のディストリビューションキットを入手できます)を経由して、サーバープログラムが実行されるPCの側面から。 彼女は利用可能なデバイスのリストをポーリングし、正しいデバイスを見つけて、彼との接続を確立します。

デバイス側には、接続の状態を制御するトリガーがあります。 接続が検出されると、プライマリポーリングモジュール(pingの一種)を呼び出します。 正常に完了すると、デバイスは対話モードになります。

このモードの特徴は、伝送がシステムの2つのコンポーネントのいずれかを開始することです。 このシステムの動作原理は、2つの理由から選択されました。アクションに対する短い応答時間、追加のタイマーの欠如(PCのデーモンからのTIMEOUTタイマーを除く)、そしてもちろん、比較的単純な交換プロトコルです。 理論上は、デバイスの側面からタイムアウトが必要です。 BTモジュールを使用すると、問題が発生する場合があります。



実装



ここで、上記のスキームに従って何が起こったのかを見てみましょう。







デバイスを検索した後、デバイスに接続し、コマンドの受信を待ちます。 いくつかの要求には応答しますが、他の要求には単に特定のアクションが実行されます。



プロトタイプのセットアップは非常に簡単です。

#!/usr/bin/python2 # -*- coding: utf-8 -*- import serial import dbus import subprocess import time import os #        BT ,             ,       .        ,            . #      ser = serial.Serial('/dev/rfcomm0', 115200, timeout=1) #     DBUS,       Amarok. bus = dbus.SessionBus() am = bus.get_object('org.kde.amarok', '/Player') #           ,        . commands = { 'p': [am.PlayPause, []], '>': [am.Next, []], '<': [am.Prev, []], 'm': [am.Mute, []], '+': [am.VolumeUp, [1,]], '-': [am.VolumeDown, [1,]] } print 'Connected' #        . try: while 1: try: #     ,       ,             . line = ser.read(1) #                 except serial.serialutil.SerialException: #      ,     ,        . ser.close() time.sleep(0.5) while 1: try: #      ser = serial.Serial('/dev/rfcomm0', 115200, timeout=1) break #     , ..         ,    2      . except serial.serialutil.SerialException: time.sleep(2) #      (      , ,        ) , if len(line) == 1: #     ,      if line[0] == 'C': print 'Command' #   2   -     line += ser.read(2) #        2  - ..      3 . if len(line) == 3: print "0x%02x 0x%02x 0x%02x" % (ord(line[0]), ord(line[1]), ord(line[2])) #     ping,          , ..     ""    . if ord(line[1]) == 0x00 and ord(line[2]) == 0x00: print 'Device ping' ser.write('A') ser.write(chr(0x00)) ser.write(chr(0x02)) ser.write(chr(ord('O'))) ser.write(chr(ord('K'))) print 'Ansver to device' #      ,     . if ord(line[1]) == 0x02: #     . mlen = ord(line[2]) message = ser.read(mlen) #              if message in commands: current = commands[message] current[0](*current[1]) #    -    . except KeyboardInterrupt: ser.close() del am print 'Exiting' #      BT . # cleaning cmd = "sudo rfcomm unbind all" runner(cmd)
      
      







プロトコル



プロトコルは非常に単純で、3バイトのヘッダーと最大長255バイトのメッセージで構成されます。メッセージがない場合と同様に、ヘッダーのみです。 ヘッダーは、メッセージのタイプ、受信者アドレス、およびメッセージサイズを示します。 プロトコルのより詳細な記述はこのドキュメントで述べられます 。 もちろん、実際には、説明されているすべてが実装されているわけではありませんが、少なくとも機能します。 交換でのメッセージやその他のエラーの受信にはまだ問題がありますが。



装置



ここに私が手に入れた実際のタイプのデバイスとPC側のモジュールがあります。このデバイスは、システムの最初のバージョンを構築する際の失敗の苦い経験から、プロトタイプとして作成されました。

画像

デバイスは、Cで記述されたファームウェアによって制御され、独自のプロジェクト構造を使用してビルドします。これは、以下のプロジェクトへのリンクをクリックして確認できます。

 /* main work function */ void work(void) { unsigned short i, j; unsigned char mailbox_num = 0; volatile ProtoIOMBox * mbox; /*       ,       - . */ //     . /* check status */ check_status(); //    ""     ,      .                  . /* send ping */ mbox->outbox->header = 'C'; /* Command */ mbox->outbox->size = 0x00; /* 0 for ping request */ mbox->outbox_s = PROTO_IO_MBOX_READY; /* Box ready */ mbox->inbox->size = 64; /* buffer len for control */ mbox->inbox_s = PROTO_IO_MBOX_READY; /* Box ready */ /* wait connection estabilished */ while (status == 0); /* send ping message */ proto_send_msg(mailbox_num); /* wait to send message */ while (mbox->outbox_s <= PROTO_IO_MBOX_SEND); if (mbox->outbox_s == PROTO_IO_MBOX_COMPLETE) LCD_String("Con", 36, 6, 1, WHITE, GLASSY); else LCD_String("Un", 36, 6, 1, RED, GLASSY); /* get ping message */ /* FIXME wtf? this not work or work parity */ //proto_get_msg(mailbox_num); /* wait to get message */ while (mbox->inbox_s <= PROTO_IO_MBOX_SEND); if (mbox->inbox_s == PROTO_IO_MBOX_COMPLETE) { LCD_String("OK", 36 + 3 * 7, 6, 1, GREEN, GLASSY); for (i = 0; i < mbox->inbox->size; i++) LCD_Char(mbox->inbox->message[i], 70 + i * 6, 6, 1, WHITE, GLASSY); } else LCD_String("ERR", 36 + 3 * 7, 6, 1, RED, GLASSY); //               .    ,            . /* infinity loop */ while (1) { if (button_state.state[B_LGHT] == B_CLICK) { sender('+'); button_state.state[B_LGHT] = B_RELEASE; } /*       */ } }
      
      







また、メッセージを送受信するためのモジュール(プロジェクトではproto.oモジュール-proto.cソースコードとproto.hヘッダーファイル)について個別に言及したいと思います。 コードが大きいため、コードは提供しません。 しかし、一般的にどのように機能するかを説明します。

モジュールは完全に中断から動作するように設計されていますが、データ転送は現在正しく実装されていないため、予備の初期化呼び出しが必要です。 メッセージの受信と送信は2つのステートマシンを使用して実行され、バイトが送信されると状態が変化します。 メッセージの有効性とエラー処理を確認しました。



システム全体のビデオも添付します。





あとがき



現在、新しいボード( BeagleBone Black )を入手し、このボードのソフトウェアのファイナライズに取り組んでいるので、プロジェクトが凍結したら、パブリックドメインに置くことにしました。 すべてのソースコードはGitHubアカウントで利用できます

また、可能な限り、プロジェクトに関するご質問にお答えいたします。



All Articles