GSV4BT.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import sys
  4. import bluetooth
  5. import time
  6. class GSV4BT():
  7. # https://www.manualslib.com/manual/1380505/Me-Gsv-4.html?page=30#manual
  8. scalings = {
  9. # (ID, value for 0xFFFF, unit)
  10. '2mV': (0x01, 2.1 , 'mV/V'), # Measuring range ±2 mV/V (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x01
  11. '10mV': (0x02, 10.5, 'mV/V'), # Measuring range ±10 mV/V (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x02
  12. '5V': (0x03, 5.25, 'V' ), # Measuring range 0-5 V (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x03
  13. '10V': (0x07, 10.5, 'V' ), # Measuring range 0-10 V (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x07
  14. 'PT1000': (0x04, 1050, '°C' ), # Measuring range PT1000 (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x04
  15. 'K': (0x06, 1050, '°C' ) # Measuring range K-thermocouple cable (set_gain 0xB2 <p1> <p2>) with p1=ch, p2=0x06
  16. }
  17. channelModes = [
  18. None,
  19. None,
  20. None,
  21. None
  22. ]
  23. values = [0] * 4
  24. frequencies = [
  25. # (ID, fNom in Hz, fEff in Hz)
  26. (0xA0, 0.63, 0.625),
  27. (0xA1, 1.25, 1.250),
  28. (0xA2, 2.5 , 2.500),
  29. (0xA3, 3.75, 3.750),
  30. (0xA4, 6.25, 6.250),
  31. (0xA5, 7.5 , 7.500),
  32. (0xA6, 12.5, 12.40),
  33. (0xA7, 15 , 14.7 ),
  34. (0xA8, 25 , 24.4 ),
  35. (0xA9, 125 , 125 ),
  36. (0xAA, 250 , 250 ),
  37. (0xAB, 500 , 500 ),
  38. (0xAC, 937.5, 900 ),
  39. ]
  40. def __init__(self, addr):
  41. self.addr = addr
  42. self.uuid = None
  43. self.sock = None
  44. self.requiresSetup = True
  45. def printError(self, msg, var = ""):
  46. print("ERROR: GSV4BT {}:".format(self.addr), msg, var)
  47. def printWarning(self, msg, var = ""):
  48. print("WARNING: GSV4BT {}:".format(self.addr), msg, var)
  49. def connect(self):
  50. if self.sock:
  51. return True
  52. service_matches = bluetooth.find_service(address=self.addr, uuid=bluetooth.SERIAL_PORT_CLASS)
  53. if len(service_matches) == 0:
  54. self.printError("BT device not found.")
  55. return False
  56. first_match = service_matches[0]
  57. self.port = first_match["port"]
  58. self.name = first_match["name"]
  59. self.host = first_match["host"]
  60. self.sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
  61. try:
  62. ret = self.sock.connect((self.addr, self.port))
  63. except bluetooth.btcommon.BluetoothError as e:
  64. self.printError(e)
  65. self.sock.close()
  66. self.sock = None
  67. return False
  68. self.printWarning("Connected to \"{}\" on {} port {}".format(self.name, self.host, self.port))
  69. self.sock.settimeout(0.3)
  70. return True
  71. def isConnected(self):
  72. return self.sock != None
  73. def disconnect(self):
  74. if self.sock:
  75. self.sock.close()
  76. self.sock == None
  77. def sendRaw(self, data):
  78. if self.sock:
  79. try:
  80. self.sock.send(data)
  81. except bluetooth.btcommon.BluetoothError as e:
  82. self.printError("send", e)
  83. def sendCommand(self, code, *params):
  84. data = bytes([code] + list(params))
  85. return self.sendRaw(data)
  86. def setNormalMode(self):
  87. return self.sendRaw(bytes([0x26, 0x01, 0x62, 0x65, 0x72, 0x6C, 0x69, 0x6E]))
  88. def getMode(self):
  89. self.sendCommand(0x27)
  90. return self.waitResponse(0x27)
  91. def getFirmwareVersion(self):
  92. self.sendCommand(0x2B)
  93. return self.waitResponse(0x2B)
  94. def getTxStatus(self):
  95. self.sendCommand(0x29)
  96. return self.waitResponse(0x29)
  97. # https://www.manualslib.com/manual/1380505/Me-Gsv-4.html?page=34#manual
  98. def setGain(self, channel, scalingName = '2mV'):
  99. self.channelModes[channel] = scalingName
  100. self.sendCommand(0xB2, channel, self.scalings[scalingName][0])
  101. return self.waitResponse(0xB2)
  102. def setFrequency(self, freq = 10):
  103. for id, fNom, fEff in self.frequencies:
  104. value = id
  105. if freq < fEff:
  106. break
  107. self.sendCommand(0x12, value)
  108. return self.waitResponse(0x12)
  109. def startTransmission(self):
  110. self.sendCommand(0x24)
  111. return self.waitResponse(0x24)
  112. def stopTransmission(self):
  113. self.sendCommand(0x23)
  114. return self.waitResponse(0x23)
  115. def getValue(self):
  116. self.sendCommand(0x3B)
  117. lastFrameCarry = None
  118. def recvRaw(self):
  119. if not self.sock:
  120. return
  121. data = None
  122. try:
  123. data = self.sock.recv(1024)
  124. except bluetooth.btcommon.BluetoothError as e:
  125. self.printError("receive", e)
  126. if data == None:
  127. return
  128. if self.lastFrameCarry:
  129. data = self.lastFrameCarry + data
  130. self.lastFrameCarry = None
  131. i = 0
  132. while i < len(data):
  133. prefix = data[i]
  134. start = i
  135. i += 1
  136. end = data.find(b'\r\n', start)
  137. if end == -1:
  138. self.lastFrameCarry = data[start:]
  139. break
  140. else:
  141. i = end + 2
  142. if prefix == 0xA5:
  143. # measured values
  144. if end+2 - start != 11:
  145. self.printError("invalid values frame: length {}".format(end+2-start), data[start:end+2])
  146. continue
  147. self.parseValues(data[start+1:end])
  148. elif prefix == 0x3B:
  149. # response
  150. if end+2 - start < 10:
  151. self.printError("invalid response frame: length {}".format(end+2-start), data[start:end+2])
  152. continue
  153. code = int(data[start+1])
  154. n = int(data[start+2])
  155. length = int.from_bytes(data[start+3:start+5], 'big', signed=False)
  156. no = data[start+5:start+8].decode('ascii')
  157. if end+2 - start != length + 10:
  158. self.printError("incorrect response length field: {}".format(length), data[start:end+2])
  159. continue
  160. self.parseResponse(code, n, no, data[start+8:end])
  161. _valuesCb = None
  162. def setValuesCb(self, cb):
  163. self._valuesCb = cb
  164. def parseValues(self, data):
  165. for i in range(4):
  166. self.values[i] = int.from_bytes(data[i*2:i*2+2], 'big', signed=False)
  167. # map range to units
  168. if self.channelModes[i] == None:
  169. continue
  170. _, scale, unit = self.scalings[self.channelModes[i]]
  171. self.values[i] = (self.values[i] - 32768) / 32768 * scale
  172. if self._valuesCb:
  173. self._valuesCb(self.values)
  174. _respCode = None
  175. _respData = None
  176. def _responseCbWait(self, code, data):
  177. self._respCode = code
  178. self._respData = data
  179. _responseCb = None
  180. def setResponseCb(self, cb):
  181. self._responseCb = cb
  182. def waitResponse(self, sentCode):
  183. userRespCb = self._responseCb
  184. self._respCode = None
  185. self._respData = None
  186. self.setResponseCb(self._responseCbWait)
  187. self.recvRaw()
  188. if self._respCode == None:
  189. return None
  190. if self._respCode != sentCode:
  191. self.printError("invalid response code:", hex(self._respCode))
  192. return None
  193. self.setResponseCb(userRespCb)
  194. return self._respData
  195. def parseResponse(self, code, n, no, data):
  196. if n > 1:
  197. self.printWarning("more than 1 response:", n-1)
  198. if self._responseCb:
  199. self._responseCb(code, data)
  200. def getForces(self):
  201. self.recvRaw()
  202. return self.values[0:3]
  203. def close(self):
  204. self.sock.close()
  205. if __name__ == "__main__":
  206. cell = GSV4BT("00:0B:CE:04:F6:66")
  207. def cb(values):
  208. print(values)
  209. while True:
  210. if cell.isConnected():
  211. cell.recvRaw()
  212. else:
  213. cell.connect()
  214. cell.setNormalMode()
  215. cell.setFrequency(20) # Hz
  216. cell.setGain(0, '2mV')
  217. cell.setGain(1, '2mV')
  218. cell.setGain(2, '2mV')
  219. cell.setGain(3, '5V')
  220. cell.setValuesCb(cb)
  221. print(cell.getMode())
  222. time.sleep(0.3)