Catégories
Recherche technologique

openThread et GreenPower – DotDot

Et pourquoi pas CHIP ? (https://www.connectedhomeip.com/) Et bien tout simplement car aucune spécification n’est encore publique actuellement !

Les documents de référence

L’objectif

L’objectif n’est pas d’implémenter l’ensemble du protocole dotdot mais le strict minimum pour passer à l’étape suivante : le pilotage depuis une commande green power. Pour se faire seul la commande toggle du cluster on/off sera implémentée sur le premier endpoint de l’actionneur. Seront donc ignoré :

  • La découverte des produits et des ressources depuis le /.well-known et de fait la gestion de l’UID produit.
  • Tous les mécanismes de sécurité.
  • La gestion du multicast, seul un post unicast sera géré par le serveur.
  • Les bindings et autres tokens

On peut donc résumé que le client enverra un post coap depuis la cli, le serveur quand à implémentera ce post sur la ressource « /zcl/e/1/s6/c/2 » qui correspond a la commande toggle du cluster on/off sur l’enpoint 1 du produit. Commande qui n’a pas besoin de payload.

Le code !

Jusqu’à présent nous avons compilé le projet exemple qui donne accès à une ligne de commande. Pour réaliser notre objectif on définira le leader comme l’actionneur, on ne touchera pas au code du routeur. Pour simplifier l’on va modifier le projet example cli pour :

  • créé automatiquement le réseau en tant que leader
  • activé coap et lui ajouter la ressource zcl/e/1/s6/c/2
  • ajouter un handler sur cette ressource
  • imprimer sur la sortie RTT les informations permettant de s’assurer que toute les étapes ce sont bien passée
  • a chaque fois que le handler de la ressource coap est appelée changé d’état une variable représentant la charge et l’afficher.
void CoAPHandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
 {
     static bool light_on_state = false;
     OT_UNUSED_VARIABLE(aContext);
     OT_UNUSED_VARIABLE(aMessage);
     OT_UNUSED_VARIABLE(aMessageInfo);

     otPlatLog(OT_LOG_LEVEL_NONE, 
               OT_LOG_REGION_PLATFORM, 
               "Light %s",light_on_state?"ON":"OFF");
     light_on_state = !light_on_state;
}
int main(int argc, char *argv[])
 {
     otInstance *instance;
...

// configure node as leader
otError lError;

otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "Create new network : dataset ");
otOperationalDataset aDataset;
uint8_t demoMasterKey[OT_MASTER_KEY_SIZE] = {0x9d,0x7d,0xdc,0xb4,0x40,0x8b,0xe4,0x70,0xe2,0x24,0x02,0x56,0xfe,0xaf,0x9c,0xf2};

if( OT_ERROR_NONE == otDatasetCreateNewNetwork(instance, &aDataset) )
{
    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS, set active ");
    aDataset.mChannel = 15;
    memcpy(aDataset.mMasterKey.m8, demoMasterKey, OT_MASTER_KEY_SIZE);
    if( OT_ERROR_NONE == otDatasetSetActive(instance, &aDataset) )
    {
        otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS, enable IPv6 ");
        if( OT_ERROR_NONE == otIp6SetEnabled(instance, true) )
        {
            otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS, start as leader ");
            lError = otThreadSetEnabled(instance,true);
            if( OT_ERROR_NONE == lError )
            {
                otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS\n");
                const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(instance);

                for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
                {
                    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM,
                        "%x:%x:%x:%x:%x:%x:%x:%x", tSwap16(addr->mAddress.mFields.m16[0]), tSwap16(addr->mAddress.mFields.m16[1]),
                        tSwap16(addr->mAddress.mFields.m16[2]), tSwap16(addr->mAddress.mFields.m16[3]), tSwap16(addr->mAddress.mFields.m16[4]),
                        tSwap16(addr->mAddress.mFields.m16[5]), tSwap16(addr->mAddress.mFields.m16[6]), tSwap16(addr->mAddress.mFields.m16[7]));
                }                    
            }
            else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED %s\n", otThreadErrorToString(lError)); }    
        }
        else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED\n"); }    
    }
    else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED\n"); }    
}
else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED\n"); }    


// Initialize coap service
otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "CoAP start : ");
if( OT_ERROR_NONE == otCoapStart(instance, OT_DEFAULT_COAP_PORT) )
{
    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS\n");

    // add ressources
    otCoapResource mResource;
    char zclToggleRscr[] = "zcl/e/1/s6/c/2";
    memset(&mResource, 0, sizeof(mResource));
    mResource.mUriPath = zclToggleRscr;
    mResource.mHandler = CoAPHandleRequest;

    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "CoAP add ressource : ");
    lError = otCoapAddResource(instance, &mResource);
    if( OT_ERROR_NONE == lError )
    {
        otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS\n");
    }
    else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED %s\n", otThreadErrorToString(lError)); }    
}
else
{
    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED\n");
}


while (!otSysPseudoResetWasRequested())
{
    ...

return 0;
}

Et le résultat :