Can Arduino Call Functions Besides cayenne.loop?

I have an Arduino Uno reading data from an HX711 load sensor and then relaying that data to a Raspberry Pi 3B+ via serial so that it can be hosted on Cayenne. It is running the Cayenne MQTT library. In addition to serving the data to Cayenne via USB serial protocol, the Arduino reads out to an I2C LCD and conditionally lights a Neopixel strip based on the load cell reading. I have also included an ISR (interrupt service routine) to calibrate the scale when necessary via a button press.

Problem: Code runs well when simply serving data via the Cayenne_OUT. Code also runs when executing all of the features above without writing to cayenne. When I attempt to do both in the same program, I get nothing.

My questions are:

  1. Can the Arduino run any other function calls in the void loop, ie the ISR, other than cayene.loop()? Where must other function calls be placed so they are readable?

  2. Can function calls be made from the Cayenne_OUT function to do other things with the sensor data? For example, once sensor is read, it is then handed off to an external function for LCD printing or Neopixel setting before being written to Cayenne. Does all code have to be contained in the Cayenne_OUT function?

yes, it can run any other functions from the main loop.

yes, you can call other function from CAYENNE_OUT. but if you dont want to use CAYENNE_OUT, then you can publish data from the main loop but should be sent at 15 second interval.

void loop() {
   Cayenne.loop();
   
   if(millis() - lastMillis > 15000) {
      lastMillis = millis();
      Cayenne.virtualWrite(0, lastMillis);
   }
}
1 Like

I am honestly unable to assess where the blockage is. I am getting a successful connection in the terminal of the RPi3 when I run the connection script, but there is no data being pushed to Cayenne and the Arduino remains offline. I am going to attach the rather lengthy code if you can detect any issues with the lines related to Cayenne. Again, I know I don’t write super clean code as a beginner, but the script runs perfectly, but gets caught up when trying to implement the Cayenne credentials.

    //include I2C LCD Libraries
    #include <Wire.h>
    #include <LiquidCrystal_I2C.h>
    //Create I2C Object w/address, columns, and rows
    LiquidCrystal_I2C lcd(0x27, 20, 4);

    //include HX711 Library
    #include "HX711.h"

    //include Cayenne MQTT Library for Serial Coms with Pi
    #include <CayenneMQTTSerial.h>

    //Include Adafruit Neopixel Library and define Pin# for gauge
    #include <Adafruit_NeoPixel.h>
    #define PIN 6

    //Declare Pins for the Mass ISR and a Mass Global
    const byte ISRSwitch = 2;//Turns on LEDs and must be engaged for calibration ISR
    const byte ISRButton = 3;//Will initiate calibration ISR if ISRSwitch == 0
    const byte ISRButtonLED = 4;//Turns on with ISRSwitch
    const byte ISRSwitchLED = 5;//Turns on with ISRSwitch
    
    //Create State Flags for ISRs
    volatile boolean calibrationStart = false;//changes with IRSButton
    volatile boolean notify = false;//changes with ISRButton
    volatile boolean switchON = false;//changes with ISRSwitch

    //Declare buttons and dials for calibration sequence
    const byte next = 7;
    const byte setButton = 8;
    const byte dialPot = A0;
    //Declare state flags for calibration sequence
    boolean setEmptyMass = false;
    boolean setFullVolume = false;
    boolean setFullMass = false;
    boolean setColorOne = false;
    boolean setColorTwo = false;
    boolean setColorThree = false;
    //Default values for calibration variables
float emptyMass = 16.0;//Default mass of empty container in lbs
float fullVolume = 660.0;//default volume of full container in fluid ounces
float pourVolume = 12.0;
float fullMass;//Global variable to be changed in Calibration

//Cayenne credentials
char username[] = "********************";//omitted
char password[] = "*********************";//omitted
char clientID[] = "***********************";//omitted

//Define Cayenne Channels
const byte volumeChannel = 0;//shows volume in keg
const byte poursChannel = 1;//shows how many pours remain

//Set HX711 Pins for the scale
#define DOUT 11
#define CLK 12
//Create HX711 Object
HX711 scale(DOUT, CLK);
//Set calibration factor for scale
#define calibration_factor  -10080
#define zero_factor 1052867

//Create buffers for variable readings and LCD string printouts
char line1 [21];
char lg_str [6];//handles liquidVolume
char line2 [21];//reports liquidVolume string
char sm_str [5];//handles poursRemaining

//Name of Current Beer on Tap and Brewery
char myBeer[] = "Saturn in Scorpio";
char myBrewery[] = "Foreign Objects";

//Create Adafruit Neopixel Object with colors
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
uint32_t red = strip.Color(255,0,0);
uint32_t orange = strip.Color(255,140,0);
uint32_t yellow = strip.Color(255,255,0);
uint32_t green = strip.Color(0,128,0);
uint32_t blue = strip.Color(0,0,255);
uint32_t navyBlue = strip.Color(0,0,128);
uint32_t purple = strip.Color(128,0,128);
uint32_t white = strip.Color(127,127,127);
uint32_t off = strip.Color(0,0,0);
uint32_t colorOne = red;
uint32_t colorTwo = blue;
uint32_t colorThree = white;

//Variables for Cayenne call
unsigned long lastMillis = 0;
float liquidVolume;
float poursRemaining;

