B4R Question Unable to send lot of data

lucdrb

Member
Licensed User
Hi,

I'll try to command a Servo with my phone, everything goes well but when it send (from phone to arduino) a lot of data (angle for the servo) I got this error:

Out of bounds error. Array length = 1, Index = 1

I've send the data directly or with B4Rserialisator to the arduino but always the same error when I slide the seekbar quickly.

the StackBufferSize and the MaxBufferSize = 1000

Any idea of how should I done to send the data and don't have any error?

Thanks
Luc
 

emexes

Well-Known Member
Licensed User
Difficult to say. Show us the code.

Unless you're wondering why Array length = 1, Index = 1 produces an Out of bounds error, in which case the answer is that with zero-based indexing, your Array of length one eg Dim A(1) has only one single element and it is called A(0), ie not A(1).
 

lucdrb

Member
Licensed User
Here are the code
B4A
B4X:
Sub Process_Globals

End Sub

Sub Globals
    Private Status As String
    Private lblDeviceMessage As Label
    Private lblState As Label
    Private SeekBar1 As SeekBar
    Private SeekBar2 As SeekBar
    Private btnConnect As Button
    Private ProgressBar1 As ProgressBar
    Private ser As B4RSerializator
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("1")
End Sub

Sub Activity_Resume
    SetState
End Sub

Sub Activity_Pause (UserClosed As Boolean)
    If UserClosed = True And lblState.Text = "connected" Then
        CallSub2(Starter, "SendMessage", Array As Byte(0, 0))
    End If
End Sub

Public Sub SetState
    'tbtLED.Enabled = Starter.connected
    btnConnect.Enabled = Not(Starter.connected)
    ProgressBar1.Visible = Starter.connecting
    Dim Status As String
    If Starter.Connected Then
        Status = "connected"
    Else If Starter.TryToConnect Then
        Status = "trying to connect..."
    Else If Starter.Connecting Then
        Status = "HC-06 found connecting..."
    Else
        Status = "disconnected"
    End If
    lblState.Text = $"Status: ${Status}"$
End Sub

Public Sub MessageFromDevice(msg As String)
    lblDeviceMessage.Text = msg
End Sub

Sub btnConnect_Click
    CallSub(Starter, "Connect")
End Sub

Public Sub NewValue(data() As Object)
    If data.Length = 0 Then Return
    For Each o As Object In data
        Log(o)
    Next
End Sub

Sub SeekBar_ValueChanged (Value As Int, UserChanged As Boolean)
    If UserChanged = False Then Return
    Dim s As SeekBar = Sender
'    CallSub2(Starter, "SendMessage", Array As Byte(s.Tag, Value))
    Dim data() As Object = Array As Object(s.Tag,Value)
    CallSub2(Starter, "EnvoieData", data)
End Sub

Public Sub EnvoieData(data() As Object)
    ast.astreams.Write(ser.ConvertArrayToBytes(data))
End Sub
B4R
B4X:
Sub Process_Globals

    Private SoftwareSerial1 As SoftwareSerial
    Private astream As AsyncStreams
    Public Serial1 As Serial
    Public servo1 As Servo
    Public pinservo As Pin
    Public Timer1 As Timer
    Public angleservo As UInt
    Private ser As B4RSerializator
    
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    pinservo.Initialize (10,pinservo.MODE_OUTPUT)    'connect servo to pin 10 of Arduino
    SoftwareSerial1.Initialize(9600, 11, 12) 'software serial port on pins 12 and 11
    astream.Initialize(SoftwareSerial1.Stream, "astream_NewData", Null)
    astream.MaxBufferSize=1000
    servo1.Attach(pinservo.PinNumber)                'assign servo to device on pin servo
    Timer1.Initialize ("Timer1_Tick",1000)           'Call Timer every 1/2 second
    Timer1.Enabled=True
End Sub

Sub Timer1_Tick
    astream.Write("Millis there: ".GetBytes)
    astream.Write(NumberFormat(Millis, 0, 0).GetBytes)
    astream.Write(Array As Byte(10)) 'end of line character. AsyncStreamsText will cut the message here
End Sub

Sub AStream_NewData (Buffer() As Byte)
    Dim sto(10) As Object
    Dim data() As Object = ser.ConvertBytesToArray(Buffer,sto)
    If data.Length = 0 Then Return 'invalid message
    servo1.Write(data(1))
End Sub
As I said many data received in the Arduino make the error code
Out of bounds error. Array length = 1, Index = 1

Luc
 

emexes

