科技不总是冷冰冰,智能便携打印机让文字更有温度!——嵌入式功能实现篇

本文主要是介绍科技不总是冷冰冰,智能便携打印机让文字更有温度!——嵌入式功能实现篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简介:利用Wi-Fi&BLE云模组让普通打印机实现App端输入文字即可实时&远程打印。

1、功能需求

基本打印流程:
(1)用户通过涂鸦App完成设备配网。
(2)将需要打印的文字或者简单图片输入文本框。
(3)输入完成后APP将文本框中的内容全部转换成bmp单色位图存储到云端,并将图片的url给到App。
(4)分别将第一段url和第二段url下发到设备,设备端将url拼接。
(5)设置打印份数后点击开始打印。
(6)设备收到打印命令后访问url,下载图片到模组后开始打印。
(7)返回打印结果(成功/失败)。

功能名称功能详解
打印份数设置具体要打印的份数,默认一份
生成第一段链接APP生成的图片会存储到云端,以url形式给到设备,url长度超过255个字节,由于IoT平台string类型DP最多只能容纳255个字节,因此需要两个DP
生成第二段链接将得到的两组url拼接成完整的url,设备访问该url将图片下载到模组
开始打印设置好打印份数,下发打印命令,设备开始打印
正在打印份数根据设置的打印份数,设备会上报当前正在打印的份数在APP显示
缺纸报警检测到打印机无纸时无法打印,并上报缺纸状态到APP
电量显示电量以10%间隔显示在APP
低电量报警电量在低于10%时APP电量报警
打印结果
打印状态打印成功、打印失败(缺纸、电池电量不足、图片拉去失败)

2、环境搭建

(1)开发环境搭建可以参考Wi-Fi 模组二次开发教程——1. SoC开发环境搭建。如果已经有虚拟机和乌班图的开发环境可直接跳至4.2 下载编译依赖工具处进行剩余环境搭建。
(2)产品创建可以参考Wi-Fi模组二次开发教程——2. 涂鸦IoT平台介绍。由于IoT平台品类中没有打印机,所以选择找不到品类?。默认自定义方案,填入产品名称和产品型号,通讯协议默认Wi-Fi&蓝牙,创建产品后添加所需的功能点。
在这里插入图片描述
功能点定义完成后选择设备面板为自由配置面板,其他步骤均可参考Wi-Fi模组二次开发教程——2. 涂鸦IoT平台介绍完成产品创建。
(3)参考Wi-Fi模组二次开发课程——3. 快速上手来完成代码修改编译、固件上传、获取token、烧录授权和设备配网。

3、功能实现

3.1 打印功能

打印是打印机最重要的功能点,其他所有的功能点都是在此基础上实现的。通过步进电机和热敏头的协同工作来实现字符和图片的打印。
打印头与涂鸦Wi-Fi&BLE双模模组之间通过SPI进行数据通信,具体打印步骤为:
(1)模组每次向热敏头发送48位数据。
(2)热敏头接收到数据后在CLK信号的上升沿将数据传输到移位寄存器。
(3)一行(48位)数据传输完成后将/LAT信号拉低紧接着拉高把数据存储到锁存寄存器中。
(4)将DST信号拉高激活打印元件,接着使步进电机转动两步完成一点行的打印。
(5)在电机转过两步后要及时将DST信号拉低,长时间加热会损坏加热元件甚至冒烟。
在这里插入图片描述

步进电机的步长为0.01325mm,一点行的宽度为0.0625mm,因此打印出一点行的数据需要步进电机转两步。DST信号激活频率为步进电机每转两步激活一次。
打印时序图如下:
在这里插入图片描述
步进电机时序图如下:
在这里插入图片描述

打印功能部示例代码:

