Advertisement
Guest User

Untitled

a guest
Feb 18th, 2018
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.26 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. import collections
  4. import time
  5. import bluetooth
  6. import sys
  7. import subprocess
  8.  
  9. CONTINUOUS_REPORTING = "04" # Easier as string with leading zero
  10.  
  11. COMMAND_LIGHT = 11
  12. COMMAND_REPORTING = 12
  13. COMMAND_REQUEST_STATUS = 15
  14. COMMAND_REGISTER = 16
  15. COMMAND_READ_REGISTER = 17
  16.  
  17. #input is Wii device to host
  18. INPUT_STATUS = 20
  19. INPUT_READ_DATA = 21
  20.  
  21. EXTENSION_8BYTES = 32
  22. #end "hex" values
  23.  
  24. BUTTON_DOWN_MASK = 8
  25.  
  26. TOP_RIGHT = 0
  27. BOTTOM_RIGHT = 1
  28. TOP_LEFT = 2
  29. BOTTOM_LEFT = 3
  30.  
  31. BLUETOOTH_NAME = "Nintendo RVL-WBC-01"
  32.  
  33. SCALE_1 = "34:AF:2C:2E:84:BE"
  34. TR_SCALE_A = "34:AF:2C:2D:4E:C2"
  35.  
  36.  
  37. class EventProcessor:
  38. def __init__(self):
  39. self._measured = False
  40. self.done = False
  41. self._events = []
  42.  
  43. def mass(self, event):
  44. if event.totalWeight > 30:
  45. self._events.append(event.totalWeight)
  46. if not self._measured:
  47. print("Starting measurement.")
  48. self._measured = True
  49. elif self._measured:
  50. self.done = True
  51.  
  52. @property
  53. def weight(self):
  54. if not self._events:
  55. return 0
  56. histogram = collections.Counter(round(num, 1) for num in self._events)
  57. return histogram.most_common(1)[0][0]
  58.  
  59.  
  60. class BoardEvent:
  61. def __init__(self, topLeft, topRight, bottomLeft, bottomRight, buttonPressed, buttonReleased):
  62.  
  63. self.topLeft = topLeft
  64. self.topRight = topRight
  65. self.bottomLeft = bottomLeft
  66. self.bottomRight = bottomRight
  67. self.buttonPressed = buttonPressed
  68. self.buttonReleased = buttonReleased
  69. #convenience value
  70. self.totalWeight = topLeft + topRight + bottomLeft + bottomRight
  71.  
  72.  
  73. class Wiiboard:
  74. def __init__(self, processor):
  75. # Sockets and status
  76. self.receivesocket = None
  77. self.controlsocket = None
  78.  
  79. self.processor = processor
  80. self.calibration = []
  81. self.calibrationRequested = False
  82. self.LED = False
  83. self.address = None
  84. self.buttonDown = False
  85. for i in xrange(3):
  86. self.calibration.append([])
  87. for j in xrange(4):
  88. self.calibration[i].append(10000) # high dummy value so events with it don't register
  89.  
  90. self.status = "Disconnected"
  91. self.lastEvent = BoardEvent(0, 0, 0, 0, False, False)
  92.  
  93. try:
  94. self.receivesocket = bluetooth.BluetoothSocket(bluetooth.L2CAP)
  95. self.controlsocket = bluetooth.BluetoothSocket(bluetooth.L2CAP)
  96. except ValueError:
  97. raise Exception("Error: Bluetooth not found")
  98.  
  99. def isConnected(self):
  100. return self.status == "Connected"
  101.  
  102. # Connect to the Wiiboard at bluetooth address <address>
  103. def connect(self, address):
  104. if address is None:
  105. print("Non existant address")
  106. return
  107. self.receivesocket.connect((address, 0x13))
  108. self.controlsocket.connect((address, 0x11))
  109. if self.receivesocket and self.controlsocket:
  110. print("Connected to Wiiboard at address " + address)
  111. self.status = "Connected"
  112. self.address = address
  113. self.calibrate()
  114. useExt = ["00", COMMAND_REGISTER, "04", "A4", "00", "40", "00"]
  115. self.send(useExt)
  116. self.setReportingType()
  117. print("Wiiboard connected")
  118. else:
  119. print("Could not connect to Wiiboard at address " + address)
  120.  
  121. def receive(self):
  122. #try:
  123. # self.receivesocket.settimeout(0.1) #not for windows?
  124. while self.status == "Connected" and not self.processor.done:
  125. data = self.receivesocket.recv(25)
  126. intype = int(data.encode("hex")[2:4])
  127. if intype == INPUT_STATUS:
  128. # TODO: Status input received. It just tells us battery life really
  129. self.setReportingType()
  130. elif intype == INPUT_READ_DATA:
  131. if self.calibrationRequested:
  132. packetLength = (int(str(data[4]).encode("hex"), 16) / 16 + 1)
  133. self.parseCalibrationResponse(data[7:(7 + packetLength)])
  134.  
  135. if packetLength < 16:
  136. self.calibrationRequested = False
  137. elif intype == EXTENSION_8BYTES:
  138. self.processor.mass(self.createBoardEvent(data[2:12]))
  139. else:
  140. print("ACK to data write received")
  141.  
  142. self.status = "Disconnected"
  143. self.disconnect()
  144.  
  145. def disconnect(self):
  146. if self.status == "Connected":
  147. self.status = "Disconnecting"
  148. while self.status == "Disconnecting":
  149. self.wait(100)
  150. try:
  151. self.receivesocket.close()
  152. except:
  153. pass
  154. try:
  155. self.controlsocket.close()
  156. except:
  157. pass
  158. print("WiiBoard disconnected")
  159.  
  160. # Try to discover a Wiiboard
  161. def discover(self):
  162. print("Press the red sync button on the board now")
  163. address = None
  164. bluetoothdevices = bluetooth.discover_devices(duration=6, lookup_names=True)
  165. for bluetoothdevice in bluetoothdevices:
  166. if bluetoothdevice[1] == BLUETOOTH_NAME:
  167. address = bluetoothdevice[0]
  168. print("Found Wiiboard at address " + address)
  169. if address is None:
  170. print("No Wiiboards discovered.")
  171. return address
  172.  
  173. def createBoardEvent(self, bytes):
  174. buttonBytes = bytes[0:2]
  175. bytes = bytes[2:12]
  176. buttonPressed = False
  177. buttonReleased = False
  178.  
  179. state = (int(buttonBytes[0].encode("hex"), 16) << 8) | int(buttonBytes[1].encode("hex"), 16)
  180. if state == BUTTON_DOWN_MASK:
  181. buttonPressed = True
  182. if not self.buttonDown:
  183. print("Button pressed")
  184. self.buttonDown = True
  185.  
  186. if not buttonPressed:
  187. if self.lastEvent.buttonPressed:
  188. buttonReleased = True
  189. self.buttonDown = False
  190. print("Button released")
  191.  
  192. rawTR = (int(bytes[0].encode("hex"), 16) << 8) + int(bytes[1].encode("hex"), 16)
  193. rawBR = (int(bytes[2].encode("hex"), 16) << 8) + int(bytes[3].encode("hex"), 16)
  194. rawTL = (int(bytes[4].encode("hex"), 16) << 8) + int(bytes[5].encode("hex"), 16)
  195. rawBL = (int(bytes[6].encode("hex"), 16) << 8) + int(bytes[7].encode("hex"), 16)
  196.  
  197. topLeft = self.calcMass(rawTL, TOP_LEFT)
  198. topRight = self.calcMass(rawTR, TOP_RIGHT)
  199. bottomLeft = self.calcMass(rawBL, BOTTOM_LEFT)
  200. bottomRight = self.calcMass(rawBR, BOTTOM_RIGHT)
  201. boardEvent = BoardEvent(topLeft, topRight, bottomLeft, bottomRight, buttonPressed, buttonReleased)
  202. return boardEvent
  203.  
  204. def calcMass(self, raw, pos):
  205. val = 0.0
  206. #calibration[0] is calibration values for 0kg
  207. #calibration[1] is calibration values for 17kg
  208. #calibration[2] is calibration values for 34kg
  209. if raw < self.calibration[0][pos]:
  210. return val
  211. elif raw < self.calibration[1][pos]:
  212. val = 17 * ((raw - self.calibration[0][pos]) / float((self.calibration[1][pos] - self.calibration[0][pos])))
  213. elif raw > self.calibration[1][pos]:
  214. val = 17 + 17 * ((raw - self.calibration[1][pos]) / float((self.calibration[2][pos] - self.calibration[1][pos])))
  215.  
  216. return val
  217.  
  218. def getEvent(self):
  219. return self.lastEvent
  220.  
  221. def getLED(self):
  222. return self.LED
  223.  
  224. def parseCalibrationResponse(self, bytes):
  225. index = 0
  226. if len(bytes) == 16:
  227. for i in xrange(2):
  228. for j in xrange(4):
  229. self.calibration[i][j] = (int(bytes[index].encode("hex"), 16) << 8) + int(bytes[index + 1].encode("hex"), 16)
  230. index += 2
  231. elif len(bytes) < 16:
  232. for i in xrange(4):
  233. self.calibration[2][i] = (int(bytes[index].encode("hex"), 16) << 8) + int(bytes[index + 1].encode("hex"), 16)
  234. index += 2
  235.  
  236. # Send <data> to the Wiiboard
  237. # <data> should be an array of strings, each string representing a single hex byte
  238. def send(self, data):
  239. if self.status != "Connected":
  240. return
  241. data[0] = "52"
  242.  
  243. senddata = ""
  244. for byte in data:
  245. byte = str(byte)
  246. senddata += byte.decode("hex")
  247.  
  248. self.controlsocket.send(senddata)
  249.  
  250. #Turns the power button LED on if light is True, off if False
  251. #The board must be connected in order to set the light
  252. def setLight(self, light):
  253. if light:
  254. val = "10"
  255. else:
  256. val = "00"
  257.  
  258. message = ["00", COMMAND_LIGHT, val]
  259. self.send(message)
  260. self.LED = light
  261.  
  262. def calibrate(self):
  263. message = ["00", COMMAND_READ_REGISTER, "04", "A4", "00", "24", "00", "18"]
  264. self.send(message)
  265. self.calibrationRequested = True
  266.  
  267. def setReportingType(self):
  268. bytearr = ["00", COMMAND_REPORTING, CONTINUOUS_REPORTING, EXTENSION_8BYTES]
  269. self.send(bytearr)
  270.  
  271. def wait(self, millis):
  272. time.sleep(millis / 1000.0)
  273.  
  274.  
  275. def main():
  276. processor = EventProcessor()
  277.  
  278. board = Wiiboard(processor)
  279. if len(sys.argv) == 1:
  280. print("Discovering board...")
  281. address = board.discover()
  282. else:
  283. address = sys.argv[1]
  284.  
  285. try:
  286. # Disconnect already-connected devices.
  287. # This is basically Linux black magic just to get the thing to work.
  288. subprocess.check_output(["bluez-test-input", "disconnect", address], stderr=subprocess.STDOUT)
  289. subprocess.check_output(["bluez-test-input", "disconnect", address], stderr=subprocess.STDOUT)
  290. except:
  291. pass
  292.  
  293. print("Trying to connect...")
  294. board.connect(address) # The wii board must be in sync mode at this time
  295. board.wait(200)
  296. # Flash the LED so we know we can step on.
  297. board.setLight(False)
  298. board.wait(500)
  299. board.setLight(True)
  300. board.receive()
  301.  
  302. print(processor.weight)
  303.  
  304. # Disconnect the balance board after exiting.
  305. subprocess.check_output(["bluez-test-device", "disconnect", address], stderr=subprocess.STDOUT)
  306. if __name__ == "__main__":
  307. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement