From: Bdale Garbee Date: Sat, 5 Apr 2025 23:15:46 +0000 (-0600) Subject: clean things up, prepare to flesh out application with css work, et al X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=bf51b422430432b0aaebe5feae67d989dd6b2f72;p=fw%2Fquantimotor clean things up, prepare to flesh out application with css work, et al --- diff --git a/application.py b/application.py deleted file mode 100755 index 862e74e..0000000 --- a/application.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2025 Bdale Garbee . GPLv3+ - -import os -import cherrypy -import plotly.express as px -import gpiod -import iio -import signal -import sys -import time - -# configuration for each channel on ADS8688 -OFFSET = "0" -SCALE = "0.078127104" -VOLTAGES = ['voltage0', 'voltage1', 'voltage2', 'voltage3', 'voltage4', 'voltage5', 'voltage6', 'voltage7'] - -file_path = os.getcwd() -from gpiod.line import Direction, Value - -def set_line_values(chip_path, line_values): - value_str = {Value.ACTIVE: "Active", Value.INACTIVE: "Inactive"} - - request = gpiod.request_lines( - chip_path, - consumer=sys.argv[0], - config={ - tuple(line_values.keys()): gpiod.LineSettings(direction=Direction.OUTPUT) - }, - ) - request.set_values(line_values) - -def handler(signum, frame): - set_line_values( - "/dev/gpiochip0", - { 4: Value.INACTIVE, # indicate 'not ready' to LPC - 16: Value.INACTIVE, # pyro off - 17: Value.INACTIVE, # alarm b off - 20: Value.INACTIVE, # turn continuity LED off - 21: Value.INACTIVE, # turn armed LED off - 27: Value.INACTIVE # alarm a off - } - ) - sys.exit(0) - -# having systemd use SIGINT to avoid CherryPy consuming the kill signal -signal.signal(signal.SIGINT, handler) - -class Root(object): - @cherrypy.expose - - # define what happens on default (index) page - def index(self): - cherrypy.log("index") - - # some sort of demo data - df = px.data.gapminder().query("country=='Canada'") - - # constrain height to 600 to avoid having to scroll for other info? - fig = px.line(df, x="year", y="lifeExp", \ - title='Life expectancy in Canada', height=600) - - # fig.to_html gives us the entire interactive graph as a python string! - plotly_data = fig.to_html(full_html=False) - - # for now, just concatenate a header and a footer - html_data = '

QuantiMotor

' + \ - plotly_data + \ - "

Thats all folks!

" - - # give the complete html to the client - return html_data - -if __name__ == '__main__': - ctx = iio.LocalContext() - ctrl = ctx.find_device('ads8688') - - # initialize hardware - set_line_values( - "/dev/gpiochip0", - {25: Value.ACTIVE, # take ADS8688 out of reset - 4: Value.ACTIVE, # indicate 'ready' to LPC - 16: Value.INACTIVE, # pyro off - 17: Value.INACTIVE, # alarm b off - 20: Value.INACTIVE, # turn continuity LED off - 21: Value.INACTIVE, # turn armed LED off - 27: Value.INACTIVE # alarm a off - } - ) - - # configure ADC channels - for id in VOLTAGES: - chan = ctrl.find_channel(id) - # must set scale before offset, so offset 0 is valid! - chan.attrs['scale'].value = SCALE - chan.attrs['offset'].value = OFFSET - - config = { - 'global': { - 'server.socket_host': '0.0.0.0', - 'server.socket_port': 8080, - }, - } - - cherrypy.quickstart(Root(), '/', config) - diff --git a/application/app.py b/application/app.py new file mode 100755 index 0000000..862e74e --- /dev/null +++ b/application/app.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Bdale Garbee . GPLv3+ + +import os +import cherrypy +import plotly.express as px +import gpiod +import iio +import signal +import sys +import time + +# configuration for each channel on ADS8688 +OFFSET = "0" +SCALE = "0.078127104" +VOLTAGES = ['voltage0', 'voltage1', 'voltage2', 'voltage3', 'voltage4', 'voltage5', 'voltage6', 'voltage7'] + +file_path = os.getcwd() +from gpiod.line import Direction, Value + +def set_line_values(chip_path, line_values): + value_str = {Value.ACTIVE: "Active", Value.INACTIVE: "Inactive"} + + request = gpiod.request_lines( + chip_path, + consumer=sys.argv[0], + config={ + tuple(line_values.keys()): gpiod.LineSettings(direction=Direction.OUTPUT) + }, + ) + request.set_values(line_values) + +def handler(signum, frame): + set_line_values( + "/dev/gpiochip0", + { 4: Value.INACTIVE, # indicate 'not ready' to LPC + 16: Value.INACTIVE, # pyro off + 17: Value.INACTIVE, # alarm b off + 20: Value.INACTIVE, # turn continuity LED off + 21: Value.INACTIVE, # turn armed LED off + 27: Value.INACTIVE # alarm a off + } + ) + sys.exit(0) + +# having systemd use SIGINT to avoid CherryPy consuming the kill signal +signal.signal(signal.SIGINT, handler) + +class Root(object): + @cherrypy.expose + + # define what happens on default (index) page + def index(self): + cherrypy.log("index") + + # some sort of demo data + df = px.data.gapminder().query("country=='Canada'") + + # constrain height to 600 to avoid having to scroll for other info? + fig = px.line(df, x="year", y="lifeExp", \ + title='Life expectancy in Canada', height=600) + + # fig.to_html gives us the entire interactive graph as a python string! + plotly_data = fig.to_html(full_html=False) + + # for now, just concatenate a header and a footer + html_data = '

QuantiMotor

