In this TechTip we’ll be looking at printing a label to a Zebra printer by sending the ZPL string to the com port or Ethernet port on the HMI. Zebra printers are increasingly popular in production and packaging and are highly configurable and able to print barcodes, QR codes, Data matrix codes etc. (for reference, the printer used in this demo was a Zebra GK420t.) This isn’t intended to be a lesson in the ZPL language, but we will run through the label string used for our demo: The Label we’re creating, the variable data shown with { } – we’ll replace these variables with HMI data The code we’ll use for this label is below: ^XA ^CF0,50 ^FO100,50^FDLamonde Automation Ltd.^FS ^CF0,20 ^FO100,100^FDDemonstration of using a Weintek HMI to generate a label on a^FS ^FO100,120^FDa zebra printer using free protocol.^FS ^FO100,160 ^BXN,10,200,,,,,1 ^FDPart Number: {PARTNUMBER} ^FS ^CF0,40 ^FO350,170^FDDate: {DD}:{MM}:{YY}^FS ^FO350,210^FDTime: {HH}:{MM}:{SS}^FS ^FO350,250^FDOperator: {OPERATOR}^FS ^XZ We can preview and tweak it using this webpage. A breakdown of our demo ZPL code is below… a useful reference is this PDF. ^XA A Zebra ZPL string starts with command ^XA ^CF0,50 ^CFf,h,w is selecting a font (A-Z & 0-9), its height in dots and width in dots. Leaving “w” out means standard width for the height” . So here we are using font 0, 50 dots high, standard width. ^FO100,50^FDLamonde Automation Ltd.^FS ^FO,x,y,z is “Field Origin” x is the x position, y is the y position and z is the justification (0=left, 1 =right 2=script dependant). ^FD is the Field Data tag. and ^FS is the Field Separator tag. Our text field is at position 100,50. ^CF0,20 Changed font size here to 20 dots high, standard width ^FO100,100^FDDemonstration of using a Weintek HMI to generate a label on a^FS Field Origin of 100,100 and some text ^FO100,120^FDa zebra printer using free protocol.^FS Field origin of 100,120 and some more text ^FO100,160 Field origin of 100,60 for our datamatrix ^BXN,10,200,,,,,1 ^BXo,h,s,c,r,f,g,a is the code format for generating a datamatrix “o” is orientation (N=normal, R = rotated 90 c/w, I = inverted, B= read bottom up). “h” is height, “s” is quality (0, 50, 80, 100, 140, 200), “c” is columns to encode, “r” is rows to encode, “f” is format ID (0-6) – default is 6, which is full 256 ISO 8-bit set, “g” is escape control character, and finally “a” is aspect ratio – 1 = square, 2 = rectangle ^FDPart Number: {PARTNUMBER} This is the Field Data that we will be putting in the datamatrix ^FS Field separator ^CF0,40 Font 0, 40 dots high, default width ^FO350,170^FDDate: {DD}:{MM}:{YY}^FS Field origin 30,170, Field Data, Field Separator ^FO350,210^FDTime: {HH}:{MM}:{SS}^FS Field origin 350,210, Field Data, Field Separator ^FO350,250^FDOperator: {OPERATOR}^FS Field origin 350,250, Field Data, Field Separator ^XZ A Zebra string ends with command ^XZ HMI Communication Setup For Serial -you may need to change to suit your printer setup, but these are common settings – 9600, Parity: None, 8 Data Bits, 1 Stop bit: For Ethernet -set the IP address to suit your printer, the port number for Zebra printers is usually 9100, but check your device: Building The String and Sending To The Printer In a previous TechTip, we covered using “StringLength” this TechTip uses what we did there to generate a string to send to a Zebra printer. We’re using StringLength since we don’t want any null characters and our data may vary in length. The “partnumber” variable is from HMI Local Word LW0 onwards and “operator” variable is from HMI Local Word LW100 onwards. The resulting string is also placed in LW 1000 onwards – useful for verifying the output without printing using offline simulation. Of course, in your application, you could use data from your PLC or connected device instead of HMI local words. Time and date are taken from the HMI Local words -these are converted from decimal to ascii using the DEC2ASCII command. The macro in full is shown below: macro_command main() char outputstring[600] // the output string char header[300]="^XA^CF0,50^FO100,50^FDLamonde Automation Ltd.^FS^CF0,20^FO100,100^FDDemonstration of using a Weintek HMI to generate a label on a^FS^FO100,120^FDa zebra printer using free protocol.^FS^FO100,160^BXN,10,200,,,,,1^FD" // first fixed part of the code char partnumber[24] // partnumber is ascii char from LW0 onwards char operator[10] // operator variable (which we'll get from LW100 onwards) char fs[3]="^FS" // fixed "^FS" code char datepart[40]="^CF0,40^FO350,170^FDDate: " // Fixed text for date label - ascii char char timepart[40] ="^FO350,210^FDTime: " // Fixed text for time label - ascii char char operatorpart[40]="^FO350,250^FDOperator: " // Fixed text for operator label - ascii char char xz[3]="^XZ" char dateday[2] // day as ascii char char datemonth[2] // month as ascii char char dateyear[4] // year as ascii char char colon[1]=":" // colon separator char hours[2] // hours as ascii char char minutes[2]="06" // mins as ascii char char seconds[2]="00" // secs as ascii char short datedaydec // day as 16bit integer from the HMI short datemonthdec // month as 16bit integer from the HMI short dateyeardec // year as 16bit integer from the HMI short hoursdec // hours as 16bit integer from the HMI short minutesdec // minutes as 16bit integer from the HMI short secondsdec // seconds as 16bit integer from the HMI short length1 // first offset used in string concatenation short length2 // second offset used in string concatenation FILL(OutputString[0], 0, 600) // clear outputstring GetData(datedaydec, "Local HMI", LW, 9020, 1) // get the day as a number and... DEC2ASCII(datedaydec, dateday[0], 2) // convert to ascii and put in dateday GetData(datemonthdec, "Local HMI", LW, 9021, 1) // get the month as a number and... DEC2ASCII(datemonthdec, datemonth[0], 2) // convert to ascii and put in datemonth GetData(dateyeardec, "Local HMI", LW, 9022, 1) // get the year as a number and... DEC2ASCII(dateyeardec, dateyear[0], 4) // convert to ascii and put in dateyear GetData(hoursdec, "Local HMI", LW, 9019, 1) // get the hours as a number and... DEC2ASCII(hoursdec, hours[0], 2) // convert to ascii and put in hours GetData(minutesdec, "Local HMI", LW, 9018, 1) // get the minutes as a number and... DEC2ASCII(minutesdec, minutes[0], 2) // convert to ascii and put in minutes GetData(secondsdec, "Local HMI", LW, 9017, 1) // get the seconds as a number and... DEC2ASCII(secondsdec, seconds[0], 2) // convert to ascii and put in seconds GetData(partnumber[0], "Local HMI", LW, 0, 24) // get partnumber GetData(operator[0], "Local HMI", LW, 100, 10) // get operator name StringCat(header[0], OutputString[0]) // concatenate header into OutputString length1 = StringLength(header[0]) // measure header and put result into “length1” StringCat(partnumber[0], OutputString[length1]) // offset partnumber into outputstring by length of header (length1) length2 = StringLength(partnumber[0]) + length1 // measure partnumber and add to length1 to give the next offset StringCat(fs[0], OutputString[length2]) // use length2 as the offset length1 = StringLength(fs[0]) + length2 // measure fs and add to length2 to give the next offset StringCat(datepart[0], OutputString[length1]) // etc length2 = StringLength(datepart[0]) + length1 StringCat(dateday[0], OutputString[length2]) // etc length1 = StringLength(dateday[0]) + length2 StringCat(colon[0], OutputString[length1]) length2 = StringLength(colon[0]) + length1 StringCat(datemonth[0], OutputString[length2]) length1 = StringLength(datemonth[0]) + length2 StringCat(colon[0], OutputString[length1]) length2 = StringLength(colon[0]) + length1 StringCat(dateyear[0], OutputString[length2]) length1 = StringLength(dateyear[0]) + length2 StringCat(fs[0], OutputString[length1]) length2 = StringLength(fs[0]) + length1 StringCat(timepart[0], OutputString[length2]) length1 = StringLength(timepart[0]) + length2 StringCat(hours[0], OutputString[length1]) length2 = StringLength(hours[0]) + length1 StringCat(colon[0], OutputString[length2]) length1 = StringLength(colon[0]) + length2 StringCat(minutes[0], OutputString[length1]) length2 = StringLength(minutes[0]) + length1 StringCat(colon[0], OutputString[length2]) length1 = StringLength(colon[0]) + length2 StringCat(seconds[0], OutputString[length1]) length2 = StringLength(seconds[0]) + length1 StringCat(fs[0], OutputString[length2]) length1 = StringLength(fs[0]) + length2 StringCat(operatorpart[0], OutputString[length1]) length2 = StringLength(operatorpart[0]) + length1 StringCat(operator[0], OutputString[length2]) length1 = StringLength(operator[0]) StringCat(fs[0], OutputString[length1]) length2 = StringLength(fs[0]) + length1 StringCat(xz[0], OutputString[length2]) length1 = StringLength(xz[0]) OUTPORT(outputstring[0], "Free Protocol", 600) // fire it out of the serial or ethernet port depeding on device config SetData(outputstring[0], "Local HMI", LW, 1000, 600)// use this to check your assembled string on screen using offline simulator end macro_command As usual, we have a demo project available for download here.
For more information on TechTip: Using Weintek HMI to Print a label on a Zebra Printer using Free Protocol talk to Lamonde Automation Ltd