/**
* @function:set_motor_phases
* @brief: set motor phases
* @param[in]: phase[4]
* @return: none
*/
STATIC VOID set_motor_phases(CONST UINT8_T phase[4])
{tuya_gpio_write(PH1, phase[0]);tuya_gpio_write(PH2, phase[1]);tuya_gpio_write(PH3, phase[2]);tuya_gpio_write(PH4, phase[3]);
}
/**
* @function:idle_motor
* @brief: idel status no power consumption
* @param[in]: none
* @return: none
*/
VOID idle_motor(VOID)
{if (isIdle) {return;}CONST UINT8_T idle_phase[] = {0, 0, 0, 0};set_motor_phases(idle_phase);isIdle = TRUE;
}
/**
* @function:set_motor_step
* @brief: 
* @param[in]: none
* @return: none
*/
VOID set_motor_step(VOID) 
{isIdle = false;CONST INT_T totalSteps = ARRAY_SIZE;currStep = (currStep + totalSteps + 1) % totalSteps;set_motor_phases(motor_phases[currStep]);
}
/**
* @function:tuya_motor_feedPaper_line
* @brief: Feed paper for `count` lines
* @param[in]: count -> print line num
* @param[in]: direction: FORWARD->forward  BACKWARD->backward
* @param[in]: speed: Adjust the motor speed unit:ms
* @return: none
*/
VOID tuya_motor_feedPaper_line(UINT_T count, INT8_T direction, UINT8_T speed)
{INT_T i;/* out of paper or cuont num is 0 return */if (1 == tuya_TmlHead_out_of_paper_alarm() || 0 == count) {return;}CONST UINT_T stepsPerLine = ARRAY_SIZE / 2;for (i = 0; i < (stepsPerLine * count); i++) {tuya_motor_set_motor_step(direction);tuya_hal_system_sleep(speed);	// delay 2ms unit:ms}
}
/**
* @function:begin_print_line
* @brief: send 1bpp data to printhead and begin heating
* @param[in]: data -> The data to be printed
* @return: none
*/
STATIC VOID begin_print_line(UINT8_T* data)
{if (1 == out_of_paper_alarm()) {return;}bk_spi_master_dma_send(&g_spi_msg);tuya_hal_system_sleep(5);tuya_gpio_write(PRINT_DST, FALSE);tuya_gpio_write(PRINT_LAT, FALSE);tuya_gpio_write(PRINT_LAT, TRUE);tuya_gpio_write(PRINT_DST, TRUE);
}
/**
* @function:end_printLine
* @brief:end heating 
* @param[in]: none
* @return: none
*/
STATIC VOID end_printLine(VOID)
{tuya_gpio_write(PRINT_DST, FALSE);
}
/**
* @function: print_1bLine
* @brief: Print one line of data
* @param[in]: data -> Data to be printed
* @return: none
*/
STATIC VOID print_1bLine(UINT8_T* data)
{if (1 == out_of_paper_alarm()) {return;}begin_print_line(data);motor_feedPaper_line(1, FORWARD, 2);    // The actual number of steps is 2*Paramend_printLine();	
}

要合理控制步进电机送纸速度,速度过快容易走不动纸,速度过慢加热头会对热敏纸同一个点长时间加热使热敏纸颜色由黑变灰甚至变白,影响打印效果。

3.2 打印份数

打印份数默认打印一份,在APP上设置参数可调整打印份数。由于步进电机和热敏头不能长时间连续工作,因此打印份数不宜设置过多,否则容易烧坏电机和热敏头。

3.3 生成第一段链接 & 生成第二段链接

由于平台受限,url长度超过255个字节,string类型DP最多只能容纳255个字节,因此需要两个DP。url在代码中本质为字符串。APP将两端url下发到设备后再将得到的两组url拼接成完整的url,设备访问该url将图片下载到模组。

        case PRINT_NUM_DPID:bmp_info.paper_num = root->value.dp_value;break;case CREATE_LINK1_DPID:memset (bmp_info.first_url, 0, 255);memcpy(bmp_info.first_url, root->value.dp_str, strlen(root->value.dp_str));break;case CREATE_LINK2_DPID:memset (bmp_info.second_url, 0, 255);memcpy(bmp_info.second_url, root->value.dp_str, strlen(root->value.dp_str));break;
3.4 开始打印 & 正在打印的份数

设备收到打印命令后访问url下载图片到模组后开始打印。由于模组RAM有限,剩余空间只有40k左右,而需要打印的图片很可能超过了40k,因此采用一边拉取部分图片一边打印的方式完成打印。打印份数bmp_info.paper_num控制for循环的次数,bmp_info.print_num即为当前正在打印的份数,将print_num实时上报到APP即可显示当前正在打印的份数。
开始打印部分代码:

    CHAR_T *image_url = (CHAR_T *)malloc(512*sizeof(CHAR_T));strcpy(image_url, "https://storage-proxy.tuyacn.com:7779/dst=");tuya_hal_semaphore_wait(pv_handle);strcat(image_url, bmp_info.first_url);strcat(image_url, bmp_info.second_url);for (bmp_info.print_num = 0; bmp_info.print_num < bmp_info.paper_num; bmp_info.print_num++) {iot_httpc_download_file(image_url, PULL_SIZE, iot_download_image_cb, NULL, bmp_info.total_len, file_hmac); tuya_bmp_print_num_update(++bmp_info.print_num);}
3.5 缺纸报警

打印机内部采用一个反射性光电通断侦测传感器,当缺纸时,光电侦测发出的光无法被反射,输出高电平。当纸张正常,光电侦测发出的光被反射,由接收管接收,输出低电平。将输出的电平值实时上报到APP。当缺纸时,不能启动加热头和电机。
缺纸检测部分代码:

/*** @function: out_of_paper_alarm* @brief: detection printer have paper* @param: none* @return: ALARM-->out of paper  NORMAL-->have a paper* @others: none*/
UCHAR_T out_of_paper_alarm(VOID)
{if (NO_PAPER == tuya_gpio_read(PAPER_SENSOR)) {return PAPER_ALARM;} else {return PAPER_NORMAL;}
}dp_arr[2].dpid = OUT_OF_PAPER_DPID; dp_arr[2].type = PROP_BOOL;     dp_arr[2].time_stamp = 0;dp_arr[2].value.dp_bool = out_of_paper_alarm();
3.6 电量显示 & 低电量报警

打印机采用7.4v可充电锂电池供电,采用ADC采集电池端的电压,显示10%、20%、…、90%、100%电量值,当电池电压等于10%时上报APP,并且红灯亮起,提醒用户充电。大于10%时绿灯常亮。该任务放在单独的线程中每3s采集一次电池电压,并上报电量值。

在这里插入图片描述
ADC_ELECTRICITY处分压后电压为2.1v。
部分示例代码:

/**
* @function:tuya_BatMon_BatStatus
* @brief: Battery level display
* @param[in]: none
* @return: none
*/
VOID tuya_BatMon_BatStatus(VOID)
{UINT16_T battery_val = 0;BOOL_T bat_alarm = 0;while (1) {battery_val = tuya_BatMon_BatVal_get(2400, 4096, 4, 400);if (battery_val <= precent_10) {bat_alarm = 1;tuya_BatMon_ch443k_toggle(BAT_LED_PIN, FALSE);tuya_gpio_write(BAT_LED_PIN, FALSE);} else {bat_alarm = 0;tuya_BatMon_ch443k_toggle(BAT_LED_PIN, TRUE);tuya_gpio_write(BAT_LED_PIN, TRUE);}if (battery_val >= precent_100) {vlotage_percent = _100p;} else if (battery_val < precent_100 && battery_val >= precent_90) {vlotage_percent = _90p;} else if (battery_val < precent_90 && battery_val >= precent_80) {vlotage_percent = _80p;} else if (battery_val < precent_80 && battery_val >= precent_70) {vlotage_percent = _70p;} else if (battery_val < precent_70 && battery_val >= precent_60) {vlotage_percent = _60p;} else if (battery_val < precent_60 && battery_val >= precent_50) {vlotage_percent = _50p;} else if (battery_val < precent_50 && battery_val >= precent_40) {vlotage_percent = _40p;} else if (battery_val < precent_40 && battery_val >= precent_30) {vlotage_percent = _30p;} else if (battery_val < precent_30 && battery_val >= precent_20) {vlotage_percent = _20p;} else {vlotage_percent = _10p;}/* Voltage detection frequency */tuya_hal_system_sleep(CKECK_TIME);tuya_update_Bat_Val_dp(bat_alarm);}
}

e if (battery_val < precent_50 && battery_val >= precent_40) {
vlotage_percent = _40p;
} else if (battery_val < precent_40 && battery_val >= precent_30) {
vlotage_percent = _30p;
} else if (battery_val < precent_30 && battery_val >= precent_20) {
vlotage_percent = _20p;
} else {
vlotage_percent = _10p;
}

    /* Voltage detection frequency */tuya_hal_system_sleep(CKECK_TIME);tuya_update_Bat_Val_dp(bat_alarm);}}

