A CGRAM tem exactamente 8 slots (0–7). Cada slot guarda um bitmap de 5×8 pixels: 8 bytes onde apenas os 5 bits menos significativos de cada byte definem as colunas. Uma vez registado com createChar(slot, array), o char é impresso com lcd.write(slot).
lcd.createChar() antes de lcd.init(), ou pelo menos antes do primeiro lcd.print(). Reinicializar o LCD pode apagar a CGRAM.Para uma interface de controlo de motores os ícones mais úteis são: seta de direção, engrenagem de estado, bloco de barra de velocidade e símbolo de grau para ângulo de servo.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// Slot 0 — Seta direita → (FORWARD)
byte fwd[8] = {
0b00000, 0b00100, 0b00110,
0b11111, 0b11111, 0b00110,
0b00100, 0b00000
};
// Slot 1 — Seta esquerda ← (BACKWARD)
byte bwd[8] = {
0b00000, 0b00100, 0b01100,
0b11111, 0b11111, 0b01100,
0b00100, 0b00000
};
// Slot 2 — Engrenagem ⚙ (motor activo)
byte gear[8] = {
0b01110, 0b11111, 0b11011,
0b11111, 0b01110, 0b11111,
0b11011, 0b01110
};
// Slot 3 — Círculo ○ (motor parado)
byte stop_ic[8] = {
0b01110, 0b10001, 0b10001,
0b10001, 0b10001, 0b10001,
0b01110, 0b00000
};
// Slot 4 — Grau ° (ângulo servo)
byte grau[8] = {
0b00110, 0b01001, 0b01001,
0b00110, 0b00000, 0b00000,
0b00000, 0b00000
};
// Slot 5 — Bloco cheio █ (barra velocidade)
byte bloco[8] = {
0b11111, 0b11111, 0b11111,
0b11111, 0b11111, 0b11111,
0b11111, 0b11111
};
// Slot 6 — Meio bloco ▌ (resolução de barra)
byte meio[8] = {
0b11000, 0b11000, 0b11000,
0b11000, 0b11000, 0b11000,
0b11000, 0b11000
};
// Slot 7 — Alerta ⚠ (situação de emergência)
byte alerta[8] = {
0b00100, 0b01110, 0b01110,
0b11111, 0b10001, 0b11111,
0b00000, 0b00100
};
void registarIcones() {
lcd.createChar(0, fwd); // → FWD
lcd.createChar(1, bwd); // ← BWD
lcd.createChar(2, gear); // ⚙ activo
lcd.createChar(3, stop_ic); // ○ parado
lcd.createChar(4, grau); // ° grau
lcd.createChar(5, bloco); // █ barra
lcd.createChar(6, meio); // ▌ meia barra
lcd.createChar(7, alerta); // ⚠ alerta
}
void setup() {
registarIcones(); // ANTES de lcd.init()
lcd.init(); lcd.backlight();
}// Barra de 60 posições em 20 chars — usa slots 5 (█) e 6 (▌)
void barraVelocidade(int linha, int vel255) {
// vel255: 0–255 → mapear para 0–60 posições
int pos60 = vel255 * 60 / 255;
int cheios = pos60 / 3; // blocos █ completos
int resto = pos60 % 3; // 0=nada, 1=▌, 2=█ parcial
lcd.setCursor(0, linha);
for (int i = 0; i < 20; i++) {
if (i < cheios) lcd.write(5); // █ cheio
else if (i == cheios && resto == 1) lcd.write(6); // ▌ meio
else if (i == cheios && resto == 2) lcd.write(5); // █ (2/3)
else lcd.write(32); // espaço
}
}
// Uso no loop:
// barraVelocidade(2, velocidade); // linha 2, velocidade 0-255#include <AFMotor.h>
AF_DCMotor motor(1);
bool forward = true;
int vel = 0;
bool parado = true;
void dashboardMotor(int anguloServo) {
// Linha 0 — direção com ícone
lcd.setCursor(0, 0);
if (parado) {
lcd.write(3); // ○ parado
lcd.print(" Motor: PARADO ");
} else {
lcd.write(forward ? 0 : 1); // → ou ←
lcd.print(forward ? " Motor: FORWARD "
: " Motor: BACKWARD ");
}
// Linha 1 — velocidade numérica
lcd.setCursor(0, 1);
lcd.write(2); // ⚙
lcd.print(" Vel: "); lcd.print(vel);
lcd.print(" ("); lcd.print(vel*100/255);
lcd.print("%)"); lcd.print(" ");
// Linha 2 — barra de velocidade
barraVelocidade(2, vel);
// Linha 3 — servo e alerta
lcd.setCursor(0, 3);
lcd.print("Sv: "); lcd.print(anguloServo);
lcd.write(4); // °
if (vel > 220) {
lcd.print(" "); lcd.write(7); // ⚠ velocidade alta
lcd.print(" VEL. ALTA! ");
} else {
lcd.print(" OK ");
}
}