Wir schreiben Firmware fĂĽr TI cc2530 auf Z-Stack 3.0 fĂĽr ZigBee Sonoff BASICZBR3 Relais mit ds18b20 Sensor



Es wird davon ausgegangen, dass der Leser bereits ĂĽber Grundkenntnisse der C-Sprache verfĂĽgt, etwas ĂĽber Zigbee, den cc2530-Chip, Methoden zum Flashen und Verwenden des Cigbee2mqtt weiĂź und auch mit Projekten wie zigbee2mqtt vertraut ist. Wenn nicht, machen Sie sich bereit oder lesen Sie es unter https://myzigbee.ru und https://www.zigbee2mqtt.io/. Der
Artikel wird zuerst ausführlich geschrieben, beschleunigt sich jedoch allmählich und hört nicht bei den Details auf, sondern beschreibt den fertigen Firmware-Code. Wenn jemand nicht an Argumenten interessiert ist, öffnen Sie einfach die Firmware-Quellen und lesen Sie sie.

Der Quellcode der fertigen Firmware

Der Code- und Entwicklungsansatz erhebt keinen Anspruch auf Ideal. "Ich bin kein Zauberer, ich lerne nur."

Zweck


Das Hauptziel ist es, zu verstehen, wie man lange Zeit Firmware für Z-Stack schreibt. Aus diesem Grund habe ich mich entschlossen, eine alternative Firmware für das fertige Gerät zu implementieren (als Beispiel wurde das Sonoff BASICZBR3- Relais ausgewählt ) und den beliebten Temperatursensor ds18b20 anzuschließen.

Außerdem wollte ich Anfängern von ZigBee-Entwicklern ein Beispiel für die Entwicklung einer Firmware für den TI cc2530-Chip auf Z-Stack zeigen.

1. Vorbereitung


Um mit der Entwicklung zu beginnen, mĂĽssen Sie Z-Stack 3.0.2 herunterladen und installieren. Dies ist ein SDK fĂĽr die Entwicklung von Firmware mit Beispielen und Dokumentation.
Sie müssen auch IAR Embeded Workbench für 8051 herunterladen und installieren - dies ist eine Entwicklungsumgebung mit der Fähigkeit, für TI cc2530-Chips zu kompilieren. Die kostenlose Nutzungsdauer beträgt 1 Monat (der Suchende findet jedoch eine Lösung).

Für die Entwicklung und das Debuggen verwende ich CCDebugger - es ermöglicht nicht nur das Flashen von cc2531 / cc2530-Chips, sondern auch das Debuggen der Anwendung in der IAR-Umgebung.



Um die Experimente, das Prototyping und das Debuggen auf dem Devboard und dem entsprechenden cc2530-Modul zu vereinfachen:



2. Erstellen einer neuen Anwendung


Wir erstellen ein neues Projekt basierend auf GenericApp. Dies ist ein Beispiel fĂĽr eine grundlegende Z-Stack-Anwendung. Es befindet sich im Ordner Z-Stack 3.0.2 \ Projects \ zstack \ HomeAutomation \ GenericApp.
Wir kopieren es in der Nähe und benennen es beispielsweise in DIYRuZRT um (nennen wir die Anwendung für unser Gerät).

Im Ordner CC2530DB befinden sich Dateien:

  • GenericApp.ewd - Projekteinstellungen fĂĽr C-SPY
  • GenericApp.ewp - Projektdatei
  • GenericApp.eww - Arbeitsbereich

Benennen Sie die Dateien in DIYRuZRT.eww und DIYRuZRT.ewp um.

In allen Dateien (einschließlich des Quellordners) ändern wir auch alle Verweise auf GenericApp in DIYRuZRT.

Öffnen Sie nun das DIYRuZRT.ewp-Projekt in IAR. Wir wählen die Konfiguration von RouterEB aus und führen Rebuild All durch.



