// https://forum.arduino.cc/t/can-someone-explain-this-code-trying-to-learn/1031873
// https://wokwi.com/projects/342871593282699859
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
# define PIXEL_PIN 6
# define NLAMPS 20 // tank display size
Adafruit_NeoPixel tank(NLAMPS, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// tank model stuff
# define FULL_TANK ((NLAMPS - 1) * 32 + 31) // mixed point tank level
# define TANK_FILL_RATE A0 // analog input for tank fill rate
# define FILL_ON A1 // switch controls filling of tank
# define UPPER_FLOAT 5 // outputs from level sensors - use as inputs to logic
# define LOWER_FLOAT 4
// pump stuff
# define NPUMPS 2
unsigned char pumpState[NPUMPS] = {0, 0};
const unsigned char pumpRelay[NPUMPS] = {9, 8}; // pump relays
const unsigned char pumpButton[NPUMPS] = {3, 2}; // pump manual controls
void setup() {
Serial.begin(115200);
Serial.println("Hello Tank World!\n");
setupRETCON();
tank.begin(); // neopixel
pinMode(FILL_ON, INPUT_PULLUP);
pinMode(UPPER_FLOAT, OUTPUT);
pinMode(LOWER_FLOAT, OUTPUT);
for (unsigned char ix = 0; ix < NPUMPS; ix++) {
pinMode(pumpButton[ix], INPUT_PULLUP);
pinMode(pumpRelay[ix], OUTPUT);
digitalWrite(pumpRelay[ix], LOW);
}
tank.show();
}
void loop()
{
// run the model tank and pumps thing
tankPumpWorld();
printStatus();
// below here you have full speed loop
// here is just a simple empty/fill example:
loopRETCON();
}
// handle tank and pump simulation.
int tankLevel;
void tankPumpWorld() {
static unsigned long lastLoop;
unsigned long now = millis(); //grab current time
if (now - lastLoop < 50) return; // 20 Hz all around
lastLoop = now;
// tank simulation and manual controls
// flawed checkPumpSwitches();
tankSimulate(); // update the tank model
displayTank();
}
// system status report
void printStatus()
{
static unsigned long lastReport;
unsigned long now = millis();
if (now - lastReport < 1333) return;
lastReport = now;
unsigned char tankFull = digitalRead(UPPER_FLOAT); // just read the status lamp!
unsigned char tankEmpty = !digitalRead(LOWER_FLOAT); // just read the status lamp!
if (tankFull) Serial.print("tank is full ");
if (tankEmpty) Serial.print("tank is empty ");
if (pumpState[0]) Serial.print("pump one is ON ");
if (pumpState[1]) Serial.print("pump two is ON ");
if (!digitalRead(FILL_ON)) {
Serial.print(" the tank is filling at ");
Serial.print((analogRead(TANK_FILL_RATE) >> 6) + 1);
Serial.print(" units per tick.");
}
else Serial.print(" the fill valve is closed.");
Serial.print(" tank level "); Serial.println(tankLevel);
}
// fill and empty the tank.
void tankSimulate()
{
int rate = (analogRead(TANK_FILL_RATE) >> 6) + 1;
// Serial.println(rate); 1 to 16 units per tick, fine
if (digitalRead(FILL_ON) == LOW)
addToTank(rate);
runPumps();
}
void displayTank()
{
int tankI = tankLevel >> 5;
for (unsigned char ix = 0; ix < NLAMPS; ix++) tank.setPixelColor(ix, 0);
if (tankLevel)
for (unsigned char ix = 0; ix <= tankI; ix++) tank.setPixelColor(ix, 0x0000ff);
digitalWrite(UPPER_FLOAT, tankI == 19);
digitalWrite(LOWER_FLOAT, tankLevel > 0 ? HIGH : LOW);
tank.show();
}
// turn a pump on or off - logic
void switchPump(unsigned char thePump, unsigned char onOff)
{
pumpState[thePump] = onOff;
}
// get sgtate of pump running/not running
unsigned char pumpIsOn(unsigned char thePump)
{
return pumpState[thePump];
}
// turn off or on the pumps - mechanical. subtract from tank.
// heed the pushbutton manual pump control!
void runPumps()
{
for (unsigned char ix = 0; ix < NPUMPS; ix++)
if (pumpState[ix] || !digitalRead(pumpButton[ix])) {
addToTank(-5);
}
}
// add or subtract from the tank. limit 0..full
void addToTank(int units)
{
tankLevel += units;
if (tankLevel > FULL_TANK) tankLevel = FULL_TANK;
if (tankLevel < 0) tankLevel = 0;
}
// spam message suppression
void printOnce(char *msg)
{
static char message[32] = {0};
if (strcmp(message, msg)) {
Serial.println(msg);
strcpy(message, msg);
}
}
// unused parts from development
// press the button, turn on/leave on pump
// flawed - interferes with another pump on/off logic
void checkPumpSwitchesNOTUSED1()
{
for (unsigned char ix = 0; ix < NPUMPS; ix++) {
if (pumpIsOn(ix)) {
if (digitalRead(pumpButton[ix]))
switchPump(ix, 0);
}
else {
if (!digitalRead(pumpButton[ix]))
switchPump(ix, 1);
}
}
}
// OLD: press the button, toggle the coresponding pump on/off
void checkPumpSwitchesNOTUSED0()
{
static unsigned char lastPumpButton[NPUMPS] = {HIGH, HIGH};
for (unsigned char ix = 0; ix < NPUMPS; ix++) {
unsigned char thisPumpButton = digitalRead(pumpButton[ix]);
if (lastPumpButton[ix] != thisPumpButton) {
if (!thisPumpButton) {
Serial.print(" saw button "); Serial.println(ix);
switchPump(ix, pumpIsOn(ix) ? 0 : 1);
}
lastPumpButton[ix] = thisPumpButton;
}
}
}
// test loop with simple logic?
/* test patterns for tank simulator
// read the model
unsigned char tankFull = digitalRead(UPPER_FLOAT); // just read the status lamps!
unsigned char tankEmpty = !digitalRead(LOWER_FLOAT);
// and use switchPump(pumpNumber, onOrOff) to control the pumps
if (tankFull) // if the tank is full
switchPump(0, 1); // start pump 0
if (tankEmpty) // if the tank is empty
switchPump(0, 0); // stop pump 0
*/