Printing label from Node-Red Flow

Hi Everyone,

I found this post and it's exactly what I'm looking to do(print labels from Node-Red Flow):

@Steve-Mcl and @madmax were discussing generating labels with QR codes from a flow. I modified it slightly and am now generating the PDF file necessary for my application. However it was never discussed how this PDF was being printed out on the label printer. @madmax can provide more information on how you actually printed out the label? Also what label printer did you use?

Thank you,
Travis

Hi!

I use the one Zebra GK420d.

I print it directly from lp linux.

Here's an example
msg.druck = "lp -d Zebra_Etiketten_duckerpi -o media=94x264 "+msg.filename;
return msg;

What kind of system are you? raspberry pi? mac?

1 Like

Hi @madmax,

Thank you so much for replying!

I'll be running Node-Red on a Mac. I'm open to label printers so I'll take a look at the Zebra GK420d.

What are you sending that object to? An Exec node? I have never worked with a printer from software so this is new territory for me.

Just looked at the printer. I was hoping for a cut tape printer and not thermal. These labels will be placed on devices we ship to customers so they need to be a bit more "rugged". I'll see if I can find an IP addressable cut tape printer that might be suitable.

Something like this would be ideal.

The p-touch models can usually be operated from node.

E.g...

If not, there seems to be a CLI you could call from an exec node.

1 Like

Hi @Steve-Mcl,

Thank you very much for chiming in. Much appreciated.

Yes, I was looking at the node-ptouch package(had done a p-touch nodejs search). The example just shows text but this must be using raster commands to send data to the printer I assume. Brother has some convoluted documentation on it here. Comparing the constants.js file in the NodeJS library to the commands in Brother's docs don't 100% line up but they're close which give me hope.

I think I can covert the PDF to a Raster, and then feed it to the printer over a socket. I'm about 99% this will work.

I can't do much at this point as I don't have a P Touch printer. Considering ordering one. Just tough to do if I'm not certain it will work.

Thoughts?

The PDF comes after the barcode creation which is already a raster image. Just ditch the PDF part. I never understood why the op of the other thread needed that.

1 Like

Got it. So is Base64 a raster image? Sorry, I've worked with Base64 displaying images on small embedded devices, but this is my first encounter with Rasters.

@Steve-Mcl,

I'd like to start by thanking you for your time thus far. I know this is getting outside the scope of this forum but I will convert my development to an NPM library and also create a Node for Node-Red if I can get this to work in case it is of some use to others in the future.

Update on this project.

I have a Brother P950NW here I am working with now.

Unfortunately the off the shelf P Touch NPM libraries are of little to no use as they are either not compatible or not even complete.

My current work flow is using Brother's P Touch Editor software to send print files over the network connection, I then use WireShark to sniff the data being sent over the TCP socket on port 9100. Through this I have been able to determine the control commands being sent:

// 300 00 bytes Invalidate
// 1b 69 61 01 Switch to Raster Mode
// 1b 40 Initialize
// 1b 69 7a c4 00 18 00 c9 02 00 00 02 00 Print information Command
// 1b 69 4b 08 Cut Mode
// 1b 69 4d 40 Enable Cut Mode
// 1b 69 41 01 Auto Cut Every Sheet
// 1b 69 6b 63 01 00 Unknown
// 1b 69 64 Margin
// ...image bytes
// 1b 69 61 ff Unknown, runs at end.  Assume exits raster mode.

I determined what the commands are by referencing this Brother documentation.

Now sniffing the packets sent by the P Touch utility the buffer arrays are quite large and look to be Binary Bitmap buffers which matches up to the Brother Documentation. A print request that contains just a QR code and some text similar to what is generated in the Node-Red flow comes out to a payload size of 35848 bytes. Conversely if I output a Buffer object from the Base64 node in the flow it is only around 890 bytes. So this is a red flag.

I did try writing the commands and image file buffer array generated by the base64 image node but it just prints a really long blank label.

I believe what needs to be done is to covert the Base64 image buffer object to a BMP(bit map) array, this however is my first encounter with this stuff so hoping someone can provide some insight.

No, base64 is a string version of something (in this case, an image)

What you likely need is the bitmap data.

This can be easily achieved by returning a jimp image from an image node then accessing msg.payload.bitmap.data (I think/off top of head, use a debug to see)

1 Like

@Steve-Mcl,

Ok that's getting somewhere. That generated 40kb of data.