Der RouterEB-Ordner wird im CC2530DB-Ordner erstellt, und die DIYRuZRT.d51-Datei wird im EXE-Ordner angezeigt. Diese Datei eignet sich zum Flashen und Debuggen ĂĽber IAR.

Wenn wir die Firmware jedoch ĂĽber den SmartRF Flash Programmer flashen mĂĽssen, werden wir kleine Ă„nderungen vornehmen. Ă„ndern Sie dazu in den Projekteinstellungen im Abschnitt Link auf der Registerkarte Ausgabe die Einstellungen fĂĽr die Ausgabedatei und das Format:



Danach wird die Firmware-Datei DIYRuZRT.hex im EXE-Ordner erstellt, die zum Flashen mit anderen Tools und auf andere Weise geeignet ist .
Nach dem Hochladen dieser Firmware stellt das Gerät jedoch keine Verbindung zum Netzwerk her. Nun, wir werden verstehen.

3. Ein bisschen Terminologie


Die ZigBee-Terminologie hat folgende Konzepte:

  • Endpunkt - Der Beschreibungspunkt des Endgeräts. Normalerweise in einfachen Geräten ein Endpunkt. Es können mehrere davon in Multifunktionsgeräten sowie in Geräten mit unterschiedlichen Interaktionsprofilen (ein Profil - ein Endpunkt) vorhanden sein.
  • Cluster (Cluster) - Eine Reihe von Attributen und Befehlen, die sich auf eine einzelne Funktion beziehen (Ein / Aus, Dimmen, Temperaturmessungen usw.). Der Cluster gibt die vom Endpunkt realisierten Opportunities an. In einem Endpunkt können Sie mehrere verschiedene Cluster implementieren, jedoch nicht dieselben.
  • Attribut (Attribut) - Ein Merkmal eines Clusters, dessen Wert gelesen oder geschrieben werden kann. Ein Cluster kann viele Attribute haben.
  • Befehl - Eine Kontrollnachricht, die der Cluster verarbeiten kann. Ein Team kann Parameter haben. Dies wird durch eine Funktion implementiert, die ausgefĂĽhrt wird, wenn ein Befehl und Parameter empfangen werden.

Arten von Clustern, Attributen und Befehlen sind in der ZigBee-Cluster-Bibliothek standardisiert. Hersteller können jedoch ihre eigenen Cluster mit ihren eigenen Attributen und Teams verwenden.

Einige erbärmliche Produzenten kümmern sich nicht um die Standards und tun etwas gegen den Standard. Dann muss man sich an sie anpassen. Die Z-Stack-

Terminologie hat auch eigene Konzepte , zum Beispiel:

  • OSAL (Operating System Abstraction Layer) - die Abstraktionsebene des Betriebssystems. Hier arbeiten sie mit Aufgaben (Aufgaben), Nachrichten (Nachrichten), Ereignissen (Ereignissen), Timern (Timern) und anderen Objekten.
  • HAL (Hardware Abstraction Layer) - Ebene der Geräteabstraktion. Hier arbeiten sie mit Tasten (Tasten), LEDs (LEDs), Interrupts (Interrupt) usw.

Die Hardwareschicht bietet eine Isolierung des Programmcodes und der von ihr gesteuerten Geräte. Die Betriebsebene bietet Mechanismen zum Erstellen und Interagieren zwischen Anwendungselementen.

Dies alles erwartet Sie unten und im Prinzip bei der Entwicklung der Firmware.

4. Was haben wir in der Basisanwendung?


Der Anwendungscode befindet sich im Quellordner:

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

OSAL_DIYRuZRT.c - Die Hauptdatei, in der das Array der Task-Handler (Task)  pTaskEventHandlerFn taskArr gefĂĽllt und die Initialisierungsfunktion osalInitTasks implementiert ist .

Alle anderen Dateien werden benötigt, um diese Initialisierer und Handler zu implementieren.

