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'"}).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