B4R Tutorial Timers, Loopers and CallSubPlus

Erel

Administrator
Staff member
Licensed User
Arduino C example #1:
B4X:
void setup() {
  pinMode(13, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
}
While the above design is simple and works properly for very small examples, it doesn't work with larger programs as the single thread cannot do anything else while it sleeps.

The B4R solution is to use a Timer instead. There could be many timers running and they will all work without interfering each other.

Blink program with a timer:
B4X:
Sub Process_Globals
   Public Serial1 As Serial
   Private Timer1 As Timer
   Private pin13 As Pin
End Sub

Private Sub AppStart
   Serial1.Initialize(115200)
   Log("AppStart")
   pin13.Initialize(13, pin13.MODE_OUTPUT)
   Timer1.Initialize("Timer1_Tick", 1000)
   Timer1.Enabled = True 'don't forget to enable it
End Sub

Private Sub Timer1_Tick
   pin13.DigitalWrite(Not(pin13.DigitalRead))
End Sub
CallSubPlus is similar to a timer that fires once. CallSubPlus allows you to run a sub after a specified duration. Like a timer it doesn't block the thread.

Blink example based on CallSubPlus:

B4X:
Sub Process_Globals
  Public Serial1 As Serial
  Private pin13 As Pin
End Sub

Private Sub AppStart
  Serial1.Initialize(115200)
  Log("AppStart")
  pin13.Initialize(13, pin13.MODE_OUTPUT)
  LedOn(0)
End Sub

Private Sub LedOn (tag As Byte)
  pin13.DigitalWrite(True)
  CallSubPlus("LedOff", 1000, 0)
End Sub

Private Sub LedOff(tag As Byte)
  pin13.DigitalWrite(False)
  CallSubPlus("LedOn", 1000, 0)
End Sub
The sub signature must be the same as the subs above.

Note that CallSubPlus was recently added to other B4X tools with CallSubUtils class: https://www.b4x.com/android/forum/threads/60877/#content

Internally the single thread runs in a loop and manages a messages queue.
You can use AddLooper keyword to set a sub that will be called on each internal loop. This is similar to a timer that runs as fast as possible.
Note that you can call this method multiple times to add multiple subs that will run each loop.

If you find yourself calling AddLooper and then calling Delay in the "looper" sub then you should replace it with a timer.

AddLooper example:
B4X:
Sub Process_Globals
  Public Serial1 As Serial
  Private counter As ULong
End Sub

Private Sub AppStart
  Serial1.Initialize(115200)
  Log("AppStart")
  AddLooper("Looper1")
End Sub

Sub Looper1
   counter = counter + 1
   If counter mod 100000 = 0 Then
     Log("Took me ", Millis, " milliseconds to count to ", counter)
     Log(counter / Millis, " loops per milliseconds")
   End If
End Sub
Arduino Uno: 19.6 loops per millisecond
Arduino MKR1000: 150 loops per millisecond
Arduino 101: 150 loops per millisecond
Arduino Due: 415 loops per millisecond.
ESP8266: 500 loops per milliseconds.
ESP32: 1750 loops per millisecond
 
Last edited:

max123

Active Member
Licensed User
Hi Erel, I've tried your example to test addLooper keyword on my ESP8266 (NodeMCU 1.0 board).

If I use this setting as Board Type (NodeMCU 1.0 (ESP-12E Module) (UploadSpeed = 921600)) I get about 300 cycles loops every millisecond, I think that by default the CPU is at 80MHz, but if I put this setting ((1.0 NodeMCU (ESP-12E Module) (CPUfrequency = 160)), the serial write speed remains at 921600 but the CPU runs at 160MHz and actually get a doubling of speed coming around 550-600 loop cycles per millisecond.

All this I had already tried long time ago with Arduino IDE and doing various tests I saw that the doubling of frequency is effective and you will get twice the performance, this seems to be the same for all ESP8266 boards, I have an ESP-01 and works even with that.

Many thanks for this fantastic Tool (B4R simply the Best) ;)

Screen Shot 06-23-16 at 10.59 AM.PNG
 

Erel

Administrator
Staff member
Licensed User
I get similar results. v1.20 BETA #2 allows you to set all parameters:

 

miker2069

Active Member
Licensed User
For what it's worth I ran the looper timing test on a Arduino Nano (16 Mhz) and average about 18 loops / ms :)
 

Erel

Administrator
Staff member
Licensed User
The performance list in the first post. B4R v1.80 beta #2 will add (initial) support for ESP32.
 
Top