LED won't dance, .... Help needed (Neo Pixel, Edge Impulse)

Folks, I've been beating my head against this for weeks and am somewhat desperate at this point. I would really like a double check for silly or nuanced failures. I haven't been able to find any and am wondering if the continously running PDM for the microphone could somehow be stepping on a clock that is also used by NeoPixel or something else nefarious - I don't know how to troubleshoot that.

I have a fairly complex little program that uses Edge Impulse, NeoPixels, Motors, PDM Mic etc.) This program is 99.9% working, the last step is to understand why the machine won't light the LED blue when the "State" is "Drinking". There are multiple state machines, with the primary one being the "DrinkHandler" - it has a few states: Offline, Static, Drinking, Feeding, Opening. Most of the time, it sits in Static mode, and it listens for sounds using the PDM mic, and if the Edge Impulse model detects the sound more than 4 times in a 5 second window, it will flag as "ConsistentDrinking = 1" and the first thing that should happen is setting State = Drinking and a call to "cometHandler()", which does a fading comet trail on an LED strip, driven by the Adafruit Neopixel library. Problem is that the lights won't light (only for the call to cometHandler from the Drinking State, all others work fine) and I cannot for the life of me figure why. I have put print calls literally everywhere and the cometHandler traverses properly every step of the way, it enables the driver Im using to power them, counts up and down, detects the call for "blue", and does everything it is supposed to do. Other calls to this function, from various button pushes work properly and the cometHandler() does it's thing perfectly. I have changed those other calls to ask for "blue" and that also works. So, I have tested every piece of this separately and it all works, just not the final pass together. The full code for the project is here:

Then, here are some excerpts from the full code:

The primary state machine (and the LED cometHandler() thing after):

void DrinkHandler(){

  switch (State) {
    case Offline: break;  // Beeping or Playing a melody with delays
  	case Static:  // Listening for drink sounds ...
  	  if ( ButtonOutput == Click and !endStop){
        digitalWrite(MotEnable,  HIGH);
  			FeedMillis = millis();
  			State = Feeding;
        PrintMessage(FeedingMsg,0);
  			break;
  		}
  		if ( ButtonOutput == LongPress and !backStop ){
        digitalWrite(MotEnable,  HIGH);
  			State = Opening;
        PrintMessage(OpeningMsg,0);
  			break;
  		}
  		if ( consistentDrinking == 1 ) {
        PrintMessage(DrinkingMsg,0);
        DrinkMillis = millis();
  			State = Drinking;
        consistentDrinking = 0;
  			break;
  		}
      else {
        countDrinks();
      }
    break;
  	case Drinking:  // a treat is possible
      cometHandler(Blue, DrinkTimeout);
      if ( ButtonOutput == Click and !endStop ){
        digitalWrite(MotEnable,  HIGH);
  			FeedMillis = millis();
  			State = Feeding;
        PrintMessage(FeedingMsg,0);
  			break;
  		}
  		if ( ScoopButton.isPressed() and !endStop ) {
        PrintMessage(ScoopMsg,1);
        digitalWrite(MotEnable,  HIGH);
        FeedMillis = millis();
  			State = Feeding;
    	  PrintMessage(FeedingMsg,0);
  			break;
  		}
  		if ( millis() > DrinkMillis + DrinkTimeout ){
        PrintMessage(DrinkTimeoutMsg,1); 
        DrinkMillis = 0;
  			State = Static;
        PrintMessage(StaticMsg,1);
        break;
  		}
      else break;
  	case Feeding:
      backStop = 0;
      RunMotor(0);
      cometHandler(Green, FeedTimeout);
  		if ( ButtonOutput == DoubleClick ){
  			StopMotor();
        PrintMessage(UserStopMsg,1);
        Beep(1000,2);  // Sets State to Offline for beep, then back to Static
        PrintMessage(StaticMsg,1);
        consistentDrinking = 0;
        break;
  		}
  		if ( StopButton.isPressed() ) {
  			StopMotor();
  			State = Static;
        endStop = 1;
        PrintMessage(StopMsg,1);
        PlaySong();    // Sets State to Offline for song, then back to Static
        PrintMessage(StaticMsg,1);
        consistentDrinking = 0;
        break;
  		}
  		if ( millis() > FeedMillis + FeedTimeout ) {
        StopMotor();
        FeedMillis = 0;
        PrintMessage(FeedTimeoutMsg,1);
        State = Static;
        PrintMessage(StaticMsg,1);
        consistentDrinking = 0;
  			break;
  		}
      else break;
  	case Opening:
      cometHandler(Red, 210000);  // Red comets while backing up, enough time to get to end
      endStop = 0;
  	  RunMotor(1);
  		if ( ButtonOutput == DoubleClick ){
  			StopMotor();
        PrintMessage(UserStopMsg,1);
        Beep(1000,2);    // Sets State to Offline for song, then back to Static
        PrintMessage(StaticMsg,1);
  			break;
  		}
  		if ( StopButton.isPressed() ) {
  			StopMotor();
        backStop = 1;
        PrintMessage(StopMsg,1);
        Beep(1000,1);      // Sets State to Offline for song, then back to Static  
        PrintMessage(StaticMsg,1);
  			break;
  		}
    break;
  }
}

