Simple driver code for Microchip MRF24J40 radio

Update February 2017:

User “actondev” on the Microchip Forum has posted a version for the PIC16. I haven’t tested it.

Click this to download it: MRF24J40_actondev_forPIC16.zip.


Update November 2016:

I now have this working with the MRF24J40MD module. No changes at all were needed from the code for the MRF24J40MB.


Back in September 2011 I wrote about the rocket telemetry system I built using the Microchip MRF24J40MB radio module.

As I mentioned back then, I ended up re-writing Microchip’s “MiWi P2P” stack to vastly simplify it for my application. A few people have asked for a copy of my simplified driver code, and today I’m posting it (after having cleaned up a few loose ends).

The radio supports IEEE 802.15.4 on the 2.4 GHz ISM band. The “-MB” is Microchip’s long-range module – it has the radio, antenna, power amplifier (PA), and low noise amplifier (LNA), and it’s good for ranges of 2500 meters or more (line-of-sight, outdoors). It comes pre-approved by the FCC for unlicensed use. You can read my old post for more info about the module.

File package

To get started, download the ZIP file from here.

Update 2012-03: “maxruben” from the Microchip Forum has ported this driver to the PIC24. Click here to download his PIC24 version. I haven’t tested it, but he says it works. (Thanks for permission to post this!)

I’m also told the code has been successfully ported to the PIC18, but I don’t have a copy of that. (If someone sends me one, I’ll post it here.)

This contains a whole buildable project (for MPLAB IDE v8.83) that works on my PIC32MX440-based platform.  I’ve built it with C32 v1.11b and v2.02; it should be trivial to port it to other MCUs (see notes about that below).

The files include a very short demo program in main.c that shows how to use the driver to send and receive simple packets.

The driver itself consists of 3 files:

MRF24J40.c – Driver source code.
MRF24J40.h – Headers and public function declarations.
radioAddress.h – Sets the address for your radio.

Now would be a good time to unzip the files and have a quick look at the source code.

Oh, and:

Software rights: I hereby grant everyone and everything in the universe permission – to the extent I have rights to grant – to use and modify this software for any purpose whatsoever. In exchange, you agree not to sue me about it. I make no promises. By using the design you agree that if you’re unhappy the most I owe you is what you paid me (zip). That seems fair.

Be aware that the original MiWi P2P v3.1.3 code this started from (of which there might not be any code left) was copyrighted by Microchip – they offer it free for use with their hardware (which is all it’s useful for), and I doubt they’d want to sue their own customers over it, but talk to them if you have concerns.

That having been said, if you work for Microchip (this means you, Yifeng) and find this code useful enough to refer it to customers, or if you want to supply it directly, you are very welcome to do so. I’d appreciate (but do not demand) in return (a) credit in the source code to this posting on NerdFever.com, and (b) a small token of your appreciation. A RealICE would be great. If that’s too much, how about a Microchip T-shirt or coffee mug? (I already have a ICD3 and a Microchip bag; but swag is good. You know how to find me.)

Update: A free RealICE arrived about 2 weeks after I posted this.  Thank you, Microchip (and Yifeng)!

General concept – Initialization

Call RadioInit() to initialize the radio.

General concept – Transmission

To transmit a packet, fill out the “Tx” structure to describe the packet, then call RadioTxPacket().

General concept – Reception

Call RadioRXPacket(). If there is at least one received packet the return value is non-zero (it returns the number of un-processed received packets) and the next packet is described by structure “Rx”.

Do what you want with the packet, and then call RadioDiscardPacket() to throw away that packet. Now you can call RadioRXPacket() again to get the next one (if any).

API – Transmitting

void RadioTXRaw(void);

Low-level routine to transmit a single packet described by Tx.

The Tx structure must be completely setup before calling this routine. (Don’t set the lqi or rssi fields; these are used only on received packets.)

It does no error checking – it assumes the transmitter is not busy, and does not do anything automatically (like incrementing the frame number or recovering from crashes). It does not block.

This routine is not normally used as an API, but it is available for transmitting unusual packets. For normal use, call RadioTXPacket() instead.

void RadioTXPacket(void);

High-level API to transmit a single packet.

Fill out the following fields of the “Tx” structure to describe your packet, then call this routine to transmit:

unsigned frameType // normally PACKET_TYPE_DATA
unsigned securityEnabled // must be 0 (security not supported)
unsigned framePending // must be 0
unsigned ackRequest // usually 1
unsigned panIDcomp // usually 1
unsigned dstAddrMode // usually SHORT_ADDR_FIELD
unsigned frameVersion // must be 0
unsigned srcAddrMode // usually NO_ADDR_FIELD
UINT16 dstPANID // PAN ID of destination radio
UINT64 dstAddr; // address of destination radio
UINT8 payloadLength // length of payload field (bytes)
UINT8 * payload // points at payload start

You don’t have to fill out the other fields; they’ll be filled automatically. (See the RadioInitP2P() function below; this will fill out most of this stuff for you.)

Any fields that are the same as the previous packet don’t need to be re-filled out either – they will keep the values they had before, unless you change them. For many applications this means the only things you need to change from one packet to the next are the “payloadLength” and the contents of the payload itself.

