2016年12月28日水曜日

Raspberry Pi B + python3 + flask + i2c + PWM まず LCD その2

今回は、前回の続きです。
前回の Flask の Web アプリから入力した文字列を、I2C 接続された LCD に表示させます。

i2c 関係のプログラムを追加します。
i2c_lcd.py です。
前回の「pwm_flask」の下、「pwm_flask.py」と同じ場所に置きます。
ソースはこれで clone できます。
$ git clone git@bitbucket.org:nagasako/pwm_flask.git

ディレクトリの構造はこのようになります。
pwm_flask/
├── i2c_lcd.py
├── pwm_flask.py
├── static
│   └── style.css
└── templates
    ├── base.html
    └── input_text_for_lcd.html

以前は、root でなければ実行できませんでした。
現在は、root でなくても i2c にアクセスできるので、ウェブアプリにしてもそのまま実行できます。

i2c_lcd.py
#!/usr/bin/env python
# coding: UTF-8
'''
$ sudo apt-get install python-smbus i2c-tools

comment out this line in /etc/modprobe.d/raspi-blacklist.conf
blacklist i2c-bcm2708

add the following lines to /etc/modules
 i2c-dev
 i2c-bcm2708
and then reboot.

search all addr.
$ sudo i2cdetect -y 1
'''

import smbus
import time
import datetime


class st7032i:
    def __init__(self, addr=0x3e, ch=1, contrast=0x20):
        try:
            self.addr = addr
            self.ch = ch
            self.bus = smbus.SMBus(ch)
            self.contrast = contrast
            self.reset()
            self.enable = True
        except:
            self.enable = False

    def reset(self):
        contrast_h = 0x70 | (self.contrast & 0x0f)
        contrast_l = 0x54 | ((self.contrast >> 4) & 0x03)
        self.bus.write_i2c_block_data(
            self.addr, 0, [0x38, 0x39, 0x14, contrast_h, contrast_l, 0x6c])
#         // ST7032 Initial Program Code Example For 8051 MPU(8 Bit Interface) の初期化サンプルの通り
#         // Function Set : 8bit bus mode, 2-line mode,normal font,normal instruction mode
#         LCD_write(LCD_RS_CMD, 0b00111000, fd);      // 0x38
#         // Function Set : extension instruction mode
#         LCD_write(LCD_RS_CMD, 0b00111001, fd);      // 0x39
#         // Internal OSC frequency(extension instruction mode)
#         LCD_write(LCD_RS_CMD, 0b00010100, fd);      // 0x14
#         // Contrast set(extension instruction mode) コントラスト値下位4bit設定
#         LCD_write(LCD_RS_CMD, 0b01110000 | (LCD_CONTRAST & 0xF), fd); // 0x78 = 0x70 + 0x8
#         // Power/ICON/Contrast set(extension instruction mode) コントラスト値上位2bit設定
#         LCD_write(LCD_RS_CMD, 0b01011100 | ((LCD_CONTRAST >> 4) & 0x3), fd); // 0x5c + 0
#         // Follower control。internal follower on,
#         LCD_write(LCD_RS_CMD, 0b01101100, fd);      // 0x6c

        time.sleep(0.25)
        self.bus.write_i2c_block_data(self.addr, 0, [0x0c, 0x01, 0x06])
#         // Function Set。normal instruction mode。
#         // LCD_write(LCD_RS_CMD, 0b00111000, fd);   // 0x38
#         // Display On
#         LCD_write(LCD_RS_CMD, 0b00001100, fd);      // 0x0c
#         // Clear Display
#         LCD_write(LCD_RS_CMD, 0b00000001, fd);      // 0x01
#         // Entry Mode Set
#         LCD_write(LCD_RS_CMD, 0b00000110, fd);      // 0x06
        time.sleep(0.05)

    def clear(self):
        if self.enable:
            self.bus.write_i2c_block_data(self.addr, 0, [0x01])

    def mov_to(self, row=0, col=0):
        if self.enable:
            self.bus.write_i2c_block_data(self.addr, 0, [0x80 + 0x40 * row + col])

    def put_str(self, the_str):
        if self.enable:
            self.bus.write_i2c_block_data(self.addr, 0x40, list(map(ord, the_str)))


if __name__ == '__main__':
    try:
        my_lcd = st7032i(0x3e, 1)
        if True and my_lcd.enable:
            while True:
                time_str = datetime.datetime.now().strftime("%H:%m:%S")
                date_str = datetime.datetime.now().strftime("%y/%m/%d")
                my_lcd.clear()
                my_lcd.mov_to(0, 0)
                my_lcd.put_str(date_str)
                my_lcd.mov_to(1, 0)
                my_lcd.put_str(time_str)
                print(date_str + ' ' + time_str)
                time.sleep(1)
        # if False and my_lcd.enable:
        #     light_channel = 0
        #     adc = SpiAdc(light_channel)
        #     if adc is not None and my_lcd.enable:
        #         # Define delay between readings
        #         delay = 1
        #         while True:
        #             # Read the light sensor data
        #             light_level = adc.readChannel()
        #             # light_volts = adc.convertVolts(light_level, 2)

        #             # Print out results
        #             # print("Light: {} ({}V)".format(light_level,light_volts))
        #             # print("%4d %1.2f" % (light_level, light_volts))
        #             the_str = "    " + str(light_level)
        #             my_lcd.mov_to(0, 0)
        #             my_lcd.put_str(the_str[-4:])
        #             # Wait before repeating loop
        #             time.sleep(delay)

    except:
        print("Error accessing default I2C bus")

