نحن نكتب برنامجًا ثابتًا لـ TI cc2530 على Z-Stack 3.0 لـ Zigbee Sonoff BASICZBR3 مع مستشعر ds18b20



من المفترض أن القارئ لديه بالفعل معرفة أولية بلغة C ، ويعرف شيئًا عن Zigbee ، وشريحة cc2530 ، وطرق تحديثه واستخدامه ، كما أنه على دراية بمشاريع مثل zigbee2mqtt. إذا لم يكن الأمر كذلك ، فاستعد أو اذهب للقراءة على https://myzigbee.ru و https://www.zigbee2mqtt.io/ تمت كتابة
المقالة أولاً بالتفصيل ، ولكن يتم تسريعها تدريجيًا ولا تتوقف عند التفاصيل ، ولكنها تصف رمز البرنامج الثابت النهائي. إذا كان شخص ما غير مهتم بالمنطق ، فما عليك سوى فتح مصادر البرامج الثابتة وقراءتها.

الكود المصدري للبرنامج الثابت النهائي

لا تدعي الكود ونهج التطوير أنها مثالية. "أنا لست ساحرًا ، أنا أتعلم فقط."

هدف


الهدف الرئيسي هو فهم كيفية كتابة البرامج الثابتة لـ Z-Stack لفترة طويلة. لذلك ، قررت تنفيذ برنامج ثابت بديل للمعدات النهائية (تم اختيار تتابع Sonoff BASICZBR3 كمثال ) وإضافة القدرة على توصيل مستشعر درجة الحرارة ds18b20 الشهير.

بالإضافة إلى ذلك ، أردت أن أعرض على مطوري Zigbee للمبتدئين مثالًا على تطوير البرامج الثابتة لشريحة TI cc2530 على Z-Stack.

1. التحضير


لبدء التطوير ، تحتاج إلى تنزيل Z-Stack 3.0.2 وتثبيته - هذا هو SDK لتطوير البرامج الثابتة مع الأمثلة والوثائق.
تحتاج أيضًا إلى تنزيل وتثبيت IAR Embeded Workbench لـ 8051 - هذه بيئة تطوير مع القدرة على الترجمة لرقائق TI cc2530. فترة الاستخدام المجانية هي شهر واحد (ولكن سيجد الباحث حلاً).

من أجل التطوير والتصحيح ، أستخدم CCDebugger - فهو لا يسمح فقط بوميض رقائق cc2531 / cc2530 ، ولكن أيضًا تصحيح التطبيق في بيئة IAR.



لتبسيط التجارب والنماذج وتصحيح الأخطاء التي أقوم بها على devboard ووحدة cc2530 المقابلة:



2. إنشاء تطبيق جديد


نقوم بإنشاء مشروع جديد يعتمد على GenericApp. هذا مثال على تطبيق Z-Stack الأساسي. وهو موجود في مجلد Z-Stack 3.0.2 \ Projects \ zstack \ HomeAutomation \ GenericApp.
نقوم بنسخه في مكان قريب وإعادة تسميته ، على سبيل المثال ، إلى DIYRuZRT (دعنا نطلب التطبيق لجهازنا).

يوجد داخل مجلد CC2530DB ملفات:

  • GenericApp.ewd - إعدادات المشروع لـ C-SPY
  • GenericApp.ewp - ملف المشروع
  • GenericApp.eww - مساحة العمل

إعادة تسمية الملفات إلى DIYRuZRT.eww و DIYRuZRT.ewp.

داخل جميع الملفات (بما في ذلك المجلد المصدر) ، نقوم أيضًا بتغيير جميع المراجع إلى GenericApp إلى DIYRuZRT.

افتح مشروع DIYRuZRT.ewp الآن في IAR. نختار تكوين RouterEB ونقوم بإجراء Rebuild All.



سيتم إنشاء مجلد RouterEB في مجلد CC2530DB ، وسيظهر ملف DIYRuZRT.d51 داخل مجلد EXE - هذا الملف مناسب للوميض والتصحيح من IAR.

ولكن إذا احتجنا إلى وميض البرامج الثابتة عبر SmartRF Flash Programmer ، فسوف نقوم بإجراء تغييرات صغيرة. للقيام بذلك ، في إعدادات المشروع في قسم الارتباط في علامة التبويب الإخراج ، قم بتغيير الإعدادات لملف الإخراج والتنسيق:



بعد ذلك ، سيتم إنشاء ملف البرنامج الثابت DIYRuZRT.hex في مجلد EXE ، وهو مناسب للوميض من الأدوات الأخرى وبطرق أخرى .
ولكن بعد تحميل هذه البرامج الثابتة ، لا يتصل الجهاز بالشبكة. حسنا ، سوف نفهم.

3. قليلا من المصطلحات


تحتوي مصطلحات زيجبي على المفاهيم التالية:

  • نقطة النهاية - نقطة وصف الجهاز النهائي. عادة في الأجهزة البسيطة نقطة نهاية واحدة. يمكن أن يكون هناك العديد منها في الأجهزة متعددة الوظائف ، وكذلك في الأجهزة ذات ملفات تعريف تفاعل مختلفة (ملف تعريف واحد - نقطة نهاية واحدة).
  • الكتلة (الكتلة) - مجموعة من السمات والأوامر المتعلقة بوظيفة واحدة (تشغيل / إيقاف ، تعتيم ، قياسات درجة الحرارة ، إلخ). تشير الكتلة إلى الفرص التي حققتها نقطة النهاية. في نقطة نهاية واحدة ، يمكنك تنفيذ عدة مجموعات مختلفة ، ولكن ليس نفس المجموعات.
  • السمة (سمة) - سمة من سمات الكتلة التي يمكن قراءتها أو كتابتها. يمكن أن يكون للكتلة العديد من السمات.
  • الأمر - رسالة تحكم يمكن للكتلة معالجتها. قد يكون للفريق معلمات. يتم تنفيذ ذلك بواسطة دالة يتم تنفيذها عند تلقي أمر ومعلمات.

يتم توحيد أنواع المجموعات والسمات والأوامر في مكتبة مجموعة زيجبي. لكن يمكن للمصنعين استخدام مجموعاتهم الخاصة ، مع سماتهم وفرقهم الخاصة.

بعض المنتجين يرثى لهم لا يهتمون بالمعايير ويفعلون شيئًا بشأن المعيار. ثم عليك أن تتكيف معهم. تحتوي

