How do I get controlP5 generated knob in Processing to change values in Arduino

Background: I am working on a electric brewing project using an Arduino Uno R3, two DS18b20 digital temp sensors and Processing IDE as a GUI. I am using the sensors to monitor and control two separate heating elements on SSRs for various steps in beer brewing. I have successfully used the following Arduino and Processing code to send serial data to Processing to generate a dumb temp output GUI showing two separate temps (ACTUAL1,2), relay set points (SETPOINT1,2) and relay states (HEATPIN1,2).

Problem: The control knob generated with the controlP5 library is in the Processing sketch as you will see below but I am asking for help getting it to communicate back to Arduino. I may be going about this all wrong dataflow wise: Arduino to serial to Processing...back to serial? back to Arduino? I tried to scrap most of the Arduino code and use Firmata to Arduino alone but I still need to code in the Dallas sensors into Arduino. Any suggestions would be greatly appreciated, very new to coding. I have the brewing process, hardware and wiring down but lack experience with code. Thank you in advance.

Arduino Code:

//#include <Boards.h>
//#include <Firmata.h>
 
#include <OneWire.h>
#include <DallasTemperature.h>
int ACTUAL1;
int ACTUAL2;
int SETPOINT1 = 145;
int SETPOINT2 = 150;
int HEATPIN1 = 13;
int HEATPIN2 = 12;
#define ONE_WIRE_BUS 3
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress Probe1 = { 0x28, 0xFF, 0x14, 0x23, 0x16, 0x15, 0x03, 0xB9 };
DeviceAddress Probe2 = { 0x28, 0xFF, 0xBA, 0x0D, 0x16, 0x15, 0x03, 0x4D };
 
void setup(void)
{
  pinMode(HEATPIN1, OUTPUT);
  pinMode(HEATPIN2, OUTPUT);
  Serial.begin(9600);
  sensors.begin();
  sensors.setResolution(Probe1, 10);
  sensors.setResolution(Probe2, 10);
}
 
void printTemperature(DeviceAddress deviceAddress)
{
  float TempC = sensors.getTempC(deviceAddress);
  if (TempC == -127.00) {
    Serial.print("Error getting temperature");
  } else {
    ACTUAL1 = (DallasTemperature::toFahrenheit(TempC));
    ACTUAL2 = (DallasTemperature::toFahrenheit(TempC));
    Serial.print(ACTUAL1);
    //Serial.print(ACTUAL2);
  }
}
 
void loop(void)
{
  delay(2000);
  sensors.requestTemperatures();
 
  //Serial.read();
  //SETPOINT1 = Serial.parseInt();
 
  printTemperature(Probe1);
  if (ACTUAL1 < 100) {
    Serial.print(" ");
  }
  Serial.print(",");
  Serial.print(SETPOINT1);
  if (SETPOINT1 < 100) {
    Serial.print(" ");
  }
  if (ACTUAL1 >= SETPOINT1)
  {
    Serial.print ("OFF");
    digitalWrite(HEATPIN1, LOW);
  }
  else
  {
    Serial.print ("ON ");
    digitalWrite(HEATPIN1, HIGH);
  }
 
  printTemperature(Probe2);
  if (ACTUAL2 < 100) {
    Serial.print(" ");
  }
  Serial.print(SETPOINT2);
  if (SETPOINT2 < 100) {
    Serial.print(" ");
  }
  if (ACTUAL2 >= SETPOINT2)
  {
    Serial.print ("OFF.");
    digitalWrite(HEATPIN2, LOW);
  }
  else
  {
    Serial.print ("ON .");
    digitalWrite(HEATPIN2, HIGH);
  }
}

Processing Code:

import controlP5.*;
ControlP5 cp5;
Knob myKnobA;
Knob myKnobB;
 
import processing.serial.*;
Serial port;
String ACTUAL1 = "";
String ACTUAL2 = "";
String SETPOINT1 = "";
String SETPOINT2 = "";
String HEATPIN1 = "";
String HEATPIN2 = "";
String data = "";
int index = 0;
PFont font;
 
 
void setup()
{
 
  smooth();
  noStroke();
  cp5 = new ControlP5(this);
  myKnobA = cp5.addKnob("SETPOINT 1")
    .setRange(50, 215)
      .setValue(50)
        .setPosition(50, 350)
          .setRadius(50)
            .setNumberOfTickMarks(33)
              .setTickMarkLength(10)
                .snapToTickMarks(true)
                  .setColorForeground(color(255))
                    .setColorBackground(color(0, 160, 100))
                      .setColorActive(color(255, 255, 0))
                        .setDragDirection(Knob.VERTICAL)
                          ;
  myKnobB = cp5.addKnob("SETPOINT 2")
    .setRange(50, 215)
      .setValue(50)
        .setPosition(200, 350)
          .setRadius(50)
            .setNumberOfTickMarks(33)
              .setTickMarkLength(10)
                .snapToTickMarks(true)
                  .setColorForeground(color(255))
                    .setColorBackground(color(0, 160, 100))
                      .setColorActive(color(255, 255, 0))
                        .setDragDirection(Knob.VERTICAL)
                          ;
 
  size(360, 500);
  port = new Serial(this, "COM4", 9600);
  port.bufferUntil('.'); 
  font = loadFont("AgencyFB-Bold-200.vlw");
  textFont(font, 100);
}
 
