Let’s get to know how to use two LEDs as Human-machine interface. When it comes to electronics I am a minimalist, I like my circuits as small as possible. I then came up with a HMI (human-machine interface) that is very minimalist, it is capable of showing numbers with two digits (from 0 to 99) using only two LEDs.
One can ague that only morse code (at one LED) can be smaller than this. The idea here is simple, one LED will blink for the tens and another for the units. So for example for number 28 the tens LED will blink twice and the units LED will blink eight times. Same for number 89, where the tens LED will blink eight times and the units LED will blink nine times.
Test circuit
For this experiment you will need an Arduino (really any one will do), one potentiometer, two LEDs and two resistors (I am using 1k Ohm value). Look for the schematic diagram below.
Notice that this potentiometer is just one of the many things you can use as input data for this human machine interface. You could for example read data from a i2c sensor (BMP280, SHT21, etc), get internet data using an internet shield or an ESP32, and even more. The important is that the data is to be limited betwen one and two digits integers (0-99).
Coding for this HMI
There are a couple of steps necessary to convert an analog value into two LEDs blinking in the correct pattern. I will explain this step by step and the final complete code will be at the end. First you need to transform your analog data into a 1 to 99 integer value; I am using the map() function for that:
analoginput= map(analogRead(A1), 0, 1023, 0, 99);
I read the analog input A1 and convert its 0-1023 value to 0-99, then place the result into “analoginput”. Again, one could read any value from any sensor using any protocol, being enough to convert it to a 0-99 integer value.
Second step is to separate tens from units, I do that by comparing the integer value to chunks or parts of integers:
if(analoginput < 10){
tens= 0;
unit= analoginput;
}else if(analoginput >= 10 && analoginput < 20){
tens= 1;
unit= analoginput - 10;
}else if(analoginput >= 20 && analoginput < 30){
tens= 2;
unit= analoginput - 20;
}else if(analoginput >= 30 && analoginput < 40){
tens= 3;
unit= analoginput - 30;
}else if(analoginput >= 40 && analoginput < 50){
tens= 4;
unit= analoginput - 40;
}else if(analoginput >= 50 && analoginput < 60){
tens= 5;
unit= analoginput - 50;
}else if(analoginput >= 60 && analoginput < 70){
tens= 6;
unit= analoginput - 60;
}else if(analoginput >= 70 && analoginput < 80){
tens= 7;
unit= analoginput - 70;
}else if(analoginput >= 80 && analoginput < 90){
tens= 8;
unit= analoginput - 80;
}else if(analoginput >= 90 && analoginput < 100){
tens= 9;
unit= analoginput - 90;
}else{
tens= 0;
unit= 0;
}
One could do that for 0-999 also, by nesting IFs in the correct way. Notice that this is very “labour intense”, a bunch of IFs one after the other. This is the way I figure how to solve the problem, but not the only way it could be done. Both pieces of code above are inside a loop that is executed every 50ms (milisseconds):
if (enterFunction2 == true && waittime == true) { //Enter this function every xx milisseconds and IF LEDs are not blinking
previousTime2 = time2;
It also is only executed once “waittime” is true, meaning these calculations only happen when the data is not being shown. Now for the data showing part, digital output 6 (tens) is toggled (turn ON and OFF alternatively) every time this function is entered, if “blinkingtens” is true:
if(blinkingtens == true && blinkingunits == false){
digitalWrite(7, LOW);
if(enteredtens == false){
enteredtens = true;
doubletens = 2 * tens;
if(doubletens == 0){
doubletens= 1;
}
}
doubletens --;
if(doubletens != 0){
digitalWrite(6, !digitalRead(6));
}else{
blinkingunits = true;
blinkingtens = false;
}
}
Due to the usage of toggling I have to execute the function twice as “slow”, doing:
doubletens = 2 * tens;
I then do the same for the units (digital output 7), also doubling the number of this it executes, due to the toggle function:
else if(blinkingunits == true && blinkingtens == false){
digitalWrite(6, LOW);
blinkingtens= false;
if(enteredunit == false){
enteredunit = true;
doubleunit = 2 * unit;
if(doubleunit == 0){
doubleunit= 1;
}
}
doubleunit --;
if(doubleunit != 0){
digitalWrite(7, !digitalRead(7));
}else{
waittime = true;
blinkingunits = false;
startedwait = true;
}
}
When I have shown both tens and units, I do a (roughly) 700ms delay with everything turned off, before going to the next round:
else if(waittime == true){
if(startedwait == true){
digitalWrite(6, LOW);
digitalWrite(7, LOW);
startedwait = false;
elapsedtime = millis();
}
if(millis() - elapsedtime > 698){
waittime = false;
}
}
Then the process is repeated all over again, just with a new analog value that was freshly read from the analog input. The complete code can be seen below. It is NON BLOCKING, meaning you can execute other pieces of code in the mean time. Uses only 10% of program space and 10% of memory, very light program.
unsigned long time1;
unsigned long previousTime;
bool enterFunction = true;
unsigned long time2;
unsigned long previousTime2;
bool enterFunction2 = true;
bool blinkingtens= false;
bool enteredtens= false;
int doubletens= 0;
bool blinkingunits = false;
bool enteredunit = false;
int doubleunit = 0;
int unit = 0;
int tens = 0;
bool waittime = true;
bool startedwait = false;
long elapsedtime;
int analoginput;
int digituponentering = 1;
void setup() {
Serial.begin(9600);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
}
void loop() {
time1 = micros();
time2 = micros();
if (enterFunction == true) {
previousTime = time1;
// Start your code below
//-----------------------
if(blinkingtens == true && blinkingunits == false){
digitalWrite(7, LOW);
if(enteredtens == false){
enteredtens = true;
doubletens = 2 * tens;
if(doubletens == 0){
doubletens= 1;
}
}
doubletens --;
if(doubletens != 0){
digitalWrite(6, !digitalRead(6));
}else{
blinkingunits = true;
blinkingtens = false;
}
}else if(blinkingunits == true && blinkingtens == false){
digitalWrite(6, LOW);
blinkingtens= false;
if(enteredunit == false){
enteredunit = true;
doubleunit = 2 * unit;
if(doubleunit == 0){
doubleunit= 1;
}
}
doubleunit --;
if(doubleunit != 0){
digitalWrite(7, !digitalRead(7));
}else{
waittime = true;
blinkingunits = false;
startedwait = true;
}
}else if(waittime == true){
if(startedwait == true){
digitalWrite(6, LOW);
digitalWrite(7, LOW);
startedwait = false;
elapsedtime = millis();
}
if(millis() - elapsedtime > 698){
waittime = false;
}
}else{
blinkingtens= true;
enteredtens = false;
enteredunit = false;
}
//-----------------------
// End of your code
}
if (enterFunction2 == true && waittime == true) { //Enter this function every xx milisseconds and IF LEDs are not blinking
previousTime2 = time2;
// Le o sensor algumas vezes
analoginput= map(analogRead(A1), 0, 1023, 0, 99);
Serial.println(analoginput);
if(analoginput < 10){
tens= 0;
unit= analoginput;
}else if(analoginput >= 10 && analoginput < 20){
tens= 1;
unit= analoginput - 10;
}else if(analoginput >= 20 && analoginput < 30){
tens= 2;
unit= analoginput - 20;
}else if(analoginput >= 30 && analoginput < 40){
tens= 3;
unit= analoginput - 30;
}else if(analoginput >= 40 && analoginput < 50){
tens= 4;
unit= analoginput - 40;
}else if(analoginput >= 50 && analoginput < 60){
tens= 5;
unit= analoginput - 50;
}else if(analoginput >= 60 && analoginput < 70){
tens= 6;
unit= analoginput - 60;
}else if(analoginput >= 70 && analoginput < 80){
tens= 7;
unit= analoginput - 70;
}else if(analoginput >= 80 && analoginput < 90){
tens= 8;
unit= analoginput - 80;
}else if(analoginput >= 90 && analoginput < 100){
tens= 9;
unit= analoginput - 90;
}else{
tens= 0;
unit= 0;
}
}
// The DELAY time is adjusted in the constant below >>
if (time1 - previousTime < 299990) { // 1 million microsencods= 1 second delay
/* I have actually used 0.999990 seconds, in a trial to compensate the time that
this IF function takes to be executed. this is really a point that
need improvement in my code */
enterFunction = false;
}
else {
enterFunction = true;
}
if (time2 - previousTime2 < 49990) { // 1 million microsencods= 1 second delay
/* I have actually used 0.999990 seconds, in a trial to compensate the time that
this IF function takes to be executed. this is really a point that
need improvement in my code */
enterFunction2 = false;
}
else {
enterFunction2 = true;
}
}
End result
I made a video showing how this code and prototype works, check below:
Leave a Reply