簡介:利用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);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/299038.html
標籤:其他
上一篇:【IoT】產品設計:OEM、ODM、EMS 的區別是什么?
下一篇:攝像頭逆向分析之旅
