subDesTagesMitExtraKaese %!s(int64=4) %!d(string=hai) anos
pai
achega
9787d73e97

+ 60 - 40
software/appWindow.py

@@ -12,7 +12,9 @@ from ui import *
 
 import tkinter as tk
 import tk_tools
+from tkinter import filedialog
 import time
+import numpy as np
 
 
 class Main(tk.Tk, Table):
@@ -30,49 +32,46 @@ class Main(tk.Tk, Table):
     self.forceSensors = LoadCells()
     self.forceSensors.start()
     self.motorController = PWM(32)
-    self.pid = PID()
-
-    self.motorEnabled = False
+    self.pid = PID(100/22, 5, 1)
+    self.pid.setWindup(1)
     
     print('initializing database...')
     Table.__init__(self, 
-    ["windspeed", "motor_pwm"] + 
+    ["windspeed", "set_value", "motor_pwm"] + 
     ["pressure_{}".format(i) for i in range(8)] + 
     ["adc_{}".format(i) for i in range(1)] + 
+    ["force_X_sum", "force_Y_sum", "force_Z_sum"] +
     ["force_X_1", "force_Y_1", "force_Z_1"] +
     ["force_X_2", "force_Y_2", "force_Z_2"] + 
     ["force_X_3", "force_Y_3", "force_Z_3"])
 
 
     print('initializing GUI...')
-    menubar = tk.Menu(self)
-    filemenu = tk.Menu(menubar, tearoff=0)
-    filemenu.add_command(label="Save settings", command = lambda: self.popupmsg("Not supported just yet!"))
-    filemenu.add_separator()
-    filemenu.add_command(label="Exit", command=self.stop)
-    menubar.add_cascade(label="File", menu=filemenu)
+    self.menubar = tk.Menu(self)
+ 
+    self.frameVar = tk.StringVar()
+    self.menubar.add_radiobutton(indicatoron=0, variable=self.frameVar, value='Page_1', command=self.show_frame, label="Bedienelemente")
+    self.menubar.add_radiobutton(indicatoron=0, variable=self.frameVar, value='Page_2', command=self.show_frame, label="Kräfte")
+    self.menubar.add_radiobutton(indicatoron=0, variable=self.frameVar, value='Page_3', command=self.show_frame, label="Druck")
+    self.menubar.add_radiobutton(indicatoron=0, variable=self.frameVar, value='Page_4', command=self.show_frame, label="Einstellungen")
+
+    self.menubar.add_command(state=tk.DISABLED, label="          ")
 
-    tk.Tk.config(self, menu=menubar)
+    self.motorEnabled = tk.IntVar()
+    self.menubar.add_checkbutton(indicatoron=0, variable=self.motorEnabled, background='#dd5252', label="Motor freischalten", command=lambda: 
+        self.menubar.entryconfigure("Motor freischalten", background='#dd5252' if self.motorEnabled.get() == 0 else 'green'))
+    self.motorEnabled.set(0)
 
-    label = tk.Label(self, text="Bedienelemente", font=LARGE_FONT)
-    label.pack(pady=10,padx=10)
-    # top menu
-    top = tk.Frame(self, borderwidth=2, relief="solid")
-    button1 = tk.Button(top, text="Bedienelemente", command=lambda: self.show_frame(Page_1))
-    button1.pack(side=tk.LEFT)
+    self.menubar.add_command(state=tk.DISABLED, label="          ")
 
-    button2 = tk.Button(top, text="Kräfte", command=lambda: self.show_frame(Page_2))
-    button2.pack(side=tk.LEFT)
+    self.menubar.add_command(label="Messwerte speichern", command = self.save_dialog)
+    self.menubar.add_command(label='Messwerte löschen', command=self.reset)
 
-    button3 = tk.Button(top, text="Druck", command=lambda: self.show_frame(Page_3))
-    button3.pack(side=tk.LEFT)
+    self.menubar.add_command(state=tk.DISABLED, label="          ")
 
-    button4 = tk.Button(top,text="Einstellungen",command=lambda: self.show_frame(Page_4))
-    button4.pack(side=tk.LEFT)
+    self.menubar.add_command(label="Beenden", foreground="red", command=self.stop)
 
-    button5 = tk.Button(top, text="QUIT", fg="red",command=self.stop)
-    button5.pack(side=tk.LEFT)
-    top.pack(side="top", fill="both")
+    tk.Tk.config(self, menu=self.menubar)
 
     container = tk.Frame(self)
     container.pack(side="top", fill="both", expand = True)
@@ -85,19 +84,20 @@ class Main(tk.Tk, Table):
 
       frame = F(container, self)
 
-      self.frames[F] = frame
+      self.frames[F.__name__] = frame
 
       frame.grid(row=0, column=0, sticky="nsew")
 
-    self.show_frame(Page_1)
+    self.frameVar.set('Page_1')
+    self.show_frame()
 
     print('program ready!')
 
+    self.intervalDelay = 300 # ms
     self.interval()
 
-  def show_frame(self, cont):
-    self.currentFrame = self.frames[cont]
-    self.currentFrame.tkraise()
+  def show_frame(self):
+    self.frames[self.frameVar.get()].tkraise()
 
   def popupmsg(self, msg=""):
       popup = tk.Toplevel(self.master)
@@ -106,35 +106,55 @@ class Main(tk.Tk, Table):
       label.pack(side="top", fill="x", pady=10)
       b1 = tk.Button(popup, text="Okay", command=popup.destroy)
       b1.pack()
-      
+  
+  def save_dialog(self):
+    f = filedialog.asksaveasfile(mode='w+', defaultextension=".csv")
+    if f is None:
+      return
+    self.saveAsCsv(f)
+    f.close()
+
   def interval(self):
-    self.after(300,self.interval)
+    self.after(self.intervalDelay, self.interval)
     start = time.time()
+
+    setValue = self.frames['Page_1'].speedSlider.get()
+    self.pid.setInput(setValue)
+
     adcValue = self.adc.getVoltage(0)
-    windSpeed = adcValue * 1337
+    windSpeed = adcValue * 5
     pwmValue = self.pid.update(windSpeed)
-    if not self.motorEnabled:
+    
+    if self.motorEnabled.get() == 0:
+      self.pid.clear()
       pwmValue = 0
+    elif pwmValue > 100:
+      pwmValue = 100
+    elif pwmValue < 0:
+      pwmValue = 0
+
     self.motorController.setDutyCycle(pwmValue)
 
     i2cValues = self.pressureSensors.getValues()
+    btValues = self.forceSensors.getForces()
 
     self.addRow(
-      [windSpeed, pwmValue] + 
+      [windSpeed, setValue, pwmValue] + 
       i2cValues +
       [adcValue] +
-      self.forceSensors.getForces(0) +
-      self.forceSensors.getForces(1) +
-      self.forceSensors.getForces(2)
+      list(np.sum(btValues, axis = 0, initial=0)) +
+      list(btValues.flatten())
     )
     print("sensors: {:8.3f} ms".format((time.time() - start)*1000))
 
     start = time.time()
     for frame in self.frames:
-      self.frames[frame].update(self.frames[frame] == self.currentFrame)
+      self.frames[frame].update(frame == self.frameVar.get())
 
     print("draw:    {:8.3f} ms".format((time.time() - start)*1000))
     
   def stop(self):
     self.forceSensors.stop()
+    self.motorEnabled.set(0)
+    self.motorController.setDutyCycle(0)
     self.quit()

+ 10 - 7
software/database.py

@@ -13,6 +13,10 @@ class Table:
     self.timestamps.fill(datetime.now())
     self.rowIndex = 0
 
+  def reset(self):
+    self.rows.fill(float('NaN'))
+    self.rowIndex = 0
+
   def rowIdToOffset(self, id):
     return self.nRows - (id % self.nRows) - 1
 
@@ -51,13 +55,12 @@ class Table:
   def getLastValue(self, column):
     return self.getLastValues(1, column)[0]
 
