PIC18F14K50の他のサンプルを動かしてみる

調子に乗ってPIC18F14K50の他のサンプルを秋月の800円ボードで動かしてみました。

動作環境はLinuxMint13 + PicKit3です。「MPLAB-X」「MPLAB C18ツールチェーン」「Microchip Library for Applications」はすでにインストールされているものとします。(といっても、ダウンロードして、スーパーユーザー権限でスクリプトを走らせるだけでインストールできますが)

まず、サンプルプロジェクトを開いて初期設定を行います。ここではUSBキーボードのサンプルプロジェクトを動かしてみます。

  1. MPLAB-Xを起動します
  2. 「File」→「Open Project」で既存のプロジェクトを開きます。
    ダイアログボックスが開くので、ホームディレクトリの下の
    $HOME/microchip_solutions_v2013-06-15/USB/Device – HID – Keyboard/Firmware/MPLAB.X
    を開きます。
  3. プロジェクトが読み込まれますが、この状態では他のチップ用になっていますので、PIC18F14K50では動きません。
  4. 「File」→「Project Properties(Device – HID – Keyboard)」でプロジェクトの設定を行います。
  5. 左下の方にある「Manage Configurations」をクリックします。
  6. 開いたダイアログから「LPC_USB_Development_Kit_PIC18F14K50」を選択して、右側の「Set Active」ボタンをクリックします。
  7. 「LPC_USB_Development_Kit_PIC18F14K50」の右側に「(active)」が移動したら、「OK」をクリックします。
  8. 左側の「Categories」から「Conf: [LPC_USB_Development_Kit_PIC18F14K50]」の下にある「PICkit3」を選択します。
  9. 右半分がPICkit3関連の設定画面に変わります。
  10. 上の方の「Option categories:」から「Power」を選択します。
  11. 「Power target circuit from PICkit3」にチェックを入れて、「Apply」をクリックします。
    こうしないと、秋月の800円ボードに電源供給されないので書き込みができません。
  12. 「OK」をクリックして、ダイアログを抜けます。

次にビルド&書き込みを行います。

  1. 秋月の800円ボードのUSBコネクタをホストから外します。ICSPの信号線とUSBのD+/D-の信号線は同じ端子なので、USBを抜かないと書き込みできません。つまり、電源も供給されないので、PICkit3から電源供給してやらないとダメなのです。
  2. 次に、一旦PICkit3のUSB端子を外して改めて接続します。外すことでPICkit3からの電源供給を止めてやります。そうしないと、次にボードと接続する際に活線挿抜になってしまいます。運が悪いとボードが壊れるかもしれません。
  3. 秋月の800円ボードのICSP端子をPICkit3に接続します。
  4. IDE上の下向きの緑矢印がついたボタン(マウスカーソルを載せると「Make and Program Device」という表示が出ます)をクリックします。
  5. しばらくまつとコンパイル、書き込み、ベリファイが行われます。
    「Programming/Verify complete」と表示されたら完了です。
  6. PICkit3をボードから引き抜きます。
    この段階でPICkit3側のMiniUSBコネクタも一度引き抜いておいてもいいでしょう。
  7. 適当なエディタを開いて、新しいファイルを作成して、文字入力ができる状態にしておきます。
  8. ボードのUSBコネクタとホストをUSBケーブルで接続します。
  9. エディタに勝手に文字が入力されていきます。
    ここで慌てて他のウインドウを選択するとそちらに入力されていきますので、ご注意を(笑)
    動作を確認したら、USBケーブルを抜きましょう。

本来は評価ボードにはスイッチがついているので、そのスイッチを押すと文字が入力されるようなのですが、800円ボードにはそんなものはありませんので、勝手にキー入力されていってしまいます。

何のキーが入力されるかは、keyboard.c の907行目で HID REPORT にキーのコードに相当するusageを設定しています。usageはUSBの仕様書のうち、HID Usage Tableの53頁からの一覧表に乗っていて、4が「A」のキーに相当します。その後の926行目付近ではキーを話した場合のREPORTを送っています。

