Hello NXP JN5169 Zigbee World: Connecting and Reconnecting Devices Correctly

Good day! 





Today we will continue to study ZigBee using the example of NXP JN5169 microcontrollers. In the first article, I talked about the periphery of the microcontroller, and secondly , how to connect to the ZigBee network and do basic operations there. The main topic of today's article is the correct and reliable connection to the ZigBee network, as well as the processing of reconnections. Moreover, we will do this not only for router devices, but also for end devices.





At first I thought that end devices are much simpler than routers. After all, routers through themselves pump information from different nodes, and manage other devices. It turned out on the contrary, with routers much less fuss - they just work out of the box. But the end devices significantly add a smut - then reconnect them, then put them to bed, and they have network communication not when it comes to it, but only when it should be. In general, in the article we will talk in detail about the end devices.





Also, the article will touch upon an additional topic about my attempts to refine the code and wrap it in C ++ classes. But not everything is so simple if you use a truncated compiler from ten years ago.





Are you ready to continue your immersion in the ZigBee world?





Theoretical introductory

In ZigBee networks, a distinction is made between the procedure for joining a new device, and rejoining a device that has already been connected to the network earlier. What is the difference?





Consider the option of connecting a new device. A completely fresh piece of hardware does not yet know on which channel the ZigBee network works, what devices are nearby, what network addresses they have, and the transport encryption key of the network is also unknown to the device. The network also knows nothing about the device, the device has no network address, and there are no entries about this device in the routing tables. 





, ( 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. 





This is not the whole diff.
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





The code:





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





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








All Articles