Probed remaining frame bytes for Bluetooth/power icons on a blank panel: - found cross-wired glyph dots (byte0 bit7, byte4 bit7) and an I2C timeout (byte8 bit4), but no standalone icon bits - waveshare reference does not drive these icons; likely multi-bit or unpopulated on the glass (cosmetic, deprioritized) - restore temphummon.c as the active firmware build in CMakeLists
216 lines
7.2 KiB
C
216 lines
7.2 KiB
C
/**
|
|
* iconfinder — ESP32-H2 Zero + 1.9" Segment E-Paper (IST7134 via I²C)
|
|
*
|
|
* Hunt the POWER (battery) and BLUETOOTH icons.
|
|
* Per the manual: "13th position = °C/°F, 14th unused, and icons at other
|
|
* positions" — we empirically probe the remaining untested frame bytes on a
|
|
* BLANK background (so a lone icon is unmistakable).
|
|
*
|
|
* Bytes probed (0-indexed):
|
|
* byte 0 bits 5-7 (high bits we never tested; leading-1 glyph is 0-4)
|
|
* byte 2 all 8 (second byte of top-tens pair)
|
|
* byte 6 all 8 (second byte of bottom-tens pair)
|
|
* byte 13 bits 3-7 (after the C/F letter bits 0-2)
|
|
* byte 14 all 8 (manual says unused, confirm)
|
|
* Dangerous black-control bits (byte 2 bit4, byte 4 bit7, byte 13 bit1)
|
|
* are SKIPPED to keep the panel stable.
|
|
*
|
|
* Wiring (GPIO4→SDA, GPIO5→SCL, GPIO10→RST, GPIO11→BUSY, 3V3→VCC, GND→GND)
|
|
*/
|
|
#include <string.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "driver/i2c_master.h"
|
|
#include "driver/gpio.h"
|
|
#include "esp_log.h"
|
|
#include "esp_timer.h"
|
|
|
|
#define PIN_SDA GPIO_NUM_4
|
|
#define PIN_SCL GPIO_NUM_5
|
|
#define PIN_RST GPIO_NUM_10
|
|
#define PIN_BUSY GPIO_NUM_11
|
|
#define PIN_BOOT GPIO_NUM_9
|
|
|
|
#define FRAME_LEN 15
|
|
#define ADVANCE_MODE 1
|
|
#define AUTO_HOLD_MS 3000
|
|
#define BOOT_TIMEOUT_MS 0
|
|
|
|
static i2c_master_bus_handle_t bus;
|
|
static i2c_master_dev_handle_t cmd_dev, dat_dev;
|
|
|
|
static void epd_reset(void)
|
|
{
|
|
gpio_set_level(PIN_RST, 1); vTaskDelay(pdMS_TO_TICKS(200));
|
|
gpio_set_level(PIN_RST, 0); vTaskDelay(pdMS_TO_TICKS(20));
|
|
gpio_set_level(PIN_RST, 1); vTaskDelay(pdMS_TO_TICKS(200));
|
|
}
|
|
|
|
static void epd_wait_busy(void)
|
|
{
|
|
while (gpio_get_level(PIN_BUSY) == 0) vTaskDelay(pdMS_TO_TICKS(1));
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
}
|
|
|
|
static void epd_lut_GC(void)
|
|
{
|
|
uint8_t lut[] = {0x82, 0x20, 0x00, 0xA0, 0x80, 0x40, 0x63};
|
|
i2c_master_transmit(cmd_dev, lut, sizeof(lut), pdMS_TO_TICKS(100));
|
|
}
|
|
static void epd_lut_DU_WB(void)
|
|
{
|
|
uint8_t lut[] = {0x82, 0x80, 0x00, 0xC0, 0x80, 0x80, 0x62};
|
|
i2c_master_transmit(cmd_dev, lut, sizeof(lut), pdMS_TO_TICKS(100));
|
|
}
|
|
|
|
static void epd_temperature(void)
|
|
{
|
|
uint8_t a[] = {0x7E, 0x81, 0xB4};
|
|
i2c_master_transmit(cmd_dev, a, sizeof(a), pdMS_TO_TICKS(100));
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
uint8_t b[] = {0xE7, 0x0E};
|
|
i2c_master_transmit(cmd_dev, b, sizeof(b), pdMS_TO_TICKS(100));
|
|
}
|
|
|
|
static void epd_init(void)
|
|
{
|
|
epd_reset();
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
uint8_t p[] = {0x2B, 0xA7, 0xE0};
|
|
i2c_master_transmit(cmd_dev, p, sizeof(p), pdMS_TO_TICKS(100));
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
epd_temperature();
|
|
}
|
|
|
|
static void epd_write(const uint8_t *frame, uint8_t extra_byte)
|
|
{
|
|
uint8_t w[] = {0xAC, 0x2B, 0x40, 0xA9, 0xA8};
|
|
i2c_master_transmit(cmd_dev, w, sizeof(w), pdMS_TO_TICKS(100));
|
|
i2c_master_transmit(dat_dev, frame, FRAME_LEN, pdMS_TO_TICKS(100));
|
|
i2c_master_transmit(dat_dev, &extra_byte, 1, pdMS_TO_TICKS(100));
|
|
uint8_t d[] = {0xAB, 0xAA, 0xAF};
|
|
i2c_master_transmit(cmd_dev, d, sizeof(d), pdMS_TO_TICKS(100));
|
|
epd_wait_busy();
|
|
uint8_t s[] = {0xAE, 0x28, 0xAD};
|
|
i2c_master_transmit(cmd_dev, s, sizeof(s), pdMS_TO_TICKS(100));
|
|
}
|
|
|
|
/* Show one bit of one byte on a BLANK background. */
|
|
static void show_blank_plus(uint8_t frame_byte, uint8_t phys_bit, int tag)
|
|
{
|
|
uint8_t f[FRAME_LEN];
|
|
memset(f, 0, FRAME_LEN);
|
|
f[frame_byte] |= (uint8_t)(1u << phys_bit);
|
|
ESP_LOGI("iconfinder", "T%d: byte %d bit %d (global %d)",
|
|
tag, frame_byte, phys_bit, frame_byte * 8 + phys_bit);
|
|
|
|
uint8_t off[FRAME_LEN];
|
|
memset(off, 0, FRAME_LEN);
|
|
epd_lut_GC();
|
|
epd_write(off, 0x00);
|
|
epd_lut_DU_WB();
|
|
epd_write(f, 0x00);
|
|
}
|
|
|
|
static void boot_init(void)
|
|
{
|
|
gpio_config_t bc = {
|
|
.pin_bit_mask = (1ULL << PIN_BOOT), .mode = GPIO_MODE_INPUT,
|
|
.pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE,
|
|
.intr_type = GPIO_INTR_DISABLE,
|
|
};
|
|
gpio_config(&bc);
|
|
}
|
|
|
|
static void wait_for_advance(void)
|
|
{
|
|
if (ADVANCE_MODE == 0) { vTaskDelay(pdMS_TO_TICKS(AUTO_HOLD_MS)); return; }
|
|
ESP_LOGI("iconfinder", "press BOOT to advance next...");
|
|
int64_t t_start = esp_timer_get_time();
|
|
for (;;) {
|
|
if (gpio_get_level(PIN_BOOT) == 0) {
|
|
vTaskDelay(pdMS_TO_TICKS(30));
|
|
while (gpio_get_level(PIN_BOOT) == 0) vTaskDelay(pdMS_TO_TICKS(10));
|
|
vTaskDelay(pdMS_TO_TICKS(30));
|
|
break;
|
|
}
|
|
if (BOOT_TIMEOUT_MS > 0 &&
|
|
esp_timer_get_time() - t_start > BOOT_TIMEOUT_MS * 1000LL) {
|
|
ESP_LOGW("iconfinder", "no BOOT press in %d ms", BOOT_TIMEOUT_MS);
|
|
break;
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(20));
|
|
}
|
|
}
|
|
|
|
void app_main(void)
|
|
{
|
|
i2c_master_bus_config_t bc = {
|
|
.i2c_port = -1, .sda_io_num = PIN_SDA, .scl_io_num = PIN_SCL,
|
|
.clk_source = I2C_CLK_SRC_DEFAULT, .glitch_ignore_cnt = 7,
|
|
.flags.enable_internal_pullup = 1,
|
|
};
|
|
ESP_ERROR_CHECK(i2c_new_master_bus(&bc, &bus));
|
|
i2c_device_config_t cc = {
|
|
.dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = 0x3C, .scl_speed_hz = 100000,
|
|
};
|
|
i2c_device_config_t dc = {
|
|
.dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = 0x3D, .scl_speed_hz = 100000,
|
|
};
|
|
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus, &cc, &cmd_dev));
|
|
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus, &dc, &dat_dev));
|
|
|
|
gpio_config_t rc = {
|
|
.pin_bit_mask = (1ULL << PIN_RST), .mode = GPIO_MODE_OUTPUT,
|
|
.pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE,
|
|
.intr_type = GPIO_INTR_DISABLE,
|
|
};
|
|
gpio_config(&rc); gpio_set_level(PIN_RST, 1);
|
|
gpio_config_t busy_cfg = {
|
|
.pin_bit_mask = (1ULL << PIN_BUSY), .mode = GPIO_MODE_INPUT,
|
|
.pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE,
|
|
.intr_type = GPIO_INTR_DISABLE,
|
|
};
|
|
gpio_config(&busy_cfg);
|
|
|
|
boot_init();
|
|
epd_init();
|
|
|
|
ESP_LOGI("iconfinder", "== Probe icon-region bytes on blank panel ==");
|
|
|
|
int tag = 0;
|
|
|
|
/* byte 0 bits 5-7 (leading-1 glyph is bits 0-4; high bits unknown) */
|
|
for (int b = 5; b < 8; b++) { show_blank_plus(0, b, ++tag); wait_for_advance(); }
|
|
|
|
/* byte 4 (top-ones second byte): we know bits 0-3 = glyph right col,
|
|
* bit 5 = decimal. The icon candidates are bits 4,6,7. Probe those. */
|
|
for (int b = 4; b < 8; b++) {
|
|
if (b == 5) continue; /* decimal, already known */
|
|
show_blank_plus(4, b, ++tag); wait_for_advance();
|
|
}
|
|
|
|
/* byte 8 (bottom-ones second byte): same reasoning, bits 4,6,7 */
|
|
for (int b = 4; b < 8; b++) {
|
|
if (b == 5) continue; /* decimal, already known */
|
|
show_blank_plus(8, b, ++tag); wait_for_advance();
|
|
}
|
|
|
|
/* byte 10 (bottom-tenths second byte): bits 4,6,7 (bit5=percent) */
|
|
for (int b = 4; b < 8; b++) {
|
|
if (b == 5) continue; /* percent, already known */
|
|
show_blank_plus(10, b, ++tag); wait_for_advance();
|
|
}
|
|
|
|
/* byte 14 all bits (manual says unused - confirm) */
|
|
for (int b = 0; b < 8; b++) {
|
|
show_blank_plus(14, b, ++tag); wait_for_advance();
|
|
}
|
|
|
|
ESP_LOGI("iconfinder", "== Done. ==");
|
|
uint8_t off[FRAME_LEN];
|
|
memset(off, 0, FRAME_LEN);
|
|
epd_lut_DU_WB();
|
|
epd_write(off, 0x00);
|
|
while (1) vTaskDelay(pdMS_TO_TICKS(1000));
|
|
}
|