Replacing the electronics in a hot tub: Arduino to control pumps and the heater
This is the third post in the series “Replacing the electronics in a hot tub”. The first post is here. This one is about the first version of the Arduino micro-controllers programming.
I decided to separate the Sensors and Actuators circuits on two Arduinos for performance control. One Arduino will read sensors and manage displays. Some sensors “block” the processor while taking a reading, which might affect the response time. The other Arduino will read buttons and switches, and activate relays or other subsystems. I use Arduino Mega because it has more pins available and more memory to store the code. I will optimise if necessary later.
Actuators
The “actuators” code runs on an Arduino. The “sensors” Arduino is quite busy and commands sent to the Actuators Arduino need to be processed quickly. Also, the Arduino is handling the buttons/keypad that will be used to interface with the Main Controller. Buttons, and switches in general, have to be “debounced” to provide a clean on/off state, while not tying the processor with a ‘delay()’ instruction.
Lets look at a bit of code
This first piece will debounce a button/switch. As soon as the controller reads a positive contact, it registers a ‘buttonPressed’ state at the next CPU cycle. If the button is released, it won’t do anything because the ‘buttonDebounce’ interval has not been completed. This is quite crude, and I used a ‘buttonDebounce’ of 1/2 a second while testing. It works well and doesn’t tie the processor. The only drawback is that the pump might turn on and off if the user keeps the button “pressed” for more than 1/2 a second. (think inebriated user…)
if (digitalRead(button1Pin)) {
if (millis() - lastButton1Push >= buttonDebounce) {
lastButton1Push = millis();
button1Pressed = true;
}
}

Sending data
The next function sends a string that is formatted as a JSON string. In this particular case, the output would be: {“controller”: “buttonPushed”, “button1”: 0 or 1, “button2”: 0 or 1}. I found that using JSON (see below) to exchange data between modules made everything more flexible and standard. In this case, the string is sent to the Raspberry Pi through the USB port. Using ‘Serial.print()’ is slow, but the RASPI is fast enough to register the button press and take the necessary actions.
if (button1Pressed || button2Pressed) {
Serial.print("{\"controller\": \"buttonPushed\",");
Serial.print(" \"button1\": ");
Serial.print(button1Pressed);
Serial.print(", \"button2\": ");
Serial.print(button2Pressed);
Serial.println("}");
button1Pressed = false;
button2Pressed = false;
}
Identify
In this next piece of code, a command is sent from the RASPI to the Arduino to ask what its “identity” is. I keep the commands (and other messages) sent to the Arduino as short as possible. I don’t want the input buffer to ever overflow with incoming data. The final version of the code might use abbreviated commands, but I will test that later.
When testing using the linux computer, I found out that the two Arduino would exchange USB ports randomly. So I decided to include a function that asks the Arduino to identify itself.
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
if (command == "identify") {
Serial.flush();
Serial.println("{\"identity\": \"actuators\"}");
Receiving commands
The next code block is the complete input processor function for incoming commands
// Read and process commands from Raspberry Pi
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
if (command == "identify") {
Serial.flush();
Serial.println("{\"identity\": \"actuators\"}");
//Serial.println("}");
//Serial.print("identity");
} else if (command == "heater1ON") {
digitalWrite(heaterPin, LOW);
heater1On = true;
} else if (command == "heater1OFF") {
digitalWrite(heaterPin, HIGH);
heater1On = false;
} else if (command == "pump1ON") {
digitalWrite(pump1Pin, LOW);
pump1On = true;
} else if (command == "pump1OFF") {
digitalWrite(pump1Pin, HIGH);
pump1On = false;
} else if (command == "pump2ON") {
digitalWrite(pump2Pin, LOW);
pump2On = true;
} else if (command == "pump2OFF") {
digitalWrite(pump2Pin, HIGH);
pump2On = false;
}
}

The top part is the ‘identify’ code seen above. Following it is a series of simple actions depending on what the main processor, the RASPI, is sending. It’s just setting different pins on and off (HIGH and LOW) to control the various relays and the heater. This will be optimized in a later version, as it is quite slow, in Arduino speed, and might make the code harder to read when the five pumps and the heater are there.
Keypad
I decided to test a keypad for direct data entry, like inputing temperature directly instead of repeatedly pressing a button to go up and down. I used an available library.

#include <Keypad.h>
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the symbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
byte rowPins[ROWS] = { 14, 15, 16, 17 }; //connect to the row pinouts of the keypad
byte colPins[COLS] = { 18, 19, 20, 21 }; //connect to the column pinouts of the keypad
//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
//................many more lines of code.......
void loop() {
char customKey = customKeypad.getKey();
if (customKey) {
Serial.print("{\"controller\": \"keypad\","); //JSON again
Serial.print(" \"keyPressed\": \"");
Serial.print(customKey);
Serial.println("\"}");
}
Again, the output is a JSON structure that passes the pressed key (variable ‘keyPressed’ to the main computer. {“controller”: “keypad”, “keyPressed”: customKey}.
Periodic data sending
The last piece of code sends a series of status for the relays (pumps and heater) controlled by this Arduino. The StatusInterval is 10 seconds for the tests. The output is JSON formatted, of course.
if (millis() - lastDataSent >= statusInterval) {
lastDataSent = millis();
Serial.print("{\"controller\": \"actuators/status\",");
//sprintf(jsonBuffer, "\"heater\": heaterOn, \"pump\": pumpOn");
//Serial.print(jsonBuffer);
Serial.print(" \"heater1\": ");
Serial.print(heater1On);
Serial.print(", \"pump1\": ");
Serial.print(pump1On);
Serial.print(", \"pump2\": ");
Serial.print(pump2On);
Serial.println("}");
}
This code will probably be transferred to a function. A call would be automatic every ‘statusInterval’ time, but also a call when something is activated, as an acknowledgment.
About using JSON
I chose to use JSON formatting in most of the exchanges between the processors/controllers because I find that the format lets me write easier to debug code. In particular, the Arduinos send JSON formatted strings because they process many different types of output and the RASPI if fast enough to handle everything in a timely manner. That Raspberry Pi, as you will see later, receives and sends data from many sources and JSON makes it efficient.
Also, JSON uses text strings to exchange data. While text manipulation is “slow” in Arduino processing time, the simplicity of not having to convert numbers back and forth on the Arduino is worth the speed reduction. I have limited the “command” structure sent to the Arduinos to single words. This allows faster processing on the micro-controllers. This might change if I find that too many messages are “lost” during data exchanges.
There’s a lot more justification in the following posts about Python programming for the main controller, a Raspberry Pi.
Leave a Reply