// Distance meter using the RSSI function of two RFM69 radios. // This is a very VERY "ish" gauge of *relative* distance (not actual units // like feet or meters or anything), and is prone to interference from // walls, bodies, direction and the like...it's intended for an artistic // presentation, nothing scientific. #include <RFM69.h> // get it here: https://www.github.com/lowpowerlab/rfm69 #include <SPI.h> #include "FastLED.h" // RADIO HARDWARE CONFIG --------------------------------------------------- #define TX_NODE_ID 1 #define RX_NODE_ID 2 #define NETWORK_ID 100 // The same on all nodes that talk to each other #define ENCRYPTKEY "sampleEncryptKey" // Same 16 characters on all nodes! // Match frequency to the hardware version of the radio on your Feather: //#define FREQUENCY RF69_433MHZ //#define FREQUENCY RF69_868MHZ #define FREQUENCY RF69_915MHZ #define IS_RFM69HCW true // Set true if using RFM69HCW module // RFM69 pins on Feather 32U4 board: #define RFM69_CS 8 #define RFM69_IRQ 7 #define RFM69_IRQN 4 #define RFM69_RST 4 // A jumper across two pins selects whether this board is the // TRANSMITTER or the RECEIVER. That way the SAME code can be uploaded // to BOTH boards. I got tired of having to comment out or enable a // line every time I uploaded to one or the other. #define TX_SENSE_1 9 #define TX_SENSE_2 10 RFM69 radio(RFM69_CS, RFM69_IRQ, IS_RFM69HCW, RFM69_IRQN); bool isTransmitter; // SIGNAL FILTERING CONFIG ------------------------------------------------- #define MEDIAN_SIZE 5 // Number of RSSI readings to be median filtered #define AVERAGE_SIZE 8 // Number of median results to be averaged // Signal values are stored as abs(RSSI), hence the positive values here: #define SIGNAL_MIN 26 // abs() of signal strength at closest distance #define SIGNAL_MAX 100 // " furthest distance #define SIGNAL_INIT ((SIGNAL_MAX + SIGNAL_MIN) / 2 - SIGNAL_MIN) uint8_t medianBuf[MEDIAN_SIZE], // Prior abs(RSSI) readings averageBuf[AVERAGE_SIZE], // Prior medians medianIdx = 0, // Current position in medianBuf[] averageIdx = 0; // Current position in averageBuf[] uint32_t sum = SIGNAL_INIT * AVERAGE_SIZE + (AVERAGE_SIZE / 2); // DISPLAY CONFIG ---------------------------------------------------------- #define LED_TYPE WS2812 #define DATA_PIN 6 // NeoPixels are connected to this pin #define NUM_LEDS 20 // How many leds in your strip? CRGB leds[NUM_LEDS]; //create your NeoPixel array #define COLOR_ORDER GRB int led = 13; uint8_t average = 10; uint8_t distance = 15; // Play with this number to affect the range CRGBPalette16 currentPalette; TBlendType currentBlending; int HUE = 60; int SATURATION = 255; int BRIGHTNESS = 255; #define FPS 25 // Animation rate (frames per second) // SETUP FUNCTION - runs once at startup ----------------------------------- void setup() { // pinMode(led, OUTPUT); // Turns the onboard LED on -- useful for testing. // digitalWrite(led, HIGH); // Comment out these two lines if you want it off while running // Un-comment these 3 lines to refine your signal variables ----------- // while (!Serial) {} // Serial.begin(9600); // Serial.println("hello"); // Set up jumper detect on TX_SENSE pins pinMode(TX_SENSE_2, OUTPUT); digitalWrite(TX_SENSE_2, LOW); pinMode(TX_SENSE_1, INPUT_PULLUP); // Reset the RFM module: pinMode(RFM69_RST, OUTPUT); digitalWrite(RFM69_RST, HIGH); delay(100); digitalWrite(RFM69_RST, LOW); delay(100); // Test for jumper on TX_SENSE pins isTransmitter = digitalRead(TX_SENSE_1); // Initialize radio: radio.initialize(FREQUENCY, isTransmitter ? TX_NODE_ID : RX_NODE_ID, NETWORK_ID); if(IS_RFM69HCW) radio.setHighPower(); // Only for RFM69HCW & HW! radio.setPowerLevel(31); // Output range 0 (5dBm) to 31 (20dBm) radio.encrypt(ENCRYPTKEY); memset(medianBuf , SIGNAL_INIT, sizeof(medianBuf)); memset(averageBuf, SIGNAL_INIT, sizeof(averageBuf)); // Set up a timer interrupt for LED animation. This ensures uniform // timing for animation while radio ACK time may be unpredictable. // THIS CODE IS SPECIFIC TO ATMEGA32U4; will not run on M0, Teensy3, etc. TCCR1A = _BV(WGM11) | _BV(WGM10); // Fast PWM mode TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11) | _BV(CS10); // 1:64 prescale OCR1A = ((F_CPU / 64) / FPS) - 1; TIMSK1 = _BV(TOIE1); // Enable overflow interrupt on Timer/Counter 1 // tell FastLED about the LED strip configuration FastLED.addLeds<LED_TYPE, DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS) .setCorrection(TypicalLEDStrip) .setDither(BRIGHTNESS < 255); currentBlending = LINEARBLEND; } // LOOP FUNCTION - runs repeatedly ----------------------------------------- void loop() { if(isTransmitter) { // Send a small message to the receiver node... radio.sendWithRetry(RX_NODE_ID, "<3", 2); } else { // Receiver waits for message, sends acknowledgement. // The ACK is needed to gauge distance on BOTH ends. radio.receiveDone(); if(radio.ACKRequested()) radio.sendACK(); } if(radio.RSSI) { // Got signal strength? uint16_t s = abs(radio.RSSI); if(s < SIGNAL_MIN) s = SIGNAL_MIN; else if(s > SIGNAL_MAX) s = SIGNAL_MAX; s -= SIGNAL_MIN; // s is now 0 to (MAX-MIN) medianBuf[medianIdx] = s; // Store new value in median buffer if(++medianIdx >= MEDIAN_SIZE) medianIdx = 0; // Median filter discards 'outliers,' the most spurious readings; // abrupt changes that are possibly inaccurate. // Make a sorted copy of median buffer to find actual current median // without disturbing original values. Since it's just a few elements, // a crude insertion sort is done... uint8_t m[MEDIAN_SIZE], median, average, i, j, k; memcpy(m, medianBuf, sizeof(m)); for(i=1; i<MEDIAN_SIZE; i++) { k = m[i]; for(j=i; j && (m[j-1] > k); j--) m[j] = m[j-1]; m[j] = k; } median = m[MEDIAN_SIZE / 2]; // Following median, the average of prior results is taken, // further smoothing out any jumpy bits... sum -= averageBuf[averageIdx]; averageBuf[averageIdx] = median; sum += median; if(++averageIdx >= AVERAGE_SIZE) averageIdx = 0; average = sum / AVERAGE_SIZE; // 'average' is the signal strength from the radio... // it'll likely be between 0 (closest) and 75 or so // (furthest...like, down the block). // UNCOMMENT THESE SERIAL COMMANDS to see what your radios are sending while the Feather is plugged in via USB //Serial.print("Average "); //Serial.println(average); //Serial.print("Adjusted "); //Serial.println(300-(average*distance)); //Serial.print("Brightness "); //Serial.println(constrain(map ((300-(average*distance)),-400,300,0,255),10,255)); //This line is your main calculation. It's taking "average" (your radio signal, averaged out for smoothness) and //mutliplying it by "distance" (currently set at 15, but play with that number to change the range), //and then constraining it between a brightness of 10 (dimmest setting) and 255 (brightest setting). BRIGHTNESS=(constrain(map ((300-(average*distance)),-400,300,0,255),10,255)); } } ISR(TIMER1_OVF_vect) { // Timer 1 interrupt for animation // Display *prior* frame of data at start of interrupt -- this ensures // uniform timing for effects that may have variable processing times. FastLED.show(); // Render one frame of animation here...pick one function, comment out other Solid(); //Colors(); } void Solid() // all one color, currently set to yellow by the HUE variable { fill_solid(leds, NUM_LEDS, CHSV(HUE, SATURATION, BRIGHTNESS)); } void Colors() // I matched each LED to the color of its corresponding flower { for(int yell = 0 ; yell < 3; yell++ ) { //LEDs 1-3 are Yellow (HUE 60) leds[yell] = CHSV(60, SATURATION, BRIGHTNESS); } for (int wht = 3 ; wht < 9; wht++ ) { //LEDs 4-9 are White (saturation 100) leds[wht] = CHSV(60, 100, BRIGHTNESS); } for (int pur = 9 ; pur < 19; pur++ ) { //LEDs 10-19 are Purple (hue 200) leds[pur] = CHSV(200, 255, BRIGHTNESS); } }

