问题
I have been fumbling with getting data from a bluetooth RS232 converter using the python for android jnius library. I had thought that it would be as simple as the PySerial library, but as I am still new to java the implementation is quite different.
I created the bluetooth connection quite easily, but as soon as I try to read or write data I get a jnius.jnius.JavaException
that No methods matching your arguments,
and that the methods available for read
are: '()I', '([B)I', '([BII)I'
and for write
are '(I)V', '([B)V', '([BII)V'
. I tried finding this in the developer android docs as well as DuckDuckGoing it but with no clarity.
I also tried the BufferedReader
example (Here) using the readLine()
method, but I am constantly getting an error: JavaException: JVM exception occurred: Attempt to invoke virtual method 'int java.io.InputStream.read(byte[], int, int)' on a null object reference
.
Can someone please point me to the documentation to understand the above read and write arguments?
Also, please help me to understand why the read, readLine() and write
objects does not return any data?
Java libraries I call:
BluetoothAdapter = autoclass('android.bluetooth.BluetoothAdapter')
BluetoothDevice = autoclass('android.bluetooth.BluetoothDevice')
BluetoothSocket = autoclass('android.bluetooth.BluetoothSocket')
InputStreamReader = autoclass('java.io.InputStreamReader')
BufferedReader = autoclass('java.io.BufferedReader')
UUID = autoclass('java.util.UUID')
System = autoclass('java.lang.System')
Connecting code (got this from Github and above link):
def get_socket_stream(self, name):
paired_devices = BluetoothAdapter.getDefaultAdapter().getBondedDevices().toArray()
self.rfsocket == None
for device in paired_devices:
if device.getName() == name:
if device.bluetoothEnabled:
if not device.connected:
self.rfsocket = device.createInsecureRfcommSocketToServiceRecord(
UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"))
self.reader = InputStreamReader(self.rfsocket.getInputStream(), 'LATIN-1')
recv_stream = BufferedReader(self.reader)
send_stream = self.rfsocket.getOutputStream()
break
print(self.rfsocket.getRemoteDevice().getName())
if self.rfsocket.port <= 0:
self.rfsocket = device.createRfcommSocket(1)
if not self.rfsocket.connected:
print('port two: ',self.rfsocket.port)
self.rfsocket.connect()
Read and write code (source: same as above link):
def GetBSerial(self):
self.messager('Trying Serial')
self.recv_stream, self.send_stream = self.get_socket_stream(devicename)
if not self.rfsocket.connected and self.recv_stream == None:
print("Get paired device failed")
else:
print('Initiating trigger')
self.weight_ticker()
def GetInput(self):
self.send_stream.write("Hallo\r\n")
self.send_stream.flush
if self.rfsocket.connected and self.recv_stream != None:
if self.weigh_tme > 0:
while self.recv_stream.ready != None:
try:
self.scale_output = self.recv_stream.readLine()
except jnius.jnius.JavaException as e:
print("JavaException: ", e, self.rfsocket.connected)
except ValueError as e:
print("Misc error: ", e)
try:
self.show_input(self.scale_output)
except ValueError:
pass
Update:
So I finally got the Input using the readLine()
method to not return an error and also return the string. I cleaned things up a bit, but the code does not differ much. Main thing though is I checked whether the device != None
and if rfsocket.connected:
before creating getInputStream within my eventloop, so as not to re-create the socket object. Have to test more to see where was the main problem. Still do not no what the arguments are of the read
and write
method. The readLine()
method returns the string intermitantly or not at all and my eventloop seems to not work with the readLine()
method.
Update on update:
The event loop works again. My bad, I did not call the trigger object correctly. The readLine()
method has a strange pattern in that on the first read it gives me JavaException: JVM exception occurred: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
, subsequent reads gives me pieces of the expected string or an empty string. I had similar string bits and pieces returned when I received the data via a hardline using PySerial. The solution was resetting the input buffer. Is there something similar in the above java libraries?
Finally cracked the exceptions:
Yes, this is exciting! After many hours I had noticed that I could not get the Input anymore only exceptions. I tried the BufferedInputStream
library and got the same result, there was no more intermittant read. Why? So I re-Apk'd the main
file of last night and boom intermittant Input read once again.
The reason was if I create the java objects when the rfsocket
Bluetooth object is not connected to the specified port, Null
objects was initialised on a different port which for some reason was not seen by the if
blocks self.recv_stream is not None
and self.recv_stream != None
. Probably because they are not Null
objects but Null
for the subsequent port 1 socket connection I specified.
The readline()
work as is in my example, the read()
takes three arguments bytes(), int offset, int len(bytes()
which is not clear from the jnius.jnius.exception
hieroglyphics' message. Still figuring out the write
method. One thing that you can specify in the BufferedReader
method is a 2nd argument for the chunk size you want to read or in java speak defaultCharBufferSize
.
回答1:
So I am posting my answers as I have solved them all.
To connect to bluetooth I built on the GitHub example by following the suggestions on the android developers site. I had to set my own socket explicitly before creating the getOutputStream
and getInputStream
java objects otherwise the ports of the connection and objects will not be the same. You only need to call the GetBSerial()
to connect
def get_socket_stream(self, name):
defaultCharBufferSize = 8192
try:
blueAdapt = BluetoothAdapter.getDefaultAdapter()
if self.rfsocket is not None:
if self.rfsocket.connected:
reader = InputStreamReader(self.rfsocket.getInputStream(), getEncode)
recv_stream = BufferedReader(reader)
send_stream = self.rfsocket.getOutputStream()
else:
self.rfsocket = self.device.createRfcommSocketToServiceRecord(UUID.fromString(getUuid))
if self.get_port_connect():
reader = InputStreamReader(self.rfsocket.getInputStream(), getEncode)
recv_stream = BufferedReader(reader, defaultCharBufferSize)
send_stream = self.rfsocket.getOutputStream()
else:
if blueAdapt is not None:
if blueAdapt.isEnabled():
paired_devices = blueAdapt.getBondedDevices().toArray()
self.rfsocket = None
for self.device in paired_devices:
if self.device.getName() == name:
if self.device.bluetoothEnabled:
self.rfsocket = self.device.createRfcommSocketToServiceRecord(
UUID.fromString(getUuid))
if self.rfsocket is not None:
if self.get_port_connect(): #connect and set the port before creating java objects
reader = InputStreamReader(self.rfsocket.getInputStream(), getEncode)
recv_stream = BufferedReader(reader, defaultCharBufferSize)
send_stream = self.rfsocket.getOutputStream()
break
else:
self.ids.bluet_info.text = '[b]Bluetooth not enabled[/b]'
if recv_stream is not None and send_stream is not None:
return recv_stream, send_stream
else:
return False, False
except UnboundLocalError as e:
return False, False
except TypeError as e:
return False, False
def get_port_connect(self):
try:
if self.rfsocket.port <= 0:
self.rfsocket = self.device.createRfcommSocket(1) #set the port explicitly
if not self.rfsocket.connected:
self.rfsocket.connect()
else:
if not self.rfsocket.connected:
self.rfsocket.connect()
if self.rfsocket.connected:
self.ids.bluet_info.text = '[b]Connected[/b]'
return True
except jnius.jnius.JavaException as e:
self.ids.bluet_info.text = '[b]Cannot connect to socket[/b]'
def GetBSerial(self):
try:
getDevname = self.the.config.get('bluetoothsettings', 'stringbluetdevname')
self.recv_stream, self.send_stream = self.get_socket_stream(getDevname)
except jnius.jnius.JavaException as e:
self.ids.bluet_info.text = '[b]Not Connected[/b]'
I used the readLine()
method, but to use the read()
method, there are two ways to build a string. Either externally (I only tried this one) or in an Array.
Import:
CharBuilder = autoclass('java.lang.Character')
Externally:
if self.recv_stream.ready() != None:
r = self.recv_stream.read()
theChar = CharBuilder.toChars(r) #gives a tuple of which the first element is a character
self.read += theChar[0]
You have to play around with building your string to know where the string must start.
The first thing about the write()
method is it takes a bytes object. So you build a bytearray()
and use it as an argument. Example using ESC/POS printer initialise command and a string:
i = [27,64] #ASCII escape integer and at sign integer
pre = bytearray(i)
cmd = 'Hello You\n'.encode('UTF-8')
#extend bytearray
pre.extend(cmd)
self.send_stream.write(pre)
self.send_stream.flush()
Could not figure out how to create a bytearray integer and string in one go, yet.
来源:https://stackoverflow.com/questions/58477961/how-to-use-the-pyjnius-read-and-write-arguments-for-bluetooth-getinputstream-and