この辺を読んでソースを弄っていけば、変なキーボード(CTRL + ALT + DEL専用キーボードとか)も作れるはずです。(^^;

LPS331による気圧計の製作

しばらく前に秋月で安価な高精度気圧センサLPS331のモジュールが出ていたので購入してあったのですが、これを電池駆動の気圧センサとして仕立ててみました。

OLYMPUS DIGITAL CAMERA

おもなキーパーツは、

  • 気圧センサ:LPS331AP
  • 液晶モジュール:AQM0802A
  • マイコン:PIC16F1823
    何でもよかったのですが、手頃なサイズでI2Cをもっているので採用しました。
  • スイッチングレギュレータ:HT7750A
    47uHのインダクタを使って一旦5Vを生成します。
  • CMOS三端子レギュレータ:S-812C33AY-B-G
    5VからPICマイコンおよびLCDモジュール用の3.3V生成に1個、センサの電源用に1個使っています。消費電流を抑えるため、CMOSのレギュレータとしています。

といったところです。

小型軽量かつ長時間駆動を目指すため、単4電池1本からスイッチングレギュレータを使って電源を生成しています。
当初はHT7733Aを使用し、LCフィルタでノイズを低減して使用していたのですが、どうも動作が安定しないのでノイズが原因かと疑って、一旦5Vにしてから3.3VのCMOSリニアレギュレータで電源を生成しています。(根本原因は他にあったので、結局これがどのくらい影響しているかはわかりません)

OLYMPUS DIGITAL CAMERA

センサにはLEDが搭載されていますが、電池での動作時間を伸ばすため、その右上の電流制限抵抗を除去することでLEDへの電流をカットしています。

OLYMPUS DIGITAL CAMERA

背面は試行錯誤の繰り返しで汚くなってしまいました。

ソフトウェアはMPLAB-X+XC8で作成していますが、このLPS331というデバイスは結構くせがあり苦労しました。(XC8にもいろいろ癖があって苦労しました)
当初は自動モードで勝手に連続変換させて、変換終了時に割り込みにてPICに通知、値を読み取って表示させようとしていました。しかしこちらの記事にあるように連続モードで変換すると、変換の処理自体?が抜けていることがあるのです。そこで、約1秒周期でワンショットの変換コマンドを発行することにしたのですが、それでも表示の変動が起こります。「レジスタは初期値に関わらず全部書き込まないとダメ」など、いろいろWebで見かけた情報を取り込みながら、最終的には約2秒周期での変換とすると変換値の暴れが収まるようですので、約2秒周期での変換&表示としています。それでも時折温度については±1℃程度の範囲で暴れることがあるようですが、妥協しました。

OLYMPUS DIGITAL CAMERA

以前作ったArduinoベースの温度・湿度・気圧計との比較です。

気圧についてはLPS331APの方がごくわずか・・・0.2hPa程度ですが、低めに出る傾向があるようです。
温度の方はLPS331APの方が0.7℃程度ですが低めに出ています。
値の安定性については気圧・温度ともにSCP1000の方が明らかに安定しています。

LPS331のほうはドキュメントもよく読むとおかしな所が結構あって(ドキュメントの)出来がよくありません。ドキュメントの出来が悪いデバイスは概してデバイス自体にもいろいろ問題を抱えている場合が多いように経験しています。

#原因切り分け用に買い足したLPS331APがあるのだけど、どうしようかな・・・(^^;

それと消費電流ですが、電池のところで測定すると、センサが動作していないタイミングでは1.8mAくらいで、センサが動作すると4mA程度に上がります。ただ、デジタルテスタで測定しているので、どのくらいが本当のピークなのかはわかりません。センサの動作時間自体は100msにも満たないようなので、平均でも2mA以下くらいなのでしょうか。2mAとすると、単4アルカリ乾電池の容量は700~800mAh程度のようですので、電池1本で350~400時間程度(電圧が下がってくるので、もうちょっと短いか)=約2週間程度の動作時間というところでしょうか。

現状では消費電流を下げて駆動時間を伸ばすためにPICの動作周波数は内蔵オシレータ使用で125kHzとしてあります。さらに消費電流を下げるために表示更新の待ち時間でのPIC自体のスリープも考えたのですが、内部の31kHzのLFINTOSCでスリープ動作させる方法が見つかりませんでした。どうやら外付けの32kHzのクリスタルで動作させないといけないようで、そこまで部品追加するのは面倒なので止めました。

ソースも置いておこうかな・・・と思ったのですが、コメントが古い(試行錯誤する前)ので、とりあえずやめておきます。(コメントを整理したら置くかも・・)

TwitterをUSB LCDモジュールに表示させてみる

製作したUSB LCDモジュールですが、Twitterのtweetを表示させてみました。

Streaming API で指定したキーワードが含まれているTweetを表示させてみます。

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import sys, json, tweepy, re, os

# Account
consumer_key= '        '
consumer_secret= '        '
access_token= '        '
access_token_secret= '        '

class Listener(tweepy.streaming.StreamListener):
    def on_status(self, status):
        message = status.user.name.encode("utf-8") + ":" +  \
                  re.sub(r'\n',r' ',status.text.encode("utf-8"))
        print status.id,":", \
               status.lang,":", \
               status.user.screen_name.encode("utf-8"),":", \
               status.user.name.encode("utf-8"),":", \
               re.sub(r'\n',r' ',status.text.encode("utf-8"))
        os.system('./usblcd.py "'+message+'"')
        return True
 
    def on_error(self, status):
        print status
  
if __name__ == '__main__':
    l = Listener()
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_token, access_token_secret)
 
    stream = tweepy.Stream(auth, l)
    stream.filter(languages=['ja'],track=['tbs','fujitv','ntv','tvasahi','tvtokyo'])

