1
0
Fork 0

Adds basic functionality to web interface; partial function of LCD driver

This commit is contained in:
Shawn Nock 2017-01-12 16:52:59 +00:00
parent 38cee3f4b0
commit fef826f1d3
9 changed files with 205 additions and 4 deletions

View File

@ -9,6 +9,7 @@ setup (name = 'trespassed',
license = "APLv2", license = "APLv2",
keywords = "", keywords = "",
install_requires = ['twisted[tls]', 'sysfs-gpio','crcmod', install_requires = ['twisted[tls]', 'sysfs-gpio','crcmod',
'enum34', 'pyserial','attrs','pyopenssl','klein','jinja2'], 'enum34', 'pyserial','attrs','pyopenssl',
'klein','jinja2','smbus-cffi'],
include_package_data = True, include_package_data = True,
) )

View File

@ -1,3 +1,5 @@
import sys
from twisted.internet import defer, reactor from twisted.internet import defer, reactor
from twisted.python import log from twisted.python import log
@ -45,6 +47,10 @@ def set_output(pin_no, level):
log.msg("Clearing {}".format(pin_no)) log.msg("Clearing {}".format(pin_no))
pin.reset() pin.reset()
def get_output(pin_no):
pin = PINS[pin_no]
return pin.read()
skip = True skip = True
def quit(*args, **kwargs): def quit(*args, **kwargs):
global skip global skip

105
software/trespassed/lcd.py Normal file
View File

@ -0,0 +1,105 @@
import time
from smbus import SMBus
ADDR = 0x20
BUS = 1
LCD_DATA = 1
LCD_CMD = 0
LCD_LINE_ADDR = {0: 0x80,
1: 0xc0}
# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005
LCD_WIDTH = 16
# MCP23008 Registers
IODIR = 0x00
IOCON = 0x05
IOCON_SREAD = (1 << 5)
IOCON_DISSLW = (1 << 4)
IOCON_ODR = (1 << 2)
IOCON_INTPOL = (1 << 1)
GPIO = 0x09
# HD44780 Pins
RS = (1 << 1)
ENABLE = (1 << 2)
DB4 = (1 << 3)
DB5 = (1 << 4)
DB6 = (1 << 5)
DB7 = (1 << 6)
LCD_BACKLIGHT = (1 << 7)
# HD44780 Commands
class LCD(object):
def __init__(self, addr=ADDR):
self.addr = addr
self.bus = SMBus(BUS)
self._init_mcp()
self._init_hd44780()
def _init_mcp(self):
# Set MCP23008 pins as outputs
self._write_reg(IODIR, 0x00)
# Disable incrementing pointer
self._write_reg(IOCON, IOCON_SREAD)
# Enable Backlight
self._write_reg(0x09, 1 << 7)
def _write4(self, bits):
message = (bits << 3) | LCD_BACKLIGHT
self.bus.write_byte(self.addr, message)
self._latch(message)
def write_cmd(self, byte):
self._write4((byte & 0xf0) >> 4)
self._write4((byte & 0x0f))
def _init_hd44780(self):
# Initialize, set to 4bit mode
self._write4(DB5)
self.write_cmd((DB5 << 4) | DB7)
self.write_cmd(1) # Clear Display
self.write_cmd(0 | (DB4 | DB5 | DB6 | DB7)) # Display ON, Cursor ON, Cursor Blinking
self.write_cmd(0 | (DB5 | DB6)) # Entry Mode
def _write_reg(self, reg, val):
self.bus.write_byte_data(self.addr, reg, val)
# def _write(self, byte, backlight=LCD_BACKLIGHT_ON, mode=0):
# bits_high = (mode << 1) | ((byte & 0xF0) << 3) | backlight
# bits_low = (mode << 1) | ((byte & 0xf) << 3) | backlight
# self.bus.write(self.addr, bits_high)
# self._latch(bits_high)
# self.bus.write(self.addr, bits_low)
# self._latch(bits_low)
def _latch(self, bits):
time.sleep(E_DELAY)
self.bus.write_byte(self.addr, bits | ENABLE)
time.sleep(E_PULSE)
self.bus.write_byte(self.addr, (bits & ~ENABLE))
time.sleep(E_DELAY)
# def write(self, line, string):
# string = string.ljust(LCD_WIDTH, " ")[:LCD_WIDTH]
# self._write(line)
# for i in range(LCD_WIDTH):
# self.write_cmd(ord(string[i]) | RS)
LCD_SINGLETON = LCD()
def lcd_write_status(status):
return
lines = status.split('\n')[:2]
for i in range(len(lines)):
LCD_SINGLETON.write(LCD_LINE_ADDR[i], lines[i])

View File

@ -13,7 +13,7 @@ from twisted.application.internet import TimerService
from twisted.internet import protocol, reactor, defer from twisted.internet import protocol, reactor, defer
from twisted.python import log from twisted.python import log
from trespassed.game import input_parse from trespassed.game import input_parse, INPUT_MAP
from trespassed.gpio import set_output, OUTPUTS from trespassed.gpio import set_output, OUTPUTS
from trespassed.email import email_failure from trespassed.email import email_failure
from trespassed.media import play_sound_effect from trespassed.media import play_sound_effect

View File

