From 1fd8ee72c0b9a32d754ece211b1790bea0265b4e Mon Sep 17 00:00:00 2001 From: maniacbug Date: Thu, 12 May 2011 14:21:44 -0700 Subject: [PATCH] Created new example, copied from starping --- examples/starping_relay/.gitignore | 1 + examples/starping_relay/makefile | 295 +++++++++++++++++++++ examples/starping_relay/printf.h | 33 +++ examples/starping_relay/starping_relay.pde | 293 ++++++++++++++++++++ 4 files changed, 622 insertions(+) create mode 100644 examples/starping_relay/.gitignore create mode 100644 examples/starping_relay/makefile create mode 100644 examples/starping_relay/printf.h create mode 100644 examples/starping_relay/starping_relay.pde diff --git a/examples/starping_relay/.gitignore b/examples/starping_relay/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/examples/starping_relay/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/examples/starping_relay/makefile b/examples/starping_relay/makefile new file mode 100644 index 0000000..2ea7538 --- /dev/null +++ b/examples/starping_relay/makefile @@ -0,0 +1,295 @@ +# 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 \ +$(ARDUINO_LIB)/EEPROM/EEPROM.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_LIB)/EEPROM -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: upload1 upload2 + +upload1: output/$(PROJECT_NAME).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -P $(PORT) $(AVRDUDE_WRITE_FLASH) + +upload2: output/$(PROJECT_NAME).hex + $(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/starping_relay/printf.h b/examples/starping_relay/printf.h new file mode 100644 index 0000000..63501e4 --- /dev/null +++ b/examples/starping_relay/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/examples/starping_relay/starping_relay.pde b/examples/starping_relay/starping_relay.pde new file mode 100644 index 0000000..2100c31 --- /dev/null +++ b/examples/starping_relay/starping_relay.pde @@ -0,0 +1,293 @@ +/* + 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 Star Group + * + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * role_pin low, and the others will be 'ping transmit' units. The ping units + * unit will send out the value of millis() once a second. The pong unit will + * respond back with a copy of the value. Each ping unit can get that response + * back, and determine how long the whole cycle took. + * + * This example requires a bit more complexity to determine which unit is which. + * The pong receiver is identified by having its role_pin tied to ground. + * The ping senders are further differentiated by a byte in eeprom. + */ + +#include +#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 'pong' receiver. +const int role_pin = 7; + +// +// Topology +// + +// Radio pipe addresses for the nodes to communicate. Only ping nodes need +// dedicated pipes in this topology. Each ping node has a talking pipe +// that it will ping into, and a listening pipe that it will listen for +// the pong. The pong node listens on all the ping node talking pipes +// and sends the pong back on the sending node's specific listening pipe. + +const uint64_t talking_pipes[5] = { 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL }; +const uint64_t listening_pipes[5] = { 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3AB4LL, 0x3A3A3A3AA5LL, 0x3A3A3A3A96LL }; + +// +// 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_invalid = 0, role_ping_out, 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; + +// +// Address management +// + +// Where in EEPROM is the address stored? +const uint8_t address_at_eeprom_location = 0; + +// What is our address (SRAM cache of the address from EEPROM) +// Note that zero is an INVALID address. The pong back unit takes address +// 1, and the rest are 2-6 +uint8_t node_address; + +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; + + // + // Address + // + + if ( role == role_pong_back ) + node_address = 1; + else + { + // Read the address from EEPROM + uint8_t reading = EEPROM.read(address_at_eeprom_location); + + // If it is in a valid range for node addresses, it is our + // address. + if ( reading >= 2 && reading <= 6 ) + node_address = reading; + + // Otherwise, it is invalid, so set our address AND ROLE to 'invalid' + else + { + node_address = 0; + role = role_invalid; + } + } + + // + // Print preamble + // + + Serial.begin(9600); + printf_begin(); + printf("\n\rRF24/examples/starping/\n\r"); + printf("ROLE: %s\n\r",role_friendly_name[role]); + printf("ADDRESS: %i\n\r",node_address); + + // + // Setup and configure rf radio + // + + radio.begin(); + + // + // Open pipes to other nodes for communication + // + + // The pong node listens on all the ping node talking pipes + // and sends the pong back on the sending node's specific listening pipe. + if ( role == role_pong_back ) + { + radio.openReadingPipe(1,talking_pipes[0]); + radio.openReadingPipe(2,talking_pipes[1]); + radio.openReadingPipe(3,talking_pipes[2]); + radio.openReadingPipe(4,talking_pipes[3]); + radio.openReadingPipe(5,talking_pipes[4]); + } + + // Each ping node has a talking pipe that it will ping into, and a listening + // pipe that it will listen for the pong. + if ( role == role_ping_out ) + { + // Write on our talking pipe + radio.openWritingPipe(talking_pipes[node_address-2]); + // Listen on our listening pipe + radio.openReadingPipe(1,listening_pipes[node_address-2]); + } + + // + // Start listening + // + + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + + radio.printDetails(); + + // + // Prompt the user to assign a node address if we don't have one + // + + if ( role == role_invalid ) + { + printf("\n\r*** NO NODE ADDRESS ASSIGNED *** Send 1 through 6 to assign an address\n\r"); + } +} + +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) ); + + // 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 > 250 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); + } + + // Try again 1s later + delay(1000); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + uint8_t pipe_num; + if ( radio.available(&pipe_num) ) + { + // 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 from node %i...",got_time,pipe_num+1); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Open the correct pipe for writing + radio.openWritingPipe(listening_pipes[pipe_num-1]); + + // Retain the low 2 bytes to identify the pipe for the spew + uint16_t pipe_id = listening_pipes[pipe_num-1] & 0xffff; + + // Send the final one back. + radio.write( &got_time, sizeof(unsigned long) ); + printf("Sent response to %04x.\n\r",pipe_id); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } + + // + // Listen for serial input, which is how we set the address + // + if (Serial.available()) + { + // If the character on serial input is in a valid range... + char c = Serial.read(); + if ( c >= '1' && c <= '6' ) + { + // It is our address + EEPROM.write(address_at_eeprom_location,c-'0'); + + // And we are done right now (no easy way to soft reset) + printf("\n\rManually reset address to: %c\n\rPress RESET to continue!",c); + while(1); + } + } +} +// vim:ai:ci sts=2 sw=2 ft=cpp