~ekez/tui-webpage

db950c09803583b394d9b49c9e42345b4c390719 — Zeke Medley 8 days ago 7931fdb
Add escaping the IDE writeup
2 files changed, 211 insertions(+), 0 deletions(-)

M public/index.html
A public/non-arduino-ide.html
M public/index.html => public/index.html +1 -0
@@ 28,6 28,7 @@
	<li><a href="/1-thursday.html">Arduino communication guide</a></li>
	<li><a href="/serial-visualization.html">Plotting data with your Arduino</a></li>
	<li><a href="/lab-2.html">Lab 2 Code Walkthrough</a></li>
	<li><a href="/non-arduino-ide.html">Escaping the Arduino IDE</a></li>
      </ol>
    </body>
</html>

A public/non-arduino-ide.html => public/non-arduino-ide.html +210 -0
@@ 0,0 1,210 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <title>IDE Escape | TUI</title>
  <style>
    code{white-space: pre-wrap;}
    span.smallcaps{font-variant: small-caps;}
    span.underline{text-decoration: underline;}
    div.column{display: inline-block; vertical-align: top; width: 50%;}
    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
    ul.task-list{list-style: none;}
    pre > code.sourceCode { white-space: pre; position: relative; }
    pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
    pre > code.sourceCode > span:empty { height: 1.2em; }
    code.sourceCode > span { color: inherit; text-decoration: inherit; }
    div.sourceCode { margin: 1em 0; }
    pre.sourceCode { margin: 0; }
    pre { font-size: 130%; }
    @media screen {
    div.sourceCode { overflow: auto; }
    }
    @media print {
    pre > code.sourceCode { white-space: pre-wrap; }
    pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
    }
    pre.numberSource code
      { counter-reset: source-line 0; }
    pre.numberSource code > span
      { position: relative; left: -4em; counter-increment: source-line; }
    pre.numberSource code > span > a:first-child::before
      { content: counter(source-line);
        position: relative; left: -1em; text-align: right; vertical-align: baseline;
        border: none; display: inline-block;
        -webkit-touch-callout: none; -webkit-user-select: none;
        -khtml-user-select: none; -moz-user-select: none;
        -ms-user-select: none; user-select: none;
        padding: 0 4px; width: 4em;
        color: #aaaaaa;
      }
    pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
    div.sourceCode
      {   }
    @media screen {
    pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
    }
    code span.al { color: #ff0000; font-weight: bold; } /* Alert */
    code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
    code span.at { color: #7d9029; } /* Attribute */
    code span.bn { color: #40a070; } /* BaseN */
    code span.bu { } /* BuiltIn */
    code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
    code span.ch { color: #4070a0; } /* Char */
    code span.cn { color: #880000; } /* Constant */
    code span.co { color: #60a0b0; font-style: italic; } /* Comment */
    code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
    code span.do { color: #ba2121; font-style: italic; } /* Documentation */
    code span.dt { color: #902000; } /* DataType */
    code span.dv { color: #40a070; } /* DecVal */
    code span.er { color: #ff0000; font-weight: bold; } /* Error */
    code span.ex { } /* Extension */
    code span.fl { color: #40a070; } /* Float */
    code span.fu { color: #06287e; } /* Function */
    code span.im { } /* Import */
    code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
    code span.kw { color: #007020; font-weight: bold; } /* Keyword */
    code span.op { color: #666666; } /* Operator */
    code span.ot { color: #007020; } /* Other */
    code span.pp { color: #bc7a00; } /* Preprocessor */
    code span.sc { color: #4070a0; } /* SpecialChar */
    code span.ss { color: #bb6688; } /* SpecialString */
    code span.st { color: #4070a0; } /* String */
    code span.va { color: #19177c; } /* Variable */
    code span.vs { color: #4070a0; } /* VerbatimString */
    code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
  </style>
  <link rel="stylesheet" href="/style.css">
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
</head>
<body>
  <nav>
    <a href="/">home</a>
  </nav>
  <hr>
<h1 id="escaping-the-arduino-ide">Escaping the Arduino IDE</h1>
<p>In <a href="/1-thursday.html">last week’s</a> Thursday lab we discussed making our Arduino programs interactive. The approach outlined there was limited though as we could only perform that interaction from inside of the Arduino IDE. In practice and in your projects we want to escape the IDE and thus increase the amount of digital real estate that our tangible interfaces have access to.</p>
<p>In today’s Thursday bonus lab we will perform this escape act and start communicating with our Arduinos from Python programs. Having done that we will then explore how we can expand the types of user input that we can use to control our Arduino.</p>
<h2 id="act-1---the-escape">Act 1 - The escape</h2>
<p>As we saw last week, we can send messages to our Arduino via the IDE’s serial monitor. For example, with the code below loaded onto our Arduino we can send messages to the device which it will send back by opening <code>Tools -&gt; Serial Monitor</code> in the context menu and typing into the top bar.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1"></a><span class="dt">void</span> setup() {</span>
<span id="cb1-2"><a href="#cb1-2"></a>  Serial.begin(<span class="dv">9600</span>);</span>
<span id="cb1-3"><a href="#cb1-3"></a>}</span>
<span id="cb1-4"><a href="#cb1-4"></a></span>
<span id="cb1-5"><a href="#cb1-5"></a><span class="dt">void</span> loop() {</span>
<span id="cb1-6"><a href="#cb1-6"></a>  <span class="cf">if</span> (Serial.available()) {</span>
<span id="cb1-7"><a href="#cb1-7"></a>    String message = Serial.readStringUntil(<span class="ch">&#39;</span><span class="sc">\n</span><span class="ch">&#39;</span>);</span>
<span id="cb1-8"><a href="#cb1-8"></a>    Serial.print(<span class="st">&quot;: &quot;</span> + message + <span class="st">&quot;</span><span class="sc">\n</span><span class="st">&quot;</span>);</span>
<span id="cb1-9"><a href="#cb1-9"></a>  }</span>
<span id="cb1-10"><a href="#cb1-10"></a>}</span></code></pre></div>
<p>An interesting feature of this ‘serial’ style of interaction is that it is neither unique to Arduinos nor the Arduino IDE. We can communicate with our Arduino via this method using essentially whatever language or method we’d like. Let’s take a look at how we might do so uding the <a href="https://python.org">Python programming langauge</a>.</p>
<p>Python is a nice choice for this task as its syntax is designed to be relatively human readable even if you don’t know the language and it has an extensive echosystem of libraries to interface with other systems.</p>
<h3 id="getting-set-up">Getting set up</h3>
<p>Start by following the directions <a href="https://wiki.python.org/moin/BeginnersGuide/Download">here</a> to install Python if you do not have it installed already. Having done that you ought to be able to open the Terminal / Command Prompt application on macOS / Windows and run python by typing <code>python</code>.</p>
<p>Here is an example python session in my terminal where I ask the langauge to print <code>hello!</code> to the screen:</p>
<pre><code>$ python3
&gt;&gt;&gt; print(&#39;hello!&#39;)hello!
&gt;&gt;&gt; exit()</code></pre>
<p>Having installed python we can install a library to interface with the Arduino’s serial port by running the following command in our terminals:</p>
<pre><code>$ python3 -m pip install pyserial</code></pre>
<h3 id="interfacing-with-our-arduino">Interfacing with our Arduino</h3>
<p>Having gotten set up, go ahead and upload the “echo” Arduino sketch from the introduction to this section to your Arduino board. Then copy the following python program into a file called <code>pyserial.py</code> on your computer:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="im">import</span> serial</span>
<span id="cb4-2"><a href="#cb4-2"></a></span>
<span id="cb4-3"><a href="#cb4-3"></a><span class="kw">def</span> get_ports():</span>
<span id="cb4-4"><a href="#cb4-4"></a>    <span class="co">&quot;&quot;&quot;List all of the avaliable ports on the device.&quot;&quot;&quot;</span></span>
<span id="cb4-5"><a href="#cb4-5"></a>    <span class="im">import</span> serial.tools.list_ports</span>
<span id="cb4-6"><a href="#cb4-6"></a>    <span class="cf">return</span> serial.tools.list_ports.comports()</span>
<span id="cb4-7"><a href="#cb4-7"></a></span>
<span id="cb4-8"><a href="#cb4-8"></a><span class="kw">def</span> prompt_for_and_get_port():</span>
<span id="cb4-9"><a href="#cb4-9"></a>    <span class="co">&quot;&quot;&quot;Prompts the user to select a port. Returns the selected</span></span>
<span id="cb4-10"><a href="#cb4-10"></a><span class="co">    one.</span></span>
<span id="cb4-11"><a href="#cb4-11"></a></span>
<span id="cb4-12"><a href="#cb4-12"></a><span class="co">    Input is expected to be a number in [0, num_ports) and is</span></span>
<span id="cb4-13"><a href="#cb4-13"></a><span class="co">    read from standard in. An input outside of that range will result</span></span>
<span id="cb4-14"><a href="#cb4-14"></a><span class="co">    in re-prompting until a valid input is provided.</span></span>
<span id="cb4-15"><a href="#cb4-15"></a><span class="co">    &quot;&quot;&quot;</span></span>
<span id="cb4-16"><a href="#cb4-16"></a>    ports <span class="op">=</span> get_ports()</span>
<span id="cb4-17"><a href="#cb4-17"></a>    <span class="cf">for</span> index, port <span class="kw">in</span> <span class="bu">enumerate</span>(ports):</span>
<span id="cb4-18"><a href="#cb4-18"></a>        <span class="bu">print</span>(<span class="st">&quot;</span><span class="sc">{}</span><span class="st">) </span><span class="sc">{}</span><span class="ch">\t</span><span class="sc">{}</span><span class="st">&quot;</span>.<span class="bu">format</span>(index, port.device, port.description))</span>
<span id="cb4-19"><a href="#cb4-19"></a>    selection <span class="op">=</span> <span class="bu">int</span>(<span class="bu">input</span>(<span class="st">&quot;please input the port number that your port is on: &quot;</span>))</span>
<span id="cb4-20"><a href="#cb4-20"></a>    <span class="cf">while</span> selection <span class="op">&lt;</span> <span class="dv">0</span> <span class="kw">or</span> selection <span class="op">&gt;=</span> <span class="bu">len</span>(ports):</span>
<span id="cb4-21"><a href="#cb4-21"></a>        <span class="bu">print</span>(<span class="st">&quot;error: </span><span class="sc">{}</span><span class="st"> is not between 0 and </span><span class="sc">{}</span><span class="st">&quot;</span>.<span class="bu">format</span>(selection, <span class="bu">len</span>(ports) <span class="op">-</span> <span class="dv">1</span>))</span>
<span id="cb4-22"><a href="#cb4-22"></a>        selection <span class="op">=</span> <span class="bu">int</span>(<span class="bu">input</span>(<span class="st">&quot;please input the port number that your port is on: &quot;</span>))</span>
<span id="cb4-23"><a href="#cb4-23"></a>    <span class="cf">return</span> ports[selection]</span>
<span id="cb4-24"><a href="#cb4-24"></a></span>
<span id="cb4-25"><a href="#cb4-25"></a><span class="kw">def</span> prompt_for_and_get_serial(baudrate<span class="op">=</span><span class="dv">9600</span>):</span>
<span id="cb4-26"><a href="#cb4-26"></a>    <span class="co">&quot;&quot;&quot;Prompts the user to select a port and returns a serial object</span></span>
<span id="cb4-27"><a href="#cb4-27"></a><span class="co">    connected to that port.</span></span>
<span id="cb4-28"><a href="#cb4-28"></a></span>
<span id="cb4-29"><a href="#cb4-29"></a><span class="co">    By default the returned serial object uses a 9600 baudrate but</span></span>
<span id="cb4-30"><a href="#cb4-30"></a><span class="co">    this can be modified by passing one to the function.</span></span>
<span id="cb4-31"><a href="#cb4-31"></a><span class="co">    &quot;&quot;&quot;</span></span>
<span id="cb4-32"><a href="#cb4-32"></a>    port <span class="op">=</span> prompt_for_and_get_port()</span>
<span id="cb4-33"><a href="#cb4-33"></a>    <span class="cf">return</span> serial.Serial(port<span class="op">=</span>port.device, baudrate<span class="op">=</span>baudrate)</span>
<span id="cb4-34"><a href="#cb4-34"></a></span>
<span id="cb4-35"><a href="#cb4-35"></a>serial <span class="op">=</span> prompt_for_and_get_serial()</span>
<span id="cb4-36"><a href="#cb4-36"></a></span>
<span id="cb4-37"><a href="#cb4-37"></a><span class="cf">while</span> <span class="va">True</span>:</span>
<span id="cb4-38"><a href="#cb4-38"></a>    send <span class="op">=</span> <span class="bu">input</span>(<span class="st">&#39;&gt;&gt; &#39;</span>)</span>
<span id="cb4-39"><a href="#cb4-39"></a>    serial.write(<span class="bu">bytes</span>(send, <span class="st">&#39;utf-8&#39;</span>))</span>
<span id="cb4-40"><a href="#cb4-40"></a>    <span class="bu">print</span>(serial.readline().decode(<span class="st">&#39;utf-8&#39;</span>)[:<span class="op">-</span><span class="dv">1</span>])</span></code></pre></div>
<p>If you are using a Mac you can do this by copying the above code and running <code>pbpaste &gt; pyserial.py</code> inside of your Terminal window. If you are on Windows and you are comfortable using a text editor you may create the file like that.</p>
<p>The program that you’re looking at exposes a function called <code>prompt_for_and_get_serial</code> which when run will ask the user what port their Arduino is connected to and open a serial connection to the Arduino there. It will then take input from the user, send that input to the Arduino, and display the Arduino’s response.</p>
<p>Here is an example of an interaction with the program. Note that I run <code>python3 pyserial.py</code> to start the program.</p>
<pre><code>$ python3 pyserial.py
0) /dev/cu.blueberry-SPPDev-11  n/a
1) /dev/cu.blueberry-SPPDev-4   n/a
2) /dev/cu.Bluetooth-Incoming-Port  n/a
3) /dev/cu.iPhone-WirelessiAPv2 n/a
4) /dev/cu.elzekeo-SPPDev   n/a
5) /dev/cu.usbmodem14201    Arduino Uno
please input the port number that your port is on: 5
&gt;&gt; hello
: hello
&gt;&gt; goodbye
: goodbye</code></pre>
<p>Having done that we have successfully escaped the Arduino IDE and are controlling the Arduino from a python program! You can find the complete source code with some documentation for this program <a href="https://git.sr.ht/~ekez/tui-webpage/tree/master/item/examples/pyserial.py">here</a>.</p>
<h2 id="act-2---expanding-our-interaction-options">Act 2 - Expanding our interaction options</h2>
<p>Recall from the Wednesday lab item two for your homework:</p>
<pre><code>Change the code so that you can control the RGB values with multiple key presses. For example, pressing ‘r’ 5 times will set the brightness to 50% (or brightness = 127) and pressing ‘r’ 10 times will set it to 100% (or brightness = 255)</code></pre>
<p>Performing this interaction in the Arduino IDE produces a less than desirable result. In order to send data to the Arduino via the IDE you are required to type your input into the text box and then press enter. This effectively transforms the interaction from ‘press r five times’ to ‘press r followed by enter 5 times.’</p>
<p>An advantage of using our Python script is that we no longer have this constraint. We can send input to the user whenever we would like with <code>serial.write()</code>.</p>
<p>In order to do this I’ve provided a python script which exposes a <code>getch</code> function. <code>getch</code> reads a character from input on the terminal without requiring that the user presses enter. You can find the source code <a href="https://git.sr.ht/~ekez/tui-webpage/tree/master/item/examples/getch.py">here</a> along with instructions on using it at the top of the file.</p>
<p>Here’s an example of a program that sends a number to the arduino representing the amount of time that has passed between when the user last pressed a key and the current keypress. This program uses the functions from the two provided python programs to do this:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">def</span> get_time():</span>
<span id="cb7-2"><a href="#cb7-2"></a>    <span class="cf">return</span> <span class="bu">round</span>(time.time() <span class="op">*</span> <span class="dv">1000</span>)</span>
<span id="cb7-3"><a href="#cb7-3"></a></span>
<span id="cb7-4"><a href="#cb7-4"></a>serial <span class="op">=</span> prompt_for_and_get_serial()</span>
<span id="cb7-5"><a href="#cb7-5"></a>lastPress <span class="op">=</span> get_time()</span>
<span id="cb7-6"><a href="#cb7-6"></a></span>
<span id="cb7-7"><a href="#cb7-7"></a><span class="cf">while</span> <span class="va">True</span>:</span>
<span id="cb7-8"><a href="#cb7-8"></a>    prompt <span class="op">=</span> getch()</span>
<span id="cb7-9"><a href="#cb7-9"></a>    <span class="cf">if</span> prompt <span class="op">==</span> <span class="st">&#39;.&#39;</span>:</span>
<span id="cb7-10"><a href="#cb7-10"></a>        <span class="cf">break</span></span>
<span id="cb7-11"><a href="#cb7-11"></a></span>
<span id="cb7-12"><a href="#cb7-12"></a>    pressTime <span class="op">=</span> get_time()</span>
<span id="cb7-13"><a href="#cb7-13"></a>    elapsed <span class="op">=</span> <span class="dv">3</span> <span class="op">*</span> (pressTime <span class="op">-</span> lastPress) <span class="op">//</span> <span class="dv">4</span></span>
<span id="cb7-14"><a href="#cb7-14"></a></span>
<span id="cb7-15"><a href="#cb7-15"></a>    <span class="cf">if</span> elapsed <span class="op">&lt;</span> <span class="dv">255</span>:</span>
<span id="cb7-16"><a href="#cb7-16"></a>        serial.write(<span class="bu">bytes</span>(<span class="bu">str</span>(<span class="dv">255</span> <span class="op">-</span> elapsed), <span class="st">&#39;utf-8&#39;</span>))</span>
<span id="cb7-17"><a href="#cb7-17"></a></span>
<span id="cb7-18"><a href="#cb7-18"></a>    <span class="co"># Write a space so that the Arduino can tell the sent numbers</span></span>
<span id="cb7-19"><a href="#cb7-19"></a>    <span class="co"># apart.</span></span>
<span id="cb7-20"><a href="#cb7-20"></a>    serial.write(b<span class="st">&#39; &#39;</span>)</span>
<span id="cb7-21"><a href="#cb7-21"></a></span>
<span id="cb7-22"><a href="#cb7-22"></a>    lastPress <span class="op">=</span> pressTime</span></code></pre></div>
<h2 id="your-task-should-you-choose-to-accept-it">Your task should you choose to accept it</h2>
<p>Use the example code here to communicate with the Arduino. Can you enable an interaction not otherwise possible with just the Arduino IDE?</p>
<p>Some ideas:</p>
<ol type="1">
<li>Implement the task from homework 2 but without requiring the user to press enter between letters.</li>
<li>Light up a LED on the Arduino according to how quickly the user is pressing buttons.</li>
<li>Write an arduino program to light up a LED according to numbers being sent over the serial interface (<code>Serial.parseInt()</code> may be helpful here) and then use python to send some information about your computers internet connection to the Arduino.</li>
</ol>
</body>
</html>