Dynamic LED

7セグメントLEDをマイコンにつないで表示するためには、ドットを含めると8本の制御線が必要になる。一個だけならば良いが、複数個になると個数x8本の制御線が必要になるので、そのままでは不可能。

良く使われているのがダイナミック点灯という方式で、これは7セグメントLEDを一個ずつ順番に点灯させ、高速に切り替えることで全ての桁が点灯しているように見せる、残像を利用した方式。この方式なら制御線は8本+LEDの個数だけで済む。

秋月電子通商で7セグメントLED4個をダイナミック点灯用に並べたモジュールが出ているので、これをRaspberry Piで試してみた。使用したのはカソードコモンの高輝度青色LEDタイプ

LEDのダイナミック点灯プログラムは高速なコンパイラ言語で作るのが普通で、Pythonのようなインタプリタ言語ではチラついたりして上手く行かないのでは無いか?と思ったのだが、結論から言うとそれほど問題なく使うことができた。
ただし他に重い処理が走っていない場合で、負荷が高くなるとチラつきが発生してしまう可能性は十分にある。

Raspberry PiとLEDモジュールの接続は、GPIOの左列でI2C用のポートを除いたGPIO4以降をLEDのセグメントA~Fの信号線、右列のGPIO14以降をLED1~4のセレクト用、GPIO7をDPの信号線として使用した。

7segWire

LEDセレクト用の信号線には47Ωの抵抗を入れてある。

プログラムは以下の通り。ダイナミック点灯処理はスレッドとして実装した。

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

import RPi.GPIO as GPIO
import threading
import time
import socket

ledport = (14, 15, 18, 23)
segport = (4, 17, 27, 22, 10, 9, 11, 7)
digit = ((1, 1, 1, 1, 1, 1, 0),     # 0
         (0, 1, 1, 0, 0, 0, 0),     # 1
         (1, 1, 0, 1, 1, 0, 1),     # 2
         (1, 1, 1, 1, 0, 0, 1),     # 3
         (0, 1, 1, 0, 0, 1, 1),     # 4
         (1, 0, 1, 1, 0, 1, 1),     # 5
         (1, 0, 1, 1, 1, 1, 1),     # 6
         (1, 1, 1, 0, 0, 0, 0),     # 7
         (1, 1, 1, 1, 1, 1, 1),     # 8
         (1, 1, 1, 1, 0, 1, 1),     # 9
         (0, 0, 0, 0, 0, 0, 0),     # Blank
         (0, 0, 0, 0, 0, 0, 1))     # Minus

class DynamicLed:
    def __init__(self):
        for n in range(8):
            GPIO.setup(segport[n], GPIO.OUT)
            GPIO.output(segport[n], False)
        for n in range(4):
            GPIO.setup(ledport[n], GPIO.OUT)
            GPIO.output(ledport[n], True)
    def set_digit(self, no, num):
        dot = num & 0x80
        num = num & 0x7F
        if(no == 0):
            GPIO.output(ledport[3], True)
        else:
            GPIO.output(ledport[no - 1], True)
        for n in range(7):
            GPIO.output(segport[n], digit[num][n])
        GPIO.output(segport[7], dot)
        GPIO.output(ledport[no], False)

rlock = threading.RLock()

class LedThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.ss = DynamicLed()
        self.dig = [0, 0, 0, 0]
        self.running = True
    def run(self):
        while self.running:
            rlock.acquire()
            for n in range(0, 4):
                self.ss.set_digit(n, self.dig[n])
                time.sleep(0.005)
            rlock.release()
    def stop(self):
        self.running = False
    def set(self, a, b, c, d):
        rlock.acquire()
        self.dig[0] = a
        self.dig[1] = b
        self.dig[2] = c
        self.dig[3] = d
        rlock.release()
    def set_num(self, num, width=0, dot=-1):
        str = '{0:>.{width}f}'.format(num, width=width)
        dp = 0;
        pos = 3;
        rlock.acquire()
        for n in range(len(str) - 1, -1, -1):
            if str[n] == '.':
                dp = 0x80
            elif str[n] == '-':
                self.dig[pos] = 11  # MINUS
                pos = pos - 1
            else:
                self.dig[pos] = int(str[n]) | dp
                dp = 0
                pos = pos - 1
            if pos < 0:
                break
        for n in range(0, pos + 1):
            self.dig[n] = 10    # BLANK
        if dot >= 0:
            self.dig[dot] = self.dig[dot] | 0x80
        rlock.release()