-  def saveAsCsv(self, filename):
-    with open(filename,"w+", newline='') as my_csv:
-      csvWriter = csv.writer(my_csv,delimiter=',')
-      csvWriter.writerow(self.columnNames)
-      timestamps = self.getLastTimestamps(self.rowIndex)
-      for ts, vals in zip(self.getLastTimestamps(self.rowIndex), self.getLastRows(self.rowIndex)):
-        csvWriter.writerow([ts] + list(vals))
+  def saveAsCsv(self, fd):
+    csvWriter = csv.writer(fd,delimiter=',')
+    csvWriter.writerow(self.columnNames)
+    timestamps = self.getLastTimestamps(self.rowIndex)
+    for ts, vals in zip(self.getLastTimestamps(self.rowIndex), self.getLastRows(self.rowIndex)):
+      csvWriter.writerow([ts] + list(vals))
 
 if __name__ == "__main__":
   t = Table(["col1", "col2"])

+ 5 - 0
software/motorController/pidController.py

@@ -97,6 +97,8 @@ class PID:
             self.last_error = error
 
             self.output = self.PTerm + (self.Ki * self.ITerm) + (self.Kd * self.DTerm)
+            
+        return self.output
 
     def setKp(self, proportional_gain):
         """Determines how aggressively the PID reacts to the current error with setting Proportional Gain"""
@@ -110,6 +112,9 @@ class PID:
         """Determines how aggressively the PID reacts to the current error with setting Derivative Gain"""
         self.Kd = derivative_gain
 
