Pokelure Code

//*                                                                                                                                                                   
  // 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);
  }
}

Filed under: Code            
12/15/2020 2:12:00 PM