今回はキーワードを’tbs’,’fujitv’,’ntv’,’tvasahi’,’tvtokyo’として、テレビ局に関するTweetを拾ってみます。

sDSC06133

Pythonスクリプトを動かすとこんな感じで表示されました。

どんどん表示される感じがわかるように動画ファイルも上げておきます。

 

USB LCDモジュールの製作

PIC18F14K50を使ってUSBで接続・制御するグラフィックLCDモジュールを作ってみました。

OLYMPUS DIGITAL CAMERA

LCDモジュールは手持ちの秋月の128×64のグラフィック液晶です。

OLYMPUS DIGITAL CAMERA

PIC18F14K50では端子が不足するので、74HC273(Dラッチ)を使って制御信号をラッチしてからLCDモジュールのデータ線に信号を与えています。

OLYMPUS DIGITAL CAMERA

この74HC273はパーツ箱に眠っていたもので、デートコードは8820でした。つまり1988年の第20週ですから25年以上前の1988年5月下旬頃に製造されたものということになります。無事に動くか少し不安ではありましたが・・・。

OLYMPUS DIGITAL CAMERA

裏側はこんな感じでぐちゃぐちゃになってしまいました。本当はLCDは180度回した形で取り付けたかったのですが、このLCD、図面の右側から端子が振ってあって、思いっきり間違えてしまいました。それで急遽LCDの方を回して、コネクタをもう一つつける形にしてしまいました。

ファームウェアは例によって /dev/ttyACM0 としてUSBシリアルとして認識されるようになっていて、液晶モジュールへの制御信号とデータの繰り返しで垂れ流すとそのままLCDモジュールをPICが制御するようになっています。PC側はPythonで記述してあって、一旦GDライブラリを使ってビットマップ上にTrueType(のビットマップフォント)を展開した後、LCD用のコマンド列を生成するようになっています。LCDにはnaga10フォントで半角24文字×6行の表示ができるようになっています。

今度はBeagleBoneBlack + Arduinoが登場?

またHACK A DAYの記事からですが、インテルアーキテクチャのArduinoに続いて、またArduino関連で新しい話がでてきました。

どうも、BeagleBone.orgとArduinoのコラボレーション・・・ということらしいのですが、Arduino Treというのが2014年4月に登場するみたいです。

(写真はArduino.ccからの借用です)

内容はArduino+BeagleBoneBlackのようで見たまんまみたいです。

Arduino側はATmega32u4 16MHz、BeagleBone側はAM3359(Cortex-A8) 1GHz+DDR3 512MBとのこと。

ネットワーク通信と負荷のかかる演算処理はCortex-A8側で、リアルタイム性の要求される制御や現存のArduinoのシールド資産を使った制御はArduino側で、ということでしょうね。

気になるのはお値段です。Arduinoはハードウェアとしては大した回路ではなく、両面基板でも実現容易な部類なのですが、BeagleBoneBlackの方は(電子工作向けとしては)かなりの高スペックな基板・・・確か6層の微小スルーホール仕様だったと思います。BeagleBoneBlackはサイズを徹底的に切り詰めている感じでしたが、こちらではそれを結構大きなサイズで使うのでお値段も安くなさそうな気がします。