Die Liste der Task-Handler pTaskEventHandlerFn-TasksAr r enthält Funktionsreferenzen. Einige Aufgaben werden durch die entsprechenden Kompilierungsanweisungen verbunden / getrennt.

Sie können Kompilierungsanweisungen in den Compileroptionen für definierte Symbole anzeigen und konfigurieren:



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 ist die Anwendungsstartfunktion, die von der Anwendung ausgefĂĽhrte Aufgaben registriert.

Die Registrierung der Aufgaben erfolgt in der richtigen Reihenfolge, und jede Aufgabe erhält eine eigene Nummer. Es ist wichtig , in der gleichen Reihenfolge wie in der folgen tasksArr Array , wie Handler werden entsprechend der Aufgabennummer aufgerufen.

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 );
}

Unsere Anwendung hat den Funktionshandler zclDIYRuZRT_event_loop und die Initialisierungsfunktion zclDIYRuZRT_Init registriert . Sie werden zuletzt in der Liste hinzugefĂĽgt.
Dies sind die beiden Hauptfunktionen unserer Anwendung. Die Implementierung dieser Funktionen erfolgt in der Datei zcl_DIYRuZRT.c .

zclDIYRuZRT_Init - Aufgabenregistrierungsfunktion.
DIYRuZRT_ENDPOINT - die von unserer Anwendung implementierte Endpunktnummer.

Die Registrierungsschritte, die unsere Anwendung beschreiben, werden nacheinander ausgefĂĽhrt:

  • bdb_RegisterSimpleDescriptor — . SimpleDescriptionFormat_t zclDIYRuZRT_SimpleDesc — , , , . OSAL_DIYRuZRT_data.c
  • zclGeneral_RegisterCmdCallbacks — zclGeneral_AppCallbacks_t zclDIYRuZRT_CmdCallbacks — , .
  • zcl_registerAttrList — zclAttrRec_t zclDIYRuZRT_Attrs — , .
  • zcl_registerForMsg - Registriert den Empfang von Kontrollnachrichten.
  • RegisterForKeys - Wir unterschreiben unsere Aufgabe fĂĽr den Empfang von Schaltflächenklickereignissen.