Now it seems to be an array of 0x00 and 0xFF bytes. I assume these are just on/off bits for the dot matrix.

In contrast the array sent out by the P-Touch editor software is not just an array of 0x00/0xFF bytes. Here is just a portion of the image file sent by the P-Touch Software:

1b 40 1b 69 7a c4 00 18 00 ae 00 00 00 02 00 1b
69 4b 08 1b 69 4d 40 1b 69 41 01 1b 69 6b 63 01
00 1b 69 64 0e 00 4d 02 47 02 00 bb 00 47 02 00
bb 00 47 02 00 bb 00 47 02 00 bb 00 47 02 00 bb
00 47 02 00 bb 00 47 02 00 bb 00 47 02 00 bb 00
47 02 00 bb 00 47 02 00 bb 00 47 02 00 bb 00 47
02 00 bb 00 47 02 00 bb 00 47 02 00 bb 00 47 02
00 bb 00 47 02 00 bb 00 47 02 00 bb 00 47 08 00
e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff
ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08
00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f
ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47
08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00
1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00
47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00
00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7
00 47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8
00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff
d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08 00
e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff
ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08
00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f
ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47
08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00
1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00
47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00
00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7
00 47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8
00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff
d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08 00
e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff
ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08
00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f
ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47
08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00
1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00
47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00
00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7
00 47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8
00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff
d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08 00
e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff
ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08
00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f
ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47
08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00
1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00
47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00
00 1f ff ff d7 00 47 08 00 e8 00 00 1f eb ff eb
00 47 08 00 e8 00 00 1f eb ff eb 00 47 08 00 e8
00 00 1f eb ff eb 00 47 08 00 e8 00 00 1f eb ff
eb 00 47 08 00 e8 00 00 1f eb ff eb 00 47 08 00
e8 00 00 1f eb ff eb 00 47 08 00 e8 00 00 1f eb
ff eb 00 47 08 00 e8 00 00 1f eb ff eb 00 47 08
00 e8 00 00 1f eb ff eb 00 47 08 00 e8 00 00 1f
eb ff eb 00 47 08 00 e8 00 00 1f eb ff eb 00 47
08 00 e8 00 00 1f eb ff eb 00 47 08 00 e8 00 00
1f eb ff eb 00 47 08 00 e8 00 00 1f eb ff eb 00
47 08 00 e8 00 00 1f eb ff eb 00 47 08 00 e8 00
00 1f eb ff eb 00 47 08 00 e8 00 00 1f eb ff eb
00 47 08 00 e8 00 00 1f eb ff eb 00 47 08 00 e8
00 00 1f eb ff eb 00 47 08 00 e8 00 00 1f eb ff
eb 00 47 08 00 e8 00 00 1f eb ff eb 00 47 08 00
e8 00 00 1f eb ff eb 00 47 08 00 e8 00 00 1f eb
ff eb 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08
00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f
ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47
08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00
1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00
47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00
00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7
00 47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8
00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff
d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08 00
e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff
ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08
00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f
ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47
08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00
1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00
47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00
00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7
00 47 08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8
00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff ff
d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08 00
e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f ff
 ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47 08
00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00 1f
ff ff d7 00 47 08 00 e8 00 00 1f ff ff d7 00 47
08 00 e8 00 00 1f ff ff d7 00 47 08 00 e8 00 00
1f ff ff d7 00 47 08 00

There appears to be more information in this array than just on/off bits.

Still Digging.

There is definitely a reoccurring pattern here:

0e 00 4d 02 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
02 00 bb 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 
08 00 e8 00 00 1f ff ff d7 00 47 

I might be getting too deep in the weeds here. God I hate printers.

I would capture a full transmission from the application, save it to file and then play it back from node red to confirm you can achieve a good print.

After that, I'd dissect the file and attempt to insert my own image.

I'd also probably grab the image data from the good transmission and set the pixels of a jimp image to see if it looks correct.

It might be the image data in the transmission is an odd layout, may not contain alpha, may be 8 bit etc etc.

Are there any clues in the manual?

1 Like

@Steve-Mcl,

You read my mind. I printed just a period to the label printer from the P-Touch software. Obtained the transmission payload using WireShark, then replicated it in my NodeJS test app.

So you can see where I'm at here is the test app I'm currently running:

Through this method my test app does in fact print a label with a period. Which makes sense as the transmission data in WireShark matches 100%.

