How to siimulate thousands of devices in Node-red - iot
I have created a simulation of a device using Node red . Basically the device send UDP packets every second until it receives an acknowledgement. I have done that successfully
The problem is am trying to simulate 10000 of such devices. Hence I would need to automate the device creation/enrollment process. Is there a way I can achieve this in Node-red
The json below shows how one device is supposed to behave. it sends a UDP packet to a server and waits for an ACK . If the ACK is not received within a time period, It resends the packet.
Here is the node-red JSON of one device.
[{"id":"a53cc152.31f27","type":"comment","z":"d51aa60c.64d028","name":"Listener","info":"","x":106,"y":134,"wires":[]},{"id":"a972f44d.eaa9e8","type":"comment","z":"d51aa60c.64d028","name":"Sender","info":"","x":136,"y":332,"wires":[]},{"id":"ff1376ae.e08a58","type":"function","z":"d51aa60c.64d028","name":"stringToBytes ","func":"//var binary = msg.payload.toString(2);\n// 6 because it's zero based count\n//var bit7 = binary.charAt(6);\n//msg.payload = bit7;\n//return msg;\n str=msg.payload\n var ch, st, re = [];\n for (var i = 0; i < str.length; i++ ) {\n ch = str.charCodeAt(i); // get char \n st = []; // set up \"stack\"\n do {\n st.push( ch & 0xFF ); // push byte to stack\n ch = ch >> 8; // shift value down by 1 byte\n } \n while ( ch );\n // add stack contents to result\n // done because chars have \"wrong\" endianness\n re = re.concat( st.reverse() );\n }\n msg.payload=re\n // return an array of bytes\n return msg;\n\n","outputs":1,"noerr":0,"x":566,"y":381,"wires":[["98c6c5a8.aaf1c8"]]},{"id":"85d26758.5ee0f8","type":"function","z":"d51aa60c.64d028","name":"ConfigMessge","func":"msg.payload='830546613660520101010201225A8622475A862247E96665D15684EF030000242D0000000000E308000001FFA94F0A1F00FF0B08000000000000002E50000000000000000000000000000000000000000000000000'\nmsg.ip = '127.0.0.1'\nmsg.port='57920'\nreturn msg;","outputs":1,"noerr":0,"x":353,"y":375,"wires":[["ff1376ae.e08a58"]]},{"id":"c83f781f.ae3428","type":"debug","z":"d51aa60c.64d028","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":919,"y":632,"wires":[]},{"id":"98c6c5a8.aaf1c8","type":"http request","z":"d51aa60c.64d028","name":"ParserAPIRequest","method":"GET","ret":"txt","url":"http:/api","tls":"","x":590,"y":529,"wires":[["eac23a48.b479d8","66759bdf.7fd864"]]},{"id":"4a724471.e5158c","type":"inject","z":"d51aa60c.64d028","name":"TimerTrigger","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":148,"y":377,"wires":[[]]},{"id":"eac23a48.b479d8","type":"http response","z":"d51aa60c.64d028","name":"ParserAPIResponse","statusCode":"200","headers":{"content-type":"application/json"},"x":602,"y":614,"wires":[]},{"id":"66759bdf.7fd864","type":"function","z":"d51aa60c.64d028","name":"CheckForAck?","func":"// Loop function\n\ncontext.loops = context.loops || 0;\nmsg\nif (context.loops!==0)\n{\n\n\ncontext.ACK =0\ncontext.ACK=flow.get('ACK')|| 0;\n//msg.payload=context.ACK\nif(context.ACK === 1 ) \n { \n //\n//return msg;\n return [null, null];\n }\nelse\n{\n // msg.payload='No ACK triggered'+ context.ACK\n context.loops = context.loops + 1\n \n}\n \n \n}\n return [msg, msg];\n","outputs":"2","noerr":0,"x":857,"y":364,"wires":[["faa8c055.7dd7d","f8eb8444.61e8f8","a3286965.3120d8"],[]]},{"id":"a3286965.3120d8","type":"udp out","z":"d51aa60c.64d028","name":"Resend Packet","addr":"127.0.0.1","iface":"","port":"","ipv":"udp4","outport":"13500","base64":false,"multicast":"false","x":1116,"y":230,"wires":[]},{"id":"faa8c055.7dd7d","type":"debug","z":"d51aa60c.64d028","name":"Console.log","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":1095,"y":494,"wires":[]},{"id":"f8eb8444.61e8f8","type":"delay","z":"d51aa60c.64d028","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":848,"y":420,"wires":[["66759bdf.7fd864"]]},{"id":"637d9a4c.1182d4","type":"udp in","z":"d51aa60c.64d028","name":"RecieveAck","iface":"","port":"13501","ipv":"udp4","multicast":"false","group":"","datatype":"utf8","x":173,"y":177,"wires":[["8b85adf3.38d6d"]]},{"id":"8b85adf3.38d6d","type":"switch","z":"d51aa60c.64d028","name":"AssignAck","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"'ACK'","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":384,"y":193,"wires":[["1d63307c.82e67"]]},{"id":"66ffd66c.b3c118","type":"function","z":"d51aa60c.64d028","name":"StoreAckInContext","func":"context.ACK=0\nflow.set('ACK',1);\nmsg.payload=flow.get('ACK')\n\nreturn msg;","outputs":1,"noerr":0,"x":717,"y":178,"wires":[["be667fba.f6701"]]},{"id":"be667fba.f6701","type":"debug","z":"d51aa60c.64d028","name":"Console.log","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":909,"y":155,"wires":[]},{"id":"1d63307c.82e67","type":"change","z":"d51aa60c.64d028","name":"Reset","rules":[{"t":"set","p":"ACK","pt":"flow","to":"0","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":165,"wires":[["66ffd66c.b3c118"]]},{"id":"68ded0.44db513","type":"status","z":"d51aa60c.64d028","name":"","scope":["66759bdf.7fd864"],"x":667,"y":282,"wires":[["a3286965.3120d8"]]}]
Related
How to update another byte of a CAN message while changing a byte in real-time?
I'm new to CANoe and CAPL scripting. I need to update a byte of CAN message based on a change in another byte through a panel. I added a CAPL script for the automatic change/update but when I send/update the byte through the panel it sends the default values for the other bytes. It gets corrected in the next sample. includes { } variables { message M2P_0x101 msg1; msTimer timer10ms; byte counter = 1; } on message M2P_0x101 { if(counterAlive == 15) { counterAlive = 0; } else { counter++; } msg1.byte(0) = this.byte(0); msg1.byte(1) = this.byte(1); msg1.byte(2) = this.byte(2); msg1.byte(3) = this.byte(3); // Changing through the Panel msg1.byte(4) = this.byte(3)+1; msg1.byte(5) = this.byte(5); msg1.byte(6) = counter; msg1.byte(7) = this.byte(7); } on timer timer10ms { output(msg1); } on start { setTimerCyclic(timer10ms, 10); } 4th & 6th byte has default value because 3rd byte was changed from the panel. I expected the 4th byte to be 1(value of 3rd byte + 1) and the 6th byte to be 4(counter value). How to solve it? As far as I understand, it seems that the panel inputs/changes are sent to the CAN bus first and then CAPL node reads it and makes changes. This could be the reason for overwriting the other bytes of the message with default values. Is there a way to include the changes made by CAPL before the panel sends the message to the CAN bus? A combination of Panel & CAPL node?
Is there any way to save mulaw audio stream from twilio in a file
I am using Twilio voice stream feature and i don't want to use Twilio record functionality. When Twilio starts sending voice stream to my server i want to store it into disk as an audio file in realtime.
I was running into the same issue today and figured a way to generate a WAVE Header for the mu-law header: If you're following Twilio's blog post, that's the code I ended implementing: wss.on('connection', (socket) => { socket.on('message', (msg) => { const { event, ...message } = JSON.parse(msg); switch (event) { case 'start': let streamSid = message.start.streamSid; socket.wstream = fs.createWriteStream(__dirname + `/${Date.now()}.wav`, { encoding: 'binary' }); // This is a mu-law header for a WAV-file compatible with twilio format socket.wstream.write(Buffer.from([ 0x52,0x49,0x46,0x46,0x62,0xb8,0x00,0x00,0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20, 0x12,0x00,0x00,0x00,0x07,0x00,0x01,0x00,0x40,0x1f,0x00,0x00,0x80,0x3e,0x00,0x00, 0x02,0x00,0x04,0x00,0x00,0x00,0x66,0x61,0x63,0x74,0x04,0x00,0x00,0x00,0xc5,0x5b, 0x00,0x00,0x64,0x61,0x74,0x61,0x00,0x00,0x00,0x00, // Those last 4 bytes are the data length ])); break; case 'media': // decode the base64-encoded data and write to stream socket.wstream.write(Buffer.from(message.media.payload, 'base64')); break; case 'stop': // Now the only thing missing is to write the number of data bytes in the header socket.wstream.write("", () => { let fd = fs.openSync(socket.wstream.path, 'r+'); // `r+` mode is needed in order to write to arbitrary position let count = socket.wstream.bytesWritten; count -= 58; // The header itself is 58 bytes long and we only want the data byte length console.log(count) fs.writeSync( fd, Buffer.from([ count % 256, (count >> 8) % 256, (count >> 16) % 256, (count >> 24) % 256, ]), 0, 4, // Write 4 bytes 54, // starts writing at byte 54 in the file ); }); break; } }); });
You can use FFmpeg to convert the Twilio mulaw to a regular WAV file. If you use the class below, then you will just need to send the Twilio stream buffers when they arrive. Something like: recording = StreamAudioRecording(tempDirectory) recording.start_recording(call_id) <loop over buffer packets arriving> recording.write_buffer(buffer) recording_audio_path = recording.stop_recording() This is the class implementation import os RAW_AUDIO_FILE_EXTENSION = "ulaw" CONVERTED_AUDIO_FILE_EXTENSION = "wav" class StreamAudioRecording: def __init__(self, audio_recording_path): self.audio_recording_path = audio_recording_path self.f = None self.audio_file_path = None def start_recording(self, call_id): self.audio_file_path = os.path.join(self.audio_recording_path, f" . {call_id}.{RAW_AUDIO_FILE_EXTENSION}") self.f = open(self.audio_file_path, 'wb') def write_buffer(self, buffer): self.f.write(buffer) def stop_recording(self): self.f.close() converted_audio_path = self.audio_file_path.replace(RAW_AUDIO_FILE_EXTENSION, CONVERTED_AUDIO_FILE_EXTENSION) self.convert_call_recording(self.audio_file_path, converted_audio_path) return converted_audio_path #staticmethod def convert_call_recording(mulaw_path, wav_path): command = f"ffmpeg -y -loglevel panic -f mulaw -ar 8k -ac 1 -i {mulaw_path} {wav_path}" os.system(command)
If your using Node.js, the solution that #tdeo provided works great. I was inspired and I made a simple library using this solution. It's available now on npm. https://www.npmjs.com/package/twilio-media-stream-save-audio-file
Lua: publish different sensors data simultaneously to topic
I am able to publish a data of different sensors (like temperature value and motion detection as zero or one) one at a time on a specific topic ( topic/temp/motion) using Lua code and subscribe in on my android app however i am not able to publish it simultaneously on same topic or sub-topic. Some ideas or examples would be great. Below are some major bits of Lua code used. orgID = "quickstart" -- IoT Foundation organization ID broker = "test.mosquitto.org" --orgID..".messaging.internetofthings.ibmcloud.com" -- IP or hostname of IoTF service mqttPort = 1883 -- MQTT port (default 1883: non-secure) userID = "" -- blank for quickstart userPWD = "" -- blank for quickstart macID = "18fe34e1b007" -- unique Device ID or Ethernet Mac Address <==== Modify this! clientID = ":esp8266:18fe34e1b007" -- Client ID count = 0 -- Test number of mqtt_do cycles mqttState = 0 -- State control topic = "topic/temp/motion" led = 4 --gpio.mode(led,gpio.OUTPUT) --dht sensor settings------------------------------ pin = 1 -- PIR initialization section pir = 2 x= 0 -- variable for sending motion detection information in "0 or 1" function DHT_do() status, temp, humi, temp_dec, humi_dec = dht.read(pin) --gpio.write(led, gpio.LOW) end function mqtt_do() count = count + 1 -- tmr.alarm counter ------------------------------------------- pir conditional code if gpio.read(pir) ~= last_state then last_state = gpio.read(pir) if last_state == 1 then print("ON") x = 1 gpio.write(led,gpio.HIGH) else print("OFF") x = 0 gpio.write(led,gpio.LOW) m:publish(topic,x, 0, 0, function(conn) print(x) print("temp_data:"..temp)
Why [ MetaTrader 4 ] can't write/open a file in windows 10 with the FileWrite() function?
I tried implementing the solutions from this post Cannot open file on Ubuntu But to no avail. I keep getting a 5002 error. (failed to open file) after FileWrite() Even when I try out the example script on https://docs.mql4.com/files/filewrite I get the same error. I tried running MetaTrader Terminal 4 in administrator mode but to no avail either.Does anyone know a solution to this? #property version "1.00" #property strict //--- show the window of input parameters when launching the script #property script_show_inputs //--- parameters for receiving data from the terminal input string InpSymbolName = "EURUSD"; // Сurrency pair input ENUM_TIMEFRAMES InpSymbolPeriod = PERIOD_H1; // Time frame input int InpFastEMAPeriod = 12; // Fast EMA period input int InpSlowEMAPeriod = 26; // Slow EMA period input int InpSignalPeriod = 9; // Difference averaging period input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Price type //--- parameters for writing data to file input string InpFileName = "MACD.csv"; // File name input string InpDirectoryName = "Data"; // Folder name //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { bool sign_buff[]; // signal array (true - buy, false - sell) datetime time_buff[]; // array of signals' appear time int sign_size=0; // signal array size double macd_buff[]; // array of indicator values datetime date_buff[]; // array of indicator dates int macd_size=0; // size of indicator arrays //--- set indexing as time series ArraySetAsSeries( sign_buff, true ); ArraySetAsSeries( time_buff, true ); ArraySetAsSeries( macd_buff, true ); ArraySetAsSeries( date_buff, true ); //--- reset last error code ResetLastError(); //--- copying the time from last 1000 bars int copied = CopyTime( NULL, 0, 0, 1000, date_buff ); if ( copied <= 0 ) { PrintFormat( "Failed to copy time values. Error code = %d", GetLastError() );` return; } //--- prepare macd_buff array ArrayResize( macd_buff, copied ); //--- copy the values of main line of the iMACD indicator for ( int i = 0; i< copied; i++ ) { macd_buff[i] = iMACD( InpSymbolName, InpSymbolPeriod, InpFastEMAPeriod, InpSlowEMAPeriod, InpSignalPeriod, InpAppliedPrice, MODE_MAIN, i ); } //--- get size macd_size = ArraySize( macd_buff ); //--- analyze the data and save the indicator signals to the arrays ArrayResize( sign_buff, macd_size - 1 ); ArrayResize( time_buff, macd_size - 1 ); for ( int i = 1; i < macd_size; i++ ) { //--- buy signal if ( macd_buff[i-1] < 0 && macd_buff[i] >= 0 ) { sign_buff[sign_size] = true; time_buff[sign_size] = date_buff[i]; sign_size++; } //--- sell signal if ( macd_buff[i-1] > 0 && macd_buff[i] <= 0 ) { sign_buff[sign_size] = false; time_buff[sign_size] = date_buff[i]; sign_size++; } } //--- open the file for writing the indicator values (if the file is absent, it will be created automatically) ResetLastError(); int file_handle = FileOpen( InpDirectoryName // aDirNAME + "//" // aFileSystemTreeBranchSEPARATOR + InpFileName, // aFileNAME FILE_READ|FILE_WRITE|FILE_CSV // fileIoMODE_FLAGs ); if ( file_handle != INVALID_HANDLE ) { PrintFormat( "%s file is available for writing", InpFileName ); PrintFormat( "File path: %s\\Files\\", TerminalInfoString( TERMINAL_DATA_PATH ) ); //--- first, write the number of signals FileWrite( file_handle, sign_size ); //--- write the time and values of signals to the file for ( int i = 0; i < sign_size; i++ ) FileWrite( file_handle, time_buff[i], sign_buff[i] ); //--- close the file FileClose( file_handle ); PrintFormat( "Data is written, %s file is closed", InpFileName ); } else PrintFormat( "Failed to open %s file, Error code = %d", InpFileName, GetLastError() ); }
Alt. A) the algo does not check for a positive presence of an InpDirectoryName node in the filesystem. Alt. B) in case the Alt. A) passes, there ought be used a compatible delimiter in aFilePATH + aFileNAME string, i.e. == "\\", ( aFileSystemTreeBranchSEPARATOR ref.: Help for details ) thena fileIO shall take place in \Terminal\Common\Files ( in case a FILE_COMMON bitmask flag was added (used) in the int open_flags parameter), otherwise,the operations will take place in a (sub)-directory of a MetaTrader Terminal 4 local folder== MQL4\Files ( for a live ExpertAdvisor or Custom Indicator or Script mode of operations )or== MQL4\Tester\Files ( in case of using ExpertAdvisor or Custom Indicatorinside a back-testing framework of MT4 Strategy Tester).
Zebra Printer - Not Printing PNG Stream *Provided my own answer*
I think I'm very close to getting this to print. However it still isn't. There is no exception thrown and it does seem to be hitting the zebra printer, but nothing. Its a long shot as I think most people are in the same position I am and know little about it. Any help anyone can give no matter how small will be welcomed, I'm losing the will to live using (var response = request.GetResponse()) { using (var responseStream = response.GetResponseStream()) { using (var stream = new MemoryStream()) { if (responseStream == null) { return; } responseStream.CopyTo(stream); stream.Position = 0; using (var zipout = ZipFile.Read(stream)) { using (var ms = new MemoryStream()) { foreach (var e in zipout.Where(e => e.FileName.Contains(".png"))) { e.Extract(ms); } if (ms.Length <= 0) { return; } var binaryData = ms.ToArray(); byte[] compressedFileData; // Compress the data using the LZ77 algorithm. using (var outStream = new MemoryStream()) { using (var compress = new DeflateStream(outStream, CompressionMode.Compress, true)) { compress.Write(binaryData, 0, binaryData.Length); compress.Flush(); compress.Close(); } compressedFileData = outStream.ToArray(); } // Encode the compressed data using the MIME Base64 algorithm. var base64 = Convert.ToBase64String(compressedFileData); // Calculate a CRC across the encoded data. var crc = Calc(Convert.FromBase64String(base64)); // Add a unique header to differentiate the new format from the existing ASCII hexadecimal encoding. var finalData = string.Format(":Z64:{0}:{1}", base64, crc); var zplToSend = "~DYR:LOGO,P,P," + finalData.Length + ",," + finalData; const string PrintImage = "^XA^FO0,0^IMR:LOGO.PNG^FS^XZ"; try { var client = new System.Net.Sockets.TcpClient(); client.Connect(IpAddress, Port); var writer = new StreamWriter(client.GetStream(), Encoding.UTF8); writer.Write(zplToSend); writer.Flush(); writer.Write(PrintImage); writer.Close(); client.Close(); } catch (Exception ex) { // Catch Exception } } } } } } private static ushort Calc(byte[] data) { ushort wCrc = 0; for (var i = 0; i < data.Length; i++) { wCrc ^= (ushort)(data[i] << 8); for (var j = 0; j < 8; j++) { if ((wCrc & 0x8000) != 0) { wCrc = (ushort)((wCrc << 1) ^ 0x1021); } else { wCrc <<= 1; } } } return wCrc; }
The following code is working for me. The issue was the commands, these are very very important! Overview of the command I have used below, more can be found here PrintImage ^XA Start Format Description The ^XA command is used at the beginning of ZPL II code. It is the opening bracket and indicates the start of a new label format. This command is substituted with a single ASCII control character STX (control-B, hexadecimal 02). Format ^XA Comments Valid ZPL II format requires that label formats should start with the ^XA command and end with the ^XZ command. ^FO Field Origin Description The ^FO command sets a field origin, relative to the label home (^LH) position. ^FO sets the upper-left corner of the field area by defining points along the x-axis and y-axis independent of the rotation. Format ^FOx,y,z x = x-axis location (in dots) Accepted Values: 0 to 32000 Default Value: 0 y = y-axis location (in dots) Accepted Values: 0 to 32000 Default Value: 0 z = justification The z parameter is only supported in firmware versions V60.14.x, V50.14.x, or later. Accepted Values: 0 = left justification 1 = right justification 2 = auto justification (script dependent) Default Value: last accepted ^FW value or ^FW default ^IM Image Move Description The ^IM command performs a direct move of an image from storage area into the bitmap. The command is identical to the ^XG command (Recall Graphic), except there are no sizing parameters. Format ^IMd:o.x d = location of stored object Accepted Values: R:, E:, B:, and A: Default Value: search priority o = object name Accepted Values: 1 to 8 alphanumeric characters Default Value: if a name is not specified, UNKNOWN is used x = extension Fixed Value: .GRF, .PNG ^FS Field Separator Description The ^FS command denotes the end of the field definition. Alternatively, ^FS command can also be issued as a single ASCII control code SI (Control-O, hexadecimal 0F). Format ^FS ^XZ End Format Description The ^XZ command is the ending (closing) bracket. It indicates the end of a label format. When this command is received, a label prints. This command can also be issued as a single ASCII control character ETX (Control-C, hexadecimal 03). Format ^XZ Comments Label formats must start with the ^XA command and end with the ^XZ command to be in valid ZPL II format. zplToSend ^MN Media Tracking Description This command specifies the media type being used and the black mark offset in dots. This bulleted list shows the types of media associated with this command: Continuous Media – this media has no physical characteristic (such as a web, notch, perforation, black mark) to separate labels. Label length is determined by the ^LL command. Continuous Media, variable length – same as Continuous Media, but if portions of the printed label fall outside of the defined label length, the label size will automatically be extended to contain them. This label length extension applies only to the current label. Note that ^MNV still requires the use of the ^LL command to define the initial desired label length. Non-continuous Media – this media has some type of physical characteristic (such as web, notch, perforation, black mark) to separate the labels. Format ^MNa,b a = media being used Accepted Values: N = continuous media Y = non-continuous media web sensing d, e W = non-continuous media web sensing d, e M = non-continuous media mark sensing A = auto-detects the type of media during calibration d, f V = continuous media, variable length g Default Value: a value must be entered or the command is ignored b = black mark offset in dots This sets the expected location of the media mark relative to the point of separation between documents. If set to 0, the media mark is expected to be found at the point of separation. (i.e., the perforation, cut point, etc.) All values are listed in dots. This parameter is ignored unless the a parameter is set to M. If this parameter is missing, the default value is used. Accepted Values: -80 to 283 for direct-thermal only printers -240 to 566 for 600 dpi printers -75 to 283 for KR403 printers -120 to 283 for all other printers Default Value: 0 ~DY Download Objects Description The ~DY command downloads to the printer graphic objects or fonts in any supported format. This command can be used in place of ~DG for more saving and loading options. ~DY is the preferred command to download TrueType fonts on printers with firmware later than X.13. It is faster than ~DU. The ~DY command also supports downloading wireless certificate files. Format ~DYd:f,b,x,t,w,data Note When using certificate files, your printer supports: - Using Privacy Enhanced Mail (PEM) formatted certificate files. - Using the client certificate and private key as two files, each downloaded separately. - Using exportable PAC files for EAP-FAST. - Zebra recommends using Linear sty d = file location .NRD and .PAC files reside on E: in firmware versions V60.15.x, V50.15.x, or later. Accepted Values: R:, E:, B:, and A: Default Value: R: f = file name Accepted Values: 1 to 8 alphanumeric characters Default Value: if a name is not specified, UNKNOWN is used b = format downloaded in data field .TTE and .TTF are only supported in firmware versions V60.14.x, V50.14.x, or later. Accepted Values: A = uncompressed (ZB64, ASCII) B = uncompressed (.TTE, .TTF, binary) C = AR-compressed (used only by Zebra’s BAR-ONE® v5) P = portable network graphic (.PNG) - ZB64 encoded Default Value: a value must be specified clearDownLabel ^ID Description The ^ID command deletes objects, graphics, fonts, and stored formats from storage areas. Objects can be deleted selectively or in groups. This command can be used within a printing format to delete objects before saving new ones, or in a stand-alone format to delete objects. The image name and extension support the use of the asterisk (*) as a wild card. This allows you to easily delete a selected groups of objects. Format ^IDd:o.x d = location of stored object Accepted Values: R:, E:, B:, and A: Default Value: R: o = object name Accepted Values: any 1 to 8 character name Default Value: if a name is not specified, UNKNOWN is used x = extension Accepted Values: any extension conforming to Zebra conventions Default Value: .GRF const string PrintImage = "^XA^FO0,0,0^IME:LOGO.PNG^FS^XZ"; var zplImageData = string.Empty; using (var response = request.GetResponse()) { using (var responseStream = response.GetResponseStream()) { using (var stream = new MemoryStream()) { if (responseStream == null) { return; } responseStream.CopyTo(stream); stream.Position = 0; using (var zipout = ZipFile.Read(stream)) { using (var ms = new MemoryStream()) { foreach (var e in zipout.Where(e => e.FileName.Contains(".png"))) { e.Extract(ms); } if (ms.Length <= 0) { return; } var binaryData = ms.ToArray(); foreach (var b in binaryData) { var hexRep = string.Format("{0:X}", b); if (hexRep.Length == 1) { hexRep = "0" + hexRep; } zplImageData += hexRep; } var zplToSend = "^XA" + "^FO0,0,0" + "^MNN" + "~DYE:LOGO,P,P," + binaryData.Length + ",," + zplImageData + "^XZ"; var label = GenerateStreamFromString(zplToSend); var client = new System.Net.Sockets.TcpClient(); client.Connect(IpAddress, Port); label.CopyTo(client.GetStream()); label.Flush(); client.Close(); var cmd = GenerateStreamFromString(PrintImage); var client2 = new System.Net.Sockets.TcpClient(); client2.Connect(IpAddress, Port); cmd.CopyTo(client2.GetStream()); cmd.Flush(); client2.Close();var clearDownLabel = GenerateStreamFromString("^XA^IDR:LOGO.PNG^FS^XZ"); var client3 = new System.Net.Sockets.TcpClient(); client3.Connect(IpAddress, Port); clearDownLabel.CopyTo(client3.GetStream()); clearDownLabel.Flush(); client3.Close(); } } } } } }
Easy once you know how. Zebra ZPL logo example in base64 Python3 import crcmod import base64 crc16 = crcmod.predefined.mkCrcFun('xmodem') s = hex(crc16(ZPL_LOGO.encode()))[2:] print (f"crc16: {s}") Poorly documented may I say the least