shimmeringbee / zstack Goto Github PK
View Code? Open in Web Editor NEWImplementation of a ZNP and support code designed to interface with Texas Instruments Z-Stack, written in Go.
Home Page: https://shimmeringbee.io/
License: Apache License 2.0
Implementation of a ZNP and support code designed to interface with Texas Instruments Z-Stack, written in Go.
Home Page: https://shimmeringbee.io/
License: Apache License 2.0
When some low power End Devices choose the Coordinator as their parent, they may have issues if ZStack is restarted.
This is caused by ZStack requiring resolution of the IEEE address for any event. As the coordinator is responsible for the End Device it is also responsible for knowing their IEEE address. When ZStack restarts the adapter on Initialise it issues a hard reset, this clears the CC2530's child cache (amongst other things).
return z.writeNVRAM(invokeCtx, ZCDNVStartUpOption{StartOption: 0x03})
This is not a problem for Routers as they regularily reintroduce themselves and the mesh network reforms, some End Devices do not, or do so very infrequently.
It is sensible to issue the hard reset when the adapter is being reconfigured to be a different network, however during normal operation (and restarts) we should not hard reset the adapter.
We could take a couple of approaches:
AF_DATA_REQUEST responded to by AF_DATA_CONFIRM
AF_INCOMING_MSG
The naming of ZStack message stucts is inconsistent. Sometimes the shimmeringbee code has created a "Resp" struct which is the response to a "Req", but ZStack natively calls some async responses "Resp".
Messages should be renamed to be, for example:
SYS_VERSION = SysVersion and SysVersionReply
ZB_BIND_DEVICE = ZbBindDevice/ZbBindDeviceReply
ZB_BIND_CONFIRM = ZbBindConfirm
ZDO_ACTIVE_EP_REQ = ZdoActiveEpReq/ZdoActiveEpReqReply
ZDO_ACTIVE_EP_RSP = ZdoActiveEpResp
That is, when a message is syncronous the name of the SRSP is the same as SREQ, with Reply appended.
Add support for registering endpoints, ZDO_SIMPLE_DESC_REQ syncronous, command 0x04.
Add support for registering endpoints, ZDO_SIMPLE_DESC_RSP syncronous, command 0x84.
Add support for active endpoints, ZDO_ACTIVE_EP_REQ syncronous, command 0x05.
Add support for active endpoints, ZDO_ACTIVE_EP_RSP asyncronous, command 0x85.
Hello when I try to run example QueryNodeDescription and QueryNodeEndpoints always failed by timeout. I try to increase it but it doesn't help. Is example is working or I do something wrong?
I am able to join/leave devices, but no incoming messages come through when I trigger any of the sensors.
I don't know where to start debugging, I tried to insert logging statements into node_receive_message.go
.
func (z *ZStack) startMessageReceiver() {
log.Println("startMessageReceiver: subscribing")
_, z.messageReceiverStop = z.subscriber.Subscribe(&AfIncomingMsg{}, func(v interface{}) {
log.Println("startMessageReceiver: got one")
I got "subscribing" but didn't get a single "got one" message. My code is:
port, err := serial.Open("/dev/ttyACM0", &serial.Mode{
BaudRate: 115200, // from TI's docs
})
if err != nil {
return err
}
defer port.Close()
port.SetRTS(true) // "modem status bit RequestToSend"
nodeTable, err := loadNodeTable()
if err != nil {
return err
}
saveNodeTableToDisk := func() error {
return jsonfile.Write(nodeTableFilename, nodeTable.Nodes())
}
zigbeeComms := zstack.New(port, nodeTable)
zigbeeComms.WithGoLogger(logex.Prefix("zigbee", rootLogger))
defer zigbeeComms.Stop()
netCfg := zigbee.NetworkConfiguration{ // not my actual details
PANID: zigbee.PANID(12345),
ExtendedPANID: zigbee.ExtendedPANID(1357768367493424001),
NetworkKey: zigbee.NetworkKey([16]byte{149, 221, 234, 37, 233, 64, 31, 188, 12, 84, 124, 202, 39, 142, 31, 13}),
Channel: 15,
}
// initialise ZStack and CC253X
if err := func() error {
ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel()
return zigbeeComms.Initialise(ctx, netCfg)
}(); err != nil {
return fmt.Errorf("initialize: %w", err)
}
if err := zigbeeComms.PermitJoin(ctx, true); err != nil {
return fmt.Errorf("PermitJoin: %w", err)
}
for {
event, err := zigbeeComms.ReadEvent(ctx)
if err != nil {
return err
}
switch e := event.(type) {
case zigbee.NodeJoinEvent:
logl.Info.Printf("JOIN: %v\n", e.Node)
go exploreDevice(zigbeeComms, e.Node)
case zigbee.NodeLeaveEvent:
logl.Info.Printf("LEAVE: %v\n", e.Node)
case zigbee.NodeUpdateEvent: // this gets fired every 30s regardless of network activity
logl.Debug.Printf("UPDATE: %v\n", e.Node)
// it's a good trigger to update the node table
// TODO: detect if we have changes
if err := saveNodeTableToDisk(); err != nil {
return err
}
case zigbee.NodeIncomingMessageEvent:
logl.Debug.Printf("MSG: %v\n", e)
default:
logl.Error.Println("got unrecognized event")
}
}
}
I'm running CC2531 with the recommended firmware, I flashed the dongle just to be sure. It works out-of-the-box with https://github.com/dyrkin/zigbee-steward
Add support for CC2652 based adapters, these use firmware from Koenkk which is based on Z-Stack 3.X.0, because this supporst Zigbee 3.0 there are some differences to the initialisation of the adapter. To do this, we will need to:
CC2531
(*zstack.SysResetInd)(0xc00000b8f0)({
Reason: (zstack.ResetReason) 0,
TransportRevision: (uint8) 2,
ProductID: (uint8) 0,
MajorRelease: (uint8) 2,
MinorRelease: (uint8) 6,
HardwareRevision: (uint8) 3
})
CC2653
(*zstack.SysResetInd)(0xc000204008)({
Reason: (zstack.ResetReason) 0,
TransportRevision: (uint8) 2,
ProductID: (uint8) 1,
MajorRelease: (uint8) 2,
MinorRelease: (uint8) 7,
HardwareRevision: (uint8) 1
})
zStack 1.2.0 is indicated by 0, 3.x.0 is indiciated by 1 and 3.0.x is indiciated by 2. (No not a typo).
Based upon the SysResetInd.ProductID we can switch initialisation behaviour, if the device does not return a valid version, then assume CC2531 behaviour.
Zigbee herdsman implementation can be found here: https://github.com/Koenkk/zigbee-herdsman/blob/master/src/adapter/z-stack/adapter/startZnp.ts
Our initial target for support is the CC253X USB adapters flashed with the zigbee2mqtt.io z-stack coordinator firmware. Thus zstack
must be capable of initialising the adapter and starting it in a coordinator role.
Finding documentation on this is a little tricky, so learning from other existing open source tooling out there for the CC253X, we should initialise as follows.
Reset Adapter
// Reset (SOFT)
// Perform Configuration and State Reset - NVRAM - ZCD_NV_STARTUP_OPTION (0x0003) 0x03 (Clear State, Clear Config)
// Reset (SOFT)
// Set Logical Type as COORDINATOR - NVRAM - ZCD_NV_LOGICAL_TYPE (0x0087) 0x00 (Coordinator) (01 = Router, 02 = End Device)
// Enable Network Security - NVRAM - ZCD_NV_SECURITY_MODE (0x0064) 0x01 (Enable Security)
// Enable distribution of network keys - NVRAM - ZCD_NV_PRECFGKEYS_ENABLE (0x0063) 0x01 (Use precfg keys)
// Set Network Key - NVRAM - ZCD_NV_PRECFGKEY (0x0062) [16]byte (Set network key)
// Set ZDO Direct Callback - NVRAM - ZCD_NV_ZDO_DIRECT_CB (0x008f) 0x01 (True, Don't write in ZDO_MSG_CB_INCOMING)
// Set Channels - NVRAM - ZCD_NV_CHANLIST (0x0084) Bitmap (Setting 1 statically uses that, multiple scan least busy)
// Set PAN ID - NVRAM - ZCD_NV_PANID (0x0083) [2]byte (Set PAN ID)
// Set Extended PAN ID - NVRAM - ZCD_NV_EXTPANID (0x002d) [8]byte (Set extended PAN ID)
// Set Enable TC Link Key - NVRAM - ZCD_NV_USE_DEFAULT_TCLK (0x006d) 0x01 (Enable TC Link Key)
// Set TC Link Key - NVRAM - ZCD_NV_TCLK_TABLE_START (0x0101) [20]byte (Default TC Link, more complex than just key structure)
The Z-Stack software on the CC chips maintain a mapping of network address to IEEE addresses (extended addresses) while they are operating, once they reboot this means that they loose the cache (unless they are children of the coordinator, or there is a binding).
The Z-Stack library has been designed around accepting the IEEE address as a destination for packets/commands because they do not change - as such once the CC chip restarts this can result in z-stack not being able to send messages or receiving messages.
Thus zstack needs a network<->IEEE cache which can be presisted between program runs. Like znp
it's probably worth abstracting this out to something that can be provided to zstack
on start up, and persisted periodically.
At current if the USB device is disconnected, or stops responding zstack
will usually spin a thread. We need to stop this, and ideally we need to communicate back to the consumer that all hope is lost so that application can recover or exit.
While trying to run code using zstack on a Raspberry Pi 3, initialisation fails, where as the same configuration works on an AMD64 architectured machine.
This could be to do with different sizes of int on the different platforms, they are the same endian so thankfull doesn't appear to be a byte order issue.
This issue may also effect ZCL, bytecodec or unpi.
Add support for registering endpoints, AF_REGISTER syncronous, command 0x09.
As in the title, at the moment it gets handled externally by providing a node table, this exposes way too much about the the implementation. We now also have the persistence
framework which zda
now uses - zstack
needs to migrate to it so the storage mechanism is easilys swapped out.
While the network manager owns the NodeTable, it's also used by a Go routine in node_receive_message.go. As such it's possible for a concurrent read/write. A read/write lock might be appropriate here.
Add support for binding, ZDO_BIND_REQ syncronous, command 0x21.
Add support for unbinding, ZDO_UNBIND_REQ syncronous, command 0x22.
Given this code:
netCfg, err := zigbee.GenerateNetworkConfiguration()
if err != nil {
return err
}
if err := zigbeeComms.Initialise(ctx, netCfg); err != nil {
return err
}
.. Initialize()
fails every time.
If I take what GenerateNetworkConfiguration()
gives and type it as static config, Initialize()
fails for the first time my process runs (when it notices that some config inside the stick doesn't match what I was given), but if I run my process again, it now starts working. So I guess it has something to do with this branch:
Line 31 in 35282bb
IIRC the error message I got was adapter rejected permit join state change: state ...
(sorry I don't have the dynamic part of that error message written down..) from here
Line 33 in 35282bb
My hardware/firmware is documented in #20
We defined an event for devices being rediscovered, i.e. not in network manager, appears through a message or via a LQI table poll. We need to identify how best to send this message nicely.
The API for interacting with zigbee nodes should not rely on the Network Address, this can/does change when there are conflicts. Higher levels of the stack should probably be masked from Network Addresses, but rather use IEEE Addresses.
On a large network after an short to medium amount of time the USB stick appears to crash. With two sticks running alongside each other, the network with 50 devices will crash, the stick with 2 devices remains stable.
Looking at zigbee2mqtt's zigbee-herdsman the implementation for znp uses a queue with concurrency limits. Searching their previous issues suggests the crashes can happen due to very busy interaction with the stick.
Our implementation needs to look into a technique to limit concurrency, we could either queue go routines in a queue with a pool of go routines (ala zigbee-herdsman), or some kind of syncronisation mechanism similar to a concurrency limited rwmutex.
The channel selection is either ignored or incorrectly managed, as a network of 15 ended up being around 23.
It could be the bitshift logic does not work correctly.
Hello, could you please provide examples of using your library in real life?
A simple Hello World application, for example.
Thank you!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.