void draw()
{
 
 
  background(0, 0, 0);
  fill(46, 209, 2);
  text(ACTUAL1, 50, 100);
  fill(0, 102, 153);
  text(SETPOINT1, 50, 200);
  fill(50, 2, 100);
  text(HEATPIN1, 50, 300);
 
  fill(46, 209, 2);
  text(ACTUAL2, 200, 100);
  fill(0, 102, 153);
  text(SETPOINT2, 200, 200);
  fill(50, 2, 100);
  text(HEATPIN2, 200, 300);
 
  float S1;
  S1 = (myKnobA.getValue());
  println(S1);
}
 
void keyPressed() {
  switch(key) {
    case('1'):
    myKnobA.setValue(50);
    break;
    case('2'):
    myKnobA.setValue(215);
    break;
    case('3'):
    myKnobB.setValue(50);
    break;
    case('4'):
    myKnobB.setValue(215);
    break;
  }
}
 
void serialEvent (Serial port)
{
  data = port.readStringUntil('.');
  data = data.substring(0, data.length() - 1);
  index = data.indexOf(",");
 
  ACTUAL1 = data.substring(0, data.length() - 16);
  SETPOINT1 = data.substring(index+1, data.length() - 12);
  HEATPIN1 = data.substring(index+4, data.length() - 9);
 
  ACTUAL2 = data.substring(index+7, data.length() - 6);
  SETPOINT2 = data.substring(index+10, data.length() - 3);
  HEATPIN2 = data.substring(index+13, data.length());
}
  float S1;
  S1 = (myKnobA.getValue());
  println(S1);

(How) (is) (the) (Arduino) (supposed) (to) (know) (that) (you) (are) (doing) (that)?

My thinking is that I want to be able to change the value for the variable I declared initially in Arduino sketch : "int SETPOINT1 = 145;" this setpoint is initialized in this example at 145 degrees F but I want to be able to have this variable changed by the ControlP5 knob.

can I have Processing write to serial as the knob changes and have Arduino read these changes and change the variable SETPOINT1 accordingly?

can I have Processing write to serial as the knob changes and have Arduino read these changes and change the variable SETPOINT1 accordingly?

Of course you can.

PaulS - I am still struggling with this, can you please provide additional help? You say I can send data back and forth via serial but you don't provide any additional comments or suggestions. I've already used serial data successfully here using same code above from Arduino to Processing as write-string/read-substrings. So am I thinking correctly to use a port.write() command to send integer back to Arduino via serial?...

Processing Code:

  float SET;
  SET = (myKnobA.getValue());
  println(SET);
  port.write(SET);

Ardino Code:

  Serial.read();
  SETPOINT1 = Serial.parseInt();

Arduino appears to reading from serial ok but my Procession syntax is incorrect and I get an error " The method write(byte[]) in the type Serial is not applicable for the arguments (float)"

What is the appropriate Processing syntax to write the getValue to serial? Thanks!

Write only sends bytes to the serial port so you have two options

  1. get your value from the knob into an int and split it in two and send each byte separately.
  2. do a serial print of the float to the port.

On the Arduino side note that serial read only receives one byte.

The way you designed your loop hinders you in using any kind of communication to the arduino.

You start with a delay(2000); so for 2 seconds your Arduino will respond to nothing.
Then you send a couple of bytes to Processing.
The Serial buffers will probably filled, but your code does not care.
Most of the time it is busy anyway, waiting for the next time to send.

You want a state report each 2 seconds. (IMHO)

Make your a stateReport routine and have it called each two seconds.
Have a look at BlinkWithoutDelay for a blueprint.

This way around you have your Arduino free for PID-heater control,
or whatever may be suitable in you setup.

For handling of serial input on the Arduino side there are many examples available.

Using '.' as a message delimiter makes it hard for you to interpret any captured data.
Sticking to '\n' as a delimiter eliminates that, since editors use it too.

For the parsing you do on the Processing, I would suggest using something like

String[] fields = splitTokens(inMessage, ",");

and using a ',' between all values.

Wow ok, that's all great comments thank you much Whandall and Grumpy_Mike, but my head is spinning more than ever now. I really want to be able to get through this myself but admit again that I'm new to coding. I partially understand your points about how I can use BlinkWithoutDelay example to allow switching, serial reading and serial writing code to run at the same time without being interrupted.