pwm_flask.py は一部修正です。
#!/usr/bin/env python
# coding: UTF-8
from flask import Flask, request, redirect
from flask import url_for, render_template, flash
# from flask import session, g, abort
from i2c_lcd import st7032i
import datetime


DEBUG = True
SECRET_KEY = 'something_secret_pwm_flask'
USERNAME = 'admin'
PASSWORD = 'adminpass'

app = Flask(__name__)
app.config.from_object(__name__)
# app.run(host='0.0.0.0', port=5000)


@app.route('/')
def input_text_for_lcd():
    return render_template('input_text_for_lcd.html')


@app.route('/send_text', methods=['POST'])
def send_text():
    date_str = datetime.datetime.now().strftime("%y/%m/%d")
    time_str = datetime.datetime.now().strftime("%H:%m:%S")
    msg_text = request.form['msg_text']
    print('send_text msg_text=', msg_text)
    my_lcd = st7032i(0x3e, 1)
    my_lcd.clear()
    my_lcd.mov_to(0, 0)
    my_lcd.put_str(time_str)
    my_lcd.mov_to(1, 0)
    if msg_text:
        my_lcd.put_str(msg_text)
    flash('New entry was successfully posted "' + msg_text + '" at ' + date_str + ' ' + time_str)
    return redirect(url_for('input_text_for_lcd'))


if __name__ == '__main__':
    app.run()

実行はこう。python3 でも動きます。
$ export FLASK_APP=pwm_flask.py
$ python -m flask run --host='0.0.0.0'

プログラム中に「app.run(host='0.0.0.0')」とすれば、他のパソコンからアクセスできるはずなのですが、うまくいかなかったので、export を使う方式にしています。
文字列出力にある「list(map(ord, the_str))」は、python3 の仕様変更のため list が必要になっています。



2016年12月27日火曜日

Raspberry Pi B + python3 + flask + i2c + PWM まず LCD

flask で簡単なサイトを作って、そこで入力されたメッセージを、I2C で接続された LCD パネルに出力してみます。

I2C 接続の LCD に出力するプログラムは前回の「Raspberry Pi B + python3 + flask + i2c + PWM 準備」です。
これに Flask の入力画面をくっつけます。

今回は、Flask で文字入力するところまでです。

venv は問題があったので、やめておきます。
まず、virtual env でやりたいので、環境を作ります。
pi@raspberrypi:~/Documents $ python3 -m venv env_flask
pi@raspberrypi:~/Documents $ cd env_flask/
pi@raspberrypi:~/Documents/env_flask $ mkdir proj
pi@raspberrypi:~/Documents/env_flask $ cd proj
pi@raspberrypi:~/Documents/env_flask/proj $ 

ここで、環境を切り替えておきます。
pi@raspberrypi:~/Documents/env_flask/proj $ source ../bin/activate
(env_flask) pi@raspberrypi:~/Documents/env_flask/proj $ 

python3 では動かないところがあるので、python2 でやります。
Jessie Lite ではこれ。
$ sudo apt-get install python-pip

Flask のディレクトリを作ります。チュートリアルを参考にしています。
$ mkdir pwm_flask
$ cd pwm_flask/
$ mkdir static
$ mkdir templates

ディレクトリ pwm_flask の下にプログラムの主処理部分となる pwm_flask.py を置きます。
$ cat pwm_flask.py
#!/usr/bin/env python
# coding: UTF-8
from flask import Flask, request, session, g, redirect
from flask import url_for, abort, render_template, flash

DEBUG = True
SECRET_KEY = 'something_secret_pwm_flask'
USERNAME = 'admin'
PASSWORD = 'adminpass'

app = Flask(__name__)
app.config.from_object(__name__)


@app.route('/')
def input_text_for_lcd():
    return render_template('input_text_for_lcd.html')


@app.route('/send_text', methods=['POST'])
def send_text():
    msg_text = request.form['msg_text']
    print('send_text msg_text=', msg_text)
    flash('New entry was successfully posted ' + msg_text)
    return redirect(url_for('input_text_for_lcd'))


if __name__ == '__main__':
    app.run()

input_text_for_lcd() は、GET で呼ばれた時の画面表示データを作ります。何もしていません。テンプレートに「input_text_for_lcd.html」を使っています。
send_text() は、先ほどのテプレートにある form で送信ボタンをクリックした時に実行される処理が書かれています。POST でやってきた時の処理です。
ここの print はデバッグ用です。
flash() は、次の画面でメッセージを表示させるための関数です。珍しいので使ってみました。

