This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

BLE Mesh 4.0 send notification to all GATT connections

Hi,

I have a mesh network with multiple smartphones, which are connected over GATT. If now a light command is triggered on one smartphone, I want to synchronize the other smarthpones, but I am currently unsure where to tackled this problem. I think I can check all GATT connections on every node and then notify each smartphone connected, right? But how can I allocate a specific message for a GATT connection? I see, that in the light switch examples, only the access_model_reply() in access.c is used, but never a notification is sent from a node to the smarthpone? 

So, how can I publish a specific message with specific opcode to a GATT connected smartphone?

Edit:

So I did the following and used the GATT connection:

void send_data_to_smartphone(uint8_t *data, uint16_t size)
{
    for(uint8_t i = 0; i < MESH_GATT_CONNECTION_COUNT_MAX; i++)
    {
        if(BLE_CONN_HANDLE_INVALID != m_gatt.connections[i].conn_handle)
        {
            
            uint8_t * p_packet = mesh_gatt_packet_alloc(i, MESH_GATT_PDU_TYPE_NETWORK_PDU,size, 0);

            /* We should not have any other GATT stuff going on at this point. */
            NRF_MESH_ASSERT(p_packet != NULL);

            memcpy(p_packet, data, size);
            NRF_MESH_ERROR_CHECK(mesh_gatt_packet_send(i, p_packet));

            __LOG(LOG_SRC_BEARER, LOG_LEVEL_INFO, "Send Packet with length %d to Handle %d\n", size, i);
        }
    }
}

And I receive the message on the Smartphone correctly in 

protected void parseMeshPduNotifications(@NonNull final byte[] pdu, @NonNull final MeshNetwork network) throws ExtendedInvalidCipherTextException {
        final List<NetworkKey> networkKeys = network.getNetKeys();
        final int ivi = ((pdu[1] & 0xFF) >>> 7) & 0x01;
        final int nid = pdu[1] & 0x7F;
        //Here we go through all the network keys and filter out network keys based on the nid.
        for (int i = 0; i < networkKeys.size(); i++) {
            NetworkKey networkKey = networkKeys.get(i);
            final SecureUtils.K2Output k2Output = SecureUtils.calculateK2(networkKey.getKey(), SecureUtils.K2_MASTER_INPUT);
            if (nid == k2Output.getNid()) {
                final byte[] networkHeader = deObfuscateNetworkHeader(pdu, MeshParserUtils.intToBytes(network.getIvIndex()), k2Output.getPrivacyKey());
                if (networkHeader != null) {
                    final int ctlTtl = networkHeader[0];
                    final int ctl = (ctlTtl >> 7) & 0x01;
                    final int ttl = ctlTtl & 0x7F;
                    Log.v(TAG, "TTL for received message: " + ttl);

                    final byte[] sequenceNumber = ByteBuffer.allocate(3).order(ByteOrder.BIG_ENDIAN).put(networkHeader, 1, 3).array();
                    final int src = MeshParserUtils.unsignedBytesToInt(networkHeader[5], networkHeader[4]);

                    final int sequenceNo = MeshParserUtils.getSequenceNumber(sequenceNumber);
                    Log.v(TAG, "Sequence number of received access message: " + MeshParserUtils.getSequenceNumber(sequenceNumber));

                    final ProvisionedMeshNode node = network.getNode(src);
                    if (node != null) {
                        //Check if the sequence number has been incremented since the last message sent and return null if not
                        if (sequenceNo > node.getSequenceNumber()) {
                            if (!MeshParserUtils.isValidSequenceNumber(sequenceNo)) {
                                return;
                            }
                            node.setSequenceNumber(sequenceNo);
                        } else {
                            return;
                        }
                    } else {
                        return;
                    }
                    //TODO validate ivi
                    byte[] nonce;
                    final byte[] ivIndex = MeshParserUtils.intToBytes(network.getIvIndex());
                    try {

                        final int networkPayloadLength = pdu.length - (2 + networkHeader.length);
                        final byte[] transportPdu = new byte[networkPayloadLength];
                        System.arraycopy(pdu, 8, transportPdu, 0, networkPayloadLength);
                        final byte[] decryptedNetworkPayload;
                        final MeshMessageState state;
                        if (pdu[0] == MeshManagerApi.PDU_TYPE_NETWORK) {
                            nonce = createNetworkNonce((byte) ctlTtl, sequenceNumber, src, ivIndex);
                            decryptedNetworkPayload = SecureUtils.decryptCCM(transportPdu, k2Output.getEncryptionKey(), nonce, SecureUtils.getNetMicLength(ctl));
                            state = getState(src);
                        } else {
                            nonce = createProxyNonce(sequenceNumber, src, ivIndex);
                            decryptedNetworkPayload = SecureUtils.decryptCCM(transportPdu, k2Output.getEncryptionKey(), nonce, SecureUtils.getNetMicLength(ctl));
                            state = getState(MeshAddress.UNASSIGNED_ADDRESS);
                        }
                        if (state != null) {
                            //TODO look in to proxy filter messages
                            ((DefaultNoOperationMessageState) state).parseMeshPdu(node, pdu, networkHeader, decryptedNetworkPayload);
                            return;
                        }
                    } catch (InvalidCipherTextException ex) {
                        if (i == networkKeys.size() - 1) {
                            throw new ExtendedInvalidCipherTextException(ex.getMessage(), ex.getCause(), TAG);
                        }
                    }
                }
            }
        }
    }

