jefffhaynes/XBee

TxRequestExtFrame with 16-bit address

Closed this issue · 19 comments

I was reading through the Xbee documentaion and came across this.

Address Table

The XBee modules can store up to ten address table entries. For applications where a single device (for example coordinator) may send unicast transmissions to more than ten devices, the application should implement an address table to store the 16-bit and 64-bit addresses for each remote device. Any XBee that will send data to more than ten remotes should also use API firmware. The application can then send both the 16-bit and 64-bit addresses to the XBee in the API transmit frames which will significantly reduce the number of 16-bit address discoveries and greatly improve data throughput.

If an application will support an address table, the size should ideally be larger than the maximum number of destination addresses the device will communicate with. Each entry in the address table should contain a 64-bit destination address and its last known 16-bit address.

When sending a transmission to a destination 64-bit address, the application should search the address table for a matching 64-bit address. If a match is found, the 16-bit address should be populated into the 16-bit address field of the API frame. If a match is not found, the 16-bit address should be set to 0xFFFE (unknown) in the API transmit frame.

Could you add the ability to to send a TxRequestExtFrame with the 16-bit address? I looked at it and it just uses 0xFFFE since we don't know it. It would also be cool if the library added the 16-bit address to the node when data is received from the node so I would have an easy way of getting the 16-bit address of the node and when I used the alternate frame that uses the 16-bit address it could just pull it from the node itself so I wouldn't need to keep a separate table of 64-bit and 16-bit pairs.

Also at the moment I can only use Visual Studio 2013 so I can't install Xbee 5.0. I don't know what it would take to add it to the version 4 branch. I am going to try and get at least Visual Studio 2015, but I don't know if I will be able to get it or not.

I am working on this project at work so I can't use the community addition unfortunately. The only option I have available to me is 2015 and I may not even be able to get that. Its frustrating for sure!

Thanks!

So just to clarify this - if I simply pass the short address from the node in TransmitDataAsync, will that be sufficient? For example,

var transmitRequest = new TxRequestExtFrame(Address.LongAddress, Address.ShortAddress, data);

Also, as to the DataReceived event including the address, I'm not opposed to it but the address is already available from the sender (the emitting node). Are you saying you also would like it in the event itself?

Yes I think that frame will work. I would just need a way to use it and disable enableAck and I see that TransmitDataAsync only uses.

var transmitRequest = new TxRequestExtFrame(Address.LongAddress, data);

When it comes to the address I saw that I can get to the short address from the sender, but as the code currently stands it only returns 0xFFFE, but I guess with this new constructor you would change that.

Deleted previous erroneous replies for clarity -

I'm seeing the short address get returned if the node has a short address configured. Are you seeing something different?

ok, not sure if I got what you wanted or not but I pushed 4.3.2. Give it a shot and let me know if it's missing anything.

For some reason I am still getting FFFE for the short address. I am am looking at it in the NodeDiscovered event.

Right now it is DC6B

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using XBee;
using System.Diagnostics;
using log4net;

[assembly: log4net.Config.XmlConfigurator(Watch = true)]

namespace XBee_Test
{

public partial class Form1 : Form
{
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    const string terminator = "\n";
    bool stop = false;

    Stopwatch totalTime = new Stopwatch();
    XBeeController controller = new XBeeController("COM9", 9600);
    Dictionary<LongAddress,RemoteDevice> remoteDevices = new Dictionary<LongAddress,RemoteDevice>();
    //LongAddress currentAddress;
    List<XBeeNode> nodes = new List<XBeeNode>();
    //BackgroundWorker pollWorkerTest1 = new BackgroundWorker();
    System.Timers.Timer timer = new System.Timers.Timer(100);

    string sendString = "Stardusthurtstheeye!";
    int nodeCount = 0;
    int currentDevice = 0;

