M.C.P.C. (Mamesibori Creation Plus Communication)

印刷屋から五反田のWeb屋に転職したCLのブログです。

AliExpressの0.96インチ有機ELディスプレイをArduino Pro Mini(3.3V)で駆動

有機ELディスプレイは「画素が自ら発光」するので、画面に表示していないときは液晶ディスプレイのような表示面へのバックライト漏れが発生せず、夜間に情報を表示させる用途などでは特に有用です。今回は、有機ELディスプレイArduino Pro Mini(3.3V)で制御してみます。

まず、輸入します。AliExpressで$6ぐらいです。有機ELディスプレイOLEDと表記しているようです。接続するときの線数がなるべく少ない方がいいので、信号線2線+電源2線の合計4線で制御できる、I2C接続の表示器を探します。AliExpressでの検索キーワードは、「arduino oled i2c」などで。

f:id:C_L:20140822005941j:plain

次に、ブレッドボードに配線を行います。

f:id:C_L:20140822010003j:plain

電源ラインをオーバーレイ表示すると以下の通りです。

f:id:C_L:20140822010005j:plain

パーツを配置します。配置しているArduino Pro Miniは、3.3V版であることに注意! 5V版を使う場合は、ほかに3.3V電圧を作り出す部品と、信号線の電圧をずらすレベルシフタという部品を配置せねばなりません。

Arduino Pro Miniは、I2C通信用のピンが基盤の内側の変なところにありますので、ピンヘッダを立てておきます。A4ピンがSDA、A5ピンがSCLなので、OLEDのシルク印刷に合わせて配線します。

f:id:C_L:20140822010157j:plain

Arduino Pro Miniは、プログラム書き込み用のUSBシリアル変換器が別パーツとなっているので、接続します。

f:id:C_L:20140822010024j:plain

PCに接続したら、PCのArduino IDEに、

u8glib - Universal Graphics Library for 8 Bit Embedded Systems - Google Project Hosting

のライブラリを入れて、サンプルスケッチの途中、コメントアウトしているところの中から、

//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); // I2C / TWI

の行の先頭の//を消して、スケッチをArduinoに転送します。

すると、きっとOLEDが光るはずです。

f:id:C_L:20140822010025j:plain

お疲れ様!

Arduino Pro Mini 328 3.3V 8MHz

Arduino Pro Mini 328 3.3V 8MHz

FTDI USBシリアル変換アダプター(5V/3.3V切り替え機能付き)

FTDI USBシリアル変換アダプター(5V/3.3V切り替え機能付き)

普通のブレッドボード

普通のブレッドボード

普通のジャンパワイヤ(オス?オス)

普通のジャンパワイヤ(オス?オス)

アイネックス ピン配列交換ケーブル 8本入り EX-002

アイネックス ピン配列交換ケーブル 8本入り EX-002

aitendoのI2C低電圧キャラクタ液晶モジュールをArduino Pro Mini(3.3V)で駆動(配線編)

いろいろあってArduino Pro Mini(3.3V)の互換機を10枚入手したので、「Arduino電波時計を部品を集め組んでみたはいいが室内だとJJY標準電波の入りが悪すぎて検証できないので電波時計用のJJY標準電波みたいなものを発生するArduino時計」を作っています(←ばかじゃないのか)。

その過程で、電波時計の電波を発生するAdruino時計の現在時刻表示インジケータが無くて不便なので、キャラクター液晶ディスプレイ(以下LCD)をつなげたいなあと思っていたんですが、この前シュタインズゲートでいうところの主人公たちのラボの下階にあるお店、ブラウン管工房のモデルになった液晶工房の跡地に入っているaitendo実店舗(←じきに移転するそうです)に行ったら

★50%OFF祭り★I2C低電圧キャラクタ液晶モジュール(16x2行) [SPLC792-I2C-M]

が375円で売っていたので3個買った。しかしこれがいろいろ不親切でなかなか文字が出なかったのだけれども、何とか文字が出たのでここに記す。

※重要!
この液晶モジュール内でI2Cラインが10kΩプルアップ済みです。
Arduino Pro Mini側でI2CのSDA/SCLをプルアップするとダメ!
Raspberry PiはI2Cがプルアップ済みなのでこの液晶モジュールそのままだと認識しない

画像にうっすらと色帯をオーバーレイさせておいたので電気周りの接続はわかると思います。液晶モジュール基板の印刷とArduinoのピン名が違うので注意。ブレッドボードとLCDの接続には9連ピンヘッダを使っていますが、すべてのピンを使っているわけではありません。