/*********************************************************************
 * 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 - Funktion der Ereignishandler unserer Anwendung.

Zunächst werden die Systemereignisse in einer Schleife verarbeitet:

  • ZCL_INCOMING_MSG - Gerätesteuerungsbefehle werden in zclDIYRuZRT_ProcessIncomingMsg verarbeitet.
  • KEY_CHANGE - Schaltflächenklickereignisse werden in zclDIYRuZRT_HandleKeys verarbeitet .
  • ZDO_STATE_CHANGE - Ereignisse zur Ă„nderung des Netzwerkstatus.

  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 );
    }

Als nächstes folgt die Verarbeitung des Sonderereignisses DIYRuZRT_EVT_1 , das den Status der LED HAL_LED_2 umschaltet und den Timer für 500 m mit demselben Ereignis startet. Dies startet das Blinken der 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 );
  }

Tatsache ist, dass beim Start der Firmware das Ereignis HAL_KEY_SW_1 auftritt und darin der Timer und das Ereignis DIYRuZRT_EVT_1 initialisiert werden . Und wenn Sie die S2-Taste drücken, hört das Blinken auf (meine LED bleibt an). Durch erneutes Drücken beginnt es zu blinken.

5. HAL: LEDs und Tasten


"Warten Sie, welche LED und Tasten?", Fragen Sie. Zunächst konzentrieren sich alle Beispiele in Z-Stack auf verschiedene Arten von Debug-Boards der SmartRF05 EB-Serie:



Ich habe ein etwas anderes Debug-Board und ein Modul mit einem Chip.

Auf der Platine befinden sich 2 Tasten (+ Reset) und 3 LEDs (+ Stromanzeige). Hier blinkt einer von ihnen (D2), wenn die Firmware ordnungsgemäß funktioniert.

Nachdem wir die Kontakte angerufen haben, bestimmen wir die Entsprechung von Pins, Dioden und Tasten:

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

HAL ist also eine Hardware-Abstraktionsschicht , eine Möglichkeit, von der Implementierung von Geräten zu abstrahieren. Der Anwendungscode verwendet Makros und Funktionen, die mit Abstraktionen wie Button 1 oder LED 2 arbeiten , und die spezifische Entsprechung von Abstraktionen und Geräten wird separat festgelegt.

Mal sehen, was für eine Art von HAL_LED_2 ist und wie man versteht, an welchem ​​Pin es hängt. Bei der

Suche finden wir die Datei hal_led.h , in der diese Konstanten und die HalLedSet- Funktion beschrieben werden , in der die LED-Nummer und der LED-Modus ĂĽbertragen werden. Im Inneren wird die HalLedOnOff- Funktion aufgerufen , um die LED ein- und auszuschalten, die wiederum entweder HAL_TURN_ON_LED2 oder ausfĂĽhrtHAL_TURN_OFF_LED2 .

HAL_TURN_ON_LED2 und HAL_TURN_OFF_LED2 sind die in hal_board_cfg.h beschriebenen Makros . Makros ändern sich je nach Hardwarekonfiguration.
In meinem Fall:

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

Etwas höher in der Datei befinden sich die Entsprechungen von LED2_SBIT und LED2_POLARITY :

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

Dies bedeutet, dass sich LED 2 an Pin P1_1 befindet und der Schaltpegel hoch ist. Nach dem Code zu urteilen, sollte die LED erlöschen, wenn die Taste gedrückt wird, aber bei uns bleibt sie an. Wenn in dieser Datei hal_board_cfg.h ändern wir :

#define LED2_POLARITY     ACTIVE_HIGH

auf der

#define LED2_POLARITY     ACTIVE_LOW

dann erlischt jetzt die LED, wenn Sie die S2-Taste drĂĽcken, wie es logisch sein sollte.

Um allgemeine Dateien, die nicht mit unserer Anwendung zusammenhängen, nicht zu ändern, ist es besser, Folgendes zu tun:

  • Erstellen Sie eine Kopie der Datei hal_board_cfg.h (aus dem Ordner Z-Stack 3.0.2 \ Components \ hal \ target \ CC2530EB \) in unserem Quellordner und nennen Sie sie beispielsweise hal_board_cfg_DIYRuZRT.h
  • Lassen Sie uns unsere Kopie der Datei zur allerersten machen (wodurch die Verbindung der freigegebenen Datei ausgeschlossen wird). Erstellen Sie eine preinclude.h- Datei in unserem Quellordner und schreiben Sie die folgende Zeile:

#include "hal_board_cfg_DIYRuZRT.h"

  • Geben Sie an, dass die Verbindung dieser Datei die allererste ist - in den Projekteinstellungen:

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



Jetzt können wir die Geräteparameter in unserer Datei hal_board_cfg_DIYRuZRT.h und in der Datei preinclude.h ändern , ohne freigegebene Dateien bearbeiten zu müssen.

Ich habe die Compiler-Direktiven in dieselbe preinclude.h-Datei übertragen und sie in den Compiler-Optionen gelöscht:

#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

In derselben Datei hal_board_cfg_DIYRuZRT.h finden wir die Beschreibung der S1-Taste und des Joystick Center Press:

/* 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

Dies entspricht den Pin-Pins auf der Platine.

Schauen wir uns die Hardware-Initialisierung an - das Makro HAL_BOARD_INIT in derselben Datei. Standardmäßig ist die Direktive HAL_BOARD_CC2530EB_REV17 aktiviert , daher sehen wir uns die entsprechende Makrovariante an.

/* ----------- 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;                                             \
}

In diesem Makro werden die Modi und Register des Prozessors initialisiert.
Stattdessen werden LED2_DDR und andere durch P1DIR ersetzt. Registrieren Sie diesen Port P1 und laden Sie die Betriebsmodus-Pins (Eingang oder Ausgang). Dementsprechend setzt LED2_BV 1 pro Bit des entsprechenden Pins (in unserem Fall 1 Bit, was P1_1- Pin entspricht ):



Register und Prozessormodi sind in der Dokumentation des

„cc253x-Benutzerhandbuchs“ beschrieben.

Es ist jedoch nirgends sichtbar, wie die Tasten konfiguriert sind. Schaltflächen werden ähnlich verarbeitet, jedoch in einer anderen Datei - hal_key.c . Es definiert die Parameter der Schaltflächen und Funktionen HalKeyInit , HalKeyConfig, HalKeyRead , HalKeyPoll . Diese Funktionen sind für die Initialisierung des Subsystems für die Arbeit mit Schaltflächen und das Lesen von Werten verantwortlich.

Standardmäßig wird die Tastenverarbeitung alle 100 ms auf einem Timer ausgeführt. Pin P2_0 für die aktuelle Konfiguration wird dem Joystick zugewiesen und sein aktueller Status wird als Klick gelesen - daher startet der LED-Blink-Timer.

6. Wir konfigurieren das Gerät für uns


Ă„nderung in der Datei zcl_DIYRuZRT.h :

  • DIYRuZRT_ENDPOINT am 1

in der Datei OSAL_DIYRuZRT_data.c :

  • DIYRuZRT_DEVICE_VERSION bei 1
  • zclDIYRuZRT_ManufacturerName auf {6, 'D', 'I', 'Y', 'R', 'u', 'Z'}
  • zclDIYRuZRT_ModelId auf {9, 'D', 'I', 'Y', 'R', 'u', 'Z', '_', 'R', 'T'}
  • zclDIYRuZRT_DateCode auf {8, '2', '0', '2', '0', '0', '4', '0', '5'}

Damit das Gerät auf einem beliebigen Kanal eine Verbindung zum Netzwerk herstellen kann (standardmäßig nur 11, angegeben in der Direktive DEFAULT_CHANLIST in der Datei Tools \ f8wConfig.cfg ), müssen Sie diese Funktion in der Datei preinclude.h angeben, indem Sie den Wert der Direktive ändern.
Wir fügen auch die Kompilierungsanweisung DISABLE_GREENPOWER_BASIC_PROXY hinzu, damit der GREENPOWER-Endpunkt nicht für unser Gerät erstellt wird.

Schalten Sie auch die unnötige Unterstützung für den LCD-Bildschirm aus.

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

Damit unser Gerät automatisch versucht, eine Verbindung zum Netzwerk herzustellen, fügen wir die Verbindung zum Netzwerk im Funktionscode zclDIYRuZRT_Init hinzu .

bdb_StartCommissioning(BDB_COMMISSIONING_MODE_NWK_STEERING |
                         BDB_COMMISSIONING_MODE_FINDING_BINDING);

Führen Sie danach Build aus, füllen Sie die Firmware in den Chip und beginnen Sie mit dem Pairing auf dem Koordinator. Ich überprüfe den Betrieb des ZigBee-Netzwerks in ioBroker.zigbee. So sieht das neu angeschlossene Gerät aus:



Großartig, es stellte sich heraus, dass das Gerät verbunden wurde!

7. Wir erschweren den Betrieb des Geräts


Versuchen wir nun, die Funktionalität ein wenig anzupassen:

  • Der Vorgang zum Verbinden des Geräts mit dem Netzwerk erfolgt durch langes DrĂĽcken der Taste.
  • Wenn sich das Gerät bereits im Netzwerk befand, wird es durch langes DrĂĽcken aus dem Netzwerk angezeigt.
  • Kurzes DrĂĽcken - schaltet den Status der LED um.
  • Der Status der LED sollte beibehalten werden, wenn das Gerät nach einem Stromausfall startet.

Um meine eigene Schaltflächenverarbeitung einzurichten, habe ich die Funktion DIYRuZRT_HalKeyInit ähnlich der im Modul hal_key.c erstellt , jedoch ausschließlich für meine Schaltflächen.

//    ()
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 */
}

