tag:blogger.com,1999:blog-43206753632398070302024-02-20T20:55:11.198+00:00Electronics and computingHere's some useful or interesting stuff about electronics and computing. Putting it here also helps me to remember what I have done and how I did it!SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.comBlogger11125tag:blogger.com,1999:blog-4320675363239807030.post-3719655125926378212018-12-25T13:54:00.000+00:002018-12-29T16:27:40.610+00:00Coding the BinaryBots Totem sensor board with MakeCode<h3>
Introduction</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZzxvP2mp11PogL8L-TaZkWQlRjQLkcIIOS5gtM28DpDmeEvFPpe8Bu8JCXXUgkNII8xA3JEvE8cCAKQG7NO3GqzeQYsd38oTUupJUn_fL4VgoV-kVXyG1XEt_IrDwJgvQ2o42JH1eyYOm/s1600/IMG_4245.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1200" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZzxvP2mp11PogL8L-TaZkWQlRjQLkcIIOS5gtM28DpDmeEvFPpe8Bu8JCXXUgkNII8xA3JEvE8cCAKQG7NO3GqzeQYsd38oTUupJUn_fL4VgoV-kVXyG1XEt_IrDwJgvQ2o42JH1eyYOm/s320/IMG_4245.JPG" width="240" /></a></div>
CBisEducation is a relative newcomer in the world of STEM
education. Their first products were BinaryBots Dimm and UFO cardboard
robots, a robotic arm and a remote controlled car, all using the BBC
micro:bit. The newest kits in the BinaryBots range are the three Planet Totem kits - Crab, Spider and Tortoise. All three kits have the same electronics –
just the mechanics differ between them.<br />
<br />
The Totem custom-designed boards are made for the BBC micro:bit and comprise a “power board” and a “sensor board”.<br />
<br />
The power board consists of an edge connector into which you
slot the micro:bit, a 3xAAA battery holder, three motor ports (one of which controls the crab's claw) and an
on/off switch. The sensor board contains 4 RGB LEDs, a piezo-sounder, vibration motor, a light-level sensor, temperature sensor and two touch button/slider sensors.<br />
<br />
Whether you follow the instructional videos on the <a class="jive-link-external-small" href="http://www.binarybots.co.uk/" rel="nofollow noopener" target="_blank">BinaryBots website</a>, or use the booklet provided (“Inventors Manual”), the first bit of coding you will do will be to make the claw open and close using the excellent <a class="jive-link-external-small" href="https://makecode.microbit.org/" rel="nofollow noopener" target="_blank">MakeCode for micro:bit</a> block-based coding environment.<br />
<br />
As far as I could see, after that, you are on your own unless you then move to the Python environment as there are no further examples or tutorials using MakeCode. Specifically, there is no way to control any of the components on the sensor board using MakeCode blocks. For younger children, this could be disappointing, as they would want to make use of the sensor board with its RGB LEDs, vibration motor and piezo-sounder. It could be quite a big leap to go straight into Python coding. This blogpost is about how to control the sensor board with MakeCode.<br />
<div>
<br /></div>
<h3>
Hardware description</h3>
<h4>
Power board</h4>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghKp0sgSxBQszZg3fr9OjhJIfGtVPEqEMk_RVB6Hw-tKsXCNbqoPc9NR5bD9LKPG3KT7LxjAQTCz6sCgpXY8hycWTDeg4ehsYP_2QP8J__vM_xS5WcEMPkC-WcskSGfMWu5GncHgoCmkhu/s1600/IMG_4217.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghKp0sgSxBQszZg3fr9OjhJIfGtVPEqEMk_RVB6Hw-tKsXCNbqoPc9NR5bD9LKPG3KT7LxjAQTCz6sCgpXY8hycWTDeg4ehsYP_2QP8J__vM_xS5WcEMPkC-WcskSGfMWu5GncHgoCmkhu/s320/IMG_4217.JPG" width="320" /></a></div>
Rather than use a small servo to control the positioning
of the claw pincers, BinaryBots have opted for a standard DC micro-metal
motor which uses a worm gear to slow it down (and increase torque). One
micro metal motor is provided in the kit. This operates one of the
crab’s claws (the other claw is fixed). Motor port 1 on the
power board connects to pins P13 & P14 on the inserted micro:bit. Pins P19 & P20 on the
micro:bit are reserved for the I2C port. The three
most commonly used pins (P0, P1 AND P2) plus 3V and GND and brought out onto banana plug
terminals at the front of the power board.<br />
<div style="min-height: 8pt; padding: 0px;">
<br />
<table style="width: 100%;"><tbody>
</tbody><caption><b>Power board pin assignments</b></caption><caption><b><br /></b></caption>
<tbody>
<tr>
<th>Board function</th>
<th>Micro:bit pins</th>
</tr>
<tr>
<td>Motor 1</td>
<td>Pins P13 and P14</td>
</tr>
<tr>
<td>Motor 2</td>
<td>Pins P15 and P16</td>
</tr>
<tr>
<td>Motor 3</td>
<td>Pins P8 and P12</td>
</tr>
<tr>
<td>Pin 0 (front of board)</td>
<td>P0</td>
</tr>
<tr>
<td>Pin 1 (front of board)</td>
<td>P1</td>
</tr>
<tr>
<td>Pin 2 (front of board)</td>
<td>P2</td>
</tr>
<tr>
<td>3V (front of board)</td>
<td>3V</td>
</tr>
<tr>
<td>GND (front of board)</td>
<td>GND</td>
</tr>
</tbody>
</table>
<br />
<h4>
Sensor board</h4>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXGxCgpO1ACugqy_UNOMNuiWIJxzgnvW13IIyFKJoAV3VYqDOa1MFAGkIggamdE8nvTHl5Wya3_oDVUaCCkh77EB2t3pxIJPpylHctZiJLoAUVvKX0APtvpB8nBfvmzq5EbufuSpO-Zc-6/s1600/IMG_4216.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXGxCgpO1ACugqy_UNOMNuiWIJxzgnvW13IIyFKJoAV3VYqDOa1MFAGkIggamdE8nvTHl5Wya3_oDVUaCCkh77EB2t3pxIJPpylHctZiJLoAUVvKX0APtvpB8nBfvmzq5EbufuSpO-Zc-6/s320/IMG_4216.JPG" width="320" /></a></div>
The sensor board is actually a standalone microcontroller
board based on the STM32F051K6 ARM Cortex-M0 Access line MCU with 32
Kbytes Flash and 48 MHz CPU. The micro:bit communicates with the sensor
board CPU via I2C and the sensor board CPU, in turn, communicates with
its own sensors and actuators and passes the data back along the I2C bus
to the micro:bit. This means that the micro:bit only needs to know the
I2C address of the board – not of the individual sensors and actuators.
The firmware on the board listens on the I2C bus and sends the data
requested. The touch button/slider sensors are quite complex; along each strip there are three “button points” and 100 “slider points” that can be detected.<br />
<br />
<h3>
Coding with Python</h3>
BinaryBots have provided several examples of Python code on their website [<a class="" href="https://www.binarybots.co.uk/activities" rel="nofollow">https://www.binarybots.co.uk/activities</a>]
to move the crab’s pincers, control the LEDs and read the touch and
temperature sensors. For beginners, I think it would have been more
helpful if BinaryBots had written a Python module with functions to
which you can pass parameters, rather than having to use the ‘struct’
data format, which can be quite complicated.<br />
<div style="min-height: 8pt; padding: 0px;">
<br /></div>
The Python struct.pack function converts all the data into two formats (‘B’ for 8-bit data and ‘H’ for 16-bit data).<br />
<div style="min-height: 8pt; padding: 0px;">
<br /></div>
The board uses command numbers to identify which board device to use. Here are all the valid commands and data expected:<br />
<table align="left" border="1" cellpadding="0" cellspacing="0" style="border: none; margin-left: 6.75pt; margin-right: 6.75pt; padding: 0 5.4pt 0 5.4pt;"><tbody>
<tr><td style="background: white; border-bottom: solid #666666 1.5pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
<strong>Function</strong></div>
</td><td style="background: white; border-bottom: solid #666666 1.5pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
<strong>Command number</strong></div>
</td><td style="background: white; border-bottom: solid #666666 1.5pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
<strong>Data Format</strong></div>
</td></tr>
<tr><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
LED</div>
</td><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
0</div>
</td><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
‘<BBBBB’</div>
</td></tr>
<tr><td style="border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
Buzzer</div>
</td><td style="border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
1</div>
</td><td style="border-bottom: solid #666666 1.0pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
‘<BBH’’</div>
</td></tr>
<tr><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
Vibrator</div>
</td><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
2</div>
</td><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
‘<BHB’</div>
</td></tr>
<tr><td style="border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
Read Buttons</div>
</td><td style="border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
4</div>
</td><td style="border-bottom: solid #666666 1.0pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
‘B’</div>
</td></tr>
<tr><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
Read Sliders</div>
</td><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
5</div>
</td><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
‘B’</div>
</td></tr>
<tr><td style="border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
Read Light sensor</div>
</td><td style="border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
6</div>
</td><td style="border-bottom: solid #666666 1.0pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
‘B’</div>
</td></tr>
<tr><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
Read Temperature sensor</div>
</td><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
7</div>
</td><td style="background: #CCCCCC; border-bottom: solid #666666 1.0pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
‘B’</div>
</td></tr>
<tr><td style="border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
Reset</div>
</td><td style="border-bottom: solid #666666 1.0pt; border-left: none; border-right: solid #666666 1.0pt; border-top: none; border: 1px solid black; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
8</div>
</td><td style="border-bottom: solid #666666 1.0pt; border: 1px solid black; border: none; border: none; padding: 0 5.4pt 0 5.4pt;" valign="top" width="200"><div align="center" style="margin-bottom: .0001pt; text-align: center;">
‘<BB’</div>
</td></tr>
</tbody></table>
<div style="min-height: 8pt; padding: 0px;">
<br /></div>
<div style="min-height: 8pt; padding: 0px;">
<br /></div>
<div style="min-height: 8pt; padding: 0px;">
Where
there is more than one byte, the ‘<’ symbol signifies that the byte
values are to be passed in ‘little-endian’ order. In the case of the
LEDs, the five bytes (BBBBB) are:</div>
<div style="min-height: 8pt; padding: 0px;">
<br /></div>
[0],[LED#], [R], [G], [B].<br />
<div style="min-height: 8pt; padding: 0px;">
<br /></div>
<br />
LED#
can take one of five values (one for each LED or 0 for all four). For
example, to turn all four sensor board LEDs green, the example Python
code is:<br />
<br />
<code>i2c.write(42,struct.pack('<BBBBB',0,0,0,255,0))</code>
<br />
<code><br /></code>
Although the sensor board has a light sensor and temperature
sensor, there is no documentation provided on either sensor, apart from
some sample code. Although BinaryBots provide some sample code for
reading these sensors, they do not provide any information on what the
numbers represent. The Python code for the light and temperature sensors
suggest that the outputs are 16-bit signed integers (0-1023) so it
appears they are analogue sensors. The light sensor is clearly visible
and marked on the board but there is no sign of a temperature sensor. I
conclude that the temperature reading is actually the ARM Core CPU
temperature, which is supported by the fact that placing my finger
directly onto the MCU caused the reading to rise.</div>
<br />
<div style="min-height: 8pt; padding: 0px;">
<br />
Here is the micropython code for reading the temperature sensor and printing
the raw analogue value via the USB cable to the PC console (REPL):<br />
<br />
<code>
from microbit import * </code><br />
<code>import struct </code><br />
<code>sleep(150) </code><br />
<code>while True: </code><br />
<code> temp = i2c.write(42,struct.pack('B',7)) </code><br />
<code> sleep(150) </code><br />
<code> i2c.write(42,struct.pack('B',7)) </code><br />
<code> sleep(10) </code><br />
<code> rx = i2c.read(42,2) </code><br />
<code> temp = rx[0] + (256 * rx[1]) </code><br />
<code> print (temp)
</code>
<br />
<code><br /></code>
Substituting
<code>
(42,struct.pack('B',6))
</code>
with
<code>
(42,struct.pack('B',7))
</code>
will print the raw light sensor readings, instead of the CPU temperature, to the REPL.<br />
<br /></div>
<h3>
Coding with MakeCode</h3>
As discussed in the Introduction, MakeCode does not provide an easy way of controlling the sensor board. Ideally, BinaryBots would have done what other companies like Seeed Studios have done, and produced a MakeCode extension so that blocks were available for controlling the sensors and actuators on the sensor board. Although there are no MakeCode blocks available that can control the sensor board, the MakeCode editor provides a Javascript/Typescript coding window. By converting the Python code into Javascript, it is possible to control the sensor board with MakeCode.<br />
<br />
The equivalent Javascript of the example given above to turn on all four LEDs in a green colour...<br />
<br />
<code>i2c.write(42,struct.pack('<BBBBB',0,0,0,255,0))</code>
<br />
<br />
<div style="min-height: 8pt; padding: 0px;">
is</div>
<br />
<code>
let buf = pins.createBuffer(5); </code><br />
<code> buf.setNumber(NumberFormat.Int8LE, 0, 0); </code><br />
<code> buf.setNumber(NumberFormat.Int8LE, 1, 0); </code><br />
<code> buf.setNumber(NumberFormat.Int8LE, 2, 0); </code><br />
<code> buf.setNumber(NumberFormat.Int8LE, 3, 255); </code><br />
<code> buf.setNumber(NumberFormat.Int8LE, 4, 0); </code><br />
<code> pins.i2cWriteBuffer(42, buf, false);
</code>
<br />
<div style="min-height: 8pt; padding: 0px;">
<br /></div>
The first line sets up a 5 byte buffer ‘buf’. The next five
lines put 5 bytes into the buffer and the last line writes the buffer
‘buf’ to device address 42 on the i2c bus (i.e. the sensor board). Two
of the components on the sensor board (the buzzer and the vibrator)
require 2 bytes plus one word (i.e. 4 bytes in total). In those cases,
the word data format is NumberFormat.Int16LE.<br />
<br />
If you go back to the Blocks window, you'll see something like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHwipAqs6gtlQ6COnrOPpvS7aTMasosindA1K8g86Ru8ELo6U6b98dC_uGLO31H-HgRyfrLb9k4ELu3RCVmaCmxw5PYn71hOlPkD-6w89a2l3V0jm3Vziv9mAIFzk9Mo2W_5K-Jfvd_CKH/s1600/2018-12-25.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="421" data-original-width="429" height="313" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHwipAqs6gtlQ6COnrOPpvS7aTMasosindA1K8g86Ru8ELo6U6b98dC_uGLO31H-HgRyfrLb9k4ELu3RCVmaCmxw5PYn71hOlPkD-6w89a2l3V0jm3Vziv9mAIFzk9Mo2W_5K-Jfvd_CKH/s320/2018-12-25.png" width="320" /></a></div>
<br />
<br />
<div style="min-height: 8pt; padding: 0px;">
As can be seen, the Python code is much more economical. However, I wanted
to use MakeCode because I have not yet found a reliable way to use the <a class="jive-link-external-small" href="http://wiki.seeedstudio.com/Grove-Ultrasonic_Ranger/" rel="nofollow noopener" target="_blank">Grove Ultrasonic Ranger</a> in micropython and I wanted one of those to make my Crab more entertaining. Also, I wanted to show that it is possible!</div>
<div style="min-height: 8pt; padding: 0px;">
<br /></div>
<div style="min-height: 8pt; padding: 0px;">
</div>
To save some typing and make my code more efficient, I created some MakeCode functions out of the
Javascript. Once the Javascript functions have been defined, MakeCode
turns them into blocks, as per the image below, which sends commands to the sensor board to make some noises with the piezo sounder.<br />
<br />
<br />
<br />
<img alt="Close" height="319" id="lb_image" src="https://www.element14.com/community/servlet/JiveServlet/showImage/293607990-2879-647693/2018-12-09+%285%29.png" style="max-height: 100%; max-width: 100%;" width="320" /><br />
<br />
Once you have created the custom blocks and functions that you need using Javascript, you can then go back to the Blocks window and use them with the normal MakeCode blocks. The screenshot below shows a load of my custom blocks/functions combined with other blocks, including Seeed Studio blocks to control their 'ultrasonic ranger', as well as a neopixel ring.<br />
<br />
<img alt="Custom blocks" class="jive-image" height="321" src="https://www.element14.com/community/servlet/JiveServlet/downloadImage/293607990-2879-647688/1250-633/2018-12-10.png" width="640" /><br />
<br />
Although it takes a little while to work out the correct Javascript to do what you want with the sensor board, it can be done! Hopefully, with the information provided here, other people will be able to do the same.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dytSCuMd8IE5hUOWsesD5okqSkXOIvStal6CHoBwfzdZuZ0YL6s3KK2BXNE0dzgf2vi-w8jMWGGIOnx9FUCVA' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>
<br />SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com0tag:blogger.com,1999:blog-4320675363239807030.post-70461998234163567682018-07-12T15:08:00.001+01:002018-07-12T15:08:35.187+01:00Using an I2C backpack with 16 x 2 LCD screen and a micro:bit<h3>
Introduction</h3>
<div>
I couldn't resist buying some I2C LCD backpacks (based on the PCF8574 remote 8-bit i/o expander chip) from China recently as they were less than £1 each. </div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxuYKO1QKAnO8zRYUaOi69Qcf3CxAoK-pVaiInVai_yk8GxEb7mRzgyHFxEhMpHZUkktz3huslrU8yhKJKGtR2-elbpZmjAn7PtKeEocnYh6rBGuIS3KRCLNVsYcTdjGYB5oo0NxAX9HVw/s1600/lcd-backpack-5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1024" data-original-width="1024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxuYKO1QKAnO8zRYUaOi69Qcf3CxAoK-pVaiInVai_yk8GxEb7mRzgyHFxEhMpHZUkktz3huslrU8yhKJKGtR2-elbpZmjAn7PtKeEocnYh6rBGuIS3KRCLNVsYcTdjGYB5oo0NxAX9HVw/s320/lcd-backpack-5.jpg" width="320" /></a></div>
<div>
They work fine at 3.3v as well as 5v, so I thought they'd be great for the 16 x 2 LCD screens that I have <a href="http://www.smythe-consulting.com/2018/04/converting-5v-lcd-displays-to-3v.html" target="_blank">converted to run at 3v</a>. </div>
<div>
<br /></div>
<div>
I then discovered that someone in the Chinese micro:bit community had written an I2C LCD package for MakeCode for micro:bit. That makes everything very easy!</div>
<div>
<br /></div>
<h3>
Connecting things up</h3>
<div>
I usually fit a 16 x 1 female socket to my LCD displays, as this makes it very easy to connect to various backpacks, or indeed to a breadboard using jumper wires. The backpacks I bought came with male header pins already soldered in place so it was just a matter of pushing the backpack into place on the LCD display's female socket strip. Remember that you need to use a 3v LCD screen with the micro:bit.</div>
<div>
<br /></div>
<div>
To connect the backpack to a micro:bit, you need access to the I2C pins by using an edge connector breakout board for the micro:bit such as the <a href="https://www.proto-pic.co.uk/breadbit-breakout-board-for-microbit.html" target="_blank">bread:bit edge connector board from Proto-Pic</a>. Connect the VCC pin on the LCD backpack to 3v on the micro:bit, GND to GND, SCL to pin 19 on the micro:bit
and SDA to pin 20. </div>
<div>
<br /></div>
<h3>
Coding with MakeCode for micro:bit</h3>
<div>
Before you start, you need to know the I2C address for the LCD backpack. By default it is set to 0x3F (hex) or 63 in decimal. You can change its I2C address by shorting out one or more of the three pairs of pads A0, A1 and A2 on the back of the backpack, just below the contrast potentiometer. Refer to Table 5 of the <a href="http://www.nxp.com/products/interface_and_connectivity/i_c/i_c_general_purpose_i_o/series/PCF8574_74A.html" target="_blank">datasheet</a> for details. (n.b. there is a different variant of this backpack whose default address is 0x27. If you have this one, refer to Table 4 instead).<br />
<br />
Armed with that knowledge, now open <a href="https://makecode.microbit.org/" target="_blank">MakeCode</a> on your browser. You will need to "Add Package" from the middle palette, then cut and paste the following URL into the box that appears: <a href="https://github.com/microbit-makecode-packages/i2cLCD1602">https://github.com/microbit-makecode-packages/i2cLCD1602</a> . Now you should see a new (green) entry in the middle palette (I2C_LCD1602) which, if you click on it, reveals the blocks you need to control the LCD. The code below just uses a loop to display an incrementing number on the screen.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSJJl_uh0-EmuTPE42rGPwAaLtDdryg4G4LnhZt4zYZpr4cqd_cbg8MjW-yV6Ltryg5304lMrvgrQkCsQk-w8-PQn2v7RPXrsGOB7iDm-sw1dirZl9P4Pl54T2wRPqD3ZwPIpgkomv7d5I/s1600/2018-07-12.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="692" data-original-width="1600" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSJJl_uh0-EmuTPE42rGPwAaLtDdryg4G4LnhZt4zYZpr4cqd_cbg8MjW-yV6Ltryg5304lMrvgrQkCsQk-w8-PQn2v7RPXrsGOB7iDm-sw1dirZl9P4Pl54T2wRPqD3ZwPIpgkomv7d5I/s640/2018-07-12.png" width="640" /></a></div>
<br /></div>
<div>
<br /></div>
<div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
That's the easiest (and cheapest) way I have found yet to add an LCD screen to a micro:bit.</div>
SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com0tag:blogger.com,1999:blog-4320675363239807030.post-62673599659783429612018-05-27T17:50:00.001+01:002018-05-27T18:53:47.865+01:00Using an 8 x 7-segment SPI display with the BBC micro:bit<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWh3vM7AhYITtTf3LcOLrP24QSj5CE8bhTRuI9am98DxzepSKmcRiJx-AvLxRjb2efgs2mzXT8Y0SFMM0Jpf5CJKcHSSX_U6Pf0yIjX_ZDx-Q9syNuSxv2GX-T9t6l4tvidgVDi9yVsKJU/s1600/IMG_3243.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWh3vM7AhYITtTf3LcOLrP24QSj5CE8bhTRuI9am98DxzepSKmcRiJx-AvLxRjb2efgs2mzXT8Y0SFMM0Jpf5CJKcHSSX_U6Pf0yIjX_ZDx-Q9syNuSxv2GX-T9t6l4tvidgVDi9yVsKJU/s400/IMG_3243.JPG" width="400" /></a></div>
<b><br /></b>
<b>Introduction</b><br />
I got sent ten (!) of these MAX7219 displays by accident when I had ordered something completely different on Aliexpress, so I thought I'd better learn how to use them. As usual, there are some Arduino libraries for these, and also for the Raspberry Pi but I could only find <a href="https://microbit-playground.co.uk/components/7-segment-display-max7219" target="_blank">one example</a> for the BBC micro:bit. That example is a mashup of two different drivers but, when I tried it, I found that quite a lot of things didn't work properly. In any case, I don't really like just plugging in someone else's driver because I prefer to understand what is going on "under the hood". Also, using modules with the micro:bit is a bit of a pain as it's a two-stage process which slows down program development and debugging.<br />
<br />
So, armed with the <a href="https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf" target="_blank">MAX7219 data sheet</a>, I decided to work things out for myself.<br />
<br />
<b>Connecting things up</b><br />
This is very straightforward. The display has five pins. Apart from VCC and GND (it's 3.3v-friendly), there is DIN (data in), CS (chip select) and CLK (clock). As it is an SPI device, the default connections on the micro:bit are CLK=P13 and DIN=P14 (although these are just the defaults - you can use any pins). In addition, you'll need to connect CS to any micro:bit pin (I used P0).<br />
<br />
<b>Micropython code</b><br />
The good news is that testing your connections is easy because the MAX7219 has a test mode which overrides any other settings so it should work every time. The photo above shows "test mode", where each segment of each digit is lit up at full intensity. The MAX7219 is programmed by sending 16 bits, of which the 4 "most significant bits" are ignored, the next 4 bits are the register addresses and the 8 "least significant bits" are the data to send to the registers. Referring to page 10 of the data sheet, to enable the "display test" mode, we need to send the register address 0xXf (where X is "don't care), followed by 0x01. Try this (using the Mu editor):<br />
<br />
<code>from microbit import * </code><br />
<code>spi.init(baudrate=1000000,bits=8,mode=0, sclk=pin13, mosi=pin15, miso=pin14) #setup SPI</code><br />
<code>spi.write(b'\x0f\x01')#enable test mode</code><br />
<br />
Whoa! That doesn't work though. The reason is that (to send anything to the display) you need to take the chip select pin (P0) low, then send the data, then take the chip select pin high again. So, this should work:<br />
<br />
<code>from microbit import * </code><br />
<code>spi.init(baudrate=1000000,bits=8,mode=0, sclk=pin13, mosi=pin15, miso=pin14) # setup SPI</code><br />
<code>pin0.write_digital(0) # take CS pin low</code><br />
<code>spi.write(b'\x0f\x01')# enable test mode </code><br />
<code>pin0.write_digital(1) # take CS high to latch data
</code>
<br />
<code><br /></code>
That's the easy bit! The tricky thing I found about using this display is that (apart from "display test" mode), nothing will work as you want it to, unless you make some essential settings. The data sheet explains what each of the registers is for, but it took me a while to realise that nothing much works unless you configure the following registers as below:<br />
<ul>
<li>Test mode (0x0f) set to off (0x00)</li>
<li>Decode mode (0x09) set to off (0x00)</li>
<li>Set "scan limit" (0x0b) for 8 columns (0x07)</li>
<li>Shutdown register (0x0c) enabled (0x01)</li>
<li>Intensity register (0x0a) set to something between 1 (0x01 - dim) and 15 (0x0f - very bright)</li>
</ul>
<br />
In addition to that problem, some of the settings are persistent after a power-down, so you also need to clear all the registers to start with (because there is no way to find out what the existing settings are). So, the things to remember are that you need:<br />
<br />
<ol>
<li>Before sending commands/data, to take CS low, then send your commands/data, then take CS high.</li>
<li>To clear all the registers so you start with a "clean slate".</li>
<li>To make the essential settings.</li>
<li>Then to send the data you want to display.</li>
</ol>
Here's an example program showing how to put it all together. In the example, I have used a python dictionary to hold the bit patterns for all the numbers and most of the letters of the alphabet.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguH_8o5Uxxv7fNoxpnzraXlkTv2aSPs8PhgnUI0gPwVgP3PLjPen-dY1ITw5KrHvAM_TQp1fQM65DA1dJOPpu26IeSYQDguoIsUCpn5NiG_yGU5kDDv7gX2Fs3VC5UoU2LfmpuuqwO-yPJ/s1600/IMG_3244.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguH_8o5Uxxv7fNoxpnzraXlkTv2aSPs8PhgnUI0gPwVgP3PLjPen-dY1ITw5KrHvAM_TQp1fQM65DA1dJOPpu26IeSYQDguoIsUCpn5NiG_yGU5kDDv7gX2Fs3VC5UoU2LfmpuuqwO-yPJ/s400/IMG_3244.JPG" width="400" /></a></div>
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<code>
from microbit import *<br />
spi.init(baudrate=1000000,bits=8,mode=0, sclk=pin13, mosi=pin15, miso=pin14) #setup SPI<br />
# initialisation code<br />
<br />
pin0.write_digital(0)<br />
spi.write(b'\x0f\x00')#enable normal mode (disable test mode)<br />
pin0.write_digital(1) #latch data<br />
sleep(300)<br />
<br />
# zero-out all registers<br />
for cmd in range(16):<br />
pin0.write_digital(0)<br />
packet = cmd << 8<br />
# e.g., if cmd is 0101, 0101 << 8 becomes 010100000000<br />
spi.write(bytearray(packet))<br />
pin0.write_digital(1)<br />
<br />
# set some essential parameters<br />
pin0.write_digital(0)<br />
spi.write(bytearray([0x09,0x00])) #enable no decode<br />
pin0.write_digital(1)<br />
<br />
pin0.write_digital(0)<br />
spi.write(bytearray([0x0b,0x07])) #enable 8 cols<br />
pin0.write_digital(1)<br />
<br />
pin0.write_digital(0)<br />
spi.write(bytearray([0x0c,0x01])) # enable shutdown register<br />
pin0.write_digital(1)<br />
<br />
<br />
# set intensity<br />
pin0.write_digital(0)<br />
spi.write(b'\x0a\x0f')# set intensity to 15 (max)<br />
pin0.write_digital(1) #latch data<br />
<br />
# dictionary of bit patterns<br />
# n.b. decimal point can be added to any character by adding 128 to its decimal value<br />
DIGITS = {<br />
' ': 0,<br />
'-': 1,<br />
'_': 8,<br />
'\'': 2,<br />
'0': 126,<br />
'1': 48,<br />
'2': 109,<br />
'3': 121,<br />
'4': 51,<br />
'5': 91,<br />
'6': 95,<br />
'7': 112,<br />
'8': 127,<br />
'9': 123,<br />
'a': 125,<br />
'b': 31,<br />
'c': 13,<br />
'd': 61,<br />
'e': 111,<br />
'f': 71,<br />
'g': 123,<br />
'h': 23,<br />
'i': 16,<br />
'j'<span style="white-space: pre;"> </span>: 24,<br />
# 'k'<span style="white-space: pre;"> </span>Can't represent<br />
'l': 6,<br />
# 'm'<span style="white-space: pre;"> </span>Can't represent<br />
'n': 21,<br />
'o': 29,<br />
'p': 103,<br />
'q': 115,<br />
'r': 5,<br />
's': 91,<br />
't': 15,<br />
'u': 28,<br />
'v': 28,<br />
# 'w'<span style="white-space: pre;"> </span>Can't represent<br />
# 'x'<span style="white-space: pre;"> </span>Can't represent<br />
'y': 59,<br />
'z': 109,<br />
'A': 119,<br />
'B': 127,<br />
'C': 78,<br />
'D': 126,<br />
'E': 79,<br />
'F': 71,<br />
'G': 94,<br />
'H': 55,<br />
'I': 48,<br />
'J': 56,<br />
# 'K'<span style="white-space: pre;"> </span>Can't represent<br />
'L': 14,<br />
# 'M'<span style="white-space: pre;"> </span>Can't represent<br />
'N': 118,<br />
'O': 126,<br />
'P': 103,<br />
'Q': 115,<br />
'R': 70,<br />
'S': 91,<br />
'T': 15,<br />
'U': 62,<br />
'V': 62,<br />
# 'W'<span style="white-space: pre;"> </span>Can't represent<br />
# 'X'<span style="white-space: pre;"> </span>Can't represent<br />
'Y': 59,<br />
'Z': 109,<br />
',': 128,<br />
'.': 128,<br />
'!': 176,<br />
}<br />
<br />
def write_segs(col,char): # turn on the relevant segments<br />
pin0.write_digital(0)<br />
spi.write(bytearray([col,char]))<br />
pin0.write_digital(1) #latch data<br />
<br />
def letter(charx): # Look up character in DIGITS dictionary & return<br />
value = DIGITS.get(str(charx))<br />
return value<br />
<br />
def blank_cols(): # blank out all columns<br />
<span style="white-space: pre;"> </span>for col in range(8):<br />
<span style="white-space: pre;"> </span>pin0.write_digital(0)<br />
<span style="white-space: pre;"> </span>spi.write(bytearray([col+1,0x00])) # range is 0-7, cols are 1-8!<br />
<span style="white-space: pre;"> </span>pin0.write_digital(1)<br />
<span style="white-space: pre;"> </span><br />
def write_str(disp_str): #<br />
<span style="white-space: pre;"> </span>len_str=len(disp_str) # find length of string<br />
<span style="white-space: pre;"> </span>if len_str>8:<br />
<span style="white-space: pre;"> </span>len_str=8 #truncate if too long for display<br />
<span style="white-space: pre;"> </span>c=len_str #start column<br />
<span style="white-space: pre;"> </span>for x in range(len_str):<br />
<span style="white-space: pre;"> </span>n=disp_str[x]<br />
<span style="white-space: pre;"> </span>write_segs(c,letter(n))<br />
<span style="white-space: pre;"> </span>c-=1 #Next (i.e. previous) COLUMN<br />
<span style="white-space: pre;"> </span><br />
################################################################ now send some stuff to the display!<br />
while True:<br />
blank_cols() # blank all columns<br />
write_str('Hello ') # write another string<br />
sleep(2000)<br />
write_str('there ') #write a string<br />
sleep(2000)<br />
for displ_count in range(1,500): #count up from 0 to 500<br />
write_str("{0:0=8d}".format(displ_count)) #turn into 8-digit str with leading spaces# <br />
<br />
</code>
SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com0tag:blogger.com,1999:blog-4320675363239807030.post-33653050715670539482018-05-08T14:19:00.002+01:002018-05-08T17:05:48.184+01:00MCP9808 digital temperature sensor, BBC micro:bit and LCD screen<br />
<b>Introduction</b><br />
In my <a href="http://www.smythe-consulting.com/2018/05/using-mcp9808-digital-temperature.html" target="_blank">previous post</a>, I described how to connect and code the Microchip MCP9808 digital temperature sensor. That allowed you to send accurate temperature data via the REPL to a connected PC.<br />
<br />
In this post, I shall describe how to send the temperature readings to an LCD screen. This could be the basis of a BBC micro:bit weather station or data logging project, or maybe for environmental control. N.B. I wasn't going to risk leaving my micro:bit in the freezer long enough to see how cold it really is - apart from anything else, I doubt whether the LCD would be too happy at -18 °C!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhH3-vp4_aDrlw5Wf7s96ZyaHqwHFWIyeIT2-iZ73uR05SjPJTA2sfukE8S6Ux41nLZoLeoRUEiEkDmZoRsWpXtWSwjIM8gE-N5r7vFNsQNWEwP8-jYj_eNepx0Xgp-gy5NznpLTmvT0uJP/s1600/IMG_3223.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhH3-vp4_aDrlw5Wf7s96ZyaHqwHFWIyeIT2-iZ73uR05SjPJTA2sfukE8S6Ux41nLZoLeoRUEiEkDmZoRsWpXtWSwjIM8gE-N5r7vFNsQNWEwP8-jYj_eNepx0Xgp-gy5NznpLTmvT0uJP/s640/IMG_3223.JPG" width="640" /></a></div>
<br />
<br />
I came across a <a href="http://www.multiwingspan.co.uk/micro.php?page=serlcd" target="_blank">post on the excellent MultiWingSpan website</a> showing how the micro:bit could be used with a Sparkfun Serial Enabled 3.3V 16x2 LCD Character Display. Unfortunately, those displays are quite expensive (around £24), whereas you can pick up a parallel (5v) 16x2 LCD screen for as little as £2. If you already have one of the Sparkfun ones or don't mind buying one, you can skip the next section.<br />
<br />
<b>So how can we make our own (cheaper) equivalent to the Sparkfun serial LCD?</b><br />
The microbit is a 3.3v logic level device, so first we need an LCD screen that will work at that voltage. You can either pay a bit more and get a 3.3v screen (but they are hard to come by in the UK), or convert a 5v one, like I described in <a href="http://www.smythe-consulting.com/2018/04/converting-5v-lcd-displays-to-3v.html" target="_blank">an earlier post</a>.<br />
<br />
Next, we need a serial UART LCD backpack like the Sparkfun one. I happened to have bought one from Proto-Pic in a sale last year for the princely sum of £2.40. They have discontinued that now but Hobbytronics in the UK sell <a href="http://www.hobbytronics.co.uk/i2clcd-backpack-v2" target="_blank">a similar one</a> (and, as a bonus, it also works over I2C) for £6. All of these backpacks have their own microprocessor that converts serial UART data into the correct parallel format for any display based on the Hitachi HD44780 compatible interface.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizuC9o7KPpgVlCASU32DFdTKvf4NkZIhnphIQA0eXW8zqw2jsDcvQV1QUArpUZSwXwMd4UYyljeUeUw6abbj6-gaEA86xK73lpjydAQqeFD20Ld8R0tZiUKnjdhdocDmPnZst0OGFR72Ql/s1600/IMG_3226.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizuC9o7KPpgVlCASU32DFdTKvf4NkZIhnphIQA0eXW8zqw2jsDcvQV1QUArpUZSwXwMd4UYyljeUeUw6abbj6-gaEA86xK73lpjydAQqeFD20Ld8R0tZiUKnjdhdocDmPnZst0OGFR72Ql/s400/IMG_3226.JPG" width="400" /></a></div>
<br />
Adding up the cost of components (using the Hobbytronics backpack), it comes to about £9.<br />
<br />
<b>Connecting things up</b><br />
Please refer to my <a href="http://www.smythe-consulting.com/2018/05/using-mcp9808-digital-temperature.html" target="_blank">previous post</a> for connecting and programming the MCP9808 digital temperature sensor. Also have a look at the <a href="http://www.multiwingspan.co.uk/micro.php?page=serlcd" target="_blank">MultiWingSpan post</a> for how to use the serial backpack. Depending on which serial backpack you are using, you may need the datasheet. My Proto-Pic one was very similar to the Sparkfun one but not identical so, if you find characters not appearing on the right row or column, that could be the reason. For convenience, I connected the data connection on my backpack to micro:bit pin 12 - the same as in the MultiWingSpan post.<br />
<br />
<b>Micropython code</b><br />
I borrowed MultiWingSpan's code for the function "move_cursor(row,col)" for sending data to the serial backpack as it is very neat! I have extended it to work with 20x4 displays as well as 16x2. As mentioned above, the Proto-Pic backpack uses a slightly different addressing scheme from the Sparkfun display so I had to adjust the numbers a little as characters weren't coming out in the right place. I also had to look up in the HD44780 datasheet how to send special characters such as the degree symbol ° (decimal 223).<br />
<span style="font-family: monospace;"><br /></span>
<span style="font-family: monospace;">from microbit import *</span><br />
<span style="font-family: monospace;"># set up screen</span><br />
<span style="font-family: monospace;"># command set 1</span><br />
<span style="font-family: monospace;">bl_full = [254,02,255] # backlight on full</span><br />
<span style="font-family: monospace;">bl_half = [254,02,80] # half</span><br />
<span style="font-family: monospace;">bl_off = [254,02,0] # off</span><br />
<span style="font-family: monospace;">clr_scr = [254,01]</span><br />
<span style="font-family: monospace;"><br /></span>
<span style="font-family: monospace;"># command set 2</span><br />
<span style="font-family: monospace;">col_wide = [124,03] # 20 cols</span><br />
<span style="font-family: monospace;">col_narr = [124,04] # 16 cols</span><br />
<span style="font-family: monospace;">rows_four = [124,05] # 4 rows</span><br />
<span style="font-family: monospace;">rows_two = [124,06] # 2 rows</span><br />
<span style="font-family: monospace;"><br /></span>
<span style="font-family: monospace;"># choose cursor position - row 0,1,2,3 col, 0-15</span><br />
<span style="font-family: monospace;">def move_cursor(row,col):</span><br />
<span style="font-family: monospace;"> cmd = [254,128]</span><br />
<span style="font-family: monospace;"> if row==1:</span><br />
<span style="font-family: monospace;"> cmd[1]+=16 # adds 16 to cmd and stores it back</span><br />
<span style="font-family: monospace;"> if row==2:</span><br />
<span style="font-family: monospace;"> cmd[1]+=40 # adds 40 to cmd and stores it back</span><br />
<span style="font-family: monospace;"> if row==3:</span><br />
<span style="font-family: monospace;"> cmd[1]+=60 # adds 60 to cmd and stores it back</span><br />
<span style="font-family: monospace;"> cmd[1]+=col </span><br />
<span style="font-family: monospace;"> uart.write(bytes(cmd))</span><br />
<span style="font-family: monospace;"> sleep(100)</span><br />
<span style="font-family: monospace;"> </span><br />
<span style="font-family: monospace;"># define character code for degree symbol</span><br />
<span style="font-family: monospace;">deg_symb = [223] #from HD44780 datasheet</span><br />
<span style="font-family: monospace;"><br /></span>
<span style="font-family: monospace;"># wait half a second for the splash screen</span><br />
<span style="font-family: monospace;">sleep(500)</span><br />
<span style="font-family: monospace;"><br /></span>
<span style="font-family: monospace;"># initialise uart</span><br />
<span style="font-family: monospace;">uart.init(baudrate=9600, bits=8, parity=None, stop=1, tx=pin12)</span><br />
<span style="font-family: monospace;">uart.write(bytes(rows_two)) # set 16x2</span><br />
<span style="font-family: monospace;">uart.write(bytes(col_narr)) # set 16x2</span><br />
<span style="font-family: monospace;">uart.write(bytes(bl_full)) # set backlight to full brightness</span><br />
<span style="font-family: monospace;"><br /></span>
<span style="font-family: monospace;"># clear display</span><br />
<span style="font-family: monospace;">move_cursor(0,0)</span><br />
<span style="font-family: monospace;">uart.write(bytes(clr_scr))</span><br />
<span style="font-family: monospace;">sleep(200)</span><br />
<span style="font-family: monospace;"><br /></span>
<span style="font-family: monospace;"><br /></span>
<span style="font-family: monospace;"># wait half a second for the splash screen</span><br />
<span style="font-family: monospace;">sleep(500)</span><br />
<span style="font-family: monospace;"><br /></span>
<span style="font-family: monospace;"># n.b. cursor is still at 0,0</span><br />
<span style="font-family: monospace;">uart.write("Temp.: ")</span><br />
<span style="font-family: monospace;"># sleep(50)</span><br />
<span style="font-family: monospace;">move_cursor(0,11)</span><br />
<span style="font-family: monospace;">uart.write(bytes(deg_symb))</span><br />
<span style="font-family: monospace;">uart.write("C")</span><br />
<span style="font-family: monospace;">move_cursor(1,0)</span><br />
<span style="font-family: monospace;">uart.write("This is line two") # change this to something useful!</span><br />
<span style="font-family: monospace;">move_cursor(0,7) #move cursor back again for temp. data</span><br />
<span style="font-family: monospace;">##########################################################</span><br />
<span style="font-family: monospace;"># Measure temperature and send to LCD</span><br />
<span style="font-family: monospace;">while True:</span><br />
<span style="font-family: monospace;"> i2c.write(0x18,bytearray([0x05])) # select ambient temp. register</span><br />
<span style="font-family: monospace;"> sleep(200) # small pause needed to allow the device to update</span><br />
<span style="font-family: monospace;"> t_amb=i2c.read(0x18,2) # read two bytes from the ambient temp. register</span><br />
<span style="font-family: monospace;"> t_hi = (t_amb[0] & 0x0f) << 4 # mask upper 4 bits (alarm settings)</span><br />
<span style="font-family: monospace;"> t_lo = t_amb[1] / 16 # lower 8 bits (LSB)</span><br />
<span style="font-family: monospace;"> if t_amb[0] & 0x10 == 0x10: # take twos complement for -ve readings</span><br />
<span style="font-family: monospace;"> temp = (t_hi + t_lo) - 256.0</span><br />
<span style="font-family: monospace;"> else:</span><br />
<span style="font-family: monospace;"> temp = t_hi + t_lo</span><br />
<span style="font-family: monospace;"> uart.write('%.1f' % temp) # display to 1 dec place</span><br />
<span style="font-family: monospace;"> move_cursor(0,7) #move cursor back again ready for next reading</span><br />
<span style="font-family: monospace;"> sleep(500) </span><br />
<span style="font-family: monospace;">uart.init(115200) # reset uart</span>SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com0tag:blogger.com,1999:blog-4320675363239807030.post-48499789340537205032018-05-07T19:54:00.000+01:002018-07-12T11:40:56.793+01:00Using an MCP9808 digital temperature sensor with the BBC micro:bit<b>Introduction</b><br />
<a href="https://makecode.microbit.org/">MakeCode </a>for the BBC micro:bit has a Temperature block that provides a temperature reading in °C. There are two problems with this:<br />
<ol>
<li>The block infers the temperature from the temperature of the silicon die on the main CPU. This tends to overestimate the temperature due to self-heating of the CPU and the degree of self-heating depends on the load on the processor. Typically, the reading will be 3 degrees Celsius higher than the ambient temperature.</li>
<li>You might, in any case, want to monitor the temperature somewhere other than where the micro:bit is situated (e.g. outside).</li>
</ol>
<div>
The way around this is to connect a separate temperature sensor to the micro:bit. Several tutorials exist for doing this, but the ones I have seen are all for analogue devices such as the TMP36 where the voltage output by the sensor is proportional to the temperature. These are relatively low-precision devices (accurate to ±1°C at +25°C).</div>
<div>
<br /></div>
<div>
I wanted to use a more accurate, digital temperature sensor and found the Microchip MCP9808, which has a typical accuracy ±0.25°C from -40°C to +125°C. This uses the I2C bus and works over a 2.7V ~ 5.5V logic level voltage range. Another possibility would be the Maxim/Dallas DS18B20, which uses a "one-wire protocol" but it takes at least 0.75 secs for each conversion, plus I haven't found out an easy way of using this with the micro:bit. In contrast, the MCP9808 is very easy to use, has a conversion time of only 250ms at maximum resolution and doesn't interfere with any other devices. The easiest way to use it with the micro:bit is to buy a breakout board that exposes its connections via breadboard-friendly pins. Adafruit makes a popular <a href="https://learn.adafruit.com/adafruit-mcp9808-precision-i2c-temperature-sensor-guide/overview">MCP9808 breakout board</a> but I just went for a cheap Chinese board (costing around £2.50) as they do the same job.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLgGXATkytlKPWZR6s8EfqEwKhTKNOgPa9LsZlHkVxdgGHUyTWkVtSg4lxTN_08u1AAIGpa5FKx_gI8YrJjUh96zL_vDpCtvXmRp9CThnsL3_-yeeyzbWya1R_ZtIo2_qf_Gt5byvujYkA/s1600/IMG_3221.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1200" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLgGXATkytlKPWZR6s8EfqEwKhTKNOgPa9LsZlHkVxdgGHUyTWkVtSg4lxTN_08u1AAIGpa5FKx_gI8YrJjUh96zL_vDpCtvXmRp9CThnsL3_-yeeyzbWya1R_ZtIo2_qf_Gt5byvujYkA/s640/IMG_3221.JPG" width="480" /></a></div>
<br /></div>
<div>
The I2C protocol is a serial protocol that allows different data to be sent to different devices along a "bus". Each device must have its own address on the bus. The sensor has a default I2C address of 0x18 but this can be changed to one of seven other addresses by connecting one of more of the additional connections A0, A1, A2 to VCC, via the inbuilt resistors. This would allow you to have up to eight temperature sensors running from the same micro:bit, and controlled separately. See my previous blog posts on using I2C devices with the micro:bit.<br />
<br /></div>
<div>
<b>Connecting the sensor to the micro:bit</b></div>
<div>
Although there are eight pins, we need just four connections to get going. </div>
<div>
<br /></div>
<div>
VCC to 3v on the micro:bit, GND to GND, SCL to pin 19 on the micro:bit and SDA to pin 20. To access pins 19 and 20, we need an edge connector breakout board for the micro:bit such as the <a href="https://www.proto-pic.co.uk/breadbit-breakout-board-for-microbit.html" target="_blank">bread:bit edge connector board from Proto-Pic</a>.<br />
<br /></div>
<div>
The other four pins on the MCP9808 are for changing its I2C address and a pin that can be used as a trigger if, for example, certain temperature thresholds are reached. </div>
<div>
<br /></div>
<div>
The micro:bit already has internal pullup resistors on the I2C lines so no other components are needed if you just want to measure temperature and are happy with the default I2C address.</div>
<div>
<br /></div>
<div>
<b>Configuration</b></div>
<div>
By default, the sensor's I2C address is 0x18, its resolution is +0.0625°C and its conversion time is 250ms. For the purposes of this guide, we will leave these defaults as they are.</div>
<div>
<br /></div>
<div>
<b>Micropython code</b><br />
You'll need the excellent <a href="https://codewith.mu/">Mu editor </a>for this, and you must also have REPL set up because we will be sending the temperature readings from the micro:bit via the programming cable to the REPL window in Mu. If you have Windows PC, you'll need to install the driver for this (download it from the Mu website).<br />
<br /></div>
<div>
The micro:bit implementation of I2C in micropython is slightly different from other boards. The I2C bus does not need to be separately initialised unless the default parameters are unsuitable for the device you wish to connect. As with some other I2C devices, the required configuration registers are written to, then data is read from the bus. In our case, we tell the MCP9808 what function we require by an "i2c.write" to the relevant register address (in this case 0x05 - the ambient temperature register). Then we "i2c.read" a specified number of bytes.<br />
<br />
The <a href="http://ww1.microchip.com/downloads/en/DeviceDoc/25095A.pdf" target="_blank">datasheet</a> for the MCP9808 explains that the temperature is stored as two bytes (one word). The digital word is loaded to a 16-bit read-only ambient temperature register that contains 13-bit temperature data in two’s complement format. The other 3 bits (i.e. bits 7, 6 and 5 of the first byte) contain the alert temperature settings. Bit 4 of the first byte is the sign (i.e. positive or negative Celsius value). If bit 4 is 1, the temperature is negative. This is equivalent to saying that negative temperatures will be represented as 256 plus the numeric value of bits 3 to 0 of the first byte. The second byte of the word contains the 8 least significant bits of the ambient temperature.<br />
<br />
Before you flash the code to the micro:bit, remember to click the REPL button so that you can see the data coming back from the micro:bit.<br />
<br />
<br /></div>
<span style="font-family: monospace;">from microbit import *</span><br />
<span style="font-family: monospace;">sleep(1000)</span><br />
<span style="font-family: monospace;">while True:</span><br />
<span style="font-family: monospace;"> i2c.write(0x18,bytearray([0x05])) # select ambient temp. register</span><br />
<span style="font-family: monospace;"> sleep(100) # small pause needed to allow the device to update</span><br />
<span style="font-family: monospace;"> t_amb=i2c.read(0x18,2) # read two bytes from the ambient temp. register</span><br />
<span style="font-family: monospace;"> t_hi = (t_amb[0] & 0x0f) << 4 # mask upper 4 bits (alarm settings)</span><br />
<span style="font-family: monospace;"> t_lo = t_amb[1] / 16 # lower 8 bits (LSB)</span><br />
<span style="font-family: monospace;"> if t_amb[0] & 0x10 == 0x10: # take twos complement for -ve readings</span><br />
<span style="font-family: monospace;"> temp = (t_hi + t_lo) - 256.0</span><br />
<span style="font-family: monospace;"> else:</span><br />
<span style="font-family: monospace;"> temp = t_hi + t_lo</span><br />
<span style="font-family: monospace;"> print('%.2f' % round(temp,2)) # round and print to 2 places</span><br />
<span style="font-family: monospace;"> sleep(500)</span><br />
<br />
<div>
In <a href="http://www.smythe-consulting.com/2018/05/mcp9808-digital-temperature-sensor-bbc.html" target="_blank">my next post</a>, I will describe how to connect a <a href="http://www.smythe-consulting.com/2018/04/converting-5v-lcd-displays-to-3v.html" target="_blank">3V LCD screen</a> instead of sending the temperature readings to the REPL. Then you will have a stand-alone digital thermometer which can be used, for example, as part of a weather station project, data logging, or for environmental control.</div>
SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com0tag:blogger.com,1999:blog-4320675363239807030.post-79840635883570424172018-04-21T20:03:00.000+01:002018-05-08T16:40:24.408+01:00Converting 5v LCD displays to 3vI wanted to attach a 16x2 LCD screen to a BBC micro:bit, which uses 3.3v logic (like the Raspberry Pi). You can buy 3.3v LCD screens, but they are a bit pricey and not so easy to find. A very cheap and ubiquitous 5v LCD is the "QAPASS 1602a 16x2". I don't know who makes these things and QAPASS may just refer to some quality control test, rather than the device itself. The important thing is that under one of the black "blobs" is a Hitachi HD44780 display controller.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeF_uj53C3FX3ym9DzS2tPDc4q7K4GKp1phyo9A47wpoYsgQIhH0poDKzZs-QitZRLPTv16JcCjLXEl7x0wks4BnSgsVMJIVKFqKNR7bx7I4qixMVPWR0TSv0D3oTMAZ0MjSnMOuRwqcwC/s1600/IMG_3051.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="801" data-original-width="1600" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeF_uj53C3FX3ym9DzS2tPDc4q7K4GKp1phyo9A47wpoYsgQIhH0poDKzZs-QitZRLPTv16JcCjLXEl7x0wks4BnSgsVMJIVKFqKNR7bx7I4qixMVPWR0TSv0D3oTMAZ0MjSnMOuRwqcwC/s320/IMG_3051.JPG" width="320" /></a></div>
<br />
<br />
These displays have unused pads on them including space for an 8-pin voltage convertor chip. This is designed to allow a 3v version of the display to be sold by adding a negative voltage generator to the LCD module.<br />
<br />
Converting the module to 3v is simply a matter of soldering the correct voltage converter chip (ICL7660) onto the space provided, plus two 10µF capacitors across pads marked C1 and C2. Finally, the voltage convertor needs to be enabled by soldering across the jumper J3 and removing the solder from across J1.<br />
<br />
The <a href="http://cpc.farnell.com/intersil/icl7660cbaz/volt-converter-smd-7660-soic8/dp/SC10339" target="_blank">ICL7660 voltage convertor</a> can be obtained for around £1. Finding the correct capacitors in small quantities was a bit more tricky. Unless you have access to some already, you'll probably have to buy 100. <a href="http://cpc.farnell.com/multicomp/mcca000554/mlcc-1206-y5v-10v-10uf/dp/CA06718" target="_blank">Here</a> are some suitable ones.<br />
<br />
It can be seen that the voltage converter and additional capacitors are surface-mounted. Although they are more fiddly to solder than "through the hole" components, a normal soldering iron is fine (preferably with a fine tip). The technique I used is to melt some solder onto one of the pads for the 7660 chip then, holding the chip down against the pads, apply the soldering iron briefly to the relevant leg of the chip to remelt it and "fix" the chip in position. At this stage it is important to ensure that the chip is correctly aligned with all of the pads. If not, repeat this procedure until it is. Now, the other 7 legs of the chip can be soldered in place by heating each leg in turn and then dabbing the solder onto the leg. Miraculously, the solder will flow onto the pad and over the leg! Once you have soldered all 8 legs, check that there are no "bridges" between adjacent legs. If there are, a quick dab with the soldering iron should sort it out - if not, hold some solder wick between the affected bridge and the soldering iron and that should suck up the excess.<br />
<br />
Now use a similar process for each of the capacitors; melt some solder onto one of the pads, hold the capacitor in place on top of it, then reapply the soldering iron to melt it into position. Check the alignment, and repeat.<br />
<br />
To remove the solder from J1, heat up the pad with the soldering iron and either use a "solder sucker" or a piece of solder wick until there is a clear gap between each half of the pad J1. Then heat up the J3 pad and apply some solder to bridge the gap.<br />
<br />
Job done!<br />
<br />
The picture below shows the new components in place as well as the change in jumper position. You'll also notice that I have soldered a 16-way socket onto the back of the LCD circuit board. I find that this makes it easy to connect serial/parallel convertor boards if required.<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaPzmhtbc-VnuTgmkNbulIIZv6c8TYoxREId3sV3buSg-XSWCvBn8KV-qQJ-oUdbKpoJTcnZJlcuF0DAU6S2oxNzXV2aY_zXdol3WDlYkhhTUOsrlu3lRjueB7JgJeii7JkQifWKx5ZMth/s1600/IMG_3050.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="821" data-original-width="1600" height="164" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaPzmhtbc-VnuTgmkNbulIIZv6c8TYoxREId3sV3buSg-XSWCvBn8KV-qQJ-oUdbKpoJTcnZJlcuF0DAU6S2oxNzXV2aY_zXdol3WDlYkhhTUOsrlu3lRjueB7JgJeii7JkQifWKx5ZMth/s320/IMG_3050.JPG" width="320" /></a></div>
<br />SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com1tag:blogger.com,1999:blog-4320675363239807030.post-9382930165865929102017-05-29T15:31:00.001+01:002018-01-07T19:00:57.581+00:00Tutorial - using Amazon Dash with a Pi Zero and node.js <h2>
Introduction<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOGZ4vSo-RFKZ95uxOdEztVm-eoUS7t2qZPPI5KU3Wd3vJyq82wqpGVj1YzUr5QXBb84plg8gkmXpvas40o8UtD5Dgq4SrDM84XKZT947HYWPu4QtvF3tlp6LSUDHQL6Wl0tW4d-nJpZYx/s1600/IMG_1201%255B1%255D.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOGZ4vSo-RFKZ95uxOdEztVm-eoUS7t2qZPPI5KU3Wd3vJyq82wqpGVj1YzUr5QXBb84plg8gkmXpvas40o8UtD5Dgq4SrDM84XKZT947HYWPu4QtvF3tlp6LSUDHQL6Wl0tW4d-nJpZYx/s320/IMG_1201%255B1%255D.JPG" width="320" /></a></div>
</h2>
<div>
In <a href="https://www.raspberrypi.org/magpi/issues/57/" target="_blank">MagPi Magazine (issue 57)</a> there was an article about how you can subvert an Amazon Dash button and use it as a Internet of Things ("IoT") button. However, the code in the article had several syntax errors. Once I fixed the syntax errors I still couldn't get it to work with my button. Then I found that a key ingredient, tcpdump (needed by scapy), was missing - so I installed that too. I ended up with some working code but it wasn't very reliable, especially using wireless on the Raspberry Pi Zero W. After doing a bit of research and experimentation, I got it working better by using "dasher", a node.js script.</div>
<div>
<br /></div>
<div>
<a href="http://nodejs.org/" target="_blank">Node.js</a> is a server-based runtime environment for executing JavaScript code. It allows non-blocking multi-threading applications to be deployed. This is a fancy way of saying that you can leave it running in the background on your computer (Raspberry Pi in this case) and it will just sit there listening until something triggers it (without consuming lots of processing power).<br />
<br />
A companion application, NPM, is a package manager for node.js scripts.<br />
<br />
As in the MagPi article, dasher would allow the Dash button to toggle an IoT bulb such as the <a href="https://www.lifx.com/" target="_blank">Lifx</a> or <a href="http://www2.meethue.com/en-gb/" target="_blank">Philips Hue</a>. I don't have one of those so I'll use a simple LED and then move on to IFTTT. Rather than tie up a Raspberry Pi 3, I thought this would be a good use for the cheap-as-chips Raspberry Pi Zero W. Incidentally, although this works well on a Pi Zero W, it doesn't seem to be very reliable using a basic Pi Zero with a wireless dongle attached to the USB port. If you've only got a non-wireless Pi Zero, it does work well with a wired network connection (e.g. a <a href="https://shop.pimoroni.com/products/three-port-usb-hub-with-ethernet-and-microb-connector" target="_blank">USB hub with a network port</a>) if you happen to have one lying around.<br />
<br />
Before we install dasher, the Dash button needs to be configured following the <a href="https://www.amazon.com/gp/help/customer/display.html?nodeId=201746340" target="_blank">Amazon instructions</a> so that it stores the SSID and password of your home wireless network (but with the last step omitted - so that no product is associated with the button). Strangely, you need to use an Android or iOS mobile phone to do this - not a tablet. If, like me, you've only got a tablet, you can use a little-known Android app called <a href="https://www.amazon.co.uk/gp/help/customer/display.html?nodeId=201887180" target="_blank">Amazon Underground</a> on an Android tablet. I forgot to say - if you order something with the button of your choice, you get the price of the button refunded, so it makes sense to get a button for something you use anyway, place one order then de-activate the button. Then go through the "add button" process again and quit the app before the final step.<br />
<div>
<br /></div>
How will we know if we have got our Dash button working? It makes sense to test it by controlling a GPIO pin first.<br />
<br />
<div>
<h2>
Let's get started</h2>
</div>
<div>
I started by following the instructions on the <a href="https://github.com/maddox/dasher" target="_blank">dasher git-hub page</a>. However, my initial attempt didn't work; also, the version of node.js that you end up with is v4.0, although the current (at the time of writing) version is 6.10.3. I thought I'd do it properly and install the latest version of node.js. First, I prepared my Raspberry Pi Zero W with a new image of Raspbian (date 10 April 2017). The current version of Raspbian comes with a very old version of node, and this has to be updated for dasher to work properly. After configuring the timezone and WiFi interface, I installed a couple of pre-requisites (libpcap-dev and npm).</div>
<ul>
<li>Open console and install node, libpcap-dev and npm</li>
</ul>
<ol></ol>
<code>#Install node.js to remove nodejs legacy </code><br />
<code>sudo apt-get install node<br />sudo apt-get install libpcap-dev<br />sudo apt-get install npm</code><br />
<ul>
<li>Install newer version of Node.JS (I used ARM v6)</li>
</ul>
<code>wget https://nodejs.org/dist/v6.10.3/node-v6.10.3-linux-armv6l.tar.gz<br />tar -xvf node-v6.10.3-linux-armv6l.tar.gz<br />cd node-v6.10.3-linux-armv6l<br />sudo cp -R <span class="pl-k">*</span> /usr/local/<br />sudo node --version</code><br />
<br />
This should respond with v6.10.3. If so, node is installed and up to date so we can proceed with installing dasher.<br />
<ul>
<li>Install dasher</li>
</ul>
<code>cd ..<br />sudo git clone https://github.com/maddox/dasher.git<br />cd dasher<br />sudo npm install</code><br />
N.B. ignore gyp warnings (doesn't seem to matter)<br />
Go and make a cup of coffee - installation takes a long time<br />
<br />
When that is finished, check that your button can be found (make a note of the MAC address if you haven't already done so)....<br />
<code>sudo ./script/find_button</code><br />
<div>
<br />
<div>
<h2>
Toggle a GPIO pin using dasher</h2>
</div>
We can configure dasher to monitor the home network and run a shell script if it sees a "UDP" request from the MAC address of the Dash button to join the network (which it will do when the button is pressed). To do this, we need to modify the config.json file according to the example given in the dasher documentation. Mine looks like this:<br />
<div>
<br /></div>
<div>
<code>{"buttons":[</code></div>
<div>
{</div>
<div>
"name": "Command Exec Button",</div>
<div>
"address": "<span style="font-family: monospace;">[MY MAC ADDRESS]</span>",</div>
<div>
"cmd": "/home/pi/dash_button.sh"</div>
<div>
}</div>
<div>
]}</div>
</div>
</div>
<div>
<br /></div>
Now we need to write the shell script (dash_button.sh) to toggle a GPIO. I connected an LED (with resistor) to GPIO14 so that, when the Dash button is pressed, it toggles on/off.<br />
<br />
<code>#!/bin/bash</code><br />
<code>GPIO=14<br />GPIODIR=/sys/class/gpio/gpio$GPIO</code><br />
<code># i.e. GPIODIR = /sys/class/gpio/gpio14<br /># use $GPIODIR to save typing long folder trees<br />echo "Configuring GPIO $GPIO"</code><br />
<code>#check if gpio14 is already exported.</code><br />
<code># If not, export gpio14 then set as output</code><br />
<code>if [ ! -e "$GPIODIR" ] #tests for the (not !) existence of gpio14 folder<br />then<br /> echo "Exporting GPIO"<br /> echo $GPIO > /sys/class/gpio/export<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>echo out > $GPIODIR/direction #set as output<br />else<br /> echo "GPIO already exported"<br />fi<br /><br />#now that gpio14 is configured, toggle on/off depending on state<br />read state < $GPIODIR/value<br />sleep 1<br />echo $(( ! state )) > $GPIODIR/value</code><br />
<br />
Before you can run this shell script, you need to make it executable and add it to your path:<br />
<code>sudo chmod -u+x dash_button.sh<br />
sudo cp dash_button.sh /usr/local/bin</code><br />
<div>
<br /></div>
To run dasher:
<br />
<code>
sudo npm run start</code>
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/GKuI_CDu80E/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/GKuI_CDu80E?feature=player_embedded" width="320"></iframe></div>
<br />
<div>
<h2>
IFTTT</h2>
Now that we have got the button working with GPIO pins, we can use it to set up a trigger action using IFTTT instead (you can't do both with the same button, unfortunately). IFTTT allows you to connect web-enabled "services" together so that, IF something happens on one service (a "THIS" trigger), THEN an action will occur on another service (a "THAT" action).<br />
<br />
Assuming you have a Gmail account, a good one for testing purposes is to add a row to a Google Sheet each time the button is pressed.</div>
<div>
<br />
In this case, the "dasher" script will monitor the home network and trigger an HTTP request if it sees a "UDP" request from the MAC address of the Dash button to join the network (which it will do when the button is pressed).<br />
<br />
The next step is to configure the IFTTT "Maker Webhooks" service to "watch" for the HTTP request that the dasher script will generate when it sees the Dash button join the network. Assuming you have set up an account on IFTTT, you will need to activate the Maker Webhooks service which will generate your own private key (you will need this later).<br />
<br />
Now, in IFTTT you can build your applet. Click on the "+" in the applet builder and choose the Maker Webhooks service (Receive a web request) as the trigger. Give it a name (in my case "trigger_dash"). Now click on the "+" to add an action. Choose the "Google Drive" service, then "Add row to spreadsheet". You can leave all the defaults as they are and just press "Create action", then (on the next page that appears) "Finish". If you click on the down arrow below your IFTTT user name on the main IFTTT page (top right) and choose Services, then choose the Maker Webhooks service you will be able to click on the Documentation button for that service (top right again) you will see the full URL that you need to generate (in dasher) in order to trigger the applet. This takes the format<br />
<br />
https://maker.ifttt.com/trigger/[EVENT NAME]/with/key/[YOUR KEY]<br />
<br />
Next make your own config.json file containing the mac address of your button and Maker Webhooks key. I recommend that you first rename or copy the config.json file you made to toggle a GPIO pin to config2.json or something else, so that you can always revert back to it if you need to test things are working.<br />
<br /></div>
<code>sudo nano /config/config.json </code> <br />
<br />
Mine looks like this (note that "trigger_dash" is the name I used in Maker Webhooks when setting up the IFTTT applet):<br />
<code>
{"buttons":[<br />
{<br />
"name": "trigger_dash",<br />
"protocol": "udp",<br />
"address": "[YOUR MAC ADDRESS]",<br />
"url": "https://maker.ifttt.com/trigger/trigger_dash/with/key/[YOUR KEY]",<br />
"method": "POST"<br />
}<br />
]}<br />
</code><br />
<br />
I have kept it simple for testing purposes. Once you get it working you can add more buttons, or pass more information to IFTTT.
<br />
<code>sudo npm run start</code><br />
<br />
With any luck, you should see dasher start running and confirm that the button has been added. Now, we can configure it so that it runs automatically, every time that you start up the Raspberry Pi.<br />
<br />
<h2>
Run from startup
</h2>
<div>
Just follow the instructions <a href="https://github.com/maddox/dasher/wiki/Running-Dasher-on-a-Raspberry-Pi-at-startup" target="_blank">here</a>. These instructions also show you how to check that your Dash button is being detected. Now, you should be able to reboot your Raspberry Pi then, every time the Dash button is pressed, Google will add a new line to a spreadsheet. If it doesn't work, open a console and type:<br />
<code>
tail -f /var/log/dasher.log
</code>
<br />
<br />
That should show you if dasher detected the button press, and what it did. I have found that the IFTTT Google service doesn't always work.
Good luck!</div>
<ul>
</ul>
SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com8tag:blogger.com,1999:blog-4320675363239807030.post-53512072332164821962017-05-13T22:56:00.000+01:002017-05-13T23:24:15.363+01:00Transistor logic gates - where analogue becomes digital<h2>
Introduction</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnVFLoKOmb6G41VxvzhWwlxA8xjSRE2XD5YyPBLKCiNHNw3Gnjo_Tj8EDKPZG9_Zhx6pN2JpI4R-ONY95AlmBtOZsXpPJIGReKBluOAIpao4NsmNXALMuohL8iZ864j5t9hjolERSivYSv/s1600/IMG_1155.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnVFLoKOmb6G41VxvzhWwlxA8xjSRE2XD5YyPBLKCiNHNw3Gnjo_Tj8EDKPZG9_Zhx6pN2JpI4R-ONY95AlmBtOZsXpPJIGReKBluOAIpao4NsmNXALMuohL8iZ864j5t9hjolERSivYSv/s640/IMG_1155.JPG" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
We all know that computers are made using thousands (or millions) of transistors, but not many of us have "hands on" experience of constructing the fundamental building-blocks of digital logic ourselves. Those building blocks are "logic gates".<br />
<br />
I decided to experiment with making my own logic gates using some of the many Snap Circuits components I have accumulated over the years.<br />
<br />
In electronics, a logic gate is a device implementing a Boolean function. Every possible logical function can be represented by a combination of only <a href="https://en.wikipedia.org/wiki/NAND_gate" title="NAND gate">NAND gates</a> or only <a href="https://en.wikipedia.org/wiki/NOR_gate" title="NOR gate">NOR gates</a>. In practice, other common logic gates such as <a href="https://en.wikipedia.org/wiki/AND_gate" target="_blank">AND</a>, <a href="https://en.wikipedia.org/wiki/OR_gate" target="_blank">OR</a>, <a href="https://en.wikipedia.org/wiki/Inverter_(logic_gate)" target="_blank">NOT</a> and <a href="https://en.wikipedia.org/wiki/XOR_gate" target="_blank">XOR</a> are also used as building blocks.<br />
<br />
<h2>
Transistors</h2>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiai0fByJEbi-2GiKXJwiLb8KqLmVOMniGWK1ARM3WTj6vBPZWZFERN1tejTSBEkEOAG1XhY3674l1DqUdUMhZOYM_4mN_goSBi7zaibN8BBHzKjViCPQPL9QqvhGY9KCFXnEf8gtS0QWpe/s1600/NPNvsPNP.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="140" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiai0fByJEbi-2GiKXJwiLb8KqLmVOMniGWK1ARM3WTj6vBPZWZFERN1tejTSBEkEOAG1XhY3674l1DqUdUMhZOYM_4mN_goSBi7zaibN8BBHzKjViCPQPL9QqvhGY9KCFXnEf8gtS0QWpe/s200/NPNvsPNP.png" width="200" /></a></div>
<br />
Turning now to the transistor, if a sufficient voltage is applied between the collector and base (normally ~ 0.7V), this will allow current to flow between the collector and the emitter (i.e. it will "turn on" the transistor).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS6FoCC1b5yZtdXbHAEs4BrXrAhF4VGZPVjMvyaXPNx0NKuqclx673TbB8xeQBSLrrlIAafPLzlRAt0E0LnLTT4iyaTttkTXz5QU2ty5YWoFMBaNA8pc2WZ5jIN-tuZxvaIurP1n1Na6FF/s1600/transistor+switch.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS6FoCC1b5yZtdXbHAEs4BrXrAhF4VGZPVjMvyaXPNx0NKuqclx673TbB8xeQBSLrrlIAafPLzlRAt0E0LnLTT4iyaTttkTXz5QU2ty5YWoFMBaNA8pc2WZ5jIN-tuZxvaIurP1n1Na6FF/s1600/transistor+switch.gif" /></a></div>
<br />
A judicious choice of base resistor is necessary to prevent excessive current flowing through the base (destroying the transistor). Also, a "load" resistor should be chosen that allows sufficient but not excessive current to flow through the load device (i.e. output device). For my circuit, I used a 4.5V battery pack and NPN transistors (with base resistors of 1k Ohms) driving an LED through a 100 Ohm load (plus the 33 Ohm internal resistor). By applying sufficient (analogue) base voltage until current flows from the collector through the emitter, we have made a digital device (an electronic switch). This is the starting point for digital electronics.<br />
<br />
<h2>
Logic gates</h2>
The first, and simplest logic gate I built was the NOT gate (also known as an inverter). This needs just one transistor. If the input voltage is "high" (in logic terms "1"), then the output voltage is "low" (in logic terms "0") and vice versa.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwLpTRxhMTHyvV4Ky5W1ihmBGszsYhROUBM0-O_tW3Z54LgckRuuP1pvvhTNCK-kVDm6sO5slR_TxZ-GugDN3SgCIBOYIhJoH2QpbR9lQfDQzH48jrGTqJcyq8umGOlYvsT2KtCzfR0BUN/s1600/IMG_1141.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwLpTRxhMTHyvV4Ky5W1ihmBGszsYhROUBM0-O_tW3Z54LgckRuuP1pvvhTNCK-kVDm6sO5slR_TxZ-GugDN3SgCIBOYIhJoH2QpbR9lQfDQzH48jrGTqJcyq8umGOlYvsT2KtCzfR0BUN/s320/IMG_1141.JPG" width="320" /></a></div>
<br />
<br />
Next I built a NAND gate. This needs two transistors in series, with the output load being driven between the collector and ground (0V). <i>nb.</i> <i>You can turn it into an AND gate by placing the load between the supply voltage and the collector. </i><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV0dG1ecHEAwP9-vkdgkPRcgSRjQ74EXD703nC2kuspFZDM6-VYRmt-WPnQE7GFSp4PDJ82oLCtT-8XHMDao3UMOxqE35y67Hj1hYpCr5PS0z0GbbzfHUxEI8_rKyliQk7yefe-AHTEi8u/s1600/IMG_1159.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV0dG1ecHEAwP9-vkdgkPRcgSRjQ74EXD703nC2kuspFZDM6-VYRmt-WPnQE7GFSp4PDJ82oLCtT-8XHMDao3UMOxqE35y67Hj1hYpCr5PS0z0GbbzfHUxEI8_rKyliQk7yefe-AHTEi8u/s320/IMG_1159.JPG" width="320" /></a></div>
<br />
<br />
The function of a NAND gate can be expressed in the truth table below (see <a href="https://www.allaboutcircuits.com/textbook/digital/chpt-3/ttl-nand-and-gates/" target="_blank">All About Circuits</a>):<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMceyuk7J7y__yD7LjI5_lYMkLXwDHwaitxu4aI_6bDMsczYU8-TsPXaRZ_I1Y-Bzhdu46MkQtSc96VbtwgdAxkG-42R3clldrBcV10dNQSvws2QPe3bg_2rW_EXiNtPg6iC50-XrdWEes/s1600/NAND.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMceyuk7J7y__yD7LjI5_lYMkLXwDHwaitxu4aI_6bDMsczYU8-TsPXaRZ_I1Y-Bzhdu46MkQtSc96VbtwgdAxkG-42R3clldrBcV10dNQSvws2QPe3bg_2rW_EXiNtPg6iC50-XrdWEes/s1600/NAND.png" /></a></div>
<br />
<br />
The final "building block" logic gate I built was a NOR gate which needs two transistors in parallel.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP6QdsvK8XiygDgPYfGQkCU4pvWAz0TixW3jAasCkz2CEDpy3Sjhp8ZhgQOSACEj1TdAtxdSUPd6gxENc1GdKnf25Rpcn8dvM1vRY1qT8Yp7f-o7cfVSOSCJd-SxWhp3AAILwBNHUhoMEI/s1600/IMG_1146.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP6QdsvK8XiygDgPYfGQkCU4pvWAz0TixW3jAasCkz2CEDpy3Sjhp8ZhgQOSACEj1TdAtxdSUPd6gxENc1GdKnf25Rpcn8dvM1vRY1qT8Yp7f-o7cfVSOSCJd-SxWhp3AAILwBNHUhoMEI/s320/IMG_1146.JPG" width="320" /></a></div>
<br />
<br />
The truth table is as follows:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh_JWwGx3nqt7E0LtihKFRww9A8eiqK7CxEwmLPYGqloIN7tnlWhxaCqLqaj3EtLUFEKVvqtD33kj4TfI2ywLX_-fmBkR-90JWwtyzADrLNegAlu3LW_vzegywz3KFLPAFHmVkaJfL5Qcx/s1600/NOR.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh_JWwGx3nqt7E0LtihKFRww9A8eiqK7CxEwmLPYGqloIN7tnlWhxaCqLqaj3EtLUFEKVvqtD33kj4TfI2ywLX_-fmBkR-90JWwtyzADrLNegAlu3LW_vzegywz3KFLPAFHmVkaJfL5Qcx/s1600/NOR.png" /></a></div>
<br />
Now for something a bit more complicated - an Exclusive-OR ("XOR") gate. This outputs a “high” ("1") logic level if the inputs are at <i>different</i> logic levels, either 0 and 1 or 1 and 0. Conversely, it outputs a “low” ("0") logic level if the inputs are at the <i>same</i> logic levels.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg63sG5EBbNT2XCZs1S2uQ646EAi4nCDRkNTg1_bKopKB57Mc_-1K9h368DcxlQGCDklTjV4godZ6ocDcSVpHxzaI2bjVDvNFZYyrMj20VcbLYAYpsiCooeyWQHMTWKOjFn4YzuFOHsD9zE/s1600/XOR.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg63sG5EBbNT2XCZs1S2uQ646EAi4nCDRkNTg1_bKopKB57Mc_-1K9h368DcxlQGCDklTjV4godZ6ocDcSVpHxzaI2bjVDvNFZYyrMj20VcbLYAYpsiCooeyWQHMTWKOjFn4YzuFOHsD9zE/s1600/XOR.png" /></a></div>
XOR gates can be constructed from various combinations of AND, OR, NAND, NOR or NOT gates. I made mine from pairs of NAND and NOT gates.<br />
<br />
<h2>
Adders</h2>
Finally, I made a "<a href="https://en.wikipedia.org/wiki/Adder_(electronics)" target="_blank">half adder</a>" circuit by adding an AND gate in parallel with an XOR gate.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXCiJnRIX1BvCyA47BHwTdfZMiOSqmXP8eL_zuXBwmHLUzcz4ZyQBXVtZJSeUjbo0L__OLhxpUDbnadI7iddLShYHIhZ_Uutl6G6XtdwYDhYbbXn9fnoL0wRH0YfCiDS1RXQiTpXHnEXpW/s1600/Half_Adder.svg.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="110" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXCiJnRIX1BvCyA47BHwTdfZMiOSqmXP8eL_zuXBwmHLUzcz4ZyQBXVtZJSeUjbo0L__OLhxpUDbnadI7iddLShYHIhZ_Uutl6G6XtdwYDhYbbXn9fnoL0wRH0YfCiDS1RXQiTpXHnEXpW/s200/Half_Adder.svg.png" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigDQQMVvhdtPZttmAxLMuu-1-f5DxZCcZ1doEpKTD3uN1mdkwySEp8oidSGpNOv9iL32k2DRnocI0epVWaGnp5AvZ6prPTrl4vK4PqEylo7tPnv6DDCCXewy3JTnU0AUoQ7-VZQYzAaVWv/s1600/IMG_1157.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="305" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigDQQMVvhdtPZttmAxLMuu-1-f5DxZCcZ1doEpKTD3uN1mdkwySEp8oidSGpNOv9iL32k2DRnocI0epVWaGnp5AvZ6prPTrl4vK4PqEylo7tPnv6DDCCXewy3JTnU0AUoQ7-VZQYzAaVWv/s400/IMG_1157.JPG" width="400" /></a></div>
<br />
<br />
Inspecting the truth table reveals that this circuit enables two one-bit binary numbers to be added together, giving a "sum" and a "carry".<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5Kn7BvgG-wlW4N1fzt3OQWI0rLya0r0ui2emnnjB29qJiH6gvItJfpImeEfsqFUiKSMIGT81rynBivg34SwBp15ThTzipn-K6hRwwwQxRmW9wtKhRYDVucXNEEbXdLlK3TZ4Y5Xelh56D/s1600/HA-TRUTH-TABLE.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="131" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5Kn7BvgG-wlW4N1fzt3OQWI0rLya0r0ui2emnnjB29qJiH6gvItJfpImeEfsqFUiKSMIGT81rynBivg34SwBp15ThTzipn-K6hRwwwQxRmW9wtKhRYDVucXNEEbXdLlK3TZ4Y5Xelh56D/s200/HA-TRUTH-TABLE.jpg" width="200" /></a></div>
Finally, the full significance of all these logic gates can be appreciated. Using only transistors and resistors, a circuit can be created that adds two binary numbers together. Although there is a "carry" output, there is no "carry" input (hence the name half adder), so only one-bit numbers can be added together, which somewhat limits its usefulness.<br />
<br />
A full adder allows for "carry in" signals as well as "carry out" signals. This makes it possible to add larger numbers together, as there is a mechanism for carrying from a "less significant bit" to a "more significant bit". For example, by cascading eight full adders together, eight-bit binary numbers can be added together (i.e. numbers up to 255 in decimal).SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com0tag:blogger.com,1999:blog-4320675363239807030.post-81450639085127127882017-04-07T11:00:00.000+01:002017-04-08T11:04:58.258+01:00Driving an Adafruit 4x14-segment I2C display with the BBC micro:bit<h2>
Introduction </h2>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizmwsUA-fn8VyGT2s3aOYa1C8jDG7Mouj27XaEBplHEHZVQ2s-NSeB9tBqwfbJaQH6wgRwd_g2PucxEK-Tu00scXC_VAc1LMHTsyPFdUDZQVYjr3iBB9E0g9fYV7Ix5oVqgZmUx46_WqCe/s1600/14-seg+beer.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizmwsUA-fn8VyGT2s3aOYa1C8jDG7Mouj27XaEBplHEHZVQ2s-NSeB9tBqwfbJaQH6wgRwd_g2PucxEK-Tu00scXC_VAc1LMHTsyPFdUDZQVYjr3iBB9E0g9fYV7Ix5oVqgZmUx46_WqCe/s320/14-seg+beer.JPG" width="320" /></a></div>
I have previously posted about using <a href="https://www.adafruit.com/products/1049" target="_blank">Adafruit 8x8 displays</a>
with the BBC micro:bit. I also recently bought an <a href="https://shop.pimoroni.com/products/quad-alphanumeric-display-0-54-digits-w-i2c-backpack" target="_blank">Adafruit 4x14-segment display</a> so I could experiment with displaying text as well as just numbers. Like the 8x8 LED matrix, the 4x14-segment display is soldered onto an I2C "backpack" that
contains an HT16K33 LED driver/controller.<br />
<br />
The I2C protocol is a serial
protocol that allows different data to be sent to different devices
along a "bus". Each device must have its own address on the bus. The
Adafruit backpack has a default address of 0x70 but this <a href="https://learn.adafruit.com/adafruit-led-backpack/changing-i2c-address" target="_blank">can be changed</a>
to one of seven other addresses by soldering across one or more of the
three jumpers on the backpack. This would allow you to have several
4x14-segment displays running from the same micro:bit,and controlled
separately.<br />
<br />
My post about the 8x8 LED matrix provided instructions for using a Micropython library for the HT16K33 to simplify the Python code. However, that library doesn't support the various bit-patterns needed to form each of the 60-plus characters that can be displayed on this particular display, so it doesn't help much. In any case, I find it a slow process using libraries with the micro:bit as you have to upload the Python code first (which generates an error message that takes a long time to disappear) THEN copy the library to the micro:bit and reboot. If you just want to make a small change to your code, this gets quite tedious.<br />
<br />
The <a href="https://microbit-micropython.readthedocs.io/en/latest/i2c.html" target="_blank">micro:bit Micropython reference page</a> shows there are only three micro:bit I2C functions. Sending data to an I2C device is not complicated as there is only really one function that you need to know about. Assuming you are not changing the default configuration, you don't need to initialise the I2C device; neither do you normally need to read from a 4x14-segment display, which only leaves the function:<br />
<pre style="background: rgba(238 , 238 , 238 , 0.92); color: black;">microbit.i2c.write(addr, buf, <span style="font-style: italic;">repeat</span><span style="color: blue;">=</span><span style="color: #9700cc;">False</span>)
</pre>
<span class="sig-paren">In our case, we can also omit the "repeat=False" parameter. So, all we need to do is call: </span><br />
<pre style="background: rgba(238 , 238 , 238 , 0.92); color: black;">i2c.write(<span style="color: #0066ff;">0x70</span>, bytearray(buf))
</pre>
<span class="sig-paren"></span>
<span class="sig-paren">... where 0x70 is the I2C address of the </span><span class="sig-paren">HT16K33 and bytearray(buf) contains the data that you want to send to the </span>HT16K33 LED controller.<br />
<br />
The HT16K33 uses a number of command codes to control the display, and an area of RAM to store the segment display data. The main command codes we need are:<br />
<br />
0x21 Start oscillator<br />
0x80 Blank the display<br />
0x81 Send the RAM data to the display<br />
0x83 Blink the display @2Hz<br />
0xE0 Dim the display<br />
0xEF Set brightness to maximum<br />
<br />
To display characters, we need to write the data to the HT16K33 RAM and then send the data to the display itself. Since we are always going to send a whole set of data for each of the four segments, we will always preface the data for which LED segments to switch on by the starting address of the RAM on the HT16K33, i.e. 0x0. This is then followed by four two-byte pairs (one pair for each
"digit", working from left to right). The second byte for each digit is
essentially the traditional 7-segment bit pattern (except that the
middle segment is split into two). The first byte consists of the extra
segments (e.g. the diagonal ones) needed to form the letters of the
alphabet. Then you send the control byte $81 to "latch" the data to the
display.
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOjptkr2zvLY2JkxL0le4ePvKqkHmJ7Y4xFOh4jGTEptPXnWVCMg7Gd6XbCydx3k8FkCcqLxatPfEtNLhH_O6u0o19W5m7PnUc24FljRrPd1AyjLwVF5gw-psPhNXqMiP9ZeB7FhW7vdLx/s1600/led_matrix_segments2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOjptkr2zvLY2JkxL0le4ePvKqkHmJ7Y4xFOh4jGTEptPXnWVCMg7Gd6XbCydx3k8FkCcqLxatPfEtNLhH_O6u0o19W5m7PnUc24FljRrPd1AyjLwVF5gw-psPhNXqMiP9ZeB7FhW7vdLx/s320/led_matrix_segments2.png" width="209" /></a></div>
The bit mapping shown above can be represented as two eight-bit binary numbers where A is the least significant bit, as per the sequence below (the first bit isn't used; you can make it 0 or 1). Bytes 1 and 2 are sent in reverse:<br />
--- byte 2 --- , --- byte 1 ---<br />
0 <b>DP N M L K J H , G2 G1 F E D C B A</b><br />
<br />
To turn on just the <b>A</b> segment, use binary 00000000,00000001 and reverse-> hex 0x1, 0x0<br />
To turn on just the <b>G1</b> segment, use binary 00000000,01000000 and reverse-> hex 0x40, 0x0<br />
To make "0", turn on segments <b>F E D C B A</b> - use binary 00000000,00111111 and reverse-> hex 0x3F, 0x0<br />
To make "K", turn on segments <b>N K , G1 F E - </b>use binary 00100100,01110000 and reverse-> hex 0x70,0x24
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<h2>
</h2>
<h2>
Connecting things up</h2>
This assumes you have already
assembled the display by soldering the two pairs of 14-segment displays to the backpack. If
not, refer to <a href="https://learn.adafruit.com/adafruit-led-backpack/0-54-alphanumeric" target="_blank">this Adafruit page.</a><br />
<br />
The first step is to connect the display to the micro:bit. Compared with the other Adafruit I2C backpack displays, this device has an extra pin which connects to the logic level voltage of the processor you are using (i.e. 3.3v). The display works best at 5v, so a power supply that provides both 5v and 3.3v is ideal. I used a <a href="http://www.petervis.com/Raspberry_PI/Breadboard_Power_Supply/YwRobot_Breadboard_Power_Supply.html" target="_blank">YwRobot MB102 USB breadboard power supply module</a> (pictured). This allows you to power either side of a solderless breadboard with a different regulated voltage by plugging it into a USB port.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5pHQnZEhrKCYev9BQtwwkdEFd9Ze_saOP4xiwIfhM_kYyWIqagvPUGYymZ7asa1gDpxCnUrmEa-tDSkfxcN3BnZ7RdMSBON3Fzj9owUcwKPScbXXe3uKK4RPn4Tq72sjZVwphRAFqJHDN/s1600/breadbit2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5pHQnZEhrKCYev9BQtwwkdEFd9Ze_saOP4xiwIfhM_kYyWIqagvPUGYymZ7asa1gDpxCnUrmEa-tDSkfxcN3BnZ7RdMSBON3Fzj9owUcwKPScbXXe3uKK4RPn4Tq72sjZVwphRAFqJHDN/s320/breadbit2.jpg" width="240" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
You'll need to use an edge-connector board
to access the I2C pins (P19 and P20). Some ready-made edge-connectors
don't have those two pins soldered in place so you might need to get the
soldering iron out. Assuming you have them, connecting is easy
(SDA=P20, SCL=P19). Since I was using solderless breadboard, I used the very neat <a href="https://www.proto-pic.co.uk/breadbit-breakout-board-for-microbit.html" target="_blank">bread:bit edge connector board from Proto-Pic</a>. Combined with the USB breadboard power supply, this makes a great prototyping setup.<br />
<br />
Micropython on the micro:bit automatically enables the internal pullup
resistors on the I2C lines, so the usual 5.1k resistors on the SCL and
SDA connections are not needed.<br />
<br />
<h2>
Python code </h2>
There are two types of command used:<br />
1. Send a control byte to the display to turn it on/off/blink etc. This takes the form
<br />
<pre style="background: rgba(238 , 238 , 238 , 0.92); color: black;">i2c.write(<span style="color: #0066ff;">0x70</span>, bytearray([control byte]))
</pre>
2 Send character data to the display controller RAM<br />
<pre style="background: rgba(238 , 238 , 238 , 0.92); color: black;">i2c.write(<span style="color: #0066ff;">0x70</span>,([<span style="color: #0066ff;">0x0</span>,digit1,digit2,digit3,digit4]))
</pre>
Here's some code I used to test the various
functions that the display uses. It clears the display, then shows all the printable characters in groups of four (some of the lower case letters are not that convincing to be honest). Finally it displays a message that dims and then flashes, followed by succession of "wheel spokes" giving the appearance that they are spinning around. I have included virtually the whole character set for reference, but in practice you would just include the characters that you are going to use.<br />
<br />
<code>
from microbit import i2c, sleep<br />
# address of HT16K33 is 0x70<br />
<br />
sleep(1) # allow time for display to settle after power on/reset<br />
# start oscillator<br />
i2c.write(0x70, bytearray([0x21]))<br />
<br />
<br />
# To display something: <br />
# send (i2c device address,([RAM start addr, four pairs of bytes ]))<br />
# i2c.write( 0x70 ,([0x0 ,digit1,digit2,digit3,digit4]))<br />
# turn on the display<br />
<br />
<br />
# Clear the display<br />
i2c.write(0x70, bytearray([0x80]))<br />
sleep(1000)<br />
<br />
<br />
# data for each character<br />
# byte1,byte2<br />
#0 = 0x3f,0x0<br />
#1 = 0x6,0x0<br />
#2 = 0xdb,0x0<br />
#3 = 0xcf,0x0<br />
#4 = 0xe6,0x0<br />
#5 = 0xed,0x0<br />
#6 = 0xfd,0x0<br />
#7 = 0x1,0xc<br />
#8 = 0xff,0x0<br />
#9 = 0xc7,0x8<br />
#. = 0x0,0x40<br />
#+ = 0xc0,0x12<br />
#- = 0xc0,0x0<br />
#/ = 0x0,0xc<br />
#| = 0x0,0x10<br />
#\ = 0x0,0x21<br />
#0x = 0x8d,0xc<br />
#all segments = 0x3f,0x3f<br />
#A 0xF7,0x0<br />
#B 0x8F,0x12<br />
#C 0x39,0x0<br />
#D 0xF,0x12<br />
#E 0xF9,0x0<br />
#F 0xF1,0x0<br />
#G 0xBD,0x0<br />
#H 0xF6,0x0<br />
#I 0x9,0x12<br />
#J 0x1E,0x0<br />
#K 0x70,0x24<br />
#L 0x38,0x0<br />
#M 0x36,0x5<br />
#N 0x36,0x21<br />
#O 0x3F,0x0<br />
#P 0xF3,0x0<br />
#Q 0x3F,0x20<br />
#R 0xF3,0x20<br />
#S 0xED,0x0<br />
#T 0x0,0x12<br />
#U 0x3E,0x0<br />
#V 0x30,0xC<br />
#W 0x36,0x28<br />
#X 0x0,0x2D<br />
#Y 0x0,0x15<br />
#Z 0x9,0xC<br />
#a 0x58,0x10<br />
#b 0x78,0x20<br />
#c 0xD8,0x0<br />
#d 0x8E,0x8<br />
#e 0x58,0x8<br />
#f 0x71,0x0<br />
#g 0x8E,0x4<br />
#h 0x70,0x10<br />
#i 0x0,0x10<br />
#j 0xE,0x0<br />
#k 0x0,0x36<br />
#l 0x30,0x0<br />
#m 0xD4,0x10<br />
#n 0x50,0x10<br />
#o 0xDC,0x0<br />
#p 0x70,0x1<br />
#q 0x86,0x4<br />
#r 0x50,0x0<br />
#s 0x88,0x20<br />
#t 0x78,0x0<br />
#u 0x1C,0x0<br />
#v 0x4,0x20<br />
#w 0x14,0x28<br />
#x 0xC0,0x28<br />
#y 0xC,0x28<br />
#z 0x48,0x8<br />
<br />
while True:<br />
<br />
# Turn on all segments<br />
i2c.write(0x70,bytearray([0x0,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep(1000)<br />
<br />
# print out the character set four at a time<br />
#0 1 2 3<br />
i2c.write(0x70, bytearray([0x0,0x3f,0x0,0x6,0x0,0xdb,0x0,0xcf,0x0]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#4 5 6 7<br />
i2c.write(0x70, bytearray([0x0,0xe6,0x0,0xed,0x0,0xfd,0x0,0x1,0xc]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#8 9 0x all<br />
i2c.write(0x70, bytearray([0x0,0xff,0x0,0xe7,0x0,0xed,0x12,0x3f,0x3f]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#- / . +<br />
i2c.write(0x70, bytearray([0x0,0x0,0x40,0xc0,0x12,0xc0,0x0,0x0,0xc]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#A B C D<br />
i2c.write(0x70, bytearray([0x0,0xF7,0x0,0x8F,0x12,0x39,0x0,0xF,0x12]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#E F G H<br />
i2c.write(0x70, bytearray([0x0,0xF9,0x0,0xF1,0x0,0xBD,0x0,0xF6,0x0]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#I J K L<br />
i2c.write(0x70, bytearray([0x0,0x9,0x12,0x1E,0x0,0x70,0x24,0x38,0x0]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#M N O P <br />
i2c.write(0x70, bytearray([0x0,0x36,0x5,0x36,0x21,0x3F,0x0,0xF3,0x0]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#Q R S T <br />
i2c.write(0x70, bytearray([0x0,0x3F,0x20,0xF3,0x20,0xED,0x0,0x1,0x12]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#U V W X <br />
i2c.write(0x70, bytearray([0x0,0x3E,0x0,0x30,0xC,0x36,0x28,0x0,0x2D]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#Y Z all all<br />
i2c.write(0x70, bytearray([0x0,0x0,0x15,0x9,0xC,0x3f,0x3f,0x3f,0x3f]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#a b c d <br />
i2c.write(0x70, bytearray([0x0,0x58,0x10,0x78,0x20,0xD8,0x0,0x8E,0x8]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#e f g h <br />
i2c.write(0x70, bytearray([0x0,0x58,0x8,0x71,0x0,0x8E,0x4,0x70,0x10]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#i j k l <br />
i2c.write(0x70, bytearray([0x0,0x0,0x10,0xE,0x0,0x0,0x36,0x30,0x0]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#m n o p <br />
i2c.write(0x70, bytearray([0x0,0xD4,0x10,0x50,0x10,0xDC,0x0,0x70,0x1]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#q r s t <br />
i2c.write(0x70, bytearray([0x0,0x86,0x4,0x50,0x0,0x88,0x20,0x78,0x0]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#u v w x <br />
i2c.write(0x70, bytearray([0x0,0x1C,0x0,0x4,0x20,0x14,0x28,0xC0,0x28]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
#y z <br />
i2c.write(0x70, bytearray([0x0,0xC,0x20,0x48,0x8,0x0,0x0,0x0,0x0]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (1000)<br />
<br />
# display a message<br />
# | L | O | V | E <br />
i2c.write(0x70,bytearray([0x00,0x38,0x0,0x3f,0x0,0x30,0xC,0xf9,0x0]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep(1000)<br />
<br />
# | B | E | E | R <br />
i2c.write(0x70,bytearray([0x00,0x8F,0x12,0xf9,0x00,0xf9,0x00,0xf3,0x20]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep(1000)<br />
<br />
<br />
i2c.write(0x70, bytearray([0xE0])) # dim the display (range 0xE0 to 0xEF)<br />
sleep (1000)<br />
i2c.write(0x70, bytearray([0xEF])) # full brightness again<br />
sleep (1000)<br />
i2c.write(0x70, bytearray([0x83])) # blink the display (0x83=2Hz blink, 0x85=1Hz, 0x87=0.5Hz)<br />
sleep (3000)<br />
<br />
# Clear the display<br />
i2c.write(0x70, bytearray([0x80]))<br />
sleep(1000)<br />
<br />
# make a spinney-round thing!<br />
for iterations in range(20):<br />
i2c.write(0x70, bytearray([0x0,0x0,0x12,0x0,0x12,0x0,0x12,0x0,0x12]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (21)<br />
<br />
i2c.write(0x70, bytearray([0x0,0x0,0xc,0x0,0xc,0x0,0xc,0x0,0xc]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (20)<br />
<br />
i2c.write(0x70, bytearray([0x0,0xc0,0x0,0xc0,0x0,0xc0,0x0,0xc0,0x0]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (20)<br />
<br />
i2c.write(0x70, bytearray([0x0,0x0,0x21,0x0,0x21,0x0,0x21,0x0,0x21]))<br />
i2c.write(0x70, bytearray([0x81])) # display on<br />
sleep (20)<br />
<br />
# turn off the display<br />
i2c.write(0x70, bytearray([0x80]))<br />
sleep(1000)<br />
</code><br />
<div class="separator" style="clear: both; text-align: center;">
<code><iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/qlqlWIL4THk/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/qlqlWIL4THk?feature=player_embedded" width="320"></iframe></code></div>
<code>
<br />
</code>SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com0tag:blogger.com,1999:blog-4320675363239807030.post-46628232728816910642017-03-31T22:24:00.000+01:002017-04-08T10:58:04.321+01:00Driving Adafruit I2C 8x8 LED matrices with the BBC micro:bit<h2>
Introduction </h2>
I bought a couple of <a href="https://www.adafruit.com/products/1049" target="_blank">Adafruit 8x8 displays</a> in a sale and tried them out with various microcontrollers. This post is about using them with the BBC micro:bit. It assumes you have already assembled the display by soldering the LED matrix to the backpack. If not, refer to <a href="https://learn.adafruit.com/adafruit-led-backpack/" target="_blank">this Adafruit guide</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgh2mD7XR5TDYkPnOiTVzC7oPL_6q6VVA9aRHh8fh1Cix0ty5WZa_AmFKH_ePCxE7D2NsrSEIzlm8VkJXR1ElAgbu8VP5ngW0QIjjvTj893z4ELwrf6GBdi9PSUx6PCPjoBG2g81dmqjlMC/s1600/IMG_0837.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgh2mD7XR5TDYkPnOiTVzC7oPL_6q6VVA9aRHh8fh1Cix0ty5WZa_AmFKH_ePCxE7D2NsrSEIzlm8VkJXR1ElAgbu8VP5ngW0QIjjvTj893z4ELwrf6GBdi9PSUx6PCPjoBG2g81dmqjlMC/s400/IMG_0837.JPG" width="400" /></a></div>
<br />
<br />
They consist of an 8x8 LED matrix soldered onto an I2C "backpack" that contains an HT16K33 LED driver/controller. The I2C protocol is a serial protocol that allows different data to be sent to different devices along a "bus". Each device must have its own address on the bus. The Adafruit backpack has a default address of 0x70 but this <a href="https://learn.adafruit.com/adafruit-led-backpack/changing-i2c-address" target="_blank">can be changed</a> to one of seven other addresses by soldering across one or more of the three jumpers on the backpack. This would allow you to have several LED matrices running from the same micro:bit,and controlled separately.<br />
<br />
<h2>
Connecting things up</h2>
The first step is to connect the display to the micro:bit. You'll need to use an <a href="http://www.rkonlinestore.co.uk/rkub1-edge-connector-breakout-board-solder-kit-for-bbc-microbit-2560-p.asp" target="_blank">edge-connector board</a> to access the I2C pins (P19 and P20). Some ready-made edge-connectors don't have those two pins soldered in place so you might need to get the soldering iron out. Assuming you have them, connecting is easy (SDA=P20, SCL=P19) but I used a separate power supply for the display as it will struggle to light up all the pixels if you draw power from the micro:bit supply. Don't forget to connect the earth (- supply) connections on the two power sources togther (i.e. connect an extra wire from -ve on the backpack to -ve on the micro:bit edge connector). Micropython on the micro:bit automatically enables the internal pullup resistors on the I2C lines, so the usual 5.1k resistors on the SCL and SDA connections are not needed.<br />
<br />
<h2>
Python code </h2>
I was happy to find that deshipu from the MicroPython forum had already ported a <a href="https://bitbucket.org/thesheep/microbit-ht16k33/src" target="_blank">Python library</a>
for the HT16K33 LED driver/controller to work with MicroPython. This
library allows the same I2C backpack to be used with three different
display types. You will need to copy the library to the users\mu_code folder on your
PC.<br />
<br />
Here's some code I used to test the various functions that the display uses. It clears the display, then fills it again, then dims the display in steps and clears it again. Then it draws a "smiley face" (using the byte array image() expressed in binary to make it more obvious which pixels should be lit up) before flashing the display on and off before finally clearing it again.<br />
<br />
You can design your pattern on a piece of paper and put a '1 where you want the pixel lit and a '0' where you want a pixel off. Once you have got the pattern you want, you could convert the binary to hex to save space as Micropython on the micro:bit doesn't leave much space for programs/data.<br />
<br />
<br />
<code>
from microbit import i2c, sleep<br />
from ht16k33 import Matrix8x8<br />
# address of HT16K33 is 0x70<br />
display = Matrix8x8(i2c, address=0x70)<br />
<br />
# Clear the display<br />
display.fill(0x00)<br />
display.show()<br />
sleep(1000)<br />
<br />
# Fill the display<br />
display.fill(0xff)<br />
display.show()<br />
sleep(1000)<br />
<br />
#step through some different brightness levels <br />
display.brightness(1)<br />
sleep(3000)<br />
display.brightness(7)<br />
sleep(3000)<br />
display.brightness(15)<br />
sleep(3000)<br />
<br />
<br />
# Clear the display<br />
display.fill(0x00)<br />
display.show()<br />
sleep(1000)<br />
<br />
# define the bitmap image to be displayed (in this case a smiley face)<br />
image = (<br />
0b01111110,<br />
0b10000001,<br />
0b10100101,<br />
0b10000001,<br />
0b10100101,<br />
0b10011001,<br />
0b10000001,<br />
0b01111110,<br />
)<br />
<br />
#draw the image<br />
for y, line in enumerate(image):<br />
for x in range(8):<br />
if line & (1 << x):<br />
display.pixel(y, x, 1)<br />
display.show()<br />
sleep(3000)<br />
# Blink the display<br />
display.blink_rate(0x83)<br />
sleep(3000)<br />
# Stop blinking?<br />
display.blink_rate(0x81)<br />
sleep(3000)<br />
<br />
# Clear the display<br />
display.fill(0x00)<br />
display.show()<br />
sleep(1000)<br />
</code>
<br />
<br />
Getting it onto the micro:bit is a two-stage process. First "flash" the test code to the micro:bit in the usual way using Mu. This will result in an error as the required library is not on the micro:bit yet. Once the error message has finished scrolling on the micro:bit's display, press the "Files" button in Mu. If you don't see that button, you probably have an old version of Mu, so replace it with the current version. This will open up a new window below the code window. Provided you have put the library file in the mu_code folder on your PC, you should see it in the right-hand panel in Mu (along with any other files in your mu_code folder). Using the mouse, drag it and drop it into the left-hand window. This should copy it to the micro:bit. Now, if you disconnect the programming cable, power the micro:bit off and on again and the code should run. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe width="320" height="266" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/EdpVf5Lqr2E/0.jpg" src="https://www.youtube.com/embed/EdpVf5Lqr2E?feature=player_embedded" frameborder="0" allowfullscreen></iframe></div>
<br />SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com0tag:blogger.com,1999:blog-4320675363239807030.post-62292217521644734942017-03-18T22:57:00.001+00:002017-04-08T11:42:37.765+01:00Driving DotStar APA102 LED strings with the BBC micro:bit<h2>
Introduction </h2>
<br />
Although <a href="https://www.adafruit.com/category/168" target="_blank">NeoPixels</a> are very popular and relatively cheap nowadays, there is another type of individually-addressable LED string that offers some distinct advantages.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyJM__z7ancaCNoD98tCLjZl_PrSz0lhK9h4Xkgd69lVLmGFTrF6thkOpbjkx69sOQcX7glsqkqhwgn-LsFL0RZwXfLSjDJ6Jge9St6pmF_kO1nnQcSVMngQ0XC5w9CsON6H5ixUw6jU1E/s1600/IMG_0829.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyJM__z7ancaCNoD98tCLjZl_PrSz0lhK9h4Xkgd69lVLmGFTrF6thkOpbjkx69sOQcX7glsqkqhwgn-LsFL0RZwXfLSjDJ6Jge9St6pmF_kO1nnQcSVMngQ0XC5w9CsON6H5ixUw6jU1E/s320/IMG_0829.JPG" width="320" /></a></div>
<br />
<br />
The APA102 and APA102C LEDs are small super-bright RGB LED lighting
modules. Each LED is a single light source and LEDs can be chained to
create light strips of varying length. Light strips are commonly
available in 10, 20, 30 and 60 LED lengths (and also in other form factors such as the <a href="https://shop.pimoroni.com/products/blinkt" target="_blank">Pimoroni Blinkt</a>). Strips can be cut and joined
as desired. These LEDs are marketed by Adafruit under the name <a href="https://thepihut.com/products/adafruit-dotstar-digital-led-strip-white-144-led-m-0-5-meter-white" target="_blank">'DotStar'</a>. The APA102 and APA102C modules use an industry standard SPI interface.
This is significantly easier to use than the single wire interface of
the NeoPixel (WS2812) and other similar modules (all of which require specific
interface timing which can be difficult to achieve). <br />
<br />
By using SPI, the APA102 offers much faster data and PWM rates than NeoPixels - which allows "persistence of vision" effects. The higher data rate is particularly noticeable on long strings of LEDs.<br />
<br />
Since I already had some APA102s, I decided to try them out with the BBC micro:bit. The only example I found on the Internet was a re-write of the Pimoroni Python library. However, it just uses "bit banging" to switch the LEDs on and off, and, whilst this was just about acceptable on a Blinkt with 8 LEDs, it was very slow on a long string of 30 LEDs. I decided to write my own Python code using the BBC micro:bit's built-in SPI support. I'm no Python expert so I expect the examples below could be improved but they should demonstrate the basic principles.<br />
<br />
After <a href="https://cpldcpu.com/2014/11/30/understanding-the-apa102-superled/" target="_blank">a bit of research</a>, I discovered what is needed to drive an APA102 device. Lighting data is sent to the LED strip by sending a 'start frame', brightness data for each LED and then an 'end frame'.<br />
<br />
The 'start frame' is 32 zero bits sent as four zero value bytes.<br />
<br />
00000000 00000000 00000000 00000000<br />
<br />
The brightness data for each LED consists of four bytes, a start byte which has its three most significant bits set and the least significant five bits setting the overall brightness, then three bytes which set the intensity of the LED's blue, green and red brightness; 0 is off, 255 ($FF) is fully on.<br />
<br />
111xxxxx bbbbbbbb gggggggg rrrrrrrr<br />
<br />
The first byte of the LED's data, the overall brightness setting, has a byte value of 224 ($E0) to 255 ($FF); this allows for 32 levels of brightness from lowest to maximum respectively. It is reported that, on some devices, LED flicker may be more noticeable when the overall brightness is set less than maximum and, to avoid this, it is recommended to send $FF as the brightness setting and solely control the LED brightness by adjusting the blue, green and red byte values.<br />
<br />
The 'end frame' is 32 one bits sent as four 255 ($FF) value bytes. Because of the way the LEDs pass data through themselves, it is necessary to send additional clock pulses to latch the sent data into the final LED. Sending the 'end frame' provides for this.<br />
<br />
11111111 11111111 11111111 11111111<br />
<br />
An 'end frame' needs to be sent for every set of 64 LEDs in the light strip. A light strip of 1 to 64 LEDs requires a single 'end frame', a light strip of 65 to 128 LEDs require two, and so on.<br />
<br />
Note that if the LED count for a strip is incorrectly set the 'end frame' is equivalent to setting a LED fully on. It is therefore advised to ensure the 'end frame' is not sent before all data for the entire light strip is sent or else this will set a LED within the light strip fully on.<br />
<br />
If the 'end frame' is not sent, the last LED in a light strip may not have its colour or brightness updated until the next set of brightness data is sent to the light strip.<br />
<br />
<h2>
Connecting things up</h2>
APA102s normally have four connections:<br />
<ul>
<li>5v</li>
<li>GND</li>
<li>Clock </li>
<li>Data</li>
</ul>
Note that the BBC micro:bit is a 3.3V device and, in any case, its outputs cannot provide enough current to light the LEDs so a separate 5v power source should be provided (with a common GND connection to the micro:bit). LED strips can be chained together so the clock and data signals should be provided to the correct end of the strip (this is normally marked using arrows on the strip itself).<br />
<br />
In this situation, the micro:bit will be the SPI Master and the APA102 will be the SPI Slave. On the micro:bit, the SPI connections are therefore:<br />
<ul>
<li>Clock (SCK) - pin P13</li>
<li>Data out (MOSI) - pin P15</li>
</ul>
I used a small breadboard to make the connections. In the case of the Blinkt (or other Raspberry Pi versions of the APA102, I plugged the Blinkt into a "cobbler" inserted into the breadboard and used wires to connect to the micro:bit's pins (you'll need an <a href="http://www.kitronik.co.uk/5601b-edge-connector-breakout-board-for-bbc-microbit-pre-built.html" target="_blank">edge connector from Kitronik</a> or elsewhere to access P13 and P15). After I had written this, <a href="https://forum.micropython.org/viewtopic.php?f=17&t=3171#p18675" target="_blank">deshipu on the MicroPython forum pointed out</a> that you don't need to use P13 and P15 for the SPI connections - it's actually just a convention - and you can use P0, P1 or P2, so the edge connector would not be needed. <i>[Don't forget to change the spi.init() parameters if you do this]</i><br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL93lqp-2R6GEZ1OEPbld_I23Xf1HJvZBNF_GvrMkRhptmP5_TSD8_2iCu4l2rSdqAmkH-UEdM8kQnR2F_4C4GBorziJZrdausthzlVg2PZsL_cye7seQMIFgR-EP4YuJa_uts_wj_JT2-/s1600/IMG_0824.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL93lqp-2R6GEZ1OEPbld_I23Xf1HJvZBNF_GvrMkRhptmP5_TSD8_2iCu4l2rSdqAmkH-UEdM8kQnR2F_4C4GBorziJZrdausthzlVg2PZsL_cye7seQMIFgR-EP4YuJa_uts_wj_JT2-/s320/IMG_0824.JPG" width="320" /></a></div>
<br />
<h2>
Python code</h2>
I used the excellent <a href="http://codewith.mu/" target="_blank">Mu editor</a> to write the code and flash it to the micro:bit.<br />
<br />
<h3>
Example 1 </h3>
The first snippet below shows how to switch on one LED in red (change the values of the variables r,g,b for other colours). For testing purposes I used a Blinkt with 8 LEDs (addressed as 0 to 7). You might need to "tweak" the indents if you copy and paste from here.<br />
<br />
<code>
from microbit import *<br />
# initialise SPI<br />
spi.init(baudrate=1000000,bits=8,mode=0, sclk=pin13, mosi=pin15, miso=pin14) # setup SPI<br />
# number of pixels in the chain<br />
num_pixels = 8<br />
on_pixel = 5 # define which pixel to turn on (e.g. 5)<br />
<br />
x = 0xff # full brightness<br />
r = 0xff # red fully on<br />
g = 0x00 # green fully off<br />
b = 0x00 # blue fully off<br />
<br />
# start frame<br />
spi.write(b'\x00\x00\x00\x00') #start frame<br />
for i in range(num_pixels): # for each pixel<br />
spi.write(b'\xff\x00\x00\x00') # all colours off<br />
spi.write(b'\xff\xff\xff\xff') #end frame<br />
<br />
# light up LEDs<br />
while True:<br />
buf=bytearray([x,b,g,r]) #send the colours in reverse order<br />
sleep(1)<br />
spi.write(b'\x00\x00\x00\x00') #start frame<br />
for i in range(num_pixels): #check each value of i<br />
if i==on_pixel:<br />
spi.write(buf)<br />
else:<br />
spi.write(b'\xff\x00\x00\x00') # off<br />
# end frame <br />
spi.write(b'\xff\xff\xff\xff') <br />
</code>
<br />
<h3>
Example 2</h3>
<div class="MsoPlainText">
In this example,<span style="font-family: inherit;"> the first
four LEDs are set to red, green, blue, white and the rest are set to purple then they are all blinked on and off every second.</span></div>
<div class="MsoPlainText">
<span style="font-family: inherit;"><br /></span></div>
<div class="MsoPlainText">
<span style="font-family: inherit;">
<code>
from microbit import *<br /># init<br />spi.init(baudrate=1000000,bits=8,mode=0, sclk=pin13, mosi=pin15, miso=pin14) #setup SPI<br /># number of pixels in the chain<br />num_pixels = 8<br /><br />#### turn all off #### </span></div>
<div class="MsoPlainText">
<span style="font-family: inherit;"># start frame<br />spi.write(b'\x00\x00\x00\x00') #start frame<br />for i in range(num_pixels): # for each pixel<br /> spi.write(b'\xff\x00\x00\x00') # all colours off<br />spi.write(b'\xff\xff\xff\xff') # end frame<br /><br />##### light up LEDs ####<br />while True:<br /> sleep(1000)<br /> spi.write(b'\x00\x00\x00\x00') # start frame<br /> spi.write(b'\xff\x00\x00\xff') # red n.b. colours sent in reverse order (b,g,r)<br /> spi.write(b'\xff\x00\xff\x00') # green<br /> spi.write(b'\xff\xff\x00\x00') # blue<br /> spi.write(b'\xff\xff\xff\xff') # white<br /> for i in range(4,8,1): #for the last 4 pixels purple<br /> spi.write(b'\xff\xff\x00\xff') # all colours off<br /> spi.write(b'\xff\xff\xff\xff') # end frame<br /><br />#all off again<br /> sleep(1000)<br /> spi.write(b'\x00\x00\x00\x00') # start frame<br /><br /> for i in range(num_pixels):<br /> spi.write(b'\xff\x00\x00\x00') # all off<br /> spi.write(b'\xff\xff\xff\xff') # end frame</span></div>
</code>
<div class="MsoPlainText">
<span style="font-family: inherit;"><br /></span></div>
<br />
<h3>
Example3</h3>
This example is best demonstrated on a long strip of LEDs (I have used 30) as it "bounces" a coloured pixel up and down the whole length of the strip. It builds on Example 1 by turning on each pixel in turn, until it reaches the end of the strip, then comes back down again. The two sleep(15) commands can be changed to alter the speed with which the pixel appears to travel up and then back down again. It will obviously still work on a shorter strip but the best effect is on a long strip. If you are feeling adventurous, you might like to experiment with changing the colour as it goes up and down. <br />
<br />
<code>
from microbit import *<br />
# initialise SPI<br />
spi.init(baudrate=1000000,bits=8,mode=0, sclk=pin13, mosi=pin15, miso=pin14) #setup SPI<br />
# number of pixels in the chain<br />
num_pixels = 30<br />
x = 0xff # brightness control<br />
r = 0xff # red value<br />
g = 0x0f # green value<br />
b = 0xab # blue value<br />
buf=bytearray([x,b,g,r]) # colour mix<br />
<br />
#### start with all pixels off ####<br />
spi.write(b'\x00\x00\x00\x00') # start frame<br />
for i in range(num_pixels): # for each pixel<br />
spi.write(b'\xff\x00\x00\x00') # all colours off<br />
spi.write(b'\xff\xff\xff\xff') # tail<br />
<br />
#### now loop up and down ####<br />
while True:<br />
for j in range(num_pixels): # going up!<br />
# light up LEDs<br />
<br />
sleep(15)<br />
spi.write(b'\x00\x00\x00\x00') # start frame<br />
for i in range(num_pixels): # check each value of i<br />
if i==j:<br />
spi.write(buf) # colour<br />
else:<br />
spi.write(b'\xff\x00\x00\x00') # off<br />
spi.write(b'\xff\xff\xff\xff') # end frame<br />
<br />
for k in range(num_pixels-1,0,-1): # going down!<br />
sleep(15)<br />
spi.write(b'\x00\x00\x00\x00') # start frame<br />
for i in range(num_pixels): # check each value of i<br />
if i==k:<br />
spi.write(buf) # colour<br />
else:<br />
spi.write(b'\xff\x00\x00\x00') # off<br />
spi.write(b'\xff\xff\xff\xff') #end frame<br />
<br />
</code><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/hpsgpnO6sEA/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/hpsgpnO6sEA?feature=player_embedded" width="320"></iframe></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/sjgpoHM9RLQ/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/sjgpoHM9RLQ?feature=player_embedded" width="320"></iframe></div>
<br />
Enjoy!<br />
<br />
<br />
<br />
<br />
<br />SteveSmythehttp://www.blogger.com/profile/10720876040452109262noreply@blogger.com3