{"id":10238,"date":"2013-12-15T22:04:34","date_gmt":"2013-12-16T03:04:34","guid":{"rendered":"http:\/\/scruss.com\/blog\/?p=10238"},"modified":"2021-02-25T11:11:59","modified_gmt":"2021-02-25T16:11:59","slug":"my-raspberry-pi-talks-to-my-oscilloscope","status":"publish","type":"post","link":"https:\/\/scruss.com\/blog\/2013\/12\/15\/my-raspberry-pi-talks-to-my-oscilloscope\/","title":{"rendered":"My Raspberry Pi talks to my Oscilloscope"},"content":{"rendered":"<p><span style=\"color: #ff0000;\">Hey!<\/span> This post is <span style=\"text-decoration: underline; color: #ff0000;\">completely ancient<\/span>. It doesn&#8217;t even use Python 3. Advice given here might be well out of date.<\/p>\n<hr \/>\n<p>\u00e2\u20ac\u00a6 it complains that the oscilloscope is always making waves.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-10239\" src=\"http:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2013\/12\/DS1EB134907266_0.png\" alt=\"DS1EB134907266_0\" width=\"320\" height=\"234\" srcset=\"https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2013\/12\/DS1EB134907266_0.png 320w, https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2013\/12\/DS1EB134907266_0-160x117.png 160w\" sizes=\"auto, (max-width: 320px) 100vw, 320px\" \/><\/p>\n<p>Ahem. Anyway. I have a <a href=\"http:\/\/www.rigolna.com\/products\/digital-oscilloscopes\/ds1000e\/ds1102e\/\">Rigol DS1102E 100 MHz Digital Oscilloscope<\/a>. For such a cheap device, it&#8217;s remarkable that you can control it using <a href=\"http:\/\/digital.ni.com\/public.nsf\/allkb\/044FA220F32774ED86256DB3005850CA\">USB Test &amp; Measurement Class<\/a> commands. I&#8217;d been wanting to use a Raspberry Pi as a headless data acquisition box with the oscilloscope for a while, but Raspbian doesn&#8217;t ship with the usbtmc kernel module. I thought I was stuck.<\/p>\n<p>Alex Forencich turned up in the <a href=\"http:\/\/www.raspberrypi.org\/phpBB3\/viewtopic.php?f=66&amp;t=60784#p469035\">forum<\/a> with an all-Python solution: <a href=\"http:\/\/alexforencich.com\/wiki\/en\/python-usbtmc\/start\">Python USBTMC<\/a> (source: <a href=\"https:\/\/github.com\/alexforencich\/python-usbtmc\">alexforencich \/ python-usbtmc<\/a>). I got this working quite nicely today on both the Raspberry Pi and my Ubuntu laptop. Here&#8217;s how I installed it:<\/p>\n<ol>\n<li>Check your device&#8217;s USB code with lsusb:<br \/>\n<span style=\"font-family: andale mono,times;\">$ lsusb <\/span><br \/>\n<span style=\"font-family: andale mono,times;\">Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. <\/span><br \/>\n<span style=\"font-family: andale mono,times;\">\u00e2\u20ac\u00a6.<\/span><br \/>\n<span style=\"font-family: andale mono,times;\"><strong>Bus 001 Device 004: ID 1ab1:0588 Rigol Technologies DS1000 SERIES<\/strong><\/span><\/li>\n<li>Ensure that libusb-1.0 is installed:<br \/>\n<span style=\"font-family: andale mono,times;\">sudo apt-get install libusb-1.0-0<\/span><\/li>\n<li>Create a new group, usbtmc:<br \/>\n<span style=\"font-family: andale mono,times;\">sudo groupadd usbtmc<\/span><\/li>\n<li>Add yourself to this group:<br \/>\n<span style=\"font-family: andale mono,times;\">sudo usermod -a -G usbtmc pi<\/span><\/li>\n<li>As root, create a file <span style=\"font-family: andale mono,times;\">\/etc\/udev\/rules.d\/usbtmc.rules<\/span>. You&#8217;ll need to put in your device&#8217;s ID values:<br \/>\n<span style=\"font-family: andale mono,times;\"># USBTMC instruments<\/span><br \/>\n<span style=\"font-family: andale mono,times;\"># Rigol DS1100 &#8211; ID 1ab1:0588 Rigol Technologies DS1000 SERIES<\/span><br \/>\n<span style=\"font-family: andale mono,times;\">SUBSYSTEMS==&#8221;usb&#8221;, ACTION==&#8221;add&#8221;, ATTRS{idVendor}==&#8221;1ab1&#8243;, ATTRS{idProduct}==&#8221;0588&#8243;, GROUP=&#8221;usbtmc&#8221;, MODE=&#8221;0660&#8243;<\/span><br \/>\n(all of the <em>SUBSYSTEMS<\/em> to <em>MODE=<\/em> should be one one line)<\/li>\n<li>Download and install the latest pyusb (Raspbian version is rather old):<br \/>\n<span style=\"font-family: andale mono,times;\">git clone https:\/\/github.com\/walac\/pyusb.git<\/span><br \/>\n<span style=\"font-family: andale mono,times;\">cd pyusb<\/span><br \/>\n<span style=\"font-family: andale mono,times;\">python setup.py build<\/span><br \/>\n<span style=\"font-family: andale mono,times;\">sudo python setup.py install<\/span><\/li>\n<li>Now get python-usbtmc:<br \/>\n<span style=\"font-family: andale mono,times;\">git clone https:\/\/github.com\/alexforencich\/python-usbtmc.git<\/span><br \/>\n<span style=\"font-family: andale mono,times;\">cd python-usbtmc<\/span><br \/>\n<span style=\"font-family: andale mono,times;\">python setup.py build<\/span><br \/>\n<span style=\"font-family: andale mono,times;\">sudo python setup.py install<\/span><\/li>\n<li>For this simple demo, you&#8217;ll need to convert the USB vendor IDs to decimal:<br \/>\n<span style=\"font-family: andale mono,times;\">0x1ab1 = 6833<\/span><br \/>\n<span style=\"font-family: andale mono,times;\">0x0588 = 1416<\/span><\/li>\n<li>Now, start python as root (<span style=\"font-family: andale mono,times;\">sudo python<\/span>) then type:<br \/>\n<span style=\"font-family: andale mono,times;\">import usbtmc<\/span><br \/>\n<span style=\"font-family: andale mono,times;\">instr =\u00c2\u00a0 usbtmc.Instrument(6833, 1416)<\/span><br \/>\n<span style=\"font-family: andale mono,times;\">print(instr.ask(&#8220;*IDN?&#8221;))<\/span><\/li>\n<li>This <em>should<\/em> return something like:<br \/>\n<span style=\"font-family: andale mono,times;\">Rigol Technologies,DS1102E,DS1EB13490xxxx,00.02.06.00.01<\/span><\/li>\n<\/ol>\n<p>If you get the status line, congratulations! You now have a fully working usbtmc link.\u00c2\u00a0I haven&#8217;t had much time to play with this, but I know I can make really nice screenshots to an attached USB drive using the command: <span style=\"font-family: andale mono,times;\">instr.write(&#8220;:HARDcopy&#8221;)<\/span>. Many more commands can be found in the <strong>DS1000D\/E Programming Guide<\/strong>, available on <a href=\"http:\/\/www.rigolna.com\/products\/digital-oscilloscopes\/ds1000e\/\">Rigol<\/a>&#8216;s site.<\/p>\n<p>I had a couple of problems, though:<\/p>\n<ol>\n<li><del>The library seems to need root privileges, despite the udev rule thing<\/del>. After creating the udev rule, you will need to reboot. This is the simplest way of getting it to work without being root.<\/li>\n<li><del>Reading from the &#8216;scope&#8217;s memory\u00c2\u00a0 chokes on non-UTF8 characters. If I do:<\/del><br \/>\n<del> <span style=\"font-family: andale mono,times;\">rawdata = instr.ask(&#8220;:WAV:DATA? CHAN1&#8221;)[10:]<\/span><\/del><br \/>\n<del> I get a lengthy Python error which ends:<\/del><br \/>\n<del> \u00e2\u20ac\u00a6<\/del><br \/>\n<del> <span style=\"font-family: andale mono,times;\">File &#8220;\/usr\/lib\/python2.7\/encodings\/utf_8.py&#8221;, line 16, in decode<\/span><\/del><br \/>\n<del> <span style=\"font-family: andale mono,times;\">\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 return codecs.utf_8_decode(input, errors, True)<\/span><\/del><br \/>\n<del> <span style=\"font-family: andale mono,times;\">UnicodeDecodeError: &#8216;utf8&#8217; codec can&#8217;t decode byte 0x99 in position 10: invalid start byte<\/span><\/del><br \/>\n<del> I have no idea what that means, or how to fix it<\/del>. Alex suggested using ask_raw instead of ask, and the data comes through with no complaints.<\/li>\n<\/ol>\n<p>I&#8217;ve still got to work my way through the Rigol&#8217;s data format, but other people have done that before:<\/p>\n<ol>\n<li><a href=\"http:\/\/www.cibomahto.com\/2010\/04\/controlling-a-rigol-oscilloscope-using-linux-and-python\/\">Controlling a Rigol oscilloscope using Linux and Python | C i b o M a h t o . c o m<\/a><\/li>\n<li><a href=\"http:\/\/www.righto.com\/2013\/07\/rigol-oscilloscope-hacks-with-python.html\">Ken Shirriff&#8217;s blog: Four Rigol oscilloscope hacks with Python<\/a><\/li>\n<\/ol>\n<p>I&#8217;ll post any updates here, along with the Raspberry Pi forum topic: <a href=\"http:\/\/www.raspberrypi.org\/phpBB3\/viewtopic.php?f=66&amp;t=60784&amp;p=469648\">USB Test &amp; Measurement class (usbtmc) driver?<\/a><\/p>\n<p>Incidentally, if you&#8217;re working with WFM data dumps from the Rigol &#8216;scopes (and you should, because they make storing data to USB drives quick), <a href=\"https:\/\/github.com\/mabl\/pyRigolWFM\">mabl\/pyRigolWFM<\/a> is basically magic. Not merely can it describe and decode those binary files, it can do pretty graphics with no thought required:<\/p>\n<p><a href=\"http:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2013\/12\/figure_1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-10266\" src=\"http:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2013\/12\/figure_1-1024x555.png\" alt=\"made by pyRigolWFM\" width=\"474\" height=\"256\" srcset=\"https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2013\/12\/figure_1-1024x555.png 1024w, https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2013\/12\/figure_1-160x86.png 160w, https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2013\/12\/figure_1-320x173.png 320w, https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2013\/12\/figure_1.png 1938w\" sizes=\"auto, (max-width: 474px) 100vw, 474px\" \/><\/a>Hat tip for the mention: <a href=\"http:\/\/www.adafruit.com\/blog\/2013\/12\/20\/raspberry_pi-piday-raspberrypi-75\/\">MP3 Options &amp; Oscilloscope Interfacing For Raspberry Pi @Raspberry_Pi #piday #raspberrypi \u00c2\u00ab adafruit industries blog<\/a><\/p>\n<p><strong>Update, 2013-12-20<\/strong>: I&#8217;ve successfully managed to run most of <a href=\"http:\/\/www.righto.com\/2013\/07\/rigol-oscilloscope-hacks-with-python.html\">Ken&#8217;s examples<\/a> with Alex&#8217;s code. The major modification you have to do is use <span style=\"font-family: andale mono,times;\">ask_raw<\/span> instead of <span style=\"font-family: andale mono,times;\">ask<\/span>. Example code shown below:<\/p>\n<pre><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n#!\/usr\/bin\/python\n# -*- coding: utf-8 -*-\n\n&quot;&quot;&quot;\nDownload data from a Rigol DS1102E oscilloscope and graph with matplotlib\n         using  Alex Forencich's python-usbtmc pure python driver\n                https:\/\/github.com\/alexforencich\/python-usbtmc\nscruss - 2013-12-20\n\nbased on\nDownload data from a Rigol DS1052E oscilloscope and graph with matplotlib.\nBy Ken Shirriff, http:\/\/righto.com\/rigol\n\nwhich in turn was\nBased on http:\/\/www.cibomahto.com\/2010\/04\/controlling-a-rigol-oscilloscope-using-linux-and-python\/\nby Cibo Mahto.\n&quot;&quot;&quot;\n\nimport usbtmc\nimport time\nimport numpy\nimport matplotlib.pyplot as plot\n\n# initialise device\ninstr =  usbtmc.Instrument(0x1ab1, 0x0588) # Rigol DS1102E\n\n# read data\ninstr.write(&quot;:STOP&quot;)\ninstr.write(&quot;:WAV:POIN:MODE RAW&quot;)\n# first ten bytes are header, so skip\nrawdata = instr.ask_raw(&quot;:WAV:DATA? CHAN1&quot;)&#x5B;10:]\ndata_size = len(rawdata)\n\n# get metadata\nsample_rate = float(instr.ask_raw(':ACQ:SAMP?'))\ntimescale = float(instr.ask_raw(&quot;:TIM:SCAL?&quot;))\ntimeoffset = float(instr.ask_raw(&quot;:TIM:OFFS?&quot;))\nvoltscale = float(instr.ask_raw(':CHAN1:SCAL?'))\nvoltoffset = float(instr.ask_raw(&quot;:CHAN1:OFFS?&quot;))\n\n# show metadata\nprint &quot;Data size:      &quot;, data_size\nprint &quot;Sample rate:    &quot;, sample_rate\nprint &quot;Time scale:     &quot;, timescale\nprint &quot;Time offset:    &quot;, timeoffset\nprint &quot;Voltage offset: &quot;, voltoffset\nprint &quot;Voltage scale:  &quot;, voltscale\n\n# convert data from (inverted) bytes to an array of scaled floats\n# this magic from Matthew Mets\ndata = numpy.frombuffer(rawdata, 'B')\ndata = data * -1 + 255\ndata = (data - 130.0 - voltoffset\/voltscale*25) \/ 25 * voltscale\n\n# creat array of matching timestamps\ntime = numpy.linspace(timeoffset - 6 * timescale, timeoffset + 6 * timescale,\n                      num=len(data))\n\n# scale time series and label accordingly\nif (time&#x5B;-1] &amp;amp;lt; 1e-3):\n    time = time * 1e6\n    tUnit = &quot;\u00c2\u00b5S&quot;\nelif (time&#x5B;-1] &amp;amp;lt; 1):\n    time = time * 1e3\n    tUnit = &quot;mS&quot;\nelse:\n    tUnit = &quot;S&quot;\n\n# Plot the data\nplot.plot(time, data)\nplot.title(&quot;Oscilloscope Channel 1&quot;)\nplot.ylabel(&quot;Voltage (V)&quot;)\nplot.xlabel(&quot;Time (&quot; + tUnit + &quot;)&quot;)\nplot.xlim(time&#x5B;0], time&#x5B;-1])\nplot.show()\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Hey! This post is completely ancient. It doesn&#8217;t even use Python 3. Advice given here might be well out of date. \u00e2\u20ac\u00a6 it complains that the oscilloscope is always making waves. Ahem. Anyway. I have a Rigol DS1102E 100 MHz Digital Oscilloscope. For such a cheap device, it&#8217;s remarkable that you can control it using [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[7],"tags":[1286,2506,2540,2510,2536,2760,229,2756],"class_list":["post-10238","post","type-post","status-publish","format-standard","hentry","category-computers-suck","tag-debian","tag-oscilloscope","tag-python","tag-raspberrypi","tag-raspbian","tag-rigol","tag-usb","tag-usbtmc"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pQNZZ-2F8","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts\/10238","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/comments?post=10238"}],"version-history":[{"count":9,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts\/10238\/revisions"}],"predecessor-version":[{"id":16641,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts\/10238\/revisions\/16641"}],"wp:attachment":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/media?parent=10238"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/categories?post=10238"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/tags?post=10238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}