diff --git a/examples/Basic_TwoWay/Basic_TwoWay.pde b/examples/Basic_TwoWay/Basic_TwoWay.pde index f77dcd7..7f7b52b 100644 --- a/examples/Basic_TwoWay/Basic_TwoWay.pde +++ b/examples/Basic_TwoWay/Basic_TwoWay.pde @@ -10,8 +10,8 @@ #include const int pin_reset = 6; -const int pin_cs = 7; -const int pin_interrupt = 5; +const int pin_cs = 10; // default CS pin on ATmega8/168/328 +const int pin_interrupt = 2; // default interrupt pin on ATmega8/168/328 Mrf24j mrf(pin_reset, pin_cs, pin_interrupt); @@ -20,75 +20,67 @@ long tx_interval = 1000; void setup() { Serial.begin(9600); - + + mrf.reset(); + mrf.init(); + mrf.set_pan(0xcafe); // This is _our_ address mrf.address16_write(0x6001); // uncomment if you want to receive any packet on this channel - // mrf.set_promiscuous(true); + //mrf.set_promiscuous(true); + + // uncomment if you want to enable PA/LNA external control + //mrf.set_palna(true); + + // uncomment if you want to buffer all PHY Payload + //mrf.set_bufferPHY(true); - attachInterrupt(0, interrupt_routine, CHANGE); + attachInterrupt(0, interrupt_routine, CHANGE); // interrupt 0 equivalent to pin 2(INT0) on ATmega8/168/328 last_time = millis(); + interrupts(); } -volatile uint8_t gotrx; -volatile uint8_t txok; - void interrupt_routine() { - // read and clear from the radio - byte last_interrupt = mrf.read_short(MRF_INTSTAT); - if (last_interrupt & MRF_I_RXIF) { - gotrx = 1; - } - if (last_interrupt & MRF_I_TXNIF) { - txok = 1; - } + mrf.interrupt_handler(); // mrf24 object interrupt routine } void loop() { - int tmp; - interrupts(); + mrf.check_flags(&handle_rx, &handle_tx); unsigned long current_time = millis(); if (current_time - last_time > tx_interval) { last_time = current_time; Serial.println("txxxing..."); - mrf.send16(0x4202, 4, "abcd"); + mrf.send16(0x4202, "abcd"); } - if (txok) { - txok = 0; - Serial.print("tx went ok:"); - tmp = mrf.read_short(MRF_TXSTAT); - Serial.print(tmp); - if (!(tmp & ~(1<> 6, HEX); - } - Serial.println(); - } - if (gotrx) { - gotrx = 0; - noInterrupts(); - mrf.rx_disable(); - - byte frame_length = mrf.read_long(0x300); // read start of rxfifo - Serial.print("received a packet ");Serial.print(frame_length, DEC);Serial.println(" bytes long"); - Serial.println("Packet data:"); - for (int i = 1; i <= frame_length; i++) { - tmp = mrf.read_long(0x300 + i); - Serial.print(tmp, HEX); - } - Serial.print("\r\nLQI/RSSI="); - byte lqi = mrf.read_long(0x300 + frame_length + 1); - byte rssi = mrf.read_long(0x300 + frame_length + 2); - Serial.print(lqi, HEX); - Serial.println(rssi, HEX); - - mrf.rx_enable(); - interrupts(); - - } - } + +void handle_rx() { + Serial.print("received a packet ");Serial.print(mrf.get_rxinfo()->frame_length, DEC);Serial.println(" bytes long"); + + if(mrf.get_bufferPHY()){ + Serial.println("Packet data (PHY Payload):"); + for (int i = 0; i < mrf.get_rxinfo()->frame_length; i++) { + Serial.print(mrf.get_rxbuf()[i]); + } + } + + Serial.println("\r\nASCII data (relevant data):"); + for (int i = 0; i < mrf.rx_datalength(); i++) { + Serial.write(mrf.get_rxinfo()->rx_data[i]); + } + + Serial.print("\r\nLQI/RSSI="); + Serial.print(mrf.get_rxinfo()->lqi, DEC); + Serial.print("/"); + Serial.println(mrf.get_rxinfo()->rssi, DEC); +} + +void handle_tx() { + if (mrf.get_txinfo()->tx_ok) { + Serial.println("TX went ok, got ack"); + } else { + Serial.print("TX failed after ");Serial.print(mrf.get_txinfo()->retries);Serial.println(" retries\n"); + } +} \ No newline at end of file diff --git a/examples/RX_only/RX_only.pde b/examples/RX_only/RX_only.pde index 07d598c..67d3807 100644 --- a/examples/RX_only/RX_only.pde +++ b/examples/RX_only/RX_only.pde @@ -10,62 +10,63 @@ #include const int pin_reset = 6; -const int pin_cs = 7; -const int pin_interrupt = 5; +const int pin_cs = 10; // default CS pin on ATmega8/168/328 +const int pin_interrupt = 2; // default interrupt pin on ATmega8/168/328 Mrf24j mrf(pin_reset, pin_cs, pin_interrupt); void setup() { Serial.begin(9600); - + + mrf.reset(); + mrf.init(); + mrf.set_pan(0xcafe); // This is _our_ address mrf.address16_write(0x6001); - mrf.set_channel(12); // uncomment if you want to receive any packet on this channel - // mrf.set_promiscuous(true); + //mrf.set_promiscuous(true); + + // uncomment if you want to enable PA/LNA external control + //mrf.set_palna(true); + + // uncomment if you want to buffer all PHY Payload + //mrf.set_bufferPHY(true); - attachInterrupt(0, interrupt_routine, CHANGE); + attachInterrupt(0, interrupt_routine, CHANGE); // interrupt 0 equivalent to pin 2(INT0) on ATmega8/168/328 + interrupts(); } -volatile uint8_t gotrx; - void interrupt_routine() { - // read and clear from the radio - byte last_interrupt = mrf.read_short(MRF_INTSTAT); - if (last_interrupt & MRF_I_RXIF) { - gotrx = 1; - } + mrf.interrupt_handler(); // mrf24 object interrupt routine } void loop() { - int tmp; - interrupts(); - if (gotrx) { - gotrx = 0; - noInterrupts(); - mrf.rx_disable(); - - // read start of rxfifo - byte frame_length = mrf.read_long(0x300); - Serial.print("received a packet "); - Serial.print(frame_length, DEC); - Serial.println(" bytes long"); - - Serial.println("Packet data:"); - for (int i = 1; i <= frame_length; i++) { - tmp = mrf.read_long(0x300 + i); - Serial.print(tmp, HEX); - } - - Serial.print("\r\nLQI/RSSI="); - byte lqi = mrf.read_long(0x300 + frame_length + 1); - byte rssi = mrf.read_long(0x300 + frame_length + 2); - Serial.print(lqi, HEX); - Serial.println(rssi, HEX); - - mrf.rx_enable(); - interrupts(); - } + mrf.check_flags(&handle_rx, &handle_tx); } + +void handle_rx() { + Serial.print("received a packet ");Serial.print(mrf.get_rxinfo()->frame_length, DEC);Serial.println(" bytes long"); + + if(mrf.get_bufferPHY()){ + Serial.println("Packet data (PHY Payload):"); + for (int i = 0; i < mrf.get_rxinfo()->frame_length; i++) { + Serial.print(mrf.get_rxbuf()[i]); + } + } + + Serial.println("\r\nASCII data (relevant data):"); + for (int i = 0; i < mrf.rx_datalength(); i++) { + Serial.write(mrf.get_rxinfo()->rx_data[i]); + } + + Serial.print("\r\nLQI/RSSI="); + Serial.print(mrf.get_rxinfo()->lqi, DEC); + Serial.print("/"); + Serial.println(mrf.get_rxinfo()->rssi, DEC); +} + +void handle_tx() { + // code to transmit, nothing to do +} \ No newline at end of file diff --git a/examples/TX_only/TX_only.pde b/examples/TX_only/TX_only.pde index 89c12a2..314f51a 100644 --- a/examples/TX_only/TX_only.pde +++ b/examples/TX_only/TX_only.pde @@ -10,8 +10,8 @@ #include const int pin_reset = 6; -const int pin_cs = 7; -const int pin_interrupt = 5; +const int pin_cs = 10; // default CS pin on ATmega8/168/328 +const int pin_interrupt = 2; // default interrupt pin on ATmega8/168/328 Mrf24j mrf(pin_reset, pin_cs, pin_interrupt); @@ -20,35 +20,38 @@ long tx_interval = 1000; void setup() { Serial.begin(9600); - + + mrf.reset(); + mrf.init(); + mrf.set_pan(0xcafe); // This is _our_ address mrf.address16_write(0x6001); + + // uncomment if you want to enable PA/LNA external control + //mrf.set_palna(true); - attachInterrupt(0, interrupt_routine, CHANGE); - + attachInterrupt(0, interrupt_routine, CHANGE); // interrupt 0 equivalent to pin 2(INT0) on ATmega8/168/328 last_time = millis(); interrupts(); } void interrupt_routine() { - // read and clear interrupt flags from the radio - byte last_interrupt = mrf.read_short(MRF_INTSTAT); - // we don't care about the rx acks, but we need them to be flushed so it can keep receiving - if (last_interrupt & MRF_I_RXIF) { - mrf.rx_flush(); - } + mrf.interrupt_handler(); // mrf24 object interrupt routine } - void loop() { - int tmp; - unsigned long current_time = millis(); - if (current_time - last_time > tx_interval) { - last_time = current_time; - Serial.println("txxxing..."); - char * msg = "hello world"; - mrf.send16(0x4202, strlen(msg), msg); - } - + mrf.check_flags(&handle_rx, &handle_tx); } + +void handle_rx() { + // data to receive, nothing to do +} + +void handle_tx() { + if (mrf.get_txinfo()->tx_ok) { + Serial.println("TX went ok, got ack"); + } else { + Serial.print("TX failed after ");Serial.print(mrf.get_txinfo()->retries);Serial.println(" retries\n"); + } +} \ No newline at end of file diff --git a/mrf24j.cpp b/mrf24j.cpp index 38f113c..1e5d325 100644 --- a/mrf24j.cpp +++ b/mrf24j.cpp @@ -1,13 +1,34 @@ /** * mrf24j.cpp, Karl Palsson, 2011, karlp@tweak.net.au - * modifed bsd license / apache license + * modified bsd license / apache license */ -#include "WProgram.h" #include "mrf24j.h" +// aMaxPHYPacketSize = 127, from the 802.15.4-2006 standard. +static uint8_t rx_buf[127]; + +// essential for obtaining the data frame only +// bytes_MHR = 2 Frame control + 1 sequence number + 2 panid + 2 shortAddr Destination + 2 shortAddr Source +static int bytes_MHR = 9; +static int bytes_FCS = 2; // FCS length = 2 +static int bytes_nodata = bytes_MHR + bytes_FCS; // no_data bytes in PHY payload, header length + FCS + +static int ignoreBytes = 0; // bytes to ignore, some modules behaviour. + +static boolean bufPHY = false; // flag to buffer all bytes in PHY Payload, or not + +volatile uint8_t flag_got_rx; +volatile uint8_t flag_got_tx; + +static rx_info_t rx_info; +static tx_info_t tx_info; +/** + * Constructor MRF24J Object. + * @param pin_reset, @param pin_chip_select, @param pin_interrupt + */ Mrf24j::Mrf24j(int pin_reset, int pin_chip_select, int pin_interrupt) { _pin_reset = pin_reset; _pin_cs = pin_chip_select; @@ -17,10 +38,9 @@ Mrf24j::Mrf24j(int pin_reset, int pin_chip_select, int pin_interrupt) { pinMode(_pin_cs, OUTPUT); pinMode(_pin_int, INPUT); + SPI.setBitOrder(MSBFIRST) ; + SPI.setDataMode(SPI_MODE0); SPI.begin(); - // arguably should not be here... - reset(); - init(); } void Mrf24j::reset(void) { @@ -34,7 +54,7 @@ byte Mrf24j::read_short(byte address) { digitalWrite(_pin_cs, LOW); // 0 top for short addressing, 0 bottom for read SPI.transfer(address<<1 & 0b01111110); - byte ret = SPI.transfer(0x0); + byte ret = SPI.transfer(0x00); digitalWrite(_pin_cs, HIGH); return ret; } @@ -53,7 +73,7 @@ byte Mrf24j::read_long(word address) { void Mrf24j::write_short(byte address, byte data) { digitalWrite(_pin_cs, LOW); - // 0 for top address, 1 bottom for write + // 0 for top short address, 1 bottom for write SPI.transfer((address<<1 & 0b01111110) | 0x01); SPI.transfer(data); digitalWrite(_pin_cs, HIGH); @@ -93,15 +113,17 @@ word Mrf24j::address16_read(void) { * Simple send 16, with acks, not much of anything.. assumes src16 and local pan only. * @param data */ -void Mrf24j::send16(word dest16, byte len, char * data) { - +void Mrf24j::send16(word dest16, char * data) { + byte len = strlen(data); // get the length of the char* array int i = 0; - write_long(i++, 9); // header length - write_long(i++, 9+2+len); //+2 is because module seems to ignore 2 bytes after the header?! + write_long(i++, bytes_MHR); // header length + // +ignoreBytes is because some module seems to ignore 2 bytes after the header?!. + // default: ignoreBytes = 0; + write_long(i++, bytes_MHR+ignoreBytes+len); -// 0 | pan compression | ack | no security | no data pending | data frame[3 bits] + // 0 | pan compression | ack | no security | no data pending | data frame[3 bits] write_long(i++, 0b01100001); // first byte of Frame Control -// 16 bit source, 802.15.4 (2003), 16 bit dest, + // 16 bit source, 802.15.4 (2003), 16 bit dest, write_long(i++, 0b10001000); // second byte of frame control write_long(i++, 1); // sequence number 1 @@ -116,7 +138,9 @@ void Mrf24j::send16(word dest16, byte len, char * data) { write_long(i++, src16 & 0xff); // src16 low write_long(i++, src16 >> 8); // src16 high - i+=2; // All testing seems to indicate that the next two bytes are ignored. + // All testing seems to indicate that the next two bytes are ignored. + //2 bytes on FCS appended by TXMAC + i+=ignoreBytes; for (int q = 0; q < len; q++) { write_long(i++, data[q]); } @@ -136,13 +160,13 @@ void Mrf24j::set_channel(byte channel) { } void Mrf24j::init(void) { -/* - // Seems a bit ridiculous when I use reset pin anyway + /* + // Seems a bit ridiculous when I use reset pin anyway write_short(MRF_SOFTRST, 0x7); // from manual while (read_short(MRF_SOFTRST) & 0x7 != 0) { ; // wait for soft reset to finish } -*/ + */ write_short(MRF_PACON2, 0x98); // – Initialize FIFOEN = 1 and TXONTS = 0x6. write_short(MRF_TXSTBL, 0x95); // – Initialize RFSTBL = 0x9. @@ -162,12 +186,81 @@ void Mrf24j::init(void) { set_interrupts(); set_channel(12); // max power is by default.. just leave it... - //Set transmitter power - See “REGISTER 2-62: RF CONTROL 3 REGISTER (ADDRESS: 0x203)”. + // Set transmitter power - See “REGISTER 2-62: RF CONTROL 3 REGISTER (ADDRESS: 0x203)”. write_short(MRF_RFCTL, 0x04); // – Reset RF state machine. write_short(MRF_RFCTL, 0x00); // part 2 delay(1); // delay at least 192usec } +/** + * Call this from within an interrupt handler connected to the MRFs output + * interrupt pin. It handles reading in any data from the module, and letting it + * continue working. + * Only the most recent data is ever kept. + */ +void Mrf24j::interrupt_handler(void) { + uint8_t last_interrupt = read_short(MRF_INTSTAT); + if (last_interrupt & MRF_I_RXIF) { + flag_got_rx++; + // read out the packet data... + noInterrupts(); + rx_disable(); + // read start of rxfifo for, has 2 bytes more added by FCS. frame_length = m + n + 2 + uint8_t frame_length = read_long(0x300); + + // buffer all bytes in PHY Payload + if(bufPHY){ + int rb_ptr = 0; + for (int i = 0; i < frame_length; i++) { // from 0x301 to (0x301 + frame_length -1) + rx_buf[rb_ptr++] = read_long(0x301 + i); + } + } + + // buffer data bytes + int rd_ptr = 0; + // from (0x301 + bytes_MHR) to (0x301 + frame_length - bytes_nodata - 1) + for (int i = 0; i < rx_datalength(); i++) { + rx_info.rx_data[rd_ptr++] = read_long(0x301 + bytes_MHR + i); + } + + rx_info.frame_length = frame_length; + // same as datasheet 0x301 + (m + n + 2) <-- frame_length + rx_info.lqi = read_long(0x301 + frame_length); + // same as datasheet 0x301 + (m + n + 3) <-- frame_length + 1 + rx_info.rssi = read_long(0x301 + frame_length + 1); + + rx_enable(); + interrupts(); + } + if (last_interrupt & MRF_I_TXNIF) { + flag_got_tx++; + uint8_t tmp = read_short(MRF_TXSTAT); + // 1 means it failed, we want 1 to mean it worked. + tx_info.tx_ok = !(tmp & ~(1 << TXNSTAT)); + tx_info.retries = tmp >> 6; + tx_info.channel_busy = (tmp & (1 << CCAFAIL)); + } +} + + +/** + * Call this function periodically, it will invoke your nominated handlers + */ +void Mrf24j::check_flags(void (*rx_handler)(void), void (*tx_handler)(void)){ + // TODO - we could check whether the flags are > 1 here, indicating data was lost? + if (flag_got_rx) { + flag_got_rx = 0; + rx_handler(); + } + if (flag_got_tx) { + flag_got_tx = 0; + tx_handler(); + } +} + +/** + * Set RX mode to promiscuous, or normal + */ void Mrf24j::set_promiscuous(boolean enabled) { if (enabled) { write_short(MRF_RXMCR, 0x01); @@ -176,6 +269,49 @@ void Mrf24j::set_promiscuous(boolean enabled) { } } +rx_info_t * Mrf24j::get_rxinfo(void) { + return &rx_info; +} + +tx_info_t * Mrf24j::get_txinfo(void) { + return &tx_info; +} + +uint8_t * Mrf24j::get_rxbuf(void) { + return rx_buf; +} + +int Mrf24j::rx_datalength(void) { + return rx_info.frame_length - bytes_nodata; +} + +void Mrf24j::set_ignoreBytes(int ib) { + // some modules behaviour + ignoreBytes = ib; +} + +/** + * Set bufPHY flag to buffer all bytes in PHY Payload, or not + */ +void Mrf24j::set_bufferPHY(boolean bp) { + bufPHY = bp; +} + +boolean Mrf24j::get_bufferPHY(void) { + return bufPHY; +} + +/** + * Set PA/LNA external control + */ +void Mrf24j::set_palna(boolean enabled) { + if (enabled) { + write_long(MRF_TESTMODE, 0x07); // Enable PA/LNA on MRF24J40MB module. + }else{ + write_long(MRF_TESTMODE, 0x00); // Disable PA/LNA on MRF24J40MB module. + } +} + void Mrf24j::rx_flush(void) { write_short(MRF_RXFLUSH, 0x01); } diff --git a/mrf24j.h b/mrf24j.h index dc55949..a06589b 100644 --- a/mrf24j.h +++ b/mrf24j.h @@ -1,4 +1,4 @@ -/* +/* * File: mrf24j.h * copyright Karl Palsson, karlp@tweak.net.au, 2011 * modified BSD License / apache license @@ -7,7 +7,11 @@ #ifndef LIB_MRF24J_H #define LIB_MRF24J_H -#include "WProgram.h" +#if defined(ARDUINO) && ARDUINO >= 100 // Arduino IDE version >= 1.0 + #include "Arduino.h" +#else // older Arduino IDE versions + #include "WProgram.h" +#endif #include "SPI.h" @@ -143,52 +147,88 @@ #define MRF_I_RXIF 0b00001000 #define MRF_I_TXNIF 0b00000001 +typedef struct _rx_info_t{ + uint8_t frame_length; + uint8_t rx_data[116]; //max data length = (127 aMaxPHYPacketSize - 2 Frame control - 1 sequence number - 2 panid - 2 shortAddr Destination - 2 shortAddr Source - 2 FCS) + uint8_t lqi; + uint8_t rssi; +} rx_info_t; + +/** + * Based on the TXSTAT register, but "better" + */ +typedef struct _tx_info_t{ + uint8_t tx_ok:1; + uint8_t retries:2; + uint8_t channel_busy:1; +} tx_info_t; class Mrf24j { + public: + Mrf24j(int pin_reset, int pin_chip_select, int pin_interrupt); + void reset(void); + void init(void); -public: -Mrf24j(int pin_reset, int pin_chip_select, int pin_interrupt); -void reset(void); -void init(void); + byte read_short(byte address); + byte read_long(word address); -byte read_short(byte address); -byte read_long(word address); + void write_short(byte address, byte data); + void write_long(word address, byte data); -void write_short(byte address, byte data); -void write_long(word address, byte data); + word get_pan(void); + void set_pan(word panid); -word get_pan(void); -void set_pan(word panid); + void address16_write(word address16); + word address16_read(void); -void address16_write(word address16); -word address16_read(void); + void set_interrupts(void); -void set_interrupts(void); + void set_promiscuous(boolean enabled); -void set_promiscuous(boolean enabled); + /** + * Set the channel, using 802.15.4 channel numbers (11..26) + */ + void set_channel(byte channel); -/** - * Set the channel, using 802.15.4 channel numbers (11..26) - */ -void set_channel(byte channel); + void rx_enable(void); + void rx_disable(void); -void rx_enable(void); -void rx_disable(void); + /** If you want to throw away rx data */ + void rx_flush(void); -/** If you want to throw away rx data */ -void rx_flush(void); + rx_info_t * get_rxinfo(void); -void send16(word dest16, byte len, char * data); + tx_info_t * get_txinfo(void); -private: - int _pin_reset; - int _pin_cs; - int _pin_int; + uint8_t * get_rxbuf(void); + int rx_datalength(void); + void set_ignoreBytes(int ib); + + /** + * Set bufPHY flag to buffer all bytes in PHY Payload, or not + */ + void set_bufferPHY(boolean bp); + + boolean get_bufferPHY(void); + + /** + * Set PA/LNA external control + */ + void set_palna(boolean enabled); + + void send16(word dest16, char * data); + + void interrupt_handler(void); + + void check_flags(void (*rx_handler)(void), void (*tx_handler)(void)); + + private: + int _pin_reset; + int _pin_cs; + int _pin_int; }; #endif /* LIB_MRF24J_H */ - -