if __name__ == "__main__":
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    # get ip address
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.connect(('google.com', 0)) # dummy connect
    ip = sock.getsockname()[0]
    sock.close()
    array = ip.rsplit('.')
    # led thread start
    led = LedThread()
    led.start();
    try:
        while True:
            led.set_num(int(array[0]), dot=3)
            time.sleep(1)
            led.set_num(int(array[1]), dot=3)
            time.sleep(1)
            led.set_num(int(array[2]), dot=3)
            time.sleep(1)
            led.set_num(int(array[3]))
            time.sleep(1)
    except KeyboardInterrupt:
        print '\nbreak'
    led.stop()
    led.join()
    GPIO.cleanup()

LEDに表示させる適当なデータが思い付かなかったので、Raspberry Pi自身のIPアドレスを取得して4回に分けて表示させるようにしてみた。実行すると以下のように表示される。

7segip

数字4桁だけでは表示できるデータ量が少ないので用途が限られるが、バックライト付きLCDよりも消費電力が少なく、離れた場所からでも目立つという利点がある。

桁数がこれ以上多くなるとRaspberry PiのGPIOで直接制御するのは難しくなるが、その場合はデコーダICを使うと、少ない制御線で更に多くのLEDをダイナミック点灯させる事が可能になる。
ただし、その場合はタイミングがまた変わってくるので、注意が必要だ。

広告

TWEEPY(2)

つづき。

Pythonでtwitterを扱うライブラリは何種類か存在しているが、ここでは tweepy を使用する。最初に試してみたのがたまたまこれだったというだけで、特に理由があって選択したわけでは無い。あえて言うならpipで簡単にインストールできたから、というのが理由。

Raspberry Piにpipがインストール済みであれば

$ pip install tweepy

でインストールできる。
pipがインストールされていない場合は、その前に

$ easy_install pip

でインストールしておく。
easy_installもインストールされていない場合は、さらにその前に

$ sudo apt-get install python-setuptools

でインストールしておく必要がある。

tweepyが正常にインストールできたかどうかは、シェルでpythonを起動して「import tweepy」と入力すれば良い。何の警告も出なければ正常。

tweepyを使用して単純にツイートさせるプログラムは、以下のようになる。

#!/usr/bin/env python

import tweepy                                                               
import datetime
import locale

consumer_key = "xxxxxxxxxxxxx"
consumer_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
access_key = "xxxxxxxxxxxxxxxxxxxx"
access_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)                   
auth.set_access_token(access_key, access_secret)                            
api = tweepy.API(auth_handler=auth)                                         

date = datetime.datetime.today()

api.update_status(date.strftime("%Y-%m-%d %H:%M:%S"))    

consumer_key 、consumer_secret 、access_key、access_secretには取得済みのコードをそのまま記述。実行すると以下のように現在時刻がツイートされるはず。

tweet1

これだけでは面白くないので、 少し手を加えてI2C温度センサーからのデータをツイートさせてみる。プログラムは以下の通り。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
  
import smbus
import time
import tweepy                                                               
import datetime
import locale

class i2c:
    def __init__(self, bus, addr):
        self.b = smbus.SMBus(bus)
        self.addr = addr
    def put(self, cmd, data):
        self.b.write_byte_data(self.addr, cmd, data)
    def get(self, cmd):
        return self.b.read_byte_data(self.addr, cmd)
    def getblock(self, cmd, len):
        return self.b.read_i2c_block_data(self.addr, cmd, len)

class tweet:
    def __init__(self):
        self.consumer_key = "xxxxxxxxxxxxxxxxxxxxxxx"
        self.consumer_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        self.access_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        self.access_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    def tweet(self, msg):
        auth = tweepy.OAuthHandler(self.consumer_key, self.consumer_secret)                   
        auth.set_access_token(self.access_key, self.access_secret)                            
        api = tweepy.API(auth_handler = auth)                                         
        api.update_status(msg)
    
if __name__ == '__main__':
    date = datetime.datetime.today()
    tw = tweet()
    dev = i2c(1, 0x48)
    result = dev.getblock(0x00, 12)
    temp = (result[0] << 8 | result[1]) >> 3
    if(temp >= 4096):
        temp -= 8192;
    msg = '{0} current temperature {1:7.2f}'\
        .format(date.strftime("%Y-%m-%d %H:%M:%S"), temp / 16.0)
    tw.tweet(msg)    

