Wixel Mesh Network?

Hello,

I’ve been trying to find an inexpensive solution for a project. I’m trying to setup a network of sensor modules around the house, some of which would also control the lights in each room. For this I need a bi-directional link, as I need to know the current light status to correctly toggle them.

From the specs, the Wixels seem like a perfect little device to power this, however, digging around, I could not find any similar projects. There are one-way many-to-one apps and it’s close to what I want, but not quite.

Essentialy I want to have one master wixel and a number of satelites. satelites would not only need to talk and listen to the master, but also relay data for other satelites, ones that are out of range of the master, extending the range of the network.

My question is this. Do you think Wixel is suitable for a mesh network like this? Is it worth trying to experiment with the apps or should I look somewhere else? Or maybe someone already doing a similar project?

Hello, Armon.

I don’t follow your reasoning. Suppose you have three sensors that affect the state of a light. I would have all three sensor devices just transmit their readings to whoever wants to hear. I would have the light-controlling module listen for the sensor readings over the radio and decide what to do based on the current sensor values and the current state of the light. No bidirectional communication is required; the sensor devices don’t need to know anything about the light and the light-controlling device doesn’t need to transmit anything. You could implement this simple example by running the wireless_adc_tx app on the sensing Wixels and running a modified version of the wireless_adc_rx app on the Wixel controlling the light. Those apps are available in the Wixel SDK.

Actually, the wireless_adc_tx/wireless_adc_rx apps I mentioned are a good example of many-to-many communication because you have can have as many transmitters and receivers as you want. The IO repeater app, which can be downloaded from the Wixel User’s Guide or in the Wixel SDK, is an even better example because every Wixel can be a transmitter, receiver, or both.

The Wixel hardware is suitable for a mesh network like this, but unfortunately no one has written any apps that do actual mesh networking like you describe where data can be relayed among Wixels to extend the range of the network. Maybe the Wixel range will be good enough for you and you won’t need a mesh network? There’s a trade-off between Wixel distance and percentage of packets lost, so if you decide to not care too much about the number of packets lost you can probably get more range than the advertised 50 feet.

–David

Thanks for reply.

For my project, I want to equip each room with a single device that contains a wixel, a relay or two and a couple of sensors as one single package. The relay is for actual light switching. The device would need to transmit data from sensors to a single master wixel (connected to pc) and listen to its commands. Unfortunately I do not see how it can be done without bi-directional communication.

Interesting enough, I have received my pair of Wixels today soon after I made the post. I did not think they’d arrive so quickly. I’ve loaded the Wireless serial app on them and have tested the range. While it reaches into the next room no problem, it will not cover the whole house.

I have to say that these little wixels are very impressive and it’s great news that they can be used for a mesh network.

I’ll play around with the apps you mentioned and see what works.

one question though, when wixel is connected, it’s name is a short hash. Are these serial numbers unique? can they be used as IDs in the code?

Thanks!

Yes, each Wixel has a unique, random, 32-bit serial number and you can use it in your code.

If you’re willing to learn a lot and write your own Wixel apps then yes, you could make a mesh network. Otherwise you’ll probably have to just settle for many-to-many communication (not mesh).

–David

I’ve been trying to understand how the radio works.
Got stuck on one point:

ISR(RF,0){ … }

what exactly (and where?) generates this interupt? I cannot find any mention of RF variable or procedure anywhere.

The radio hardware that is built into the CC2511F32 generates the interrupt. If you look in cc2511_map.h you can see that

ISR(RF, 0)

expands to:

void ISR_RF() __interrupt(RF_VECTOR) __using(0)

which in turn expands to:

void ISR_RF() __interrupt(16) __using(0)

–David

I see, thank you for the explanation.

2 more wixels arrived today. Will try to sniff the radio_link traffic tonight. As it turned out it doesn’t do much with just one unit.

Hello again. I’m fiddling about with the very simplistic mesh protocol. Need some advice.

in the radio_link library, are there any limits to the packet size?

If there are multiple devices within range of each other, how can I ensure they don’t interfere?

I may’ve just answered my own question. the 0 byte of the packet is the length, would that mean the max size is 255 bytes?

Hello.

The radio_link library is not suitable for making a mesh network unless you heavily modify it. You might want to use the radio_queue library instead.

Yes, there is a limit imposed by the radio_link library. See the documentation of radioLinkTxCurrentPacket for more info. If that limit is a problem, you could easily change the radio_link library to have a different limit in your copy of the Wixel SDK.

One way to avoid interference is to put groups of Wixels on different radio channels (at least 2 channels apart), but of course that defeats the purpose of making a mesh network. One way to help avoid packet collisions it to use some randomness in the timing of your packets. The radio_queue and radio_link libraries both do this.

