良い一日!
今日は、NXP JN5169 マイクロコントローラーの例を使用して ZigBee の研究を続けます。最初の記事ではマイクロコントローラーの周辺について、2番目の記事ではZigBeeネットワークに接続して基本的な操作を行う方法について説明しました。今日の記事の主なトピックは、ZigBee ネットワークへの正しく信頼できる接続と、再接続の処理です。また、ルーター機器だけでなく、エンド機器にも対応いたします。
最初は、エンド デバイスはルーターよりもはるかに単純だと思っていました。結局のところ、ルーターはそれ自体を介してさまざまなノードから情報を送り出し、他のデバイスを管理します。それどころか、ルーターを使用すると、大騒ぎがはるかに少なくなり、箱から出してすぐに動作することがわかりました。しかし、エンド デバイスはスマットを大幅に追加します - その後、それらを再接続してからベッドに置きます。ネットワーク通信は、必要なときだけではなく、必要なときにだけ行われます。一般に、この記事ではエンドデバイスについて詳しく説明します。
また、この記事では、コードを改良して C ++ クラスでラップするという私の試みに関する追加のトピックに触れます。しかし、10 年前の切り捨てられたコンパイラを使用している場合、すべてがそれほど単純ではありません。
ZigBee の世界に没頭し続ける準備はできていますか?
理論入門
ZigBee ネットワークでは、新しいデバイスに参加する手順と、以前にネットワークに既に接続されているデバイスを再参加する手順が区別されます。違いはなんですか?
新しいデバイスを接続するオプションを検討してください。完全に新しいハードウェアは、ZigBee ネットワークがどのチャネルで機能するか、近くにあるデバイス、それらが持つネットワーク アドレス、およびネットワークの転送暗号化キーもデバイスに認識されていません。また、ネットワークはデバイスについて何も知らず、デバイスにはネットワーク アドレスがなく、ルーティング テーブルにこのデバイスに関するエントリがありません。
, ( 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.

, . , - , , .

- . .

:
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_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, .
:
https://www.nxp.com/docs/en/user-guide/JN-UG-3113.pdf
https://www.nxp.com/docs/en/user-guide/JN-UG-3114.pdf
https://zigbeealliance.org/wp-content/uploads/2019/12/docs-13-0402-13-00zi-Base-Device-Behavior-Specification-2-1.pdf
:
JN-AN-1219-Zigbee-3-0-Controller-and-Switch
JN-AN-1217-Zigbee-3-0-Base-Device <----
JN-AN-1220-Zigbee-3-0-センサー
コード:
https://github.com/grafalex82/hellozigbee/tree/hello_zigbee_part_2
https://github.com/grafalex82/hellozigbee/tree/hello_zigbee_end_device