Help combining multiple codes with different triggers

Hello everyone, so I am using a lightblue bean as my board, so far I have liked it a lot for doing some really basic things however now I’m wanting to go a little further. I have 2 different loops that I want to join together and I have no Idea how to do so. The first comes from the adafruit goggles. which is below.

#include <SoftwareSerial.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
 #include <avr/power.h>
#endif

#define RX_PIN    2 // Connect this Trinket pin to BLE 'TXO' pin
#define CTS_PIN   1 // Connect this Trinket pin to BLE 'CTS' pin
#define LED_PIN   0 // Connect NeoPixels to this Trinket pin
#define NUM_LEDS 32 // Two 16-LED NeoPixel rings
#define FPS      30 // Animation frames/second (ish)

SoftwareSerial    ser(RX_PIN, -1);
Adafruit_NeoPixel pixels(NUM_LEDS, LED_PIN);

void setup() {
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000L)
  // MUST do this on 16 MHz Trinket for serial & NeoPixels!
  clock_prescale_set(clock_div_1);
#endif
  // Stop incoming data & init software serial
  pinMode(CTS_PIN, OUTPUT); digitalWrite(CTS_PIN, HIGH);
  ser.begin(9600);

  pixels.begin(); // NeoPixel init
  // Flash space is tight on Trinket/Gemma, so setBrightness() is avoided --
  // it adds ~200 bytes.  Instead the color picker input is 'manually' scaled.
}

uint8_t  buf[3],              // Enough for RGB parse; expand if using sensors
         animMode = 0,        // Current animation mode
         animPos  = 0;        // Current animation position
uint32_t color    = 0x400000, // Current animation color (red by default)
         prevTime = 0L;       // For animation timing

void loop(void) {
  int      c;
  uint32_t t;

  // Animation happens at about 30 frames/sec.  Rendering frames takes less
  // than that, so the idle time is used to monitor incoming serial data.
  digitalWrite(CTS_PIN, LOW); // Signal to BLE, OK to send data!
  for(;;) {
    t = micros();                            // Current time
    if((t - prevTime) >= (1000000L / FPS)) { // 1/30 sec elapsed?
      prevTime = t;
      break;                                 // Yes, go update LEDs
    }                                        // otherwise...
    if((c = ser.read()) == '!') {            // Received UART app input?
      while((c = ser.read()) < 0);           // Yes, wait for command byte
      switch(c) {
       case 'B':       // Button (Control Pad)
        if(readAndCheckCRC(255-'!'-'B', buf, 2) & (buf[1] == '1')) {
          buttonPress(buf[0]); // Handle button-press message
        }
        break;
       case 'C':       // Color Picker
        if(readAndCheckCRC(255-'!'-'C', buf, 3)) {
          // As mentioned earlier, setBrightness() was avoided to save space.
          // Instead, results from the color picker (in buf[]) are divided
          // by 4; essentially equivalent to setBrightness(64).  This is to
          // improve battery run time (NeoPixels are still plenty bright).
          color = pixels.Color(buf[0]/4, buf[1]/4, buf[2]/4);
        }
        break;
       case 'Q':       // Quaternion
        skipBytes(17); // 4 floats + CRC (see note below re: parsing)
        break;
       case 'A':       // Accelerometer
#if 0
        // The phone sensors are NOT used by this sketch, but this shows how
        // they might be read.  First, buf[] must be delared large enough for
        // the expected data packet (minus header & CRC) -- that's 16 bytes
        // for quaternions (above), or 12 bytes for most of the others.
        // Second, the first arg to readAndCheckCRC() must be modified to
        // match the data type (e.g. 'A' here for accelerometer).  Finally,
        // values can be directly type-converted to float by using a suitable
        // offset into buf[] (e.g. 0, 4, 8, 12) ... it's not used in this
        // example because floating-point math uses lots of RAM and code
        // space, not suitable for the space-constrained Trinket/Gemma, but
        // maybe you're using a Pro Trinket, Teensy, etc.
        if(readAndCheckCRC(255-'!'-'A', buf, 12)) {
          float x = *(float *)(&buf[0]),
                y = *(float *)(&buf[4]),
                z = *(float *)(&buf[8]);
        }
        // In all likelihood, updates from the buttons and color picker
        // alone are infrequent enough that you could do without any mention
        // of the CTS pin in this code.  It's the extra sensors that really
        // start the firehose of data.
        break;
#endif
       case 'G':       // Gyroscope
       case 'M':       // Magnetometer
       case 'L':       // Location
        skipBytes(13); // 3 floats + CRC
      }
    }
  }
  digitalWrite(CTS_PIN, HIGH); // BLE STOP!

  // Show pixels calculated on *prior* pass; this ensures more uniform timing
  pixels.show();

  // Then calculate pixels for *next* frame...
  switch(animMode) {
   case 0: // Pinwheel mode
    for(uint8_t i=0; i<NUM_LEDS/2; i++) {
      uint32_t c = 0;
      if(((animPos + i) & 7) < 2) c = color; // 4 pixels on...
      pixels.setPixelColor(   i, c);         // First eye
      pixels.setPixelColor(NUM_LEDS-1-i, c); // Second eye (flipped)
    }
    animPos++;
    break;
   case 1: // Sparkle mode
    pixels.setPixelColor(animPos, 0);     // Erase old dot
    animPos = random(NUM_LEDS);           // Pick a new one
    pixels.setPixelColor(animPos, color); // and light it
    break;
  }
}

