I2C 16×2 LCDで日本語(カタカナ)を表示する(pythonプログラムあり)

LCDでカタカナ表示

こんにちは。ラズパイやArduinoでセンサ等を利用していると、気温などの数値をどうやって表示するか意外に悩みます。そんなメッセージや数字を表示したい時に便利なのがLCDディスプレイです。LCDは電光掲示板のようにドットで表現された文字を表示するデバイスで、ラズパイの5V電源でも動かすことが出来ます。

Osoyoo製LCD。電子工作入門セットに入っていました。

上の電子工作入門セットに入っていたOsoyoo製LCDは、I2Cで簡単にラズパイと接続できて、さらに制御用pythonプログラムも公式で公開されているのでとっても簡単にメッセージが表示できる…、のですがこの制御プログラム、英数字の出力のみでカタカナの表示方法が全く説明されていません。ドキュメントは日本語なのに不親切極まりないです。(初心者向けセットなのにマニュアル等一切なし。せめて各文字に対応するアドレスだけでも公開して欲しい…)

というわけで今回はこれにカタカナや記号を表示させることを目標にしたいと思います。

(追記)
このLCDの裏にはんだ付けされているI2C接続用マイコン「HD44780」は有名らしく、こちらで検索するとわりとヒットしました。今回はOsoyooのプログラムをもとにpythonで操作する方法をご紹介します。

このICは広く利用されているようで、説明されているページも多くありました。

表示は出来るっぽい

Osoyooの公式ページに従って進めてみます。

