Competitions are human nature. We humans compete in everything: in sport, at work, in studies, in short, we are always striving to be better and better and stand out before other competitors.
Something that every existing competition needs is to measure: to have a metric / a form of quantification that allows the competition to be objective and able to point the winner in a clear and direct way. One of the most used metrics in competitions is the scoreboards.
Measuring the points of two rivals and, at the end of the competition, choosing the winner who has the most points is one of the oldest, most efficient and effective metrics in competitions ever used.
And this is exactly what you, the reader, will learn to do in this article: you will learn how to make your own scoreboard using a NodeMCU (ESP8266), the screen being an OLED 0.96 ”I²C display and using three buttons to control your scoreboard : side A score, side B score and score reset / reset.
In addition, the scoreboard you will learn to make will send your result via the Internet, using the MQTT protocol, from second to second, allowing anyone in the world with Internet connectivity to follow the result of your score.
In detail, with this article you will learn:
- How to program NodeMCU (ESP8266) with Arduino IDE
- How to make connections from the OLED 0.96 ”I²C display and how to operate it in your program
- How to score points for two competitors / teams / sides
- How to send the scoreboard result, second by second, via MQTT
Now, the complete presentation of the IoT scoreboard development using NodeMCU (ESP8266) will begin.
Developing the Scoreboard project with IoT using NodeMCU (ESP8266)As previously mentioned, the project consists of creating a scoreboard with NodeMCU (ESP8266), using a 0.96 ”I²C OLED display as the screen and having three buttons (score on side A, score on side B and reset / reset of scoreboard).
Thus, the schematic circuit of the project presented in Figure 1 was developed.
The following is a list of materials needed to create this project:
Bill of materials
- 01 x NodeMCU (ESP8266)
- 01 x 0.96 ”I²C OLED display. Both monochrome and bicolor versions can be used.
- 02 x push-buttons
- Male-male jumpers
- 02 x 400 point protoboards
- 01 x 5V / 2A switched DC power supply, for project power without the Computer (optional)
- 01 x micro-USB cable for programming (and powering) the project by the computer
In general terms, the schematic circuit of the project has three main parts: NodeMCU (ESP8266), OLED display 0.96 ”I²C and buttons. See in detail below the explanations of each of these parts.
NodeMCU (ESP8266) - OverviewNodeMCU (ESP8266) consists of a complete development board, containing good processing and memories for an embedded device, wi-fi connectivity, a good number of GPIOs available for development and multiple communication interfaces (I²C, UART, SPI, etc. ).
In addition, it has a very inviting cost considering all its features, therefore being a great alternative for prototypes and products that involve Internet of Things concepts.
In addition, NodeMCU (ESP8266) has a perfect format to fit protoboards, facilitating rapid prototyping without the need for welding.
Thus, NodeMCU (ESP8266) is a very interesting option for both professionals and hobbyists / makers.
0.96 ”I²C OLED display - overviewThe 0.96 ”I²C OLED display consists of a small physical display (0.96” screen), which uses the I²C as a communication interface. This means that all that this display needs to operate is the supply (Vcc and GND, being VCC 3.3V or 5V) and the I²C communication signals (SDA and SCL or SCK).
It is a great display option, because with 4 wires you have a fully operational display and, given its small physical dimensions, it is applicable even in more miniaturized projects.
Push-buttons - OverviewThe schematic circuit of the project also has three buttons (push-buttons) for operation of the scoreboard, namely:
- Team A score button: button used to raise the side / team / competitor's score A. Each time the button is pressed, the score goes up by one point.
- Team B scoring button: button used to raise the side / team / competitor B's score. Each time the button is pressed, the score goes up by one point.
- Scoreboard reset / restart button: button used to reset the scoreboard, that is, leave both sides with zero points.
In this case, in order to avoid fluctuating voltage levels (which means the risk of detecting a false push of the button by the scoreboard program, something that would be disastrous for this project), the ESP8266 internal pull-up resistors were used in the GPIOs to which the buttons are on (D3, D4 and D5).
Such pull-up resistors are used to guarantee a well-defined voltage level (at 3.3V) when the buttons are not pressed. When pressed, the voltage read by the respective GPIOs will be GND (zero Volt).
In this way, a good reading of the button voltages is guaranteed without any other external electronic components besides the buttons themselves, which makes the solution elegant and economical.
In addition, the project program makes a software routine for filtering mechanical button shake (debounce), thus avoiding multiple false triggers for each real push of a button.
Now it's time for embedded software. Curious to know how to program NodeMCU (ESP8266) on Arduino IDE? See below.
Programming NodeMCU (ESP8266) on the Arduino IDENodeMCU (ESP8266) can be programmed in the Arduino IDE, so that the development of projects with it is very similar to what is done with any common Arduino.
This facilitates both the development of new projects and the port / transfer of other projects made on other hardware / platforms to NodeMCU (ESP8266).
Installation of libraries to use the OLED displayTo use OLED in the program for this project, it is necessary to install two libraries:
- Adafruit SSD1306
- Adafruit GFX Library
Both can be installed simply and quickly via Arduino IDE, in the library manager (Sketch> Include Library> Manage Libraries). Just enter their names in the search field and install the newest versions of them.
MQTT ProtocolMQTT is an ideal communication protocol for Internet of Things, as it allows communication between devices via the Internet in a fast, simple way and using little Internet bandwidth and computing power of the devices (CPU and memory) for that.
This project makes use of this protocol to send, via the Internet, the current scoreboard every second of the project's operation.
The project described here uses a public MQTT broker (free and free for community use), so that you can test the project without having to pay for a private MQTT broker in the cloud. The access information for this MQTT broker is as follows:
- Broker público utilizado no projeto: broker.hivemq.com
- Porta do broker:1883
It is time for the project's source code, where you will know in detail how it works. In addition, you can have insights on how to modify it, improve it or adapt it to your needs.
Now, I'll offer the full source code for this project.
Project source codeThe source code of the project can be found below. The way to program NodeMCU (ESP8266) with this source code is very similar to any other common Arduino.
First, copy and paste it into your Arduino IDE, change the contents of the ssid_wifi and password_wifi variables respectively with the name and password of the wifi network that NodeMCU (ESP8266) should connect to, compile and upload / program. NodeMCU (ESP8266).
#include <stdio.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/* Definicoes gerais */
#define SIM 0x01
#define NAO 0x00
#define TENTATIVAS_CONEXAO_BROKER 10
/* Definições - botões */
#define GPIO_BOTAO_RESET_PLACAR D3
#define GPIO_BOTAO_LADO_A D4
#define GPIO_BOTAO_LADO_B D5
#define TEMPO_DEBOUNCE_BOTAO 200 //ms
/* Definições - placar */
#define PLACAR_LADO_A 0x00
#define PLACAR_LADO_B 0x01
#define TEMPO_PARA_ENVIAR_PLACAR_MQTT 1000 //ms
/* Definicoes da UART de debug */
#define DEBUG_UART_BAUDRATE 115200
/* Definições - display OLED */
#define OLED_ADDRESS 0x3C
#define OLED_RESET -1
#define OLED_SCREEN_HEIGHT 64
#define OLED_SCREEN_WIDTH 128
#define OLED_LINHA_1 0
#define OLED_LINHA_2 16
#define OLED_LINHA_3 32
#define OLED_LINHA_4 48
/* Definições - MQTT */
#define MQTT_PUB_TOPIC "mqtt_projeto_placar"
/* WIFI */
const char* ssid_wifi = " "; /* Nome da rede wi-fi para se conectar */
const char* password_wifi = " "; /* Senha da rede wi-fi para se conectar */
WiFiClient espClient;
/* MQTT */
const char* broker_mqtt = "broker.hivemq.com"; /* URL do broker MQTT */
int broker_port = 1883; /* Porta do broker MQTT */
PubSubClient MQTT(espClient);
/* Display OLED - ligações no NodeMCU */
/* Ligação: SDA -> D2
* SCL -> D1
* VCC -> 3V
* GND -> GND
*/
Adafruit_SSD1306 display(OLED_SCREEN_WIDTH, OLED_SCREEN_HEIGHT, &Wire, OLED_RESET);
/*
* Variáveis e objetos globais
*/
int pontos_lado_a = 0;
int pontos_lado_b = 0;
unsigned long timestamp_envio_placar = 0;
/* Prototypes */
void init_wifi(void);
void init_MQTT(void);
void conecta_MQTT(void);
void conecta_wifi(void);
void verifica_conexao_wifi(void);
void verifica_conexao_mqtt(void);
void escreve_placar_no_display(void);
void reset_placar(void);
void aumenta_placar(char lado_placar);
char verifica_se_botao_foi_pressionado(int gpio_botao);
void aguarda_botao_ser_solto(int gpio_botao);
/* Funcao: inicializa conexao wi-fi
* Parametros: nenhum
* Retorno: nenhum
*/
void init_wifi(void)
{
delay(10);
Serial.println("------WI-FI -----");
Serial.print("Tentando se conectar a rede wi-fi ");
Serial.println(ssid_wifi);
Serial.println("Aguardando conexao");
conecta_wifi();
}
/* Funcao: inicializa variaveis do MQTT para conexao com broker
* Parametros: nenhum
* Retorno: nenhum
*/
void init_MQTT(void)
{
MQTT.setServer(broker_mqtt, broker_port);
}
/* Funcao: conecta com broker MQTT (se nao ha conexao ativa)
* Parametros: nenhum
* Retorno: nenhum
*/
void conecta_MQTT(void)
{
char mqtt_id_randomico[5] = {0};
int contador_tentativas_conexao = 0;
while (!MQTT.connected())
{
Serial.print("* Tentando se conectar ao broker MQTT: ");
Serial.println(broker_mqtt);
/* gera id mqtt randomico */
randomSeed(random(9999));
sprintf(mqtt_id_randomico, "%ld", random(9999));
if (MQTT.connect(mqtt_id_randomico))
Serial.println("Conectado ao broker MQTT com sucesso!");
else
{
contador_tentativas_conexao++;
/* Se após o número de tentativas definidas em TENTATIVAS_CONEXAO_BROKER não conectar,
reinicia automaticamente o ESP8266 */
if (contador_tentativas_conexao > TENTATIVAS_CONEXAO_BROKER)
{
Serial.println("Reiniciando em 1s...");
delay(2000);
ESP.restart();
}
else
{
Serial.print("Tentativa: ");
Serial.println(contador_tentativas_conexao);
Serial.println("Falha na tentativa de conexao com broker MQTT.");
Serial.println("Nova tentativa em 2s...");
delay(2000);
}
}
}
}
/* Funcao: conexao a uma rede wi-fi
* Parametros: nenhum
* Retorno: nenhum
*/
void conecta_wifi(void)
{
unsigned long tempo_sem_conexao = 0;
if (WiFi.status() == WL_CONNECTED)
return;
WiFi.begin(ssid_wifi, password_wifi);
while (WiFi.status() != WL_CONNECTED)
{
delay(100);
Serial.print(".");
/* Se se passar 10 segundos sem se conectar, reinicia automaticamente */
tempo_sem_conexao = tempo_sem_conexao+100;
if (tempo_sem_conexao >= 10000)
ESP.restart();
}
Serial.println();
Serial.print("Conectado com sucesso a rede wi-fi: ");
Serial.println(ssid_wifi);
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
/* Funcao: verifica e garante conexao wi-fi
* Parametros: nenhum
* Retorno: nenhum
*/
void verifica_conexao_wifi(void)
{
conecta_wifi();
}
/* Funcao: verifica e garante conexao MQTT
* Parametros: nenhum
* Retorno: nenhum
*/
void verifica_conexao_mqtt(void)
{
conecta_MQTT();
}
/* Funcao: escreve placar no display OLED
* Parametros: nenhum
* Retorno: nenhum
*/
void escreve_placar_no_display(void)
{
char linha_placar[9]={0};
display.clearDisplay();
display.setTextColor(WHITE);
/* Escreve cabeçalho */
display.setTextSize(2);
display.setCursor(0,OLED_LINHA_1);
display.println(" Placar");
/* Escreve placar */
display.setTextSize(3);
display.setTextColor(WHITE);
/* Formata string do placar e escreve no display */
sprintf(linha_placar, "%02d X %02d", pontos_lado_a, pontos_lado_b);
display.setCursor(0,OLED_LINHA_3);
display.println(linha_placar);
/* Exibe todas informaçõe sno display */
display.display();
}
/* Funcao: faz o reset do placar
* Parametros: nenhum
* Retorno: nenhum
*/
void reset_placar(void)
{
pontos_lado_a = 0;
pontos_lado_b = 0;
}
/* Funcao: aumenta em um ponto o placar do lado informado
* Parametros: lado (PLACAR_LADO_A ou PLACAR_LADO_B)
* Retorno: nenhum
*/
void aumenta_placar(char lado_placar)
{
if (lado_placar == PLACAR_LADO_A)
pontos_lado_a++;
if (lado_placar == PLACAR_LADO_B)
pontos_lado_b++;
}
/* Funcao: aumenta em um ponto o placar do lado b
* Parametros: nenhum
* Retorno: nenhum
*/
void aumenta_placar_lado_b(void)
{
pontos_lado_b++;
}
/* Funcao: verifica se botão foi pressionado (considerando debounce)
* Parametros: GPIO do botão
* Retorno: SIM: botão foi pressionado
* NAO: botão não foi pressionado
*/
char verifica_se_botao_foi_pressionado(int gpio_botao)
{
char status_botao = NAO;
if (digitalRead(gpio_botao) == LOW)
{
/* O botão foi detectado como pressionado.
Agora é feita a rotina de debounce para confirmar. */
delay(TEMPO_DEBOUNCE_BOTAO);
if (digitalRead(gpio_botao) == LOW)
{
/* Foi confirmado que o botão foi pressionado. */
status_botao = SIM;
}
}
return status_botao;
}
/* Funcao: aguarda botão ser solto
* Parametros: GPIO do botão
* Retorno: nenhum
*/
void aguarda_botao_ser_solto(int gpio_botao)
{
while(digitalRead(gpio_botao) == LOW)
{
delay(1);
}
}
void setup()
{
/* UARTs setup */
Serial.begin(DEBUG_UART_BAUDRATE);
/* Inicializa placar */
reset_placar();
/* Inicializa GPIOs dos botões (reset do placar,aumento de pontuação do lado A e aumento de
pontuação do lado B */
pinMode(GPIO_BOTAO_RESET_PLACAR, INPUT_PULLUP);
pinMode(GPIO_BOTAO_LADO_A, INPUT_PULLUP);
pinMode(GPIO_BOTAO_LADO_B, INPUT_PULLUP);
/* Inicializa display */
if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS))
{
Serial.println("[ERRO] não foi possivel inicializar display. O NodeMCU será reiniciado em 1s...");
delay(1000);
ESP.restart();
}
else
{
Serial.println("Display inicializado.");
escreve_placar_no_display();
delay(500);
}
/* Inicializa wi-fi */
init_wifi();
/* Inicializa MQTT e faz conexao ao broker MQTT */
init_MQTT();
conecta_MQTT();
/* Inicializa variavel de controle de envio do placar via MQTT */
timestamp_envio_placar = millis();
}
void loop()
{
char placar_mqtt[10]={0};
/* Garante que wi-fi e MQTT estejam conectados */
verifica_conexao_wifi();
verifica_conexao_mqtt();
/* Verifica se botão de reset de placar foi acionado.
Se sim, reseta placar (coloca em 0 x 0) */
if (verifica_se_botao_foi_pressionado(GPIO_BOTAO_RESET_PLACAR) == SIM)
{
Serial.println("* O placar foi resetado.");
reset_placar();
escreve_placar_no_display();
aguarda_botao_ser_solto(GPIO_BOTAO_RESET_PLACAR);
}
/* Verifica se botão de aumento de pontuação do lado A foi acionado.
Se sim, aumenta em 1 ponto a pontuação do lado A */
if (verifica_se_botao_foi_pressionado(GPIO_BOTAO_LADO_A) == SIM)
{
Serial.println("* Pontuacao do lado A aumentada.");
aumenta_placar(PLACAR_LADO_A);
escreve_placar_no_display();
aguarda_botao_ser_solto(GPIO_BOTAO_LADO_A);
}
/* Verifica se botão de aumento de pontuação do lado B foi acionado.
Se sim, aumenta em 1 ponto a pontuação do lado B */
if (verifica_se_botao_foi_pressionado(GPIO_BOTAO_LADO_B) == SIM)
{
Serial.println("* Pontuacao do lado B aumentada.");
aumenta_placar(PLACAR_LADO_B);
escreve_placar_no_display();
aguarda_botao_ser_solto(GPIO_BOTAO_LADO_B);
}
/* Verifica se é o momento de enviar o placar via MQTT */
if ( (millis() - timestamp_envio_placar) >= TEMPO_PARA_ENVIAR_PLACAR_MQTT )
{
sprintf(placar_mqtt, "%02d X %02d", pontos_lado_a, pontos_lado_b);
MQTT.publish(MQTT_PUB_TOPIC, placar_mqtt);
timestamp_envio_placar = millis();
}
/* Keep-alive do MQTT */
MQTT.loop();
}
Now, we will see how this project works.
Operation of the IoT Scoreboard ProjectThe project works as follows: after programming NodeMCu (ESP8266), the scoreboard starts to zero, as shown in figure 2.
When pressing the key to increase the score of side / team A by one, the score is updated, as shown in figure 3.
When pressing the key to increase the score of side / team B by one, the score is updated, as shown in figure 4.
If you use any MQTT client, connect to the project's broker (broker.hivemq.com, port 1883) and subscribe to the topic mqtt_projeto_placar, you will be able to track your score on the Internet.
Figure 5 shows this monitoring using the MQTT client called MQTTLens.
Finally, when the scoreboard reset / restart key is pressed, it returns to zero to zero, as shown in figure 6.
Now, I will make available the project files of the Electronic Board PCBGOGO IoT Scoreboard.
PCBGOGO IoT Scoreboard Printed Circuit Board ConstructionFor this project, you can use a nodemcu - ESP8266. In addition, all buttons and the display will be connected to the board structure.
The 3D design of the printed circuit board is shown below.
This printed circuit board was developed from the electronic diagram below.
Finally, we present the structure of the printed circuit board in the 2D view.
If you want to assemble your PCBGOGO IoT Scoreboard, you can download the filesand purchase your test plates for free at PCBGOGO.
AcknowledgmentSilício Lab thanks to the support of PCBGOGOfor offering free test PCB's for the assembly of this project. You can also obtain your test PCB's at PCBGOGO.
Comments