Apache Cordova使用cordova-plugin-ble-central调用ble.write无法打印且无报错(Android)
我开发了一款Apache Cordova应用,希望通过cordova-plugin-ble-central插件使用蓝牙打印机打印收据。我使用了以下测试代码,但无法打印数据,且未返回任何错误或响应。其中前三个变量为从设备自动获取的正确值,现将代码贴出,恳请提供建议或帮助:
var BT_device=57:4C:54:03:1D:0D; var BT_service_id=18f0; var BT_characteristic_id=2af1; BT_test_template = function() { var data = []; var $_line = BT_line(42); data = [ //{"type":"command","value":HW_INIT}, {"type":"command","value":TXT_BOLD_ON}, {"type":"command","value":TXT_ALIGN_RT}, {"type":"text","value":"Test receipt"}, {"type":"command","value":TXT_BOLD_OFF}, {"type":"command","value":CTL_LF}, {"type":"command","value":CTL_LF}, {"type":"command","value":TXT_ALIGN_LT}, {"type":"command","value":TXT_FONT_B}, {"type":"text","value": $_line }, {"type":"command","value":CTL_LF}, //{"type":"command","value":TXT_NORMAL}, {"type":"text","value": "Cheese burger" }, {"type":"command","value":CTL_LF}, {"type":"text","value": "1 x 2.00" }, {"type":"command","value":CTL_LF}, //{"type":"command","value":TXT_FONT_B}, {"type":"text","value": $_line }, {"type":"command","value":CTL_LF}, {"type":"command","value":TXT_NORMAL}, {"type":"command","value":TXT_BOLD_ON}, {"type":"text","value": "TOTAL" }, {"type":"command","value":TXT_BOLD_OFF}, {"type":"command","value":CTL_LF}, {"type":"command","value":TXT_NORMAL}, {"type":"text","value": "Cash" }, {"type":"command","value":CTL_LF}, {"type":"command","value":CTL_LF} ]; return data; }; BT_print_test = function(){ try { current_page_id = getCurrentPage(); BT_device = $("#" + current_page_id + " .mac_address").val(); BT_service_id = $("#" + current_page_id + " .data1").val(); BT_characteristic_id = $("#" + current_page_id + " .data2").val(); BT_tpl = ''; if( !empty(BT_device) && !empty(BT_service_id) && !empty(BT_characteristic_id) ){ BT_tpl = BT_test_template(); checkBluetooth('BT_init_print'); } else { showToast( t("No selected printer. please restart your printer"), 'danger' ); } } catch(err) { dump2( err.message ); } }; checkBluetooth = function( $actions ){ if( isdebug() ){ BT_actions( $actions ) ; return ; } /*ANDROID*/ cordova.plugins.diagnostic.hasBluetoothSupport(function(available){ if(available){ cordova.plugins.diagnostic.isBluetoothEnabled(function(enabled){ if(enabled){ BT_actions( $actions ) ; } else { ble.enable(function(){ setTimeout(function(){ BT_actions( $actions ) ; }, 1*500 ); }, function(){ checkBluetooth(''); }); } }, function(error){ showToast( t("The following error occurred:") + " " + error , 'danger' ); }); } else { showToast( t("Bluetooth not available on this device") , 'danger' ); } }, function(error){ showToast( t("The following error occurred:") + " " + error , 'danger' ); }); }; BT_actions = function( $run_funtions ){ if( !empty($run_funtions) ){ switch ($run_funtions){ case "BT_init_print": setTimeout(function(){ BT_init_print(); }, 1*500 ); break; case "BT_startScan": setTimeout(function(){ BT_startScan(); }, 1*500 ); break; } } }; BT_init_print = function(){ showDialog(true,'printer_loader'); BT_print_done = false; BT_command_count = 0; BT_connect_data = false; /*DEBUG*/ if( isdebug() ){ setTimeout(function(){ BT_set_status( t("Connecting")+ "..." ); }, 1*500 ); BT_connect( BT_device ); BT_connect_status = setInterval(function(){ if(BT_connect_data){ BT_set_status( t("Printing") + "..." ); $result = BT_connect_data; dump($result); BT_connect_data = false; BT_service_id = $result.service; BT_characteristic_id = $result.characteristic; BT_print( BT_tpl ); } }, 1*1000 ); return ; } /*END DEBUG*/ ble.isConnected( BT_device , function(){ setTimeout(function(){ BT_set_status( t("Printing")+ "..." ); }, 1*500 ); BT_print( BT_tpl ); }, function(){ setTimeout(function(){ BT_set_status( t("Connecting")+ "..." ); }, 1*500 ); BT_connect_data = false; BT_connect( BT_device ); BT_connect_status = setInterval(function(){ if(BT_connect_data){ BT_set_status( t("Printing") + "..." ); $result = BT_connect_data; dump($result); BT_connect_data = false; BT_service_id = $result.service; BT_characteristic_id = $result.characteristic; BT_print( BT_tpl ); } }, 2*1000 ); }); }; BT_set_status = function(message){ $("#printer_loader h3").html( message ); }; BT_print = function(data){ dump("BT_print"); dump(data); BT_err_msg = ''; if(data.length>0){ $.each( data , function( data, val ) { setTimeout(function(){ dump(val); switch (val.type){ case "command": BT_command(val.value); break case "text": BT_text(val.value); break case "image": BT_image(val.value); break; } }, 1*1000); }); BT_cron = setInterval( BT_cron_print , 1*1000); } else { BT_print_done = true; BT_err_msg = t("No data to print"); } }; BT_cron_print = function(){ try { if( isdebug() ){ $time_out = 15*1000; } else { $time_out = 15*1000; if(cordova.platformId === "ios"){ $time_out = 4*1000; } } BT_print_timeout = setTimeout(function(){ BT_command_count = 0; showDialog(false,'printer_loader'); clearInterval(BT_cron); }, $time_out ); $tpl_count = BT_tpl.length; if( isNaN($tpl_count)){ BT_print_done = true; } if( BT_command_count >= BT_tpl.length ){ BT_print_done = true; } if(BT_print_done){ BT_command_count = 0; if(!empty(BT_err_msg)){ showToast( BT_err_msg , 'danger' ); } showDialog(false,'printer_loader'); clearInterval(BT_cron); clearTimeout(BT_print_timeout); } } catch(err) { dump2( err.message ); } }; BT_text = function($text){ try { var data = stringToBytes($text); ble.write( BT_device , BT_service_id , BT_characteristic_id , data, function(result){ BT_command_count++; }, function(err){ BT_command_count++; if ((typeof err.errorMessage !== "undefined") && ( err.errorMessage !== null)) { BT_err_msg+= err.errorMessage; BT_err_msg+="\n"; } else { BT_err_msg+= JSON.stringify(err); BT_err_msg+="\n"; } }); } catch(err) { BT_command_count++; BT_err_msg+= err.message; BT_err_msg+="\n"; } }; BT_command = function( $command ){ try { data = new Uint8Array( $command.length ); $.each( $command , function( key, val ) { data[key]=val; }); ble.write( BT_device , BT_service_id , BT_characteristic_id , data.buffer, function(result){ BT_command_count++; }, function(err){ BT_command_count++; if ((typeof err.errorMessage !== "undefined") && ( err.errorMessage !== null)) { BT_err_msg+= err.errorMessage; BT_err_msg+="\n"; } else { BT_err_msg+= JSON.stringify(err); BT_err_msg+="\n"; } }); } catch(err) { BT_command_count++; BT_err_msg+= err.message; BT_err_msg+="\n"; } };
确认BLE特征可写属性
蓝牙打印机的目标特征必须支持Write或Write Without Response属性。用BLE调试工具(如nRF Connect)扫描设备,查看2af1特征的属性,确保它具备写入权限,这是数据能发送到打印机的基础。修复异步写入的时序问题
当前用固定延迟的setTimeout触发写入操作,会导致蓝牙队列拥堵或数据丢包。必须改成链式异步调用,等待前一个写入完成后再执行下一个:BT_print = function(data) { dump("BT_print"); dump(data); BT_err_msg = ''; let index = 0; function writeNext() { if (index >= data.length) { BT_print_done = true; return; } const val = data[index]; dump(val); switch (val.type) { case "command": BT_command(val.value, writeNext); break; case "text": BT_text(val.value, writeNext); break; case "image": BT_image(val.value, writeNext); break; } index++; } if (data.length > 0) { writeNext(); BT_cron = setInterval(BT_cron_print, 1 * 1000); } else { BT_print_done = true; BT_err_msg = t("No data to print"); } };同时修改
BT_text和BT_command,传入回调函数确保顺序执行:BT_text = function($text, callback) { try { var data = stringToBytes($text); ble.write(BT_device, BT_service_id, BT_characteristic_id, data, function(result){ BT_command_count++; callback(); }, function(err){ BT_command_count++; BT_err_msg += err.errorMessage ? err.errorMessage + "\n" : JSON.stringify(err) + "\n"; callback(); }); } catch(err) { BT_command_count++; BT_err_msg += err.message + "\n"; callback(); } }; BT_command = function($command, callback) { try { const data = new Uint8Array($command.length); $.each($command, function(key, val) { data[key] = val; }); ble.write(BT_device, BT_service_id, BT_characteristic_id, data.buffer, function(result){ BT_command_count++; callback(); }, function(err){ BT_command_count++; BT_err_msg += err.errorMessage ? err.errorMessage + "\n" : JSON.stringify(err) + "\n"; callback(); }); } catch(err) { BT_command_count++; BT_err_msg += err.message + "\n"; callback(); } };验证打印指令的兼容性
TXT_BOLD_ON、CTL_LF等指令需要匹配打印机的ESC/POS指令集,不同品牌打印机指令可能有差异。比如标准ESC/POS的加粗开启指令是[0x1B, 0x45, 0x01],换行是[0x0A],请参考打印机官方文档替换对应指令。优化连接状态检测逻辑
代码中BT_connect函数未实现完整,建议在连接成功的回调中直接触发打印,避免依赖定时器轮询BT_connect_data,减少不可预期的延迟或失败。增强调试日志
打开dump3注释,或在ble.write的回调中打印写入的数据长度、设备响应结果,确认数据是否成功发送到打印机,定位无响应的具体环节。
内容的提问来源于stack exchange,提问作者shayan izadi