Here's the cometHandler():

void cometHandler(int color, int cometTimeout){
 switch (ledState){
  case start:
    cometStartTime = millis();
    ledState = show;
    digitalWrite(LedEnable, HIGH);  // enable the LED through the driver
  break;  
  case show:
    if (millis() < cometStartTime + cometTimeout - 100){  // end the comet timer before drink timer
      for (int i = 0; i < cometSize; i++)
        switch (color){
          case Red:
            strip.setPixelColor( Position+i, Bright, 0, 0 );
          break;
          case Green:
            strip.setPixelColor( Position+i, 0, Bright, 0 );
          break;
          case Blue:
            strip.setPixelColor( Position+i, 0, 0, Bright );
          break;
        }
      strip.show();
      pixelStartTime = millis();
      Position += Direction;
      ledState = pause;
      for (int j = 0; j < numLEDs; j++)
        if (random(10) > 5) fadePixel(j,color,fadePct);
    }
    else {  // Stop the comet show by turning all pixels to black
      for (int j = 0; j < numLEDs; j++) strip.setPixelColor(j,0,0,0);
      strip.show();
      cometStartTime = 0;
      ledState = start;
      digitalWrite(LedEnable,LOW);
      break;
    }
  break;
  case pause:
    if(millis() > pixelStartTime + pixelTimeout){
      if (Position == (numLEDs - cometSize) || Position == 0) Direction *= -1;
      ledState = show;
    }
  break;
 }
}

Post one more box with "the whole code"... leave the section you have as-is.

  1. Verify that "strip.show()" is after condition statements rather than inside. Test the condition, make the decisions, set the colors, set a flag, then "strip.show()"
  2. Verify you have a color that has not been forced to 0, 0, 0 (did the bad color value follow into the function call)

The whole code is on pastebin with syntax highlighting. Is it preferred to put it in a little window? Thanks for suggestions, I will double check those this afternoon.

OK, I did double check your suggestions @xfpd and thanks for providing them. Indeed the .show command is happening after the conditions and setting of the pixel colors for the string. This was clear anyway because the cometHandler function works as expected when called from a simple button push.

The suggestion about the wrong colors being injected was an interesting one, because I have a timer both for the led pattern and for the state - they were previously set to the same time, this could potentially cause the set to black to occur after the expected time and possibly get injected when it isn't supposed to be. I had thought of that actually and already put a little time-100 in there so that this would not happen. But regardless, I put some comments into the Faded() function to read back the pixel values after they are supposed to have been written, and they have values that change over time, so somehow the strip.show() is setting them properly, and I can read them back but the LEDs are not physically lighting.