–David

slowly learning how it all works.

I’m working with 3 wixels at the moment, 1 sniffer and a pair for development.

There is a slight problem: Every time I need to upload a modified app, I first need to shut down the listening console. After upload, the console needs to be reconnected. Also if the wixel gets disconnected from USB and reconnected back, the console needs to be restarted.

Do you know if there’s a way to keep a console in “listening” state? where it would passively listen to a particular port?

That would considerably speed up debugging.

No, I don’t know of any great way to solve those problems. As far as I know, that’s just how Microsoft’s USB-to-serial driver (usbser.sys) behaves. Only one program can have a handle open to the serial port at a time, and if you have a handle open while you are unplugging and replugging the device, bad things happen.

My one suggestion would be to use a terminal program that makes it easy to temporarily disconnect and reconnect. In Tera Term, I just press Ctrl+I to disconnect, but the Tera Term window still stays around. Then I press Alt+N and maybe a few other keystrokes to reconnect.

–David

Thanks, I’ll try that.

I’ve decided to start with discovery mechanism. So far, got the following working:

Master sends broadcast packet (senderID and recipientID are the same), those who hear it, respond and in turn send their own broadcast packet.

I’m modifying the adcRecport structure to make packets easier to manage:

struct adcReport{
uint8 length;
uint8 senderID[4];
uint8 recipientID[4];
} adcReport

the next step is to keep adding fields to the structure:

struct adcReport{
uint8 length;
uint8 senderID[4];
uint8 recipientID[4];
char command;
char data;
} adcReport

This is where the problems start. the data in the command field does not get transmitted correctly. I get garbage on the other end.

What is the correct way to send a string inside a packet?

Could the problem be because I’m creating a txPacket with adcReport type and converting to (uint8) later? (as required by radio_queue library)

You shouldn’t convert an adcReport to a uint8; you probably meant uint8 * which is different.

Basically you just need to make sure that your packet doesn’t exceed the the maximum length allowed by the radio_queue library and ensure that both sides of the transmission agree on the format of the packet. If you want to send a string, then you will need to put an array of chars or uint8s somewhere in the packet; “char data;” will hold a single character, not an entire string.

–David

Sorry, you’re absolutely right, my mistake.

i meant (uint8 *) txPacket;
also in the struct it was supposed to be

char * command;

which should allow me to store a string. I understand I need to put a null at the end.

I’ll check if the packet size is set correctly. I have to do it manually as, for some odd reason, sizeof(rfPacket) does not return the correct value of packet size.

As I said before, you should put a char array in your packet. You should NOT put a pointer to a char (char *) in the packet. That won’t work. A pointer is just the memory address of some data in the Wixel. Because different Wixels have different memories, the receiver Wixel can’t really do anything with a pointer that it gets from the transmitter Wixel. Instead you should transmit the actual data. For now, you should just set a maximum length for your strings and make a char array of that size.

struct mypacket
{
  char command[6];
}

–David

Hello again,

I’m trying to decide on the routing algorithm. One option is to have each node store the whole routing table. But it may grow rather large.

The spec sheet states that wixels have 4k of RAM and 29KB program memory size.

Is it possible to store data in the program memory area?

Hello, Armon.

Yes, you can read and write data from the flash (program) memory. Writing to flash is a lot harder than writing to RAM but it can be done. You can read about it in the “Flash Controller” section of the CC2511F32 datasheet.

–David

Encountered another problem.

After I tested broadcast->response mechanic with 4 wixels, I continued developing with only 2.

Right now, only 1 wixel can reply to broadcast. Powering on more than 1 will result either in no packets recieved or recieving packet from one of them, usually one with strongest signal.

I do not understand where I have gone wrong:

typedef struct header
{
    uint8 length;
    char type;
    uint8 senderID[4]; //
    uint8 recipientID[4];
    uint8 hops;
} header;

void transmitService()
{

    header XDATA * txPacket;
    static uint16 lastTx = 0;

    if ((uint16)(getMs() - lastTx) >= param_report_period_ms && (txPacket = (header XDATA *) radioQueueTxCurrentPacket()))
    {
        lastTx = getMs();
        memcpy(txPacket->senderID,serialNumber,4);
        memcpy(txPacket->recipientID,serialNumber,4);
        txPacket->length = sizeof(*txPacket);
  
        printf("sending packet from:%02X-%02X-%02X-%02X to:%02X-%02X-%02X-%02X %5u",
        		txPacket->senderID[3],
        		txPacket->senderID[2],
        		txPacket->senderID[1],
        		txPacket->senderID[0],
        		txPacket->recipientID[3],
        		txPacket->recipientID[2],
        		txPacket->recipientID[1],
        		txPacket->recipientID[0],
                   (uint16)getMs()
                   );
        (uint8) txPacket;
        radioQueueTxSendPacket();
    }
}

