//*
// Pokelure code based on a sample I found for a bride and groom's flowers.
// by Julie Barrett
// I used a Feather M0 for this. The same code works for both boards.
// Put a jumper across pins 9 and 10 on one of the boards for this to work.
// The lights will glow stronger when the two radios are closer.
// 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"
//#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL)
// Required for Serial on Zero based boards
// #define Serial SERIAL_PORT_USBVIRTUAL
//#endif
#if defined(ARDUINO_SAMD_ZERO) // Feather M0 w/Radio
#define RFM69_CS 8
#define RFM69_INT 3
#define RFM69_RST 4
#define LED 13
#endif
// 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 3
#define RFM69_IRQN digitalPinToInterrupt(RFM69_IRQ )
#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 5 // NeoPixels are connected to this pin
#define NUM_LEDS 1 // 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 = 20; // Play with this number to affect the range
CRGBPalette16 currentPalette;
TBlendType currentBlending;
int HUE = 120; //Turquoiseish for lure
//int HUE = 64; //yellow for pikachu candy
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));
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);
}
}