مصطلحات Z-Stack أيضًا على مفاهيم خاصة بها ، على سبيل المثال:

  • OSAL (طبقة تجريد نظام التشغيل) - مستوى تجريد نظام التشغيل. هنا تعمل مع المهام (المهام) والرسائل (الرسائل) والأحداث (الأحداث) والموقتات (المؤقتات) والكائنات الأخرى.
  • HAL (طبقة تجريد الأجهزة) - مستوى تجريد المعدات. هنا تعمل مع الأزرار (المفاتيح) ، المصابيح (المصابيح) ، المقاطعات (المقاطعة) ، إلخ.

توفر طبقة الأجهزة عزلًا لرمز البرنامج والمعدات التي تتحكم فيها. يوفر المستوى التشغيلي آليات لبناء والتفاعل بين عناصر التطبيق.

استخدام كل هذا في انتظارك أدناه ، ومن حيث المبدأ ، عند تطوير البرامج الثابتة.

4. ماذا لدينا داخل التطبيق الأساسي؟


يوجد رمز التطبيق في مجلد المصدر:

  • OSAL_DIYRuZRT.c
  • zcl_DIYRuZRT.h
  • zcl_DIYRuZRT.c
  • zcl_DIYRuZRT_data.c — ,

OSAL_DIYRuZRT.c - الملف الرئيسي الذي يتم فيه تعبئة صفيف معالجات المهام (المهمة)  pTaskEventHandlerFn taskArr وتنفيذ وظيفة تهيئة osalInitTasks .

هناك حاجة إلى كافة الملفات الأخرى لتنفيذ هذه التهيئة والمعالجات. يتم ملء

قائمة معالجات المهام pTaskEventHandlerFn rAr بمراجع الدالات. يتم توصيل / فصل بعض المهام عن طريق توجيهات الترجمة المقابلة.

يمكنك عرض وتكوين توجيهات الترجمة في خيارات برنامج مترجم الرموز المحددة:



const pTaskEventHandlerFn tasksArr[] = {
  macEventLoop,
  nwk_event_loop,
#if !defined (DISABLE_GREENPOWER_BASIC_PROXY) && (ZG_BUILD_RTR_TYPE)
  gp_event_loop,
#endif  
  Hal_ProcessEvent,
#if defined( MT_TASK )
  MT_ProcessEvent,
#endif
  APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_ProcessEvent,
#endif
  ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_event_loop,
#endif
  //Added to include TouchLink functionality
  #if defined ( INTER_PAN )
    StubAPS_ProcessEvent,
  #endif
  // Added to include TouchLink initiator functionality
  #if defined ( BDB_TL_INITIATOR )
    touchLinkInitiator_event_loop,
  #endif
  // Added to include TouchLink target functionality
  #if defined ( BDB_TL_TARGET )
    touchLinkTarget_event_loop,
  #endif
  zcl_event_loop,
  bdb_event_loop,
  zclDIYRuZRT_event_loop
};

osalInitTasks هي وظيفة بدء تشغيل التطبيق التي تسجل المهام التي يؤديها التطبيق.

يتم تسجيل المهام بالترتيب ، وتحصل كل مهمة على رقمها الخاص. من المهم اتباع نفس الترتيب كما في مجموعة المهامArr ، مثل يتم استدعاء المعالجات وفقًا لرقم المهمة.

void osalInitTasks( void )
{
  uint8 taskID = 0;

  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

  macTaskInit( taskID++ );
  nwk_init( taskID++ );
#if !defined (DISABLE_GREENPOWER_BASIC_PROXY) && (ZG_BUILD_RTR_TYPE)
  gp_Init( taskID++ );
#endif
  Hal_Init( taskID++ );
#if defined( MT_TASK )
  MT_TaskInit( taskID++ );
#endif
  APS_Init( taskID++ );
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_Init( taskID++ );
#endif
  ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_Init( taskID++ );
#endif
  // Added to include TouchLink functionality
#if defined ( INTER_PAN )
  StubAPS_Init( taskID++ );
#endif
// Added to include TouchLink initiator functionality
#if defined( BDB_TL_INITIATOR )
  touchLinkInitiator_Init( taskID++ );
#endif
// Added to include TouchLink target functionality
#if defined ( BDB_TL_TARGET )
  touchLinkTarget_Init( taskID++ );
#endif
  zcl_Init( taskID++ );
  bdb_Init( taskID++ );
  zclDIYRuZRT_Init( taskID );
}

سجل تطبيقنا معالج الوظائف zclDIYRuZRT_event_loop ووظيفة التهيئة zclDIYRuZRT_Init . تتم إضافتها آخر في القائمة.
هذه هي وظيفتان رئيسيتان لتطبيقنا. يتم تنفيذ هذه الوظائف في ملف zcl_DIYRuZRT.c .

zclDIYRuZRT_Init - وظيفة تسجيل المهام.
DIYRuZRT_ENDPOINT - رقم نقطة النهاية الذي ينفذه تطبيقنا.

يتم تنفيذ خطوات التسجيل التي تصف طلبنا بالتسلسل:

  • bdb_RegisterSimpleDescriptor — . SimpleDescriptionFormat_t zclDIYRuZRT_SimpleDesc — , , , . OSAL_DIYRuZRT_data.c
  • zclGeneral_RegisterCmdCallbackszclGeneral_AppCallbacks_t zclDIYRuZRT_CmdCallbacks — , .
  • zcl_registerAttrListzclAttrRec_t zclDIYRuZRT_Attrs — , .
  • zcl_registerForMsg - تسجيل استلام رسائل التحكم.
  • RegisterForKeys - نوقع مهمتنا لاستقبال أحداث النقر على الزر.

/*********************************************************************
 * SIMPLE DESCRIPTOR
 */
// This is the Cluster ID List and should be filled with Application
// specific cluster IDs.
const cId_t zclDIYRuZRT_InClusterList[] =
{
  ZCL_CLUSTER_ID_GEN_BASIC,
  ZCL_CLUSTER_ID_GEN_IDENTIFY,
  
  // DIYRuZRT_TODO: Add application specific Input Clusters Here. 
  //       See zcl.h for Cluster ID definitions
  
};
#define ZCLDIYRuZRT_MAX_INCLUSTERS   (sizeof(zclDIYRuZRT_InClusterList) / sizeof(zclDIYRuZRT_InClusterList[0]))