void setup() {
//Open Serial Communication

//Baud rate can be specified by calling Cayenne.begin(username, password, clientID, 9600);
Cayenne.begin(username, password, clientID, 9600);

//Create the ISRs and pinModes for ISRSwitch and ISRButton
digitalWrite(ISRSwitch, HIGH);
digitalWrite(ISRButton, HIGH);
pinMode(ISRSwitch, INPUT_PULLUP);
pinMode(ISRButton, INPUT_PULLUP);

attachInterrupt(digitalPinToInterrupt(ISRSwitch), LEDStateISR, FALLING);
attachInterrupt(digitalPinToInterrupt(ISRButton), CalibrationISR, FALLING);

pinMode(ISRSwitchLED, OUTPUT);
pinMode(ISRButtonLED, OUTPUT);

//Declare pinModes for calibration buttons
pinMode(setButton, INPUT_PULLUP);
pinMode(next, INPUT_PULLUP); 

//Initiate Neopixel Object and clear the pixels
strip.begin();
strip.show();

//Initialize your LCD object as being a 16 column, 2 row LCD
lcd.begin();

for(int i= 0; i< 3; i++) //flash the LCD 3 times to test upon Power On
  {
    lcd.backlight();
    delay(250);
    lcd.noBacklight();
    delay(250);
  }
    lcd.backlight(); //set backlight as ON after 3 flashes

//Introduction prints name of beer on LCD row 1 and Brewery to row 2
lcd.setCursor(0,0);
lcd.print(myBeer);
lcd.setCursor(0,1);
lcd.print(myBrewery);

//Turn on the Scale
scale.set_scale(calibration_factor);
scale.set_offset(zero_factor);

}