If the transmitter is busy when this is called, this routine will block until the transmitter is done with the previous packet. Typically this is less than 3 milliseconds. If you don’t want to be blocked, do not call this routine until (RadioStatus.TX_BUSY == 0).

I’ve never seen it happen, but have read reports that (very rarely) the radio firmware can crash and the radio will need to be reset. If this happens, the driver will automatically reset the radio within 20 milliseconds; you don’t have to do anything but be aware of the (very unlikely) possibility of being blocked for 20 ms. If this is a problem, use RadioTXRaw() and manage it yourself.

Note that if the radio doesn’t get an acknowledgement from the far-end radio in a brief time (< 500 uS; see IEEE 802.15.4 for details), it will re-transmit the packet automatically. It will do this twice before giving up (for a total of 3 transmissions).

This improves the chance of the receiver getting the correct data, but it is not a guarantee. An acknowledgement means the receiver got the packet and computed a correct 16-bit CRC for it. It also releases the transmitter to send the next packet. Don’t rely on the ACK as indicating more than a high probability that the data was received correctly; a 16 bit CRC is short enough that you will occasionally see an errored packet that has a correct CRC by chance. If you really need to be sure, use your own CRC (I recommend a 64 bit one).

UINT8 RadioTXResult(void);

Returns status of the most-recently transmitted packet. The possible return values are:

0

No result yet because TX is busy or far end hasn’t had enough time to respond.

TX_SUCCESS (1)

Packet was received and acknowledged by far end (*1)

TX_FAILED (2)

Packet was not acknowledged by far end (*2)

*1 The number of re-transmissions used by the transmitter is in RadioStatus.TX_RETRIES (0, 1, or 2). Acknowledgement by the far-end is not a guarantee that the packet was delivered in all cases. In a multi-node network, it is possible in some configurations that the acknowledgment received was meant for a different transmitter (not you), because IEEE 802.15.4 acknowledgments are not addressed.

*2 It can happen that a packet was in fact received successfully at the far-end, but the acknowledgment was not received locally. In this case the local transmitter will attempt to re-send the packet up to 2 more times. If this happens, it is possible that the far-end receiver will get up to 3 copies of the same packet; it is up to the receiver to notice that they’re duplicates and discard the extras. (Check for duplicate frame numbers.)

If you don’t care whether the receiver got the packet, you don’t need to call this.

UINT8 RadioWaitTXResult(void);

Same as RadioTXResult(), except this routine blocks for up to 19 milliseconds, and always returns either TX_SUCCESS or TX_FALIED. (It waits for the result.) Normally you’ll get a result in < 3 ms, but it could be up to 19 milliseconds if the radio crashes.

Again, if you don’t care whether the receiver got the packet, you don’t need to call this.

API – Receiving

UINT8 RadioRXPacket(void);

This returns the count of received packets waiting to be processed, and puts the next packet to be processed (if any) into the structure “Rx”. If there are no received packets, it returns 0.

Received packets are buffered in RAM until you finish processing them. The buffer can hold PACKET_BUFFERS packets (defined in the .h file; must be a power of 2).

If the receive buffer overruns (you don’t process them fast enough), this will be reflected in RadioStatus.RXBufferOverruns (see the .h file).

If the return value is 0, there are no more un-processed packets in the buffer, and the “Rx” structure still describes the previous packet.

The next packet waiting to be processed is in Rx.

In most modes the radio hardware filters out received packets that aren’t addressed to your radio. But in some modes it doesn’t (useful for network monitoring, etc.) This routine gives you all received packets delivered by the radio hardware. If the radio doesn’t filter them, it is up to you to look at the address fields and determine if the packet was meant for you.

Also be aware that successive identical packets (same frame number) will be received if the far-end misses your acknowledgement (it will re-transmit). Check for that if you care.

void RadioDiscardPacket(void);

Discards the received packet in Rx, freeing up memory in the buffer for another packet.

Call this routine after you have processed each received packet.

API – General

BOOL RadioInit(void);

Call this once to initialize the radio. It will set the device address to MY_PAN_ID, MY_SHORT_ADDRESS, and MY_LONG_ADDRESS (all as setup in radioHeaders.h), and set the radio to channel 11.

You are free to change these at any time.

BOOL RadioSetChannel(UINT8 channel);

Tune radio to given channel. Returns true if it worked (if the channel number was a valid IEEE 802.15.4 channel number, usually in the range 11 to 25), false otherwise.

Note that this does not affect the channel used by the far-end radio(s).

void RadioSetAddress(UINT16 shortAddress, UINT64 longAddress, UINT16 panID);

Use this to change your own node address. If your address won’t change and you initialized with RadioInit(), then you never need to use this.

void RadioSetSleep(UINT8 powerState);

If passed a 0, puts radio to sleep. It will draw 0.245 mA while sleeping.

If passed a 1, wakes up the radio. It will draw approximately 28.4 mA when in receive mode and a nominal peak current of 130 mA while transmitting (but an average of only 65.8 mA as fast as I can get it to transmit.)

