/*
Exemplo de um painel de senhas para atendimento utilizando
barramento I2C por software com SCL compartilhado e UMA Franzininho DIY.
by Anderson Costa
Inspirado nos exemplos da comunidade Wokwi
https://wokwi.com/arduino/projects/307841659182252608 by Koepel
https://wokwi.com/arduino/projects/307849131382014528 by sutaburosu
*/
#include <SoftwareWire.h>
#include <TinyDebug.h>
#include "LiquidCrystal_I2C.h"
#define MAX_PWDS 9999 // Define a senha máxima para reset automático
#define MAX_OUTS 6 // Define o número máximo de Guichês
// Cria três barramentos I2C de software
SoftwareWire wire[] = {
SoftwareWire(PB1, SCL),
SoftwareWire(PB3, SCL),
SoftwareWire(PB5, SCL),
};
// Cria uma instância em vetor para cada tela do LCD
LiquidCrystal_I2C lcd[] = {
LiquidCrystal_I2C(&wire[0], 0x27, 16, 2),
LiquidCrystal_I2C(&wire[0], 0x3F, 16, 2),
LiquidCrystal_I2C(&wire[1], 0x27, 16, 2),
LiquidCrystal_I2C(&wire[1], 0x3F, 16, 2),
LiquidCrystal_I2C(&wire[2], 0x27, 16, 2),
LiquidCrystal_I2C(&wire[2], 0x3F, 16, 2),
};
constexpr auto lcd_count = sizeof(lcd) / sizeof(lcd[0]);
void setup() {
Debug.begin();
// Inicializa os LCDs
for (uint8_t screen = 0; screen < lcd_count; screen++) {
lcd[screen].init();
lcd[screen].backlight();
lcd[screen].setCursor(0, 0);
lcd[screen].print("Info Painel ");
lcd[screen].print(screen + 1);
}
}
char title[] = "SENHA GUICHE";
byte last = 0;
byte position = MAX_OUTS;
// Define vetor de saídas
byte outputs[] = { NULL, 1, 2, 3, 4, 5, 6 };
// Define vetor das saídas que restaram (Reserva todas as saídas)
byte leftover[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
byte firstRandom;
byte disableCount = 0;
void loop() {
static uint16_t pos = 0;
static uint16_t pwd = 0;
uint8_t lscr = 0;
uint8_t screen = (pos / 64) * 2 + ((pos / 16) & 1);
uint8_t column = pos % 16;
uint8_t row = (pos / 32) & 1;
screen %= lcd_count;
if (screen != lscr) {
lscr = screen;
pwd++;
}
// Verifica se está na linha do título
if (row == 0) {
lcd[screen].setCursor(column, row);
lcd[screen].print(title[column]);
} else {
lcd[screen].setCursor(column, row);
// Posição da senha
if (column == 0)
lcd[screen].print(pwd);
// Posição do guichê
if (column == 12) {
lcd[screen].print(0);
// Gera um indice aleatório da lista de saídas
last = outputsRandom(position);
lcd[screen].print(last);
position--; // Decrementa o indice de saídas
}
}
// Reseta senha do dia
if (pwd > MAX_PWDS) {
pwd = 0;
}
pos++;
// Verifica se ultrapassou na última posição
if (pos > 192) {
pos = 0;
position = MAX_OUTS; // Inicializa o indice de saídas
last = 0; // Inicializa a variável do último sorteio
}
}
byte outputsRandom(byte index)
{
byte nr = 0;
byte i = 0;
do // Continua se o número for igual ao do guichê anterior
{
// Pega um número aleatório entre 1 e o indice informado
nr = random(1, index + 2);
} // Verifica se é o primeiro número sorteado
while ((disableCount == 0) &&
(index == (MAX_OUTS - disableCount)) &&
(nr == firstRandom));
// Verifica se é o primeiro número sorteado
if (index == (MAX_OUTS - disableCount)) {
firstRandom = nr;
}
// Percorre a lista de saídas
for (byte s = 1; s <= MAX_OUTS; s++) {
// Verifica se a saída não foi descartada
if (outputs[s] != 0) {
// Adiciona a saída que ainda não foi sorteada na lista
leftover[++i] = outputs[s];
}
}
// Percorre a lista de saídas
for (byte s = 1; s <= MAX_OUTS; s++) {
// Verifica o número que foi sorteado
if (outputs[s] == leftover[nr]) {
// Descarta o número sorteado da lista de saídas
outputs[s] = 0;
}
}
return nr;
}