+    def setInput(self, setpoint):
+        self.SetPoint = setpoint
+
     def setWindup(self, windup):
         """Integral windup, also known as integrator windup or reset windup,
         refers to the situation in a PID feedback controller where

+ 30 - 23
software/ui/Page_1.py

@@ -9,47 +9,54 @@ class Page_1(tk.Frame):
   plotLen = 100
   def __init__(self, parent, controller):
     tk.Frame.__init__(self, parent)
-    self.t = 0
     self.controller = controller
 
     # graph
-    self.serialPlot = Plot(xaxis=(0, self.plotLen * .3), yaxis=(0, 5),
+    self.serialPlot = Plot(xaxis=(0, self.plotLen * .3), yaxis=(0, 25),
                       ytitle="Windgeschwindigkeit m/s",
                       xtitle="vergangene Zeit in s",
                       title="Geschwindigkeitsverlauf",
-                      line_colors=["#2222ff", "#22ff22", "#ff2222"])
+                      line_colors=GRAPH_COLORS)
 
     canvas = self.serialPlot.create_canvas(self)
     canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
 
-    # right menu
-    left = tk.Frame(self, borderwidth=2, relief="solid")
-    container = tk.Frame(left, borderwidth=2, relief="solid")
-    label1 = tk.Label(container, text="I could be a canvas, but I'm a label right now")
-
-    self.label4 = tk.Label(self,font=("Arial","30"),fg="red")
-    self.label4.pack()
-    self.label4.config(text=str(self.t))
+    # legend
+    container = tk.Frame(self, relief="solid")
+    container.pack(side="bottom", fill="both", padx=20, pady=20)
+    
+    tk.Label(container, text="Ist-Geschwindigkeit").grid(row=0, column=1)
+    tk.Frame(container, height = 3,width = 50,bg = GRAPH_COLORS[0]).grid(row=0, column=0)
+    tk.Label(container, text="Soll-Geschwindigkeit").grid(row=1, column=1)
+    tk.Frame(container, height = 3,width = 50,bg = GRAPH_COLORS[1]).grid(row=1, column=0)
+    tk.Label(container, text="PWM-Wert, skaliert auf 0-25").grid(row=2, column=1)
+    tk.Frame(container, height = 3,width = 50,bg = GRAPH_COLORS[2]).grid(row=2, column=0)
 
-    SendButton = tk.Button(left, text='Quit', command=self.controller.stop)
-    label2 = tk.Label(left, text="I could be a button")
-    label3 = tk.Label(left, text="So could I")
+    # right menu
+    rightFrame = tk.LabelFrame(self, text="Bedienelemente")
+    rightFrame.pack(side="left", fill="both", padx=5, pady=5)
 
-    left.pack(side="left", fill="both")
-    container.pack(fill="both", padx=7, pady=5)
-    SendButton.pack()
+    self.droLabel = tk.Label(rightFrame,font=("Arial","30"),fg="red")
+    self.droLabel.pack(side="top", fill="both")
 
-    label1.pack()
+    # controls
+    self.speedSlider = tk.Scale(rightFrame, from_=0, to=22, resolution=0.1, orient=tk.HORIZONTAL, width=50)
+    self.speedSlider.pack(side="top", fill="both", padx=5, pady=5)
+    label2 = tk.Label(rightFrame, text="Ventilator Soll-Wert in m/s")
     label2.pack()
-    label3.pack()
 
-    controller.pid.SetPoint = 0.4 # m/s
 
   def update(self, visible):
+
     if visible:
+      self.serialPlot.setTimeScale(self.plotLen, self.controller.intervalDelay)
       timestamps = (np.datetime64(datetime.now()) - self.controller.getLastValues(self.plotLen, "datetime")) / np.timedelta64(1,'s')
       self.serialPlot.plot_data(
-        xs=[timestamps, timestamps],
-        ys=[self.controller.getLastValues(self.plotLen, "adc_0"), np.linspace(0, 5, self.plotLen)]
+        xs=[timestamps, timestamps, timestamps],
+        ys=[
+          self.controller.getLastValues(self.plotLen, "windspeed"), 
+          self.controller.getLastValues(self.plotLen, "set_value"),
+          self.controller.getLastValues(self.plotLen, "motor_pwm") / 4
+        ]
       )
-      self.label4.config(text="{:5.3f} V".format(self.controller.getLastValue("adc_0")))
+      self.droLabel.config(text="{:5.3f} V - {:5.2} m/s".format(self.controller.getLastValue("adc_0"), self.controller.getLastValue("windspeed")))

+ 42 - 6
software/ui/Page_2.py

@@ -10,18 +10,48 @@ class Page_2(tk.Frame):
   def __init__(self, parent, controller):
     tk.Frame.__init__(self, parent)
     self.controller = controller
-    label = tk.Label(self, text="Kräfte", font=LARGE_FONT)
-    label.pack(pady=10,padx=10)
     
     # graphs
     self.forcePlot = Plot(xaxis=(-2, 2), yaxis=(-2, 2),
-          ytitle="y-Kraft [mV]",
-          xtitle="x-Kraft [mV]",
+          ytitle="y-Kraft in mV",
+          xtitle="x-Kraft in mV",
           title="XY-Graph",
-          line_colors=["#2222ff", "#22ff22", "#ff2222", "#000000"])
+          line_colors=GRAPH_COLORS)
     canvas = self.forcePlot.create_canvas(self)
     canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
 
+    # legend
+    container = tk.Frame(self, relief="solid")
+    container.pack(side="bottom", fill="both", padx=20, pady=20)
+    
+    tk.Label(container, text="Sensor 1").grid(row=0, column=1)
+    tk.Frame(container, height = 3,width = 50,bg = GRAPH_COLORS[0]).grid(row=0, column=0)
+    tk.Label(container, text="Sensor 2").grid(row=1, column=1)
+    tk.Frame(container, height = 3,width = 50,bg = GRAPH_COLORS[1]).grid(row=1, column=0)
+    tk.Label(container, text="Sensor 3").grid(row=2, column=1)
+    tk.Frame(container, height = 3,width = 50,bg = GRAPH_COLORS[2]).grid(row=2, column=0)
+
+    # right menu
+    rightFrame = tk.LabelFrame(self, text="Kräfte")
+    rightFrame.pack(side="left", fill="both", padx=5, pady=5)
+
+    tareButton = tk.Button(rightFrame, text='Kräfte tarieren', command=self.controller.forceSensors.tare, justify=tk.LEFT, anchor="w")
+    tareButton.pack(side="top", fill="both", padx=5, pady=5)
+
+    gridFrame = tk.Frame(rightFrame, relief="solid")
+    gridFrame.pack(side="top", fill="both", padx=20, pady=20)
+
+    self.readOuts = {}
+    for sensor in range(4):
+      for axis in range(3):
+        name = "force_{}_{}".format(chr(ord('X') + axis), 'sum' if sensor==0 else sensor)
+        label = tk.Label(gridFrame, text=name)
+        label.grid(row=sensor*2, column=axis)
+        self.readOuts[name] = tk.StringVar()
+        entry = tk.Entry(gridFrame, textvariable=self.readOuts[name], width=10)
+        entry.grid(row=sensor*2+1, column=axis)
+
+
   def update(self, visible):
     if visible:
       self.forcePlot.plot_data(
@@ -29,4 +59,10 @@ class Page_2(tk.Frame):
             [np.sin(np.linspace(0, 6.282, self.plotLen))],
         ys=[self.controller.getLastValues(self.plotLen, "force_Y_{}".format(i+1)) for i in range(3)] + 
             [np.cos(np.linspace(0, 6.282, self.plotLen))]
-      )
+      )
+      for name in self.readOuts:
+        val = self.controller.getLastValue(name)
+        if np.isnan(val):
+          self.readOuts[name].set("")
+        else:
+          self.readOuts[name].set("{:1.3f} mV".format(val))

+ 51 - 5
software/ui/Page_3.py

@@ -1,15 +1,61 @@
 import tkinter as tk
 import tk_tools
-
+import numpy as np
+from datetime import datetime
+from .Plot import Plot
 from .globals import *
 
 class Page_3(tk.Frame):
+  plotLen = 100
   def __init__(self, parent, controller):
     tk.Frame.__init__(self, parent)
     self.controller = controller
-    label = tk.Label(self, text="Druck", font=LARGE_FONT)
-    label.pack(pady=10,padx=10)
+
+    # graph
+    self.pressurePlot = Plot(xaxis=(0, self.plotLen * .3), yaxis=(-120, 120),
+                      ytitle="Druck in Pa",
+                      xtitle="vergangene Zeit in s",
+                      title="Druckverlauf",
+                      line_colors=GRAPH_COLORS)
+
+    canvas = self.pressurePlot.create_canvas(self)
+    canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+
+    # legend
+    container = tk.Frame(self, relief="solid")
+    container.pack(side="bottom", fill="both", padx=20, pady=20)
+    
+    for i in range(8):
+      tk.Label(container, text="Sensor {}".format(i)).grid(row=i, column=1)
+      tk.Frame(container, height = 3,width = 50,bg = GRAPH_COLORS[i]).grid(row=i, column=0)
+
+    # right menu
+    rightFrame = tk.LabelFrame(self, text="Druck")
+    rightFrame.pack(side="left", fill="both", padx=5, pady=5)
+
+    gridFrame = tk.Frame(rightFrame, relief="solid")
+    gridFrame.pack(side="top", fill="both", padx=20, pady=20)
+
+    self.readOuts = {}
+    for sensor in range(8):
+      name = "pressure_{}".format(sensor)
+      label = tk.Label(gridFrame, text=name)
+      label.grid(row=sensor, column=0)
+      self.readOuts[name] = tk.StringVar()
+      entry = tk.Entry(gridFrame, textvariable=self.readOuts[name], width=10)
+      entry.grid(row=sensor, column=1)
 
   def update(self, visible):
-    #self.controller.getLastValue('adc_0')
-    pass
+    if visible:
+      self.pressurePlot.setTimeScale(self.plotLen, self.controller.intervalDelay)
+      timestamps = (np.datetime64(datetime.now()) - self.controller.getLastValues(self.plotLen, "datetime")) / np.timedelta64(1,'s')
+      self.pressurePlot.plot_data(
+        xs=[timestamps] * 8,
+        ys=[self.controller.getLastValues(self.plotLen, "pressure_{}".format(i)) for i in range(8)]
+      )
+      for name in self.readOuts:
+        val = self.controller.getLastValue(name)
+        if np.isnan(val):
+          self.readOuts[name].set("")
+        else:
+          self.readOuts[name].set("{:1.3f} Pa".format(val))

+ 34 - 2
software/ui/Page_4.py

@@ -1,12 +1,44 @@
 import tkinter as tk
 import tk_tools
+import numpy as np
+from datetime import datetime
+from .Plot import Plot
+from .globals import *
 
 from .globals import *
 class Page_4(tk.Frame):
   def __init__(self, parent, controller):
     tk.Frame.__init__(self, parent)
-    label = tk.Label(self, text="Einstellungen", font=LARGE_FONT)
+    self.controller = controller
+
+    frame = tk.LabelFrame(self, text="Einstellungen")
+    frame.pack(side=tk.LEFT, fill=tk.BOTH)
+    label = tk.Label(frame, text="Einstellungen", font=LARGE_FONT)
     label.pack(pady=10,padx=10)
 
+    tk.Label(frame, text="Aktualisierungsinterval in ms").pack()
+    self.intervalSlider = tk.Scale(frame, from_=50, to=1000, resolution=10, orient=tk.HORIZONTAL)
+    self.intervalSlider.set(300)
+    self.intervalSlider.pack(side="top", fill="both", padx=5, pady=5)
+
+    gridFrame = tk.LabelFrame(self, text="System Check")
+    gridFrame.pack(side=tk.LEFT, fill=tk.BOTH)
+
+    self.sysLabels = []
+    for i in range(1, len(self.controller.columnNames)):
+      name = controller.columnNames[i]
+      la = tk.Label(gridFrame, fg='red', padx=5, pady=5, text="{:32s}".format(name))
+      la.grid(row=i, column=0)
+      lb = tk.Label(gridFrame, fg='red', padx=5, pady=5, text="")
+      lb.grid(row=i, column=1)
+      self.sysLabels.append((la, lb))
+
   def update(self, visible):
-    pass
+    if visible:
+      self.controller.intervalDelay = self.intervalSlider.get()
+      row = self.controller.getLastRows(1)[0]
+      for i in range(len(row)):
+        nameLabel, valueLabel = self.sysLabels[i]
+        color = 'red' if np.isnan(row[i]) else 'black' if row[i] == 0 else 'green'
+        nameLabel.config( fg = color)
+        valueLabel.config(fg = color, text="{:5.4f}".format(row[i]))

+ 6 - 0
software/ui/Plot.py

@@ -8,3 +8,9 @@ class Plot(linear_plot):
 
     linear_plot.__init__(self, **kwargs)
     self.set_scale(xaxis, yaxis)
+
+  def setTimeScale(self, plotLen, delay):
+    newXaxis = (0, plotLen * delay / 1000)
+    if newXaxis != self.xaxis:
+      self.xaxis = newXaxis
+      self.on_resize()

+ 12 - 1
software/ui/globals.py

@@ -1,3 +1,14 @@
 
 
-LARGE_FONT= ("Verdana", 12)
+LARGE_FONT= ("Verdana", 12)
+
+GRAPH_COLORS = [
+  "#396AB1",
+  "#DA7C30",
+  "#3E9651",
+  "#CC2529",
+  "#535154",
+  "#6B4C9A",
+  "#922428",
+  "#948B3D"
+]

+ 8 - 15
software/ui/plotim.py

@@ -10,13 +10,6 @@ import math
 import numpy as np
 
 
-def vertical_text(text):
-    newtext = ""
-    for character in text:
-        newtext += character + "\n"
-    return newtext
-
-
 class linear_plot(object):
     def __init__(self, bordernorth=50, bordersouth=50, bordereast=30, borderwest=50,
                  title="Linear Plot", \
@@ -26,8 +19,7 @@ class linear_plot(object):
         self.bordersouth = bordersouth
         self.bordereast = bordereast
         self.borderwest = borderwest
-        self.originalyaxistitle = ytitle
-        self.yaxistitle = vertical_text(ytitle)
+        self.yaxistitle = ytitle
         self.xaxistitle = xtitle
         self.yaxis = [0, 10]
         self.xaxis = [0, 10]
@@ -65,10 +57,9 @@ class linear_plot(object):
         self.graphx = self.windowx - self.bordereast - self.borderwest
         self.graphy = self.windowy - self.bordernorth - self.bordersouth
        
-        self.canvas.create_text(self.borderwest + self.graphx / 2, self.bordernorth / 2, text=self.title)
-        self.canvas.create_text(self.borderwest / 3, self.bordernorth + self.graphy / 2, text=self.yaxistitle)
-        self.canvas.create_text(self.borderwest + self.graphx / 2, self.windowy - self.bordersouth / 3,
-                                text=self.xaxistitle)
+        self.canvas.create_text(self.borderwest + self.graphx / 2, self.bordernorth / 2, text=self.title, font=("Arial","26"))
+        self.canvas.create_text(self.borderwest / 3, self.bordernorth + self.graphy / 2, text=self.yaxistitle, angle=90)
+        self.canvas.create_text(self.borderwest + self.graphx / 2, self.windowy - self.bordersouth / 3, text=self.xaxistitle)
 
         self.canvas.create_rectangle(self.bordernorth, self.borderwest, (self.windowx - self.bordereast),
                                      (self.windowy - self.bordersouth), fill="white", outline="white")
@@ -130,7 +121,8 @@ class linear_plot(object):
                                     (self.windowy - self.bordersouth) - (increment / self.yincrement) * (
                                             self.windowy - self.bordernorth - self.bordersouth), fill="#bbbbbb",
                                     dash=(2, 2), width=2 if increment % 5 == 0 else 1)
-            self.canvas.create_text(self.borderwest - 12,
+            if increment % 5 == 0:
+              self.canvas.create_text(self.borderwest - 12,
                                     (self.windowy - self.bordersouth) - (increment / self.yincrement) * (
                                             self.windowy - self.bordernorth - self.bordersouth), \
                                     text="{0:4.4}".format((int((self.yaxis[0]) * (10 ** (self.yrangefactor)))) / (
@@ -144,7 +136,8 @@ class linear_plot(object):
                 (self.windowy - self.bordersouth) - 1, \
                 self.bordersouth + (increment / self.xincrement) * (self.windowx - self.bordereast - self.borderwest),
                 (self.windowy - self.bordersouth - self.graphy), fill="#bbbbbb", dash=(2, 2), width=2 if increment % 5 == 0 else 1)
-            self.canvas.create_text(
+            if increment % 5 == 0:
+              self.canvas.create_text(
                 self.borderwest + (increment / self.xincrement) * (self.windowx - self.bordereast - self.borderwest),
                 (self.windowy - self.bordersouth) + 12, \
                 text="{0:4.4}".format((int((self.xaxis[0]) * (10 ** (self.xrangefactor)))) / (10 ** (self.xrangefactor)) + (

+ 25 - 8
software/wirelessLoadCell/loadCells.py

@@ -8,6 +8,7 @@ import time
 import bluetooth
 import threading
 import ctypes
+import numpy as np
 
 class LoadCells():
   def __init__(self):
@@ -16,7 +17,9 @@ class LoadCells():
       GSV4BT("00:0B:CE:04:F6:67"),
       GSV4BT("00:0B:CE:04:F6:68"),
     )
-  
+    self.tareValues = np.ndarray(shape=(len(self.cells),3), dtype=float)
+    self.tareValues.fill(0)
+
   def connect(self):
     success = True
     for cell in self.cells:
@@ -51,17 +54,31 @@ class LoadCells():
     self.thread = threading.Thread(target=self.reconnectThread)
     self.thread.start()
   
+
+  def tare(self):
+    for i in range(len(self.cells)):
+      cell = self.cells[i]
+      if (not cell.isConnected()) or cell.requiresSetup:
+        self.tareValues[i].fill(0)
+      else:
+        cell.getValue()
+        self.tareValues[i] = cell.getForces()
+    self.tareValues[np.isnan(self.tareValues)] = 0
+
   def stop(self):
     if self.running:
       self.running = False
 
-  def getForces(self, id):
-    cell = self.cells[id]
-    if (not cell.isConnected()) or cell.requiresSetup:
-      return [None,None,None]
-
-    cell.getValue()
-    return cell.getForces()
+  def getForces(self):
+    res = np.ndarray(shape=(len(self.cells),3), dtype=float)
+    for i in range(len(self.cells)):
+      cell = self.cells[i]
+      if (not cell.isConnected()) or cell.requiresSetup:
+        res[i].fill(float('NaN'))
+      else:
+        cell.getValue()
+        res[i] = cell.getForces()
+    return res - self.tareValues
 
   def scan(self):
     nearby_devices = bluetooth.discover_devices(lookup_names=True)