当前位置: 首页 > news >正文

喵喵蓝牙热敏打印机(下)

目录

  • 前言
  • 一、电量、温度、缺纸检测
    • 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任务整合 前言 喵喵蓝牙热敏打印机&#xff08;上&#xff09; 内容有点多&#xff0c;就分为了上下两篇。 一、电量、温度、缺纸检测 先启…...

软件测试第1章 软件测试是什么

目录​​​​​​​ 内容说明 一、软件测试与质量概览需要熟悉什么 二、如何理解质量保证 三、软件测试的误区-程序员和测试的关系 四、软件测试是什么&#xff1f; 五、软件测试的目的 六、软件测试与软件质量保证 七、软件测试的必要性 八、软件测试的基本概念分析 …...

【技术分享】 hysteria2从服务端到客户端部署教程

hysteria2从服务端到客户端部署教程 前言 在如今的网络环境中&#xff0c;尤其是涉及跨国访问的场景中&#xff0c;hysteria2作为一个新兴的传输协议工具&#xff0c;凭借其高效的传输能力和灵活的配置方式&#xff0c;受到了越来越多用户的青睐。本教程将带您一步步完成hyst…...

C++入门基础知识16

C 的关键字&#xff08;接上一篇博文&#xff01;&#xff01;&#xff01;&#xff09; 54. typeid 指出指针或引用指向的对象的实际派生类型。 55. typename typename&#xff08;类型名字&#xff09;关键字告诉编译器把一个特殊的名字解释成一个类型。在下列情况下必须对一…...

浏览器调试工具-Chrome Dev Tools

浏览器调试模式下的各个调试工具是常用的工具集&#xff0c;能够帮助开发者理解、调试和优化网页。 1.打开方式 直接在浏览器中按下F12键右键点击页面上的任一元素&#xff0c;选择“检查”&#xff08;Inspect&#xff09;在浏览器右上角点击菜单按钮&#xff0c;选择“更多…...

基于车联网大数据平台的用户驾驶习惯行为画像分析

近年来&#xff0c;新能源汽车行业的迅速发展推动了汽车智能化的趋势。新能源汽车上配备了成千上万的传感器&#xff0c;这些传感器采集了大量的行车数据被用于车辆运行状况的监控与分析。另一方面&#xff0c;采集到的大量行车数据&#xff0c;也能很好地体现用户的驾驶习惯。…...

Ubuntu24.04搭建maxkb开发环境

接上文&#xff1a;windows10搭建maxkb开发环境&#xff08;劝退指南&#xff09; 上文在windows10环境搭建maxkb开发环境遇到各种坑&#xff0c;后面就转战ubuntu平台&#xff0c;果然比较顺利的完成开发环境搭建。当然遇到相关的问题还是可以参考上文《windows10搭建maxkb开发…...

C++ 指针和引用的区别

1.引用在定义时必须初始化&#xff0c;而指针没有要求 2.引用一旦引用了一个实体就不能在引用其它实体&#xff0c;指针可以在任何时候指向同一类型的指针 3.没有空引用&#xff0c;但是有空指针 4.在sizeof中含义不同&#xff1a;引用结果为引用类型的大小&#xff0c;但指…...

python绘制蕨菜叶分形

一花一叶一世界,一草一木一浮生. 使用了四个不同的线性变换&#xff0c;根据概率选择其中一个变换并更新 x 和 y 坐标。然后将生成的绿色点绘制出来&#xff0c;形成一片蕨菜叶。 import numpy as np import matplotlib.pyplot as pltdef fern_fractal(num_points):# 初始化坐…...

1分钟了解pandas

Pandas 是一个强大的 Python 库&#xff0c;用于数据分析和数据处理。它为 Python 提供了高效的数据结构和数据分析工具&#xff0c;使得数据操作变得简单而直观。Pandas 由 Wes McKinney 在 2008 年创建&#xff0c;并迅速成为数据科学领域中最受欢迎的库之一。 安装 Pandas …...

