Engee documentation
Notebook

Joystick with position sensor MPU6050 for STM32F4

This demo looks at the Engee model for reading axial rotation angles with a Kalman filter, converting the resulting angles, and transmitting the constrained rotation angles to a serial port.

Introduction

The project, based on the Engee model stm32_mpu6050.engee, performs the following tasks:

  • receiving acceleration and rotation speed in three rotation axes from MPU6050 sensor via I2C channel of STM32F446RE controller;
  • conversion of these values into axial rotations - roll, pitch, yaw with filtering by Kalman filter;
  • receiving discrete signals "Fixation", "Scale changeover";
  • conversion of axial rotations into coordinate deviations of the joystick normal vector in the Cartesian coordinate system;
  • conversion of coordinate deviations into angles $\vartheta, \varphi$ of joystick rotation in spherical coordinates;
  • restricting the obtained angles $\left[ 0; \ \frac{\pi}{2} \right], \left[ 0; \ 2\pi \right]$ accordingly;
  • output of variables: angles $\vartheta, \varphi$, "Fixation", "Scale switching" to the universal transceiver (USART).

Elements and environment:

  • Debug board: *NUCLEO-F446RE.
  • Microcontroller: *STM32F446RE.
  • Input channels: I2C, 2$\times$ DI
  • Output channels: USART (COM)
  • Tri-axis accelerometer-gyroscope sensor: MPU6050 (GY-521)
  • Development Environment: *Engee -> VS Code 1.92.1 + PlatformIO 6.1.15.
  • Framework PlatformIO: *stm32duino.

The used PlatformIO framework - stm32duino is used due to the availability of ready debugged plug-in files for working with MPU6050 sensor and Kalman filter for Arduino microcontrollers.

Project files

Project directory in the file browser:

joystick_path.png

  • for_PlatformIO - folder with files for PlatformIO project:
    • include - folder with plug-in files:
      • I2Cdev.h - I2C interface header file;
    • Kalman.h - Kalman filter header file (also plugged in the model!);
    • MPU6050.h - MPU6050 sensor header file (also plugged in the model!);
    • stm32_mpu6050.h - generated Engee model header file;
  • source - folder with C/C++ source files:
      • I2Cdev.cpp - I2C interface source file;
    • main.cpp - main user programme file;
    • MPU6050.cpp - sensor source file MPU6050;
      • stm32_mpu6050.cpp - renamed generated Engee model source file;
  • joystick_description.ngscript - current Engee script;
  • stm32_mpu6050.engee - Engee model of this project.

Let's add paths and names of some files and folders for command control of modelling, code generation and work with files.

In [ ]:
Pkg.add(["FilePathsBase"])
In [ ]:
имя_модели = "stm32_mpu6050";
папка_проекта = "$(@__DIR__)/";
путь_модели = папка_проекта * имя_модели * ".engee";
путь_генератора_кода = папка_проекта * "model_code/";
путь_platformIO_CPP = папка_проекта * "for_PlatformIO/src/";
путь_platformIO_H = папка_проекта * "for_PlatformIO/include/";

Model description

The model stm32_mpu6050.engee of this project can be categorised into the following functional groups of blocks:

  • blocks of conversion of axial rotation angles,
  • blocks of interaction with the microcontroller periphery.

joystick_model.png

The calculations realised by the subsystem EulersToDecart perform transformations of the values of rotation angles along the axes obtained by the Kalman filter into deviations in Cartesian coordinates of the normal vector of the joystick originating from the coordinate origin:

$$\Delta X = \sin(KalmanYAngle)\cdot\cos(KalmanZAngle)$$ $$\Delta Y = \sin(KalmanYAngle)\cdot\sin(KalmanZAngle)$$ $$\Delta Z = \cos(KalmanYAngle)$$

The subsystem DecartToSpheric essentially performs inverse transformations:

$$\vartheta = \tan^{-1}\frac{\sqrt{\Delta X^2 + \Delta Y^2 }}{\Delta Z}$$ $$\varphi = \tan^{-1}\frac{\Delta Y}{\Delta X}$$

After converting the received signals into joystick rotation angles, they are limited according to the specified ranges.

Peripheral units

The project model contains 4 blocks of microcontroller periphery, realised by means of blocks C Function:

  • I2C1_MPU6050_Kalman - for initialising the I2C_1 interface, receiving data from MPU6050 and filtering angles in the rotation axes;
  • GPIO10_INPUT - for initialisation of PB6 contact as a digital input, receiving its state;
  • GPIO11_INPUT - for initialisation of PA7 contact as a binary input, obtaining its state;
  • USART2_ToSerial - for initialisation of USART_2 interface and transmission via serial port at 9600 baud rate, outputting an array of values to the serial port.

To connect in the files of code generation results ready code for I2C, MPU6050 and Kalman filter, these files and the path to them are specified in the block C_Function "I2C1_MPU6050_Kalman":

joystick_include.png

Also the block C Function "I2C1_MPU6050_Kalman" generates the change of sensor rotation angles around Y and Z axes during the simulation.

Detailed description of the code operation from the blocks C Function is given in its comments.

Results of the model work

To simulate the conversion of rotation angle values, let's load and run the model stm32_mpu6050.engee:

In [ ]:
if имя_модели in [m.name for m in engee.get_all_models()]
    m = engee.open(имя_модели);
else
    m = engee.load(путь_модели);
end

данные = engee.run(m);

From the obtained modelling data we extract variables for signal construction:

  • KalmanY - modelled rotation angle around the Y axis,
  • KalmanZ - modelled angle of rotation around the Z axis,
  • SatTheta - calculated angle $\vartheta$,
  • SatPhi - calculated angle $\varphi$.
In [ ]:
# из данных модели извлекаем переменные для построения
KalmanY = Base.stack(данные["KalmanXYZ"].value, dims = 1)[:, 2];
KalmanZ = Base.stack(данные["KalmanXYZ"].value, dims = 1)[:, 3];
SatTheta = данные["SatTheta"].value;
SatPhi = данные["SatPhi"].value;
In [ ]:
using Plots;
gr();
plot(
     plot(данные["KalmanXYZ"].time, [KalmanY, KalmanZ];
          label=["Вращение по Y, рад" "Вращение по Z, рад"], lw=1, legend=:bottomright),
     plot(данные["SatTheta"].time, [SatTheta, SatPhi];
          label=["Угол ϑ, рад" "Угол φ, рад"], lw=1, legend=:bottomright);
     layout=(1,2), size=(900,300)
)
Out[0]:

As can be seen from the graphs, the angles of rotation of the sensor around the Y and Z axes generated by the block C Function "I2C1_MPU6050_Kalman" are converted to the angles $\vartheta, \varphi$ without changing the values.

Code generation

Let's generate code from the model to load the control algorithm into the microcontroller.

In [ ]:
engee.generate_code(путь_модели, путь_генератора_кода); # генерация кода из модели
[ Info: Generated code and artifacts: /user/start/examples/codegen/stm32_mpu6050_joystick/model_code

Since the project in IDE VS Code + PlatformIO will be further built from C++ files, it is necessary for successful building to change the extension .c of the generated file to .cpp. First of all, let's go to the generated files folder and look through its contents:

In [ ]:
cd(путь_генератора_кода);   # переход в директорию
readdir()   # вывод содержимого директории
Out[0]:
3-element Vector{String}:
 "main.c"
 "stm32_mpu6050.c"
 "stm32_mpu6050.h"

Here: stm32_mpu6050.c is the C file generated by the code from the previous script cell. Let's change the extension of the new generated C file. To overwrite the file with the existing name, apply the attribute force = true. After that we will again display the contents of the folder with the code generation results.

In [ ]:
mv(имя_модели * ".c", имя_модели * ".cpp"; force=true); # изменение расширения сгенерированного файла
readdir()   # вывод содержимого директории
Out[0]:
3-element Vector{String}:
 "main.c"
 "stm32_mpu6050.cpp"
 "stm32_mpu6050.h"

The extension of the new generated C file has been changed. Before adding the project files to the IDE, it remains to move the generated files to the src and include folders for the PlatformIO project. To do this, it is convenient to use the library FilePathsBase.jl

In [ ]:
## При необходимости раскомментируйте ячейку для скачивания и установки библиотеки
# import Pkg;
# Pkg.add("FilePathsBase")
In [ ]:
using FilePathsBase

# переносим файлы по папкам для проекта PlatformIO
mv(AbstractPath(имя_модели * ".cpp"),
   AbstractPath(путь_platformIO_CPP * имя_модели * ".cpp");
   force = true);
mv(AbstractPath(имя_модели * ".h"),
   AbstractPath(путь_platformIO_H * имя_модели * ".h");
   force = true);

The generated main.c will not be used in the project, you can delete the file and its directory:

In [ ]:
cd("..") # переходим на уровень выше в файловой браузере
rm(путь_генератора_кода; recursive=true) # удаляем папку и main.c

Now you can proceed to hardware configuration and work with IDE.

Hardware

The hardware part of the project, consisting of the previously mentioned elements, has the following connection scheme: The MPU6050 sensor is connected to the I2C_1 interface on the NUCLEO F446RE debugging board, the push-button contacts for discrete signals are connected to the GPIO10 and GPIO11 pins.

joystick_scheme.png

Programming of the controller and reading of the USART data is done via USB.

Project assembly

Create a project in the workspace of the VSCode+PlatformIO development environment with the following configuration file settings platformio.ini:

[env:nucleo_f446re]
platform = ststm32
board = nucleo_f446re
framework = arduino     

In the include and src directories, add files from the corresponding directories from the Engee file browser of the current project.

joystick_vscode_proj.png

After that, let's run the Build project and make sure the build is successful:

image.png

Code execution on STM32F4

Connect the STM32F4 controller with the periphery of the sensor and two pushbuttons to the serial port, then start the "Upload" compiled project code into the controller and make sure that the upload is successful:

image.png

The values of the variable angles of the joystick normal tilt and the lock and zoom signals are transmitted to the serial port of the computer:

serial.gif

To display signal graphs from the serial port, this project uses the programme SerialPlot.

Conclusion

In this demo, the Engee model was developed to convert the rotation angles of the MPU6050 position sensor around the axes. The code generated from the model, once loaded into the STM32F446RE microcontroller, reproduces the embedded functions.

Additional help is available in the Engee documentation to help with this project:

Blocks used in example