テンプレートです。
$ cat templates/input_text_for_lcd.html 
{% extends "base.html" %}
{% block body %}
  <form action="{{ url_for('send_text') }}" method=post>
    <dl>
      <dt>Message:
      <dd><input type="text" size="30" name="msg_text">
      <dd><input type="submit" value="send">
    </dl>
  </form>
  <div>
  {% with messages = get_flashed_messages() %}
    {% if messages %}
      <ul class=flashes>
      {% for message in messages %}
        <li>{{ message }}</li>
      {% endfor %}
      </ul>
    {% endif %}
  {% endwith %}
  </div>
{% endblock %}

ここで使われている base.html です。
$ cat templates/base.html 
<!doctype html>
<html>
<head>
<title>PWM_Flask</title>
<meta charset="utf-8">
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
  {% block body %}{% endblock %}
</body>
</html>

形だけの style です。
$ cat static/style.css 
body {
    font-family: sans-serif;
    background: #eee;
}

ディレクトリの構造はこのようになっています。
pwm_flask/
├── pwm_flask.py
├── static
│   └── style.css
└── templates
    ├── base.html
    └── input_text_for_lcd.html

Jessie Lite の場合は、これを別途インストールします。
仮想環境を使わない場合で、Lite でない時はインストールする必要はありません。
(env_flask) pi@raspberrypi:~/Documents/env_flask/proj/pwm_flask $ sudo apt-get install python3-smbus i2c-tools
(env_flask) pi@raspberrypi:~/Documents/env_flask/proj/pwm_flask $ pip install flask
(env_flask) pi@raspberrypi:~/Documents/env_flask/proj/pwm_flask $ pip install RPIO
$ sudo apt-get install python3-smbus i2c-tools
$ sudo pip install flask

Lite でない時はもう入っているはずです。
$ pip freeze
Flask==0.12
Jinja2==2.8
MarkupSafe==0.23
RPi.GPIO==0.6.3
Werkzeug==0.11.13
argparse==1.2.1
chardet==2.3.0
click==6.6
colorama==0.3.2
html5lib==0.999
itsdangerous==0.24
ndg-httpsclient==0.3.2
pyOpenSSL==0.13.1
pyasn1==0.1.7
python-apt==0.9.3.12
requests==2.4.3
six==1.8.0
smbus==1.1
urllib3==1.9.1
wheel==0.24.0
wsgiref==0.1.2

python のバージョン確認です。
$ python -V
Python 2.7.9

実行です。
Jessie Lite の場合、window 環境はないので、外のパソコンから見に行けるように実行します。
$ export FLASK_APP=pwm_flask.py
$ flask run --host=0.0.0.0

export は毎回ではなく、一度だけで OK。
flask run の代わりにこれでも大丈夫。これなら python3 でも確認できます。
$ python -m flask run --host='0.0.0.0'


表示するには、Raspberry pi の ip アドレス、例えば 192.168.1.234 であれば 192.168.1.234:5000 で見に行けます。

Lite ではない Jessie の時は、ブラウザが動くので、これで見に行けます。
$ python pwm_flask.py
こちらの場合は、localhost:5000 で表示できます。


何かを入力すると、入力したメッセージが表示されます。
今回はここまで。
次回は、LCD に表示させます。

2016年12月24日土曜日

Raspberry Pi B + python3 + flask + i2c + PWM 準備


raspbian-jessie-lite に Python3 をインストールして flask で i2c (  + PWM)

今回は、その準備。
jessie light で環境を作ってみます。
light 版ではない場合は、既にインストールされているライブラリなどがあるので、そこのインストールは飛ばしてください。

8GB SD メモリを挿入。SDFormatter でフォーマットして、わかりやすいように JESSIE と名前をつけておきます。

$ diskutil list
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *160.0 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                  Apple_HFS MacHD                   159.2 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
/dev/disk1 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *8.0 GB     disk1
   1:                 DOS_FAT_32 JESSIE                  8.0 GB     disk1s1
$

/dev/disk1 が JESSIE の SD メモリ。
書き込み。
$ diskutil unmountDisk /dev/disk1
Unmount of all volumes on disk1 was successful
$ sudo dd bs=1m if=~/Downloads/2016-11-25-raspbian-jessie-lite.img of=/dev/disk1

しばらく放置。
$ diskutil eject /dev/disk1

Raspberry Pi にメモリを挿入して電源投入。

ログインします。
何も入っていない状態では、842Mbyte しか使っていない。
pi@raspberrypi:~ $ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       7.3G  842M  6.1G  12% /
devtmpfs        214M     0  214M   0% /dev
tmpfs           218M     0  218M   0% /dev/shm
tmpfs           218M  4.5M  213M   3% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           218M     0  218M   0% /sys/fs/cgroup
/dev/mmcblk0p1   63M   21M   43M  34% /boot
pi@raspberrypi:~ $ 



その前に、
$ sudo raspi-config
で ssh を enable にします。sshが不要な方は、disable のままで構いません。
i2c も enable にしておきます。
timezone や画面の周りの黒いところを取ったりなど、お好みで設定。
.ssh/authorized_keys に自分の id_rsa.pub を書き込んでおくと便利。
timezone も Asia/Tokyo にしておきます。
pi@raspberrypi:~ $ sudo raspi-config