On your suggestion ill also try converting my delimiter of "." to /n but will also have to rewrite the Procession read accordingly.

I keep thinking there is an easier way to accomplish all of this like using Processing to determine logic and switch my digital pins? Can Processing handle the dallas digital sensor logic?

I think that leaving the management of sensors (actors, ...) on the Arduino side is better.

Fiddling bits and using low level protocols is what the Arduino does best.
Parsing, visualizing, logfiles, databases, internet, sms, ...
that's better left to the pc side, where the limitations are much less tight.

I would design a simple protocol based on human readable text,
so you can test it with any serial program. For instance:

"R,...." for reports, "A,...." for alarms, "C,...." for commands, "E,...." for errors, ...

Easy to parse, because the first field implies the content of the rest of the line.

Reports, alarms, acknowledgments, ... going to the pc.
Commands going the other way.

Naturally you can use any other delimiter as long as it will not be contained in the data.

I keep thinking there is an easier way to accomplish all of this

No.
That you are being told is not complex. It only appears complex because of your lack of experience.

I scrapped the Processing GUI and decided to go another route. I bought a Arduino Yun and am now using the iPhone Blynk app for remote communications with my iPhone. I can now interact back and forth between the Blynk apps GUI and the Arduino Yun. What made this very easy was making virtual pin assignments. Arduino Yun writes temp etc to virtual pins that Blynk pics up on. In reverse, Blynk writes set points to virtual pins that Arduino pics up on. I can now interact fully with the Yun by phone alone, no PC needed for anything but sketch upload and console (serial monitor) starting.

Here is the new Yun code if you are interested...still using two (2) DS18B20 digital sensors, currently testing my beer/cider/wine fermentation chamber and draft fridge and reporting two separate temps, one on the jacket of my fermenter and one in the air space of the air conditioner controlled chamber

#include <Console.h>
#include <OneWire.h>
#include <DallasTemperature.h>
float ACTUAL1;
float ACTUAL2;
int SETPOINT1 = 60;
int SLEW1 = 10;
int COOLPIN1 = 13;
#define ONE_WIRE_BUS 3
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress Probe1 = { 0x28, 0xFF, 0x99, 0x4F, 0x16, 0x15, 0x03, 0x1C };
DeviceAddress Probe2 = { 0x28, 0xFF, 0x03, 0x50, 0x16, 0x15, 0x03, 0xB4 };
#include <BlynkSimpleYun.h>;
char auth[] = "57f2e98c3f4f4368b37f70affabc7c9d";

void setup()
{
  pinMode(COOLPIN1, OUTPUT);
  sensors.begin();
  sensors.setResolution(Probe1, 10);
  Bridge.begin();
  Console.begin();
  while (!Console);
  Console.println("Setup Complete");
  Blynk.begin(auth);
}

void printTemperature(DeviceAddress deviceAddress)
{
  float TempC = sensors.getTempC(deviceAddress);
  if (TempC == -127.00) {
    Console.print("ERR");
  } else {
    ACTUAL1 = (DallasTemperature::toFahrenheit(TempC));
    ACTUAL2 = (DallasTemperature::toFahrenheit(TempC));
    Console.print(ACTUAL1);
    Console.print(ACTUAL2);
  }
}

BLYNK_WRITE(V1) {
  SETPOINT1 = param.asInt();
  Blynk.virtualWrite(V5, SETPOINT1);
  Blynk.virtualWrite(V6, SETPOINT1 - SLEW1);
}

BLYNK_WRITE(V2) {
  SLEW1 = param.asInt();
  Blynk.virtualWrite(V5, SETPOINT1);
  Blynk.virtualWrite(V6, SETPOINT1 - SLEW1);
}

void loop()
{
  Blynk.run();
  Console.println();
  sensors.requestTemperatures();
  printTemperature(Probe1);
  Blynk.virtualWrite(V3, ACTUAL1);
  //Blynk.virtualWrite(V4, ACTUAL1);
  Console.print(SETPOINT1);
  Console.print(SETPOINT1 - SLEW1);
  if (ACTUAL1 > SETPOINT1)
  {
    digitalWrite(COOLPIN1, HIGH);
    Blynk.virtualWrite(V7, HIGH);
    Console.print("ON");
  }
  if (ACTUAL1 < SETPOINT1 - SLEW1)
  {
    digitalWrite(COOLPIN1, LOW);
    Blynk.virtualWrite(V7, LOW);
    Console.print("OFF");
  }
  printTemperature(Probe2);
  Blynk.virtualWrite(V13, ACTUAL2);
  //Blynk.virtualWrite(V14, ACTUAL2);
}