boolean readAndCheckCRC(uint8_t sum, uint8_t *buf, uint8_t n) {
  for(int c;;) {
    while((c = ser.read()) < 0); // Wait for next byte
    if(!n--) return (c == sum);  // If CRC byte, we're done
    *buf++ = c;                  // Else store in buffer
    sum   -= c;                  // and accumulate sum
  }
}

void skipBytes(uint8_t n) {
  while(n--) {
    while(ser.read() < 0);
  }
}

void buttonPress(char c) {
  pixels.clear(); // Clear pixel data when switching modes (else residue)
  switch(c) {
   case '1':
    animMode = 0; // Switch to pinwheel mode
    break;
   case '2':
    animMode = 1; // Switch to sparkle mode
    break;
   case '3':
    break;
   case '4':
    break;
   case '5': // Up
    break;
   case '6': // Down
    break;
   case '7': // Left
    break;
   case '8': // Right
    break;
  }
}

Now the second one I would like to integrate the code from lightblue bean ANCS project with the first code. Which is as follows.

AncsNotification notifications[8];

bool callHappened = false;

void setup() {
  // Serial port is initialized automatically; we don't have to do anything
  BeanAncs.enable();
  Bean.enableMotionEvent(HIGH_G_EVENT);
}

void loop() {
  int msgAvail = BeanAncs.notificationsAvailable();

  if (callHappened) {
    while (!Bean.checkMotionEvent(HIGH_G_EVENT)) {
      incomingCall();
    }
    callHappened = false;
  }

  if (msgAvail) {
    Bean.setLedRed(30);

    BeanAncs.getNotificationHeaders(notifications, 8);

    for (int i = 0; i < msgAvail; i++) {
      Serial.print("cat:");
      Serial.println(notifications[i].catID);
      Serial.print("flg:");
      Serial.println(notifications[i].flags);
      Serial.print("evt:");
      Serial.println(notifications[i].eventID);
      Serial.print("cnt:");
      Serial.println(notifications[i].catCount);
      Serial.print("id: ");
      Serial.println(notifications[i].notiUID);

      if (notifications[i].catID == 1 || notifications[i].catID == 2) {
        callHappened = true;
      }

      char data[64] = {0};
      int len = BeanAncs.getNotificationAttributes(
        NOTI_ATTR_ID_MESSAGE,
        notifications[i].notiUID,
        39,
        (uint8_t *)data,
        5000);

      Serial.print("l: ");
      Serial.print(len);
      Serial.print(" ");
      for (int j = 0; j < len; j++) {
        Serial.print(data[j]);
      }
      Serial.println();
      delay(3000);
    }
    Bean.setLedRed(0);
  }
}