这篇关于科技不总是冷冰冰,智能便携打印机让文字更有温度!——嵌入式功能实现篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/301521

相关文章

C#借助Spire.XLS for .NET实现在Excel中添加文档属性

《C#借助Spire.XLSfor.NET实现在Excel中添加文档属性》在日常的数据处理和项目管理中,Excel文档扮演着举足轻重的角色,本文将深入探讨如何在C#中借助强大的第三方库Spire.... 目录为什么需要程序化添加Excel文档属性使用Spire.XLS for .NET库实现文档属性管理Sp

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

Python实现快速扫描目标主机的开放端口和服务

《Python实现快速扫描目标主机的开放端口和服务》这篇文章主要为大家详细介绍了如何使用Python编写一个功能强大的端口扫描器脚本,实现快速扫描目标主机的开放端口和服务,感兴趣的小伙伴可以了解下... 目录功能介绍场景应用1. 网络安全审计2. 系统管理维护3. 网络故障排查4. 合规性检查报错处理1.

Python轻松实现Word到Markdown的转换

《Python轻松实现Word到Markdown的转换》在文档管理、内容发布等场景中,将Word转换为Markdown格式是常见需求,本文将介绍如何使用FreeSpire.DocforPython实现... 目录一、工具简介二、核心转换实现1. 基础单文件转换2. 批量转换Word文件三、工具特性分析优点局