// Distance meter using the RSSI function of two RFM69 radios. // This is a very VERY "ish" gauge of *relative* distance (not actual units // like feet or meters or anything), and is prone to interference from // walls, bodies, direction and the like...it's intended for an artistic // presentation, nothing scientific. #include <RFM69.h> // get it here: https://www.github.com/lowpowerlab/rfm69 #include <SPI.h> #include "FastLED.h" // RADIO HARDWARE CONFIG --------------------------------------------------- #define TX_NODE_ID 1 #define RX_NODE_ID 2 #define NETWORK_ID 100 // The same on all nodes that talk to each other #define ENCRYPTKEY "sampleEncryptKey" // Same 16 characters on all nodes! // Match frequency to the hardware version of the radio on your Feather: //#define FREQUENCY RF69_433MHZ //#define FREQUENCY RF69_868MHZ #define FREQUENCY RF69_915MHZ #define IS_RFM69HCW true // Set true if using RFM69HCW module // RFM69 pins on Feather 32U4 board: #define RFM69_CS 8 #define RFM69_IRQ 7 #define RFM69_IRQN 4 #define RFM69_RST 4 // A jumper across two pins selects whether this board is the // TRANSMITTER or the RECEIVER. That way the SAME code can be uploaded // to BOTH boards. I got tired of having to comment out or enable a // line every time I uploaded to one or the other. #define TX_SENSE_1 9 #define TX_SENSE_2 10 RFM69 radio(RFM69_CS, RFM69_IRQ, IS_RFM69HCW, RFM69_IRQN); bool isTransmitter; // SIGNAL FILTERING CONFIG ------------------------------------------------- #define MEDIAN_SIZE 5 // Number of RSSI readings to be median filtered #define AVERAGE_SIZE 8 // Number of median results to be averaged // Signal values are stored as abs(RSSI), hence the positive values here: #define SIGNAL_MIN 26 // abs() of signal strength at closest distance #define SIGNAL_MAX 100 // " furthest distance #define SIGNAL_INIT ((SIGNAL_MAX + SIGNAL_MIN) / 2 - SIGNAL_MIN) uint8_t medianBuf[MEDIAN_SIZE], // Prior abs(RSSI) readings averageBuf[AVERAGE_SIZE], // Prior medians medianIdx = 0, // Current position in medianBuf[] averageIdx = 0; // Current position in averageBuf[] uint32_t sum = SIGNAL_INIT * AVERAGE_SIZE + (AVERAGE_SIZE / 2); // DISPLAY CONFIG ---------------------------------------------------------- #define LED_TYPE WS2812 #define DATA_PIN 6 // NeoPixels are connected to this pin #define NUM_LEDS 20 // How many leds in your strip? CRGB leds[NUM_LEDS]; //create your NeoPixel array #define COLOR_ORDER GRB int led = 13; uint8_t average = 10; uint8_t distance = 15; // Play with this number to affect the range CRGBPalette16 currentPalette; TBlendType currentBlending; int HUE = 60; int SATURATION = 255; int BRIGHTNESS = 255; #define FPS 25 // Animation rate (frames per second) // SETUP FUNCTION - runs once at startup ----------------------------------- void setup() { // pinMode(led, OUTPUT); // Turns the onboard LED on -- useful for testing. // digitalWrite(led, HIGH); // Comment out these two lines if you want it off while running // Un-comment these 3 lines to refine your signal variables ----------- // while (!Serial) {} // Serial.begin(9600); // Serial.println("hello"); // Set up jumper detect on TX_SENSE pins pinMode(TX_SENSE_2, OUTPUT); digitalWrite(TX_SENSE_2, LOW); pinMode(TX_SENSE_1, INPUT_PULLUP); // Reset the RFM module: pinMode(RFM69_RST, OUTPUT); digitalWrite(RFM69_RST, HIGH); delay(100); digitalWrite(RFM69_RST, LOW); delay(100); // Test for jumper on TX_SENSE pins isTransmitter = digitalRead(TX_SENSE_1); // Initialize radio: radio.initialize(FREQUENCY, isTransmitter ? TX_NODE_ID : RX_NODE_ID, NETWORK_ID); if(IS_RFM69HCW) radio.setHighPower(); // Only for RFM69HCW & HW! radio.setPowerLevel(31); // Output range 0 (5dBm) to 31 (20dBm) radio.encrypt(ENCRYPTKEY); memset(medianBuf , SIGNAL_INIT, sizeof(medianBuf)); memset(averageBuf, SIGNAL_INIT, sizeof(averageBuf)); // Set up a timer interrupt for LED animation. This ensures uniform // timing for animation while radio ACK time may be unpredictable. // THIS CODE IS SPECIFIC TO ATMEGA32U4; will not run on M0, Teensy3, etc. TCCR1A = _BV(WGM11) | _BV(WGM10); // Fast PWM mode TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11) | _BV(CS10); // 1:64 prescale OCR1A = ((F_CPU / 64) / FPS) - 1; TIMSK1 = _BV(TOIE1); // Enable overflow interrupt on Timer/Counter 1 // tell FastLED about the LED strip configuration FastLED.addLeds<LED_TYPE, DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS) .setCorrection(TypicalLEDStrip) .setDither(BRIGHTNESS < 255); currentBlending = LINEARBLEND; } // LOOP FUNCTION - runs repeatedly ----------------------------------------- void loop() { if(isTransmitter) { // Send a small message to the receiver node... radio.sendWithRetry(RX_NODE_ID, "<3", 2); } else { // Receiver waits for message, sends acknowledgement. // The ACK is needed to gauge distance on BOTH ends. radio.receiveDone(); if(radio.ACKRequested()) radio.sendACK(); } if(radio.RSSI) { // Got signal strength? uint16_t s = abs(radio.RSSI); if(s < SIGNAL_MIN) s = SIGNAL_MIN; else if(s > SIGNAL_MAX) s = SIGNAL_MAX; s -= SIGNAL_MIN; // s is now 0 to (MAX-MIN) medianBuf[medianIdx] = s; // Store new value in median buffer if(++medianIdx >= MEDIAN_SIZE) medianIdx = 0; // Median filter discards 'outliers,' the most spurious readings; // abrupt changes that are possibly inaccurate. // Make a sorted copy of median buffer to find actual current median // without disturbing original values. Since it's just a few elements, // a crude insertion sort is done... uint8_t m[MEDIAN_SIZE], median, average, i, j, k; memcpy(m, medianBuf, sizeof(m)); for(i=1; i<MEDIAN_SIZE; i++) { k = m[i]; for(j=i; j && (m[j-1] > k); j--) m[j] = m[j-1]; m[j] = k; } median = m[MEDIAN_SIZE / 2]; // Following median, the average of prior results is taken, // further smoothing out any jumpy bits... sum -= averageBuf[averageIdx]; averageBuf[averageIdx] = median; sum += median; if(++averageIdx >= AVERAGE_SIZE) averageIdx = 0; average = sum / AVERAGE_SIZE; // 'average' is the signal strength from the radio... // it'll likely be between 0 (closest) and 75 or so // (furthest...like, down the block). // UNCOMMENT THESE SERIAL COMMANDS to see what your radios are sending while the Feather is plugged in via USB //Serial.print("Average "); //Serial.println(average); //Serial.print("Adjusted "); //Serial.println(300-(average*distance)); //Serial.print("Brightness "); //Serial.println(constrain(map ((300-(average*distance)),-400,300,0,255),10,255)); //This line is your main calculation. It's taking "average" (your radio signal, averaged out for smoothness) and //mutliplying it by "distance" (currently set at 15, but play with that number to change the range), //and then constraining it between a brightness of 10 (dimmest setting) and 255 (brightest setting). BRIGHTNESS=(constrain(map ((300-(average*distance)),-400,300,0,255),10,255)); } } ISR(TIMER1_OVF_vect) { // Timer 1 interrupt for animation // Display *prior* frame of data at start of interrupt -- this ensures // uniform timing for effects that may have variable processing times. FastLED.show(); // Render one frame of animation here...pick one function, comment out other Solid(); //Colors(); } void Solid() // all one color, currently set to yellow by the HUE variable { fill_solid(leds, NUM_LEDS, CHSV(HUE, SATURATION, BRIGHTNESS)); } void Colors() // I matched each LED to the color of its corresponding flower { for(int yell = 0 ; yell < 3; yell++ ) { //LEDs 1-3 are Yellow (HUE 60) leds[yell] = CHSV(60, SATURATION, BRIGHTNESS); } for (int wht = 3 ; wht < 9; wht++ ) { //LEDs 4-9 are White (saturation 100) leds[wht] = CHSV(60, 100, BRIGHTNESS); } for (int pur = 9 ; pur < 19; pur++ ) { //LEDs 10-19 are Purple (hue 200) leds[pur] = CHSV(200, 255, BRIGHTNESS); } }