@ -2,6 +2,7 @@ from twisted.application.internet import TimerService
from twisted.internet import reactor from twisted.internet import reactor
from trespassed.timer import get_timer from trespassed.timer import get_timer
from trespassed.lcd import lcd_write_status
__all__ = ['get_status', 'set_status', __all__ = ['get_status', 'set_status',
'status_service', 'get_log', 'clear_log'] 'status_service', 'get_log', 'clear_log']
@ -12,12 +13,14 @@ class Status(object):
def __init__(self, name, initial_status='Loading...'): def __init__(self, name, initial_status='Loading...'):
self.name = name self.name = name
self.log = [] self.log = []
self.update()
self.set(initial_status) self.set(initial_status)
self.update()
def update(self): def update(self):
self._status1 = "{:.10} {:5}".format(self.name, self._status1 = "{:.10} {:5}".format(self.name,
get_timer().get_human()) get_timer().get_human())
lcd_write_status(self.get())
def set(self, status): def set(self, status):
self._status2 = status self._status2 = status
self.log.append({'when': get_timer().get_human(), self.log.append({'when': get_timer().get_human(),

View File

@ -9,12 +9,17 @@ from twisted.python import log
from twisted.web.server import Site from twisted.web.server import Site
from twisted.web.static import File from twisted.web.static import File
from trespassed.gpio import OUTPUTS, get_output, set_output
from trespassed.status import get_status, get_log from trespassed.status import get_status, get_log
app = Klein() app = Klein()
env = jinja2.Environment( env = jinja2.Environment(
loader=jinja2.PackageLoader(__name__, 'web/templates')) loader=jinja2.PackageLoader(__name__, 'web/templates'))
def output_status(number):
return get_output(number)
env.filters['output_status'] = output_status
@app.route('/static/', branch=True) @app.route('/static/', branch=True)
def static(request): def static(request):
return File(resource_filename(__name__, 'web/static/')) return File(resource_filename(__name__, 'web/static/'))
@ -34,6 +39,17 @@ def status(request):
'status2': status2, 'status2': status2,
'log': get_log()}) 'log': get_log()})
@app.route('/outputs', methods=['GET', 'POST'])
@defer.inlineCallbacks
def outputs(request):
if request.method == 'POST':
args = json.loads(request.content.getvalue())
yield set_output(*args)
request.setResponseCode(200)
defer.returnValue('{}')
page = env.get_template('outputs.html')
defer.returnValue(page.render(outputs=OUTPUTS))
class WebService(service.Service): class WebService(service.Service):
def __init__(self, endpoint_desc): def __init__(self, endpoint_desc):
self.endpoint_desc = endpoint_desc self.endpoint_desc = endpoint_desc

View File

@ -0,0 +1,28 @@
define(['ajax'], function(ajax) {
'use strict';
function button_handler(evt) {
if (evt.target.classList.contains('active')) {
evt.stopPropagation();
return;
}
console.log(evt);
var parts = evt.target.id.split('-');
var output_id = parseInt(parts[parts.length - 1]);
var to_state = parts[parts.length - 2] === 'on' ? true : false;
var partner_id = 'btn-' + (to_state ? 'off-' : 'on-') + output_id;
console.log(partner_id);
ajax.post_json('/outputs', [output_id, to_state]).then(
function () {
evt.target.classList.add('active');
var partner = document.getElementById(partner_id);
partner.classList.remove('active');
}, function () {
alert("Failed to set output");
});
evt.stopPropagation();
}
document.getElementById('output_table').addEventListener('click',
button_handler);
});

View File

@ -8,7 +8,7 @@
{% block sidebar %} {% block sidebar %}
<li class="active"><a href="/">Overview <span class="sr-only">(current)</span></a></li> <li class="active"><a href="/">Overview <span class="sr-only">(current)</span></a></li>
<li><a href="/beacons">Output Control</a></li> <li><a href="/outputs">Output Control</a></li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -0,0 +1,42 @@
{% extends "base.html" %}
{% block title %} Override Outputs {% endblock %}
{% block head_js %}
<script data-main="static/js/outputs"
src="/static/js/require.min.js"></script>
{% endblock %}
{% block sidebar %}
<li><a href="/">Overview <span class="sr-only">(current)</span></a></li>
<li class="active"><a href="/outputs">Output Control</a></li>
{% endblock %}
{% block content %}
<!-- <h1 class="page-header">Dashboard</h1> -->
<div class="row">
<div id="table_div" class="col-sm-12 col-md-12">
<!-- <h4 class="sub-header">Log</h3> -->
<table class="table">
<thead>
<th>Output</th>
<th>Actions</th>
</thead>
<tbody id="output_table">
{% for output in outputs %}
<tr><td>{{ output.name }}</td>
<td>
{% if output.value | output_status %}
<button id="btn-on-{{ output.value }}" class="btn btn-primary btn-lg active">On</button>
<button id="btn-off-{{ output.value }}" class="btn btn-primary btn-lg">Off</button>
{% else %}
<button id="btn-on-{{ output.value }}" class="btn btn-primary btn-lg">On</button>
<button id="btn-off-{{ output.value }}" class="btn btn-primary btn-lg active">Off</button>
{% endif %}
</td>
<tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}