Browse Source

Merge branch 'master' of https://dyndns.wheretonext.de:8810/subDesTagesMitExtraKaese/windkanal

subDesTagesMitExtraKaese 4 years ago
parent
commit
8d32b29e02

+ 2 - 1
.gitignore

@@ -1,3 +1,4 @@
 *.code-workspace
 __pycache__/
-.vscode
+.vscode
+*.csv

+ 22 - 2
software/appWindow.py

@@ -1,7 +1,9 @@
 # The code for changing pages was derived from: http://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter
 # License: http://creativecommons.org/licenses/by-sa/3.0/	
 
+from database import Table
 from analogPressure.sdpArray import SdpArray
+from analogPressure.mcp3008 import MCP3008
 from digitalPressure.sdp610Array import Spd610Array
 from wirelessLoadCell.loadCells import LoadCells
 from ui import *
@@ -11,12 +13,26 @@ import tk_tools
 from time import *
 
 
-class Main(tk.Tk):
+class Main(tk.Tk, Table):
   def __init__(self, *args, **kwargs):
     tk.Tk.__init__(self, *args, **kwargs)
     tk.Tk.wm_title(self, "Windkanal-Tool")
+
+    self.adc = MCP3008(0,0)
+    self.pressureSensors = Spd610Array()
+    self.forceSensors = LoadCells()
+    self.motorController = None
     
+    Table.__init__(self, 
+    ["time", "windspeed", "motor_pwm"] + 
+    [f"pressure_{i}" for i in range(8)] + 
+    [f"adc_{i}" for i in range(8)] + 
+    [f"force_X_1", f"force_Y_1", f"force_Z_1"] +
+    [f"force_X_2", f"force_Y_2", f"force_Z_2"] + 
+    [f"force_X_3", f"force_Y_3", f"force_Z_3"])
     
+    self.saveAsCsv("test.csv")
+
     container = tk.Frame(self)
     container.pack(side="top", fill="both", expand = True)
     container.grid_rowconfigure(0, weight=1)
@@ -64,6 +80,8 @@ class Main(tk.Tk):
 
     self.show_frame(Page_1)
 
+    self.interval()
+
   def show_frame(self, cont):
     frame = self.frames[cont]
     frame.tkraise()
@@ -76,7 +94,9 @@ class Main(tk.Tk):
       b1 = tk.Button(popup, text="Okay", command=popup.destroy)
       b1.pack()
   def interval(self):
-    
+    adcValues = self.adc.read()
+
+    self.addRow([time.time()] + adcValues)
     for frame in self.frames:
       frame.update()
 

+ 27 - 0
software/database.py

@@ -0,0 +1,27 @@
+
+import csv
+
+class Table:
+  columnNames = []
+  rows = []
+  def __init__(self, columns):
+    self.columnNames = columns
+
+  def addRow(self, values):
+    self.rows.append(values)
+
+  def getLastRow(self):
+    if len(self.rows) > 0:
+      return rows[len(self.rows)-1]
+    else:
+      return [0] * len(self.columnNames)
+
+  def getLastValue(self, column):
+    col = self.columnNames.index(column)
+    return self.getLastRow(col)
+
+  def saveAsCsv(self, filename):
+    with open(filename,"w+", newline='') as my_csv:
+      csvWriter = csv.writer(my_csv,delimiter=',')
+      csvWriter.writerow(self.columnNames)
+      csvWriter.writerows(self.rows)

+ 1 - 1
software/ui/Page_1.py

@@ -43,5 +43,5 @@ class Page_1(tk.Frame):
     label3.pack()
 
   def update(self):
-    self.serialPlot.update(3)
+    self.serialPlot.update(controller.getLastValue("force_X_1"))
     self.label4.config(text="{:3d} Nm".format(3))

+ 3 - 1
software/ui/Page_2.py

@@ -11,4 +11,6 @@ class Page_2(tk.Frame):
     button1 = tk.Button(self, text="Page_0", command=lambda: controller.show_frame(Page_0))
     button1.pack()
     button2 = tk.Button(self, text="Page_1", command=lambda: controller.show_frame(Page_1))
-    button2.pack()
+    button2.pack()
+  def update(self):
+    pass

+ 2 - 0
software/ui/Page_3.py

@@ -12,3 +12,5 @@ class Page_3(tk.Frame):
     button1.pack()
     button2 = tk.Button(self, text="Page_1", command=lambda: controller.show_frame(Page_1))
     button2.pack()
