喵喵蓝牙热敏打印机(下)
目录
- 前言
- 一、电量、温度、缺纸检测
- 1.电量检测
- 2.针头温度检测
- 3.缺纸检测
- 二、蓝牙APP通信打印
- 1.蓝牙初始化
- 2.APP通信打印
- 三、FreeRTOS任务整合
前言
喵喵蓝牙热敏打印机(上)
内容有点多,就分为了上下两篇。
一、电量、温度、缺纸检测
先启动一下串口通信,方便后续操作。
Serial.begin(115200);
1.电量检测
原理图:


逻辑分析:电池电量检测可以通过检测POWER_ADC的分压值来转换。
这个里的分压值是个模拟变化量,所以我们要用到ADC模数转换。
代码如下:
#include <adc.h>
#define POWER_ADC 34//Arduino IDE中为ESP32 配置的ADC默认环境就能//用,所以直接定义一个引脚即可,不用初始化。int battery;
int batt_all;int get_battery_adc()
{int data = 0;//获取IO口电压值data = analogReadMilliVolts(POWER_ADC);//直接通过内置函数获取电压值return data;
}void Deal_battery_adc()
{battery = map(get_battery_adc(),1650,2100,0,100);for (int i = 0; i < 10; i++)//为了使获取的电压值比较稳定,就先获取10个,然后取平均值。{batt_all+=battery;delay(1000);}battery_fina=batt_all/10;batt_all=0;printf("电量:%d\r\n",batt_all/10);
}

重点解析:map(get_battery_adc(),1650,2100,0,100);
map函数主要就是作为一个映射功能。
由于该硬件使用的是18650电池,该电池满电4.2v,终止电压2.75v。但由于该硬件的步进电机到3.3v就驱动不了了,所以ADC获取的电压模拟量会在1650和2100之间(实际上是3300和4200之间,但两个10k电阻分压一半)。

2.针头温度检测


uint8_t head_temp = 23; //打印头温度
uint16_t temp_adc;
void HeatTemp() {temp_adc = analogRead(PIN_TEMP);Serial.print("打印头温度ADC:"); Serial.println(temp_adc);head_temp = 23;
}
直接读即可
3.缺纸检测