あとは、BeagleBoneBlackの方は、もうちょっとOSの方がなんとかならないかと・・・(^^;

インテルアーキテクチャのArduino登場!!

HACK A DAYのこの記事で知ったのですが、インテルがローマのMaker FaireでArduinoとハードウェア(シールド)およびソフトウェアコンパチブルの開発ボードを発表したようです。その名も「ガリレオ(Galileo)」。

(画像は Arduino.cc からの借用です)

本家arduino.ccの記事によると、ボードとしてのハードウェアスペックはこんな感じみたいです。

  • Atomプロセッサよりもさらに小さな(=低コスト、低消費電力)Quarkという400MHzの32bit Pentium相当のCPUコアを搭載。
    Quarkについては9月に行われたIDF(Intel Developper Forum)で発表された組み込み向けのプロセッサの模様(参考記事
  • オンチップの16KBのL1キャッシュ、512KBのSoC内蔵SRAM(いわゆるスクラッチパッドでしょう)
  • Fast Ethernet(10/100M)
  • PCI-Express ミニカードスロット
  • USB2.0 ホストコネクタ
  • USB2.0デバイスコネクタ(Arduinoとしてのプログラミング用)
  • JTAG
  • リセットボタン
  • 8MBのSPIフラッシュメモリ
  • 32GBまでのMicroSDHCコネクタ
  • 11KBのEEPROM

Arduinoとしては、

  • シールドがそのまま使える(細かくはI2Cのスレーブが使えないとかあるみたいですが)
  • アナログ、デジタル(PWM含む)の端子はそのまま使える模様
  • digital 1ピン、2ピンのシリアル(UART)も使える
  • Arduinoの開発環境からボードとして「Intel Galileo」を選択すると、Arduino IDEの開発環境でそのまま使える
  • スケッチがボード上の不揮発メモリに残っていれば電源ONで動作する(Arduinoも同じですね)
  • 電源は5V入力で、ACアダプタの定格としては3Aを推奨。
    一見大食いですが、3.3V、5Vの各ピンの電流として800mAという記載があるので、その分を含んでいるのでしょう。

というところで、Arduinoっぽいものを目指しているのではなく、Arduinoとの高い互換性を狙っているように見えます。

インテルはこのボード(Galileo)を大学向けに5000枚ほどバラ撒くようです。(参考記事

 

・・・・で、感想です。

今ある情報だけではソフトウェアの構成がいまいちわかりませんが、単なるArduinoの領域に400MHzのPentiumはオーバースペック過ぎるように思えます。当然インテルもわかっているでしょうが。ただ、ロボット関連などでは結構な演算性能が求められるはずなので、必要な分野もあるだろう、ということでテストマーケティングに投入する(寄付する)のでしょう。

そして興味深い(期待したい)のは、今後、どの分野に展開していくかです。

今時、ネットワークおよびネットワークに関連する機能(http,smtp,ftp,cifs,…)は様々な機器(お遊び含む)で求められるので、それをお気楽お手軽に実現するための基盤としてLinuxをみんな使う(使いたい)わけです。だから安価で環境の整っているRaspberryPiやBeagleBoneに興味を示す人が多いわけです。

となると、Galileoボードは安価に市販されるのか、Linuxは動作するのか、というところが注目したいところです。Pentium 400MHzクラスであれば、GUIは別にしてLinuxでもBSDでも問題なく載るはずです。そこに膨大なArduinoシールドの資産を使うと、いろんなものが作れる(作る人がいる)のではないか、と思うのは自分だけではないはずです。

BeagleBoneBlackが49ドル、RaspberryPiが35ドルで販売されていることを考えると、60ドル以下、できれば50ドルを割る価格で販売してくれないかな~、と思うんですが、どうでしょう、インテルさん。70ドル以上ではAndroidとRaspberryPiで爆進するARM版Linux・・・このままではいずれ(すでに?)組込み分野で席巻することになるでしょう・・・の勢いは止められないと思いますので。

いずれにせよ、今後に注目です。

PIC18F14K50でI2C液晶を制御

CDCで動くようになった秋月800円PIC18F14K50ボードですが、これに秋月I2C小型液晶(他でも売ってますが・・・)をつけてみました。

まあ、日本語データシートに沿ってI2Cの初期化と、液晶の説明書に沿って液晶の初期化をして、CDCでの入力された文字を(エコーバックに加えて)液晶にも表示させているだけです。

実験風景はこんな感じです。

OLYMPUS DIGITAL CAMERA

OLYMPUS DIGITAL CAMERA

1枚目の写真は全景で、ttyACM0として認識した後、GtkTermで入力した文字が表示された様子です。2枚目はPIC周辺の拡大写真です。
PICkit3でプログラムを書き込んだ後PGC/PGDは抜いておかないと、これらがUSBのD+/D-になっているためUSBで認識できませんので、2本だけ線が外してあります。ブレッドボード上でピロンと2本伸びたジャンパ線がI2Cの信号線です。I2Cバスのプルアップ抵抗(2.7kΩ)は液晶の下にあります。最初は液晶モジュールのピッチ変換基板に載っているのと同じ10kΩを使っていたら動作が安定せず、2時間くらい悩んでしまいました。やっぱり規格はちゃんと調べて値を決めないとダメですね。
縦に実装している小さな基板にはSOT23パッケージの5V→3.3Vレギュレータ(TAR5S33)が見えていない面に載っています。液晶モジュールの電源電圧が3.3Vなので入れていて、この基板の裏側にはチップ部品を載せるパッドがあるのでそこにパスコンを載せています。
ソフトウェアの方ですが、液晶の制御はI2Cで行いますが、シングルマスタモードでの動作前提なので簡単でした。

PIC18F14K50はPIOはもちろんのこと、今回使ったI2Cの他に、ADC、コンパレータ、PWM、タイマ、UARTなどがある上に、Linuxでバッチリ認識できるUSB付きでボードで800円、チップ単体だと150円(SSOP)とか170円(DIPSOP)で手に入ります。そもそもPICというのがプログラミングする上で多少ナニではありますが・・・、周辺コントローラ(Peripheral Interface Controller = PIC)なのでそう割り切ればまあいいでしょう。

ここまでの作業はすべてLinuxMint13上で実施しているのですが、このやり方だとPICおよび周辺回路のプログラミング・デバッグはx86のLinuxでしっかりやっつけてほぼ完成したところでRaspberryPiに持っていく、ということが非常に容易になります。実際に今回作ったものを持っていってみました。

Screenshot-rdesktop

残念ながら、RaspberryPiではGtkTermは動かないようなのですが、上記の通りしっかり認識できています。
削除していない評価ボードのプログラムが悪さしているものの、他のターミナルプログラム(cutecom)でも液晶に表示ができています。

CDCサンプルプログラムにシリアルナンバーをつける

複数の同じUSBデバイスを識別する方法として、シリアルナンバーで区別する方法がありますが、MicrochipのCDCサンプルプログラムでは以下の通り、シリアルナンバーはサポートされていません。

$ sudo lsusb -d 04d8:000a -v

Bus 001 Device 004: ID 04d8:000a Microchip Technology, Inc. CDC RS-232 Emulation Demo
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            2 Communications
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x04d8 Microchip Technology, Inc.
  idProduct          0x000a CDC RS-232 Emulation Demo
  bcdDevice            1.00
  iManufacturer           1 Microchip Technology Inc.
  iProduct                2 CDC RS-232 Emulation Demo
  iSerial                 0             ←ここ
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           67
    bNumInterfaces          2
   (以下略)

そこで、シリアルナンバー対応してみました。

usb_descripters.c の 166行目付近の device_dsc を以下のように修正します。

/* Device Descriptor */
ROM USB_DEVICE_DESCRIPTOR device_dsc=
{
    0x12,                   // Size of this descriptor in bytes
    USB_DESCRIPTOR_DEVICE,  // DEVICE descriptor type
    0x0200,                 // USB Spec Release Number in BCD format
    CDC_DEVICE,             // Class Code
    0x00,                   // Subclass code
    0x00,                   // Protocol code
    USB_EP0_BUFF_SIZE,      // Max packet size for EP0, see usb_config.h
    0x04D8,                 // Vendor ID
    0x000A,                 // Product ID: CDC RS-232 Emulation Demo
    0x0100,                 // Device release number in BCD format
    0x01,                   // Manufacturer string index
    0x02,                   // Product string index
    0x03,                   // Device serial number string index
    0x01                    // Number of possible configurations
};

修正したのは下から2つめの「// Device serial number string index」で、0x00だったのを0x03にして、string indexの3がシリアルナンバーであることを指定します。

さらに同じファイルのもっと後の方にstring descriptorの定義があるので、ここも修正します。

//Language code string descriptor
ROM struct{BYTE bLength;BYTE bDscType;WORD string[1];}sd000={
sizeof(sd000),USB_DESCRIPTOR_STRING,{0x0409}};

//Manufacturer string descriptor
ROM struct{BYTE bLength;BYTE bDscType;WORD string[25];}sd001={
sizeof(sd001),USB_DESCRIPTOR_STRING,
{'M','i','c','r','o','c','h','i','p',' ',
'T','e','c','h','n','o','l','o','g','y',' ','I','n','c','.'
}};

//Product string descriptor
ROM struct{BYTE bLength;BYTE bDscType;WORD string[25];}sd002={
sizeof(sd002),USB_DESCRIPTOR_STRING,
{'C','D','C',' ','R','S','-','2','3','2',' ',
'E','m','u','l','a','t','i','o','n',' ','D','e','m','o'}
};

//Serial string descriptor                  これを追加
ROM struct{BYTE bLength;BYTE bDscType;WORD string[8];}sd003={
sizeof(sd003),USB_DESCRIPTOR_STRING,
{'S','N','0','0','0','0','0','1'}
};

//Array of configuration descriptors
ROM BYTE *ROM USB_CD_Ptr[]=
{
    (ROM BYTE *ROM)&configDescriptor1
};
//Array of string descriptors
ROM BYTE *ROM USB_SD_Ptr[USB_NUM_STRING_DESCRIPTORS]=
{
    (ROM BYTE *ROM)&sd000,
    (ROM BYTE *ROM)&sd001,
    (ROM BYTE *ROM)&sd002,
    (ROM BYTE *ROM)&sd003              // これを追加
};

追加箇所は2箇所、sd003に関するところです。
この修正だけだとUSB_NUM_STRING_DESCRIPTORSの定義が変わらないため、コンパイルでエラーが出ますので、usb_config.h も修正します。144行目に

#define USB_NUM_STRING_DESCRIPTORS 3

というのがあるので、これを

#define USB_NUM_STRING_DESCRIPTORS 4

に修正します。

これでコンパイルしてFlashに書き込んで、USBコネクタに挿入すると、

$ sudo lsusb -d 04d8:000a -v

Bus 001 Device 005: ID 04d8:000a Microchip Technology, Inc. CDC RS-232 Emulation Demo
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            2 Communications
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x04d8 Microchip Technology, Inc.
  idProduct          0x000a CDC RS-232 Emulation Demo
  bcdDevice            1.00
  iManufacturer           1 Microchip Technology Inc.
  iProduct                2 CDC RS-232 Emulation Demo
  iSerial                 3 SN000001
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           67
    bNumInterfaces          2
    bConfigurationValue     1
    :
    :
   以下略

となって、無事にシリアルナンバーがつきました。

PIC18F14K50でUSBシリアル

日経Linuxの9月号にPIC18F14K50をLinuxで使う、という趣旨の記事が載っています。

要はLinux上のMPLABX環境でPIC18F14K50のCDC(Communication Device Class)のサンプルプログラムをコンパイルして、PICkit3で書き込んで、CDCとして認識させて動かす、というものです。このサンプル自体はずーっと昔にWindows上のMPLABでコンパイルして動かしたことがあるのですが、Linuxではうまく動かず放ってありました。

手持ちに秋月のPIC18F14K50の800円ボードもPICkit3もあるし、ちょっとやってみたいこともあるので試してみました。環境は LinuxMint13 32bit です。

大きな流れとしては、

  1. MPLABXをインストール
  2. MPLAB C18ツールチェーンをインストール
    MPLAB C18ツールチェーンは普通はたどり着けないところにあるので、wget で http://www.microchip.com/mplabc18-linux-installer からツールチェーンをもってきてインストール。これまでうまくいかなかったのはこれ。結局ライブラリは C18 でしか動かない、ってことなのですね。
  3. Microchip Library for Applications をインストール
  4. プロジェクトを作成し、サンプルプログラムを開く。
    IDE v8 Project から CDC Basic Demo – C18 – Low Pin Count USB Development Kit.mcp を開く。 開いたら「Power Target circuit from PICkit3」のチェックボックスをONにして、PICkit3から電源供給できるようにする。

というところです。

で、書き込んだ後、ボードをUSBケーブルでホストPC(Mint13)と接続するのですが、接続後に見えるデバイスは「/dev/ttyACM0」ではなく「/dev/ttyUSB0」と「/dev/ttyUSB1」になってしまいます。ロードされるドライバも cdc_acm ではなく、ftdi_sioがロードされてしまい、正しく動作しません。

この原因は ftdi_sio ドライバにUSBデバイスID「04d8:000a」が記述されていて、そちらがロードされてしまうためのようです。とりあえず回避するには、

$ sudo rmmod ftdi_sio

として、ftdi_sio ドライバを外してやり、その後でUSBケーブルを差し直すと正しく /dev/ttyACM0 として見えて、ちゃんと動作するようになります。恒久的には ftdi_sio ドライバを /etc/modprobe.d の下の blacklist に入れてやればおそらく解決するのでしょうが、当然本物のFTDIチップが動かなくなります。

なんでこんなことになっているかというと、この件のパッチが投げられている
https://patchwork.kernel.org/patch/1464661/
によると、FTDIチップをエミュレーションするファームウェアでこのMicrochipの評価ボードのベンダID/デバイスIDをそのまま使っているハードウェアベンダがいるため・・・・ということのようです。(うー、勘弁して・・・・)

ドライバに直接手を入れれば修正できるようなのですが、ちょっと面倒臭いですね・・・。何かいい方法(ベンダID/デバイスIDを指定して blacklist するなど)があればいいのですが・・・。

で、何とかならないかと、試しに /etc/udev/rules.d/10-cdcacm.rules として以下の内容のファイルを作ります。

SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", GROUP="adm", MODE="0666", SYMLINK+="cdc_acm"

udevをリスタートしてルールを読み込ませます。

$ sudo service udev restart

試しに、

$ sudo modprobe ftdi_sio

として、先にrmmod してあった ftdi_sio を読み込ませて、その後で、USBを抜き差ししてみます。
dmesgの最後を見てみると、

[ 5431.756641] udevd[4801]: starting version 175
[ 5479.158712] USB Serial support registered for FTDI USB Serial Device
[ 5479.159765] usbcore: registered new interface driver ftdi_sio
[ 5479.159768] ftdi_sio: v1.6.0:USB FTDI Serial Converters Driver
[ 5490.096591] usb 1-1.3: USB disconnect, device number 10
[ 5492.337738] usb 1-1.3: new full-speed USB device number 11 using ehci_hcd
[ 5492.434666] cdc_acm 1-1.3:1.0: This device cannot do calls on its own. It is not a modem.
[ 5492.434688] cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device

となっていて、とりあえず ttyACM0 として認識しているようです。

しかし再起動してみると・・・・ftdi_sioとして認識してしまう・・・orz

どうやらどちらのドライバが先に認識するか・・・だけみたい。/etc/udev/rules.d/10-cdcacm.rules ではコントロールできなさそうなことはわかりました・・・。

やっぱり ftdi_sioをblacklistして、rules.dでFTDIドライバを読ませるとかすればいいのでしょうかね・・・?

<追伸>

ちなみに、バージョン3.12-rcや3.2.51のカーネルソースでは、上記パッチが適用されてるっぽいです。一方で、

$ uname -a
Linux G530-2-Mint 3.2.0-23-generic #36-Ubuntu SMP Tue Apr 10 20:41:14 UTC 2012 i686 i686 i386 GNU/Linux

ん~?カーネルのバージョンを上げれば解決するのだろうか??
Synapticでカーネルパッケージを入れてみました。

$ uname -a
Linux G530-2-Mint 3.2.0-53-generic-pae #81-Ubuntu SMP Thu Aug 22 21:23:47 UTC 2013 i686 i686 i386 GNU/Linux

・・・・治りました。ここまでの苦労は何だったんでしょう(^^;
やっぱり、アップデートはちゃんとやっとかないとダメですね・・・。

TwitterのストリーミングAPIを試してみた

Twitterからリアルタイムに情報を得るStreaming APIを試してみました。

$ sudo easy_install pip
$ sudo pip install tweepy --upgrade

として、パッケージ管理ツール pip をインストールした後、tweepy をアップグレードしました。その後、キーワードを含む tweet を入手するスクリプトをいろんなところを参考にしながらなんとか作りました。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import json
import tweepy  
import re

# Account 
consumer_key= '  '
consumer_secret= ' '
access_token= ' '
access_token_secret= ' '

class Listener(tweepy.streaming.StreamListener):
    def on_data(self, data):
        if data.startswith("{"):
            tweet = json.loads(data)
            if 'text' in tweet:
                user = tweet['user']
                text = re.sub(r'\n',r' ',tweet['text'].encode('utf-8')) # 改行コード除去
                print tweet['id'],":",tweet['lang'],":",user['screen_name'].encode('utf-8'),":", \
                              user['name'].encode('utf-8'),":", text
        return True

    def on_error(self, status):
        print status

if __name__ == '__main__':
    l = Listener()
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_token, access_token_secret)

    stream = tweepy.Stream(auth, l)
#    filterの宣言部分 @ /usr/local/lib/python2.7/dist-packages/tweepy/streaming.py
#    def filter(self, follow=None, track=None, async=False, locations=None, 
#        count = None, stall_warnings=False, languages=None):
#    パラメータの使い方は https://dev.twitter.com/docs/streaming-apis/parameters
    stream.filter(languages=['ja'],track=['AKB', 'HKT', 'NMB'])

15~27行目がデータ受信時の処理で、データを分析してユーザー名やscreeen_name、tweetの内容などを表示します。29行目からが認証で、34行目でstreaming apiを呼び出しています。
39行目でフィルタの内容を設定していて、ここでは日本語のTweetで、「AKB,HKT,NMB」を含むものをキーワードとしています。キーワードは日本語のようなスペース区切りではない言語では正常に動作しないようです。

同じ処理は、on_data ではなく、 on_statusを使うとJSONの解析もTweepy側でやってくれるようで、on_status版も上げておきます。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import json
import tweepy  
import re

# Account
consumer_key= ' '
consumer_secret= ' '
access_token= ' '
access_token_secret= ' '

class Listener(tweepy.streaming.StreamListener):
    def on_status(self, status):
        print status.id,":", \
               status.lang,":", \
               status.user.screen_name.encode("utf-8"),":", \
               status.user.name.encode("utf-8"),":", \
               re.sub(r'\n',r' ',status.text.encode("utf-8"))
        return True

    def on_error(self, status):
        print status

if __name__ == '__main__':
    l = Listener()
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_token, access_token_secret)

    stream = tweepy.Stream(auth, l)
#    filterの宣言部分 @ /usr/local/lib/python2.7/dist-packages/tweepy/streaming.py
#    def filter(self, follow=None, track=None, async=False, locations=None, 
#        count = None, stall_warnings=False, languages=None):
#    パラメータの使い方は https://dev.twitter.com/docs/streaming-apis/parameters
    stream.filter(languages=['ja'],track=['AKB', 'HKT', 'NMB'])

取得できるデータは、あるtweetを参考に分析してみたところ、

{u'contributors': None,
 u'truncated': False,
 u'text': u'\u301017\u6642....00#NMB #HKT48\u3000#HKT',
 u'in_reply_to_status_id': None,
 u'id': 3744....11520,
 u'favorite_count': 0,
 u'source': u'<a href="http://twittbot.net/" rel="nofollow">twittbot.net</a>',
 u'retweeted': False,
 u'coordinates': None,
 u'entities': {u'symbols': [],
               u'user_mentions': [],
               u'hashtags': [{u'indices': [68, 74], u'text': u'AKB48'},
                             {u'indices': [75, 79], u'text': u'AKB'},
                             {u'indices': [80, 86], u'text': u'SKE48'},
                             {u'indices': [87, 91], u'text': u'SKE'},
                             {u'indices': [92, 98], u'text': u'NMB48'},
                             {u'indices': [99, 103], u'text': u'NMB'},
                             {u'indices': [104, 110], u'text': u'HKT48'},
                             {u'indices': [111, 115], u'text': u'HKT'}],
               u'urls': [{u'url': u'http://t.co/f4....w6kL', u'indices': [8, 30], u'expanded_url': u'http://com.nicovideo.jp/community/co....411', u'display_url': u'com.nicovideo.jp/community/co....\u2026'}]},
 u'in_reply_to_screen_name': None,
 u'id_str': u'3744....1520',
 u'retweet_count': 0,
 u'in_reply_to_user_id': None,
 u'favorited': False,
 u'user': {u'follow_request_sent': None,
           u'profile_use_background_image': True,
           u'default_profile_image': True,
           u'id': 63.....87,
           u'verified': False,
           u'profile_image_url_https': u'https://.......png',
           u'profile_sidebar_fill_color': u'DDEEF6',
           u'profile_text_color': u'333333',
           u'followers_count': 1,
           u'profile_sidebar_border_color': u'C0DEED',
           u'id_str': u'63.....87',
           u'profile_background_color': u'C0DEED',
           u'listed_count': 0,
           u'profile_background_image_url_https': u'https://......png',
           u'utc_offset': 32400,
           u'statuses_count': 25632,
           u'description': None,
           u'friends_count': 1,
           u'location': u'',
           u'profile_link_color': u'0084B4',
           u'profile_image_url': u'http://......png',
           u'following': None,
           u'geo_enabled': False,
           u'profile_background_image_url': u'http://......png',
           u'name': u'\u308a\u3063..........\uff09',
           u'lang': u'ja',
           u'profile_background_tile': False,
           u'favourites_count': 0,
           u'screen_name': u'KK....ty',
           u'notifications': None,
           u'url': None,
           u'created_at': u'Sun Jul 15 18:..:.. +0000 2012',
           u'contributors_enabled': False,
           u'time_zone': u'Osaka',
           u'protected': False,
           u'default_profile': True,
           u'is_translator': False},
 u'geo': None,
 u'in_reply_to_user_id_str': None,
 u'possibly_sensitive': False,
 u'lang': u'ja',
 u'created_at': u'Mon Sep 02 08:..:.. +0000 2013',
 u'filter_level': u'medium',
 u'in_reply_to_status_id_str': None,
 u'place': None}

というような感じで、ユーザー名などは入れ子になったデータ構造になっていますので、on_data版ではソースの20行目のように一旦’user’データを取り出しています。