とりあえずドキュメントに従ってLCD制御用pythonプログラムをダウンロードして、文字を表示させてみる。LCDとラズパイの5V、OUT、SDA(#2ピン)、SDL(#3ピン)を接続して、制御プログラムのmain関数の中に次のように書けば英数字は表示できます。

def main():
  lcd_init()
  lcd_string("Hello world!",LCD_LINE_1)

このままだと表示されたメッセージが一瞬で消えてしまうので、一番下の二行をコメントアウトしておきます。

#finally:
  #lcd_byte(0x01,LCD_CMD)

これで英数字は問題ないのですが、カタカナを表示しようとすると文字化けしてしまいます。

対策

かなり強引ですが、文字化けすることを見越して元のテキストを変えてしまう変換表を作ってみました。表示したいテキストを一回この関数で変換した後、上記の方法でとりあえず正しい文字が表示できます。

def katakana(text):
	list1a = u"「。、」ヲ・ィァェゥォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゛゜"
	list1b = u"アァイィウゥエェオォカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミ"
	list2a = u"ガギグゲゴザジズゼゾダヂヅデドバビブベボ"
	list2b = u"ザシジスズセゼソゾタダチヂッツナニヌネノ"
	list3a = u"パピプペポ"
	list3b = u"ナニヌネノ"
	
	text_changed = ""
	for l in text:
		#カタカナを変換
		if list1a.find(l) >= 0:
			text_changed += list1b[list1a.find(l)]
		#濁音を清音+濁点に
		elif list2a.find(l) >= 0:
			text_changed += list2b[list2a.find(l)]
			text_changed += u"マ"
		#半濁音を清音+半濁点に
		elif list3a.find(l) >= 0:
			text_changed += list3b[list3a.find(l)]
			text_changed += u"ミ"
		#その他の文字はそのままにする
		else:
			text_changed += l
	
	return text_changed
def main():
  lcd_init()
  text = u"ラズベリーパイ"
 text2 = katakana(text)
  lcd_string(text2,LCD_LINE_1)

対策②

上の方法は対処療法であまりよろしくないので、いっそ表示出来る文字を全て調べてみました。制御プログラムを見たところによると、文字の表示は「0x80 0x##」を送ることで出来るみたいです。「##」の部分に対応する文字を全て表示させてみた結果はこのようになりました。

ほぼShift-JISっぽいが少し違う所も…

どうしてもPCで出せなかった文字は説明にしてあります。英数字やカタカナはShift-JISに沿っていますが、「千」「万」「円」「√」などの文字もあります。これだけあれば色んな情報が表示出来そうですね。個人的には「℃」が使いたいのですが、「゜」「C」で表示出来そうです。

これらの文字を出すにはmain関数内に次のように書きます。

def main():
  lcd_init()
  
  #何行目に表示するか指定
  lcd_byte(LCD_LINE_1,LCD_CMD)
  
  #0xFC=「円」を表示
 lcd_byte(0xFC,LCD_CHR)

「円」を表示することができたでしょうか?
これだと一文字しか表示できず、また毎回表を見るのも面倒なので文字コードの順に並べたリストを作ってみました。文字コードnの文字がn番目に出てくるようにしてあります。これでカタカナを含む文字列を上の表の文字コードに変換して表示出来ます。

2019/11/3追記: 表示される文字のリストcodesの0x27のシングルクォーテーションが抜けていましたので修正しました。

def lcd_string_kana(message,line):
	#文字コードnの文字 = n番目の文字
	codes = u'線線線線線線線線線線線線線線線線                !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}→←                                 。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゛゜αäβεμσρq√陰ι×¢£nöpqθ∞ΩüΣπxν千万円÷ 塗'
	#変換する文字の辞書(ex.「バ」=「ハ」「゛」)
	dic ={u'ガ':u'カ゛',u'ギ':u'キ゛',u'グ':u'ク゛',u'ゲ':u'ケ゛',u'ゴ':u'コ゛',u'ザ':u'サ゛',u'ジ':u'シ゛',u'ズ':u'ス゛',u'ゼ':u'セ゛',u'ゾ':u'ソ゛',u'ダ':u'タ゛',u'ヂ':u'チ゛',u'ヅ':u'ツ゛',u'デ':u'テ゛',u'ド':u'ト゛',u'バ':u'ハ゛',u'ビ':u'ヒ゛',u'ブ':u'フ゛',u'ベ':u'ヘ゛',u'ボ':u'ホ゛',u'パ':u'ハ゜',u'ピ':u'ヒ゜',u'プ':u'フ゜',u'ペ':u'ヘ゜',u'ポ':u'ホ゜',u'℃':u'゜C'}
	message = message.ljust(LCD_WIDTH," ")
	lcd_byte(line, LCD_CMD)
	
	#濁音など、文字の変換
	message2 = ""
	for i in range(LCD_WIDTH):
		if ( message[i] in dic.keys() ):
			message2 += dic[message[i]]
		else:
			message2 += message[i]
	
	#文字コードのリストにある文字なら表示、それ以外は表示しない
	for i in range(LCD_WIDTH):
		if (codes.find(message2[i]) >= 0):
			lcd_byte(codes.find(message2[i])+1,LCD_CHR)
		elif (message2[i] != u' '):
			print "No such character!    :" + message2[i]

ついでに「ガ」などLCDでは二文字で表示する文字を「カ」「゛」に分解する処理も混ぜておきました。

全体のプログラムも載せておきますご参考までにどうぞ。

#coding:utf-8
import smbus
import time
import sys

# Define some device parameters
I2C_ADDR  = 0x3f # I2C device address, if any error, change this address to 0x3f
LCD_WIDTH = 16   # Maximum characters per line

# Define some device constants
LCD_CHR = 1 # Mode - Sending data
LCD_CMD = 0 # Mode - Sending command

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line
LCD_LINE_3 = 0x94 # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xD4 # LCD RAM address for the 4th line

LCD_BACKLIGHT  = 0x08  # On
#LCD_BACKLIGHT = 0x00  # Off

ENABLE = 0b00000100 # Enable bit

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

#Open I2C interface
#bus = smbus.SMBus(0)  # Rev 1 Pi uses 0
bus = smbus.SMBus(1) # Rev 2 Pi uses 1

def lcd_init():
  # Initialise display
  lcd_byte(0x33,LCD_CMD) # 110011 Initialise
  lcd_byte(0x32,LCD_CMD) # 110010 Initialise
  lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
  lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off 
  lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
  lcd_byte(0x01,LCD_CMD) # 000001 Clear display
  time.sleep(E_DELAY)

def lcd_byte(bits, mode):
  # Send byte to data pins
  # bits = the data
  # mode = 1 for data
  #        0 for command

  bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT
  bits_low = mode | ((bits<<4) & 0xF0) | LCD_BACKLIGHT
  
  # High bits
  bus.write_byte(I2C_ADDR, bits_high)
  lcd_toggle_enable(bits_high)

  # Low bits
  bus.write_byte(I2C_ADDR, bits_low)
  lcd_toggle_enable(bits_low)

def lcd_toggle_enable(bits):
  # Toggle enable
  time.sleep(E_DELAY)
  bus.write_byte(I2C_ADDR, (bits | ENABLE))
  time.sleep(E_PULSE)
  bus.write_byte(I2C_ADDR,(bits & ~ENABLE))
  time.sleep(E_DELAY)

def lcd_string(message,line):
  # Send string to display

  message = message.ljust(LCD_WIDTH," ")

  lcd_byte(line, LCD_CMD)

  for i in range(LCD_WIDTH):
    lcd_byte(ord(message[i]),LCD_CHR)

def lcd_string_kana(message,line):
	#文字コードnの文字 = n番目の文字
	codes = u'線線線線線線線線線線線線線線線線                !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}→←                                 。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゛゜αäβεμσρq√陰ι×¢£nöpqθ∞ΩüΣπxν千万円÷ 塗'
	#変換する文字の辞書(ex.「バ」=「ハ」「゛」)
	dic ={u'ガ':u'カ゛',u'ギ':u'キ゛',u'グ':u'ク゛',u'ゲ':u'ケ゛',u'ゴ':u'コ゛',u'ザ':u'サ゛',u'ジ':u'シ゛',u'ズ':u'ス゛',u'ゼ':u'セ゛',u'ゾ':u'ソ゛',u'ダ':u'タ゛',u'ヂ':u'チ゛',u'ヅ':u'ツ゛',u'デ':u'テ゛',u'ド':u'ト゛',u'バ':u'ハ゛',u'ビ':u'ヒ゛',u'ブ':u'フ゛',u'ベ':u'ヘ゛',u'ボ':u'ホ゛',u'パ':u'ハ゜',u'ピ':u'ヒ゜',u'プ':u'フ゜',u'ペ':u'ヘ゜',u'ポ':u'ホ゜',u'℃':u'゜C'}
	message = message.ljust(LCD_WIDTH," ")
	lcd_byte(line, LCD_CMD)
	
	#濁音など、文字の変換
	message2 = ""
	for i in range(LCD_WIDTH):
		if ( message[i] in dic.keys() ):
			message2 += dic[message[i]]
		else:
			message2 += message[i]
	
	#文字コードのリストにある文字なら表示、それ以外は表示しない
	for i in range(LCD_WIDTH):
		if (codes.find(message2[i]) >= 0):
			lcd_byte(codes.find(message2[i])+1,LCD_CHR)
		elif (message2[i] != u' '):
			print "No such character!    :" + message2[i]

def main():
  # Main program block

  # Initialise display
  lcd_init()
  argv = sys.argv
  argc = len(argv)
  if (argc == 2):
  	lcd_string(katakana(argv[1].decode('utf-8')),LCD_LINE_1)
  elif (argc == 3):
  	lcd_string(katakana(argv[1].decode('utf-8')),LCD_LINE_1)
  	lcd_string(katakana(argv[2].decode('utf-8')),LCD_LINE_2)
  else:
  	#lcd_string(katakana(u"No data."),LCD_LINE_1)
  	lcd_string_kana(u"キオン:∞℃です",LCD_LINE_1)
  	lcd_string_kana(u"チョキン:1千万円",LCD_LINE_2)

if __name__ == '__main__':

  try:
    main()
  except KeyboardInterrupt:
    pass
  #finally:
    #lcd_byte(0x01, LCD_CMD)

カタカナや記号を含めたメッセージを表示することに成功しました!
色々表示させて遊んでみます。

データの表示、ユーザーインターフェースなど色々使えそう

温度・湿度・気圧が全部測れるセンサーGAOHOU製GY-BME280の使い方

はじめに

温湿度計を作ろうと思って調べていたら、こちらのセンサーにたどり着きました。

4ピン温湿度+気圧測定モジュール

 

2週間かかりましたが無事到着。使い方を調べようと思ったのですが、他の似たようなモジュールの方が有名みたいでこの製品について説明しているページが皆無…そんなもん先に調べとけ
ということで実際に使ってみてわかったことを紹介します。

このページでは4ピンのモジュール「GY-BMEP」について説明します。
6ピンのBME280モジュールでは無いので注意して下さい!

何ができる

  • 温度・湿度・気圧がわりと良い精度で測定できる
  • I2C接続
  • アドレスは0x76または0x77が選べる
  • 読み出しは早い

センサーの精度はわりといい感じです。この辺はずっと使ってみないとわかりませんが。

Vin、GND、SCL、SDAの4つのピンがあるので、I2Cで接続します。Arduino Nanoを使う場合は
SCL→A5ピン
SDA→A4ピン
に接続します。

電源は6ピンのモジュールと違って3.3Vでも5Vでも使えるみたいです。非常に便利です。
I2C通信を行うスケッチはこちらのページ丸パクリ参考にさせて頂きました!

※説明欄ではSPIでも通信できると謳っていますが出来ませんでした。
チップ自体は対応していますが、接続するピンがありません

真ん中の3つ端子はなに?

日本語で探しても答えにたどり着けず、モジュール名で検索して英語のQ&Aを見てみたら回答がありました。

どうやらI2C通信のデバイスアドレスを選択するためのものだそうです。
1つしか使っていないときは問題になりませんが、2つ以上同じ機器を使う時は、同じアドレスが2つ存在しないようにアドレスを変更します。このデバイスは通常時は0x76ですが、次のようにすると0x77に変更できる、とあります。

金の端子の真ん中と左の間を鋭利なナイフ等で傷つけて断線し、右と真ん中をハンダ付けして導通させます。

繊細な作業が必要そう

外気と室内の両方の気温が知りたい時とかに使えそうですね

 

今回は以上です
質問等ありましたら下のフォームからどうぞ

モータードライバ L9110でモーターを逆回転させる

はじめに

カーテンを自動で開閉する仕組みを作りたかったときに、モータを逆回転したいなと思ったことがありました。プログラムを変えるとか簡単な回路で済むかと思ったら、意外と難しそうだったのでモジュールを使うことにしました。今回はIC L9110およびそれを使ったモジュールの使い方のメモです。

モジュール

今回使うのはこのモジュールです。
Amazonでなんと1個150円!思ったより小さくていい感じです。

サイズは10円玉大

ちなみに、1つのモジュールで2つのモーターを別々に動かせます。
電流は各800mAまでだそうです。

ピンは6つあって、Vcc、GND、Aの入力2つ、Bの入力2つです。緑の部分にモーター線をネジ止めします。入力のどちらか片方を0Vに落とすとモーターが正回転or逆回転します。

うーん、便利!
こういう保護回路とか全部ついた簡単モジュールはいいですね。

データシート

L9110のデータシートの意訳です。

  • 電源電圧:2.5Vから12V
  • 許容電流(定常):800mA×2個
  • 許容電流(瞬間):2.0A
  • 無負荷時電流:2μA
  • 制御電流:0.5mA
  • TTL/CMOSレベルに対応
  • 誘導電流、高電圧に対する保護回路付き

入出力レベル

入力1入力2出力1出力2
HLHL
LHLH
LLLL
HHLL

※以下、実際に使ってみてわかった注意点

  • 出力電圧は電源電圧の80%くらいになる
  • 制御入力は何も接続されていないときはHighレベルになる(内部プルアップ)