I have found that in practice you can completely disconnect the radio from Vdd (using a MOSFET switch) for short periods (up to a few seconds, anyway) while it’s in the “sleep” mode, without needing to re-initialize the radio when waking up (if you power it back up and then “wake” it, it works fine).

If you periodically cycle between power-off and “sleep” this way, you can effectively reduce the average sleep current to a small fraction of 0.245 mA.

UINT8 RadioEnergyDetect(void);

Does a single 128 microsecond energy detect on the current channel. Returns the RSSI for the channel.

This is mainly useful for protocols (like 802.15.4 and Zigbee) in which you listen before transmitting to be sure no other radio is transmitting on the channel at the same time. It also can be used to choose a channel with less noise.

General notes

All routines here do not block (they return immediately) unless noted otherwise.

There is code for interrupt-driven SPI transfers, but I never got it working (see thread on Microchip Forum about it; http://www.microchip.com/forums/m573732.aspx). The switch is SPI_INTERRUPTS; don’t turn it on.

The hardware SPI transfer does work, and is significantly faster than bit-banging the SPI transfer (which also works).

Defining the HARDWARE_SPI switch turns on the hardware mode. Commenting it out uses bit-banging. The hardware mode is preferable because it’s faster and requires less MCU cycles, but if your MCU doesn’t support it, you can use bit-banging.

PACKET Tx, Rx;

These contain full descriptions of the packet to be transmitted or received.

MRF24J40_STATUS volatile RadioStatus;

Complete description of radio state.

Addressing

Each radio has 3 types of addresses:

  • A “PAN ID” (16 bits)
  • A “short address” (16 bits)
  • A “long address” (64 bits)

At any given time, a radio is addressed by a combination of a PAN ID (shared by all other radios in the same network) and either a long or short address.

For the driver to receive a packet (in most modes), the incoming packet must have a destination address containing the PAN ID of your radio and the address of your radio (in either long or short form). It also must be transmitted on the channel that the receiver is tuned to.

These addresses are set during initialization in the RadioInit() function, based on the values of the macros MY_PAN_ID, MY_SHORT_ADDRESS, and MY_LONG_ADDRESS in radioHeaders.h.

Please choose your own values (don’t keep the ones in radioHeaders.h; otherwise everyone using this driver will be on the same PAN ID and address). You can change the values either by changing the macros in radioHeaders.h or by calling RadioSetAddress().

Demo code

The demo code gives a simple example of how to use the driver for communication between a pair of radios, both of which have the same address.

Files

The demo code adds the following source files to the driver:

hardware.c – Source for configuration of the hardware platform.
hardware.h – Pin definitions, etc. for the hardware platform.
debug.h – Debugging macros.
main.c – The demo program.

Platform customization

To get the demo to run on your hardware, you’ll have to modify hardware.c and hardware.h to match your platform. These files are written now to support a PIC32MX440 MCU on my own hardware. You shouldn’t have to modify any other files.

In hardware.h, make sure that macro BAUD_RATE matches your terminal baud rate, and the pin definitions (for radio control and SPI interface) match your platform.

In hardware.c, the function BoardInit() must initialize your platform, and trigger the radio ISR function (in MRF24J40.c) when the radio INT pin goes high. The ReadUART() function must return the ASCII code sent by the terminal, or 0 if there is no data from the terminal.

Running the demo

Run the demo program on two boards. They should be within radio range of each other.

Attach an ASCII terminal to the serial I/O of each MCU. (I used UART2.) A PC-based terminal program such as Hyperterminal or PuTTY works fine. The terminal should be set to 8 data bits, 1 stop bit, and no parity, and the baud rate to BAUD_RATE (I use 460,800 bps).

Type A, B, or C on either terminal. Each of the 3 keys should send a different message (in a single packet) to the other radio. The message is printed on the terminal of the receiver.

How the demo code works

The inner while() loop checks for received packets from the radio. If it finds one, it checks that the frame number is not duplicated from the previous packet (this can happen if the far-end missed our ACK and re-transmitted; usually only happens under very weak signal conditions). If it’s not a duplicate, it prints out the contents of the packet payload.

The switch() statement checks for keys from the terminal, and sends a packet containing a payload appropriate to each key.

Note that during initialization the function RadioInitP2P() is called. This initializes the Tx structure to send packets in a simple point-to-point mode. Each packet contains data, doesn’t use security (not implemented in the driver), requests an ack from the far-end radio, and is addressed to the same address as the local radio (both radios have the same address). It uses a 16-bit ‘short’ address. Here’s the code:

void RadioInitP2P(void)
{
Tx.frameType = PACKET_TYPE_DATA;
Tx.securityEnabled = 0;
Tx.framePending = 0;
Tx.ackRequest = 1;
Tx.panIDcomp = 1;
Tx.dstAddrMode = SHORT_ADDR_FIELD;
Tx.frameVersion = 0;
Tx.srcAddrMode = NO_ADDR_FIELD;
Tx.dstPANID = RadioStatus.MyPANID;
Tx.dstAddr = RadioStatus.MyShortAddress;
Tx.payload = txPayload;
}

Once you’ve called this to initialize the Tx structure, further packets can be sent just by changing the contents of the payload buffer (*Tx.payload), setting the payload length (Tx.payloadLength), and calling RadioTxPacket().

See the demo source code (main.c) to see how this is done.

Further reading

If you want to understand how the driver works in detail, before looking at the source code, skim over:

http://standards.ieee.org/getieee802/download/802.15.4-2006.pdf

and

http://ww1.microchip.com/downloads/en/DeviceDoc/39776C.pdf

The former is the IEEE spec. The latter is Microchip’s datasheet for the radio – look at sections 3.2 (Initialization), 3.11 (Reception), and 3.12 (Transmission). You can skip the rest.

82 thoughts on “Simple driver code for Microchip MRF24J40 radio

  1. Thank for your paper
    I have two MRF24J40MA moduls, i want design a board with PIC 24 conect with this modul via SPI, You can give me schematic and the steps to config for MRF24J40MA modul by your code. I am a new RF guy, so i dont have experience about it, Please show me some base steps.
    Thank you,
    thanh

  2. I haven’t used the MRF24J40 with the PIC24 (only with the PIC32). The PIC24 code came from someone else who modified my code to work on the PIC24.

    If you look at the rest of this blog site, you’ll find schematics for my hardware and the complete code that will build and run on that PIC32 based hardware.

    So the most basic steps I can offer:

    * Install MPLABE IDE and C32 on your PC
    * Build a PCB like mine (you can download the Eagle files from this site)
    * Download the source code
    * Build the object code
    * Burn the object to the PIC32 with a ICD3 or PICKIT3
    * Run

    Good luck!

  3. Thanks for this explaination related to the MRF24J40.

    I am using the MRF24J40MB chip for my project but facing a problem with ack, when i am doing the setting related to auto ack [ bit5 of rxmcr for auto ack; bit5 in header for expecting ack and bit2 in TXNCON ]. The transmitter transmits and tells maximum retries [ 3 times ] and transmission fails. can you suggest what may be the problem related with acknowledgement in auto ack.
    At the receiver I am getting the very first packet but ack is not being given.

    I am able to communicate without ack.

  4. Are you sure the receiver is actually sending the ACKs?

    I’d try setting up a 3rd radio for debugging – just set it to receive everything and print it out; that way you can see what is really going on.

    Or, you can get something like the Microchip ZENA (which does the same thing, already configured).

    Good luck!

    –Dave

  5. yes I am sure that I am getting my first packet only but it comes twice I am able to see it on hyperteminal.

    Also if i am setting for no ack then it works fine.

    I don’t have ZENA or any other packet sniffer.

    Please guide me if any other way is there to make it work?

    Is there any way to verify that receiver had send ack.

    Regards,
    -arvind

  6. Hi,

    iam using MRF24J40 RF transceiver, I have gone through the data sheet of MRF24J40 and Iam not able to understand how to start the communication. Iam facing a start-up issues.

    I am Planning to use PIC18F452 MCU and Intergrate MRF24J40 RF transceiver over SPI, The data sheet says as follows for INITIALIZING THE MRF24J40
    SOFTRST (0x2A) = 0x07 – Perform a software Reset. The bits will be automatically cleared to ‘0’ by hardware.
    2. PACON2 (0x18) = 0x98 – Initialize FIFOEN = 1 and TXONTS = 0x6.
    and soon on

    How can i Configure this, should i program MRF24J40 or should i code it my PIC18f452 and send configuration command SOFTRST (0x2A) = 0x07 over SPI
    Kindly suggest and correct me if i am wrong

    Regards
    Santhosh

  7. Hi Santhosh,

    Sorry for my slow reply – I was travelling all last week.

    The only way you can setup the MRF24J40 is by sending it commands over the SPI. The PIC18 (in your case) needs to setup SPI communications with the MRF24J40, then initialize and configure the radio by loading the registers as described in the data sheet. The only way to access the radio registers is via SPI commands.

    Have a look at the initMRF24J40() routine in my code (here on this site) to see how it’s done.

    Also, read over sections 2.14 and 2.15 in the datasheet carefully to understand how the radio registers work.

    I hope this is helpful.

    –Dave

  8. Hi Dave

    Thank you for taking the time and replying for my quire and the suggestion was very use full for my understanding. i have one more quire The data sheet says it supports Zigbee /miwi and miwi pro how to use this stack or how to configure them..

    Regards

    Santhosh

  9. Hi,

    I’m porting your driver to Linux, and i would like to see the sequence byte of a simple transmission (header + payload). Its important to me because my driver its not working, and i thinking its because the byter order i am using.

    Thanks a lot

  10. Hi Dave,

    Yes, i was reading the datasheet..
    In your Frame Control Header, Are you setting to 0x1061, correct?
    Isn’t a problem using two radio with the same address (MY_SHORT_ADDRESS) as you are doing?

    Thanks

  11. No, it’s not a problem.

    I set both radios to the same address. Actually, I’m using 3 radios, all set to the same address. Anything sent by any radio is received by all the other radios.

    You only need different addresses if you want some radios to ignore messages that are not addressed to them.

  12. Dave,
    This is very good. I’ve been trying to use the Microchip simple demo program and adapt it to my own project, utilizing the 18f2580 and the MRF24j40 and it’s been nightmareish.
    I’d like to abandon that and try to adapt your code. May I ask, what do I need to do to run it on C18 or do I need the C32 compiler for this one? and is it not a whole lot more than configuring the registers and definitions in the header files to get my 18f2580 to work with the radio module?

    As the fans above, please give me any guidance and I’ll try to supply you back with the PIC18 code for this cool module.

    Jay

  13. Hi Jay,

    It would be great to have a PIC18 version – a lot of people have asked for it.

    The current code is written for the PIC32 and the C32 compiler; you’ll need the C18 compiler to build it for the PIC18.

    I don’t think it will be difficult to port it if you’re familiar with the PIC18.

    Start with hardware.c; you’ll need to change the configuration fuses and adapt BoardInit() for the PIC18 and your hardware platform. Ditto for hardware.h; the declarations will have to match your hardware.

    You may want to check the hardware SPI code to make sure it matches the SPI peripheral on the PIC18 (spiPut and spiGet in MRF24J40.c); however even without that the bit-bang SPI routine should work as-is.

    Last, you’ll need to adapt the interrupt handler to match the C18 conventions for the ISR; it shouldn’t be more than changing the function header.

    In theory that should be it; the rest of the code ought to work without changes once that’s done.

    If you get stuck let me know and I’ll try to help.

    –Dave

  14. Dave, I’m having trouble with the structure code:

    UINT8 Val;
    struct
    {
    UINT8 TXIF :1; // transmission finished interrupt (TX no longer busy)
    UINT8 :2;
    UINT8 RXIF :1;

    beginning with the first line above I get these messages, then they almost identically repeat for the next 3:

    -Syntax Error:’;’expected, but ‘Val’ found
    -Invalid declarator expected'(‘or identifier
    -Declarator error
    -Inconsistent type

    The code appears correct but the compiler doesn’t like it. Would it be because of previous definitions that I can’t figure out or something that has to do with the 32 bit processor and need be translated to 16 bit code? Any hints?

    Thanks, the code looks great

  15. Hi Jay,

    I’m not sure from your email if it’s complaining about the UINT8 or the structure.

    For me, UINT8 is defined in plib.h (supplied by Microchip). If that’s the problem, you can just define it like this:

    #define UINT8 unsigned char

    If it’s the bit-field structure, I’m not sure what the problem is. I think that’s pretty standard C syntax.

    You could try:

    * Add a space after the colon (:)
    * Give the unused field (UINT8 :2;) a name – like this:

    UINT8 unused :2;

    Neither of those should break anything, and maybe they’ll make the compiler happy.

    Let me know if that helps.

    –Dave

  16. Dave, you are right on. The next code that choked it after defining the UINT8 was the UINT16. I’m reading, like I hadn’t, the MicroC for the PIC18 and picking up more that I didn’t realize. (I’m new to PIC’s but not new to electronics and C in general).

    Probably now have to define UINT16 as an unsigned double or something, right? I was trying to see if I’d figure it out ‘fore I needed to ask for help, although, I wanted to get back to you quick as I could on the suggestion you made to correct it the UINT8.

    I’m making great progress, changed the areas of concern to the PIC18 and am thinking I’m down to final program tweaks. I’ll contact you next in an email to give you specifics, ya mon?

    RockinJay

  17. Hi Jay,

    Here are the definitions I used to use (before getting them from Microchip’s plib.h file):

    #define INT8 char
    #define INT16 int
    #define INT32 long

    #define UINT8 unsigned INT8
    #define UINT16 unsigned INT16
    #define UINT32 unsigned INT32

    #define FLOAT32 float
    #define FLOAT64 double

    (I suspect the float definitions aren’t actually used in the radio code.)

    However you need to look in your C compiler manual and find out how to modify these defintions for your compiler. The idea is that “INTx” is a signed integer x bits wide (8, 16, or 32) and “UINTx” is an unsigned integer.

    The way C works is that “char”, “int”, “long”, etc. can be different lengths on different compilers – these definitions are meant to abstract that out, so you can be sure (for example) that a UINT16 is always 16 bits wide.

    So you need to go thru your compiler manual and make sure the definitions will get you what you want, and change them as necessary. (They are probably close to or exactly correct already, but you should verify.)

  18. Thank you very much Dave, for such a simple and well documented stack. I was willing to do some projects with the MRF24J40 transceiver, but I was overwhelmed by the complexity of Microchip’s P2P stack. Now I feel confident to integrate it in my PIC24 setup.
    Regards.

  19. Dave, I’m stumped by the expressions:
    BOOL RadioInit(void);
    BOOL RadioSetChannel(UINT8 channel);

    I’m using MicroC lite, BTW, and it doesn’t like the word “BOOL” while declaring the functions for the Radio.

    Can those be changed to “INT” or “void” with no effectual change?

    Next, in the function:

    void RadioInitP2P(void) – the compiler doesn’t like all the expressions of Tx.whatever – the error is:

    “Operator ‘.’is not applicable to these operands’TX’ ”
    (same error for each of the Tx.something members of the function) is that a Compiler thing do ya think?

  20. Jay,

    I’d do instead:

    #define BOOL unsigned char

    BOOL just means it’s a single-bit (0 or 1) variable; there’s no need to use more than 1 byte to store it, so “unsigned char” is better than “int” (“void” is not a good idea).

    Re the RadioInitP2P errors, it sounds like you’re not including the MRF24J40.h file.

    That file defines the structure PACKET and then Tx like this:

    extern PACKET Tx;

    Tx itself is defined near the top of MRF24J40.c:

    PACKET Tx, Rx;

    The syntax is standard C; I don’t think your compiler should have a problem with it.

    I hope this helps.

    –Dave

  21. Dave,

    For the life of me I can’t see why it’s not picking up the definition of the Tx structure in the MRF24J40.h file. It’s been included all along but won’t recognize the extern declairations. I added extern struct & union and the statement for Tx and Rx doesn’t give me trouble, but the 2 union statements are coming up in error.

    extern union MRF24J40_STATUS RadioStatus;
    extern struct PACKET Tx;
    extern struct PACKET Rx;
    extern union UINT8 RXBuffer[PACKET_BUFFERS][RX_BUFFER_SIZE];

    But the PACKET structures won’t seem to be recognized because I’m getting the same error I was:
    “Operator ‘.’is not applicable to these operands’TX’ ”
    The header file is in there, and the compiler still doesn’t see the Tx as the pointer. What am I missing?

    Jay

  22. Hi,
    that is a good work you’ve done there.

    I have one question. What would happen if i make 4 devices with the same addresses? will there be any problem with the acks or any other thing? or all the set will work normally?

    what i want is not to mess with your code and make my own frames in a “higher level”

  23. 802.15.4 ACKs aren’t addressed.

    So if you make multiple devices with the same address, each of the other 3 radios will send an ACK (if they get the packet). The transmitting radio will be happy if it sees any of them.

    If that bothers you (not being able to trust the ACK), then don’t use the same address for each radio. If you don’t care about the ACKs, then you can use the same address – it’ll work.

    Cheers,

    –Dave

  24. Dave,
    This code is much easier to understand than the Microchip version, many thanks!

    I do have an issue with using your code on my builtboard and microchip’s starterkit. I’ve made the appropriate changes to the configuration files for the PIC32MX340 and PIC32MX360. When I run the code on each it will only run until I’ve sent or received a message. I’m using a ZENA and see the acknowledgement. Then both boards freeze and it’s unclear as to what’s causing this. The 802.15.4 INT on both boards read 0 volts. Do you know why this is happening? I can’t send or receive multiple messages

    Thanks,

    Allen

  25. Hi Allen,

    Without knowing more, my first guess is that the interrupt service code isn’t getting executed.

    If you have an oscilloscope, watch the INT line and see if it ever goes high (even for a moment).

    Also try setting a breakpoint in the ISR – see if it gets there at all.

    –Dave

  26. Dave,

    First of all thanks for your code it has been very helpful in learning about the MRF24J40. I have been trying for the past couple of weeks to get the sleep current down I have 2 boards one with the MRF24J40 sleep current 170 uA and an another with the MRF24J40MB with sleep current 230 uA. I am running this on a battery and need the sleep current to be down in the 5 to 10 uA. Have you or anyone been able to get the sleep current less. I have read the datasheet and when through my initialization, sleep, and wake routines. I have configured it to be in deep sleep and using the wake pin not the internal clock.

    Thanks,
    Kyle

  27. Hi dave,

    Dave i am trying to check on 802.15.4 IEEE Frame format.. I have configured the radio and send some values.. But i am facing hell lot of problem in understanding a simple frame format i tried referring to IEEE 802.15.4 frame format. I tried to send a data frame.. This is how i have configured
    Frame Type Security

    b0 b1 b2
    Data Frame 1 0 0

  28. Hi Kyle,

    There was a thread on the Microchip Forum about this a year or so back. It seems the quoted sleep current in the datasheet (2 uA “Sleep Clock Disabled”) isn’t real – you can’t actually configure the hardware to do that. (At least this was the consensus back then; maybe a silicon rev since then has fixed it; I don’t know.)

    The only way I know to get the sleep current below what you’re seeing is to remove power from the radio module. If you put a MOSFET on Vdd to turn the power on/off, you can cut off the power and get the current to zero.

    I’ve found that if you put the radio to sleep first, then pull power, you can leave the power off for some period (I didn’t try longer than 20 seconds or so), then turn it back on and wake the radio – it’ll pick up where it left off without needing to be reset.

    So, therefore, if you put the radio to sleep you should be able PWM the power at some low duty cycle to get the average current almost arbitrarily low. (Note that I haven’t tried this other than the experiment just mentioned.)

    Good luck! (If you try this, I’d appreciate a comment here with your experience.)

    –Dave

  29. Hello I am using 2 MRF24J40 I can not see the behavior described above, I only change:

    MY_LONG_ADDRESS
    MY_SHORT_ADDRESS

    IS there another needed change?

  30. Hi Edgar,

    What “behavior described above” did you have in mind?

    What hardware platform are you running on? Under “Platform customization” the post says:

    To get the demo to run on your hardware, you’ll have to modify hardware.c and hardware.h to match your platform. These files are written now to support a PIC32MX440 MCU on my own hardware.

    You understand that means you need to either build an exact duplicate of my hardware, or modify those two files to match your own hardware, right?

    –Dave

  31. Hi Edgar,

    You’re welcome to post a link to your code and schematics here; maybe someone will be able to help you.

    If you can be more specific about what you did and what problems
    you’re having, I might be able suggest something. Lots of people have
    been able to make this code work for them successfully.

    Best regards,

    –Dave

  32. Hi Dave

    Thanks a lot for the reply.
    I am using a PIC32MX575F256L on both boards, the spi channel used for the radios is the channel 2 as is in your code, basically I only changed the addresses (short and extended, PAN ID is the same for both boards), the MCU pins: WAKE,RESET,INT and CS.
    The UART channel is the channel 2 and it works ok.
    The internal clock is 8MHz so the configuration clock is the same.

    One of my boards has push buttons, I use a push button to send a Radio Packet but the other board does not receive that message.

    What are the common reasons for this situation?
    Do I need to comment out something?

    Best regards

  33. I forgot to update the associated interrupt number for the RF_INT_PIN pin on my board.

    I will test it later

    Could be that the problem?

    Thank you

  34. Hi Edgar,

    I looked very quickly at your code and schematic.

    I don’t have time to look more carefully until later in the week (maybe Friday) but the code is close enough to my version that I think I can probably figure out what the problem is.

    Can you tell me:

    * Can you talk to the board over the serial port OK?

    * Does the INT pin on the radio ever go high? (Check with an oscilloscope).

    * If you set a breakpoint in the ISR, does the PIC ever get there?

    * Do you have a ZENA or similar? If you do, can you tell if either radio ever transmits anything at all?

    I’ll have a look later this week and let you know what I can.

    –Dave

  35. Hi Dave

    *I can receive messages over the serial port but the keys that I send are not detected (at the specified baud rate)

    *Rigth now I do not have an oscilloscope but if I mesure the INT pin voltage, it is always in high state (3.3v)

    *The PIC never goes to the ISR.

    *I do not have a ZENA or another kind of sniffer.

    I tried to call a function inside the infinite loop, with all the excecuted operations in the ISR but the message neither is detected =(

    Thank you so much Dave!

    I hope you have time to look at the possible root cause of my problem.
    I will be waiting for your comments.

    Regards

  36. Hi Edgar,

    I apologize for taking so long to get to this – the holidays have been busy here.

    I had a quick look today.

    Before I offer comments, a few things I’d like to confirm I understand correctly:

    1 – You have 2 devices that are talking to each other, one is the “sonometro_v3”, the other is the “zigbee-pc”, correct?

    2 – The PC is connected to the “zigbee_pc”, the “sonometro_v3” is NOT connected to a PC, instead you push a button on that to send a packet, correct?

    3 – The code in “Node1_PC” goes on the “zigbee_pc”, the code in “Node 1” goes on the “sonometro”, correct?

    Comments:

    a) How many MRF24J40 radios do you have on each board? I see two (U3 and U5); there should be only one. Are these just alternate ways to populate the PCB, or do you really have 2 radios (that won’t work)?

    b) My code is written assuming BOTH radios have the SAME address. I see in radioAddress.h that you have the 2 radios using different addreses – try setting them both the same. (You can change them if you like, but you’ll have to make sure you have each unit send to the correct receiving address – it’s simpler if they are both the same address.)

    c) I think you have the SDI I/O pins connected backwards. You have to connect SDO (out) on the radio to SDI (in) on the PIC, and vice-versa. This alone will prevent it from working things. See my schematic (http://nerdfever.com/files/Rev4.2.pdf); this works. (To verify, attach an oscilloscope to the SDI pad on the radio; make sure you see signals from the PIC on this pin.)

    d) You’re using port SDI2 on the PIC; I used SDI1. I’m not sure you’ve made the needed changes to address the proper port (I haven’t checked this carefully; maybe you don’t need any changes.)

    e) You said the keys you send from the PC are are not detected; that worries me. Are you sure the PC is set at 460,800 bps, 8, N, 1? Check the signals from the PC on an oscilloscope to make sure they’re really there and the bits are 1/460800 seconds wide.

    f) In my experience it’s easier to get things working on a single hardware platform (same on both ends) than on two different ones. So my suggestion is to first get 2 of the “zigbee” boards talking to each other OK, and only then (once you are sure at least one end is working correctly) work on the other one.

    Please try fixing (b) and (c) above at least (and check against my schematic); let me know if that helps.

    Good luck, and Happy New Year,

    –Dave

  37. Hello Dave thank you for the reply.
    You understood all correctly (sorry for the poor explanation).

    I fixed (b) and (c) and I can see signals of life, but now the behaviour is that I only receive one message I commented out //if (Rx.frameNumber != lastFrameNumber) and I put breakpoints to catch other messages but only one is detected when I use the code that I share you.

    I tested it sending a counter periodically and in this way the number of received messages varies.

    void HeartBeat_Task(void)
    {
    static U08 cnt = 0x30;

    cnt++;
    if(cnt > 0x39)
    {
    cnt = 0x30;
    }
    Tx.payloadLength = 1;
    Tx.payload = &cnt;
    RadioTXPacket();

    I read that the transmission time is less than 3 millisecond, I send the periodic message above of this time.

    What could be the problem now?

    Thanks Dave and Happy New Year!!

  38. Hello Dave.

    All these things you understood correctly.
    I fixed (b) and (c) and I have a better behaviour, Now the radios can talk but I have a new problem, I send different messages but only the first message is received (sometimes two are received).

    When I send the messages waiting more time, It possible to receive more than one message (2 or 3), I read that the transmission time is less than 3 ms so I am in the range.

    What could be the reason of these behaviour?

    Thank you so much Dave your comments have been very helpful.

    Happy new year!!

  39. How does the TX_PENDING_ACK could affect in this problem?

    Another important information is that the board that I shall to reset for receive other message is the board connected on the PC (the board that receives the messages)

    -Edgar

  40. Dave it works excellent if I call these functions after receive each message:
    BoardInit(); // setup hardware
    RadioInit(); // cold start MRF24J40 radio

    If you have no idea what is happening I will continue working in this way

    Thank you so much

    -Edgar

  41. At a guess, you’re having problems resetting the interrupt.

    You can run it that way (reset after every packet), but you’ll probably reduce the maximum thruput, and may lose some packets while you’re resetting (depends on how often there are packets sent to receive.)

    Have a look at other users of the INT1 pin (other peripherals); it sounds like you have some kind of conflict that’s preventing the ISR from working properly.

    Good luck!

  42. Hello Dave, sorry for make a lot of questions…

    Is this code recommended for applications where is needed a network or only to P2P applications?

    I mean how could be avoided or handled a collision?

    Thanks again!

  43. Hi Edgar,

    You can use it in a network configuration if you want. However read the post – if you want some more complex things it might be easier to start from Microchip’s MiWi P2P code.

    I’m not sure what kind of collisions you’re worried about.

    –Dave

  44. Dave,

    It’s been a while since you heard from me. In the beginning, I was enthusiastic but a little clueless on the whole Microchip system. OK, I finally figured out a nice little 8 bit pic circuit utilizing the Microchip MiWi stack and the MRF24J40 radio modules – MPLAB X and C18 compiler.

    I didn’t try to adapt your code, but what I DID do was get the Basic MiWi Demo Nodes 1 and 2 communicating with Microchip’s out-of-the-box stack. That means you can use whatever tricks you want with their code especially because this 8 bit PIC has 1k of EEprom and that’s all ya need to utilize the Network Freeze feature, right? Has enough Flash to handle the program code and a few k of SRAM.

    Here’s the schematic, and an example of 2 modules which I hand built. Microchip has already built the code around this chip and only the configuration in the Header.c file need be configured pretty much. Of course what else is define the chip in the code (like the rest of the chips in the code) and tell the MPLAB X IDE what chip it’s dealing with, right? If it compiles it will fly on this circuit.

    http://www.creativeheadspace.com/profile/RockinJay

    (the top 2 images are my project)

    So, sorry I didn’t get to mess with your code, especially because after 4 months of pulling my hair out not really understanding what I was doing, only knowing the I needed a 28 pin DIP so I was trying to adapt the wrong chip (I thought I’d use the 28 pin DIP version (18F25J50) of the PIC that is embedded in the 8 Bit Wireless Development Kit I’d purchased, 18F46J50 – I was wrong because for that family of chips; the code was written to use Port D, and I couldn’t get around it, the smaller dip chip only had Ports A B C)

    BUT…Then I discovered that Microchip had written the code for the PICDEM Z 8 bit board family. In the code, if you declared the PICDEM Z (in Microchip’s demo code) then you’d be using the 18F4620(40 pin DIP) OR 18F2620 (28 pin DIP). Read the data sheet, you can also use the 18F2525 (28 pin DIP with a little less Flash, that’s it), so I got some samples and they ROCK….like me…hee hee….

    Dave, this chip is a great lil’ 8 bit PIC that will work the first time with the Microchip MiWi stack out-of-the box!!!! with proper config. If you were to read the Data sheet for the PIC18F2525 you’d see how to set the config bits based on the way they are doing things with the other chips in the code.

    P.S. My modules differ from each other because I have the PAN Coordinator (Node 1) listening for a signal then blinking LED’s from the RFD (Node 2) which uses a Motion Sensor instead of a push button switch. I plug Node 2 into a battery and I plug Node 1 into the USB and am able to shut of the blinking LED’s from a button on the screen – created in C#. Basically I’m monitoring motion in another room…….
    Cool device, works awesome…..

  45. Hi Dave,
    I’m trying to do the same thing as you. I’d like to establish a communication between two stations.
    I’m using 2 I/O Expansion Board with for each other an USB Starter Kit II with PIC32MX795F512L.
    I modified Hardware.h and Hardware.c like you said but my problem is when I want to use the hyperterminal, i don’t have any serial port on the Board, so i tried to use an USB/serial adaptor, that I use the usb on my kit and the serial on te PC, but with no success :/.
    Do you have an idea of what can I do with this issue ?
    Thanks for your reply.
    Regards, Jeremy.

Leave a Reply

Your email address will not be published. Required fields are marked *