LCD Arduino 機能
VDD VCC 液晶コントローラ駆動電圧(3.3V)
SHL 上下方向のオリジン。HIGHにしておく(VCCへ)
DIRC 左右方向のオリジン。LOWにしておく (GNDへ)
GND GND グランド(液晶コントローラ仕様書ではVSS)
DATA SDA I2CデータバスのSDAライン(10kΩプルアップ済)
CLK SCL I2CデータバスのSCLライン(10kΩプルアップ済)
REST LOWで初期化。常にHIGHにしておく
GND
BL+ バックライト用の電圧入力(<3.3V)

液晶の右のランドパターン(SHL、DIRC)で、液晶の表示方向が変わります。液晶モジュールの組み立て時に配線をどう取り回すかによって変更する設定になります。常に一定方向で表示させたい場合はジャンパ線をはんだ付けして固定するといいです(写真では黄色い線でジャンパしています)。

I2C通信線のプルアップの件ですが、最初Pro Mini互換機(3.3V)で表示できていたので、撮影用にArduino Pro Mini(3.3V)純正品に交換してみたのですがLCDが起動しないので、そーいえば純正の方には表面実装のプルアップ用抵抗をはんだ付けしてたのを思い出した。せっかくはんだ付けしたのに、ちえ。芥子粒大の小さいチップ抵抗を外したらLCDにはちゃんと表示されるようになりました。

あと、モジュールに5V入れるときっと壊れます。この写真で使ったLCDも、実はバックライトに5Vをぶっこんでバックライト用LEDが1個死んでいるんです。かといって3.3Vを生で入れるとそれでもバックライトがまぶしいので、抵抗を入れてちょっと暗くなるようにしています(BL+ライン)。どうせ表示するとき必ずバックライトをつけるんだからと思う人は、LCD側でVDDから抵抗を1本介しBL+にぶっこんでもいいと思います。

RSTも、常にHIGHレベルをぶっこんでおくならばLCD側でVDDとつながるよう配線。それをやると、入力ピンはVCC(VDD)、GND、SDA(DATA)、SCL(CLK)の4ピンだけになります。I2Cさまさまですね!

夏休みの工作で、Arduinoでなんか小物を作るときはすごく便利な液晶モジュールだと思います(安いし)、レッツトライ!