That's why I'm wondering if there is some sort of very subtle conflict with underlying clocks or interrupts that are either in the PDM code or in the NeoPixel code. But, the LEDs light via the cometHandler() function when called elsewhere in the program, and I don't ever stop the PDM, so for some reason, only this particular call to that function isn't working. Very frustrating!

I would try FastLED or something, but this micro isn't compatible with FastLED, being a NRF52840.

How much memory does this have? How many neopixels do you have? Each neopixel needs 24 bits.

Verify the pixel/pixels/range of pixels being addressed are fully within in the range of "Number_of_pixels" or any array loading the pixels. I'm just telling you the things that caught me.

You should always post the entire sketch in question. You have looked at it so much that a second set of untrained eyes can spot something that you have "seen" a hundred times, but passed over.

If you want to see WS2812 under tight control, check Anas Kuzechie...

Thanks, lots of memory - only 30% used. Yes, I have a strip with just 17 pixels and they are handled fine with the setup I have.

I just now decided to try something brute force and just light all of the pixels blue when the state is Drinking, instead of calling the cometHandler animation and saw something strange. There is an onboard NeoPixel, and I have been manually setting this one to Red/Green/Blue as a physical indicator on the board. Somehow attempting to write the strip to blue was causing the onboard led to be blue... that's not supposed to be happening and maybe a clue. That built in one should be addressed using the built-in name of LED_BLUE. There is some kind of interaction there that I have to figure out. This is a Xiao BLE Sense by the way.

Change all "BLUE" words to "AZURE" and roll it down the hill. (maybe one of your variables is a keyword from the library)

  • or BLUEX

Dang, that was a coincidence / false alarm. I think the last state of the inbuilt led was blue, so it remained lit after an early exit to the AI audio detection. No interference with the strip.

BTW swapped out Blue for BlueX and no change

Post your full sketch, along with the build output (RAM present, RAM used, et c.). It will help finding the cause.

Here's the build output:

I have made a few changes since the start of the post but the primary issue (no blue leds when the DrinkHandler() State==Drinking) is still present. I noted there's no need for a timeout on the cometHandler(), because the various States have their own timeouts or otherwise exit when a button is pushed, therefore I just pulled that out. Here's the full sketch:

/* Define the number of slices per model window. 
For more info: https://docs.edgeimpulse.com/docs/continuous-audio-sampling */

#define EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW 6

#include <PDM.h>
#include <DogDrink_inferencing.h>
#include <ezButton.h>
#include <Adafruit_NeoPixel.h>

const int ButtonTimeout = 500;  // Time in millis to detect types of button click
const int FeedTimeout =  5000;   // Time in milliseconds that PB will be dispensed
const int DrinkTimeout = 15000;  // Time in milliseconds after drinking starts that PB is enabled
const int sampleTimeout = 250;   // Interval after which we will check the inference results

int consistentDrinking = 0;
int isDrinking = 0;
int drinkCounter = 0;
int drinkLimit = 4;     // How many isDrinking detected before considered consistent.
int drinkStart = 0;
int drinkDwell = 5000;  // How long to be listening for more drink signals after the first

int endStop   = 0;
int backStop  = 0;
int cometStop = 0;

enum {Static, Feeding, Drinking, Opening, Offline};
int State = Static;

enum {Waiting, Counting};
int ButtonState  = Waiting;
int drinkState = Waiting;

enum {Recording, Inferencing};
int pdmState = Recording;

enum {Red,Green,Blue};
int color = Red;
int Bright = 128;

enum {show, pause};
int ledState = show;

int Position = 0;
int Direction = 1;
float fadePct = .20;

int cometSize                = 3;
int pixelTimeout             = 40;  // miliseconds between pixel events
unsigned long cometStartTime = 0;
unsigned long pixelStartTime = 0;
unsigned long sampleStart    = 0;

