05. Firmware - reivaxy/kinetix GitHub Wiki

The firmware

I prefer using Platformio on Visual Studio Code, rather than the Arduino IDE, because it better handles the configuration of the board, the libraries, that makes the project much easier to share, thanks to the platformio.ini file.

Binary release

You can download a released .bin file from https://github.com/reivaxy/kinetix/releases and upload it to your device by several means:

Using Python from your computer

Start with installing Python3 if you don't already have it https://www.python.org/downloads/

Then install esptool:

pip install esptool

Depending on your OS you will then run either 'esptool.exe' (Windows) or 'python esptool.py' (others) like this, after replacing the port parameter to the one your ESP32 is connected to and with the name of the binary file that you downloaded

On Windows you may need to add the path to esptool.exe to your PATH environment variable.

Then run:

esptool --chip esp32s3 --port "COM14" --baud 460800 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq 80m --flash-size 8MB 0x10000 <Name of binary file>

If this does not work, try:

python -m esptool --chip esp32s3 --port "COM14" --baud 460800 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq 80m --flash-size 8MB 0x10000 <Name of binary file>

To find the COM port, plug the ESP32-s3 to a USB socket, then:

On Windows, open a CMD: Press Win + R to open the Run dialog. Type cmd and press Enter. In the CMD command, type

wmic path Win32_SerialPort get Name,DeviceID

On Mac, open a terminal, and type

ls /dev/tty.*

Wireless upgrade, using the Android mobile application or the PC Application

Warning

This can only work if the firmware to be replaced also supports OTA update. If it does not, you will need to first upgrade to a release that supports it, using one of the other methods (esptool on command line or from IDE)

Starting with its version 0.2-OTA the Android Application allows to upload a new version of the firmware using Wifi. This app is available in release 0.12.

Warning

This feature requires the ESP32 WiFi antenna to be plugged in.

Once your KinetiX device is already running a firmware version that handles OTA, here is how to flash a new version:

With your web browser

Check the Browser application page: https://github.com/reivaxy/kinetix/wiki/09.-Chrome-Browser-Application#update-the-firmware-wirelessly

With your phone

Check the Android application page: https://github.com/reivaxy/kinetix/wiki/06.-Android-Application#updating-the-firmware-of-the-esp32

With the PC application

Check the computer application page: https://github.com/reivaxy/kinetix/wiki/08.-Laptop-Application#updating-the-firmware-of-the-esp32

From the full development environment

See next Chapter:

Development environment

Tools

Install Visual Studio Code (VSC): https://code.visualstudio.com/download

Run it, open the Extension dialog, search Platformio and click Install

Sources

If you are not familiar with github, you can just download a released source code zip or tar.gz file from the latest release https://github.com/reivaxy/kinetix/releases and then unzip it to your hard drive.

If you are familiar with github, clone the repository to your hard drive.

Better yet, fork it so that you can make contributions!

Run Visual studio code and create a workspace using the subdirectory with the sources "firmware/esp32"

The platformio.ini file will make sure you have the appropriate board configuration and libraries.

Use the VSC environment selector to pick one environment from the ones in the platformio.ini file.

vsc_env_picker

Pick either a left hand or right hand configuration.

You'll need to uodate the COM port to upload and monitor in the chosen env:

To know which port your hand is connected to, open a terminal in VSC, and type the command

pio device list

Compare the results when your hand is disconnected to when your hand is connected.

The code

The classes included allow to very easily code new positions and movements, use settings that can be modified from one of the applications (Android or Computer), use a small oled screen if available, ...

Positions and movements

Everything is handled by 4 concepts:

A finger position is an integer between 0 (fully open) and 100 (fully closed), no matter what are the actual servo angles matching these position, which can be different from a finger to another, and from left hand and right hand.