django-celery应用-定时执行测试cases

1、celery周期性任务 简介-----celery beat 是一个调度程序&#xff0c;它定期启动任务&#xff0c;然后由集群中的可用工作节点执行这些任务。 django-celery-beat celery默认的调度程序是 celery.beat.PersistentScheduler &#xff0c;它简单地跟踪本地 shelve 数据库文件中…...

【C++深度探索】unordered_set、unordered_map封装

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;C从入门至进阶 这里将会不定期更新有关C/C的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目录…...

CSS——字体背景(Font Background)

一、字体族 1、字体的相关样式&#xff1a; ① color 用来设置字体颜色&#xff08;前景颜色&#xff09; ② font-size 字体的大小 和font-size相关的单位&#xff1a; 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(模型/视图&#xff09;结构 文章目录 Model/View(模型/视图&#xff09;结构简介视图组件Model/View结构的一些概念项目控件组&#xff08;item Widgets&#xff09;模型/视图 如何使用项目视图组设置行的颜色交替变换拖拽设置编辑操作其他操作 选择模型自定义选择多…...

硬件工程师必须掌握的MOS管详细知识

MOS管&#xff0c;全称为金属-氧化物半导体场效应晶体管&#xff08;Metal-Oxide-Semiconductor Field-Effect Transistor&#xff0c;MOSFET&#xff09;&#xff0c;是一种重要的半导体器件&#xff0c;广泛应用于电子工业中各种电路的开关、放大、调制、数字电路和模拟电路等…...

希尔排序,详细解析(附图解)

1.希尔排序思路 希尔排序是一种基于插入排序的算法&#xff0c;通过将原始数据分成若干个子序列&#xff0c;然后对子序列进行插入排序&#xff0c;逐渐减小子序列的间隔&#xff0c;最后对整个序列进行一次插入排序。 1.分组直接插入排序&#xff0c;目标接近有序--------…...

【C语言篇】编译和链接以及预处理介绍(下篇)

文章目录 前言#和###运算符##运算符 命名约定#undef命令⾏定义条件编译#if和#endif多个分支的条件编译判断是否被定义嵌套指令 头文件被包含头文件被包含的方式本地文件包含库文件的包含 嵌套文件包含 其他预处理指令 写在最后 前言 本篇接前一篇【C语言篇】编译和链接以及预处…...

利用Llama2 7b自己实现一套离线AI

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff0c; 可以当故事来看&#xff0c;轻松学习。 离了 ChatGPT 本人简直寸步难行&#xff0c;今天 ChatGPT 大面积宕机&#xff0c;服务直到文章写作&am…...

Ciallo~(∠・ω・ )⌒☆第十七篇 Ubuntu基础使用 其一

Ubuntu是一种基于Linux的操作系统&#xff0c;它是开源的、免费的&#xff0c;并且具有广泛的用户群体。 基本文件操作&#xff1a;Ubuntu使用命令行工具来进行文件操作。以下是一些常用的命令&#xff1a; 切换到用户主目录&#xff1a; cd ~ 切换到上级目录&#xff1a; cd .…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

问题&#xff1a; pgsql数据库通过备份数据库文件进行还原时&#xff0c;如果表中有自增序列&#xff0c;还原后可能会出现重复的序列&#xff0c;此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”&#xff0c;…...

深度解析云存储:概念、架构与应用实践

在数据爆炸式增长的时代&#xff0c;传统本地存储因容量限制、管理复杂等问题&#xff0c;已难以满足企业和个人的需求。云存储凭借灵活扩展、便捷访问等特性&#xff0c;成为数据存储领域的主流解决方案。从个人照片备份到企业核心数据管理&#xff0c;云存储正重塑数据存储与…...

python打卡day49@浙大疏锦行

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 一、通道注意力模块复习 & CBAM实现 import torch import torch.nn as nnclass CBAM(nn.Module):def __init__…...