tag:blogger.com,1999:blog-59831108810181724522024-03-12T18:06:07.211-06:00Bot ThoughtsA blog of robotics, electronics, mechanics, programming, and engineering. <br>Pictures, source code, circuit diagrams, ideas, thoughts, drawings, sketches and real-life goofups.Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.comBlogger468125tag:blogger.com,1999:blog-5983110881018172452.post-6912776872828645112022-04-01T15:16:00.001-06:002022-04-04T09:47:10.257-06:00Electronic Cricket Prank<p>Courtesy of my prankster daughter, I came downstairs to find Bot Thoughts labs "decorated" for April Fool's Day...</p><p> </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-x9dMk73KooVGJ2uJOLFwjsvjS4hALPdz3kOYBvT8MgcQW_FhTottSOxnt6NeYDfMXTcGcVTqQ7Qjy-mve-R_JjlhJlIql811gKlhA5CcaGsRBYVWPlu4i0dIcBc_dRLiaTB2x_vGVy3WqW8WzBy_bkQVMPpuP5K6PdFDDfV476BMkNLfz2eWE5Z9zw/s4032/PXL_20220401_200735949.jpg" style="margin-left: 1em; margin-right: 1em;"><img alt="desk covered in balloons and streamers" border="0" data-original-height="3024" data-original-width="4032" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-x9dMk73KooVGJ2uJOLFwjsvjS4hALPdz3kOYBvT8MgcQW_FhTottSOxnt6NeYDfMXTcGcVTqQ7Qjy-mve-R_JjlhJlIql811gKlhA5CcaGsRBYVWPlu4i0dIcBc_dRLiaTB2x_vGVy3WqW8WzBy_bkQVMPpuP5K6PdFDDfV476BMkNLfz2eWE5Z9zw/w400-h300/PXL_20220401_200735949.jpg" title="desk covered in balloons and streamers" width="400" /></a></div><br /><p></p><p>Clearly, I had no choice but to exact revenge. To that end, I will be hiding an electronic cricket in her room to chirp at random, lengthy intervals. But where might one find an electric cricket on short notice? By repurposing a little device I built. Read on...</p><span><a name='more'></a></span><p>Fortunately, I happen to be in the middle of redesigning my <a href="https://www.tindie.com/products/bot_thoughts/lost-model-alarm/">Lost RC Airplane Finder</a> for ultra-micro airplanes and have one assembled and ready to rock.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCILXsDjxDq5LcBnHiBOxxhKm0QujdJ9Hlyd-8ZeDUHQEg8dMmEUbyvsBeXjZDCp-cWZ2B47rpCUt1pl9wNdOHIb02RHuE0K5XLzRlonXvLUd1p6cDTPs8Ndv0_LgxVHj5jOFHwZ7AZHOfXi_2pLAjMBwDGcEm6ySfcI-NxNizJotPya9EyvP4Ka8jZQ/s2119/PXL_20220401_200716856~2.jpg" style="margin-left: auto; margin-right: auto;"><img alt="prototype lost model finder/alarm" border="0" data-original-height="1589" data-original-width="2119" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCILXsDjxDq5LcBnHiBOxxhKm0QujdJ9Hlyd-8ZeDUHQEg8dMmEUbyvsBeXjZDCp-cWZ2B47rpCUt1pl9wNdOHIb02RHuE0K5XLzRlonXvLUd1p6cDTPs8Ndv0_LgxVHj5jOFHwZ7AZHOfXi_2pLAjMBwDGcEm6ySfcI-NxNizJotPya9EyvP4Ka8jZQ/w400-h300/PXL_20220401_200716856~2.jpg" title="Prototype lost model finder/alarm" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><i>Lost Model Alarm R0.4 Prototype</i><br /></td></tr></tbody></table><p><br />This little beeper is tiny (the board is the size of a penny), powered by a CR1225 lithium battery, will beep for <i>days</i> and, best of all, it's programmable, featuring an ATtiny25 micro-controller<i>.</i><i> This will be perfect!</i> </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjktMbdiOw9RWnOpQ3bbQtmdVIwsJ3CvczQmcoi2wML7TifrpBPU9C54UFXKV4kNcXdPmGZR1QON9oxhMcyFz8kJRI6FvcMy4VKC50WVriLW2C-aO5mkxeDp3PBBI59tnGu5AHEFjIJC0g35uxjQ353k1YN1yvFVN24ByTI1XeL9gC9G8C5tvnJXQkfAA/s1024/drevil.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="Dr Evil" border="0" data-original-height="618" data-original-width="1024" height="121" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjktMbdiOw9RWnOpQ3bbQtmdVIwsJ3CvczQmcoi2wML7TifrpBPU9C54UFXKV4kNcXdPmGZR1QON9oxhMcyFz8kJRI6FvcMy4VKC50WVriLW2C-aO5mkxeDp3PBBI59tnGu5AHEFjIJC0g35uxjQ353k1YN1yvFVN24ByTI1XeL9gC9G8C5tvnJXQkfAA/w200-h121/drevil.png" title="Dr Evil" width="200" /></a></div><p></p><p>Now all I need to do is revise the original firmware.<br /></p><p>The beeper is designed to do two main things: provide a configurable flight time alarm, and provide an audible beacon to help you find your plane if you lose control and can't see where it landed. It also tells you if your CR1225 is getting low.<br /></p><p>While I don't need any of that code, the beeping and delay functions made it a snap to reprogram the little guy to sound like a cricket.</p><p>The main loop, once simplified, looks like the following.<br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;"> </span></div><div><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">main</span><span style="color: #d4d4d4;">() {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">disableWatchdog</span><span style="color: #d4d4d4;">();</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">sei</span><span style="color: #d4d4d4;">();</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">slowClock</span><span style="color: #d4d4d4;">();</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">loop</span><span style="color: #d4d4d4;">();</span></div><div><span style="color: #d4d4d4;">}</span></div><div><span style="color: #d4d4d4;"> </span></div></div><p>To save battery, the module powers down and only wakes up when the watchdog timer fires. It also slows down the main clock using the clock pre-scaler function and the CLKDIV8 fuse. Just to be sure the watchdog timer doesn't fire too soon, it is disabled until we're ready to loop. Interrupts are enabled with <i>sei()</i>. Then the clock is slowed down, then it begins the main loop, shown below.<br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><br /><div><span style="color: #569cd6;">void</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">loop</span><span style="color: #d4d4d4;">() {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">uint16_t</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">next_chirp_sec</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">;</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">r</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">;</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">while</span><span style="color: #d4d4d4;"> (</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">) {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">enableWatchdog</span><span style="color: #d4d4d4;">(); </span><span style="color: #6a9955;">// (re-)enable watchdog</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">set_sleep_mode</span><span style="color: #d4d4d4;">(SLEEP_MODE_PWR_DOWN);</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">sleep_mode</span><span style="color: #d4d4d4;">();</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #569cd6;">after</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">next_chirp_sec</span><span style="color: #d4d4d4;">)) {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">chirp</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">CHIRP_CYCLES</span><span style="color: #d4d4d4;">);</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">next_chirp_sec</span><span style="color: #d4d4d4;"> += </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">random</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">r</span><span style="color: #d4d4d4;">];</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">r</span><span style="color: #d4d4d4;"> += </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">;</span></div><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">r</span><span style="color: #d4d4d4;"> >= </span><span style="color: #b5cea8;">16</span><span style="color: #d4d4d4;">) </span><span style="color: #9cdcfe;">r</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">;</span></div></div><span style="color: #d4d4d4;"></span></div><div><span style="color: #d4d4d4;"> }</span></div><div><span style="color: #d4d4d4;"> }</span></div><div><span style="color: #d4d4d4;">}</span></div></div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><br /></div><p>The main loop function is responsible for checking to
see if it's time to chirp, chirping, and managing the sleep/wake cycle. </p><p>The MCU runs the loop once every second. To do so it first
enables the watchdog timer (WDT). It has to do this in each pass of the
loop because after the WDT interrupt fires and wakes up the MCU, it is
automatically disabled by hardware. That done, the code puts the tiny
into power down mode to be awoken later.<br /></p><p>After a second, the WDT wakes up the MCU and
it checks if it is time to chirp. If so, it chirps and sets the time for the next chirp. In either case, it
starts the loop over, and goes to sleep, and so on. </p><p>The interval between chirps is made maddeningly erratic by selecting the next value in the <i>random</i> array. The index starts over at 0 when it reaches the end of the array. Hopefully the interval is sufficient to make this somewhat difficult to find but not too difficult.<br /></p><p></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;"> </span></div><div><span style="color: #569cd6;">static</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">long</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">random</span><span style="color: #d4d4d4;">[</span><span style="color: #b5cea8;">16</span><span style="color: #d4d4d4;">] = {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">26</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">11</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">17</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">11</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">18</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">19</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">30</span></div><div><span style="color: #d4d4d4;">};</span></div><br /></div><p></p><p>Finally, the chirp is simply a series of short beeps, each separated by a short interval.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">void</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">chirp</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">l</span><span style="color: #d4d4d4;">) {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">i</span><span style="color: #d4d4d4;">;</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">i</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">i</span><span style="color: #d4d4d4;"> < </span><span style="color: #9cdcfe;">l</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">i</span><span style="color: #d4d4d4;">++) {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">beepOn</span><span style="color: #d4d4d4;">();</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">wait_ms</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">CHIRP_DELAY_MS</span><span style="color: #d4d4d4;">);</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">beepOff</span><span style="color: #d4d4d4;">();</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">wait_ms</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">CHIRP_DELAY_MS</span><span style="color: #d4d4d4;">);</span></div><div><span style="color: #d4d4d4;"> }</span></div><div><span style="color: #d4d4d4;">}</span></div></div><p></p><p>By listening to a couple of cricket videos I determined by ear that a chirp delay of 12ms and 13 cycles was close enough to real life. <br /></p><p>Update: Annoying cricket was annoying. Success! I had to give in and show her where it was before it drove the poor girl crazy.<br /></p>Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-27025680317531911082021-11-22T19:00:00.310-07:002021-11-22T19:00:00.216-07:00Make an ESP8266 WiFi Temperature Sensor & Python Flask Backend - Part 4<p><a href="https://www.bot-thoughts.com/2021/11/make-esp8266-wifi-temperature-sensor_0889444787.html">Last time</a>, we combined our example sketches into a functioning temperature sensor and refactored the backend to receive temperature data. This time, let's get basic plotting working. To do that, we need to:</p><ul style="text-align: left;"><li>Save temperature data on the backend<br /></li><li>Implement an API for getting that data</li><li>Write a client-side JavaScript app to plot the data with Chart.js</li></ul><p>Here's our familiar diagram; we'll focus on the items marked with an orange star.</p><p></p><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-pCnTYHa2n0s/YZKHv_oIFBI/AAAAAAAA4bs/jgdwjFsturIXo7BCgdixlO7uTHuKyU1UgCNcBGAsYHQ/s539/temp_sensor4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="214" data-original-width="539" height="159" src="https://1.bp.blogspot.com/-pCnTYHa2n0s/YZKHv_oIFBI/AAAAAAAA4bs/jgdwjFsturIXo7BCgdixlO7uTHuKyU1UgCNcBGAsYHQ/w400-h159/temp_sensor4.png" width="400" /></a></div><p></p><div style="text-align: left;">Let's start with saving temperature data... <span><a name='more'></a></span></div><h1 style="text-align: left;">Save Temperature Data</h1><p>Eventually you'd want to save temperature data to a database. To keep things simple, I'm simply going to log the data to a file in CSV (comma-separated) format. Each entry will include temperature (C), a timestamp, the units of measurement ("C"), and the sender's IP address. <br /></p><p>Logging the data is simply a matter of appending the data to a local file each time it's posted to the API. To get the timestamp, we need to import <span style="font-family: Roboto Mono;">datetime</span>. </p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">from</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">datetime</span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">import</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">datetime</span><br /></div></div><p>We also need a format string for the timestamp. </p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #9cdcfe;">date_fmt</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">"%y-%m-</span><span style="color: #569cd6;">%d</span><span style="color: #ce9178;"> %H:%M:%S"</span> </div></div><p>The timestamp will be in the form: <i>YYYY-MM-DD hh:mm:ss</i><br /></p><p>The IP address is available from Flask's request object via <span style="font-family: Roboto Mono;">request.remote_addr</span>. The code looks like this:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">now</span><span style="color: #d4d4d4;"> = </span><span style="color: #4ec9b0;">datetime</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">strftime</span><span style="color: #d4d4d4;">(</span><span style="color: #4ec9b0;">datetime</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">utcnow</span><span style="color: #d4d4d4;">(), date_fmt)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">with</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">open</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"temp.log"</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">"a"</span><span style="color: #d4d4d4;">) </span><span style="color: #c586c0;">as</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">write</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"</span><span style="color: #569cd6;">{ts}</span><span style="color: #ce9178;">,</span><span style="color: #569cd6;">{ip}</span><span style="color: #ce9178;">,</span><span style="color: #569cd6;">{tc:4.2f}</span><span style="color: #ce9178;">,C</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">format</span><span style="color: #d4d4d4;">(</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">ts</span><span style="color: #d4d4d4;">=</span><span style="color: #9cdcfe;">now</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">ip</span><span style="color: #d4d4d4;">=</span><span style="color: #9cdcfe;">request</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">remote_addr</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">tc</span><span style="color: #d4d4d4;">=</span><span style="color: #9cdcfe;">tc</span><span style="color: #d4d4d4;">))</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">close</span><span style="color: #d4d4d4;">()</span></div></div><p>Once everything is running, our temp.log begins filling up with entries:</p><p><span style="color: #0b5394;"><span style="font-family: Roboto Mono;">21-11-14 17:55:47,192.168.1.82,20.75,C<br />21-11-14 17:55:58,192.168.1.82,20.75,C<br />21-11-14 17:56:08,192.168.1.82,20.75,C</span></span><span style="color: #0b5394;"><span style="font-family: Roboto Mono;"><br /></span></span></p><p>The next step is to get the backend API working to retrieve this data.</p><h1 style="text-align: left;">Backend GET API</h1><p>We already have a route defined in our Flask app to POST data. We need another route to GET data. I've stubbed it out:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #dcdcaa;">@</span><span style="color: #9cdcfe;">app</span><span style="color: #dcdcaa;">.</span><span style="color: #dcdcaa;">route</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"/temp"</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">methods</span><span style="color: #d4d4d4;">=[</span><span style="color: #ce9178;">"GET"</span><span style="color: #d4d4d4;">])</span></div><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">temp_get</span><span style="color: #d4d4d4;">():</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">""</span></div></div><h2 style="text-align: left;">Reading The File <br /></h2><p>First, we open the file. One of the principles of software engineering is Don't Repeat Yourself (DRY). We could add to our new route the following open statement.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">with</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">open</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"temp.log"</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">"r"</span><span style="color: #d4d4d4;">) </span><span style="color: #c586c0;">as</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">:</span></div></div><p>The problem is if we decide to change the log file, we have to do it in two places: here and in our temp_post() route. Instead, following DRY, make a single variable for the logfile name</p><p></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #9cdcfe;">log_file</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"temp.log"</span><br /></div></div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><br /></div><p>and use that in both routes like this:<br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">with</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">open</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">log_file</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">"r"</span><span style="color: #d4d4d4;">) </span><span style="color: #c586c0;">as</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">:</span></div></div><p><br />Now we read the lines out of the file into an array using readlines().</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">with</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">open</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">log_file</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">"r"</span><span style="color: #d4d4d4;">) </span><span style="color: #c586c0;">as</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">readlines</span><span style="color: #d4d4d4;">()</span></div></div><h2 style="text-align: left;">Converting to JSON</h2><p>Our API is supposed to return JSON data. We'll return an array of JSON objects that look like this:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">{</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"tempC"</span><span style="color: #d4d4d4;">: </span><span style="color: #b5cea8;">20.75</span><span style="color: #d4d4d4;">,</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"unit"</span><span style="color: #d4d4d4;">: </span><span style="color: #d4d4d4;"><span style="color: #ce9178;">"C"</span>,</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"ip"</span><span style="color: #d4d4d4;">: </span><span style="color: #ce9178;">"192.168.1.82"</span><span style="color: #d4d4d4;">,</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"timestamp"</span><span style="color: #d4d4d4;">: </span><span style="color: #ce9178;">"21-11-14 17:55:58"</span></div><div><span style="color: #d4d4d4;">}</span></div></div><p></p><p>We can either construct the JSON by creating an array of formatted strings, or we can create a data structure and use <span style="font-family: Roboto Mono;">jsonify()</span>. The former is preferable, because we're not reinventing the wheel with one more opportunity to goof up the format. </p><p>We'll loop through the lines of data, converting each to a <i>dict</i> and adding it to an <i>list</i>. Then we'll convert the <i>list</i> of <i>dicts</i> JSON and return the text.</p><p>We need to import jsonify.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">from</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">flask</span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">import</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">Flask</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">request</span><span style="color: #d4d4d4;">, </span><span style="color: #dcdcaa;">jsonify</span></div></div><p>With that, I came up with the following route function:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #dcdcaa;">@</span><span style="color: #9cdcfe;">app</span><span style="color: #dcdcaa;">.</span><span style="color: #dcdcaa;">route</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"/temp"</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">methods</span><span style="color: #d4d4d4;">=[</span><span style="color: #ce9178;">"GET"</span><span style="color: #d4d4d4;">])</span></div><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">temp_get</span><span style="color: #d4d4d4;">():</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">result</span><span style="color: #d4d4d4;"> = []</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">with</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">open</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">log_file</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">"r"</span><span style="color: #d4d4d4;">) </span><span style="color: #c586c0;">as</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">readlines</span><span style="color: #d4d4d4;">()</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">d</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">ts</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">ip</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">tc</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">unit</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">d</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">rstrip</span><span style="color: #d4d4d4;">().</span><span style="color: #dcdcaa;">split</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">","</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">entry</span><span style="color: #d4d4d4;"> = {}</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">entry</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">"tempC"</span><span style="color: #d4d4d4;">] = </span><span style="color: #4ec9b0;">float</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">tc</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">entry</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">"unit"</span><span style="color: #d4d4d4;">] = </span><span style="color: #9cdcfe;">unit</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">entry</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">"timestamp"</span><span style="color: #d4d4d4;">] = </span><span style="color: #9cdcfe;">ts</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">entry</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">"ip"</span><span style="color: #d4d4d4;">] = </span><span style="color: #9cdcfe;">ip</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">result</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">append</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">entry</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">jsonify</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">result</span><span style="color: #d4d4d4;">)</span></div></div><p>Note that python reads in the newline (<span style="color: #0b5394;"><span style="font-family: Roboto Mono;">\n</span></span>) for each line so we use <span style="font-family: Roboto Mono;">rstrip()</span> to remove it. </p><p></p><p>When I visit the endpoint with the browser, I see properly formatted JSON data in the form of an array of objects with the expected keys and values:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-QlZ4tZoSFG8/YZFeOS2l0tI/AAAAAAAA4bQ/-zRhri89DEQjztv6L-fz7UTMeaN9J5bEgCPcBGAYYCw/s420/temp_json1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="227" data-original-width="420" height="216" src="https://1.bp.blogspot.com/-QlZ4tZoSFG8/YZFeOS2l0tI/AAAAAAAA4bQ/-zRhri89DEQjztv6L-fz7UTMeaN9J5bEgCPcBGAYYCw/w400-h216/temp_json1.png" width="400" /></a></div><p></p><h1 style="text-align: left;"></h1><div style="text-align: left;">All that's left is to write our client-side plotting application. <br /></div><h1 style="text-align: left;">Plotting</h1><p></p><p>Our Flask app not only implements a backend API but also will implement a Single Page Application. That simply means it will serve a single webpage which loads client-side JavaScript to perform the plotting. So let's start with the webpage.</p><h2 style="text-align: left;">index.html</h2><p>Flask allows you to serve static pages for a given route, or to render a page template, server-side. We'll use the approach.</p><p>First of all, let's create a subdirectory for our static content called <span style="font-family: Roboto Mono;">static</span>.</p><p>We'll create a boilerplate <span style="font-family: Roboto Mono;">index.html</span> with an <span style="font-family: Roboto Mono;">H1</span> header.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: grey;"><!</span><span style="color: #569cd6;">DOCTYPE</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">html</span><span style="color: grey;">></span></div><div><span style="color: grey;"><</span><span style="color: #569cd6;">html</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">lang</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"en"</span><span style="color: grey;">></span></div><div><span style="color: grey;"><</span><span style="color: #569cd6;">head</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">meta</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">charset</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"utf-8"</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">meta</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">http-equiv</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"X-UA-Compatible"</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">content</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"IE=edge"</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">meta</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">http-equiv</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Cache-Control"</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">content</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"no-cache, no-store, must-revalidate"</span><span style="color: #d4d4d4;"> </span><span style="color: grey;">/></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">meta</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">http-equiv</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Pragma"</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">content</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"no-cache"</span><span style="color: #d4d4d4;"> </span><span style="color: grey;">/></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">meta</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">http-equiv</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Expires"</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">content</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"0"</span><span style="color: #d4d4d4;"> </span><span style="color: grey;">/></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">title</span><span style="color: grey;">></span><span style="color: grey;"></span><span style="color: #d4d4d4;">Temperature Plot</span><span style="color: #d4d4d4;"></span><span style="color: grey;"></</span><span style="color: #569cd6;">title</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">link</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">rel</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"shortcut icon"</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">href</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"{{ url_for('static', filename='favicon.ico') }}"</span><span style="color: grey;">></span></div><div><span style="color: grey;"></</span><span style="color: #569cd6;">head</span><span style="color: grey;">></span></div><div><span style="color: grey;"><</span><span style="color: #569cd6;">body</span><span style="color: grey;">></span></div><br /><div><span style="color: grey;"><</span><span style="color: #569cd6;">h1</span><span style="color: grey;">></span><span style="color: #d4d4d4;">Temperature Plot</span><span style="color: grey;"></</span><span style="color: #569cd6;">h1</span><span style="color: grey;">></span></div><br /><div><span style="color: grey;"></</span><span style="color: #569cd6;">body</span><span style="color: grey;">></span></div><div><span style="color: grey;"></</span><span style="color: #569cd6;">html</span><span style="color: grey;">></span></div></div><p></p><p>To return this page, instead of "hello world", for the "/" route in our Flask app, have to configure the <span style="font-family: Roboto Mono;">static_dir </span>for app when our app is instantiated.</p><p></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #9cdcfe;">app</span><span style="color: #d4d4d4;"> = </span><span style="color: #4ec9b0;">Flask</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">__name__</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">static_folder</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"./static"</span><span style="color: #d4d4d4;">)</span><br /></div></div><p>In our "/" route, we change the return statement, to call the <span style="font-family: Roboto Mono;">send_static_file()</span> method on app.<br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #dcdcaa;">@</span><span style="color: #9cdcfe;">app</span><span style="color: #dcdcaa;">.</span><span style="color: #dcdcaa;">route</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"/"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">index</span><span style="color: #d4d4d4;">():</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">app</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">send_static_file</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'index.html'</span><span style="color: #d4d4d4;">)</span></div></div><p>Testing with your browser shows it works!</p><h2 style="text-align: left;">plot.js</h2><p>The only thing left is to make use of chart.js and JavaScript to get the data and plot it.</p><p>I created plot.js in the static directory and populated it with the above chart object.<br /></p><p>So let's obtain the data from our API that we made earlier. I'm going to use jQuery to call the API asynchronously. If you're not familiar with the concept, basically the query runs in a thread and when it's done, it executes the code you specify.</p><p>For now we'll just print the data to the console. But before we get into the JavaScript we have to make two additions to our <span style="font-family: Roboto Mono;">index.html</span>. One loads jQuery, the other loads the plot.js file. I added the following to the head section of <span style="font-family: Roboto Mono;">index.html</span>.<br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">script</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">type</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"text/javascript"</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;"> </span></div><div><span style="color: #9cdcfe;">src</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"</span><span style="color: grey;">></span></div><div><span style="color: grey;"></</span><span style="color: #569cd6;">script</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">script</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">type</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"text/javascript"</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">src</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"static/plot.js"</span><span style="color: grey;">></</span><span style="color: #569cd6;">script</span><span style="color: grey;">></span></div></div><p></p><p></p><p>Next, in plot.js, we add the code to perform an asynchronous get request against our API. The anonymous function simply logs the response data to the console.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> </span><span style="color: #4fc1ff;">url</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">"http://localhost:5000/temp"</span><span style="color: #d4d4d4;">;</span></div><br /><div><span style="color: #9cdcfe;">$</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">getJSON</span><span style="color: #d4d4d4;">(</span><span style="color: #4fc1ff;">url</span><span style="color: #d4d4d4;">).</span><span style="color: #dcdcaa;">done</span><span style="color: #d4d4d4;">((</span><span style="color: #9cdcfe;">response</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">=></span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">console</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">log</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">response</span><span style="color: #d4d4d4;">);</span></div><div><span style="color: #d4d4d4;">});</span></div></div><p></p><p>When you visit the index page, you can view console output by pressing F12 to open developer tools. You can expand the data to see that, indeed, we're retrieving the JSON data.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-sJ7YFrUNKRw/YZFfYHrjkGI/AAAAAAAA4bY/piT6UddAZII-kzyAUqVlHAe-UMZO8xyLQCNcBGAsYHQ/s507/json_2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="348" data-original-width="507" height="220" src="https://1.bp.blogspot.com/-sJ7YFrUNKRw/YZFfYHrjkGI/AAAAAAAA4bY/piT6UddAZII-kzyAUqVlHAe-UMZO8xyLQCNcBGAsYHQ/s320/json_2.png" width="320" /></a></div>Now that we have that working, let's plot it in Chart.js.<br /><p></p><p></p><p>It took me awhile to sort it out, but Chart.js is looking for a line chart object that looks like this:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">{</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">type</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"line"</span><span style="color: #d4d4d4;">,</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">labels</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> [],</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">datasets</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> [{}],</span></div><div><span style="color: #d4d4d4;"> },</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">options</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">scales</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">y</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">beginAtZero</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">true</span><span style="color: #d4d4d4;">,</span></div><div><span style="color: #d4d4d4;"> },</span></div><div><span style="color: #d4d4d4;"> },</span></div><div><span style="color: #d4d4d4;"> },</span></div><div><span style="color: #d4d4d4;">});</span></div></div><p>The
data consists of a label for each data point, an array of data sets.
Each data set consists of data points. We'll only have one data set,
temperature from our single device.</p><p>First, import the necessary JChart scripts in our <span style="font-family: Roboto Mono;">index.html</span>, like so:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: grey;"><</span><span style="color: #569cd6;">head</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> ...</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">script</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">type</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"text/javascript"</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">src</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"</span><span style="color: grey;">></</span><span style="color: #569cd6;">script</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">script</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">src</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"https://d3js.org/d3-color.v1.min.js"</span><span style="color: grey;">></</span><span style="color: #569cd6;">script</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">script</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">src</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"https://d3js.org/d3-interpolate.v1.min.js"</span><span style="color: grey;">></</span><span style="color: #569cd6;">script</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">script</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">src</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"https://d3js.org/d3-scale-chromatic.v1.min.js"</span><span style="color: grey;">></</span><span style="color: #569cd6;">script</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">script</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">src</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"https://cdn.jsdelivr.net/npm/chart.js@3.6.0/dist/chart.min.js"</span><span style="color: grey;">></</span><span style="color: #569cd6;">script</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">script</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">type</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"text/javascript"</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">src</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"static/plot.js"</span><span style="color: grey;">></</span><span style="color: #569cd6;">script</span><span style="color: grey;">></span></div><div><span style="color: #d4d4d4;"> </span><span style="color: grey;"><</span><span style="color: #569cd6;">title</span><span style="color: grey;">></span><span style="color: #d4d4d4;">Temperature Plot</span><span style="color: grey;"></</span><span style="color: #569cd6;">title</span><span style="color: grey;">></span></div><div><span style="color: grey;"></</span><span style="color: #569cd6;">head</span><span style="color: grey;">></span></div></div><p>To begin working on the plotting, we need to add a canvas with id "myChart" to our html body below the H1 tags.<br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: grey;"><</span><span style="color: #569cd6;">canvas</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">id</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"myChart"</span><span style="color: grey;">></</span><span style="color: #569cd6;">canvas</span><span style="color: grey;">></span><br /></div></div><p>In jQuery we grab the 2d context for the canvas like this.<br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> </span><span style="color: #4fc1ff;">ctx</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">$</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"#myChart"</span><span style="color: #d4d4d4;">).</span><span style="color: #dcdcaa;">get</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">).</span><span style="color: #dcdcaa;">getContext</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"2d"</span><span style="color: #d4d4d4;">);</span></div></div></div></div></div><p>Except, if you just put this at the top of the script it won't work because it executes before the document is fully loaded. So we wrap our code in a <span style="font-family: Roboto Mono;">$().ready()</span> handler to ensure it runs after the document has been loaded.<br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #9cdcfe;">$</span><span style="color: #d4d4d4;">().</span><span style="color: #dcdcaa;">ready</span><span style="color: #d4d4d4;">(() </span><span style="color: #569cd6;">=></span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> </span><span style="color: #4fc1ff;">url</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">"http://localhost:5000/temp"</span><span style="color: #d4d4d4;">;</span></div><div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> </span><span style="color: #4fc1ff;">ctx</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">$</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"#myChart"</span><span style="color: #d4d4d4;">).</span><span style="color: #dcdcaa;">get</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">).</span><span style="color: #dcdcaa;">getContext</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"2d"</span><span style="color: #d4d4d4;">);</span></div></div></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> </span><span style="color: #4fc1ff;">chart</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Chart</span><span style="color: #d4d4d4;">(</span><span style="color: #4fc1ff;">ctx</span><span style="color: #d4d4d4;">, {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">type</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"line"</span><span style="color: #d4d4d4;">,</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">labels</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> [],</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">datasets</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> [{}],</span></div><div><span style="color: #d4d4d4;"> },</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">options</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">scales</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">y</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">beginAtZero</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">true</span><span style="color: #d4d4d4;">,</span></div><div><span style="color: #d4d4d4;"> },</span></div><div><span style="color: #d4d4d4;"> },</span></div><div><span style="color: #d4d4d4;"> },</span></div><div><span style="color: #d4d4d4;"> });</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">$</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">getJSON</span><span style="color: #d4d4d4;">(</span><span style="color: #4fc1ff;">url</span><span style="color: #d4d4d4;">).</span><span style="color: #dcdcaa;">done</span><span style="color: #d4d4d4;">((</span><span style="color: #9cdcfe;">response</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">=></span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">console</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">log</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">response</span><span style="color: #d4d4d4;">);</span></div><div><span style="color: #d4d4d4;"> });</span></div><div><span style="color: #d4d4d4;">});</span></div></div><p>With that working, adding <span style="font-family: Roboto Mono;">chart.update()</span> within the <span style="font-family: Roboto Mono;">getJSON</span> response handler makes the chart appear. Next, I populated the Chart object's labels. To keep it simple, I just numbered them 1..n. </p><p>Finally, I added each temperature entry to the Chart dataset. All together, the code looks like this:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">$</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">getJSON</span><span style="color: #d4d4d4;">(</span><span style="color: #4fc1ff;">url</span><span style="color: #d4d4d4;">).</span><span style="color: #dcdcaa;">done</span><span style="color: #d4d4d4;">((</span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">=></span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #4fc1ff;">chart</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">labels</span><span style="color: #d4d4d4;"> = </span><span style="color: #4ec9b0;">Array</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">from</span><span style="color: #d4d4d4;">({ </span><span style="color: #9cdcfe;">length</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">length</span><span style="color: #d4d4d4;"> }, (</span><span style="color: #9cdcfe;">_</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">i</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">=></span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">i</span><span style="color: #d4d4d4;">);</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">var</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">ds</span><span style="color: #d4d4d4;"> = </span><span style="color: #4fc1ff;">chart</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">datasets</span><span style="color: #d4d4d4;">[</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">].</span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">;</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">ds</span><span style="color: #d4d4d4;"> = [];</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">forEach</span><span style="color: #d4d4d4;">((</span><span style="color: #9cdcfe;">entry</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">=></span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">ds</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">push</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">entry</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">tempC</span><span style="color: #d4d4d4;">);</span></div><div><span style="color: #d4d4d4;"> });</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #4fc1ff;">chart</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">update</span><span style="color: #d4d4d4;">();</span></div><div><span style="color: #d4d4d4;"> });</span></div></div><p>Rather than plotting all the data, I decided to only plot the most recent 50 entries. I define a variable N, set it to 50, and then use slice(-N) on the array.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">$</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">getJSON</span><span style="color: #d4d4d4;">(</span><span style="color: #4fc1ff;">url</span><span style="color: #d4d4d4;">).</span><span style="color: #dcdcaa;">done</span><span style="color: #d4d4d4;">((</span><span style="color: #9cdcfe;">r</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">=></span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">var</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">r</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">slice</span><span style="color: #d4d4d4;">(-</span><span style="color: #9cdcfe;">N</span><span style="color: #d4d4d4;">);</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">var</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">dataset</span><span style="color: #d4d4d4;"> = [];</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #4fc1ff;">chart</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">labels</span><span style="color: #d4d4d4;"> = </span><span style="color: #4ec9b0;">Array</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">from</span><span style="color: #d4d4d4;">({ </span><span style="color: #9cdcfe;">length</span><span style="color: #9cdcfe;">:</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">length</span><span style="color: #d4d4d4;"> }, (</span><span style="color: #9cdcfe;">_</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">i</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">=></span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">i</span><span style="color: #d4d4d4;">);</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">forEach</span><span style="color: #d4d4d4;">((</span><span style="color: #9cdcfe;">entry</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">=></span><span style="color: #d4d4d4;"> {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">dataset</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">push</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">entry</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">tempC</span><span style="color: #d4d4d4;">);</span></div><div><span style="color: #d4d4d4;"> });</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #4fc1ff;">chart</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">datasets</span><span style="color: #d4d4d4;">[</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">].</span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">dataset</span><span style="color: #d4d4d4;">;</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #4fc1ff;">chart</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">update</span><span style="color: #d4d4d4;">();</span></div><div><span style="color: #d4d4d4;"> });</span></div></div><p>Et voila, it works!<br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-xJzNvVrBS6g/YZFrRRrNcSI/AAAAAAAA4bg/ZqZrhsN-GwYPZ9Lkc0alZ6ZmnUQyK-dSgCNcBGAsYHQ/s791/plot1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="456" data-original-width="791" height="230" src="https://1.bp.blogspot.com/-xJzNvVrBS6g/YZFrRRrNcSI/AAAAAAAA4bg/ZqZrhsN-GwYPZ9Lkc0alZ6ZmnUQyK-dSgCNcBGAsYHQ/w400-h230/plot1.png" width="400" /></a></div><h1 style="text-align: left;">Done For Now <br /></h1><p>Yay! We've got a sensor that sends data to a backend API, the data is saved, then retrieved and plotted by our front end web application. We've worked with ESP8266 using Arduino, Python with Flask, JSON data formats, jQuery, JavaScript and Chart.js. <br /></p><p>Hopefully you've found this journey helpful if you're new to software engineering or some of these technologies. Or, at least, hopefully it was fun to follow along. Now you kind of get a general idea of how I approach these sorts of projects.<br /></p><h2 style="text-align: left;">So what's next?</h2><p>My plans to refine the project include accommodating multiple sensors, multiple types of measurements (temp, humidity, soil moisture, etc.). I'd like to use an off-the-shelf plotting app like <a href="https://thingsboard.io/" target="_blank">ThingsBoard</a>. The ESP8266 configuration needs some improvements as well.</p><p>Stay tuned for further updates.<br /></p>Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-23394448170752782562021-11-19T19:00:00.084-07:002021-11-21T09:01:26.841-07:00Make an ESP8266 WiFi Temperature Sensor & Python Flask Backend - Part 3<div><p>In the <a href="https://www.bot-thoughts.com/2021/11/make-esp8266-wifi-temperature-sensor.html">previous article</a>, I created a rudimentary Flask backend to receive example data from our ESP8266. Here's a further decomposed system diagram to give you a better idea of where we're going. The orange stars indicate our focus for this article.<br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-P6bk1icARKM/YZAb7pebYzI/AAAAAAAA4a8/9nvpdMQTc_YWa1QANrCLftKthp3m7Ef2wCNcBGAsYHQ/s539/temp_sensor3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="214" data-original-width="539" src="https://1.bp.blogspot.com/-P6bk1icARKM/YZAb7pebYzI/AAAAAAAA4a8/9nvpdMQTc_YWa1QANrCLftKthp3m7Ef2wCNcBGAsYHQ/s16000/temp_sensor3.png" /></a></div><p></p><p></p>In a moment, I'll incorporate the <span style="font-family: Roboto Mono;">HttpPostClient</span>
code into the temperature sensor example, send temperature data instead
of example data, and refactor the backend API to receive temperature
data. The GET API, index.html, and plot.js for plotting the data will be the focus in a future blog post.</div><div><br />So without further ado, I'll combine our example sketches into one...<span><a name='more'></a></span> </div><div><h1 style="text-align: left;">Combining Our Examples</h1><p>To keep the sketch from becoming a mess of code, it's time to make what we've done so far more modular with good <a href="https://courses.cs.washington.edu/courses/cse403/96sp/coupling-cohesion.html" target="_blank">decoupling and cohesion</a>.</p><p>We want our modules to correspond to a single logical entity or function. We want to minimize dependencies between modules.</p><h2 style="text-align: left;">Coupling, Cohesion, Modules<br /></h2><p>Let's list the functionality of our application:</p><ul style="text-align: left;"><li>Setup everything</li><li>Read the temperature sensor</li><li>Connect to wifi</li><li>Post data to an API </li><li>Repeat the above actions (except Setup)</li></ul><p>Starting with the DS18B20 example, I'm going create a new tab for all the DS18B20 stuff, and simplify the main tab and rename it to <span style="font-family: Roboto Mono;">WiFiTempSensor</span> and save the new sketch with that name.</p><p>I'll put in some function calls representing the functionality above. Here's an initial stab at the main tab:</p><p><span style="color: #0b5394;"><span style="font-family: Roboto Mono;">void setup(void) {<br /> // start serial port<br /> Serial.begin(9600);<br /> Serial.println("WiFi Temp Sensor");<br /> setupDS18B20();<br /> setupHttpClient();<br />}<br /><br />void loop(void) {<br /> float tempC = readDS18B20C();<br /> // convert tempC to JSON<br /> postJsonData(json);<br />} </span></span><br /></p>
<p></p>
<p>That code is a lot cleaner than what we had before. Coupling between setup() and the various setup functions is very low because each of the functions "hides" the details of implementation. Same deal with loop().</p><p>Next, I copied over all the old temperature example code into a tab
"DS18B20" and all the previous HttpClientPost code into a tab
"HttpClient". <br /></p><h2 style="text-align: left;">HTTP Client Code <br /></h2><p>In HttpClient, I refactored <span style="font-family: Roboto Mono;">SERVER_IP</span> to <span style="font-family: Roboto Mono;">SERVER_URL</span> and specified the URL, changing the API endpoint from <span style="font-family: Roboto Mono;">/postplain</span> to <span style="font-family: Roboto Mono;">/temp</span>.<br /></p><p>Here's what I came up with:</p><p><span style="color: #0b5394;"><span style="font-family: Roboto Mono;">#include <ESP8266WiFi.h><br />#include <ESP8266HTTPClient.h><br /><br />#define SERVER_URL "http://192.168.1.15:5000/temp"<br /><br />// Assumes Serial has been set up<br />void setupHttpClient() <br />{<br /> WiFi.beginSmartConfig();<br /><br /> while (WiFi.status() != WL_CONNECTED) {<br /> delay(500);<br /> Serial.print(".");<br /> }<br /> Serial.println("");<br /> Serial.print("Connected! IP address: ");<br /> Serial.println(WiFi.localIP());<br />}<br /> <br />void postJsonData(char *data) <br />{<br /> if ((WiFi.status() == WL_CONNECTED)) {<br /><br /> WiFiClient client;<br /> HTTPClient http;<br /><br /> Serial.print("[HTTP] begin...\n");<br /> http.begin(client, SERVER_URL); //HTTP<br /> http.addHeader("Content-Type", "application/json");<br /><br /> Serial.print("[HTTP] POST...\n");<br /><br /> int httpCode = http.POST(data);<br /><br /> if (httpCode > 0) {<br /> <br /> Serial.printf("[HTTP] POST... code: %d\n", httpCode);<br /><br /> if (httpCode == HTTP_CODE_OK) {<br /> const String& payload = http.getString();<br /> Serial.println("received payload:\n<<");<br /> Serial.println(payload);<br /> Serial.println(">>");<br /> }<br /><br /> } else {<br /> Serial.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());<br /> }<br /><br /> http.end();<br /> }<br />}</span></span> <br /></p><p>Notice that the two functions here deal with posting data via HTTP. They're logically related. Hence, this module has decent cohesion. Also, notice that I changed the API endpoint from <span style="font-family: Roboto Mono;">/postplain</span> to <span style="font-family: Roboto Mono;">/temp</span>. We'll have to change the backend in the near future.</p><h2 style="text-align: left;">DS18B20 Code<br /></h2><p>I did the same sort of thing with the DS18B20 code.</p><p><span style="color: #0b5394;"><span style="font-family: Roboto Mono;">#include <OneWire.h><br />#include <DallasTemperature.h><br /><br />#define ONE_WIRE_BUS 2<br /><br />OneWire oneWire(ONE_WIRE_BUS);<br />DallasTemperature sensors(&oneWire);<br /><br />/*<br /> * The setup function. We only start the sensors here<br /> */<br />void setupDS18B20(void)<br />{<br /> sensors.begin();<br />}<br /><br />/*<br /> * Main function, get and show the temperature<br /> */<br />float readCelciusDS18B20()<br />{ <br /> Serial.print("Requesting temperatures...");<br /> sensors.requestTemperatures(); // Send the command to get temperatures<br /> Serial.println("DONE");<br /> float tempC = sensors.getTempCByIndex(0);<br /><br /> if(tempC != DEVICE_DISCONNECTED_C) {<br /> Serial.print("Temperature for the device 1 (index 0) is: ");<br /> Serial.println(tempC);<br /> } else {<br /> Serial.println("Error: Could not read temperature data");<br /> }<br /> return tempC;<br />}</span></span></p><h2 style="text-align: left;">Main Tab</h2><p>Then I went back to the main tab and made some minor modifications to convert from the floating point temperature to json format.<span style="color: #0b5394;"><span style="font-family: Roboto Mono;"></span></span></p><p><span style="color: #0b5394;"><span style="font-family: Roboto Mono;">void setup(void) {<br /> // start serial port<br /> Serial.begin(9600);<br /> Serial.println("WiFi Temp Sensor");<br /> setupDS18B20();<br /> setupHttpClient();<br />}<br /><br />char json[256];<br /><br />void loop(void) {<br /> float tempC = readCelciusDS18B20();<br /> tempCelciusToJson(json, tempC);<br /> postJsonData(json);<br /> delay(10000);<br />}<br /><br />void tempCelciusToJson(char *json, float t) {<br /> if (json)<br /> sprintf(json, "{ \"tempC\": \"%4.2f\" }", t);<br />}</span></span></p><p>The reason I didn't embed the conversion of temperature data into <span style="font-family: Roboto Mono;">postJsonData() </span>has to do with coupling. I wanted to decouple that routine from any knowledge of temperature or the JSON format I am chosing, making it more general purpose. If I can use it without any modifications to send any other kind of sensor data or using any kind of JSON format.<br /></p><p>Now all that's left is to refactor the backend to accept the temperature data.<br /></p><h1 style="text-align: left;">Refactoring the API Backend</h1><p>Last time we created a route handler for the example code. We'll just refactor it by changing the API endpoint to /temp and then adding some code to receive the JSON data and print it out. I ran into some issues and so I went ahead and added try-except blocks and printed out request headers for my own debugging.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #dcdcaa;">@</span><span style="color: #9cdcfe;">app</span><span style="color: #dcdcaa;">.</span><span style="color: #dcdcaa;">route</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"/temp"</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">methods</span><span style="color: #d4d4d4;">=[</span><span style="color: #ce9178;">"POST"</span><span style="color: #d4d4d4;">])</span></div><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">temp_post</span><span style="color: #d4d4d4;">():</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">request</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">headers</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">try</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">request</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">json</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">tc</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">'tempC'</span><span style="color: #d4d4d4;">]</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">tf</span><span style="color: #d4d4d4;"> = </span><span style="color: #4ec9b0;">float</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">tc</span><span style="color: #d4d4d4;">) * </span><span style="color: #b5cea8;">9.0</span><span style="color: #d4d4d4;"> / </span><span style="color: #b5cea8;">5.0</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">32.0</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">tc</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">tf</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"{'status': 'success'}"</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">except</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">Exception</span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">as</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"Exception: </span><span style="color: #569cd6;">{}</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">format</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">))</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"{'status': 'exception'}"</span></div></div><p>After issuing the python ./app.py command, I get the following:</p><p><span style="font-family: Roboto Mono;"><span style="color: #0b5394;">192.168.1.76 - - [13/Nov/2021 12:38:59] "POST /temp HTTP/1.1" 200 -<br />Host: 192.168.1.15:5000<br />User-Agent: ESP8266HTTPClient<br />Accept-Encoding: identity;q=1,chunked;q=0.1,*;q=0<br />Connection: keep-alive<br />Content-Type: application/json<br />Content-Length: 20<br /><br /><br />21.56 70.80799999999999</span></span><br /></p><p>So that's successful! Over in Serial Monitor, I also see signs of success:</p><p><span style="color: #0b5394;"><span style="font-family: Roboto Mono;">Requesting temperatures...DONE <br />Temperature for the device 1 (index 0) is: 21.56 <br />[HTTP] begin...<br />[HTTP] POST...<br />[HTTP] POST... code: 200<br />received payload:<br /><< <br />{'status': 'success'} <br />>> </span></span></p><p>As promised, I combined the two example/prototype sketches into a single sketch that implements the most important functionality: measuring temperature and posting it to a URL. I refactored the backend to accept the data and print it.</p><p>Revisiting the requirements, we're making good progress:</p><ul style="text-align: left;"><li><strike>Periodically measure the temperature</strike></li><li><strike>Sensor probe must be waterproof <br /></strike></li><li>Sensor operates wirelessly (almost; we'll figure out power later)<br /></li><li>Store the data centrally </li><li>Plot the data</li><li><strike>At least +/- 1 Degree F accuracy </strike><br /></li><li>Alert the user if temperature is above the threshold </li></ul></div><a href="https://github.com/shimniok/ESP8266_DS18B20_Example/releases/tag/Step5" target="_blank">Here is all the code so far</a><br /><div><p></p>Next time, I'll implement basic chart plotting and maybe add a few improvements.<br /><p></p></div>Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-81216384372427918902021-11-15T19:00:00.022-07:002021-11-21T08:58:17.748-07:00Make an ESP8266 WiFi Temperature Sensor & Python Flask Backend - Part 2<p></p><p>In the <a href="https://www.bot-thoughts.com/2021/11/example-esp8266-temp-sensor-and-python.html">previous article</a>, I used an example sketch to read the temperature sensor, and another example sketch to connect to WiFi and POST data to an API that I still have to write. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-hyfQypb8Gxc/YY6o_BXgroI/AAAAAAAA4ac/tSeNMHeVDhsSBttItWjhYRNgk9-_TEwkgCNcBGAsYHQ/s539/temp_sensor2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="249" data-original-width="539" src="https://1.bp.blogspot.com/-hyfQypb8Gxc/YY6o_BXgroI/AAAAAAAA4ac/tSeNMHeVDhsSBttItWjhYRNgk9-_TEwkgCNcBGAsYHQ/s16000/temp_sensor2.png" /></a></div><p></p><p>Once I build out this basic example capability, I'll incorporate it into the temperature example and refactor the backend to receive temperature data.</p><p></p><p>As you can probably guess, next is to write a prototype Flask app, then I'll add the code to receive example data from our ESP8266. Please continue reading...</p><span><a name='more'></a></span><p></p><p></p><h1 style="text-align: left;">Flask Backend</h1><p></p><p>Whenever I develop Flask applications I like to follow best practices and compartmentalize the app's dependencies using <a href="https://virtualenv.pypa.io/en/latest/" target="_blank"><span style="font-family: Roboto Mono;">virtualenv</span></a> and <a href="https://direnv.net/" target="_blank"><span style="font-family: Roboto Mono;">direnv</span></a>. The former creates a Python installation specific to your project. The
latter is a convenient way to automatically switch to that installation
when working in the directory.<br /></p><h2 style="text-align: left;">Set Up The Environment<br /></h2><p>I'm
assuming you already created a directory for your project (mine is
<span style="font-family: Roboto Mono;">temp8266</span>). Create a new virtual python environment in the <span style="font-family: Roboto Mono;">env</span> subdirectory via <span style="font-family: Roboto Mono;">direnv -p python3 env</span></p><p>You can now install python dependencies with <span style="font-family: Roboto Mono;">pip</span> and capture those dependencies into a <span style="font-family: Roboto Mono;">requirements.txt</span> file using <span style="font-family: Roboto Mono;">pip --freeze</span>. Since I already did all that for you, just create a <span style="font-family: Roboto Mono;">requirements.txt</span> file containing:</p><p><span style="color: #3d85c6;"><span style="font-family: Roboto Mono;">click==8.0.3<br />dataclasses==0.8<br />Flask==2.0.2<br />importlib-metadata==4.8.1<br />itsdangerous==2.0.1<br />Jinja2==3.0.2<br />MarkupSafe==2.0.1<br />typing-extensions==3.10.0.2<br />Werkzeug==2.0.2<br />zipp==3.6.0</span></span><br /></p><p>And then run the command <span style="font-family: Roboto Mono;">pip install -r requirements.txt</span> which will install the packages specified in that text file.<br /></p><p>Now create a <span style="font-family: Roboto Mono;">.envrc</span> file containing the following lines:</p><p><span style="color: #3d85c6;"><span style="font-family: Roboto Mono;">source env/bin/activate<br />export FLASK_APP="app"<br />export FLASK_ENV="development"<br />export FLASK_DEBUG=1<br />export SERVER_NAME="0.0.0.0:5000"</span></span><br /></p><p>The
first line sources a virtualenv shell script that switches environment variables
around to use the virtual python environment. The remaining lines will
be used later to run our Flask app.</p><p>Once the <span style="font-family: Roboto Mono;">.envrc</span> is created, type <span style="font-family: Roboto Mono;">direnv</span> allow to confirm that the changes made to <span style="font-family: Roboto Mono;">.envrc </span>were made by you. Anytime you <span style="font-family: Roboto Mono;">cd</span> into the directory, direnv will run the .envrc script and set up your virtual python environment. When you <span style="font-family: Roboto Mono;">cd</span> out of the directory, it will restore your previous environment.<br /></p><p>Now that the environment is set up, it's time to create our simple Flask app. <br /></p><h2 style="text-align: left;">Simple Flask App</h2><p>All we need to display a simple message is a file, <span style="font-family: Roboto Mono;">app.py</span>, containing:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">import</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">os</span></div><div><span style="color: #c586c0;">from</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">flask</span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">import</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">Flask</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">request</span></div><br /><div><span style="color: #9cdcfe;">app</span><span style="color: #d4d4d4;"> = </span><span style="color: #4ec9b0;">Flask</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">__name__</span><span style="color: #d4d4d4;">)</span></div><br /><div><span style="color: #dcdcaa;">@</span><span style="color: #9cdcfe;">app</span><span style="color: #dcdcaa;">.</span><span style="color: #dcdcaa;">route</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"/"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">index</span><span style="color: #d4d4d4;">():</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Hello world"</span></div><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">__name__</span><span style="color: #d4d4d4;"> == </span><span style="color: #ce9178;">'__main__'</span><span style="color: #d4d4d4;">:</span><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">Starting..."</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">app</span><span style="color: #d4d4d4;">.</span><span style="color: #dcdcaa;">run</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">host</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">'0.0.0.0'</span><span style="color: #d4d4d4;">)</span></div></div><p></p><p>You program a Flask app by defining functions that handle requests for different URLs, called routes. The root URL is handled by index(). It simply responds with "Hello World".</p><p>To start the webserver, type <span style="font-family: Roboto Mono;">python app.py </span>at the shell prompt and you should see:</p><p><span style="color: #3d85c6;"><span style="font-family: Roboto Mono;"> * Serving Flask app 'app' (lazy loading)<br /> * Environment: development<br /> * Debug mode: on<br /> * Running on http://192.168.1.15:5000/ (Press CTRL+C to quit)<br /> * Restarting with stat<br /> * Debugger is active!<br /> * Debugger PIN: 995-911-114</span></span><br /><br />Now point your browser to <span style="color: #3d85c6;"><span style="font-family: Roboto Mono;">http://127.0.0.1:5000/</span></span> (or<span style="color: #3d85c6;"><span style="font-family: Roboto Mono;"> </span></span><br /><span style="color: #3d85c6;"><span style="font-family: Roboto Mono;"><span style="color: #3d85c6;"><span style="font-family: Roboto Mono;">http://localhost:5000/</span></span></span></span>)<span style="color: #3d85c6;"><span style="font-family: Roboto Mono;"><span style="color: #3d85c6;"></span></span></span> and you should see "Hello World" displayed. Flask will output the following (ignore the favicon.ico request):</p><p><span style="color: #3d85c6;"><span style="font-family: Roboto Mono;">127.0.0.1 - - [12/Nov/2021 10:58:57] "GET / HTTP/1.1" 200 -<br />127.0.0.1 - - [12/Nov/2021 10:58:57] "GET /favicon.ico HTTP/1.1" 404 -<br /></span></span></p><p><a href="https://github.com/shimniok/ESP8266_DS18B20_Example/releases/tag/Backend1" target="_blank">Here's my source code so far</a></p><p>Stop the flask process (CTRL/C). Now it's time to add an API endpoint.</p><h2 style="text-align: left;">Add an API Endpoint</h2><p>Before we add another endpoint, we need to know what data our Huzzah is posting, exactly.<br /></p><p>Let's say your SERVER_IP is set to "192.168.0.1". The example sketch we installed will post the following JSON to <span style="font-family: Roboto Mono;">http://192.168.0.1/postplain:</span></p><p><span style="font-family: Roboto Mono;"><span style="color: #0b5394;"><span style="font-family: Roboto Mono;">{ "hello": "world" } </span></span> <br /></span></p><p>The Flask route we need to handle is <span style="font-family: Roboto Mono;">/postplain</span></p><p>So let's add a route to receive the data and print it out. Add this route function after index():</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #dcdcaa;">@</span><span style="color: #9cdcfe;">app</span><span style="color: #dcdcaa;">.</span><span style="color: #dcdcaa;">route</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'/postplain'</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">methods</span><span style="color: #d4d4d4;">=[</span><span style="color: #ce9178;">"POST"</span><span style="color: #d4d4d4;">])</span></div><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">postplain</span><span style="color: #d4d4d4;">():</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">request</span><span style="color: #d4d4d4;">.</span><span style="color: #9cdcfe;">json</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">data</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Success"</span></div></div></div><p>We'll need to import request from flask.<br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: "Droid Sans Mono", "monospace", monospace, "Droid Sans Fallback"; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">from</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">flask</span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">import</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">Flask</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">request</span></div></div><p>Type <span style="font-family: Roboto Mono;">python app.py</span><br /><br /> at the prompt and your app will run. Make sure your ESP8266 is still connected to your PC and running and open the Serial Monitor. You may be disappointed to see 404 errors still showing up. What gives?<br /><br />Well, our Flask app is listening to connections on port 5000/tcp, but our ESP8266 <span style="font-family: Roboto Mono;">HttpPostClient</span> example didn't specify a port, so it is still trying to talk to port 80.</p><h2 style="text-align: left;">Pointing ESP8266 to Port 5000</h2>We'll edit the <span style="font-family: Roboto Mono;"> HttpPostClient</span> code to communicate with our host on port 5000. Change this line:<br /><p><span style="font-family: Roboto Mono;">#define SERVER_IP "192.168.1.15"</span><br /><br />to this:<br /><br /><span style="font-family: Roboto Mono;">#define SERVER_IP "192.168.1.15:5000"</span><br /><br />Upload your sketch and restart, open your Serial Monitor. Use EspTouch to configure the ESP8266 again, and ...wait, what?!</p><p><span style="color: #3d85c6;"><span style="font-family: Roboto Mono;">..................................................................... <br />Connected! IP address: 192.168.1.76 <br />[HTTP] begin...<br />[HTTP] POST...<br />[HTTP] POST... code: 404</span></span><br /></p><p>Still getting a 404? Over in the Flask app we see:</p><p><span style="color: #3d85c6;"><span style="font-family: Roboto Mono;">192.168.1.76 - - [12/Nov/2021 11:19:24] "POST /postplain/ HTTP/1.1" 404 -</span></span><br /></p><p>Interesting. Why is there a trailing slash (/) in the URL? Let's take a look at the ESP8266 code again. </p><p>There it is:<br /></p><p><span style="color: #3d85c6;"><span style="font-family: Roboto Mono;">http.begin(client, "http://" SERVER_IP "/postplain<span style="color: red;">/</span>"); //HTTP</span></span></p><p>Remove the trailing slash (red), upload the sketch, restart, reconfigure with EspTouch and then... et voila! Serial Monitor shows:<br /></p><p style="text-align: left;"><span style="font-family: Roboto Mono;"><span style="color: #3d85c6;">[HTTP] POST... code: 200<br />received payload:<br /><< <br />Success <br />>> </span></span><br /></p><p>Flask app shows:</p><p><span style="color: #3d85c6;"><span style="font-family: Roboto Mono;">192.168.1.76 - - [12/Nov/2021 11:26:22] "POST /postplain HTTP/1.1" 200 -<br />{'hello': 'world'}</span></span><br /></p><p>It works! Our ESP8266 is connecting to our newly written Flask backend app and sending the example JSON data.<br /></p><p><a href="https://github.com/shimniok/ESP8266_DS18B20_Example/releases/tag/Backend2" target="_blank">This is all the code so far</a></p><p>Hopefully you can see that this iterative, baby-steps, bite-at-a-time approach works really well. When something goes wrong it is easy to isolate the cause. Also, the changes I commit to the repo are relatively small and easy to review, or easy to undo.<br /></p><p>Next time I'll incorporate the <span style="font-family: Roboto Mono;">HttpPostClient</span> example into the temperature example and refactor the backend API to receive temperature data.<br /></p>Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-23908974258939677512021-11-11T19:00:00.013-07:002021-11-21T08:59:14.859-07:00Make an ESP8266 WiFi Temperature Sensor & Python Flask Backend - Part 1<p>Ok, yeah, it's true, I'm a "few" years late to the ESP IoT game. But now that I've finally gotten around to it, I thought it would be fun to start with an Internet of Things (IoT) temperature monitor as I have several use cases in mind:</p><ul style="text-align: left;"><li>Chest freezer monitor and alarm,<br /></li><li>Measure temperature differentials between floors in my house,</li><li>Fish tank temperature monitoring and alerting,</li><li>Weather monitoring for vegetable garden</li><li>Template platform for deploying other kinds of sensors<br /></li></ul><p>For now, I'm going to prototype a temperature sensor that posts data to an API. I'll showcase several technologies and walk you through the process I use to develop projects like this. </p><p>Here are the requirements I put together for the freezer monitor...</p><span><a name='more'></a></span><h1 style="text-align: left;">Requirements</h1><ul style="text-align: left;"><li>Periodically measure the temperature</li><li>Sensor probe must be waterproof <br /></li><li>Sensor operates wirelessly<br /></li><li>Store the data centrally </li><li>Plot the data</li><li>At least +/- 1 Degree F accuracy <br /></li><li>Alert the user if temperature is above the threshold</li></ul><p>I felt it would be wise to create a simple remote temperature monitoring prototype that meets some of these requirements. I can always add features and make it ready for every at some later date.<br /></p><p>I selected the following architecture to support rapid prototyping, provide reasonable accuracy<br /></p><h1 style="text-align: left;">Architecture</h1><div>The main components include:</div><ul style="text-align: left;"><li>WiFi-enabled IoT device with temperature sensor</li><li>Backend website</li><li>APIs running on the website to POST and GET temperature data<br /></li><li>A JavaScript client application that plots data </li></ul><p>Technologies include: <br /></p><ul style="text-align: left;"><li><a href="https://www.adafruit.com/product/2471" target="_blank">Adafruit Huzzah ESP8266</a> </li><li><a href="http://arduino.cc" target="_blank">Arduino</a> IDE and toolchain<br /></li><li>DS18B20 temperature sensor in a water-proof housing</li><li>Huzzah posts measurements to an HTTP REST/JSON API</li><li>The API will be implemented with Python <a href="https://palletsprojects.com/p/flask/">Flask</a> on Linux Mint</li><li>Client-side JavaScript will plot data client-side with <a href="https://www.chartjs.org/" target="_blank">Chart.js</a> </li><li>The client-side JavaScript will request data via a REST/JSON API call<br /></li></ul><p>Here's a diagram showing the components and data flows. <br /></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-MOCBUfTPZ6Y/YY6ju9pyDBI/AAAAAAAA4aM/sq_c_Nf25C0pFH2yqhk43UfEHiMho_NswCNcBGAsYHQ/s539/temp_sensor1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="206" data-original-width="539" src="https://1.bp.blogspot.com/-MOCBUfTPZ6Y/YY6ju9pyDBI/AAAAAAAA4aM/sq_c_Nf25C0pFH2yqhk43UfEHiMho_NswCNcBGAsYHQ/s16000/temp_sensor1.png" /></a></div>The Huzzah is a breadboard-friendly ESP8266 implementation that's affordable and convenient to use.<br /><div><p>For the backend API, I chose Flask, "a lightweight <a href="https://wsgi.readthedocs.io/">WSGI</a> web application framework." It will be deployed to my Linux Mint system. Since Mint and Raspbian share a common ancestor in Debian this project shouldn't be hard to migrate to a Raspberry Pi.<br /></p>Chart.js is a simple but flexible charting library.<br /><p>I'll step you through the "iterative" process I usually followed in developing everything.<br /></p><div><h1 style="text-align: left;">Writing Sensor Firmware</h1><div style="text-align: left;">We'll start with the code to measure the temperature using the DS18B20. This is a Dallas Semiconductor sensor that uses <a href="https://www.maximintegrated.com/en/design/technical-documents/tutorials/1/1796.html" target="_blank">1-Wire Protocol</a>. Such a sensor uses a single line for data plus a ground wire and an optional power wire.<br /></div><h2 style="text-align: left;">DS18B20</h2>The easiest approach is to start with the example sketch for the temperature sensor. You'll need to grab two libraries using Arduino Library Manager: OneWire and DallasTemperature. Next, open <span style="font-family: Roboto Mono;">File > Examples > DallasTemperature > Simple</span> example sketch. Connector your sensor, compile and upload the sketch, open Serial Monitor, and make sure the sketch is working. </div><div> </div><div>Save it, as we are going to add code to it later to meet our requirements.</div><div></div><div> </div><div><a href="https://github.com/shimniok/ESP8266_DS18B20_Example/releases/tag/DS18B20" target="_blank">My source code for this step</a>.<br /><h2 style="text-align: left;">HTTP Client & ESP-Touch<br /></h2><p>Next we need to know how to get the ESP8266 to connect to a URL. For this I opened a new example sketch: <span style="font-family: Roboto Mono;">File > Examples > ESP8266HTTPClient > PostHttpClient</span>.</p><p>You may notice <span style="font-family: Roboto Mono;">STASSID</span> and <span style="font-family: Roboto Mono;">STAPSK</span> are pre-processor macros in the source. They're intended to contain your WiFi SSID and PSK (password). You really, really do NOT want to hard-code this sensitive data into your source code, especially if you check it into a source code repository.<br /></p><p>Fortunately, you don't have to. Espressif provides <a href="https://www.espressif.com/en/products/software/esp-touch/overview" target="_blank">ESP-Touch Protocol</a> with an app for Android and iOS that will conveniently
provide your newly-flashed ESP devices with the SSID and PSK for your WiFi AP.</p><p>To make use of this capability, install the EspTouch app, open it, and fill in your SSID and Password (PSK) information. </p><p>Then, in your Arduino source, replace the line: </p><p><span style="font-family: Roboto Mono;">WiFi.begin(STASSID, STAPSK); </span></p><p>with:</p><p><span style="font-family: Roboto Mono;">WiFi.beginSmartConfig();</span> </p><p>and delete the following lines:</p><p><span style="font-family: Roboto Mono;">#ifndef STASSID<br />#define STASSID "ssid"<br />#define STAPSK "password"<br />#endif </span></p>Finally, change the <span style="font-family: Roboto Mono;">SERVER_IP</span> macro to your backend web server's IP. (I don't love the idea of hardcoding the IP address, but we can fix that later).</div><div> </div><div>For now we just need to make sure the ESP8266 connects to our WiFi Access Point (AP). Upload the sketch and open Serial Monitor. The ESP should be printing out "." repeatedly. Now, open your EspTouch app and fill in the SSID and Password (PSK). Press Confirm to send the WiFi config to the ESP8266. The app will tell you when it succeeds and the ESP will indicate it's connected to your AP.<br /><p>You should see something like this in Serial Monitor:</p><p><span style="color: #351c75;"><span style="font-family: Roboto Mono;">....................................................... <br />Connected! IP address: 192.168.1.76 <br />[HTTP] begin...<br />[HTTP] POST...<br />[HTTP] POST... code: 404<br /></span></span><br /></p><p></p><p>So the WiFi connection works, but of course the POST fails because my Linux host, which happens to have a web server running, hasn't implemented <span style="font-family: Roboto Mono;">/postplain</span>, hence the 404 error. If your host didn't have a web server, you'd get a different error (usually "connection refused" or "timeout") </p><p><a href="https://github.com/shimniok/ESP8266_DS18B20_Example/releases/tag/InitialPostClient" target="_blank">My source code for this step</a><br /></p><p>In the next article we'll work on the Python Flask backend.<br /></p></div></div>Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-26732129184256903352021-10-28T18:00:00.003-06:002021-11-13T11:59:08.588-07:00Tips for Beginners: How to Eat an Elephant<p>That brand new Arduino of yours isn't going to code itself, is it? If you're a beginner at programming, or even just at Arduino, here's a tip that I know will help you out.</p><h2 style="text-align: left;">How to Eat an Elephant <br /></h2><p>If you want to make some cool project, take it a small step at a time. In my robotics classes kids that followed this approach finished faster than the ones who tried to program everything at once. </p><h2 style="text-align: left;">Example <br /></h2><p>Let's say you have a project where you want to use a temperature sensor, an Arduino, and an OLED display to show the temperature. When you press a button, the display changes between Celsius and Fahrenheit.<br /></p><p>Don't try to code all that in one shot. Instead, add tiny bits of functionality incrementally and get each new thing working before adding the next. Here is how I would approach such a project.<br /></p><h4 style="text-align: left;">Step 1<br /></h4><p></p><p>First, make sure you can blink an LED. Why? You're making sure your new board works properly and that you can flash a new program to it. In addition, the LED can be a debug signal for you in the next step.</p><h4 style="text-align: left;">Step 2<br /></h4><p>Next, read the state of a button. Wire up the button to one of your digital inputs with an external pull-up resistor or use INPUT_PULLUP. With a pull-up resistor, the button is pressed when the digital signal is low, and released when the signal is high. Change your blink code so that when the button is pressed, you light up the LED. When it isn't pressed, turn off the LED.</p><p>You already know the LED works so if there's a problem, you have fewer things to troubleshoot.</p><p>And that's why we started with blinking the LED.</p><h4 style="text-align: left;">Step 3</h4><p>We'll work on the OLED display later. We want to work toward displaying the sensor reading so let's use Serial for now. Print a generic "hello world" every second using <span style="font-family: courier;"><span><span style="font-size: small;">Serial.println()</span></span>.</span><br /></p><p>In the next steps we can build on that to print out our sensor reading over Serial. Make sure you can see the message in the Arduino IDE Serial Monitor. Once this is working we can work on the sensor</p><h4 style="text-align: left;">Step 4<br /></h4><p>Our next step is to read the value from the sensor with <span style="font-family: courier;">analogRead()</span> and print the result using <span style="font-family: courier;">Serial.println()</span> and see if the value makes sense. Is it bouncing all over the place or always stuck at minimum or maximum? If so, troubleshoot. If it seems to hold more or less steady and changes according to temperature (try touching the sensor to raise the temperature), you're probably good to go.<br /></p><h4 style="text-align: left;">Step 5 <br /></h4><p></p><p>Figure out how to convert the value you read previously into Celsius and print that with Serial. You'll need to refer to a datasheet or tutorial for that particular sensor type. </p><h4 style="text-align: left;">Step 6</h4><p>Repeat step 5 but with Celsius.<br /></p><h4 style="text-align: left;">Step 7</h4><p>Work on displaying a generic "hello world" on your OLED display. Then try displaying a generic value. It doesn't have to look pretty just yet.<br /></p><h4 style="text-align: left;">Step 8 <br /></h4><p>Once you have that figured out, display the temperature in C. </p><h4 style="text-align: left;">Step 9 <br /></h4><p>Then figure out how to switch between C and F when the button is pressed. </p><h4 style="text-align: left;">Step 10</h4><p>Make the OLED display look prettier. Again break it down. Maybe learn how to display large numbers. Then work on displaying a thermometer graphic. Then work on displaying a bar graph within the thermometer. And so on.</p>So that's how you eat an elephant. The little bites make sure you don't have too many errors and bugs to deal with at once. It seems like every time I try to do too much at once, I'm the one getting bitten--in the butt.<br /><p></p>Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-32007401988266929862020-12-16T08:00:00.066-07:002020-12-16T08:00:00.251-07:00Still alive, what I've been doing, plus what's next: Zumo Sumo<div>Well, 2020 has been a raging dumpster fire, eh? Hope you're doing ok and staying safe and healthy. I'd love to hear from you, so hit me up on twitter (@bot_thoughts) or comment.</div><div><br /></div><div>It's been ... awhile... since I last posted. Life intervened and I needed a break from robotics blogging. Is anyone still doing blogs these days? I should probably be on TikTok... (yeah like that'll happen) </div><div><div class="separator" style="clear: both; text-align: center;"><br /></div>Some of what I've been up to since last time:</div><div><ul style="text-align: left;"><li>Teaching Lego robot sumo at my daughter's school</li><li>Home automation</li><li>Collecting and repairing fountain pens</li><li>Painted my first paintings (acrylic on canvas)</li><li>Retired my rusty, formerly robotic Jeep Grand Wagoneer & got a 4Runner</li><li>Wheeling in Moab, Ouray, and trails in Colorado</li><li>Making a few things with my Taig MicroLathe II</li><li>Printing with my new Creality Ender 3</li></ul><div>Next up... I'm excited to teach my kid and a couple of her friends Arduino programming using Pololu Zumo robots with epic sumo battles to follow!</div></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-jGTfCV-6mZ4/X9kD1J2nAhI/AAAAAAAAyE4/mZgkAjZZW0Usk0m7iexUU1eZdcEreZjoACPcBGAsYHg/s3021/PXL_20201215_184020478%257E2.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2266" data-original-width="3021" height="300" src="https://1.bp.blogspot.com/-jGTfCV-6mZ4/X9kD1J2nAhI/AAAAAAAAyE4/mZgkAjZZW0Usk0m7iexUU1eZdcEreZjoACPcBGAsYHg/w400-h300/PXL_20201215_184020478%257E2.jpg" width="400" /></a></div><br /><div><br /></div>Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-86912262018800948702018-01-10T17:24:00.002-07:002018-01-10T17:28:51.474-07:00Decoding GM's ALDL with Teensy 3.6<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-Alj202n3lQs/WHBED0mLFQI/AAAAAAAAW6o/wWkYEwcoGmY8aKNFO-UTva-1-x1WNVtawCLcB/s1600/aldl_teensy2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://4.bp.blogspot.com/-Alj202n3lQs/WHBED0mLFQI/AAAAAAAAW6o/wWkYEwcoGmY8aKNFO-UTva-1-x1WNVtawCLcB/s400/aldl_teensy2.jpg" width="400" /></a></div>
<br />
I want to log my <a href="http://www.bot-thoughts.com/2014/06/avc-sharc-fsv.html">Jeep's</a> Engine Control Module (ECM) diagnostic data reliably and inexpensively any time the vehicle is running so I can tune it to pass emissions tests and reduce pollution.<br />
<br />
To do that, I am using a new <a href="https://www.pjrc.com/store/teensy36.html">Teensy 3.6</a> microcontroller to first decode the data stream out of the ECM and then store it on a microSD card for later retrieval.<br />
<br />
My Jeep is running a retrofitted General Motors #1227747 ECM, which is the brains of a mid-80's Throttle Body Injection (TBI) system found on Chevy and GMC trucks.<br />
<br />
The ECM, which predates OBDII systems, spits out a serial data stream called <i>Assembly Line Diagnostic Link</i> (ALDL) at 160 <a href="https://en.wikipedia.org/wiki/Baud">baud</a>. This data stream protocol is <i>nothing</i> like plain old <a href="https://en.wikipedia.org/wiki/RS-232">RS-232</a> serial protocol, however...<br />
<a name='more'></a><br />
<h2>
What is the RS-232 Protocol?</h2>
Each byte of data transmitted in RS-232 protocol is preceded by one start bit. Typically each byte is terminated by one stop bit, with no parity bit (8N1), though other combinations are possible.<br />
<br />
All bit transmissions take the same time, based on the configured baud rate, and their values are indicated by voltage levels, often 12V and -12V for 0 and 1, respectively.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-X_3kFrjKxCU/WHAvrSMl6tI/AAAAAAAAW50/36BvTyYA56wUEIV2CRJ1OasPFRIJdyU6gCEw/s1600/Screenshot%2Bfrom%2B2017-01-06%2B17%253A00%253A31.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="268" src="https://3.bp.blogspot.com/-X_3kFrjKxCU/WHAvrSMl6tI/AAAAAAAAW50/36BvTyYA56wUEIV2CRJ1OasPFRIJdyU6gCEw/s400/Screenshot%2Bfrom%2B2017-01-06%2B17%253A00%253A31.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">By Ktnbn, derivative work: Samuel Tardieu (talk),<br />
<a href="https://commons.wikimedia.org/w/index.php?curid=6101703">Rs232_oscilloscope_trace.jpg</a>, <br />
CC SA 1.0</td></tr>
</tbody></table>
TTL serial protocol, used by microcontrollers, differs only in voltage levels, using 0V to represent a 0 and Vcc (5V, 3.3V) for a 1. The ALDL protocol also uses TTL voltages but the similarities end there.<br />
<h2>
What is the ALDL Protocol?</h2>
At 160 baud, each bit is transmitted in 6250 microseconds (<span style="background-color: white; color: #222222;"><span style="font-family: inherit;">μ</span></span>sec) but instead of indicating value with voltage level, ALDL indicates it with the length of a logic-low pulse (sort of like <a href="https://en.wikipedia.org/wiki/1-Wire">1-Wire</a>).<br />
<br />
A short duration pulse represents logic 0 while a long duration pulse represents logic 1. All bits start with the falling edge of this low pulse.<br />
<br />
My ECM represents a 0 with 368 <span style="background-color: white; color: #222222;"><span style="font-family: inherit;">μ</span></span>sec pulse and 1 with a 4400 <span style="background-color: white; color: #222222;"><span style="font-family: inherit;">μ</span></span>sec pulse, but timing differs between ECM part numbers. And some error between ECMs can be expected. I captured timing samples with a <a href="https://www.saleae.com/">Saleae</a> Logic Pro 8, below.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-MGE-smu-nCk/WHBB7_7GMII/AAAAAAAAW6Y/mj9gDcFmiEs5VArv69Nqqcc8bwj5K1JugCLcB/s1600/aldl1a.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="127" src="https://3.bp.blogspot.com/-MGE-smu-nCk/WHBB7_7GMII/AAAAAAAAW6Y/mj9gDcFmiEs5VArv69Nqqcc8bwj5K1JugCLcB/s400/aldl1a.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">"0" bit timing</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://1.bp.blogspot.com/-cdhjKx8SdcA/WHBB7xHAq5I/AAAAAAAAW6U/d0GKnTB1pcM-EmhMUJkd3ekHDCeULCzhwCLcB/s1600/aldl2a.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="127" src="https://1.bp.blogspot.com/-cdhjKx8SdcA/WHBB7xHAq5I/AAAAAAAAW6U/d0GKnTB1pcM-EmhMUJkd3ekHDCeULCzhwCLcB/s400/aldl2a.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">"1" bit timing</td></tr>
</tbody></table>
<h2>
How To Decode ALDL Bits</h2>
I tasked one of my Teensy 3.6 Kickstarter rewards with decoding the protocol. It has a microSD card, making data logging a breeze.<br />
<br />
<a href="http://www.techedge.com.au/vehicle/aldl160/160serial.htm">One way to decode</a> is to use a standard UART set to 2400 or 1600 baud, treating each ALDL bit as a UART byte. Just count the logic 1 bits in the received character.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://4.bp.blogspot.com/-FjYiQwGRCvQ/WlZLMxG4aHI/AAAAAAAAcwY/oOeJtXlPK5wxrdT8QFsZjN0CZNghiWceQCLcBGAs/s1600/aldl_async.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="146" data-original-width="573" height="101" src="https://4.bp.blogspot.com/-FjYiQwGRCvQ/WlZLMxG4aHI/AAAAAAAAcwY/oOeJtXlPK5wxrdT8QFsZjN0CZNghiWceQCLcBGAs/s400/aldl_async.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">ALDL 160 baud and async serial 1600 baud</td></tr>
</tbody></table>
<br />
For example, using 1600 baud, an ALDL 0 bit is received as a 11111111 byte and a 1 bit is received as 00000111, depending on how the timing lines up.<br />
<br />
In fact, the timing <i>doesn't</i> quite line up, though it can be made to work, as evidenced by WinALDL and other software.<br />
<br />
Instead, the Teensy measures the timing directly in software with a combination of timers and interrupts doing the bulk of the work. It was easy to code and performance isn't an issue, so why not?<br />
<br />
To detect the signal's falling edge (the start of a bit) I use attachInterrupt() to run an interrupt handler function.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">attachInterrupt(digitalPinToInterrupt(AldlPin), handlePinChange, FALLING);</span><br />
<div>
<br />
In the handler, I start a timer that calls a function, <i>doSample()</i>, 2000 <span style="background-color: white; color: #222222;"><span style="font-family: inherit;">μ</span></span>sec later to read the signal and determine if the bit is a 1 or 0.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">timer.begin(doSample, 2000);</span><br />
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-OBQV9J5whYU/WlZAtO2eEpI/AAAAAAAAcwI/Q05tfhkHoRQ5erlqQbczrzJVbCjmjGUNACLcBGAs/s1600/aldl.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="216" data-original-width="572" height="150" src="https://2.bp.blogspot.com/-OBQV9J5whYU/WlZAtO2eEpI/AAAAAAAAcwI/Q05tfhkHoRQ5erlqQbczrzJVbCjmjGUNACLcBGAs/s400/aldl.png" width="400" /></a></div>
<br />
Why 2000 <span style="background-color: white; color: #222222;"><span style="font-family: inherit;">μ</span></span>sec? To account for bit timing differences between ECMs.<br />
<br /></div>
In <i>doSample()</i>, I shift the current byte to make room for the new bit, and then set the new least significant bit to 1 <i>only</i> if the signal is still low (representing ALDL 1). Otherwise it stays 0.<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">uint16_t myByte = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">void doSample() {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> timer.end();</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> myByte <<= 1;</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> if (digitalRead(AldlPin) == LOW) {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> // Bit is a 1</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> digitalWrite(LoPin, HIGH);</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> myByte |= 1;</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> } else {</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> // Bit is a 0</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> digitalWrite(LoPin, LOW);</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> myByte &= 0x1ff;</span><br />
<br />
But wait. In that last line of code, why am I keeping 9 bits of data? And why is <i>myByte</i> actually a 16-bit word?<br />
<h2>
Synchronizing ALDL</h2>
The ALDL data stream is transmitted as an unending series of bits, organized into 20-byte frames, so how do you know which bit is the first in a byte? Or which byte is first in a frame?<br />
<br />
How do you <i>guarantee </i>synchronization with the ALDL data stream? By sending a sync character.<br />
<br />
To distinguish the sync character from any other byte, which could have any value from 0x00 to 0xff, GM uses 9-bit bytes and <span style="font-family: "courier new" , "courier" , monospace;">0x1ff</span> as the sync character, the only byte with the first bit set to 1.<br />
<br />
As an added bonus, the sync character is the only time the ECM sends nine 1 bits in a row, so your receiver can reliably re-synchronize just by counting strings of "1" bits.<br />
<br />
In the <i>doSample()</i> handler, after shifting a bit into <i>myByte</i>, additional code checks to see if it has received the sync character and, if so, resets the bit counter to 0 and begins counting out 9 bits at a time to form the bytes. The code doesn't need to count the bytes in a frame. It's not necessary.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Sync to the sync character</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // GM 160 ALDL sends a sync character starting with</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // a "1" bit (all other bytes start with a "0" bit)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // followed by 0xff.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (myByte == 0x1ff) {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> digitalWrite(HiPin, HIGH);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.println("***** SYNC *****");</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> bitCount = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> myByte = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> } else if (++bitCount >= 9) { // Received next 9 bits?</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Mask off the low byte and print</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> digitalWrite(HiPin, LOW);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.println(int(myByte & 0x0ff));</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> bitCount = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span><br />
<br />
This code is transmitting the decoded bytes over its serial peripheral for testing. Speaking of which...<br />
<h2>
Testing</h2>
To find inevitable bugs, I needed a test ECM to generate ALDL data.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-vVreS5rtS5k/WHBEOvmXxoI/AAAAAAAAW6s/JqYyPhhSdC4MH_BTLLiPhpPj0U73E0EIgCLcB/s1600/aldl_ecmsolder.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="225" src="https://2.bp.blogspot.com/-vVreS5rtS5k/WHBEOvmXxoI/AAAAAAAAW6s/JqYyPhhSdC4MH_BTLLiPhpPj0U73E0EIgCLcB/s400/aldl_ecmsolder.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">ECM test points: check engine light, ALDL out, 12V, GND.</td></tr>
</tbody></table>
I have a couple of spare ECMs and powered it with a spare PC power supply that I converted into a bench supply for this project. I soldered wires onto the bottom of the ECM board to tap into the pins for power, check engine, and ALDL.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-6Gb_ko3HG_g/WHBEoxp0Y3I/AAAAAAAAW6w/iDMf24T4HXUl1bHogHXI6LT8sSibIaTJQCLcB/s1600/aldl_pcsupply.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="225" src="https://2.bp.blogspot.com/-6Gb_ko3HG_g/WHBEoxp0Y3I/AAAAAAAAW6w/iDMf24T4HXUl1bHogHXI6LT8sSibIaTJQCLcB/s400/aldl_pcsupply.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The project was good motivation to convert my spare PC power supply</td></tr>
</tbody></table>
With an ECM just sitting there powered by 12V with no sensors hooked up, it spits out a predicable, always-repeating frame of bytes that is perfect for testing.<br />
<br />
The Teensy code prints out each byte decoded from the ALDL stream to my workstation. I compare the Teensy stream against two things:<br />
<ul>
<li>Manual decoding</li>
<li>TunerPro decoding</li>
</ul>
Using my Saleae Logic Pro 8, I captured the datastream coming out of a test ECM on the workbench and then manually decoded the 1's and 0's after finding a sync character. Turning the stream of bits into bytes, verified that both streams were identical.<br />
<br />
And, just to be sure, I also used TunerPro (which I use for tuning and ad-hoc data logging) to print the stream for comparison.<br />
<br />
After fixing a couple issues, everything checked out. Yay! Now I can do some tuning with the Wideband O<sub>2</sub> sensor.Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-24164338233925612962017-07-10T19:00:00.000-06:002017-07-12T09:18:00.094-06:00Diagnosing Watches With Electronics<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-SGREzzjF-Jw/WV_53rt2mUI/AAAAAAAAYss/siHlMVPn4ZMkCpBmXHQ00u4RVdnptZ23QCKgBGAs/s1600/IMG_20170630_141154.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="1600" data-original-width="1243" height="200" src="https://1.bp.blogspot.com/-SGREzzjF-Jw/WV_53rt2mUI/AAAAAAAAYss/siHlMVPn4ZMkCpBmXHQ00u4RVdnptZ23QCKgBGAs/s200/IMG_20170630_141154.jpg" width="155" /></a></div>
I've been fascinated by mechanical watches since I was a kid. Little machines on the wrist, marking out the passage of time with an unexpected level of accuracy, at least if the watch is in good shape.<br />
<br />
To measure accuracy and diagnose issues, watchmakers use a watch timer which listens for and measures intervals between ticks. However, the tools are rather expensive for hobbyists, such as yours truly.<br />
<br />
Fortunately, free or low-cost options like <a href="http://watchoscope.com/">Watch-o-Scope</a> ease the burden, but require a sensor and preamplifier. Here's my take on this fun little preamp project.<br />
<a name='more'></a><br />
<h2>
Preamplifier</h2>
Watch-o-Scope <a href="http://www.watchoscope.com/amplifier.html">provides</a> a schematic of a circuit that amplifies frequencies within a set band to filter out noise.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-lhcwmC22YgY/WV6iyKB_KjI/AAAAAAAAYr8/BNO4OpkD2GAwoKuXsjo55WpsPxq1IC_mwCLcBGAs/s1600/wos_filteramp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="403" data-original-width="732" height="220" src="https://3.bp.blogspot.com/-lhcwmC22YgY/WV6iyKB_KjI/AAAAAAAAYr8/BNO4OpkD2GAwoKuXsjo55WpsPxq1IC_mwCLcBGAs/s400/wos_filteramp.png" width="400" /></a></div>
<br />
The circuit is made of three inverting operational amplifiers in series with active filtering. Here's how that works. A basic version of an inverting op amp circuit looks like this:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://1.bp.blogspot.com/-1V_FYwj1Mfs/WV6ZtdwJ93I/AAAAAAAAYrg/KlUj6oxO5EsXfoeDuGCxBGy5sLk5Cj35wCLcBGAs/s1600/300px-Op-Amp_Inverting_Amplifier.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="175" data-original-width="300" src="https://1.bp.blogspot.com/-1V_FYwj1Mfs/WV6ZtdwJ93I/AAAAAAAAYrg/KlUj6oxO5EsXfoeDuGCxBGy5sLk5Cj35wCLcBGAs/s1600/300px-Op-Amp_Inverting_Amplifier.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Source: Wikipedia (Jul 6, 2017)</td></tr>
</tbody></table>
The gain is given by:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-JhQMIQ7sH4Y/WV6aH8Zil9I/AAAAAAAAYrk/vYYnURAHKeEh0A2GT9edbONjBq3RkpLjgCLcBGAs/s1600/CodeCogsEqn%2B%25286%2529.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="39" data-original-width="90" src="https://4.bp.blogspot.com/-JhQMIQ7sH4Y/WV6aH8Zil9I/AAAAAAAAYrk/vYYnURAHKeEh0A2GT9edbONjBq3RkpLjgCLcBGAs/s1600/CodeCogsEqn%2B%25286%2529.gif" /></a></div>
<br />
To implement an active low pass filter with gain, we place a capacitor in parallel with Rf. For AC signals, the gain of the circuit depends on feedback <a href="https://en.wikipedia.org/wiki/Electrical_impedance">impedance</a>, Zf, which is the sum of resistance and reactance:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-fALUOxKEaSI/WV6cap8P8zI/AAAAAAAAYro/ElvSCTb61TUfPd89UNUn6iZlF_eKOD2YwCLcBGAs/s1600/CodeCogsEqn%2B%25289%2529.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="43" data-original-width="120" src="https://2.bp.blogspot.com/-fALUOxKEaSI/WV6cap8P8zI/AAAAAAAAYro/ElvSCTb61TUfPd89UNUn6iZlF_eKOD2YwCLcBGAs/s1600/CodeCogsEqn%2B%25289%2529.gif" /></a></div>
<br />
Where:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-98CHOMmavf8/WV6dfFc7zzI/AAAAAAAAYrw/b6zL-DM0LNwYzOBG8wOvLxvMq4yC2yfxwCLcBGAs/s1600/CodeCogsEqn%2B%252811%2529.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="16" data-original-width="66" src="https://2.bp.blogspot.com/-98CHOMmavf8/WV6dfFc7zzI/AAAAAAAAYrw/b6zL-DM0LNwYzOBG8wOvLxvMq4yC2yfxwCLcBGAs/s1600/CodeCogsEqn%2B%252811%2529.gif" /></a></div>
<br />
and, yeah, the <i>j</i> is an imaginary number.<br />
<br />
You can see that as frequency increases, Zf decreases. In turn, gain decreases with increasing frequency, thus, we have a low pass filter.<br />
<br />
Adding a capacitor in series with Rin creates a high pass filter for the input to the op amp, where impedance, Zin, decreases with increasing frequency:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-rV5RRxM-vxA/WV6fO8p7xvI/AAAAAAAAYr0/lPRVO5TT0z8TX5pWiKFEydFsURoJ7WqQACLcBGAs/s1600/CodeCogsEqn%2B%252812%2529.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="41" data-original-width="121" src="https://2.bp.blogspot.com/-rV5RRxM-vxA/WV6fO8p7xvI/AAAAAAAAYr0/lPRVO5TT0z8TX5pWiKFEydFsURoJ7WqQACLcBGAs/s1600/CodeCogsEqn%2B%252812%2529.gif" /></a></div>
<br />
So that gain increases with increasing frequency; a high pass filter.<br />
<br />
You can compute the <a href="https://en.wikipedia.org/wiki/Cutoff_frequency">cutoff frequency</a> of each filter as:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/--YUJv9Ob5fw/WV6hxbLFoiI/AAAAAAAAYr4/FU5UCTTgYmoQVks97k3u2GWK0-0CzWmdQCLcBGAs/s1600/CodeCogsEqn%2B%252813%2529.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="37" data-original-width="89" src="https://4.bp.blogspot.com/--YUJv9Ob5fw/WV6hxbLFoiI/AAAAAAAAYr4/FU5UCTTgYmoQVks97k3u2GWK0-0CzWmdQCLcBGAs/s1600/CodeCogsEqn%2B%252813%2529.gif" /></a></div>
<br />
Put it all together and we have a high-gain, band pass amplifier.<br />
<br />
You may wonder: what frequencies do we use? To find out, I recorded the unfiltered sound of one of my 100-year-old Elgin size 6 pocket watch movements using the piezo sensor amplified by my <a href="https://www.tindie.com/products/bot_thoughts/eezeeamp/">eeZeeAmp</a> board set to a gain of about 200:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-DKw21LOKArI/WScZi623wEI/AAAAAAAAYFQ/KfkgdLd4a00duXSr1ySKkQ684xJHwiFKACLcB/s1600/BulovaNoise.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="167" data-original-width="596" height="111" src="https://3.bp.blogspot.com/-DKw21LOKArI/WScZi623wEI/AAAAAAAAYFQ/KfkgdLd4a00duXSr1ySKkQ684xJHwiFKACLcB/s400/BulovaNoise.png" width="400" /></a></div>
<br />
The signal is just a mess, as you can see. My ears can barely pick out the ticking from the noise. But it is there, and you can see the ticks when you zoom in on the waveform. Using Audacity's filtering capabilities, I eventually eliminated most of the noise:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/--yiz4JE-c7w/WScbuhZPUSI/AAAAAAAAYFc/p6ji30gNhEk3KmKEiukM9CtKG_aUhEc0ACLcB/s1600/bulovanoise2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="184" data-original-width="727" height="100" src="https://1.bp.blogspot.com/--yiz4JE-c7w/WScbuhZPUSI/AAAAAAAAYFc/p6ji30gNhEk3KmKEiukM9CtKG_aUhEc0ACLcB/s400/bulovanoise2.png" width="400" /></a></div>
<br />
Most of the important sounds appeared to fall in the range of about 2000-5000 Hz. I had to widen the band, however, for some of my wristwatches.<br />
<br />
<h2>
Diagnosis</h2>
So what can we learn from a watch timer? This trace of my 1957 Bulova 23 Jewel, shows the watch is relatively accurate, relatively clean and with a low beat error.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-YheIt2jCmmk/WV6mxy6Qn-I/AAAAAAAAYsA/4E0ra_vZWfUayg_LK9xThHl8uQnOa730ACLcBGAs/s1600/bulova.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="586" data-original-width="962" height="242" src="https://1.bp.blogspot.com/-YheIt2jCmmk/WV6mxy6Qn-I/AAAAAAAAYsA/4E0ra_vZWfUayg_LK9xThHl8uQnOa730ACLcBGAs/s400/bulova.png" width="400" /></a></div>
<br />
Perfect accuracy means a <i>daily rate</i> of 0 seconds per day of gain/loss and would show as a series of dots (ticks) perfectly overlaying the horizontal line in the middle. For an old watch, 8 seconds per day is arguably tolerable.<br />
<br />
An amplitude of 224<span style="background-color: white; color: #222222;"><span style="font-family: "arial" , "helvetica" , sans-serif;">° </span></span>rotation of the balance (270<span style="background-color: white; color: #222222; font-family: "arial" , "helvetica" , sans-serif;">° </span>is ideal), combined with a fairly straight, fairly noiseless trace, suggests a clean and well-lubricated watch. And the beat error, the difference in the amount of time the balance spends rotating in each direction, is minimal.<br />
<br />
By taking a longer-term trace, we can discern periodic fluctuations point to flaws in the rotating gears of the watch, called the <a href="https://en.wikipedia.org/wiki/Wheel_train">wheel train</a>. The same Bulova 23 over a 3 hour period is enlightening.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-8UWQH2WbJ2I/WV_33BS0kWI/AAAAAAAAYso/y7yYSrj1PWYP3cI8yxxS0fRWu7vqAJSvACLcBGAs/s1600/Bulova23_overlay.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="975" data-original-width="1600" height="243" src="https://1.bp.blogspot.com/-8UWQH2WbJ2I/WV_33BS0kWI/AAAAAAAAYso/y7yYSrj1PWYP3cI8yxxS0fRWu7vqAJSvACLcBGAs/s400/Bulova23_overlay.png" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
The trace above shows fluctuations in amplitude and rate occurring about five times an hour. Until I learn more about the movement I won't know the cause. Meanwhile the blue shading I added highlights an hourly variation that may point to issues with the center (hour) wheel. The slight overall downward trend in amplitude may be due to the main spring unwinding.<br />
<br />
And with that, I now have a suitable circuit for diagnosing my various mechanical watches.<br />
<br />
References<br />
<br />
<ol>
<li><a href="https://en.wikipedia.org/wiki/Operational_amplifier_applications#Inverting_amplifier">https://en.wikipedia.org/wiki/Operational_amplifier_applications#Inverting_amplifier</a> (July 6, 2017)</li>
<li><a href="https://www.allaboutcircuits.com/textbook/alternating-current/chpt-8/what-is-a-filter/">https://www.allaboutcircuits.com/textbook/alternating-current/chpt-8/what-is-a-filter/</a> (July 6, 2017)</li>
</ol>
<br />
<br />Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-20991359447982477942017-05-29T18:00:00.000-06:002020-01-10T13:00:32.616-07:00Soothe and Glow doesn't light up<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-3T5faRc4rZE/VSf3ljUoLZI/AAAAAAAAMGY/-cusn6VZJTw/s1600/IMG_20150410_094314467.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://1.bp.blogspot.com/-3T5faRc4rZE/VSf3ljUoLZI/AAAAAAAAMGY/-cusn6VZJTw/s400/IMG_20150410_094314467.jpg" /></a></div>
<br />
Our little girl goes to sleep easier when we have a Fisher Price Soothe and Glow running. One of them didn't light up, and I fixed it. But not before my wife bought another one. Here's what was wrong.<br />
<a name='more'></a><br />
<h2>
Disassembly</h2>
Undo the hook-and-loop closure at the bottom of the animal and pull out the electronics module. It's built in two halves with a giant button on the top. The top half is just a frame around the button.<br />
<br />
Remove the battery cover retaining screw. Then remove the four screws holding the two halves of the module together.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-LwgR8O4L8Dk/VSf3lg4ST7I/AAAAAAAAMGY/XR60Rg56ykc/s1600/IMG_20150410_082719964_HDR.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://4.bp.blogspot.com/-LwgR8O4L8Dk/VSf3lg4ST7I/AAAAAAAAMGY/XR60Rg56ykc/s1600/IMG_20150410_082719964_HDR.jpg" width="400" /></a></div>
<br />
With the top half off, remove the giant button to get access to the PCB. The button has two posts with springs around them and there are two clip ears retaining the giant button to the PCB.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-utRGTPqZrnw/VSf3lsdxYKI/AAAAAAAAMGY/LNTaN44hTy4/s1600/IMG_20150410_082705855_HDR.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://1.bp.blogspot.com/-utRGTPqZrnw/VSf3lsdxYKI/AAAAAAAAMGY/LNTaN44hTy4/s1600/IMG_20150410_082705855_HDR.jpg" width="400" /></a></div>
<br />
<br />
Pry one of the clip ears loose and remove the button off of the PCB so you can inspect for problems.<br />
<h2>
Diagnosis</h2>
There are three surface mount LEDs on the board. After giving the board a cursory inspection, I thought the problem was simply that the Q1 transistor wasn't populated. But, no; when I disassembled the second, working giraffe it was missing Q1 as well. (This is common on production PCBs I've seen, probably parts that testing revealed weren't necessary or features that were trimmed to save cost).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-3iEaZVuFciU/VSf3lsoOX4I/AAAAAAAAMGY/gLSdQa9bFg4/s1600/IMG_20150410_082800.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://4.bp.blogspot.com/-3iEaZVuFciU/VSf3lsoOX4I/AAAAAAAAMGY/gLSdQa9bFg4/s1600/IMG_20150410_082800.jpg" width="400" /></a></div>
<br />
Next, I broke out the DMM to check voltages and noticed the LEDs come on when I tried to measure voltage at R1, the 0-ohm resistor jumper, which transmits power to the LEDs. That's weird.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-AjvZWMxux48/VSf6KGZg2NI/AAAAAAAAMGo/E8AaYt65H8o/s1600/IMG_20150410_101438.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="232" src="https://4.bp.blogspot.com/-AjvZWMxux48/VSf6KGZg2NI/AAAAAAAAMGo/E8AaYt65H8o/s1600/IMG_20150410_101438.jpg" width="320" /></a></div>
<br />
And if I pressed one of the on buttons hard enough the lights would sorta-kinda flicker. Seemed like a bad connection somewhere. I couldn't see anything wrong, though.<br />
<br />
Then I accidentally discovered that when I touched the 0-ohm resistor with my finger, the LEDs lit up. That points to a cracked resistor or cold solder joint.<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-g9Rk20lh_2Q/VSf3lhpzdzI/AAAAAAAAMGY/7YPyWuTQKKI/s1600/IMG_20150410_083458691_HDR.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://4.bp.blogspot.com/-g9Rk20lh_2Q/VSf3lhpzdzI/AAAAAAAAMGY/7YPyWuTQKKI/s1600/IMG_20150410_083458691_HDR.jpg" width="400" /></a></div>
<br />
And, in fact, it was a bit of both as the hot air rework station demonstrated. The resistor came loose even though only one of the two joints was molten. And there was a chunk missing.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-L5WCPl5TaPY/VSgP2OHAQhI/AAAAAAAAMHI/AC2pws6XnfQ/s1600/IMG_20150410_083642929_HDR.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="310" src="https://2.bp.blogspot.com/-L5WCPl5TaPY/VSgP2OHAQhI/AAAAAAAAMHI/AC2pws6XnfQ/s1600/IMG_20150410_083642929_HDR.jpg" width="400" /></a></div>
<br />
I initially tried replacing the resistor with a 10-ohm, but it dropped too much voltage. So, I replaced the resistor with a small section of bus wire, soldered in with the iron. And now the giraffe glows like it's supposed to.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-7obS22v2nPA/VSf3lihSQhI/AAAAAAAAMGY/mU1wt-m-Vuw/s1600/IMG_20150410_084346754.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://4.bp.blogspot.com/-7obS22v2nPA/VSf3lihSQhI/AAAAAAAAMGY/mU1wt-m-Vuw/s1600/IMG_20150410_084346754.jpg" width="400" /></a></div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-53744954649926400112017-03-29T19:00:00.000-06:002017-03-29T19:00:26.707-06:00Pi Touchscreen and Lightning Bolt<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-KHA8mP8jeTQ/WNvSy7-VRCI/AAAAAAAAXn4/jgUNAZv6fdMEBLeiA7oyR1SJKvnitBVDACLcB/s1600/lightning.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://1.bp.blogspot.com/-KHA8mP8jeTQ/WNvSy7-VRCI/AAAAAAAAXn4/jgUNAZv6fdMEBLeiA7oyR1SJKvnitBVDACLcB/s1600/lightning.png" /></a></div>
I've been working on some neat-o projects with a Raspberry Pi 7" Touchscreen on a Raspberry Pi 2 for a few weeks.<br />
<br />
But, the Pi has been plagued by the yellow lightning bolt icon, which means "your power supply sucks".<br />
<br />
Nothing I've tried, until now, has fixed it. I've been through several phone chargers in 1A, 2.1A, and 2.4A varieties, several USB cables, and I've tried a <a href="https://www.pololu.com/category/131/step-down-voltage-regulators">Pololu</a> 3.5A step-down switching regulator, and my own prototype 3A step-down regulator (I think I broke it?!)<br />
<br />
My solution: power the touchscreen and Pi from independent phone chargers, after disconnecting the 5V jumper between them. Hope this helps someone else.<br />
<br />
Subscribe and stay tuned for a future article on my Jeep-mounted <i>Overland Expedition Pi</i> with offline Mapping, Satellite Telemetry and Messaging to HQ, and APRS.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-vHjeR_Msa8k/WNvPfB8c_xI/AAAAAAAAXnw/CJyMHR4XKjEvAIaap2hfkFjVrEjxwmHbACEw/s1600/pi_aprs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://1.bp.blogspot.com/-vHjeR_Msa8k/WNvPfB8c_xI/AAAAAAAAXnw/CJyMHR4XKjEvAIaap2hfkFjVrEjxwmHbACEw/s320/pi_aprs.png" width="320" /></a></div>
<br />
<br />
<br />Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com1tag:blogger.com,1999:blog-5983110881018172452.post-32747678866621084692017-01-17T12:10:00.000-07:002017-01-17T12:10:02.328-07:00My Western Electric 302<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Wt3ikXMG1aw/WFF9SOX_cdI/AAAAAAAAWZE/oX5XT8aNm7sjULSUR1z40S36TaQg7tA2wCLcB/s1600/we302.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="242" src="https://2.bp.blogspot.com/-Wt3ikXMG1aw/WFF9SOX_cdI/AAAAAAAAWZE/oX5XT8aNm7sjULSUR1z40S36TaQg7tA2wCLcB/s400/we302.jpg" width="400" /></a></div>
<br />
I've finally fixed my only vintage phone, an old Western Electric 302, and thought I'd share the old telephones' electro-mechanical guts.<br />
<h2>
Pulses</h2>
These old phones are how <i>dialing</i> a number became a thing. Instead of push buttons and <a href="http://searchnetworking.techtarget.com/definition/DTMF">DTMF</a>, rotary telephones send pulses. And not with a 555 timer, either...</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-USr-k9QqZmY/WFFy7SXatmI/AAAAAAAAWYg/DYuxrgPF424MF9rhTgNJx9OYpauDxUOhACLcB/s1600/302_dial.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="183" src="https://2.bp.blogspot.com/-USr-k9QqZmY/WFFy7SXatmI/AAAAAAAAWYg/DYuxrgPF424MF9rhTgNJx9OYpauDxUOhACLcB/s400/302_dial.jpg" width="400" /></a></div>
<br />
<br />
<a name='more'></a>Stick a finger in the number you want to dial next, rotate the dial clockwise until your finger hits the silver finger stopper thingy.<br />
<br />
Then, remove the finger, and the dial rotates at a precise speed, sending up to 10 pulses within one second by way of contacts and cams.<br />
<br />
The pulses are timed to within 10% of 66ms with the magic of physics.<br />
<br />
How, you may ask?<br />
<h3>
Governor</h3>
The dial uses a speed governor with hinged weights attached to a spinning shaft, similar to automotive distributors' <a href="http://www.racingjunk.com/news/wp-content/uploads/2015/04/25-678x381.jpg">mechanical advance mechanisms</a> that are used to advance timing as engine rpm increases.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-00M2FnP8_hM/WFGEJd2aUJI/AAAAAAAAWZU/f9Y6Jdis3f8CqeOdQx8wcoUAXrnX1aTUQCLcB/s1600/regulator.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="278" src="https://4.bp.blogspot.com/-00M2FnP8_hM/WFGEJd2aUJI/AAAAAAAAWZU/f9Y6Jdis3f8CqeOdQx8wcoUAXrnX1aTUQCLcB/s400/regulator.png" width="400" /></a></div>
<br />
In the 302, the spinning shaft is driven by the dial return spring. As rotational speed increases, the law of inertia for the mass of the weights (1) and the centripetal force at the hinge point cause the weights to rotate outward, overcoming an adjustment spring (3).<br />
<br />
Rubber stoppers (2) mounted on the outside of the weights then contact a containing drum (4), where friction prevents further speed increase.<br />
<h3>
Contacts</h3>
The electrical pulses are made by a cam that makes and breaks a set of contacts.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-auLQKMx0mns/WFGLL7OBVWI/AAAAAAAAWZk/ot379XPApYcDefVssm24NIdVsZ-Vn5-agCLcB/s1600/contacts_label.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="262" src="https://4.bp.blogspot.com/-auLQKMx0mns/WFGLL7OBVWI/AAAAAAAAWZk/ot379XPApYcDefVssm24NIdVsZ-Vn5-agCLcB/s400/contacts_label.png" width="400" /></a></div>
<br />
The cam (1), driven by the dial's geartrain and return spring, raises and lowers the flexible contact arm (2) to make contact with the second flexible contact (3). When one initially rotates the dial clockwise, no pulses are sent.<br />
<br />
The cam (1) rotates counterclockwise along with a raised nub (3). When the nub is out of the way, the top contact is allowed to move down, remaining in constant contact with the lower contact (2).<br />
<br />
When the dial is released, the cam immediately rotates clockwise, raising the contacts (2,3), while the nub rotates into place, holding the top contact up, so that the cam can now break the connection once per rotation.<br />
<div>
<h2>
Adjustment</h2>
</div>
<div>
Adjustment of the governor spring ensures the frequency of the pulses falls within the tolerances specified ages ago by the phone company.<br />
<br />
By the way, many modern phone companies have equipment that still support pulse dialing. Including those at my central office.<br />
<br />
I don't know what test equipment the Bell companies or Western Electric used to calibrate these dials. But I used a thoroughly modern (and indispensable) <a href="https://www.saleae.com/">Saleae Logic 8 Analyzer</a>.<br />
<br />
I connected one contact to ground, the other to 5V through a pullup resistor, and measured that contact. When the connection is made, the voltage drops to 0V. When it is broken it rises to 5V.<br />
<br />
Following lubrication and cleaning of the horribly gummed up dial mechanism, it was spinning a bit too fast, with 10 pulses being sent in only 0.9s.<br />
<br />
Tediously tweaking the governor spring, I got within 1% of the target (506ms for 5 pulses as shown below).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-xAeShiqgSEg/WFGHxIVsCoI/AAAAAAAAWZc/o2F6YCpvHGs0zBswbuxLOMCzssKsmczVACLcB/s1600/302_after.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="222" src="https://1.bp.blogspot.com/-xAeShiqgSEg/WFGHxIVsCoI/AAAAAAAAWZc/o2F6YCpvHGs0zBswbuxLOMCzssKsmczVACLcB/s400/302_after.png" width="400" /></a></div>
<br />
Adjusting the duty cycle (pulse length) is a matter of bending the contacts slightly. With a little tweaking, and verifying that pulses are only sent when the dial is returning home, the dial's pulses are now within a few ms of the 66ms target across the entire rotation and range of numbers.<br />
<br />
And, more to the point, the phone company correctly recognizes the numbers I dial.<br />
<br />
Of course, none of this was possible until I first fixed the phone's internal wiring. More on that in a future post.</div>
Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-88897244436048625852016-12-26T17:00:00.000-07:002016-12-26T17:00:03.443-07:00Making Slot Car Tires<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-ieX4E1zQJYw/WGFvBsdo9pI/AAAAAAAAWtY/LM0rZoOsvOknvlXPTNLZqj_TuKCQZrnEwCKgB/s1600/IMG_20161220_091902583.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://1.bp.blogspot.com/-ieX4E1zQJYw/WGFvBsdo9pI/AAAAAAAAWtY/LM0rZoOsvOknvlXPTNLZqj_TuKCQZrnEwCKgB/s400/IMG_20161220_091902583.jpg" width="400" /></a></div>
<br />
As Christmas approached when I was a kid and Rudolph the Red Nosed Reindeer aired on the TV, Dad would return from the garage with an enormous box emblazoned with "Thunderbolt III" and a picture of two race cars battling to victory.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-zbU99kRTB08/WGFsNMq-BII/AAAAAAAAWtE/fAXjjZ4m4-ISFk8A04-2a6FHX9NprjwAACLcB/s1600/Thunderbolt3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="272" src="https://4.bp.blogspot.com/-zbU99kRTB08/WGFsNMq-BII/AAAAAAAAWtE/fAXjjZ4m4-ISFk8A04-2a6FHX9NprjwAACLcB/s400/Thunderbolt3.jpg" width="400" /></a></div>
<br />
We set up the Strombecker slot car tracks and, grasping the blue speed controllers, we brought to life the epic struggle of white <a href="https://en.wikipedia.org/wiki/Chaparral_Cars">Chaparral 2D</a> and yellow <a href="https://en.wikipedia.org/wiki/Ford_GT40">Ford GT 40 Mk II</a>, father versus son.<br />
<br />
I still have that old slot car set. And now a child of my own. And though Dad left this life in 2010 and mom passed away in September, they'd be happy to see my 7 year old daughter and I reliving those moments from my own childhood.<br />
<br />
Except... when I finally unwrapped the cars, stored carefully 35 years ago, the tires fell apart in my hands. Rats.<br />
<br />
After some online research, I discovered one can create custom slot car tires using a 2-part RTV silicone rubber. So that's what I did.<br />
<a name='more'></a>I found an <a href="http://www.alumilite.com/store/p/947-Super-Casting-Kit.aspx">Alumilite Super Casting Kit</a> for $40 with coupon from the local craft store. The kit contains 2-part silicone rubber, 2-part resin, mold release, measuring cups, and stir sticks.<br />
<br />The instructions describe using the silicone rubber to make the mold. Instead, I made a plastic mold with my Monoprice Architect 3D printer and used the rubber, instead of the 2-part resin, as the casting material.<br />
<br />
I designed the mold in FreeCAD. It is designed to mold the tires right around the original tires. The mold is basically just a bowl with a raised cylinder that the original tires snap onto. Then you pour the RTV Silicone goo around the outside of the wheels.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-KI_x-60Qs58/WGFvH2bqN1I/AAAAAAAAWtg/4NJa83rNx_0oZUh9ncR3RjSdklG1GDN0QCKgB/s1600/IMG_20161217_160215145.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="223" src="https://4.bp.blogspot.com/-KI_x-60Qs58/WGFvH2bqN1I/AAAAAAAAWtg/4NJa83rNx_0oZUh9ncR3RjSdklG1GDN0QCKgB/s400/IMG_20161217_160215145.jpg" width="400" /></a></div>
<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-oGS3HCGkUPk/WGF6lMKxTBI/AAAAAAAAWuY/WoVD7tIaWw8XNu4R9EZlYfbTZYs0ZccYACLcB/s1600/Screenshot%2Bfrom%2B2016-12-26%2B13%253A15%253A58.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="368" src="https://3.bp.blogspot.com/-oGS3HCGkUPk/WGF6lMKxTBI/AAAAAAAAWuY/WoVD7tIaWw8XNu4R9EZlYfbTZYs0ZccYACLcB/s400/Screenshot%2Bfrom%2B2016-12-26%2B13%253A15%253A58.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Transparent 3d view of the mold</td></tr>
</tbody></table>
<br />
In the first attempt, I thought it'd be fun to add black coloring. Since the silicone is white, the tire came out dark grey. I'll have to look for some clear silicone in the future.<br />
<br />
The first two test tires came out fine, and I put them on the GT40. I replaced the rear axle and tires with a set from eBay.<br />
<br />
I had a working slot car, although the silicone tires were slightly too large in diameter and inhibited contact of the pickup brushes with the track.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-9kOtU-uTXJg/WGFwAd1RPlI/AAAAAAAAWts/hjs2usYEd407-rra3qVyk8mV8joPcRpWwCKgB/s1600/IMG_20161220_083148739.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://4.bp.blogspot.com/-9kOtU-uTXJg/WGFwAd1RPlI/AAAAAAAAWts/hjs2usYEd407-rra3qVyk8mV8joPcRpWwCKgB/s400/IMG_20161220_083148739.jpg" width="400" /></a></div>
<br />
For the Chaparral, I printed smaller front tire molds, and then printed the rear tire molds. I was able to get measurements from the crusty original tires before they fell apart.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-yLHc5v-AIYg/WGFwcsNjDHI/AAAAAAAAWt4/8HC42xD1k5EF0qfblzzt3pLnMdL1jo3EwCKgB/s1600/IMG_20161220_083053228.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://4.bp.blogspot.com/-yLHc5v-AIYg/WGFwcsNjDHI/AAAAAAAAWt4/8HC42xD1k5EF0qfblzzt3pLnMdL1jo3EwCKgB/s400/IMG_20161220_083053228.jpg" width="400" /></a></div>
<br />
The next day I had a set of white tires for the Chaparral. However, they have a tendency to come off the wheels really easily. But, they do have more traction than the original rubber.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-06TG-1-SoR4/WGFwqbPAwxI/AAAAAAAAWt8/_aF9ZGRg9tQooVEVJDA5cOn6slr4-QtZwCKgB/s1600/IMG_20161220_083757171.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://2.bp.blogspot.com/-06TG-1-SoR4/WGFwqbPAwxI/AAAAAAAAWt8/_aF9ZGRg9tQooVEVJDA5cOn6slr4-QtZwCKgB/s400/IMG_20161220_083757171.jpg" width="400" /></a></div>
<br />
For the next attempt I'll 3D print a wheel and mold to create undersized tires that have to stretch a little to fit onto the original wheels.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-jqGFlqVtwYU/WGFxKJJ2kZI/AAAAAAAAWuE/lfwHqA7m7tEOTD22yZIhFKfpbxD0MCbigCKgB/s1600/IMG_20161220_083851216.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://2.bp.blogspot.com/-jqGFlqVtwYU/WGFxKJJ2kZI/AAAAAAAAWuE/lfwHqA7m7tEOTD22yZIhFKfpbxD0MCbigCKgB/s400/IMG_20161220_083851216.jpg" width="400" /></a></div>
<br />
<br />
I ended up going a little nuts over the last month, buying two more cars (both need rear tires), a New Old Stock chassis, new brushes, and a ton of track. I repaired a lot of soldering issues on the cars and track.<br />
<br />
We've had some good fun racing with family and friends. But I have more up my sleeve. I'm working on a track timer...Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-35516453285194071632016-12-24T13:03:00.001-07:002016-12-26T12:01:22.889-07:00Hello LED<div dir="ltr">
<u>My</u> wife and daughter adore Hello Kitty. No, I mean really, <i>really</i> adore her. No, see, I don't think you quite understand. Here, let me just <i>show</i> you what Christmas looks like at the Bot Thoughts laboratory:</div>
<div dir="ltr">
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-XUyRVUJnXn8/WF7TMsdOY4I/AAAAAAAAWoc/4c2gvkXPIPgZctdWOaVC7ir8i8G4fMXlACKgB/s1600/IMG_20161224_114924273.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="186" src="https://1.bp.blogspot.com/-XUyRVUJnXn8/WF7TMsdOY4I/AAAAAAAAWoc/4c2gvkXPIPgZctdWOaVC7ir8i8G4fMXlACKgB/s320/IMG_20161224_114924273.jpg" width="320" /></a></div>
<br /></div>
<div dir="ltr">
This Christmas we faced a <u>major</u> problem that would totally ruin our holiday season.<br />
<br />
At night, the smallest of the inflatable Hello Kitties were really dim! And the lights inside them were --<i>gasp</i>-- not user replaceable!</div>
<div dir="ltr">
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://4.bp.blogspot.com/-LJi_yOQlWPM/WF7Mk-uSNTI/AAAAAAAAWnc/hEslKqs5ldowUWT0yeE3FtcijWZaqQNkgCKgB/s1600/IMG_20161211_171241877.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="150" src="https://4.bp.blogspot.com/-LJi_yOQlWPM/WF7Mk-uSNTI/AAAAAAAAWnc/hEslKqs5ldowUWT0yeE3FtcijWZaqQNkgCKgB/s400/IMG_20161211_171241877.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Before: Small kitties are dim!</td></tr>
</tbody></table>
Well, we are not users, are we, gentle reader? With a little knowledge of electronics, we can fix the "unfixable". Here's how things looked with only 3 kitties left to fix.</div>
<div dir="ltr">
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-2988cbewD-0/WF7GSmpnC8I/AAAAAAAAWmg/l4rgsKn5zFI_6fWwv6DAYhX9kr2W7If-gCKgB/s1600/IMG_20161222_173149585.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="225" src="https://2.bp.blogspot.com/-2988cbewD-0/WF7GSmpnC8I/AAAAAAAAWmg/l4rgsKn5zFI_6fWwv6DAYhX9kr2W7If-gCKgB/s400/IMG_20161222_173149585.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">All but three kitties have been upgraded</td></tr>
</tbody></table>
<div dir="ltr">
So, here's how we brightened up our Christmas Kitties...<br />
<a name='more'></a></div>
<h2>
<a href="https://4.bp.blogspot.com/-x96H8xuR5-o/WF7LbZwvRgI/AAAAAAAAWm4/FEB1AhQsDgoAgROsQR9zB7PCq7bg20lNwCKgB/s1600/IMG_20161220_132148395.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="200" src="https://4.bp.blogspot.com/-x96H8xuR5-o/WF7LbZwvRgI/AAAAAAAAWm4/FEB1AhQsDgoAgROsQR9zB7PCq7bg20lNwCKgB/s200/IMG_20161220_132148395.jpg" width="112" /></a>
What We're Dealing With</h2>
<div dir="ltr">
There's a single, white, surface mount LED inside the small kitties at the back of the head, clipped to the material from the outside (see right)</div>
<div dir="ltr">
<br />
These inflatables have zippers at the bottom to access the internals. There's no need to unclip the LED.<br />
<div>
<br /></div>
The medium sided kitties have two LEDs. So, maximum of probably 20mA per LED.<br />
<br />
<div dir="ltr">
How much voltage and current do the small power supplies provide? Voltage, verified by my multimeter, is 12V. Current available is 1A.</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-1Wi78ufY74M/WF7MVCCAtRI/AAAAAAAAWnM/kvbvO-d_M1AOSS0ZrCve_j-uD6E6AUdeACKgB/s1600/IMG_20161220_132237213.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="225" src="https://2.bp.blogspot.com/-1Wi78ufY74M/WF7MVCCAtRI/AAAAAAAAWnM/kvbvO-d_M1AOSS0ZrCve_j-uD6E6AUdeACKgB/s400/IMG_20161220_132237213.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Epoxy-encapsulated single SMT LED</td></tr>
</tbody></table>
</div>
<div dir="ltr">
<h2>
Design</h2>
</div>
<div dir="ltr">
We settled on two sets of four series-connected LEDs and a series 82 ohm resistor. So, four LEDs in parallel with four LEDs.</div>
<div dir="ltr">
<br />
Make sure you use a resistor rated for the power through it. Power is given by current times voltage: P=IV. So for example, 10mA x 12V = 0.12 watts. That's nearly 1/8W so I used 1/4W and 1/2W resistors I had laying around.<br />
<br />
I selected wide-angle, 20mA, high brightness LEDs from Digikey, (Part No: <a href="https://www.digikey.com/product-detail/en/cree-inc/C535A-WJN-CU0W0231/C535A-WJN-CU0W0231-ND/5823264">C535A-WJN-CU0W0231-ND</a>)<br />
<h2>
Construction</h2>
I found some great protoboards on ebay. Long and narrow, it was easy to install the LEDs and resistors.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Ge-s8UwAKHM/WF7TGiNv7zI/AAAAAAAAWoY/2HH0YSKAOTkgofqAaBuPWZyTM2dYsxYcgCKgB/s1600/IMG_20161220_132200588.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://2.bp.blogspot.com/-Ge-s8UwAKHM/WF7TGiNv7zI/AAAAAAAAWoY/2HH0YSKAOTkgofqAaBuPWZyTM2dYsxYcgCKgB/s400/IMG_20161220_132200588.jpg" width="400" /></a></div>
<br />
Since my wife has mastered soldering while teaching the 5th graders, assembly was a team effort.<br />
<h2>
Installation</h2>
Installing the LED array was a matter of unzipping the un-inflated Kitty, reaching up in and grabbing the LED module, pulling out to access it.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-YkdR4KEio-4/WF7S9Y7r2VI/AAAAAAAAWoU/Mi9Dj88i3JAlQa5ORVL4V0vWI-xYztZ_ACKgB/s1600/IMG_20161220_132237213.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://2.bp.blogspot.com/-YkdR4KEio-4/WF7S9Y7r2VI/AAAAAAAAWoU/Mi9Dj88i3JAlQa5ORVL4V0vWI-xYztZ_ACKgB/s400/IMG_20161220_132237213.jpg" width="400" /></a></div>
<br />
<br />
Then, clipping the wires, I soldered them onto the protoboard.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-5NrEU3ID1TU/WF7SfV5wYkI/AAAAAAAAWoI/8qVEHRsBviMTtMA7FimRnbTkO1x8pt_DwCKgB/s1600/IMG_20161220_132513888.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://1.bp.blogspot.com/-5NrEU3ID1TU/WF7SfV5wYkI/AAAAAAAAWoI/8qVEHRsBviMTtMA7FimRnbTkO1x8pt_DwCKgB/s400/IMG_20161220_132513888.jpg" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
I placed a dollop of Goop to glue the new board onto the old module.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-WwQ8lvEZDA8/WF7Sv6c1BQI/AAAAAAAAWoM/5ME6a9E4B241IfKzYxG2lqs2YvV1FCU3QCKgB/s1600/IMG_20161220_132531963.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://4.bp.blogspot.com/-WwQ8lvEZDA8/WF7Sv6c1BQI/AAAAAAAAWoM/5ME6a9E4B241IfKzYxG2lqs2YvV1FCU3QCKgB/s400/IMG_20161220_132531963.jpg" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-WvOkjjwtMLk/WF7Sv14nfbI/AAAAAAAAWoM/mdhIxnGOzVM19z0IDcuu691iQtmgd0KGwCKgB/s1600/IMG_20161220_132546635.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://2.bp.blogspot.com/-WvOkjjwtMLk/WF7Sv14nfbI/AAAAAAAAWoM/mdhIxnGOzVM19z0IDcuu691iQtmgd0KGwCKgB/s400/IMG_20161220_132546635.jpg" width="400" /></a></div>
<br />
<br />
Then added a zip tie (pink of course), belt-and-suspenders style, and we have bright kitties again!<br />
<br />
Merry Christmas and/or Happy Holidays!<br />
<div dir="ltr">
<h2>
Oh, Here's an LED Refresher</h2>
</div>
<div dir="ltr">
These white LEDs have a forward voltage drop of about 2.8V and a maximum current of 20mA. By reducing current through the LEDs with a resistor, and by placing the LEDs in series, the current demands on the power supply are minimized while extending LED life -- at the cost of a slight reduction in brightness.</div>
<div dir="ltr">
<br /></div>
<div dir="ltr">
Kirchoff's Voltage Law states that in a circuit, when you traverse a single path starting and ending at a given node, the net sum of voltages is 0. You have 12V supply, so the load drops 12V. </div>
<div dir="ltr">
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/--nXm5F_bpOw/WGFfMX-HeEI/AAAAAAAAWsA/6LbYNJWl07I5orKd-XxmCo39olGQ1seuACLcB/s1600/led_voltage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="330" src="https://2.bp.blogspot.com/--nXm5F_bpOw/WGFfMX-HeEI/AAAAAAAAWsA/6LbYNJWl07I5orKd-XxmCo39olGQ1seuACLcB/s400/led_voltage.png" width="400" /></a></div>
<br />
<br /></div>
<div dir="ltr">
If you place four LEDs in series, the sum of voltage drops is around 12V. What about current through each?</div>
<div dir="ltr">
<br /></div>
<div dir="ltr">
Well, Kirchoff's Current Law states that the sum of currents entering and leaving any node in a circuit is 0. That means the current through each LED and resistor connected in series is the same.<br />
<br />
It also means that connecting four LEDs in parallel draws four times the current as four in series. That's why I put the LEDs in series.</div>
<div dir="ltr">
<br /></div>
<div dir="ltr">
Finally, Ohm's Law states that voltage is equal to resistance times current for a resistor (V=IR).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-BfbID8wHyyc/WGFfzWS2TqI/AAAAAAAAWsI/E0JyZ3jRatcZhSiN8bvj7mG2rtT8WwnzgCLcB/s1600/led_current.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="330" src="https://3.bp.blogspot.com/-BfbID8wHyyc/WGFfzWS2TqI/AAAAAAAAWsI/E0JyZ3jRatcZhSiN8bvj7mG2rtT8WwnzgCLcB/s400/led_current.png" width="400" /></a></div>
<br /></div>
<div dir="ltr">
Using all those laws, you can define the current you want through the LEDs and solve for the resistance value you need.<br />
<br />
In this case, if I want I=15mA, and each LED drops 2.8V, then using Kirchoff's Voltage law, I can solve for the voltage across the resistor:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-zF6Noy5zX78/WGFg2nDSvnI/AAAAAAAAWsY/rg3bjSvnBs45dzLcyXB1LqXRASn0f-OUgCLcB/s1600/CodeCogsEqn%2B%25284%2529.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://2.bp.blogspot.com/-zF6Noy5zX78/WGFg2nDSvnI/AAAAAAAAWsY/rg3bjSvnBs45dzLcyXB1LqXRASn0f-OUgCLcB/s1600/CodeCogsEqn%2B%25284%2529.gif" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-csFrtuGc2tE/WGFg7NRMKVI/AAAAAAAAWsc/YMmce1BNemgBONYJ19NxVNE6NjELvIpvACLcB/s1600/CodeCogsEqn%2B%25285%2529.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://1.bp.blogspot.com/-csFrtuGc2tE/WGFg7NRMKVI/AAAAAAAAWsc/YMmce1BNemgBONYJ19NxVNE6NjELvIpvACLcB/s1600/CodeCogsEqn%2B%25285%2529.gif" /></a></div>
<br />
Then using Ohm's Law I can solve for the resistance, R, that results in a 0.8V drop and 10mA of current.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-u7KJ8zrapp0/WGFnaBJr-EI/AAAAAAAAWs0/V-dXpOTm9Qc5qR75VKhtL2or60etjM_mACLcB/s1600/Screenshot%2Bfrom%2B2016-12-26%2B11%253A39%253A26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://1.bp.blogspot.com/-u7KJ8zrapp0/WGFnaBJr-EI/AAAAAAAAWs0/V-dXpOTm9Qc5qR75VKhtL2or60etjM_mACLcB/s1600/Screenshot%2Bfrom%2B2016-12-26%2B11%253A39%253A26.png" /></a></div>
<br />
Closest <a href="http://www.logwell.com/tech/components/resistor_values.html">E24</a> (5% tolerance) resistor is 56 ohms, but it's a good idea to see what happens in the worst case.<br />
<br />
Most voltage regulators are within 5% (12.6V). Now if the resistor is at the low end of the 5% tolerance range (53 ohm), you'd end up with 26mA which is too high for the LED.<br />
<br />
If you recalculated R for the worst case voltage and worst case current (20mA), you'd compute R=70. With the next E24 resistor of 75 ohms, you'd have a reasonably safe margin.<br />
<br />
You could also check the LED datasheet and see if there is a worst-case (lowest) voltage drop on the LEDs. If you really wanted to get fancy, you could design a current source circuit instead that would be a lot less sensitive to power supply voltage error. But that's another story for another day.</div>
</div>
Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-17522395063892818522016-12-12T17:30:00.000-07:002016-12-12T17:30:09.724-07:00DIY Vehicle Speed Sensor BufferHere's the riveting tale of how I created a Vehicle Speed Sensor (VSS) interface circuit for my Jeep Grand Wagoneer (yeah, <a href="http://www.bot-thoughts.com/2014/06/avc-sharc-fsv.html"><i>that</i> Jeep</a>).
<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://4.bp.blogspot.com/-WuU9U1fZDpk/WEc4ciWodpI/AAAAAAAAWQw/zcqFFI2cy2kAJVuRv4FBF3iRr7Z2P7FvQCLcB/s1600/vss.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="200" src="https://4.bp.blogspot.com/-WuU9U1fZDpk/WEc4ciWodpI/AAAAAAAAWQw/zcqFFI2cy2kAJVuRv4FBF3iRr7Z2P7FvQCLcB/s200/vss.jpg" width="130" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Jeep VSS</td></tr>
</tbody></table>
Speed sensors are used by cruise control systems and by fuel injection computers. Jeep used one for the former.<br />
<br />
I wanted the sensor for the GM throttle body injection (TBI) system I retrofitted onto the Jeep's old AMC 360 c.i. V8.<br />
<br />
Back when my Jeep was built, vehicle speedometers were driven by a flexible steel cable, the <i>speedometer cable</i>, connected by a tiny gear to the output shaft of the transmission. <br />
<br />
Jeep, as with other vehicles of the era, split the <i>speedo cable </i>in half and stuck the VSS in between.<br />
<br />
So, with insufficient hobby funds to buy an expensive, off-the-shelf <a href="http://www.jagsthatrun.com/Pages/SpeedSensors_Speedometer.html">Vehicle Speed Sensor/Buffer</a>, but wanting to reap the benefits of a VSS, I created my own interface. Here's how it went down...
<br />
<a name='more'></a><h2>
The VSS Signal</h2>
Made of a rotating coil and fixed magnet (or maybe vice versa), the voltage across the two output wires of the VSS is a sine wave. It puts out 8000 pulses per mile.
<br />
<br />
As one would hope, the VSS signal's frequency increases with speed. But testing revealed that the <i>amplitude</i> increases too: from less than 1Vpp below 10mph, to a peak of around 4Vpp above 50mph.<br />
<br />
The data points were all I could reliably obtain using a cordless drill to run the thing. But it provided sufficient understanding to devise an interface circuit.<br />
<br />
<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;border-color:#aabcfe;}
.tg td{font-family:Arial, sans-serif;font-size:14px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#aabcfe;color:#669;background-color:#e8edff;}
.tg th{font-family:Arial, sans-serif;font-size:14px;font-weight:normal;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#aabcfe;color:#039;background-color:#b9c9fe;}
.tg .tg-baqh{text-align:center;vertical-align:top}
.tg .tg-14nr{background-color:#bbdaff;text-align:center;vertical-align:top}
.tg .tg-j0tj{background-color:#D2E4FC;text-align:center;vertical-align:top}
</style>
<br />
<table class="tg">
<tbody>
<tr>
<th class="tg-14nr">Speed (mph)</th>
<th class="tg-14nr">Amplitude (Vpp)</th>
</tr>
<tr>
<td class="tg-j0tj">10</td>
<td class="tg-j0tj">0.6</td>
</tr>
<tr>
<td class="tg-baqh">15</td>
<td class="tg-baqh">1.0</td>
</tr>
<tr>
<td class="tg-j0tj">25</td>
<td class="tg-j0tj">3.6</td>
</tr>
<tr>
<td class="tg-baqh">62</td>
<td class="tg-baqh">4.0</td></tr>
</tbody></table>
<h2>
What the ECM Wants</h2>
Unfortunately for me, the GM ECM expects to see 2000 pulses per mile. And it expects a nice square wave, compatible with 5V logic.<br />
<br />
The former can be managed by a counter/divider or MCU. The latter is easily accomplished with a <a href="https://en.wikipedia.org/wiki/Comparator"><span id="goog_1747423731"></span>comparator<span id="goog_1747423732"></span></a>. But that's only if the original signal amplitude is large enough. It wasn't.
<br />
<br />
My interface circuit would have to amplify the VSS signal first, then convert it to square wave, and finally divide the frequency by 4. But that wasn't all...
<br />
<h2>
AC/DC</h2>
Before I can amplify or really do anything at all, the VSS signal's DC offset needs to be kicked to the curb.<br />
<br />
That way, when using a single-supply op amp, I can use a 1/2 VCC virtual ground (2.5V) and the amplified output will have a 2.5V DC offset that will work nicely with a comparator.<br />
<br />
A series capacitor does this dirty deed dirt cheap. Um. Yeah. Anyway...<br />
<h2>
Amplification</h2>
Below 10 mph, the VSS signal amplitude was too low to work with. A single-supply operational amplifier with a high gain ensures the output reaches the 0-5V rails at all but vehicle speeds below 2mph, which the ECM doesn't care about anyway.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-F8wpDZULC3o/WEdGGW8KC3I/AAAAAAAAWQ8/_vDrh8_HookKXe_W9PQycgF0w7ldxAoEgCLcB/s1600/clipping.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="200" src="https://3.bp.blogspot.com/-F8wpDZULC3o/WEdGGW8KC3I/AAAAAAAAWQ8/_vDrh8_HookKXe_W9PQycgF0w7ldxAoEgCLcB/s320/clipping.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Example of a sine wave clipping</td></tr>
</tbody></table>
At higher speeds and greater sensor signal amplitudes, the output signal maxes out near the rails, cutting off the peaks (see pic, right).<br />
<br />
That's called clipping. For music amplification, clipping sucks. For this signal it doesn't matter since we want a square wave in the end anyway.<br />
<br />
One problem. After building the amplifier circuit and testing a little, the oscilloscope revealed unpredictable voltage spikes when the VSS spun at extremely low speeds.<br />
<h2>
Filtering</h2>
Those spikes, amplified into pulses, would trick downstream logic circuits into reporting a faster-than-actual speed.<br />
<br />
Filtering seemed like a perfectly sensible thing to do, assuming the noise was above the maximum frequency of the VSS signal. I suspected it was.<br />
<br />
So, what was this magical maximum frequency I designed to?<br />
<br />
Figuring on a speed of under, say, 100mph (the Jeep can achieve this only when driving downhill with a subtantial tailwind), one computes the maximum frequency as a unit conversion from pulses per mile to pulses per second (Hz):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-3WNsnMHemoE/WEdJXTV35yI/AAAAAAAAWRM/9lU4_aIl3h0D5tA02JsmC4YwCAI55iz9wCLcB/s1600/equation.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="35" src="https://4.bp.blogspot.com/-3WNsnMHemoE/WEdJXTV35yI/AAAAAAAAWRM/9lU4_aIl3h0D5tA02JsmC4YwCAI55iz9wCLcB/s400/equation.jpg" width="400" /></a></div>
<br />
The target was a low pass filter with a corner frequency around 200Hz. The low pass filter is the same as the amplifier, but with a capacitor in parallel with the feedback resistor. Use the formula below to find C given the feedback resistor R:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-19zdmw9SNeE/WEdIZiRTLHI/AAAAAAAAWRE/BmzhOVEqsR41t2gvJ9LBeVgRrzN_wMMbACLcB/s1600/CodeCogsEqn%2B%25281%2529.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://2.bp.blogspot.com/-19zdmw9SNeE/WEdIZiRTLHI/AAAAAAAAWRE/BmzhOVEqsR41t2gvJ9LBeVgRrzN_wMMbACLcB/s1600/CodeCogsEqn%2B%25281%2529.gif" /></a></div>
<br />
That's all well and good, but why not be a little trickier than that?<br />
<br />
Using a smaller capacitor than calculated, the amplifier frequency response have gain at lower frequencies where the input was low amplitude, and would roll off to attenuate the signal at higher frequencies when the signal reached its maximum, while further attenuating higher frequency noise.<br />
<br />
If you wanted to get fancy, you could carefully measure more input signal data points of frequency vs. amplitude, determine the best low frequency gain and roll off to maintain signal amplitude to tighter tolerances. Or a filter with sharper roll off could be used if noise is a really big problem.<br />
<br />
Sometimes, it doesn't pay to engineer the snot out of stuff. As far as I can tell from testing, as long as the output signal amplitude is over about 2Vp-p across its range of 2-100mph and the noise is somewhat attenuated, then it is good enough for me to get this done so I can work on related projects and get the Jeep through emissions.<br />
<br />
I used a trial and error approach to set the corner frequency until I got a reasonable amplitude at the lower speeds without any noise. I ended up using a 0.1uF capacitor.<br />
<br />
The frequency response of the circuit starts with a gain of around 30dB rolling off gradually to -40dB by 200Hz, according to LTSpice:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-ZadfaGfiKOo/WEdXz8o0TJI/AAAAAAAAWR8/6LDfuLM9M9QMpf8P9nmVR1GeJF9BpRddACLcB/s1600/Screenshot%2Bfrom%2B2016-12-06%2B17%253A26%253A01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="145" src="https://3.bp.blogspot.com/-ZadfaGfiKOo/WEdXz8o0TJI/AAAAAAAAWR8/6LDfuLM9M9QMpf8P9nmVR1GeJF9BpRddACLcB/s400/Screenshot%2Bfrom%2B2016-12-06%2B17%253A26%253A01.png" width="400" /></a></div>
<br />
I also found that a 22-33uF series capacitor on the input worked better than lower values that I tried.<br />
<br />
Notice in the schematic below that the + input is connected to a virtual ground of 2.5V, half VCC, in turn providing a 2.5V DC offset to the output signal.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-_wlOLceNJMw/WEdMZV801FI/AAAAAAAAWRg/3gK5Wiogf6I7fazzpX28GkfABpoJ8cB8QCLcB/s1600/circuit1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://4.bp.blogspot.com/-_wlOLceNJMw/WEdMZV801FI/AAAAAAAAWRg/3gK5Wiogf6I7fazzpX28GkfABpoJ8cB8QCLcB/s320/circuit1.jpg" width="320" /></a></div>
<h2>
Converting to Square Wave</h2>
While a comparator can convert a sine wave to a square wave, in the real world a noisy signal may bounce back and forth across the threshold, causing the comparator to trip when it shouldn't. To prevent this, you need hysteresis.
<br />
<br />
A normal comparator has one voltage threshold. By contrast, a <a href="https://en.wikipedia.org/wiki/Schmitt_trigger">Schmitt Trigger</a> has <i>two</i> thresholds. The high threshold is for rising waveforms, and the low threshold is for falling waveforms. That way, once the signal rises above the high threshold, it has to fall all the way below the low threshold before the output signal changes. And vice versa.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-hG_5WJhrOJg/WE8xMIuzDFI/AAAAAAAAWVs/MnBRzkNNCOAFqM6glxcd7XIY8cVErPHpQCLcB/s1600/hysteresis.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" src="https://1.bp.blogspot.com/-hG_5WJhrOJg/WE8xMIuzDFI/AAAAAAAAWVs/MnBRzkNNCOAFqM6glxcd7XIY8cVErPHpQCLcB/s400/hysteresis.png" width="400" /></a></div>
<br />
Fun fact, you can use an operational amplifier to implement a Schmitt Trigger. So I was able to use one dual op amp IC to implement the filter/amplifier and the Schmitt Trigger.
<br />
<br />
By selecting 100k for all three resistors in the Schmitt Trigger circuit below, you wind up with a high threshold of 2/3 VCC and a low threshold of 1/3 VCC.
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Xr_Rz7X9WCM/WEdLpZvE79I/AAAAAAAAWRc/r0DN9xrrAy0svY8OuTaGM7DcnyAuP6dkACEw/s1600/circuit2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://2.bp.blogspot.com/-Xr_Rz7X9WCM/WEdLpZvE79I/AAAAAAAAWRc/r0DN9xrrAy0svY8OuTaGM7DcnyAuP6dkACEw/s320/circuit2.jpg" width="272" /></a></div>
<h2>
Dividing the Signal</h2>
Rather than using a logic divider or flip-flop, since I didn't have any on hand, I just used a cheap ATtiny25. It probably draws less current anyway. It's definitely more flexible.<br />
<br />
A simple C <a href="https://github.com/shimniok/vssb/blob/master/firmware/vssb.c">program</a> sets up the INT0 pin interrupt to trigger on the rising edge of the conditioned VSS square wave. By toggling the PB3 output pin within the INT0 pin interrupt service routine, and also toggling PB4 every other time the ISR is called, we get a divide-by-2 on PB3 and divide-by-4 on PB4.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-U9a8MvKslCE/WEdSN1A9D6I/AAAAAAAAWRs/6u8CPpSt1S8gm8_IjFPjxzxTKRC_KM8ewCLcB/s1600/Screenshot%2Bfrom%2B2016-12-06%2B17%253A04%253A31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://3.bp.blogspot.com/-U9a8MvKslCE/WEdSN1A9D6I/AAAAAAAAWRs/6u8CPpSt1S8gm8_IjFPjxzxTKRC_KM8ewCLcB/s400/Screenshot%2Bfrom%2B2016-12-06%2B17%253A04%253A31.png" width="400" /></a></div>
<br />
<br />
Skipping every other interrupt for toggling PB4 is achieved by checking bit 0 of a simple count variable. If it's 1, toggle PB4. If I needed to conserve flash and/or execution time I guess this would be a good way, though probably not the most readable method.<br />
<br />
How would you code this? Is there a better way?<br />
<h2>
The Result</h2>
I prototyped the design on a breadboard throughout the development of the circuit, testing it with the VSS I'd pulled from my Jeep. <br />
<br />
Once the output signal seemed reasonable, I reinstalled the VSS and hooked up the breadboard in the Jeep and tested it out driving around. I didn't see any anomalous behavior. Then I hooked up TunerPro to my ECM to display the speed as seen by the ECM.<br />
<br />
My initial revision was a divide-by-2 circuit, so I quickly refactored the code to look as it does above, and voila, correct speed reading.<br />
<br />
The next step was fabricating a prototype circuit board... but more on that in a future post...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-S8FF9AcImCA/WE83QcJnlCI/AAAAAAAAWV4/QimXeQlyBos3id1D2qePRkUx5OIwnzg3wCLcB/s1600/vssb_pcb.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="222" src="https://1.bp.blogspot.com/-S8FF9AcImCA/WE83QcJnlCI/AAAAAAAAWV4/QimXeQlyBos3id1D2qePRkUx5OIwnzg3wCLcB/s320/vssb_pcb.jpg" width="320" /></a></div>
<br />Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-65078914477181110902016-11-28T18:00:00.000-07:002016-11-28T18:00:00.160-07:00Teaching Programming to Kids (Cont'd)After much struggle to select a microcontroller and programming system for teaching programming to my 5th grade club, everything suddenly came together.<br />
<br />
In a flash I remembered PICAXE, which are low cost BASIC-programmable microcontrollers.<br />
<br />
Even better, since all the kids have Chromebooks, PICAXE can be programmed with a Chrome <a href="http://www.picaxe.com/Software/PICAXE/Blockly-for-PICAXE/">App</a> using the Google <a href="https://developers.google.com/blockly/">Blockly</a> graphical programming framework.<br />
<br />
For example, an LED blinky program looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-w18T1AdbJos/WDoMX_W7xJI/AAAAAAAAWCo/jxGdqFVSR40M3Mqhm8GJGs3Uj7N3XswoACLcB/s1600/Screenshot%2Bfrom%2B2016-11-26%2B15%253A27%253A04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://3.bp.blogspot.com/-w18T1AdbJos/WDoMX_W7xJI/AAAAAAAAWCo/jxGdqFVSR40M3Mqhm8GJGs3Uj7N3XswoACLcB/s1600/Screenshot%2Bfrom%2B2016-11-26%2B15%253A27%253A04.png" /></a></div>
<br />
As pointed out by my SHARC pals during a lunch discussion, 5th graders aren't expert typers so this kind of graphical block system sidesteps a lot of frustration. And I feel this system is better suited for robotics than native Scratch is.<br />
<br />
The PICAXE Blockly IDE has some neat features.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-cxDzpkDl4fo/WDoLCEekBVI/AAAAAAAAWCw/Nvhou0HirLcSmt8TGzT_6UQ-72Jq3FS2QCEw/s1600/Screenshot%2Bfrom%2B2016-11-26%2B15%253A15%253A33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="290" src="https://1.bp.blogspot.com/-cxDzpkDl4fo/WDoLCEekBVI/AAAAAAAAWCw/Nvhou0HirLcSmt8TGzT_6UQ-72Jq3FS2QCEw/s400/Screenshot%2Bfrom%2B2016-11-26%2B15%253A15%253A33.png" width="400" /></a></div>
<br />
First, the IDE includes a simulator so kids can run their code safely before trying it on the robots.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-xKwT0068Ty4/WDoMX9D798I/AAAAAAAAWCs/C7SbyL1fTuUI-JpKJUExkxP2ee3SuuwbwCEw/s1600/Screenshot%2Bfrom%2B2016-11-26%2B15%253A27%253A16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://4.bp.blogspot.com/-xKwT0068Ty4/WDoMX9D798I/AAAAAAAAWCs/C7SbyL1fTuUI-JpKJUExkxP2ee3SuuwbwCEw/s1600/Screenshot%2Bfrom%2B2016-11-26%2B15%253A27%253A16.png" /></a></div>
Second, they can see the BASIC language program by clicking a tab so they can get an introduction to standard programming languages.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-1wKi0Lo3zuc/WDoP220yVjI/AAAAAAAAWC8/YDV27VzkcHQwXZwYeUtQ5_2YlFQc6F1FwCLcB/s1600/Screenshot%2Bfrom%2B2016-11-26%2B15%253A42%253A21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://4.bp.blogspot.com/-1wKi0Lo3zuc/WDoP220yVjI/AAAAAAAAWC8/YDV27VzkcHQwXZwYeUtQ5_2YlFQc6F1FwCLcB/s1600/Screenshot%2Bfrom%2B2016-11-26%2B15%253A42%253A21.png" /></a></div>
<br />
My sample PICAXE-14M2 is now successfully blinking an LED on pin B.5. Soon I'll be prototyping motor control using the Pololu DRV8835 driver breakout boards I bought during the Black Friday sale.<br />
<br />
And, I'm almost done designing a baseboard, similar to my <a href="https://www.tindie.com/products/bot_thoughts/put-in-project-duino/">PIPduino</a>, that uses a Pololu step-up/step-down regulator (<a href="https://www.pololu.com/product/2121">S10V4F5</a>) for power, also a Black Friday purchase.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-COGtdy6JrTI/WDoQvWCLRQI/AAAAAAAAWDA/eqZeSv0sAeMqN2iW43aBKLHAeRp-maqKQCLcB/s1600/Screenshot%2Bfrom%2B2016-11-26%2B15%253A45%253A58.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-COGtdy6JrTI/WDoQvWCLRQI/AAAAAAAAWDA/eqZeSv0sAeMqN2iW43aBKLHAeRp-maqKQCLcB/s320/Screenshot%2Bfrom%2B2016-11-26%2B15%253A45%253A58.png" width="201" /></a></div>
<br />
Since the PICAXE programmer cables are too costly for our budget I'm going to find or make cheap FTDI breakout boards or else put an FTDI and USB connector onboard.<br />
<br />
It feels good to have finalized the MCU and programming system for the kids' robots. I'll order the baseboards soon and hopefully I won't have screwed up anything so we can get started programming in 2-3 weeks.<br />
<br />Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-12209224565571195812016-11-23T21:02:00.000-07:002016-11-25T09:35:20.699-07:00Black Friday / Cyber MondayHere's a list of sales you'll want to check out this Holiday:<br>
<br>
<a href="https://www.pololu.com/blackfriday2016">Pololu Black Friday</a> - already started! And an awesome sale with great deals once again. My order is placed :) Switching power supplies, robots (Zumo, 3pi, and the new Romi chassi); IR rangers from Sharp, Polou, and a time-of-flight breakout; wheels, motors, motor controllers, the cool A* ATmega32U4 controllers, and lots, lots more. Plus freebies and free(ish) shipping.<br>
<br>
<a href="https://www.sparkfun.com/news/2236">Sparkfun Black Friday / Cyber Monday</a> - a wide range of items on sale this year. I'm eyeing the ESP8266 boards, MicroView, as well as the HackRF and RockBLOCK Iridium SatComm module. The FLiR Lepton board is on sale too.<br>
<br>
BGMicro has a sale going right now, too. Take 10% off your ENTIRE order by using the code TTM at checkout through 11/23.<br>
<br>
<ul>
<li><a alt="http://www.bgmicro.com/Wholesale-2.aspx" class="gmail_msg" data-saferedirecturl="https://www.google.com/url?q=http://r20.rs6.net/tn.jsp?f%3D0017oBFAfde7ME9eNB09MGtOCbeShzrg7yp_ly5WG-QtaJX1VhSoFNvT50ypXeeIAT92p5hYSmO0uZ1ec8VaPpRDKYXcfIPWw5hl3iSzHNMs6aQK9ZB-1NuRNvtz1Up0UHMAI2Dr8Y-NRFTjWYN1YJcc479EzH8k6HwMgWxXdJBYMnXo2khbQCfIwI5eoduvzIB%26c%3DXDU4YFyMFNjwSfONd5HA8_i4XWVFWhGCxMbIy74xVnqMlDFYzzeI7g%3D%3D%26ch%3DI-H767R5l00ADZcJuXgioNQIQZ8ecgZlDiTkFo1ZD7x09Fs3-0lf7w%3D%3D&source=gmail&ust=1479880848037000&usg=AFQjCNFRtQ4nwUdw7wbsRf6x0edwUjpTDQ" href="http://r20.rs6.net/tn.jsp?f=0017oBFAfde7ME9eNB09MGtOCbeShzrg7yp_ly5WG-QtaJX1VhSoFNvT50ypXeeIAT92p5hYSmO0uZ1ec8VaPpRDKYXcfIPWw5hl3iSzHNMs6aQK9ZB-1NuRNvtz1Up0UHMAI2Dr8Y-NRFTjWYN1YJcc479EzH8k6HwMgWxXdJBYMnXo2khbQCfIwI5eoduvzIB&c=XDU4YFyMFNjwSfONd5HA8_i4XWVFWhGCxMbIy74xVnqMlDFYzzeI7g==&ch=I-H767R5l00ADZcJuXgioNQIQZ8ecgZlDiTkFo1ZD7x09Fs3-0lf7w==" shape="rect" style="background-color: white; color: blue; position: relative; text-align: center; z-index: 0;" target="_blank"><span style="font-family: inherit;">Click Here To Check Out The Items On The Wholesale Page</span></a></li>
<li><a alt="http://www.bgmicro.com/vss.aspx" class="gmail_msg" data-saferedirecturl="https://www.google.com/url?q=http://r20.rs6.net/tn.jsp?f%3D0017oBFAfde7ME9eNB09MGtOCbeShzrg7yp_ly5WG-QtaJX1VhSoFNvTzZpFoGNJx5ice9Oc6X1K60t1nF_jr-jnMEoaucwImeowuj5NkjHpBqoiuEityrz9pehNAQc95j_q4tf_XccW3m0annce6ZQkpl0eKWkXdRPIVVCdCkEIGe4cc83aVzEUg%3D%3D%26c%3DXDU4YFyMFNjwSfONd5HA8_i4XWVFWhGCxMbIy74xVnqMlDFYzzeI7g%3D%3D%26ch%3DI-H767R5l00ADZcJuXgioNQIQZ8ecgZlDiTkFo1ZD7x09Fs3-0lf7w%3D%3D&source=gmail&ust=1479880848037000&usg=AFQjCNFTSZyiYObbV_JkUixwzRS8uXteXw" href="http://r20.rs6.net/tn.jsp?f=0017oBFAfde7ME9eNB09MGtOCbeShzrg7yp_ly5WG-QtaJX1VhSoFNvTzZpFoGNJx5ice9Oc6X1K60t1nF_jr-jnMEoaucwImeowuj5NkjHpBqoiuEityrz9pehNAQc95j_q4tf_XccW3m0annce6ZQkpl0eKWkXdRPIVVCdCkEIGe4cc83aVzEUg==&c=XDU4YFyMFNjwSfONd5HA8_i4XWVFWhGCxMbIy74xVnqMlDFYzzeI7g==&ch=I-H767R5l00ADZcJuXgioNQIQZ8ecgZlDiTkFo1ZD7x09Fs3-0lf7w==" shape="rect" style="background-color: white; color: blue; position: relative; text-align: center; z-index: 0;" target="_blank"><span style="font-family: inherit;">Click Here to Check Out 1/2 Price PICs</span></a></li>
<li><span style="font-family: inherit;"><a alt="http://www.bgmicro.com/pack.aspx" class="gmail_msg" data-saferedirecturl="https://www.google.com/url?q=http://r20.rs6.net/tn.jsp?f%3D0017oBFAfde7ME9eNB09MGtOCbeShzrg7yp_ly5WG-QtaJX1VhSoFNvT37ySKzAKd35m81r3SNosVbyuqD-hX2LesEUGTyXAUMmohOyPKMilfKXMXSeD4Y6o2nX_1MoELG-26KJCF5hbItQAJo_k6OC4sQcKSeSzK63NGIMuiIZEmBiWYtQAE0TBg%3D%3D%26c%3DXDU4YFyMFNjwSfONd5HA8_i4XWVFWhGCxMbIy74xVnqMlDFYzzeI7g%3D%3D%26ch%3DI-H767R5l00ADZcJuXgioNQIQZ8ecgZlDiTkFo1ZD7x09Fs3-0lf7w%3D%3D&source=gmail&ust=1479880848037000&usg=AFQjCNHUz_NuZVOXQjjvU473Ec5LwkcCuA" href="http://r20.rs6.net/tn.jsp?f=0017oBFAfde7ME9eNB09MGtOCbeShzrg7yp_ly5WG-QtaJX1VhSoFNvT37ySKzAKd35m81r3SNosVbyuqD-hX2LesEUGTyXAUMmohOyPKMilfKXMXSeD4Y6o2nX_1MoELG-26KJCF5hbItQAJo_k6OC4sQcKSeSzK63NGIMuiIZEmBiWYtQAE0TBg==&c=XDU4YFyMFNjwSfONd5HA8_i4XWVFWhGCxMbIy74xVnqMlDFYzzeI7g==&ch=I-H767R5l00ADZcJuXgioNQIQZ8ecgZlDiTkFo1ZD7x09Fs3-0lf7w==" shape="rect" style="background-color: white; color: blue; position: relative; text-align: center; z-index: 0;" target="_blank">Click Here To Check Out The Items On The Pack Page</a><span style="background-color: white; color: #403f42; text-align: center;"> </span></span></li>
</ul>
<br>
<div>
Parallax has a sale going too. Save $30 or more on:<br>
<ul class="gmail_msg">
<li class="gmail_msg" style="color: black; font-family: sans-serif; font-size: 13px;"><strong class="gmail_msg"><a class="gmail_msg" data-saferedirecturl="https://www.google.com/url?q=http://crm.parallax.com/sites/all/modules/civicrm/extern/url.php?u%3D964%26qid%3D1662424&source=gmail&ust=1479880839507000&usg=AFQjCNF7ogrNHi4KPbYKHCdc6t_xxII4KA" href="http://crm.parallax.com/sites/all/modules/civicrm/extern/url.php?u=964&qid=1662424" style="color: #7e57c2; position: relative; z-index: 0;" target="_blank">Scribbler 3 (S3) Robot</a></strong></li>
<li class="gmail_msg" style="color: black; font-family: sans-serif; font-size: 13px;"><strong class="gmail_msg"><a class="gmail_msg" data-saferedirecturl="https://www.google.com/url?q=http://crm.parallax.com/sites/all/modules/civicrm/extern/url.php?u%3D965%26qid%3D1662424&source=gmail&ust=1479880839507000&usg=AFQjCNGb3gi0O__5M1uWWoYB0D5YjQR7lg" href="http://crm.parallax.com/sites/all/modules/civicrm/extern/url.php?u=965&qid=1662424" style="color: #7e57c2; position: relative; z-index: 0;" target="_blank">Arduino Shield-Bot Kit</a></strong></li>
<li class="gmail_msg" style="color: black; font-family: sans-serif; font-size: 13px;"><strong class="gmail_msg"><a class="gmail_msg" data-saferedirecturl="https://www.google.com/url?q=http://crm.parallax.com/sites/all/modules/civicrm/extern/url.php?u%3D966%26qid%3D1662424&source=gmail&ust=1479880839507000&usg=AFQjCNGQgUipm1dh0EwilLDIgy6C2yjbiQ" href="http://crm.parallax.com/sites/all/modules/civicrm/extern/url.php?u=966&qid=1662424" style="color: #7e57c2; position: relative; z-index: 0;" target="_blank">BlocklyProp Starter Kit</a> [This looks cool; I'm considering Blockly for my 5th graders] </strong></li>
<li class="gmail_msg" style="color: black; font-family: sans-serif; font-size: 13px;"><strong class="gmail_msg"><a class="gmail_msg" data-saferedirecturl="https://www.google.com/url?q=http://crm.parallax.com/sites/all/modules/civicrm/extern/url.php?u%3D967%26qid%3D1662424&source=gmail&ust=1479880839507000&usg=AFQjCNGNGrNUrk9dO0aOvJwNoe3QR8fFLA" href="http://crm.parallax.com/sites/all/modules/civicrm/extern/url.php?u=967&qid=1662424" style="color: #7e57c2; position: relative; z-index: 0;" target="_blank">ELEV-8 v3 Quadcopter Kit</a></strong></li>
<li class="gmail_msg" style="color: black; font-family: sans-serif; font-size: 13px;"><strong class="gmail_msg"><a class="gmail_msg" data-saferedirecturl="https://www.google.com/url?q=http://crm.parallax.com/sites/all/modules/civicrm/extern/url.php?u%3D968%26qid%3D1662424&source=gmail&ust=1479880839507000&usg=AFQjCNG2yYjT81ZAuEJ02XHexSpl9RQNhQ" href="http://crm.parallax.com/sites/all/modules/civicrm/extern/url.php?u=968&qid=1662424" style="color: #7e57c2; position: relative; z-index: 0;" target="_blank">ELEV-8 v3 Quadcopter Starter Pack</a></strong></li>
</ul><div><font face="sans-serif"><span style="font-size: 13px;">Trossen robotics: </span></font></div><div><span class="gmail_msg" style="color: rgb(96, 96, 96); font-family: Helvetica; font-size: 18.2px; text-align: center; background-color: rgb(255, 255, 255); line-height: 1.6;">We're taking<strong class="gmail_msg" style="color: rgb(255, 0, 0);"> 20% off</strong></span><span class="gmail_msg" style="color: rgb(96, 96, 96); font-family: Helvetica; font-size: 18.2px; text-align: center; background-color: rgb(255, 255, 255); line-height: 1.6;"> <a href="http://trossenrobotics.us5.list-manage1.com/track/click?u=b4f6f5f73e1403fad5ecb77ba&id=3d47808c21&e=ed515ce01e" class="gmail_msg" target="_blank" data-saferedirecturl="https://www.google.com/url?q=http://trossenrobotics.us5.list-manage1.com/track/click?u%3Db4f6f5f73e1403fad5ecb77ba%26id%3D3d47808c21%26e%3Ded515ce01e&source=gmail&ust=1480176240247000&usg=AFQjCNHcUo_9zfinSpzAvb6fcjcc2KmkpA" style="color: rgb(109, 198, 221); word-wrap: break-word;">RobotGeek</a> and <a href="http://trossenrobotics.us5.list-manage.com/track/click?u=b4f6f5f73e1403fad5ecb77ba&id=3fe1d33937&e=ed515ce01e" class="gmail_msg" target="_blank" data-saferedirecturl="https://www.google.com/url?q=http://trossenrobotics.us5.list-manage.com/track/click?u%3Db4f6f5f73e1403fad5ecb77ba%26id%3D3fe1d33937%26e%3Ded515ce01e&source=gmail&ust=1480176240247000&usg=AFQjCNG8SJyKi_27fiyPq400HXVsaVjnSg" style="color: rgb(109, 198, 221); word-wrap: break-word;">Interbotix</a> products</span><span class="gmail_msg" style="color: rgb(96, 96, 96); font-family: Helvetica; font-size: 18.2px; text-align: center; background-color: rgb(255, 255, 255); line-height: 1.6;">!</span><span class="gmail_msg" style="color: rgb(96, 96, 96); font-family: Helvetica; font-size: 18.2px; text-align: center; background-color: rgb(255, 255, 255); line-height: 1.6;"> Use coupon code "<strong class="gmail_msg"><em class="gmail_msg">Friday16</em></strong></span><span class="gmail_msg" style="font-family: Helvetica; font-size: 18.2px; text-align: center; background-color: rgb(255, 255, 255); color: rgb(51, 51, 51); line-height: 1.6;">"</span><span class="gmail_msg" style="color: rgb(96, 96, 96); font-family: Helvetica; font-size: 18.2px; text-align: center; background-color: rgb(255, 255, 255); line-height: 1.6;"> at checkout.</span></div><div><span class="gmail_msg" style="color: rgb(96, 96, 96); font-family: Helvetica; font-size: 18.2px; text-align: center; background-color: rgb(255, 255, 255); line-height: 1.6;"><br></span>
<a href="http://www.newark.com/overstock-warehouse?CMP=e-email-Promo-112316-NA-Overstock&et_cid=28486446&et_rid=885692847&cmp=">Newark</a> has tons of overstock stuff for sale</div>
<br>
<br></div>
Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-19040490990262430272016-11-08T06:00:00.000-07:002016-11-08T06:00:27.320-07:00On Teaching 5th Grade ProgrammingI know that 5th graders can code. <div>
<br /></div>
<div>
The kids last year did a great job programming their Lego Mindstorm NXT robots in LabView, although I witnessed some confusion about the graphical representations. Perhaps Scratch, specifically Scratch 4 Arduino (<a href="http://s4a.cat/">S4A</a>), might be an option. <a href="http://blog.minibloq.org/">MiniBloq</a> would be another possibility.<div>
<div>
<div>
<br /></div>
<div>
Still, I feel it will be easier for kids to learn a syntactically simple text-based language, like Python, Lua, BASIC, or SPIN. Why not C-syntax languages? I have witnessed too much time wasted on syntax problems--semicolons and braces--which I think distracts from core learning.</div>
</div>
<div>
<br />It is a bit of a quandary for me. What do you think?</div>
<div>
<h2>
Microcontroller Board</h2>
</div>
<div>
Low cost is a key factor. We'll have 5-7 teams, so anything that costs $25 or less would be affordable.</div>
<div>
<br /></div>
<div>
With DIP microcontrollers, we could use breadboards (adding slightly to cost) but kids may spend more time troubleshooting loose wires than solving the problem so I'd prefer to build a baseboard.</div>
<div>
<br /></div>
<div>
I am a huge fan of 3-pin servo-style connectors for my robots which greatly simplifies wiring, but non-reversable connectors would be better.</div>
<div>
<br /></div>
<div>
There's always the Raspberry Pi Zero at only $5. The cost is right but the kids may have to learn some basic Linux and we'd need a baseboard with serial or bluetooth. And honestly a Pi is way overpowered for what I have in mind.</div>
<h2>
Python</h2>
<div>
Since learning Python, it feels like the BASIC of the new century. It's way more powerful, but simple to learn the basics, is wildly cross-platform portable, hugely popular, and much more. </div>
<div>
<br /></div>
<div>
And of course I'm a big fan of MicroPython, which powers <a href="http://www.robotshop.com/en/openmv-cam-image-sensor.html">OpenMV Cam</a>.</div>
<div>
<br /></div>
<div>
However, it is quite a challenge to find an suitable microcontroller board with a full MicroPython port that doesn't cost too much. PyBoard is over budget and requires a baseboard too. Teensy 3.5 and is powerful and low cost, but requires a baseboard and the port isn't done yet. </div>
<h2>
Lua</h2>
<div>
Seems good for education and the interpreter is somewhat lightweight like MicroPython but, similarly, choices of microcontroller boards appear to be limited. And Lua is nowhere near as popular as Python so they might struggle more to find help after the club.</div>
<h2>
BASIC</h2>
<div>
This old language lends itself well to education. It's about as lightweight as interpreters come, so I should be able to get a BASIC implementation running on my PIPduino, which would be free for the club (thanks to glacially slow sales).</div>
<div>
<br /></div>
<div>
Coridium's $10 ARM-based <a href="http://www.coridiumcorp.com/prod-specs1.html">BASIC Chip</a>, which is an LPC1114, is designed for embedded use and even the cost of fabbing a baseboard wouldn't kill the budget.</div>
<div>
<br /></div>
<div>
But BASIC isn't standardized across platforms so I'm not sure they could readily apply what they've learned on their PCs like they could with Python or even Lua.</div>
</div>
</div>
Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com3tag:blogger.com,1999:blog-5983110881018172452.post-61858138438887813602016-11-07T12:00:00.000-07:002016-11-07T12:00:08.902-07:00Teaching 5th Grade ElectronicsI'm pleased to say that I'm shaping young minds into engineers, leading an after-school club called <i>Technology - Robotics - Innovation</i> at my daughter's elementary school. <br /><br /><div>
Week 3 is coming up this Thursday.<div>
<br /></div>
<div>
Week 1 was introductory and included a fun bridge-building team exercise where we got to flex our teamwork and problem-solving muscles. </div>
<div>
<br /></div>
<div>
Week 2 the kids had fun learned about electronics: voltage, current, LEDs and built their own LED circuit on breadboards. </div>
<div>
<br /></div>
<div>
Great news: half the kids build non-working circuits. Exactly what I'd hoped for! See, I didn't tell them that LEDs conduct current in one direction so that they could learn through thinking and discovery. </div>
<div>
<br /></div>
<div>
Students came up with great ideas on what might be wrong and some discovered that flipping the battery connections lit the LED (nice!). When I told them about LEDs and diodes, they fixed their circuits by flipping the LEDs.</div>
<div>
<br /></div>
<div>
We'll build on the LED circuit and their light-sensing soldering kit circuit to develop sensors for line following. After Thanksgiving, they'll build team robots. Then we'll start on programming!</div>
</div>
<div>
<br /></div>
<div>
I plan to publish the curriculum and materials as open source / creative commons for others to use and help us improve it.</div>
<div>
<br />This is a dream come true. I've been wanting to teach these things for several years now and I'm super excited to finally have that opportunity!</div>
Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-78818231710919003592016-05-23T17:30:00.000-06:002016-05-23T17:30:07.551-06:00Robot Sumo with Lego MindstormsTwo deadly, plastic opponents face off.<br />
<br />
Their beeps and whirs are drowned out by the deafening cheer echoing in the elementary school auditorium.<br />
<br />
Two robots enter the sumo ring. Only one emerges, victorious. The other? Tipped over, out of the ring, parts scattered, wheels spinning in futility...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-CeuGHzeGhhA/Vz8x4huIlmI/AAAAAAAATt0/GrYCxkNNlDQsnrdKrDBE4R3zCRsZF2oEwCKgB/s1600/IMG_20160415_110706606.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://2.bp.blogspot.com/-CeuGHzeGhhA/Vz8x4huIlmI/AAAAAAAATt0/GrYCxkNNlDQsnrdKrDBE4R3zCRsZF2oEwCKgB/s400/IMG_20160415_110706606.jpg" width="400" /></a></div>
<br />
The culmination of many weeks of tinkering, teams from the Robot Club at my girl's elementary school faced off against teams from two other schools in a Lego Mindstorms Sumo death match (ok, <i>they</i> didn't call it a death match... but I sure will).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-hu4japcvVm8/Vz8z73Gji9I/AAAAAAAATu0/aoEAdHREK6gZqUzawbWQIHmbSChLpvN3gCKgB/s1600/VID_20160415_112010461.mp4" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="223" src="https://4.bp.blogspot.com/-hu4japcvVm8/Vz8z73Gji9I/AAAAAAAATu0/aoEAdHREK6gZqUzawbWQIHmbSChLpvN3gCKgB/s400/VID_20160415_112010461.mp4" width="400" /></a></div>
<br />
For several weeks I came in every week to help the kids design, build, and code their robots and it was incredibly fun and rewarding. Turns out 5th graders are really smart. The kids had no trouble with the LabView-style graphical programming and had working robots quickly.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-W-fgQgaMF_w/Vz81mHjOi3I/AAAAAAAATv4/8Xfwm7PtPiABYYKXtZTr4jlve_n3lExoQCKgB/s1600/VID_20160415_112702299.mp4" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="223" src="https://4.bp.blogspot.com/-W-fgQgaMF_w/Vz81mHjOi3I/AAAAAAAATv4/8Xfwm7PtPiABYYKXtZTr4jlve_n3lExoQCKgB/s400/VID_20160415_112702299.mp4" width="400" /></a></div>
<br />
There were a few physical design issues. Teachable moments in the area of physics were plentiful. Center of gravity. How caster wheels work. Traction, friction. Stuff like that. In the end our school fielded some very competitive robots!<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-L3COaWw88as/Vz82XJt8uYI/AAAAAAAATwQ/EGZzsYCFuqUPESIkPWKGAboEibgLcV16gCKgB/s1600/IMG_20160415_100717574.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://2.bp.blogspot.com/-L3COaWw88as/Vz82XJt8uYI/AAAAAAAATwQ/EGZzsYCFuqUPESIkPWKGAboEibgLcV16gCKgB/s400/IMG_20160415_100717574.jpg" width="400" /></a></div>
<br />
One team started a few weeks behind but was able to build a tracked robot (below) based on some instructions I dug up. They fought against time for weeks and finally, in true robot experimenter fashion, got their code working only days before the competition.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Z6hjzfIDEmo/Vz8zKTPYSEI/AAAAAAAATuc/f1w77QzgX4MBMzXZb6rxaBoVdUMOe2DmgCKgB/s1600/IMG_20160415_100700011.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://1.bp.blogspot.com/-Z6hjzfIDEmo/Vz8zKTPYSEI/AAAAAAAATuc/f1w77QzgX4MBMzXZb6rxaBoVdUMOe2DmgCKgB/s640/IMG_20160415_100700011.jpg" width="360" /></a></div>
<br />
I built one, also. And battled the kids. And lost more matches than I won!<br />
<br />
I guess I am not smarter than a 5th grader.<br />
<br />
But I definitely couldn't be more proud of these kids for sticking to it, never forgetting to have fun, and building some awesome sumo bots.Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com3tag:blogger.com,1999:blog-5983110881018172452.post-3231729742784315462016-03-01T17:00:00.000-07:002016-03-02T10:45:55.983-07:00Sparkfun AVC 2016<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-71R6trIheGY/VtW-tZoQ5yI/AAAAAAAAR8Y/EXU87pWhV74/s1600/DatabusStartLine.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="158" src="https://4.bp.blogspot.com/-71R6trIheGY/VtW-tZoQ5yI/AAAAAAAAR8Y/EXU87pWhV74/s400/DatabusStartLine.jpg" width="400" /></a></div>
<br />
Sparkfun <a href="https://www.sparkfun.com/news/2039">announced</a> that the 2016 Autonomous Vehicle Competition will be happening in September this year!<br />
<br />
That's good. It'll be cooler than summer, almost certainly sunny and a pleasant 70-80 degrees. Plus we have loads of extra time to procrastinate. Win-win!<br />
<br />
The big news is they're making some kind of addition to the AVC involving -- if the pictorial hint is to be believed -- little kids driving around in home made go-karts?!? Or... I really don't know...<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://cdn.sparkfun.com/assets/home_page_posts/2/0/3/9/nimbychibimiku8.gif" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="225" src="https://cdn.sparkfun.com/assets/home_page_posts/2/0/3/9/nimbychibimiku8.gif" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">What does it mean!?!?!?</td></tr>
</tbody></table>
Maybe autonomous road racing? That'd be sweet. Maybe kids will race with bots? Maybe robot kids will... nevermind.<br />
<br />
What about me? Though life has been leaving boot prints on my backside for the better part of the last year, IF the AVC additions are super-interesting, Data Bus may have to make a comeback.<br />
<br />
I haven't forgotten about rovers. In fact, I've been working on some Rover-related goodies in the meanwhile...<br />
<a name='more'></a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-I5P8PMblIaE/VtXKMuJ3-iI/AAAAAAAAR9I/IQ6i4iH6_Fw/s1600/2016-03-01%2B09.41.22.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="161" src="https://2.bp.blogspot.com/-I5P8PMblIaE/VtXKMuJ3-iI/AAAAAAAAR9I/IQ6i4iH6_Fw/s400/2016-03-01%2B09.41.22.jpg" width="400" /></a></div>
<b>RoverPower</b> [<a href="https://github.com/shimniok/RoverPower">github</a>] is ready for field testing. I wanted to eliminate the quirks of Data Bus' old switched regulator and this new design should do so with extreme prejudice.<br />
<br />
It provides rovers with 5V, 1A from an automotive-grade LM2940 5V 1A regulator [<a href="http://www.ti.com/lit/ds/symlink/lm2940c.pdf">datasheet.pdf</a>] with 6-26V input and low dropout voltage. The current design supports up to 3S battery.<br />
<br />
With over-voltage, over-current, over-temperature and reverse polarity protection, not to mention the ability to effortlessly shrug off massive voltage spikes from inductive loads, the LM2940 will survive the worst a Rover can throw at it without breaking a sweat.<br />
<br />
And speaking of which, the board's efficient thermal dissipation design mean you get to use all 1A out of the regulator without thermal overload. Filtering capacitors ensure plenty of clean, stable power for sensitive rover electronics.<br />
<br />
During initial tests, an earlier version of this design solved spurious resets due to motor voltage spikes on my RedBot (Magician) robot. We saw these symptoms on robots entering the Parker <a href="http://www.theroverrally.com/">Rover Rally</a> and Data Bus' old regulator would shut down at odd times. This board should solve all of these issues.<br />
<br />
RoverPower sits between Battery and ESC with 4 pairs of 5V/GND pins for clean, simple wiring.<br />
<br />
This configuration also sets the stage for an I2C-based voltage/current sensor, RoverMeter [<a href="https://github.com/shimniok/RoverMeter">github</a>], based on the <a href="http://www.ti.com/product/INA138">INA138</a> (or INA168) sensor IC that monitors voltage drop across a shunt resistor.<br />
<br />
Where it's different from the rest of the pack is the addition of an ATtiny onboard that will provide signal processing and an I2C interface so you get stable, accurate measurements.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-K5IiFz0Bp6E/Um6rTzGXjpI/AAAAAAAAJfY/XE1kNzvqblw/s1600/RoverMux_DataBus1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="https://4.bp.blogspot.com/-K5IiFz0Bp6E/Um6rTzGXjpI/AAAAAAAAJfY/XE1kNzvqblw/s400/RoverMux_DataBus1.png" width="400" /></a></div>
<br />
Good ol' <b>RoverMux</b> is still <a href="https://www.tindie.com/products/bot_thoughts/rc-rovermux-for-self-driving-robots/">available on Tindie</a> and in its current revision, is as easy as can be to hook up, while remaining dead reliable. It's been saving Data Bus' bacon since 2011.<br />
<br />
My <b>RoverGyro</b> will need a redesign. The chip it was based on was prematurely declared EOL. What to pick instead? A few folks on the DIY Rovers list have been raving about the Bosch BNO055 [<a href="https://www.adafruit.com/datasheets/BST_BNO055_DS000_12.pdf">datasheet.pdf</a>] which is actually a 9DOF IMU system-on-chip featuring a built-in ARM Cortex M0 performing sophisticated sensor fusion.<br />
<br />
I've been tinkering with a few other Rover boards too. Now that the AVC is announced, I guess I better get busy and get these designs finished, tested, and available to buy on Tindie. :)<br />
<br />
Hopefully, too, OpenMV Cam rewards will ship in a few months and go on sale, so folks wanting to employ machine vision will be able to do so.Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com5tag:blogger.com,1999:blog-5983110881018172452.post-88684083349534168992016-02-23T17:00:00.000-07:002016-02-23T17:00:09.305-07:00Chinese Robot KitKids can build robots, sure, but... which robot kit should be their <i>first</i>?<br />
<br />
Lego Mindstorms? Pololu 3pi or Zumo? What would you recommend?<br />
<br />
Every time I show my robots to elementary school kids that are really interested in building their own, I realize that I can't recommend a good starter robot kit. Why not?<br />
<br />
I feel prospective <i>roboteers</i> deserve a kit that is low cost, can do two or more interesting things, is expandable, and is accompanied both by quality learning material and programming software that younger kids can understand and use.<br />
<br />
So, I want to roll my own kit. But, where to start? Maybe... here:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-cEYyWkFL26E/VsyL1HaSkqI/AAAAAAAARv4/oolydr3Upxs/s1600/2016-02-23%2B07.10.13.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://3.bp.blogspot.com/-cEYyWkFL26E/VsyL1HaSkqI/AAAAAAAARv4/oolydr3Upxs/s400/2016-02-23%2B07.10.13.jpg" width="400" /></a></div>
<br />
I searched for a low-cost chassis and found vendors on AliExpress selling a robot kit.<br />
<br />
It includes an Arduino clone, sensor shield, battery holder, motor driver / regulator board, motors, encoder discs, wheels, caster, and an acrylic chassis.<br />
<br />
All for a ridiculously low price. Was it any good? Read on...<br />
<a name='more'></a><h2>
Kit Review</h2>
What you actually get in the kit is a good start, but you'd need some additional bits to make it all work.<br />
<br />
The motors come with wires, mounting brackets and hardware, and encoder discs, but no encoder boards. The chassis and hardware are very similar to the Sparkfun's now-retired <a href="https://www.sparkfun.com/products/retired/10825">Magician robot chassis</a>.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-RZTOscBCUzo/VsyPlep6inI/AAAAAAAARwQ/iLUuwQO6kKM/s1600/2016-02-23%2B09.54.22.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="143" src="https://2.bp.blogspot.com/-RZTOscBCUzo/VsyPlep6inI/AAAAAAAARwQ/iLUuwQO6kKM/s200/2016-02-23%2B09.54.22.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Pan-Tilt bracket</td></tr>
</tbody></table>
There's a pan-tilt head similar to the <a href="https://www.adafruit.com/products/1967">one sold by Adafruit</a>, but the kit includes only one of the required <i>two</i> 90g micro servos. Fortunately I had an extra laying around. The bracket is supposed to clip into some unknown FPV camera, I think.<br />
<br />
The power board includes an H-bridge motor driver IC (L298N) with robust heat sink, and a 7805 linear 5V regulator.<br />
<br />
However, for all the boards, you must supply the standoffs and mounting hardware and you get to drill your own holes, unlike the Magician. A sonar sensor is included, though with no mounting bracket.<br />
<br />
Still, it's a good starting point. Even after you add the missing pieces, total cost should be far south of $100.<br />
<br />
You can always add on Sharp IR distance sensors, line following sensors, a robot firefighting apparatus, a claw, or various other things.<br />
<h2>
Bot Thoughts Electronics?</h2>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-vld9KVwdrag/VsyQHUveraI/AAAAAAAARwc/l2Y7WDRIBO4/s1600/PIPduino1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="159" src="https://3.bp.blogspot.com/-vld9KVwdrag/VsyQHUveraI/AAAAAAAARwc/l2Y7WDRIBO4/s200/PIPduino1.png" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">PIPduino has I2C, SPI, servo headers.</td></tr>
</tbody></table>
For my kit, I think I'd rather use in-house, Bot Thoughts electronics. My Uno-compatible <a href="https://www.tindie.com/products/bot_thoughts/pipduino/?pt=full_prod_search">PIPduino</a> lends itself nicely to robotics, for example.<br />
<br />
My RoverPower regulator board is a good fit, too. I'd need to design a motor driver board, though.<br />
<br />
Or, perhaps source a nice switching supply and motor driver from Pololu instead.<br />
<br />
The basic kit would have to include encoder sensors and some multi-purpose environment sensing. Expansion kits could be added for specific purposes like maze solving, firefighting, sumo, and line following.<br />
<h2>
Programming</h2>
For programming, I know the Lego Mindstorms visual programming language seems to work, because the robot club at my daughter's elementary school use it with success.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-gwGr9oDNoPw/VsyUWR2VPlI/AAAAAAAARw0/OhCIXhyQ1Yo/s1600/ev3.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="143" src="https://2.bp.blogspot.com/-gwGr9oDNoPw/VsyUWR2VPlI/AAAAAAAARw0/OhCIXhyQ1Yo/s400/ev3.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">EV3 Visual Programming</td></tr>
</tbody></table>
<a href="https://scratch.mit.edu/">Scratch</a> for Arduino might be an option; it's for ages 6 and up and is popular, apparently. Or, <a href="https://www.kickstarter.com/projects/791396812/minibloq-graphical-programming-environment-for-ard">Minibloq</a>, the first Kickstarter I backed, developed by folks who regularly teach robotics to kids.<br />
<br />
For older kids I'm thinking <a href="https://micropython.org/">MicroPython</a> would be ideal. Or ... (looks around nervously) ... BASIC, which is what I learned on. The <a href="http://www.coridiumcorp.com/prod-specs1.html">Coridium ARM BASIC</a> chip is an option.<br />
<br />
There is something to be said for simple syntax, no semicolons, and no curly-braces...<br />
<br />
Well, what do you think? Worth doing? What would your kit look like? Let me know in the comments or click the "Contact Me" link on my blog page.Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com1tag:blogger.com,1999:blog-5983110881018172452.post-78695793759748904362015-11-30T07:23:00.004-07:002015-11-30T10:18:20.394-07:00Cyber Monday 2015Updated with lots more sales going on!<br />
<h2>
Tindie.com</h2>
<h3 style="background-color: white; color: #222222; font-family: Helvetica, Arial, sans-serif; line-height: 1em; margin: 0px 0px 10px;">
Bot Thoughts</h3>
<b>20% off - 80CFB16</b><br />
<div style="background-color: white; color: #222222; font-family: Helvetica, Arial, sans-serif; font-size: 14px; line-height: 19px;">
</div>
<ul>
<li><a href="https://www.tindie.com/products/bot_thoughts/eezee-propeller/" rel="nofollow" style="background: transparent; color: #0088cc; cursor: pointer; text-decoration: none; word-wrap: break-word;">eeZee Propeller</a>, an affordable Propeller breakout</li>
<li><a href="https://www.tindie.com/products/bot_thoughts/pipduino/" rel="nofollow" style="background: transparent; color: #0088cc; cursor: pointer; text-decoration: none; word-wrap: break-word;">PIPduino</a>, a 'duino designed to mount in your project</li>
<li><a href="https://www.tindie.com/products/bot_thoughts/the-better-lm386-for-breadboard/" rel="nofollow" style="background: transparent; color: #0088cc; cursor: pointer; text-decoration: none; word-wrap: break-word;">eeZeeAmp LM386</a> with lots of features for breadboarding</li>
<li><a href="https://www.tindie.com/products/bot_thoughts/rc-rovermux-for-self-driving-robots/" rel="nofollow" style="background: transparent; color: #0088cc; cursor: pointer; text-decoration: none; word-wrap: break-word;">RoverMux</a>: take control of your robot rover before it crashes</li>
</ul>
<b>10% off - 6B54471</b><br />
<div style="background-color: white; color: #222222; font-family: Helvetica, Arial, sans-serif; font-size: 14px; line-height: 19px;">
</div>
<ul>
<li>Convenient <a href="https://www.tindie.com/products/bot_thoughts/eezee-power/" rel="nofollow" style="background: transparent; color: #0088cc; cursor: pointer; text-decoration: none; word-wrap: break-word;">eeZeePower USB</a> or <a href="https://www.tindie.com/products/bot_thoughts/5v-breadboard-dc-adapter/" rel="nofollow" style="background: transparent; color: #0088cc; cursor: pointer; text-decoration: none; word-wrap: break-word;">eeZeePower DC Jack</a> versions</li>
<li>ATtiny Boards: <a href="https://www.tindie.com/products/bot_thoughts/eezee-attiny2313-target-board/" rel="nofollow" style="background: transparent; color: #0088cc; cursor: pointer; text-decoration: none; word-wrap: break-word;">ATtiny4313</a>, <a href="https://www.tindie.com/products/bot_thoughts/eezee-attiny244484-breakout/" rel="nofollow" style="background: transparent; color: #0088cc; cursor: pointer; text-decoration: none; word-wrap: break-word;">ATtiny84</a>, <a href="https://www.tindie.com/products/bot_thoughts/eezee-tiny-breakout-programming-board-kit/" rel="nofollow" style="background: transparent; color: #0088cc; cursor: pointer; text-decoration: none; word-wrap: break-word;">ATtiny85/ATtiny13</a></li>
<li><a href="https://www.tindie.com/products/bot_thoughts/eezee-microsd-breakout-4/" rel="nofollow" style="background: transparent; color: #0088cc; cursor: pointer; text-decoration: none; word-wrap: break-word;">eeZeeMicroSD</a>, great for breadboard Arduino and more</li>
<li>Coupons good Monday, Nov 30 thru Friday, Dec 6</li>
</ul>
<h3>
HackARobot</h3>
<ul>
<li><a href="https://www.tindie.com/products/HackARobot/hackabot-nano-arduino-compatible-robot-kit/1">Hackabot Nano</a>, Arduino Robotic Kit, $30 off (coupon code : 8882BB8)</li>
</ul>
<div>
<div style="background-color: white; margin-bottom: 10px;">
<h3>
<b>PartFusion</b></h3>
</div>
<div style="background-color: white; margin-bottom: 10px;">
<div style="background-color: white;">
</div>
<ul>
<li><span style="color: #222222; font-family: "helvetica" , "arial" , sans-serif; font-size: 14px; line-height: 19px;">10% off all products with F3E57A2 from Black Friday to Cyber Monday</span></li>
</ul>
<h3>
<b>FemtoCow</b></h3>
</div>
<div style="background-color: white; margin-bottom: 10px;">
30% off of selected products with 849EA4C<br />
<ul>
<li><a href="https://www.tindie.com/products/FemtoCow/usb912/">usb912</a></li>
<li><a href="https://www.tindie.com/products/FemtoCow/esp8266-ftdi-and-breadboard-adapter-with-33v-reg/">esp8266 breadboard adapter w/ FTDI, 3.3v regulator</a></li>
</ul>
<h3>
Bobricius</h3>
<div>
<b>30% off - 95A4EBB</b><br />
<ul>
<li><a href="https://www.tindie.com/products/bobricius/supercapacitor-powered-arduino-led-wrist-watch/">World's first Supercapacitor powered arduino LED wrist watch</a></li>
<li>R<a href="https://www.tindie.com/products/bobricius/wrist-watch-with-retro-display-an-lir2032-rechargeable-battery/">etro wrist watch with bubble display QDSP 6064</a></li>
<li><a href="https://www.tindie.com/products/bobricius/blue-led-ring-circle-display-wristwatch/">Blue led ring circle display wristwatch</a></li>
</ul>
<b>
</b><b>20% off - CA268CD </b><br />
for all other products etc... best sellers<br />
<ul>
<li><a href="https://www.tindie.com/products/bobricius/255-voice-pcm-sound-generator-8-triggers-16bit-44khz/">255-Voice PCM Sound Generator, 8 triggers, 16bit</a></li>
<li><a href="https://www.tindie.com/products/bobricius/mini-micro-usb-lithium-ion-coin-cell-battery-charger-lir2032-cr2032-replacement/">USB Lithium Ion coin cell battery charger LIR2032</a></li>
<li><a href="https://www.tindie.com/products/bobricius/charlieplex-5x4-charlieplexed-color-0603-led-matrix-for-arduino-micro-lol/">5x4 charlieplexed blue 0603 led matrix</a></li>
</ul>
</div>
<div>
And my flagship, new 2016 model</div>
<div>
<ul>
<li><a href="https://www.tindie.com/products/bobricius/picoduino/">PicoDuino Attiny85 arduino & RGB led</a></li>
</ul>
<h3>
limpkin</h3>
<div style="background-color: white;">
</div>
<ul>
<li><span style="color: #222222; font-family: "helvetica" , "arial" , sans-serif; font-size: 14px; line-height: 19px;">10% off the </span><a href="https://www.tindie.com/products/limpkin/mooltipass-offline-password-keeper/1" style="font-family: Helvetica, Arial, sans-serif; font-size: 14px; line-height: 19px;" target="_blank">Mooltipass</a><span style="color: #222222; font-family: "helvetica" , "arial" , sans-serif; font-size: 14px; line-height: 19px;"> - F8BA799</span></li>
<li><span style="color: #222222; font-family: "helvetica" , "arial" , sans-serif; font-size: 14px; line-height: 19px;">80% off the </span><a href="https://www.tindie.com/products/limpkin/the-whistled-control-your-lights-by-whistling/1" style="font-family: Helvetica, Arial, sans-serif; font-size: 14px; line-height: 19px;" target="_blank">Whistled</a><span style="color: #222222; font-family: "helvetica" , "arial" , sans-serif; font-size: 14px; line-height: 19px;">! - FAC7006</span></li>
<li><span style="color: #222222; font-family: "helvetica" , "arial" , sans-serif; font-size: 14px; line-height: 19px;">50% off the </span><a href="https://www.tindie.com/products/limpkin/esp8266-wifi-module-breakout/5" style="font-family: Helvetica, Arial, sans-serif; font-size: 14px; line-height: 19px;" target="_blank">ESP8266 development board</a><span style="color: #222222; font-family: "helvetica" , "arial" , sans-serif; font-size: 14px; line-height: 19px;"> - FF99903</span></li>
</ul>
<h3 style="background-color: white;">
<span style="color: #222222; font-family: "helvetica" , "arial" , sans-serif;">f0086</span></h3>
<ul>
<li>20% off <a href="https://www.tindie.com/products/f0086/mini-led-cube-smd/" target="_blank">mini LED cube SMD</a> - 33D0B50</li>
</ul>
</div>
</div>
<h3 style="margin: 30px 0px 10px;">
nick64</h3>
<div>
<b>10% discount - 302B08C</b><br />
<div>
<ul>
<li><a href="https://www.tindie.com/products/Nick64/60mm-x-60mm-bi-color-led-matrix-driver-module-diy-kit/" target="_blank">60x60mm bicolor LED matrix driver module diy kit</a> </li>
<li><a href="https://www.tindie.com/products/Nick64/jollibot-arduino-based-line-follower-diy-kit/">jollibot arduino-based line-follower diy kit</a></li>
</ul>
</div>
</div>
<h2 style="margin: 30px 0px 10px;">
Pololu</h2>
<b>Happy Thanksgiving from Pololu! </b>Our <a href="https://www.pololu.com/blackfriday2015">Black Friday/Cyber Monday sale</a> is almost here! We will be offering huge discounts on hundreds of products and automatically upgrading you to the next best price break for everything else. We will also have some great limited-quantity doorbusters. The special offers become active Wednesday morning (November 25) at midnight PST (3:00 AM EST/08:00 UTC), and the sale runs through Cyber Monday (November 30), ending at 11:59 PM PST. More information is coming soon, including a full list of the doorbusters and sale items, but for now, <a href="https://www.pololu.com/blackfriday2015">here is a sampling of the discounts</a>.<br />
<h2>
Sparkfun</h2>
Cyber Monday and all its glory is nearly upon us! For this year's celebration of festively-timed discounts, we're putting a huge selection of some of this year's best-selling and most popular products on sale for one day only – from 10-40% off! Check out our <a href="http://sparkfun.us6.list-manage.com/track/click?u=8f49618731958db855ab7abeb&id=7aaf22e0ba&e=344c0f076c">Cyber Monday page</a> to get the scoop on the day's special deals!<br />
<h2>
GHI Electronics</h2>
<span style="background-color: white; color: #606060; font-family: "helvetica"; font-size: 15px; line-height: 22.5px;">Let's get right to our </span><a href="http://ghielectronics.us9.list-manage.com/track/click?u=a4e51d4ff899124001f2b9e77&id=2bd5391cec&e=c0507e361e" style="background-color: white; color: #007ace; font-family: Helvetica; font-size: 15px; line-height: 22.5px; word-wrap: break-word;" target="_blank">Black Friday through Cyber Monday Sale</a><span style="background-color: white; color: #606060; font-family: "helvetica"; font-size: 15px; line-height: 22.5px;">.</span><br />
<ul style="background-color: white; color: #606060; font-family: Helvetica; font-size: 15px; line-height: 22.5px;">
<li style="margin-left: 15px;"><strong>$1 Holey board</strong></li>
<li style="margin-left: 15px;"><strong>$3 Holey Moley board (limited edition, only available during this sale)</strong></li>
<li style="margin-left: 15px;"><strong>$8.88 mBuino mbed</strong></li>
<li style="margin-left: 15px;"><strong>50% OFF</strong><ul>
<li style="margin-left: 15px;"><strong>Cerberus boards (Cerb40, Cerbuino, Cerberus)</strong></li>
<li style="margin-left: 15px;"><strong>G120HDR</strong></li>
<li style="margin-left: 15px;"><strong>G30HDR</strong></li>
<li style="margin-left: 15px;"><strong>FEZ Spider Mainboard</strong></li>
</ul>
</li>
<li style="margin-left: 15px;"><strong>20% OFF</strong><ul>
<li style="margin-left: 15px;"><strong>FEZ Reaper Tinker Kit</strong></li>
<li style="margin-left: 15px;"><strong>all NETMF Development Systems</strong></li>
<li style="margin-left: 15px;"><strong>all Raspberry PI HATs</strong></li>
<li style="margin-left: 15px;"><strong>BrainPad .NET</strong></li>
</ul>
</li>
</ul>
<strong style="background-color: white; color: #606060; font-family: Helvetica; font-size: 15px; line-height: 22.5px;">The sale is available for 4 days but the stock may not, so place your order now!</strong><br />
<strong style="background-color: white; color: #606060; font-family: Helvetica; font-size: 15px; line-height: 22.5px;"><a href="http://ghielectronics.us9.list-manage.com/track/click?u=a4e51d4ff899124001f2b9e77&id=224b950b03&e=c0507e361e" rel="nofollow" style="color: #007ace; font-weight: normal; word-wrap: break-word;" target="_blank">https://www.ghielectronics.<wbr></wbr>com/catalog/sale</a></strong><br />
<h2>
Electronics Goldmine</h2>
$5 flat rate shipping, lots of things on sale--too many to list.<br />
<br />
Check them out here: <a href="http://www.goldmine-elec.com/">www.goldmine-elec.com</a><br />
<br />
<h2>
Ham Radio Outlet</h2>
Lots of rigs on sale, check them here: <a href="http://hamradio.com/">hamradio.com</a><br />
<br />
<h2>
A Main Hobbies</h2>
<a href="http://www.amainhobbies.com/radio-control-cyber-monday-deals/i140?utm_source=weekly_newsletter_rc&utm_medium=e-mail&utm_campaign=2015-11-29-cyber-monday&utm_content=rc-cars-trucks">Electronic kits 10% off, 15% off all else.</a><br />
<br />
<h2>
Particle</h2>
<table style="background-color: white; border-collapse: collapse; border-spacing: 0px; color: #222222; font-family: arial, sans-serif; font-size: 14px; table-layout: fixed; width: 600px;"><tbody>
<tr><td style="margin: 0px; padding: 0px 32px; vertical-align: top; word-break: break-word; word-wrap: break-word;"><h1 style="color: #9f1b32; font-family: sans-serif; line-height: 44px; margin-bottom: 0px; margin-top: 0px; text-align: center;">
<span style="color: black;"><span style="font-size: small;">Today Only: Particle Sale</span></span></h1>
<h3 style="color: #a3a3a3; font-family: sans-serif; font-weight: normal; line-height: 22px; margin-bottom: 16px; margin-top: 18px; text-align: center;">
<span style="font-size: small;">30% Off Your Favorite Particle Products</span></h3>
</td></tr>
</tbody></table>
<table style="background-color: white; border-collapse: collapse; border-spacing: 0px; color: #222222; font-family: arial, sans-serif; font-size: 14px; table-layout: fixed; width: 600px;"><tbody>
<tr><td style="margin: 0px; padding: 0px 32px; vertical-align: top; word-break: break-word; word-wrap: break-word;"><div style="background-color: #e9e9e9; font-size: 2px; line-height: 2px; margin: 0px auto 24px; width: 60px;">
</div>
</td></tr>
</tbody></table>
<table style="background-color: white; border-collapse: collapse; border-spacing: 0px; color: #222222; font-family: arial, sans-serif; font-size: 14px; table-layout: fixed; width: 600px;"><tbody>
<tr><td style="margin: 0px; padding: 0px 32px; vertical-align: top; word-break: break-word; word-wrap: break-word;"><div style="color: #5d5d5d; font-family: sans-serif; line-height: 24px; margin-bottom: 24px;">
<span style="font-size: x-small;"><span style="color: black;">Ah Cyber Monday—the nerdy younger sibling of Black <span class="aBn" data-term="goog_1170809145" style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dashed; border-bottom-width: 1px; position: relative; top: -2px; z-index: 0;" tabindex="0"><span class="aQJ" style="position: relative; top: 2px; z-index: -1;">Friday</span></span>. It’s one of our favorite shopping holidays here at Particle, and we couldn’t help but to get in on the fun. For today only we're offering </span><a href="http://particle.cmail20.com/t/t-l-ihikdjk-dyldxdyj-y/" style="color: #9f1b32;" target="_blank"><span style="color: black;">30% off</span></a><span style="color: black;"> some of our favorite Particle products like the Internet Button and Photon Kit. The ideal gifts to get for the engineer in your life, your favorite colleague, or, let's be honest...yourself.</span></span></div>
</td></tr>
</tbody></table>
<table style="background-color: white; border-collapse: collapse; border-spacing: 0px; color: #222222; font-family: arial, sans-serif; font-size: 14px; table-layout: fixed; width: 600px;"><tbody>
<tr><td style="margin: 0px; padding: 0px 32px; vertical-align: top; word-break: break-word; word-wrap: break-word;"><div style="font-size: 1px; line-height: 20px;">
</div>
</td></tr>
</tbody></table>
<table style="background-color: white; border-collapse: collapse; border-spacing: 0px; color: #222222; font-family: arial, sans-serif; font-size: 14px; table-layout: fixed; width: 600px;"><tbody>
<tr><td style="margin: 0px; padding: 0px 32px; vertical-align: top; word-break: break-word; word-wrap: break-word;"><div style="margin-bottom: 24px; margin-top: 0px; text-align: center;">
<u></u><a href="http://particle.cmail20.com/t/t-l-ihikdjk-dyldxdyj-j/" style="background-color: #00aedf; border-radius: 3px; color: white; display: inline-block; font-family: Roboto, Tahoma, sans-serif; font-weight: 700; line-height: 24px; padding: 13px 35px 12px; text-decoration: none !important;" target="_blank">Get 30% Off Now</a><u></u></div>
</td></tr>
</tbody></table>
<table style="background-color: white; border-collapse: collapse; border-spacing: 0px; color: #222222; font-family: arial, sans-serif; font-size: 14px; table-layout: fixed; width: 600px;"><tbody>
<tr><td style="margin: 0px; padding: 0px 32px; vertical-align: top; word-break: break-word; word-wrap: break-word;"><div style="font-size: 1px; line-height: 20px;">
</div>
</td></tr>
</tbody></table>
</div>
Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0tag:blogger.com,1999:blog-5983110881018172452.post-90928038300879417442015-09-21T18:00:00.000-06:002017-02-02T18:06:38.848-07:00Fixing Flaky LCD Monitor with ESR Meter<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="margin-left: 1em; margin-right: 1em;">
</div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-1hvnJuEiYts/VgA9JuN6ijI/AAAAAAAAQWg/O_CdZUq5AnM/s1600/IMG_20150921_105105.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://1.bp.blogspot.com/-1hvnJuEiYts/VgA9JuN6ijI/AAAAAAAAQWg/O_CdZUq5AnM/s400/IMG_20150921_105105.jpg" width="400" /></a></div>
<br />
My Dell E2210H LCD monitor was really wonky.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-YSTjJ9Jm1d8/VgA-oc6DVQI/AAAAAAAAQWs/y8A8JyhUbEc/s1600/IMG_20150918_162827841.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="150" src="https://2.bp.blogspot.com/-YSTjJ9Jm1d8/VgA-oc6DVQI/AAAAAAAAQWs/y8A8JyhUbEc/s200/IMG_20150918_162827841.jpg" width="200" /></a></div>
<br />
Powering up from sleep, it would only occasionally come back to life, usually after resetting itself several times.<br />
<br />
More often, it would power off or go into power saving mode, leaving the front panel buttons inoperative.<br />
<br />
Occasionally it would reset or power off while operating normally.<br />
<br />
Here's how I fixed it, using my DIY ESR test harness to find a bad capacitor without desoldering.<br />
<a name='more'></a><br />
You can find disassembly videos and tutorials out on the web specific to your monitor. Remember, safety is your responsibility so please learn how to safely deal with dangerous high voltages, how to safely discharge capacitors, etc.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-8QXKXXkCm68/VgA3ateo3YI/AAAAAAAAQWA/YdJpkqmc-8o/s1600/IMG_20150919_090929738.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="200" src="https://2.bp.blogspot.com/-8QXKXXkCm68/VgA3ateo3YI/AAAAAAAAQWA/YdJpkqmc-8o/s200/IMG_20150919_090929738.jpg" width="150" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Low voltage drop; low ESR.</td></tr>
</tbody></table>
Meanwhile, after disassembling, I used my <a href="http://www.bot-thoughts.com/2009/11/esr-test-harness.html">Equivalent Series Resistance (ESR) Test Harness</a> to identify bad aluminum electrolytic power supply capacitors.<br />
<br />
The device sends a 1.0 Vp-p, 1kHz - 250kHz square wave through a capacitor on the board while the oscilloscope displays voltage drop across the capacitor.<br />
<br />
You turn on the ESR harness, connect Channel 1 to the Probe BNC and Channel 2 to the Trigger BNC.<br />
<br />
Then touch the red/black probes to the positive/negative capacitor terminals while they are on the board. Which is nice; you don't have to desolder every cap.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-5bTuKrhTEXY/VgA3uvMoW2I/AAAAAAAAQWI/xBbMUKWq_H4/s1600/IMG_20150918_162734342.jpg" imageanchor="1" style="clear: right; display: inline !important; margin-bottom: 1em; margin-left: auto; margin-right: auto; text-align: center;"><img border="0" height="200" src="https://2.bp.blogspot.com/-5bTuKrhTEXY/VgA3uvMoW2I/AAAAAAAAQWI/xBbMUKWq_H4/s200/IMG_20150918_162734342.jpg" width="150" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">High voltage drop; high ESR.</td></tr>
</tbody></table>
The harness incorporates a voltage divider, so you can compute ESR based on voltage drop and the harness' series resistance.<br />
<br />
But usually it's really obvious when you find a bad capacitor.<br />
<br />
What you should see is a very low voltage drop across the capacitor as pictured above right.<br />
<br />
A capacitor with overly high ESR will drop far more voltage as shown in the bottom right picture.<br />
<br />
And that is just what I found on one of the 100uF supply capacitors on the main driver board for the LCD. The rest of the capacitors tested ok.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-boufeLVKmVY/VgB4P_XMRcI/AAAAAAAAQXE/jRehtgNslyY/s1600/dell_e221h.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="390" src="https://3.bp.blogspot.com/-boufeLVKmVY/VgB4P_XMRcI/AAAAAAAAQXE/jRehtgNslyY/s400/dell_e221h.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The bad cap looked fine but tested bad.</td></tr>
</tbody></table>
The test harness I built uses a 6.8 ohm series resistor so the ESR is computed as:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://latex.codecogs.com/gif.latex?%5C%5CR%3D%5Cfrac%7BV_R%5Ccdot%206.8%5COmega%7D%7BV_R-V_C%7D%20%5C%5C%20%5C%5CR%3D%5Cfrac%7B0.7V%20%5Ccdot%206.8%5COmega%7D%7B1.0V-0.7V%7D%20%5C%5C%20%5C%5CR%3D%5Cfrac%7B4.76%7D%7B0.3%7D%3D15.9%5COmega" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://latex.codecogs.com/gif.latex?%5C%5CR%3D%5Cfrac%7BV_R%5Ccdot%206.8%5COmega%7D%7BV_R-V_C%7D%20%5C%5C%20%5C%5CR%3D%5Cfrac%7B0.7V%20%5Ccdot%206.8%5COmega%7D%7B1.0V-0.7V%7D%20%5C%5C%20%5C%5CR%3D%5Cfrac%7B4.76%7D%7B0.3%7D%3D15.9%5COmega" /></a></div>
<br />
Normal ESR for good capacitors of similar size are <i>orders of magnitude</i> less than that.<br />
<br />
With such a high ESR, the capacitor was slowly charging and discharging, likely confusing whatever circuit or MCU was controlling the main power.<br />
<br />
After replacing the capacitor with a good one, the monitor works normally, as expected. All with a minimum of work and the very low cost of a capacitor.<br />
<br />
Admittedly, I somehow missed the bad capacitor the first time I tested so I ended up buying a power supply board. Now I have a spare. Oops.Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com2tag:blogger.com,1999:blog-5983110881018172452.post-17587892829043486762015-08-06T08:30:00.000-06:002015-08-06T08:30:00.718-06:00Automated OLED Test Jig: eeZee MicroSDHow does one test boards one sells on Tindie?<br />
<br />
With a fancy-pants, standalone, OLED-equipped, high-zoot test jig, of course.<br />
<br />
At least, that's what I built for testing my <a href="https://www.tindie.com/products/bot_thoughts/eezee-microsd-breakout-4/">eeZee MicroSD</a> boards. They're microSD breakouts for breadboard Arduinos. They've got the microSD socket plus 3.3V regulator, level-shifter IC, and various passives.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-FArjrRDLkF0/VcNng_-lgdI/AAAAAAAAPOY/e58Hsg2QmAA/s1600/uSD_nojumpers.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="http://1.bp.blogspot.com/-FArjrRDLkF0/VcNng_-lgdI/AAAAAAAAPOY/e58Hsg2QmAA/s400/uSD_nojumpers.png" width="400" /></a></div>
<br />
The test jig features an Arduino on the top of a two-layer permanent breadboard apparatus. Power is supplied by convenient USB connector. I manually place the board onto good ol' pogo pins soldered into the D10-D13 positions (plus power and ground).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-FB1EVLdKif4/VTiOOGIj20I/AAAAAAAAMmU/vzNl4rPGmeM/s1600/IMG_20150422_233057.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="http://1.bp.blogspot.com/-FB1EVLdKif4/VTiOOGIj20I/AAAAAAAAMmU/vzNl4rPGmeM/s1600/IMG_20150422_233057.jpg" width="320" /></a></div>
<br />A red and a green LED indicates overall status, while the <a href="http://www.digole.com/index.php?productID=545">Digole Serial OLED</a> displays pass/fail status for each of the tests: initializing the card, creating a file, and removing a file, with an overall board status.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-wc4xGhqNGSE/VTiOT5LrQWI/AAAAAAAAMmk/8YrXM7N5cr8/s1600/IMG_20150422_233143.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="http://3.bp.blogspot.com/-wc4xGhqNGSE/VTiOT5LrQWI/AAAAAAAAMmk/8YrXM7N5cr8/s1600/IMG_20150422_233143.jpg" width="320" /></a></div>
The test takes just a few hundred milliseconds to complete. The test jig is ready to go at any time. Just plug in a USB cable for power I can run through dozens of boards in a few minutes.<br />
<br />
Here's the source code for the test jig: <a href="https://github.com/shimniok/eeZeeMicroSD/blob/master/examples/Arduino/TestJig/TestJig.ino">TestJig.ino</a>Mike Shimniokhttp://www.blogger.com/profile/17602015624941667574noreply@blogger.com0