fix(printer): limit thermal print power draw
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user