adding more files

This commit is contained in:
2026-03-21 14:24:10 +01:00
parent dd537b7961
commit 019223b225
8 changed files with 1358 additions and 0 deletions

759
pr_review.txt Normal file
View File

@@ -0,0 +1,759 @@
review my pull request.
this files has changed:
RPi/fake_smbus.py
dro.py
test_model.py
todo.txt
test_model.py passes all tests, here is the result:
´´´bash
smbus2 not available; using fake smbus2.py
........
----------------------------------------------------------------------
Ran 8 tests in 0.000s
OK
´´´
here is the diff between my version of RPi/fake_smbus.py and main:
´´´diff
+import tkinter as tk
+
+encoders = []
+class SMBus:
+ def __init__(self, bus):
+ print(f"fake SMBus initialized on bus {bus}")
+ self.bus = bus
+
+
+ def read_i2c_block_data(self, addr, cmd, length):
+ data = []
+ for encoder in encoders:
+ pos_as_byte_array = encoder.get_position().to_bytes(2, 'little', signed=True)
+ data.append(pos_as_byte_array[0])
+ data.append(pos_as_byte_array[1])
+
+ #print(f"SMBus data set to: {data}")
+ return data
+
+def createTestEncoder():
+ encoder = TestEncoder()
+ encoders.append(encoder)
+ return encoder
+
+class TestEncoder:
+ """ represent a test encoder controlled by keyboard input """
+ def __init__(self):
+ self._position = 0
+
+ def _rotate_cw(self, event=None):
+ self._position += 1
+
+ def _rotate_ccw(self, event=None):
+ self._position -= 1
+
+ def get_position(self) -> int:
+ return self._position
´´´
---
here is the diff between my version of dro.py and main:
´´´diff
diff --git a/dro.py b/dro.py
index 5071466..6e3b919 100755
--- a/dro.py
+++ b/dro.py
@@ -1,396 +1,370 @@
#/usr/bin/python3
-import math
-import encoder
-import threading
-import time
import tkinter as tk
from tkinter import simpledialog
from enum import IntEnum
-import RPi.GPIO as GPIO
import atexit
import subprocess
import getopt
import sys
import shutil
+import logging
+
+logger = logging.getLogger(__name__)
+
+# Prefer system-installed `smbus2`, otherwise fall back to local `smbus2.py`.
+try:
+ import smbus2
+except ImportError:
+ import RPi.fake_smbus as smbus2 # type: ignore
+ logger.warning('smbus2 not available; using fake smbus2.py')
class XMode(IntEnum):
RADIUS = 0
DIAMETER = 1
class Updated(IntEnum):
POS_X = 0
POS_Z = 1
X_MODE = 2
FULLSCREEN = 3
-X_DIRECTION_PIN = 13 #gpio 23
-X_CLOCK_PIN = 11 #gpio 24
-Z_DIRECTION_PIN = 18 #gpio 17
-Z_CLOCK_PIN = 16 #gpio 27
-
class Axis:
""" represent one axis in the model """
- def __init__(self, name):
- self._position = 0
- self._scale = 1
+ def __init__(self, name: str):
+ self._position = 0 # raw position from encoder
+ self._scale = 1.0
+ self._offset = 0.0
self._name = name
- def set_position(self, pos):
+ def get_raw_position(self):
+ """Return the uncompensated raw position (no offset applied)."""
+ return self._position
+
+ def set_position(self, pos: int):
self._position = pos
- def get_position(self):
- return self._position
+ def set_offset(self, offset: float):
+ self._offset = offset
- def get_position_as_string(self):
- return str("{:.2f}".format(self._position))
+ def get_position(self):
+ return self._position + self._offset
- def set_scale(self, scale):
- self._scale = scale
+ def set_scale(self, scale: float):
+ self._scale = scale
- def update_position(self, direction):
- self._position += direction * self._scale
+ def get_scale(self) -> float:
+ """Return the scale factor (mm per step)."""
+ return self._scale
class Model:
""" represent the DRO model """
def __init__(self, fullscreen = False):
self._observers = []
self._x_mode = XMode.RADIUS # denote x axis mode (radius/diameter)
self._axis = {}
self._axis['x'] = Axis('x')
self._axis['z'] = Axis('z')
self._fullscreen = fullscreen
def attatch(self, o):
self._observers.append(o)
def notify(self, updated = None):
for o in self._observers:
o.update(updated)
- def set_position(self, pos, axis):
+ def steps_to_position(self, axis, steps):
if axis in self._axis:
- factor = 0.5 if axis == 'x' and self.get_x_mode() == XMode.DIAMETER else 1
- self._axis[axis].set_position(factor*pos)
+ return steps * self._axis[axis].get_scale()
+ return 0
- self.notify(self._string_to_updated_axis(axis))
+ def set_position(self, axis, pos):
+ """ set position in mm. Always radius for x axis """
+ if axis in self._axis:
+ if abs(self._axis[axis].get_position() - pos) >= 0.005:
+ self._axis[axis].set_position(pos)
+ self.notify(self._string_to_updated_axis(axis))
- def get_position_as_string(self, axis):
+ def set_offset(self, axis, offset):
+ """ set offset in mm. Always radius for x axis """
if axis in self._axis:
- return str("{:.2f}".format(self.get_position(axis)))
- return None
+ self._axis[axis].set_offset(offset)
+ self.notify(self._string_to_updated_axis(axis))
def get_position(self, axis):
if axis in self._axis:
- factor = 2 if axis == 'x' and self.get_x_mode() == XMode.DIAMETER else 1
- return factor*self._axis[axis].get_position()
+ return self._axis[axis].get_position()
return None
- def update_position(self, direction, axis):
- """ update position of given axis and notify observers """
+ def get_position_uncompensated(self, axis):
+ """ get position without offset applied """
if axis in self._axis:
- self._axis[axis].update_position(direction)
-
- self.notify(self._string_to_updated_axis(axis))
+ return self._axis[axis].get_raw_position()
+ return None
def get_x_mode(self):
return self._x_mode
def set_toggle_x_mode(self):
if self._x_mode == XMode.RADIUS:
self._x_mode = XMode.DIAMETER
-
elif self._x_mode == XMode.DIAMETER:
self._x_mode = XMode.RADIUS
self.notify(Updated.X_MODE)
def set_scale(self, axis, scale):
if axis in self._axis:
self._axis[axis].set_scale(scale)
def set_fullscreen(self, v):
self._fullscreen = v
self.notify(Updated.FULLSCREEN)
def is_fullscreen(self):
return self._fullscreen
def _string_to_updated_axis(self, axis_str):
if axis_str == 'x':
return Updated.POS_X
if axis_str == 'z':
return Updated.POS_Z
return None
class Controller:
""" represent the controller in the MVC pattern """
def __init__(self, model, test_mode = False):
self._model = model
self._test_mode = test_mode
- def handle_x_position_update(self, dir):
- self._model.update_position(dir, 'x')
+ self._bus = smbus2.SMBus(1)
+ self._address = 0x08 # Arduino I2C Address
+
+ def handle_x_position_update(self, steps):
+ self._model.set_position('x', self._model.steps_to_position('x', steps))
- def handle_z_position_update(self, dir):
- self._model.update_position(dir, 'z')
+ def handle_z_position_update(self, steps):
+ self._model.set_position('z', self._model.steps_to_position('z', steps))
def handle_btn_x0_press(self):
- self._model.set_position(0, 'x')
+ self._model.set_offset('x', -self._model.get_position_uncompensated('x'))
def handle_btn_z0_press(self):
- self._model.set_position(0, 'z')
+ self._model.set_offset('z', -self._model.get_position_uncompensated('z'))
def handle_btn_toggle_x_mode(self):
self._model.set_toggle_x_mode()
def hanlde_btn_x(self):
title = 'current radius' if self._model.get_x_mode() == XMode.RADIUS else 'current diameter'
pos = simpledialog.askfloat('x', title, initialvalue=0.0, minvalue=-4000.0, maxvalue=4000.0)
+
+ if self._model.get_x_mode() == XMode.DIAMETER and pos is not None:
+ pos = pos / 2.0
+
if pos is not None:
- self._model.set_position(pos, 'x')
+ self._model.set_offset('x', pos-self._model.get_position_uncompensated('x'))
def hanlde_btn_z(self):
pos = simpledialog.askfloat('z', 'current z position', initialvalue=0.0, minvalue=-4000.0, maxvalue=4000.0)
if pos is not None:
- self._model.set_position(pos, 'z')
+ self._model.set_offset('z', pos-self._model.get_position_uncompensated('z'))
def toggle_fullscreen(self, event=None):
if self._model.is_fullscreen() == True:
self._model.set_fullscreen(False)
else:
self._model.set_fullscreen(True)
def end_fullscreen(self, event=None):
self._model.set_fullscreen(False)
def handle_btn_calc(self):
calculator_cmd = shutil.which('galculator')
if calculator_cmd != None:
try:
subprocess.Popen(calculator_cmd)
except:
print("can't start calculator")
else:
print("can't find calculator")
def shutdown(self):
if not self._test_mode:
subprocess.call('sudo shutdown -h now', shell=True)
else:
print('will not shutdown in test mode')
+ def poll_i2c_data(self):
+ """ poll I2C data from Arduino """
+ try:
+ data = self._bus.read_i2c_block_data(self._address, 0, 4)
+ #self.handle_x_position_update(int(data[0] + (data[1] << 8)))
+ self.handle_x_position_update(int.from_bytes(bytes(data[0:2]), 'little', signed=True))
+ self.handle_z_position_update(int.from_bytes(bytes(data[2:4]), 'little', signed=True))
+ except Exception as e:
+ logger.error(f"I2C read error: {e}")
+
class View:
""" represent the view in the MVC pattern """
def __init__(self, model, test_mode = False):
self._model = model
self._model.attatch(self)
self._controller = Controller(model, test_mode)
- self.x_dirty = False
- self.z_dirty = False
-
- # use tkinter's after() loop on the main thread instead of a background thread
- # This avoids cross-thread UI calls and reduces context switching overhead.
self.window = tk.Tk()
self.window.resizable(False, False)
#self.window.attributes('-zoomed', zoomed) # This just maximizes it so we can see the window. It's nothing to do with fullscreen.
self.window.attributes("-fullscreen", self._model.is_fullscreen())
w_frame = tk.Frame(self.window)
w_frame.grid(row=0, column=0, sticky='n')
e_frame = tk.Frame(self.window)
e_frame.grid(row=0, column=1, sticky='n')
# x frame
x_frame = tk.Frame(w_frame, relief=tk.GROOVE, borderwidth=3)
x_frame.grid(row=0, column=0, padx=5, pady=5)
# use a StringVar for quicker updates from the main thread
- self.x_var = tk.StringVar(value=self._model.get_position_as_string('x'))
+ self.x_var = tk.StringVar(value=self._position_to_string(self._model.get_position('x')))
self.x_label = tk.Label(x_frame, textvariable=self.x_var, anchor="e", font=("Helvetica", 80), fg="green", bg="black", width=6)
self.x_label.grid(row=0, column=0, padx=5, pady=5)
btn_set_x = tk.Button(master=x_frame, text="X", padx=5, pady=5, font=("Helvetica", 80), command=self._controller.hanlde_btn_x)
btn_set_x.grid(row=0, column=1, padx=5, pady=5, sticky="nw")
btn_set_x0 = tk.Button(master=x_frame, text="X_0", padx=5, pady=5, font=("Helvetica", 80), command=self._controller.handle_btn_x0_press)
btn_set_x0.grid(row=0, column=2, padx=5, pady=5, sticky="nw")
# Z frame
z_frame = tk.Frame(w_frame, relief=tk.GROOVE, borderwidth=3)
z_frame.grid(row=1, column=0, padx=5, pady=5)#, sticky="nw")
- self.z_var = tk.StringVar(value=self._model.get_position_as_string('z'))
+ self.z_var = tk.StringVar(value=self._position_to_string(self._model.get_position('z')))
self.z_label = tk.Label(z_frame, textvariable=self.z_var, anchor="e", font=("Helvetica", 80), fg="green", bg="black", width=6)
self.z_label.grid(row=0, column=0, padx=5, pady=5)
btn_set_z = tk.Button(master=z_frame, text="Z", padx=5, pady=5, font=("Helvetica", 80), command=self._controller.hanlde_btn_z)
btn_set_z.grid(row=0, column=1, padx=5, pady=5, sticky="nw")
btn_set_z0 = tk.Button(master=z_frame, text="Z_0", padx=5, pady=5, font=("Helvetica", 80), command=self._controller.handle_btn_z0_press)
btn_set_z0.grid(row=0, column=2, padx=5, pady=5, sticky="nw")
# status frame
status_frame = tk.Frame(w_frame, relief=tk.GROOVE, borderwidth=3)
status_frame.grid(row=3, column=0, padx=5, pady=5, sticky='e')
self.x_mode_var = tk.StringVar(value=str(self._mode_to_text()))
self.x_mode_label = tk.Label(status_frame, textvariable=self.x_mode_var, font=("Helvetica", 80), fg="red")
self.x_mode_label.grid(row=0, column=0, padx=5, pady=5, sticky="e")
# button frame
btn_frame = tk.Frame(e_frame, relief=tk.GROOVE, borderwidth=3)
btn_frame.grid(row=0, column=0, padx=5, pady=5)
btn_rd = tk.Button(master=btn_frame, text="r/D", padx=5, pady=5, font=("Helvetica", 80), width=4, command=self._controller.handle_btn_toggle_x_mode)
btn_rd.grid(row=1, column=0, padx=5, pady=5, sticky="nw")
btn_calc = tk.Button(master=btn_frame, text="Calc", padx=5, pady=5, font=("Helvetica", 80), width=4, command=self._controller.handle_btn_calc)
btn_calc.grid(row=2, column=0, padx=5, pady=15, sticky="nw")
#btn_enter = tk.Button(master=btn_frame, text="TBD", padx=5, pady=5, font=("Helvetica", 80), width=4)#, command=self._controller.horizontal_end)
self.photo = tk.PhotoImage(file="power.png")
btn_off = tk.Button(master=btn_frame, image=self.photo, command=self._controller.shutdown)
btn_off.grid(row=3, column=0, padx=5, pady=15, sticky="nw")
# bind keyboar inputs
self.window.bind("<F11>", self._controller.toggle_fullscreen)
self.window.bind("<Escape>", self._controller.end_fullscreen)
# schedule the GUI update loop on the main thread
- self._last_x = self._model.get_position('x')
- self._last_z = self._model.get_position('z')
self._update_interval_ms = 100 # update at 10Hz by default
- self.window.after(self._update_interval_ms, self._update_view)
+ self.window.after(self._update_interval_ms, self._poll_input)
self.window.update()
- #self.update()
def _mode_to_text(self):
if self._model.get_x_mode() == XMode.DIAMETER:
return 'D'
if self._model.get_x_mode() == XMode.RADIUS:
return 'r'
def _position_to_string(self, pos):
return str("{:.2f}".format(pos))
def start(self):
self.window.mainloop()
def update_x(self):
""" update x position in view (main thread) """
- self.x_dirty = False
pos = self._model.get_position('x')
- # avoid frequent updates when change is negligible
- if abs(pos - self._last_x) >= 0.005:
- self._last_x = pos
- self.x_var.set(self._position_to_string(pos))
+ if self._model.get_x_mode() == XMode.DIAMETER:
+ pos = pos * 2.0
+ self.x_var.set(self._position_to_string(pos))
def update_z(self):
""" update z position in view (main thread) """
- self.z_dirty = False
pos = self._model.get_position('z')
- if abs(pos - self._last_z) >= 0.005:
- self._last_z = pos
- self.z_var.set(self._position_to_string(pos))
+ self.z_var.set(self._position_to_string(pos))
def update(self, updated = None):
""" called by model when something has changed """
if updated == Updated.POS_X:
- self.x_dirty = True
+ self.update_x()
if updated == Updated.POS_Z:
- self.z_dirty = True
+ self.update_z()
if updated == Updated.X_MODE:
# main thread will pick this up through the scheduled update
self.x_mode_var.set(str(self._mode_to_text()))
self.update_x()
if updated == Updated.FULLSCREEN:
self.window.attributes("-fullscreen", self._model.is_fullscreen())
- def _update_view(self):
+ def _poll_input(self):
""" scheduled function (runs on tkinter main loop) to update the view."""
try:
- if self.x_dirty:
- self.update_x()
- if self.z_dirty:
- self.update_z()
+ # request data
+ self._controller.poll_i2c_data()
+
finally:
# re-schedule
- self.window.after(self._update_interval_ms, self._update_view)
-
-@atexit.register
-def cleanup():
- GPIO.cleanup(X_CLOCK_PIN)
- GPIO.cleanup(X_DIRECTION_PIN)
- GPIO.cleanup(Z_CLOCK_PIN)
- GPIO.cleanup(Z_DIRECTION_PIN)
- print("done")
-
-def test(arg):
- print(arg)
-
-
-class TestEncoder:
- """ represent a test encoder controlled by keyboard input """
- def __init__(self, view, event_cbk, inc_key = 'a', dec_key = 'b'):
-
- view.window.bind(inc_key, self._rotate_cw)
- view.window.bind(dec_key, self._rotate_ccw)
-
- self._event_cbk = event_cbk
-
- def _rotate_cw(self, event=None):
- self._event_cbk(encoder.Direction.CW)
-
- def _rotate_ccw(self, event=None):
- self._event_cbk(encoder.Direction.CCW)
+ self.window.after(self._update_interval_ms, self._poll_input)
def main():
_zoomed = False
_test = False
try:
opts, args = getopt.getopt(sys.argv[1:], "", ["zoomed", "test"])
except getopt.GetoptError as err:
# print help information and exit:
print(err) # will print something like "option -a not recognized"
sys.exit(2)
for o, a in opts:
- print(o)
- print(a)
if o == "--zoomed":
_zoomed = True
elif o == "--test":
_test = True
else:
assert False, "unhandled option"
model = Model(_zoomed)
view = View(model, _test)
model.set_scale('x', -2.5/200) #negative to flip direction
model.set_scale('z', 90/1000)
- GPIO.setwarnings(False) ## Turn off warnings
- GPIO.setmode(GPIO.BOARD) ## Use BOARD pin numbering
- GPIO.setup(X_DIRECTION_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
- GPIO.setup(Z_DIRECTION_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
- GPIO.setup(X_CLOCK_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
- GPIO.setup(Z_CLOCK_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
-
if _test:
- x_test_encoder = TestEncoder(view, view._controller.handle_x_position_update, 'a', 'z')
- z_test_encoder = TestEncoder(view, view._controller.handle_z_position_update, 's', 'x')
-
- x_encoder = encoder.Encoder(X_CLOCK_PIN, X_DIRECTION_PIN, view._controller.handle_x_position_update)
- z_encoder = encoder.Encoder(Z_CLOCK_PIN, Z_DIRECTION_PIN, view._controller.handle_z_position_update)
-
- GPIO.add_event_detect(X_CLOCK_PIN, GPIO.RISING)
- GPIO.add_event_detect(Z_CLOCK_PIN, GPIO.RISING)
-
- GPIO.add_event_callback(X_CLOCK_PIN, x_encoder.update)
- GPIO.add_event_callback(Z_CLOCK_PIN, z_encoder.update)
+ try:
+ x_test_encoder = smbus2.createTestEncoder()
+ view.window.bind('a', x_test_encoder._rotate_cw)
+ view.window.bind('z', x_test_encoder._rotate_ccw)
+ z_test_encoder = smbus2.createTestEncoder()
+ view.window.bind('s', z_test_encoder._rotate_cw)
+ view.window.bind('x', z_test_encoder._rotate_ccw)
+ except:
+ logger.error("couldn't create test encoders...")
view.start()
if __name__ == '__main__':
main()
´´´
---
here is the diff between my version of test_model.py and main:
´´´diff
diff --git a/test_model.py b/test_model.py
index dae5311..91e21b5 100644
--- a/test_model.py
+++ b/test_model.py
@@ -1,95 +1,93 @@
import unittest
import dro
-import encoder
class Observer:
def __init__(self):
self.reset()
self.nbr_of_calls = 0
self.has_been_called = False
def update(self, updated = None):
self.nbr_of_calls += 1
self.has_been_called = True
self.updated = updated
def reset(self):
self.nbr_of_calls = 0
self.has_been_called = False
class TestModel(unittest.TestCase):
#def check_pos(self, actual, expected):
# self.assertTrue(int(self.model.get_position_as_string('x')) == 1)
def setUp(self):
self.model = dro.Model()
def test_init(self):
self.assertEqual(self.model.get_position('x'), 0)
self.assertEqual(self.model.get_position('z'), 0)
- def test_notify_observers(self):
- o = Observer()
- self.model.attatch(o)
-
- self.assertFalse(o.has_been_called)
- self.model.update_position(encoder.Direction.CW, 'x')
- self.assertTrue(o.has_been_called)
- self.assertTrue(int(self.model.get_position('x')) == 1)
- self.assertTrue(o.updated == dro.Updated.POS_X)
-
- o.reset()
-
- self.model.update_position(encoder.Direction.CW, 'z')
- self.assertTrue(int(self.model.get_position('z')) == 1)
- self.assertTrue(o.has_been_called)
- self.assertTrue(int(self.model.get_position('z')) == 1)
- self.assertTrue(o.updated == dro.Updated.POS_Z)
-
def test_wrong_axis(self):
o = Observer()
self.model.attatch(o)
- self.model.set_position(1, 'apa')
+ self.model.set_position('apa', 1)
self.assertFalse(o.has_been_called)
def test_set_pos(self):
o = Observer()
self.model.attatch(o)
- self.model.set_position(1, 'x')
+ self.model.set_position('x', 1)
self.assertTrue(o.updated == dro.Updated.POS_X)
- self.model.set_position(-11, 'z')
+ self.model.set_position('z', -11)
self.assertTrue(o.updated == dro.Updated.POS_Z)
self.assertEqual(o.nbr_of_calls, 2)
self.assertEqual(int(self.model.get_position('x')), 1)
self.assertEqual(int(self.model.get_position('z')), -11)
def test_get_pos_as_string(self):
- self.model.set_position(-11.49, 'x')
+ self.model.set_position('x', -11.49)
self.assertEqual(self.model.get_position('x'), -11.49)
def test_toogle_x_mode(self):
o = Observer()
self.model.attatch(o)
- self.model.set_position(-11.49, 'x')
+ self.model.set_position('x', -11.49)
self.model.set_toggle_x_mode()
- self.assertEqual(self.model.get_position('x'), -22.98)
+ self.assertEqual(self.model.get_position('x'), -11.49)
self.assertTrue(o.updated == dro.Updated.X_MODE)
- def test_set_diam(self):
- if self.model.get_x_mode() == dro.XMode.RADIUS:
- self.model.set_toggle_x_mode()
- self.assertEqual(self.model.get_x_mode(), dro.XMode.DIAMETER)
-
- self.model.set_position(10, 'x')
- self.assertEqual(self.model.get_position('x'), 10)
- self.model.set_toggle_x_mode()
- self.assertEqual(self.model.get_position('x'), 5)
+ def test_set_position_no_notify_if_unchanged(self):
+ o = Observer()
+ self.model.attatch(o)
+
+ self.model.set_position('x', 10)
+ self.assertEqual(o.nbr_of_calls, 1)
+
+ self.model.set_position('x', 10)
+ self.assertEqual(o.nbr_of_calls, 1) # No change, no notify
+
+ self.model.set_position('x', 20)
+ self.assertEqual(o.nbr_of_calls, 2) # Changed, should notify
+
+ def test_set_position_no_notify_if_small_change(self):
+ o = Observer()
+ self.model.attatch(o)
+
+ self.model.set_position('x', 10)
+ self.assertEqual(o.nbr_of_calls, 1)
+
+ self.model.set_position('x', 10+0.003)
+ self.assertEqual(o.nbr_of_calls, 1) # No change, no notify
+ def test_steps_to_position(self):
+ self.model.set_scale('x', 2.5/200) # 200 steps per unit
+ pos = self.model.steps_to_position('x', 500)
+ self.assertEqual(pos, 6.25) # 200 * 1.5 = 300 steps
if __name__ == '__main__':
unittest.main()
´´´
---
here is the diff between my version of todo.txt and main:
´´´diff
diff --git a/todo.txt b/todo.txt
index cf193f0..fd78b5e 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,46 +1,50 @@
-askfloat funkar inte med alltid??????
-tester för r/D och ser/read position
+askfloat funkar inte med alltid??????
+KLAR i2c:
+ handle_btn_x0_press måste sätta en offset i axis som gör att offset+position blir 0
+ self._model.set_offset('x', -get_position)
+ motsvarande för hanlde_btn_x
+
KLART
desktop-filen pekar på ett shell-script som startar dro.py
dro.desktop autostartar inte???
men går att starta via file-explorer?????
KLART
stänga av med shutdown utan lösen:
sudo visudo
user_name ALL=(ALL) NOPASSWD: /sbin/poweroff, /sbin/reboot, /sbin/shutdown
KLART
stänga av med fysisk knapp:
"adding this to /boot/config.txt:
dtoverlay=gpio-shutdown
KLART avstängningsknapp
knapp som kortsluter pin 5 (GPIO3, grön) och jord
och
"adding this to /boot/config.txt:
dtoverlay=gpio-shutdown
"
och
https://askubuntu.com/questions/168879/shutdown-from-terminal-without-entering-password
SW
knapp till funktion/command:
subprocess.call('sudo shutdown -h now', shell=True)
utöka test.py
*funkar det med olika ISR? eller måste det vara en central?
VERKAR FUNKA
lägg till z också...
GJODE ETT TEST MED z_clock_pin på gpio13 FUNKAR...
HW funkar inte
testa enligt c:\Users\johan\Documents\verkstad\emco compact 5\bob\my-bob.drawio
// 1. 4.7kOhm motstånd pull up, inte pull up i HW...
// verkar bli för liten spänning
eller
2. inget motstånd, pull up i HW... FUNKAR...
det blir 3.22V HIGH på GPIO
´´´