喵喵蓝牙热敏打印机(下)
目录
- 前言
- 一、电量、温度、缺纸检测
- 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 .…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