Still trying to figure out the format of this image data. That seems to be the missing link.

maybe now try some other simple characters with straight edges like L or I or <

1 Like

Just added a print file for a pipe character. Update GitHub repo.

Well good news and bad news.

Good news is I am successfully printing labels on the Brother P950NW P-Touch label printer.

Bad news is I never could get the Raster command set working. There was just too much to unpack and the documentation was not clear enough to implement a working solution.

I instead had to utilize their ESC/P command set which has support for printing Text, Bar Codes, and QR codes. My requirements were just to print a QR code then to the right of that some text. I was able to accomplish this with the ESC/P commands. Resulting in this demo label:

I wish I could have used Raster so we could create an image file for the label, then pass it into a print node that would print the image input into it. This would have made it more dynamic and would have greatly improved the usability of such a node. That said I am running out of time on this project so this solution will have to do for my current purposes.

That said if anyone wants to expand on this at a later date I would be happy to collaborate. I would think there are use cases for printing labels from Node-Red and I'm surprised there isn't anything developed yet.

The P-Touch printer line is really nice and the P950NW is a great network connected industrial printer for use cases like this.

Here is a demo flow. Hopefully someone will find it useful in the future.
Note you will need to change the IP address in the TCP Out node to match the IP of the printer on your network. This was tested on the P950NW only, however it should work with any PXXX series printer.

[{"id":"7485e39c17734bdd","type":"inject","z":"3e723b31668690b3","name":"","props":[{"p":"address","v":"00:13:A2:00:01:02:03:04","vt":"str"},{"p":"engineer","v":"Morgan","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":810,"y":1100,"wires":[["ed5f98e9b4d777e8"]]},{"id":"ed5f98e9b4d777e8","type":"function","z":"3e723b31668690b3","name":"","func":"var PTOUCH_ESC_P_MODE = Buffer.from([0x1b, 0x69, 0x61, 0x00]);\nvar INITIALIZE = Buffer.from([0x1b,0x40]);\nvar LARGE_QR = Buffer.from([0x1b,0x69,0x51,0x0C,0x02,0x00,0x00,0x00,0x00,0x02,0x00]);\nvar AUTO_LENGTH = Buffer.from([0x1b,0x69,0x6c,0x00,0x00]);\nvar LEFT_MARGIN = Buffer.from([0x1b, 0x24, 0x3C, 0x00]);\nvar FONT = Buffer.from([0x1B, 0x6B, 0x00]);\nvar FONT_SIZE = Buffer.from([0x1B, 0x58, 0x34]);\n\nvar payload = [];\npayload.push(PTOUCH_ESC_P_MODE); //Command to put printer in ESC P command mode\npayload.push(INITIALIZE); //Command to initialize printer for commands\n\n//QR Code Bytes\npayload.push(LARGE_QR); //QR code config bytes 12 dots per per cell, Model 2, auto input\npayload.push(Buffer.from(msg.address+\"\\\\\\\\\\\\\", \"utf-8\")); //Payload to embed in QR Code.  Note requires three \\ to end QR payload\n\n//Text bytes\npayload.push(AUTO_LENGTH); //Auto set length of label\npayload.push(LEFT_MARGIN); //1\" left margin to allow for QR code on left side of label\npayload.push(FONT); //Helsinki font\npayload.push(FONT_SIZE); //12pt font size\npayload.push(Buffer.from(msg.address.substring(0,11)+\"\\n\", \"utf-8\"));  //print first half of address on top line\npayload.push(LEFT_MARGIN); //1\" left margin to allow for QR code on left side of label\npayload.push(Buffer.from(msg.address.substring(12,23)+\"\\n\", \"utf-8\"));  //print second half of address on second line\npayload.push(LEFT_MARGIN); //1\" left margin to allow for QR code on left side of label\npayload.push(Buffer.from(\"QC: \"+msg.engineer, \"utf-8\")); //print quality control engineer on third line\npayload.push(Buffer.from([0x0c])); //execute print command\n\nmsg.payload = Buffer.concat(payload);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":980,"y":1100,"wires":[["767988cf5db162d6"]]},{"id":"767988cf5db162d6","type":"tcp out","z":"3e723b31668690b3","host":"192.168.0.50","port":"9100","beserver":"client","base64":false,"end":true,"name":"","x":1200,"y":1100,"wires":[]}]

@Steve-Mcl @Steve-Mcl @madmax thank you for contributing what you could to this thread.

4 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.