+  def update(self):
+    pass

+ 3 - 1
software/ui/Page_4.py

@@ -10,4 +10,6 @@ class Page_4(tk.Frame):
     button1 = tk.Button(self, text="Page_0", command=lambda: controller.show_frame(Page_0))
     button1.pack()
     button2 = tk.Button(self, text="Page_1", command=lambda: controller.show_frame(Page_1))
-    button2.pack()
+    button2.pack()
+  def update(self):
+    pass

+ 197 - 5
software/wirelessLoadCell/GSV4BT.py

@@ -2,18 +2,58 @@ import sys
 import bluetooth
 
 class GSV4BT():
+  # https://www.manualslib.com/manual/1380505/Me-Gsv-4.html?page=30#manual
+  scalings = {
+    # (ID, value for 0xFFFF, unit)
+    '2mV':    (0x01, 2.1 , 'mV/V'),  # Measuring range ±2 mV/V (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x01
+    '10mV':   (0x02, 10.5, 'mV/V'),  # Measuring range ±10 mV/V (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x02
+    '5V':     (0x03, 5.25, 'V'   ),  # Measuring range 0-5 V (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x03
+    '10V':    (0x07, 10.5, 'V'   ),  # Measuring range 0-10 V (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x07
+    'PT1000': (0x04, 1050, '°C'  ),  # Measuring range PT1000 (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x04
+    'K':      (0x06, 1050, '°C'  )   # Measuring range K-thermocouple cable (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x06
+  }
+  channelModes = [
+    None,
+    None,
+    None,
+    None
+  ]
+
+  frequencies = [
+    # (ID, fNom in Hz, fEff in Hz)
+    (0xA0, 0.63, 0.625),
+    (0xA1, 1.25, 1.250),
+    (0xA2, 2.5 , 2.500),
+    (0xA3, 3.75, 3.750),
+    (0xA4, 6.25, 6.250),
+    (0xA5, 7.5 , 7.500),
+    (0xA6, 12.5, 12.40),
+    (0xA7, 15  , 14.7 ),
+    (0xA8, 25  , 24.4 ),
+    (0xA9, 125 , 125  ),
+    (0xAA, 250 , 250  ),
+    (0xAB, 500 , 500  ),
+    (0xAC, 937.5, 900 ),
+  ]
+
   def __init__(self, addr):
     self.addr = addr
     self.uuid = None
     self.sock = None
 
+  def printError(self, msg):
+    print("ERROR:   GSV4BT {}:".format(self.addr), msg)
+
+  def printWarning(self, msg):
+    print("WARNING: GSV4BT {}:".format(self.addr), msg)
+
   def connect(self):
     if self.sock:
       return True
 
     service_matches = bluetooth.find_service(address=self.addr)
     if len(service_matches) == 0:
-      print("BT device {} not found.".format(self.addr))
+      self.printError("BT device not found.")
       return False
     
     first_match = service_matches[0]
@@ -22,19 +62,171 @@ class GSV4BT():
     self.name = first_match["name"]
     self.host = first_match["host"]
 
-    print("Connecting to \"{}\" on {}".format(self.name, self.host))
+    self.printWarning("Connecting to \"{}\" on {}".format(self.name, self.host))
 
     self.sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
-    return self.sock.connect((self.host, self.port))
+    ret = self.sock.connect((self.host, self.port))
+    self.sock.settimeout(0.3)
+    return ret
+  
+  def isConnected(self):
+    return self.sock and True
   
   def sendRaw(self, data):
     self.sock.send(data)
 
