Previous Posts Here:
I am going to get into some technical details which hopefully isn't boring for the audience of this contest. If anybody, it is for the judges as it is important to understand the amount of work I have put into this on a technical level.
I had mentioned in a previous post that the chip kit pi has some issues that make it different that an Atmel ATMega (Arduino).
The Wire library for arduino allows you to send and receive multiple bytes of data between one stop and start sequence.
However, the ChipKit Pi (Pic 32) has an issue doing this.
Because I am sending commands to the ControlSwitch from the RPFS via I2C, I need to keep command intact. The commands consist of a controlbyte, a checkbyte, and other data up to 10 bytes. I worry about missing a byte or getting out of sequence. By using the wire library I though the built in I2C protocol would be suffice. Alas, I can only send 1 byte at time. This means that if I sent the ChipKit Pi 10 bytes, the OnReceive event handler will be called 10 times. So I have to keep "track" of what is going on in order to completely process a command. That is, there needs to be persistence between the event handler calls to keep track of the current status of a command to ensure all information is received and valid before doing what the command tells it to do.
So I had to get creative and created a block algorithm for this. I used the block algorithm for all I2c communications except for the Magnetometer.
A high level general description is that a specific byte is sent that represents a block start. Another specific byte is then sent as a stop byte. The block algorithm does not allow repeated starts. That is that once a block is started, it must be specifically ended. If another start byte is sent before a stop byte, the start byte is counted as data in the block.
The down side to this is that the stopbyte can never be used for data. That is 1 value out of 256 values so I decided that is ok. Actually there is another byte that cannot be used either, the resetbyte.
This is sent to reset the block status and let the receiver know a brand new block is coming in. If the receiver is in the middle of receiving a block, that block is reset.
The resetbyte cannot be used as data either. So 2 values out of 256 cannot be used.
So A block of data:
[StartByte] [data1..10] [StopByte]
Up to 10 bytes can be sent at a time. The protocol is set so that way only 1 complete block can be had at one time. That is, if the receiver has not processed the completed block, a new block cannot be sent. To ensure this does not create a lock, the sender sends a reset block every time before sending a new block.
A quick flow chart of the block algorithm.
I apologize, the code display is not working well for me and stripping out my indentations. I will work on fixing it later.
Here is a snapshot of the code that sends a block of data. This code is running on the RPFS (Raspberry Pi)
//Block Definitions #define BLOCKSTART 204 define BLOCKSTOP 190 define BLOCKRESET 195 int SendBlock(int address,int *data,int count) bool error = false; //Send Block Reset if(wiringPiI2CWrite(fd,BLOCKRESET) == -1) error = true; //Send Block Start if(wiringPiI2CWrite(fd,BLOCKSTART) == -1) error = true; //Send the address if(wiringPiI2CWrite(fd,address) == -1) error = true; for(int i=0;i<count && !error;i++) if(wiringPiI2CWrite(fd,data[i]) == -1) error = true; if(wiringPiI2CWrite(fd,BLOCKSTOP) == -1) error = true; if(error) return -1; else return 0;
Here is an example of how the to receive a block, one byte at a time.
//Block setup variables bool blockStarted = false; int block[10]; int blockCounter = 0; bool blockCompleted = false; bool blockBlock = false; int blockSkipped = 0 ; //For chipkit Pi, numBytes = 1 always void I2CReceiveEventBlock(int numBytes) { //Every 2 bytes are our data pairs, writes come in groups of 3 unsigned char cb, cbc, reg; cb = Wire.receive(); //If blockBlock is set we cannot get a new block until this one is processed if(!blockBlock) { //Block Start if(cb == 204) { blockStarted = true; blockCounter = 0; return; } //Block End if(cb == 190) { if(blockStarted) { blockStarted = false; blockCompleted = true; blockBlock = true; return; } } //Block Reset if(cb == 195) { blockStarted = false; blockCompleted = false; blockCounter = 0; } //Data if(blockStarted) { block[blockCounter++] = cb; blockCounter %= 10; } } else blockSkipped++; }
When a block is completed, you can see a variable on line 41 that is set, blockCompleted = true.
In the main loop of the code, you check this variable routinely and if it toggles to true, then you know you have a new block of data to process.
The ProcessBlock function in the whole code takes the data portion of the block and parses it out. The first byte of the data portion is usually the "register" I wish to read or write to. The important thing is that after a block is processed, that the variable blockBlock is set back to false so the event handler can resume getting a new block. You can see on line 18 above if this variable is set to true, the protocol will start skipping bytes.
The files I2C.cpp and I2C.h contain the sending protocol. The receiving protocol is part of the ControlSwitch and is embedded in that code. It is also embedded into the Arduino code.
ToDo: Make this a library that can be used for both the ChipKit Pi, and the Arduino.
Top Comments