void incomingCall() {
  Bean.setLed(100, 0, 0);
  delay(150);
  Bean.setLed(0, 100, 0);
  delay(150);
  Bean.setLed(0, 0, 100);
  delay(150);
  Bean.setLed(0, 0, 0);
}

Now I know that last part is not using the neopixels, which that I will address on my own but I don’t know how to get this to use the color picker or sparkle all while still listening for the ancs. I was thinking of splitting it into multiple loops but I was told that this is bad coding etiquette and should stray away from that. So now I’m confused how to add the first code with the ANCS code. Can someone please help me.

This Simple Merge Demo may help get you started.

You need to ensure that the different programs are not trying to use the same thing (such as an I/O pin) for different purposes.

Unfortunately it is not unusual to find that different libraries do not work together.

...R

Robin2:
This Simple Merge Demo may help get you started.

You need to ensure that the different programs are not trying to use the same thing (such as an I/O pin) for different purposes.

Unfortunately it is not unusual to find that different libraries do not work together.

...R

Thanks for pointing me to this, just two more questions, so in this I am creating two sub loops then? and how wil the ancs loop interrupt the first loop? as I am not using a button but an event from the ancs loop to trigger from going from the first loop 1 to ancs loop and back to loop one when ancs loop is cleared. Sorry not trying to be a pain. just learning.

itkujo:
Thanks for pointing me to this, just two more questions, so in this I am creating two sub loops then?

The correct term is "functions"

and how wil the ancs loop interrupt the first loop? as I am not using a button but an event from the ancs loop to trigger from going from the first loop 1 to ancs loop and back to loop one when ancs loop is cleared. Sorry not trying to be a pain. just learning.

The short answer is that I don't know. It would take me a long time to figure out what your programs do. Can you provide a more extensive description and maybe references to key lines in the code?

It may be helpful for you to make the combined program and provide the description based on that rather than on the separate programs. And post the combined program, of course.

...R

In general you rename the original loop() as e.g. pixelLoop() and beansLoop(), and in your main sketch you have

void loop() {
 pixelLoop();
 beansLoop();
}

This will work as long as the loops are not blocking, what is not the case with the first (Neopixel) loop().