Current default time zone: 'Asia/Tokyo'
Local time is now:      Sat Dec 17 11:45:39 JST 2016.
Universal Time is now:  Sat Dec 17 02:45:39 UTC 2016.

pi@raspberrypi:~ $ date
Sat 17 Dec 11:45:52 JST 2016
pi@raspberrypi:~ $ 

それから更新。
$ sudo apt-get update
$ sudo apt-get -y upgrade

$ uname -a
Linux raspberrypi 4.4.34+ #930 Wed Nov 23 15:12:30 GMT 2016 armv6l GNU/Linux
$ sudo rpi-update

最近の jessie lite では not found になるので、インストールしてから。
$ sudo apt-get install rpi-update
$ sudo rpi-update
$ sudo reboot

$ uname -a
Linux raspberrypi 4.4.38+ #938 Thu Dec 15 15:17:54 GMT 2016 armv6l GNU/Linux

python3も入っていない。
$ python -V
Python 2.7.9
$ python3 -V
-bash: python3: command not found

python3 と venv をインストール。それと pip も。
$ sudo apt-get -y install python3
$ python3 -V
Python 3.4.2
$ sudo apt-get -y install python3.4-venv
$ sudo apt-get install python3-pip

ここまでくると 1.1G になってしまう。
pi@raspberrypi:~ $ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       7.3G  1.1G  5.9G  16% /
devtmpfs        214M     0  214M   0% /dev
tmpfs           218M     0  218M   0% /dev/shm
tmpfs           218M  4.5M  213M   3% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           218M     0  218M   0% /sys/fs/cgroup
/dev/mmcblk0p1   63M   21M   43M  34% /boot
pi@raspberrypi:~ $ 

関係のない人は関係がない、wifi の設定をしてしまいます。
線で繋ぐのは面倒なので。
$ sudo sh -c 'wpa_passphrase YourSSID YourPassphrase >> /etc/wpa_supplicant/wpa_supplicant.conf'

wpa_supplicant.conf の中の #psk= の行は削除。
country=GB
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
        ssid="YourSSID"
        #psk="YourPassphrase"
        psk=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
}

固定 ip を設定。dhcpcd.conf の最後のところに追加。
$ sudo nano /etc/dhcpcd.conf

# for MySSID
interface wlan0
static ip_address=192.168.1.234/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1

電源を落として、lan ケーブルを抜いてしまう。
$ sudo shutdown -h now

別のパソコンから ssh で接続。
$ ssh pi@192.168.1.234

pip3 でインストールされているライブラリを見てみます。
$ pip3 freeze
chardet==2.3.0
colorama==0.3.2
html5lib==0.999
requests==2.4.3
six==1.8.0
urllib3==1.9.1
wheel==0.24.0

jessie lite ではほとんど何も入っていません
jessie では flask とか入っていま。



まず、そのいろいろ入っている Jessie でやってみます。
i2cdetect で接続確認。
上は LCD ユニットだけを接続、下は PWM 素子も接続した状態。
LCD ユニットは、秋月電子で売っている ST7032。
pi@raspberrypi:~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --                         
pi@raspberrypi:~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e -- 
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: 70 -- -- -- -- -- -- --                         
pi@raspberrypi:~ $ 


Raspberry Pi Type B から I2C で LCD モジュールに出力するプログラム。
i2c_lcd.py
#!/usr/bin/env python
# coding: UTF-8
import smbus
import RPi.GPIO as GPIO
import time
import math
import datetime
# from pypwm.spi_adc import SpiAdc


class st7032i:
    def __init__(self, addr=0x3e, ch=1, contrast=0x20):
        try:
            self.addr = addr
            self.ch = ch
            self.bus = smbus.SMBus(ch)
            self.contrast = contrast
            self.reset()
            self.enable = True
        except:
            self.enable = False

    def reset(self):
        contrast_h = 0x70 | (self.contrast & 0x0f)
        contrast_l = 0x54 | ((self.contrast >> 4) & 0x03)
        self.bus.write_i2c_block_data(
            self.addr, 0, [0x38, 0x39, 0x14, contrast_h, contrast_l, 0x6c])
#         // ST7032 Initial Program Code Example For 8051 MPU(8 Bit Interface) の初期化サンプルの通り
#         // Function Set : 8bit bus mode, 2-line mode,normal font,normal instruction mode
#         LCD_write(LCD_RS_CMD, 0b00111000, fd);      // 0x38
#         // Function Set : extension instruction mode
#         LCD_write(LCD_RS_CMD, 0b00111001, fd);      // 0x39
#         // Internal OSC frequency(extension instruction mode)
#         LCD_write(LCD_RS_CMD, 0b00010100, fd);      // 0x14
#         // Contrast set(extension instruction mode) コントラスト値下位4bit設定
#         LCD_write(LCD_RS_CMD, 0b01110000 | (LCD_CONTRAST & 0xF), fd); // 0x78 = 0x70 + 0x8
#         // Power/ICON/Contrast set(extension instruction mode) コントラスト値上位2bit設定
#         LCD_write(LCD_RS_CMD, 0b01011100 | ((LCD_CONTRAST >> 4) & 0x3), fd); // 0x5c + 0
#         // Follower control。internal follower on,
#         LCD_write(LCD_RS_CMD, 0b01101100, fd);      // 0x6c

        time.sleep(0.25)
        self.bus.write_i2c_block_data(self.addr, 0, [0x0c, 0x01, 0x06])