    public Form1()
    {
        InitializeComponent();
        timer.AutoReset = true;
        timer.Elapsed += timer_Elapsed;
        //pollWorkerTest1.WorkerSupportsCancellation = true;
        //pollWorkerTest1.DoWork += pollWorkerTest1_DoWork;
        //pollWorkerTest1.ProgressChanged += pollWorkerTest1_ProgressChanged;
        //pollWorkerTest1.RunWorkerCompleted += pollWorkerTest1_RunWorkerCompleted;
    }

    private async void btnConnectCoordinator_Click(object sender, EventArgs e)
    {
        await controller.OpenAsync();
        controller.NodeDiscovered += controller_NodeDiscovered;
        lblHost.Text = "Host: Connected";
        lblHost.ForeColor = Color.Green;
        btnDiscoverNetwork.Visible = true;
        btnConnectCoordinator.Visible = false;
        log.Debug("Connect to Coordinator button pressed.");
    }

    void controller_NodeDiscovered(object sender, NodeDiscoveredEventArgs e)
    {
        remoteDevices.Add(e.Node.Address.LongAddress, new RemoteDevice(e.Node, e.Name));
        nodes.Add(e.Node);
        e.Node.DataReceived += Node_DataReceived;
        nodeCount += 1;
        this.Invoke((MethodInvoker) delegate() {
            lblNumNodes.Text = "Nodes: " + nodeCount.ToString();
            nodeGridView.Rows.Add(e.Name, String.Format("{0:X}", e.Node.Address.LongAddress) ,String.Format("{0:X}",e.Node.Address.ShortAddress), 0, 0, 0, 0, 0);
        });
        log.Debug(e.Name + " Discovered! - Address: " + e.Node.Address.LongAddress.Low);
    }

    async void Node_DataReceived(object sender, XBee.DataReceivedEventArgs e)
    {
        timer.Stop();
        log.Debug("Timer Stopped");

        XBeeNode node = (XBeeNode)sender;
        RemoteDevice device;
        string receivedText = Encoding.ASCII.GetString(e.Data); 

        remoteDevices.TryGetValue(node.Address.LongAddress, out device);

        nodeGridView["Received", currentDevice].Value = ++device.NumRec;

        if (!receivedText.Equals(sendString))
            nodeGridView["FailedMatch", currentDevice].Value = ++device.NoMatch;

        currentDevice++;
        if (currentDevice > nodes.Count - 1)
            currentDevice = 0;

        log.Debug("Data Received: " + node.Address.LongAddress.Low);
        if (!stop)
        {
            nodeGridView["Sent", currentDevice].Value = ++remoteDevices[nodes[currentDevice].Address.LongAddress].NumSent;
            try
            {
               log.Debug("Data Sent: " + nodes[currentDevice].Address.LongAddress.Low);
               await nodes[currentDevice].TransmitDataAsync(Encoding.UTF8.GetBytes(sendString + terminator), false);
            }
            catch
            {

            }
            timer.Start();
            log.Debug("Timer Started");
        }
    }

    private async void btnDiscoverNetwork_Click(object sender, EventArgs e)
    {
        log.Debug("Discover Network button pressed");
        btnDiscoverNetwork.Visible = false;
        await controller.DiscoverNetworkAsync();
        btnStartTest1.Visible = true;
    }

    async void btnStartTest1_Click(object sender, EventArgs e)
    {
        log.Debug("Test1 button pressed");
        btnStartTest1.Visible = false;
        btnStopTest1.Visible = true;
        totalTime.Start();
        nodeGridView["Sent", currentDevice].Value = ++remoteDevices[nodes[0].Address.LongAddress].NumSent;

        log.Debug("Data Sent: " + nodes[currentDevice].Address.LongAddress.Low);
        await nodes[currentDevice].TransmitDataAsync(Encoding.UTF8.GetBytes(sendString + terminator), false);
        timer.Start();
        log.Debug("Timer Started");

    }

