WIP: trabalho local antes de acertar SSH

This commit is contained in:
master 2025-12-28 22:36:05 +00:00
parent 09e5be3233
commit 87afe944bc
10 changed files with 195 additions and 176 deletions

0
assoc Normal file
View File

0
auth Normal file
View File

0
cd Normal file
View File

0
git Normal file
View File

View File

@ -2,26 +2,33 @@
// TOP = 0x71 | BOTTOM = 0x70
// I2C_PORT = I2C_NUM_0
//
// NOTA: agora o código NÃO engole erros de I2C.
// display_init() devolve esp_err_t e podes usar ESP_ERROR_CHECK(display_init()).
// MODO "SAFE":
// - Se I2C não estiver instalado, ou se não existir display, ignora tudo (sem spam)
// - display_init() NUNCA aborta o firmware (devolve ESP_OK e deixa display disabled)
#include "driver/i2c.h"
#include "esp_log.h"
#include "esp_err.h"
#include "display.h"
#include <stdint.h>
#include <stdbool.h>
#define I2C_PORT I2C_NUM_0
// Endereços reais
#define DISP_TOP_ADDR 0x71 // display superior
#define DISP_BOTTOM_ADDR 0x70 // display inferior
static uint16_t rotate180(uint16_t m);
static uint16_t charset(char c);
#define DISP_TOP_ADDR 0x71
#define DISP_BOTTOM_ADDR 0x70
static const char *TAG = "DISPLAY";
// =======================================================
// FLAG GLOBAL
// =======================================================
static bool s_display_enabled = false;
void display_set_enabled(bool en) { s_display_enabled = en; }
bool display_is_enabled(void) { return s_display_enabled; }
// =======================================================
// MAPA DE SEGMENTOS (igual ao teu)
// =======================================================
@ -48,11 +55,20 @@ static const char *TAG = "DISPLAY";
#define SEG_G (SEG_ML | SEG_MR)
static uint16_t charset(char c);
// =======================================================
// FUNÇÕES GENÉRICAS PARA QUALQUER DISPLAY (AGORA COM ERRO)
// FUNÇÕES I2C (internas)
// =======================================================
static inline bool i2c_driver_is_installed(void)
{
// truque simples: tenta criar um cmd e dar begin "vazio" é overkill.
// Mais seguro: tenta uma escrita curta e ver se dá INVALID_STATE.
// Aqui vamos só confiar na primeira escrita do display_init().
return true;
}
static esp_err_t disp_send_cmd(uint8_t addr, uint8_t cmd)
{
return i2c_master_write_to_device(I2C_PORT, addr, &cmd, 1, pdMS_TO_TICKS(50));
@ -77,13 +93,17 @@ static esp_err_t disp_clear(uint8_t addr)
return i2c_master_write_to_device(I2C_PORT, addr, buf, sizeof(buf), pdMS_TO_TICKS(50));
}
// =======================================================
// INIT PARA OS DOIS DISPLAYS (TOP 0x71 | BOTTOM 0x70)
// INIT (SAFE)
// =======================================================
esp_err_t display_init(void)
{
// por defeito: desligado
s_display_enabled = false;
// Se o driver I2C não estiver instalado, a primeira chamada vai dar INVALID_STATE.
// Nós tratamos isso como "não há display nesta board" e seguimos sem logs.
const uint8_t cmd1 = 0x21; // oscillator ON
const uint8_t cmd2 = 0x81; // display ON, blink OFF
const uint8_t cmd3 = 0xEF; // brightness MAX
@ -92,111 +112,207 @@ esp_err_t display_init(void)
// TOP
err = disp_send_cmd(DISP_TOP_ADDR, cmd1);
if (err != ESP_OK) { ESP_LOGE(TAG, "TOP 0x%02X cmd1 falhou: %s", DISP_TOP_ADDR, esp_err_to_name(err)); return err; }
if (err == ESP_ERR_INVALID_STATE) return ESP_OK; // I2C não instalado -> ignora
if (err != ESP_OK) return ESP_OK; // display ausente/cabos -> ignora
err = disp_send_cmd(DISP_TOP_ADDR, cmd2);
if (err != ESP_OK) { ESP_LOGE(TAG, "TOP 0x%02X cmd2 falhou: %s", DISP_TOP_ADDR, esp_err_to_name(err)); return err; }
if (err != ESP_OK) return ESP_OK;
err = disp_send_cmd(DISP_TOP_ADDR, cmd3);
if (err != ESP_OK) { ESP_LOGE(TAG, "TOP 0x%02X cmd3 falhou: %s", DISP_TOP_ADDR, esp_err_to_name(err)); return err; }
if (err != ESP_OK) return ESP_OK;
err = disp_clear(DISP_TOP_ADDR);
if (err != ESP_OK) { ESP_LOGE(TAG, "TOP 0x%02X clear falhou: %s", DISP_TOP_ADDR, esp_err_to_name(err)); return err; }
if (err != ESP_OK) return ESP_OK;
// BOTTOM
err = disp_send_cmd(DISP_BOTTOM_ADDR, cmd1);
if (err != ESP_OK) { ESP_LOGE(TAG, "BOTTOM 0x%02X cmd1 falhou: %s", DISP_BOTTOM_ADDR, esp_err_to_name(err)); return err; }
if (err != ESP_OK) return ESP_OK;
err = disp_send_cmd(DISP_BOTTOM_ADDR, cmd2);
if (err != ESP_OK) { ESP_LOGE(TAG, "BOTTOM 0x%02X cmd2 falhou: %s", DISP_BOTTOM_ADDR, esp_err_to_name(err)); return err; }
if (err != ESP_OK) return ESP_OK;
err = disp_send_cmd(DISP_BOTTOM_ADDR, cmd3);
if (err != ESP_OK) { ESP_LOGE(TAG, "BOTTOM 0x%02X cmd3 falhou: %s", DISP_BOTTOM_ADDR, esp_err_to_name(err)); return err; }
if (err != ESP_OK) return ESP_OK;
err = disp_clear(DISP_BOTTOM_ADDR);
if (err != ESP_OK) { ESP_LOGE(TAG, "BOTTOM 0x%02X clear falhou: %s", DISP_BOTTOM_ADDR, esp_err_to_name(err)); return err; }
if (err != ESP_OK) return ESP_OK;
// OK -> ativa display
s_display_enabled = true;
ESP_LOGI(TAG, "📟 Displays OK: TOP=0x%02X BOTTOM=0x%02X", DISP_TOP_ADDR, DISP_BOTTOM_ADDR);
return ESP_OK;
}
// =======================================================
// RAW / CLEAR (com guard)
// =======================================================
// =======================================================
// RAW PARA TOP E BOTTOM
// =======================================================
void display_raw_top(int pos, uint16_t mask)
{
if (!s_display_enabled) return;
esp_err_t err = disp_raw(DISP_TOP_ADDR, pos, mask);
if (err != ESP_OK) {
ESP_LOGE(TAG, "raw_top falhou (pos=%d addr=0x%02X): %s", pos, DISP_TOP_ADDR, esp_err_to_name(err));
// se o driver desaparecer a meio, desativa e cala
if (err == ESP_ERR_INVALID_STATE) s_display_enabled = false;
// opcional: não logar para evitar spam
// ESP_LOGE(TAG, "raw_top falhou: %s", esp_err_to_name(err));
}
}
void display_raw_bottom(int pos, uint16_t mask)
{
if (!s_display_enabled) return;
esp_err_t err = disp_raw(DISP_BOTTOM_ADDR, pos, mask);
if (err != ESP_OK) {
ESP_LOGE(TAG, "raw_bottom falhou (pos=%d addr=0x%02X): %s", pos, DISP_BOTTOM_ADDR, esp_err_to_name(err));
if (err == ESP_ERR_INVALID_STATE) s_display_enabled = false;
// opcional: não logar
}
}
void display_clear_top(void)
{
if (!s_display_enabled) return;
esp_err_t err = disp_clear(DISP_TOP_ADDR);
if (err != ESP_OK) ESP_LOGE(TAG, "clear_top falhou: %s", esp_err_to_name(err));
if (err != ESP_OK) {
if (err == ESP_ERR_INVALID_STATE) s_display_enabled = false;
}
}
void display_clear_bottom(void)
{
if (!s_display_enabled) return;
esp_err_t err = disp_clear(DISP_BOTTOM_ADDR);
if (err != ESP_OK) ESP_LOGE(TAG, "clear_bottom falhou: %s", esp_err_to_name(err));
if (err != ESP_OK) {
if (err == ESP_ERR_INVALID_STATE) s_display_enabled = false;
}
}
// =====================
// ROTATE 180 para 14 segmentos REAL
// (mapa correto para o teu HT16K33)
// =====================
static uint16_t rotate180(uint16_t m)
{
uint16_t r = 0;
// A (topo) <-> D (baixo)
if (m & SEG_A) r |= SEG_D;
if (m & SEG_D) r |= SEG_A;
// B (top-right) <-> E (bottom-left)
if (m & SEG_B) r |= SEG_E;
if (m & SEG_E) r |= SEG_B;
// C (bottom-right) <-> F (top-left)
if (m & SEG_C) r |= SEG_F;
if (m & SEG_F) r |= SEG_C;
// Meio vertical ML ↔ MR
if (m & SEG_ML) r |= SEG_MR;
if (m & SEG_MR) r |= SEG_ML;
// Alfanuméricos topo TL/TM/TR ↔ BL/BM/BR
if (m & SEG_TL) r |= SEG_BL;
if (m & SEG_BL) r |= SEG_TL;
if (m & SEG_TM) r |= SEG_BM;
if (m & SEG_BM) r |= SEG_TM;
if (m & SEG_TR) r |= SEG_BR;
if (m & SEG_BR) r |= SEG_TR;
// DP é DP (ponto)
if (m & SEG_DP) r |= SEG_DP;
return r;
}
// =======================================================
// CHARSET — mantido EXACTAMENTE como o teu
// TEXTO / CHARS
// =======================================================
void display_char_top(int pos, char c)
{
if (!s_display_enabled) return;
display_raw_top(pos, charset(c));
}
void display_char_bottom(int pos, char c)
{
if (!s_display_enabled) return;
display_raw_bottom(pos, charset(c));
}
void display_text_top(const char *txt)
{
if (!s_display_enabled) return;
for (int i = 0; i < 4; i++) {
char c = (txt && txt[i]) ? txt[i] : ' ';
display_char_top(i, c);
}
}
void display_text_bottom(const char *txt)
{
if (!s_display_enabled) return;
for (int i = 0; i < 4; i++) {
char c = (txt && txt[i]) ? txt[i] : ' ';
display_char_bottom(i, c);
}
}
// =======================================================
// NÚMEROS
// =======================================================
static const uint16_t digit_mask[10] =
{
[0] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,
[1] = SEG_B | SEG_C,
[2] = SEG_A | SEG_B | SEG_G | SEG_E | SEG_D,
[3] = SEG_A | SEG_B | SEG_G | SEG_C | SEG_D,
[4] = SEG_F | SEG_G | SEG_B | SEG_C,
[5] = SEG_A | SEG_F | SEG_G | SEG_C | SEG_D,
[6] = SEG_A | SEG_F | SEG_G | SEG_E | SEG_D | SEG_C,
[7] = SEG_A | SEG_B | SEG_C,
[8] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,
[9] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G,
};
void display_digit_top(int pos, uint8_t val)
{
if (!s_display_enabled) return;
if (val > 9) val = 0;
display_raw_top(pos, digit_mask[val]);
}
void display_digit_bottom(int pos, uint8_t val)
{
if (!s_display_enabled) return;
if (val > 9) val = 0;
display_raw_bottom(pos, digit_mask[val]);
}
void display_number_top(int num)
{
if (!s_display_enabled) return;
if (num < 0) num = 0;
if (num > 9999) num = 9999;
display_digit_top(3, (uint8_t)(num % 10));
display_digit_top(2, (uint8_t)((num / 10) % 10));
display_digit_top(1, (uint8_t)((num / 100) % 10));
display_digit_top(0, (uint8_t)((num / 1000) % 10));
}
void display_number_bottom(int num)
{
if (!s_display_enabled) return;
if (num < 0) num = 0;
if (num > 9999) num = 9999;
display_digit_bottom(3, (uint8_t)(num % 10));
display_digit_bottom(2, (uint8_t)((num / 10) % 10));
display_digit_bottom(1, (uint8_t)((num / 100) % 10));
display_digit_bottom(0, (uint8_t)((num / 1000) % 10));
}
// =======================================================
// RELÓGIO (HH:MM com DP no meio)
// =======================================================
void display_set_time_top(int horas, int minutos)
{
if (!s_display_enabled) return;
if (horas < 0) horas = 0;
if (horas > 99) horas = 99;
if (minutos < 0) minutos = 0;
if (minutos > 59) minutos = 59;
int h1 = horas / 10;
int h2 = horas % 10;
int m1 = minutos / 10;
int m2 = minutos % 10;
uint16_t mid = digit_mask[h2] | SEG_DP;
display_digit_top(0, (uint8_t)h1);
display_raw_top(1, mid);
display_digit_top(2, (uint8_t)m1);
display_digit_top(3, (uint8_t)m2);
}
// =======================================================
// CHARSET (igual ao teu)
// =======================================================
static uint16_t charset(char c)
@ -215,7 +331,7 @@ static uint16_t charset(char c)
case '8': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
case '9': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G;
// LETRAS COMPLETAS (mantidas)
// LETRAS
case 'A': case 'a': return SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G;
case 'B': case 'b': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
case 'C': case 'c': return SEG_A | SEG_F | SEG_E | SEG_D;
@ -253,110 +369,10 @@ static uint16_t charset(char c)
}
}
// =======================================================
// DISPLAY CARACTER / TEXTO
// =======================================================
void display_char_top(int pos, char c)
void display_debug_segment(uint16_t bitmask)
{
display_raw_top(pos, charset(c));
}
void display_char_bottom(int pos, char c)
{
display_raw_bottom(pos, charset(c));
}
void display_text_top(const char *txt)
{
for (int i = 0; i < 4; i++) {
char c = (txt && txt[i]) ? txt[i] : ' ';
display_char_top(i, c);
}
}
void display_text_bottom(const char *txt)
{
for (int i = 0; i < 4; i++) {
char c = (txt && txt[i]) ? txt[i] : ' ';
display_char_bottom(i, c);
}
}
// =======================================================
// DISPLAY DE NÚMEROS (igual ao teu)
// =======================================================
static const uint16_t digit_mask[10] =
{
[0] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,
[1] = SEG_B | SEG_C,
[2] = SEG_A | SEG_B | SEG_G | SEG_E | SEG_D,
[3] = SEG_A | SEG_B | SEG_G | SEG_C | SEG_D,
[4] = SEG_F | SEG_G | SEG_B | SEG_C,
[5] = SEG_A | SEG_F | SEG_G | SEG_C | SEG_D,
[6] = SEG_A | SEG_F | SEG_G | SEG_E | SEG_D | SEG_C,
[7] = SEG_A | SEG_B | SEG_C,
[8] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,
[9] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G,
};
void display_digit_top(int pos, uint8_t val)
{
if (val > 9) val = 0;
display_raw_top(pos, digit_mask[val]);
}
void display_digit_bottom(int pos, uint8_t val)
{
if (val > 9) val = 0;
display_raw_bottom(pos, digit_mask[val]);
}
void display_number_top(int num)
{
if (num < 0) num = 0;
if (num > 9999) num = 9999;
display_digit_top(3, num % 10);
display_digit_top(2, (num / 10) % 10);
display_digit_top(1, (num / 100) % 10);
display_digit_top(0, (num / 1000) % 10);
}
void display_number_bottom(int num)
{
if (num < 0) num = 0;
if (num > 9999) num = 9999;
display_digit_bottom(3, num % 10);
display_digit_bottom(2, (num / 10) % 10);
display_digit_bottom(1, (num / 100) % 10);
display_digit_bottom(0, (num / 1000) % 10);
}
// =======================================================
// display_set_time_top() — mantém SEG_DP no sítio certo
// =======================================================
void display_set_time_top(int horas, int minutos)
{
if (horas < 0) horas = 0;
if (horas > 99) horas = 99;
if (minutos < 0) minutos = 0;
if (minutos > 59) minutos = 59;
int h1 = horas / 10;
int h2 = horas % 10;
int m1 = minutos / 10;
int m2 = minutos % 10;
uint16_t mid = digit_mask[h2] | SEG_DP;
display_digit_top(0, (uint8_t)h1);
display_raw_top(1, mid);
display_digit_top(2, (uint8_t)m1);
display_digit_top(3, (uint8_t)m2);
(void)bitmask;
// opcional: podes mostrar um padrão fixo se display estiver ativo
// if (!display_is_enabled()) return;
// display_raw_top(0, bitmask);
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include <stdbool.h>
// =======================================================
// ENDEREÇOS I2C (definidos no .c)
@ -19,6 +20,8 @@ void display_raw_bottom(int pos, uint16_t mask);
void display_clear_top(void);
void display_clear_bottom(void);
void display_set_enabled(bool en);
bool display_is_enabled(void);
// =======================================================
// TEXTO E CARACTERES

View File

@ -208,7 +208,7 @@ void app_main(void) {
printf("i2c_init falhou: %s\n", esp_err_to_name(ei));
}
// 2) Scan I2C (ver o que existe no barramento)
// 2) Scan I2C (ver o que existe no barramento)
i2c_scan();
// 3) Inicializa displays SEM abortar (para não rebootar em loop)

0
prof Normal file
View File

0
run Normal file
View File

0
sta Normal file
View File