void loop() {
  Cayenne.loop();
  
  //Begins calibration sequence if ISR has been run
  while (calibrationStart == true){
    while (notify == true){
      notify = false;//reset notify flag
      setEmptyMass = true;//raise emptyMass flag
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Entering Calibration");
      lcd.setCursor(0,1);
      lcd.print("Lower Red Toggle");
      lcd.setCursor(0,2);
      lcd.print("Directions to Follow");
      delay(5000);
      lcd.clear();    
    }
    while(setEmptyMass == true){
      //lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Put empty container");
      lcd.setCursor(0,1);
      lcd.print("on the scale and...");
      lcd.setCursor(0,2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0,3);
      lcd.print("To SKIP Press Green");
      if (digitalRead(setButton) == 0){
        setEmptyMass = false;
        setFullVolume = true;
        emptyMass = scale.get_units();
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Empty Mass is Set!");
        dtostrf(emptyMass,4,1,sm_str);
        sprintf(line1, "Empty Mass: %s lbs", sm_str);
        lcd.setCursor(0,1);
        lcd.print(line1);
        delay(3000);
        lcd.clear();  
      }
      if (digitalRead(next) == 0){
        setEmptyMass = false;
        setFullVolume = true;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Empty Mass is Set!");
        dtostrf(emptyMass,4,1,sm_str);
        sprintf(line1, "Empty Mass: %s lbs", sm_str);
        lcd.setCursor(0,1);
        lcd.print(line1);
        delay(3000);
        lcd.clear();  
      }
    }
    while(setFullVolume == true){
      //lcd.clear();
      float localPot = analogRead(dialPot);//read the volume calibration dial
      float localVolume = map(localPot, 0, 1023, 320,1280); //convert reading to ounces between 2.5 and 10 gallons
      localVolume = constrain(localVolume, 320, 1280);//restrict the reading to an acceptable range 
      lcd.setCursor(0,0);
      lcd.print("Adjust Vol with Dial");
      dtostrf(localVolume,4,0,sm_str);
      sprintf(line1, "Total Vol: %s oz", sm_str);
      lcd.setCursor(0,1);
      lcd.print(line1);
      lcd.setCursor(0,2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0,3);
      lcd.print("To SKIP Press Green");
      if(digitalRead(setButton) == 0){
        setFullVolume = false;
        setColorOne = true;
        fullVolume = localVolume;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Full Volume is Set!");
        dtostrf(fullVolume,4,0,sm_str);
        sprintf(line1, "Total Vol: %s oz", sm_str);
        lcd.setCursor(0,1);
        lcd.print(line1);
        delay(3000);
        lcd.clear();
      }
      if(digitalRead(next) == 0){
        setFullVolume = false;
        setColorOne = true;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Full Volume is Set!");
        dtostrf(fullVolume,4,0,sm_str);
        sprintf(line1, "Total Vol: %s oz", sm_str);
        lcd.setCursor(0,1);
        lcd.print(line1);
        delay(3000);
        lcd.clear();
      }
    }
    
while(setColorOne ==true){
      char colorPrintOne[] = "red";
      char* color = choosePrint();
      uint32_t localColor = chooseColor(color);
      printColor(localColor);
      
      lcd.setCursor(0,0);
      lcd.print("Adjust Color w/ Dial");
      sprintf(line1, "1st Color: %s   ", color);
      lcd.setCursor(0,1);
      lcd.print(line1);
      lcd.setCursor(0,2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0,3);
      lcd.print("To SKIP Press Green");
      if(digitalRead(setButton) == 0){
        setColorOne = false;
        setColorTwo = true;
        colorOne = localColor;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Color One is Set!");
        sprintf(line1, "1st Color: %s   ", color);
        lcd.setCursor(0,1);
        lcd.print(line1);
        printColor(colorOne);
        delay(3000);
        pixelsOff();
        lcd.clear();
      }
      if(digitalRead(next) == 0){
        setColorOne = false;
        setColorTwo = true;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Color One is Set!");
        sprintf(line1, "1st Color: %s   ", colorPrintOne);
        lcd.setCursor(0,1);
        lcd.print(line1);
        printColor(colorOne);
        delay(3000);
        pixelsOff();
        lcd.clear();
    }
   } 
while(setColorTwo ==true){
      char colorPrintTwo[] = "blue";
      char* color = choosePrint();
      uint32_t localColor = chooseColor(color);
      printColor(localColor);
      
      lcd.setCursor(0,0);
      lcd.print("Adjust Color w/ Dial");
      sprintf(line1, "2nd Color: %s   ", color);
      lcd.setCursor(0,1);
      lcd.print(line1);
      lcd.setCursor(0,2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0,3);
      lcd.print("To SKIP Press Green");
      if(digitalRead(setButton) == 0){
        setColorTwo = false;
        setColorThree = true;
        colorTwo = localColor;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Color Two is Set!");
        sprintf(line1, "2nd Color: %s   ", color);
        lcd.setCursor(0,1);
        lcd.print(line1);
        printColor(colorTwo);
        delay(3000);
        pixelsOff();
        lcd.clear();
      }
      if(digitalRead(next) == 0){
        setColorTwo = false;
        setColorThree = true;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Color Two is Set!");
        sprintf(line1, "2nd Color: %s   ", colorPrintTwo);
        lcd.setCursor(0,1);
        lcd.print(line1);
        printColor(colorTwo);
        delay(3000);
        pixelsOff();
        lcd.clear();
    }
   } 
   while(setColorThree ==true){
      char colorPrintThree[] = "white";
      char* color = choosePrint();
      uint32_t localColor = chooseColor(color);
      printColor(localColor);
      
      lcd.setCursor(0,0);
      lcd.print("Adjust Color w/ Dial");
      sprintf(line1, "3rd Color: %s   ", color);
      lcd.setCursor(0,1);
      lcd.print(line1);
      lcd.setCursor(0,2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0,3);
      lcd.print("To SKIP Press Green");
      if(digitalRead(setButton) == 0){
        setColorThree = false;
        setFullMass = true;
        colorThree = localColor;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Color Three is Set!");
        sprintf(line1, "3rd Color: %s   ", color);
        lcd.setCursor(0,1);
        lcd.print(line1);
        printColor(colorThree);
        delay(3000);
        pixelsOff();
        lcd.clear();
      }
      if(digitalRead(next) == 0){
        setColorThree = false;
        setFullMass = true;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Color Three is Set!");
        sprintf(line1, "3rd Color: %s   ", colorPrintThree);
        lcd.setCursor(0,1);
        lcd.print(line1);
        printColor(colorThree);
        delay(3000);
        pixelsOff();
        lcd.clear();
    }
   }  
    while(setFullMass == true){
      //lcd.clear();
      fullMass = scale.get_units(); 
      lcd.setCursor(0,0);
      lcd.print("Set Total Mass");
      dtostrf(fullMass,5,1,lg_str);
      sprintf(line1, "Full Mass: %s lbs", lg_str);
      lcd.setCursor(0,1);
      lcd.print(line1);
      lcd.setCursor(0,2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0,3);
      lcd.print("DO NOT Press Green");
      if (digitalRead(setButton)==0){
        setFullMass = false;
        calibrationStart = false;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Full Mass is Set!");
        lcd.setCursor(0,1);
        lcd.print(line1);
        delay(3000);
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Calibration Over!");
        lcd.setCursor(0,1);
        lcd.print("Tasty IPAs Await.");
        lcd.setCursor(0,2);
        lcd.print("Enjoy your beer...");
        lcd.setCursor(0,3);
        lcd.print("LET'S GO RANGERS!");
        delay(3000);
        switchON = false;
        digitalWrite(ISRButtonLED, switchON);
        digitalWrite(ISRSwitchLED, switchON);
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print(myBeer);
        lcd.setCursor(0,1);
        lcd.print(myBrewery);
      }
    }
  }
  kegTester();
  if(millis() - lastMillis > 15000){
    lastMillis = millis();
    Cayenne.virtualWrite(volumeChannel, liquidVolume);
    Cayenne.virtualWrite(poursChannel, poursRemaining);  
  }
}

void kegTester (void)
{
  //Grabs current mass of container
  float currentMass = scale.get_units();
  float liquidMassLbs = currentMass - emptyMass;//weight in lbs of beer only
  float liquidMassOunces = liquidMassLbs * 16.0; // weight of beer in ounces
  noInterrupts();//Block ISR
  float fullMassLocal = fullMass;
  float fullLiquidMassOunces = ((fullMassLocal - emptyMass)*16.0);
  interrupts();//reenable ISR
  //convert the mass of liquid into fluid ounces
  liquidVolume = map (liquidMassOunces, 0,fullLiquidMassOunces, 0, fullVolume);
  
  liquidVolume = constrain(liquidVolume, 0, fullVolume);//keeps data in acceptable range
  //calculate the # of pours in container based on a tulip glass
  poursRemaining = liquidVolume /pourVolume;

  //Prints total ounces remaining to LCD
  dtostrf(liquidVolume,5,1,lg_str);
  sprintf(line1, "Keg Level: %s oz", lg_str);
  lcd.setCursor(0,2);
  lcd.print(line1);

  //Prints Beers Remaining to LCD
  dtostrf(poursRemaining,4,1,sm_str);
  sprintf(line2, "Pours Left: %s", sm_str);
  lcd.setCursor(0,3);
  lcd.print(line2);

  //Convert volume of liquid to the 60 pixles on the strip
  int pixels = map(liquidVolume, 0, fullVolume, 0, 59);
  pixels = constrain(pixels, 0,59);
  //calls function to light the strip and passes the mapped pixels
  LightShow(pixels); 
    
}