enum ButtonType {None, Click, DoubleClick, LongPress};
int ButtonType = None;

int ButtonCount  = -1;
int ButtonOutput = None;

char ClickMsg[]       = "CLICK";
char LongPressMsg[]   = "LONGPRESS";
char DoubleClickMsg[] = "(DOUBLECLICK) ";

char StaticMsg[]   = "Nothing is happening.";
char FeedingMsg[]  = "Feeding... ";
char OpeningMsg[]  = "Opening... ";
char DrinkingMsg[] = "Drinking... ";

char ScoopMsg[]    = "scoop button pressed.";
char StopMsg[]     = "stopped by endstop.";
char UserStopMsg[] = "stopped by user.";

char DrinkTimerMsg[]   = "Drink timer started ... ";
char FeedTimerMsg[]    = "Feed timer started ... ";
char FeedTimeoutMsg[]  = "Feed timer elapsed.";
char DrinkTimeoutMsg[] = "Drink timer elapsed.";

unsigned int Tones[16]
  = {415, 329, 369, 246, 246, 369, 415, 329, 415, 369, 329, 246, 246, 369, 415, 311};
unsigned int Durations[16]
  = {700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 800, 900, 1000, 2000};
unsigned int Pauses[16]
  = {20, 20, 20, 1400, 20, 20, 20, 1400, 20, 20, 20, 1500, 40, 50, 60, 2100};
int lastStep = 16;

unsigned long ButtonMillis = 0;
unsigned long FeedMillis   = 0;
unsigned long DrinkMillis  = 0;
unsigned long ToneMillis   = 0;
unsigned long sampleMillis = 0;

const int Buzzer     = D0;
const int MotEnable  = D8;
const int MotForward = D7;
const int MotBack    = D6;
const int LedEnable  = D10;  // Provides power to the LED throught the motor drive
const int LedPin     = D1;

#define numLEDs 17  // Number of NeoPixels
Adafruit_NeoPixel strip(numLEDs, LedPin, NEO_GRBW + NEO_KHZ800);

ezButton UserButton  (D3);
ezButton ScoopButton (D4);
ezButton StopButton  (D2);


/** Audio buffers, pointers and selectors */
typedef struct {
    signed short *buffers[2];
    unsigned char buf_select;
    unsigned char buf_ready;
    unsigned int buf_count;
    unsigned int n_samples;
} inference_t;

static inference_t inference;
static bool record_ready = false;
static signed short *sampleBuffer;
static bool debug_nn = false; // Set true to see features from the raw signal
static int print_results = -(EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW);

//    Arduino setup function
extern "C" {
  #include <hal/nrf_pdm.h>
}

/** PDM clock frequency calculation based on 32MHz clock and decimation filter ratio 80
more clock gen info https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf5340%2Fpdm.html
param sampleRate in Hz, return uint32_t clk value */

static uint32_t pdm_clock_calculate(uint64_t sampleRate)
{   const uint64_t PDM_RATIO = 80ULL;
    const uint64_t CLK_32MHZ = 32000000ULL;
    uint64_t clk_control = 4096ULL * (((sampleRate * PDM_RATIO) * 1048576ULL) / (CLK_32MHZ + ((sampleRate * PDM_RATIO) / 2ULL)));
    return (uint32_t)clk_control;  }