That Neopixel loop() effectively consists of two parts, where the Neopixel part at its end should execute every 1/30 second. The first part uses the remaining time to check the ser input, and breaks out of the for(;:wink: loop after about 30ms.

So the first part should be removed from that loop, and the remaining pixelLoop() must be called every 30ms.
The extracted for(;:wink: loop again can be split into two parts, the pixel timing and BLE data handling. The pixel timing now goes into the main loop(), and calls the remaining pixelLoop() only every 30ms. The BLE data handling can be moved into another bleLoop(), to be called regularly from the main loop().

Unfortunately the SoftwareSerial library again is blocking, and should be replaced by Serial or some non-blocking library, e.g. AltSoftSerial.

The beansLoop() also is blocking, it uses delay() all over, which must be replaced by non-blocking delays.

You picked two very non-cooperative sketches, which deserve a deep redesign when they shall be combined into one sketch. I'd suggest that you use ALib0 and separate the sketches into tasks, which then are made non-blocking. The task macros allow to e.g. replace every blocking delay() by non-blocking taskDelay(), and while loops by taskWaitFor() and taskSwitch().

The resulting loop() may look like this:

void loop() {
 every(30) pixelLoop(); //animation
 bleLoop(); //sensor input, set animation type
 beanLoop(); //get data from BeanAncs
 incomingCallLoop(); //blink LEDs while incoming call is detected
 //eventually more tasks...
}

where all previously blocking loops are converted into non-blocking tasks, by enclosing the code in beginTask() and endTask().

Do you think that you can split the sketches into pieces, which then can be made non-blocking?

Thank you Robin2 and Dr. Diettrich thank you both for the information. I will start working on this and see what I come up with. I'll then test as well as upload to help others in the future or to debug. I have seen a lot of things around here with code switching between buttons but maybe I can show what I'm doing and help others.

Also is it possible that within all non ANCS loops I could use my callhappened Boolean and make it go to another loop if call happened is true, just wondering. anyhow, here goes to the next bit of coding. and thanks again, I really appreciate the fast response on this.

ok, so I took the delays out off the loop by doing this, would it work? here is the before and after of the code. but long story short I'm using millis()+200 and so on to alternate the lighting.

From this

void incomingCall() {
  Bean.setLed(100, 0, 0);
  delay(150);
  Bean.setLed(0, 100, 0);
  delay(150);
  Bean.setLed(0, 0, 100);
  delay(150);
  Bean.setLed(0, 0, 0);
}
void incomingCall() {
  unsigned long currentMillis = millis();
  Bean.setLed(100, 0, 0);
 unsigned long currentMillis = millis()+200;
  Bean.setLed(0, 100, 0);
  unsigned long currentMillis = millis()+400;
  Bean.setLed(0, 0, 100);
  unsigned long currentMillis = millis()+600;
  Bean.setLed(0, 0, 0);
}

with a 800ms interval.

Nice try :wink:
You should not ignore compiler warnings. Does the new code blink at all?

See the BlinkWithoutDelay example how to implement non-blocking pauses.

itkujo:
ok, so I took the delays out off the loop by doing this, would it work? here is the before and after of the code. but long story short I’m using millis()+200 and so on to alternate the lighting.

From this

void incomingCall() {

Bean.setLed(100, 0, 0);
  delay(150);
  Bean.setLed(0, 100, 0);
  delay(150);
  Bean.setLed(0, 0, 100);
  delay(150);
  Bean.setLed(0, 0, 0);
}

Here is a technique that gives the function a 1-shot timer to ‘delay’ on need and a state machine to run only the part desired. Note that it must be run at least as often as you expect precision in the timing. From the look of it, it should only run when there is an incoming call.

void incomingCall() 
{
  static byte callState = 0;
  static unsigned long milliStart = 0;
  static unsigned long milliWait = 0;

  if ( milliWait > 0 )
  {
    if ( millis() - milliStart >= milliWait )      milliWait = 0;
    else                                                   return; // wait not over, check again next time
  }

  switch ( callState )
  {
    case 0 :
    Bean.setLed(100, 0, 0);
    milliStart = millis();
    milliWait = 150;
    callState = 1;
    break;

    case 1 :
    Bean.setLed(0, 0, 100);
    milliStart = millis();
    milliWait = 150;
    callState = 2;
    break;

    case 2 :
    Bean.setLed(0, 0, 0);
    callState = 0;
    break;
  }
}
}

Much better :slight_smile:

But now you cannot make the LED blink, because milliwait can be nothing but zero - should not be static.

Using task macros the function could be simplified into something like this:

void incomingCall(bool on) {
taskBegin();
  taskWaitFor(on);
  Bean.setLed(100, 0, 0);
  taskDelay(150);
  Bean.setLed(0, 100, 0);
  taskDelay(150);
  Bean.setLed(0, 0, 100);
  taskDelay(150);
  Bean.setLed(0, 0, 0);
  //taskDelay(150); //optional
taskEnd();
}

DrDiettrich:
Much better :slight_smile:

But now you cannot make the LED blink, because milliwait can be nothing but zero - should not be static.

You should read that code again. As is, 1st call will run case 0 that.... sets milliWait to 150.

You are right, but this only inverts the problem: how to stop blinking then?

That's why I suggest an on/off parameter or a global variable, to indicate the call state.

All I showed is a technique to remove delays from functions.
I would call the function from inside of a trigger or state machine but that's beyond the scope of what I showed.

Hey guys I wanted to say that I really appreciate all of your help. This project has been partially completed now, but I am at a completely new stand still but all of your help and the practice has really helped me understand Arduino a whole lot more.