#         // Function Set。normal instruction mode。
#         // LCD_write(LCD_RS_CMD, 0b00111000, fd);   // 0x38
#         // Display On
#         LCD_write(LCD_RS_CMD, 0b00001100, fd);      // 0x0c
#         // Clear Display
#         LCD_write(LCD_RS_CMD, 0b00000001, fd);      // 0x01
#         // Entry Mode Set
#         LCD_write(LCD_RS_CMD, 0b00000110, fd);      // 0x06
        time.sleep(0.05)

    def clear(self):
        if self.enable:
            self.bus.write_i2c_block_data(self.addr, 0, [0x01])

    def mov_to(self, row=0, col=0):
        if self.enable:
            self.bus.write_i2c_block_data(self.addr, 0, [0x80 + 0x40 * row + col])

    def put_str(self, the_str):
        if self.enable:
            self.bus.write_i2c_block_data(self.addr, 0x40, map(ord, the_str))


if __name__ == '__main__':
    try:
        my_lcd = st7032i(0x3e, 1)
        if True and my_lcd.enable:
            while True:
                time_str = datetime.datetime.now().strftime("%H:%m:%S")
                date_str = datetime.datetime.now().strftime("%y/%m/%d")
                my_lcd.clear()
                my_lcd.mov_to(0, 0)
                my_lcd.put_str(date_str)
                my_lcd.mov_to(1, 0)
                my_lcd.put_str(time_str)
                print(date_str + ' ' + time_str)
                time.sleep(1)
        # if False and my_lcd.enable:
        #     light_channel = 0
        #     adc = SpiAdc(light_channel)
        #     if adc is not None and my_lcd.enable:
        #         # Define delay between readings
        #         delay = 1
        #         while True:
        #             # Read the light sensor data
        #             light_level = adc.readChannel()
        #             # light_volts = adc.convertVolts(light_level, 2)

        #             # Print out results
        #             # print("Light: {} ({}V)".format(light_level,light_volts))
        #             # print("%4d %1.2f" % (light_level, light_volts))
        #             the_str = "    " + str(light_level)
        #             my_lcd.mov_to(0, 0)
        #             my_lcd.put_str(the_str[-4:])
        #             # Wait before repeating loop
        #             time.sleep(delay)

    except:
        print("Error accessing default I2C bus")



今度は Jessie Lite でやってみる。
その前に確認。
pi@raspberrypi:~ $ cat /etc/modules
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.

i2c-dev
pi@raspberrypi:~ $ 

i2c-dev が入っているので、OK。
入っていない時は、sudo rasps-config か、手で入れる。

必要なライブラリのインストール。
pi@raspberrypi:~ $ sudo apt-get install python-smbus i2c-tools

i2cdetect で確認。

pi@raspberrypi:~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e -- 
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: 70 -- -- -- -- -- -- --                         
pi@raspberrypi:~ $ 

OK。

先ほどのソースを実行してみる。
ファイル名を「i2c_lcd.py」としています。
pi@raspberrypi:~ $ python i2c_lcd.py
16/12/24 14:12:35
16/12/24 14:12:36
16/12/24 14:12:37
16/12/24 14:12:38
16/12/24 14:12:39
16/12/24 14:12:40

16/12/24 14:12:41

LCD に、時刻が表示されている。OK。

今度は、PCA9685 で LED をランダムに明るさを変えてみます。
0x40 が PCA9685 のアドレス。
PCA9685 が3つつながっていても対応できるようにしています。
これだけのファイルを置きます。
pi@raspberrypi:~ $ ls -l *.py
-rw-r--r-- 1 pi pi  500 Dec 24 14:23 const.py
-rw-r--r-- 1 pi pi 4496 Dec 24 14:15 i2c_lcd.py
-rw-r--r-- 1 pi pi 5480 Dec 24 14:41 i2c_pwm_demo.py
-rw-r--r-- 1 pi pi 5065 Dec 24 14:28 i2c_pwm_lib.py
-rw-r--r-- 1 pi pi 3737 Dec 24 14:32 pypwm_db.py
pi@raspberrypi:~ $ 

「i2c_lcd.py」は先ほどの LCD 出力デモのファイル。
「i2c_pwm_demo.py」が main のプログラム。実行するのはこれ。

「pypwm_db.py」は、現在の明るさをデータベースに格納して、次の電源が入った時に、明るさの設定を維持するようにしています。

pi@raspberrypi:~ $ cat const.py
#!/usr/bin/env python
# coding: UTF-8

import os

kRevision = '110'
kPort = '5555'
kFilterPWM = 'pwm'