const cId_t zclDIYRuZRT_OutClusterList[] =
{
  ZCL_CLUSTER_ID_GEN_BASIC,
  
  // DIYRuZRT_TODO: Add application specific Output Clusters Here. 
  //       See zcl.h for Cluster ID definitions
};
#define ZCLDIYRuZRT_MAX_OUTCLUSTERS  (sizeof(zclDIYRuZRT_OutClusterList) / sizeof(zclDIYRuZRT_OutClusterList[0]))


SimpleDescriptionFormat_t zclDIYRuZRT_SimpleDesc =
{
  DIYRuZRT_ENDPOINT,                  //  int Endpoint;
  ZCL_HA_PROFILE_ID,                     //  uint16 AppProfId;
  // DIYRuZRT_TODO: Replace ZCL_HA_DEVICEID_ON_OFF_LIGHT with application specific device ID
  ZCL_HA_DEVICEID_ON_OFF_LIGHT,          //  uint16 AppDeviceId; 
  DIYRuZRT_DEVICE_VERSION,            //  int   AppDevVer:4;
  DIYRuZRT_FLAGS,                     //  int   AppFlags:4;
  ZCLDIYRuZRT_MAX_INCLUSTERS,         //  byte  AppNumInClusters;
  (cId_t *)zclDIYRuZRT_InClusterList, //  byte *pAppInClusterList;
  ZCLDIYRuZRT_MAX_OUTCLUSTERS,        //  byte  AppNumInClusters;
  (cId_t *)zclDIYRuZRT_OutClusterList //  byte *pAppInClusterList;
};


zclDIYRuZRT_event_loop - وظيفة معالجات الأحداث لتطبيقنا .

أولاً ، تتم معالجة أحداث النظام في حلقة:

  • ZCL_INCOMING_MSG - تتم معالجة أوامر التحكم في الجهاز في zclDIYRuZRT_ProcessIncomingMsg.
  • KEY_CHANGE - تتم معالجة أحداث النقر على الزر في zclDIYRuZRT_HandleKeys .
  • ZDO_STATE_CHANGE - أحداث تغيير حالة الشبكة.

  if ( events & SYS_EVENT_MSG )
  {
    while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( zclDIYRuZRT_TaskID )) )
    {
      switch ( MSGpkt->hdr.event )
      {
        case ZCL_INCOMING_MSG:
          // Incoming ZCL Foundation command/response messages
          zclDIYRuZRT_ProcessIncomingMsg( (zclIncomingMsg_t *)MSGpkt );
          break;

        case KEY_CHANGE:
          zclDIYRuZRT_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
          break;

        case ZDO_STATE_CHANGE:
          zclDIYRuZRT_NwkState = (devStates_t)(MSGpkt->hdr.status);

          // now on the network
          if ( (zclDIYRuZRT_NwkState == DEV_ZB_COORD) ||
               (zclDIYRuZRT_NwkState == DEV_ROUTER)   ||
               (zclDIYRuZRT_NwkState == DEV_END_DEVICE) )
          {
            giGenAppScreenMode = GENERIC_MAINMODE;
            zclDIYRuZRT_LcdDisplayUpdate();
          }
          break;

        default:
          break;
      }

      // Release the memory
      osal_msg_deallocate( (uint8 *)MSGpkt );
    }

التالي هو معالجة الحدث الخاص DIYRuZRT_EVT_1 ، الذي يحول حالة LED HAL_LED_2 ويبدأ المؤقت لمدة 500 متر مع نفس الحدث. يؤدي هذا إلى بدء وميض LED HAL_LED_2 .

  if ( events & DIYRuZRT_EVT_1 )
  {
    // toggle LED 2 state, start another timer for 500ms
    HalLedSet ( HAL_LED_2, HAL_LED_MODE_TOGGLE );
    osal_start_timerEx( zclDIYRuZRT_TaskID, DIYRuZRT_EVT_1, 500 );
    
    return ( events ^ DIYRuZRT_EVT_1 );
  }

والحقيقة هي أنه عند بدء تشغيل البرنامج الثابت ، يقع حدث HAL_KEY_SW_1 ويتم فيه تهيئة المؤقت وحدث DIYRuZRT_EVT_1 . وإذا ضغطت على زر S2 ، فسيتوقف الوميض (يظل مؤشر LED الخاص بي قيد التشغيل). الضغط عليه مرة أخرى سيبدأ في الوميض.

5. هال: المصابيح والأزرار


"انتظر ، أي LED والأزرار؟" ، تسأل. في البداية ، تركز جميع الأمثلة في Z-stack على أنواع مختلفة من لوحات التصحيح لسلسلة SmartRF05 EB:



لدي لوحة تصحيح مختلفة قليلاً ووحدة مع شريحة.

يوجد زران (+ إعادة ضبط) و 3 مصابيح LED (+ مؤشر الطاقة) على اللوحة. فيما يلي وميض واحد منهم (D2) عندما تعمل البرامج الثابتة بشكل صحيح.

بعد استدعاء جهات الاتصال ، نحدد مراسلات الدبابيس والثنائيات والأزرار:

  • D1 - P10
  • D2 - P11
  • D3 - P14
  • S2 - P20
  • S1 - P01

لذلك ، HAL هي طبقة تجريد الأجهزة ، وهي طريقة للتجريد من تنفيذ المعدات. يستخدم رمز التطبيق وحدات الماكرو والوظائف التي تعمل مع التجريدات مثل زر 1 أو LED 2 ، ويتم تعيين المراسلات الخاصة بالتجريدات والمعدات بشكل منفصل.

دعنا نرى ما هو نوع HAL_LED_2 وكيفية فهم أي دبوس معلق عليه. من خلال

البحث ، نجد ملف hal_led.h ، حيث يتم وصف هذه الثوابت والدالة HalLedSet ، حيث يتم إرسال رقم LED ووضعه . في الداخل ، يتم استدعاء وظيفة HalLedOnOff لتشغيل وإيقاف تشغيل LED ، والذي بدوره ينفذ إما HAL_TURN_ON_LED2 أوHAL_TURN_OFF_LED2 .

HAL_TURN_ON_LED2 و HAL_TURN_OFF_LED2 هما وحدات الماكرو الموضحة في hal_board_cfg.h . تتغير وحدات الماكرو وفقًا لتكوين الأجهزة.
في حالتي:

#define HAL_TURN_OFF_LED2()       st( LED2_SBIT = LED2_POLARITY (0); )
#define HAL_TURN_ON_LED2()        st( LED2_SBIT = LED2_POLARITY (1); )