void setup() {

  UserButton.setDebounceTime(10);
  UserButton.setCountMode(COUNT_RISING);  // only increments count when released

  StopButton.setDebounceTime(10);
  ScoopButton.setDebounceTime(10);

  pinMode( MotEnable,  OUTPUT);
  pinMode( MotForward, OUTPUT);
  pinMode( MotBack,    OUTPUT);
  pinMode( Buzzer,     OUTPUT);
  pinMode( LedEnable,  OUTPUT);
  pinMode( LedPin,     OUTPUT);

  strip.begin(); // Initialize NeoPixel strip object (REQUIRED)
  strip.show();  // Initialize all pixels to 'off'
  strip.setBrightness(Bright); // Set BRIGHTNESS to about 1/5 (max = 255)

    // put your setup code here, to run once:
    Serial.begin(115200);
    // comment out the below line to cancel the wait for USB connection (needed for native USB)
    int serialAttempts = 5;
    for (int attempt = 0; attempt < serialAttempts && !Serial; ++attempt)
      { delay(30); }
    if(Serial) Serial.println("Edge Impulse Inferencing Demo");

    // summary of inferencing settings (from model_metadata.h)
    if(Serial){
    ei_printf("Inferencing settings:\n");
    ei_printf("\tInterval: %.2f ms.\n", (float)EI_CLASSIFIER_INTERVAL_MS);
    ei_printf("\tFrame size: %d\n", EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);
    ei_printf("\tSample length: %d ms.\n", EI_CLASSIFIER_RAW_SAMPLE_COUNT / 16);
    ei_printf("\tNo. of classes: %d\n", sizeof(ei_classifier_inferencing_categories) /
        sizeof(ei_classifier_inferencing_categories[0]));
    }

    run_classifier_init();
    if (microphone_inference_start(EI_CLASSIFIER_SLICE_SIZE) == false) {
        ei_printf("ERR: Could not allocate audio buffer (size %d), this could be due to the window length of your model\r\n", EI_CLASSIFIER_RAW_SAMPLE_COUNT);
        return;
    }
}


void loop()
{
  UserButton.loop();
  StopButton.loop();
  ScoopButton.loop();
  ButtonHandler();
  DrinkHandler();
}


/* PDM buffer full callback, Get data and call audio thread callback */

static void pdm_data_ready_inference_callback(void)
{
    int bytesAvailable = PDM.available();

    // read into the sample buffer
    int bytesRead = PDM.read((char *)&sampleBuffer[0], bytesAvailable);

    if (record_ready == true) {
        for (int i = 0; i<bytesRead>> 1; i++) {
            inference.buffers[inference.buf_select][inference.buf_count++] = sampleBuffer[i];

            if (inference.buf_count >= inference.n_samples) {
                inference.buf_select ^= 1;
                inference.buf_count = 0;
                inference.buf_ready = 1;
            }
        }
    }
}

/* Init inferencing struct and setup/start PDM
@param[in]  n_samples  The n samples
@return     { description_of_the_return_value } */

static bool microphone_inference_start(uint32_t n_samples)
{
    inference.buffers[0] = (signed short *)malloc(n_samples * sizeof(signed short));

    if (inference.buffers[0] == NULL) {
        return false;
    }

    inference.buffers[1] = (signed short *)malloc(n_samples * sizeof(signed short));

    if (inference.buffers[1] == NULL) {
        free(inference.buffers[0]);
        return false;
    }

    sampleBuffer = (signed short *)malloc((n_samples >> 1) * sizeof(signed short));

    if (sampleBuffer == NULL) {
        free(inference.buffers[0]);
        free(inference.buffers[1]);
        return false;
    }

    inference.buf_select = 0;
    inference.buf_count = 0;
    inference.n_samples = n_samples;
    inference.buf_ready = 0;

    // configure the data receive callback
    PDM.onReceive(&pdm_data_ready_inference_callback);

PDM.setBufferSize(4096);

    // initialize PDM with:
    // - one channel (mono mode)
    // - a 16 kHz sample rate
    if (!PDM.begin(1, 16000)) {
                if(Serial) { ei_printf("Failed to start PDM!"); }
        microphone_inference_end();
        return false;
    }

    /* Calculate sample rate from sample interval */
    uint32_t audio_sampling_frequency = (uint32_t)(1000.f / EI_CLASSIFIER_INTERVAL_MS);

    if(audio_sampling_frequency != 16000) {
        nrf_pdm_clock_set((nrf_pdm_freq_t)pdm_clock_calculate(audio_sampling_frequency));
    }

    // set the gain, defaults to 20
    PDM.setGain(80);

    record_ready = true;
    return true;
}



