Understanding Golang “Concurrency” using Arduino UNO.
What is concurrency and why do we need them in program?
Usually programs are made of chunks of codes which can be executed parallelly without affecting the real functionality. Executing more than such a chunk of code simultaneously is the main idea of concurrency. For example A web server responding to multiple request parallelly and not waiting for a request to be completely responded can be an example of concurrency.
Go has rich support for concurrency using goroutines and channels and we are going to understand them writing a go program to blink 2 LEDs in an Arduino Uno.
We will use a go compiler “TinyGo” specially designed to be used for small devices like microcontrollers and CLI tools. We will use it to build and flash our Arduino UNO board.
Steps to follow: (Windows)
1. Download and install go, tinyGo and avr-gcc/avrdude.
2. Setup Arduino UNO board and make sure the drivers are installed and the board is identified.
3. Identify the COM port the Arduino UNO board is using to communicate with the computer. The port can be found in Device Manager under the Ports section. The port should look like COMx.
We would need two LEDs, 2 resistors ranging anywhere between 2 kilo ohm to 5 kilo ohm depending on the LEDs you choose. Connect them as per the diagram below. Use a breadboard if possible. We are going to replace the LED connected to digital pin 13 with the Arduino’s own LED just for our convenience. Go ahead with 2 LEDs if you wish.
The implementation of concurrency:
Concurrency in Go can be implemented using two main concepts,
Goroutines are functions capable of running concurrently with other functions. We can create a goroutine using the keyword “go”. There is an implicit goroutine created by go when the main function starts executing.
Channels helps two goroutines to communicate between themselves and synchronize themselves.
We will look into three cases to understand concurrency in go.
In all the programs we will have 2 functions:
1. bulb1() => This function is to turn the LED at pin D13 on and off.
2. bulb2() => This function is to turn the LED at pin D2 on and off.
Case 1: Without concurrency.
Have a look at the following piece of code.
Considering our Arduino UNO board communicates with our computer using the port COM3, we need to execute the following command to build and upload our program to the Arduino board.
‘tinygo flash -target arduino -port COM3 .\blink_case1.go’
Replace the COM port with whatever port is being used for you.
You should see the LED connected to pin D13 blinking every 500 milliseconds.
Why is the other LED not blinking even though we have called the function bulb2? Well if you read through the function bulb1() which is being called at line 9, The led is being turned on and off continuously inside an infinite loop hence the control never comes out of the function to where we are invoking the function bulb2() at line 10. The flow never reaches line 10. We are going to fix this issue in the next case with the help of goroutines.
Case 2: Implementing concurrency using goroutine.
In this scenario we are going to deal with the issue we had in case one where the flow of control was stuck at where we called the function bulb1().
What if we could run bulb1() function concurrently to the main execution which runs on the implicit goroutine? Would that help us fix the issue we had? Well we will see. We will invoke the function bulb1() as a goroutine using the “go” keyword.
Upload the code using the following command.
‘tinygo flash -scheduler tasks -target arduino -port COM3 .\blink_case2.go’
Scheduler is required for goroutines while building using tinygo or any LLVM based compiler. In line 9, function bulb1() is invoked with the go keyword. This would create a new goroutine and the function bulb1() is going to run on the new goroutine. This time the control flow reaches line 10 and the function bulb2() executes concurrent to function bulb1(). This time we should be able to see both the LEDs blinking.
This setup works perfectly fine but what if we want LED connected to Pin D2 toggle only after the LED connected to Pin D13 toggles? Well then we need the goroutine of function bulb2 to communicate with the the goroutine of function bulb1. How do we do that? This is where go channels comes to our rescue. Lets look into it in case 3.
Case 3: Using channels to synchronize two goroutines.
This time we are going to execute both the functions in two different goroutines apart from main subroutines. We will use a go channel to communicate between the two goroutines. Toggling of the LED connected to D2 will depend on the toggling of the one connected to D13. Lets have a look at the code.
Upload the program to the Arduino UNO board using the command we used in case 2. In this case both the functions runs in separate goroutines. “Channel <-” operator is used to write to a channel and “<- Channel” to read from a channel. In the code above, we write a string to the channel in function bulb1() each time we turn the LED on or off. We read from the channel in the function bulb2(). Note that the size of the channel is “1” hence the write to channel operator in function bulb1() needs to wait till the channel is read in function bulb2() and is emptied each time before the LED connected to D2 is toggled. This adds synchronization between both the goroutines. If we read through the function main(), we see that there is an infinite loop at the end of the function. This is to keep the main goroutine running. If the main goroutine ends, all the goroutines dies off with it.
Hope these example cases gave us some idea about how concurrency is achieved in go using goroutines and channels.