أعلى قليلاً في الملف هي مراسلات LED2_SBIT و LED2_POLARITY :

  /* 2 - Red */
  #define LED2_BV           BV(1)
  #define LED2_SBIT         P1_1
  #define LED2_DDR          P1DIR
  #define LED2_POLARITY     ACTIVE_HIGH

هذا يعني أن LED 2 يقع على دبوس P1_1 ومستوى التبديل مرتفع. ولكن ، بناءً على الرمز ، يجب أن يخرج مؤشر LED عند الضغط على الزر ، ولكن يظل معنا. إذا قمنا في هذا الملف hal_board_cfg.h بتغيير:

#define LED2_POLARITY     ACTIVE_HIGH

على ال

#define LED2_POLARITY     ACTIVE_LOW

ثم يخرج LED عند الضغط على زر S2 ، كما يجب أن يكون حسب المنطق.

من أجل عدم تغيير الملفات الشائعة التي لا تتعلق بتطبيقنا ، من الأفضل أن تفعل خلاف ذلك:

  • قم بإنشاء نسخة من ملف hal_board_cfg.h (من مجلد Z-Stack 3.0.2 \ Components \ hal \ target \ CC2530EB \) في المجلد المصدر وقم بتسميته ، على سبيل المثال ، hal_board_cfg_DIYRuZRT.h
  • دعنا نجعل نسختنا من الملف هي النسخة الأولى (وبالتالي استبعاد اتصال الملف المشترك). قم بإنشاء ملف preinclude.h في مجلد المصدر الخاص بنا واكتب السطر هناك:

#include "hal_board_cfg_DIYRuZRT.h"

  • تشير إلى أن اتصال هذا الملف هو الأول - في إعدادات المشروع:

$PROJ_DIR$\..\Source\preinclude.h



الآن يمكننا تغيير معلمات المعدات في ملف hal_board_cfg_DIYRuZRT.h وفي ملف preinclude.h دون الحاجة إلى تحرير الملفات المشتركة.

لقد قمت بنقل توجيهات المترجم إلى نفس الملف preinclude.h وحذفها في خيارات المترجم:

#define SECURE 1
#define TC_LINKKEY_JOIN
#define NV_INIT
#define NV_RESTORE
#define xZTOOL_P1
#define xMT_TASK
#define xMT_APP_FUNC
#define xMT_SYS_FUNC
#define xMT_ZDO_FUNC
#define xMT_ZDO_MGMT
#define xMT_APP_CNF_FUNC
#define LEGACY_LCD_DEBUG
#define LCD_SUPPORTED DEBUG
#define MULTICAST_ENABLED FALSE
#define ZCL_READ
#define ZCL_WRITE
#define ZCL_BASIC
#define ZCL_IDENTIFY
#define ZCL_SCENES
#define ZCL_GROUPS

في نفس الملف hal_board_cfg_DIYRuZRT.h نجد وصف زر S1 وجويستيك سنتر برس:

/* S1 */
#define PUSH1_BV          BV(1)
#define PUSH1_SBIT        P0_1

/* Joystick Center Press */
#define PUSH2_BV          BV(0)
#define PUSH2_SBIT        P2_0
#define PUSH2_POLARITY    ACTIVE_HIGH

هذا يتوافق مع دبابيس الأزرار الموجودة على اللوحة.

لنلق نظرة على تهيئة الأجهزة - الماكرو HAL_BOARD_INIT في نفس الملف. بشكل افتراضي ، يتم تمكين توجيه HAL_BOARD_CC2530EB_REV17 ، لذلك ننظر إلى خيار الماكرو المقابل.

/* ----------- Board Initialization ---------- */
#if defined (HAL_BOARD_CC2530EB_REV17) && !defined (HAL_PA_LNA) && \
    !defined (HAL_PA_LNA_CC2590) && !defined (HAL_PA_LNA_SE2431L) && \
    !defined (HAL_PA_LNA_CC2592)

#define HAL_BOARD_INIT()                                         \
{                                                                \
  uint16 i;                                                      \
                                                                 \
  SLEEPCMD &= ~OSC_PD;                       /* turn on 16MHz RC and 32MHz XOSC */                \
  while (!(SLEEPSTA & XOSC_STB));            /* wait for 32MHz XOSC stable */                     \
  asm("NOP");                                /* chip bug workaround */                            \
  for (i=0; i<504; i++) asm("NOP");          /* Require 63us delay for all revs */                \
  CLKCONCMD = (CLKCONCMD_32MHZ | OSC_32KHZ); /* Select 32MHz XOSC and the source for 32K clock */ \
  while (CLKCONSTA != (CLKCONCMD_32MHZ | OSC_32KHZ)); /* Wait for the change to be effective */   \
  SLEEPCMD |= OSC_PD;                        /* turn off 16MHz RC */                              \
                                                                 \
  /* Turn on cache prefetch mode */                              \
  PREFETCH_ENABLE();                                             \
                                                                 \
  HAL_TURN_OFF_LED1();                                           \
  LED1_DDR |= LED1_BV;                                           \
  HAL_TURN_OFF_LED2();                                           \
  LED2_DDR |= LED2_BV;                                           \
  HAL_TURN_OFF_LED3();                                           \
  LED3_DDR |= LED3_BV;                                           \
  HAL_TURN_OFF_LED4();                                           \
  LED4_SET_DIR();                                                \
                                                                 \
  /* configure tristates */                                      \
  P0INP |= PUSH2_BV;                                             \
}

في هذا الماكرو يتم تهيئة أوضاع المعالج وسجلاته.
بدلاً من LED2_DDR وغيرها سيتم استبدالها P1DIR - تسجيل هذا المنفذ P1 ، دبابيس وضع عملية الشحن (الإدخال أو الإخراج). وفقًا لذلك ، يتم ضبط LED2_BV على 1 لكل بت من الطرف المطابق (في حالتنا ، 1 بت ، والذي يتوافق مع P1_1 pin ):



يتم وصف أوضاع التسجيل والمعالج في وثائق

"دليل مستخدم cc253x".

ولكن لا يمكن رؤية كيفية تكوين الأزرار في أي مكان. تتم معالجة الأزرار بشكل مشابه ، ولكن في ملف آخر - hal_key.c . يحدد معلمات الأزرار والوظائف HalKeyInit ، HalKeyConfig، HalKeyRead ، HalKeyPoll . هذه الوظائف مسؤولة عن تهيئة النظام الفرعي للعمل مع الأزرار وقيم القراءة.