Aufruf dieser Funktion zur Makro- Datei HAL_BOARD_INIT hal_board_cfg_DIYRuZRT.h hinzugefĂĽgt . Deaktivieren Sie den integrierten hal_key in derselben Datei hal_board_cfg_DIYRuZRT.h, um Konflikte zu vermeiden :

#define HAL_KEY FALSE

weil Der Standard-Tastenleser ist deaktiviert. Wir werden dies selbst tun.
In der Initialisierungsfunktion zclDIYRuZRT_Init starten wir den Timer zum Lesen der Zustände der Tasten. Der Timer generiert unser HAL_KEY_EVENT- Ereignis .

osal_start_reload_timer( zclDIYRuZRT_TaskID, HAL_KEY_EVENT, 100);

In der Ereignisschleife behandeln wir das Ereignis HAL_KEY_EVENT , indem wir die Funktion DIYRuZRT_HalKeyPoll aufrufen :

//  
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);
}

Durch Speichern des Status der Schaltflächen in der Variablen halKeySavedKeys können wir den Zeitpunkt der Änderung bestimmen - Drücken und Loslassen der Schaltflächen.

Wenn Sie auf die Schaltfläche klicken, starten Sie den Timer für 5 Sekunden. Wenn dieser Timer ausgelöst wird, wird das Ereignis DIYRuZRT_EVT_LONG generiert . Wenn die Taste losgelassen wird, wird der Timer zurückgesetzt. In jedem Fall ändern wir den Status der LED, wenn Sie die Taste drücken.