void LightShow(int pixels){
 //Clears all pixels
 for(int b=0; b<=60; b++){
  strip.setPixelColor(b,off);
  strip.show();
}
//Lights the first 20 pixels to red based on volume
if (pixels < 20){
  for(int i=0; i<=pixels; i++){
    strip.setPixelColor(i, colorOne);
  }
  strip.show();
}
//Lights first 20 to red and second 20 to blue
if (pixels >=20 && pixels <40){
  for(int i=0; i<20; i++){
    strip.setPixelColor(i, colorOne);
  }
  for(int j=20; j<=pixels; j++){
    strip.setPixelColor(j, colorTwo);
  }
  strip.show();
}
//Lights first 20 to red, second 20 to blue, third to white
if (pixels >=40){
  for(int i=0; i<20; i++){
    strip.setPixelColor(i, colorOne);
  }
  for(int j=20; j<=39; j++){
    strip.setPixelColor(j, colorTwo);
  }
  for(int k=40; k<=pixels; k++){
    strip.setPixelColor(k, colorThree);
  }
  strip.show();//writes the colors to the strip
} 
}

//ISR to Calibrate New Full Mass Upon Button Press
void CalibrationISR (void){
  if(switchON == true){
  //ISR only executes if Trigger is flipped
    //switchON == false;
    calibrationStart = true;
    notify = true;}
    }
 
 void LEDStateISR (void){
  switchON = true;
  digitalWrite(ISRSwitchLED, switchON);
  digitalWrite(ISRButtonLED, switchON); 
 }
char* choosePrint (void){
    int localPot = analogRead(dialPot);//read the calibration dial
      int colorSelect = map(localPot, 0, 900, 1,8); //convert reading to 1-8 for switch cases
      colorSelect = constrain(colorSelect, 1, 8);//restrict the reading to an acceptable range 
      char* colorPrint;
      switch(colorSelect){
        case 1:
          colorPrint = "red";
          break;
        case 2:
          //localOne = orange;
          colorPrint = "orange";
          break;
        case 3:
          //localOne = yellow;
          colorPrint = "yellow";
          break;
        case 4:
          //localOne = green;
          colorPrint = "green";
          break;
        case 5:
          //localOne = blue;
          colorPrint = "blue";
          break;
        case 6:
          //localOne = navyBlue;
          colorPrint = "navy";
          break;
        case 7:
          //localOne = purple;
          colorPrint = "purple";
          break;
        case 8:
          //localOne = white;
          colorPrint = "white";
          break;
      }
      return colorPrint;
 }
uint32_t chooseColor(String colorPrint){
  uint32_t color;
  if (colorPrint == "red"){
    color = red;
  }
  else if (colorPrint == "orange"){
    color = orange;
  }
  else if (colorPrint == "yellow"){
    color = yellow;
  }
  else if (colorPrint == "green"){
    color = green;
  }
  else if (colorPrint == "blue"){
    color = blue;
  }
  else if (colorPrint == "navy"){
    color = navyBlue;
  }
  else if (colorPrint == "purple"){
    color = purple;
  }
  else if (colorPrint == "white"){
    color = white;
  }
  return color;
}

void printColor (uint32_t color){
  for(int b=0; b<=60; b++){
    strip.setPixelColor(b,color);
    strip.show();
    } 
}

void pixelsOff (void){
  for(int b=0; b<=60; b++){
    strip.setPixelColor(b,off);
    strip.show();
    }
}

such vast code i could suggest you dont use serial communication. add an ethernet shield or wifi.
try removing all serial print lines from your code.

in your code if the button is not pressed, it will remain in continous infinite loop.

calibrationStart is actually a boolean flag variable that initiates that block of code once the interrupt service routine is initiated by a button press. Upon boot, that flag is
set to false and only changes if both a switch and button have been pressed by the enduser so that it is not unintentionally initiated.

Raspberry Pi Certified Educator

Pi-Top Certified Learning Designer

West Hollow Middle School

Half Hollow Hills CSD

Twitter: https://twitter.com/reginius214

Instagram: https://www.instagram.com/reginius214/

The code does not contain any print calls as I know that will interfere with the serial communication between the arduino and cayenne via the Pi.

Raspberry Pi Certified Educator

West Hollow Middle School

Half Hollow Hills CSD

Twitter and Instagram: @reginius214

Web: www.reginiusscience.com

i just made a test setup with your code (with no periperials) and only change i made was send random data to cayenne:

  if (millis() - lastMillis > 15000) {
    lastMillis = millis();
    x++;
    Cayenne.virtualWrite(volumeChannel, x);
    Cayenne.virtualWrite(poursChannel, x);
  }