+  def sendCommand(self, code, *params):
+    data = bytes([code] + params)
+    return self.sendRaw(data)
+
+  def setNormalMode(self):
+    return self.sendRaw(bytes(0x26, 0x01, 0x62, 0x65, 0x72, 0x6C, 0x69, 0x6E))
+
+  def getMode(self):
+    self.sendCommand(0x27)
+    return self.waitResponse(0x27)
+
+  def getFirmwareVersion(self):
+    self.sendCommand(0x2B)
+    return self.waitResponse(0x2B)
+  
+  def getTxStatus(self):
+    self.sendCommand(0x29)
+    return self.waitResponse(0x29)
+
+  # https://www.manualslib.com/manual/1380505/Me-Gsv-4.html?page=34#manual
+  def setGain(self, channel, scalingName = '2mV'):
+    self.channelModes[channel] = scalingName
+    self.sendCommand(0xB2, channel, self.scalings[scalingName][0])
+    return self.waitResponse(0xB2)
+
+  def setFrequency(self, freq = 10):
+    for id, fNom, fEff in self.frequencies:
+      value = id
+      if freq < fEff:
+        break
+    self.sendCommand(0x12, value)
+    return self.waitResponse(0x12)
+
+  def startTransmission(self):
+    self.sendCommand(0x24)
+    return self.waitResponse(0x24)
+
+  def stopTransmission(self):
+    self.sendCommand(0x23)
+    return self.waitResponse(0x23)
+
+  def getValue(self):
+    self.sendCommand(0x3B)
+
+  lastFrameCarry = None
   def recvRaw(self):
-    return self.sock.recv(1024)
+    data = self.sock.recv(1024)
+    if self.lastFrameCarry:
+      data = self.lastFrameCarry + data
+
+    i = 0
+    while i < len(data):
+      prefix = data[i]
+      start = i
+      i += 1
+      end = data.find(b'\r\n', start)
+
+      if end == -1:
+        self.lastFrameCarry = data[start:]
+        break
+      else:
+        i = end + 2
+
+      if prefix == 0xA5:
+        # measured values
+        if end+2 - start != 11:
+          self.printError("invalid frame: values", data[start:end+2])
+          continue
+        self.parseValues(data[start+1:end])
+      elif prefix == 0x3B:
+        # response
+        if end+2 - start < 10:
+          self.printError("invalid frame: response", data[start:end+2])
+          continue
+
+        code   = int(data[start+1])
+        n      = int(data[start+2])
+        length = int.from_bytes(data[start+3:start+5], 'little', False)
+        no     = int.from_bytes(data[start+5:start+8], 'little', False)
+        if end+2 - start != length + 10:
+          self.printError("invalid length: {}", data[start:end+2])
+          continue
+        self.parseResponse(code, n, no, data[start+8:end])
+
+  _valuesCb = None
+
+  def setValuesCb(self, cb):
+    self._valuesCb = cb
+
+  def parseValues(self, data):
+    values = [None]*4
+    for i in range(4):
+      values[i] = int.from_bytes(data[i*2:i*2+2], 'little', False)
+      # map range to units
+      if self.channelModes[i] == None:
+        continue
+      _, scale, unit = self.scalings[self.channelModes[i]]
+      values[i] = (values[i] - 32768) / 32768 * scale
+      if not self._valuesCb:
+        self.printWarning("missed reading: ch {}: {:8.3f} {}".format(i, values[i], unit))
+    if self._valuesCb:
+      _valuesCb(values)
+
+  _responseCb = None
+
+  def setResponseCb(self, cb):
+    self._responseCb = cb
+
+  def waitResponse(self, sentCode):
+    userRespCb = self._responseCb
+    recvCode = None
+    recvData = None
+    def cb(code, data):
+      recvCode = code
+      recvData = data
+    self.setResponseCb(cb)
+    self.recvRaw()
+    if recvCode != sentCode:
+      self.printError("invalid response code:", recvCode)
+      return None
+    self.setResponseCb(userRespCb)
+    return recvData
+
+  _responseNo = 0
+  def parseResponse(self, code, n, no, data):
+    if no != (self._responseNo + 1) % 2**24:
+      self.printWarning("responses skipped:", (no-self._responseNo+2**24-1) % 2**24)
+    self._responseNo = no
+    if n > 0:
+      self.printWarning("more than 1 response:", n-1)
+
+    if self._responseCb:
+      self._responseCb(code, data)
 
   def getForces(self):
     return (0, 0, 0)
 
   def close(self):
-    self.sock.close()
+    self.sock.close()
+
+if __name__ == "__main__":
+  cell = GSV4BT("01:23:45:67:89:01")
+  while True:
+    if cell.isConnected():
+      cell.recvRaw()
+    else:
+      cell.connect()
+      cell.setNormalMode()
+      cell.setFrequency(20) # Hz
+      cell.setGain(0, '2mV')
+      cell.setGain(1, '2mV')
+      cell.setGain(2, '2mV')
+      cell.setGain(3, '5V')
+      cell.setValuesCb(lambda values: print(values))
+    time.sleep(0.3)