But there seems to be a problem with the network key for this packet. Do I need to code my message before sending it over GATT?

Edit 2: The problem seems to be with the network ID and the network header. How do I need to add this on my nRF52833? Are there already some functionalities that provide these?

Parents
  • HI,

    Whenever the server model is updated, it will send out a Status message to its configured publish address. (This happens in addition to any Status message sent directly as an answer to an acknowledged Set message..) This means you should have the corresponding client model on the smartphones, all subscribed to the same group as you set as publish address on the server model.

    While there is a predefined group address for all proxy nodes (0xFFFC, all-proxies), there is no such address for "all nodes connected over GATT to a proxy". This means you must configure the address yourself on each smartphone. (In any case it is a good thing to be able to configure the network as you want, e.g. it will be possible to configure non-GATT devices to subscribe to the Status updates as well.)

    Regards,
    Terje

  • Hi Terje,

    I think you missunderstood my question. What I want to achieve is to send notifications to the smartphone from a connected node in the mesh network. The problem here is, that when I send a network PDU with mesh_gatt_packet_send() from the light control examples, I receive the message on the phone, but the additional header data seems not to be included. This header data should include the nid, the ivi etc... How can I generate this additional information for the network PDU?

Reply
  • Hi Terje,

    I think you missunderstood my question. What I want to achieve is to send notifications to the smartphone from a connected node in the mesh network. The problem here is, that when I send a network PDU with mesh_gatt_packet_send() from the light control examples, I receive the message on the phone, but the additional header data seems not to be included. This header data should include the nid, the ivi etc... How can I generate this additional information for the network PDU?

Children
  • Hi,

    There still should be no reason to hook into the stack on the transport layer. (Which you do when directly using mesh_gatt_packet_send().) As the smartphone is a proper node in the network you should use the access layer (through the Access layer API) for sending messages.

    I must admit I still do not quite understand what your use case is, what nodes are involved, and what kind of notification is going from what node to what node and when. Can you elaborate, possibly with a figure / drawing of the network?

    Regards,
    Terje

  • Hi,

    You are right, I tackle this problem from the transport layer, because I don't see how I can create a message for GATT on the application layer. In the mesh_gatt.c there is the m_gatt.connections[i], which contains all GATT connections to smartphones, so I think I need somehow this handle on the application layer. When I look in the access layer, I can use access_model_publish(access_model_handle_t handle, const access_message_tx_t * p_message), but this handle is for a model and not for a GATT connection.

    So, how can I send a message directly to a GATT connection?

  • Hi,

    From the viewpoint of the application, all nodes in a Bluetooth mesh network are addressed exactly the same way, and you always operate on models. That includes for instance node configuration, where you use the configuration model. Even the provisioner is a "normal" node on the network in this sense.

    GATT is nothing but just another way for nodes to join the network. Just as there are no methods to specifically send messages to nodes connected over the ADV bearer, there are no methods to specifically send messages to nodes connected over the GATT bearer.

    What this all means is, for any mesh node (including smartphones), you communicate to and from that node using the mesh models only. For instance by using access_model_publish(). There is no separate GATT communication channel.

    From your descriptions, it sounds like you ask for the default configuration for the Light switch example. Note how among the descriptions for how to test that example, it reads: "you can also press Button 1 on the servers to locally toggle the state of their LED 1, and the status reflecting this state will be sent to the client board." I suggest that you look into the Light switch example, and in particular the configuration part.

    Regards,
    Terje

Related