if you say the while (calibrationStart == true){ is not executed by pressing the button, then it worked for me.

here is the entire code:

//include I2C LCD Libraries
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
//Create I2C Object w/address, columns, and rows
LiquidCrystal_I2C lcd(0x27, 20, 4);
int x;
//include HX711 Library
#include "HX711.h"

#include <CayenneMQTTSerial.h>

char username[] = "";
char password[] = "";
char clientID[] = "";

//Include Adafruit Neopixel Library and define Pin# for gauge
#include <Adafruit_NeoPixel.h>
#define PIN 6

//Declare Pins for the Mass ISR and a Mass Global
const byte ISRSwitch = 2;//Turns on LEDs and must be engaged for calibration ISR
const byte ISRButton = 3;//Will initiate calibration ISR if ISRSwitch == 0
const byte ISRButtonLED = 4;//Turns on with ISRSwitch
const byte ISRSwitchLED = 5;//Turns on with ISRSwitch

//Create State Flags for ISRs
volatile boolean calibrationStart = false;//changes with IRSButton
volatile boolean notify = false;//changes with ISRButton
volatile boolean switchON = false;//changes with ISRSwitch

//Declare buttons and dials for calibration sequence
const byte next = 7;
const byte setButton = 8;
const byte dialPot = A0;
//Declare state flags for calibration sequence
boolean setEmptyMass = false;
boolean setFullVolume = false;
boolean setFullMass = false;
boolean setColorOne = false;
boolean setColorTwo = false;
boolean setColorThree = false;
//Default values for calibration variables
float emptyMass = 16.0;//Default mass of empty container in lbs
float fullVolume = 660.0;//default volume of full container in fluid ounces
float pourVolume = 12.0;
float fullMass;//Global variable to be changed in Calibration

//Cayenne credentials


//Define Cayenne Channels
const byte volumeChannel = 0;//shows volume in keg
const byte poursChannel = 1;//shows how many pours remain

//Set HX711 Pins for the scale
#define DOUT 11
#define CLK 12
//Create HX711 Object
HX711 scale(DOUT, CLK);
//Set calibration factor for scale
#define calibration_factor  -10080
#define zero_factor 1052867

//Create buffers for variable readings and LCD string printouts
char line1 [21];
char lg_str [6];//handles liquidVolume
char line2 [21];//reports liquidVolume string
char sm_str [5];//handles poursRemaining

//Name of Current Beer on Tap and Brewery
char myBeer[] = "Saturn in Scorpio";
char myBrewery[] = "Foreign Objects";

//Create Adafruit Neopixel Object with colors
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
uint32_t red = strip.Color(255, 0, 0);
uint32_t orange = strip.Color(255, 140, 0);
uint32_t yellow = strip.Color(255, 255, 0);
uint32_t green = strip.Color(0, 128, 0);
uint32_t blue = strip.Color(0, 0, 255);
uint32_t navyBlue = strip.Color(0, 0, 128);
uint32_t purple = strip.Color(128, 0, 128);
uint32_t white = strip.Color(127, 127, 127);
uint32_t off = strip.Color(0, 0, 0);
uint32_t colorOne = red;
uint32_t colorTwo = blue;
uint32_t colorThree = white;

//Variables for Cayenne call
unsigned long lastMillis = 0;
float liquidVolume;
float poursRemaining;

void setup() {
  //Open Serial Communication
  Cayenne.begin(username, password, clientID, 9600);

  //Create the ISRs and pinModes for ISRSwitch and ISRButton
  digitalWrite(ISRSwitch, HIGH);
  digitalWrite(ISRButton, HIGH);
  pinMode(ISRSwitch, INPUT_PULLUP);
  pinMode(ISRButton, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(ISRSwitch), LEDStateISR, FALLING);
  attachInterrupt(digitalPinToInterrupt(ISRButton), CalibrationISR, FALLING);

  pinMode(ISRSwitchLED, OUTPUT);
  pinMode(ISRButtonLED, OUTPUT);

  //Declare pinModes for calibration buttons
  pinMode(setButton, INPUT_PULLUP);
  pinMode(next, INPUT_PULLUP);

  //Initiate Neopixel Object and clear the pixels
  strip.begin();
  strip.show();

  //Initialize your LCD object as being a 16 column, 2 row LCD
  lcd.begin();

  for (int i = 0; i < 3; i++) //flash the LCD 3 times to test upon Power On
  {
    lcd.backlight();
    delay(250);
    lcd.noBacklight();
    delay(250);
  }
  lcd.backlight(); //set backlight as ON after 3 flashes

  //Introduction prints name of beer on LCD row 1 and Brewery to row 2
  lcd.setCursor(0, 0);
  lcd.print(myBeer);
  lcd.setCursor(0, 1);
  lcd.print(myBrewery);

  //Turn on the Scale
  scale.set_scale(calibration_factor);
  scale.set_offset(zero_factor);

}

void loop() {
  Cayenne.loop();

  //Begins calibration sequence if ISR has been run
  while (calibrationStart == true) {
    while (notify == true) {
      notify = false;//reset notify flag
      setEmptyMass = true;//raise emptyMass flag
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Entering Calibration");
      lcd.setCursor(0, 1);
      lcd.print("Lower Red Toggle");
      lcd.setCursor(0, 2);
      lcd.print("Directions to Follow");
      delay(5000);
      lcd.clear();
    }
    while (setEmptyMass == true) {
      //lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Put empty container");
      lcd.setCursor(0, 1);
      lcd.print("on the scale and...");
      lcd.setCursor(0, 2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0, 3);
      lcd.print("To SKIP Press Green");
      if (digitalRead(setButton) == 0) {
        setEmptyMass = false;
        setFullVolume = true;
        emptyMass = scale.get_units();
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Empty Mass is Set!");
        dtostrf(emptyMass, 4, 1, sm_str);
        sprintf(line1, "Empty Mass: %s lbs", sm_str);
        lcd.setCursor(0, 1);
        lcd.print(line1);
        delay(3000);
        lcd.clear();
      }
      if (digitalRead(next) == 0) {
        setEmptyMass = false;
        setFullVolume = true;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Empty Mass is Set!");
        dtostrf(emptyMass, 4, 1, sm_str);
        sprintf(line1, "Empty Mass: %s lbs", sm_str);
        lcd.setCursor(0, 1);
        lcd.print(line1);
        delay(3000);
        lcd.clear();
      }
    }
    while (setFullVolume == true) {
      //lcd.clear();
      float localPot = analogRead(dialPot);//read the volume calibration dial
      float localVolume = map(localPot, 0, 1023, 320, 1280); //convert reading to ounces between 2.5 and 10 gallons
      localVolume = constrain(localVolume, 320, 1280);//restrict the reading to an acceptable range
      lcd.setCursor(0, 0);
      lcd.print("Adjust Vol with Dial");
      dtostrf(localVolume, 4, 0, sm_str);
      sprintf(line1, "Total Vol: %s oz", sm_str);
      lcd.setCursor(0, 1);
      lcd.print(line1);
      lcd.setCursor(0, 2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0, 3);
      lcd.print("To SKIP Press Green");
      if (digitalRead(setButton) == 0) {
        setFullVolume = false;
        setColorOne = true;
        fullVolume = localVolume;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Full Volume is Set!");
        dtostrf(fullVolume, 4, 0, sm_str);
        sprintf(line1, "Total Vol: %s oz", sm_str);
        lcd.setCursor(0, 1);
        lcd.print(line1);
        delay(3000);
        lcd.clear();
      }
      if (digitalRead(next) == 0) {
        setFullVolume = false;
        setColorOne = true;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Full Volume is Set!");
        dtostrf(fullVolume, 4, 0, sm_str);
        sprintf(line1, "Total Vol: %s oz", sm_str);
        lcd.setCursor(0, 1);
        lcd.print(line1);
        delay(3000);
        lcd.clear();
      }
    }

    while (setColorOne == true) {
      char colorPrintOne[] = "red";
      char* color = choosePrint();
      uint32_t localColor = chooseColor(color);
      printColor(localColor);

      lcd.setCursor(0, 0);
      lcd.print("Adjust Color w/ Dial");
      sprintf(line1, "1st Color: %s   ", color);
      lcd.setCursor(0, 1);
      lcd.print(line1);
      lcd.setCursor(0, 2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0, 3);
      lcd.print("To SKIP Press Green");
      if (digitalRead(setButton) == 0) {
        setColorOne = false;
        setColorTwo = true;
        colorOne = localColor;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Color One is Set!");
        sprintf(line1, "1st Color: %s   ", color);
        lcd.setCursor(0, 1);
        lcd.print(line1);
        printColor(colorOne);
        delay(3000);
        pixelsOff();
        lcd.clear();
      }
      if (digitalRead(next) == 0) {
        setColorOne = false;
        setColorTwo = true;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Color One is Set!");
        sprintf(line1, "1st Color: %s   ", colorPrintOne);
        lcd.setCursor(0, 1);
        lcd.print(line1);
        printColor(colorOne);
        delay(3000);
        pixelsOff();
        lcd.clear();
      }
    }
    while (setColorTwo == true) {
      char colorPrintTwo[] = "blue";
      char* color = choosePrint();
      uint32_t localColor = chooseColor(color);
      printColor(localColor);

      lcd.setCursor(0, 0);
      lcd.print("Adjust Color w/ Dial");
      sprintf(line1, "2nd Color: %s   ", color);
      lcd.setCursor(0, 1);
      lcd.print(line1);
      lcd.setCursor(0, 2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0, 3);
      lcd.print("To SKIP Press Green");
      if (digitalRead(setButton) == 0) {
        setColorTwo = false;
        setColorThree = true;
        colorTwo = localColor;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Color Two is Set!");
        sprintf(line1, "2nd Color: %s   ", color);
        lcd.setCursor(0, 1);
        lcd.print(line1);
        printColor(colorTwo);
        delay(3000);
        pixelsOff();
        lcd.clear();
      }
      if (digitalRead(next) == 0) {
        setColorTwo = false;
        setColorThree = true;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Color Two is Set!");
        sprintf(line1, "2nd Color: %s   ", colorPrintTwo);
        lcd.setCursor(0, 1);
        lcd.print(line1);
        printColor(colorTwo);
        delay(3000);
        pixelsOff();
        lcd.clear();
      }
    }
    while (setColorThree == true) {
      char colorPrintThree[] = "white";
      char* color = choosePrint();
      uint32_t localColor = chooseColor(color);
      printColor(localColor);

      lcd.setCursor(0, 0);
      lcd.print("Adjust Color w/ Dial");
      sprintf(line1, "3rd Color: %s   ", color);
      lcd.setCursor(0, 1);
      lcd.print(line1);
      lcd.setCursor(0, 2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0, 3);
      lcd.print("To SKIP Press Green");
      if (digitalRead(setButton) == 0) {
        setColorThree = false;
        setFullMass = true;
        colorThree = localColor;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Color Three is Set!");
        sprintf(line1, "3rd Color: %s   ", color);
        lcd.setCursor(0, 1);
        lcd.print(line1);
        printColor(colorThree);
        delay(3000);
        pixelsOff();
        lcd.clear();
      }
      if (digitalRead(next) == 0) {
        setColorThree = false;
        setFullMass = true;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Color Three is Set!");
        sprintf(line1, "3rd Color: %s   ", colorPrintThree);
        lcd.setCursor(0, 1);
        lcd.print(line1);
        printColor(colorThree);
        delay(3000);
        pixelsOff();
        lcd.clear();
      }
    }
    while (setFullMass == true) {
      //lcd.clear();
      fullMass = scale.get_units();
      lcd.setCursor(0, 0);
      lcd.print("Set Total Mass");
      dtostrf(fullMass, 5, 1, lg_str);
      sprintf(line1, "Full Mass: %s lbs", lg_str);
      lcd.setCursor(0, 1);
      lcd.print(line1);
      lcd.setCursor(0, 2);
      lcd.print("To SET Press Blue");
      lcd.setCursor(0, 3);
      lcd.print("DO NOT Press Green");
      if (digitalRead(setButton) == 0) {
        setFullMass = false;
        calibrationStart = false;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Full Mass is Set!");
        lcd.setCursor(0, 1);
        lcd.print(line1);
        delay(3000);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Calibration Over!");
        lcd.setCursor(0, 1);
        lcd.print("Tasty IPAs Await.");
        lcd.setCursor(0, 2);
        lcd.print("Enjoy your beer...");
        lcd.setCursor(0, 3);
        lcd.print("LET'S GO RANGERS!");
        delay(3000);
        switchON = false;
        digitalWrite(ISRButtonLED, switchON);
        digitalWrite(ISRSwitchLED, switchON);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print(myBeer);
        lcd.setCursor(0, 1);
        lcd.print(myBrewery);
      }
    }
  }
  kegTester();
  if (millis() - lastMillis > 15000) {
    lastMillis = millis();
    x++;
    Cayenne.virtualWrite(volumeChannel, x);
    Cayenne.virtualWrite(poursChannel, x);
  }
}

void kegTester (void)
{
  //Grabs current mass of container
  float currentMass = scale.get_units();
  float liquidMassLbs = currentMass - emptyMass;//weight in lbs of beer only
  float liquidMassOunces = liquidMassLbs * 16.0; // weight of beer in ounces
  noInterrupts();//Block ISR
  float fullMassLocal = fullMass;
  float fullLiquidMassOunces = ((fullMassLocal - emptyMass) * 16.0);
  interrupts();//reenable ISR
  //convert the mass of liquid into fluid ounces
  liquidVolume = map (liquidMassOunces, 0, fullLiquidMassOunces, 0, fullVolume);

  liquidVolume = constrain(liquidVolume, 0, fullVolume);//keeps data in acceptable range
  //calculate the # of pours in container based on a tulip glass
  poursRemaining = liquidVolume / pourVolume;

  //Prints total ounces remaining to LCD
  dtostrf(liquidVolume, 5, 1, lg_str);
  sprintf(line1, "Keg Level: %s oz", lg_str);
  lcd.setCursor(0, 2);
  lcd.print(line1);

  //Prints Beers Remaining to LCD
  dtostrf(poursRemaining, 4, 1, sm_str);
  sprintf(line2, "Pours Left: %s", sm_str);
  lcd.setCursor(0, 3);
  lcd.print(line2);

  //Convert volume of liquid to the 60 pixles on the strip
  int pixels = map(liquidVolume, 0, fullVolume, 0, 59);
  pixels = constrain(pixels, 0, 59);
  //calls function to light the strip and passes the mapped pixels
  LightShow(pixels);

}

void LightShow(int pixels) {
  //Clears all pixels
  for (int b = 0; b <= 60; b++) {
    strip.setPixelColor(b, off);
    strip.show();
  }
  //Lights the first 20 pixels to red based on volume
  if (pixels < 20) {
    for (int i = 0; i <= pixels; i++) {
      strip.setPixelColor(i, colorOne);
    }
    strip.show();
  }
  //Lights first 20 to red and second 20 to blue
  if (pixels >= 20 && pixels < 40) {
    for (int i = 0; i < 20; i++) {
      strip.setPixelColor(i, colorOne);
    }
    for (int j = 20; j <= pixels; j++) {
      strip.setPixelColor(j, colorTwo);
    }
    strip.show();
  }
  //Lights first 20 to red, second 20 to blue, third to white
  if (pixels >= 40) {
    for (int i = 0; i < 20; i++) {
      strip.setPixelColor(i, colorOne);
    }
    for (int j = 20; j <= 39; j++) {
      strip.setPixelColor(j, colorTwo);
    }
    for (int k = 40; k <= pixels; k++) {
      strip.setPixelColor(k, colorThree);
    }
    strip.show();//writes the colors to the strip
  }
}

//ISR to Calibrate New Full Mass Upon Button Press
void CalibrationISR (void) {
  if (switchON == true) {
    //ISR only executes if Trigger is flipped
    //switchON == false;
    calibrationStart = true;
    notify = true;
  }
}

void LEDStateISR (void) {
  switchON = true;
  digitalWrite(ISRSwitchLED, switchON);
  digitalWrite(ISRButtonLED, switchON);
}
char* choosePrint (void) {
  int localPot = analogRead(dialPot);//read the calibration dial
  int colorSelect = map(localPot, 0, 900, 1, 8); //convert reading to 1-8 for switch cases
  colorSelect = constrain(colorSelect, 1, 8);//restrict the reading to an acceptable range
  char* colorPrint;
  switch (colorSelect) {
    case 1:
      colorPrint = "red";
      break;
    case 2:
      //localOne = orange;
      colorPrint = "orange";
      break;
    case 3:
      //localOne = yellow;
      colorPrint = "yellow";
      break;
    case 4:
      //localOne = green;
      colorPrint = "green";
      break;
    case 5:
      //localOne = blue;
      colorPrint = "blue";
      break;
    case 6:
      //localOne = navyBlue;
      colorPrint = "navy";
      break;
    case 7:
      //localOne = purple;
      colorPrint = "purple";
      break;
    case 8:
      //localOne = white;
      colorPrint = "white";
      break;
  }
  return colorPrint;
}
uint32_t chooseColor(String colorPrint) {
  uint32_t color;
  if (colorPrint == "red") {
    color = red;
  }
  else if (colorPrint == "orange") {
    color = orange;
  }
  else if (colorPrint == "yellow") {
    color = yellow;
  }
  else if (colorPrint == "green") {
    color = green;
  }
  else if (colorPrint == "blue") {
    color = blue;
  }
  else if (colorPrint == "navy") {
    color = navyBlue;
  }
  else if (colorPrint == "purple") {
    color = purple;
  }
  else if (colorPrint == "white") {
    color = white;
  }
  return color;
}

void printColor (uint32_t color) {
  for (int b = 0; b <= 60; b++) {
    strip.setPixelColor(b, color);
    strip.show();
  }
}

void pixelsOff (void) {
  for (int b = 0; b <= 60; b++) {
    strip.setPixelColor(b, off);
    strip.show();
  }
}

i used arduino mega as the sketch was quite big for uno

I wonder if there is something blocking in the kegTester() function from which the virtualWrite functions are gathering their data. In your example x was being set in void loop()
from within your timing construct while in my code liquidVolume and poursRemaining are being set in the kegTester function. Is it possible that these global variables are not being handed back to the virtualWrite call from the external function?

Raspberry Pi Certified Educator

Pi-Top Certified Learning Designer

West Hollow Middle School

Half Hollow Hills CSD

Website: https://www.reginiusscience.com

Twitter: https://twitter.com/reginius214

Instagram: https://www.instagram.com/reginius214/

when you upload your code on your arduino uno, do you get any random data in your serial monitor.

Ok so here is something I find very odd. When I run the original code w/o the cayenne components, the arduino resets when I open the serial monitor. There is no data being printed
to the serial monitor, but upon that reset, it is entering that first infinite while loop that is initiated by the ISR. I assume it is having that same experience when the Pi attempts to read the Arduino when the cayenne-ser.sh script runs. Any thoughts
as to why this behavior is happening?

Raspberry Pi Certified Educator

Pi-Top Certified Learning Designer

West Hollow Middle School

Half Hollow Hills CSD

Website: https://www.reginiusscience.com

Twitter: https://twitter.com/reginius214

Instagram: https://www.instagram.com/reginius214/

this is normal https://www.eevblog.com/forum/microcontrollers/arduino-serial-monitor-reset/ and if you want to fix it https://playground.arduino.cc/Main/DisablingAutoResetOnSerialConnection/

So far I have been able to get data across to Cayenne when eliminating the following elements from the code: 1) the attachInterrupts in the void setup AND 2) everything related
to printing to the LCD. The kegtester function works fine and serves its data to the virtualWrite calls. Is there something in the LiquidCrystal_I2C library that is blocking the Cayenne call?

