B4J Question B4J and serial problem

hakha4

Member
Licensed User
Newbie to B4J. I made a small program receiving data from an arduino on serial port. Code below works for a while and then crashes:

B4X:
Sub AStream_NewData (Buffer() As Byte)
    '***********************************************
    Dim inputdata As String = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
    Dim node_id As String
    Dim node_location As String
    Dim data_1 As String
    Dim data_2 As String
    'Dim OutList As List
    Dim NL As String
    NL = Chr(13)& Chr(10)
    '**************************



  If inputdata.EndsWith(NL) Then
    Log("data OK")

Dim OutList() As String = (Regex.Split(",", inputdata))

    node_id  = OutList(0)
    Log(node_id)
    node_location  = OutList(1)
    Log(node_location)
    data_1 = OutList(2)
    Log(data_1)
    data_2 =  OutList(3)
    Log(data_2)


    Else
    Log("data ERROR")
    End If
    'Log(OutList)

End Sub
LOG :
Program started.
data ERROR
data OK
GRUND
36.00
21.00
data OK
Sensor 1
GRUND
36.00
21.00
data ERROR
data OK
GRUND
36.00
21.00
Error occurred on line: 60 (main).
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at anywheresoftware.b4a.shell.Shell.runGoodChain(Shell.java:334)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:166)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:156)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:93)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:82)
at anywheresoftware.b4a.BA$3.run(BA.java:178)
at com.sun.javafx.application.PlatformImpl$4$1.run(Unknown Source)
at com.sun.javafx.application.PlatformIm
pl$4$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl$4.run(Unknown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$100(Unknown Source)
at com.sun.glass.ui.win.WinApplication$3$1.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ArrayIndexOutOfBoundsException: 3
at b4j.example.main._astream_newdata(main.java:183)
... 23 more

Can someone give a hint how to catch serial data in a robust way.


BTW awesome software when you get the hang of it ! Thank's

Regards Håkan
 
Last edited:

hakha4

Member
Licensed User
Thanks Erel for pointing out how to post properly. Anyone that can tell what's wrong and give an idea how to code ?
 

raphaelcno

Active Member
Licensed User
It seems like the 'inputdata' variable only contains the last bytes received in AStream_NewData. Unfortunately you cannot have control about how many bytes have arrived when Sub AStream_NewData is executed. The Sub does not wait until it receives NL before it starts its execution.

As I understand from your code, you send 4 fields separated by "," from your Arduino.

Most of the time you are lucky to receive all bytes before Sub AStream_NewData is executed.

But sometimes you receive first 1 field
=> Sub AStream_NewData is executed
=> inputdata.EndsWith(NL) = False
=> Log("data ERROR"),
then you receive the last 3 fields and NL
=> Sub AStream_NewData is executed
=> inputdata.EndsWith(NL) = True
=> OutList() = Regex.Split(",", inputdata) is executed, but OutList() contains only 3 elements (not 4)
=> data_2 = OutList(3) is executed and crashes.
That's why you get error message "Caused by: java.lang.ArrayIndexOutOfBoundsException: 3".

You need to store the received data in a global variable until you receive NL, something like this:

B4X:
Sub Globals
   Dim inputdata As String = ""
End Sub
Sub AStream_NewData (Buffer() As Byte)
   inputdata = inputdata & BytesToString(Buffer, 0, Buffer.Length, "UTF8")
   '...
   If inputdata.EndsWith(NL) Then
      Dim OutList() AsString = (Regex.Split(",", inputdata))
      inputdata = ""
      '...
   End If
End Sub
This code will probably work as long as Arduino doesn't send a new message immediately after the previous one, otherwise you could get bytes from the new message just after NL from the previous message => inputdata.EndsWith(NL) = False .
You can use AsyncStreamsText class if necessary (http://www.b4x.com/android/forum/th...eful-when-working-with-streams-of-text.27002/).

Edit: I don't know if AsyncStreamsText class can be used in B4J or if it's only for B4A.
 
Last edited:

hakha4

Member
Licensed User
Thank's for help. Making inputdata global improved things and indata is more reliable now. I lookup AsyncStreamsText and try to implement this
 
Top