A finger movement describes a finger position and a speed, with 1 being the fastest and fractions of 1 being slower (it's actually a step increment rather than a speed).

A hand movement is a collection of 5 finger movements, each having an optional starting delay. For instance when closing all finger, you may want the thumb to end on top, so you will make it start to close a bit later than the others, or move slower.

For instance:

HandMovement* HandMovementFactory::rock() {
   HandMovement *handMovement = new HandMovement(hand, "Rock");
   // Thumb
   handMovement->setFM(0, new FingerMovement(100, 0));
   handMovement->setFM(1, new FingerMovement(0, 0));
   handMovement->setFM(2, new FingerMovement(100, 0));
   handMovement->setFM(3, new FingerMovement(100, 0));
   handMovement->setFM(4, new FingerMovement(0, 0));
   return handMovement;
}

A sequence is a collection of hand movements, each one with a maximum duration.

It can repeat indefinitely or a specific number of times.

For instance, the "scratch" command is a sequence with two hand movements, one that half opens the hand, and one that closes the hand, and it repeats. Each hand movement describes the positions of all fingers.

void MessageProcessor::scratch() {
  log_i("Starting scratch sequence");
  HandMovementFactory *hmf = new HandMovementFactory(hand);
  seq = new Sequence(0); // 0 is repeat forever
  seq->addMovement(hmf->scratchOpen(), 600);
  seq->addMovement(hmf->scratchClose(), 600);
  seq->start();
}

Another example with "come"

void MessageProcessor::come() {
  log_i("Starting come sequence");
  HandMovementFactory *hmf = new HandMovementFactory(hand);
  seq = new Sequence(5);
  seq->addMovement(hmf->comeOpen(), 500);
  seq->addMovement(hmf->comeClose(), 500);
  seq->start();
}

Bluetooth control

The MessageProcessor handles messages sent by the App (or any other BLE client) through Bluetooth.

Messages can be settings read/write, movement requests, OTA start request, ...

When a movement requests is received, if a sequence was running it will be stopped, and all relative objects freed (minus possible bugs...) so release memory, then the movement will be executed.

Implementing new features

Features that do not involve new hardware

The ONE thing that you must be careful to not break is the OTA feature. Because once it is broken, if you published the firmware and people updated their device, they will no longer be able to update the firmware wirelessly (which is very convenient for non tech savvy people).

Features that involve new hardware (sensors, actuators, ...)

Try as much as possible to make the new hardware code optional:

  • by using compilation directives.
  • by using mock classes.

The compilation directives such as

  #ifdef WITH_OLED_DISPLAY
  display = new RealDisplay();
  #else
  display = new MockDisplay();
  #endif

allow to include or exclude some code from the compilation (not just from the execution) depending on the presence of flags on the compilation options in the platformio.ini file.

For instance

build_flags = !python gitVersion.py -std=gnu++17 -DWITH_OLED_DISPLAY

will result in having the code display = new RealDisplay(); compiled and executed, but not the code display = new MockDisplay();

As for the mock classes, they allow to not have to use the compilation directives everywhere in the code, which is hard to read and maintain.

In the example above, the MockDisplay class has the exact same methods as the RealDisplay class, but they do nothing. This way, each time we want to display something we call the method to do so without worrying about the display availability, and if there is no physical display, it does not break, it just does nothing by calling the mock display class methods.

Here is a full example of the Optional OLED screen display

Using editable "experiment settings"

Quite often, features might need values that should be adjustable, either once for all by iterative testing, or depending on each user for instance.

Some 'Experiments settings" have been implemented to allow you to have values that can be adjusted from the control apps (running on your Android phone or your computer) without requiring you to update these apps.

There are four booleans, b_1, b_2, b_3, b_4

Four strings s_1, s_2, s_3, s_4

and four integers i_1, i_2, i_3, i_4

Which are exposed on the PC application:

And the Android app:

(screen capture soon)

These settings have generic names, since I don't know how you will use them.

In the firmware you can access them by their name through the setting object instantiated at initialization:

   // Let's read offset and threshold from all-purpose "experiment" settings
   int offset = settings->getInt("i_1", 380);
   int threshold = settings->getInt("i_2", 2);

The second parameter is the default value, in case they have not been initialized from the App yet.

Ideally, once the new feature works fine, and if the code is to be merged into the main branch of this repository, they should be replaced with new specific settings, freeing the "experiments" settings for new experimentations. But of course this will require modifying the applications to expose these new settings.

⚠️ **GitHub.com Fallback** ⚠️