From 79628e7849cc3b9f92bc7203ed3d8fbe49d8e537 Mon Sep 17 00:00:00 2001 From: maniacbug Date: Sat, 30 Apr 2011 15:31:04 -0700 Subject: [PATCH] Added ack payload. Added a sample using pingpair as a base with minimal changes. --- RF24.cpp | 111 +++++++++- RF24.h | 13 +- examples/pingpair_pl/.gitignore | 1 + examples/pingpair_pl/makefile | 290 +++++++++++++++++++++++++++ examples/pingpair_pl/pingpair_pl.pde | 249 +++++++++++++++++++++++ examples/pingpair_pl/printf.h | 33 +++ nRF24L01.h | 14 ++ 7 files changed, 703 insertions(+), 8 deletions(-) create mode 100644 examples/pingpair_pl/.gitignore create mode 100644 examples/pingpair_pl/makefile create mode 100644 examples/pingpair_pl/pingpair_pl.pde create mode 100644 examples/pingpair_pl/printf.h diff --git a/RF24.cpp b/RF24.cpp index b9606cd..6b8a073 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -11,7 +11,7 @@ #include "RF24.h" #include "nRF24L01.h" -#undef SERIAL_DEBUG +#define SERIAL_DEBUG #ifdef SERIAL_DEBUG #define IF_SERIAL_DEBUG(x) (x) #else @@ -51,6 +51,18 @@ uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) /******************************************************************/ +uint8_t RF24::read_register(uint8_t reg) +{ + csn(LOW); + SPI.transfer( R_REGISTER | ( REGISTER_MASK & reg ) ); + uint8_t result = SPI.transfer(0xff); + + csn(HIGH); + return result; +} + +/******************************************************************/ + uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) { uint8_t status; @@ -190,7 +202,7 @@ void RF24::print_observe_tx(uint8_t value) /******************************************************************/ RF24::RF24(uint8_t _cepin, uint8_t _cspin): - ce_pin(_cepin), csn_pin(_cspin), payload_size(32) + ce_pin(_cepin), csn_pin(_cspin), payload_size(32), ack_packet_available(false) { } @@ -327,6 +339,7 @@ boolean RF24::write( const void* buf, uint8_t len ) // Transmitter power-up write_register(CONFIG, _BV(EN_CRC) | _BV(PWR_UP)); + delay(2); // Send the payload write_payload( buf, len ); @@ -339,18 +352,37 @@ boolean RF24::write( const void* buf, uint8_t len ) // Monitor the send uint8_t observe_tx; uint8_t status; + uint8_t retries = 255; do { status = read_register(OBSERVE_TX,&observe_tx,1); IF_SERIAL_DEBUG(Serial.print(status,HEX)); IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); + if ( ! retries-- ) + { + IF_SERIAL_DEBUG(printf("ABORTED: too many retries\n\r")); + break; + } } while( ! ( status & ( _BV(TX_DS) | _BV(MAX_RT) ) ) ); if ( status & _BV(TX_DS) ) result = true; - - IF_SERIAL_DEBUG(Serial.println(result?"...OK.":"...Failed")); + + IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); + + ack_packet_available = ( status & _BV(RX_DR) ); + uint8_t pl_len = 0; + if ( ack_packet_available ) + { + write_register(STATUS,_BV(RX_DR) ); + csn(LOW); + SPI.transfer( R_RX_PL_WID ); + pl_len = SPI.transfer(0xff); + csn(HIGH); + IF_SERIAL_DEBUG(Serial.print("[AckPacket]/")); + IF_SERIAL_DEBUG(Serial.println(pl_len,DEC)); + } // Yay, we are done. ce(LOW); @@ -378,12 +410,11 @@ boolean RF24::available(void) boolean RF24::available(uint8_t* pipe_num) { uint8_t status = get_status(); + IF_SERIAL_DEBUG(print_status(status)); boolean result = ( status & _BV(RX_DR) ); if (result) { - IF_SERIAL_DEBUG(print_status(status)); - // If the caller wants the pipe number, include that if ( pipe_num ) *pipe_num = ( status >> RX_P_NO ) & B111; @@ -394,6 +425,12 @@ boolean RF24::available(uint8_t* pipe_num) // actually READ the payload? write_register(STATUS,_BV(RX_DR) ); + + // Handle ack payload receipt + if ( status & _BV(TX_DS) ) + { + write_register(STATUS,_BV(TX_DS)); + } } return result; @@ -458,5 +495,67 @@ void RF24::openReadingPipe(uint8_t child, uint64_t value) write_register(EN_RXADDR,en_rx); } } + +/******************************************************************/ + +void RF24::toggle_features(void) +{ + csn(LOW); + SPI.transfer( ACTIVATE ); + SPI.transfer( 0x73 ); + csn(HIGH); +} + +/******************************************************************/ + +void RF24::enableAckPayload(void) +{ + // + // enable ack payload and dynamic payload features + // + + write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + + // If it didn't work, the features are not enabled + if ( ! read_register(FEATURE) ) + { + // So enable them and try again + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + } + + IF_SERIAL_DEBUG(printf("FEATURE=%i\n\r",read_register(FEATURE))); + + // + // Enable dynamic payload on pipe 0 + // + + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); +} + +/******************************************************************/ + +void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) +{ + const uint8_t* current = (const uint8_t*)buf; + + csn(LOW); + SPI.transfer( W_ACK_PAYLOAD | ( pipe & B111 ) ); + uint8_t data_len = min(len,32); + while ( data_len-- ) + SPI.transfer(*current++); + + csn(HIGH); +} + +/******************************************************************/ + +boolean RF24::isAckPayloadAvailable(void) +{ + boolean result = ack_packet_available; + ack_packet_available = false; + return result; +} + // vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RF24.h b/RF24.h index 2b2d6b0..d68deac 100644 --- a/RF24.h +++ b/RF24.h @@ -21,6 +21,7 @@ private: uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ uint8_t csn_pin; /**< SPI Chip select */ uint8_t payload_size; /**< Fixed size of payloads */ + boolean ack_packet_available; /**< Whether there is an ack packet waiting */ protected: /** @@ -54,6 +55,8 @@ protected: * @return Current value of status register */ uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len) ; + + uint8_t read_register(uint8_t reg) ; /** * Write a chunk of data to a register @@ -95,7 +98,7 @@ protected: * @return Current value of status register */ uint8_t read_payload(void* buf, uint8_t len) ; - +public: /** * Empty the receive buffer * @@ -109,7 +112,7 @@ protected: * @return Current value of status register */ uint8_t flush_tx(void); - +protected: /** * Retrieve the current status of the chip * @@ -135,6 +138,7 @@ protected: */ void print_observe_tx(uint8_t value) ; + void toggle_features(void); /**@}*/ public: @@ -299,6 +303,11 @@ public: */ void openReadingPipe(uint8_t number, uint64_t address); + void enableAckPayload(void); + + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + + boolean isAckPayloadAvailable(void); }; /** diff --git a/examples/pingpair_pl/.gitignore b/examples/pingpair_pl/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/examples/pingpair_pl/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/examples/pingpair_pl/makefile b/examples/pingpair_pl/makefile new file mode 100644 index 0000000..a1c6d4c --- /dev/null +++ b/examples/pingpair_pl/makefile @@ -0,0 +1,290 @@ +# Arduino Makefile +# Arduino adaptation by mellis, eighthave, oli.keller +# Modified by Kerry Wong to support NetBeans +# Modified by Rob Gray (Graynomad) for use with Windows and no IDE +# This works in my environment and I use it to program two different +# 328-based boards and a Mega2560. It's not necessarily robust and +# I may have broken something in the original file that I don't use. +# +# This makefile allows you to build sketches from the command line +# without the Arduino environment. +# +# Instructions for using the makefile: +# +# 1. Copy this file into the folder with your sketch. The project code file +# should have a .c extension however the file gets copied to a .cpp before +# compilation so you still write in C++. +# +# 2. Modify the lines between the double ### rows to set the paths +# comm ports etc for your system. EG. c:/progra~1/arduino/arduino-00 +# for the Arduino IDE, Note the use of short folder name, don't use +# "Program files" because spaces will break the build. +# +# Set the line containing "MCU" to match your board's processor. +# Typically ATmega328 or ATmega2560. If you're using a LilyPad Arduino, +# change F_CPU to 8000000. +# +# 3. At the command line, change to the directory containing your +# program's file and the makefile. +# +# 4. Type "make" and press enter to compile/verify your program. +# The default make target will also perform the uplode using avrdude. +# +# The first time this is done all required libraries will be built +# and a core.a file will be created in the output folder. +# +# NOTES: +# All output goes into a folder called "output" underneath the working folder. +# The default all: target creates symbol (.sym) and expanded assembly +# (.lss) files and uploads the program. +# +# +########################################################## +########################################################## +# Select processor here +MCU = atmega328p +#MCU = atmega2560 + +ifeq ($(MCU),atmega2560) +UPLOAD_RATE = 115200 +AVRDUDE_PROTOCOL = stk500v2 +COM = 39 +endif + +ifeq ($(MCU),atmega328p) +UPLOAD_RATE = 57600 +AVRDUDE_PROTOCOL = stk500v1 +COM = 33 +endif + +UNAME := $(shell uname) + +ifeq ($(UNAME),Darwin) +ARDUINO_VERSION = 21 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc +PORT = /dev/tty.usbserial-A600eHIs +PORT2 = /dev/tty.usbserial-A9007LmI +else +ARDUINO_VERSION = 22 +ARDUINO_DIR = /opt/arduino-00$(ARDUINO_VERSION) +AVR_TOOLS_PATH = /usr/bin +AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools +PORT = /dev/ttyUSB0 +PORT2 = /dev/ttyUSB1 +endif + +PROJECT_NAME = $(notdir $(PWD)) +PROJECT_DIR = . +ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino +ARDUINO_AVR = $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr +ARDUINO_LIB = $(ARDUINO_DIR)/libraries +F_CPU = 16000000 + +########################################################## +########################################################## + +# Note that if your program has dependencies other than those +# already listed below, you will need to add them accordingly. +C_MODULES = \ +$(ARDUINO_CORE)/wiring_pulse.c \ +$(ARDUINO_CORE)/wiring_analog.c \ +$(ARDUINO_CORE)/pins_arduino.c \ +$(ARDUINO_CORE)/wiring.c \ +$(ARDUINO_CORE)/wiring_digital.c \ +$(ARDUINO_CORE)/WInterrupts.c \ +$(ARDUINO_CORE)/wiring_shift.c \ + +CXX_MODULES = \ +$(ARDUINO_CORE)/Tone.cpp \ +$(ARDUINO_CORE)/main.cpp \ +$(ARDUINO_CORE)/WMath.cpp \ +$(ARDUINO_CORE)/Print.cpp \ +$(ARDUINO_CORE)/HardwareSerial.cpp \ +$(ARDUINO_LIB)/SPI/SPI.cpp \ +../../RF24.cpp + +CXX_APP = output/$(PROJECT_NAME).cpp +MODULES = $(C_MODULES) $(CXX_MODULES) +SRC = $(C_MODULES) +CXXSRC = $(CXX_MODULES) $(CXX_APP) +FORMAT = ihex + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +#DEBUG = stabs +DEBUG = + +OPT = s + +# Place -D or -U options here +CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) +CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(ARDUINO_VERSION) + +# Place -I options here +CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) -I$(PROJECT_DIR) -I$(ARDUINO_AVR) -I$(ARDUINO_LIB)/SPI -I../.. + +CXXINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_LIB) + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +#CSTANDARD = -std=gnu99 +CDEBUG = -g$(DEBUG) +#CWARN = -Wall -Wstrict-prototypes +CWARN = -Wall # show all warnings +#CWARN = -w # suppress all warnings +CMAP = -Wl,-Map,output.map +####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CTUNING = -ffunction-sections -fdata-sections +CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +MMCU = -mmcu=$(MCU) + +CFLAGS = $(CDEBUG) -O$(OPT) $(CMAP) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA) +CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS) +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs +LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections +#LDFLAGS = -O$(OPT) -lm -Wl,-Map,output/$(PROJECT_NAME).map + +# Programming support using avrdude. Settings and variables. +AVRDUDE_PORT = $(PORT) +AVRDUDE_WRITE_FLASH = -U flash:w:output/$(PROJECT_NAME).hex:i + +AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf \ +-p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) + +# Program settings +CC = $(AVR_TOOLS_PATH)/avr-gcc +CXX = $(AVR_TOOLS_PATH)/avr-g++ +LD = $(AVR_TOOLS_PATH)/avr-gcc +OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy +OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump +AR = $(AVR_TOOLS_PATH)/avr-ar +SIZE = $(AVR_TOOLS_PATH)/avr-size +NM = $(AVR_TOOLS_PATH)/avr-nm +AVRDUDE = $(AVR_TOOLS_PATH)/avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) +OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = $(CFLAGS) -mmcu=$(MCU) +ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU) +ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU) +ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU) + +# Default target. +# This is th etarget that gets executed with a make command +# that has no parameters, ie "make". +all: applet_files build sym lss size upload + +build: elf hex + +output/$(PROJECT_NAME).cpp: $(PROJECT_NAME).pde + test -d output || mkdir output + echo "#include " > $@ + echo "#line 1 \"$<\"" >> $@ + cat $< >> $@ + +elf: output/$(PROJECT_NAME).elf +hex: output/$(PROJECT_NAME).hex +eep: output/$(PROJECT_NAME).eep +lss: output/$(PROJECT_NAME).lss +#sym: output/$(PROJECT_NAME).sym + +# Upload HEX file to Arduino +upload: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT2) $(AVRDUDE_WRITE_FLASH) + +sym: + $(NM) -n -C --format=posix output/$(PROJECT_NAME).elf > output/$(PROJECT_NAME).sym + +# Display size of file. +size: + $(SIZE) output/$(PROJECT_NAME).elf + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + +coff: output/$(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + + extcoff: $(PROJECT_NAME).elf + $(COFFCONVERT) -O coff-ext-avr output/$(PROJECT_NAME).elf $(PROJECT_NAME).cof + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + $(OBJCOPY) -O $(FORMAT) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --no-change-warnings \ + --change-section-lma .eeprom=0 $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Link: create ELF output file from library. +#output/$(PROJECT_NAME).elf: $(PROJECT_NAME).c output/core.a +output/$(PROJECT_NAME).elf: output/$(PROJECT_NAME).o output/core.a + $(LD) $(ALL_LDFLAGS) -o $@ output/$(PROJECT_NAME).o output/core.a + +output/core.a: $(OBJ_MODULES) + @for i in $(OBJ_MODULES); do echo $(AR) rcs output/core.a $$i; $(AR) rcs output/core.a $$i; done + +# Compile: create object files from C++ source files. +.cpp.o: + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + +# Automatic dependencies +%.d: %.c + $(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +%.d: %.cpp + $(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@ + +# Target: clean project. +clean: + $(REMOVE) output/$(PROJECT_NAME).hex output/$(PROJECT_NAME).eep output/$(PROJECT_NAME).cof output/$(PROJECT_NAME).elf \ + output/$(PROJECT_NAME).map output/$(PROJECT_NAME).sym output/$(PROJECT_NAME).lss output/core.a \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) + +#.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter +.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter + +#include $(SRC:.c=.d) +#include $(CXXSRC:.cpp=.d) diff --git a/examples/pingpair_pl/pingpair_pl.pde b/examples/pingpair_pl/pingpair_pl.pde new file mode 100644 index 0000000..96896e3 --- /dev/null +++ b/examples/pingpair_pl/pingpair_pl.pde @@ -0,0 +1,249 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + */ + +/** + * Example RF Radio Ping Pair + * + * This is an example of how to use the RF24 class. Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +#include +#include "nRF24L01.h" +#include "RF24.h" +#include "printf.h" + +// +// Hardware configuration +// + +// Set up nRF24L01 radio on SPI bus plus pins 8 & 9 + +RF24 radio(8,9); + +// sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver +// Leave open to be the 'ping' transmitter +const short role_pin = 7; + +const short led_pin = 13; // flash the led when a packet has been sent + +// +// Topology +// + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xE8E8F0F0E1LL, 0xE8E8F0F0D2LL }; + +// +// Role management +// +// Set up role. This sketch uses the same software for all the nodes +// in this system. Doing so greatly simplifies testing. The hardware itself specifies +// which node it is. +// +// This is done through the role_pin +// + +// The various roles supported by this sketch +typedef enum { role_ping_out = 1, role_pong_back } role_e; + +// The debug-friendly names of those roles +const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; + +// The role of the current running sketch +role_e role; + +void setup(void) +{ + // + // Role + // + + // set up the role pin + pinMode(role_pin, INPUT); + digitalWrite(role_pin,HIGH); + delay(20); // Just to get a solid reading on the role pin + + // read the address pin, establish our role + if ( digitalRead(role_pin) ) + role = role_ping_out; + else + role = role_pong_back; + + // + // Print preamble + // + + Serial.begin(9600); + printf_begin(); + printf("\n\rRF24/examples/pingpair_pl/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // We will be using the Ack Payload feature, so please enable it + radio.enableAckPayload(); + + // + // Open pipes to other nodes for communication + // + + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + // Open 'our' pipe for writing + // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading) + + if ( role == role_ping_out ) + { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } + else + { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); +} + +void loop(void) +{ + // + // Ping out role. Repeatedly send the current time + // + + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + unsigned long time = millis(); + printf("Now sending %lu...",time); + radio.write( &time, sizeof(unsigned long) ); + + if ( radio.isAckPayloadAvailable() ) + { + static char response[32]; + radio.read(response,32); + printf("Ack: [%s]",response); + + // try flushing the rx buffer to get the ack packet out of the system ... + radio.flush_rx(); + } + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 500 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Stop listening while we are clocking out the data + radio.stopListening(); + + // Dump the payloads until we've gotten everything + unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + // Begin listening again + radio.startListening(); + } + + // Try again later + digitalWrite(led_pin,HIGH); + delay(3000); + digitalWrite(led_pin,LOW); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + boolean done = false; + while (!done) + { + // Fetch the payload, and see if this was the last one. + done = radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got payload %lu...",got_time); + } + + // First, stop listening so we can talk + radio.stopListening(); + + //delay(250); + + // necessary to flush the ack packet out + // but kills the entire tx queue, which I might not + // want in the future!! + radio.flush_tx(); + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + + //delay(250); + + // Add an ack packet for the next time around + static unsigned id = 0; + static char pl_buffer[10]; + memset(pl_buffer,' ',10); + pl_buffer[9] = 0; + snprintf(pl_buffer,10,"id %04x",id++); + radio.writeAckPayload( 1, pl_buffer, 10 ); + + } + } +} +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/examples/pingpair_pl/printf.h b/examples/pingpair_pl/printf.h new file mode 100644 index 0000000..63501e4 --- /dev/null +++ b/examples/pingpair_pl/printf.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2011 James Coliz, Jr. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + */ + +/** + * @file printf.h + * + * Setup necessary to direct stdout to the Arduino Serial library, which + * enables 'printf' + */ + +#ifndef __PRINTF_H__ +#define __PRINTF_H__ + +#include "WProgram.h" + +int serial_putc( char c, FILE *t ) +{ + Serial.write( c ); + + return c; +} + +void printf_begin(void) +{ + fdevopen( &serial_putc, 0 ); +} + +#endif // __PRINTF_H__ diff --git a/nRF24L01.h b/nRF24L01.h index 10aedb3..5f4d1b8 100644 --- a/nRF24L01.h +++ b/nRF24L01.h @@ -47,6 +47,8 @@ #define RX_PW_P4 0x15 #define RX_PW_P5 0x16 #define FIFO_STATUS 0x17 +#define DYNPD 0x1C +#define FEATURE 0x1D /* Bit Mnemonics */ #define MASK_RX_DR 6 @@ -87,13 +89,25 @@ #define TX_EMPTY 4 #define RX_FULL 1 #define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 +#define EN_DPL 2 +#define EN_ACK_PAY 1 +#define EN_DYN_ACK 0 /* Instruction Mnemonics */ #define R_REGISTER 0x00 #define W_REGISTER 0x20 #define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 #define R_RX_PAYLOAD 0x61 #define W_TX_PAYLOAD 0xA0 +#define W_ACK_PAYLOAD 0xA8 #define FLUSH_TX 0xE1 #define FLUSH_RX 0xE2 #define REUSE_TX_PL 0xE3