/** Get raw audio signal data */

static int microphone_audio_signal_get_data(size_t offset, size_t length, float *out_ptr)
{
    numpy::int16_to_float(&inference.buffers[inference.buf_select ^ 1][offset], out_ptr, length);
    return 0;
}

/** @brief      Stop PDM and release buffers */

static void microphone_inference_end(void)
{
    PDM.end();
    free(inference.buffers[0]);
    free(inference.buffers[1]);
    free(sampleBuffer);
}

#if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_MICROPHONE
#error "Invalid model for current sensor."
#endif

void DrinkHandler(){

  switch (State) {
    case Offline: break;  // Beeping or Playing a melody with delays
  	case Static:  // Listening for drink sounds ...
      strip.clear();
      digitalWrite(LedEnable, LOW);
  	  if ( ButtonOutput == Click and !endStop){
        digitalWrite(MotEnable,  HIGH);
  			FeedMillis = millis();
  			State = Feeding;
        PrintMessage(FeedingMsg,0);
  			break;
  		}
  		if ( ButtonOutput == LongPress and !backStop ){
        digitalWrite(MotEnable,  HIGH);
  			State = Opening;
        PrintMessage(OpeningMsg,0);
  			break;
  		}
  		if ( consistentDrinking == 1 ) {
        PrintMessage(DrinkingMsg,0);
        DrinkMillis = millis();
  			State = Drinking;
        consistentDrinking = 0;
  			break;
  		}
      else {
        countDrinks();
      }
    break;
  	case Drinking:  // a treat is possible
      cometHandler(Blue);
      if ( ButtonOutput == Click and !endStop ){
        digitalWrite(MotEnable,  HIGH);
  			FeedMillis = millis();
  			State = Feeding;
        PrintMessage(FeedingMsg,0);
  			break;
  		}
  		if ( ScoopButton.isPressed() and !endStop ) {
        PrintMessage(ScoopMsg,1);
        digitalWrite(MotEnable,  HIGH);
        FeedMillis = millis();
  			State = Feeding;
    	  PrintMessage(FeedingMsg,0);
  			break;
  		}
  		if ( millis() > DrinkMillis + DrinkTimeout ){
        PrintMessage(DrinkTimeoutMsg,1); 
        DrinkMillis = 0;
  			State = Static;
        PrintMessage(StaticMsg,1);
        break;
  		}
      else break;
  	case Feeding:
      backStop = 0;
      RunMotor(0);
      cometHandler(Green);
  		if ( ButtonOutput == DoubleClick ){
  			StopMotor();
        PrintMessage(UserStopMsg,1);
        PrintMessage(StaticMsg,1);
        Beep(1000,2);  // Sets State to Offline for beep, then back to Static
        break;
  		}
  		if ( StopButton.isPressed() ) {
  			StopMotor();
        endStop = 1;
        PrintMessage(StopMsg,1);
        PrintMessage(StaticMsg,1);
        PlaySong();    // Sets State to Offline for song, then back to Static
        break;
  		}
  		if ( millis() > FeedMillis + FeedTimeout ) {
        StopMotor();
        FeedMillis = 0;
        PrintMessage(FeedTimeoutMsg,1);
        State = Static;
        PrintMessage(StaticMsg,1);
  			break;
  		}
      else break;
  	case Opening:
      cometHandler(Red);  // Red comets while backing up, enough time to get to end
      endStop = 0;
  	  RunMotor(1);
  		if ( ButtonOutput == DoubleClick ){
  			StopMotor();
        PrintMessage(UserStopMsg,1);
        PrintMessage(StaticMsg,1);
        Beep(1000,2);    // Sets State to Offline for song, then back to Static
  			break;
  		}
  		if ( StopButton.isPressed() ) {
  			StopMotor();
        backStop = 1;
        PrintMessage(StopMsg,1);
        PrintMessage(StaticMsg,1);
        Beep(1000,1);      // Sets State to Offline for song, then back to Static  
  			break;
  		}
    break;
  }
}

