diff --git a/index.html b/index.html index 06e6add..87291f0 100644 --- a/index.html +++ b/index.html @@ -1,14 +1,20 @@ - + + +

WebBLE with Paperang

- + + +
+
+
\ No newline at end of file diff --git a/script.js b/script.js index 243d13f..a3f1307 100644 --- a/script.js +++ b/script.js @@ -1,22 +1,136 @@ var btservice; var sockchar; -// service: 49535343-fe7d-4ae5-8fa9-9fafd205e455 -// characteristic: 49535343-6daa-4d02-abf6-19569aca69fe - -async function connect(){ +var notify; +/* +Some useful facts about printers: + service: 49535343-fe7d-4ae5-8fa9-9fafd205e455 +characteristics values are + Write to: 49535343-6daa-4d02-abf6-19569aca69fe + notify: 49535343-1e4d-4bd9-ba61-23c647249616 +On BLE, max size of one pack is 512, means 502 for each data pack. +*/ +async function connect() { + // Call browser popup to let user select device let printer = await navigator.bluetooth.requestDevice({ - acceptAllDevices:true, - optionalServices:['49535343-fe7d-4ae5-8fa9-9fafd205e455'] + acceptAllDevices: true, + optionalServices: ['49535343-fe7d-4ae5-8fa9-9fafd205e455'] }); console.log(printer); - - let pserver = await printer.gatt.connect(); + + let pserver = await printer.gatt.connect(); // BLE connect btservice = await pserver.getPrimaryService('49535343-fe7d-4ae5-8fa9-9fafd205e455'); sockchar = await btservice.getCharacteristic('49535343-6daa-4d02-abf6-19569aca69fe'); + notify = await btservice.getCharacteristic('49535343-1e4d-4bd9-ba61-23c647249616'); + notify.startNotifications().then(() => { + notify.addEventListener('characteristicvaluechanged', (event) => { + let value = event.target.value; + let a = []; + // Convert raw data bytes to hex values just for the sake of showing something. + // In the "real" world, you'd use data.getUint8, data.getUint16 or even + // TextDecoder to process raw data bytes. + for (let i = 0; i < value.byteLength; i++) { + a.push(('00' + value.getUint8(i).toString(16)).slice(-2)); + } + console.log('Notify:' , a.join(' ')); + }) + }) +} + +async function selfdiag() { + let cmdstr = Uint8Array.from([2, 27, 0, 1, 0, 0, 70, 137, 94, 158, 3]); + await sockchar.writeValue(cmdstr); +} + +async function sendprint(data) { + if (data.length < 500) { + await sockchar.writeValue(genpack(0, data)); + return; + } + function aslice(arr, size) { + let ret = []; + for (let i = 0; i < arr.length; i += size) { + let piece = arr.slice(i, i + size); + ret.push(piece); + } + return ret; + } + let packs = aslice(data, 500); + console.log(packs) + for (each in packs) { + let m = await sockchar.writeValue(genpack(0, packs[each])); + console.log(m) + } +} + + +function randfill(n) { + let arr = Array(n); + for (let i = 0; i < n; i += 40) { + for (let j = 0; j < 40; j++) { + if (j % 2) { + arr[i + j] = 255; + } else { + arr[i + j] = 0; + } + + } + } + return arr; } -async function selfdiag(){ - let cmdstr = Uint8Array.from([2,27,0,1,0,0,70,137,94,158,3]); - sockchar.writeValue(cmdstr); + +// --- JSDoc Enabled funcs + +/** + * + * @param {int} cmd Command Byte (1 byte) + * @param {array} data Data Array (Max 2016 bytes) + * @param {int} packetid Packet id (1 byte) + */ +function genpack(cmd, data = [0], packetid = 0) { + function b32split(i) { + // js sucks to be a low level byte manipulator. + let r = new ArrayBuffer(4); + let v = new DataView(r); + v.setUint32(0, i, true); + let t8 = new Uint8Array(r); + let arr = [...t8]; + return arr; + } + let length = data.length; + if (length > 2016) { console.error("genpack: Requested pack too large"); return null; }; + let bytes = Array(); + bytes.push(2); //First byte 0x02 + bytes.push(cmd); // 2nd byte is command + bytes.push(packetid); // 3rd byte is packet id + + // Spliting the length byte + let lengthByte = b32split(length); + bytes.push(lengthByte[0], lengthByte[1]); // 4th and 5th byte is length + + // append payload, starts from 6th byte. + bytes = bytes.concat(data); + + // calculate CRC32. + let crcval = CRC32.buf(data, 0x35769521); + bytes = bytes.concat(b32split(crcval)); // append that 4 bytes. + + bytes.push(3); // last ending byte + + let bytestream = Uint8Array.from(bytes) + return bytestream; +} + +/* +* Graphical rendering +*/ + +function bitmapRender(){ + let node = document.getElementById("inputtext"); + domtoimage.toPng(node, {width: 340}).then(function(dataUrl){ + var img = new Image(); + img.src = dataUrl; + document.body.appendChild(img); + }) } \ No newline at end of file diff --git a/util/crc32.js b/util/crc32.js new file mode 100644 index 0000000..c92664a --- /dev/null +++ b/util/crc32.js @@ -0,0 +1,115 @@ +/*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +/*exported CRC32 */ +var CRC32; +(function (factory) { + /*jshint ignore:start */ + /*eslint-disable */ + if(typeof DO_NOT_EXPORT_CRC === 'undefined') { + if('object' === typeof exports) { + factory(exports); + } else if ('function' === typeof define && define.amd) { + define(function () { + var module = {}; + factory(module); + return module; + }); + } else { + factory(CRC32 = {}); + } + } else { + factory(CRC32 = {}); + } + /*eslint-enable */ + /*jshint ignore:end */ +}(function(CRC32) { +CRC32.version = '1.2.2'; +/*global Int32Array */ +function signed_crc_table() { + var c = 0, table = new Array(256); + + for(var n =0; n != 256; ++n){ + c = n; + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + table[n] = c; + } + + return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table; +} + +var T0 = signed_crc_table(); +function slice_by_16_tables(T) { + var c = 0, v = 0, n = 0, table = typeof Int32Array !== 'undefined' ? new Int32Array(4096) : new Array(4096) ; + + for(n = 0; n != 256; ++n) table[n] = T[n]; + for(n = 0; n != 256; ++n) { + v = T[n]; + for(c = 256 + n; c < 4096; c += 256) v = table[c] = (v >>> 8) ^ T[v & 0xFF]; + } + var out = []; + for(n = 1; n != 16; ++n) out[n - 1] = typeof Int32Array !== 'undefined' ? table.subarray(n * 256, n * 256 + 256) : table.slice(n * 256, n * 256 + 256); + return out; +} +var TT = slice_by_16_tables(T0); +var T1 = TT[0], T2 = TT[1], T3 = TT[2], T4 = TT[3], T5 = TT[4]; +var T6 = TT[5], T7 = TT[6], T8 = TT[7], T9 = TT[8], Ta = TT[9]; +var Tb = TT[10], Tc = TT[11], Td = TT[12], Te = TT[13], Tf = TT[14]; +function crc32_bstr(bstr, seed) { + var C = seed ^ -1; + for(var i = 0, L = bstr.length; i < L;) C = (C>>>8) ^ T0[(C^bstr.charCodeAt(i++))&0xFF]; + return ~C; +} + +function crc32_buf(B, seed) { + var C = seed ^ -1, L = B.length - 15, i = 0; + for(; i < L;) C = + Tf[B[i++] ^ (C & 255)] ^ + Te[B[i++] ^ ((C >> 8) & 255)] ^ + Td[B[i++] ^ ((C >> 16) & 255)] ^ + Tc[B[i++] ^ (C >>> 24)] ^ + Tb[B[i++]] ^ Ta[B[i++]] ^ T9[B[i++]] ^ T8[B[i++]] ^ + T7[B[i++]] ^ T6[B[i++]] ^ T5[B[i++]] ^ T4[B[i++]] ^ + T3[B[i++]] ^ T2[B[i++]] ^ T1[B[i++]] ^ T0[B[i++]]; + L += 15; + while(i < L) C = (C>>>8) ^ T0[(C^B[i++])&0xFF]; + return ~C; +} + +function crc32_str(str, seed) { + var C = seed ^ -1; + for(var i = 0, L = str.length, c = 0, d = 0; i < L;) { + c = str.charCodeAt(i++); + if(c < 0x80) { + C = (C>>>8) ^ T0[(C^c)&0xFF]; + } else if(c < 0x800) { + C = (C>>>8) ^ T0[(C ^ (192|((c>>6)&31)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|(c&63)))&0xFF]; + } else if(c >= 0xD800 && c < 0xE000) { + c = (c&1023)+64; d = str.charCodeAt(i++)&1023; + C = (C>>>8) ^ T0[(C ^ (240|((c>>8)&7)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|((c>>2)&63)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|(d&63)))&0xFF]; + } else { + C = (C>>>8) ^ T0[(C ^ (224|((c>>12)&15)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|((c>>6)&63)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|(c&63)))&0xFF]; + } + } + return ~C; +} +CRC32.table = T0; +// $FlowIgnore +CRC32.bstr = crc32_bstr; +// $FlowIgnore +CRC32.buf = crc32_buf; +// $FlowIgnore +CRC32.str = crc32_str; +})); diff --git a/util/dom-to-image.min.js b/util/dom-to-image.min.js new file mode 100644 index 0000000..bc73227 --- /dev/null +++ b/util/dom-to-image.min.js @@ -0,0 +1,2 @@ +/*! dom-to-image 10-06-2017 */ +!function(a){"use strict";function b(a,b){function c(a){return b.bgcolor&&(a.style.backgroundColor=b.bgcolor),b.width&&(a.style.width=b.width+"px"),b.height&&(a.style.height=b.height+"px"),b.style&&Object.keys(b.style).forEach(function(c){a.style[c]=b.style[c]}),a}return b=b||{},g(b),Promise.resolve(a).then(function(a){return i(a,b.filter,!0)}).then(j).then(k).then(c).then(function(c){return l(c,b.width||q.width(a),b.height||q.height(a))})}function c(a,b){return h(a,b||{}).then(function(b){return b.getContext("2d").getImageData(0,0,q.width(a),q.height(a)).data})}function d(a,b){return h(a,b||{}).then(function(a){return a.toDataURL()})}function e(a,b){return b=b||{},h(a,b).then(function(a){return a.toDataURL("image/jpeg",b.quality||1)})}function f(a,b){return h(a,b||{}).then(q.canvasToBlob)}function g(a){"undefined"==typeof a.imagePlaceholder?v.impl.options.imagePlaceholder=u.imagePlaceholder:v.impl.options.imagePlaceholder=a.imagePlaceholder,"undefined"==typeof a.cacheBust?v.impl.options.cacheBust=u.cacheBust:v.impl.options.cacheBust=a.cacheBust}function h(a,c){function d(a){var b=document.createElement("canvas");if(b.width=c.width||q.width(a),b.height=c.height||q.height(a),c.bgcolor){var d=b.getContext("2d");d.fillStyle=c.bgcolor,d.fillRect(0,0,b.width,b.height)}return b}return b(a,c).then(q.makeImage).then(q.delay(100)).then(function(b){var c=d(a);return c.getContext("2d").drawImage(b,0,0),c})}function i(a,b,c){function d(a){return a instanceof HTMLCanvasElement?q.makeImage(a.toDataURL()):a.cloneNode(!1)}function e(a,b,c){function d(a,b,c){var d=Promise.resolve();return b.forEach(function(b){d=d.then(function(){return i(b,c)}).then(function(b){b&&a.appendChild(b)})}),d}var e=a.childNodes;return 0===e.length?Promise.resolve(b):d(b,q.asArray(e),c).then(function(){return b})}function f(a,b){function c(){function c(a,b){function c(a,b){q.asArray(a).forEach(function(c){b.setProperty(c,a.getPropertyValue(c),a.getPropertyPriority(c))})}a.cssText?b.cssText=a.cssText:c(a,b)}c(window.getComputedStyle(a),b.style)}function d(){function c(c){function d(a,b,c){function d(a){var b=a.getPropertyValue("content");return a.cssText+" content: "+b+";"}function e(a){function b(b){return b+": "+a.getPropertyValue(b)+(a.getPropertyPriority(b)?" !important":"")}return q.asArray(a).map(b).join("; ")+";"}var f="."+a+":"+b,g=c.cssText?d(c):e(c);return document.createTextNode(f+"{"+g+"}")}var e=window.getComputedStyle(a,c),f=e.getPropertyValue("content");if(""!==f&&"none"!==f){var g=q.uid();b.className=b.className+" "+g;var h=document.createElement("style");h.appendChild(d(g,c,e)),b.appendChild(h)}}[":before",":after"].forEach(function(a){c(a)})}function e(){a instanceof HTMLTextAreaElement&&(b.innerHTML=a.value),a instanceof HTMLInputElement&&b.setAttribute("value",a.value)}function f(){b instanceof SVGElement&&(b.setAttribute("xmlns","http://www.w3.org/2000/svg"),b instanceof SVGRectElement&&["width","height"].forEach(function(a){var c=b.getAttribute(a);c&&b.style.setProperty(a,c)}))}return b instanceof Element?Promise.resolve().then(c).then(d).then(e).then(f).then(function(){return b}):b}return c||!b||b(a)?Promise.resolve(a).then(d).then(function(c){return e(a,c,b)}).then(function(b){return f(a,b)}):Promise.resolve()}function j(a){return s.resolveAll().then(function(b){var c=document.createElement("style");return a.appendChild(c),c.appendChild(document.createTextNode(b)),a})}function k(a){return t.inlineAll(a).then(function(){return a})}function l(a,b,c){return Promise.resolve(a).then(function(a){return a.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),(new XMLSerializer).serializeToString(a)}).then(q.escapeXhtml).then(function(a){return''+a+""}).then(function(a){return''+a+""}).then(function(a){return"data:image/svg+xml;charset=utf-8,"+a})}function m(){function a(){var a="application/font-woff",b="image/jpeg";return{woff:a,woff2:a,ttf:"application/font-truetype",eot:"application/vnd.ms-fontobject",png:"image/png",jpg:b,jpeg:b,gif:"image/gif",tiff:"image/tiff",svg:"image/svg+xml"}}function b(a){var b=/\.([^\.\/]*?)$/g.exec(a);return b?b[1]:""}function c(c){var d=b(c).toLowerCase();return a()[d]||""}function d(a){return a.search(/^(data:)/)!==-1}function e(a){return new Promise(function(b){for(var c=window.atob(a.toDataURL().split(",")[1]),d=c.length,e=new Uint8Array(d),f=0;f