IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 科技不总是冷冰冰,智能便携打印机让文字更有温度!——嵌入式功能实现篇 -> 正文阅读

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

简介:利用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*Param
	end_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);

}
 }
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-09 11:56:09  更:2021-09-09 11:56:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:22:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码