kMinVal = 0
kMaxVal = 4095
kMinValSliderZero = 0
kMaxValSliderZero = 1023
kDefaultVal = 3600
kChNum = 16
kChMin = 0
kChMax = kChNum
kBlockNum = (kChNum / 2)
kEachCh = 16
kPwmFreq = 200
kDemoDisableFile = '/tmp/demo_disable'

os_name = os.uname()
if os_name[0] == 'Darwin':
    dbname = '/Users/pi/pwm_prefs.db'
elif os_name[0] == 'Linux':
    dbname = '/home/pi/pwm_prefs.db'
pi@raspberrypi:~ $ 

pi@raspberrypi:~ $ cat i2c_pwm_demo.py
#!/usr/bin/python
# coding: UTF-8

from i2c_pwm_lib import PWM
import i2c_lcd
import time
import datetime
import random
import os
import pypwm_db
import const


class TestPWM():
    def __init__(self, addr=0x40, freq=200):
        self.prefs = const.dbname
        self.kChNum = const.kChNum
        self.pwm = PWM(addr, debug=True)
        if self.pwm.i2c is None:
            self.pwm = None
        if self.pwm is not None:
            self.pwm.setPWMFreq(freq)
            self.lcd = None     # i2c_lcd.st7032i(0x3e, 1)
            # if self.lcd.bus is None:
            #    self.lcd = None
            self.debug = False
            self.pwm.debug = True

    def outPWM(self, ch, width):
        if self.pwm is not None:
            self.pwm.setPWM(ch, 0, width)
        if self.debug:
            if self.lcd is not None:
                self.lcd.mov_to(0, 0)
                the_str = 'ch = %2d' % ch
                self.lcd.put_str(the_str)
                self.lcd.mov_to(1, 0)
                the_str = '%4d' % width
                self.lcd.put_str(the_str)

    def read_prefs(self, ch):
        pypwmdb = pypwm_db.pypwm_db(self.prefs)
        width = pypwmdb.get_width_of_ch(ch)
        return width

    def init_all_leds(self):
        pwm_vals = []
        for ch in range(self.kChNum + 1):
            the_val = self.read_prefs(ch)
            pwm_vals.append(the_val)
            if self.debug:
                print('init_all_leds append ch = %d, val = %d' % (ch, the_val))
        for idx in range(self.kChNum):
            pwm_val = pwm_vals[idx + 1]
            self.outPWM(idx, pwm_val)
            if self.debug:
                print('init_all_leds outPWM ch = %d, val = %d' % (idx, pwm_val))


class DemoPWM():
    def __init__(self):
        self.kChNum = const.kChNum
        self.kChMax = self.kChNum - 1
        self.kMaxPWM = const.kMaxVal
        self.kMinPWM = 1
        self.ch_list = range(self.kChNum)
        random.shuffle(self.ch_list)
        self.vals = []
        self.kmms = []
        self.mms = []
        self.demo = [1, 1, 2, 3, 6, 10, 18, 33, 61, 111, 203, 370, 674, 1228, 2239, 4081]
        self.kDemoDisableFile = const.kDemoDisableFile
        for ch in self.ch_list:
            self.vals.append(self.kMinPWM)
            mm = 1.05 + float(ch) / 40.0
            self.kmms.append(mm)
            self.mms.append(mm)

    def demo_01(self, test_pwm):
        for ch in self.ch_list:
            val = self.vals[ch]
            kmm = self.kmms[ch]
            mm = self.mms[ch]
            val *= mm
            if val > self.kMaxPWM:
                val = self.kMaxPWM
                mm = 1.0 / kmm
            elif val < self.kMinPWM:
                val = self.kMinPWM
                mm = kmm
            test_pwm.outPWM(ch, int(val))
            self.vals[ch] = val
            self.mms[ch] = mm

    def demo_02(self, test_pwm, offset, diff):
        for ch in self.ch_list:
            index = ch + offset
            if index > self.kChMax:
                index -= self.kChNum
            elif index < 0:
                index += self.kChNum
            val = self.demo[index]
            test_pwm.outPWM(ch, val)
        offset += diff
        if offset >= self.kChNum:
            offset = self.kChMax
            diff = -1
        elif offset < 0:
            offset = 0
            diff = 1
        return (offset, diff)

    def is_demo_disable(self):
        demo_disable = False
        if os.path.isfile(self.kDemoDisableFile):
            demo_disable = True
        return demo_disable