بشكل افتراضي ، يتم تنفيذ معالجة الزر على جهاز ضبط الوقت كل 100 مللي ثانية. يتم تعيين دبوس P2_0 للتكوين الحالي إلى ذراع التحكم ويتم قراءة حالته الحالية بنقرة - وبالتالي ، يبدأ موقت وميض LED.

6. نقوم بتكوين الجهاز لأنفسنا


التغيير في الملف zcl_DIYRuZRT.h :

  • DIYRuZRT_ENDPOINT في 1

في الملف OSAL_DIYRuZRT_data.c :

  • DIYRuZRT_DEVICE_VERSION في 1
  • zclDIYRuZRT_ManufacturerName على {6، 'D'، 'I'، 'Y'، 'R'، 'u'، 'Z'}
  • zclDIYRuZRT_ModelId في {9، 'D'، 'I'، 'Y'، 'R'، 'u'، 'Z'، '_'، 'R'، 'T'}
  • zclDIYRuZRT_DateCode على {8، '2'، '0'، '2'، '0'، '0'، '4'، '0'، '5'}

لكي يتمكن الجهاز من الاتصال بالشبكة على أي قناة (فقط 11 افتراضيًا ، محدد في توجيه DEFAULT_CHANLIST في ملف Tools \ f8wConfig.cfg ) ، يجب عليك تحديد هذه الميزة في ملف preinclude.h عن طريق تغيير قيمة التوجيه.
نضيف أيضًا توجيه الترجمة DISABLE_GREENPOWER_BASIC_PROXY بحيث لا يتم إنشاء نقطة نهاية GREENPOWER لجهازنا.

قم أيضًا بإيقاف تشغيل الدعم غير الضروري لشاشة LCD.

//#define LCD_SUPPORTED DEBUG
#define DISABLE_GREENPOWER_BASIC_PROXY
#define DEFAULT_CHANLIST 0x07FFF800  // ALL Channels

لكي يحاول جهازنا الاتصال بالشبكة تلقائيًا ، سنضيف الاتصال بالشبكة في رمز الوظيفة zclDIYRuZRT_Init .

bdb_StartCommissioning(BDB_COMMISSIONING_MODE_NWK_STEERING |
                         BDB_COMMISSIONING_MODE_FINDING_BINDING);

بعد ذلك ، قم بتنفيذ Build ، واملأ البرنامج الثابت في الشريحة وابدأ في الاقتران بالمنسق. أتحقق من تشغيل شبكة Zigbee في ioBroker.zigbee ، هذه هي الطريقة التي يبدو بها الجهاز المتصل الجديد:



رائع ، اتضح أن توصيل الجهاز!

7. نحن نعقد تشغيل الجهاز


الآن دعنا نحاول تكييف الوظيفة قليلاً:

  • تتم عملية توصيل الجهاز بالشبكة بالضغط لفترة طويلة على الزر.
  • إذا كان الجهاز متصلاً بالشبكة بالفعل ، فإن الضغط عليه لفترة طويلة يعرضه من الشبكة.
  • الضغط القصير - يبدل حالة LED.
  • يجب الحفاظ على حالة LED عند بدء تشغيل الجهاز بعد انقطاع التيار الكهربائي.

لإعداد معالجة الأزرار الخاصة بي ، قمت بإنشاء وظيفة DIYRuZRT_HalKeyInit مشابهة لتلك الموجودة في وحدة hal_key.c ، ولكن حصريًا لمجموعة الأزرار الخاصة بي.

//    ()
void DIYRuZRT_HalKeyInit( void )
{
  /*      0 */
  halKeySavedKeys = 0;

  PUSH1_SEL &= ~(PUSH1_BV); /*    - GPIO */
  PUSH1_DIR &= ~(PUSH1_BV); /*    -  */
  
  PUSH1_ICTL &= ~(PUSH1_ICTLBIT); /*      */
  PUSH1_IEN &= ~(PUSH1_IENBIT);   /*     */
  
  PUSH2_SEL &= ~(PUSH2_BV); /* Set pin function to GPIO */
  PUSH2_DIR &= ~(PUSH2_BV); /* Set pin direction to Input */
  
  PUSH2_ICTL &= ~(PUSH2_ICTLBIT); /* don't generate interrupt */
  PUSH2_IEN &= ~(PUSH2_IENBIT);   /* Clear interrupt enable bit */
}

تمت إضافة استدعاء هذه الوظيفة إلى ملف HAL_BOARD_INIT الماكرو hal_board_cfg_DIYRuZRT.h . لتجنب التعارض ، قم بتعطيل hal_key المدمج في نفس الملف hal_board_cfg_DIYRuZRT.h :

#define HAL_KEY FALSE

لان تم تعطيل قارئ الأزرار القياسي ، وسوف نقوم بذلك بأنفسنا.
في وظيفة التهيئة zclDIYRuZRT_Init ، نبدأ المؤقت لقراءة حالات الأزرار. سيقوم المؤقت بإنشاء حدث HAL_KEY_EVENT .

osal_start_reload_timer( zclDIYRuZRT_TaskID, HAL_KEY_EVENT, 100);

وفي حلقة الحدث ، سنتعامل مع حدث HAL_KEY_EVENT عن طريق استدعاء دالة DIYRuZRT_HalKeyPoll :

//  
void DIYRuZRT_HalKeyPoll (void)
{
  uint8 keys = 0;

  //   1 ?
  if (HAL_PUSH_BUTTON1())
  {
    keys |= HAL_KEY_SW_1;
  }
  
  //   2 ?
  if (HAL_PUSH_BUTTON2())
  {
    keys |= HAL_KEY_SW_2;
  }
  
  if (keys == halKeySavedKeys)
  {
    //  -  
    return;
  }
  //        . 
  halKeySavedKeys = keys;

  //     
  OnBoard_SendKeys(keys, HAL_KEY_STATE_NORMAL);
}

يسمح لنا حفظ حالة الأزرار في متغير halKeySavedKeys بتحديد لحظة التغيير - الضغط على الأزرار وتحريرها .

عند النقر على الزر ، ابدأ تشغيل المؤقت لمدة 5 ثوانٍ. إذا انطفئ هذا المؤقت ، فسيتم إنشاء حدث DIYRuZRT_EVT_LONG . إذا تم تحرير الزر ، تتم إعادة ضبط المؤقت. على أي حال ، إذا قمت بالضغط على الزر ، نقوم بتبديل حالة LED.