Well-Known Member
Licensed User
Another missed-opportunity for defensive/diagnostic programming is:
B4X:
Sub AStream_NewData (Buffer() As Byte)
    ...
    If data.Length = 0 Then Return 'invalid message
    servo1.Write(data(1))
End Sub
where if data.Length = 1, then it passes the invalid message check, but... <downward whistling sound> boom!

edit: just realised: can you tell I am currently watching Air Crash Investigation?
 
Last edited:

emexes

Well-Known Member
Licensed User
We should pitch a new tv series like: B4X Crash Investigations. I am experiencing a crapload of déjà vu.

Every case has at least one instance of the narrator saying: ... and what could have caused that problem, nobody could imagine ...

 

emexes

Well-Known Member
Licensed User
narrator: the pilot stuggles to understand the situation and take back control ...

pilot: we've lost all control, nothing is working, it doesn't make sense...

Still, our situation as programmers could be worse:

narrator: by the time the pilots realise what is happening, it is too late... there is no hope to save the plane or its passengers.

 

Cableguy

Expert
Licensed User
We should pitch a new tv series like: B4X Crash Investigations. I am experiencing a crapload of déjà vu.

Every case has at least one instance of the narrator saying: ... and what could have caused that problem, nobody could imagine ...
.... and yet, unaware of his mistake, he heads on coding...
 

emexes

Well-Known Member
Licensed User
.... and yet, unaware of his mistake, he heads on coding...
It's like you're watching the same program - are you in Australia too? The last investigation was:

Narrator: the path to the airport involves navigating the highest mountains in the world...

What could possibly go wrong?!?! I don't want to spoil the ending for you but, to put a positive spin on it, it was probably the quickest disembarkment ever.

Have now moved on to Border Security, which is also surprisingly akin to debugging software. One moment there was a watchdog, now there is a woman from Africa saying I don't know what happened, I don't how that got in there, look, I'm really, really emotional, I even feel like crying. Been there, felt that :)
 

emexes

Well-Known Member
Licensed User
I'm happy that someone who doesn't make any mistake judge that
I don't know what happened, I don't how that got in there, look, I'm really, really emotional, I even feel like crying. Been there, felt that :)
I make mistakes all the time, and off-by-one's overshooting-the-runway are my speciality, probably because half my BASIC time is with a dialect where the array lower bound is not fixed at 0.

by the way how it make the thing go on?
I was thinking that the problem might be packets being split by AsynchStreams during B4A-to-B4R transfer, but Erel had already noted that more concisely:
Why aren't you using prefix mode?
and the only practical observation I could add was that the invalid message check by length did not catch the case of .Length = 1. This probably jumped out at me because it's something that bites me every day too, and I know that when under pressure to get a program working, sometimes simple things get missed, especially the edge/boundary cases like that.

So, we are all with you on this :)
 

lucdrb

Member
Licensed User
Changed
Initialize to InitializePrefix
Remove the If data.Length = 0 Then Return (for the serenity of emexes ;)).

But the problem is the same, I got the message: Out of bounds error. Array length = 10, Index = 65535 when I slide the seekbar fast.

For the codes it's still a work in progress.
Here are the code:
B4A
B4X:
#Region  Project Attributes
    #ApplicationLabel: HC05LightDimmer
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals

End Sub

Sub Globals

    Private lblStatus As Label
    Private btnConnect As Button
    Private lblMessage As Label
    Private ProgressBar1 As ProgressBar
    Private skbDimmer As SeekBar
    Private lblDimmer As Label

    Private Status As String
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("Main")
End Sub

Sub Activity_Resume
    SetState
End Sub

Sub Activity_Pause (UserClosed As Boolean)
    If UserClosed = True And lblStatus.Text = "connected" Then
        CallSub2(Starter, "SendMessage", Array As Object(1, 90))
    End If
End Sub

Public Sub SetState
    btnConnect.Enabled = Not(Starter.connected)
    ProgressBar1.Visible = Starter.connecting
    Dim Status As String
    If Starter.Connected Then
        Status = "connected"
    Else If Starter.TryToConnect Then
        Status = "trying to connect..."
    Else If Starter.Connecting Then
        Status = "HC-06 found connecting..."
    Else
        Status = "disconnected"
    End If
    lblStatus.Text = $"Status: ${Status}"$
End Sub

Public Sub MessageFromDevice(msg As String)
    lblMessage.Text = msg
End Sub

Sub btnConnect_Click
    CallSub(Starter, "Connect")
End Sub