电路看着很复杂,但其实原理很简单,直接读35引脚,为1就是缺纸,为0就是有纸。
int NO_Paper;
void read_paper_statue()
{if(digitalRead(PRINT) == 1){Serial.printf("缺纸1111\n");NO_Paper=1;}else{Serial.printf("有纸0000\n");NO_Paper=0;}
}
二、蓝牙APP通信打印
1.蓝牙初始化
这部分主要就是设置个波特率顺便初始化一下SPI
#include <BluetoothSerial.h>void init_Blue()
{Serial.begin(115200);print_Blue_Data = (uint8_t*)malloc(50 * 1024); printerSPI.begin(PIN_CLK, -1, PIN_DI, -1);printerSPI.setFrequency(2000000);//print_Blue_Data = (uint8_t *)malloc(3 * 1024 * 1024);}
2.APP通信打印
这一部分就是全力依靠樱猫大佬的代码了
上位机和设备建立链接时需要发送的指令和顺序如下:
1.上位机发送 24 指令,设置 CRC 校验的密钥,固定四字节 crcKey = 0x35769521
2.发送 10 指令获取设备的 SN 码,需要蓝牙返回指定的 SN 码:“P1001705253855”
3.上位机发送 66 指令,设备返回固定字符串信息:”BK3432”,蓝牙设备型号
4.上位机发送 127 指令,设备返回固定信息:{0x76, 0x33, 0x2e, 0x33, 0x38, 0x2e, 0x31, 0x39,
0, 0, 0, 0};
5.上位机发送 25 指令,设定打印机加热时间(热密度)
6.上位机发送 48 指令,请求设备名称,设备返回自己设定的名字
7.上位机发送 16 指令,请求设备电池信息,设备返回当前电压
8.上位机发送 31 指令,请求关机时间,设备返回设定的关机时间默认 3600 分钟
9.上位机发送 4 指令,获取打印机版本信息,默认版本 2.0.1,设备返回{0x01, 0x00, 0x02};
10.上位机发送 44 指令,设定纸张类型,设备纸张固定,不需要修改。
上述流程中,需要注意设备返回的数据,在开发蓝牙打印机的功能时,需要正确返回数据,否则会导致设备连接失败,并且每条指令在接受完成之后,设备都需要进行回复,回复内容固定为 00,类型根据接收到的类型来定。如果指令没有正确回应,软件会重复发送直至超时。
上位机和设备之间传输打印数据发送的指令和数据流程如下:
1.打印时会先发送两次 25 指令用以设置打印热密度,也就是打印加热时间。
2.发送 26(进料线)指令,也就是开始打印指令
3.然后发送 0 指令,开始传输数据,分包发送打印的数据,一包最大 1920
4.数据发送完毕之后,会再次发送 26 指令,收到之后,打印机开始打印数据。
指令定义:
#define PRINT_DATA 0 // 打印数据
#define PRINT_DATA_COMPRESS 1 // 打印数据压缩
#define FIRMWARE_DATA 2 // 固件数据
#define USB_UPDATE_FIRMWARE 3 // USB更新固件
#define GET_VERSION 4 // 获取版本
#define SENT_VERSION 5 // 发送版本
#define GET_MODEL 6 // 获取模式
#define SENT_MODEL 7 // 发送模式
#define GET_BT_MAC 8 // 获取蓝牙MAC
#define SENT_BT_MAC 9 // 发送蓝牙MAC
#define GET_SN 10 // 获取SN码
#define SENT_SN 11 // 发送SN码
#define GET_STATUS 12 // 获取状态
#define SENT_STATUS 13 // 发送状态
#define GET_VOLTAGE 14 // 获取电压
#define SENT_VOLTAGE 15 // 发送电压
#define GET_BAT_STATUS 16 // 获取蓝牙状态
#define SENT_BAT_STATUS 17 // 发送蓝牙状态
#define GET_TEMP 18 // 获取打印头温度
#define SENT_TEMP 19 // 发送打印头温度
#define SET_FACTORY_STATUS 20 // 设置出厂状态
#define GET_FACTORY_STATUS 21 // 获取出厂状态
#define SENT_FACTORY_STATUS 22 // 发送出厂状态
#define SENT_BT_STATUS 23 // 发送蓝牙状态
#define SET_CRC_KEY 24 // 设置CRC密钥
#define SET_HEAT_DENSITY 25 // 设定热密度
#define FEED_LINE 26 // 进料线
#define PRINT_TEST_PAGE 27 // 打印测试页
#define GET_HEAT_DENSITY 28 // 获取热密度
#define SENT_HEAT_DENSITY 29 // 发送热密度
#define SET_POWER_DOWN_TIME 30 // 设定关机时间
#define GET_POWER_DOWN_TIME 31 // 获取断电时间
#define SENT_POWER_DOWN_TIME 32 // 发送断电时间
#define FEED_TO_HEAD_LINE 33 // 送至标题行
#define PRINT_DEFAULT_PARA 34 // 打印默认参数
#define GET_BOARD_VERSION 35 // 获取板子版本
#define SENT_BOARD_VERSION 36 // 发送板子版本
#define GET_HW_INFO 37 // 获取硬件信息
#define SENT_HW_INFO 38 // 发送硬件信息
#define SET_MAX_GAP_LENGTH 39 // 设定最大间隙长度
#define GET_MAX_GAP_LENGTH 40 // 获取最大间隙长度
#define SENT_MAX_GAP_LENGTH 41 // 发送最大间隙长度
#define GET_PAPER_TYPE 42 // 获取纸张类型
#define SENT_PAPER_TYPE 43 // 发送纸张类型
#define SET_PAPER_TYPE 44 // 设置纸张类型
#define GET_COUNTRY_NAME 45 // 获取国家名称
#define SENT_COUNTRY_NAME 46 // 发送国家名称
#define DISCONNECT_BT 47 // 断开蓝牙
#define GET_DEV_NAME 48 // 获取设备名称
#define SENT_DEV_NAME 49 // 发送设备名称
#define CMD_39 57
#define CMD_40 64
#define CMD_41 65
#define CMD_42 66
#define CMD_43 67
#define CMD_7F 127
#define CMD_80 128
#define CMD_81 129
#define CMD_82 130
上述通信需要用到CRC数据校验
crc.c
/*** \file* Functions and types for CRC checks.** Generated on Tue Jan 7 07:33:25 2020* by pycrc v0.9.2, https://pycrc.org* using the configuration:* - Width = 32* - Poly = 0x04c11db7* - XorIn = 0xffffffff* - ReflectIn = True* - XorOut = 0xffffffff* - ReflectOut = True* - Algorithm = table-driven*/
#include "crc.h" /* include the header file generated with pycrc */
#include <stdlib.h>
#include <stdint.h>/*** Static table used for the table_driven implementation.*/
static const crc_t crc_table[256] = {0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};crc_t crc_update(crc_t crc, const void *data, size_t data_len)
{const unsigned char *d = (const unsigned char *)data;unsigned int tbl_idx;while (data_len--) {tbl_idx = (crc ^ *d) & 0xff;crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffffffff;d++;}return crc & 0xffffffff;
}
crc.h
/*** \file* Functions and types for CRC checks.** Generated on Tue Jan 7 07:33:20 2020* by pycrc v0.9.2, https://pycrc.org* using the configuration:* - Width = 32* - Poly = 0x04c11db7* - XorIn = 0xffffffff* - ReflectIn = True* - XorOut = 0xffffffff* - ReflectOut = True* - Algorithm = table-driven** This file defines the functions crc_init(), crc_update() and crc_finalize().** The crc_init() function returns the initial \c crc value and must be called* before the first call to crc_update().* Similarly, the crc_finalize() function must be called after the last call* to crc_update(), before the \c crc is being used.* is being used.** The crc_update() function can be called any number of times (including zero* times) in between the crc_init() and crc_finalize() calls.** This pseudo-code shows an example usage of the API:* \code{.c}* crc_t crc;* unsigned char data[MAX_DATA_LEN];* size_t data_len;** crc = crc_init();* while ((data_len = read_data(data, MAX_DATA_LEN)) > 0) {* crc = crc_update(crc, data, data_len);* }* crc = crc_finalize(crc);* \endcode*/
#ifndef CRC_H
#define CRC_H#include <stdlib.h>
#include <stdint.h>#ifdef __cplusplus
extern "C" {
#endif/*** The definition of the used algorithm.** This is not used anywhere in the generated code, but it may be used by the* application code to call algorithm-specific code, if desired.*/
#define CRC_ALGO_TABLE_DRIVEN 1/*** The type of the CRC values.** This type must be big enough to contain at least 32 bits.*/
typedef uint32_t crc_t;/*** Calculate the initial crc value.** \return The initial crc value.*/
static inline crc_t crc_init(void)
{return 0xffffffff;
}/*** Update the crc value with new data.** \param[in] crc The current crc value.* \param[in] data Pointer to a buffer of \a data_len bytes.* \param[in] data_len Number of bytes in the \a data buffer.* \return The updated crc value.*/
crc_t crc_update(crc_t crc, const void *data, size_t data_len);/*** Calculate the final crc value.** \param[in] crc The current crc value.* \return The final crc value.*/
static inline crc_t crc_finalize(crc_t crc)
{return crc ^ 0xffffffff;
}#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif#endif /* CRC_H */
Arduino_CRC32.c
/*Copyright (c) 2020 Arduino. All rights reserved.This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU Lesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*//*************************************************************************************** INCLUDE**************************************************************************************/#include "Arduino_CRC32.h"/*************************************************************************************** PUBLIC MEMBER FUNCTIONS**************************************************************************************/void Arduino_CRC32::init(uint32_t start)
{crc32_start = start;
}uint32_t Arduino_CRC32::calc(uint8_t const data[], uint32_t const len)
{crc_t crc32;crc32 = crc_update(crc32_start, data, len);crc32 = crc_finalize(crc32);return crc32;
}
Arduino_CRC32.h
/*Copyright (c) 2020 Arduino. All rights reserved.This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU Lesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/#ifndef ARDUINO_CRC32_H_
#define ARDUINO_CRC32_H_/*************************************************************************************** INCLUDE**************************************************************************************/#include <stdint.h>
#include "crc.h"
/*************************************************************************************** CLASS DECLARATION**************************************************************************************/class Arduino_CRC32
{public:crc_t crc32_start;void init(uint32_t start);uint32_t calc(uint8_t const data[], uint32_t const len);};#endif /* ARDUINO_CRC32_H_ */
蓝牙通信打印主要函数:
struct
{uint8_t packType;uint8_t packIndex;uint16_t dataLen;
} packHeader;
uint8_t gotStartByte = 0;
uint8_t c;
uint16_t readpos = 0;
uint8_t dataPack_read[2048];void paperang_app()
{uint16_t i = 0;// 重新设置class of deviceSerialBT.begin("MaoPaperang");esp_bt_cod_t cod;cod.major = 6; // 主设备类型cod.minor = 0b100000; // 次设备类型cod.service = 0b00000100000; // 服务类型esp_bt_gap_set_cod(cod, ESP_BT_INIT_COD); // 设置蓝牙设备的class of devicecrc_t crc32_key = 0x35769521; // 初始化 CRC32 计算器packHeader.dataLen = 0; // 初始化数据包头部的数据长度为 0while (1){ButtonRun(); // 执行按钮操作的函数Pin_Stb_Low(); // 关闭STAPower的函数// 监听蓝牙串口输入if (SerialBT.available()){c = SerialBT.read(); // 读取一个字节的数据// 如果读取到起始字节并且之前未找到起始字节if (c == START_BYTE && gotStartByte == 0){gotStartByte = 1;readpos = 0;}else if (readpos == 1){// 读取数据包类型、索引和数据长度packHeader.packType = c;packHeader.packIndex = SerialBT.read();packHeader.dataLen = SerialBT.read();packHeader.dataLen += SerialBT.read() << 8;}else if (readpos == 2 && packHeader.dataLen != 0 && packHeader.dataLen < 2048){// 读取数据包内容i = packHeader.dataLen - 1;if (packHeader.packType == PRINT_DATA){// 如果是打印数据包类型,则读取打印数据print_Blue_Data[printDataCount++] = c;while (i){while (SerialBT.available() == 0);print_Blue_Data[printDataCount++] = SerialBT.read();--i;}}else{// 否则,读取一般数据包dataPack_read[dataPack_read_pos++] = c;while (i){dataPack_read[dataPack_read_pos++] = SerialBT.read();--i;}}}else if (readpos < 7){// 如果读取位置小于7,则暂时不做处理;}else if (c == END_BYTE && readpos == 7){// 如果读取到结束字节且读取位置为7,则处理数据paperang_process_data(); // 处理数据的函数gotStartByte = 0;dataPack_read_pos = 0;readpos = 0;packHeader.dataLen = 0;}else{// 出现错误情况Serial.println("ERROR");gotStartByte = 0;dataPack_read_pos = 0;readpos = 0;packHeader.dataLen = 0;}if (gotStartByte == 1)++readpos; // 如果找到起始字节,则增加读取位置}else{vTaskDelay(1);digitalWrite(PIN_VHEN, 1);}}
}
收到数据的处理函数
void paperang_process_data()
{uint32_t tmp32;switch (packHeader.packType){case PRINT_DATA:return;// 设置CRC密钥case SET_CRC_KEY:{tmp32 = dataPack_read[0] << 24 + dataPack_read[1] << 16 + dataPack_read[2] << 8 + dataPack_read[3];// crc32.init(tmp32);crc_t crc32_key = tmp32;break;}// 获取版本信息并发送case GET_VERSION:paperang_send_msg(SENT_VERSION, PRINTER_VERSION, 3);break;// 获取设备名称并发送case GET_DEV_NAME:paperang_send_msg(SENT_DEV_NAME, (uint8_t *)PRINTER_NAME, 2);break;// 获取SN码并发送case GET_SN:paperang_send_msg(SENT_SN, (uint8_t *)PRINTER_SN, strlen((char *)PRINTER_SN));// 获取电池电量并发送case GET_BAT_STATUS:// BatteryPower();PRINTER_BATTERY = get_battery_adc();paperang_send_msg(SENT_BAT_STATUS, &PRINTER_BATTERY, 1);break;// 获取国家名称并发送case GET_COUNTRY_NAME:paperang_send_msg(SENT_COUNTRY_NAME, (uint8_t *)COUNTRY_NAME, 2);break;case CMD_42:paperang_send_msg(CMD_43, (uint8_t *)CMD_42_DATA, strlen(CMD_42_DATA));break;case CMD_7F:paperang_send_msg(CMD_80, CMD_7F_DATA, 12);break;case CMD_81:paperang_send_msg(CMD_82, CMD_81_DATA, 16);break;case CMD_40:paperang_send_msg(CMD_41, CMD_40_DATA, 1);break;// 设置打印密度case SET_HEAT_DENSITY:heat_density = dataPack_read[0];break;// 获取打印密度并发送case GET_HEAT_DENSITY:paperang_send_msg(SENT_HEAT_DENSITY, &heat_density, 1);break;// 获取打印头温度case GET_TEMP:head_temp = get_adc_temperatrue();paperang_send_msg(SENT_TEMP, &head_temp, 1);break;// 进料线case FEED_LINE:flag_data.printing_flag=1;if (printDataCount / 48 != 0){startPrint();motor_run_step(300); // 打印完毕出纸长度,单位:点,8点/mm。 }flag_data.printing_flag=0;printDataCount = 0;break;default:break;}paperang_send_ack(packHeader.packType);
}
数据发送接收函数
void paperang_send(void)
{SerialBT.write(dataPack, dataPack_len);
}
// 蓝牙回复指令
void paperang_send_ack(uint8_t type)
{uint8_t ackcrc = 0;dataPack[0] = START_BYTE;dataPack[1] = type;dataPack[2] = 0x00;dataPack[3] = 0x01;dataPack[4] = 0x00;dataPack[5] = 0x00;// crc32_result = crc32.calc(&ackcrc, 1);// 初始化 CRC32 计算crc_t crc32_result = 0xFFFFFFFF;crc32_result = crc_update(crc32_result, &ackcrc, 1);crc32_result ^= 0xFFFFFFFF;memcpy(dataPack + 6, (uint8_t *)&crc32_result, 4);dataPack[10] = END_BYTE;dataPack_len = 11;paperang_send();
}// 蓝牙回复信息
void paperang_send_msg(uint8_t type, const uint8_t *dat, uint16_t len)
{dataPack[0] = START_BYTE;dataPack[1] = type;dataPack[2] = 0x00;memcpy(dataPack + 3, (uint8_t *)&len, 2);memcpy(dataPack + 5, dat, len);dataPack_len = 5 + len;// crc32_result = crc32.calc(dat, len);crc_t crc32_result = 0xFFFFFFFF;crc32_result = crc_update(crc32_result, dat, len);crc32_result ^= 0xFFFFFFFF;memcpy(dataPack + dataPack_len, (uint8_t *)&crc32_result, 4);dataPack[dataPack_len + 4] = END_BYTE;dataPack_len += 5;paperang_send();
}
打印函数
char *CMD_42_DATA PROGMEM = "BK3432";
uint8_t CMD_7F_DATA[] PROGMEM = {0x76, 0x33, 0x2e, 0x33, 0x38, 0x2e, 0x31, 0x39, 0, 0, 0, 0};
uint8_t CMD_81_DATA[] PROGMEM = {0x48, 0x4d, 0x45, 0x32, 0x33, 0x30, 0x5f, 0x50, 0x32, 0, 0, 0, 0, 0, 0, 0};
uint8_t PRINTER_VERSION[] PROGMEM = {0x01, 0x00, 0x02}; // 打印机版本2.0.1
uint8_t *CMD_40_DATA PROGMEM = {0x00};
uint8_t heat_density = 64; // 热密度
void startPrint()
{int pinnumber;int i;int cache; // 分批打印标志,避免同时开启所有打印头// onprint = 1;onprint = 1;PowerONTime = millis();read_paper_statue();if (NO_Paper == 0) {static unsigned char motor_add = 0;Serial.println("[INFO]正在打印...");Serial.printf("[INFO]共%u行\n", printDataCount / 48);digitalWrite(PIN_VHEN, 1);cache = -1;for (uint32_t pointer = 0; pointer < printDataCount; pointer += 48){cache = -1;while (cache != 0){ // 分批加热打印头if (cache == -1){cache = 0;}pinnumber = 0;for (i = cache; i < 48; i++){pinnumber = pinnumber + shuzu16to2jingzhi[print_Blue_Data[pointer + i]]; // 这里用了excel公式做了个数组来统计0-255转换成二进制后1的个数(懒得写算法)输出当前行打印点数if (pinnumber <= printpin){ // 判断打印的数据中要加热的点数是否超过规定最大值,如果超过了就先加热指定的点printDatacache[i] = print_Blue_Data[pointer + i];if (i == 47){cache = 0;}}else{cache = i;break;}}send_Blue_Data(printDatacache);// 控制各个STB引脚for (int j = 0; j < 48; j++){printDatacache[j] = 0;}if (pinnumber != 0){run_stb(0);run_stb(1);run_stb(2);run_stb(3);run_stb(4);run_stb(5);}}motor_run_step(4); }motor_stop();Pin_Stb_Low();clearData();printDataCount = 0;Serial.println("[INFO]打印完成");digitalWrite(PIN_VHEN, 0);PowerONTime = millis();onprint = 0;}else{startBeep();delay(500);stopBeep();delay(500);startBeep();delay(500);stopBeep();}
}
三、FreeRTOS任务整合
在 Arduino IDE 的 ESP32 环境里有内置的FreeRTOS系统,直接使用即可。
使用说明详见STM32第十九课:FreeRTOS移植和使用
我这里使用了两个任务,一个用来执行主要任务蓝牙通信打印,一个用来实时监测报警提示用户。
#include <Arduino.h>
#include <BluetoothSerial.h>
#include <SPI.h>
#include <LED.h>
#include <motor.h>
#include <adc.h>
#include <print.h>
#include <img.h>
#include <bmiao.h>void task_print(void *pvParameters)//打印任务
{for (;;) {paperang_app();}
}void task_LED(void *pvParameters)//温度,打印检测
{for (;;) {read_paper_statue();//vTaskDelay(500);if(flag_data.printing_flag==0){Led_Status(2);}if(flag_data.printing_flag==1){Led_Status(3);}if(NO_Paper == 1){digitalWrite(PIN_VHEN, 0);//电机警告startBeep();vTaskDelay(500);stopBeep();vTaskDelay(500);startBeep();vTaskDelay(500);stopBeep();}else{stopBeep();digitalWrite(PIN_VHEN, 1);}}}
void init_task()//初始化任务
{BaseType_t task = NULL;task = xTaskCreate(task_print, // 任务函数"task_print", // 任务名1024*6, // 任务栈NULL, // 任务参数1, // 任务优先级NULL // 任务句柄);if(task == NULL){Serial.printf("printf err\n");}task = xTaskCreate(task_LED, // 任务函数"task_LED", // 任务名1024*4, // 任务栈NULL, // 任务参数2, // 任务优先级NULL // 任务句柄);if(task == NULL){Serial.printf("led err\n");}
}void setup()
{init_Blue();Led_Config(); //初始化LEDinit_motor(); //初始化电机Init_Print(); //初始化打印头init_printer();pinMode(PIN_VHEN, OUTPUT);digitalWrite(PIN_VHEN, LOW);//缺纸IO初始化pinMode(PRINT, INPUT);pinMode(PIN_VHEN, OUTPUT);digitalWrite(PIN_VHEN, LOW);analogReadResolution(TEMPERATRUE_ADC_BIT);init_task();//启动任务Serial.print("init_task OK\r\n");
}
void loop()
{
}
其中电机警告是使用电机微动来实现的,通过电机的的微动来发声。
void startBeep(void)//微动
{// 设定总的转动时间为 1 秒(1000000 微秒)unsigned long totalTime = 1000000; unsigned long stepTime = 100; // 每步延时 2000 微秒unsigned long steps = totalTime / stepTime;for (unsigned long i = 0; i < steps; ++i) {goFront1(stepTime);}
}
void stopBeep(void)//停止
{digitalWrite(PIN_MOTOR_AP,LOW);digitalWrite(PIN_MOTOR_AM,LOW);digitalWrite(PIN_MOTOR_BP,LOW);digitalWrite(PIN_MOTOR_BM,LOW);
}
相关文章:
喵喵蓝牙热敏打印机(下)
目录 前言一、电量、温度、缺纸检测1.电量检测2.针头温度检测3.缺纸检测 二、蓝牙APP通信打印1.蓝牙初始化2.APP通信打印 三、FreeRTOS任务整合 前言 喵喵蓝牙热敏打印机(上) 内容有点多,就分为了上下两篇。 一、电量、温度、缺纸检测 先启…...
软件测试第1章 软件测试是什么
目录 内容说明 一、软件测试与质量概览需要熟悉什么 二、如何理解质量保证 三、软件测试的误区-程序员和测试的关系 四、软件测试是什么? 五、软件测试的目的 六、软件测试与软件质量保证 七、软件测试的必要性 八、软件测试的基本概念分析 …...
【技术分享】 hysteria2从服务端到客户端部署教程
hysteria2从服务端到客户端部署教程 前言 在如今的网络环境中,尤其是涉及跨国访问的场景中,hysteria2作为一个新兴的传输协议工具,凭借其高效的传输能力和灵活的配置方式,受到了越来越多用户的青睐。本教程将带您一步步完成hyst…...
C++入门基础知识16
C 的关键字(接上一篇博文!!!) 54. typeid 指出指针或引用指向的对象的实际派生类型。 55. typename typename(类型名字)关键字告诉编译器把一个特殊的名字解释成一个类型。在下列情况下必须对一…...
浏览器调试工具-Chrome Dev Tools
浏览器调试模式下的各个调试工具是常用的工具集,能够帮助开发者理解、调试和优化网页。 1.打开方式 直接在浏览器中按下F12键右键点击页面上的任一元素,选择“检查”(Inspect)在浏览器右上角点击菜单按钮,选择“更多…...
基于车联网大数据平台的用户驾驶习惯行为画像分析
近年来,新能源汽车行业的迅速发展推动了汽车智能化的趋势。新能源汽车上配备了成千上万的传感器,这些传感器采集了大量的行车数据被用于车辆运行状况的监控与分析。另一方面,采集到的大量行车数据,也能很好地体现用户的驾驶习惯。…...
Ubuntu24.04搭建maxkb开发环境
接上文:windows10搭建maxkb开发环境(劝退指南) 上文在windows10环境搭建maxkb开发环境遇到各种坑,后面就转战ubuntu平台,果然比较顺利的完成开发环境搭建。当然遇到相关的问题还是可以参考上文《windows10搭建maxkb开发…...
C++ 指针和引用的区别
1.引用在定义时必须初始化,而指针没有要求 2.引用一旦引用了一个实体就不能在引用其它实体,指针可以在任何时候指向同一类型的指针 3.没有空引用,但是有空指针 4.在sizeof中含义不同:引用结果为引用类型的大小,但指…...
python绘制蕨菜叶分形
一花一叶一世界,一草一木一浮生. 使用了四个不同的线性变换,根据概率选择其中一个变换并更新 x 和 y 坐标。然后将生成的绿色点绘制出来,形成一片蕨菜叶。 import numpy as np import matplotlib.pyplot as pltdef fern_fractal(num_points):# 初始化坐…...
1分钟了解pandas
Pandas 是一个强大的 Python 库,用于数据分析和数据处理。它为 Python 提供了高效的数据结构和数据分析工具,使得数据操作变得简单而直观。Pandas 由 Wes McKinney 在 2008 年创建,并迅速成为数据科学领域中最受欢迎的库之一。 安装 Pandas …...
django-celery应用-定时执行测试cases
1、celery周期性任务 简介-----celery beat 是一个调度程序,它定期启动任务,然后由集群中的可用工作节点执行这些任务。 django-celery-beat celery默认的调度程序是 celery.beat.PersistentScheduler ,它简单地跟踪本地 shelve 数据库文件中…...
【C++深度探索】unordered_set、unordered_map封装
🔥 个人主页:大耳朵土土垚 🔥 所属专栏:C从入门至进阶 这里将会不定期更新有关C/C的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉 文章目录…...
CSS——字体背景(Font Background)
一、字体族 1、字体的相关样式: ① color 用来设置字体颜色(前景颜色) ② font-size 字体的大小 和font-size相关的单位: em 相对于当前元素的一个font-size rem 相对于根元素的一个font-size ③ font-family 字体族&#x…...
秋招突击——8/15——知识补充——Socket通信
文章目录 引言正文基于TCP协议的Socket通信基于UDP协议的Socket通信服务端如何接收更多项目多进程多线程IO多路复用select轮询IO多路复用epoll事件通知 使用Socket实现同一个机器上的多线程通信服务端创建对应socket监听端口客户端发起对应的连接请求 总结 引言 上次面试腾讯的…...
Qt第十四章 模型视图
Model/View(模型/视图)结构 文章目录 Model/View(模型/视图)结构简介视图组件Model/View结构的一些概念项目控件组(item Widgets)模型/视图 如何使用项目视图组设置行的颜色交替变换拖拽设置编辑操作其他操作 选择模型自定义选择多…...
硬件工程师必须掌握的MOS管详细知识
MOS管,全称为金属-氧化物半导体场效应晶体管(Metal-Oxide-Semiconductor Field-Effect Transistor,MOSFET),是一种重要的半导体器件,广泛应用于电子工业中各种电路的开关、放大、调制、数字电路和模拟电路等…...
希尔排序,详细解析(附图解)
1.希尔排序思路 希尔排序是一种基于插入排序的算法,通过将原始数据分成若干个子序列,然后对子序列进行插入排序,逐渐减小子序列的间隔,最后对整个序列进行一次插入排序。 1.分组直接插入排序,目标接近有序--------…...
【C语言篇】编译和链接以及预处理介绍(下篇)
文章目录 前言#和###运算符##运算符 命名约定#undef命令⾏定义条件编译#if和#endif多个分支的条件编译判断是否被定义嵌套指令 头文件被包含头文件被包含的方式本地文件包含库文件的包含 嵌套文件包含 其他预处理指令 写在最后 前言 本篇接前一篇【C语言篇】编译和链接以及预处…...
利用Llama2 7b自己实现一套离线AI
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家, 可以当故事来看,轻松学习。 离了 ChatGPT 本人简直寸步难行,今天 ChatGPT 大面积宕机,服务直到文章写作&am…...
Ciallo~(∠・ω・ )⌒☆第十七篇 Ubuntu基础使用 其一
Ubuntu是一种基于Linux的操作系统,它是开源的、免费的,并且具有广泛的用户群体。 基本文件操作:Ubuntu使用命令行工具来进行文件操作。以下是一些常用的命令: 切换到用户主目录: cd ~ 切换到上级目录: cd .…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
