Phase 2:
In the last post we’ve setup Visual Studio for developing against the .NET nanoFramework. In this post we’ll be writing our first lines of code. Goal is to test if we can communicate over Bluetooth with our ESP32 board. Now, the code we’ll be writing is not your typical ‘hello world’ program, but more of a foundation for the later work we’ll be doing for making a Heliostat. What we want is that our ESP32 can respond to various commands we send over Bluetooth. The role of the ‘foundation’ code is to give some structure on how to add these commands.
Sources
There are 2 GitHub repositories for the Heliostat project.
- In GitHub - HelioStat AllPhases you’ll find the exact same code we’re showing in these blog posts. For each post, a new folder is added to the repository. Hence, the code for this blog post is in GitHub - HelioStat AllPhases/heliostat - Phase2. Note that this repository has a main branch, and a WROOM branch. This is because when i started this project, i was using the WROOM based ESP32 board, and later switched to the WROVER board. Phase2 will be the last phase that uses the WROOM board. From the next blog post on we’ll be using the WROVER board.
- The other repository, GitHub - HelioStat is the one that i use for real. If you want to report issues with the code or contribute, then use this repository.
Main function
Let’s look at some code.
The Main function is called by .NET nanoFramework when the board starts running. First thing we do is write some text to the debug output. This text will be visible in the output window of Visual Studio, and thus gives us a convenient way to monitor what our code is doing without actually debugging it.
Next we setup the GPIO pins we’ll be using.
Note that we defined an enum for the pins that we want to use. This allows us to change pin numbers by editing a single file instead of looking for pin numbers throughout the code.
Setting up Bluetooth
Bluetooth is setup like below. First we create an instance of the Bluetooth Serial profile. Next we attach some event handlers, one for connection events, and another one for when we receive some data. Lastly we start the Bluetooth advertising, under the ‘HelioStat’ name.
We need to hold on to the created Bluetooth instance. We do that in the main function.
context.BluetoothSpp = SetUpBlueTooth();
The context is a class we defined ourselves, and that we set as a static variable.
public static Context context = new Context();
This brings us to a shortcoming, or better said a concession of the current version of the .NET nanoFramework. There is currently no support for dependency injection. This is why we are having this Context class, a cheap alternative.
CommandHandler
With everything setup, let’s take a look at our Bluetooth received data event handler.
The only thing we do here is take the string we received over Bluetooth and pass it on to the CommandHandler class. Let’s call this string the ‘command’.
The role of the CommandHandler is to invoke the code that matches the command given. Now, we don’t want a whole bunch of ‘if then else’ code in the CommandHandler, we want something more generic. Remember the ‘foundation’ stuff? To come to that, we define an interface ITask.
ITask Interface
This interface contains some properties that each instance of the interface needs to implement. These properties are:
- a command string: This is the name of command, we use it to compare against the incoming command over Bluetooth.
- a description: This tells in short what the command will be doing
- a help text: This gives detailed information about the command and its parameters if there are any.
The interface also contains an Execute method. This method will be called by the CommandHandler when it has determined which task to run.
This is the full definition of the interface:
TestOnboardLed Task
And this is the task that blinks the onboard led. Notice that it inherits from ITask.
From the code above we learn that the command to blink the led is ’testled’. Now, let’s go back to our CommandHandler to see how it knows which task to execute.
Finding the Task to execute
First, we create a list with all possible tasks. Note that the code for this phase of the project also contains some other tasks.
Next, we cycle through this list and compare the incoming command with the commands defined by the tasks. If a match is found, the Execute method of the task is called.
And that’s it. We can now add as many Tasks as we want and have the code in a separate class. Just don’t forget to add your new Tasks to that array in the CommandHandler.
By this time you might wonder why the ITask interface has that Description and Help property. Well, if we start having many tasks with maybe many parameters, it would be nice if we could just ask our ESP32 board for all the commands it knows, and what parameters they accept. The CommandHandler has 1 hardcoded command, which is ‘help’. Issue that command, and you get send back a list of all other commands, and their description. Also, if you want to know if a certain command has parameters, you can send ‘help’ and the name of the command, like ‘help testled’.
Demo
If you want to see all this in action, check the below video.
What’s next
In the next phase we’ll repeat what we just did here, but we’ll introduce a software concept called ‘Dependency injection’ to organize our program in a better way. To be able to do that, we need an ESP32 board with more Ram. Stay tuned.