if __name__ == '__main__':
    while (True):
        if True or not os.path.isfile(const.kDemoDisableFile):
            test_pwm = TestPWM(0x40, const.kPwmFreq)
            if test_pwm.pwm is not None:
                test_pwm.init_all_leds()
            # time.sleep(90)

            if test_pwm.pwm is None:
                test_pwm = None
            test_2_pwm = TestPWM(0x41, const.kPwmFreq)
            if test_2_pwm.pwm is None:
                test_2_pwm = None
            if test_pwm is not None:
                my_lcd = test_pwm.lcd
            prev_time = round(time.time())

            demo_pwm = DemoPWM()
            if demo_pwm.is_demo_disable():
                pass
            else:
                offset = 0
                diff = 1
                for xx in range(1000):
                    if test_pwm.pwm is not None:
                        demo_pwm.demo_01(test_pwm)
                        # (offset, diff) = demo_pwm.demo_02(test_pwm, offset, diff)

                    if test_2_pwm is not None:
                        # demo_pwm.demo_01(test_2_pwm)
                        (offset, diff) = demo_pwm.demo_02(test_2_pwm, offset, diff)

                    time.sleep(0.001)

                    if round(time.time()) > prev_time:
                        prev_time = round(time.time())
                        time_str = datetime.datetime.now().strftime("%H:%m:%S")
                        date_str = datetime.datetime.now().strftime("%y/%m/%d")
                        if my_lcd is not None:
                            my_lcd.clear()
                            my_lcd.mov_to(0, 0)
                            my_lcd.put_str(date_str)
                            my_lcd.mov_to(1, 0)
                            my_lcd.put_str(time_str)
        else:
            time.sleep(1)

pi@raspberrypi:~ $ 

pi@raspberrypi:~ $ cat i2c_pwm_lib.py
#!/usr/bin/env python
# coding: UTF-8

import time
import math
import smbus

# ============================================================================
# PCA9685 16-Channel PWM Servo Driver
# ============================================================================


class i2c_lib():
    @staticmethod
    def getPiRevision():
        "Gets the version number of the Raspberry Pi board"
        try:
            with open('/proc/cpuinfo', 'r') as f:
                for line in f:
                    if line.startswith('Revision'):
                        return 1 if line.rstrip()[-1] in ['1', '2'] else 2
        except:
            return 0

    @staticmethod
    def getPiI2CBusNumber():
        # Gets the I2C bus number /dev/i2c#
        return 1 if i2c_lib.getPiRevision() > 1 else 0

    def __init__(self, address, busnum=-1, debug=False):
        self.address = address
        self.bus = smbus.SMBus(busnum if busnum >= 0 else i2c_lib.getPiI2CBusNumber())
        self.debug = debug

    def errMsg(self, msg):
        print("Error in %s accessing 0x%02X: Check your I2C address" % (msg, self.address))
        return -1

    def write8(self, reg, value):
        "Writes an 8-bit value to the specified register/address"
        try:
            self.bus.write_byte_data(self.address, reg, value)
            if self.debug:
                print("I2C: Wrote 0x%02X to register 0x%02X" % (value, reg))
            return 0
        except IOError:
            return self.errMsg('write8(reg=0x%02x, value=%d)' % (reg, value))

    def readU8(self, reg):
        "Read an unsigned byte from the I2C device"
        try:
            result = self.bus.read_byte_data(self.address, reg)
            if self.debug:
                print(
                    "I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % (
                        self.address, result & 0xFF, reg))
            return result
        except IOError:
            return self.errMsg('readU8(reg=0x%02x)' % (reg))


class PWM():
    # Registers/etc.
    __SUBADR1 = 0x02
    __SUBADR2 = 0x03
    __SUBADR3 = 0x04
    __ALLCALLADR = 0x05
    __MODE1 = 0x00
    __MODE2 = 0x01
    __PRESCALE = 0xFE
    __LED0_ON_L = 0x06
    __LED0_ON_H = 0x07
    __LED0_OFF_L = 0x08
    __LED0_OFF_H = 0x09
    __ALLLED_ON_L = 0xFA
    __ALLLED_ON_H = 0xFB
    __ALLLED_OFF_L = 0xFC
    __ALLLED_OFF_H = 0xFD

    def __init__(self, address=0x40, debug=False):
        self.i2c = i2c_lib(address, debug=False)
        self.address = address
        self.debug = debug
        if (self.debug):
            print("Reseting PCA9685 (%02x)" % (address))
        result = self.i2c.write8(self.__MODE1, 0x00)
        if self.debug:
            print("result = %d" % (result))
        if result < 0:
            self.i2c = None

        if (self.debug):
            if self.i2c is not None:
                result = self.i2c.readU8(self.__ALLCALLADR)
                print('__ALLCALLADR = %02x' % (result))
                result = self.i2c.readU8(self.__SUBADR1)
                print('__SUBADR1 = %02x' % (result))
                result = self.i2c.readU8(self.__SUBADR2)
                print('__SUBADR2 = %02x' % (result))
                result = self.i2c.readU8(self.__SUBADR3)
                print('__SUBADR3 = %02x' % (result))

    def setPWMFreq(self, freq):
        "Sets the PWM frequency"
        prescaleval = 25000000.0  # 25MHz
        prescaleval /= 4096.0  # 12-bit
        prescaleval /= float(freq)
        prescaleval = int(round(prescaleval)) - 1
        if (self.debug):
            print("Setting PWM frequency to %d Hz" % (freq))
            print("Estimated pre-scale: %d" % (prescaleval))
        if (self.debug):
            print("Final pre-scale: %d" % (prescaleval))

        oldmode = self.i2c.readU8(self.__MODE1)
        newmode = (oldmode & 0x7F) | 0x10  # sleep
        self.i2c.write8(self.__MODE1, newmode)  # go to sleep
        self.i2c.write8(self.__PRESCALE, prescaleval)
        self.i2c.write8(self.__MODE1, oldmode)
        time.sleep(0.005)
        self.i2c.write8(self.__MODE1, oldmode | 0x80)

        # self.i2c.write8(self.__MODE2, 0x10) # INVRT = 1, OUTDRV = 0
        if (self.debug):
            result = self.i2c.readU8(self.__MODE1)
            print('__MODE1 = %02x' % (result))
            result = self.i2c.readU8(self.__MODE2)
            print('__MODE2 = %02x' % (result))

    def setPWM(self, channel, on, off):
        "Sets a single PWM channel"
        self.i2c.write8(self.__LED0_ON_L + 4 * channel, on & 0xFF)
        self.i2c.write8(self.__LED0_ON_H + 4 * channel, on >> 8)
        self.i2c.write8(self.__LED0_OFF_L + 4 * channel, off & 0xFF)
        self.i2c.write8(self.__LED0_OFF_H + 4 * channel, off >> 8)
        # print('ch = %d, on = %d, off = %d' % (channel, on, off))

    def all_led_on(self):
        self.i2c.write8(self.__ALLLED_ON_L, 0x00)
        self.i2c.write8(self.__ALLLED_ON_H, 0x10)

    def all_led_off(self):
        self.i2c.write8(self.__ALLLED_OFF_L, 0x00)
        self.i2c.write8(self.__ALLLED_OFF_H, 0x10)
