fix(printer): limit thermal print power draw

This commit is contained in:
admin
2026-04-30 00:38:05 +08:00
parent d735446547
commit 7e77c6809a
16 changed files with 1372 additions and 10 deletions

View File

@@ -261,6 +261,21 @@ static bool json_get_bool(cJSON *obj, const char *key, bool fallback) {
return fallback;
}
static const char *json_get_print_density(cJSON *obj, const char *key, const char *fallback) {
const char *density = json_get_str(obj, key, fallback);
if (density == NULL || density[0] == '\0') {
return fallback;
}
if (strcmp(density, "test") == 0 ||
strcmp(density, "较淡") == 0 ||
strcmp(density, "中等") == 0 ||
strcmp(density, "较浓") == 0 ||
strcmp(density, "最深") == 0) {
return density;
}
return fallback;
}
static void add_printer_status_fields(cJSON *obj, const printer_runtime_status_t *printer) {
if (obj == NULL || printer == NULL) {
return;
@@ -526,6 +541,7 @@ static void handle_print_pattern(uint32_t seq, const char *name, cJSON *payload)
uint16_t height = (uint16_t)clamp_u32(height_arg, 16, 512);
bool wait_done = json_get_bool(payload, "wait_done", true);
bool ignore_precheck = json_get_bool(payload, "ignore_precheck", false);
const char *density = json_get_print_density(payload, "density", "test");
int64_t start = now_ms();
emit_progress(seq, name, "build_pattern", NULL);
@@ -548,7 +564,7 @@ static void handle_print_pattern(uint32_t seq, const char *name, cJSON *payload)
raster_len,
384,
height,
"test",
density,
&options,
&job_id,
err,
@@ -599,6 +615,7 @@ static void handle_image_generate(uint32_t seq, const char *name, cJSON *payload
bool prompt_extend = json_get_bool(payload, "prompt_extend", true);
bool ignore_precheck = json_get_bool(payload, "ignore_precheck", false);
bool wait_done = json_get_bool(payload, "wait_done", true);
const char *density = json_get_print_density(payload, "density", "test");
int64_t start = now_ms();
emit_progress(seq, name, "image_generate", NULL);
@@ -693,7 +710,7 @@ static void handle_image_generate(uint32_t seq, const char *name, cJSON *payload
raster_len,
raster_width,
raster_height,
"test",
density,
&options,
&job_id,
err,

View File

@@ -21,6 +21,7 @@ static const char *TAG = "voice_interaction";
#define VOICE_IMAGE_MARKER_TEMPLATE \
"%s线稿风格白色背景边缘清晰细节简洁高对比避免大面积阴影和灰度渐变适配热敏打印机打印。"
#define VOICE_IMAGE_MARKER_TEMPLATE_FALLBACK_SUBJECT "简洁卡通图案"
#define VOICE_IMAGE_PRINT_DENSITY "较淡"
typedef struct {
char prompt[VOICE_IMAGE_MARKER_PROMPT_MAX_LEN + 1];
@@ -285,13 +286,13 @@ static esp_err_t voice_submit_marker_generated_image(const voice_image_generatio
uint32_t job_id = 0;
char submit_err[128] = {0};
printer_print_options_t print_opt = {
.ignore_precheck = true,
.ignore_precheck = false,
};
esp_err_t submit_rc = printer_protocol_submit_raster_job_ex(raster,
raster_len,
raster_width,
raster_height,
NULL,
VOICE_IMAGE_PRINT_DENSITY,
&print_opt,
&job_id,
submit_err,

View File

@@ -159,6 +159,7 @@ uint32_t runtime_policy_printer_stop_timeout_ms(void);
uint32_t runtime_policy_printer_operation_timeout_ms(void);
uint16_t runtime_policy_printer_strobe_on_us(void);
uint16_t runtime_policy_printer_strobe_interval_us(void);
uint16_t runtime_policy_printer_max_dots_per_pulse(void);
uint16_t runtime_policy_printer_motor_step_us(void);
bool runtime_policy_printer_motor_reverse(void);
uint8_t runtime_policy_printer_steps_per_line(void);

View File

@@ -46,6 +46,10 @@
#define CONFIG_TQ_PRINTER_STROBE_INTERVAL_US 200
#endif
#ifndef CONFIG_TQ_PRINTER_MAX_DOTS_PER_PULSE
#define CONFIG_TQ_PRINTER_MAX_DOTS_PER_PULSE 32
#endif
#ifndef CONFIG_TQ_PRINTER_MOTOR_STEP_US
#define CONFIG_TQ_PRINTER_MOTOR_STEP_US 2000
#endif
@@ -95,7 +99,7 @@
#endif
#ifndef CONFIG_TQ_PRINTER_NTC_R25_OHMS
#define CONFIG_TQ_PRINTER_NTC_R25_OHMS 10000
#define CONFIG_TQ_PRINTER_NTC_R25_OHMS 30000
#endif
#ifndef CONFIG_TQ_PRINTER_NTC_BETA
@@ -193,6 +197,10 @@ uint16_t runtime_policy_printer_strobe_interval_us(void) {
return clamp_u16((uint16_t)CONFIG_TQ_PRINTER_STROBE_INTERVAL_US, 0, 10000);
}
uint16_t runtime_policy_printer_max_dots_per_pulse(void) {
return clamp_u16((uint16_t)CONFIG_TQ_PRINTER_MAX_DOTS_PER_PULSE, 1, 384);
}
uint16_t runtime_policy_printer_motor_step_us(void) {
return clamp_u16((uint16_t)CONFIG_TQ_PRINTER_MOTOR_STEP_US, 100, 20000);
}

View File

@@ -32,7 +32,7 @@ static const uint16_t k_latch_pulse_us = 1;
static const bool k_strobe_active_high = true;
static const char *TAG = "printer_driver";
static const uint8_t k_print_yield_lines = 8;
static const uint8_t k_print_yield_lines = 1;
static const uint16_t k_gap_yield_steps = 32;
typedef struct {
@@ -196,6 +196,84 @@ static void fire_strobe_locked(uint16_t on_us, uint16_t interval_us) {
fire_strobe_pin_locked(CONFIG_TQ_PRINT_STB_56_PIN, on_us, interval_us);
}
static void write_line_bits_locked(const uint8_t *line, size_t line_bytes);
static uint16_t count_black_dots(const uint8_t *line, size_t line_bytes) {
uint16_t count = 0;
for (size_t i = 0; i < line_bytes; ++i) {
count = (uint16_t)(count + (uint16_t)__builtin_popcount((unsigned int)line[i]));
}
return count;
}
static bool build_black_dot_slice(const uint8_t *line,
size_t line_bytes,
uint16_t first_black_dot,
uint16_t max_black_dots,
uint8_t *out_line) {
if (line == NULL || out_line == NULL || max_black_dots == 0) {
return false;
}
memset(out_line, 0, line_bytes);
uint16_t seen = 0;
uint16_t copied = 0;
for (size_t i = 0; i < line_bytes; ++i) {
uint8_t byte = line[i];
if (byte == 0) {
continue;
}
for (int bit = 7; bit >= 0; --bit) {
uint8_t mask = (uint8_t)(1u << bit);
if ((byte & mask) == 0) {
continue;
}
if (seen >= first_black_dot) {
out_line[i] |= mask;
copied++;
if (copied >= max_black_dots) {
return true;
}
}
seen++;
}
}
return copied > 0;
}
static uint16_t print_line_with_power_limit_locked(const uint8_t *line,
size_t line_bytes,
uint16_t black_dots,
uint16_t max_dots_per_pulse,
uint16_t strobe_on_us,
uint16_t strobe_interval_us) {
if (black_dots == 0) {
write_line_bits_locked(line, line_bytes);
pulse_latch_locked();
return 0;
}
if (max_dots_per_pulse >= black_dots) {
write_line_bits_locked(line, line_bytes);
pulse_latch_locked();
fire_strobe_locked(strobe_on_us, strobe_interval_us);
return 1;
}
uint8_t limited_line[48];
uint16_t subpulses = 0;
for (uint16_t first = 0; first < black_dots; first = (uint16_t)(first + max_dots_per_pulse)) {
if (!build_black_dot_slice(line, line_bytes, first, max_dots_per_pulse, limited_line)) {
break;
}
write_line_bits_locked(limited_line, line_bytes);
pulse_latch_locked();
fire_strobe_locked(strobe_on_us, strobe_interval_us);
subpulses++;
}
return subpulses;
}
static void motor_step_once_locked(uint16_t step_delay_us) {
uint8_t pattern = k_motor_step_table[s_state.motor_phase & 0x03];
motor_apply_pattern_locked(pattern);
@@ -618,6 +696,7 @@ esp_err_t platform_printer_print(const platform_printer_print_request_t *request
uint8_t steps_per_line = (request->motor_steps_per_line > 0)
? request->motor_steps_per_line
: runtime_policy_printer_steps_per_line();
uint16_t max_dots_per_pulse = runtime_policy_printer_max_dots_per_pulse();
rc = ESP_OK;
if (!request->ignore_precheck) {
@@ -630,6 +709,9 @@ esp_err_t platform_printer_print(const platform_printer_print_request_t *request
}
int64_t deadline_ms = (int64_t)(esp_timer_get_time() / 1000) + (int64_t)timeout_ms;
uint32_t power_limited_lines = 0;
uint32_t total_subpulses = 0;
uint16_t max_black_dots = 0;
for (uint16_t line = 0; line < request->height; ++line) {
if (request->cancel_flag != NULL && *request->cancel_flag) {
@@ -651,9 +733,20 @@ esp_err_t platform_printer_print(const platform_printer_print_request_t *request
}
const uint8_t *line_ptr = &request->raster[(size_t)line * line_bytes];
write_line_bits_locked(line_ptr, line_bytes);
pulse_latch_locked();
fire_strobe_locked(strobe_on_us, strobe_interval_us);
uint16_t black_dots = count_black_dots(line_ptr, line_bytes);
if (black_dots > max_black_dots) {
max_black_dots = black_dots;
}
uint16_t subpulses = print_line_with_power_limit_locked(line_ptr,
line_bytes,
black_dots,
max_dots_per_pulse,
strobe_on_us,
strobe_interval_us);
if (subpulses > 1) {
power_limited_lines++;
}
total_subpulses += subpulses;
for (uint8_t i = 0; i < steps_per_line; ++i) {
if (request->cancel_flag != NULL && *request->cancel_flag) {
@@ -679,6 +772,14 @@ esp_err_t platform_printer_print(const platform_printer_print_request_t *request
}
safe_drive_off_locked(true);
if (power_limited_lines > 0) {
ESP_LOGI(TAG,
"print power limited: lines=%u max_black_dots=%u max_dots_per_pulse=%u subpulses=%u",
(unsigned)power_limited_lines,
(unsigned)max_black_dots,
(unsigned)max_dots_per_pulse,
(unsigned)total_subpulses);
}
xSemaphoreGive(s_state.lock);
return rc;
}