After about 5 days of a sidebar with @peekay123 - I have an update. What started as an enhancement to a previously working POC turned into an education in neopixels within Particle as well as a lesson in some red herrings and rabbit holes. In fact, the neopixel side of things turned out to be the least of my worries.
The original project was an SR04 sensor on an Argon which, when tripped, sent events over mesh to a Xenon where an LED was turned on/off. I had that working a couple weeks ago and wanted to add a Neopixel Featherwing, with some sort of light show to replace the LED. All of this was on OS 1.5.2.
@peekay123 assisted me with understanding the ins/outs of combining the original Xenon sketch on the receiving end with new neopixel code. After a few iterations, I lost a few days when some bizarre behavior with the previously working events handler started to occur. @peekay123 could not reproduce this, and today it was clear why.
Earlier today, I located a wiring error on the Argon board, which may have happened when the project got jostled around in the course of testing this past week. With that corrected, I turned my attention back to the Xenon, but was not seeing events. So I decided to swap out the Argon’s SR04 and from there everything worked fine.
Fully commented code and a video will be shown below.
Argon Code (sensor side)
// Originally from: https://makersportal.com/blog/2018/11/11/internet-of-things-bluetooth-and-wifi-mesh-network-with-particle-argon-and-xenon-boards
// Sensor gateway using the Argon board. This contains the HC-SR04
// Ultrasonic code and sends data to the other nodes to notify of a trip
#include "HC_SR04.h"
double cm = 0.0;
bool beam_status = false;
int trigPin = D4;
int echoPin = D5; // JJD pin works despite notes to the contrary in Particle's documentation.
HC_SR04 rangefinder = HC_SR04(trigPin, echoPin);
void setup()
{
Spark.variable("cm", &cm, DOUBLE);
}
void loop()
{
cm = rangefinder.getDistanceCM();
if (cm<30){
if (beam_status==false){
Particle.publish("tripStatus", "breach");
beam_status = true;
}
} else {
if (beam_status==false){
} else {
Particle.publish("tripStatus", "all_clear");
beam_status = false;
}
}
delay(100);
}
Xenon code (receiving side)
/* JJD 2/13/23
Based on original LED sketch here:
https://makersportal.com/blog/2018/11/11/internet-of-things-bluetooth-and-wifi-mesh-network-with-particle-argon-and-xenon-boards
Includes peekay123's suggestions on the switch setup
Combining Xenon sketches to receive events from the SR04 sketch on my Argon via myHandler,
to change pixel behavior on Xenon. Both pieces work when tested separately.
For specifics on neopixel parameters, see original examples. Too much text to clutter here.
*/
/* ======================= includes ================================= */
#include "Particle.h"
#include "neopixel.h"
/* ======================= prototypes =============================== */
void colorAll(uint32_t c, uint8_t wait);
void colorWipe(uint32_t c, uint8_t wait);
void rainbow(uint8_t wait);
void rainbowCycle(uint8_t wait);
uint32_t Wheel(byte WheelPos);
/* ======================= extra-examples.cpp ======================== */
SYSTEM_THREAD(ENABLED); // added per web research to avoid Xenon dropping off
SYSTEM_MODE(AUTOMATIC);
// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_COUNT 32 // Changed for Neopixel Featherwing
#define PIXEL_PIN D5 // D5 works despite the notes below
#define PIXEL_TYPE WS2812B
#define BRIGHTNESS 10 // These things are way too bright!!
// Define types of effects in this enum
enum effect_type {NONE, CLEAR, RAINBOW}; // Thanks to peekay123!
// define the global variable which holds the current effect
effect_type effect = NONE; // Start with no effect (does nothing)
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel. Avoid connecting
// on a live circuit...if you must, connect GND first.
void setup() {
// *** Neopixel Setup code *** //
strip.setBrightness(BRIGHTNESS);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
// Subscribe to sensor tripping using Particle.subscribe
Particle.subscribe("tripStatus", myHandler); // the name of the publish() call in sensor node [ publish("tripStatus") ]
}
void loop() {
/*
This is a non-blocking way to show the desired LED effect.
The only "blocking" is during the execution of the effect.
The effect is set in "myHandler" which is run whenever loop() completes.
Once set, the swtich() statement runs the selected effect and then resets
the effect to NONE to basically do nothing until the effect value is changed again.
*/
switch(effect) { // Thanks to peekay123!
case NONE: // No effect so do nothing
break;
case CLEAR: // CLEAR effect
// Use ColorAll() so could clear screen to other color than black by setting R/G/B value in strip.Color()
colorAll(strip.Color(0, 0, 0), 20);
effect = NONE; // effect complete, reset to NONE
break;
case RAINBOW: // RAINBOW effect
rainbow(20); // Do effect
colorAll(strip.Color(0, 0, 0), 20); // JJD added this again to see if it will stop green
effect = NONE; // effect complete, reset to NONE
break;
default: // Catch for invalid value, acts as NONE
break;
}
}
// Now for the myHandler function, which is called when the cloud tells us that our sensor's event has been published.
void myHandler(const char *event, const char *data)
{
if (strcmp(data,"breach")==0) {
// if the sensor's threshold is breached, then...
effect = RAINBOW; // play rainbow
}
else if (strcmp(data,"all_clear")==0) {
// if your sensor's beam is all clear, then...
effect = CLEAR;
}
}
/* ======================= Neopixel behaviors ======================== */
// Set all pixels in the strip to a solid color, then wait (ms)
void colorAll(uint32_t c, uint8_t wait) {
uint16_t i;
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
}
strip.show();
delay(wait);
}
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout, then wait (ms)
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) { // 1 cycle of all colors on wheel
for(i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
if(WheelPos < 85) {
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
} else if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
}
Here’s a video of the working project: