PCA9685 は「16-channel, 12-bit PWM Fm+ I²C-bus LED controller」です。秋月やアマゾンでも製品が売られています。ここでは、表面実装タイプの素子を秋月の変換基板に乗せて使っています。
画面にスライダを表示して、1チャンネル毎に明るさを制御してみますが、今回はスライダを作るところまでにします。
前回のものに修正を加えます。
ディレクトリ構造を変更しました。
こうなります。
pwm_flask,py が主となるファイルです。
pwm_flask/
├── i2c_lcd.py
├── i2c_pwm.py
├── pwm_flask.py
├── static
│ ├── css
│ │ └── style.css
│ └── js
│ ├── jquery-3.1.1.min.js
│ └── jquery-3.1.1.min.map
└── templates
├── base.html
├── input_text_for_lcd.html
└── set_pwm.html
スライダで値をセットして送信ボタン、では寂しいので、スライダをグリグリしている時の値も送信されるようにします。
そうするためには Ajax を使うことになります。
そのために jQuery を使います。static/js におきます。
これで clone して、「9119531」でチェックアウトすると、今回のバージョンになります。
$ git clone git@bitbucket.org:nagasako/pwm_flask.git $ cd pwm_flask $ git checkout 9119531
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
self.prev_now = 0
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")
i2c_pwm.py
#!/usr/bin/env python
# coding: UTF-8
'''
i2c_pwm.py
'''
def set_pwm(ch, val):
print('sned_pwm: ', ch, val)
if __name__ == '__main__':
set_pwm(1, 123)
pwm_flask.py
#!/usr/bin/env python
# coding: UTF-8
'''
$ export FLASK_APP=pwm_flask.py
$ python -m flask run --host=0.0.0.0
'''
from threading import Lock
from flask import Flask, request, redirect
from flask import url_for, render_template, flash
# from flask import session, g, abort
from i2c_pwm import set_pwm
import datetime
import os
import json
OS_NAME = os.uname()
if 'raspberrypi' in OS_NAME:
# Raspberry Pi
RASPI = True
from i2c_lcd import st7032i
my_lcd = st7032i(0x3e, 1)
lock = Lock()
else:
RASPI = False
print(OS_NAME, RASPI)
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']
if RASPI:
# my_lcd = st7032i(0x3e, 1)
with lock:
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)
else:
print('send_text: ', msg_text)
flash('New entry was successfully posted "' + msg_text + '" at ' + date_str + ' ' + time_str)
return redirect(url_for('input_text_for_lcd'))
class DevPCA:
def __init__(self, ch, val):
self.ch = ch
self.val = val
@app.route('/set_pwm', methods=['GET'])
def set_pwm_get():
dev_list = []
for ch in range(16):
dev_pca = DevPCA(ch, 50)
dev_list.append(dev_pca)
return render_template('set_pwm.html', pca_channels=dev_list)
@app.route('/set_pwm_post', methods=['POST'])
def set_pwm_post():
ch_str = request.form['ch']
val_str = request.form['val']
now_str = request.form['now']
# print('set_pwm_post ch_str, val_str, now_str=', ch_str, val_str, now_str)
ch = int(ch_str)
val = float(val_str)
now = int(now_str)
if RASPI:
# my_lcd = st7032i(0x3e, 1)
with lock:
if now > my_lcd.prev_now:
print(now, my_lcd.prev_now)
my_lcd.prev_now = now
set_pwm(ch, val)
my_lcd.clear()
my_lcd.mov_to(0, 0)
my_lcd.put_str(ch_str)
my_lcd.mov_to(1, 0)
my_lcd.put_str(val_str)
else:
msg_text = "ch={0:2d} val={1:3.1f} now={2:d}".format(ch, val, now)
print('set_pwm_post: ', msg_text)
return json.dumps({'status': 'OK', 'ch': ch, 'val': val})
if __name__ == '__main__':
app.run()
style.css
body {
font-family: sans-serif;
background: #eee;
}
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='css/style.css') }}">
{% block head %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
{% block bottom %}{% endblock %}
</html>
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>
<a href="{{ url_for('set_pwm_get') }}">set_pwm</a>
</div>
<div>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</div>
{% endblock %}
set_pwm.html
{% extends "base.html" %}
{% block head %}
<style>
.range_class {
width: 400px!important;
}
</style>
<script src="{{ url_for('static', filename='js/jquery-3.1.1.min.js') }}"></script>
{% endblock %}
{% block body %}
<form action="" method=post>
<table>
<thead>
<tr>
<th> ch </th>
<th> PCA9685 value </th>
</tr>
</thead>
<tbody>
{% for pca_ch in pca_channels %}
<tr>
<td>{{ pca_ch.ch + 1 }}</td>
<td>
<input type="range" name="{{ pca_ch.ch }}" class="range_class"
value="{{ pca_ch.value }}" min="0" max="100" step="0.1">
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
<div>
<span id="pwm_ch"></span>
<span id="pwm_val"></span>
</div>
<div>
<a href="{{ url_for('input_text_for_lcd') }}">input_text_for_lcd</a>
</div>
<div>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</div>
{% endblock %}
{% block bottom %}
<script type="text/javascript">
<!--
$('.range_class').on('input', function() {
var the_name = $(this).attr('name');
var the_ch = +the_name + 1;
var the_value = $(this).val();
var now = Date.now();
var msg = the_ch + ' value=' + the_value;
var data_str = 'ch=' + the_name;
data_str += '&val=' + the_value;
data_str += '&now=' + now;
$('#pwm_ch').text(the_ch);
$('#pwm_val').text(the_value);
// console.log(msg);
$.ajax({
type: "POST",
url: "{{ url_for('set_pwm_post') }}",
data: data_str,
success: function(resp) {
// console.log('success:' + resp);
},
error: function(resp) {
console.log('error:' + resp);
}
});
});
// -->
</script>
{% endblock %}
実行コマンドはこれです。
pi~/Documents/env_flask/proj/pwm_flask:develop $ export FLASK_APP=pwm_flask.py pi~/Documents/env_flask/proj/pwm_flask:develop $ python -m flask run --host=0.0.0.0
python3 でも動作します。
set_pwm.html の下の方に ajax でスライダの値を送信する処理があります。
ch とその値を送信しています。もうひとつ Date.now() の値も送っています。
これは、例えば0から100%まで値を変えた時、データの発生順で処理されるとは限らないためです。最後は100%になっているはずなのに、受信データの最後が100%になっているとは限らないのです。古いデータを受信した場合はそのデータを捨てるために Date.now() でその時の時刻を利用しています。
set_pwm.py で lock = Lock() をしています。なんだか気持ち悪いので、修正予定です。

0 件のコメント:
コメントを投稿