' + \ + plotly_data + \ + "

Thats all folks!

" + + # give the complete html to the client + return html_data + +if __name__ == '__main__': + ctx = iio.LocalContext() + ctrl = ctx.find_device('ads8688') + + # initialize hardware + set_line_values( + "/dev/gpiochip0", + {25: Value.ACTIVE, # take ADS8688 out of reset + 4: Value.ACTIVE, # indicate 'ready' to LPC + 16: Value.INACTIVE, # pyro off + 17: Value.INACTIVE, # alarm b off + 20: Value.INACTIVE, # turn continuity LED off + 21: Value.INACTIVE, # turn armed LED off + 27: Value.INACTIVE # alarm a off + } + ) + + # configure ADC channels + for id in VOLTAGES: + chan = ctrl.find_channel(id) + # must set scale before offset, so offset 0 is valid! + chan.attrs['scale'].value = SCALE + chan.attrs['offset'].value = OFFSET + + config = { + 'global': { + 'server.socket_host': '0.0.0.0', + 'server.socket_port': 8080, + }, + } + + cherrypy.quickstart(Root(), '/', config) + diff --git a/application/quantimotor.py b/application/quantimotor.py deleted file mode 100755 index 9947382..0000000 --- a/application/quantimotor.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2025 Bdale Garbee . GPLv3+ - -import gpiod -import iio -import signal -import sys -import time - -ctx = iio.LocalContext() -ctrl = ctx.find_device('ads8688') -# configuration for each channel on ADS8688 -OFFSET = "0" -SCALE = "0.078127104" -#VOLTAGES = ['voltage0', 'voltage1', 'voltage2', 'voltage3', 'voltage4', 'voltage5', 'voltage6', 'voltage7'] -VOLTAGES = ['voltage0', 'voltage1'] - -from gpiod.line import Direction, Value - -def handler(signum, frame): - print('Ctrl+C pressed, shutting down cleanly.') - set_line_values( - "/dev/gpiochip0", - { 4: Value.INACTIVE, # indicate 'not ready' to LPC - 16: Value.INACTIVE, # pyro off - 17: Value.INACTIVE, # alarm b off - 20: Value.INACTIVE, # turn continuity LED off - 21: Value.INACTIVE, # turn armed LED off - 25: Value.INACTIVE, # put ADS8688 in reset - 27: Value.INACTIVE # alarm a off - } - ) - exit(1) - -signal.signal(signal.SIGINT, handler) - -def set_line_values(chip_path, line_values): - value_str = {Value.ACTIVE: "Active", Value.INACTIVE: "Inactive"} - - request = gpiod.request_lines( - chip_path, - consumer=sys.argv[0], - config={ - tuple(line_values.keys()): gpiod.LineSettings(direction=Direction.OUTPUT) - }, - ) - request.set_values(line_values) - -if __name__ == "__main__": - try: - # initialize hardware - set_line_values( - "/dev/gpiochip0", - {25: Value.ACTIVE, # take ADS8688 out of reset - 4: Value.ACTIVE, # indicate 'ready' to LPC - 16: Value.ACTIVE, # pyro on - 17: Value.ACTIVE, # alarm b on - 20: Value.ACTIVE, # turn continuity LED on - 21: Value.INACTIVE, # turn armed LED off - 27: Value.ACTIVE # alarm a on - } - ) - - # configure ADC channels - for id in VOLTAGES: - chan = ctrl.find_channel(id) - chan.attrs['offset'].value = OFFSET - chan.attrs['scale'].value = SCALE - - # Iterate until ctrl/c - while True: - for id in VOLTAGES: - chan = ctrl.find_channel(id) - rawstring = chan.attrs['raw'].value - voltage = (float(rawstring) + float(OFFSET)) * float(SCALE) / 1000 - print("{0}: {1:0.3f}, ".format( chan.id, voltage), end="") - print() - time.sleep(1) - - except OSError as ex: - print(ex, "\nD'oh!") - diff --git a/debian/quantimotor.install b/debian/quantimotor.install index 3836ff4..defb57a 100644 --- a/debian/quantimotor.install +++ b/debian/quantimotor.install @@ -1,4 +1,4 @@ -application.py usr/share/quantimotor +application/* usr/share/quantimotor/ui bcm2837-rpi-zero-2-w.dtb usr/share/quantimotor enable_ads.py usr/share/quantimotor quantimotor.conf etc/modules-load.d diff --git a/startup b/startup index d9b6b17..33e1a40 100755 --- a/startup +++ b/startup @@ -1,12 +1,16 @@ #!/bin/sh +# QuantiMotor application start-up script +# Copyright 2025 Bdale Garbee , GPLv3 # set up gpio lines, enabling ADC and setting channel parameters /usr/share/quantimotor/enable_ads.py +# set up sampling trigger based on kernel high resolution timer mkdir /sys/kernel/config/iio/triggers/hrtimer/instance1 echo instance1 > /sys/bus/iio/devices/iio\:device0/trigger/current_trigger - echo 1000 > /sys/bus/iio/devices/trigger0/sampling_frequency + +# enable all 8 ADS8688 channels for sampling on each trigger echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage0_en echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage1_en echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage2_en @@ -15,8 +19,12 @@ echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage4_en echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage5_en echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage6_en echo 1 > /sys/bus/iio/devices/iio\:device0/scan_elements/in_voltage7_en + +# enable triggered buffer sampling echo 1 > /sys/bus/iio/devices/iio\:device0/buffer/enable echo "quantimotor startup script ran" > /tmp/status -/usr/share/quantimotor/application.py +# launch the user interface +/usr/share/quantimotor/ui/app.py +