Anyone added a Bridge Client to for loops to keep 'listening' for a new command?

Hi everyone,

I love how my Yun, but have a particular issue. I have built upon the standard Bridge example in order to accept REST calls to launch strips of LED lights into certain patterns. But, due to the single-threaded nature of the Arduino, once I've call the function from the web and launch into the loop, I can't break that loop and send it to another through a REST call since it's not listening.

Can I add a listener to the loop? Could I then listen while in the loop and break out for another command? Has anyone tried this.

I would LOVE any advice.

Thank you! -Liz

Here's the code:

// Libraries
#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>
#include "FastLED.h"

#define DATA_PIN 3
#define NUM_LEDS 60 //number of LEDs

uint8_t max_bright = 255; //rainbow

struct CRGB leds[NUM_LEDS];

//RAINBOW
// Initialize global variables for sequences
uint8_t thisdelay = 5;                                        // A delay value for the sequence(s)
uint8_t thishue = 0;                                          // Starting hue value.
uint8_t deltahue = 10;                                        // Hue change between pixels.


// Yun server
YunServer server;

void setup(){
  Serial.begin(57600);
  pinMode(13,OUTPUT);
  digitalWrite(13, LOW);
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
  FastLED.setBrightness(max_bright);
  set_max_power_in_volts_and_milliamps(5, 500); 

  Bridge.begin();
  
  // Start Yun server
  server.listenOnLocalhost();
  server.begin();

}

void loop()
{
  // Get clients coming from server
  YunClient client = server.accept();

  // There is a new client?
  if (client) {
    // Process request
    process(client);

    // Close connection and free resources.
    client.stop();
  }

  // Poll every 50ms
  delay(50); 
  
}

// Process called when the REST API is called
void process(YunClient client) {
  
  // Read the command
  String command = client.readStringUntil('/');

  if (command == "dress") {   
     dressCommand(client);
  }

}

// Process dress commands
void dressCommand(YunClient client) {
  
  // Read command and react accordingly
  String command = client.readStringUntil('\r');
  
  ////////////////////////////////////////////////////////////////////////////
  
  if (command == "rec") {
   heartbeat(3);
   FastLED.clear();
   FastLED.show();
  }

  if (command == "test") {

  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
  }
  
  if (command == "equ") {
   equality(1);
   FastLED.clear(); 
   FastLED.show();
  }
  
}

/////////////////////////////////////////////////////
////////////////////////////////////////////////////

void heartbeat(int count)
{
  while(count > 0)
  {
for (int b=50;b<255;b++){ //brightness goes up
 for(int i=0;i<NUM_LEDS;i++){
   leds[i] = CHSV( 0, 255, b);
  //leds [i] = CRGB::Red;
 }
  FastLED.show();
    delay (10); // how long it takes to fade down
 }
//delay(500);

 //red pulse down
    for (int b=255;b>50;b--){ //brightness goes down
    for(int i=0;i<NUM_LEDS;i++){
         leds[i] = CHSV( 0, 255, b);
 }
  FastLED.show();
    delay (10); // how long it takes to fade down
 }
 count = count - 1;
}
}

///////////////////////////////////////////////////////
///////////////////////////////////////////////////////

void equality(int count)
{
    while(count > 0)
  {
      for (int n=0;n<3000;n++){
      rainbow_march();
    show_at_max_brightness_for_power(); 
   }
    count = count - 1;
  }
}
void rainbow_march() {                                        // The fill_rainbow call doesn't support brightness levels
  thishue++;                                                  // Increment the starting hue.
  fill_rainbow(leds, NUM_LEDS, thishue, deltahue);  // Use FastLED's fill_rainbow routine.
} // rainbow_march()

You could put another listen call inside your loop, but that is not the way to go - it will very quickly get messy and unmanageable.

The loops and delays that you have are easy to implement, but they have limitations that are apparent in this project - they tie up the processing and don't let other tasks complete. Your best bet is to approach this from a completely different direction: in an ideal implementation, your loop() function is called very frequently and each call completes quickly. As you have it now, that happens until you get a REST call, at which time the loop() function is busy processing that request and does not return until the sequences is complete - not good.

I think the first thing you should do is experiment with the Blink Without Delay example and understand how it works. Then do some research on the concept of a "state machine." Your code will need to keep track of what pattern it is displaying, where it is in the pattern, and how many milliseconds are needed before the next change in the pattern. Use the Blink Without Delay concept to keep track of the time when the next change should occur, and when that time comes, update the outputs, and advance to the next step of the sequence.

The general idea is that loop() is called over and over, and each time it checks to see if it is time to do the next step. Most of the time it won't be, so it will quickly return. Every so often it will be time for an update, so the outputs are changed, the time for next change is updated, and the loop() function returns.

Maybe you should consider using the non-blocking mailbox instead of the REST and make your logic asynchronous using the mailbox feature like an MQ (i.e. pull next command from the queue when you are ready).