Wednesday 23 April 2014

(Ab)using CurrentCost dev boards part 4

Last time around, I'd managed to get as far as starting and stopping the transmitter. I also alluded to the fact that it was slightly more difficult than I'd expected! I also needed to tap the receiver in order to see what's going on! First thing, in order to be able to see that we've transmitted valid data, we need to be able to receive. The CurrentCost EnviR base is already set up correctly for receiving, which should make life easier. So, we need to take the receiver apart and see what we can find.





One of the things you'll notice is an RF module with a red antenna wire nearby. To the left of that, there's silkscreen marking for an alternate RF receiver, with twelve connectors underneither. As far as I have been able to determine, the RF module is a HopeRF RFM01, which is the counterpart to the module used in the transmitter. There are two variants of it - the SMD version, which is used in the picture above, and a DIP package, which has twelve pins on the underside.



Helpfully the connections for both the SMD and DIP modules are connected together, so we have an easy place to tap the signal without having to even solder anything! Lastly, the silkscreening for the seperate components seem to be for RF Monolithics RXC101 so CurrentCost could have the choice of two different RF modules, or place the components direct on the board. Yes, this does mean that the RFM01 module uses the RXC101. On another aside, you might note that there's a programming connector with silkscreen next to it - this is the same pinout as the CurrentCost Dev board, so I could have saved myself some time by looking at this rather than reversing the connections!

In this instance, the RFM01 is configured to use an SPI interface, so once again it's time to break out the Bus Pirate - this is becoming a very useful tool! For the SPI bus we need to connect five wires. Using the DIP picture above, we can connect the following wires:

DIP (pin num)
Bus Pirate
SDI (3)
MOSI
SCK (4)
CLK
nSEL(5)
AUX
SDO (6)
MISO
GND (8)
GND

Now we can use the SPI sniffer on the Bus Pirate to observe messages that are received, testing with a real CurrentCost device first of course! This is done in exactly the same way as sniffing the SPI bus in the previoud Part 2.5.

Now we can get on with coding! Basically, once we start to transmit, the transmitter will pulse it's nIRQ line each time it wants a new bit. All we need to do is supply the bits! One thing I discovered from reading the RXC101 datasheet (page 15) is that all transmissions have to start with a preamble (0xAA) and then a sync word (0x2DD4), which is hard coded. Without this, the receiver will not receive the message - I know, I tried without to begin with!

Building on the code we had previously (step 3), I added a bit more. I'm not reproducing the code from before, as it'll simply take up space.

// Data to transmit
uint8_t txBuffer[8] = { 0x01, 0x02, 0x04, 0x55,
   0xAA, 0x04, 0x02, 0x01 };

void main() {
 InitRadio();   // Configure the radio
 TXData(txBuffer);  // Send the data!

 while(1);   // Stop!

}

// Transmit the datablock 
void TXData(unsigned char *txBuff) {
 uint8_t cnt;

 StartTX();   // power up the TX
 __delay_us(100);

 SendFSK(0xAA);   // Transmit header
 SendFSK(0x2D);   // Sync word
 SendFSK(0xD4);   // Sync word

 for (cnt = 0; cnt<9; cnt++) {   // data packet
  SendManFSK(txBuff[cnt]);
 }
 WaitFSK();   // wait for last bit to be transmitted
 StopTX();   // Turn off TX
}

// Manchester encode and send one byte
void SendManFSK(uint8_t data) {
 uint8_t cnt = 8;
 while (cnt--)
 {
  if (data & 0x80) {
   Sendbit(1);
   Sendbit(0);
  }
  else {
   Sendbit(0);
   Sendbit(1);
  }
  data <<= 1;
 }
}

// Send raw FSK byte - not manchester encoded
void SendFSK(uint8_t data)
{
    uint8_t cnt=8;
    while (cnt--)
    {
        if (data & 0x80) {
            Sendbit(1);
        } else {
            Sendbit(0);
        }
        data <<=1;
    }
}