//   
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);
  }
}

Bei der Verarbeitung eines Ereignisses mit langer Drucklaufzeit berĂĽcksichtigen wir nun den aktuellen Status des Netzwerks ĂĽber das Strukturattribut 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 );
  }

Wir gehen weiter. Der Zustand der LED wird in einer Variablen gespeichert, deren Wert wir im NV-Speicher speichern. Wenn das Gerät startet, lesen wir den Wert aus dem Speicher in eine Variable.

  //  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. Jetzt werden wir uns mit Zigbee befassen


Bisher haben wir die Hardware aussortiert - mit der Taste steuern wir die LED. Jetzt implementieren wir dasselbe durch Zigbee.

Zur Steuerung des Relais reicht es aus, unseren einzigen Endpunkt zu verwenden und den GenOnOff- Cluster zu implementieren . Wir lesen die Zigbee-Cluster-Bibliotheksspezifikation fĂĽr den GenOnOff- Cluster :





Es reicht aus, das OnOff-Attribut und die Befehle On, Off, Toggle zu implementieren. FĂĽgen
Sie zunächst die Direktive zu preinclude.h hinzu :

#define ZCL_ON_OFF

In der Beschreibung unserer Attribute zclDIYRuZRT_Attrs fĂĽgen wir neue Clusterattribute hinzu:

  // ***  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
    }
  },

Wir fĂĽgen den Cluster auch der Liste der unterstĂĽtzten eingehenden Endpunktcluster zclDIYRuZRT_InClusterList hinzu . FĂĽgen Sie der

Tabelle zclDIYRuZRT_CmdCallbacks einen Handler hinzu, um Steuerbefehle zu implementieren .