pi@raspberrypi:~ $ 

pi@raspberrypi:~ $ cat pypwm_db.py
#!/usr/bin/env python
# coding: UTF-8

import sqlite3
import const


class pypwm_db():
    def __init__(self, fname):
        self.con = None
        self.fname = fname
        if fname is not None:
            self.con = sqlite3.connect(fname)   # , isolation_level=None
            no_db = True
            try:
                cur = self.con.cursor()
                cur.execute('SELECT width FROM pwmvals WHERE ch=1')
                no_db = False
            except Exception as ex:
                print(ex)
            self.con.close()

            if no_db:
                # Create table
                self.con = sqlite3.connect(fname)
                cur = self.con.cursor()
                cur.execute('''CREATE TABLE pwmvals (ch INTEGER, width INTEGER,
                                block INTEGER, which INTEGER,
                                row INTEGER, col INTEGER,
                                enable INTEGER)''')
                for ch in range(200):
                    if 1 <= ch <= const.kChNum:
                        width = const.kDefaultVal
                        block = (ch - 1) % 16
                        which = 0
                        row = 0
                        col = 0
                        enable = 1
                        tp = (ch, width, block, which, row, col, enable,)
                        cur.execute(''' INSERT INTO pwmvals VALUES (?, ?, ?, ?, ?, ?, ?)''', tp)
                    else:
                        width = 0
                        block = 0
                        which = 0
                        row = 0
                        col = 0
                        enable = 0
                        tp = (ch, width, block, which, row, col, enable,)
                        cur.execute(''' INSERT INTO pwmvals VALUES (?, ?, ?, ?, ?, ?, ?)''', tp)
                self.con.commit()
                cur.close()

    def connect(self):
        if self.fname is not None:
            self.con = sqlite3.connect(self.fname)
        else:
            self.con = None
        return self.con

    def get_width_of_ch(self, ch):
        width = None
        self.connect()
        if self.con is not None:
            try:
                cur = self.con.cursor()
                tp = (ch,)
                cur.execute('SELECT width FROM pwmvals WHERE ch=?', tp)
                for row in cur:
                    width = row[0]
                cur.close()
            except Exception as ex:
                print(ex)
                width = None
        return width

    def put_width_of_ch(self, ch, width):
        sql = 'UPDATE pwmvals SET width=? WHERE ch=?;'
        self.connect()
        if self.con is not None:
            try:
                cur = self.con.cursor()
                tp = (width, ch,)
                cur.execute(sql, tp)
                self.con.commit()
                cur.close()
            except Exception as ex:
                print(ex)


if __name__ == '__main__':
    dbname = const.dbname
    pypwmdb = pypwm_db(dbname)
    widths = []
    ch_max = const.kChNum + 1

    """
    # save prev width
    for ch in range(ch_max):
        width = pypwmdb.get_width_of_ch(ch)
        widths.append(width)

    # init table
    for ch in range(ch_max):
        width = ch * 2
        pypwmdb.put_width_of_ch(ch, width)

    # read table
    for ch in range(ch_max):
        width = pypwmdb.get_width_of_ch(ch)
        print("ch = %3d, width = %4d" % (ch, width))

    # restore table
    for ch in range(ch_max):
        width = widths[ch]
        pypwmdb.put_width_of_ch(ch, width)
    """

    # read table
    for ch in range(ch_max):
        width = pypwmdb.get_width_of_ch(ch)
        print("ch = %3d, width = %4d" % (ch, width))
pi@raspberrypi:~ $ 

PCA9685 に接続された16個の LED がランダムっぽく明滅します