Sub skbDimmer_ValueChanged (Value As Int, UserChanged As Boolean)
    Dim s As SeekBar = Sender
    lblDimmer.Text = Value
    Dim data() As Object = Array As Object(s.Tag,Value)
    CallSub2(Starter, "SendMessage", data)
End Sub

'STARTER 
Public Sub SendMessage(data() As Object)
    ast.astreams.Write(ser.ConvertArrayToBytes(    data))
End Sub

'AST
Public Sub Initialize (TargetModule As Object, EventName As String, In As InputStream, out As OutputStream)
    mTarget = TargetModule
    mEventName = EventName
    astreams.Initializeprefix(In,True, out, "astreams")
    sb.Initialize
End Sub
B4R
B4X:
#Region Project Attributes
    #AutoFlushLogs: True
    #CheckArrayBounds: True
    #StackBufferSize: 600
#End Region

Sub Process_Globals
    Public Serial1 As Serial
    Private SoftwareSerial1 As SoftwareSerial
    Private astream As AsyncStreams
    Private Timer1 As Timer
    Private ser As B4RSerializator
    Private servoGouv As Servo
    Private PinServoGouv As Pin
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    PinServoGouv.Initialize(10, PinServoGouv.MODE_OUTPUT)
    SoftwareSerial1.Initialize(9600, 11, 12)
    astream.InitializePrefix(SoftwareSerial1.Stream,True, "astream_NewData", Null)
    astream.MaxBufferSize = 500
    astream.WaitForMoreDataDelay = 300
    Timer1.Initialize("timer1_Tick", 1000)
    Timer1.Enabled = True
    servoGouv.Attach(PinServoGouv.PinNumber)
End Sub

Sub Timer1_Tick
astream.Write("Millis here: ".GetBytes)
    astream.Write(NumberFormat(Millis, 0, 0).GetBytes)
    astream.Write(Array As Byte(10)) 'end of line character. AsyncStreamsText will cut the message here
End Sub

Sub AStream_NewData (Buffer() As Byte)
    Dim sto(10) As Object
    Dim data() As Object = ser.ConvertBytesToArray(Buffer,sto)
    Log(data(1))
    servoGouv.Write(data(1))
End Sub
Thanks in advance
 

emexes

Well-Known Member
Licensed User
Changed Initialize to InitializePrefix
Ripper. Are both sides of the link now using Prefix mode?
Remove the If data.Length = 0 Then Return (for the serenity of emexes ;)).
Thanks ;-)

Although I would have left it in, but changed to match the element that you are accessing, eg:
B4X:
If data.Length < 2 Then Return    'invalid message eg, data(1) does not exist
servo1.Write(data(1))
but that is just masking the problem - it'd be nice to find out why you are receiving single-byte packets.
I got the message: Out of bounds error. Array length = 10, Index = 65535 when I slide the seekbar fast.
I don't have hardware here to duplicate and test your configuration, but... a clue might be that 65535 is unsigned version of -1.
 
Last edited:

lucdrb

Member
Licensed User
I think that to much infos are the problem.
I'll try to send less data.

Thanks
Luc
 

miker2069

Active Member
Licensed User
Your problem sounds awfully familar to an issue I had a couple years back - sending data between a B4J and B4R. I was using prefixed mode as well and hit pretty much same time issue. May be this thread will give you some clues in how to work around it - see this
 

miker2069

Active Member
Licensed User
Mulling it over a bit, and being that you used the term "sending lots of data" (to B4R) you're probably hitting a network buffer issue. See the post I linked it as I hit something similar. Briefly my work around was that in my object array, the *first* element in the array was the number of total elements that ultimately needed to be received. I then only sent max 100 elements at a time (so I never hit a network buffer limit and exploded it). So if I had say a 50 element object array to send/receive - the 0 element was set to 50, and elements 1-51 had the data. On receiving end since I knew I expected 50 and received 50 in the first pass, I knew I was done. If the sending app (your B4A) had to send 150 elements - the first pass the 0 element is set to 150, object array elements 1-99 had the first 99 elements, and then I knew to *receive* the remaining 50 elements in the next send. I just kept track and assembled it on the receiving side.

It's worth noting, the object array size could be quite large, and the issue was solely in the network layer (the buffer). I tested this by converted a huge object array from whatever my source data was, and then converted it back from the object array all on the esp8266 (my device at the time). I only had issues when attempting to send over the network. Presumably just like the B4J your B4A has no clue it's talking to an B4R app and it's assuming whatever it's sending can be handled by it. Chunking up your data should resolve your issue.
 
Top