//   
static void zclDIYRuZRT_HandleKeys( byte shift, byte keys )
{
  if ( keys & HAL_KEY_SW_1 )
  {
    //       - 5 
    osal_start_timerEx(zclDIYRuZRT_TaskID, DIYRuZRT_EVT_LONG, 5000);
    //  
    updateRelay(RELAY_STATE == 0);
  }
  else
  {
    //     
    osal_stop_timerEx(zclDIYRuZRT_TaskID, DIYRuZRT_EVT_LONG);
  }
}

الآن ، عند معالجة حدث مطول ، ننتبه إلى الحالة الحالية للشبكة من خلال سمة البنية bdbAttributes.bdbNodeIsOnANetwork

  //  DIYRuZRT_EVT_LONG
  if ( events & DIYRuZRT_EVT_LONG )
  {
    //    
    //      ?
    if ( bdbAttributes.bdbNodeIsOnANetwork )
    {
      //  
      zclDIYRuZRT_LeaveNetwork();
    }
    else 
    {
      //    
      bdb_StartCommissioning(
        BDB_COMMISSIONING_MODE_NWK_FORMATION | 
        BDB_COMMISSIONING_MODE_NWK_STEERING | 
        BDB_COMMISSIONING_MODE_FINDING_BINDING | 
        BDB_COMMISSIONING_MODE_INITIATOR_TL
      );
      //  ,   
      osal_start_timerEx(zclDIYRuZRT_TaskID, DIYRuZRT_EVT_BLINK, 500);
    }
    
    return ( events ^ DIYRuZRT_EVT_LONG );
  }

نذهب أبعد من ذلك. سيتم حفظ حالة LED في متغير ، والذي سنحفظ قيمته في ذاكرة NV. عندما يبدأ الجهاز ، سنقرأ القيمة من الذاكرة إلى متغير.

  //  NVM   RELAY STATE
  if ( SUCCESS == osal_nv_item_init( NV_DIYRuZRT_RELAY_STATE_ID, 1, &RELAY_STATE ) ) {
    //   RELAY STATE  
    osal_nv_read( NV_DIYRuZRT_RELAY_STATE_ID, 0, 1, &RELAY_STATE );
  }
  //   
  applyRelay();

//   
void updateRelay ( bool value )
{
  if (value) {
    RELAY_STATE = 1;
  } else {
    RELAY_STATE = 0;
  }
  //   
  osal_nv_write(NV_DIYRuZRT_RELAY_STATE_ID, 0, 1, &RELAY_STATE);
  //   
  applyRelay();
}
  
//   
void applyRelay ( void )
{
  //  
  if (RELAY_STATE == 0) {
    //    1
    HalLedSet ( HAL_LED_1, HAL_LED_MODE_OFF );
  } else {
    //    1
    HalLedSet ( HAL_LED_1, HAL_LED_MODE_ON );
  }
}

8. الآن سنتعامل مع زيجبي


حتى الآن ، قمنا بفرز الأجهزة - باستخدام الزر الذي نتحكم في LED. الآن نقوم بتنفيذ نفس الشيء من خلال Zigbee.

للتحكم في التتابع ، يكفي لنا استخدام نقطة النهاية الوحيدة وتنفيذ مجموعة GenOnOff . قرأنا مواصفات مكتبة Zigbee Cluster Library لمجموعة GenOnOff :





يكفي تنفيذ السمة OnOff والأوامر On و Off و Toggle.
أولاً ، أضف التوجيه إلى preinclude.h :

#define ZCL_ON_OFF

في وصف سماتنا zclDIYRuZRT_Attrs نضيف سمات الكتلة الجديدة:

  // ***  On/Off  ***
  {
    ZCL_CLUSTER_ID_GEN_ON_OFF,
    { // 
      ATTRID_ON_OFF,
      ZCL_DATATYPE_BOOLEAN,
      ACCESS_CONTROL_READ,
      (void *)&RELAY_STATE
    }
  },
  {
    ZCL_CLUSTER_ID_GEN_ON_OFF,
    {  //  On/Off 
      ATTRID_CLUSTER_REVISION,
      ZCL_DATATYPE_UINT16,
      ACCESS_CONTROL_READ | ACCESS_CLIENT,
      (void *)&zclDIYRuZRT_clusterRevision_all
    }
  },

كما نضيف المجموعة إلى قائمة مجموعات نقاط النهاية الواردة المدعومة zclDIYRuZRT_InClusterList .

لتنفيذ أوامر التحكم ، أضف معالجًا إلى جدول zclDIYRuZRT_CmdCallbacks .

/*********************************************************************
 *    ZCL 
 */