Raspberry Pi Certified Educator

Pi-Top Certified Learning Designer

West Hollow Middle School

Half Hollow Hills CSD

Website: https://www.reginiusscience.com

Twitter: https://twitter.com/reginius214

Instagram: https://www.instagram.com/reginius214/

I too came across the reset article, but disabling it also disables the ability for Cayenne to connect via serial.

Raspberry Pi Certified Educator

Pi-Top Certified Learning Designer

West Hollow Middle School

Half Hollow Hills CSD

Website: https://www.reginiusscience.com

Twitter: https://twitter.com/reginius214

Instagram: https://www.instagram.com/reginius214/

there were some users that had complaint about the same.

Was there a resolution to this? Being able to see data on the system via the LCD is a critical design component. Perhaps is there another library that users have used? I have been one of the users that have reported this in the
past, but switching the library I was using seemed to help. I am now using that library but am encountering the blockage again.

Raspberry Pi Certified Educator

Pi-Top Certified Learning Designer

West Hollow Middle School

Half Hollow Hills CSD

Twitter and Instagram: @reginius214

Web: www.reginiusscience.com

any specific reason for using arduino with serial connection with raspberry pi?

The network protocol (EAP) used by our building does not allow me to make use of an Ethernet or WiFi shield because I cannot enter the appropriate credentials. Same problem with an ESP8266. I can however modify the wpa_supplicant
file on the Pi so that is my access point.

I’ve tried to maintain the arduino as the micro controller because it has a more robust set of libraries plus the built in ADC for various sensors.

I’ve thought about passing the data via serial into a python script and then connecting to Cayenne with just the Pi. I’m just not totally versed in how to do that just yet.

Raspberry Pi Certified Educator

Pi-Top Certified Learning Designer

West Hollow Middle School

Half Hollow Hills CSD

Twitter and Instagram: @reginius214

Web: www.reginiusscience.com

do you have an arduino mega with you?

Yes

Raspberry Pi Certified Educator

Pi-Top Certified Learning Designer

West Hollow Middle School

Half Hollow Hills CSD

Twitter and Instagram: @reginius214

Web: www.reginiusscience.com