    private void btnStopTest1_Click(object sender, EventArgs e)
    {
        log.Debug("Stop Test1 button pressed");
        int totalNoMatch = 0, totalSent = 0, totalReceived = 0, totalRadioFaults = 0, totalNoReplies = 0;

        stop = true;
        totalTime.Stop();
        timer.Stop();
        TimeSpan eT = totalTime.Elapsed;
        lblElapsedTime.Text = String.Format("Total Time: {0:00}:{1:00}:{2:00}:{3:00}", eT.Hours, eT.Minutes, eT.Seconds, eT.Milliseconds / 10);
        lblElapsedTime.Visible = true;
        lblMS.Text = String.Format("{0:00}ms", eT.TotalMilliseconds);
        lblMS.Visible = true;
        btnReset.Visible = true;
        btnStopTest1.Visible = false;

        foreach (RemoteDevice device in remoteDevices.Values)
        {
            totalNoMatch += device.NoMatch;
            totalNoReplies += device.NoReply;
            totalReceived += device.NumRec;
            totalSent += device.NumSent;
            totalRadioFaults += device.RadioFaults;
        }
        lblTestResults.Text = String.Format("Sent: {0}  Received: {1}  Radio Faults: {2}  No Replies: {3}  No Match: {4}", totalSent, totalReceived, totalRadioFaults, totalNoReplies, totalNoMatch);
        lblTestResults.Visible = true;
    }

    async void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        RemoteDevice device;
        log.Debug("Timer tick event started");

        remoteDevices.TryGetValue(nodes[currentDevice].Address.LongAddress, out device);

        nodeGridView["NoReply", currentDevice].Value = ++device.NoReply;
        device.ThreeNoReply++;
        if (device.ThreeNoReply == 3)
        {
            timer.Stop();
            log.Debug("Timer Stopped");
            device.ThreeNoReply = 0;
            nodeGridView["RadioFaults", currentDevice].Value = ++device.RadioFaults;

            currentDevice++;
            if (currentDevice > nodes.Count - 1)
                currentDevice = 0;
            log.Debug("Data Sent: " + nodes[currentDevice].Address.LongAddress.Low);
            await nodes[currentDevice].TransmitDataAsync(Encoding.UTF8.GetBytes(sendString + terminator), false);
            timer.Start();
            log.Debug("Timer Started");
        }
    }

    private void btnReset_Click(object sender, EventArgs e)
    {
        log.Debug("Reset button pressed");
        lblTestResults.Visible = false;
        btnReset.Visible = false;
        lblElapsedTime.Visible = false;
        lblMS.Visible = false;
        btnStopTest1.Visible = false;
        btnStartTest1.Visible = true;
        totalTime.Reset();
        stop = false;
        currentDevice = 0;

        foreach (RemoteDevice device in remoteDevices.Values)
        {
            device.NoMatch = 0;
            device.NoReply = 0;
            device.NumRec = 0;
            device.NumSent = 0;
            device.RadioFaults = 0;
            device.ThreeNoReply = 0;
        }

        foreach (DataGridViewRow row in nodeGridView.Rows)
        {
            for (int i = 1; i < 6; i++)
            {
                row.Cells[i].Value = 0;
            }
        }

    }

    async void btnStartTest2_Click(object sender, EventArgs e)
    {
        
    }
}

}

You're right! Sorry about that, it was a pretty insidious bug. Fixed in 4.3.3.

Glad you were able to find the bug! It is working well on my end.

One thing in relation to this that could be a problem is if the transmission fails because the 16-bit address changed for some reason. This is what the manual has to say about it.

The API provides indication of a remote device's 16-bit address in the following frames:
• All receive data frames
Rx Data (0x90)
Rx Explicit Data (0x91)
IO Sample Data (0x92)
Node Identification Indicator (0x95)
Route Record Indicator (0xA1)
and so forth
• Transmit status frame (0x8B)
The application should always update the 16-bit address in the address table when one of these
frames is received to ensure the table has the most recently known 16-bit address. If a transmission
failure occurs, the application should set the 16-bit address in the table to 0xFFFE (unknown).

I don't really know how often or likely the 16-bit address is to change, but you may want to do as this suggests. I can keep track of whether I am getting responses from my nodes and change the 16-bit address manually, but others may not realize that this can happen. What do you think?

Cool thanks!

Added in 5.3.3/1.4.3 Core