static zclGeneral_AppCallbacks_t zclDIYRuZRT_CmdCallbacks =
{
  zclDIYRuZRT_BasicResetCB,               // Basic Cluster Reset command
  NULL,                                   // Identify Trigger Effect command
  zclDIYRuZRT_OnOffCB,                    // On/Off cluster commands
  NULL,                                   // On/Off cluster enhanced command Off with Effect
  NULL,                                   // On/Off cluster enhanced command On with Recall Global Scene
  NULL,                                   // On/Off cluster enhanced command On with Timed Off
#ifdef ZCL_LEVEL_CTRL
  NULL,                                   // Level Control Move to Level command
  NULL,                                   // Level Control Move command
  NULL,                                   // Level Control Step command
  NULL,                                   // Level Control Stop command
#endif

وننفذها:
//    OnOff
static void zclDIYRuZRT_OnOffCB(uint8 cmd)
{
  //  ,   
  //    
  afIncomingMSGPacket_t *pPtr = zcl_getRawAFMsg();
  zclDIYRuZRT_DstAddr.addr.shortAddr = pPtr->srcAddr.addr.shortAddr;
  
  // 
  if (cmd == COMMAND_ON) {
    updateRelay(TRUE);
  }
  // 
  else if (cmd == COMMAND_OFF) {
    updateRelay(FALSE);
  }
  // 
  else if (cmd == COMMAND_TOGGLE) {
    updateRelay(RELAY_STATE == 0);
  }
}

رائع ، الآن يمكن تبديل التتابع بالأوامر.





لكن هذا لا يكفى. الآن يجب علينا أيضًا إبلاغ المنسق عن الحالة الحالية لمصباح LED ، إذا قمنا بتبديله باستخدام الزر.

مرة أخرى ، أضف التوجيه:

#define ZCL_REPORTING_DEVICE

الآن قم بإنشاء دالة zclDIYRuZRT_ReportOnOff التي ترسل رسالة حالة. سوف نطلق عليه عندما يتم تبديل LED وعندما يبدأ الجهاز.

//    
void zclDIYRuZRT_ReportOnOff(void) {
  const uint8 NUM_ATTRIBUTES = 1;

  zclReportCmd_t *pReportCmd;

  pReportCmd = osal_mem_alloc(sizeof(zclReportCmd_t) +
                              (NUM_ATTRIBUTES * sizeof(zclReport_t)));
  if (pReportCmd != NULL) {
    pReportCmd->numAttr = NUM_ATTRIBUTES;

    pReportCmd->attrList[0].attrID = ATTRID_ON_OFF;
    pReportCmd->attrList[0].dataType = ZCL_DATATYPE_BOOLEAN;
    pReportCmd->attrList[0].attrData = (void *)(&RELAY_STATE);

    zclDIYRuZRT_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
    zclDIYRuZRT_DstAddr.addr.shortAddr = 0;
    zclDIYRuZRT_DstAddr.endPoint = 1;

    zcl_SendReportCmd(DIYRuZRT_ENDPOINT, &zclDIYRuZRT_DstAddr,
                      ZCL_CLUSTER_ID_GEN_ON_OFF, pReportCmd,
                      ZCL_FRAME_CLIENT_SERVER_DIR, false, SeqNum++);
  }

  osal_mem_free(pReportCmd);
}

الآن في السجلات نرى رسائل حول تغيير في حالة LED.

9. قم بتوصيل مستشعر درجة الحرارة ds18b20


المستشعر متصل بأي دبوس مجاني (في حالتي ، اضبط P2_1 ).

أضف رمز اقتراع المستشعر إلى التطبيق. سنجري مقابلات منتظمة - مرة واحدة في الدقيقة.
فور الاستطلاع ، سيُعلمك منسق الشبكة بالقيمة الحالية.

اقرأ مواصفات ZCL لإرسال البيانات من مستشعرات درجة الحرارة. نحتاج إلى
قياس درجة حرارة الكتلة .



نرى أننا بحاجة إلى تنفيذ 3 سمات ، تمثل إحداها قيمة درجة الحرارة مضروبة في 100.

وهنا نضيف السمات عن طريق القياس مع مجموعة GenOnOff . سيتم إبلاغ المنسق بحدث DIYRuZRT_REPORTING_EVT ، والذي نخطط له في البداية مرة واحدة في الدقيقة. في معالج الحدث ، سنتصلzclDIYRuZRT_ReportTemp ، والتي ستقرأ درجة حرارة المستشعر وترسل رسالة.

//   
void zclDIYRuZRT_ReportTemp( void )
{
  //  
  zclDIYRuZRT_MeasuredValue = readTemperature();
  
  const uint8 NUM_ATTRIBUTES = 1;

  zclReportCmd_t *pReportCmd;

  pReportCmd = osal_mem_alloc(sizeof(zclReportCmd_t) +
                              (NUM_ATTRIBUTES * sizeof(zclReport_t)));
  if (pReportCmd != NULL) {
    pReportCmd->numAttr = NUM_ATTRIBUTES;

    pReportCmd->attrList[0].attrID = ATTRID_MS_TEMPERATURE_MEASURED_VALUE;
    pReportCmd->attrList[0].dataType = ZCL_DATATYPE_INT16;
    pReportCmd->attrList[0].attrData = (void *)(&zclDIYRuZRT_MeasuredValue);

    zclDIYRuZRT_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
    zclDIYRuZRT_DstAddr.addr.shortAddr = 0;
    zclDIYRuZRT_DstAddr.endPoint = 1;

    zcl_SendReportCmd(DIYRuZRT_ENDPOINT, &zclDIYRuZRT_DstAddr,
                      ZCL_CLUSTER_ID_MS_TEMPERATURE_MEASUREMENT, pReportCmd,
                      ZCL_FRAME_CLIENT_SERVER_DIR, false, SeqNum++);
  }

  osal_mem_free(pReportCmd);
}

10. صب البرامج الثابتة في الجهاز


لتغيير devboard إلى Sonoff BASICZBR3 ، تحتاج إلى ضبط مطابقة دبابيس وأزرار LED.



إعادة LED 1 على رقم التعريف الشخصي P0_7 للتحكم في التتابع. التضمين يتم بواسطة مستوى عالٍ من ACTIVE_HIGH . نعيد تعليق زر S1 على الدبوس P1_3 ، و LED المعلومات 2 على P1_0 . نترك جهاز استشعار درجة الحرارة على دبوس P2_1 . نجري كل هذه التغييرات في ملف hal_board_cfg_DIYRuZRT.h . لتحديد تكوين ، سنقوم بعمل توجيه منفصل HAL_SONOFF . إذا تم تعيينه ، فسيتم استخدام إعدادات Sonoff BASICZBR3 ، وإلا ل devboard.

#ifdef HAL_SONOFF
  /* 1 - P0_7  */
  #define LED1_BV           BV(7)
  #define LED1_SBIT         P0_7
  #define LED1_DDR          P0DIR
  #define LED1_POLARITY     ACTIVE_HIGH

  /* 2 - P1_0  */
  #define LED2_BV           BV(0)
  #define LED2_SBIT         P1_0
  #define LED2_DDR          P1DIR
  #define LED2_POLARITY     ACTIVE_LOW
#else
  /* 1 - P1_0  */
  #define LED1_BV           BV(0)
  #define LED1_SBIT         P1_0
  #define LED1_DDR          P1DIR
  #define LED1_POLARITY     ACTIVE_LOW

  /* 2 - P1_1  */
  #define LED2_BV           BV(1)
  #define LED2_SBIT         P1_1
  #define LED2_DDR          P1DIR
  #define LED2_POLARITY     ACTIVE_LOW
#endif

كان لا بد من تصحيح معلمة مهمة أخرى - وجود كوارتز "ساعة" ، لأن على لوحة Sonoff BASICZBR3 لا يتم لحامها:

//#define HAL_CLOCK_CRYSTAL
  #define OSC32K_CRYSTAL_INSTALLED FALSE

بدون هذه الخيارات ، لا يبدأ البرنامج الثابت (أو بالأحرى ، ليس دائمًا).

بعد ذلك ، نجمع البرامج الثابتة ونتصل بالبرامج الثابتة.
انتباه!!! افصل مرحل Sonoff BASICZBR3 عن طاقة التيار المتردد قبل أي اتصال وبرامج ثابتة!

نقوم بتوصيل أسلاك Sonoff BASICZBR3 باستخدام CCDebugger ، ونمحو الشريحة ونومض البرامج الثابتة الخاصة بنا.



11. نبدأ الجهاز في zigbee2mqtt و ioBroker.Zigbee


على الرغم من ظهور الجهاز في قائمة الأجهزة المتصلة ويمكننا إدارته عن طريق إرسال الأوامر ، ولكننا بحاجة إلى القيام بذلك بشكل صحيح - مع الصور والحالات.

للحصول على جهاز جديد في ioBroker.Zigbee ، تحتاج إلى تنفيذ خطوتين:

  1. zigbee-herdsman-converters. , zigbee2mqtt.
  2. ioBroker.Zigbee

يمكن إجراء جميع التغييرات أولاً في الملفات المحلية ، ثم إجراء العلاقات العامة في المستودعات المقابلة.

نجد موقع حزمة Zigbee-herdsman -converters في ioBroker المثبت (أو zigbee2mqtt). داخل الحزمة نجد ملف devices.js https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/devices.js

يحتوي هذا الملف على أوصاف لجميع الأجهزة التي يمكن أن يعمل معها ioBroker.zigbee و zigbee2mqtt . نجد فيه مجموعة من أوصاف أجهزة DIYRuZ (بعد 2300 سطر). أضف وصفًا للجهاز الجديد إلى هذه الكتلة:

    {
        zigbeeModel: ['DIYRuZ_RT'],
        model: 'DIYRuZ_RT',
        vendor: 'DIYRuZ',
        description: '',
        supports: 'on/off, temperature',
        fromZigbee: [fz.on_off, fz.temperature],
        toZigbee: [tz.on_off],
    },

في السمة fromZigbee ، نحدد المحولات التي ستعالج الرسائل الواردة من الجهاز. يتم توحيد وظيفتين لدينا. يقوم محول fz.on_off بمعالجة رسالة التشغيل / الإيقاف ، ويعالج درجة حرارة بيانات درجة الحرارة. يوضح رمز هذه المحولات (الموجود في محولات الملفات / fromZigbee.js) كيفية معالجة الرسائل الواردة وأن درجة الحرارة مقسومة على 100.

   on_off: {
        cluster: 'genOnOff',
        type: ['attributeReport', 'readResponse'],
        convert: (model, msg, publish, options, meta) => {
            if (msg.data.hasOwnProperty('onOff')) {
                const property = getProperty('state', msg, model);
                return {[property]: msg.data['onOff'] === 1 ? 'ON' : 'OFF'};
            }
        },
    },

 temperature: {
        cluster: 'msTemperatureMeasurement',
        type: ['attributeReport', 'readResponse'],
        convert: (model, msg, publish, options, meta) => {
            const temperature = parseFloat(msg.data['measuredValue']) / 100.0;
            return {temperature: calibrateAndPrecisionRoundOptions(temperature, options, 'temperature')};
        },
    },

في السمة toZigbee ، نحدد المحولات التي ستعالج أوامرنا على الجهاز. في حالتنا ، هو محول tz.on_off للتبديل التبديلات.

تمت إضافة كل شيء إلى "المحولات". من يستخدم zigbee2mqtt - يمكنك استخدامه بالفعل.

ولا يزال مستخدمو ioBroker يضيفون وصفًا للجهاز إلى ملف ioBroker.zigbee \ lib \ devices.js

    {
        vendor: 'DIYRuZ',
        models: ['DIYRuZ_RT'],
        icon: 'img/DIYRuZ.png',
        states: [
            states.state,
            states.temperature,
        ],
    },

هنا يكفي الإشارة إلى نفس النموذج بالضبط ، ملف به صورة وقائمة بالحالات. في حالتنا ، تكون الولايات قياسية أيضًا: حالة لحالة التتابع ، درجة الحرارة لعرض قيم درجة الحرارة.



12. ماذا بعد ذلك؟


لسوء الحظ ، لم أستطع معرفة جميع الجوانب والميزات التي يوفرها Z-Stack 3.0. على الأرجح لم أقم بتنفيذ نوع من الوظائف بشكل صحيح أو يمكن استخدام بعض الآليات المضمنة في تنفيذه.
لذلك ، يمكن تحسين وتطوير الحل أعلاه. فيما يلي بعض الاتجاهات:

  • لم أتمكن من العثور بسرعة على حلول لإمكانية توصيل أجهزة الأطفال عبر المرحلات. يمكن لأجهزة التوجيه الأخرى تنفيذ الأمر allow_join وتوصيل الأجهزة الجديدة من خلال نفسها ، دون الحاجة إلى إحضار الجهاز الجديد إلى المنسق. يتم تمثيل الجهاز بواسطة جهاز توجيه ، ويتم عرضه بشكل صحيح على خريطة الشبكة ، ولكنه يرفض تنفيذ الأمر "allow_join". بتعبير أدق ، يتم تنفيذ الأمر ، ولكن الأجهزة لا تتصل من خلاله.
  • أيضا ، لم تنفذ الإبلاغ الصحيح. هذه طريقة لتكوين إعلام الحالة عندما يمكنك استخدام الأمر configReport لتحديد قائمة السمات لإرسالها وتكرار الإعلام.
  • العمل مع المجموعات.
  • التعامل مع المقاطعات وتنفيذ أزرار الاقتراع من خلال المقاطعات.

حسنًا ، بالنسبة للأجهزة التالية ، تحتاج إلى التعامل مع أوضاع الطاقة وإنشاء أجهزة "سكون" على البطاريات.
كما هو الحال دائمًا ، بالإضافة إلى التعليقات ، أدعوك إلى مناقشة هذا وغيرها من الأجهزة في دردشة Telegram على Zigbee .

أريد أن أعرب عن امتناني للدعم والمساعدة في تطوير زملائي في دردشة Telegram ومجتمع Zigbee:


المراجع


يتم تضمين الوثائق الرئيسية في Z-Stack 3.0.2 SDK ويتم تثبيتها معه. ولكن سأقدم جزءًا من الروابط هنا:


All Articles