4 |
5 | **RglComm** is a GUI-based program written in the Java Language that I created to experiment with communicating with and controlling Rigol™ devices using IEEE 488 Commands [USBTMC-USB488](http://sdpha2.ucsd.edu/Lab_Equip_Manuals/usbtmc_usb488_subclass_1_00.pdf) sent over the instrument's USB interface. My eventual goal is to use this code as the basis for a program that can run simple scripts to make various measurements and perform calculations (sort of a poor man's LabVIEW™), so stay tuned if you're interested. However, I'm publishing it here so that others can learn the basic techniques needed to use usb4java to communicate these kinds of devices. My implementation of the USBTMC-USB488 protocol is just enough to enable RglComm to send commands and receive responses and does not implement all the details of the full specification.
6 |
7 | To use RglComm, first select the device to communicate with using the selector, then type the command into the text field and press the Enter key, or press the "**`RUN`**" button. Note; some devices, such as the Rigol DG4162 Function/Arbitrary Waveform Generator make need to be set to "PC" mode in the I/O menu before they will respond to commands:
8 |
9 | #### Precautions
10 | - Be careful when using commends that switch measuring modes, such as issuing a **`:MEASure:CURRent:DC?`** command when the instrument is connected to a voltage source, as this can damage the instrument.
11 | - Using the **`MEASure`** command switch from one mode to another can sometimes result in a read timeout, as it takes time for the DM3058 to internally make the mode change. So, it's better to first use a **`FUNCtion`** command t0 select the measurement mode before issuing a **`MEASure`** command.
12 | - Try to connect all instruments directly to the host computer, as adding intermediate USB hubs can interfere with communication and cause timeout errors.
13 | - Make sure the device's USB I/O Mode is set to '**`PC`**' and not to '**`Printer`**' as it's not possible to communicate with the device when set to Printer Mode.
14 |
15 | #### Commands common to most instruments include:
16 |
17 | - **`*IDN?`** - Queries the equipment ID and returns a String of text info
18 | - **`*CLS`** - Clear Status Register
19 | - **`*RST`** - Resets the instrument to factory defined condition
20 | - **`*WAI`** - Waits until all pending commands are completed, before executing any other commands
21 |
22 | #### Commands for the Rigol DM3058 Digital Multimeter include:
23 |
24 | - **`:FUNCtion:VOLTage:DC`** - Sets DM3058 to measure DC Voltage
25 | - **`:MEASure:VOLTage:DC?`** - Measure DC Voltage
26 | - **`:FUNCtion:VOLTage:AC`** - Sets DM3058 to measure AC Voltage
27 | - **`:MEASure:VOLTage:AC?`** - Measure AC Voltage
28 | - **`:FUNCtion:CURRent:DC`** - Sets DM3058 to measure DC Current
29 | - **`:MEASure:CURRent:DC?`** - Measure DC Current
30 | - **`:FUNCtion:CURRent:AC`** - Sets DM3058 to measure AC Current
31 | - **`:MEASure:CURRent:AC?`** - Measure AC Current
32 | - **`:FUNCtion:RESistance`** - Sets DM3058 to measure Resistance
33 | - **`:MEASure:RESistance?`** - Measure Resistance
34 |
35 | Note: the portions of the commands shown in lower case letters are optional and can be omitted. So, for example, sending the command **`:FUNC:VOLT:DC`** is the same as sending the command **`:FUNCtion:VOLTage:DC`**. Also, multiple commands can be entered as one line by separating each command with a '**_;_**' character.
36 |
37 | #### Commands for a Rigol DS4024 Digital Oscilloscope include:
38 |
39 | - **`:CHANnel1:COUPling?`** - Query coupling mode of Channel 1 (AC,DC,GND)
40 | - **`:CHANnel1:COUPling AC`** - Set coupling mode of Channel to AC
41 | - **`:CHANnel1:DISPlay?`** - Query display state of Channel 1 (1 = On, 0 = Off)
42 | - **`:CHANnel1:DISPlay 1`** - Enable display of Channel 1
43 | - **`:CHANnel1:OFFSet?`** - Query vertical position of Channel 1
44 | - **`:CHANnel1:OFFSet -0.3`** - Set vertical position of Channel 1 to -300mV
45 | - **`:CHANnel1:SCALe?`** - Query vertical scale of Channel 1
46 | - **`:CHANnel1:SCALe 0.5`** - Set vertical scale of Channel 1 to 500mV
47 | - **`:TIMebase:SCALe?`** - Query Timebase Scale
48 | - **`:TIMebase:SCALe 0.00001`** - Set Timebase Scale to 10uS
49 | - **`:DISPlay:DATA?`** - Download and display Screen image from DS4024 (see below)
50 |
51 |
52 |
53 | #### Commands for a Rigol DS1102E Digital Oscilloscope include:
54 |
55 | - **`:WAV:POIN:NOR;:WAVeform:DATA? CH1`** - Select normal mode then display waveform from DS1102E Ch 1 (see below)
56 | - **`:WAV:POIN:NOR;:WAVeform:DATA? CH2`** - Select normal mode then display waveform from DS1102E Ch 2
57 |
58 |
59 |
60 | #### Commands for a Rigol DG4162 Function/Arbitrary Waveform Generator include:
61 |
62 | - **`:SOURce1:FREQuency:FIXed 888888`** - Set Channel 1 Frequency to 888.888 kHz
63 | - **`:OUTPut1:STATe ON`** - Channel 1 Output On
64 | - **`:OUTPut1:STATe OFF`** - Channel 1 Output Off
65 | - **`:SOURce1:VOLTage?`** - Read Channel 1 Amplitude in Volts (pp)
66 | - **`:SOURce1:VOLTage 2.25`** - Channel 1 Amplitude to 2.25 Volts
67 | - **`:SOURce1:FUNCtion:SHAPe?`** - Read Selected Waveform Shape of Channel 1
68 | - **`:SOURce1:FUNCtion:SHAPe SQUare`** - Set Channel 1 Output to Square Wave
69 | - **`:SOURce1:FUNCtion:SHAPe SINusoid`** - Set Channel 1 Output to Sinusoid
70 | - **`:SOURce1:FUNCtion:SHAPe RAMP`** - Set Channel 1 Output to Ramp (Triangle)
71 | - **`:HCOPy:SDUMp:DATA?`** - Download and display Screen image from DG4162 (see below)
72 |
73 |
74 |
75 | #### Supported Devices
76 |
77 | Note: while I designed and tested RglComm with devices made by Rigol Technologies, it might also work with other devices that support IEEE 488 Commands sent over the devices's USB interface. However, at the moment, I have only done basic testing with the following Rigol devices:
78 |
79 | - [**DM3058** Digital Multimeter](https://www.rigolna.com/products/digital-multimeters/dm3000/) - Vendor Id: **`1ab1`**, Product Id: **`09c4`**
80 | - [**DP832** Programmable DC Power Supply](https://www.rigolna.com/products/dc-power-loads/dp800/) - Vendor Id: **`1ab1`**, Product Id: **`0e11`**
81 | - [**DS4024** Digital Oscilloscope](https://www.rigolna.com/products/digital-oscilloscopes/4000/) - Vendor Id: **`1ab1`**, Product Id: **`04b1`**
82 | - [**DS1102E** Digital Oscilloscope](https://www.rigolna.com/products/digital-oscilloscopes/1000/) - Vendor Id: **`1ab1`**, Product Id: **`0588`**
83 | - [**DSA815** Spectrum Analyzer](https://www.rigolna.com/products/spectrum-analyzers/dsa800/) - Vendor Id: **`1ab1`**, Product Id: **`0960`**
84 | - [**DG4162** Function/Arbitrary Waveform Generator](https://www.rigolna.com/products/waveform-generators/dg4000/) - Vendor Id: **`1ab1`**, Product Id: **`0641`**
85 |
86 | If you wish to use other devices, you will need to add them to the "devices" Map in RglComm.java. The utility program RglScan.java (included in .jar file) can be used to scan for Rigol devices that are powered on and connected to the computer. You can type "**`scan`**" into the command text field and then press either the Enter key, or the "**`RUN`**" button. Or, you can run it from the command line like this:
87 |
88 | **`java -cp RglComm.jar RglScan`**
89 |
90 | ### **Requirements**
91 | A [Java JDK or JVM](https://www.java.com/en/) or [OpenJDK](http://openjdk.java.net) version 8, or later must be installed in order to run the code. There is also a [**Runnable JAR file**](https://github.com/wholder/RglComm/blob/master/out/artifacts/RglComm_jar) included in the checked in code that you can download and run without having to compile the cource code.
92 |
93 | #### macOS
94 | On a Mac, just double click the **`RglComm.jar`** file and it should start. However, you'll need to right click on the .jar file and select "Open" the first time you run RglComm due to new Mac OS X security checks.
95 |
96 | #### Windows
97 | Follow [these instructions](https://windowsreport.com/jar-file-windows/) to run the JAR file on Windows. However, you will first need to install device drivers for the Rigol devices you are using. The instructions on this page provides more details, but you can use the automated driver installer [Zadig](https://zadig.akeo.ie) to install a generic driver for each Rigol device using its Vendor and Product Ids, like this:
98 |
99 |
100 |
101 | #### Linux
102 | Follow [these instructions](https://itsfoss.com/run-jar-file-ubuntu-linux/) to run the JAR file on a Linux system. However, you will first need to setup the USB permissions for each device using a [udev-based USB permission rule](http://ask.xmodulo.com/change-usb-device-permission-linux.html). For example, to set USB permissions for the DM3058 Digital Multimeter, create a file named something like "**`50-myusb.rules`**" in the "**`/lib/udev/rules.d/`**" directory that includes the following text:
103 |
104 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="09c4", GROUP="users", MODE="0666"
105 |
106 | This rule identifies the device by using its USB Vendor Id (**`1ab1`**) and Product Id (**`09c4`**). Alternately, you can grant permissions for all Rigol devices by only specifying the Vendor Id (**`1ab1`**) in the file, such as:
107 |
108 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1ab1", GROUP="users", MODE="0666"
109 |
110 | Then, save the file and restart Linux, or type this command to reload the udev rules:
111 |
112 | sudo udevadm control --reload-rules
113 |
114 | ## Credits
115 | RglComm uses the following Java code to perform some of its functions, or build this project:
116 | - [Usb4Java](http://usb4java.org) is used to perform low-level USB I/O using the USBTMC protocol
117 | - [IntelliJ IDEA from JetBrains](https://www.jetbrains.com/idea/) (my favorite development environment for Java coding. Thanks JetBrains!)
118 |
--------------------------------------------------------------------------------
/src/RglComm.java:
--------------------------------------------------------------------------------
1 | import javax.swing.*;
2 | import java.awt.*;
3 | import java.awt.event.*;
4 | import java.io.ByteArrayOutputStream;
5 | import java.text.DecimalFormat;
6 | import java.util.*;
7 | import java.util.List;
8 | import java.util.prefs.Preferences;
9 |
10 | /*
11 | * Test Program to communicate with and control Rigol devices using IEEE 488 Commands
12 | *
13 | * Author: Wayne Holder, 2019
14 | * License: MIT (https://opensource.org/licenses/MIT)
15 | */
16 |
17 | public class RglComm extends JFrame {
18 | private transient Preferences prefs = Preferences.userRoot().node(this.getClass().getName());
19 | private transient boolean running;
20 | private static List devices = new LinkedList<>();
21 | private JTextArea text = new JTextArea();
22 | private JTextField command;
23 | private JComboBox select;
24 | private USBIO usb;
25 | private byte bTag;
26 |
27 | static class Rigol {
28 | String name;
29 | short vend, prod;
30 |
31 | Rigol (String name, int vend, int prod) {
32 | this.name = name;
33 | this.vend = (short) vend;
34 | this.prod = (short) prod;
35 | }
36 |
37 | public String toString () {
38 | return name;
39 | }
40 | }
41 |
42 | static {
43 | // Selector Description VendId ProdId
44 | devices.add(new Rigol("DM3058 Digital Multimeter", 0x1AB1, 0x09C4));
45 | devices.add(new Rigol("DP832 Prog DC Power Supply", 0x1AB1, 0x0E11));
46 | devices.add(new Rigol("DS4024 Digital Oscilloscope", 0x1AB1, 0x04B1));
47 | devices.add(new Rigol("DS1102E Digital Oscilloscope", 0x1AB1, 0x0588));
48 | devices.add(new Rigol("DSA815 Spectrum Analyzer", 0x1AB1, 0x0960));
49 | devices.add(new Rigol("DG4162 Func/Wave Generator", 0x1AB1, 0x0641)); // Shows as PID 0x0588 in "Printer" mode
50 | devices.add(new Rigol("DS1054Z Digital Oscilloscope", 0x1AB1, 0x04CE)); // Not verified
51 | }
52 |
53 | class PopMenuTextField extends JTextField {
54 | private Map shortcuts = new LinkedHashMap<>();
55 | {
56 | shortcuts.put("Identify", "*IDN?");
57 | shortcuts.put("Clear Error", "*CLS");
58 | shortcuts.put("DS1102E/Wave Capture Ch1", ":WAV:POIN:MODE NOR;:WAVeform:DATA? CH1");
59 | shortcuts.put("DS1102E/Wave Capture Ch2", ":WAV:POIN:MODE NOR;:WAVeform:DATA? CH2");
60 | shortcuts.put("DS4024/Screen Capture", ":DISP:DATA?");
61 | shortcuts.put("DG4162/Screen Capture", ":HCOP:SDUM:DATA?");
62 | shortcuts.put("DM3058/Measure DC Voltage", ":FUNC:VOLT:DC;DLY1;:MEAS:VOLT:DC?");
63 | shortcuts.put("DM3058/Measure AC Voltage", ":FUNC:VOLT:AC;DLY1;:MEAS:VOLT:AC?");
64 | shortcuts.put("DM3058/Measure Resistance", ":FUNC:RES;DLY1;:MEAS:RES?");
65 | }
66 |
67 | PopMenuTextField (JComboBox select) {
68 | setToolTipText("Right click for shortcut commands");
69 | addMouseListener(new MouseAdapter() {
70 | public void mouseReleased (MouseEvent ev1) {
71 | processMouse(ev1);
72 | }
73 | public void mousePressed (MouseEvent ev1) {
74 | processMouse(ev1);
75 | }
76 | void processMouse (MouseEvent ev1) {
77 | if (ev1.isPopupTrigger()) {
78 | Rigol item = (Rigol) select.getSelectedItem();
79 | JPopupMenu menu = new JPopupMenu();
80 | boolean addSep = true;
81 | for (String key : shortcuts.keySet()) {
82 | String[] parts = key.split("/");
83 | JMenuItem menuItem = null;
84 | if (parts.length == 1) {
85 | // Add common commands
86 | menuItem = new JMenuItem(key);
87 | } else if (parts.length == 2 && item != null) {
88 | if (addSep) {
89 | menu.addSeparator();
90 | addSep = false;
91 | }
92 | // Add device-specific commands
93 | String[] tmp = item.name.split(" ");
94 | if (tmp.length >= 2 && tmp[0].equals(parts[0])) {
95 | menuItem = new JMenuItem(parts[1]);
96 | }
97 | }
98 | if (menuItem != null) {
99 | menu.add(menuItem);
100 | menuItem.addActionListener(ev2 -> {
101 | setText(shortcuts.get(key));
102 | runCommand();
103 | });
104 | }
105 | }
106 | menu.show(ev1.getComponent(), ev1.getX(), ev1.getY());
107 | }
108 | }
109 | });
110 | }
111 | }
112 |
113 | public static void main (String[] args) {
114 | new RglComm();
115 | }
116 |
117 | private void doCommand () {
118 | String cmd = command.getText();
119 | if ("scan".equalsIgnoreCase(cmd)) {
120 | appendLine(RglScan.doScan());
121 | } else {
122 | running = true;
123 | try {
124 | Rigol sel = (Rigol) select.getSelectedItem();
125 | if (sel == null) {
126 | return;
127 | }
128 | usb = new USBIO(sel.vend, sel.prod);
129 | command.setText("");
130 | String[] parts = cmd.split(";");
131 | for (int ii = 0; ii < parts.length; ii++) {
132 | boolean doPrint = ii == parts.length - 1;
133 | cmd = parts[ii];
134 | if (cmd.length() >= 3 && cmd.startsWith("DLY")) {
135 | int seconds = cmd.length() > 3 ? Integer.parseInt(cmd.substring(3)) : 1;
136 | try {
137 | Thread.sleep(seconds * 1000);
138 | } catch (InterruptedException ex) {
139 | ex.printStackTrace();
140 | }
141 | continue;
142 | }
143 | if (doPrint) {
144 | appendLine("Snd: " + cmd);
145 | }
146 | byte[] rsp = sendCmd(cmd + '\n');
147 | if (rsp != null) {
148 | int pLen = prefixLength(rsp);
149 | if (pLen > 0) {
150 | String prefix = new String(Arrays.copyOf(rsp, pLen));
151 | byte[] body = Arrays.copyOfRange(rsp, pLen, rsp.length);
152 | if (body[0] == 'B' && body[1] == 'M') {
153 | // :DISPlay:DATA?
154 | appendLine("Rsp: BMP image received: " + prefix);
155 | new ImageViewer(prefs, body);
156 | } else if (body[0] == (byte) 0xFF && body[1] == (byte) 0xD8) {
157 | // :HCOPy:SDUMp:DATA?
158 | appendLine("Rsp: JPG image received: " + prefix);
159 | new ImageViewer(prefs, body);
160 | } else {
161 | if (body.length == 600) {
162 | appendLine("Rsp: Waveform received: " + prefix);
163 | new WaveViewer(prefs, body);
164 | } else {
165 | appendLine("Rsp: Waveform received: " + prefix + " but too large to display");
166 | }
167 | }
168 | } else {
169 | if (doPrint) {
170 | if (rsp.length > 0) {
171 | String value = new String(rsp).trim();
172 | try {
173 | double dVal = Double.parseDouble(value);
174 | DecimalFormat fmt = new DecimalFormat("#.#########");
175 | value += " (" + fmt.format(dVal) + ")";
176 | } catch (NumberFormatException ex) {
177 | // Ignore
178 | }
179 | appendLine("Rsp: " + value);
180 | }
181 | }
182 | }
183 | }
184 | }
185 | } catch (Exception ex) {
186 | appendLine("Err: " + ex.toString());
187 | ex.printStackTrace();
188 | usb.resetDevice();
189 | } finally {
190 | if (usb != null) {
191 | usb.close();
192 | }
193 | running = false;
194 | }
195 | }
196 | }
197 |
198 | private int prefixLength (byte[] data) {
199 | if (data.length >= 2 && data[0] == '#' && data[1] >= '0' && data[1] <= '9') {
200 | int len = (char) data[1] - '0';
201 | if (data.length >= len + 2) {
202 | for (int ii = 2; ii < len; ii++) {
203 | if (data[ii] < '0' || data[ii] > '9') {
204 | return 0;
205 | }
206 | }
207 | return len + 2;
208 | }
209 | }
210 | return 0;
211 | }
212 |
213 | private RglComm () {
214 | super("RglComm");
215 | select = new JComboBox<>(devices.toArray(new Rigol[0]));
216 | text.setFont(getCodeFont(12));
217 | text.setColumns(40);
218 | text.setRows(20);
219 | text.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
220 | text.setEditable(false);
221 | JScrollPane scroll = new JScrollPane(text, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
222 | add(scroll, BorderLayout.CENTER);
223 | JPanel controls = new JPanel(new FlowLayout());
224 | command = new PopMenuTextField(select);
225 | command.setText("*IDN?");
226 | command.setColumns(30);
227 | controls.add(command);
228 | try {
229 | select.setSelectedIndex(prefs.getInt("select", 0));
230 | } catch (Exception ex) {
231 | // Ignore
232 | }
233 | select.addActionListener(ev -> prefs.putInt("select", select.getSelectedIndex()));
234 | controls.add(select);
235 | JButton run = new JButton("RUN");
236 | run.addActionListener(e -> runCommand());
237 | command.addKeyListener(new KeyAdapter() {
238 | @Override
239 | public void keyPressed(KeyEvent ev) {
240 | if(ev.getKeyCode() == KeyEvent.VK_ENTER) {
241 | runCommand();
242 | }
243 | }
244 | });
245 | controls.add(run);
246 | add(controls, BorderLayout.SOUTH);
247 | setLocationRelativeTo(null);
248 | setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
249 | pack();
250 | setLocation(prefs.getInt("window.x", 10), prefs.getInt("window.y", 10));
251 | // Track window resize/move events and save in prefs
252 | addComponentListener(new ComponentAdapter() {
253 | public void componentMoved (ComponentEvent ev) {
254 | Rectangle bounds = ev.getComponent().getBounds();
255 | prefs.putInt("window.x", bounds.x);
256 | prefs.putInt("window.y", bounds.y);
257 | }
258 | });
259 | setVisible(true);
260 | }
261 |
262 | private void runCommand () {
263 | if (!running){
264 | Thread worker = new Thread(this::doCommand);
265 | worker.start();
266 | }
267 | }
268 |
269 | private static Font getCodeFont (int points) {
270 | String os = System.getProperty("os.name").toLowerCase();
271 | if (os.contains("win")) {
272 | return new Font("Consolas", Font.PLAIN, points);
273 | } else if (os.contains("mac")) {
274 | return new Font("Menlo", Font.PLAIN, points);
275 | } else if (os.contains("linux")) {
276 | return new Font("Courier", Font.PLAIN, points);
277 | } else {
278 | return new Font("Courier", Font.PLAIN, points);
279 | }
280 | }
281 |
282 | private byte[] sendCmd (String cmd) {
283 | //System.out.print("Block Size: " + usb.maxPkt);
284 | // Note: making blockSize larger than 128 breaks communication with some devices
285 | int blockSize = Math.min(usb.maxPkt, 512);
286 | ByteArrayOutputStream buf = new ByteArrayOutputStream();
287 | for (int idx = 0; idx < cmd.length(); idx += blockSize) {
288 | buf.reset();
289 | int pktSize = Math.min(blockSize, cmd.length() - idx);
290 | byte term = (byte) (idx + blockSize >= cmd.length() ? 0x01 : 0x00);
291 | bTag++;
292 | buf.write(1); // 0: MsgID
293 | buf.write(bTag); // 1: bTag
294 | buf.write(bTag ^ 0xFF); // 2: bTagInverse
295 | buf.write(0x00); // 3: Reserved
296 | buf.write(pktSize & 0xFF); // 4: TransferSize
297 | buf.write(pktSize >> 8); // 5: TransferSize
298 | buf.write(0x00); // 6: TransferSize
299 | buf.write(0x00); // 7: TransferSize
300 | buf.write(term); // 8: bmTransfer Attributes (EOM is set on last packet)
301 | buf.write(0x00); // 9: Reserved(0x00)
302 | buf.write(0x00); // 10: Reserved(0x00)
303 | buf.write(0x00); // 11: Reserved(0x00)
304 | for (int ii = 0; ii < pktSize; ii++) {
305 | buf.write(cmd.charAt(idx + ii));
306 | }
307 | while ((buf.size() & 0x03) != 0) {
308 | buf.write(0x00); // Pad to multiple of 4
309 | }
310 | if (buf.size() > blockSize) {
311 | throw new IllegalStateException("buf.size(): " + buf.size() + " > " + "blockSize" + blockSize);
312 | }
313 | usb.send(buf.toByteArray());
314 | }
315 | if (cmd.contains("?")) {
316 | bTag++;
317 | ByteArrayOutputStream rec = new ByteArrayOutputStream();
318 | buf.reset();
319 | buf.write(2); // 0: MsgID
320 | buf.write(bTag); // 1: bTag
321 | buf.write(bTag ^ 0xFF); // 2: bTagInverse
322 | buf.write(0x00); // 3: Reserved
323 | buf.write(blockSize & 0xFF); // 4: TransferSize
324 | buf.write(blockSize >> 8); // 5: TransferSize
325 | buf.write(0x00); // 6: TransferSize
326 | buf.write(0x00); // 7: TransferSize
327 | buf.write(0x00); // 8: bmTransfer Attributes
328 | buf.write(0x00); // 9: Reserved(0x00)
329 | buf.write(0x00); // 10: Reserved(0x00)
330 | buf.write(0x00); // 11: Reserved(0x00)
331 | byte[] data;
332 | do {
333 | if (buf.size() > blockSize) {
334 | throw new IllegalStateException("buf.size(): " + buf.size() + " > " + "blockSize" + blockSize);
335 | }
336 | usb.send(buf.toByteArray());
337 | // delay(50);
338 | data = usb.receive();
339 | //int size = ((int) data[4] & 0xFF) + (((int) data[5] & 0xFF) << 8);
340 | for (int ii = 12; ii < data.length; ii++) {
341 | rec.write(data[ii]);
342 | }
343 | } while (data[8] == 0);
344 | return rec.toByteArray();
345 | }
346 | return null;
347 | }
348 |
349 | private void appendLine (String line) {
350 | text.append(line + "\n");
351 | text.setCaretPosition(text.getDocument().getLength());
352 | }
353 | }
354 |
--------------------------------------------------------------------------------