Heliostat Phase 2bis: WROVER ESP32 & Bluetooth

Controlling the WROVER ESP32 board using a smartphone over Bluetooth

Friday, October 28, 2022

Why a Phase 2bis?

Like explained in the previous post, we switched hardware from a WROOM to a WROVER ESP32 board. Sole reason is that the WROVER board has more Ram. This allows us to use some nanoFramework libraries like Dependency Injection and Json serialization which require more Ram to run. So, in this post we do the same things like in Phase 2, but we make use of the aforementioned Dependency Injection.

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. Phase 2bis uses the WROVER board, and the sources are in the main branch.
  • 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. In contradiction to the WROOM board, the WROVER board doesn’t have an onboard LED. So, if you want to test this code sample, you’ll need to hook up a LED yourself.

Configuring Dependency Injection

To learn more what Dependency Injection is, read about it in the .NET nanoFramework Dependency Injection Library pages, or for even more info in the Dependency injection in .NET pages.

In our case, we register 4 services. AppMessageWriter and CommandHandlerService implement interfaces IAppMessageWriter and ICommandHandlerService. The lifetime of these services is ‘Transient’, meaning that each time we ask for an instance of them, a new one is created. This makes sense, as these interfaces are not expected to keep data alive between invocations. The object that handles Bluetooth connections however we must keep in memory and reuse, hence the lifetime of NordicSpp is ‘Singleton’. And lastly, the code that responds to events from the Bluetooth object is put in a background service, so it can handle those events in a separate thread.

Setting up Bluetooth

Bluetooth is setup in the ‘ExecuteAsync’ override of our BlueToothReceiver BackgroundService. All we have to do is attach some event handlers, one for connection events, and another one for when we receive some data. And of course we need to start the Bluetooth advertising, under the ‘HelioStat’ name.

To keep on listening for Bluetooth events, we let the code enter a loop, that is only exited when the BackgroundService is terminated. I do admit i have to research this sleep time a bit, because eventually i want the ESP32 to go to sleep for about a minute between correcting for the suns position, and still be able to listen for Bluetooth commands. But for now, we let it sleep for only 1 millisecond.

CommandHandler

Let’s take a look how we handle incoming strings from Bluetooth.

The above code is the event handler that we assigned to the ‘ReceivedData’ event from the Bluetooth object. The only thing we do is ask our dependency injection container for an instance of the ICommandHandlerService, and pass the received string to it. 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 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’.

What’s next

In the next phase we’ll introduce the code that calculates the position of the sun, stay tuned.