実行すると現在の気温付きでツイートされる。室温21.12度。

tweet2

あとは一定時間で自動的にツイートするようにすれば、twitterで何かのデータを監視をするシステムに応用できるはず。ただしtwitterAPIを使用したツイートには回数制限があるため、短時間に大量のツイートを行うようなプログラムは規制に引っかかる可能性がある。

将来的にtwitterAPIの仕様が変更になる可能性もあるので、注意が必要。

TWEEPY

Raspberry Piにセンサ類を接続して取得したデータをtwitterにツイートしたり、タイムラインに反応してツイートする所謂botと呼ばれるものは、既存のライブラリを使用すれば非常に簡単に作ることができる。

ツイートさせるだけならほんの数行でできてしまうのだが、プログラム作成以前に行わなければならない準備が色々と必要で、こちらのほうがプログラムよりもある意味面倒かも知れない。

最初に必要なのはtwitterのアカウントだが、これは有効なメールアドレスがあれば簡単に作成できるので、GMailでメールアカウントを作成してそれを使うのが手っ取り早いだろう。

twitterアカウントが用意できたら、Webブラウザで以下のアドレスにアクセスして作成したtwitterアカウントでログインする。ここではあえてRaspberry Piで行ったが、WindowsでもMacでも何でもかまわない。

https://dev.twitter.com/apps

capture_001_08112013_224003

ログインしたら右上の「Create a new application」をクリック。

capture_002_08112013_224204

アプリケーション名と説明、それとWebアドレスを入力する。Webアドレスはユーザがアプリ連携を認証する時に必要なので、他のユーザと連携しないアプリなら何でもかまわないが、空白にしておく事はできない。

capture_005_08112013_224907

画面をスクロールして規約に対する「Yes, I agree」をチェックしたら、CAPTCHA認証文字列を入力して、「Create youre Twitter application」をクリックする。

capture_004_08112013_224820

アプリのホーム画面でSettingsタブをクリックして、このアプリがユーザに対して行えるアクションを選択する。デフォルトではRead Onlyになっているが、ツイートを行うのでRead and Writeを選択。

capture_006_08112013_225131

画面をスクロールして「Update this Twitter application settings」をクリックする。

capture_007_08112013_225146

アプリのホーム画面でDetailsタブをクリックし、画面をスクロールして下にある「Create my access token」をクリックする。これでこのアプリが自分自身のtwitterアカウントに対して連携を行う場合のコードを生成する事ができる。

capture_011_08112013_234355

twitterアプリが動作するためには、そのアプリ固有のキーと秘密コード、アプリとユーザ間の連携を行う為のトークンと秘密コードの四種類のコードが必要になる。アプリ固有のキーは一つしか無いが、トークンは連携を行うユーザの数だけ必要となる。

作成したキーとトークンはDetailsタブに「Consumer key」「Consumer secret」「Access token」「Access token secret」として表示されているはずなので、このキーをメモ帳等にコピーして保存しておく。これでようやくtwitterアカウント側の準備が整った。

長くなったので、実際のプログラムについては次に書く。

XRDP(3)

Raspberry PiにXRDPサーバをインストールすると、Windowsのリモートデスクトップ接続をクライアントとしてリモートで使う事ができるのだが、そのままだとキーボードが英語キーボードとして認識されてしまうため、日本語キーボードを使用している場合は記号等がキートップとは違う配置になって非常に使いにくい。

ネットで解決策を探すと、この場合は /etc/xrdp フォルダに以下のファイルを作成すれば、日本語キーボードが認識されるようだ。

  • km-0411.ini
  • km-e0010411.ini
  • km-e0200411.ini
  • km-e0210411.ini

内容的には全て同一のファイルなので、以下の手順でネットから km-e0010411.ini ファイルをダウンロードして、パーミッションを変えてからコピーすれば良い。

$ cd /etc/xrdp
$ sudo wget
http://www.mail-archive.com/xrdp-devel@lists.sourceforge.net/msg00263/km-e0010411.ini
$ sudo mv km-e0010411.ini km-0411.ini
$ sudo chmod 644 km-0411.ini
$ sudo ln -s km-0411.ini km-e0010411.ini
$ sudo ln -s km-0411.ini km-e0200411.ini
$ sudo ln -s km-0411.ini km-e0210411.ini

終わったらXRDPサーバを再起動する。

$ sudo service xrdp restart

これで以降のリモート接続からは、キートップ通りの入力が行えるようになるはず。