(「配線編」としたが「ソフトウェア編」は割愛、オレ工房様の http://ore-kb.net/archives/195 で動きますゆえ)

Arduino Pro Mini 328 3.3V 8MHz

Arduino Pro Mini 328 3.3V 8MHz

FTDI USBシリアル変換アダプター(5V/3.3V切り替え機能付き)

FTDI USBシリアル変換アダプター(5V/3.3V切り替え機能付き)

普通のブレッドボード

普通のブレッドボード

普通のジャンパワイヤ(オス?オス)

普通のジャンパワイヤ(オス?オス)

アイネックス ピン配列交換ケーブル 8本入り EX-002

アイネックス ピン配列交換ケーブル 8本入り EX-002

Arduino Pro Miniにカラーピンヘッダを使うとステキだし便利

Arduino Pro Mini 3.3Vを入手したのですが(スイッチサイエンス社 http://www.switch-science.com/catalog/876/ で¥1100ぐらい、チャイナから輸入すると$3ぐらいです)、ピンヘッダを切らしており、動かせなかったのですけれども、先日秋月電子に行ってピンヘッダを調達してきましたので、さっそくピンヘッダをつけてみました。

Arduino Pro Mini(現行の正規版)は、裏面から見たPin配置はこんな感じになっています。

10 11 12 13 A0 A1 A2 A3 VCC RST GND RAW
A6 A7 A4 A5 GRN
TXD
RXI
VCC
GND
BLK
9 8 7 6 5 4 3 2 GND RST RXI TXD

秋月電子のカラー細ピンヘッダ( http://akizukidenshi.com/catalog/g/gC-06641/ )を使って、色分けしてみました。

Pin 説明とか
VCC Vcc=3.3V(3.3Vモデル)が出るピン・電源なので赤
GND ブレッドボードなどでは青色ラインがひいてあるので青
RAW 3.3VモデルはここにUSB給電の5Vをつなぐと便利 赤とは違う紫
TXD/RXI シリアル通信Pinは通常Pinと役割が違うので区別の黄色

としてみました……

ピンヘッダをつけるのはブレッドボードに挿して使う用途なのだと思いますが、こうやってカラーピンヘッダを取り付けると、配線時すごく便利ですよ! ヤスリでうまく削って並べてはんだ付け。ブレッドボードに差し込まれたら差し込み返そう!

作例:

Arduino Pro Mini 328 3.3V 8MHz

Arduino Pro Mini 328 3.3V 8MHz

FTDI USBシリアル変換アダプター(5V/3.3V切り替え機能付き)

FTDI USBシリアル変換アダプター(5V/3.3V切り替え機能付き)

DTPの勉強会vol.14に合わせて、プレゼン作った #dtpstudy14

ほんと、東京に一人で住んでいると転職活動以外することなくて結局こうなる。

暇なのではないぞ、暇なのでは……

帰宅部活動記録(1) (ガンガンコミックスONLINE)

帰宅部活動記録(1) (ガンガンコミックスONLINE)

RaspberryPi用RGBキャラクターLCDをPerlで操作してハラハラしよう

会社はPHPのスケールアウト考えなくてよい案件しかなくて、かといってスケールが大きめの職場に転職しようにも職歴で落とされるだろうし、自然とスケールアウトとかより手作りミニマムコンピューティングに興味が行ってしまうのですけれども、

スイッチサイエンスから買ったAdafruitのRGBバックライトのキャラクターLCD、これサンプルがPythonばかりなんですけれども、こちとらPerlCPANの豊富なライブラリを使いたいわけで、えいやっとPythonのライブラリから、最低限必要な部分を抜き出してPerlで書いてみました。

f:id:C_L:20140715161210j:plain

上記GIFアニメは動作内容はダミーなんですけれども、AnyEventと組み合わせて使うと夢がひろがりんぐです。

みんな大好きAmazonLCDを買うならこちら。

Raspberry Pi用16×2 LCDキーパッドキット (RGB Negative)

Raspberry Pi用16×2 LCDキーパッドキット (RGB Negative)

Raspberry Pi用16×2 LCDキーパッドキット (RGB Positive)

Raspberry Pi用16×2 LCDキーパッドキット (RGB Positive)

僕はスイッチサイエンスの通販で買いました。CP932でカタカナ表示できてワロタやつです

http://www.switch-science.com/catalog/1478/

あとは、アクリル屋さんがこれがきっちり入るアクリルケースを作ってくれるとありがたいですね、外国ではもうある

http://www.built-to-spec.com/blog/kit-instructions/raspberry-pi-and-adafruit-lcd-enclosure/

今回ライブラリを移植する過程で、

  • Raspberry PiはIOピンが少ないので外部にMCP23017なる拡張IOで対応
  • LCD自体は日立HD44780互換インタフェースになってる
  • MCP23017からLCD(HD44780)接続時節約のためデータバス4bitにしてる
  • 4bit転送するためにライブラリ側で8bitデータから転送用4bit用データを用意してる
  • 4bitデータバスを上位下位逆に結線しているためライブラリ側で反転している

とかいうことがわかりました。MPC23017の使い方も分かったので、次回はピークメーターとか作ろうーかなー

プログラムはこちら HiPi http://raspberry.znix.com/p/install.html が必要です……

#!/usr/bin/env perl

use strict;
use warnings;
use HiPi::Device::I2C;
use HiPi::BCM2835::I2C;
use utf8;
use Encode;

my $lcd = Adafruit_CharLCDPlate->new();
$lcd->init();
$lcd->begin(16, 2);
$lcd->clear();
$lcd->message("Heiwa na hibi...\nZzz...");
sleep(13);
$lcd->clear();
$lcd->backlight( $lcd->{color}->{RED} );
$lcd->message(encode('cp932', "Apacheガツマッタ!\nMaxClients=50"));
sleep(10);
$lcd->clear();
$lcd->stop();

exit;


package Adafruit_CharLCDPlate;

# Raspberry Pi用Adafruit RGB-backlit LCD plateのPythonライブラリ
# https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/blob/e28539734dfbd6965395f0f0478ecad44ca0eaea/Adafruit_CharLCDPlate/Adafruit_CharLCDPlate.py
# から文字の表示とバックライトの変更の部分だけ最低限移植
# Perl用I2C/SMBusアクセスライブラリHiPiを使用
# http://raspberry.znix.com/p/install.html
#
# This is essentially a complete rewrite, but the calling syntax
# and constants are based on code from lrvick and LiquidCrystal.
# lrvic - https://github.com/lrvick/raspi-hd44780/blob/master/hd44780.py
# LiquidCrystal - https://github.com/arduino/Arduino/blob/master/libraries/LiquidCrystal/LiquidCrystal.cpp


use strict;
use warnings;
use HiPi::Device::I2C;
use HiPi::BCM2835::I2C;

use constant {
    # Port expander registers,
    # ポートエキスパンダ(MCP23017) のレジスタ
    MCP23017_IOCON_BANK0    => 0x0A, # IOCON when Bank 0 active / Bank 0がアクティブ時のIOCONレジスタアドレス
    MCP23017_IOCON_BANK1    => 0x15, # IOCON when Bank 1 active / Bank 1がアクティブ時のIOCONレジスタアドレス
    # These are register addresses when in Bank 1 only:,
    # 以下は、Bank 1がアクティブのみ有効なレジスタアドレス
    MCP23017_GPIOA          => 0x09,
    MCP23017_IODIRB         => 0x10,
    MCP23017_GPIOB          => 0x19,
    
    # Port expander input pin definitions
    # ポートエキスパンダ(MCP23017) の入力ピンの定義
    SELECT                  => 0,
    RIGHT                   => 1,
    DOWN                    => 2,
    UP                      => 3,
    LEFT                    => 4,
    
    # LED colors
    # LCDパネルのバックライトLEDの色
    OFF                     => 0x00,
    RED                     => 0x01,
    GREEN                   => 0x02,
    BLUE                    => 0x04,
};
use constant {
    YELLOW                  => RED + GREEN,
    TEAL                    => GREEN + BLUE,
    VIOLET                  => RED + BLUE,
    WHITE                   => RED + GREEN + BLUE,
    ON                      => RED + GREEN + BLUE,
        
    # LCD Commands
    # LCD(ポートエキスパンダの先につながっているキャラクタLCDドライバHD44780用の命令)
    LCD_CLEARDISPLAY        => 0x01,
    LCD_RETURNHOME          => 0x02,
    LCD_ENTRYMODESET        => 0x04,
    LCD_DISPLAYCONTROL      => 0x08,
    LCD_CURSORSHIFT         => 0x10,
    LCD_FUNCTIONSET         => 0x20,
    LCD_SETCGRAMADDR        => 0x40,
    LCD_SETDDRAMADDR        => 0x80,
    
    # Flags for display on/off control
    # ディスプレイOn/Offコントロールフラグ(HD44780用)3bits
    LCD_DISPLAYON           => 0x04,
    LCD_DISPLAYOFF          => 0x00,
    LCD_CURSORON            => 0x02,
    LCD_CURSOROFF           => 0x00,
    LCD_BLINKON             => 0x01,
    LCD_BLINKOFF            => 0x00,
    
    # Flags for display entry mode
    # ディスプレイエントリモードフラグ(HD44780用)2bits
    LCD_ENTRYRIGHT          => 0x00,
    LCD_ENTRYLEFT           => 0x02,
    LCD_ENTRYSHIFTINCREMENT => 0x01,
    LCD_ENTRYSHIFTDECREMENT => 0x00,
    
    # Flags for display/cursor shift
    # カーソル/ディスプレイシフトフラグ(HD44780用)2bits
    LCD_DISPLAYMOVE => 0x08,
    LCD_CURSORMOVE  => 0x00,
    LCD_MOVERIGHT   => 0x04,
    LCD_MOVELEFT    => 0x00,
};

sub new {
  my $class = shift;
  my %args = ( @_ );
  my $self = {};
  $self->{addr} = 0x20;
  $self->{i2c} = HiPi::Device::I2C->new( address => $self->{addr});
  
  $self->{color} = {
      OFF    => OFF,
      RED    => RED,
      GREEN  => GREEN,
      BLUE   => BLUE,
      YELLOW => YELLOW,
      TEAL   => TEAL,
      VIOLET => VIOLET,
      WHITE  => WHITE,
      ON     => ON,
  };
  
  # The LCD data pins (D4-D7) connect to MCP pins 12-9 (PORTB4-1), in
  # that order.  Because this sequence is 'reversed,' a direct shift
  # won't work.  This table remaps 4-bit data values to MCP PORTB
  # outputs, incorporating both the reverse and shift.
  # LCDモジュールのDATAピン(D4-D7)は、MCP23017の12-9ピン(PORTB4-1)
  # にこの順番で結線されている。逆順になっているので、通常のシフト
  # はうまく動かない。次のテーブルは、シフト・反転用に対応したMPC23017
  # のPORTBに出力する値を再定義します。
  
  $self->{flip} = [
    0b00000000, 0b00010000, 0b00001000, 0b00011000,
    0b00000100, 0b00010100, 0b00001100, 0b00011100,
    0b00000010, 0b00010010, 0b00001010, 0b00011010,
    0b00000110, 0b00010110, 0b00001110, 0b00011110,
  ];
  
  # The speed of LCD accesses is inherently limited by I2C through the
  # port expander.  A 'well behaved program' is expected to poll the
  # LCD to know that a prior instruction completed.  But the timing of
  # most instructions is a known uniform 37 mS.  The enable strobe
  # can't even be twiddled that fast through I2C, so it's a safe bet
  # with these instructions to not waste time polling (which requires
  # several I2C transfers for reconfiguring the port direction).
  # The D7 pin is set as input when a potentially time-consuming
  # instruction has been issued (e.g. screen clear), as well as on
  # startup, and polling will then occur before more commands or data
  # are issued.
  # LCDアクセスの速度は、I2Cで接続されるポートエキスパンダ(MCP23017)
  # で制限されます。
  # 「行儀のよいプログラム」は先に実行した命令の完了をLCDからポーリング
  # します。しかし、ほとんどのプログラムは37msのウェイトで済ませています。
  # enable信号は、I2Cによってそれほど速く切り替えることができません。
  # したがって、それは時間ポーリング(それはポート方向の再構成のために、
  # いくつかのI2Cが移ることを必要とする)を浪費しないためにこれらの指
  # 示を備えた安全策です。
  # D7ピンは、潜在的に時間を消費する命令が出された場合(例えばスクリー
  # ンクリア)だけでなく、起動時などに入力に設定され、複数のコマンド
  # あるいはデータが出される場合、ポーリングの必要が生じます。
  $self->{pollables} = [ LCD_CLEARDISPLAY, LCD_RETURNHOME ];
  
  # I2C is relatively slow.  MCP output port states are cached
  # so we don't need to constantly poll-and-change bit states.
  # self.porta, self.portb, self.ddrb = 0, 0, 0b00010000
  # I2Cのレスポンスは比較的遅い。MCP23017の出力ポートの変更状態
  # はキャッシュ($self->{poata}, $self->{portb}, $self->{ddrb})
  # で管理するので、書き込み後いちいちポーリングして確認する
  # 必要はありません。
  $self->{porta} = 0;
  $self->{portb} = 0;
  $self->{ddrb}  = 0b00010000;
  
  # Set MCP23017 IOCON register to Bank 0 with sequential operation.
  # If chip is already set for Bank 0, this will just write to OLATB,
  # which won't seriously bother anything on the plate right now
  # (blue backlight LED will come on, but that's done in the next
  # step anyway).
  # MCP23017のIOCONレジスタをBank0、シーケンシャル処理を有効に設定し
  # ます。Bank 1になっている想定で決め打ちのアドレスで書き込むため、
  # すでにMCP23017チップがBank 0に切り替わっていた場合、OLATBに書き
  # 込まれることになりますが、特に問題はありません(青いバックライ
  # トが点灯しますが、次のステップで解決します)。
  # 0x80 BANK 0=bank 0を選択
  # 0x20 SEQOP シーケンシャルオペレーション 0=アドレスのオートインクリメントをする
  $self->{i2c}->smbus_write_byte_data( MCP23017_IOCON_BANK1, 0 );
  
  # Brute force reload ALL registers to known state.  This also
  # sets up all the input pins, pull-ups, etc. for the Pi Plate.
  # 総当たりですべてのレジスタに設定を読み込ませます。また、
  # Pi Plateの入力ピン設定や、プルアップ設定も行います。
  
  $self->{i2c}->smbus_write_i2c_block_data(0, [
    0b00111111,       # IODIRA    R+G LEDs=outputs, buttons=inputs
    $self->{ddrb} ,   # IODIRB    LCD D7=input, Blue LED=output
    0b00111111,       # IPOLA     Invert polarity on button inputs
    0b00000000,       # IPOLB
    0b00000000,       # GPINTENA  Disable interrupt-on-change
    0b00000000,       # GPINTENB
    0b00000000,       # DEFVALA
    0b00000000,       # DEFVALB
    0b00000000,       # INTCONA
    0b00000000,       # INTCONB
    0b00000000,       # IOCON
    0b00000000,       # IOCON
    0b00111111,       # GPPUA     Enable pull-ups on buttons
    0b00000000,       # GPPUB
    0b00000000,       # INTFA
    0b00000000,       # INTFB
    0b00000000,       # INTCAPA
    0b00000000,       # INTCAPB
    $self->{porta},   # GPIOA
    $self->{portb},   # GPIOB
    $self->{porta},   # OLATA     0 on all outputs; side effect of
    $self->{portb}    # OLATB     turning on R+G+B backlight LEDs.
  ]);
  
  # Switch to Bank 1 and disable sequential operation.
  # From this point forward, the register addresses do NOT match
  # the list immediately above.  Instead, use the constants defined
  # at the start of the class.  Also, the address register will no
  # longer increment automatically after this -- multi-byte
  # operations must be broken down into single-byte calls.
  # MCP23017をBank 1に切り替えて、シーケンシャル処理を無効にします。
  # これ以降は、レジスタのアドレスはこのメソッドの最初のレジスタの順番と
  # 一致しません。このメソッドの最初で定義した定数を使うこと。
  # 更に、アドレスレジスタは、これ以降は自動的にインクリメントしません。
  # マルチバイトの操作は、シングルバイトの呼び出しに分割処理する必要
  # があります。
  # 0x80 BANK 1=bank 1を選択
  # 0x20 SEQOP シーケンシャルオペレーション 1=アドレスのオートインクリメントしない
  $self->{i2c}->smbus_write_byte_data( MCP23017_IOCON_BANK0, 0b10100000);
  
  $self->{displayshift}   = (LCD_CURSORMOVE | LCD_MOVERIGHT);
  $self->{displaymode}    = (LCD_ENTRYLEFT  | LCD_ENTRYSHIFTDECREMENT);
  $self->{displaycontrol} = (LCD_DISPLAYON  | LCD_CURSOROFF | LCD_BLINKOFF);
  
  return bless $self, $class;
}

sub init {
  my $self = shift;
  $self->write(0x33); # Init
  $self->write(0x32); # Init
  $self->write(0x28); # 2 line 5x8 matrix
                      # 0x20 Function set
                      # 0x10 DL データ長 0=4-bit length
                      # 0x08 NL 行数 1=2行
                      # 0x04 F フォント 0=5x8 dotsフォント
  $self->write(LCD_CLEARDISPLAY);
  $self->write(LCD_CURSORSHIFT    | $self->{displayshift});
  $self->write(LCD_ENTRYMODESET   | $self->{displaymode});
  $self->write(LCD_DISPLAYCONTROL | $self->{displaycontrol});
  $self->write(LCD_RETURNHOME);
}

# ----------------------------------------------------------------------
# Write operations

# Low-level 4-bit interface for LCD output.  This doesn't actually
# write data, just returns a byte array of the PORTB state over time.
# Can concatenate the output of multiple calls (up to 8) for more
# efficient batch write.
# LCD出力用のローレベル4bitインタフェース。配列にあるbyteデータをPORTBへ
# 直接書き込めないので、4bitデータの4回書き込みに変換する。
# まとめて書き込む場合最大8個連結することができる。
sub out4 {
  my $self    = shift;
  my $bitmask = shift;
  my $value   = shift;
  my $hi = $bitmask | $self->{flip}->[ $value >> 4 ];
  my $lo = $bitmask | $self->{flip}->[ $value & 0x0F];
  return [ $hi | 0b00100000, $hi, $lo | 0b00100000, $lo];
}

# Write byte, list or string value to LCD
# LCDに設定値の配列または文字列を書き込む

sub write {
  my $self = shift;
  my $value= shift;
  my $char_mode = shift || 0;
  # """ Send command/data to LCD """
  
  # If pin D7 is in input state, poll LCD busy flag until clear.
  # もしD7ピンがinputになってるなら、LCDのbusyフラグ(D7ピン)がCLEARになるまでポーリングする。
  if ($self->{ddrb} & 0b00010000 ) {
    my $lo = ( $self->{portb} & 0b00000001) | 0b01000000;
    my $hi = $lo | 0b00100000; # E=1 (strobe)
    $self->{i2c}->smbus_write_byte_data( MCP23017_GPIOB, $lo);
    while (1) {
      # Strobe high (enable)
      $self->{i2c}->smbus_write_byte_data( MCP23017_GPIOB, $hi );
      my $bits = $self->{i2c}->smbus_read_byte();
      # Strobe low, high, low.  Second nybble (A3) is ignored.
      $self->{i2c}->smbus_write_block_data( MCP23017_GPIOB, [$lo, $hi, $lo]);
      last if ( $bits & 0b00000010) == 0; # D7=0, not busy
    }
    $self->{portb} = $lo;
    
    # Polling complete, change D7 pin to output
    # ポーリングが終わったら、D7ピンをoutputに変更する
    $self->{ddrb} &= 0b11101111;
    $self->{i2c}->smbus_write_byte_data( MCP23017_IODIRB, $self->{ddrb});
  }  
  my $bitmask = $self->{portb} & 0b00000001;
  $bitmask |= 0b10000000 if $char_mode; # Set data bit if not a command
  
  # If string or list, iterate through multiple write ops
  # 引数が文字列または配列のリファレンスの場合、複数書き込みとして処理する
  if ( ( $value ^ $value ) ne '0' ) { # 文字列
    my $last = length($value) - 1;
    my $data = [];
    my $i = 0;
    foreach my $v ( split (//, $value) ) {
      # Append 4 bytes to list representing PORTB over time.
      # First the high 4 data bits with strobe (enable) set
      # and unset, then same with low 4 data bits (strobe 1/0).
      # 表わすPORTBを時間にわたってリストするために4バイトを追加してください。
      # 最初に、strobe(Enable)を1/0に設定した上位4bitデータ、
      # その後、下位4bitデータ(strobe 1/0)
      # (つまり8bitデータを4回に分けて送る:MCPとLCDのデータ線が4bitなため)
      push @$data, @{$self->out4($bitmask, ord($v))};
      # I2C block data write is limited to 32 bytes max.
      # If limit reached, write data so far and clear.
      # Also do this on last byte if not otherwise handled.
      # I2Cブロック・データ書き込みは最大32バイトに制限されています。
      # 限界が達した場合は、ここまでのデータを書いて空にしてください。
      # 限界でない場合でも、処理されていないデータがあれば、最後のバイトで
      # 書き込みを行ってください。
      if ( ( $#{$data} >= 31 ) || ($i == $last) ) {
        $self->{i2c}->smbus_write_block_data( MCP23017_GPIOB, $data );
        $self->{portb} = $data->[-1]; # Save state of last byte out
        $data = [];                   # Clear list for next iteration
      }
      $i++;
    }
  }
  elsif ( ref $value eq 'ARRAY' ) { # 配列のリファレンス
    ## Same as above, but for list instead of string
    ## 上と同じルーチンだけど配列用の処理として用意
    my $last = length($value) - 1;
    my $data = [];
    my $i = 0;
    foreach my $v ( @$value ) {
      push @$data, @{$self->out4($bitmask, $v)};
      if ( ( $#{$data} >= 31) || ($i == $last) ) {
        $self->{i2c}->smbus_write_block_data( MCP23017_GPIOB, $data );
        $self->{portb} = $data->[-1]; # Save state of last byte out
        $data = [];                   # Clear list for next iteration
      }
      $i++;
    }
  }
  else {
    # Single byte
    # シングルバイト
    my $data = $self->out4($bitmask, $value);
    $self->{i2c}->smbus_write_block_data( MCP23017_GPIOB, $data );
    $self->{portb} = $data->[-1];
  }
  # If a poll-worthy instruction was issued, reconfigure D7
  # pin as input to indicate need for polling on next call.
  # ポーリング結果で、ポーリング指示が出された場合は、次の呼び出しでポーリングする必要
  # があるので、D7ピンを入力に設定する。
  if ( (!$char_mode) && (grep {$_ eq $value } @{$self->{pollables}} ) ) {
    $self->{ddrb} |= 0b00010000;
    $self->{i2c}->smbus_write_byte_data( MCP23017_IODIRB, $self->{ddrb} ); 
  }
}

# ----------------------------------------------------------------------
# Utility methods

sub begin {
  my $self  = shift;
  my $cols  = shift;
  my $lines = shift;
  $self->{currline} = 0;
  $self->{numlines} = $lines;
  $self->clear();
}

# Puts the MCP23017 back in Bank 0 + sequential write mode so
# that other code using the 'classic' library can still work.
# Any code using this newer version of the library should
# consider adding an atexit() handler that calls this.
sub stop {
  my $self = shift;
  $self->{porta} = 0b11000000;  # Turn off LEDs on the way out
  $self->{portb} = 0b00000001;
  HiPi::BCM2835::I2C->delay(15);
  $self->{i2c}->smbus_write_byte_data( MCP23017_IOCON_BANK1, 0);
  $self->{i2c}->smbus_write_i2c_block_data(0, [
    0b00111111,     # IODIRA
    $self->{ddrb},  # IODIRB
    0b00000000,     # IPOLA
    0b00000000,     # IPOLB
    0b00000000,     # GPINTENA
    0b00000000,     # GPINTENB
    0b00000000,     # DEFVALA
    0b00000000,     # DEFVALB
    0b00000000,     # INTCONA
    0b00000000,     # INTCONB
    0b00000000,     # IOCON
    0b00000000,     # IOCON
    0b00111111,     # GPPUA
    0b00000000,     # GPPUB
    0b00000000,     # INTFA
    0b00000000,     # INTFB
    0b00000000,     # INTCAPA
    0b00000000,     # INTCAPB
    $self->{porta}, # GPIOA
    $self->{portb}, # GPIOB
    $self->{porta}, # OLATA
    $self->{portb},
 ]); # OLATB
}

sub clear {
  my $self = shift;
  $self->write(LCD_CLEARDISPLAY);
}

sub home {
  my $self = shift;
  $self->write(LCD_RETURNHOME);
}

sub message {
  my $self = shift;
  my $text = shift;
  # """ Send string to LCD. Newline wraps to second line"""
  my @lines = split(/\n/, $text); # Split at newline(s)
  my $i = 0;
  foreach my $line ( @lines ) {  # For each substring...
    if ( $i > 0 ) {               # If newline(s),
      $self->write( 0xC0 );       #  set DDRAM address to 2nd line
    }
    $self->write( $line, 1 );  # Issue substring
    $i++;
  }
}

sub backlight {
  my $self  = shift;
  my $color = shift;
  my $c     = ~$color;
  $self->{porta} = ($self->{porta} & 0b00111111) | (($c & 0b011) << 6);
  $self->{portb} = ($self->{portb} & 0b11111110) | (($c & 0b100) >> 2);
  # Has to be done as two writes because sequential operation is off.
  $self->{i2c}->smbus_write_byte_data( MCP23017_GPIOA, $self->{porta});
  $self->{i2c}->smbus_write_byte_data( MCP23017_GPIOB, $self->{portb});
}

1;

HOYAのVoiceText Web API (β版)でRaspberry Piをしゃべらせる

Raspberry Piにraspbian(wheezy)をセットアップしているという前提。cat /usr/share/sounds/alsa/Front_Center.wav | aplay で音が出ていることをあらかじめ確認しておこう。音が出る場所を変えたければ、amixer などで音が出る端子を変更したりしよう。3.5φステレオミニジャックから音を出したければ、amixer cset numid=3 1。ボリュームを変えたい場合は、alsamixerで変更しよう。

本題。

VoiceText Web API (β版) https://cloud.voicetext.jp/webapi

(リンク先ページのTwitter鳥マークの使い方はあれでいいんだっけ)

HOYAからもらったAPIキーが

9a8b7c6d5e4f3g2h

だとすると、

curl https://api.voicetext.jp/v1/tts -s -u 9a8b7c6d5e4f3g2h: -d speed=130 -d speaker=hikari -d emotion=happiness -d "text=IT系の転職すべて落ちた!"  | aplay 2> /dev/null

でRaspberry Piがしゃべる。ネットとスピーカーにつながっている必要はある。

覚えにくい場合は、

curl https://api.voicetext.jp/v1/tts \
  --silent \
  --user 9a8b7c6d5e4f3g2h: \
  --data speed=130 \
  --data speaker=hikari \
  --data emotion=happiness \
  --data "text=IT系の転職すべて落ちた!" \
| aplay 2> /dev/null

と分解してcurlコマンドのオプションを、ハイフンふたつのロングオプションに書き換えると、いろいろ捗ります。Basic認証のユーザ名にAPIキー、パスワードは空ってことですね。

Webmin 1.670から「ユーザEメールを読む」の文字化けが直っていた

私がこの会社に入った時にはすでにWebminによるサーバ設定サーバが導入されていて、なんでかというとどうも誰一人コンソールが使えないからだという。まあ使えないものはしょうがないかということで、それはそれとしてWebminってPerlで作ってあるので、PHPerしかいない会社でPerl使える私としてはいろいろ手が入れられるわけで、「ユーザEメールを読む」モジュールが文字化けするのをオレオレpatch充てて直して、Chefのcookbookに作ってあったぐらいしていたんだけれども、今日Webminを1.660から1.690にバージョンアップして「ユーザEメールを読む」を使ってみたら文字化けしないようになっていた。

mailboxes/CHANGELOG

---- Changes since 1.670 ----
When a language with a UTF-8 character set is selected, email is converted to UTF-8 before being displayed. This allows an inbox with mixed language subject lines to be properly displayed.

となむ。つうわけで、Webmin有ればPOP3でメール読みだす必要なくなったぜ、ってことでした。110/TCP(POP3)と995/TCP(POP3S)を閉じようぜ!(ひどいサーバだ)

あと、WebminってApacheを使わずHTTPをサービスしている(=Apacheが死んでいてもブラウザからサーバ設定が変更できる)ので、サーバが死にそうなときにWebminで再起動していたそうな。でもこういうサーバが死にそうな時って、スラッシングが発生している場合なので、Webminもたいていつながらがらないんですよね。Apache起因でスラッシングが起こらないよう運用を工夫しよう。