Mon Tutoriel de la voiture connectée Azure Sphere – Partie 2

Dans mon précédent article, je démarrais la présentation du matériel nécessaire et de l’installation que j’ai faite pour réaliser ma petite voiture connectée et téléguidée : https://kbeaugrandblog.wordpress.com/2021/05/07/tutoriel-de-la-voiture-connectee-azure-sphere-partie-1

Je vous propose maintenant de voir ensemble quels sont les éléments de code me permettant d’orchestrer le fonctionnement de mon appareil.

Moteurs sur Bus I2C

Seeed Studios Grove – I2C Motor Driver (TB6612FNG)

Comme je vous l’ai montré dans mon premier article, j’ai fait le choix d’utiliser un contrôleur de moteur par bus I2C. Pour ceux qui ne connaissent pas, ce bus électronique permet d’effectuer une communication en série avec de multiples périphériques connectés en série au travers d’un protocole d’adressage.

Vous pourrez trouver plus d’informations sur ce type de bus électronique : Qu’est-ce que le bus I2C et comment fonctionne-t-il sur Arduino ? – Arduino France (arduino-france.com)

Sur Azure Sphere, nous pouvons facilement ouvrir une communication I2C sur le port Grove (port auquel notre contrôleur est connecté.

Pour réaliser l’interface de communication avec le moteur, je me suis largement inspiré de la documentation et du code disponible en OpenSource sur le site du fabricant : Grove – I2C Motor Driver (TB6612FNG) – Seeed Wiki (seeedstudio.com). J’ai ensuite adapté le code pour qu’il fonctionne sur Azure Sphere.

Déclaration dans le HardwareDefinition et app_manifest.json

Avant de pouvoir utiliser n’importe quelle interface de votre carte Azure Sphere, vous devez en déclarer l’utilisation.

Pour cela j’ai modifié le fichier de définition hardware pour ajouter mon interface comme suit :

...
    "Peripherals": [
        {
            "Name": "I2C_MOTOR_DRIVER",
            "Type": "I2cMaster",
            "Mapping": "AVNET_MT3620_SK_ISU2_I2C",
            "Comment": "MT3620 SK: I2C on Grove Connector."
        },
....

Puis nous devons maintenant déclarer dans le app_manifest.json l’utilisation de ce périphérique sans quoi, nous aurons des exceptions de droits d’ouverture :

...
  "Capabilities": {
    ...
    "I2cMaster": [ "$I2C_MOTOR_DRIVER" ],
    ...
  }
...

Ouverture du périphérique et communication

Enfin l’utilisation de ce contrôleur au travers du bus I2C est maintenant assez simple :

Initialisation du périphérique

#include <errno.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>

#include <applibs/i2c.h>
#include "hw/joyitcar_appliance.h"

static int i2cFd = -1;

I2CMotorDriverExitCode JoyitCar_InitMotors(void)
{
    i2cFd = I2CMaster_Open(I2C_MOTOR_DRIVER);
    if (i2cFd == -1) {
        Log_Debug("ERROR: I2CMaster_Open: errno=%d (%s)\n", errno, strerror(errno));
        return I2CMotorDriver_ExitCode_Init_OpenMaster;
    }


    int result = I2CMaster_SetBusSpeed(i2cFd, I2C_BUS_SPEED_STANDARD);
    if (result != 0) {
        Log_Debug("ERROR: I2CMaster_SetBusSpeed: errno=%d (%s)\n", errno, strerror(errno));
        return I2CMotorDriver_ExitCode_Init_SetBusSpeed;
    }

    result = I2CMaster_SetTimeout(i2cFd, 100);
    if (result != 0) {
        Log_Debug("ERROR: I2CMaster_SetTimeout: errno=%d (%s)\n", errno, strerror(errno));
        return I2CMotorDriver_ExitCode_Init_SetTimeout;
    }

    return I2CMotorDriver_ExitCode_Success;
}

Démarrage et arrêt des moteurs

Avant de commencer, il m’a fallut retrouver certaines informations me permettant de commander les moteurs. En effet, le contrôleur permet de commander les moteurs indépendamment au travers de chanel :

typedef enum MotorChannel {
    MOTOR_CHA = 0,
    MOTOR_CHB = 1,
} MotorChannel;

Enfin, il nous sera également nécessaire de récupérer l’adresse du périphérique I2C (par défaut : 0x14) ainsi que les commandes prises en charge :

#define GROVE_MOTOR_DRIVER_I2C_CMD_BRAKE            0x00
#define GROVE_MOTOR_DRIVER_I2C_CMD_STOP             0x01
#define GROVE_MOTOR_DRIVER_I2C_CMD_CW               0x02
#define GROVE_MOTOR_DRIVER_I2C_CMD_CCW              0x03
#define GROVE_MOTOR_DRIVER_I2C_CMD_STANDBY          0x04
#define GROVE_MOTOR_DRIVER_I2C_CMD_NOT_STANDBY      0x05

Finalement, envoyer une commande au contrôleur correspond à l’envoi de 3 bytes contenant la commande, le chanel concerné ainsi qu’une valeur (ex : vitesse) :

static uint8_t _buffer[3];

static void JoyitCar_StartMotor(MotorChannel channel, int speed)
{
    if(speed > 0)
    {
        _buffer[0] = GROVE_MOTOR_DRIVER_I2C_CMD_CW;
        _buffer[2] = (uint8_t )speed;
    }
    else
    {
        _buffer[0] = GROVE_MOTOR_DRIVER_I2C_CMD_CCW;
        _buffer[2] = (uint8_t) -speed;
    }

    _buffer[1] = channel;

    Log_Debug("INFO: Starting (%d) motor %d with speed %d\n", _buffer[0], channel, _buffer[2]);

    ssize_t written = I2CMaster_Write(i2cFd, GROVE_MOTOR_DRIVER_DEFAULT_I2C_ADDR, _buffer, sizeof(_buffer));

    if (written < 0)
    {
        Log_Debug("ERROR: Failed to write to I2C (0x%d). Writing %d bytes ...\n", GROVE_MOTOR_DRIVER_DEFAULT_I2C_ADDR, sizeof(_buffer));
    }
}

static void JoyitCar_StopMotor(MotorChannel channel)
{
    _buffer[0] = GROVE_MOTOR_DRIVER_I2C_CMD_STOP;
    _buffer[1] = channel;

    Log_Debug("INFO: Stoping motor %d\n", channel);

    ssize_t written = I2CMaster_Write(i2cFd, GROVE_MOTOR_DRIVER_DEFAULT_I2C_ADDR, _buffer, sizeof(_buffer) - 1);

    if (written < 0)
    {
        Log_Debug("ERROR: Failed to write to I2C (0x%d). Writing %d bytes ...\n", GROVE_MOTOR_DRIVER_DEFAULT_I2C_ADDR, sizeof(_buffer) - 1);
    }
}

Il devient maintenant facile de contrôleur les moteurs pour avancer/reculer la voiture…

void JoyitCar_GoForward(void)
{
    JoyitCar_StartMotor(MOTOR_CHA, DEFAULT_MOTOR_SPEED);
    JoyitCar_StartMotor(MOTOR_CHB, DEFAULT_MOTOR_SPEED);
}

void JoyitCar_GoBackward(void)
{
    JoyitCar_StartMotor(MOTOR_CHA, -DEFAULT_MOTOR_SPEED);
    JoyitCar_StartMotor(MOTOR_CHB, -DEFAULT_MOTOR_SPEED);
}

void JoyitCar_TurnLeft(void)
{
    JoyitCar_StartMotor(MOTOR_CHA, -DEFAULT_MOTOR_SPEED);
    JoyitCar_StartMotor(MOTOR_CHB, DEFAULT_MOTOR_SPEED);
}

void JoyitCar_TurnRight(void)
{
    JoyitCar_StartMotor(MOTOR_CHA, DEFAULT_MOTOR_SPEED);
    JoyitCar_StartMotor(MOTOR_CHB, -DEFAULT_MOTOR_SPEED);
}

void JoyitCar_Break(void)
{
    JoyitCar_StopMotor(MOTOR_CHA);
    JoyitCar_StopMotor(MOTOR_CHB);
}

Conclusion

Nous avons maintenant réussi à mettre en place les méthodes nous permettant de contrôler les roues de la voiture et nous pouvons réaliser tous les mouvements.

Cela constituait alors ma première étape que j’ai pu vérifier et utiliser au travers des de boutons de la carte Azure Sphere.

Passons maintenant à la connexion de ma voiture à Google Home afin de la guider par la voix :

https://kbeaugrandblog.wordpress.com/2021/05/08/tutoriel-de-la-voiture-connectee-azure-sphere-partie-3/

3 commentaires sur “Mon Tutoriel de la voiture connectée Azure Sphere – Partie 2

Laisser un commentaire