//  Sends one bit of data
void Sendbit(uint8_t data) {
 while (!nIRQ);   // wait for nIRQ to toggle
 while (nIRQ);   // high then low (1.6uS)
 FSK = data;
}

// Wait for nIRQ before stopping TX
void WaitFSK(void)
{
 while (!nIRQ);   // wait for nIRQ to toggle
 while (nIRQ);   // high then low (1.6uS)
}

The observant reader might notice the sudden appearance of Manchester encoding, and be wondering where this came from. I'd come across some code written by GanglionTwitch, which was for a DIY radio receiver for CurrentCost sensors. This code can be found at http://gangliontwitch.com/ccPower.html. Knowing the data is manchester encoded is a great help, although I have no idea why it is encoded in such a way. The TX and RX modules already synchronise, so I can only presume it was a hangover from a previous iteration of code that needed some form of clock sync. Manchester encoding has the advantage of transmitting both data and clock signals together, but takes twice the bandwidth to do so.

After compiling the code I uploaded the output to the CurrentCost Dev Board, started the SPI sniffer and powered up the Dev Board. The result? Absolutely nothing! Somewhat disheartened, I used my radio to listen for a transmission, and I could definately hear RF coming out. I went through my code and didn't spot any obvious issue. I spent a great deal of time trying to figure out what's going on. Eventually, I put an oscilloscope on the inputs to the transmitter and discovered that sometimes the code was missing the nIRQ request for the next bit . I then disassembled the code that'd been produced, and things make a bit more sense... The two nice simple while statements turned into something like the following assembly:

WaitFSK: GOTO LoopA
LoopA:  BCF STATUS, 0x5
  BCF STATUS, 0x6
  BTFSS PORTA,0x2
  GOTO nxtA
  GOTO nxtB
nxtA:  GOTO LoopA
nxtB:  GOTO LoopB
  GOTO LoopB
LoopB:  BTFSC PORTA, 0x2
  GOTO nxtC
  GOTO nxtD
nxtC:  GOTO LoopB
nxtD:  GOTO Done
Done:  RETURN

I don't expect people to know PIC16 assembler, so just as explanation here: BCF uses a single instruction cycle, BTFSS/BTFSC uses one or two cycles, and GOTO always uses two cycles. That's 16 instruction cycles at a minimum to get through the code. Given the PIC uses 4 clocks cycles per instruction, and the default clock is 4MHz, the outcome is that the code isn't able to always capture the nIRQ activation and hence sometimes misses it. I took a twofold approach to fixing this - I increased the clock speed to 8MHz, and I made Sendbit() and WaitFSK() more streamlined by resorting to assembler:

//  Sends one bit of data
void Sendbit(uint8_t data) {
#asm
    sla:
        BTFSS 0x5, 0x2      //while(!nIRQ);
        GOTO sla
    slb:
        BTFSC 0x5, 0x2      //while(nIRQ);
        GOTO slb
#endasm
        FSK=data;
}
// Wait for nIRQ before stopping TX
void WaitFSK(void)
{
#asm
    wla:
        BTFSS 0x5, 0x2      //while(!nIRQ);
        GOTO wla
    wlb:
        BTFSC 0x5, 0x2      //while(nIRQ);
        GOTO wlb
#endasm
}

With these changes WaitFSK will take a minimum of 4 instruction cycles, a fourfold speedup. Mix in the clock speed increase, and it is about 8 times faster than the original C code. To be honest, I should have written this as an interrupt handler, as the nIRQ output is wired to the PIC's external interrupt pin. This was (not so) fast and dirty code though, and I just wanted results.

Compiling and uploading this code to the CurrentCost Dev board results in some action from the receiver - now the data I put in the txBuffer[] turns up at the receiver! As I'd captured a real packet from one of the spare CurrentCost Dev Boards, I was able to populate txBuffer[] with real data, and see the effect of transmitting it on the CurrentCost receiver. Now I can monitor the received data at the receiver, and I can transmit whatever data I want. This makes it possible to take apart the Current Cost protocol (C2) if you want to!

No comments:

Post a Comment