From fef826f1d362bd6f5e1b11f48e582456dcfbd1c7 Mon Sep 17 00:00:00 2001 From: Shawn Nock Date: Thu, 12 Jan 2017 16:52:59 +0000 Subject: [PATCH] Adds basic functionality to web interface; partial function of LCD driver --- software/setup.py | 3 +- software/trespassed/gpio.py | 6 + software/trespassed/lcd.py | 105 ++++++++++++++++++ software/trespassed/serial.py | 2 +- software/trespassed/status.py | 5 +- software/trespassed/web.py | 16 +++ software/trespassed/web/static/js/outputs.js | 28 +++++ software/trespassed/web/templates/index.html | 2 +- .../trespassed/web/templates/outputs.html | 42 +++++++ 9 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 software/trespassed/lcd.py create mode 100644 software/trespassed/web/static/js/outputs.js create mode 100644 software/trespassed/web/templates/outputs.html diff --git a/software/setup.py b/software/setup.py index c247f3f..a17418e 100644 --- a/software/setup.py +++ b/software/setup.py @@ -9,6 +9,7 @@ setup (name = 'trespassed', license = "APLv2", keywords = "", 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, ) diff --git a/software/trespassed/gpio.py b/software/trespassed/gpio.py index 30581cd..e1ff13e 100644 --- a/software/trespassed/gpio.py +++ b/software/trespassed/gpio.py @@ -1,3 +1,5 @@ +import sys + from twisted.internet import defer, reactor from twisted.python import log @@ -45,6 +47,10 @@ def set_output(pin_no, level): log.msg("Clearing {}".format(pin_no)) pin.reset() +def get_output(pin_no): + pin = PINS[pin_no] + return pin.read() + skip = True def quit(*args, **kwargs): global skip diff --git a/software/trespassed/lcd.py b/software/trespassed/lcd.py new file mode 100644 index 0000000..42a1431 --- /dev/null +++ b/software/trespassed/lcd.py @@ -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]) diff --git a/software/trespassed/serial.py b/software/trespassed/serial.py index 84db54e..6c51c70 100644 --- a/software/trespassed/serial.py +++ b/software/trespassed/serial.py @@ -13,7 +13,7 @@ from twisted.application.internet import TimerService from twisted.internet import protocol, reactor, defer 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.email import email_failure from trespassed.media import play_sound_effect diff --git a/software/trespassed/status.py b/software/trespassed/status.py index 65ebb58..a153104 100644 --- a/software/trespassed/status.py +++ b/software/trespassed/status.py @@ -2,6 +2,7 @@ from twisted.application.internet import TimerService from twisted.internet import reactor from trespassed.timer import get_timer +from trespassed.lcd import lcd_write_status __all__ = ['get_status', 'set_status', 'status_service', 'get_log', 'clear_log'] @@ -12,12 +13,14 @@ class Status(object): def __init__(self, name, initial_status='Loading...'): self.name = name self.log = [] - self.update() self.set(initial_status) + self.update() def update(self): self._status1 = "{:.10} {:5}".format(self.name, get_timer().get_human()) + lcd_write_status(self.get()) + def set(self, status): self._status2 = status self.log.append({'when': get_timer().get_human(), diff --git a/software/trespassed/web.py b/software/trespassed/web.py index 355c956..7419a00 100644 --- a/software/trespassed/web.py +++ b/software/trespassed/web.py @@ -9,12 +9,17 @@ from twisted.python import log from twisted.web.server import Site from twisted.web.static import File +from trespassed.gpio import OUTPUTS, get_output, set_output from trespassed.status import get_status, get_log app = Klein() env = jinja2.Environment( 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) def static(request): return File(resource_filename(__name__, 'web/static/')) @@ -34,6 +39,17 @@ def status(request): 'status2': status2, '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): def __init__(self, endpoint_desc): self.endpoint_desc = endpoint_desc diff --git a/software/trespassed/web/static/js/outputs.js b/software/trespassed/web/static/js/outputs.js new file mode 100644 index 0000000..6a3b9d2 --- /dev/null +++ b/software/trespassed/web/static/js/outputs.js @@ -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); +}); diff --git a/software/trespassed/web/templates/index.html b/software/trespassed/web/templates/index.html index 24e7084..092d6e8 100644 --- a/software/trespassed/web/templates/index.html +++ b/software/trespassed/web/templates/index.html @@ -8,7 +8,7 @@ {% block sidebar %}
  • Overview (current)
  • -
  • Output Control
  • +
  • Output Control
  • {% endblock %} {% block content %} diff --git a/software/trespassed/web/templates/outputs.html b/software/trespassed/web/templates/outputs.html new file mode 100644 index 0000000..fce8884 --- /dev/null +++ b/software/trespassed/web/templates/outputs.html @@ -0,0 +1,42 @@ +{% extends "base.html" %} +{% block title %} Override Outputs {% endblock %} + +{% block head_js %} + +{% endblock %} + +{% block sidebar %} +
  • Overview (current)
  • +
  • Output Control
  • +{% endblock %} + +{% block content %} + +
    +
    + + + + + + + + {% for output in outputs %} + + + + {% endfor %} + +
    OutputActions
    {{ output.name }} + {% if output.value | output_status %} + + + {% else %} + + + {% endif %} +
    +
    +
    +{% endblock %}