void ButtonHandler(){
  switch (ButtonState){
  	case Waiting:  // No keys being pressed
      ButtonOutput = None;
	  	if ( UserButton.isPressed() ) {
  			ButtonMillis = millis();
  			ButtonState = Counting;
	  		UserButton.resetCount();
        ButtonCount = 0;
	  	}
	  break;
  	case Counting:  // Check how many clicks
  		ButtonCount = UserButton.getCount();
	  	if ( millis() > ButtonMillis + ButtonTimeout ) {
        if (ButtonCount == 0) {ButtonOutput = LongPress;   PrintMessage(LongPressMsg,1);}
        if (ButtonCount == 1) {ButtonOutput = Click;       PrintMessage(ClickMsg,1);}
        if (ButtonCount >= 2) {ButtonOutput = DoubleClick; PrintMessage(DoubleClickMsg,0);}
        ButtonMillis = 0;
        ButtonCount = -1;
	  		ButtonState = Waiting;
  		}
    break;   
  }
}

void checkDrinking(){
      if (inference.buf_ready == 0) {
        return;
      }
      else if (inference.buf_ready == 1) {
        signal_t signal;
        signal.total_length = EI_CLASSIFIER_SLICE_SIZE;
        signal.get_data = &microphone_audio_signal_get_data;
        ei_impulse_result_t result = {0};
        EI_IMPULSE_ERROR r = run_classifier_continuous(&signal, &result, debug_nn);
        if (r != EI_IMPULSE_OK) ei_printf("ERR: Failed to run classifier (%d)\n", r);
        
        if (++print_results >= (EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW)) {
          if(result.classification[2].value > .7) {        // noise signal
            digitalWrite(LED_RED,HIGH);  // turn off any red
            digitalWrite(LED_BLUE,HIGH); // turn off any blue            
            digitalWrite(LED_GREEN,HIGH); // turn off any green
            isDrinking = 0;
          }
          else if (result.classification[1].value > .7) {  // eat signal
            digitalWrite(LED_BLUE,HIGH);  // turn off any blue
            digitalWrite(LED_GREEN,HIGH); // turn off any green
            digitalWrite(LED_RED,LOW);    // turn on RED
            isDrinking = 1;
          }
          else if (result.classification[0].value > .4) {  // drink signal
            digitalWrite(LED_RED,HIGH);   // turn off any red
            digitalWrite(LED_GREEN,HIGH); // turn off any green
            digitalWrite(LED_BLUE,LOW);   // turn on BLUE
            isDrinking = 1;
          }
          if(Serial){ 
            for (size_t i = 0; i < EI_CLASSIFIER_LABEL_COUNT; i++) {
              Serial.print(result.classification[i].label);
              Serial.print(":");
              Serial.print(result.classification[i].value);
              Serial.print(", ");
              Serial.print("  ");
            }
            Serial.print("drinkCounter:");
            Serial.println(drinkCounter);
          }
        print_results =0;
        }
        inference.buf_ready = 0;        
  }
}

void countDrinks(){
  checkDrinking(); 
  switch (drinkState){
    case Waiting:  // Not sensing drinking
      if ( isDrinking == 1 ) {
  			drinkStart = millis();
	  		drinkCounter = 0;
        drinkState = Counting;
	  	}
	  break;
  	case Counting:  // Check how many drink detections
  		if (isDrinking == 1) {
        drinkCounter++;
        isDrinking = 0;
      }
	  	if ( millis() > drinkStart + drinkDwell ) {
        if (drinkCounter >= drinkLimit) {
          consistentDrinking = 1;
          Serial.println("Consistent Drinking detected... ");
        }
        drinkStart = 0;
        drinkCounter = 0;
        drinkState = Waiting;
  		}
    break;   
  }
}