Springboot3统一返回类设计全过程(从问题到实现)

《Springboot3统一返回类设计全过程(从问题到实现)》文章介绍了如何在SpringBoot3中设计一个统一返回类,以实现前后端接口返回格式的一致性,该类包含状态码、描述信息、业务数据和时间戳,... 目录Spring Boot 3 统一返回类设计:从问题到实现一、核心需求:统一返回类要解决什么问题?

Java使用Spire.Doc for Java实现Word自动化插入图片

《Java使用Spire.DocforJava实现Word自动化插入图片》在日常工作中,Word文档是不可或缺的工具,而图片作为信息传达的重要载体,其在文档中的插入与布局显得尤为关键,下面我们就来... 目录1. Spire.Doc for Java库介绍与安装2. 使用特定的环绕方式插入图片3. 在指定位

Java使用Spire.Barcode for Java实现条形码生成与识别

《Java使用Spire.BarcodeforJava实现条形码生成与识别》在现代商业和技术领域,条形码无处不在,本教程将引导您深入了解如何在您的Java项目中利用Spire.Barcodefor... 目录1. Spire.Barcode for Java 简介与环境配置2. 使用 Spire.Barco

Java利用Spire.Doc for Java实现在模板的基础上创建Word文档

《Java利用Spire.DocforJava实现在模板的基础上创建Word文档》在日常开发中,我们经常需要根据特定数据动态生成Word文档,本文将深入探讨如何利用强大的Java库Spire.Do... 目录1. Spire.Doc for Java 库介绍与安装特点与优势Maven 依赖配置2. 通过替换

Android使用java实现网络连通性检查详解

《Android使用java实现网络连通性检查详解》这篇文章主要为大家详细介绍了Android使用java实现网络连通性检查的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录NetCheck.Java(可直接拷贝)使用示例(Activity/Fragment 内)权限要求