void respondToDiscovery(header XDATA * rxPacket)
{
		static uint16 lastTx = 0;
	    header XDATA * txPacket;
	    // We recieved discovery packet.
	    // respond with our serial number
	    if (txPacket = (header XDATA *) radioQueueTxCurrentPacket())
	      {
	        lastTx = getMs();
	        memcpy(txPacket->senderID,serialNumber,4);
	        memcpy(txPacket->recipientID,rxPacket->senderID,4);
	        txPacket->type = 'i';

	        txPacket->length = sizeof(*txPacket);
, sizeof(uint8),sizeof(int));
	        printf("command: %c, length: %d, full length: %d \n\r",txPacket->type, 1, txPacket->length);
	        (uint8) txPacket;
	        radioQueueTxSendPacket();
	      }


}

uint8 compareID(uint8 * id1, uint8 * id2){
	uint8 i;
 	for (i = 0; i < 4; i++)
 	{
 		if(id1[i] != id2[i])
 		{
 			return 0;
 		}
 	}
 	return 1;
}

void parse()
{
	header XDATA * rxPacket;
	if ((rxPacket = (header XDATA *)radioQueueRxCurrentPacket()) && usbComTxAvailable() >= 64)
	    {
		printf("\n\rnew packet: \r\n from:%02X-%02X-%02X-%02X to:%02X-%02X-%02X-%02X\n\r length: %i type: %c \n\r =========================\n\r",
					rxPacket->senderID[3],
	               rxPacket->senderID[2],
	               rxPacket->senderID[1],
	               rxPacket->senderID[0],
	               rxPacket->recipientID[3],
				   rxPacket->recipientID[2],
				   rxPacket->recipientID[1],
				   rxPacket->recipientID[0],
				   rxPacket->length,
				   rxPacket->type);
		(uint8) rxPacket;
			radioQueueRxDoneWithPacket();

		if(compareID(rxPacket->senderID,rxPacket->recipientID) == 1){
			printf("recieved broadcast\n\r");
			// respond to a broadcast
		      printf("\n\r responding to: %02X-%02X-%02X-%02X \n\r",
				               rxPacket->senderID[3],
				               rxPacket->senderID[2],
				               rxPacket->senderID[1],
				               rxPacket->senderID[0]
				               );
			  respondToDiscovery(rxPacket);
		}else if((compareID(rxPacket->recipientID,serialNumber) == 1)) {
			printf("recieved broadcast packet");

	    }else{
		      printf("recieved responce from: %02X-%02X-%02X-%02X",
		               rxPacket->senderID[3],
		               rxPacket->senderID[2],
		               rxPacket->senderID[1],
		               rxPacket->senderID[0]
		               );
		}
		putchar('\r');
		putchar('\n');
	    }
void getUSB(void)
{
	if(usbComRxAvailable())
    {
	uint8 usbCommand = usbComRxReceiveByte();

	switch(usbCommand){
		case 'm':
			toggleMasterMode();
			if(global_master_mode == 1)
				printf("mastermode activated, %i \n\r", global_master_mode);
			else
				printf("mastermode disabled, %i \n\r", global_master_mode);

			//toggle mastermode
			break;
		case 'b':
			initiateDiscovery();
			break;
		case 's':
			printStatus();
			break;
		case 'p':
			printRoutingTable();
			break;
		default:
			break;
	}
    }
}
void main(void)
{
    systemInit();
    usbInit();
    radioQueueInit();

    while(1)
    {
        updateLeds();
        boardService();
        usbComService();
        getUSB();
        parse();
        transmitService();
    }
}

}

or pastebin for some easier to read highlighting: http://pastebin.com/kT6g1MmB

Am I not using the TxQueue correctly? It was my understanding that it should check the channel is open before transmitting. Or am I not processing the packets fast enough?

The exact behaviour is this:

1 remote wixel: perfect, 100% responce
2 remote wixesl: 30-40% of responce from 1, 60% no responce at all.

I think i found the problem.

radio_queue.c

This layer does not transmit packets as quickly as possible; instead, it listens for incoming packets for a random interval of 1-4 ms between sending packets.

I have misunderstood what you said about the interval delay earlier. The delay is implemented between packets, so the radio can receive new ones and not before each individual packet.

In my app, the wixels respond to the broadcast at exactly same time, interfering with each other.
A short delay just before sending the packet should solve that.

Could you advice me on the best way to check if the channel is free after the delay?

Thanks!