Finally, Tabletop Robotics has developed a Wixel library which supports communication between multiple Wixels. It does so by relying on unique Wixel IDs (set in the Wixel Configuration Utility) rather than by changing radio channels. It attempts to ensure reliable communications between devices using a simple ACK protocol.
I have plans to integrate this library into another Wixel app and the Pololu 3pi library to allow easy communications between multiple 3pis, but one only has time for so much, I’m afraid. Probably won’t get to that for a while.
Thanks for letting us know about your new library. I have added it to the Wixel Apps post and I think it will be useful to other Wixel users.
In your library, I noticed several comments about various problems the library has. One of the most worrying ones to me is that the radioMultiTxDataBlocking function has a chance to discard a received packet even though the library has already sent an acknowledgment packet to the sender.
If I were trying to write a reliable multi-Wixel radio library, I think I would not store the TX packets in a circular buffer. The idea of the TX circular buffer used in radio_queue and radio_link is that you have several packets queued up to be sent, and they must be sent in a particular order, and you know exactly which packet you are going to send next. Instead of using a circular buffer, I would probably have some number of independent packet slots. Having 10 slots seems like a good default. Each slot can either be empty or hold a packet that I am trying to send. To ensure correct ordering of the packets and to help make sure that I don’t use up all the slots, I would avoid acquiring a new slot for a packet if there is already another slot trying to send a packet to the same receiver.
For the RX unicast packets, using slots could also help. It would allow the library to be in a state where it’s not ready to receive packets from a particular sender, and it won’t acknowledge packets from that sender, but it is ready to receive packets from other senders. Received broadcast packets should probably go in a circular buffer though.
I knew I could count on you to give some constructive feedback.
Admittedly, yes, this is a potential problem. I decided that it is fairly unlikely to happen, so I didn’t worry about it too much. However, it would be easy to solve by automatically having a separate queue for ACKs themselves, where the ID of the message being ACK’d would also be included in the ACK. That way we wouldn’t have to potentially get rid of actual messages when trying to find an ACK. That being said…
This is a much better idea than mine! Wouldn’t be too tough to implement, either. The interrupt handler would be responsible for going through the TX slots and (re)transmitting (un)ACK’d packets, meaning we don’t have to use blocking TX functions. There are some implications I’ve been considering.
Using the approach outlined above, using slot S to send a command to some Wixel W that doesn’t exist will result in slot S being used up permanently, without some additional measures to free up the slot later. Of course, as you say, it’s perfectly reliable if we can guarantee that the device W exists.
However, blocking TX functions are kind of useful, because they allow me to determine right away if a message was ACK’d or not. So, there are clearly trade-offs here.
I imagine each TX slot would store a variable for the number of tries remaining, or an expiration time in milliseconds. When the ISR gives up on sending the packet, it would mark the slot as expired, but not automatically delete it or reuse it. The main loop could check for expired TX packets, take an appropriate action (like deleting the non-responding Wixel from a list), and free the slot for other uses.
You could provide both types of functions and build the blocking functions on top of the non-blocking ones.