Hola NXP JN5169 Zigbee World: Conexión y reconexión de dispositivos correctamente

¡Buenos días! 





Hoy continuaremos estudiando ZigBee usando el ejemplo de los microcontroladores NXP JN5169. En el primer artículo hablé sobre la periferia del microcontrolador, y en el segundo , cómo conectarse a la red ZigBee y hacer operaciones básicas allí. El tema principal del artículo de hoy es la conexión correcta y confiable a la red ZigBee, así como el procesamiento de reconexiones. Además, haremos esto no solo para los dispositivos de enrutador, sino también para los dispositivos finales.





Al principio pensé que los dispositivos finales son mucho más simples que los enrutadores. Después de todo, los enrutadores a través de sí mismos bombean información de diferentes nodos y administran otros dispositivos. Resultó por el contrario, con los enrutadores mucho menos complicados, simplemente funcionan de inmediato. Pero los dispositivos finales agregan significativamente una obscenidad: luego vuelva a conectarlos, luego póngalos en la cama, y ​​tienen comunicación de red no cuando se trata de eso, sino solo cuando debería serlo. En general, en el artículo hablaremos en detalle sobre los dispositivos finales.





Además, el artículo tocará un tema adicional sobre mis intentos de refinar el código y envolverlo en clases de C ++. Pero no todo es tan simple si usas un compilador truncado de hace diez años.





¿Estás listo para continuar tu inmersión en el mundo de ZigBee?





Introducción teórica

En las redes ZigBee, se hace una distinción entre el procedimiento para unirse a un nuevo dispositivo y volver a unirse a un dispositivo que ya se ha conectado a la red anteriormente. ¿Cuál es la diferencia?





Considere la opción de conectar un nuevo dispositivo. Una pieza de hardware completamente nueva aún no sabe en qué canal funciona la red ZigBee, qué dispositivos están cerca, qué direcciones de red tienen y la clave de cifrado de transporte de la red también es desconocida para el dispositivo. La red tampoco sabe nada sobre el dispositivo, el dispositivo no tiene dirección de red y no hay entradas sobre este dispositivo en las tablas de enrutamiento. 





, ( Permit Join). - ( ).





  • Beacon request





  • (Beacon response)





  • association request





  • association response













( ), - . , .





. , , . , . . , , , .





ZigBee

. , . ZigBee API . , , , EEPROM. ( / , - ), .





, - . , . JN-AN-1220-Zigbee-3-0-Sensors ( eNodeState) . 3 :





  • NOT_JOINED - . , . Xiaomi . 





  • JOINING - . - , , .





  • JOINED - . . .





- . . 3 , ? - , . 





NOT_JOINED ( NXP). , JOINING.





JOINING - , . . , JOINED, , NOT_JOINED.





JOINED - , .





  • ( ) . , , , NOT_JOINED 





  • . , , NOT_JOINED





  • - , , . (rejoin). JOINED. 





  • , . 





    • . Xiaomi. - , , .. .





    • . rejoin





    • JOINING network discovery.





, , . , Zigbee.





? ! , , , . , Zigbee (ZPS_eAplZdoStartStack(), ZPS_eAplZdoJoinNetwork()), ZPS_EVENT_NWK_DISCOVERY_COMPLETE, ZPS_EVENT_NWK_JOINED_AS_ROUTER. , . 





, Zigbee Base Device Specification , . , , . , , ( ) .





, . : NXP . ZigBee SDK Base Device Behavior (BDB), . ( BDB_eNsStartNwkSteering()), ( BDB_EVENT_NWK_STEERING_SUCCESS). - - , , BDB .





. vAppMain()





typedef enum
{
   NOT_JOINED,
   JOINING,
   JOINED

} JoinStateEnum;

PersistedValue<JoinStateEnum, PDM_ID_NODE_STATE> connectionState;