void RunMotor(int Direction){
  if ( Direction == 0) { digitalWrite(MotForward, HIGH); }
  if ( Direction == 1) { digitalWrite(MotBack, HIGH); }
}

void StopMotor(){
  digitalWrite(MotForward, LOW); 
  digitalWrite(MotBack,    LOW); 
  digitalWrite(MotEnable,  LOW);
}


void PrintMessage(char Message[],int LineBreak){
  if(Serial) {
      if (LineBreak == 1) Serial.println(Message);
      if (LineBreak == 0) Serial.print(Message);      
  }
}

void PlaySong() {
  State = Offline;
  for ( int i ; i < lastStep; i++ ){
    tone(Buzzer,Tones[i],Durations[i]);
    delay(Durations[i]+Pauses[i]);
    noTone(Buzzer);
    }
  State = Static;
}

void Beep( int freq, int count){
  State = Offline;
  for ( int i ; i < count; i++ ){
    tone(Buzzer,freq,500);
    delay(200);
  }
  noTone(Buzzer);
  State = Static;
}

void cometHandler(int color){
  digitalWrite(LedEnable, HIGH);  // enable the LED through the driver
  switch (ledState){
    case show:
      for (int i = 0; i < cometSize; i++)
        switch (color){
          case Red:
            strip.setPixelColor( Position+i, Bright, 0, 0 );
          break;
          case Green:
            strip.setPixelColor( Position+i, 0, Bright, 0 );
          break;
          case Blue:
            strip.setPixelColor( Position+i, 0, 0, Bright );
          break;
        }
      strip.show();
      pixelStartTime = millis();
      Position += Direction;
      ledState = pause;
      for (int j = 0; j < numLEDs; j++)
        if (random(10) > 5) fadePixel(j,color,fadePct);
    break;
    case pause:
      if(millis() > pixelStartTime + pixelTimeout){
        if (Position == (numLEDs - cometSize) || Position == 0) Direction *= -1;
        ledState = show;
    }
    break;
  }
}

void fadePixel(int pixel, int color, float Amt){
  long RGB = strip.getPixelColor(pixel);
  int R = (uint8_t)((RGB >> 16) & 0xff);
  int G = (uint8_t)((RGB >> 8) & 0xff);
  int B = (uint8_t)(RGB & 0xff);
  int faded = 0;

  switch (color){
    case Red:
      faded = int(Amt*R);
      strip.setPixelColor( pixel, faded, G, B );      
    break;
    case Green:
      faded = int(Amt*G);
      strip.setPixelColor( pixel, R, faded, B );      
    break;
    case Blue:
      faded = int(Amt*B);
      strip.setPixelColor( pixel, R, G, faded );      
    break;
  }
}

Calculations with mixed types can cause trouble (DrinkTimeout is an "int"). Configure/"type" DrinkTimeout as an unsigned long (and all the other ints doing math with millis() ) and see if calculations are effecting the color/range.

Happy to try that, but I guess it only affects the nodelay timeouts. Who knows it could help!?

I converted all startTimes and Timeouts to unsigned long. Same behavior. You know, all of the logic worked, including the blue led animations before I added the PDM and Inferencing from Edge Impulse. I was using a button to stand in for the detection of the keysound, which in this case is the sound of a dog drinking from a bowl. Now to debug, I have to lean my face down by this device and make a licking/drinking sound myself lol! I have some recordings that I used for training, but it's easier (but incredibly silly) to make the sounds myself :slight_smile:

Any other thoughts on this issue? I'm about to implement a workaround, have ordered a separate blue "noods" string from Adafruit and will use the one remaining GPIO to see if I can light it. So sad I can't figure this out :frowning: but will push through it.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.