/*********************************************************************
 *    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

Und wir setzen es um:
//    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);
  }
}

GroĂźartig, jetzt kann das Relais durch Befehle geschaltet werden.





Das reicht aber nicht. Jetzt mĂĽssen wir auch den Koordinator ĂĽber den aktuellen Zustand der LED informieren, wenn wir sie mit der Taste umschalten.

FĂĽgen Sie erneut die Direktive hinzu:

#define ZCL_REPORTING_DEVICE

Erstellen Sie nun eine Funktion zclDIYRuZRT_ReportOnOff , die eine Statusmeldung sendet. Wir werden es nennen, wenn die LED geschaltet wird und wenn das Gerät startet.

//    
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);
}

Jetzt sehen wir in den Protokollen Meldungen ĂĽber eine Ă„nderung des Status der LED.

9. SchlieĂźen Sie den Temperatursensor ds18b20 an


Der Sensor ist mit einem beliebigen freien Pin verbunden (in meinem Fall P2_1 einstellen ).

Fügen Sie der Anwendung den Sensorabrufcode hinzu. Wir werden regelmäßig interviewen - einmal pro Minute.
Unmittelbar nach der Abfrage benachrichtigt Sie der Netzwerkkoordinator ĂĽber den aktuellen Wert.

Lesen Sie die ZCL-Spezifikationen zum Senden von Daten von Temperatursensoren. Wir benötigen eine Cluster-
Temperaturmessung.



Wir sehen, dass wir 3 Attribute implementieren mĂĽssen, von denen eines den mit 100 multiplizierten Temperaturwert darstellt.

Hier fĂĽgen wir die Attribute analog zum GenOnOff- Cluster hinzu . Der Koordinator wird ĂĽber das Ereignis DIYRuZRT_REPORTING_EVT informiert , das wir zu Beginn einmal pro Minute planen. Im Event-Handler werden wir anrufenzclDIYRuZRT_ReportTemp , das die Temperatur des Sensors liest und eine Nachricht sendet.

//   
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. Gießen Sie Firmware in das Gerät


Um das Devboard auf Sonoff BASICZBR3 umzustellen, mĂĽssen Sie die Ăśbereinstimmung der LED-Pins und -Tasten anpassen.



Wiederholen Sie die LED 1 an Pin P0_7 , um das Relais zu steuern. Die Aufnahme erfolgt durch ein hohes Maß an ACTIVE_HIGH . Wir hängen die S1- Taste an Pin P1_3 und die Informations-LED 2 an P1_0 wieder auf . Wir lassen den Temperatursensor an Pin P2_1 . Wir nehmen alle diese Änderungen in der Datei hal_board_cfg_DIYRuZRT.h vor . Um eine Konfiguration auszuwählen, erstellen wir eine separate Direktive HAL_SONOFF . Wenn es eingestellt ist, werden die Einstellungen für Sonoff BASICZBR3 verwendet, andernfalls für 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

Ein weiterer wichtiger Parameter musste korrigiert werden - das Vorhandensein von "Uhr" -Quarz, weil Auf der Sonoff BASICZBR3-Platine ist es nicht verlötet:

//#define HAL_CLOCK_CRYSTAL
  #define OSC32K_CRYSTAL_INSTALLED FALSE

Ohne diese Optionen startet die Firmware nicht (oder besser gesagt nicht immer).

Danach sammeln wir die Firmware und stellen eine Verbindung zur Firmware her.
BEACHTUNG!!! Trennen Sie das Sonoff BASICZBR3-Relais vor jeder Verbindung und Firmware vom Stromnetz!

Wir verbinden die Sonoff BASICZBR3-Verkabelung mit CCDebugger, löschen den Chip und flashen unsere Firmware.



11. Wir starten das Gerät in zigbee2mqtt und ioBroker.Zigbee


Das Gerät ist zwar in der Liste der angeschlossenen Geräte aufgeführt und wir können es durch Senden von Befehlen verwalten, aber wir müssen dies korrekter tun - mit Bildern und Zuständen.

Um ein neues Gerät in ioBroker.Zigbee zu erhalten , müssen Sie zwei Schritte ausführen:

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

Alle Änderungen können zuerst in lokalen Dateien und dann in den entsprechenden Repositorys vorgenommen werden.

Wir finden den Speicherort des zigbee-herdsman-converter- Pakets im installierten ioBroker (oder zigbee2mqtt). Im Paket befindet sich die Datei device.js https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/devices.js.

Diese Datei enthält Beschreibungen aller Geräte, mit denen ioBroker.zigbee und zigbee2mqtt arbeiten können . Wir finden darin einen Block mit Beschreibungen von DIYRuZ-Geräten (nach 2300 Zeilen). Fügen Sie diesem Block eine Beschreibung des neuen Geräts hinzu:

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

Im fromZigbee- Attribut geben wir die Konverter an, die vom Gerät kommende Nachrichten verarbeiten. Unsere beiden Stellen sind standardisiert. Der Konverter fz.on_off verarbeitet die Ein / Aus-Nachricht und fz.temperature verarbeitet die Temperaturdaten. Der Code dieser Konverter (in den Dateikonvertern / fromZigbee.js) zeigt, wie eingehende Nachrichten verarbeitet werden und dass die Temperatur durch 100 geteilt wird.

   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')};
        },
    },

Im Attribut toZigbee geben wir die Konverter an, die unsere Befehle an das Gerät verarbeiten. In unserem Fall ist es der tz.on_off- Wandler zum Schalten von Relais.

Alles zu den "Konvertern" hinzugefügt. Wer zigbee2mqtt verwendet - Sie können es bereits verwenden.

Und ioBroker-Benutzer fügen der Datei ioBroker.zigbee \ lib \ device.js weiterhin eine Beschreibung des Geräts hinzu

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

Hier reicht es aus, genau das gleiche Modell, eine Datei mit einem Bild und eine Liste von Zuständen anzugeben. In unserem Fall sind die Zustände ebenfalls Standard: Zustand für den Zustand des Relais, Temperatur für die Anzeige von Temperaturwerten.



12. Was kommt als nächstes?


Leider konnte ich nicht alle Aspekte und Funktionen von Z-Stack 3.0 herausfinden. Höchstwahrscheinlich habe ich eine Funktion nicht einmal richtig implementiert, oder es könnten einige integrierte Mechanismen verwendet werden, um sie zu implementieren.
Daher kann die obige Lösung verbessert und entwickelt werden. Hier sind einige Anweisungen:

  • Ich konnte nicht schnell Lösungen fĂĽr die Möglichkeit finden, untergeordnete Geräte ĂĽber Relais anzuschlieĂźen. Andere Router-Geräte können den Befehl allow_join ausfĂĽhren und neue Geräte ĂĽber sich selbst verbinden, ohne das neue Gerät zum Koordinator bringen zu mĂĽssen. Das Gerät wird durch einen Router dargestellt. Es wird korrekt auf der Netzwerkkarte angezeigt, weigert sich jedoch, den Befehl "allow_join" auszufĂĽhren. Genauer gesagt wird der Befehl ausgefĂĽhrt, aber die Geräte stellen keine Verbindung ĂĽber ihn her.
  • AuĂźerdem wurde keine korrekte Berichterstellung implementiert. Auf diese Weise können Sie die Statusbenachrichtigung konfigurieren, wenn Sie mit dem Befehl configReport eine Liste der zu sendenden Attribute und die Häufigkeit der Benachrichtigung angeben können.
  • Arbeite mit Gruppen.
  • Behandeln Sie Interrupts und implementieren Sie Polling-Schaltflächen durch Interrupts.

Nun, für die folgenden Geräte müssen Sie sich mit Stromversorgungsmodi und der Erstellung von "schlafenden" Geräten mit Batterien befassen.
Wie immer lade ich Sie zusätzlich zu den Kommentaren ein, dieses und andere Geräte im Telegramm-Chat auf Zigbee zu diskutieren .

Ich möchte mich für die Unterstützung und Unterstützung bei der Entwicklung meiner Kollegen in der Telegramm-Chat- und Zigbee-Community bedanken:


Verweise


Die Hauptdokumentation ist im Z-Stack 3.0.2 SDK enthalten und wird mit diesem installiert. Aber ich werde einen Teil der Links hier geben:


All Articles