extern "C" PUBLIC void vAppMain(void)
{
...
   // Restore network connection state
   connectionState.init(NOT_JOINED);
...
   sBDB.sAttrib.bbdbNodeIsOnANetwork = (connectionState == JOINED ? TRUE : FALSE);
   DBG_vPrintf(TRUE, "vAppMain(): Starting base device behavior... bNodeIsOnANetwork=%d\n", sBDB.sAttrib.bbdbNodeIsOnANetwork);
   BDB_vStart();
...
//    // Reset Zigbee stack to a very default state
//    ZPS_vDefaultStack();
//    ZPS_vSetKeys();
//    ZPS_eAplAibSetApsUseExtendedPanId(0);

//    // Start ZigBee stack
//    DBG_vPrintf(TRUE, "vAppMain(): Starting ZigBee stack... ");
//    status = ZPS_eAplZdoStartStack();
//    DBG_vPrintf(TRUE, "ZPS_eAplZdoStartStack() status %d\n", status);

      
      



( connectionState.init() PDM, , NOT_JOINED). , Base Device Behavior (BDB) bbdbNodeIsOnANetwork. EEPROM ( , ) .





ZPS_eAplZdoStartStack(). , .. BDB.





, . . . Join . : , Handle , . .. vJoinNetwork() , vHandleNetworkJoinAndRejoin() , . .





BDB_eNsStartNwkSteering() - network discovery - , . , PDM. . , .





PRIVATE void vJoinNetwork()
{
   DBG_vPrintf(TRUE, "== Joining the network\n");
   connectionState = JOINING;

   // Clear ZigBee stack internals
   sBDB.sAttrib.bbdbNodeIsOnANetwork = FALSE;
   sBDB.sAttrib.u8bdbCommissioningMode = BDB_COMMISSIONING_MODE_NWK_STEERING;
   ZPS_eAplAibSetApsUseExtendedPanId (0);
   ZPS_vDefaultStack();
   ZPS_vSetKeys();
   ZPS_vSaveAllZpsRecords();

   // Connect to a network
   BDB_eNsStartNwkSteering();
}

      
      



sBDB.sAttrib.bbdbNodeIsOnANetwork FALSE . , bbdbNodeIsOnANetwork . BDB_eNsStartNwkSteering() network discovery.





( NOT_JOINED JOINING). , ( JOINING JOINED). , - PDM ZPS_vSaveAllZpsRecords(), .





PRIVATE void vHandleNetworkJoinAndRejoin()
{
   DBG_vPrintf(TRUE, "== Device now is on the network\n");
   connectionState = JOINED;
   ZPS_vSaveAllZpsRecords();
   ZPS_eAplAibSetApsUseExtendedPanId(ZPS_u64NwkNibGetEpid(ZPS_pvAplZdoGetNwkHandle()));
}

      
      



ZPS_eAplAibSetApsUseExtendedPanId() , , ZigBee 3.0 Stack User Guide JN-UG-3113.





(vLeaveNetwork()) , (vHandleLeaveNetwork()). NOT_JOINED.





PRIVATE void vLeaveNetwork()
{
   DBG_vPrintf(TRUE, "== Leaving the network\n");
   sBDB.sAttrib.bbdbNodeIsOnANetwork = FALSE;
   connectionState = NOT_JOINED;

   if (ZPS_E_SUCCESS !=  ZPS_eAplZdoLeaveNetwork(0, FALSE, FALSE))
   {
       // Leave failed, probably lost parent, so just reset everything
       DBG_vPrintf(TRUE, "== Failed to properly leave the network. Force leaving the network\n");
       vHandleLeaveNetwork();
    }
}

PRIVATE void vHandleLeaveNetwork()
{
   DBG_vPrintf(TRUE, "== The device has left the network\n");

   connectionState = NOT_JOINED;

   // Clear ZigBee stack internals
   ZPS_eAplAibSetApsUseExtendedPanId (0);
   ZPS_vDefaultStack();
   ZPS_vSetKeys();
   ZPS_vSaveAllZpsRecords();
}

      
      



- () . . .





PRIVATE void vHandleRejoinFailure()
{
   DBG_vPrintf(TRUE, "== Failed to (re)join the network\n");

   vHandleLeaveNetwork();
}
      
      



. , . .





PRIVATE void APP_vTaskSwitch()
{
   ApplicationEvent value;
   if(appEventQueue.receive(&value))
   {
       DBG_vPrintf(TRUE, "Processing button message %d\n", value);

       if(value == BUTTON_SHORT_PRESS)
       {
           vToggleSwitchValue();
       }

       if(value == BUTTON_LONG_PRESS)
       {
           if(connectionState == JOINED)
               vLeaveNetwork();
           else
               vJoinNetwork();
       }
   }
}
      
      



, , . 2 . , , .





. , BDB_eNsStartNwkSteering() ZigBee. , . BDB (BDB_EVENT_REJOIN_SUCCESS) (BDB_EVENT_NWK_STEERING_SUCCESS).





PUBLIC void APP_vBdbCallback(BDB_tsBdbEvent *psBdbEvent)
{
   switch(psBdbEvent->eEventType)
   {
...
       case BDB_EVENT_REJOIN_SUCCESS:
           DBG_vPrintf(TRUE, "BDB event callback: Network Join Successful\n");
           vHandleNetworkJoinAndRejoin();
           break;

       case BDB_EVENT_NWK_STEERING_SUCCESS:
           DBG_vPrintf(TRUE, "BDB event callback: Network steering success\n");
           vHandleNetworkJoinAndRejoin();
           break;

       case BDB_EVENT_REJOIN_FAILURE:
           DBG_vPrintf(TRUE, "BDB event callback: Failed to rejoin\n");
           vHandleRejoinFailure();
           break;

       case BDB_EVENT_NO_NETWORK:
           DBG_vPrintf(TRUE, "BDB event callback: No good network to join\n");
           vHandleRejoinFailure();
           break;
...

      
      



Zigbee Device Objects (ZDO) - , . BDB,   ZDO .





PRIVATE void vAppHandleZdoEvents(ZPS_tsAfEvent* psStackEvent)
{
   if(connectionState != JOINED)
   {
       DBG_vPrintf(TRUE, "Handle ZDO event: Not joined yet. Discarding event %d\n", psStackEvent->eType);
       return;
   }

   switch(psStackEvent->eType)
   {
       case ZPS_EVENT_APS_DATA_INDICATION:
           vHandleZdoDataIndication(psStackEvent);
           break;

       case ZPS_EVENT_NWK_LEAVE_INDICATION:
           if(psStackEvent->uEvent.sNwkLeaveIndicationEvent.u64ExtAddr == 0)
               vHandleLeaveNetwork();
           break;

       case ZPS_EVENT_NWK_LEAVE_CONFIRM:
           vHandleLeaveNetwork();
           break;

       default:
           //DBG_vPrintf(TRUE, "Handle ZDO event: event type %d\n", psStackEvent->eType);
           break;
   }
}

      
      



. . 2 ,





  • ZPS_EVENT_NWK_LEAVE_INDICATION





    • 1) - ( )





    • 2) ( vHandleLeaveNetwork())





  • ( ). , ZPS_EVENT_NWK_LEAVE_CONFIRM , . 





( zigbee2mqtt) ZPS_EVENT_NWK_LEAVE_CONFIRM, ZPS_EVENT_NWK_LEAVE_INDICATION. . , ZPS_EVENT_NWK_LEAVE_CONFIRM.





.





, - . . BDB ( DEBUG_BDB, BDB). , . - 12 , , 11 .





. BDB, . . . , .





. , :





  • Node Descriptor





  • ZigBee , r21 Trust Center. ,





  • Permit Join





  • .





.





, Discarding event. . BDB , . , - . , BDB . , .





.





. , . .. . , , BDB_EVENT_REJOIN_SUCCESS, , .





, , - , . 





.





, . . :









  • ,

















  • .





. ,





  • (, . rejoin request)









  • , , , route request (“- ?”)





  • , , . 100.





  • , .









, 2 , .





: 2006 , ZigBee, . BDB : , - . ? , , permit join. BDB. , BDB BDBC_IMP_MAX_REJOIN_CYCLES 1.





: , . , ? - , BDB_EVENT_REJOIN_FAILURE NOT_JOINED, . , ZigBee. - “ - ” “- ”. , , - , . - , . vHandleRejoinFailure(). 





End devices /

ZigBee 3 :





  • ( ) , , . .





  • (, ), , .





  • , .





. , , . . .





. , , , . , , .. . - ZigBee . . , .





. . ZPS Configuration Editor . , , . / . , rechargeable battery.





. , - . . - . . , .





pdum_gen.c zps_gen.c. 





Esta no es toda la diferencia.
diff.

, . , - , , .





- . .





:





  • beacon request ( )





  • ( )





  • (association request)





  • 0x2fd4 (association response)





  • (device update)





  • (transport key), 0x924b (, )





  • 4 , - ( ACK)





, MAC , , .





, , . sleeping true. RxOnWhenIdle Node Descriptor’. , , , . , - , ? , . - .









, - ZPS_EVENT_NWK_POLL_CONFIRM. , (Poll) . , , . , ZPS_EVENT_NWK_POLL_CONFIRM. .





, , , . , / . ( zigbee2mqtt) - . ZPS_eAplZdoPoll(). , . . 200, , , .





2 . , , . ZPS_eAplZdoPoll(). ( C++, ).





PollTask::PollTask()
{
   pollPeriod = 0;
   PeriodicTask::init();
}

PollTask& PollTask::getInstance()
{
   static PollTask task;
   return task;
}

void PollTask::startPoll(int period)
{
   pollPeriod = period;
   startTimer(period);
}

void PollTask::stopPoll()
{
   stopTimer();
}

void PollTask::timerCallback()
{
   ZPS_eAplZdoPoll();

   // Restart the timer
   startTimer(pollPeriod);
}


PRIVATE void vHandleNetworkJoinAndRejoin()
{
   DBG_vPrintf(TRUE, "== Device now is on the network\n");
...
   PollTask::getInstance()->startPoll(2000);
}

PRIVATE void vHandleLeaveNetwork()
{
   DBG_vPrintf(TRUE, "== The device has left the network\n");
...
   PollTask::getInstance()->stopPoll();
...
}

      
      



“ ” zigbee2mqtt





. 2 , . - , . ZPS_EVENT_NWK_POLL_CONFIRM Success, , No Data . 





. Data Request ( . . , , )





,





  • (0x0000) (0x0d21) ZCL OnOff. 





    • Source Destination .





    • , . HW Source HW Destination .





    • ( 406) (0x0000) (0xf544)





    • 19.55





  • , 20.44 ( 416) (0x0d21) (0xf544) “ ?”





  • , , ZCL OnOff ( 418). 





    • , , 406, Sequence number 222.





  • 20 , , , ZCL Default Response ( sequence number).





    • - (0x0000), 2 , (0xf544).





  • default response APS Ack. 60 , Data Request ( 426, 430, 432) 





, , , . OTA , . , .





void vHandlePollResponse(ZPS_tsAfPollConfEvent* pEvent)
{
   switch (pEvent->u8Status)
   {
       case MAC_ENUM_SUCCESS:
       case MAC_ENUM_NO_ACK:
           ZPS_eAplZdoPoll();
           break;

       case MAC_ENUM_NO_DATA:
       default:
           break;
   }

}

PRIVATE void vAppHandleZdoEvents(ZPS_tsAfEvent* psStackEvent)
{
....
       case ZPS_EVENT_NWK_POLL_CONFIRM:
           vHandlePollResponse(&psStackEvent->uEvent.sNwkPollConfirmEvent);
           break;

      
      



/

. , . . , , .





ZigBee - rejoin request. , . , rejoin response . , , permit join ( , ).





:





  • , , ( Update Device)





  • (Device Announcement). .





  • ( )





, BDB . , . deep sleep, ?





JN-AN-1217-Zigbee-3-0-Base-Device, , End Device. , ( ). , .





OSC On / RAM On ( - ). , .  - , , , . .. .





(, ) , ( - ZPS_eAplAfSendKeepAlive()). , - , , . ZPS_bAplAfSetEndDeviceTimeout().









1.5 , Deep Sleep, . rejoin’ , .





, ( ) , . ( ) , . NXP : , - .





. , . , . - . . 





, , - , ( , - ), . ? , ? - . 





:





  • , , ,





  • , ,





  • -> .





, ? ? , ( ) - . , . ( - ) . - , . , , - , , , .





, .





  • Xiaomi ( ). Xiaomi ( Wall switch ) . , write-only . , , , .





  • Moes 5 . home assistant 5 . - 2 2 .





  • Xiaomi Aqara , , ( ), - Zigbee . , 4 ( 250), . , .





, (Xiaomi, Moes, Tuya) , .





  • -





  • 15 ,





  • , , 5 .





. PWRM_vManagePower(), . ,





  • PWRM_vInit(E_AHI_SLEEP_OSCON_RAMON) , , .





  • PWRM_eScheduleActivity() . .





PRIVATE void APP_vTaskSwitch(Context * context)
{
...
   if(ButtonsTask::getInstance()->canSleep() &&
      ZigbeeDevice::getInstance()->canSleep())
   {
       DBG_vPrintf(TRUE, "=-=-=- Scheduling enter sleep mode... ");

       static pwrm_tsWakeTimerEvent wakeStruct;
       PWRM_teStatus status = PWRM_eScheduleActivity(&wakeStruct, 15 * 32000, wakeCallBack);
       DBG_vPrintf(TRUE, "status = %d\n", status);
   }
}

      
      



( 5 ) ( ) . C - . . . , , . .





void ZigbeeDevice::pollParent()
{
   polling = true;
   DBG_vPrintf(TRUE, "Polling for zigbee messages...\n");
   ZPS_eAplZdoPoll();
}

void ZigbeeDevice::handlePollResponse(ZPS_tsAfPollConfEvent* pEvent)
{
   switch (pEvent->u8Status)
   {
       case MAC_ENUM_SUCCESS:
       case MAC_ENUM_NO_ACK:
           pollParent();
           break;

       case MAC_ENUM_NO_DATA:
           polling = false;
       default:
           break;
   }
}

bool ZigbeeDevice::canSleep() const
{
   return !polling;
}
      
      



. polling, . , ZPS_EVENT_NWK_POLL_CONFIRM No Data.





. , .





PWRM_CALLBACK(PreSleep)
{
...
   // Save the MAC settings (will get lost though if we don't preserve RAM)
   vAppApiSaveMacSettings();
...
}

PWRM_CALLBACK(Wakeup)
{
...
   // Restore Mac settings (turns radio on)
   vMAC_RestoreSettings();
...
   // Poll the parent router for zigbee messages
   ZigbeeDevice::getInstance()->handleWakeUp();
}

void ZigbeeDevice::handleWakeUp()
{
       // TODO: more code here later

       pollParent();
}
      
      



MAC zigbee , , , . . .





. , .





extern "C" PUBLIC void vISR_SystemController(void)
{
   // clear pending DIO changed bits by reading register
   uint8 wakeStatus = u8AHI_WakeTimerFiredStatus();
   uint32 dioStatus = u32AHI_DioInterruptStatus();

   DBG_vPrintf(TRUE, "In vISR_SystemController\n");

   if(ButtonsTask::getInstance()->handleDioInterrupt(dioStatus))
   {
       DBG_vPrintf(TRUE, "=-=-=- Button interrupt dioStatus=%04x\n", dioStatus);
       PWRM_vWakeInterruptCallback();
   }

   if(wakeStatus & E_AHI_WAKE_TIMER_MASK_1)
   {
       DBG_vPrintf(TRUE, "=-=-=- Wake Timer Interrupt\n");
       PWRM_vWakeInterruptCallback();
   }
}
      
      



.





, , No Data .





15- - zigbee2mqtt. , , . , , , zigbee - , . , .. . ( 3, PWRM_eScheduleActivity() - , ).





, .





Zigbee- - () ,





. , Xiaomi , - . .





1: . 





















  • , Data Request





, Data Request. , network discovery. , rejoin request. , . Device announcement ( , ), ( ). 





, , , . , - BDB Failure Recovery.









. . .





2: . , , .





:





  • , ,









  • ,









  • , rejoin request











:





  • Rejoin Request (0xdc47)





  • Network Discovery





  • Rejoin Request (0x924b)





  • Update Device , Device Announcement, ( - , )





( BDB)





,





  • bNodeIsOnANetwork=1, BDB,





  • BDB network discovery (Rejoin Cycle 1-A without Disc)





  • ZPS_EVENT_NWK_FAILED_TO_JOIN, BDB





  • BDB (Rejoin Cycle 1-B with Disc on Primary), Network Discovery





  • ZPS_EVENT_NWK_JOINED_AS_END_DEVICE, BDB, BDB - BDB event callback: Network Join Successful





, , , BDB. 4 , (, network discovery ).





.





3: , .





:





  • , ,









  • ( )









  • , rejoin request Network Discovery





- , - . .





, , .





? . , , . , , , . - , “”. 5, 10, 60 - .





. , , - .





2 :





   int rejoinFailures;
   int cyclesTillNextRejoin;
      
      



, . 15- , .





2 . .





void ZigbeeDevice::joinNetwork()
{
   DBG_vPrintf(TRUE, "== Joining the network\n");
   connectionState = JOINING;

   // Clear ZigBee stack internals
   sBDB.sAttrib.bbdbNodeIsOnANetwork = FALSE);
   sBDB.sAttrib.u8bdbCommissioningMode = BDB_COMMISSIONING_MODE_NWK_STEERING;
   ZPS_eAplAibSetApsUseExtendedPanId(0);
   ZPS_vDefaultStack();
   ZPS_vSetKeys();
   ZPS_vSaveAllZpsRecords();

   // Connect to a network
   BDB_eNsStartNwkSteering();
   DBG_vPrintf(TRUE, "  BDB_eNsStartNwkSteering=%d\n", status);
}

void ZigbeeDevice::rejoinNetwork()
{
   DBG_vPrintf(TRUE, "== Rejoining the network\n");

   sBDB.sAttrib.bbdbNodeIsOnANetwork = (connectionState == JOINED ? TRUE : FALSE);
   sBDB.sAttrib.u8bdbCommissioningMode = BDB_COMMISSIONING_MODE_NWK_STEERING;

   DBG_vPrintf(TRUE, "ZigbeeDevice(): Starting base device behavior... bNodeIsOnANetwork=%d\n", sBDB.sAttrib.bbdbNodeIsOnANetwork);
   ZPS_vSaveAllZpsRecords();
   BDB_vStart();
}

      
      



.





void ZigbeeDevice::leaveNetwork()
{
...
   rejoinFailures = 0;
...
}

void ZigbeeDevice::handleNetworkJoinAndRejoin()
{
...
   rejoinFailures = 0;
}
      
      







void ZigbeeDevice::handleRejoinFailure()
{
   DBG_vPrintf(TRUE, "== Failed to (re)join the network\n");
   polling = false;

   if(connectionState == JOINED && ++rejoinFailures < 5)
   {
       DBG_vPrintf(TRUE, "  Rejoin counter %d\n", rejoinFailures);

       // Schedule sleep for a minute
       cyclesTillNextRejoin = 4; // 4 * 15s = 1 minute
   }
   else
       handleLeaveNetwork();
}
      
      



5 , . 5 .





, , , .





bool ZigbeeDevice::needsRejoin() const
{
   // Non-zero rejoin failure counter reflects that we have received spontaneous
   // Rejoin failure message while the node was in JOINED state
   return rejoinFailures > 0 && connectionState == JOINED;
}

void ZigbeeDevice::handleWakeUp()
{
   if(connectionState != JOINED)
       return;

   if(needsRejoin())
   {
       // Device that is basically connected, but currently needs a rejoin will have to
       // sleep a few cycles between rejoin attempts
       if(cyclesTillNextRejoin-- > 0)
       {
           DBG_vPrintf(TRUE, "ZigbeeDevice: Rejoining in %d cycles\n", cyclesTillNextRejoin);
           return;
       }

       rejoinNetwork();
   }
   else
       // Connected device will just poll its parent on wake up
       pollParent();
}

      
      



(JOINED) - REJOIN_FAILED, . (rejoinNetwork() ). , .





C++

ZigBee ( ). ++ - .





, ++. , . , ++ - , , . ++ : , , , RAII, . 





NXP SDK - gcc.





SDK NXP . , ,





class Timer
{
   uint8 timerHandle;

public:
   void init(ZTIMER_tpfCallback cb, void * param, bool preventSleep = false)
   {
       ZTIMER_eOpen(&timerHandle, cb, param, preventSleep ? ZTIMER_FLAG_PREVENT_SLEEP : ZTIMER_FLAG_ALLOW_SLEEP);
   }

   void start(uint32 time)
   {
       ZTIMER_eStart(timerHandle, time);
   }

   void stop()
   {
       ZTIMER_eStop(timerHandle);
   }
};

      
      



, , .





, PDM. , .





template<class T, uint8 id>
class PersistedValue
{
   T value;

public:
   void init(const T & initValue)
   {
       uint16 readBytes;
       PDM_teStatus status = PDM_eReadDataFromRecord(id, &value, sizeof(T), &readBytes);
       if(status != PDM_E_STATUS_OK)
           setValue(initValue);

       DBG_vPrintf(TRUE, "PersistedValue::init(). Status %d, value %d\n", status, value);
   }

   T getValue()
   {
       return value;
   }

   operator T()
   {
       return value;
   }

   PersistedValue<T, id> & operator =(const T & newValue)
   {
       setValue(newValue);
       return *this;
   }

   void setValue(const T & newValue)
   {
       value = newValue;
       PDM_teStatus status = PDM_eSaveRecordData(id, &value, sizeof(T));
       DBG_vPrintf(TRUE, "PersistedValue::setValue() Status %d, value %d\n", status, value);
   }
};

      
      



, -





connectionState = JOINED;
      
      







uint8 value = JOINED;
PDM_eSaveRecordData(PDM_ID_NODE_STATE, &value, sizeof(value));
      
      



init() PDM, , .





2 .





template<tszQueue * handle>
struct QueueHandleExtStorage
{
   tszQueue * getHandle()
   {
       return handle;
   }
};

struct QueueHandleIntStorage
{
   tszQueue handle;

   tszQueue * getHandle()
   {
       return &handle;
   }
};


template<class T, uint32 size, class H>
class QueueBase : public H
{
   T queueStorage[size];

public:
   QueueBase()
   {
       // JN5169 CRT does not really call constrictors for global object
       DBG_vPrintf(TRUE, "In a queue constructor...\n");
   }

   void init()
   {
       ZQ_vQueueCreate(H::getHandle(), size, sizeof(T), (uint8*)queueStorage);
   }

   bool receive(T * val)
   {
       return ZQ_bQueueReceive(H::getHandle(), (uint8*)val) != 0;
   }

   void send(const T & val)
   {
       ZQ_bQueueSend(H::getHandle(), (uint8*)&val);
   }
};

template<class T, uint32 size>
class Queue : public QueueBase<T, size, QueueHandleIntStorage >
{};

template<class T, uint32 size, tszQueue * handle>
class QueueExt : public QueueBase<T, size, QueueHandleExtStorage<handle> >
{};

      
      



. . ( QueueHandleIntStorage). zps_gen.c ( ZPSConfig.exe) extern ( ZigBee ). QueueHandleExtStorage.





, . , , . , - Queue QueueExt . :





Queue<MyType, 3> myQueue;
myQueue.init();
myQueue.send(valueToSend);
myQueue.receive(&valueToReceive);
      
      



zigbee





extern PUBLIC tszQueue zps_msgMlmeDcfmInd;
QueueExt<MAC_tsMlmeVsDcfmInd, 10, &zps_msgMlmeDcfmInd> msgMlmeDcfmIndQueue;
      
      



, - , . : CRT, JN5169 SDK . , .init_array, .. . CRT, , . , . init() vAppMain(). , (https://www.youtube.com/watch?v=dOfucXtyEsU) - .





. . , , . - - , , .





class PeriodicTask
{
   Timer timer;

public:
   void init()
   {
       timer.init(timerFunc, this);
   }

   void startTimer(uint32 delay)
   {
       timer.start(delay);
   }

   void stopTimer()
   {
       timer.stop();
   }

protected:
   static void timerFunc(void * param)
   {
       PeriodicTask * task = (PeriodicTask*)param;

       task->timerCallback();
   }

   virtual void timerCallback() = 0;
};

      
      



,





class BlinkTask : public PeriodicTask
{
   bool fastBlinking;

public:
   BlinkTask()
   {
      fastBlinking = false;

      vAHI_DioSetDirection(0, BOARD_LED_PIN);

      PeriodicTask::init();
      startTimer(1000);
   }

   void setBlinkMode(bool fast)
   {
      fastBlinking = fast;
   }

protected:
   virtual void timerCallback()
   {
      // toggle LED
      uint32 currentState = u32AHI_DioReadInput();
      vAHI_DioSetOutput(currentState^BOARD_LED_PIN, currentState&BOARD_LED_PIN);

      //Restart the timer
      startTimer(fastBlinking ? ZTIMER_TIME_MSEC(200) : ZTIMER_TIME_MSEC(1000));
   }
};
      
      



, , . , . , , , timerCallback . init() . vAppMain(), , . , . 





ZigBee - . , API NXP . , API C - Win32 API , . NXP :





  • . ( ), .





  • , .





  • ZCL ZCL. .





  • - (, ) . , - .





  • , - , , .





, , . ZigBee .









  • ZigbeeDevice . // , ZigBee ( ZDO). BDB - Zigbee , .





  • EndpointManager . ZCL . .. .





  • Endpoint . -.





  • SwitchEndpoint , . / , , /. , .





  • ThermometerEndpoint PowerMeterEndpoint , . .





. ZigbeeDevice - , .





class ZigbeeDevice
{
   typedef enum
   {
       NOT_JOINED,
       JOINING,
       JOINED

   } JoinStateEnum;

   PersistedValue<JoinStateEnum, PDM_ID_NODE_STATE> connectionState;
   Queue<BDB_tsZpsAfEvent, 3> bdbEventQueue;
   PollTask pollTask;

   bool polling;
   int rejoinFailures;
   int cyclesTillNextRejoin;


public:
   ZigbeeDevice();

   static ZigbeeDevice * getInstance();

   void joinNetwork();
   void rejoinNetwork();
   void leaveNetwork();
   void joinOrLeaveNetwork();

   void pollParent();
   bool canSleep() const;
   bool needsRejoin() const;
   void handleWakeUp();

protected:
   void handleNetworkJoinAndRejoin();
   void handleLeaveNetwork();
   void handleRejoinFailure();
   void handlePollResponse(ZPS_tsAfPollConfEvent* pEvent);
   void handleZdoBindEvent(ZPS_tsAfZdoBindEvent * pEvent);
   void handleZdoUnbindEvent(ZPS_tsAfZdoUnbindEvent * pEvent);
   void handleZdoDataIndication(ZPS_tsAfEvent * pEvent);
   void handleZdoEvents(ZPS_tsAfEvent* psStackEvent);
   void handleZclEvents(ZPS_tsAfEvent* psStackEvent);
   void handleAfEvent(BDB_tsZpsAfEvent *psZpsAfEvent);

public:
   void handleBdbEvent(BDB_tsBdbEvent *psBdbEvent);
};
      
      



, . BDB . , BDB APP_vBdbCallback() . ZigbeeDevice , .





PUBLIC void APP_vBdbCallback(BDB_tsBdbEvent * event)
{
   ZigbeeDevice::getInstance()->handleBdbEvent(event);
}

ZigbeeDevice * ZigbeeDevice::getInstance()
{
   static ZigbeeDevice instance;
   return &instance;
}

void ZigbeeDevice::handleBdbEvent(BDB_tsBdbEvent *psBdbEvent)
{
   switch(psBdbEvent->eEventType)
   {
...
      
      



, ... .





c:/nxp/bstudio_nxp/sdk/tools/ba-elf-ba2-r36379/bin/../lib/gcc/ba-elf/4.7.4/../../../../ba-elf/lib/mcpu_jn51xx_sizeopt\libg.a(lib_a-glue.o): In function `_sbrk':
/ba_toolchain/r36379/source/gcc-4.7.4-ba-r36379-build/ba-elf/mcpu_jn51xx_sizeopt/newlib/libc/sys/basim/../../../../../../../gcc-4.7.4-ba-r36379/newlib/libc/sys/basim/glue.c:75: undefined reference to `end'
/ba_toolchain/r36379/source/gcc-4.7.4-ba-r36379-build/ba-elf/mcpu_jn51xx_sizeopt/newlib/libc/sys/basim/../../../../../../../gcc-4.7.4-ba-r36379/newlib/libc/sys/basim/glue.c:75: undefined reference to `_stack'
/ba_toolchain/r36379/source/gcc-4.7.4-ba-r36379-build/ba-elf/mcpu_jn51xx_sizeopt/newlib/libc/sys/basim/../../../../../../../gcc-4.7.4-ba-r36379/newlib/libc/sys/basim/glue.c:75: undefined reference to `_stack'
/ba_toolchain/r36379/source/gcc-4.7.4-ba-r36379-build/ba-elf/mcpu_jn51xx_sizeopt/newlib/libc/sys/basim/../../../../../../../gcc-4.7.4-ba-r36379/newlib/libc/sys/basim/glue.c:75:(.text+0x197): relocation truncated to fit: R_BA_8 against undefined symbol `_stack'
      
      



, . instance. , ,





  • . , .





  • atexit ( ). “” - .





  • - - . 





  • RTTI. 





-fno-rtti -fno-exceptions -fno-use-cxa-atexit -fno-threadsafe-statics .





EndpointManager





class EndpointManager
{
private:
   Endpoint * registry[ZCL_NUMBER_OF_ENDPOINTS+1];

   EndpointManager()
   {
       memset(registry, 0, sizeof(Endpoint*) * (ZCL_NUMBER_OF_ENDPOINTS+1));
   }

public:
   static EndpointManager * getInstance()
   {
       static EndpointManager instance;
       return &instance;
   }

   void registerEndpoint(uint8 id, Endpoint * endpoint)
   {
       registry[id] = endpoint;
       endpoint->setEndpointId(id);
       endpoint->init();
   }

   static void handleZclEvent(tsZCL_CallBackEvent *psEvent)
   {
       EndpointManager::getInstance()->handleZclEventInt(psEvent);
   }

protected:
   void handleZclEventInt(tsZCL_CallBackEvent *psEvent)
   {
       uint8 ep = psEvent->u8EndPoint;
       registry[ep]->handleZclEvent(psEvent);
   }
};
      
      



. map’ , , . .. 1, 10, 30, 31.





, .. Zigbee handleZclEvent() ( APP_ZCL_cbEndpointCallback() ). handleZclEventInt() .





- Endpoint





class Endpoint
{
   uint8 endpointId;

public:
   Endpoint();
   {
       endpointId = 0;
   }

   void setEndpointId(uint8 id);
   {
       endpointId = id;
   }

   uint8 getEndpointId() const;
   {
       return endpointId;
   }


   virtual void init() = 0;
   virtual void handleZclEvent(tsZCL_CallBackEvent *psEvent);

protected:
   virtual void handleClusterUpdate(tsZCL_CallBackEvent *psEvent) = 0;
};

void Endpoint::handleZclEvent(tsZCL_CallBackEvent *psEvent)
{
   switch (psEvent->eEventType)
   {
  ...
       case E_ZCL_CBET_CLUSTER_CUSTOM:
       case E_ZCL_CBET_CLUSTER_UPDATE:
           handleClusterUpdate(psEvent);
           break;
   }
}

      
      



handleZclEvent APP_ZCL_cbEndpointCallback() . ( handleClusterUpdate()).





C++, . ( init() handleClusterUpdate()), _sbrk() end.





. , , ( , ) . .





Endpoint,





  • handleZclEvent() Endpoint::handleZclEvent()





  • init() handleClusterUpdate() __cxa_pure_virtual(). 





- , .





__cxa_pure_virtual() _sbrk end (, , ). , , arduino - , .





extern "C" void __cxa_pure_virtual(void) __attribute__((__noreturn__));
void __cxa_pure_virtual(void)
{
 DBG_vPrintf(TRUE, "!!!!!!! Pure virtual function call.\n");
 while (1)
   ;
}
      
      



SwitchEndpoint.





class SwitchEndpoint: public Endpoint
{   
protected:
   tsZLO_OnOffLightDevice sSwitch;
   BlinkTask blinkTask;

public:
   SwitchEndpoint();
   virtual void init();

   bool getState() const;
   void switchOn();
   void switchOff();
   void toggle();

protected:
   void doStateChange(bool state);
   void reportStateChange();

protected:
   virtual void handleClusterUpdate(tsZCL_CallBackEvent *psEvent);
};
      
      



, : /, / ZigBee. . , ZigBee. , . 2 .





void SwitchEndpoint::doStateChange(bool state)
{
   DBG_vPrintf(TRUE, "SwitchEndpoint EP=%d: do state change %d\n", getEndpointId(), state);

   sSwitch.sOnOffServerCluster.bOnOff = state ? TRUE : FALSE;

   blinkTask.setBlinkMode(state);
}

void SwitchEndpoint::reportStateChange()
{
   // Destination address - 0x0000 (coordinator)
   tsZCL_Address addr;
   addr.uAddress.u16DestinationAddress = 0x0000;
   addr.eAddressMode = E_ZCL_AM_SHORT;

   DBG_vPrintf(TRUE, "Reporting attribute EP=%d value=%d... ", getEndpointId(), sSwitch.sOnOffServerCluster.bOnOff);
   PDUM_thAPduInstance myPDUM_thAPduInstance = hZCL_AllocateAPduInstance();
   teZCL_Status status = eZCL_ReportAttribute(&addr,
                                              GENERAL_CLUSTER_ID_ONOFF,
                                              E_CLD_ONOFF_ATTR_ID_ONOFF,
                                              getEndpointId(),
                                              1,
                                              myPDUM_thAPduInstance);
   PDUM_eAPduFreeAPduInstance(myPDUM_thAPduInstance);
   DBG_vPrintf(TRUE, "status: %02x\n", status);
}

      
      



. :





void SwitchEndpoint::switchOn()
{
    doStateChange(true);
    reportStateChange();
}

void SwitchEndpoint::switchOff()
{
    doStateChange(false);
    reportStateChange();
}

void SwitchEndpoint::toggle()
{
    doStateChange(!getState());
    reportStateChange();
}
      
      



:





void SwitchEndpoint::handleClusterUpdate(tsZCL_CallBackEvent *psEvent)
{
   uint16 u16ClusterId = psEvent->uMessage.sClusterCustomMessage.u16ClusterId;
   tsCLD_OnOffCallBackMessage * msg = (tsCLD_OnOffCallBackMessage *)psEvent->uMessage.sClusterCustomMessage.pvCustomData;
   uint8 u8CommandId = msg->u8CommandId;

   DBG_vPrintf(TRUE, "SwitchEndpoint EP=%d: Cluster update message ClusterID=%04x Cmd=%02x\n",
               psEvent->u8EndPoint,
               u16ClusterId,
               u8CommandId);

   doStateChange(getState());
}
      
      



.





void SwitchEndpoint::init()
{
   // Initialize the endpoint
   DBG_vPrintf(TRUE, "SwitchEndpoint::init(): register On/Off endpoint #%d...  ", getEndpointId());
   teZCL_Status status = eZLO_RegisterOnOffLightEndPoint(getEndpointId(), &EndpointManager::handleZclEvent, &sSwitch);
   DBG_vPrintf(TRUE, "eApp_ZCL_RegisterEndpoint() status %d\n", status);

   // Fill Basic cluster attributes
   // Note: I am not really sure why this device info shall be a part of a switch endpoint
   memcpy(sSwitch.sBasicServerCluster.au8ManufacturerName, CLD_BAS_MANUF_NAME_STR, CLD_BAS_MANUF_NAME_SIZE);
   memcpy(sSwitch.sBasicServerCluster.au8ModelIdentifier, CLD_BAS_MODEL_ID_STR, CLD_BAS_MODEL_ID_SIZE);
   memcpy(sSwitch.sBasicServerCluster.au8DateCode, CLD_BAS_DATE_STR, CLD_BAS_DATE_SIZE);
   memcpy(sSwitch.sBasicServerCluster.au8SWBuildID, CLD_BAS_SW_BUILD_STR, CLD_BAS_SW_BUILD_SIZE);
   sSwitch.sBasicServerCluster.eGenericDeviceType = E_CLD_BAS_GENERIC_DEVICE_TYPE_WALL_SWITCH;

   // Initialize blinking
   // Note: this blinking task represents a relay that would be tied with this switch. That is why blinkTask
   // is a property of SwitchEndpoint, and not the global task object
   // TODO: restore previous blink mode from PDM
   blinkTask.setBlinkMode(false);
}
      
      



ZigBee. . . , . EndpointManager, , -.





Basic Cluster’, . , - , , , , ZDO. . , , , - Basic Cluster , . , Basic Cluster, . .





, - BlinkTask SwitchEndpoint, . . - , . , .





. , . , SwitchEndpoint :





  • vAppMain(),





  • MainTask(),





  • ( ) Wakeup PreSleep , .





, . , . Context vAppMain().





struct Context
{
   SwitchEndpoint switch1;
};

extern "C" PUBLIC void vAppMain(void)
{
...
   Context context;
   EndpointManager::getInstance()->registerEndpoint(HELLOENDDEVICE_SWITCH_ENDPOINT, &context.switch1);
...
   while(1)
   {
       APP_vTaskSwitch(&context);
...
      
      



C++ . , , , . , , - , .





, , . ZigBee. , Base Device Behavior . BDB ZigBee . , .





, , Base Device Behavior Specification . ( ) .





-. , , .





, , ++. ZigBee , .





. - - Binding OTA Update, .







:





:





  • JN-AN-1219-Zigbee-3-0-Controller-and-Switch





  • JN-AN-1217-Zigbee-3-0-Base-Device   <----





  • JN-AN-1220-Zigbee-3-0-Sensor





El código:





https://github.com/grafalex82/hellozigbee/tree/hello_zigbee_part_2





https://github.com/grafalex82/hellozigbee/tree/hello_zigbee_end_device








All Articles