The goal: A machine will provide one switch input to count operation cycles. At different counts lubrication is required on certain points of the machine. For example, pointA will be lubricated every 200 cycles while pointB will be lubricated every 550 cycles and so on. One interval for all would be wasteful and even counterproductive.
Arduino will alarm and display on a 2x20 LCD which point's lube time has arrived. The point is lubed, alarm is reset and cycling continues. I've searched for a 'scheduler' library many different ways but the search algorithm always focuses on scheduling in the context of timed events. I've given up this wording. I also tried the greenhouse control/plant watering and maintenance interval route with no joy.
I'd like to avoid reinventing the wheel and/or having to cannibalize a library to twist it into the shape of what I want.
So, does anyone know of a library that does such a thing or, maybe a better search term?
Here's a simulation of paul paulson's 4-leds 4-buttons array of structs to manage 4 interacting leds and buttons.
If you first count cycles with something like the Digital/StateChangeDetection built-in-example, you can feed the cycle count into this sort of system.
Instread of the millis() that this code is monitoring, think of using your cycle count as the trigger, your lubrication pointA as one of the LEDs, and the lubrication interval as the blink interval. You'd modify run() to initiate the lubrication and update the LCD, etc...
Your counter tracking is basic "state change detection" and your multiple lube points and associated intervals and counters are "array of struct".
I didn't think so but was having difficulties (the mental kind) setting up the struct and accessing an array of same. I thought to save myself some work with a canned solution.
I will look into that. Thanks!
That's definitely a part of this. Eventually the presets and some bit flags, too, will be runtime changeable and stored in EEPROM.
I can share an old code I had written for something very similar
I just modified it a bit to suit your needs and published it on wokwi
You'll need two libraries for the lcd and button
Here is the simulation:
The LCD shows on the first line how many parts need maintenance / total number of parts and right adjusted the number of cycles
below that you have the list of parts needing maintenance
The red button increases the cycle count
the green button is to be pressed when you have done all the maintenance required.
The red led will blink if there is at least a part to maintain.
code is pretty straightforward, let me know if you have any question.
There is an empty function to save the state that I left empty. That's where you would want to archive stuff in EEPROM potentially. You would have also to modify the setup() to read the state from EEPROM after reboot.
click to see the code
/* ============================================
code is placed under the MIT license
Copyright (c) 2022 J-M-L
For the Arduino Forum : https://forum.arduino.cc/u/j-m-l / https://forum.arduino.cc/t/finding-a-good-search-term/1311566?u=j-m-l
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================
*/
#include <Wire.h>
#include <hd44780.h> // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header
#include <Toggle.h>
const uint8_t nbCols = 20;
const uint8_t nbRows = 4;
hd44780_I2Cexp lcd;
const byte cyclePin = 2;
const byte maintenanceDonePin = 3;
const byte alertPin = 4;
struct MaintenanceSchedule {
const char * label;
const uint16_t everyCycle;
bool needsMaintenance;
};
MaintenanceSchedule parts[] = {
{"Joint", 7, false}, // maintain every 7 cycles
{"Gripper", 10, false}, // maintain every 10 cycles
{"Vacuum", 5, false}, // maintain every 5 cycles
{"Motor", 3, false}, // maintain every 3 cycles
};
// ----------------------
const uint32_t partsCnt = sizeof parts / sizeof * parts;
uint32_t partsNeedingMaintenanceCnt = 0;
Toggle cycleButton;
Toggle maintenanceDoneButton;
uint32_t cycleCounter = 0;
char txtBuffer[3 * nbCols + 1];
void saveState() {
}
void blinkAlertIfNeeded() {
static unsigned long lastBlink;
if (partsNeedingMaintenanceCnt != 0) {
if (millis() - lastBlink >= 200) {
digitalWrite(alertPin, digitalRead(alertPin) == LOW ? HIGH : LOW);
lastBlink = millis();
}
} else digitalWrite(alertPin, LOW);
}
void showCycles() {
snprintf(txtBuffer, sizeof txtBuffer, "%08lu", cycleCounter);
lcd.setCursor(nbCols - 8, 0);
lcd.print(txtBuffer);
}
void showMaintenanceList() {
partsNeedingMaintenanceCnt = 0;
txtBuffer[0] = '\0';
for (auto &p : parts ) {
if (p.needsMaintenance) {
partsNeedingMaintenanceCnt++;
if (*txtBuffer != '\0') strlcat(txtBuffer, ", ", sizeof txtBuffer);
strlcat(txtBuffer, p.label, sizeof txtBuffer);
}
}
// erase
for (byte r = 1; r < nbRows; r++) {
lcd.setCursor(0, r);
for (byte c = 0; c < nbCols; c++) lcd.write((' '));
}
// show the parts on the LCD. can span multiple lines. we have only nbRows-1 to play with
int line = 1;
int col = 0;
lcd.setCursor(col, line);
for (int i = 0; txtBuffer[i] != '\0'; ++i) {
lcd.write(txtBuffer[i]);
col++;
// When reaching the maximum number of columns, move to the next line
if (col >= nbCols) {
col = 0;
line++;
if (line > nbRows - 1) {
break;
} else {
lcd.setCursor(col, line);
}
}
}
snprintf(txtBuffer, sizeof txtBuffer, "%03lu/%lu", partsNeedingMaintenanceCnt, partsCnt);
lcd.setCursor(0, 0);
lcd.print(txtBuffer);
}
void setup() {
pinMode(alertPin, OUTPUT);
cycleButton.begin(cyclePin);
maintenanceDoneButton.begin(maintenanceDonePin);
Serial.begin(115200);
int result = lcd.begin(nbCols, nbRows);
if (result) {
Serial.print("LCD initialization failed: ");
Serial.println(result);
hd44780::fatalError(result);
}
lcd.print("PARTS SAVER 1.0");
delay(1000);
lcd.clear();
showCycles();
showMaintenanceList();
}
void loop() {
cycleButton.poll();
if (cycleButton.onPress()) {
cycleCounter++;
saveState();
showCycles();
for (auto &p : parts ) {
if (cycleCounter % p.everyCycle == 0) {
p.needsMaintenance = true;
showMaintenanceList();
}
}
}
maintenanceDoneButton.poll();
if (maintenanceDoneButton.onPress()) {
for (auto &p : parts ) p.needsMaintenance = false;
showMaintenanceList();
saveState();
}
blinkAlertIfNeeded();
}
Personally I think that your time would be better spent learning about structs and how to use them in array. This knowledge will be useful in the future but knowledge of how to use a specific library is less likely to be generally applicable
But still, the docs on making arrays of structs is a bit difficult because the search terms do seem generic. You find structs, arrays, and structs containing arrays, but few combining them as arrays of structs.
What a gift! Very close to what I envisioned. Thanks, @J-M-L !
I have a small enclosure with a 2x20 LCD for a cancelled project. Adapting your code to the two-line display won't, it is hoped, be too difficult. I also already have those two libraries on board.
In previous projects I've had a working EEPROM restore/save and runtime parameter change. Not drop-in ready but the outline's there.
I'll update the thread when I have something substantial done.
One challenge with a smaller screen is that the list of parts needing maintenance might not fit all on screen as I use the first line for information (number of clicks and number of parts needing attention) so with only 2 lines it gives you only 1 line to display the parts labels.
this is the part of the code that builds a text buffer containing a comma separated list of all the parts needing maintenance.
and then I print that string on screen character by character so that I can manage going to the next line when a line is full.
Should work for parallel two-line display also, no?
I've disabled the first line info and start printing on line 0. That gets me at least three parts labels (alarm names) fully printed. Total counts is still briefly displayed on startup.
The code could show the count and when the parts list is too long then it would alternate between both displays (5s one, 5s the other or something like that)
You could also name the parts with one letter and have a piece of paper in the maintenance doc next to the machine that says part A is the engine, part B is XXX…
I'm thinking of a mathematical solution. Counts per point aren't that critical, a few one way or the other isn't going to matter, so I'll go with presets with high Least Common Multiples. That way there shouldn't be too many on screen at one time.
Do you mean the "Greatest Common Divisor" - ie if something needs fixing every 200 cycles and the other is 300, then you fix everything every 100 cycles otherwise you'll end up fixing stuff too late...
Could be I'm confused. In any case, rather than stare at another rabbit hole I'm just going to try to make some progress on the encoder/setting change addition.