tag:blogger.com,1999:blog-56631600551249697412020-03-18T09:00:20.253-04:00The Resistor Network This blog is a place for me to share my projects with the world. I am very interested in the field of electronics design and software development. I am interested in many aspects of technology including embedded software development, dynamic web applications and electronics design.Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.comBlogger38125tag:blogger.com,1999:blog-5663160055124969741.post-22057557779136875862019-02-12T03:04:00.000-05:002019-02-12T04:07:17.906-05:00datvideo: Storing Video on Digital Audio Tape (DAT) About a year ago I added a Digital Audio Tape (DAT) deck to my home theater system. I was turned onto the format by popular Youtuber <a href="http://www.youtube.com/channel/UC5I2hjZYiW9gZPVkvzM8_Cw">Techmoan</a> and a great video that he produced:&nbsp;<a href="http://www.youtube.com/watch?v=F4K1QKKPX_g">Digital Audio Tape: The one DAT got away</a>. I had wanted to add magnetic tape to my home theater for some time and this highly unique format seemed like a perfect choice.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-OOF5lZvhvPs/XGDunsUnNWI/AAAAAAABQNQ/RqeKcDzEGBEGfRaOIj5vqfLJxJIeRDLdQCLcBGAs/s1600/big_buck_bunny_dat_tape.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-OOF5lZvhvPs/XGDunsUnNWI/AAAAAAABQNQ/RqeKcDzEGBEGfRaOIj5vqfLJxJIeRDLdQCLcBGAs/s640/big_buck_bunny_dat_tape.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Big Buck Bunny from DAT Tape</td></tr></tbody></table>Shortly after purchasing the deck I realized that I could likely store any arbitrary binary data on the tape with the <a href="http://en.wikipedia.org/wiki/S/PDIF">S/PDIF</a> input/output. It would be really cool to merge some modern high-compression video codec with this antiquated format. Over this weekend I decided to do just that.<br /><br /><iframe width="640" height="360" src="http://www.youtube.com/embed/mOug4t5r0P4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><br />I wrote a small tool called <a href="http://github.com/aarossig/datvideo">datvideo</a> that allows storing arbitrary binary data on the tape. This tool is used to grab raw audio binary data from a sound card, search for frames of binary data, decode them and emit them into another file. This can be assembled into a pipeline to feed video data into a player such as mplayer. How cool is that?<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-5IGzCfiKlYg/XGDq6DoSfUI/AAAAAAABQMo/hRlicdZYsHQmt5pOuScKTXy782Z53FwfgCLcBGAs/s1600/sony_dtc_690_tapes_system.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://3.bp.blogspot.com/-5IGzCfiKlYg/XGDq6DoSfUI/AAAAAAABQMo/hRlicdZYsHQmt5pOuScKTXy782Z53FwfgCLcBGAs/s640/sony_dtc_690_tapes_system.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Sony DTC-690, below Marantz Blu-Ray, AV Receiver and New-Old-Stock DAT Tapes</td></tr></tbody></table>In this article, I will walk you through how I pulled this off.<br /><br /><a name='more'></a><h2>What is DAT?</h2><div><br /></div>The DAT format is especially interesting to me because it is highly advanced for the time of its debut in the late 1980's. The DAT format specifies uncompressed 48kHz, 2-channel, 16-bit PCM audio data. The fact that it is uncompressed blew my mind. This is a format that exceeds <a href="http://en.wikipedia.org/wiki/Compact_Disc_Digital_Audio">Red Book CD audio</a> specifications coupled with the charm of an unusual format. I picked up a <a href="http://www.hifiengine.com/manual_library/sony/dtc-690.shtml">Sony&nbsp;DTC-690</a> from Craigslist that was in great working order. There is just something really sweet about mid-90s Sony gear and their perfectly labelled switches. It fits in well on the shelf with my more modern Marantz components.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-W-mhujsyNgk/XGDtEajtAZI/AAAAAAABQNE/PRPYzauxqJs5HLQcASuAMVXYg27ukmppgCLcBGAs/s1600/digital_audio_tape_dat_35_65_60_denon.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-W-mhujsyNgk/XGDtEajtAZI/AAAAAAABQNE/PRPYzauxqJs5HLQcASuAMVXYg27ukmppgCLcBGAs/s640/digital_audio_tape_dat_35_65_60_denon.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">DAT Tapes - Smaller than a cassette</td></tr></tbody></table>The DAT format never caught on in the consumer market for a variety of reasons but did gain some success in the professional recording industry. The tapes were popular to use as masters. This makes sense given the fact that the data is uncompressed and stored at a high sample rate. The tapes come in a variety of lengths. I have 35, 65 and 60 minute tapes.<br /><br /><h2>Arbitrary Binary Data on a DAT Tape</h2><div><br /></div><div>The DAT tape decks include a digital input and output. These take the sample data provided to them and commit it directly to tape. This is great for audio because you can make a perfect copy of any input source. I had the idea that any arbitrary data could be fed into the digital input. It doesn't necessarily need&nbsp; to be real audio sample data.</div><div><br /></div><div>Before spending any time on this project, I worked out the data rate of the tape to make sure I could do something meaningful with it. The calculation is quite simple:</div><div><br /></div>$$ Bandwidth = SampleRate * BytesPerSample * ChannelCount\\ Bandwidth = 48000 * 2 * 2\\ Bandwidth = 187.5kB/s $$This is decently high bandwidth and likely high enough to support modern video codecs. Once I had demonstrated that there was enough bandwidth available on the tape I started to write the datvideo tool.<br /><div><br /><h2>Networking</h2></div><div><br /></div><div>In order to store arbitrary binary data on the tape, I would need a way to synchronize the receiver with the data on the tape. There could be some audio data, followed by a binary blob, followed by more audio data. The receiver does not know when it will receive a specific type of data, so a system must be devised to signal to the receiver that some binary data has begun. It would also be nice to have some error handling in the form of a CRC. Though it is unlikely that the hardware will introduce errors in the data, it is possible that something in the audio path could introduce some unexpected behavior (some gain on the signal, or the like).</div><div><br /></div><div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-bTLGzNEhcbU/XGDnF7HbMBI/AAAAAAABQMA/80EbwIkp5oMlQm6Ai13K3HmKMVBzVcDcQCLcBGAs/s1600/rfc1662_frame.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="160" data-original-width="480" height="213" src="http://3.bp.blogspot.com/-bTLGzNEhcbU/XGDnF7HbMBI/AAAAAAABQMA/80EbwIkp5oMlQm6Ai13K3HmKMVBzVcDcQCLcBGAs/s640/rfc1662_frame.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">RFC1662 Frame Format</td></tr></tbody></table></div><div>I opted to use the frame format specified by <a href="http://tools.ietf.org/html/rfc1662">RFC-1662</a> which is commonly used in PPP (point-to-point) networking. The nice thing about this format is that it is easy to implement, has some error handling and can easily mix in with other forms of data thanks to the escaping feature. More details can be found in the specification.</div><div><br /></div><h2>Audio Hardware</h2><div><br /></div><div>I used a cheap USB sound card to send/receive data to/from the DAT deck. The sound card that I selected has S/PDIF inputs and outputs. I will use a TOSLINK cable to connect this sound card to the DAT deck.</div><div><br /></div><div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-raia8UNgRJY/XGDilVEJq8I/AAAAAAABQLQ/sk-iPbNea3serEBvdW05NAIwrI0wqUyLwCLcBGAs/s1600/amazon_usb_sound_card.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-raia8UNgRJY/XGDilVEJq8I/AAAAAAABQLQ/sk-iPbNea3serEBvdW05NAIwrI0wqUyLwCLcBGAs/s640/amazon_usb_sound_card.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">USB Sound Card Ports</td></tr></tbody></table></div><div>When the binary data stored on the tape is played back, it sounds like noise and pins the level meter. It is interesting when music is interrupted with binary data and the music resumes.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-59A-Wi2hSoA/XGDpaA7QkhI/AAAAAAABQMQ/urTCxsu6kRgRAJBFLP5qaqmc2q_XltsdACLcBGAs/s1600/sony_dtc_690_level_meters.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://4.bp.blogspot.com/-59A-Wi2hSoA/XGDpaA7QkhI/AAAAAAABQMQ/urTCxsu6kRgRAJBFLP5qaqmc2q_XltsdACLcBGAs/s640/sony_dtc_690_level_meters.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Level Meters during Data Playback</td></tr></tbody></table></div><div>In order to test all of this, I created a loopback on the sound card so that I could avoid using the tape deck during development.</div><div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-xMPnMYB7nn8/XGDjjaWMONI/AAAAAAABQLo/GCAbENLNxskVE5wbORTZL0pqa8o9nQljQCLcBGAs/s1600/usb_soundcard_loopback.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-xMPnMYB7nn8/XGDjjaWMONI/AAAAAAABQLo/GCAbENLNxskVE5wbORTZL0pqa8o9nQljQCLcBGAs/s640/usb_soundcard_loopback.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">S/PDIF Loopback for Testing without the DAT deck</td></tr></tbody></table><h2>Encoding/Decoding Tapes: datvideo</h2></div><div><br /></div><div>I wrote a tool called datvideo that helps with this project. It has two modes: encode and decode. The encode mode takes an input file and generates a raw "audio" file that is intended to be recorded to the tape. The decode mode takes captured raw "audio" data and extracts data from the RFC-1662 frames. The tool can be configured to read/write from files or stdin/stdout. This makes it convenient for setting up a pipeline to read/write the tapes with. Here is the help output with a description of all command line flags that can be passed to the tool.</div><pre class="brush: bash">[andrew@andrew-laptop build]$ ./src/datvideo --help<br /><br />USAGE: <br /><br /> ./src/datvideo {-e|-d} [-n &lt;byte count=""&gt;] [-s &lt;byte count=""&gt;]<br /> [-o &lt;path&gt;] [-i &lt;path&gt;] [--] [--version] [-h]<br /><br /><br />Where: <br /><br /> -e, --encode<br /> (OR required) Put the tool in encode mode.<br /> -- OR --<br /> -d, --decode<br /> (OR required) Put the tool in decode mode.<br /><br /><br /> -n &lt;byte count=""&gt;, --buffer_size &lt;byte count=""&gt;<br /> The amount of data to buffer before writing to the out file. This is<br /> useful for streaming operations to ensure that there is always data<br /> available to read for the client without blocking.<br /><br /> -s &lt;byte count=""&gt;, --chunk_size &lt;byte count=""&gt;<br /> The size of chunks to split the file into. This is useful for<br /> streaming operations, like audio/video.<br /><br /> -o &lt;path&gt;, --output_file &lt;path&gt;<br /> The output file to use for the current operation. Do not specify for<br /> stdout.<br /><br /> -i &lt;path&gt;, --input_file &lt;path&gt;<br /> The input file to use for the current operation. Do not specify for<br /> stdin.<br /><br /> --, --ignore_rest<br /> Ignores the rest of the labeled arguments following this flag.<br /><br /> --version<br /> Displays version information and exits.<br /><br /> -h, --help<br /> Displays usage information and exits.<br /><br /><br /> A tool for storing binary data on DAT tapes.</pre><div>The following commands generate a tape image and decode a captured tape image, respectively. This encodes a photo from a local car meet that I atteded recently. These commands can be chained together with aplay and arecord. More details on that next.</div><pre class="brush: bash">[andrew@andrew-laptop build]$ ./src/datvideo --encode \<br />-i car_meet_1.jpeg -o car_meet_1.bin<br />[andrew@andrew-laptop build]$ ./src/datvideo --decode \<br />-i car_meet_1.raw -o car_meet_1.jpeg</pre><br /><h2>Raw Audio Hardware Access</h2><div><br /></div><div>Linux comes with a rich set of tools for working with audio. This demo requires the lowest level of access to the hardware, playing back and recording raw files. The aplay and arecord commands from the ALSA project allow exactly this. First, I will list out the sinks and sources. This can be done for both aplay and arecord.</div><pre class="brush: bash"># List out available audio sinks and sources<br />[andrew@andrew-laptop ~]$ aplay -l<br />**** List of PLAYBACK Hardware Devices ****<br />card 0: HDMI [HDA Intel HDMI], device 3: HDMI 0 [HDMI 0]<br /> Subdevices: 1/1<br /> Subdevice #0: subdevice #0<br />card 0: HDMI [HDA Intel HDMI], device 7: HDMI 1 [HDMI 1]<br /> Subdevices: 1/1<br /> Subdevice #0: subdevice #0<br />card 0: HDMI [HDA Intel HDMI], device 8: HDMI 2 [HDMI 2]<br /> Subdevices: 1/1<br /> Subdevice #0: subdevice #0<br />card 0: HDMI [HDA Intel HDMI], device 9: HDMI 3 [HDMI 3]<br /> Subdevices: 1/1<br /> Subdevice #0: subdevice #0<br />card 0: HDMI [HDA Intel HDMI], device 10: HDMI 4 [HDMI 4]<br /> Subdevices: 1/1<br /> Subdevice #0: subdevice #0<br />card 1: PCH [HDA Intel PCH], device 0: ALC3232 Analog [ALC3232 Analog]<br /> Subdevices: 1/1<br /> Subdevice #0: subdevice #0<br />card 2: Device [USB Sound Device], device 0: USB Audio [USB Audio]<br /> Subdevices: 0/1<br /> Subdevice #0: subdevice #0</pre><div>This allows me to determine that the hardware address of my USB sound card is "hw:2,0". It is also worth noting that I changed the default input and outputs in the pavucontrol app to S/PDIF in and out.<br /><br /><h2>Minting a Tape</h2></div><div><br /></div><div>With all of this knowledge in hand, let's chain it together to allow video streaming from the tape deck and USB sound card. I decided to use <a href="http://peach.blender.org/">Big Buck Bunny</a> as a test, which is common when dealing with media and decoders/encoders.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-QqymMrzzJI0/XGDhOoE7SEI/AAAAAAABQK8/C39zYH82rbAd6aGIJT7F7cAVyhEqSB62QCLcBGAs/s1600/big_buck_bunny.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://3.bp.blogspot.com/-QqymMrzzJI0/XGDhOoE7SEI/AAAAAAABQK8/C39zYH82rbAd6aGIJT7F7cAVyhEqSB62QCLcBGAs/s640/big_buck_bunny.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Big Buck Bunny - Short Film from the Blender Project</td></tr></tbody></table>So first, let's grab Big Buck Bunny from <a href="http://bbb3d.renderfarming.net/download.html">Renderfarming</a>. I like the1080p, 60fps version for the buttery smooth visuals :]. The video is roughly 10 minutes long which means the file needs to be around 110MB to achieve real-time playback from the tape. The file is overweight at 356MB which means that some transcoding is needed to trim it down. I noticed that there is an extra audio track. That can be stripped out too.</div><pre class="brush: bash"># Strip the AC3 audio track. MP3 is smaller.<br />[andrew@andrew-laptop Downloads]$ ffmpeg \<br />-i bbb_sunflower_1080p_60fps_normal.mp4 -map 0:0 -map 0:1 \<br />-acodec copy -vcodec copy bbb_sunflower_1080p_60fps_normal_one_audio.mp4<br /><br /># Transcode to h265 with CRF 30. This yeilds a ~100MB file.<br />andrew-desktop:Downloads andrew$ ffmpeg \<br />-i bbb_sunflower_1080p_60fps_normal_one_audio.mp4 -acodec copy \<br />-vcodec libx265 -crf 30 bbb_sunflower_1080p_60fps_normal_one_audio_h265.mp4</pre><div>I used my Mac Pro for the heavy h265 transcoding so that my tiny ThinkPad T440s didn't have to work too hard and to speed up the operation.<br /><br />Once the file was transcoded into a file of suitable size, it was placed into MPEG-TS frames. MPEG-TS is typically used for DVB systems where interruptions in the stream may happen due to interference. Once the MPEG-TS stream was ready, the file was encoded with datvideo.<br /><pre class="brush: bash"># Change the transport to MPEG-TS<br />[andrew@andrew-laptop Downloads]$ ffmpeg \<br />-i bbb_sunflower_1080p_60fps_normal_one_audio_h265.mp4 \<br />-f mpegts -vcodec copy -acodec copy \<br />bbb_sunflower_1080p_60fps_normal_one_audio_h265.ts<br /><br /># Encode the MPEG-TS to a tape image. Use 188 byte frames, which<br /># happens to be the same as the MPEG-TS frame size ;)<br />[andrew@andrew-laptop build]$ ./src/datvideo --encode \<br />-i ~/Downloads/bbb_sunflower_1080p_60fps_normal_one_audio_h265.ts \<br />--chunk_size 188 -o big_buck_bunny.bin</pre>Now that the image is ready, it is time to burn it to tape. First, the DAT deck is put into record mode. The DAT deck can be recording before starting to burn the image thanks to the RFC-1662 framing that allows detecting the start of binary data anywhere on the tape. The following aplay command is used to play the raw file to tape without modification.</div><pre class="brush: bash"># Play the track out to the DAT deck<br />[andrew@andrew-laptop build]$ aplay --format=S16_LE --device=hw:2,0 \<br />--channels=2 --rate=48000 --file-type=raw big_buck_bunny.bin</pre>Now that the MPEG transport stream has been recorded to tape, it can be played back, decoded with datvideo and played back with VLC.<br /><pre class="brush: bash"># Record from the DAT deck, piping into datvideo<br /># and decoding to a file 'bbb.ts'<br />[andrew@andrew-laptop build]$ arecord --format=S16_LE \<br />--device=hw:2,0 --channels=2 --rate=48000 \<br />--file-type=raw | ./src/datvideo --decode -o bbb.ts<br /><br /># Open the file for playback with VLC<br />[andrew@andrew-laptop build]$ vlc bbb.ts</pre>Let the file bbb.ts buffer for a few seconds and then it can be opened with VLC for playback. It is a pretty crude setup, but it works surprisingly well.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-W7PCBO24jXs/XGDxjof6fsI/AAAAAAABQNc/ChER4A8c4nwOjuIftJ5Ar3Bh50_zIc_BwCLcBGAs/s1600/big_buck_bunny_dtc_690_dat.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://2.bp.blogspot.com/-W7PCBO24jXs/XGDxjof6fsI/AAAAAAABQNc/ChER4A8c4nwOjuIftJ5Ar3Bh50_zIc_BwCLcBGAs/s640/big_buck_bunny_dtc_690_dat.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Weekend Projects :]</td></tr></tbody></table><h2>Closing Remarks</h2><div><br /></div><div>This was fun. I like doing quirky things with technology. I learned a few new things that I can probably use in my day-to-day work which is always nice. I <i>probably</i> won't go out and convert all of my favorite movies to DAT format, but this was certainly a nice way to spend a rainy weekend.</div><div><br /></div><div>I hope you enjoyed my zany use of technology and perhaps are inspired to do something yourself. I know one idea that comes to mind is TCP/IP over TOSLINK optical audio cables via the datvideo tool shown above. It should work rather well.</div><div><br /></div><div>Comments always welcome. Thanks for reading!</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com10tag:blogger.com,1999:blog-5663160055124969741.post-41466400548835688582018-12-04T01:44:00.000-05:002018-12-08T01:18:08.722-05:00PortL2 - Portable Electric Vehicle Charger <div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div>Over the past year I have been tossing around the idea of extending the electric range of my <a href="http://en.wikipedia.org/wiki/Cadillac_ELR">Cadillac ELR</a>. I have wanted to build a large scale lithium battery system for some time and this seemed like a great way to learn about the technology.<br /><br />I had originally started with the idea of charging the ELR hybrid pack while driving. The plan was to use a 48V to 390V (96S) lithium battery charger. Accessing the high voltage bus in the car proved to be challenging and made this approach too difficult for my taste. This made me decide build a portable L2 charger using a 240V inverter and an <a href="http://www.openevse.com/">OpenEVSE</a>&nbsp;instead.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-f2gAM7cNJik/XAX1mcp_cwI/AAAAAAABNZ0/J0ZfAHMnz4IVVeNdqekrsOKTE_LoNYZiwCLcBGAs/s1600/cadillac_elr_portl2_mountains.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://4.bp.blogspot.com/-f2gAM7cNJik/XAX1mcp_cwI/AAAAAAABNZ0/J0ZfAHMnz4IVVeNdqekrsOKTE_LoNYZiwCLcBGAs/s640/cadillac_elr_portl2_mountains.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Cadillac ELR Charging at Sierra Trail Head, No L2 Chargers for Miles</td></tr></tbody></table>I used four battery modules from an old <a href="http://www.enginer.us/products/conversion_kit.php">Enginer PHEV Conversion Kit</a>, with a design capacity of more than 2kWh each. The total capacity is around 7kWh now as the cells are several years old and have seen some use. The results so far have been great with the system capable of charging the car to more than 60%.<br /><br /><iframe allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/mL_ghG3cvac" width="640"></iframe> <br /><br />In addition to being a great portable car charger, this has proven to serve well in powerwall applications. I am able to shift on-peak loads to off-peak, thus saving money and reducing reliance on dirty sources of power.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-amoBZNJ2o70/XAX9ynzLhnI/AAAAAAABNas/QcYpJiPCLJ8lD6WRAx_gF8FMLbMB70T4ACLcBGAs/s1600/portl2_before_installation_elr.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://3.bp.blogspot.com/-amoBZNJ2o70/XAX9ynzLhnI/AAAAAAABNas/QcYpJiPCLJ8lD6WRAx_gF8FMLbMB70T4ACLcBGAs/s640/portl2_before_installation_elr.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">PortL2 Before Transferring to the Car</td></tr></tbody></table>I used an <a href="http://www.orionbms.com/products/orion-bms-jr/">Orion Jr. BMS</a> to monitor the pack, perform cell balancing and monitor temperatures. The video above has a complete build log and you can find more details in the rest of this article.<br /><br /><a name='more'></a><h3>Early Stages</h3><br />This idea has been on my mind since purchasing the car in November 2017. It is a really fun to car to drive and I always thought it would be cool to have a way to push the electric range a little further. Don't get me wrong: the gasoline engine is perfectly capable of driving the car as far as I need to, but this just seemed like an extra challenge and a great way to learn about lithium battery energy storage systems.<br /><br />I started by researching high voltage DC-DC converters. There are very few options on the market and usually with a power envelope that is too small or a price that is too high. One such example comes from TDK Lambda: the <a href="http://www.us.tdk-lambda.com/lp/news/press-release-bidirectional-dc-dc-converter-for-use-with-energy-storage-battery-systems/">EZA2500</a>&nbsp;supplies a peak power of 2.5kW which is a little on the low side for this application, but could work. The unfortunate reality is that the price is just too high with an MSRP in the thousands of dollars. For a crazy garage experiment, that is just too much money to spend on something that may not even work.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-3HAkKSnT20I/XAX7AOHsnKI/AAAAAAABNaI/av3irrEhQ-IDBSMM4woIAM-4j4qgf_L9wCLcBGAs/s1600/enginer_phev_disassembled.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://2.bp.blogspot.com/-3HAkKSnT20I/XAX7AOHsnKI/AAAAAAABNaI/av3irrEhQ-IDBSMM4woIAM-4j4qgf_L9wCLcBGAs/s640/enginer_phev_disassembled.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Disassembled Enginer System</td></tr></tbody></table>I kept my hunt for a suitable DC-DC converter alive and would periodically check dubious sources like eBay and Alibaba. One fine day, this Enginer PHEV Conversion Kit popped up. It was actually two kits! The components were in a slight state of disarray but everything seemed to be there including a DC-DC converter and four 2kWh LiFePO4 battery modules. This was all available at the bargain price of just $500, local pickup in Los Angeles only.<br /><br />I did some research on this kit and found that it had been installed in a Nissan Leaf as well, which has the same 96S LiMn2O4 configuration that the Chevy Volt and Cadillac ELR have. This sealed the deal for me: prior art of a working installation in a similar vehicle.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-dVdrv03L8cQ/XAX78wDu-JI/AAAAAAABNaU/3_khxEFe3y0jymd6-CZeG6YoN7we1jOfQCLcBGAs/s1600/ebay_auction_screenshot.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="524" data-original-width="1600" height="208" src="http://3.bp.blogspot.com/-dVdrv03L8cQ/XAX78wDu-JI/AAAAAAABNaU/3_khxEFe3y0jymd6-CZeG6YoN7we1jOfQCLcBGAs/s640/ebay_auction_screenshot.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Enginer PHEV Conversion Kit eBay Auction</td></tr></tbody></table><div>I offered asking price, the seller gladly accepted and I planned a trip from the San Francisco Bay Area down to Los Angeles and back in one day. I drove my Tesla Model S, supercharging 3 times while eating and resting. I left my house at 5AM and returned home at 11PM, with nearly 500lbs of cargo in the trunk. I went straight to bed and unloaded the car the next day.</div><div><br /></div><div>Some of the components were in obviously poor condition. The system actually came with 5 battery modules, but one was in very poor condition. I recycled it. The other four were in great shape. I benchmarked them and measured health to be around 85-90%. There was also a BMS that didn't seem to do much of anything. I recycled any components that were damaged.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-Ukheo379F8k/XAX839gHAWI/AAAAAAABNac/f_IGValY63IFkvImnl3NMQkBcFFfbamtgCLcBGAs/s1600/elr_high_voltage_bus.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://2.bp.blogspot.com/-Ukheo379F8k/XAX839gHAWI/AAAAAAABNac/f_IGValY63IFkvImnl3NMQkBcFFfbamtgCLcBGAs/s640/elr_high_voltage_bus.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Cadillac ELR High Voltage Bus on the Accessory Power Module (12V DC-DC Converter)</td></tr></tbody></table>I disassembled the trunk of my ELR and removed the lid from the APM (Accessory Power Module). This is a 12V DC-DC converter that supplies power on the 12V bus from the 390V bus. You can think of this as a replacement for the alternator present in most cars.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-5hwKOlLwgT8/XAX9V6oG9eI/AAAAAAABNak/frnkgAvG4HQeB1LlvKCAIYJVLEBiXcxGACLcBGAs/s1600/elr_390V_measurement.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://4.bp.blogspot.com/-5hwKOlLwgT8/XAX9V6oG9eI/AAAAAAABNak/frnkgAvG4HQeB1LlvKCAIYJVLEBiXcxGACLcBGAs/s640/elr_390V_measurement.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Fused Cadillac ELR High Voltage Bus, Measured with my DMM at 390.5V</td></tr></tbody></table>I spent a couple of hours reverse engineering the pinout of the Enginer DC-DC converter to find the enable line. One of the components missing from the auction was the on-off switch. It wasn't too hard to find that there is an internal relay that is engaged with 12V. I used an external sealed lead-acid battery to power the relay in the converter. It sprung to life and displayed 248V on my multimeter.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-YSYPIbJTfkE/XAX_XJsOxRI/AAAAAAABNa4/WwzOHJx5yA8wQUnVWFDV979_umpZfJDjwCEwYBhgL/s1600/enginer_reverse_engineering.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://3.bp.blogspot.com/-YSYPIbJTfkE/XAX_XJsOxRI/AAAAAAABNa4/WwzOHJx5yA8wQUnVWFDV979_umpZfJDjwCEwYBhgL/s640/enginer_reverse_engineering.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Enginer DC-DC Converter Powered On, Bench Testing</td></tr></tbody></table><div>I was able to tune this voltage up and down using some trimmers inside the converter. I used four incandescent lamps as a dummy load for brief intervals to calibrate the instrument. I also verified that the capacitors on the output stage are rated at 400V which means they can tolerate the high voltage of the car.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-q5zR81R1u-4/XAYALOuYDaI/AAAAAAABNbA/bRPBK3wQsh8j_mSZ9oYmo41W3BqWoJmuwCLcBGAs/s1600/enginer_dummy_load_light_bulbs.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://4.bp.blogspot.com/-q5zR81R1u-4/XAYALOuYDaI/AAAAAAABNbA/bRPBK3wQsh8j_mSZ9oYmo41W3BqWoJmuwCLcBGAs/s640/enginer_dummy_load_light_bulbs.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Enginer Converter Dummy Load Test, 1.35A at 228VDC</td></tr></tbody></table>With one arm behind my back, I carefully wired the output of the DC-DC converter to the HV bus of the car. I connected the 12V battery to engage the converter and... well.. nothing really. I had my multimeters connected to measure voltage and current. I found that the voltage was at 390V but no current was flowing from the DC-DC converter into the car.<br /><br />I tried adjusting those trimmer potentiometers and suddenly current began to flow, but not very much. I was barely able to get the current over one hundred milliamps. I began to entertain the idea that there might be a diode on the HV bus to prevent current from flowing backwards into the pack. I decided to modulate some 12V loads in the car to see if I could change the amount of current flowing from the DC-DC converter into the car. Sure enough, engaging the fans at full speed, both heated seats and the rear defroster allowed the current to increase, but not by much. At this point, it was obvious that I was doing nothing but converting 48V to 390V, then back down to 12V to run the various loads in the car. Not a single electron was making it back into the high voltage battery of the car.<br /><br />I gave up on the project and put the idea to rest for a few weeks.<br /><br /><h3>New Beginnings</h3></div><div><br /></div><div>I don't give up easily. My work cooled off a bit at the office and I had some time to think about this again. I considered every possible way of connecting to the high voltage bus of the car but ultimately decided that any approach would be a little more permanent than I would like. I didn't want to modify this ELR. It is a nice car to begin with and quite rare at less than 3000 total units produced. This prompted the idea of simply building a portable L2 charger. It would be completely isolated from the car and could easily be taken out when I ultimately get bored of this project.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-3zhCojC78zY/XAYEhpls2JI/AAAAAAABNbM/qFmaPTa1JS8KQLhGQ1uzxEleOmIwCJuqQCLcBGAs/s1600/elr_portl2_battery_frame.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://2.bp.blogspot.com/-3zhCojC78zY/XAYEhpls2JI/AAAAAAABNbM/qFmaPTa1JS8KQLhGQ1uzxEleOmIwCJuqQCLcBGAs/s640/elr_portl2_battery_frame.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">PortL2 Frame Members</td></tr></tbody></table><div>I sketched out some ideas in my notebook and decided on a simple wooden frame to hold the batteries and electronics together. Wood is easy to work with, relatively light and should be plenty strong. The basic idea was to build a simple frame with two rails that have spans across the middle holding the batteries, inverter and wiring. I used threaded rods to hold everything together so that it could be disassembled and reassembled inside the car.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-fxfKIE-RnhM/XAYE95-1A_I/AAAAAAABNbU/FuDCmLHpOUcDwcX4bUAXOuSA3FTkVRVdQCLcBGAs/s1600/port_l2_early_frame_assembled.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-fxfKIE-RnhM/XAYE95-1A_I/AAAAAAABNbU/FuDCmLHpOUcDwcX4bUAXOuSA3FTkVRVdQCLcBGAs/s640/port_l2_early_frame_assembled.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Early Stages of the Frame</td></tr></tbody></table><div>The other advantage of using threaded rod is that the construction could tolerate some small errors. I loaded the batteries onto the frame and found it to be perfectly strong enough for this static load. I also stood on top of the frame and found no issue. The obvious difference between assembling this on the floor and in a car is that the car is in motion, but so far everything has been holding up well.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-uMa4V8aIAP8/XAYFkbfoQEI/AAAAAAABNbc/FBgeYnOrkFodIur5jwhjKMjrx7-I1FmHwCLcBGAs/s1600/battery_frame_proto.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-uMa4V8aIAP8/XAYFkbfoQEI/AAAAAAABNbc/FBgeYnOrkFodIur5jwhjKMjrx7-I1FmHwCLcBGAs/s640/battery_frame_proto.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Batteries Resting on the Frame</td></tr></tbody></table><h3>The Approach</h3><div><br /></div><div>I decided on an inexpensive 4kW inverter from eBay. I checked reviews on YouTube and found that people were happy with the quality. The car can charge at a peak 3.3kW so there is some margin left over. I chose a single phase inverter to keep the cost down as I didn't plan to use this with any other appliances.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-67fe6WNyG-0/XAYHNxiJ3uI/AAAAAAABNbo/TrDUcJ1oLOcduZDqTJa9tZlcPdT3D8c4ACLcBGAs/s1600/4kw_inverter.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://2.bp.blogspot.com/-67fe6WNyG-0/XAYHNxiJ3uI/AAAAAAABNbo/TrDUcJ1oLOcduZDqTJa9tZlcPdT3D8c4ACLcBGAs/s640/4kw_inverter.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">4kW Inverter</td></tr></tbody></table><div><div><div>I added an <a href="http://powerwerx.com/anderson-sb-connectors-sb175-175amp">Anderson SB 175A</a> connector to keep the inverter modular and wired the NEMA 14-50 connector. The one caveat here is that the NEMA 14-50 connector is a split phase connector and this is not a split phase inverter. The J1772 connector found on American electric vehicles has only a line and neutral pin which is derived from the two outer hot pins of the NEMA 14-50. I decided to simply not connect the neutral line of the connector. This could use a warning sticker that this is only for use with an EV charger as this is not to any code or specification.</div><div><br /></div></div></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-8RqGmPtMavs/XAYIKJvKakI/AAAAAAABNbw/XZwjhkVoo9wKWnNSzSeTGs_rHE3LZPDeACLcBGAs/s1600/4kw_inverter_wiring.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://3.bp.blogspot.com/-8RqGmPtMavs/XAYIKJvKakI/AAAAAAABNbw/XZwjhkVoo9wKWnNSzSeTGs_rHE3LZPDeACLcBGAs/s640/4kw_inverter_wiring.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">4kW DC and AC Wiring Completed</td></tr></tbody></table><div>I painted the entire frame black and mounted the inverter on threaded rods next to the battery mounts. The wiring board was also prepared to be mounted vertically between the inverter and batteries, The quick black paint job turned out to be perfect for the project.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-sH59GrNGirY/XAYJfzhyMiI/AAAAAAABNb8/aVr6dA6Cm5UqyCZi0ttoudojzFwKJ8ZhgCLcBGAs/s1600/black_portl2_frame_inverter.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://3.bp.blogspot.com/-sH59GrNGirY/XAYJfzhyMiI/AAAAAAABNb8/aVr6dA6Cm5UqyCZi0ttoudojzFwKJ8ZhgCLcBGAs/s640/black_portl2_frame_inverter.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Painted Frame with Inverter</td></tr></tbody></table><div>The next step was to start on the DC wiring. I used two bus bars for 48V and Ground. The battery modules will be wired in a 2S2P configuration (which is ultimately a 16S2P battery). The bus bars allow the parallel connection between the batteries and the rest of the components. The battery management system uses a shunt to measure current in the pack and I opted for a 250A system fuse.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-MEpZJH-lOw0/XAYLiFBUU_I/AAAAAAABNcI/11UeumH7Df8Oo1C6Bp3kmnsoAt61JVbIgCLcBGAs/s1600/portl2_48v_wiring_components.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://3.bp.blogspot.com/-MEpZJH-lOw0/XAYLiFBUU_I/AAAAAAABNcI/11UeumH7Df8Oo1C6Bp3kmnsoAt61JVbIgCLcBGAs/s640/portl2_48v_wiring_components.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">PortL2 with some Low Voltage Components Ready for Wiring</td></tr></tbody></table><div>During this entire build, I spent hours pre balancing the batteries. It was quite clear that they were severely out of balance, but thankfully all cells were within a healthy voltage range. I used my <a href="http://www.progressiverc.com/icharger-4010duo.html">iCharger 4010 Duo</a> to balance two modules at a time. This took hours to complete. The balance current is limited to 1A which means that the final stages of charging took a very long time as the weakest cells of the packs were charged and excess energy was burnt off through the balance leads for other cells that had already reached capacity.</div><div><br /></div><div>Thankfully this could run while I was working on other tasks and kept the garage a little warmer :]</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-XY2pyiUFCeY/XAYM8z0Tr3I/AAAAAAABNcU/3ovjwZLe1IwlQ4qpAp53_F76F-xj4A3xwCLcBGAs/s1600/battery_pre_balancing.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://2.bp.blogspot.com/-XY2pyiUFCeY/XAYM8z0Tr3I/AAAAAAABNcU/3ovjwZLe1IwlQ4qpAp53_F76F-xj4A3xwCLcBGAs/s640/battery_pre_balancing.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Pre Balancing the Batteries</td></tr></tbody></table><div>I assembled two wiring harnesses for the Orion Jr. BMS: input/output and cell balancing. The input/output connector supplies power to the BMS and a connection to relays and temperature sensors. The balancing cable is used to measure and balance each individual cell in the pack. I removed several unused pins from the connector to reduce clutter in the installation.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-O7gWD0ybuVk/XAYN1a04m1I/AAAAAAABNcc/-gNmAgKqGFomShfU9NuzoPJIJyOi_lXiQCLcBGAs/s1600/orion_jr_io_connector.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://2.bp.blogspot.com/-O7gWD0ybuVk/XAYN1a04m1I/AAAAAAABNcc/-gNmAgKqGFomShfU9NuzoPJIJyOi_lXiQCLcBGAs/s640/orion_jr_io_connector.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Removing Unused I/O Pins from the Connector</td></tr></tbody></table>The Orion BMS comes with a comprehensive wiring manual which explains fuse placement and tradeoffs that can be made in battery pack design. It is quite an interesting read, as far as manuals go. I spent a few more hours wiring the following components:<br /><div><ul><li>Pack Current Shunt</li><ul><li>Measures all current flowing into and out of the pack to allow the BMS to implement state of charge (SoC) estimation and soft fuses for over discharge and over charge currents.</li></ul><li>Pack 250A Fuse</li><ul><li>A safety fuse in the event that I do something stupid and short out the pack. Something nasty would happen if a short happened, beginning with perhaps the best welder I have ever seen and ending with fire. It is wired as recommended in the wiring manual after the shunt.</li></ul><li>Load Relay, Chained</li><ul><li>A large 250A contactor is used to allow the BMS to switch off the load as needed. This can happen if a fault occurs or if the pack has been discharged. This contactor is too large to be driven by the BMS directly, so it is chained through a smaller relay with a 50mA coil.</li></ul><li>Charge Relay</li><ul><li>A small 40A relay is used to allow the BMS to switch off the charger as needed. This can happen if a fault occurs or if the pack is overcharged. It is similar to the discharge relay and allows the BMS to safely enable and disable loads.</li></ul><li>On/Off Switch</li><ul><li>I used a large circuit breaker as a toggle switch for the whole system. It is a bit overkill as the circuit breaker just applies 48V to a general purpose input on the BMS, which then toggles the load relay. I like the look and feel of the switch and already had it on hand.</li></ul></ul><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-Y11w0-XmpdY/XAYQhVKDHpI/AAAAAAABNco/1MPa61rjgUc7gMxhpM1Y4x2TZ8v2m4w7ACLcBGAs/s1600/port_l2_orion_jr_wiring_complete.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://4.bp.blogspot.com/-Y11w0-XmpdY/XAYQhVKDHpI/AAAAAAABNco/1MPa61rjgUc7gMxhpM1Y4x2TZ8v2m4w7ACLcBGAs/s640/port_l2_orion_jr_wiring_complete.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Finished Wiring</td></tr></tbody></table>After all of this was completed, the system could be transferred into the car.</div><div><br /></div><h3>Performance</h3><div><br /></div><div><div>So how well does all of this work? I am pleasantly surprised. In one test, I was able to add 4kWh of energy to the car before stopping the test. I will need to do more monitoring and BMS tuning before I feel comfortable putting this system through a full charge and discharge cycle but 4kWh is very promising.</div></div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-WbFfkjb-y6k/XAYRoM8z7fI/AAAAAAABNc0/NXhLIfn9HJ8BCJbSqAW3u2oHgiQxR8wRwCLcBGAs/s1600/portl2_cadillac_elr.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-WbFfkjb-y6k/XAYRoM8z7fI/AAAAAAABNc0/NXhLIfn9HJ8BCJbSqAW3u2oHgiQxR8wRwCLcBGAs/s640/portl2_cadillac_elr.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">PortL2 After Transfer to the ELR</td></tr></tbody></table>Initially I didn't notice much of a difference in handling, but with more driving I noticed that the weight distribution is improved significantly. The Cadillac ELR is a heavy car at just more than 4000 lbs with 61% of that distributed to the front of the car. The ideal weight distribution for handling is 50:50.<br /><div><br /></div><div>Adding an extra 280lbs to the trunk seems to have reduced understeer, which is absolutely hilarious. I have no doubt that it has a negative impact on acceleration. The ELR is not a very fast car, so it's not something I would notice. I was able to merge on the freeway just as easily as before. I didn't notice any change in stance, so the suspension seems fine with the added weight. This is not terribly surprising as the cargo capacity of this car is more than 800lbs.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-0QbAExH3sdo/XAYTJPg74yI/AAAAAAABNdA/wJWW5FR6X8QmHMCpvDiJmMO8xgK3EPT_QCLcBGAs/s1600/elr_mountain_driving_charging.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-0QbAExH3sdo/XAYTJPg74yI/AAAAAAABNdA/wJWW5FR6X8QmHMCpvDiJmMO8xgK3EPT_QCLcBGAs/s640/elr_mountain_driving_charging.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">ELR at Sierra Trail Head</td></tr></tbody></table><div>It is rather funny to see this cable emerge from the trunk only to be plugged back into the car. I also look forward to lending my first charge to a fellow EV owner. I have a friend who converted a first generation Chevy Volt to the European Ampera model. It will be great fun to snap a picture of an ELR charging an Ampera at the next cars and coffee meet.</div><div><br /></div><div>The thermal performance of this system is still something I am monitoring. The inverter is &gt;85% efficient, but I have not characterized the exact performance I am seeing. In any event, assuming 15% loss, this would result in 400W+ of radiated heat. The trunk should be left open to vent the system. The BMS is also configured to stop the load at 40C, but this may need some tuning. The summer will certainly bring increased temperatures.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-ppR6Bsw5RwE/XAYUXm3T8kI/AAAAAAABNdM/0NWKhEGGg14-bF6xTgp6oKiILC16BEHpQCLcBGAs/s1600/orion_jr_software.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-ppR6Bsw5RwE/XAYUXm3T8kI/AAAAAAABNdM/0NWKhEGGg14-bF6xTgp6oKiILC16BEHpQCLcBGAs/s640/orion_jr_software.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Orion Jr. Configuration</td></tr></tbody></table><div>The Orion Jr. comes with comprehensive software for configuring every aspect of the system, reviewing faults and streaming live data. It is impressive software that runs well on my ThinkPad under Linux.</div><div><br /></div><div>I am using a 600W eBike charger from Amazon that is working well so far. The only downside is that it will take more than 16 hours to recharge this pack at a 600W charge rate.</div><div><br /></div><h3>Closing Remarks</h3><div><br /></div><div>I hope you enjoyed this project of mine. I certainly learned a lot, which is always a goal when spending time on projects like this. It is clear that the practicality of this system is limited given that it consumes most of the trunk space. This is a weekend car though, so I am happy with the tradeoff. I look forward to attempting some longer electric-only road trips in the ELR and continuing on the quest towards the holy grail of 250+ MPG (the maximum that the car reports).</div><div><br /></div><div>When I inevitably get bored of this, I can always remove it from the car and continue to use the battery system for peak levelling some loads, like my home office and living room electronics. If you have been following my blog for any amount of time, you will have noticed that each year I build a bigger and more sophisticated battery than the previous year. The capacity is growing at a rate of nearly 10x per year. I look forward to next year.</div><div><br /></div><div>I do not advise tackling a project like this without the necessary training, expertise, experience and research. Even then, don't try this at home. I was playing with lethal voltages at times and the safety of putting all of this on a wooden frame in a car going down the freeway at 65mph is dubious at best. This is purely a recollection of my own learning experiences and not a guide for you to build your own. Stay safe out there.</div><div><br /></div><div>I hope you enjoyed the read and look forward to your comments. Onwards to more great road trips and fun projects!</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-FYvbuy-o4Zw/XAYaarYc-5I/AAAAAAABNdY/6aZrvRNZ9_UtDGua3ETUnMvuGKXhtHCFgCLcBGAs/s1600/IMG_20181202_133658.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://4.bp.blogspot.com/-FYvbuy-o4Zw/XAYaarYc-5I/AAAAAAABNdY/6aZrvRNZ9_UtDGua3ETUnMvuGKXhtHCFgCLcBGAs/s640/IMG_20181202_133658.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Nice View from Sierra Trails near San Jose, CA</td></tr></tbody></table></div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com11tag:blogger.com,1999:blog-5663160055124969741.post-52017419912023333852018-02-18T14:41:00.002-05:002018-02-18T14:41:42.352-05:00Groking the Cadillac ELR Those who know me well know that I have a passion for electric vehicles (EVs). I own a Tesla Model S, have driven it across the US and Canada <i>twice</i>, and am currently designing an electric bicycle with design cues taken from larger EVs and based on lessons learned from my electric skateboard project. I really can not scratch the itch to drive and learn more about EVs.<br /><br />Last fall I purchased a <a href="http://en.wikipedia.org/wiki/Cadillac_ELR">Cadillac ELR</a>&nbsp;to keep my Tesla company in the garage. I am a huge fan of this car for the striking good looks and the fact that it is a 2-door coupe. I had also never experienced a PHEV (Plug-In Hybrid Electric Vehicle) and <i>really</i> wanted to try out the GM Voltec platform. The prices of the Cadillac ELR are plummeting due to their relatively short-lived time on the market and the fact that they are a little unknown in the eyes of the average consumer.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-4nx-yimyS1M/WokXZedlfmI/AAAAAAAA_vQ/JFjtRhhTMs8ZTe1qQ4DGrtdzecPRyPLXQCLcBGAs/s1600/elr_at_dealership.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://2.bp.blogspot.com/-4nx-yimyS1M/WokXZedlfmI/AAAAAAAA_vQ/JFjtRhhTMs8ZTe1qQ4DGrtdzecPRyPLXQCLcBGAs/s640/elr_at_dealership.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Cadillac ELR at Cardinale GMC in Seaside, California</td></tr></tbody></table>I did not buy this car with the intention of merely letting it sit in the garage though. One of the great features of the Tesla Model S is that it lets you "nerd out" with all kinds of stats about your trip. You get total energy consumption in kWh, efficiency in Wh/mile, range estimates based on change in elevation/speed and a whole lot more that makes driving a true delight.<br /><br />The Cadillac ELR (and Chevy Volt - they share the same powertrain and a similar UI) tend to shield you from these details. I wanted more and without having to do quick mental math based on the limited information available from the built-in infotainment system.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-R2lJKmmXVnk/Wok6bNyi74I/AAAAAAAA_x8/Hr_9PmXfvtU3cGu5YEbZHGC164RcUiHtQCKgBGAs/s1600/IMG_20180217_215049.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://4.bp.blogspot.com/-R2lJKmmXVnk/Wok6bNyi74I/AAAAAAAA_x8/Hr_9PmXfvtU3cGu5YEbZHGC164RcUiHtQCKgBGAs/s640/IMG_20180217_215049.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Speed, Distance, Total Energy and Wh/mi Top | State of Charge (SoC) kWh Left | Speed mph Right</td></tr></tbody></table>I spent about a week decoding traffic on the CAN bus of the ELR in an effort to find a few signals: speed, state of charge (SoC) in kWh, and odometer or a trip of some sort. Once I found these fields, I built a UI with PyQt to render statistics in real-time.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-5GlrYn_p2ZE/Wok8APyq9iI/AAAAAAAA_yk/Du3q2NxAof4CTv97uHEcRydV8Eb615mGACKgBGAs/s1600/IMG_20180217_171438.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://2.bp.blogspot.com/-5GlrYn_p2ZE/Wok8APyq9iI/AAAAAAAA_yk/Du3q2NxAof4CTv97uHEcRydV8Eb615mGACKgBGAs/s640/IMG_20180217_171438.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">CAN Bus Interface Cable</td></tr></tbody></table>I packaged it all up into a neatly organized cable and used a <a href="http://en.wikipedia.org/wiki/Sony_Vaio_UX_Micro_PC">Sony Vaio UX</a> to read data from the CAN buses and visualize it on a small display. Continue reading for more details.<br /><br /><a name='more'></a><br /><h2>Decoding the CAN bus(es)</h2><div><br /></div><div>The first thing I did before spending any time on a fancy UI was make sure that I would be able to read the required data from a CAN bus in the car. This vehicle has two OBD-II-style connectors: one is in the drivers foot well and another in the passenger foot well.</div><div><br /></div><div>I decided to use the <a href="http://www.amazon.com/ViewTool-Interface-Raspberry-Connector-isolation/dp/B00ZW8N930">Viewtool Ginkgo USB-CAN interface</a>. Unfortunately, Linux support comes in the form of a binary blob shared object, but I was able to make it work. I wrote a wrapper around this in C to make it easier to call from Python with <a href="http://cffi.readthedocs.io/en/latest/">CFFI</a>. Before ordering any connectors and spending time building a neat cable, I just probed the connectors using spade connectors with one leg removed.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-1X2ehE4ciC0/Wok-8sqPgSI/AAAAAAAA_y0/LPhnAW35nHEjd7FzWnPrRIc-p09TeoBIwCKgBGAs/s1600/IMG_20180215_211258.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://4.bp.blogspot.com/-1X2ehE4ciC0/Wok-8sqPgSI/AAAAAAAA_y0/LPhnAW35nHEjd7FzWnPrRIc-p09TeoBIwCKgBGAs/s640/IMG_20180215_211258.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">"Half-Spade" Connectors, Inserted into the OBD-II connector. It's a great fit!</td></tr></tbody></table><div>I started out with the OBD-II connector in the drivers foot well. I went for a brief drive around my neighborhood, stepping heavily on the accelerator in order to generate some waveforms that I can try to reason with.</div><div><br /></div><div>I wrote a python script that takes all of the captured data and for each CAN address tries to interpret the packets in a variety of ways. Data is sent on the bus in "network-order", aka big-endian. If a CAN packet contains a multi-byte word, it is sent most-significant byte first. This is also obvious from just inspecting the captured data in a hex editor. The bits most frequently change in the later-received bytes.</div><div><br /></div><div>The script tries to parse the bytes in a few ways:</div><div><ul><li>Each byte individually</li><li>First byte &lt;&lt; 8 | second byte (and third/fourth, fifth/sixth, etc)</li><li>First byte &lt;&lt; 24 | second byte &lt;&lt; 16 | third byte &lt;&lt; 8 | fourth byte (and fifth/sixth/etc.)</li></ul><div>Each of these interpretations are plotted with matplotlib on a separate plot. This generates about 500 images for each trace of CAN data on the bus. It is very easy to open all of them with an image viewer and cull off the obviously useless messages.</div></div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-36-37-UIR4o/WolBhvKTedI/AAAAAAAA_zA/7bYqzJRRaLY7h7qM5MW54tqFaMxCmZHigCLcBGAs/s1600/elr_pri_0.log_plot_348_2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="800" data-original-width="1600" height="320" src="http://2.bp.blogspot.com/-36-37-UIR4o/WolBhvKTedI/AAAAAAAA_zA/7bYqzJRRaLY7h7qM5MW54tqFaMxCmZHigCLcBGAs/s640/elr_pri_0.log_plot_348_2.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Contents of 0x348 plotted</td></tr></tbody></table>The script outputs a huge number of interesting plots like the one above. This one is clearly speed in units of 0.01mph given that it correlates with my short drive around the block.<div><br /></div><div>There are a lot of less interesting plots to wade through, such as the following:</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-8TSyCHH7xd0/WolCClvXUxI/AAAAAAAA_zI/UKQ9SBhpg6AbZ7Q4RS5COAxyHk2a77ZxACLcBGAs/s1600/elr_pri_0.log_plot_589_2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="800" data-original-width="1600" height="320" src="http://2.bp.blogspot.com/-8TSyCHH7xd0/WolCClvXUxI/AAAAAAAA_zI/UKQ9SBhpg6AbZ7Q4RS5COAxyHk2a77ZxACLcBGAs/s640/elr_pri_0.log_plot_589_2.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Contents of 0x589 plotted</td></tr></tbody></table><div>I have no idea what this is. It could be a misinterpreted packet or something I just don't understand about the vehicle. In any event, it is easy to say that this is neither speed nor state of charge. It is quite easy to search through a few hundred of these in just a few minutes sorting out the unknown packets from the packets where some further interpretation might yield a useful data point.</div><div><br /></div><h2>What data exists?</h2><div><br /></div><div>The short answer is "a lot". I found curves that vaguely resemble voltage, something that looks like temperature, throttle position and I even managed to decode GPS latitude and longitude which was super interesting.</div><div><br /></div><div>The GPS lat/long are encoded as a 31-bit signed milliarcsecond values. I had to extend the sign to the 32nd bit in order to get the value to match to my current location. Super unusual, but very interesting. The GPS is of particular interest because it may be quite precise if it is performing dead reckoning. It would allow the location to be very accurate even in locations with poor GPS signal strength such as urban canyons or long tunnels.</div><div><br /></div><div>Enough about GPS though, I was able to find all of the data that I wanted between the two OBD-II ports so here's a quick breakdown:</div><div><ul><li>Primary, 0x3E9, bytes 0 - 1 Speed in 1/100mph</li><li>Primary, 0x1A1, byte 6, Throttle Position Percent, 0-254</li><li>Primary, 0x32A, bytes 0 - 3 GPS Latitude, 31-bit signed milliarcseconds</li><li>Primary, 0x32A, bytes 4 - 7 GPS Longitude, 31-bit signed milliarcseconds</li><li>Primary, 0x120, bytes 0 - 3, Odometer in 1/100mi</li><li>Alt, 0x20A, byte 7, State of Charge Percent, 0-254</li></ul><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-8QM06eySdjM/WolZSThigII/AAAAAAAA_zc/9mzg_gadfbskR9I25Wd5iD1htd_AhkmjgCLcBGAs/s1600/IMG_20180215_203725.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://4.bp.blogspot.com/-8QM06eySdjM/WolZSThigII/AAAAAAAA_zc/9mzg_gadfbskR9I25Wd5iD1htd_AhkmjgCLcBGAs/s640/IMG_20180215_203725.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Lots of data analysis :]</td></tr></tbody></table><h2>Improving the Hardware</h2></div><div><br /></div><div>Once I had proven that the necessary data was available, I ordered two <a href="http://www.amazon.com/gp/product/B06WW63TCM">OBD-II connectors</a>&nbsp;and wired them up. The primary OBD-II connector is just the standard ISO pinout:</div><div><br /></div><div><ul><li>Pin 4 - Ground</li><li>Pin 6 - Can High</li><li>Pin 14 - Can Low</li></ul><br /><div>The secondary connector is proprietary and I found some details on another webpage:&nbsp;<a href="http://evtools.info/ChevyVoltOBD2CAN.html">EVtools.info - Chevy Volt OBD2 CAN Data</a>. The alternate connector uses the following pinout:</div></div><div><br /></div><div><ul><li>Pin 3 - Can High</li><li>Pin 4 - Ground</li><li>Pin 11 - Can Low</li></ul><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-HQtanE22MeI/WolcOyoySmI/AAAAAAAA_zo/y-f4_dh_apcw1pNly9pyCXsyFGIo-zr5wCKgBGAs/s1600/IMG_20180217_161548.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://2.bp.blogspot.com/-HQtanE22MeI/WolcOyoySmI/AAAAAAAA_zo/y-f4_dh_apcw1pNly9pyCXsyFGIo-zr5wCKgBGAs/s640/IMG_20180217_161548.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Incomplete Alternate OBD-II Connector</td></tr></tbody></table><div>I made liberal use of heat shrink tubing and nylon cable sheathing to keep things neat and professional looking. I also printed out some labels for keeping track of which connector each cable mates with.</div></div><div class="separator" style="clear: both; text-align: center;"></div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-UDGDa4rt-oc/Wolc8F8QaAI/AAAAAAAA_z4/jqd6sfdccbABOKfXeJntENjtHEfSBXN3wCKgBGAs/s1600/IMG_20180217_171528.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://4.bp.blogspot.com/-UDGDa4rt-oc/Wolc8F8QaAI/AAAAAAAA_z4/jqd6sfdccbABOKfXeJntENjtHEfSBXN3wCKgBGAs/s640/IMG_20180217_171528.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Neatly Labelled Connectors :]</td></tr></tbody></table><div>I left some slack in the cables to allow positioning the CAN to USB adapter on the floor. The last missing component is on the way: a RAM Sony Vaio UX windshield mount.</div><div class="separator" style="clear: both; text-align: center;"></div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-IxZcInIp6cw/Wom_aHaVMPI/AAAAAAAA_0M/e_AgBxp7WlUOPzmYSkkAjd9f6TOUye21gCKgBGAs/s1600/IMG_20180217_172622.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://2.bp.blogspot.com/-IxZcInIp6cw/Wom_aHaVMPI/AAAAAAAA_0M/e_AgBxp7WlUOPzmYSkkAjd9f6TOUye21gCKgBGAs/s640/IMG_20180217_172622.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">CAN Bus Monitor Connected</td></tr></tbody></table><h2>Displaying the Data</h2><div><br /></div><div>I opted to use PyQt in order to build a simple interface for rendering the UI. I started with a basic setup like the following:</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-CbKUu0XK1bU/WonCYM7Z4_I/AAAAAAAA_0Y/vlFGXqkCFwYUalHquaINqCVpl0hYhV1nACLcBGAs/s1600/Screenshot_2018-02-11_18-22-40.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="600" data-original-width="1024" height="374" src="http://4.bp.blogspot.com/-CbKUu0XK1bU/WonCYM7Z4_I/AAAAAAAA_0Y/vlFGXqkCFwYUalHquaINqCVpl0hYhV1nACLcBGAs/s640/Screenshot_2018-02-11_18-22-40.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Speed Plot and Non-Functional Quit Button</td></tr></tbody></table><div>I needed just a simple plot in order to test the real-time plotting implementation. I want this application to run full screen so a quit button of sorts is necessary.</div><div><br /></div><div>After some iteration, I arrived at the more refined UI below. I use white text on a black background to keep the brightness down for night-time driving. The high-contrast is highly readable. The small EV symbol in the top left can be tapped to quit.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-W_xeUA28PEc/WonCvVJCjAI/AAAAAAAA_0c/vMfKmoqO_FQjPgyOXL4jldrik3inLn2twCLcBGAs/s1600/Screenshot_2018-02-17_21-55-41.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="600" data-original-width="1024" height="374" src="http://2.bp.blogspot.com/-W_xeUA28PEc/WonCvVJCjAI/AAAAAAAA_0c/vMfKmoqO_FQjPgyOXL4jldrik3inLn2twCLcBGAs/s640/Screenshot_2018-02-17_21-55-41.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">More Refined UI</td></tr></tbody></table><div>At this time, state of charge and speed are plotted with other numeric statistics shown above: current speed, distance traveled, energy used and Wh/mi. I would like to continue iterating on the design and add a histogram of speed, which is handy for hypermiling. It also needs a reset button and labels for the displayed statistics.</div><div><br /></div><div>I can also build a simple range estimator based on current efficiency and rated state of charge. Different algorithms can be used to try to make a more accurate estimate. Another interesting UI feature would be a warning for accelerations that are too fast. This would coach me into driving more efficiently.</div><div><br /></div><h2>Does it work?</h2><div><br /></div><div>Of course it does! Here is a screenshot from a recent hypermiling run from Mountain View, CA to the Golden Gate Bridge.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-rQ6T_gCKdSA/WonEBwCCbGI/AAAAAAAA_0w/nrmyiA2tubYjRUvvOBLlvQNArH68Nq34wCLcBGAs/s1600/Screenshot_2018-02-18_00-24-22.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="600" data-original-width="1024" height="374" src="http://4.bp.blogspot.com/-rQ6T_gCKdSA/WonEBwCCbGI/AAAAAAAA_0w/nrmyiA2tubYjRUvvOBLlvQNArH68Nq34wCLcBGAs/s640/Screenshot_2018-02-18_00-24-22.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Hypermiling from Mountain View to Golden Gate Bridge</td></tr></tbody></table><div>The Cadillac ELR has a rated range of 35 miles but I was able to push it to 44 in this case with some energy to spare. It is not an incredible run, but cool nonetheless.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-irTsad1wBzw/WonEwmlpORI/AAAAAAAA_04/u-io6GRtXHo3txLn3DN_noCSkyZynAwUACKgBGAs/s1600/IMG_20180218_002737.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://4.bp.blogspot.com/-irTsad1wBzw/WonEwmlpORI/AAAAAAAA_04/u-io6GRtXHo3txLn3DN_noCSkyZynAwUACKgBGAs/s640/IMG_20180218_002737.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Sony Vaio UX Resting on the Dash</td></tr></tbody></table><div>It was quite cold with single-digit Celsius outdoor temperatures. The tires could use a little more air too. They are at 35-36psi now, but could stand to be increased to 40psi for further efficiency improvements.</div><div>&nbsp;</div><div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-X1ZXwfICANA/WonGBO5k12I/AAAAAAAA_14/iewljOs4LywA1GPwVEuK8m9utOywrPZegCKgBGAs/s1600/IMG_20180215_211707.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="http://3.bp.blogspot.com/-X1ZXwfICANA/WonGBO5k12I/AAAAAAAA_14/iewljOs4LywA1GPwVEuK8m9utOywrPZegCKgBGAs/s640/IMG_20180215_211707.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Cadillac ELR (the car that is more door than car), Charging up for another adventure</td></tr></tbody></table></div><div>Overall, the ELR is a really fun car to drive. These stats scratch my itch to see more metrics about my driving. Maybe I will take another pass at the data and find some more useful data points to render.</div><div><br /></div><div>In the meantime, I look forward to the arrival of my windshield mount and more adventures/projects with this car. Thanks for reading!</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com20tag:blogger.com,1999:blog-5663160055124969741.post-24210955957730364202017-09-23T19:45:00.002-04:002017-09-27T00:11:43.441-04:00Crossing the Great Canadian Electric Vehicle Desert I recently returned from a three week trip crossing Canada in a Tesla Model S. The journey began in the heart of Silicon Valley and followed the West Coast of the United States until reaching Vancouver. I then travelled east on the&nbsp;<a href="http://en.wikipedia.org/wiki/Trans-Canada_Highway">Trans-Canada Highway</a> until reaching the Greater Toronto Area where I was born and raised. After spending some quality time with family and catching up with old friends I returned to California on the Tesla Supercharger Network, crossing through the Rocky Mountains of Colorado.<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-1Q784sKtrO0/WcaI16JfT_I/AAAAAAAA1a0/QHnSKCQ-560QAHzyZxw950F1lYKnPQTkACLcBGAs/s1600/road_trip_map_2.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="872" data-original-width="1484" height="376" src="http://2.bp.blogspot.com/-1Q784sKtrO0/WcaI16JfT_I/AAAAAAAA1a0/QHnSKCQ-560QAHzyZxw950F1lYKnPQTkACLcBGAs/s640/road_trip_map_2.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Green: Charging Stops | Red: Rest Stops | Purple: Trip Start/Finish</td></tr></tbody></table>It was an incredible trip and certainly takes first place for the longest of my road trips. I faced challenges crossing Canada due to the lack of charging infrastructure. With perseverance, resourcefulness and patience I was able to complete the trip without requiring a tow (though I did have a close call in Northern Ontario).<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-6X7JDpvIl6o/Wcbw7_vzN7I/AAAAAAAA1dE/cPdS6y2_58MBQWbrspxyH4eUuLpefNt7ACLcBGAs/s1600/tesla_colorado_rockies.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://4.bp.blogspot.com/-6X7JDpvIl6o/Wcbw7_vzN7I/AAAAAAAA1dE/cPdS6y2_58MBQWbrspxyH4eUuLpefNt7ACLcBGAs/s640/tesla_colorado_rockies.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Tesla Model S in Rocky National Park, Colorado</td></tr></tbody></table>Along the way I met some incredible people. One fellow performed his own EV conversion on an older model Porsche. Another built his own experimental aircraft. I was graciously invited into the home of a Tesla owner in Thunder Bay, Ontario who was happy to lend a charge and share a meal. The trip was enlightening because it was off the beaten path and allowed an opportunity to meet people who live their life just a little differently than the way I live my own.<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><video autoplay="true" loop="true" width="640"><source src="http://i.imgur.com/eFYjEeM.mp4" type="video/mp4"></source></video></td></tr><tr><td class="tr-caption" style="text-align: center;">Driving through the Rockies in Alberta</td></tr></tbody></table><br /><br />This trip encompassed 12000km (7400mi) of driving and consumed more than 2.2MWh of energy spread out over 140+ hours of driving. The trip across from Silicon Valley to Toronto took 8 days, 3 of which were spent navigating Northern Ontario. It was an exercise in patience but the prize of being just one of a few drivers to complete this route in an electric vehicle is unequivocally worth it.<br /><br /><a name='more'></a><h2>Planning</h2><div><br /></div>I will admit that this is not my first rodeo. I completed a trip similar to this in late November 2016 just after taking ownership of my car in Los Angeles.<br /><br />I spent countless hours meticulously selecting interesting sights, charging stops, places to rest and food to eat. A spreadsheet tracks all details of the trip including addresses of destinations, distance between hops, estimated charging time, cost and more. I drive a Tesla Model S with an 85kWh battery. It has close to 70000 miles on it and around 3% degradation (when charged fully, the reported mileage is 258mi from the original 265mi). All of this information is used when estimating charge times.<br /><br />I make ample use of color to convey information at a glance. Purple and yellow are used to more easily discern between days and green is used for headers and legends. When details are missing for a stop the first cell of that row is colored red to call attention to it. All details for the trip were finalized when I left.<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-kZ2EKx8kR2g/WcbkQN2qhfI/AAAAAAAA1cs/DlR5GP_ThXIIe9MdOuB9DGx6-Cgdwi2eACLcBGAs/s1600/trip_planner.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1041" data-original-width="1600" height="416" src="http://3.bp.blogspot.com/-kZ2EKx8kR2g/WcbkQN2qhfI/AAAAAAAA1cs/DlR5GP_ThXIIe9MdOuB9DGx6-Cgdwi2eACLcBGAs/s640/trip_planner.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">One of three pages of the trip planner</td></tr></tbody></table>One of the greatest resources available to EV drivers is <a href="http://www.plugshare.com/">PlugShare</a> which allows drivers to locate chargers on a map. It also allows members to list their own private chargers to share with other EV drivers who are in a bind.<br /><br /><h2>Charging Paraphernalia</h2><div><br /></div>Ordinarily driving a Tesla is a piece of cake when it comes to charging. I live in an apartment complex with no dedicated L2 charger. I charge from a 120V outlet limited to 10A (roughly 1kW). This is more than enough for my daily commute. When I decided to venture into Canada and out of range of the Superchargers, I knew that it would be wise to bring some extra tools.<br /><br />Perhaps the most interesting is the <a href="http://www.quick220.com/">Quick220</a>.&nbsp;This is a device that will safely merge two out-of-phase 120V power sources into one 240V supply. This allows the car to charge at just under 3kW which is quite respectable if charging overnight. I also brought two long extension cords: 50ft and 100ft.<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-3GCgayIHnPQ/WcbwSUmu1aI/AAAAAAAA1c8/fgMJ2WzkhY8fNs_o1FcM93RduGns8EBogCLcBGAs/s1600/tesla_charging_accessories.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-3GCgayIHnPQ/WcbwSUmu1aI/AAAAAAAA1c8/fgMJ2WzkhY8fNs_o1FcM93RduGns8EBogCLcBGAs/s640/tesla_charging_accessories.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Quick220 and 12AWG Extension Cords</td></tr></tbody></table>The Tesla Chademo adapter ended up being handier than I had expected but there are limited L3 chargers in Canada. This adapter allows a Tesla to charge from standard L3 public charging infrastructure that is normally used by Nissan Leaf or BMW i3 vehicles.<br /><br />All of this is in addition to the standard Tesla Universal Mobile Connector (UMC) cable that is supplied with the car. I purchased the NEMA 5-20 adapter to go along with my setup. This allows 16A instead of 12A to be pulled from a standard 120V outlet if it has the correct connector. This was handy in Colorado, of all places. I used the J1772 adapter for all of the L2 charging required when crossing Canada.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-zeXG9kmCaRk/WcbxmmsgCjI/AAAAAAAA1dM/9nykhK8yHNsXAlekzzgXbFvqcVKeNP45QCLcBGAs/s1600/tesla_charging_cables.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://4.bp.blogspot.com/-zeXG9kmCaRk/WcbxmmsgCjI/AAAAAAAA1dM/9nykhK8yHNsXAlekzzgXbFvqcVKeNP45QCLcBGAs/s640/tesla_charging_cables.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Tesla Charging Cables</td></tr></tbody></table><h2>Mountain View to Vancouver</h2><div><br /></div>This trip started in Mountain View, California. I finished work earlier than normal at 4:50 and hit the road. The first stop was a hotel in Redding, just 5 hours north including a short charging stop where I would also eat dinner.<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><video autoplay="true" loop="true" width="640"><source src="http://i.imgur.com/6x0yrrC.mp4" type="video/mp4"></source></video></td></tr><tr><td class="tr-caption" style="text-align: center;">Bay Area Traffic Leaving Mountain View</td></tr></tbody></table><br />I arrived at the Red Lion Hotel in Redding. They have a Tesla destination charger and it was available for my use. I plugged in the car, checked in and got a good nights sleep for the next day.<br /><br />The first stop was to visit Crater Lake in Oregon. I encountered forest fires en route which was an impressive if uneasy sight.<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-5UNsHzFgRDE/Wcb2XLzSDCI/AAAAAAAA1dY/nFPoxUFwwwAT1hQ6hIagFHWBVlVXqpVTQCLcBGAs/s1600/oregon_forest_fires.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://4.bp.blogspot.com/-5UNsHzFgRDE/Wcb2XLzSDCI/AAAAAAAA1dY/nFPoxUFwwwAT1hQ6hIagFHWBVlVXqpVTQCLcBGAs/s640/oregon_forest_fires.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Forest Fires in Oregon</td></tr></tbody></table>Crater lake was beautiful, despite being half filled with smoke. Road work added half an hour to my drive which would later make me late to an AirBnB in Happy Valley.<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-LsnpT9tZn9g/Wcb30t8wyII/AAAAAAAA1dk/177-EDjaAYUv7liPFfCjdpt_q6UCFEZjACLcBGAs/s1600/crater_lake_cropped.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://4.bp.blogspot.com/-LsnpT9tZn9g/Wcb30t8wyII/AAAAAAAA1dk/177-EDjaAYUv7liPFfCjdpt_q6UCFEZjACLcBGAs/s640/crater_lake_cropped.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Crater Lake</td></tr></tbody></table>The AirBnB host that I stayed with was pleased to let me charge from an outlet in her garage. The host seemed excited to have her first EV customer and commented that she had never been so close to a Tesla and that they were very quiet. It is always fun to give someone a quick taste of what driving an EV is like.<br /><br />The next day I continued north and paid a visit to Microsoft on the way to Vancouver.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-pMZoGKtNrS0/Wcb5W21pmEI/AAAAAAAA1d0/Ve4_0bgMZAwplxAz8wjZlDnW2VZRcYgdwCLcBGAs/s1600/microsoft_sign.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://3.bp.blogspot.com/-pMZoGKtNrS0/Wcb5W21pmEI/AAAAAAAA1d0/Ve4_0bgMZAwplxAz8wjZlDnW2VZRcYgdwCLcBGAs/s640/microsoft_sign.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Microsoft Sign</td></tr></tbody></table>I passed through Canadian Customs with no issue. The border officer asked me why I didn't drive through the US to which I responded: "I have already done that and want to try something new". He seemed entertained.<br /><br /><h2>Vancouver to Toronto</h2><div><br /></div>The next stop was North Vancouver where I used an L3 DC Fast Charger (DCFC) which kicked out 35kW. A small restaurant near the waterfront was a great place to relax while the car charged up. One of the great advantages of road tripping in an EV is that the charging time allows you to spend more time enjoying the sights.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-uYwCmKJ0Ypo/Wcb8RLpuC7I/AAAAAAAA1eM/Oa_P3GTbaFMe0Tg6NA_u5K4EPxScQ-BkwCLcBGAs/s1600/vancouver_l3_charger.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1074" data-original-width="1600" height="428" src="http://2.bp.blogspot.com/-uYwCmKJ0Ypo/Wcb8RLpuC7I/AAAAAAAA1eM/Oa_P3GTbaFMe0Tg6NA_u5K4EPxScQ-BkwCLcBGAs/s640/vancouver_l3_charger.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Charging in North Vancouver</td></tr></tbody></table><span id="goog_805659938"></span><span id="goog_805659939"></span>I stayed in a motel in British Columbia that has an L2 and L3 charger across the street. I opted for the L2 charger since I was planning to sleep and didn't want to block the L3 for anyone who would need it. I was at 90% in the morning which was perfect.<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><video autoplay="true" loop="true" width="640"><source src="http://i.imgur.com/gH33av0.mp4" type="video/mp4"></source></video></td></tr><tr><td class="tr-caption" style="text-align: center;">Raging forest fire to the side of the road in front of a traffic jam</td></tr></tbody></table><br /><br />The Rocky Mountains in British Columbia and Alberta were filled with smoke due to ongoing forest fires. Traffic was stopped for more than an hour in one instance and other motorists were getting out of their cars to chat. I joined in the fun and met a young couple from Denmark who were on vacation and a woman who was returning from a dog show. It was nice to chat with other locals who were speculating about the fires. Planes were flying over the hill in front of us and down into a nearby lake to catch water and attempt to extinguish the fires. It must have worked because traffic was allowed to proceed!<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-W6K57hb-QDE/WcaNVls2mzI/AAAAAAAA1bM/zdak0sh-lNY6Q8ab1wgQNI0L_4he9RTVQCLcBGAs/s1600/alberta_calgary_scale.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="958" data-original-width="1600" height="382" src="http://3.bp.blogspot.com/-W6K57hb-QDE/WcaNVls2mzI/AAAAAAAA1bM/zdak0sh-lNY6Q8ab1wgQNI0L_4he9RTVQCLcBGAs/s640/alberta_calgary_scale.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="font-size: 12.8px; text-align: center;">Trans-Canada Highway Junction to Calgary</td></tr></tbody></table><br /><br class="Apple-interchange-newline" />I arrived in Banff National Park and took lots of opportunities to take pictures. The skies were considerably clearer by this time as the smoke had cleared. The mountains were beautiful.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-uKwABOGyF1Q/WccC-IZv0jI/AAAAAAAA1ec/eDSbFbqJ_ZMyPS0adj_kzzRRfxfzdph0ACLcBGAs/s1600/banff_national_park.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://3.bp.blogspot.com/-uKwABOGyF1Q/WccC-IZv0jI/AAAAAAAA1ec/eDSbFbqJ_ZMyPS0adj_kzzRRfxfzdph0ACLcBGAs/s640/banff_national_park.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Miles of beautiful mountains</td></tr></tbody></table>Later this evening I visited an L2 charger in Calgary. I decided on a station located in City Hall because I assumed that it would not be occupied given that it was late in the evening. With the car charging, I set off on foot to find a place to eat, or so I thought. It became apparent that there was no way to exit the parking garage as a pedestrian without getting locked out. All marked exits had signage indicating that re-entry after a certain hour was impossible. I was effectively locked in, hungry and in need of a restroom. I spent 30 minutes trying all possible exits, but none of them would have worked. Eventually it was time to give up and I hit a McDonald's on the way out of the city heading towards my hotel. I left disappointed but with a ridiculous story that I will never forget.<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-b4CiMwmez6Q/WccFrAZCF3I/AAAAAAAA1fY/tpp8UutZbmsQCP33fCB57-Hp1DJCsl2LACLcBGAs/s1600/tesla_out_of_range.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1100" data-original-width="1600" height="440" src="http://2.bp.blogspot.com/-b4CiMwmez6Q/WccFrAZCF3I/AAAAAAAA1fY/tpp8UutZbmsQCP33fCB57-Hp1DJCsl2LACLcBGAs/s640/tesla_out_of_range.jpg" style="cursor: move;" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Driving out of range of Superchargers</td></tr></tbody></table>I continued east and eventually drove out of range of the Tesla Supercharger Network. This notice was unnerving at 10PM on the way to the next hotel. I made it to the Ramada in Brooks just before midnight with just 8% to spare. The charger was unoccupied and I was happy to plug in for the night.<br /><br />This is where the trip becomes a true experiment. I no longer have the support of the Superchargers and am on my own when it comes to charging.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-KwODfvG6LE0/WccG_xX-9uI/AAAAAAAA1fk/DR3v1Pgao1kGZXC9Xvf1dbbQJ-9ECxNrACLcBGAs/s1600/tesla_destination_charging.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-KwODfvG6LE0/WccG_xX-9uI/AAAAAAAA1fk/DR3v1Pgao1kGZXC9Xvf1dbbQJ-9ECxNrACLcBGAs/s640/tesla_destination_charging.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">L2 Charging at 8kW</td></tr></tbody></table>A brief math lesson is in order: the Tesla battery is 85kWh and most L2 chargers operate at 6-10kW. The one pictured here is running at 8kW which gives a charge time of 11 hours for 0-100%. The name of the game for the next few days is going to be patience. I budgeted these extended stops into the trip planner and it worked out for the most part. I rarely let the car drop below 10% and almost never needed a full 100% charge during the day so most of the stops were 4 hours at most.<br /><div><br /></div><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><video autoplay="true" loop="true" width="640"><source src="http://i.imgur.com/4XrRgzd.mp4" type="video/mp4"></source></video></td></tr><tr><td class="tr-caption" style="text-align: center;">The prairies of Canada are incredibly flat</td></tr></tbody></table><div><br /></div>I woke up just on the other side of the Rocky Mountains and continued east. I stopped at my first L2 charge of the day in Medicine Hat. There was a Tesla destination charger that I took advantage of while eating at the nearby Boston Pizza. The prairies of Canada are very flat! At this point the land still had undulations. As the day continued it became flatter and flatter.<br /><br /><iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/Veu-Cm7aHMw?rel=0" width="640"></iframe> <br /><br />It really makes this famous clip from Corner Gas ring true.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-bXZw-w4jCQI/WccLWK8PAjI/AAAAAAAA1gA/SHV3EYIX5tA0-Ex2ZdILdu3pU_DOKHn6QCLcBGAs/s1600/tesla_superior_tech.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1098" data-original-width="1600" height="438" src="http://3.bp.blogspot.com/-bXZw-w4jCQI/WccLWK8PAjI/AAAAAAAA1gA/SHV3EYIX5tA0-Ex2ZdILdu3pU_DOKHn6QCLcBGAs/s640/tesla_superior_tech.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Tesla at Superior Tech</td></tr></tbody></table>I stopped in a town called Swift Current where a company called <a href="http://superior-tech.ca/">Superior Tech</a> has graciously mounted a Tesla charger on the front of their building. I signed myself in on PlugShare to let others know that the charger was operational. A few moments later, a Tesla Model S 90D came barreling around the corner with the driver waving. He owns the shop and was a delight to meet. We discussed a variety of techie things and I learned that there are only 13 Teslas in the entire province of Saskatchewan and that he has one of them.<br /><br />I showed him my Quick220 and we compared the cars. His is much newer than mine. It was a great meet up and we ended up having dinner together.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-z74Mue4UM_o/WccNB3DLTxI/AAAAAAAA1gM/fMIyEC6nokwCXhpTtclfC-xv3Uq9n0yFQCLcBGAs/s1600/tesla_regina_double_tree.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-z74Mue4UM_o/WccNB3DLTxI/AAAAAAAA1gM/fMIyEC6nokwCXhpTtclfC-xv3Uq9n0yFQCLcBGAs/s640/tesla_regina_double_tree.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Tesla at the Regina Double Tree</td></tr></tbody></table>Thankful for the charge I continued on the road east. I was headed to Regina to stay at a DoubleTree that had another destination charger. I was not able to make the trip without one short charge on the way so I made a stop at a Peavey Mart between Swift Current and Regina for an hour. This gave me enough charge to comfortably make the rest of the drive.<br /><br />I was spending most of the charging time on this trip working remotely so the time was put to good use.<br /><div class="separator" style="clear: both; text-align: center;"></div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-TDQFTpWAkTI/WccONJx0IeI/AAAAAAAA1gc/crEgnYtnTv0uH6c6IapNrGuFuj6ZVr7hQCLcBGAs/s1600/tesla_whitewood.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://2.bp.blogspot.com/-TDQFTpWAkTI/WccONJx0IeI/AAAAAAAA1gc/crEgnYtnTv0uH6c6IapNrGuFuj6ZVr7hQCLcBGAs/s640/tesla_whitewood.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Charging in Whitewood</td></tr></tbody></table>The first stop after Regina was in a small town called Whitewood. The township was kind enough to install this charger on the back of their building. I brought my Boosted Board with me on this trip to ride to restaurants while charging. Unfortunately I took a tumble in this town and scuffed up my arm. Thankfully it was a minor injury. I was able to get some bandaids from a local gas station which doubled as a Subway and was back on the road in no time.<br /><br />I spent a few hours working on my laptop while the car filled up.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-fxfvqCj5tM8/WccQ9roSdXI/AAAAAAAA1g4/nr-M-9cyh4oqnK9OwqSppe-RjuFdqN_DgCLcBGAs/s1600/tesla_canada_flag.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="974" data-original-width="1600" height="388" src="http://1.bp.blogspot.com/-fxfvqCj5tM8/WccQ9roSdXI/AAAAAAAA1g4/nr-M-9cyh4oqnK9OwqSppe-RjuFdqN_DgCLcBGAs/s640/tesla_canada_flag.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">A rest area in Saskatchewan</td></tr></tbody></table>At this point, I had begun to realize that my day was over scheduled. I needed to be in Kenora, Ontario to stay in a hotel which has an L2 charger. Unfortunately, this charger is a necessary stop to make the trip to Toronto. I had to change my plans slightly and route myself to an L3 charger in Winnipeg. This charger is part of a research project at a local college to determine the effectiveness of EVs in the cold Canadian winters. Thankfully I was able to get a quick charge and I met a fellow Tesla owner who is a nursing student at the college! It was a delight to chat and share stories.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-E4vLS4alLb8/WccTT-0zdMI/AAAAAAAA1hk/M8pkEz1T5HIzbVc0GR-_kpHj0LlxX0VGACLcBGAs/s1600/tesla_charging_red_river_college.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1075" data-original-width="1600" height="430" src="http://2.bp.blogspot.com/-E4vLS4alLb8/WccTT-0zdMI/AAAAAAAA1hk/M8pkEz1T5HIzbVc0GR-_kpHj0LlxX0VGACLcBGAs/s640/tesla_charging_red_river_college.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Tesla at Red River College</td></tr></tbody></table>After spending some time at Red River College, I was able to make it to the hotel in Kenora with just a little battery left. The hotel owners have a "No Parking" sign in the EV space that I moved and was able to charge for the night. This day was a little longer than planned and I did not get to the hotel until 2AM. I did get a good nights sleep once I arrived.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-kYQXy1wkvfA/WccUUHwlHBI/AAAAAAAA1iA/Rkndv70Byo4wyYyQ0ExJcqmVl3Q-m_mtgCLcBGAs/s1600/kenora_ontario_view.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1073" data-original-width="1600" height="428" src="http://1.bp.blogspot.com/-kYQXy1wkvfA/WccUUHwlHBI/AAAAAAAA1iA/Rkndv70Byo4wyYyQ0ExJcqmVl3Q-m_mtgCLcBGAs/s640/kenora_ontario_view.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Views in Kenora</td></tr></tbody></table>I slept in the next morning to catch up on some sleep. The hotel was comfortable and the views were great. The car was at 100% and ready for another day of driving.<br /><br />The next day I was headed towards Nipigon, Ontario. There is a hotel there that is essential to complete the trip through Northern Ontario. On the way, I stopped to meet a PlugShare user in Thunder Bay who listed their Tesla charger for use. The owner of the home was very hospitable and we ended up having Thai food in his kitchen. I met his family and lovely dogs. It was a humbling experience and nice to meet the young family.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-7oiZ0JYgwJs/WccWFZ_m8EI/AAAAAAAA1ic/LJpiJaQPlzgRL9JgPkTJCcWDj2gHf_7FQCLcBGAs/s1600/tesla_thunder_bay_plugshare.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1074" data-original-width="1600" height="428" src="http://3.bp.blogspot.com/-7oiZ0JYgwJs/WccWFZ_m8EI/AAAAAAAA1ic/LJpiJaQPlzgRL9JgPkTJCcWDj2gHf_7FQCLcBGAs/s640/tesla_thunder_bay_plugshare.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Charging in Thunder Bay</td></tr></tbody></table>I arrived in Nipigon with 5% remaining, the lowest I had been on the entire trip. The chargers are not spaced very closely together in Northern Ontario and I must have underestimated the time needed to charge up.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-xGbtPfDvKXI/WccXbc1SImI/AAAAAAAA1i4/PiPBfUdU228Ma8A45XpOZc80rQZTmiz-ACLcBGAs/s1600/tesla_5_percent_nipigon.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1147" data-original-width="1600" height="458" src="http://2.bp.blogspot.com/-xGbtPfDvKXI/WccXbc1SImI/AAAAAAAA1i4/PiPBfUdU228Ma8A45XpOZc80rQZTmiz-ACLcBGAs/s640/tesla_5_percent_nipigon.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Nearly Empty!</td></tr></tbody></table>I try to avoid allowing the battery to get this low to preserve the overall lifespan of the pack. So far I think the 3% degradation is fairly good given the age and mileage of the car.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-9QaBN2gBlz0/WccXyx2bwoI/AAAAAAAA1i8/iVmexiZ0_aQEVZ9yex7NXRr6cSkTHSnEACLcBGAs/s1600/tim_hortons.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1067" data-original-width="1600" height="426" src="http://1.bp.blogspot.com/-9QaBN2gBlz0/WccXyx2bwoI/AAAAAAAA1i8/iVmexiZ0_aQEVZ9yex7NXRr6cSkTHSnEACLcBGAs/s640/tim_hortons.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Tim Hortons</td></tr></tbody></table><div>I am not sure why it took me so long, but I ordered my first Tim Hortons before stopping at an engineering firm called Ionic Engineering. They have a Tesla charger on the side of their building and accept donations for the local Food Bank in exchange for a charge.</div><div><br /></div><div>This charge was only necessary because an L3 charger failed in Elliot Lake while I was using it. The entire unit powered down with no signs of life. I suspect I may have tripped a breaker somewhere, but it cost me a couple of hours because I had to find a new charger and then wait.</div><div><br /></div><div>The next stop was in Parry Sound where I finally reached the first Supercharger in Southern Ontario! I was so excited that I forgot to take a picture. Later this evening I arrived at my parents home. By this point I hope that you can appreciate the value of the Superchargers or at least why I called Canada an EV Desert.</div><div><br /></div><h2>Tires, Tires, Tires</h2><div><br /></div><div>Over the course of this journey, I encountered zero mechanical issues with the car itself. However, my tires were worn and I knew that when I left. By the time I had arrived in Southern Ontario, I had developed a slow leak in two of my tires.</div><div><br /></div><div>I scheduled an appointment with Tesla to have 4 new tires installed and an alignment for the return trip. The technicians found a nail in one tire and that the rims had gone out of round. The next morning after I had my new tires installed, one had gone completely flat. I pulled it off with my Dad and we discovered a small hairline crack in the rim when placed under soapy water.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-NqzTFnV9htw/WcdJE3k3r5I/AAAAAAAA1kU/MZJ34afNVf8ZPt-50n1Ye3AK4K3oo3Y0wCLcBGAs/s1600/tesla_on_jack.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1099" data-original-width="1600" height="438" src="http://1.bp.blogspot.com/-NqzTFnV9htw/WcdJE3k3r5I/AAAAAAAA1kU/MZJ34afNVf8ZPt-50n1Ye3AK4K3oo3Y0wCLcBGAs/s640/tesla_on_jack.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Tesla on a Jack</td></tr></tbody></table>A local tire shop called TireHaus was able to straighten my rims and repair the crack with a weld. They had it done in just a couple of hours. The service was fantastic and I saw a noticeable improvement in efficiency (30-40Wh/mi). I had experienced excessive tire wear before but chalked it up to a bad alignment.</div><div><br /></div><h2>Returning to California</h2><div><br /></div>The return trip to California was great. I won't get into great detail here as there are loads of blog posts around that show people driving around the US on the Supercharger network. I made a stop in the Rocky Mountains, visited Hoover Dam and just generally enjoyed myself. The only major issue was that there were very strong headwinds starting in Colorado that pushed my consumption to 450Wh/mi in many cases. This just means more charging. The Superchargers are plentiful so it was not a major burden.<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><video autoplay="true" loop="true" width="640"><source src="http://i.imgur.com/D4nH9g3.mp4" type="video/mp4"></source></video></td></tr><tr><td class="tr-caption" style="text-align: center;">Sunset in Colorado</td></tr></tbody></table><br />This trip has been the experience of a lifetime and I am thrilled that I was finally able to do it. The plan was to take this trip last year but delivery of the car was delayed. I decided to avoid the harsh weather in Canada then and save it until now. It was a busy vacation, exhausting at times, but I think a great use of the free time that I have and something that I will remember for the rest of my life.<br /><br />Thanks for reading!Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com5tag:blogger.com,1999:blog-5663160055124969741.post-8257283861450960072017-01-08T00:52:00.001-05:002017-01-08T00:52:11.822-05:00pwrusbctl: A command-line tool for controlling USB-connected power strips Over the past few days I wrote a command-line tool for controlling and monitoring <a href="http://www.pwrusb.com/">PowerUSB-branded power strips</a>. These are clever devices that connect to a computer via USB and allow power monitoring and controlling the state of the outlets. I decided to call it pwrusbctl and you can <a href="http://github.com/aarossig/pwrusbctl">view the code on GitHub</a>.<br /><br />The company that sells them offer a binary blob driver, but I wanted something that I could extend and modify. I also want it to work with my RaspberryPi.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-rncYkbwgvgg/WHHRKuMzlWI/AAAAAAAAka4/ni4nN_T30zYmDNDEBbwmMRbgmjMoZYQvgCKgB/s1600/IMG_20170107_205939.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="480" src="http://4.bp.blogspot.com/-rncYkbwgvgg/WHHRKuMzlWI/AAAAAAAAka4/ni4nN_T30zYmDNDEBbwmMRbgmjMoZYQvgCKgB/s640/IMG_20170107_205939.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">PowerUSB Basic connected to a RaspberryPi</td></tr></tbody></table>I used HIDAPI as an abstraction over HID which makes the codebase portable. It has been tested to work on Linux and Mac OS. You can find more documentation in the README on GitHub. Here is an example of the output:<br /><pre class="brush:plain">[andrew@andrew-rpi-1 pwrusbctl]$ ./pwrusbctl --reset_charge_accumulator --device_info --log_indefinitely --power --energy --interval 2000000<br />Found PowerUSB device type: Basic<br />Power: 32.200001W<br />Energy: 0.000000kWh<br />Power: 32.200001W<br />Energy: 0.000537kWh<br />Power: 27.599998W<br />Energy: 0.000537kWh<br />Power: 27.599998W<br />Energy: 0.000537kWh<br />Power: 50.599998W<br />Energy: 0.000537kWh<br />Power: 172.500000W<br />Energy: 0.000537kWh<br />Power: 342.700012W<br />Energy: 0.000537kWh<br />Power: 519.799988W<br />Energy: 0.000537kWh<br />Power: 545.099976W<br />Energy: 0.000537kWh<br /></pre>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com0tag:blogger.com,1999:blog-5663160055124969741.post-22955732880729800992016-12-31T00:55:00.000-05:002016-12-31T01:33:27.059-05:00Extreme Long Range Electric Longboarding: 56km on a Boosted Board <div class="" style="clear: both; text-align: left;">I just returned from a 56km <a href="http://boostedboards.com/">Boosted Board</a> ride. I toured the bike trails along the Upper/Lower Crystal Springs Reservoirs and San Andreas Lake here in the San Francisco Bay Area. This is a new record for me. My previous range record was 27km on a single charge.</div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-mpH1H-fEZoE/WGcuFXkmw-I/AAAAAAAAj44/wzd-3smtKOg76Jcuv2rpOgrKssEPWjSIACKgB/s1600/IMG_20161230_145952.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="480" src="http://2.bp.blogspot.com/-mpH1H-fEZoE/WGcuFXkmw-I/AAAAAAAAj44/wzd-3smtKOg76Jcuv2rpOgrKssEPWjSIACKgB/s640/IMG_20161230_145952.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Boosted Board with external battery in my backpack :]</td></tr></tbody></table>To achieve this I used a very large and custom designed lithium-ion battery pack. I tossed it in my backpack and connected it to the Boosted Board where I had previously made a <a href="http://gsyt181.cn/2015/11/long-range-electric-longboarding.html">modification to expose the internal battery voltage rail</a>. This pack is arranged in a 10S6P (10-series, 6-parallel) configuration. It has a rated capacity of 21Ah at 37V nominal or roughly 777Wh. The internal battery of the Boosted Board has a rated capacity of 99Wh.<br /><br />This battery pack is designed to attach to a custom designed board that I have been working on but is not ready for prime time yet. Below is a sneak preview of the board I am working on. I have decided to name it Voyager as one of the design goals has been to prioritize range.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-5wtbr6alcw8/WGcvB--2anI/AAAAAAAAj5I/gSKK9nUoO_oqPaOaUuqzvF9xDzIdbRKuQCKgB/s1600/IMG_4491.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-5wtbr6alcw8/WGcvB--2anI/AAAAAAAAj5I/gSKK9nUoO_oqPaOaUuqzvF9xDzIdbRKuQCKgB/s640/IMG_4491.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">10S6P battery mounted to Voyager with custom charging cable attached</td></tr></tbody></table>Continue reading to learn more about my new record and construction of this battery pack. There are also more pictures of Voyager at the bottom of this post.<br /><br /><a name='more'></a>Before diving into the details of this battery pack, here is a screenshot of the route that I took. This was captured by MapMyRide.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-SlAH2K_Da00/WGdLMus9RrI/AAAAAAAAj90/7HV7V8X7WJM8wbCUX6jj7MPIc-wU1HyAgCKgB/s1600/Screenshot_20161230-175504.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="640" src="http://1.bp.blogspot.com/-SlAH2K_Da00/WGdLMus9RrI/AAAAAAAAj90/7HV7V8X7WJM8wbCUX6jj7MPIc-wU1HyAgCKgB/s640/Screenshot_20161230-175504.png" width="360" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Boosted Board Route</td></tr></tbody></table>The design goal for this battery pack was to simply cram as much energy storage into a thin box as possible. I opted for a non-balanced, non-monitored and non-protected pack to ensure that as much volume as possible is reserved for the battery cells. The additional requirement is that the pack must be able to source very high currents. This pack as designed can deliver 2500W continuously and can peak near 4kW depending on how long the burst is and how much heat I am willing to tolerate.<br /><div><br /></div><div>I will likely add a 120A fuse in the remaining cavity, just in case a short occurs.</div><div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-iuoRmxDS3bs/WGcwqPURzII/AAAAAAAAj5U/1ID1VdaWiisn2iPAO2KYSJyfivQIj8w5QCKgB/s1600/IMG_4490.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-iuoRmxDS3bs/WGcwqPURzII/AAAAAAAAj5U/1ID1VdaWiisn2iPAO2KYSJyfivQIj8w5QCKgB/s640/IMG_4490.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The aircraft-style multi-pin charge connector</td></tr></tbody></table>Due to the simplicity of this battery pack, the complexity of the charger is increased. A balancing charger must be used to ensure that the individual cell voltages are equal. I have had very good luck with discharging this pack under heavy loads. The cells will remain within a few mV of one another. I have both an <a href="http://www.progressiverc.com/icharger-1010b.html">iCharger 1010B+</a> and an <a href="http://www.progressiverc.com/icharger-4010duo.html">iCharger 4010 Duo</a>. The 4010 Duo has some incredible capabilities and can deliver a continuous 40A charge current. That is far more than required to charge this pack.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-Z8WiQewQyw8/WGcynYzCADI/AAAAAAAAj5g/DLSmFgjxn0U99QfDeLEYU1EyjqXVF3engCKgB/s1600/IMG_20161230_175419.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="480" src="http://3.bp.blogspot.com/-Z8WiQewQyw8/WGcynYzCADI/AAAAAAAAj5g/DLSmFgjxn0U99QfDeLEYU1EyjqXVF3engCKgB/s640/IMG_20161230_175419.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">iCharger 4010 Duo showing a discharged, but highly balanced pack :]</td></tr></tbody></table>I selected the <a href="http://batterybro.com/blogs/18650-wholesale-battery-reviews/40773059-new-lg-mj1-18650-battery-review-3500mah">LG MJ1</a> cells for this pack. These cells have a maximum continuous discharge of 10A with a stunning 3.5Ah capacity. This 10A continuous discharge is relatively low but when arranged in a 6-parallel configuration can provide a high continuous discharge current of 60A. This rivals the internal pack of the Boosted Board which uses far more resilient LiFePO4 cells. The LiFePO4 battery chemistry sacrifices volume/weight energy density for increased power.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-TvIj_W-vh2k/WGc2Cxl_gDI/AAAAAAAAj6Y/KF21GY65FsQOgdzdixWnByIk-ibV3zrJQCKgB/s1600/IMG_4355.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-TvIj_W-vh2k/WGc2Cxl_gDI/AAAAAAAAj6Y/KF21GY65FsQOgdzdixWnByIk-ibV3zrJQCKgB/s640/IMG_4355.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Unboxing 60 LG MJ1 cells</td></tr></tbody></table></div><div>The Voyager pack has 60 cells mounted inside a 3D-printed enclosure. There are cutouts for the high-current primary load connector and a multi-pin aircraft-style connector for charging. There are also wings on the ends for attaching the battery pack to mount points on the Voyager board. We can just ignore those when using this battery in my backpack.</div><div><br /></div><div>You might think that this battery pack is quite large and I fully agree. The idea with having known mount points is that different battery packs can be designed and installed depending on the requirements of the trip/day. I have considered building a 10S3P pack with higher-discharge cells which sacrifices range for weight.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-9ucz8iUt28A/WGc3G3IzY7I/AAAAAAAAj6g/yytT6aB2iFo6f0R5TFEp-ci5wklsm6dDgCKgB/s1600/IMG_20160831_213956.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="478" src="http://1.bp.blogspot.com/-9ucz8iUt28A/WGc3G3IzY7I/AAAAAAAAj6g/yytT6aB2iFo6f0R5TFEp-ci5wklsm6dDgCKgB/s640/IMG_20160831_213956.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Prototype enclosure: verifying board mount points and experimenting with cell arrangements :]</td></tr></tbody></table><div>The cells are connected to one another using nickel battery tabs. This is a significant step up over the soldered battery packs that I have experimented with previously. I am investing more time and money into Voyager so I decided to opt for the right tools for the job.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-RY4mjNSswiQ/WGc36OlW_xI/AAAAAAAAj6s/gK0N1s2v4FU1Fddsqg-v7u7qeU17sY6zwCKgB/s1600/IMG_4369.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-RY4mjNSswiQ/WGc36OlW_xI/AAAAAAAAj6s/gK0N1s2v4FU1Fddsqg-v7u7qeU17sY6zwCKgB/s640/IMG_4369.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">My workbench showing candidate cells and battery welder. Let the games begin!</td></tr></tbody></table>The cells are arranged in groups of 6 which are then placed in an arrangement that allows for both positive and negative to end up near the front left of the pack.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-r7v5rmEl1hA/WGc6ZbmHNhI/AAAAAAAAj64/iBconJv8kJAVl2tgFc4CIhpO8MrGDMGOgCKgB/s1600/IMG_4385.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-r7v5rmEl1hA/WGc6ZbmHNhI/AAAAAAAAj64/iBconJv8kJAVl2tgFc4CIhpO8MrGDMGOgCKgB/s640/IMG_4385.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">An individual 1S6P pack</td></tr></tbody></table>The individual groups of 6 were then tabbed together and wrapped in <a href="http://en.wikipedia.org/wiki/Kapton">Kapton</a> tape. Kapton tape is great for this application as it can tolerate a wide range of temperatures and the adhesive is typically very strong. The balance leads for each cell are routed out and terminated with tape while the pack is constructed.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-6mhs_cB6iAI/WGc7XCsDbII/AAAAAAAAj7E/mGR59-t-cegecmC1HsLyvtzEH2dak3cUACKgB/s1600/IMG_4400.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-6mhs_cB6iAI/WGc7XCsDbII/AAAAAAAAj7E/mGR59-t-cegecmC1HsLyvtzEH2dak3cUACKgB/s640/IMG_4400.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">A fully-assembled, but folded battery pack</td></tr></tbody></table><div><div><div>Once the cells were arranged into the complete 10S6P configuration the pack was flattened and wrapped entirely in Kapton tape to improve strength. The pack is nearly ready to be installed in the final enclosure.</div></div></div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-Ni9EEursUus/WGc7zs-eH6I/AAAAAAAAj7I/76l7kjJOPRkJ8Bx881NiaKr-rzT2DDJbwCKgB/s1600/IMG_4415.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-Ni9EEursUus/WGc7zs-eH6I/AAAAAAAAj7I/76l7kjJOPRkJ8Bx881NiaKr-rzT2DDJbwCKgB/s640/IMG_4415.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">A fully assembled and unfolded battery pack</td></tr></tbody></table>At this point, all of the individual balance leads and high-current load leads are exposed and ready to be terminated. The pack is seated in the enclosure with closed-cell foam to dampen vibrations. This will help keep the board quiet while riding on rough pavement.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-lNxSI9lGRPM/WGc8e7BQf3I/AAAAAAAAj7Q/V7FbGVzkEvQwXqUux9onCddN5o_JD-4kwCKgB/s1600/IMG_4417.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-lNxSI9lGRPM/WGc8e7BQf3I/AAAAAAAAj7Q/V7FbGVzkEvQwXqUux9onCddN5o_JD-4kwCKgB/s640/IMG_4417.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">A terminated 19-pin aircraft-style connector</td></tr></tbody></table><div>A balancing charger requires a wire for each of the individual cells in a pack in order to ensure that all individual cell voltages are equal to one another. I selected a 19-pin aircraft-style connector. There are 11 pins reserved for the balance leads (system ground and 10 leads, one for each cell). I then split the remaining 8 pins into 4 positive/4 negative for the main charging current. I can also charge the pack through the main connector but this has the benefit of simplifying the charging cable. It also allows leaving the board powered while charging and accessories could be powered through the charge connector as well.</div><div><br /></div><div>When dealing with batteries I like to think of them as a heavy-duty power supply that cannot be shut off. Batteries can be quite dangerous to work with especially if a short-circuit is created. To ease the installation of the aircraft connector, I soldered pigtails to each of the pins and then attached one wire at a time. This ensures that I only ever have one wire exposed at a time and removes the chances of an accidental short circuit.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-Ssn8StdYYbw/WGc9V_0DR2I/AAAAAAAAj7Y/SP6o_RIxlfwa5HRgLldyMSrek2d3VGviwCKgB/s1600/IMG_4429.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-Ssn8StdYYbw/WGc9V_0DR2I/AAAAAAAAj7Y/SP6o_RIxlfwa5HRgLldyMSrek2d3VGviwCKgB/s640/IMG_4429.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Fully assembled with dust gasket and heated-inset screws</td></tr></tbody></table><div>The battery is sealed by a lid that attaches via 9 M5 screws into brass heated-insets. A 2mm rubber o-ring is used to prevent dust from creeping into the pack. Prior to sealing the box I applied some outdoor window silicone to any port holes (ie: the primary load cable) to prevent dust from breaching the case. I also used grommets where possible.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-hhRYvH3hlSw/WGc-VpBB3TI/AAAAAAAAj7w/pGTP2aJFopwccnpNqomr3lDIKFZFRjddgCKgB/s1600/IMG_20160910_005922.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="480" src="http://3.bp.blogspot.com/-hhRYvH3hlSw/WGc-VpBB3TI/AAAAAAAAj7w/pGTP2aJFopwccnpNqomr3lDIKFZFRjddgCKgB/s640/IMG_20160910_005922.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Primary load cable grommet</td></tr></tbody></table>Prior to installing the lid, additional foam was installed on the top of the cells. Installing the lid was quite easy with just 9 M5 screws. The weight of the cells is primarily supported by the lid, but they are also press-fit between the case walls for a snug fit.<br /><div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-rwVEGYPXGYI/WGc-ua0p-HI/AAAAAAAAj70/tYy5fmAjwI4dciu8lb_5plFYsiXMjIN3wCKgB/s1600/IMG_4433.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-rwVEGYPXGYI/WGc-ua0p-HI/AAAAAAAAj70/tYy5fmAjwI4dciu8lb_5plFYsiXMjIN3wCKgB/s640/IMG_4433.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Just a few lose screws :]</td></tr></tbody></table><div>Next a charge cable was created to match the pinout of the charge connector. This allows a quick connection to an external charger. There is one caveat with this design in that the charger must be connected to the cable before the battery. The risk is that the banana leads could short together. I may consider replacing them with an XT60 connector in the future.</div></div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-n6RVaOLJU9U/WGc_jDjsNzI/AAAAAAAAj8A/yKzBdzac5MkqfacCJgx6NPYZ0Kd3jGY-wCKgB/s1600/IMG_4493.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-n6RVaOLJU9U/WGc_jDjsNzI/AAAAAAAAj8A/yKzBdzac5MkqfacCJgx6NPYZ0Kd3jGY-wCKgB/s640/IMG_4493.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Voyager battery charging cable</td></tr></tbody></table>That pretty much concludes the construction of this battery pack. It is a very simple design, which has risks. I would like to look into designing a battery system with built-in balancing and thermal management functionality. Right now I make an assumption that I can draw 60A from this pack continuously, but this is very unlikely to be possible in practice. I would expect the pack to get into a thermal run-away situation due to the lack of cooling. Thankfully, this pack rarely sees such large loads and when it does, they are just seconds to a minute in length (think hill climbing).<br /><div><br /></div><div>Here are a few more glamor shots of Voyager. The major outstanding TODOs are a remote with more precision and improvements to software, namely to speed regulation. I need to invest some time in writing tests before putting my life on the line with a long trip on Voyager.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-ROy3LRlFYkA/WGdA-tqzPZI/AAAAAAAAj8g/wnbV6cRQaME8Jfr4iK7yEb66AjvTHvV1gCKgB/s1600/IMG_4477.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-ROy3LRlFYkA/WGdA-tqzPZI/AAAAAAAAj8g/wnbV6cRQaME8Jfr4iK7yEb66AjvTHvV1gCKgB/s640/IMG_4477.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Voyager Side Shot</td></tr></tbody></table><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-mbB-ZMQZrtc/WGdBtoVenlI/AAAAAAAAj88/UbyS-9CGLKYP0RK5Ug79f7a8QBDJwtRegCKgB/s1600/IMG_20160905_221059.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="478" src="http://2.bp.blogspot.com/-mbB-ZMQZrtc/WGdBtoVenlI/AAAAAAAAj88/UbyS-9CGLKYP0RK5Ug79f7a8QBDJwtRegCKgB/s640/IMG_20160905_221059.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Voyager lighting system (it has turn signals!)</td></tr></tbody></table><br /><div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-zCQIsQ3ThBg/WGdC-I4iX5I/AAAAAAAAj9I/3I189eUCw0QCdUAEZYbVIP9I0q8-jW0ZwCKgB/s1600/IMG_4332.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-zCQIsQ3ThBg/WGdC-I4iX5I/AAAAAAAAj9I/3I189eUCw0QCdUAEZYbVIP9I0q8-jW0ZwCKgB/s640/IMG_4332.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Motor drivers and LED lighting system</td></tr></tbody></table></div><div>If you made it this far, thanks for reading! Feel free to drop me a line if you are interested in what you see here.</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com5tag:blogger.com,1999:blog-5663160055124969741.post-29824846854816074462016-11-06T21:47:00.001-05:002016-11-06T21:47:53.669-05:00ichargermon: a Linux/Mac iCharger monitoring tool I use an <a href="http://www.progressiverc.com/icharger-1010b.html">iCharger 1010B+</a> to charge a few batteries for my electric skateboard. I also top up the battery in my car from time to time. I thought it would be nice to keep track of the charging progress so I wrote a small tool to parse logs that are output over UART and format them into a human-readable format.<br /><br />I named the tool&nbsp;<a href="http://github.com/aarossig/ichargermon">ichargermon</a>. Feel free to send a pull request!<br /><br />The project is written in C/C++ and has no external dependencies at this time. If I decide to support more advanced iChargers I will add libusb as a dependency.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-6YbX42tGrgU/WB_pQCeDddI/AAAAAAAAgqY/P7Ad4GA5TU8vw6PW4FvNmswqKgBT3HrcwCLcB/s1600/3s1p_charging.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-6YbX42tGrgU/WB_pQCeDddI/AAAAAAAAgqY/P7Ad4GA5TU8vw6PW4FvNmswqKgBT3HrcwCLcB/s640/3s1p_charging.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Charging a small 3S1P battery pack</td></tr></tbody></table><div>I also have an <a href="http://www.progressiverc.com/icharger-4010duo.html">iCharger 4010Duo</a> that I would like to add support for. The 4010Duo implements a much more rich protocol (MODBUS) that supports both remote monitoring and control.<br /><br />Anyway, for now here is an example of the output. Thanks for reading!</div><pre class="brush:plain">Input voltage: 15.163V<br />Battery voltage: 40.165V<br />Battery amps: 0.990A<br />Internal temp: 30.300C<br />Cell 0 voltage: 4.009V<br />Cell 1 voltage: 4.010V<br />Cell 2 voltage: 4.012V<br />Cell 3 voltage: 3.998V<br />Cell 4 voltage: 4.014V<br />Cell 5 voltage: 4.014V<br />Cell 6 voltage: 4.014V<br />Cell 7 voltage: 4.014V<br />Cell 8 voltage: 4.017V<br />Cell 9 voltage: 4.019V<br /></pre>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com0tag:blogger.com,1999:blog-5663160055124969741.post-14816618578462571312016-02-11T03:30:00.001-05:002016-02-11T03:30:33.257-05:00VGA Generation with Freescale i.MX23 + Linux I have long wanted to become more well acquainted with the Linux kernel and finally decided to bite the bullet. I ordered an <a href="http://www.olimex.com/Products/OLinuXino/iMX233/iMX233-OLinuXino-MAXI/open-source-hardware" target="_blank">iMX233-OLinuXino-MAXI</a> from <a href="http://www.olimex.com/" target="_blank">Olimex</a> to tinker with. The Freescale i.MX23 processor is noteworthy because it is available in an LQFP-128 package which means it can be installed on a PCB by hand with inexpensive tools and a steady hand. It is also a great platform to learn with because it is well supported by the upstream kernel and has documentation available without signing an NDA.<br /><br />The first thing I did was modify the device tree (DTS files) to enable the LCD controller and tuned it to generate a VGA signal. I built a simple R/R2 DAC on a breadboard and was able to view the image on an LCD monitor.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td><a href="http://4.bp.blogspot.com/-FWPvA0LI4S4/VrxEk-msGmI/AAAAAAAAW4o/hlFnb4JXsdU/s1600/IMG_4094.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-FWPvA0LI4S4/VrxEk-msGmI/AAAAAAAAW4o/hlFnb4JXsdU/s640/IMG_4094.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="font-size: 12.8px;">The projected image next to my workstation</td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"></div>All of this was completed under Arch Linux ARM which provides a minimal base image and root filesystem upon which a large number of packages have been ported to run on ARM. This has been a great learning experience and I plan to continue working with this chip more.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-GyNhs2LX_RM/VrxCHLGL_XI/AAAAAAAAW4M/CMMF6L30Shs/s1600/IMG_4075.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-GyNhs2LX_RM/VrxCHLGL_XI/AAAAAAAAW4M/CMMF6L30Shs/s640/IMG_4075.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Hackaday, it doesn't quite like the 640 width</td></tr></tbody></table>Continue reading to see the very minor changes I made to the kernel and how I wired it &nbsp;all up.<br /><br /><a name='more'></a><h3>Arch Linux ARM</h3><div><br /></div><div>The first thing I did was install the "default" Arch Linux ARM system image onto a 2GB uSD card. You can find <a href="http://archlinuxarm.org/platforms/armv5/olinuxino" target="_blank">complete instructions</a> on how how to install <a href="http://archlinuxarm.org/" target="_blank">Arch Linux ARM</a>&nbsp;on their website. I won't duplicate the steps required for a base setup here. I recommend using a larger card if you have one available.</div><div><br /></div><h3>Custom Kernel</h3><div><br /></div><div>The next thing you will need to do is obtain a copy of the kernel sources. I recommend performing a complete checkout of the <a href="http://github.com/torvalds/linux" target="_blank">Git repo</a>&nbsp;available on GitHub. You can also download a snapshot from <a href="http://www.kernel.org/">kernel.org</a>&nbsp;if you are in a rush.&nbsp;You will be interested in version 4.4.1 to follow along with this guide.</div><div><br /></div><div>I was inspired by an <a href="http://www.olimex.com/forum/index.php?topic=995.0">Olimex forum post</a> from March of 2013. Things have changed quite a bit in Kernel land and I decided to try to adapt the changes necessary to a more modern kernel. I found that the changes were extremely minimal! Many of the changes made are either unnecessary or available upstream already. You will need to apply a <a href="http://gist.github.com/aarossig/44f5e1a926432d01390c">patch&nbsp;(linked)</a> to two files.</div><div><br /></div>The patch to imx23.dtsi simply defines a 16-bit LCD interface. The patch to&nbsp;arch/arm/boot/dts/imx23-olinuxino.dts uses the 16-bit LCD interface and configures the timing to generate VGA waveforms.<br /><pre class="brush: plain">lcdif@80030000 {<br /> pinctrl-names = "default";<br /> pinctrl-0 = &lt;&amp;lcdif_16bit_pins_a&gt;;<br /> display = &lt;&amp;display0&gt;;<br /> status = "okay";<br /><br /> display0: display0 {<br /> bits-per-pixel = &lt;16&gt;;<br /> bus-width = &lt;16&gt;;<br /><br /> display-timings {<br /> native-mode = &lt;&amp;timing0&gt;;<br /> timing0: timing0 {<br /> clock-frequency = &lt;25200000&gt;;<br /> hactive = &lt;640&gt;;<br /> hfront-porch = &lt;16&gt;;<br /> hsync-len = &lt;54&gt;;<br /> hback-porch = &lt;48&gt;;<br /> hsync-active = &lt;0&gt;;<br /><br /> vactive = &lt;480&gt;;<br /> vfront-porch = &lt;10&gt;;<br /> vsync-len = &lt;2&gt;;<br /> vback-porch = &lt;33&gt;;<br /> vsync-active = &lt;0&gt;;<br /><br /> de-active = &lt;1&gt;;<br /> pixelclk-active = &lt;0&gt;;<br /> };<br /> };<br /> };<br />};<br /></pre>That's it! There are some other recommended patches, but I did not bother to apply them. You can see more details in the <a href="http://github.com/archlinuxarm/PKGBUILDs/tree/master/core/linux-armv5">PKGBUILD</a> for the OLinuXino kernel. This is essentially a script that describes how to build the kernel.<br /><br /><h3>Building the Kernel</h3><div><br /></div><div>Building the kernel is quite painless. A complete description, in script format is available from the <a href="http://github.com/archlinuxarm/PKGBUILDs/blob/master/core/linux-armv5/PKGBUILD">PKGBUILD</a> for the OLinuXino kernel. I will summarize the steps here. I should note that what follows is not intended to be a script, but more of a guide. Your mileage may vary :]</div><pre class="brush: bash"># Configure the environment for an ARM kernel with the arm-eabi- toolchain prefix<br />export ARCH=arm<br />export CROSS_COMPILE=arm-eabi-<br /><br /># Obtain the kernel configuration for Arch Linux ARM<br />curl -o .config http://raw.githubusercontent.com/archlinuxarm/PKGBUILDs/master/core/linux-armv5/config<br /># Build the kernel (this assume you have already applied the VGA patch)<br />make prepare<br />make -j 12 # tune this to the number of cores your system has<br /><br /># Prepare updates to the root filesystem<br />mkdir ~/rootfs # put this anywhere you wish, it is a staging area<br />mkdir -p ~/rootfs/lib/modules<br />mkdir -p ~/rootfs/lib/firmware<br />mkdir -p ~/rootfs/boot/dtbs<br /><br /># Install kernel modules / device tree binaries<br />make INSTALL_MOD_PATH=~/rootfs/ modules_install<br />make INSTALL_DTBS_PATH=~/rootfs/boot/dtbs dtbs_install<br /><br /># Copy the kernel image<br />cp arch/arm/boot/zImage ~/rootfs/boot/<br /><br /># Run depmod<br />export KERNVER=`make kernelrelease`<br />depmod -b ~/rootfs/ -F System.map $KERNVER<br /><br /># Overlay the contents of ~/rootfs onto the SD card<br />mount /dev/sdd2 /mnt/rootfs # tune this to your device name and mount point<br />cd ~/rootfs/boot<br />sudo cp -R * /mnt/rootfs # do the same for lib/modules and lib/firmware</pre><div><h3>Hardware Hacking :]</h3></div><div><br /></div><div>At this point, you should be able to boot from the SD card with your newly-built kernel. Woohoo! If all is working as expected, you should see the following:</div><pre class="brush: plain">[alarm@alarm ~]$ dmesg | grep mxsfb<br />[ 1.080000] mxsfb 80030000.lcdif: initialized<br /></pre><div>At this point there are 18 GPIO lines that are very excited to show you a console, but first we need to build a DAC in order to interface those lines with a typical VGA monitor. An R/R2 DAC will be used for this purpose. They are simple to build and have enough bandwidth to pass the video signal. For this design, we will take into account the 75-ohm impedance of the monitor.<br /><br />The effective resistance of an R/R2 DAC is R. The full-swing (white) voltage on VGA is 0.7V and the GPIO voltage is 3.3V. In order to drive the monitor effectively, the DAC will form a portion of a resistive divider according to the following formula:</div>$$ 3.3 * \frac{75}{R + 75} = 0.7 $$ Solving for R yields a resistance of 278 ohms. I decided to use 270 ohms, this is close enough.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-xWFZqsr1HCw/Vrw2kq2LJbI/AAAAAAAAW3c/bHE-zrJXdnI/s1600/r_r2_ladder.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="160" src="http://1.bp.blogspot.com/-xWFZqsr1HCw/Vrw2kq2LJbI/AAAAAAAAW3c/bHE-zrJXdnI/s640/r_r2_ladder.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">R/R2 DAC (courtesy of Wikipedia)</td></tr></tbody></table>The resistors will be arranged according to the diagram above. The most significant bit is connected closest to the output and the least significant is connected furthest from the output.<br /><br />The signal produced by this setup will be an RGB565 signal. As such, three DACs were built. Two are 5-bit DAC and the other is a 6-bit DAC.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-L_6CUMw3YF8/Vrw3gsNADDI/AAAAAAAAW3k/FV4DlTfVsQo/s1600/IMG_4072.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-L_6CUMw3YF8/Vrw3gsNADDI/AAAAAAAAW3k/FV4DlTfVsQo/s640/IMG_4072.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Initial Construction :]</td></tr></tbody></table>I arranged the resistors to use breadboard space efficiently. Below you can see one completed DAC that would eventually become the red channel. In the picture below, you can also see the first version of this circuit (on the right). I constructed it with components that I had on hand: 180 and 200 ohm resistors. This applied a severe skew to the colorspace, but it proved that I should buy the right values and build a second version.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-F8NBBVNQWX4/Vrw3yYq-SUI/AAAAAAAAW3o/yUgPfmcuELs/s1600/IMG_4074.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-F8NBBVNQWX4/Vrw3yYq-SUI/AAAAAAAAW3o/yUgPfmcuELs/s640/IMG_4074.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Red Channel Completed</td></tr></tbody></table><div>After some time, I finished assembling all three channels and connected them to the OLinuXino Maxi.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-l-EGWv7wpDA/Vrw4p5LUfxI/AAAAAAAAW34/IEU_DxocgqI/s1600/IMG_4087.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-l-EGWv7wpDA/Vrw4p5LUfxI/AAAAAAAAW34/IEU_DxocgqI/s640/IMG_4087.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Completed DAC!</td></tr></tbody></table><div>The inputs to the DAC come from the LCD data lines. The OLinuXino manual contains details. Data lines 0-4 form the blue channel, 5 - 10 form the green channel and 11-15 form the blue channel.</div><div><br /></div><div>The RGB, HSync and VSync lines were routed into a VGA connector and connected to a small monitor.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-OBKl2pEGo-Q/VrxDOhlGE2I/AAAAAAAAW4c/x6oqUjBk8IE/s1600/IMG_4080.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-OBKl2pEGo-Q/VrxDOhlGE2I/AAAAAAAAW4c/x6oqUjBk8IE/s640/IMG_4080.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Overview of the setup and my website loaded with Midori</td></tr></tbody></table><div>There should be enough detail here for you to replicate the setup if you are inclined to do so. I am considering designing a small PCB that fits onto the OLinuXino MAXI and exposes a VGA connector. I'll will need to decide if this is worth it or not.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-LD3hiBCDF5Q/VrxES3CzKyI/AAAAAAAAW4k/wWiWyYNdAQQ/s1600/IMG_4085.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-LD3hiBCDF5Q/VrxES3CzKyI/AAAAAAAAW4k/wWiWyYNdAQQ/s640/IMG_4085.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">htop running under LXDE</td></tr></tbody></table><div>The system has 64MB and runs at 454MHz. It is certainly not a speed daemon by any means. It took several minutes to render Hackaday and my blog and I had to add a swap file to avoid the OOM killer.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-gBUUa692KL4/VrxE_RPyMtI/AAAAAAAAW4w/c7TzSO1t7e0/s1600/IMG_4095.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-gBUUa692KL4/VrxE_RPyMtI/AAAAAAAAW4w/c7TzSO1t7e0/s640/IMG_4095.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">htop, again</td></tr></tbody></table><div>I hope you enjoyed reading and feel free to share if you decide to copy the setup!</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com1tag:blogger.com,1999:blog-5663160055124969741.post-16779093364389809712016-01-26T06:29:00.001-05:002016-01-26T06:38:10.667-05:00MikMod on STM32F4 Over the past couple of days I have ported <a href="http://mikmod.sourceforge.net/" target="_blank">libmikmod</a> to run on an STM32F407. This is a very memory constrained environment with only 128kB of RAM but I am able to play some rather complex MOD and XM files that are in the range of 60kB in size. I had to patch libmikmod slightly and write a new audio output driver to make all of this happen.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-3eyr_nhwIbk/VqcxiVodfCI/AAAAAAAAWa0/eCTFHMrSnxE/s1600/MikModStm32F4.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-3eyr_nhwIbk/VqcxiVodfCI/AAAAAAAAWa0/eCTFHMrSnxE/s640/MikModStm32F4.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">MikMod on STM32F4 Hardware :]</td></tr></tbody></table><div>The audio quality is quite good. I currently have MikMod configured to render at 44.1kHz in mono. I could likely render in stereo but I would be limited to smaller MOD and XM files due to the increased memory usage. Here is a video that mainly shows the audio quality as captured by my camera.</div><iframe allowfullscreen="" frameborder="0" height="400" src="http://www.youtube.com/embed/qSnDJHPhh-Q" width="640"></iframe> <br /><div>The video below contains a little more technical detail, demonstration of boot and loading different audio files.</div><div><iframe allowfullscreen="" frameborder="0" height="400" src="http://www.youtube.com/embed/PMkPF3lBW04" width="640"></iframe></div><div><br /></div><div><a name='more'></a><h3>A Brief History of this Project</h3></div><div><br /></div><div>I was initially interested in working on an embedded MOD player approximately 3 years ago. I was working with AVR devices, mainly because I had access to them. I quickly ran up against the resource limitations of those devices. Fast forward to now when I have access to STM32F4 devices with 128kB of RAM and anywhere between 256kB and 1MB of flash memory.</div><div><br /></div><div>I should note that this experiment began on an STM32F401 which has 96kB of RAM. I was able to load and play small MODs and XMs on a Nucleo board. I moved up to the STM32F407 in order to play much larger and more interesting files.</div><div><br /></div><h3>Compiling the Library</h3><div><br /></div><div>The first step was to compile libmikmod for Cortex-M4 using an arm-none-eabi toolchain. I looked at other examples in the libmikmod source tree for support on unusual platforms such as the Sony Playstation Portable or a Korean Gameboy known as GP32.</div><pre class="brush:shell"># libmikmod Makefile for targetting Cortex-M4.<br /> <br />TARGET = arm-none-eabi <br /><br />CC = $(TARGET)-gcc <br />LD = $(TARGET)-gcc <br />AS = $(TARGET)-as <br />AR = $(TARGET)-ar <br />RANLIB = $(TARGET)-ranlib <br /> <br />INCLUDES = -I../include<br /><br />CPPFLAGS = -DMIKMOD_BUILD -DDRV_METAL -DHAVE_LIMITS_H<br /><br />CFLAGS = -O2 -Wall -mcpu=cortex-m4 -mthumb -mlittle-endian \<br /> -mno-thumb-interwork -g<br /><br />ARFLAGS = cr <br /> <br />COMPILE = $(CC) -c $(CFLAGS) $(CPPFLAGS) $(INCLUDES) <br /> <br />LIBS = libmikmod.a <br /> <br />OBJ = load_mod.o load_xm.o mmalloc.o mmerror.o mmio.o mdriver.o mdreg.o \<br /> mmcmp.o pp20.o s404.o xpk.o mloader.o mlreg.o mlutil.o mplayer.o \<br /> munitrk.o mwav.o npertab.o sloader.o virtch.o virtch2.o \<br /> virtch_common.o <br /><br />HEADER_DEPS = ../include/mikmod.h ../include/mikmod_internals.h<br /> <br />libmikmod.a: $(OBJ) <br /> $(AR) $(ARFLAGS) $@ $(OBJ) <br /> $(RANLIB) $@ <br /> <br />clean: <br /> rm -f $(LIBS) *.o <br /> <br />drv_nos.o: ../drivers/drv_nos.c $(HEADER_DEPS) <br /> $(COMPILE) ../drivers/drv_nos.c -o drv_nos.o <br />load_it.o: ../loaders/load_it.c $(HEADER_DEPS) <br /> $(COMPILE) ../loaders/load_it.c -o load_it.o <br />load_mod.o: ../loaders/load_mod.c $(HEADER_DEPS) <br /> $(COMPILE) ../loaders/load_mod.c -o load_mod.o <br />load_xm.o: ../loaders/load_xm.c $(HEADER_DEPS) <br /> $(COMPILE) ../loaders/load_xm.c -o load_xm.o <br />mmalloc.o: ../mmio/mmalloc.c $(HEADER_DEPS) <br /> $(COMPILE) ../mmio/mmalloc.c -o mmalloc.o <br />mmerror.o: ../mmio/mmerror.c $(HEADER_DEPS) <br /> $(COMPILE) ../mmio/mmerror.c -o mmerror.o <br />mmio.o: ../mmio/mmio.c $(HEADER_DEPS) <br /> $(COMPILE) ../mmio/mmio.c -o mmio.o <br />mmcmp.o: ../depackers/mmcmp.c $(HEADER_DEPS) <br /> $(COMPILE) ../depackers/mmcmp.c -o mmcmp.o <br />pp20.o: ../depackers/pp20.c $(HEADER_DEPS) <br /> $(COMPILE) ../depackers/pp20.c -o pp20.o <br />s404.o: ../depackers/s404.c $(HEADER_DEPS) <br /> $(COMPILE) ../depackers/s404.c -o s404.o <br />xpk.o: ../depackers/xpk.c $(HEADER_DEPS) <br /> $(COMPILE) ../depackers/xpk.c -o xpk.o <br />mdriver.o: ../playercode/mdriver.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/mdriver.c -o mdriver.o <br />mdreg.o: ../playercode/mdreg.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/mdreg.c -o mdreg.o <br />mdulaw.o: ../playercode/mdulaw.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/mdulaw.c -o mdulaw.o <br />mloader.o: ../playercode/mloader.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/mloader.c -o mloader.o <br />mlreg.o: ../playercode/mlreg.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/mlreg.c -o mlreg.o <br />mlutil.o: ../playercode/mlutil.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/mlutil.c -o mlutil.o <br />mplayer.o: ../playercode/mplayer.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/mplayer.c -o mplayer.o <br />munitrk.o: ../playercode/munitrk.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/munitrk.c -o munitrk.o <br />mwav.o: ../playercode/mwav.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/mwav.c -o mwav.o <br />npertab.o: ../playercode/npertab.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/npertab.c -o npertab.o <br />sloader.o: ../playercode/sloader.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/sloader.c -o sloader.o <br />virtch.o: ../playercode/virtch.c ../playercode/virtch_common.c $(HEADER_DEPS) <br /> $(COMPILE) ../playercode/virtch.c -o virtch.o <br />virtch2.o: ../playercode/virtch2.c ../playercode/virtch_common.c $(HEADER_DEPS)<br /> $(COMPILE) ../playercode/virtch2.c -o virtch2.o<br />virtch_common.o: ../playercode/virtch_common.c $(HEADER_DEPS)<br /> $(COMPILE) ../playercode/virtch_common.c -o virtch_common.o</pre><div>This Makefile is inspired by the GP32 and PSP drivers. They gave me insights into the minimum necessary to build libmikmod and then I took it a few steps further.<br /><br />The first thing I did was remove most of the loaders. I was only interested in MOD and XM. This results in the following diff to include/mikmod.h:</div><pre class="brush:plain">-MIKMODAPI extern struct MLOADER load_669; /* 669 and Extended-669 (by Tran/Renaissance) */<br />-MIKMODAPI extern struct MLOADER load_amf; /* DMP Advanced Module Format (by Otto Chrons) */<br />-MIKMODAPI extern struct MLOADER load_asy; /* ASYLUM Music Format 1.0 */<br />-MIKMODAPI extern struct MLOADER load_dsm; /* DSIK internal module format */<br />-MIKMODAPI extern struct MLOADER load_far; /* Farandole Composer (by Daniel Potter) */<br />-MIKMODAPI extern struct MLOADER load_gdm; /* General DigiMusic (by Edward Schlunder) */<br />-MIKMODAPI extern struct MLOADER load_gt2; /* Graoumf tracker */<br />-MIKMODAPI extern struct MLOADER load_it; /* Impulse Tracker (by Jeffrey Lim) */<br />-MIKMODAPI extern struct MLOADER load_imf; /* Imago Orpheus (by Lutz Roeder) */<br />-MIKMODAPI extern struct MLOADER load_med; /* Amiga MED modules (by Teijo Kinnunen) */<br />-MIKMODAPI extern struct MLOADER load_m15; /* Soundtracker 15-instrument */<br /> MIKMODAPI extern struct MLOADER load_mod; /* Standard 31-instrument Module loader */<br />-MIKMODAPI extern struct MLOADER load_mtm; /* Multi-Tracker Module (by Renaissance) */<br />-MIKMODAPI extern struct MLOADER load_okt; /* Amiga Oktalyzer */<br />-MIKMODAPI extern struct MLOADER load_stm; /* ScreamTracker 2 (by Future Crew) */<br />-MIKMODAPI extern struct MLOADER load_stx; /* STMIK 0.2 (by Future Crew) */<br />-MIKMODAPI extern struct MLOADER load_s3m; /* ScreamTracker 3 (by Future Crew) */<br />-MIKMODAPI extern struct MLOADER load_ult; /* UltraTracker (by MAS) */<br />-MIKMODAPI extern struct MLOADER load_umx; /* Unreal UMX container of Epic Games */<br />-MIKMODAPI extern struct MLOADER load_uni; /* MikMod and APlayer internal module format */<br /> MIKMODAPI extern struct MLOADER load_xm; /* FastTracker 2 (by Triton) */</pre><div>I also removed all of the other drivers except for drv_nos which is required as a fallback when no other driver is available.<br /><br />After some experimentation I was able to produce an archive file to link into my application.<br /><br /><h3>Initializing MikMod</h3></div><div><br /></div><div>To my surprise the library worked on the first attempt. I decided to use ChibiOS for this project. It comes with a kernel that supports pre-emptive multitasking and a hardware abstraction layer (HAL) with drivers for all of the common peripherals: UART, SPI, I2C, PWM, GPT and more.</div><div><br /></div><div>Here is the main function for my program. As you can see, it is not terribly complex. Initializing MikMod is very straightforward. I included some logging to make debugging easier.</div><pre class="brush:c">int main(void) {<br /> halInit();<br /> chSysInit();<br /><br /> initSerialConsole();<br /> initMikMod();<br /><br /> MODULE *module = Player_LoadMem(goldenages_mod,<br /> goldenages_mod_len, 4, false);<br /><br /> if (!module) {<br /> SerialLog("MikMod", "Error loading module");<br /> chThdSleep(TIME_INFINITE);<br /> }<br /><br /> SerialLog("MikMod", "Loaded module: %s", module-&gt;songname);<br /><br /> // Play the module.<br /> Player_Start(module);<br /><br /> while (Player_Active()) {<br /> MikMod_Update();<br /> }<br /><br /> SerialLog("MikMod", "Playing complete");<br /><br /> Player_Stop();<br /> Player_Free(module);<br /><br /> // Cleanup after MikMod.<br /> MikMod_Exit();<br /><br /> while(1) {<br /> chThdSleepMilliseconds(100);<br /> }<br /><br /> return 0;<br />}</pre><div>I register an error handler with MikMod and print the logs to a serial console. This is handy when attempting to play a MOD file that just barely fits in RAM. MikMod will happily invoke this function and tell you that it failed to allocate memory.</div><pre class="brush:c">static void MikModErrorHandler(void) {<br /> SerialLog("MikMod", "error %d%s: %s",<br /> MikMod_errno,<br /> MikMod_critical ? " (critical)" : "",<br /> MikMod_strerror(MikMod_errno));<br />}<br /><br />/*<br /> * Initialize the serial console for logging.<br /> */<br />void initSerialConsole(void) {<br /> SerialConfig serialConfig = {<br /> .speed = 115200,<br /> .cr1 = 0,<br /> .cr2 = 0,<br /> .cr3 = 0,<br /> };<br /><br /> sdStart(&amp;SD2, &amp;serialConfig);<br /> palSetPadMode(GPIOA, 2, PAL_MODE_ALTERNATE(7));<br /> palSetPadMode(GPIOA, 3, PAL_MODE_ALTERNATE(7));<br />}<br /><br />/*<br /> * Initialize and configure libmikmod.<br /> */<br />void initMikMod(void) {<br /> SerialLog("MikMod", "Initialization start");<br /><br /> MikMod_RegisterErrorHandler(MikModErrorHandler);<br /> MikMod_RegisterDriver(&amp;drv_metal);<br /> MikMod_RegisterAllLoaders();<br /><br /> md_mode = DMODE_INTERP | DMODE_SOFT_SNDFX | DMODE_SOFT_MUSIC;<br /> md_reverb = 0;<br /> md_mixfreq = 44100;<br /><br /> if (MikMod_Init("")) {<br /> SerialLog("MikMod", "Initialization error");<br /> chThdSleep(TIME_INFINITE);<br /> }<br /><br /> SerialLog("MikMod", "Initialization complete");<br />}</pre><div><br /><h3>PWM Audio Output</h3></div><div><br /></div><div>I contemplated using&nbsp;the CS43L22 audio codec in the early stages but decided it was not worth the effort. The STM32F4DISCOVERY includes this codec with a convenient 3.5mm jack for connection to headphones. I decided not to use the codec due to the effort required to initialize and enable streaming audio output. There are several samples available online, but if I want to work with an audio codec I would prefer to take a deep dive and read the entire datasheet.</div><div><br /></div><div>I decided to use a PWM output to generate an audio waveform and am very satisfied with the results.</div><pre class="brush:c">static PWMConfig pwmConfig = {<br /> .frequency = 16000000,<br /> .period = 255,<br /> .callback = NULL,<br /> .channels = {<br /> { .mode = PWM_OUTPUT_ACTIVE_HIGH, .callback = NULL },<br /> { .mode = PWM_OUTPUT_DISABLED, .callback = NULL },<br /> { .mode = PWM_OUTPUT_DISABLED, .callback = NULL },<br /> { .mode = PWM_OUTPUT_DISABLED, .callback = NULL },<br /> },<br />};<br /><br />static void timerCallback(GPTDriver *gptDriver) {<br /> chSysLockFromISR();<br /><br /> signed char sample = buffer[buf_index][play_pos++];<br /> pwmEnableChannelI(&amp;PWMD1, 0, PWM_FRACTION_TO_WIDTH(&amp;PWMD1, 255, sample));<br /><br /> if (play_pos == SAMPLE_LENGTH) {<br /> play_pos = 0;<br /> buf_index ^= 1;<br /> }<br /><br /> chSysUnlockFromISR();<br />}<br /><br />static GPTConfig gptConfig = {<br /> .frequency = 882000,<br /> .callback = timerCallback,<br />};</pre><div>A PWM peripheral is configured to run at a very high frequency and a general purpose timer is used to update the duty cycle at a rate of 44.1kHz. These two devices working together produce an audio waveform.<br /><br />This audio driver is double buffered. When playback begins, the first buffer is filled with audio samples and the PWM/timer are enabled. While the first buffer is playing, the second is filled with sample data. The driver then waits until playback from the second buffer has began to replace the contents of the first buffer with new audio data. This ensures that the audio output is never starved for data (buffer underrun). This is based on the assumption that generation of samples is much faster than playback.</div><pre class="brush:c">static void METAL_Update(void) {<br /> if (buf_index_update != buf_index) {<br /> VC_WriteBytes((signed char *)buffer[buf_index_update], SAMPLE_LENGTH);<br /> buf_index_update ^= 1;<br /> }<br />}<br /><br />static BOOL METAL_IsThere(void) {<br /> return 1;<br />}<br /><br />static int METAL_Init(void) {<br /> if (VC_Init()) {<br /> return 1;<br /> }<br /><br /> // Initialize the PWM peripheral.<br /> pwmStart(&amp;PWMD1, &amp;pwmConfig);<br /> palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(1));<br /> pwmEnableChannelI(&amp;PWMD1, 0, PWM_FRACTION_TO_WIDTH(&amp;PWMD1, 255, 127));<br /><br /> // Initialize the GPT peripheral.<br /> gptStart(&amp;GPTD3, &amp;gptConfig);<br /><br /> return 0;<br />}<br /><br />static void METAL_Exit(void) {<br /> VC_Exit();<br />}<br /><br />static int METAL_Reset(void) {<br /> VC_Exit();<br /> return VC_Init();<br />}<br /><br />static int METAL_PlayStart(void) {<br /> VC_PlayStart();<br /><br /> VC_WriteBytes((signed char *)buffer[0], SAMPLE_LENGTH);<br /> gptStartContinuous(&amp;GPTD3, 20);<br /><br /> return 0;<br />}<br /><br />static void METAL_PlayStop(void) {<br /> gptStop(&amp;GPTD3);<br /> pwmStop(&amp;PWMD1);<br /> VC_PlayStop();<br />}<br /><br />MIKMODAPI MDRIVER drv_nos;<br /><br />MIKMODAPI MDRIVER drv_metal = {<br /> NULL,<br /> "",<br /> "",<br /> 0,<br /> 255,<br /> "",<br /> NULL,<br /> NULL,<br /> METAL_IsThere,<br /> VC_SampleLoad,<br /> VC_SampleUnload,<br /> VC_SampleSpace,<br /> VC_SampleLength,<br /> METAL_Init,<br /> METAL_Exit,<br /> METAL_Reset,<br /> VC_SetNumVoices,<br /> METAL_PlayStart,<br /> METAL_PlayStop,<br /> METAL_Update,<br /> NULL,<br /> VC_VoiceSetVolume,<br /> VC_VoiceGetVolume,<br /> VC_VoiceSetFrequency,<br /> VC_VoiceGetFrequency,<br /> VC_VoiceSetPanning,<br /> VC_VoiceGetPanning,<br /> VC_VoicePlay,<br /> VC_VoiceStop,<br /> VC_VoiceStopped,<br /> VC_VoiceGetPosition,<br /> VC_VoiceRealVolume<br />};</pre><div>The remainder of the driver is straight forward. I implemented the required callbacks from the MikMod library to control the state of the PWM and timer peripherals. I then spent time tuning the audio output. I initially had misconfigured the period of the PWM peripheral and introduced significant aliasing into the output.<br /><br /><h3>Conclusions!</h3></div><div><br /></div><div>I may look at libmikmod some more. Some optimizations that I made included marking constant lookup tables as const to move them from RAM to flash. This made improvements in the output of arm-none-eabi-size. This change would likely benefit all platforms so I may consider upstreaming it. I would also like to look at making some dynamically allocated buffers static. Their size is known at compile time and allocating them statically would reduce heap usage and subsequent fragmentation. This is a major concern on embedded platforms.</div><div><br /></div><div>Thanks for reading!</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com3tag:blogger.com,1999:blog-5663160055124969741.post-5402561695723203512015-11-09T03:38:00.000-05:002015-11-09T04:12:28.739-05:00Long Range Electric Longboarding I bought a <a href="http://boostedboards.com/" target="_blank">Boosted Board</a> back in August of this year while living in New York City. It was an awesome purchase. It is fun to ride and I could often beat the subway to my destination on shorter trips. I have since moved to Silicon Valley and find the range to be lacking slightly. The board gets approximately 7 miles of range out of the box and my office is 10 miles from home.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-Q0h9mO1kA3o/VkAxiSIUcBI/AAAAAAAATik/VHmj8uA6bSo/s1600/IMG_3580.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-Q0h9mO1kA3o/VkAxiSIUcBI/AAAAAAAATik/VHmj8uA6bSo/s640/IMG_3580.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">My Boosted Board shortly after purchase.</td></tr></tbody></table>I added 288Wh of high-discharge lithium-ion batteries to the 99Wh of batteries that came with the board. I was inspired by the&nbsp;<a href="http://www.youtube.com/channel/UC23SELZv2KKYvqwB02c_I0A" target="_blank">Portable Electric Vehicle</a>&nbsp;Youtube Channel.&nbsp;&nbsp;During the first test ride I was able to ride more than 13 miles before stopping. The fuel gauge on the remote showed more than 20% of battery remaining.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-H5Hq2fA4DP0/VkAyBRP9qFI/AAAAAAAATis/9R5gyEw2Ufk/s1600/IMG_3911.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-H5Hq2fA4DP0/VkAyBRP9qFI/AAAAAAAATis/9R5gyEw2Ufk/s640/IMG_3911.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">My Boosted Board after the battery upgrade :]</td></tr></tbody></table>During this process I also designed a custom lighting system based on WS2812 LEDs and an Arduino Mini Pro.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-dq3eS8HOXsw/VkBRv7p39OI/AAAAAAAATmc/N3zzBBRHP54/s1600/IMG_3970.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-dq3eS8HOXsw/VkBRv7p39OI/AAAAAAAATmc/N3zzBBRHP54/s640/IMG_3970.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">These LEDs are bright!</td></tr></tbody></table><a name='more'></a><h2>Batteries, Batteries, Batteries</h2><div><br /></div><div>The Boosted Board uses <a href="http://en.wikipedia.org/wiki/Lithium_iron_phosphate_battery" target="_blank">Lithium Iron Phosphate (LiFePO4)</a> batteries. In a nutshell, this battery chemistry trades capacity for higher power and longer shelf life. Boosted has a blog detailing the <a href="http://boostedboards.com/technical-progress-part-iii-lithium-battery/" target="_blank">design process</a> behind the selection of battery chemistry and size. The battery inside the board is a 99Wh, 12 cell LiFePO4 pack.</div><div><br /></div><div>You may have been shocked to read that I selected Lithium-Ion cells to extend the range. I built two 92.5Wh 10 cell Li-Ion battery packs. This should immediately register as cause for concern. Li-Fe cells have a nominal voltage of 3.2V and Li-Ion cells have a nominal voltage of 3.7V. There are 12 LiFe cells in the board which gives a nominal voltage of 38.4V. My 10 cell pack has a nominal voltage of 37V. This is very close.</div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-xoUpo-mL06o/VkA6YfMpRcI/AAAAAAAATjQ/os5YanCIJx0/s1600/IMG_3964.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-xoUpo-mL06o/VkA6YfMpRcI/AAAAAAAATjQ/os5YanCIJx0/s640/IMG_3964.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Verifying the internal voltage</td></tr></tbody></table><div>To ensure that this could be done safely, I measured the voltage of a fully charged Boosted Board to be 40.7V. My external pack, when fully charged is also 40.7V. What this means is that I can connect the batteries in parallel. The load of the motors will be distributed across the battery packs.</div><div><br /></div><div>In addition to adding more capacity to the board, the efficiency of the internal battery will be increased. A battery that is discharged slowly is more efficient than a battery that is discharged quickly. Win win!</div><div><br /></div><h2>Battery Assembly</h2><div><br /></div>I created two 10S battery packs with Samsung INR18650-25R cells. These are high-discharge lithium-ion cells with a rate of 35A.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-dpCulEPfUY8/VkA5A23VfsI/AAAAAAAATi8/Hv8S9OcUL3o/s1600/IMG_3824.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-dpCulEPfUY8/VkA5A23VfsI/AAAAAAAATi8/Hv8S9OcUL3o/s640/IMG_3824.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Batteries!</td></tr></tbody></table>I tabbed these batteries together in series along with a balance wire. These batteries are charged externally with a balancing charger to ensure that individual cell voltages are equal to one another.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-v6XV2PJQ59M/VkA7Kxa_8JI/AAAAAAAATjY/boQ9XdHefE0/s1600/IMG_3858.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-v6XV2PJQ59M/VkA7Kxa_8JI/AAAAAAAATjY/boQ9XdHefE0/s640/IMG_3858.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Two cells and a balance cable extension</td></tr></tbody></table><div>The pack was assembled with all cells next to one another to keep the profile low. I researched different options for my batteries and found that many were too tall. There are several 10S packs available from RC hobby shops such as <a href="http://www.hobbyking.com/" target="_blank">Hobby King</a>&nbsp;but they are too tall. Clearance under the board is at a premium so it is important that any additional components are as short as possible.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-28tQzToDktU/VkA7_-wSJMI/AAAAAAAATjg/Q3SnIq3uJR8/s1600/IMG_3863.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-28tQzToDktU/VkA7_-wSJMI/AAAAAAAATjg/Q3SnIq3uJR8/s640/IMG_3863.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Tabbed cells with the balance lead attached</td></tr></tbody></table><div>These cells need to have a minimal layer of protection. I used 185mm shrink tube to protect the cells and provide rigidity to the pack. I added electrical tape to the ends of the cells as well. This made it difficult to slide the shrink tube on, so only one of the packs have this feature. It would be great if these batteries had a housing, they will take a beating so close to the ground. This will have to do for now.</div><div><br /></div><div>I settled on HXT connectors to connect the batteries to the board. They are rugged connectors with high current handling. In retrospect, I wish I had gone with something that is easier to terminate and provides better keying. Overall, they have been durable and able to withstand over 20 miles of riding so far.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-n2i_b4e0SKI/VkA8jjGq08I/AAAAAAAATjo/wfKcdqGBQmM/s1600/IMG_3870.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-n2i_b4e0SKI/VkA8jjGq08I/AAAAAAAATjo/wfKcdqGBQmM/s640/IMG_3870.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Finished pack :]</td></tr></tbody></table>I use an <a href="http://www.icharger.co.nz/Products/1010B-.aspx" target="_blank">iCharger 1010B+</a>&nbsp;balancing charger to charge these packs. It is capable of charging at up to 300W and ensures that voltage between cells remains equal. It is quite a feature packed device, capable of charging a wide variety of battery chemistries. I have no doubt that it will be useful for more projects in the future. I have a 12V, 10A power supply that I use to supply power to the charger.<br /><div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-uDlxSdEB644/VkA-EBDXWuI/AAAAAAAATj0/FC7NrsV8o2w/s1600/IMG_3874.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-uDlxSdEB644/VkA-EBDXWuI/AAAAAAAATj0/FC7NrsV8o2w/s640/IMG_3874.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">First Charge Test at 100mA, making sure the magic smoke stays inside the batteries ;]</td></tr></tbody></table>I initially tried charging at a mere 100mA to ensure that everything was connected properly. I verified voltages and learned how the charger works. I quickly stepped it up to 2.5A. This will get the pack charged in an hour. The fan inside the iCharger spins up at this rate.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-gZFTCCvz3RE/VkA-t6gtJqI/AAAAAAAATj8/_icxLE8LCP0/s1600/IMG_3878.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-gZFTCCvz3RE/VkA-t6gtJqI/AAAAAAAATj8/_icxLE8LCP0/s640/IMG_3878.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Charging at 2.5A</td></tr></tbody></table><h2>Board Modification</h2><div><br /></div><div>I had to modify the Boosted Board to externalize the supply rails from the internal battery pack. I picked up a multimeter and started probing around inside the Boosted Board. The ESC (Electronic Speed Controller) designed for the Boosted Board is impressive to say the least. It supports regenerative braking which allows energy used to brake the motors to be stored in the batteries.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-PKcCJ-IpaBY/VkA_pdyP39I/AAAAAAAATkI/xaKFR05puOM/s1600/IMG_3833.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-PKcCJ-IpaBY/VkA_pdyP39I/AAAAAAAATkI/xaKFR05puOM/s640/IMG_3833.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The Boosted Board ESC</td></tr></tbody></table>The supply rails deliver power from the battery at the front of the board through flat braided cabling that is routed through the deck of the longboard. I exposed a section of the braided cable to attach my external battery connector to.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-ZuAIRniTi7E/VkBAV_HuA3I/AAAAAAAATkQ/UwnqwYvSTz0/s1600/IMG_3837.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-ZuAIRniTi7E/VkBAV_HuA3I/AAAAAAAATkQ/UwnqwYvSTz0/s640/IMG_3837.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Exposed supply rail from the internal battery</td></tr></tbody></table>I used 12AWG silicone wire to connect external batteries. I find it to be flexible and easy to work with.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-VyQcKq8Ley0/VkBAtY_0NpI/AAAAAAAATkY/LDBGgGdmo3g/s1600/IMG_3851.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-VyQcKq8Ley0/VkBAtY_0NpI/AAAAAAAATkY/LDBGgGdmo3g/s640/IMG_3851.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">12AWG external battery cable</td></tr></tbody></table><div><div>I routed these wires through the loom at the back of the board and out the side. I should note that the use of an external battery is completely optional after this modification.</div></div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-MASAoX42A-Q/VkBBdpFvJgI/AAAAAAAATkk/hIH0nr8VjtY/s1600/IMG_3855.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-MASAoX42A-Q/VkBBdpFvJgI/AAAAAAAATkk/hIH0nr8VjtY/s640/IMG_3855.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">External Battery Cabling</td></tr></tbody></table>An HXT connector was used to terminate the external battery connector.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-oY5xUM9dwm0/VkBBuNFF8RI/AAAAAAAATks/8AMVNDg4H4s/s1600/IMG_3881.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-oY5xUM9dwm0/VkBBuNFF8RI/AAAAAAAATks/8AMVNDg4H4s/s640/IMG_3881.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">HXT Connector</td></tr></tbody></table>I used extra-strength velcro to hold the battery to the bottom of the board. This is different than the usual variety of velcro that uses a felt portion coupled with plastic hooks. This velcro has plastic hooks on both sides that engage one another. As such, it is difficult to push together and even more difficult to break apart. I can lift the weight of the entire board through the battery.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-zmsxQFsLcqU/VkBC18euPKI/AAAAAAAATk4/DoCv8lu_Sw0/s1600/IMG_3896.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-zmsxQFsLcqU/VkBC18euPKI/AAAAAAAATk4/DoCv8lu_Sw0/s640/IMG_3896.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Velcro mounting on the battery and board</td></tr></tbody></table>I built a small Y-adapter for the HXT connectors so that two packs can be connected in parallel.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-rP4VL_vROPs/VkBDaEY_DtI/AAAAAAAATlA/BVMC7PcXvD0/s1600/IMG_3909.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-rP4VL_vROPs/VkBDaEY_DtI/AAAAAAAATlA/BVMC7PcXvD0/s640/IMG_3909.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">HXT Splitter</td></tr></tbody></table><div>The batteries are mounted next to one another. I am currently using electrical tape to hold excess wire and balance connectors to the bottom of the board. I will come up with a more permanent solution soon, but this works for now.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-HKuonnnnjmo/VkBDwADYlwI/AAAAAAAATlI/fsohD5XjwRQ/s1600/IMG_3912.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-HKuonnnnjmo/VkBDwADYlwI/AAAAAAAATlI/fsohD5XjwRQ/s640/IMG_3912.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Mounted battery packs</td></tr></tbody></table>One of the most important details of this setup is that it is important to verify that the voltage of &nbsp;all battery packs are close to one another. It would be detrimental if one full pack was connected in parallel to a pack that is empty.<br /><div><br /><h2>Lighting System</h2><div><br /></div><div>You may have noticed the LED lights that appeared in the pictures above. This system is designed by <a href="http://thirdkindbeyond.com/" target="_blank">Third Kind</a>. I was not impressed with many aspects of their design, namely the fact that the LED strip must be disconnected from the battery in order to recharge. I designed a lighting system based on WS2812 LEDs and an Arduino.</div><div><br /></div><div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-DhWwzvCJCVw/VkBGiVF_OTI/AAAAAAAATlU/3esU9fiVrA8/s1600/IMG_3927.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-DhWwzvCJCVw/VkBGiVF_OTI/AAAAAAAATlU/3esU9fiVrA8/s640/IMG_3927.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Power Switch and Mode Button</td></tr></tbody></table></div><div>The lighting system runs from an internal LiPo battery that was salvaged from the Third Kind system. It was a 3S pack that I reduced to 2S. Two LM7805 linear regulators provide 5V for the LEDs and Arduino. The LEDs are split across two 5V rails to avoid exceeding the current of one of the regulators when running at max brightness.</div><div><br /></div><div>In order to ensure that the battery does not reach an overly discharged state, the ADC is used to measure the battery voltage through a resistive divider. When it reaches a pre-determined cutoff voltage, the LEDs will switch to a low duty-cycle flashing mode to indicate that the main power should be switched off and the lights recharged. Currently charging is performed using the same 1010B+ charger used for the external battery packs.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-qlF64Et4JXs/VkBHo6-kNMI/AAAAAAAATlg/LiNdEQ7WZvY/s1600/IMG_3920.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-qlF64Et4JXs/VkBHo6-kNMI/AAAAAAAATlg/LiNdEQ7WZvY/s640/IMG_3920.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Lighting system with the box open</td></tr></tbody></table><div>The construction is very simple with point-to-point used for all components. The Arduino is wrapped in shrink tube with the programming headers exposed. I decided to stick with the Arduino bootloader for the simplicity of updates. I am not a fan of the boot delay and may consider loading code using my AVR-ISP directly.</div><div><br /></div><div>The mode button allows switching between various animations. So far I have only wrote one, but have plans for a few more. I may also consider adding an IMU (Inertial Measurement Unit) to detect motion of the board. For example, when slowing down the LEDs could automatically fade to red and glow with a brightness that is a function of deceleration (after some signal processing and filtering, of course).</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-MS39fNhlXhE/VkBIsZRQ_zI/AAAAAAAATlo/HMd_6QhvYds/s1600/IMG_3922.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-MS39fNhlXhE/VkBIsZRQ_zI/AAAAAAAATlo/HMd_6QhvYds/s640/IMG_3922.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Initial testing</td></tr></tbody></table><div>The lighting system looks great and is extremely bright. I was able to come up with a color of orange that matches the Boosted Board reasonably well. I call it "Boosted Orange" which in the color gamut of the WS2812 LEDs is encoded as <span style="font-family: &quot;courier new&quot; , &quot;courier&quot; , monospace;">#ff1900</span><span style="font-family: inherit;">. I used the <a href="http://github.com/cpldcpu/light_ws2812" target="_blank">light_ws2812</a> driver from cpldcpu on GitHub. It works very well for the purpose and makes no assumptions about usage of the Arduino framework (which I don't use).</span></div><div><span style="font-family: inherit;"><br /></span></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-pCcb0jm83K0/VkBMDimak6I/AAAAAAAATl0/I5iEsPs5EQw/s1600/IMG_3923.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-pCcb0jm83K0/VkBMDimak6I/AAAAAAAATl0/I5iEsPs5EQw/s640/IMG_3923.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Front-facing and downward facing LEDs</td></tr></tbody></table>Front and downward facing LEDs were added to the front of the board. This casts a beam towards the ground on the front of the board. The sharp angle between the road and board make it difficult to cast a wide beam.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-KtZr17g2oQ4/VkBMqaRHjhI/AAAAAAAATl8/93Nz9KT0vOk/s1600/IMG_3933.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-KtZr17g2oQ4/VkBMqaRHjhI/AAAAAAAATl8/93Nz9KT0vOk/s640/IMG_3933.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Now <i>this</i> is a loaded deck ;]</td></tr></tbody></table>I should note that the board is quite heavy compared to before. The good news is that the board functions the same as it did before, without the batteries. If I am going on a short trip or riding from my bus stop to the office, I can usually leave the extra batteries at home.<br /><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-h6rAH1Q10iQ/VkBNQLPZ5yI/AAAAAAAATmI/d3JIDxMZ-uU/s1600/IMG_3939.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-h6rAH1Q10iQ/VkBNQLPZ5yI/AAAAAAAATmI/d3JIDxMZ-uU/s640/IMG_3939.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Lighting system cover and screws</td></tr></tbody></table><div>The LED lighting system is currently charged by removing the door. I expect approximately 2 hours of runtime which means a recharge will be necessary after every couple of night rides. I should probably add a durable external connector to make recharging easier.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-oeH15GiaYjs/VkBNyOK4WOI/AAAAAAAATmQ/__LDfayr6Ug/s1600/IMG_3951.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-oeH15GiaYjs/VkBNyOK4WOI/AAAAAAAATmQ/__LDfayr6Ug/s640/IMG_3951.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">FTDI Programming Interface</td></tr></tbody></table>The Arduino Mini Pro is held into the box with a small piece of high-strength velcro.<br /><div><br /></div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='640' height='400' src='http://www.blogger.com/video.g?token=AD6v5dwO88ZlcMkMnxw0RWrdhtOr1Yf8ltwxEsmqmsxaZYYNeA3SMq0-zDRklNrmI5DFpI0CzhRXau9OqCEjQAxAlQ' class='b-hbp-video b-uploaded' frameborder='0' /></div><div><br /></div><div><h2>A Warning</h2><div><br /></div><div>Using any of the information contained in this article to modify your Boosted Board will void the warranty. You risk damaging the board and injuring yourself. This article is provided for informational purposes only. If you decide to perform the same modification you accept any liability associated with doing so.</div><div><br /></div><div>Proceed with caution and&nbsp;<b>always</b>&nbsp;wear a helmet.<br /><br /><h2>Fin</h2></div><br />I hope you enjoyed reading this article as much as I enjoyed working on this project.<br /><br />Your comments are always welcome. Thanks for reading!<br /><br /></div></div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com22tag:blogger.com,1999:blog-5663160055124969741.post-53408094925101207882014-11-23T18:04:00.000-05:002014-11-23T18:05:24.481-05:00Flir Lepton Thermal Imaging Sensor + Gameduino 2 I recently got my hands on a pair of <a href="http://www.flir.com/quarknd/" target="_blank">Flir Lepton</a> thermal imaging sensors and have spent the last week bringing them online in my spare time. These are absolutely incredible devices that I believe will pave the way to consumer devices incorporating thermal imaging cameras. The footprint of the camera module (and optical assembly) is about the size of a dime. The resolution is 80x60 at 14bpp which is remarkable despite sounding low.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-3HiGjoIY3Ro/VHE-x6MZCUI/AAAAAAAAJmA/IuBEYKf-JAM/s1600/IMG_3140.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-3HiGjoIY3Ro/VHE-x6MZCUI/AAAAAAAAJmA/IuBEYKf-JAM/s1600/IMG_3140.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Thermal Andrew :]</td></tr></tbody></table><span id="goog_1716032487"></span>I have successfully implemented a driver for the Lepton module and displayed frames on an LCD. This is all running on an STM32F4 processor on a <a href="http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1847/PF260000" target="_blank">Nucleo board</a>. Attached to it is a <a href="http://excamera.com/sphinx/gameduino2/" target="_blank">Gameduino 2</a> which incorporates the <a href="http://www.ftdichip.com/Products/ICs/FT800.html" target="_blank">FT800</a> graphics processor. I have implemented my own colorization and min/max scaling before uploading the frames to the GPU.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-PEa25o5K2ms/VHFBJzpLuzI/AAAAAAAAJmM/S4d7v7aM_dY/s1600/IMG_3145.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-PEa25o5K2ms/VHFBJzpLuzI/AAAAAAAAJmM/S4d7v7aM_dY/s1600/IMG_3145.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Front System Overview</td></tr></tbody></table>I have used some simple jumper wires to interface with this camera. This setup is running at 21MHz with no issues. I am using a <a href="http://www.pureengineering.com/projects/lepton" target="_blank">breakout board</a> provided by <a href="http://www.pureengineering.com/" target="_blank">Pure Engineering</a>. You can pick up one from <a href="http://www.tindie.com/products/PureEngineering/flir-lepton-thermal-camera-breakout-2/" target="_blank">Tindie</a> if you are interested. The Lepton module can be ripped out the <a href="http://www.flir.com/flirone/" target="_blank">Flir One iPhone accessory</a> for now.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-GkDEsmyndEA/VHFBamHjHGI/AAAAAAAAJmU/JmCnZnYyGXQ/s1600/IMG_3142.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-GkDEsmyndEA/VHFBamHjHGI/AAAAAAAAJmU/JmCnZnYyGXQ/s1600/IMG_3142.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Rear System Overview and Second Camera (future project ;])</td></tr></tbody></table><div>I wrote my own driver for the Lepton core and the FT800 graphics processor. Continue reading for more details!</div><div><a name='more'></a></div><h3><br /></h3><h3>Hardware Overview</h3><div><br /></div><div>As I mentioned, I am using the Flir Lepton sensor and a breakout board designed by Pure Engineering.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-QCbCrS-B-Rg/VHJl8mEYsiI/AAAAAAAAJoQ/eDuvckx996w/s1600/IMG_3190.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-QCbCrS-B-Rg/VHJl8mEYsiI/AAAAAAAAJoQ/eDuvckx996w/s1600/IMG_3190.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Lepton seated in the Pure Engineering breakout board</td></tr></tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-ibhUX_r_e1w/VHJmSRTIUVI/AAAAAAAAJoY/vOnqCQtgA24/s1600/IMG_3188.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-ibhUX_r_e1w/VHJmSRTIUVI/AAAAAAAAJoY/vOnqCQtgA24/s1600/IMG_3188.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Breakout Board Rear</td></tr></tbody></table><h3>Protocol and Driver Implementation</h3><div><br /></div><div>This module incorporates two interfaces. One is i<sup>2</sup>c and the other is SPI. The i<sup>2</sup>c interface is used to configure the module in a number of ways. This is something that I have not explored yet. In the default configuration of this camera, it will export frame data over SPI. Each packet is 164 bytes long. The first two bytes are line number, the second two bytes are a CRC and the final 160 bytes are the pixel data for that line. Each pixel is sent as two bytes to accommodate the 14-bit range. Data is sent in big-endian format (high byte first).</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-W1cfsxDScYc/VHFMO42J4-I/AAAAAAAAJm4/n5_Ta3PcF1w/s1600/VoSPI%2BPacket.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-W1cfsxDScYc/VHFMO42J4-I/AAAAAAAAJm4/n5_Ta3PcF1w/s1600/VoSPI%2BPacket.png" height="160" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">VoSPI Packet</td></tr></tbody></table><div>Occasionally the camera will send a synchronization packet. In this case, line number is sent as xFxx. The x bits are "don't cares" so this can be tested for by applying a bitmask to the first byte of 0x0F and testing that it is equal to 0x0F.</div><div><br /></div><div>I defined some symbols to make parsing the protocol easier.</div><pre class="brush:c">#define LEPTON_PACKET_ID_LENGTH 2<br />#define LEPTON_PACKET_CRC_LENGTH 2<br />#define LEPTON_PACKET_CONTENT_LENGTH 160<br /><br />#define LEPTON_PACKET_HEADER_LENGTH \<br /> (LEPTON_PACKET_ID_LENGTH + LEPTON_PACKET_CRC_LENGTH)<br />#define LEPTON_PACKET_LENGTH \<br /> (LEPTON_PACKET_HEADER_LENGTH + LEPTON_PACKET_CONTENT_LENGTH)<br /><br />#define LEPTON_WIDTH 80<br />#define LEPTON_HEIGHT 60<br /></pre><div>I defined a Lepton_t struct to maintain a reference to its' SPI module and chip select line. I also have a LeptonColor_t struct to store a simple RGB565 color that the FT800 can use.</div><pre class="brush:c">typedef struct Lepton_t {<br /> volatile SystemSpiModule_t *Spi;<br /> volatile SystemGpioModule_t *CsPort;<br /> volatile uint32_t CsPin;<br /> uint8_t FrameBuffer[LEPTON_HEIGHT * LEPTON_PACKET_CONTENT_LENGTH];<br />} Lepton_t;<br /><br />typedef struct LeptonColor_t {<br /> struct {<br /> uint8_t red : 5;<br /> uint8_t green : 6;<br /> uint8_t blue : 5;<br /> };<br />} LeptonColor_t;<br /></pre><div>The API for this driver is very simple.</div><pre class="brush:c">/*<br /> * Initializes the Lepton module<br /> */<br />void LeptonInit(Lepton_t *lepton);<br /><br />/*<br /> * Reads a frame into the Lepton_t framebuffer.<br /> */<br />void LeptonReadFrame(Lepton_t *lepton);<br /><br />/*<br /> * Returns a pixel from the frame buffer at a given x, y<br /> */<br />uint16_t LeptonReadPixel(Lepton_t *lepton, uint8_t x, uint8_t y);<br /></pre><div>Allocating a FrameBuffer as part of the Lepton_t struct was a decision I made to keep the driver self-contained. It is certainly possible to refactor this code and not buffer the entire frame.<br /><br />The implementation of this driver is simple. There are a few private utility functions to start/end transfers, reset the camera and read various amounts of data.</div><pre class="brush:c">/* Lepton Private Functions ***************************************************/<br /><br />inline void LeptonBeginTransfer(Lepton_t *lepton) {<br /> lepton-&gt;CsPort-&gt;Output.Port &amp;= ~(1 &lt;&lt; lepton-&gt;CsPin);<br />}<br /><br />inline void LeptonEndTransfer(Lepton_t *lepton) {<br /> lepton-&gt;CsPort-&gt;Output.Port |= (1 &lt;&lt; lepton-&gt;CsPin);<br />}<br /><br />inline void LeptonReset(Lepton_t *lepton) {<br /> LeptonEndTransfer(lepton);<br /> for(volatile int i = 0; i &lt; 100000; i++);<br />}<br /><br />uint8_t LeptonReadByte(Lepton_t *lepton) {<br /> lepton-&gt;Spi-&gt;Data = 0x00;<br /> while(!lepton-&gt;Spi-&gt;Status.RxFull);<br /> return lepton-&gt;Spi-&gt;Data;<br />}<br /><br />inline bool LeptonReadLine(Lepton_t *lepton, uint8_t line, uint8_t *buffer) {<br /> bool success = true;<br /> LeptonBeginTransfer(lepton);<br /><br /> for(int i = 0; i &lt; LEPTON_PACKET_HEADER_LENGTH; i++) {<br /> buffer[i] = LeptonReadByte(lepton);<br /> }<br /><br /> if((buffer[0] &amp; 0x0F) == 0x0F) {<br /> success = false;<br /> } else if(buffer[1] != line) {<br /> success = false;<br /> }<br /><br /> for(int i = 0; i &lt; LEPTON_PACKET_CONTENT_LENGTH; i++) {<br /> buffer[i] = LeptonReadByte(lepton);<br /> }<br /><br /> LeptonEndTransfer(lepton);<br /> return success;<br />}</pre><div>These utility functions are assembled to create the Lepton driver that is exposed in Lepton.h.</div><pre class="brush:c">/* Lepton.h Implementations ***************************************************/<br /><br />void LeptonInit(Lepton_t *lepton) {<br /> // Setup the SPI Module<br /> lepton-&gt;Spi-&gt;Config.SlaveManageEnable = true;<br /> lepton-&gt;Spi-&gt;Config.InternalSelect = true;<br /> lepton-&gt;Spi-&gt;Config.DeviceMode = SystemSpiDeviceMode_Master;<br /> lepton-&gt;Spi-&gt;Config.Prescaler = SystemSpiPrescaler_2;<br /> lepton-&gt;Spi-&gt;Config.ClockPhase = SystemSpiClockPhase_Second;<br /> lepton-&gt;Spi-&gt;Config.ClockIdle = SystemSpiClockIdle_High;<br /> lepton-&gt;Spi-&gt;Config.Enabled = true;<br /><br /> LeptonReset(lepton);<br />}<br /><br />void LeptonReadFrame(Lepton_t *lepton) {<br /> for(int i = 0; i &lt; LEPTON_HEIGHT; i++) {<br /> if(!LeptonReadLine(lepton, i,<br /> &amp;lepton-&gt;FrameBuffer[i * LEPTON_PACKET_CONTENT_LENGTH])) {<br /> i--;<br /> }<br /> }<br />}<br /><br />uint16_t LeptonReadPixel(Lepton_t *lepton, uint8_t x, uint8_t y) {<br /> uint16_t pixelIndex = (y * LEPTON_PACKET_CONTENT_LENGTH) + (2 * x);<br /><br /> uint8_t high_byte = lepton-&gt;FrameBuffer[pixelIndex];<br /> uint8_t low_byte = lepton-&gt;FrameBuffer[pixelIndex + 1];<br /><br /> return (high_byte &lt;&lt; 8) | low_byte;<br />}</pre><div>This camera uses SPI mode 3 which means that you must sample on the second edge of an idle-high clock. I am clocking my camera at 21MHz which is just a slight overclock.<br /><br /><h3>Generating Pseudocolor Images</h3></div><div><br /></div><div>In the image below you can see a few fascinating details about boiling water in a kettle. You can see the hot steam that is flowing out of the spout. You can also see that the power cord is quite warm (relatively speaking).</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-FPUxjv4whbU/VHJcojp3lJI/AAAAAAAAJnc/XeUbTyey38Y/s1600/IMG_3162.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-FPUxjv4whbU/VHJcojp3lJI/AAAAAAAAJnc/XeUbTyey38Y/s1600/IMG_3162.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Boiling Kettle</td></tr></tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-R1t3sSFGJ44/VHJdDGmL-sI/AAAAAAAAJnk/PARjCOrAHQg/s1600/IMG_3163.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-R1t3sSFGJ44/VHJdDGmL-sI/AAAAAAAAJnk/PARjCOrAHQg/s1600/IMG_3163.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Kettle</td></tr></tbody></table><div>As you can see, the pseudocolor image exposes some interesting information about the scene. Generating a pseudocolor involves selecting colors from a gradient. For example, you could map black to the coolest object in your scene, red to the hottest and ignore the green and blue channels.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-XMgXe3UpHQY/VHJftNa1GVI/AAAAAAAAJnw/9Vda5PclD0Q/s1600/Black%2BRed%2BGradient.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-XMgXe3UpHQY/VHJftNa1GVI/AAAAAAAAJnw/9Vda5PclD0Q/s1600/Black%2BRed%2BGradient.png" height="128" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Red Black Gradient</td></tr></tbody></table></div><div><br /></div><div>There are basically two ways to display thermal image data. The first is as a grayscale image. This has the benefit of introducing no ambiguity into the image that we view. This is quite common in military applications. The second way is to apply a colorization algorithm to generate a <a href="http://en.wikipedia.org/wiki/False_color#Pseudocolor" target="_blank">pseudocolor</a> image. This uses the bits in our display more cleverly to display a wider range of temperatures.</div><div><br /></div><div>Consider a grayscale image on an 24-bit RGB display. In this context you would only be able to see 256 levels of temperature. If you apply false-colorization and display the image on a 24-bit RGB display you have the potential to display a much greater number of levels of temperature. As an example, I am using a rainbow to colorize my image which provides 1792 levels.</div><pre class="brush:plain">Black - Red : 256 Levels - Coldest<br />Red - Yellow : 256 Levels<br />Yellow - Green : 256 Levels<br />Green - Cyan : 256 Levels<br />Cyan - Blue : 256 Levels<br />Blue - Violet : 256 Levels<br />Violet - White : 256 Levels - Hottest<br /></pre><div>You can see that the advantage to using a pseudocolor image is very clear for most applications.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-d0mGY7qDvqg/VHJgtvfo6-I/AAAAAAAAJn4/ykzDkFZegzI/s1600/Rainbow%2BGradient.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-d0mGY7qDvqg/VHJgtvfo6-I/AAAAAAAAJn4/ykzDkFZegzI/s1600/Rainbow%2BGradient.png" height="128" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Rainbow Gradient</td></tr></tbody></table>To generate a colorized image, I first find the minimum and maximum values for a given image.</div><pre class="brush:c">LeptonReadFrame(&amp;camera);<br /><br />// Compute the min/max values of this frame.<br />int min = 16384;<br />int max = 0;<br /><br />for(int i = 0; i &lt; LEPTON_HEIGHT - 1; i++) {<br /> for(int j = 0; j &lt; LEPTON_WIDTH; j++) {<br /> uint16_t value = LeptonReadPixel(&amp;camera, j, i);<br /><br /> if(value &lt; min) {<br /> min = value;<br /> }<br /><br /> if(value &gt; max) {<br /> max = value;<br /> }<br /> }<br />}</pre><div>I generated an RGB565 lookup table of colors in C#. This table has 320 entries and is loaded into flash memory.</div><pre class="brush:c">const LeptonColor_t colors[] = {<br /> { .red = 0, .green = 0, .blue = 0 },<br /> { .red = 1, .green = 0, .blue = 0 },<br /> { .red = 2, .green = 0, .blue = 0 },<br /> { .red = 3, .green = 0, .blue = 0 },<br /> { .red = 4, .green = 0, .blue = 0 },<br /> { .red = 5, .green = 0, .blue = 0 },<br /> { .red = 6, .green = 0, .blue = 0 },<br /> { .red = 7, .green = 0, .blue = 0 },<br /> { .red = 8, .green = 0, .blue = 0 },<br /> { .red = 9, .green = 0, .blue = 0 },<br /><br /> // ...<br /><br /> { .red = 31, .green = 50, .blue = 31 },<br /> { .red = 31, .green = 51, .blue = 31 },<br /> { .red = 31, .green = 52, .blue = 31 },<br /> { .red = 31, .green = 53, .blue = 31 },<br /> { .red = 31, .green = 54, .blue = 31 },<br /> { .red = 31, .green = 55, .blue = 31 },<br /> { .red = 31, .green = 56, .blue = 31 },<br /> { .red = 31, .green = 57, .blue = 31 },<br /> { .red = 31, .green = 58, .blue = 31 },<br /> { .red = 31, .green = 59, .blue = 31 },<br /> { .red = 31, .green = 60, .blue = 31 },<br /> { .red = 31, .green = 61, .blue = 31 },<br /> { .red = 31, .green = 62, .blue = 31 },<br /> { .red = 31, .green = 63, .blue = 31 },<br />};</pre><div>This allows me to lookup the corresponding color for a given normalized temperature value very quickly. I scale the value of each pixel to be within range of the color lookup table.</div><pre class="brush:c">// Upload the frame to the GPU.<br />for(int i = 0; i &lt; LEPTON_HEIGHT; i++) {<br /> for(int j = 0; j &lt; LEPTON_WIDTH; j++) {<br /> uint16_t value = LeptonReadPixel(&amp;camera, j, i);<br /><br /> int scaled_value = (320 * (value - min)) / (max - min);<br /> scaled_value = scaled_value &lt; 0 ? 0 : scaled_value;<br /> scaled_value = scaled_value &gt; 319 ? 319 : scaled_value;<br /><br /> LeptonColor_t color = colors[scaled_value];<br /><br /> // ...<br /></pre><div>Using this technique I can utilize my LCD to display a wider range of temperature values.<br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-hp5SPZ3k7Qw/VHJioQo-nRI/AAAAAAAAJoE/ox2u4lrKZqE/s1600/IMG_3147.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-hp5SPZ3k7Qw/VHJioQo-nRI/AAAAAAAAJoE/ox2u4lrKZqE/s1600/IMG_3147.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">A "hot" apartment building against the cold NYC sky :]</td></tr></tbody></table><h3>Caveats of Interpolating Pseudocolor Images</h3><br />If you are planning to use interpolation to scale up the images captured by the Lepton sensor, pay careful attention to how you interpolate the data.<br /><br /><h4>Nearest Neighbour Interpolation</h4><br />If you take the naive approach and use nearest neighbour to scale up the images, you do not need to be concerned. The result will accurately reflect the captured images in pseudocolor but will be larger in size.<br /><br /><h4>Bilinear/Linear/Sinc Interpolation</h4><br />If you decide to use a better interpolation algorithm like bilinear, linear or sinc you must generate the final image by interpolating from the 80x60x14bpp sensor data directly. If you generate a pseudocolor image at 80x60 and scale it up, the result will not be accurate.<br /><br />Imagine that you have two objects next to one another. One object is red and the other is blue. The natural interpolation between these colors is to transition through violet. This is <b>incorrect</b>! If you interpolate the data as temperature values they should interpolate through yellow, green, cyan and finally blue. You are displaying the region between these two objects as hotter than both of them.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-pH6N_X5RyiU/VHFJNaWHueI/AAAAAAAAJmk/0IoD_V-3FbA/s1600/Interpolation%2BError.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-pH6N_X5RyiU/VHFJNaWHueI/AAAAAAAAJmk/0IoD_V-3FbA/s1600/Interpolation%2BError.png" height="128" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Interpolation Error!</td></tr></tbody></table>This has been a fun experiment and I am looking forward to working on more projects with this sensor.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-7ROnOaJTCNQ/VHJnehPTOrI/AAAAAAAAJok/g0uYdscOjbQ/s1600/IMG_3167.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-7ROnOaJTCNQ/VHJnehPTOrI/AAAAAAAAJok/g0uYdscOjbQ/s1600/IMG_3167.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Flir Lepton + Gameduino + Nucleo running on a portable USB power supply :]</td></tr></tbody></table>Thanks for reading!<br /><br /></div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com16tag:blogger.com,1999:blog-5663160055124969741.post-30198901396355082452014-05-15T16:26:00.000-04:002014-05-15T16:31:05.471-04:00FT800 with Streaming Video I like to check out the thrift stores in my area for one of a kind technical gems to add to my collection. A few years ago I came across a <a href="http://en.wikipedia.org/wiki/QuickCam" target="_blank">Connectix QuickCam</a>. This is one of the earliest webcams that didn't require a separate video capture card. Due to how easy it was to install, it was incredibly popular. So much so that Logitech ended up buying Connectix out and forking the product under their own brand.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-yzy6mGpTRaE/U3UUKd8Zp6I/AAAAAAAAE9Y/fwSYXy7l66Y/s1600/QuickCam.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-yzy6mGpTRaE/U3UUKd8Zp6I/AAAAAAAAE9Y/fwSYXy7l66Y/s1600/QuickCam.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Connectix QuickCam</td></tr></tbody></table>When I saw it in the store I really had no idea what it was aside from the fact that it was a webcam and it had a parallel port. My first instinct was that a parallel port is simply a collection of TTL lines that I could emulate with a modern microcontroller. I finally had some time to put it into action and decided to stream video frames from the camera to a <a href="http://excamera.com/sphinx/gameduino2/" target="_blank">Gameduino 2</a>&nbsp;and the <a href="http://www.ftdichip.com/Products/ICs/FT800.html" target="_blank">FT800</a> video processor that it uses.<br /><br /><iframe allowfullscreen="" frameborder="0" height="360" src="//www.youtube.com/embed/z_ik6mA1ZWw" width="640"></iframe> <br /><br />The results were great, aside from the low image quality of the camera. This experiment proves the concept of using an FT800 to composite motion video with other visual elements. A better image sensor would provide a really cool experience.<br /><br />Keep reading to see how I did it!<br /><br /><a name='more'></a><h2>The Camera</h2><div><br />The first step was to write a driver for the camera. The camera is very well documented by the original manufacturer, Connectix. Unfortunately the specification is still protected by an NDA. If you search hard enough, you can find it ;] The designers obviously had some ulterior motives when designing the protocol. In numerous sections of the specification there are references to using the camera with a non-parallel port interface.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-dCmJVB0fR70/U3UXWXyiK9I/AAAAAAAAE9g/0Srs2jVzqKk/s1600/QuickCam_FT800.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-dCmJVB0fR70/U3UXWXyiK9I/AAAAAAAAE9g/0Srs2jVzqKk/s1600/QuickCam_FT800.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">QuickCam with FT800</td></tr></tbody></table><div>There is a PIC microcontroller embedded in the connector of the QuickCam that implements a protocol for communicating with the camera.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-mspp5b1NJbo/U3Uam-E5WCI/AAAAAAAAE9o/b-q4vti-VE4/s1600/QuickCam_Microcontroller.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-mspp5b1NJbo/U3Uam-E5WCI/AAAAAAAAE9o/b-q4vti-VE4/s1600/QuickCam_Microcontroller.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">QuickCam Guts :]</td></tr></tbody></table>The entire protocol is handshake-driven. The host platform puts a word on the data bus of the parallel port and raises and lowers a line to indicate that it is ready for the camera to read it. The camera raises and lowers another line to indicate that it has read the data. This sequence is used to issue commands to the camera.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-ds26bQkWmt0/U3Uj5U3OsiI/AAAAAAAAE-U/1qk8qSeOi9U/s1600/Gameduino+2+Closeup.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-ds26bQkWmt0/U3Uj5U3OsiI/AAAAAAAAE-U/1qk8qSeOi9U/s1600/Gameduino+2+Closeup.JPG" height="358" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Gameduino 2</td></tr></tbody></table></div><div>Below is the code for the send byte routine. I usually define a struct to model devices like this to make it independent of pinout. I decided that this was just an experiment and went for hard-coded pins. I had some skew issues and had to insert a short delay after placing data on the bus. I corrected this with a short delay.</div><pre class="brush:c">void QuickCam_SendByte(uint8_t byte)<br />{<br /> SystemGpioC.Output.Port &amp;= 0xFC03;<br /> SystemGpioC.Output.Port |= (byte &lt;&lt; 2);<br /> for(volatile int i = 0; i &lt; 5; i++);<br /><br /> QuickCam_ReadByte();<br />}<br /><br />uint8_t QuickCam_ReadByte()<br />{<br /> SystemGpioA.Output.P11 = true;<br /> while(!(SystemGpioA.Input.P8));<br /> <br /> uint8_t byte = ((SystemGpioC.Input.Port &gt;&gt; 10) &amp; 0x0F) &lt;&lt; 4;<br /><br /> SystemGpioA.Output.P11 = false;<br /> while(SystemGpioA.Input.P8);<br /><br /> byte |= ((SystemGpioC.Input.Port &gt;&gt; 10) &amp; 0x0F);<br /> byte ^= 0x88;<br /> <br /> return byte;<br />}</pre><h2>The FT800</h2><div><br /></div><div>While I was brainstorming for this experiment I had to implement a few of the commands for dealing with bitmaps. I realized that a bitmap is very loosely defined in the context of the FT800. You simply give it a pointer and tell it the image format, &nbsp;stride and height. The FT800 assumes that everything you are telling it is correct and starts to render pixel data on the screen. If the pointer is not valid you will get garbage rendered onto the screen.</div><div><br /></div><div>I wondered what would happen if a bitmap handle was modified after being rendered to the screen. The results were exactly as I had expected: the screen updates! Best of all, the screen updates without emitting a new display list. This effectively gives you a framebuffer that you can treat like a sprite. The graphics processor has 256kB of video RAM. This is more than enough room for a 320x240 RGB565 image. This is very cool and gives a reason to get excited about the <a href="http://en.wikipedia.org/wiki/Compositing" target="_blank">compositing</a> nature of the FT800 device.</div><div><br /></div><div>The QuickCam can generate a test pattern. This is useful for validating endianness and color order (RGB, BGR).</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-vq53NPQjGIg/U3Ubt7fIY2I/AAAAAAAAE9w/Qik_d57lLfo/s1600/QuickCam_Test_Pattern_FT800.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-vq53NPQjGIg/U3Ubt7fIY2I/AAAAAAAAE9w/Qik_d57lLfo/s1600/QuickCam_Test_Pattern_FT800.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">QuickCam Test Pattern!</td></tr></tbody></table><div>The FT800 supports bilinear scaling and there is no cost to this. &nbsp;Below is a sample rendering four scaled-down versions of myself. This has no impact on system performance!</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-WPMuyohIvY8/U3UcloojcmI/AAAAAAAAE94/HlNRCVIUCRk/s1600/QuickCam_Tiled.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-WPMuyohIvY8/U3UcloojcmI/AAAAAAAAE94/HlNRCVIUCRk/s1600/QuickCam_Tiled.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">QuickCam Tiling</td></tr></tbody></table><h2>Hardware Interface</h2><div><br /></div><div>The hardware interface is simple enough. I used some female-to-female cables to connect to the MORPHO connector on the Nucleo board. These fit nicely on the male pins of the QuickCam parallel port connector.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-YgfahF8sbUA/U3UdM7SY2mI/AAAAAAAAE-E/9jJPJWNoqjw/s1600/Nucleo_QuickCam_Interface.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-YgfahF8sbUA/U3UdM7SY2mI/AAAAAAAAE-E/9jJPJWNoqjw/s1600/Nucleo_QuickCam_Interface.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Nucleo Interface</td></tr></tbody></table>I also needed a way to power the QuickCam. The camera sniffs power from the PS/2 bus using a wedge. I created a small patch cable to power it from USB.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-WD9xBXyt82s/U3UdMpJo3xI/AAAAAAAAE-A/JhlbNCZ9fUc/s1600/QuickCam_USB_Power.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-WD9xBXyt82s/U3UdMpJo3xI/AAAAAAAAE-A/JhlbNCZ9fUc/s1600/QuickCam_USB_Power.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">USB to PS/2 Power Adapter</td></tr></tbody></table><h2>A Note About Performance</h2><div><br /></div><div>The QuickCam is an incredibly antiquated device. The FT800 in this demo is clocked at 21MHz. This would permit a 320x240 RGB565 frame to be transmitted in 58ms. This would give a framerate of around 17fps. The FT800 can be clocked faster, but the camera cannot. &nbsp;I would love to get my hands on a more modern camera module and try this out again. Some camera modules these days even support hardware JPEG encoding. The FT800 supports hardware JPEG decoding! This would give an incredible framerate while transferring a low amount of data.</div><div><br /></div><h2>Conclusion</h2><div><br /></div><div>The more I use the FT800, the more I realize how powerful it is. With some clever tricks you can convince it to do some very interesting acrobatics. I would be interested in an FT800 with more graphics memory. You could create a memory protected compositing window manager by simply allowing applications to only paint data into their data region. The individual frame buffers would be shown and hidden by the various UI elements. Interesting stuff.</div><div><br /></div><div>I hope you have enjoyed reading this article! As always, leave your thoughts in the comments!</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com7tag:blogger.com,1999:blog-5663160055124969741.post-9412015541828046902014-04-21T01:05:00.000-04:002014-04-21T01:05:15.944-04:002048, Embedded Let me first get something out of the way... I am an embedded nut. I absolutely <b>love</b> taking a foreign piece of hardware and bringing it to life. My most recent excursion into embedded land has been with a <a href="http://www.st.com/stm32nucleo-pr" target="_blank">Nucleo</a> board designed by <a href="http://www.st.com/" target="_blank">STMicroelectronics</a>.<br /><br />I decided to pair this board with a&nbsp;<a href="http://excamera.com/sphinx/gameduino2/" target="_blank">Gameduino 2</a>&nbsp;shield designed for the popular&nbsp;<a href="http://www.arduino.cc/" target="_blank">Arduino</a>. I wrote a driver for the&nbsp;<a href="http://www.ftdichip.com/Products/ICs/FT800.html" target="_blank">FT800</a>&nbsp;graphics processor&nbsp;and implemented&nbsp;<a href="http://gabrielecirulli.com/" target="_blank">Gabriele Cirulli's</a>&nbsp;<a href="http://gabrielecirulli.github.io/2048/" target="_blank">2048</a>&nbsp;game as a first project with this hardware.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td><a href="http://2.bp.blogspot.com/-m1pSnl5lIDQ/U1SN9n4ZStI/AAAAAAAAE1c/UVJlBAYbkHk/s1600/2048+Screenshot_.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-m1pSnl5lIDQ/U1SN9n4ZStI/AAAAAAAAE1c/UVJlBAYbkHk/s1600/2048+Screenshot_.jpg" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="font-size: 13px;">2048 Screenshot</td></tr></tbody></table>The result is an awesome device that emulates the fun 2048 browser game (pretty closely, anyway). It will make a great desk ornament for passersby at work to gawk at.<br /><div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-NM5Fy187fG0/U1SLd08OZLI/AAAAAAAAE1E/fyPqurFy3_M/s1600/Nucleo+F4+Board_.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-NM5Fy187fG0/U1SLd08OZLI/AAAAAAAAE1E/fyPqurFy3_M/s1600/Nucleo+F4+Board_.jpg" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Nucleo F4 Board</td></tr></tbody></table>In this article I will give you a demonstration of the game and take you through some of the design concepts I have used to come up with this product. I wrote my own linker script, C-Runtime and driver library for this project and it was an awesome experience.<br /><br /><a name='more'></a><br /><h3>Overview</h3><div><br /></div>I funded the Gameduino 2 Kickstarter sometime ago and the board arrived ahead of schedule. I was very excited to have the piece of hardware in my hands. Unfortunately, life took its' usual turn and I ended up getting tied up with school and work projects. Thankfully, I am graduating and officially had some time to bring this board to life.<br /><br />I wrote a driver for the FT800 graphics processor and used this to implement the popular 2048 game in an embedded environment.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-n83IST6i368/U1SN8wuM5NI/AAAAAAAAE1U/QWqiFfQ1EPA/s1600/Nucleo+Gameduino+Board+Stack_.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-n83IST6i368/U1SN8wuM5NI/AAAAAAAAE1U/QWqiFfQ1EPA/s1600/Nucleo+Gameduino+Board+Stack_.jpg" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Gameduino 2, Nucleo F4 Board Stackup</td></tr></tbody></table><h3>FT800</h3><br />The FT800 is a fascinating device from FTDI. It is a high performance graphics processor capable of decoding JPEG images, rendering primitives and working with complex transformations. It does a lot of things that were previously impossible with low-cost microcontrollers.<br /><br />The FT800 also has an interesting programming model. You are expected to treat the device like RAM that you access via SPI messages. The basic messages are really quite simple and consist of write, read and command operations.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-cD5JEDddPwY/U1SU_KV9jxI/AAAAAAAAE2c/BrgbA6QCsfw/s1600/FTDI+Logo.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-cD5JEDddPwY/U1SU_KV9jxI/AAAAAAAAE2c/BrgbA6QCsfw/s1600/FTDI+Logo.jpg" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">FTDI Built-In Animated Logo</td></tr></tbody></table><h4>Command</h4><div><br /></div>I will start with the command operations as they are the simplest. There are a small selection of commands available including ACTIVE, STANDBY, RESET and a few others to manage clock sources and rates. These commands are critical for the initialization of the processor.<br /><br />To send a command, simply send 3 bytes as shown below with the chip select line pulled low for the duration of this transfer.<br /><div class="separator" style="clear: both; text-align: center;"></div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-XqQqQmLLf2M/U1SQ2J28EuI/AAAAAAAAE1w/NgYJiFpdPvY/s1600/FT800+Command.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-XqQqQmLLf2M/U1SQ2J28EuI/AAAAAAAAE1w/NgYJiFpdPvY/s1600/FT800+Command.png" height="175" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Command</td></tr></tbody></table><h4>Memory Read</h4><div><br /></div>As I mentioned previously, the programmers model of this device is to treat it like a bank of memory. As such, we require a way to read from this memory. You must assemble a message as shown below.<br /><br />First, you send two zero-bits followed by the 21-bit address. You send a dummy byte to allow the FT800 to prepare the data and then for each 8 clock pulses emitted you will receive one byte starting from the address specified.<br /><br /><div class="separator" style="clear: both; text-align: center;"></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-cvLjAxoz0PM/U1SSAOb5SMI/AAAAAAAAE18/vsouBQeWLmM/s1600/FT800+Memory+Read.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-cvLjAxoz0PM/U1SSAOb5SMI/AAAAAAAAE18/vsouBQeWLmM/s1600/FT800+Memory+Read.png" height="284" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Memory Read</td></tr></tbody></table><h4>Memory Write</h4></div><div><br /></div><div>Last and not least, we need to be able to write to memory. This is yet another simple message that will be sent to the device.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-lwYFAHeJhD8/U1SS5pFQ_6I/AAAAAAAAE2I/ND8dGQVVf-g/s1600/Memory+Write.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-lwYFAHeJhD8/U1SS5pFQ_6I/AAAAAAAAE2I/ND8dGQVVf-g/s1600/Memory+Write.png" height="252" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Memory Write</td></tr></tbody></table><h4>Memory Map</h4><div><br /></div><div>Having access to a bunch of external memory is nice, but what can we do with it? The FT800 datasheet provides us with a table indicating the various ranges of memory available for reading/writing. We are especially interested in the Command, Display List and Register Memory.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-Apf45U71yCc/U1STlAfu0XI/AAAAAAAAE2Q/SNQDfNIovZY/s1600/FT800+Memory+Map.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-Apf45U71yCc/U1STlAfu0XI/AAAAAAAAE2Q/SNQDfNIovZY/s1600/FT800+Memory+Map.png" height="352" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">FT800 Memory Map</td></tr></tbody></table><div>By maintaining state variables on the host MCU, it is possible to instruct the FT800 to do very interesting things. Here is the data structure I created to model the FT800.</div><pre class="brush:c">typedef struct FT800_t {<br /> volatile SystemSpiModule_t *Spi;<br /> volatile SystemGpioModule_t *CsPort;<br /> uint32_t CsPin;<br /> uint32_t CommandAddress;<br />} FT800_t;</pre><div>This structure allows for multiple FT800 devices to be connected to one processor. This could allow for an interesting multi-seat setup hosted by a single microcontroller. One member of this struct is the command address. This allows me to strategically write sequential commands to the RAM_CMD section of memory. The value of this address is managed by the driver I have written.<br /><br /><h3>Driver</h3></div><div><br /></div><div>I will be perfectly honest. My first go at this device was rocky at best. Most of my experiences with display technologies have been lackluster. There are too many configuration parameters that could be wrong. A good example of this is that the Gameduino uses a different "swizzle" value. Swizzle is a funny term used to describe the sub-pixel ordering (RGB, GRB, BGR etc).</div><div><br /></div><div>The first thing I did was take a look at the Gameduino 2 source. I found a few hints in there about how to generate a blank screen. I took this sequence of display list instructions and implemented them using my driver. The first step is to set the clear color, clear buffers, end the display list and then flip it onto the screen. I decided to clear to red and ended up with the following. Success, at last!</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-q-JBO9PuYtE/U1SWX0zC54I/AAAAAAAAE2o/REmGhVY2tis/s1600/Red+Screen.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-q-JBO9PuYtE/U1SWX0zC54I/AAAAAAAAE2o/REmGhVY2tis/s1600/Red+Screen.jpg" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Cleared Red Screen :]</td></tr></tbody></table><div>Here are my SPI read/write functions. These sit at the lowest level in my stack.<br /><pre class="brush:c">void FT800SpiWrite(FT800_t *ft800, uint8_t *buf, uint32_t length)<br />{<br /> for(uint32_t i = 0; i &lt; length; i++)<br /> {<br /> SystemSpiWriteRead(ft800-&gt;Spi, buf[i]);<br /> }<br />}<br /><br />void FT800SpiWriteRead(FT800_t *ft800, uint8_t *buf, uint32_t length)<br />{<br /> for(uint32_t i = 0; i &lt; length; i++)<br /> {<br /> buf[i] = SystemSpiWriteRead(ft800-&gt;Spi, buf[i]);<br /> }<br />}</pre>As I worked with the device more I built-up more complex abstractions and eventually decided to create a rectangle function. I was able to render a rectangle quite easily.<br /><br /><div class="separator" style="clear: both; text-align: center;"></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-3iBCxbnCad4/U1SYb6wDvfI/AAAAAAAAE24/gnBeN0ISIyo/s1600/First+Rectangle.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-3iBCxbnCad4/U1SYb6wDvfI/AAAAAAAAE24/gnBeN0ISIyo/s1600/First+Rectangle.jpg" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">First Rectangle (with notes of bit-packed messages)</td></tr></tbody></table>The code below is used to generate display list instructions for a rectangle primitive. I am especially proud of the way I am packing the bits in the FT800DlVertexI function. I used an anonymous union/struct with a bitfield to avoid all of the nasty bitwise operations. This isn't 100% portable to other compilers/processors but it works great in this particular instance.<br /><pre class="brush:c">void FT800DrawRectangle(FT800_t *ft800, FT800Point_t p1, FT800Point_t p2)<br />{<br /> FT800DlStartPrimitive(ft800, FT800PrimitiveType_Rectangle);<br /> FT800DlVertexI(ft800, p1.X, p1.Y, 0, 0);<br /> FT800DlVertexI(ft800, p2.X - 1, p2.Y - 1, 0, 0);<br /> FT800DlEndPrimitive(ft800);<br />}<br /><br />void FT800DlStartPrimitive(FT800_t *ft800, FT800PrimitiveType_t primitive)<br />{<br /> uint8_t startPrimitive[] = { (primitive &amp; 0x0F), 0x00, 0x00, 0x1F };<br /> FT800CoprocessorCommand(ft800, startPrimitive, 4);<br />}<br /><br />void FT800DlVertexI(FT800_t *ft800,<br /> uint16_t x, uint16_t y, uint8_t handle, uint8_t cell)<br />{<br /> union {<br /> struct {<br /> uint32_t cell : 7;<br /> uint32_t handle : 5;<br /> uint32_t y : 9;<br /> uint32_t x : 9;<br /> uint32_t cmd : 2;<br /> } arguments;<br /> <br /> uint8_t command[4];<br /> } vertex;<br /><br /> vertex.arguments.cell = cell;<br /> vertex.arguments.handle = handle;<br /> vertex.arguments.y = y;<br /> vertex.arguments.x = x;<br /> vertex.arguments.cmd = 0x02;<br /> <br /> FT800CoprocessorCommand(ft800, vertex.command, 4);<br />}<br /><br />void FT800DlEndPrimitive(FT800_t *ft800)<br />{<br /> uint8_t endPrimitive[] = { 0x00, 0x00, 0x00, 0x21 };<br /> FT800CoprocessorCommand(ft800, endPrimitive, 4);<br />}<br /><br />void FT800SendCommand(FT800_t *ft800, FT800Command_t command)<br />{<br /> uint8_t commandBuf[] = { command, 0x00, 0x00 };<br /> FT800SpiWriteCs(ft800, commandBuf, 3);<br />}<br /></pre><br />Once I had a rectangle function, I was off to the races. I quickly put together a 2048 game board and rendered it. Very exciting times!<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-mgliPEIqeb0/U1SYdg0UZdI/AAAAAAAAE3A/1eaosW2Wv90/s1600/Rendered+Game+Board.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-mgliPEIqeb0/U1SYdg0UZdI/AAAAAAAAE3A/1eaosW2Wv90/s1600/Rendered+Game+Board.jpg" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">First u2048 Render :]</td></tr></tbody></table><h3>Game Logic</h3></div><div><br />Next it was time to implement my version of 2048 that I decided to call u2048. I am modelling the game using the following data structure. It is simply a score variable and a 2D array of game tiles.<br /><pre class="brush:c">typedef struct U2048_t {<br /> int Score;<br /> U2048Tile_t Tiles[U2048_GAME_SIZE][U2048_GAME_SIZE];<br /> FT800_t *ft800;<br />} U2048_t;<br /></pre>One interesting feat of my implementation is that the game size can be changed readily, which in turn makes it easier. It is currently a #define, but it could easily be made into a game state variable.<br /><br /><h4>Random Tiles</h4><br />The first requirement is the ability to place tiles randomly. When tiles randomly appear in&nbsp;Gabriele's implementation there is a slight chance that the piece will be a 4 rather than a 2. My version always emits 2's but I could likely make a simple change to allow mine to behave more closely to the web implementation.<br /><br />Below is my method for randomly generating a tile. I am using the stdlib rand function seeded with the frame count register from the FT800 device. You probably don't want to generate your SSH keys from this random number generator but it is good enough for this simple game. I noticed one bug while writing this article. I am masking off the least significant digits in order to restrict the random number to be in the range of 0 to 3 inclusive. This will not handle a change of board size well.<br /><pre class="brush:c">void U2048RandomTile(U2048_t *game)<br />{<br /> uint8_t frameCounter[1]; <br /> FT800Read(game-&gt;ft800, FT800Register_FRAMES, frameCounter, 1);<br /><br /> srand(frameCounter[0]);<br /><br /> int randomX;<br /> int randomY;<br /> <br /> bool occupied = true;<br /> while(occupied)<br /> {<br /> randomX = rand() &amp; 0x03;<br /> randomY = rand() &amp; 0x03;<br /> <br /> occupied = (game-&gt;Tiles[randomX][randomY] != U2048Tile_Empty);<br /> }<br /> <br /> U2048NewTile(game, randomX, randomY, U2048Tile_2);<br />}<br /></pre><br /><h4>Game Actions</h4><br />There are four basic game interactions: swipe up, down, left and right. I decided that each operation can be broken down into two operations. I will define justification as pushing all tiles as far as they can travel in the swiped direction. I will also define merging as the act of adding adjacent tiles of equal value together in the direction of the action. Each swipe action consists of a justification, merging followed by a final justification. Below is the function I use to execute a game action. I will leave the gritty details of merging and justification to the code available on <a href="http://github.com/aarossig/u2048" target="_blank">GitHub</a>.<br /><pre class="brush:c">void U2048Action(U2048_t *game, U2048Action_t action)<br />{<br /> bool tilesMoved = false;<br /><br /> switch(action)<br /> {<br /> case U2048Action_SwipeRight:<br /> tilesMoved = U2048JustifyRight(game);<br /> tilesMoved |= U2048MergeRight(game);<br /> tilesMoved |= U2048JustifyRight(game);<br /> break;<br /> case U2048Action_SwipeLeft:<br /> tilesMoved = U2048JustifyLeft(game);<br /> tilesMoved |= U2048MergeLeft(game);<br /> tilesMoved |= U2048JustifyLeft(game);<br /> break;<br /> case U2048Action_SwipeUp:<br /> tilesMoved = U2048JustifyUp(game);<br /> tilesMoved |= U2048MergeUp(game);<br /> tilesMoved |= U2048JustifyUp(game);<br /> break;<br /> case U2048Action_SwipeDown:<br /> tilesMoved = U2048JustifyDown(game);<br /> tilesMoved |= U2048MergeDown(game);<br /> tilesMoved |= U2048JustifyDown(game);<br /> break;<br /> }<br /> <br /> if(tilesMoved)<br /> {<br /> U2048RandomTile(game);<br /> }<br /> <br /> U2048GameRender(game);<br />}</pre>Score is incremented whenever tiles are merged together by the following code.<br /><pre class="brush:c">void U2048PlaceNextTile(U2048_t *game, int x, int y)<br />{<br /> U2048Tile_t newTile = U2048NextTile(game-&gt;Tiles[x][y]);<br /> U2048NewTile(game, x, y, newTile);<br /> game-&gt;Score += newTile;<br />}<br /></pre>The 2048 game itself is really quite simple. I would like to revisit my justify and merge functions, I believe they can be simplified. I was able to get this game implemented in the context of a day so there is definitely room for improvement.<br /><br />Below is a video of early tests of the game logic, before I was keeping track of score.<br /><br /><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='640' height='400' src='http://www.blogger.com/video.g?token=AD6v5dwgRwToZ99RGgxa9e-LZ1Z3jpEvUjsbrvwTgmUDxEAriwY5KNHsqdxPWNy01BbkxE8XQ8qxIgcN9UWXy8w1-g' class='b-hbp-video b-uploaded' frameborder='0' /><br /><br /><h3></h3><h3>Touch Screen</h3><div><br /></div>After I finished implementing the game I needed a way to interact with it. The Gameduino 2 has a resistive touchscreen and it is made available through a set of registers in the FT800 processor. I decided to create a simplistic gesture API.<br /><pre class="brush:c">typedef enum FT800Gesture_t {<br /> FT800Gesture_None,<br /> FT800Gesture_SwipeUp,<br /> FT800Gesture_SwipeDown,<br /> FT800Gesture_SwipeLeft,<br /> FT800Gesture_SwipeRight,<br /> FT800Gesture_Touch<br />} FT800Gesture_t;<br /><br />typedef struct FT800Point_t {<br /> int16_t X;<br /> int16_t Y;<br />} FT800Point_t;<br /><br />typedef struct FT800GestureDetail_t {<br /> FT800Gesture_t Gesture;<br /> FT800Point_t Position;<br />} FT800GestureDetail_t;<br /></pre>I have a function that I can call to determine the users intent. If they are gesturing something, I will receive a non-None gesture and I can take action accordingly. Below is the game loop currently located in main.<br /><pre class="brush:c"> U2048_t u2048;<br /> U2048Init(&amp;u2048, &amp;ft800);<br /> U2048GameRender(&amp;u2048);<br /> <br /> U2048RandomTile(&amp;u2048);<br /> U2048RandomTile(&amp;u2048);<br /><br /> while(1)<br /> {<br /> FT800GestureDetail_t detail;<br /> FT800GetTouchGesture(&amp;ft800, &amp;detail);<br /><br /> switch(detail.Gesture)<br /> {<br /> case FT800Gesture_SwipeUp:<br /> U2048Action(&amp;u2048, U2048Action_SwipeUp);<br /> break;<br /> case FT800Gesture_SwipeDown:<br /> U2048Action(&amp;u2048, U2048Action_SwipeDown);<br /> break;<br /> case FT800Gesture_SwipeLeft:<br /> U2048Action(&amp;u2048, U2048Action_SwipeLeft);<br /> break;<br /> case FT800Gesture_SwipeRight:<br /> U2048Action(&amp;u2048, U2048Action_SwipeRight);<br /> break;<br /> default:<br /> break;<br /> }<br /> }<br /></pre>The numerous layers of abstraction make this code elegant.<br /><br /><h3>Game Play (Show me the Money!)</h3><div><br /></div><div>The game is quite fun to play, despite the fact that I have yet to actually beat the game. I do not have any code to handle the "win" situation as of yet. I will have to invest some time into creating a fancy credits screen. Below is a short video demonstration of the game in action!</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='640' height='400' src='http://www.blogger.com/video.g?token=AD6v5dz4pBUAJLMYCAMg5LZuzQlsZaqVnzt2rBftIgBUOtrZ7R3No9qrtT2077qT3a0JaPNgqFUwsJM6YXfX7kOBmg' class='b-hbp-video b-uploaded' frameborder='0' /></div><br />I went a little overboard with the animations and as such each turn takes considerably longer than the web-based version. The end result is a cool looking UI though and I got to exploit the functionality of the graphics processor.<br /><br /><h4>Conclusion</h4><br />Feel free to check out the code on <a href="http://github.com/aarossig/u2048" target="_blank">GitHub</a>. I am interested in your comments and suggestions so feel free to send them over.<br /><br />Thanks for reading!</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com2tag:blogger.com,1999:blog-5663160055124969741.post-47468674073244946392014-03-12T19:40:00.000-04:002014-03-12T19:40:04.415-04:00Happy 25th Birthday to the World Wide Web I remember my family's first computer well. It was 1997 and I was an easily-excited 6-year-old boy. This rather large contraption just landed on my dining room table and needless to say, I was very curious. I remember playing <a href="http://en.wikipedia.org/wiki/Moto_Racer" target="_blank">Moto Racer</a> with my Dad. I remember being amazed when we purchased our <a href="http://en.wikipedia.org/wiki/All-in-Wonder" target="_blank">ATI All-In-Wonder Pro</a> TV Tuner video card. We could somehow watch cable television on our PC... while browsing the web! What a wonder!<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-GpQMDY_X7kM/UyDhiu5nkHI/AAAAAAAAEcQ/4e97TVN9aH0/s1600/Google+Homepage.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-GpQMDY_X7kM/UyDhiu5nkHI/AAAAAAAAEcQ/4e97TVN9aH0/s1600/Google+Homepage.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Google's Homepage on March 12th, 2014</td></tr></tbody></table>Today marks the 25th anniversary of the creation of the web. Google has included a small homage to the beginning of the web that we know and love today on their homepage.<br /><br />I decided to install Netscape Navigator to see how it stands up against the modern web. This is running on Linux Mint 16, a derivative of the popular Ubuntu. Needless to say, the results are not great. Thanks to the concept of graceful-degradation though, most of the content is still highly-usable despite being arranged a little oddly.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-4PCzaJmgTrI/UyDiPcGdXbI/AAAAAAAAEcY/9Fu1O2Ig3po/s1600/Netscape+Navigator+Resistor+Network.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-4PCzaJmgTrI/UyDiPcGdXbI/AAAAAAAAEcY/9Fu1O2Ig3po/s1600/Netscape+Navigator+Resistor+Network.png" height="440" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">http://gsyt181.cn in Netscape Navigator on Linux Mint 16</td></tr></tbody></table>I tried to load a few websites that I frequent to see how well they are displayed. Read on to see more!<br /><br /><a name='more'></a><h2>The Resistor Network</h2><div><br /></div><div>I will start with my homepage as shown above. The first thing I noticed was that I had to disable JavaScript in the settings menu. My site simply refused to load and I couldn't find any traces of a debug console to see output of the parser/runtime.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-1rOH652bVuo/UyDksuKZxqI/AAAAAAAAEck/p_w68eVzi7g/s1600/Netscape+Settings+Window.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-1rOH652bVuo/UyDksuKZxqI/AAAAAAAAEck/p_w68eVzi7g/s1600/Netscape+Settings+Window.png" height="376" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Netscape Settings Window</td></tr></tbody></table>My website did surprisingly well, the content is clearly readable despite the layout of the page being completely broken. One issue is that the navigation menu moved to the bottom of the page, which worked out well in this case.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-_OaGCJ_rQEE/UyDlVXa3YtI/AAAAAAAAEcs/dVIrEkuyUVg/s1600/The+Resistor+Network+Menu.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-_OaGCJ_rQEE/UyDlVXa3YtI/AAAAAAAAEcs/dVIrEkuyUVg/s1600/The+Resistor+Network+Menu.png" height="436" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Huge Navigation Menu!</td></tr></tbody></table><h2><a href="http://www.reddit.com/" target="_blank">Reddit</a></h2><div><br /></div><div>I frequent <a href="http://www.reddit.com/r/programming/" target="_blank">r/programming</a> and <a href="http://www.reddit.com/r/linux/" target="_blank">r/linux</a> on Reddit. I decided to see how well it would fare when placed in the hands of a decade old web-browser.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-uXgsRgSQ3eU/UyDl6Wx9eNI/AAAAAAAAEc4/Q0YazoTNqEw/s1600/Reddit+Netscape.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-uXgsRgSQ3eU/UyDl6Wx9eNI/AAAAAAAAEc4/Q0YazoTNqEw/s1600/Reddit+Netscape.png" height="434" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">r/linux vs. Netscape</td></tr></tbody></table><br />Reddit did not do so well in this case. Unfortunately, they put all of their navigation panes at the top of the page. This causes the actual content to start somewhere around 40% of the way down the page.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-6g6RrYMGeww/UyDmdgRjH0I/AAAAAAAAEdA/Ekzp8DCvIks/s1600/Reddit+Content+Netscape.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-6g6RrYMGeww/UyDmdgRjH0I/AAAAAAAAEdA/Ekzp8DCvIks/s1600/Reddit+Content+Netscape.png" height="438" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Reddit Stories in Netscape</td></tr></tbody></table>If you can get past all of the scrolling, you will see that it actually did quite well. There seems to be some artifacts of AJAX use here. Check out the "loading..." strings that are visible in this document. I believe these should be hidden as I have JavaScript disabled.<br /><br /><h2><a href="http://www.google.com/" target="_blank">Google</a></h2><div><br /></div><div>The Google homepage leaves a little something to be desired. I was expecting an "Upgrade to Chrome" notice.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-8XaUmf4WLi0/UyDnEwIYtCI/AAAAAAAAEdM/mV7871Su7ko/s1600/Google+Netscape.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-8XaUmf4WLi0/UyDnEwIYtCI/AAAAAAAAEdM/mV7871Su7ko/s1600/Google+Netscape.png" height="436" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Google Homepage in Netscape&nbsp;</td></tr></tbody></table><div>The search results page looks absolutely fantastic. This brought back some fond memories.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-i2HqzQaNt4o/UyDntShjcHI/AAAAAAAAEdU/VewwBxX393I/s1600/Google+Results+Netscape.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-i2HqzQaNt4o/UyDntShjcHI/AAAAAAAAEdU/VewwBxX393I/s1600/Google+Results+Netscape.png" height="444" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Google Search Results in Netscape</td></tr></tbody></table><h2><a href="http://lwn.net/" target="_blank">LWN</a></h2><div><br /></div><div>I decided to give LWN a try. They have a fairly "minimalist" layout that I thought might do rather well in this old browser. I think it actually did pretty well, despite a couple of minor issues.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-3zTVd-jVo9U/UyDp3aJiI1I/AAAAAAAAEdg/SZUdWm0VPls/s1600/LWN+Netscape.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-3zTVd-jVo9U/UyDp3aJiI1I/AAAAAAAAEdg/SZUdWm0VPls/s1600/LWN+Netscape.png" height="436" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">LWN vs. Netscape</td></tr></tbody></table><h2>Hack a Day</h2><div>I have been reading Hack a Day for a number of years. Their homepage did rather well in the face of antiquity.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-lD0TtFc9hQs/UyDtcEyyxAI/AAAAAAAAEdw/r_kiR6H_ej0/s1600/Hack+a+Day+Netscape.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-lD0TtFc9hQs/UyDtcEyyxAI/AAAAAAAAEdw/r_kiR6H_ej0/s1600/Hack+a+Day+Netscape.png" height="436" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Hack a Day vs. Netscape</td></tr></tbody></table><h2>About Netscape</h2><div>The "About Netscape" page was particularly interesting. It reminded me of when Java was a product of Sun Microsystems and when Flash Player was a product of Macromedia.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-mV3vqOOFyNM/UyDuc1m6GMI/AAAAAAAAEd4/uP2D67eWWTw/s1600/About+Netscape.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-mV3vqOOFyNM/UyDuc1m6GMI/AAAAAAAAEd4/uP2D67eWWTw/s1600/About+Netscape.png" height="440" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">About Netscape</td></tr></tbody></table><div>This was a fun way to spend an hour. Checking out insignificant portion of the web using an ancient web browser is something any web developer should do at least once.</div><div><br /></div><div>Thanks for reading!</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com1tag:blogger.com,1999:blog-5663160055124969741.post-11388986547725480342014-01-30T23:07:00.002-05:002014-01-30T23:07:56.219-05:00Vacuum Fluorescent Display Alarm Clock I have been working on this fun little project for the last couple of days. I ordered this VFD display from <a href="http://www.noritake-elec.com/" target="_blank">Noritake</a>&nbsp;as a free sample. I am quite grateful for their generosity and I decided to put this display to good use.<br /><br />I have written a driver in C# that implements a partial selection of the commands available from this display. The driver is object-oriented with property accessors used to set configuration parameters of the display.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-C9qAerk5NJ4/UusODKjbwuI/AAAAAAAAEUU/weonIwX5GKE/s1600/VFD-Display-Weather-Time-Email.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-C9qAerk5NJ4/UusODKjbwuI/AAAAAAAAEUU/weonIwX5GKE/s1600/VFD-Display-Weather-Time-Email.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Weather Data, Emails and a Loader Animation :]</td></tr></tbody></table>I put all of this together into a sample application for a smart alarm clock that I would like to build eventually. The sample application gets my unread e-mail from Gmail and weather data from <a href="http://openweathermap.org/" target="_blank">Open Weather Map</a>. I am also attacking the 16 custom characters that reside in ram to display a few simple animations.<br /><br /><a name='more'></a><h3>The Hardware</h3><div><br /></div><div>The hardware is quite simple for this article. I have a USB to RS232 converter. This display accepts RS232C levels so you cannot use the UART of a microcontroller directly without using a transistor to invert the signal.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-L8arI0_PL68/UusTW1XZeiI/AAAAAAAAEUk/mPSLzqZ-8eU/s1600/Hardware+Overview.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-L8arI0_PL68/UusTW1XZeiI/AAAAAAAAEUk/mPSLzqZ-8eU/s1600/Hardware+Overview.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Hardware Overview&nbsp;</td></tr></tbody></table><div>The display itself is the <a href="http://www.noritake-elec.com/y-series.php?model=CU24063-Y100" target="_blank">CU24063-Y100</a> model from Noritake. I don't know a whole lot about the theory of operation behind these displays, but it is a vacuum sealed type device. It has a "getter" on the front glass to catch any impurities during the sealing process. It also contains a heater that is visible during very low-light conditions.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-gFPJEmcUWzk/UusUvHrhoZI/AAAAAAAAEUw/r9pmLVGSyHs/s1600/VFD-Display+Front.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto; text-align: center;"><img border="0" src="http://4.bp.blogspot.com/-gFPJEmcUWzk/UusUvHrhoZI/AAAAAAAAEUw/r9pmLVGSyHs/s1600/VFD-Display+Front.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Display Front</td></tr></tbody></table>This display uses a microcontroller to implement the command set. It is from the Renesas R8C lineup, a very simple and interesting processor. I will likely order some this summer to experiment with. These processors implement numerous security features including read/write protected flash pages and a 2^56 complexity key for reprogramming. Quite impressive if you ask me. Given a baud rate of 500000bps, it would take a few hundred years to brute force these devices. Not to mention the fact that you need to reset the chip after each attempt.<div><br /></div><div>Thankfully, Noritake left a nice hole in their protocol that let me read out the firmware contents. This is a story for another blog post (maybe :]).<br /><div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-1lAWbEMRyDE/UusUupNOqaI/AAAAAAAAEU0/oVy94J4N8MQ/s1600/VFD-Display+Back.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-1lAWbEMRyDE/UusUupNOqaI/AAAAAAAAEU0/oVy94J4N8MQ/s1600/VFD-Display+Back.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Display Back</td></tr></tbody></table><h3>The Driver</h3><div><br /></div><div>The first thing I did was develop a subset of the commands into a clean object-oriented driver. I created a <a href="http://github.com/aarossig/VfdDriver" target="_blank">GitHub repo</a> to share the code with anyone who wishes to drive this display. It is tested on Mono, but I can't see any reason why this wouldn't work on Microsoft's implementation. Submit an issue on GitHub if you encounter any issues and I can fire up my Windows virtual machine and debug it.</div><div><br /></div><div>I will give you a quick taste of the code here but you can see the rest on GitHub. Here is the constructor that initializes a serial port and configures it for the default baud rate of the device. Note the optional parameter that would allow you to override it.</div><pre class="brush: csharp">/// &lt;summary&gt;<br />/// Initializes a new instance of the<br />/// &lt;see cref="TheResistorNetwork.VfdDriver.VdfDisplay"&gt; class.<br />/// &lt;/summary&gt;<br />/// &lt;param name="serialPort" /&gt;Serial port.<br />/// &lt;param name="baudRate" /&gt;Baud rate.<br />public VdfDisplay (string serialPort, int baudRate = 38400)<br />{<br /> this.serialPort = new SerialPort ();<br /> this.serialPort.PortName = serialPort;<br /> this.serialPort.BaudRate = baudRate;<br /> this.serialPort.StopBits = StopBits.One;<br /> this.serialPort.Parity = Parity.None;<br /> this.serialPort.Handshake = Handshake.None;<br /> this.serialPort.Open ();<br /><br /> displayMode = VfdDisplayMode.Flickerless;<br /> brightness = VfdBrightness.Percent200;<br /> blinkMode = VfdBlinkMode.Off;<br /> cursorMode = VfdCursorMode.Underline;<br /> customCharacterMode = VfdCustomCharacterMode.Disabled;<br />}<br /></pre>This driver is for the 24x6 character display. I only have the one display from this company so I didn't bother abstracting it as an IVfdDisplay or anything like that.<br /><br />I have modelled the cursor mode using an enum that extends byte. <br /><pre class="brush: csharp">public enum VfdCursorMode : byte<br />{<br /> Underline = 0x13,<br /> Off = 0x14,<br /> Block = 0x15,<br /> UnderlineBlink = 0x16<br />}<br /></pre>This allows me to cast it directly to a byte and send it to the device during a property assignment. <br /><pre class="brush: csharp">/// &lt;summary&gt;<br />/// Gets or sets the cursor mode.<br />/// &lt;/summary&gt;<br />/// &lt;value&gt;The cursor mode.&lt;/value&gt;<br />public VfdCursorMode CursorMode<br />{<br /> get {<br /> return cursorMode;<br /> }<br /><br /> set {<br /> cursorMode = value;<br /><br /> var cmd = new byte[] { (byte)cursorMode };<br /> serialPort.Write(cmd, 0, cmd.Length);<br /><br /> Thread.Sleep (1);<br /> }<br />}<br /></pre>This should be enough to give you an idea of what is going on in this driver. Feel free to fork the repo on GitHub if you wish to implement the rest of the commands. I will be more than willing to review your code and merge it in!<br /><br /><h3>The Sample Application</h3></div></div><div><br /></div><div>I consumed my driver in a sample application that fetches e-mails, weather data, date/time and animations. This code is far from pretty, but it works. Here is a link to the <a href="http://github.com/aarossig/DeskClock" target="_blank">GitHub repo</a> for you to peek at. There are probably hundreds of ways that I could have done this better.</div><div><br /></div><div>As an example, I could have created a TextScroller class that scrolls text at any location on the screen with a configurable width. At the moment, it is a mess of hard-coded constants.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-w2otuGijuXc/UusatZL4NbI/AAAAAAAAEVE/Wqr9YWxrlLw/s1600/Display+Globe.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-w2otuGijuXc/UusatZL4NbI/AAAAAAAAEVE/Wqr9YWxrlLw/s1600/Display+Globe.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Spinning Globe Animation with no Unread Email</td></tr></tbody></table><div>It takes a few moments to initialize as it downloads contents from Open Weather API and Gmail.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-IDWDIOgbPAI/UusbqaJv_eI/AAAAAAAAEVM/EazSB8_QKA0/s1600/Display+Spinning+Envelope+Animation.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-IDWDIOgbPAI/UusbqaJv_eI/AAAAAAAAEVM/EazSB8_QKA0/s1600/Display+Spinning+Envelope+Animation.JPG" height="426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Spinning Envelope with Unread Email</td></tr></tbody></table><div>So there you have it! My VFD driver and sample application. Thanks for reading!</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com4tag:blogger.com,1999:blog-5663160055124969741.post-24242590050821543232014-01-02T17:26:00.000-05:002014-01-02T17:36:11.208-05:00Snow Day: Conky Configuration Time! It is quite snowy in southern Ontario today so I spent a bit of time configuring Conky on my system. I have heard of conky before but never bothered to put any time into configuring it.<br /><br />For those of you who don't know, Conky is a nice system monitoring application that runs on the root X window. This means that it sits on your desktop like Active Desktop, but better.<br /><br />Mine looks good, but I have seen screenshots of some very artistic configurations.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-x2vhT-S-Hbg/UsXlch3GJnI/AAAAAAAAELA/i2OD-8N2DVY/s1600/Conky.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="372" src="http://2.bp.blogspot.com/-x2vhT-S-Hbg/UsXlch3GJnI/AAAAAAAAELA/i2OD-8N2DVY/s640/Conky.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Conky Configuration :]</td></tr></tbody></table>Read more to see my conky.conf file!<br /><br /><a name='more'></a>Here is my conky config if you want to try it out and customize it!<br /><pre class="brush: plain">alignment top_left<br />background no<br />border_width 0<br />cpu_avg_samples 2<br />default_color white<br />default_outline_color white<br />default_shade_color white<br />draw_borders no<br />draw_graph_borders yes<br />draw_outline no<br />draw_shades no<br />use_xft yes<br />xftfont Ubuntu Mono:size=12<br />gap_x 60<br />gap_y 60<br />minimum_size 600 5<br />net_avg_samples 2<br />no_buffers yes<br />double_buffer yes<br />out_to_console no<br />out_to_stderr no<br />extra_newline no<br />own_window yes<br />own_window_type desktop<br />own_window_transparent yes<br />stippled_borders 0<br />update_interval 1.0<br />uppercase no<br />use_spacer none<br />show_graph_scale no<br />show_graph_range no<br /><br />TEXT<br />${alignc}${color grey}Hostname<br />${alignc}${color white}${nodename}<br /><br />${alignc}${color white}${uptime}, ${freq}MHz<br /><br />${alignc}${color grey}CPU Usage<br />${color white}${cpugraph 60}<br />${alignc}${color grey}Processes${color white} ${processes} ${color grey}Running${color white} ${running_processes}<br /><br />${alignc}${color grey}RAM<br />${alignc}${color white} ${mem}/${memmax}<br />${memgraph 60}<br />${alignc}${color grey}Swap<br />${alignc}${color white}$swap/$swapmax<br /><br />${alignc}${color grey}Networking<br />${alignc}${color grey}eth0<br />${alignc}Up ${color white}${upspeed eth0}${color grey} Down ${color white}${downspeed eth0}<br />${alignc}${color grey}wlan0<br />${alignc}${color white}Up ${color white}${upspeed wlan0}${color grey} Down ${color white}${downspeed wlan0}<br /><br />${alignc}${color grey}Root File System<br />${alignc}${color white}${fs_used /} / ${fs_size /}<br />${color white}${fs_bar 10 /}</pre>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com0tag:blogger.com,1999:blog-5663160055124969741.post-55654874366281286702014-01-01T16:14:00.000-05:002014-01-01T16:14:37.247-05:003D Perspective Projection I have wanted to write a 3D rendering engine for years and I have finally decided to spend some time on the problem. I decided to implement a simplistic rendering engine that supports the following basic features:<br /><ul><li>3D Perspective Projection (far objects are smaller than near objects)</li><li>Triangular Polygons for Rendering</li><li>Simplistic Lighting Model</li><li>Colored Polygon Faces (no texturing)</li></ul><div>The largest hurdle for me was getting past perspective projection. I did some research about matrix transformations and got caught up quite deeply in mathematical abstraction. It was the image below that really set my mind in motion. I decided to take a step back and use some simple high school trigonometry to solve the problem.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-bicWB_TpAG4/UsL9ysB-ptI/AAAAAAAAEIs/Va82WJ1IevI/s1600/Perspective+Projection.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto; text-align: center;"><img border="0" height="456" src="http://3.bp.blogspot.com/-bicWB_TpAG4/UsL9ysB-ptI/AAAAAAAAEIs/Va82WJ1IevI/s640/Perspective+Projection.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Leon Battista Alberti and Perspective Projection</td></tr></tbody></table><div class="separator" style="clear: both; text-align: left;">I have spent approximately 13 hours on this project and the results so far are fantastic looking. This entire experiment has given me great insights into the way 3D geometry is rendered onto a 2D plane.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-0refGrIKqGw/UsReoqebbdI/AAAAAAAAEJQ/afLBW_l_nJU/s1600/Large+Teapot+Render.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-0refGrIKqGw/UsReoqebbdI/AAAAAAAAEJQ/afLBW_l_nJU/s640/Large+Teapot+Render.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The Classic Utah Teapot :]</td></tr></tbody></table><div class="separator" style="clear: both; text-align: left;">In this article I will show you the various steps that I have taken to render this image. I will focus on the mathematics behind perspective projection that I used to arrive at the teapot rendering above. The image is not perfect, but I am happy with the results.</div><br /><a name='more'></a><h3>Perspective Projection</h3><div><br /></div><div>The largest stumbling block for me was rendering the geometry onto the plane that is my display. As I mentioned earlier, most explanations of perspective projection start out with camera matrix transforms. I found this very abstract. For the scope of this project I wanted to ignore the concepts of world coordinates, camera coordinates and so on. I started out with the assumption that the geometry that I wish to render is already located in front of the camera.</div><div><br /></div><div>The <a href="http://en.wikipedia.org/wiki/Viewing_frustum" target="_blank">viewing frustum</a> is the most basic concept of perspective projection. It defines the plane onto which your 3D geometry will be projected (labelled "near" in the image below).</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-j-0Y1-k1cn8/UsRj2iDxQxI/AAAAAAAAEJg/_gXwSwIrDbM/s1600/Viewing+Frustum.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="532" src="http://1.bp.blogspot.com/-j-0Y1-k1cn8/UsRj2iDxQxI/AAAAAAAAEJg/_gXwSwIrDbM/s640/Viewing+Frustum.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Viewing Frustum</td></tr></tbody></table><div>The viewing frustum is defined by the users field of view. Field of view is specified in degrees and then in combination with the width of the viewport in pixels we can determine the users distance from <br /><div class="separator" style="clear: both; text-align: center;"></div>the screen.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-q9KOcYjALAQ/UsR0OCroENI/AAAAAAAAEKQ/zmLpySKqymM/s1600/Field+of+View.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="486" src="http://2.bp.blogspot.com/-q9KOcYjALAQ/UsR0OCroENI/AAAAAAAAEKQ/zmLpySKqymM/s640/Field+of+View.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Field of View</td></tr></tbody></table></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: left;">The field of view is a user defined parameter of the rendering engine. I set will set mine to 100 degrees. Let's say that the width of the viewport for this example is 800 pixels. We can calculate the users distance from the plane as follows:</div>$$ \tan(\theta _{\mathrm{fov}} /2) = \mathrm{\frac{w/2}{d}} \\ \mathrm{d} = \mathrm{\frac{w/2}{\tan(\theta _{\mathrm{fov}} / 2)}} \\ \mathrm{d} = \mathrm{\frac{800/2}{\tan(100 / 2)}} \\ \mathrm{d} = 335.639 $$<br />Next I decided to work on projecting the actual points onto the plane. I started out on a piece of paper with a neat little drawing and started to realize that it is quite an easy feat.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-0BXY6AVxxWc/UsRzMAmxwfI/AAAAAAAAEKI/xpOyfzIvnTc/s1600/Projection+Drawing.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-0BXY6AVxxWc/UsRzMAmxwfI/AAAAAAAAEKI/xpOyfzIvnTc/s640/Projection+Drawing.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Projection Sketch</td></tr></tbody></table>This is an extremely simple problem to solve. We know the camera position and we know the point that we wish to plot. Now it is just a matter of solving for the angle of projection and then using the concept of similar triangles to resolve the point on the screen. The camera position will be (0, 0, d) where d is the distance that you calculated earlier.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-_LgAfarl9T8/UsR22GEa83I/AAAAAAAAEKc/NpueRo9QOWs/s1600/Perspective+Projection.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="424" src="http://4.bp.blogspot.com/-_LgAfarl9T8/UsR22GEa83I/AAAAAAAAEKc/NpueRo9QOWs/s640/Perspective+Projection.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Perspective Projection Diagram</td></tr></tbody></table>I will demonstrate how to solve the y-component of the projected point first. The same formula will then be applied to the x-component. First we must solve for the angle of projection in the y-axis.<br />$$ \tan(\theta_{\mathrm{proj}}) = \mathrm{\frac{P_{y}}{P_{z} + C_{z}}} \\ \theta_{\mathrm{proj}} = \tan^{-1}\left(\frac{P_{y}}{P_{z} + C_{z}}\right) $$<br />We will leave it in this form and solve for the y component of the projected point.<br />$$ \tan(\theta_{\mathrm{proj}}) = \mathrm{\frac{Proj_{y}}{C_{z}}} $$<br />We can now substitute the equation for the projected angle into the equation for the y-component of the projected point.<br />$$ \tan\left(\tan^{-1}\left(\frac{P_{y}}{P_{z} + C_{z}}\right)\right) = \mathrm{\frac{Proj_{y}}{C_{z}}} \\ \frac{P_{y}}{P_{z} + C_{z}} = \mathrm{\frac{Proj_{y}}{C_{z}}} \\ \mathrm{Proj_{y}} = \frac{C_{z}P_{y}}{P_{z} + C_{z}} $$<br />That's it! The formula is quite simple and the same can be applied to the x-component of the projected point as well.<br />$$ \mathrm{Proj_{x}} = \frac{C_{z}P_{x}}{P_{z} + C_{z}} $$<br />At this point, you will be able to plot points onto your screen!<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-Y9780w4VHhM/UsR9IZSLl_I/AAAAAAAAEKs/YwVU9uT4Qs4/s1600/Early+Render+Prototype.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="480" src="http://2.bp.blogspot.com/-Y9780w4VHhM/UsR9IZSLl_I/AAAAAAAAEKs/YwVU9uT4Qs4/s640/Early+Render+Prototype.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">An early test render :]</td></tr></tbody></table>I have implemented the rendering engine in C# using Mono on Linux. I am using the built-in System.Drawing namepsace that is based on Cairo to render the polygons. I will be publishing another article that explains how I modelled the geometry and the concepts behind lighting.</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com5tag:blogger.com,1999:blog-5663160055124969741.post-5718351113174544912013-12-21T14:55:00.000-05:002018-02-25T13:19:44.445-05:00Dealing With a Stubborn Laptop Battery I own an older model Panasonic Toughbook CF-28. I picked it up a couple of years ago for around a hundred dollars or so. It has a Pentium III 800MHz processor and 512MB of RAM, nothing to phone home about. What it lacks in performance it makes up for in its' rugged design. It is a military specification laptop that is capable of withstanding short drops, extreme temperatures and light water splashes. It also has a touchscreen. In other words, it is a fun toy.<br /><br /><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div>This laptop was missing the battery when I acquired it. I decided to opt for a cheap replacement available on eBay for around fifty dollars. When the battery arrived, I was quite pleased with the quality and performance of the product. I was easily able to get more than 5 hours of battery runtime out of the laptop.<br /><br />This laptop is far from my daily driver and I left it on the shelf for a few months. When I came back to it the battery was expectedly discharged completely. The interesting part is that it would not accept a charge despite the charge indicator being illuminated.<br /><br />I decided to disassemble the battery to investigate and was able to bring it back to life!<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-hsqW_3VJvDU/UrXp4l7hfvI/AAAAAAAAEFU/Nb9Bf3Qel2w/s1600/Fixed+Battery.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-hsqW_3VJvDU/UrXp4l7hfvI/AAAAAAAAEFU/Nb9Bf3Qel2w/s640/Fixed+Battery.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Fixed Battery</td></tr></tbody></table><br /><a name='more'></a>First I disassembled the battery to expose the inner cells. My initial assumption was that one of the cells or subpacks had failed short.<br /><div class="separator" style="clear: both; text-align: center;"></div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-UOJ9ioEAbI0/UrXs6ZMCBCI/AAAAAAAAEF8/7a79oY7KtCs/s1600/CF-28+Laptop+Battery+Inside.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-UOJ9ioEAbI0/UrXs6ZMCBCI/AAAAAAAAEF8/7a79oY7KtCs/s640/CF-28+Laptop+Battery+Inside.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">CF-28 Battery Inside</td></tr></tbody></table><div>&nbsp;I started measuring the voltages across the various cells and subpacks only to find that there were no cells at 0V. At this point, I started to over-engineer a solution. I pulled the battery management circuit board off and started looking for datasheets to see if I needed to somehow reset the logic. I came across some sketchy websites containing vague traces of a datasheet translated from Chinese to English. Needless to say, I was not getting very far.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-iNQRA8JCHQM/UrXvYgiEQUI/AAAAAAAAEGI/zQPtft7grlY/s1600/Power+Supply+and+Multimeter.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-iNQRA8JCHQM/UrXvYgiEQUI/AAAAAAAAEGI/zQPtft7grlY/s640/Power+Supply+and+Multimeter.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Power Supply and Multimeter</td></tr></tbody></table><div>I decided to go back to the pack again and measure voltages. I noticed that every cell/subpack was reading approximately 2.95V. This is just 0.05V outside of the "safe operating limits" of lithium-ion cells. I decided to try my luck at giving them a boost using my bench supply. I gave the entire pack a constant voltage supply of approximately 12V for 2 - 3 minutes while monitoring the current. I stopped charging the pack once the cells reached 3.2V each.</div><div><br /></div><div>I re-assembled the battery and am proud to report that it is now accepting a charge!</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-0LHYWY4epmM/UrXw2puETGI/AAAAAAAAEGU/KRsznLRtg90/s1600/Battery+Refresh.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-0LHYWY4epmM/UrXw2puETGI/AAAAAAAAEGU/KRsznLRtg90/s640/Battery+Refresh.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">BIOS Battery Refresh Screen</td></tr></tbody></table><div>This laptop is a really cool piece of gear. I like the "High Temperature" option in the BIOS menu. I imagine a laptop of this class might be used in desert environments. This concept just seems awesome to me.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-TenyI118MSk/UrXxRmbgJ9I/AAAAAAAAEGc/74m64UkzMCw/s1600/CF-28+High+Temperature+Mode.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-TenyI118MSk/UrXxRmbgJ9I/AAAAAAAAEGc/74m64UkzMCw/s640/CF-28+High+Temperature+Mode.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">CF-28 High Temperature Settings</td></tr></tbody></table>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com3tag:blogger.com,1999:blog-5663160055124969741.post-14637478255713864802013-12-06T21:08:00.001-05:002013-12-06T21:08:29.177-05:00A Graphical Introduction to Hash Functions with SHA-2 <table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td><a href="http://3.bp.blogspot.com/-ZL4h9SXXUNc/UqI9eSWqTJI/AAAAAAAAD_k/XoZ-liA3p6w/s1600/opengraph.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="160" src="http://3.bp.blogspot.com/-ZL4h9SXXUNc/UqI9eSWqTJI/AAAAAAAAD_k/XoZ-liA3p6w/s200/opengraph.png" width="160" /></a></td></tr><tr><td class="tr-caption" style="font-size: 13px; text-align: center;">Bitcoin Logo</td></tr></tbody></table>Cryptography is the glue that holds together our modern society of electronic banking, web authentication mechanisms and identity verification. It is also the basis for the radical shifts in the way we think about our money such as <a href="http://bitcoin.com/">Bitcoin</a>.<br /><br />I have been fascinated by the concepts of cryptography lately and decided to implement a cryptographic hashing function to try and better understand them. I will present the knowledge that I have gained in an easy to understand fashion. I am by no means an expert on this subject but if you have a general interest this should be a good starting point.<br /><br />I have decided to implement SHA-256 due to the widespread adoption and ubiquity of this particular function. This implementation is designed in LabVIEW which is a graphical programming language. Whether you are a seasoned programmer or just interested in cryptography, this gentle introduction will be beneficial to you.<br /><br /><a name='more'></a><h3>Hashing Functions</h3><div><br /></div><div>One of the basic elements of cryptography is a hashing function. A hashing function is a mathematical operation that accepts a message as an input and produces a message summary or <i>digest</i>&nbsp;as an output. A hash function will always produce the same digest for a given message.</div><div><br /></div><div>In order for a hashing function to be useful in cryptography it must be impossible to create an input that produces a desired output. If it is possible to manipulate the output by strategically choosing the input then it is said to have been compromised.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-M7A9vmjOw9w/UqJDIUnkeZI/AAAAAAAAEAA/L1iMSl_C-LE/s1600/Hashing+Function.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-M7A9vmjOw9w/UqJDIUnkeZI/AAAAAAAAEAA/L1iMSl_C-LE/s1600/Hashing+Function.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Hash Function</td></tr></tbody></table><div>Once a hashing function has been compromised, it is considered insecure. <a href="http://en.wikipedia.org/wiki/Md5">MD5</a> is a popular hashing function that has been compromised.</div><div><br /></div><h3>SHA-2</h3><div><br /></div><div>SHA-2 is a very popular hash function designed by the US National Security Agency (NSA). It is available in a variety of lengths including 224, 256, 384 and 512. The method used to arrive at the message digest for all of these is very similar. Notably, the difference between SHA-224 and SHA-256 are the use of different initial hash values and the omission of the last 32-bits of the hash.</div><div><br /></div><div>You will find that SHA-256 is available as sha256sum on most Linux systems.</div><div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-ei4fn4OR6HA/UqJJGSdCHfI/AAAAAAAAEAQ/f2NsLJFdsO0/s1600/sha256sum+shell.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="484" src="http://3.bp.blogspot.com/-ei4fn4OR6HA/UqJJGSdCHfI/AAAAAAAAEAQ/f2NsLJFdsO0/s640/sha256sum+shell.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">sha256sum in a shell</td></tr></tbody></table></div>SHA-256 is the hashing algorithm used behind Bitcoin, a cryptographic currency.<br /><br /><h3>SHA-2 Implementation</h3><div><br /></div><div>I have implemented SHA-256 in LabVIEW, a graphical programming language. I will take you through the entire implementation from start to finish. I have used the pseudo code available on the&nbsp;<a href="http://en.wikipedia.org/wiki/SHA-2">SHA-2 Wikipedia page</a>&nbsp;as a reference while writing this program. I hope that transferring it into a graphical form will help facilitate learning.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-EosHmEleGf0/UqJLGI8zmSI/AAAAAAAAEAc/qLfgBd4psYo/s1600/LabVIEW+SHA-256.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="346" src="http://3.bp.blogspot.com/-EosHmEleGf0/UqJLGI8zmSI/AAAAAAAAEAc/qLfgBd4psYo/s640/LabVIEW+SHA-256.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">LabVIEW SHA-256 User Interface</td></tr></tbody></table>Below is the source code to the SHA-256 program. Data flows from left to right. We take the string input from the Message textbox, convert it into a byte array, apply the SHA-256 hash to it and then output it into the Hash control.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-uwkxUjbYYeI/UqJpn4AACQI/AAAAAAAAEAs/afCjxF3kGOU/s1600/LabVIEW+SHA256+Source+Code.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="358" src="http://1.bp.blogspot.com/-uwkxUjbYYeI/UqJpn4AACQI/AAAAAAAAEAs/afCjxF3kGOU/s640/LabVIEW+SHA256+Source+Code.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">LabVIEW SHA-256 Source Code</td></tr></tbody></table>The red box labelled "SHA 256 U8[]" accepts a byte array and produces an array of unsigned 32-bit integers containing the hash. Below are the contents of this block. The first step is to prepare the message. Once the message is prepared, we apply the SHA-256 function to it.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-2bUhVGcuFO4/UqJrCKtrUQI/AAAAAAAAEBA/BlsQZIEGRjk/s1600/SHA-256+U8+Array.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="344" src="http://2.bp.blogspot.com/-2bUhVGcuFO4/UqJrCKtrUQI/AAAAAAAAEBA/BlsQZIEGRjk/s640/SHA-256+U8+Array.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">SHA 256 U8[] Source Code</td></tr></tbody></table><h3>Message Preparation</h3><div><br /></div><div>Below is the source code to the message preparation block. The first step is to add a single 1 bit to the message. This is accomplished by appending a 0x80 byte to the message array. Next, we need to pad the message with 0x00 bytes until the message length in bits modulo 512 is equal to 448. Since I am working with bytes, I will take the message length modulo 64 and add enough padding bytes to make the message length equal modulo 64 equal to 56.</div><div><br /></div><div>The last step is to append a 64-bit unsigned integer indicating how long the message is in bits. Follow the bottom branch of the source code to see this.</div><div><br /></div><div>Finally, the byte array is converted to an unsigned 32-bit integer array.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-GrUbk4mvk7A/UqJsDxuIWAI/AAAAAAAAEBM/KLC0f0oi3MU/s1600/Prepare+Message.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="334" src="http://3.bp.blogspot.com/-GrUbk4mvk7A/UqJsDxuIWAI/AAAAAAAAEBM/KLC0f0oi3MU/s640/Prepare+Message.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Prepare Message Source Code</td></tr></tbody></table><h3>512-bit Chunk Hashing</h3></div><div><br /></div><div>The next step is to apply the hash function to the message in 512 bit chunks. This is a simple loop that works on 512 bit (16 x 32-bit) pieces of the message at a time. The up and down arrows on the edge of the loop construct indicate that the output of the previous iteration is used as the input to the next iteration. The actual SHA-256 compression function is in "SHA 256 BLK".</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-a9m1uZrgYt0/UqJv-LXHfAI/AAAAAAAAEBY/wHvIcB2Uqn8/s1600/SHA-256+Message+Source+Code.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="380" src="http://1.bp.blogspot.com/-a9m1uZrgYt0/UqJv-LXHfAI/AAAAAAAAEBY/wHvIcB2Uqn8/s640/SHA-256+Message+Source+Code.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">SHA-256 Hash Function</td></tr></tbody></table><h3>Block Hashing</h3><div><br /></div><div>Each iteration of the loop will execute the following SHA-256 hash block function with the output of one iteration used as the input to the next. This the most complicated part of SHA-256.</div><div><br /></div><div>The first step is declare an array of 64 32-bit unsigned integers. This is the working register where the message block will be expanded by a variety of xor, right shift and right rotate operations.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-5o6ZYETI_qY/UqJ4dUKd3dI/AAAAAAAAEBo/_k6iZdlA0uM/s1600/Initialize+Block+Source+Code.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="386" src="http://1.bp.blogspot.com/-5o6ZYETI_qY/UqJ4dUKd3dI/AAAAAAAAEBo/_k6iZdlA0uM/s640/Initialize+Block+Source+Code.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Initialize the SHA-256 Block</td></tr></tbody></table><div>The result of this will be the 512-bit block expanded into a 2048 bit space. The details of these XOR, right shift and right rotate operations can be found on the Wikipedia pseudo code.</div><div><br /></div><h3>Round Constants</h3><div><br /></div><div>SHA-2 uses a group of round constants. These are numbers chosen to exhibit specific properties to make it difficult to architect an input to yield a desired output. The challenge with round constants is that they must be chosen such that a user can be convinced that they were not chosen with an ulterior motive. An example of a ulterior motive would be selecting constants that open a back door to the designer of the hashing algorithm. These numbers are known as&nbsp;<a href="http://en.wikipedia.org/wiki/Nothing_up_my_sleeve_number">nothing up my sleeve numbers</a>.</div><div><br /></div><div>The SHA-2 series of hash functions use the square and cube roots of various small prime numbers as round constants.</div><div><br /></div><h3>Compression Loop</h3><div><br /></div><div>The next stage of the block hashing algorithm is to loop through the array produced in the previous stage and apply the round constants along with a variety of XOR and rotate right operations.</div><div><br /></div><div>Again, I will refer you to the Wikipedia pseudo code for the details of these operations.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-34lnaKQdk_E/UqJ70r_vKAI/AAAAAAAAEB0/7DVHp4LFXCI/s1600/Compression+Loop+Source+Code.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="244" src="http://1.bp.blogspot.com/-34lnaKQdk_E/UqJ70r_vKAI/AAAAAAAAEB0/7DVHp4LFXCI/s640/Compression+Loop+Source+Code.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Compression Loop</td></tr></tbody></table><div>The last step of this operation is to add the results obtained in the compression loop to the previous iteration of hash values. Some implementations use a, b, c, d, e, f, g and h variables. I prefer to work with array of 8 32-bit unsigned integers.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-LVrdcLE-_WI/UqJ8-L-NbeI/AAAAAAAAECA/rYiWSHkBNsA/s1600/Finalize+Hash+Values+Source+Code.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-LVrdcLE-_WI/UqJ8-L-NbeI/AAAAAAAAECA/rYiWSHkBNsA/s1600/Finalize+Hash+Values+Source+Code.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Finalize Hash Values</td></tr></tbody></table><div><br /></div><h3>Conclusion</h3><div><br /></div><div>As you can see, implementing the SHA-2 hash functions is a relatively painless process. I certainly learned a lot along the way.</div><div><br /></div><div>The next step would be to implement a bitcoin miner in LabVIEW to try and understand what is happening when a miner is executing and some of the protocols used to communicate with the mining pools.</div><div><br /></div><h3>Source Code</h3><div><br /></div><div>If you have a copy of LabVIEW, here is a link to the <a href="http://drive.google.com/file/d/0B8rRA1u9D-X0SXR6cTE4U1dMRms/edit?usp=sharing">source code</a>. It is saved in LabVIEW 2010 format.</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com8tag:blogger.com,1999:blog-5663160055124969741.post-58286438798287999222013-12-04T00:03:00.000-05:002013-12-04T01:16:07.587-05:00A Testament to X11 Backwards Compatibility I recently scored a Hewlett Packard 1670A Deep Memory Logic Analyzer and I finally had a chance to fire it up. This unit dates back to 1992 and is packed with all sorts of interesting options for connecting peripherals to it. One particular feature that caught my eye was the option to connect to an X Server.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-zTk7DalSuOo/Up6uTscdGOI/AAAAAAAAD-I/SlFWHgDoohw/s1600/Logic+Analyzer+External+IO+Menu.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="HP 1670A Logic Analyzer" border="0" height="426" src="http://2.bp.blogspot.com/-zTk7DalSuOo/Up6uTscdGOI/AAAAAAAAD-I/SlFWHgDoohw/s640/Logic+Analyzer+External+IO+Menu.JPG" title="HP 1670A Logic Analyzer" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">HP 1670A Logic Analyzer</td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: left;">Here is the interface of the logic analyzer running on a remote X connection. I enjoy the colour scheme.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-CB7YM2b_ddg/Up6vvvbsePI/AAAAAAAAD-g/0VVuS-9gwVw/s1600/HP+1670A+Remote+X+Window.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="http://3.bp.blogspot.com/-CB7YM2b_ddg/Up6vvvbsePI/AAAAAAAAD-g/0VVuS-9gwVw/s640/HP+1670A+Remote+X+Window.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">HP 1670A user interface over an X connection :]</td></tr></tbody></table>I will give you a quick explanation as to how I was able to set this up by modifying a couple of configuration files to enable remote X connections.<br /><a name='more'></a><br />I run Linux Mint 15 with the e17 window manager (absolutely fantastic) and the gnome desktop manager (gdm). The first step was to assign my new logic analyzer an IP address as it does not support DHCP. This was fairly trivial, I merely assigned it a vacant IP on my network.<br /><br />Here is the configuration menu of the logic analyzer sporting classic interface design complete with the X logo. Take note of then convenient arrows to indicate which port each button adjusts settings for.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-MihO6Xj46y0/Up6zCEWfa8I/AAAAAAAAD-s/IJx6tOUoZ4g/s1600/Configuration+Menu.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-MihO6Xj46y0/Up6zCEWfa8I/AAAAAAAAD-s/IJx6tOUoZ4g/s640/Configuration+Menu.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Configuration Menu</td></tr></tbody></table>I especially enjoy the rotary encoder to the right of the screen as an input device. It is quite tactile and is a fun way to input the IP address. All that it is missing is the ability to depress it.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-GxC_FhmAuJg/Up6zydYJ2dI/AAAAAAAAD-0/wSKqBL9vrzU/s1600/IP+Address+Configuration.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-GxC_FhmAuJg/Up6zydYJ2dI/AAAAAAAAD-0/wSKqBL9vrzU/s640/IP+Address+Configuration.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">IP Address Information, Boring.</td></tr></tbody></table>I also found some bonus help material about the hosts file on UNIX systems. I see everything has been status quo since 1992.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-D3EFJ5Q6FGo/Up6zz3yvvUI/AAAAAAAAD-8/6VG8u6wFEK0/s1600/Hosts+File+Information.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-D3EFJ5Q6FGo/Up6zz3yvvUI/AAAAAAAAD-8/6VG8u6wFEK0/s640/Hosts+File+Information.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Hosts File information</td></tr></tbody></table>Next, I had to make a couple of changes to configuration files to allow remote X TCP connections. I followed instructions from a question on <a href="http://serverfault.com/questions/99147/karmic-koala-ubuntu-enable-remote-x-clients-through-tcp">serverfault </a>to make this happen.<br /><br />First, I modified /etc/gdm/custom.conf to allow DisallowTCP = false<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-4dsrNJglzbY/Up61VuxjZMI/AAAAAAAAD_I/w93_06E3i5U/s1600/GDM+Custom+Conf.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="414" src="http://4.bp.blogspot.com/-4dsrNJglzbY/Up61VuxjZMI/AAAAAAAAD_I/w93_06E3i5U/s640/GDM+Custom+Conf.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">/etc/gdm/custom.conf</td></tr></tbody></table>I also modified /etc/X11/xinit/xserverrc and removed the '-nolisten tcp' option. A quick restart of gdm later and I was able to establish the connection between the logic analyzer and my laptop. I find this to be a rather interesting feature of this piece of test gear. It's a shame that more devices don't implement the protocol, this is quite a cool feature if you ask me.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/--t6t64Ht3Ck/Up62K5fru5I/AAAAAAAAD_Q/R57F8CS1jAs/s1600/Waveform+Viewer.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="460" src="http://2.bp.blogspot.com/--t6t64Ht3Ck/Up62K5fru5I/AAAAAAAAD_Q/R57F8CS1jAs/s640/Waveform+Viewer.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Waveform Viewer</td></tr></tbody></table>This all reminds me very much of the <a href="http://rasteri.blogspot.ca/2011/03/chain-of-fools-upgrading-through-every.html">Chain of Fools</a>&nbsp;video from back in 2011 where Andy successfully upgraded from Microsoft's DOS 5.0 through to Windows 7 and was still able to play Doom and Monkey Island. I can definitely say that this is an impressive feat for a systems design house such as Microsoft, but the *nix's deserve some credit too!<br /><br /><iframe allowfullscreen="" frameborder="0" height="420" src="//www.youtube.com/embed/vPnehDhGa14" width="640"></iframe>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com6tag:blogger.com,1999:blog-5663160055124969741.post-5588616215490771472013-11-30T00:03:00.001-05:002013-12-03T23:02:59.867-05:00YALEDD! 16x16 LED Display I would like to present to you "Yet Another LED Display" (YALEDD!): a 16 by 16 LED Display. I am currently taking an engineering project management course and one of the objectives is to take a circuit from design to implementation while sticking to a schedule that we define.<br /><br />The class was instructed to choose a simple circuit such as an LED flasher or a simple sequential state machine composed of discrete logic, capture the schematic, layout the PCB and have it made by the end of the term.<br /><br />I decided that it would be boring to design a simple state machine. I also thought it might be pretty cool to have an electronic gizmo of my own design to show off on my desk at work.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-B-f765yfsM8/UplnGRgIdiI/AAAAAAAAD6c/y8MGNYgxEDM/s1600/IMAG0669%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://1.bp.blogspot.com/-B-f765yfsM8/UplnGRgIdiI/AAAAAAAAD6c/y8MGNYgxEDM/s640/IMAG0669%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Something Special :]</td></tr></tbody></table>This display utilizes constant current LED drivers to regulate 10mA through each of the LEDs. I have not used multiplexing to drive this display. I've done multiplexing in the past, and the&nbsp;<a href="http://www.st.com/web/catalog/sense_power/FM142/CL1854/SC1573/PF250094">STP16CPC26</a>&nbsp;drivers looked like they would be fun to experiment with. The entire design uses surface mount technology (aside from a couple of connectors) and was hand assembled by yours truly.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/--1-XGuSatic/UploZJzjQVI/AAAAAAAAD7Y/cUA1o2i0xZk/s1600/IMAG0670%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://3.bp.blogspot.com/--1-XGuSatic/UploZJzjQVI/AAAAAAAAD7Y/cUA1o2i0xZk/s640/IMAG0670%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">PCB Back</td></tr></tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-4_YUgDxK5ME/UpluJBnDq9I/AAAAAAAAD8M/YrKf9NSvhoo/s1600/IMAG0672%255B1%255D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://1.bp.blogspot.com/-4_YUgDxK5ME/UpluJBnDq9I/AAAAAAAAD8M/YrKf9NSvhoo/s640/IMAG0672%255B1%255D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">A Sea of LEDs</td></tr></tbody></table><a name='more'></a><h3>Features</h3><div>YALEDD! includes numerous features:<br /><br /><ul><li>FTDI UART to USB Interface</li><ul><li>UART Activity LEDs</li></ul><li>ATMEGA1284P Microcontroller</li><li>STP16CPC26 16 Channel LED Drivers</li><li>JTAG Debugging Interface</li><li>Separate LED Power Connector</li></ul><br />The microcontroller that I have chosen is largely overkill for a project of this complexity. I chose it because I did not want to limit myself to what I could do in software. The display currently acts as a UART slave device using a simple protocol to set/clear pixels, write the internal buffer to the LEDs and clear the display. In the future, it would be nice to have provisions for stored animations or user defined characters to make it easy to create a clock (digital or analog with this resolution) or a simple "Deli Counter".<br /><br /><h3>Implementation Details</h3></div><div><br /></div><div>The entire design is fit to a 2-layer PCB and uses SMT components. I have some concerns about the thermal performance of this device. When all of the LEDs on the panel are lit, the display generates a large amount of heat. This is due to the linear current regulators for the LEDs dropping nearly 1.5V at 10mA each.</div><div><br /></div><div>I chose the large USB-B connector to reduce the soldering effort. My original design included an SMT Micro-USB connector, but I decided that it would be too difficult to solder.</div><div><br /></div><h3>Software Details</h3><div><br /></div><div>Communications with the display are very simple. The protocol is handshake driven. You send it a command and it responds with a 0x65 ('A') to indicate that it was successful in whatever you asked it to do. If you send it an invalid command, it simply ignores it.</div><div><br /></div><div>I have written a simple driver in LabVIEW in order to drive the display. I would normally do something like this in C#, but I wanted access to their easy to use FFT library. I plan to implement a driver in C# soon and run it in Mono on a RaspberryPi.</div><div><br /></div><div>The following simple test application simply writes 33 bytes to the display. The first byte instructs the display that the following 32 bytes contain pixel data. The remaining 32 bytes indicate the state of each of the 256 LEDs.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-VYyA9pIVV7M/Uplrwy5YlgI/AAAAAAAAD7o/n5pqpXtzrrs/s1600/Simple+Set+Pixel+Test.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="640" src="http://1.bp.blogspot.com/-VYyA9pIVV7M/Uplrwy5YlgI/AAAAAAAAD7o/n5pqpXtzrrs/s640/Simple+Set+Pixel+Test.png" width="518" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Simple Test</td></tr></tbody></table><div>I then wrote a more complex example that uses FFT to generate a nice audio visualization.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-9nUf4_qwmyE/UplsTgtZ2BI/AAAAAAAAD7w/faI9Ym0qUQU/s1600/FFT+Audio+Visualization.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="368" src="http://4.bp.blogspot.com/-9nUf4_qwmyE/UplsTgtZ2BI/AAAAAAAAD7w/faI9Ym0qUQU/s640/FFT+Audio+Visualization.PNG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">FFT Audio Visualization</td></tr></tbody></table><div>I am using event driven programming techniques in LabVIEW using a queue to synchronize the threads. The code is a bit messy. I could spend some time breaking it down into SubVIs to make it easier to debug, but it works. This has become a sort of scratch pad for me to experiment with the display.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-v5HUn7WNQhY/Uplsy-vxDGI/AAAAAAAAD74/FSth74no94U/s1600/FFT+Audio+Visualization+Source.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="http://4.bp.blogspot.com/-v5HUn7WNQhY/Uplsy-vxDGI/AAAAAAAAD74/FSth74no94U/s640/FFT+Audio+Visualization+Source.PNG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">FFT Audio Visualization Source</td></tr></tbody></table><h3>Pictures</h3><div>It is a bit late, but I am eager to get this blog post out. I will post a video of the audio visualization soon, but for now I have some pictures of the build to share.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-V_-S_q1jzJI/UplwvLt4fnI/AAAAAAAAD80/wnCdCfvU62E/s1600/IMAG0674%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://1.bp.blogspot.com/-V_-S_q1jzJI/UplwvLt4fnI/AAAAAAAAD80/wnCdCfvU62E/s640/IMAG0674%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Microcontroller and FTDI</td></tr></tbody></table><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-4JpjqbPSiWs/Uplwwz2YPFI/AAAAAAAAD88/gunYwu1XG8I/s1600/IMAG0675%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://4.bp.blogspot.com/-4JpjqbPSiWs/Uplwwz2YPFI/AAAAAAAAD88/gunYwu1XG8I/s640/IMAG0675%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">LED Current Regulators</td></tr></tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-BPUQI4CyAs8/UplxJM7FU9I/AAAAAAAAD9Q/74PCmG9sXVI/s1600/IMAG0676%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://1.bp.blogspot.com/-BPUQI4CyAs8/UplxJM7FU9I/AAAAAAAAD9Q/74PCmG9sXVI/s640/IMAG0676%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Audio Visualizer</td></tr></tbody></table>I had the board made by the guys over at <a href="http://www.pentalogix.com/">Pentalogix</a>. They did a great job of getting the board turned around quickly (despite a minor error in the process).<br /><br />Stay tuned for a video demonstration. Thanks for reading!Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com2tag:blogger.com,1999:blog-5663160055124969741.post-15618194009565022312013-09-15T20:13:00.002-04:002013-09-15T20:13:55.608-04:00ARM Bare Metal Programming Embedded systems programming has been a passion of mine for a couple of years now. I really enjoy bringing a processor online and making it dance to the beat of my drum. Over the past few days I have taken an interest in writing my own linker scripts, start code and a&nbsp;<a href="http://en.wikipedia.org/wiki/Crt0" target="_blank">CRT0</a> (C Runtime). In this article I will give you some background as to why this is important. I will guide you through the steps that I have taken to bring an ARM processor from reset to main() and beyond.<br /><br />I am using the <a href="http://www.atmel.ca/devices/SAM4E16E.aspx" target="_blank">SAM4E16E</a>&nbsp;processor on a <a href="http://www.atmel.ca/tools/SAM4E-EK.aspx" target="_blank">SAM4E-EK</a> development board. I have the Atmel <a href="http://www.atmel.ca/tools/ATMELSAM-ICE.aspx" target="_blank">SAM-ICE</a>&nbsp;debugging tool to facilitate loading code. The steps that I am taking in this article will be very similar for other ARM processors. I did some bare metal programming on the&nbsp;<a href="http://www.raspberrypi.org/" target="_blank">Raspberry Pi</a>&nbsp;about a year ago and it was similar. &nbsp;My development host is an up to date <a href="http://www.linuxmint.com/" target="_blank">Linux Mint</a> 15 system.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-RgLBaqlzHHU/UjXwbNL8dqI/AAAAAAAAAnY/F4AVEJzsGFk/s1600/sam4e-ek.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="425" src="http://2.bp.blogspot.com/-RgLBaqlzHHU/UjXwbNL8dqI/AAAAAAAAAnY/F4AVEJzsGFk/s640/sam4e-ek.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Debugger (front) and Evaluation Kit (back)</td></tr></tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-Q1e_EB8W4HM/UjXxW-4d2iI/AAAAAAAAAnk/nOU7H3n8HVg/s1600/sam-ice_and_sam4e-ek.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-Q1e_EB8W4HM/UjXxW-4d2iI/AAAAAAAAAnk/nOU7H3n8HVg/s640/sam-ice_and_sam4e-ek.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">SAM-ICE and SAM4E-EK</td></tr></tbody></table><a name='more'></a><h3>Toolchain</h3><div><br /></div><div><a href="http://3.bp.blogspot.com/-8D1CpRnzbjs/UjXkY7Ptl6I/AAAAAAAAAm4/MvNuHfxWhOQ/s1600/The_GNU_logo.png" imageanchor="1" style="background-color: white; background-color: white; clear: left; display: inline !important; float: left; margin-bottom: 1em; margin-right: 1em; padding: 10px; padding: 10px; text-align: center;"><img border="0" src="http://3.bp.blogspot.com/-8D1CpRnzbjs/UjXkY7Ptl6I/AAAAAAAAAm4/MvNuHfxWhOQ/s200/The_GNU_logo.png" width="160" /></a>I have used the arm-none-eabi-gcc toolchain available from the&nbsp;<a href="http://launchpad.net/gcc-arm-embedded" target="_blank">GNU Tools for ARM Embedded Processors</a>. I am using this&nbsp;<a href="http://launchpad.net/~terry.guo/+archive/gcc-arm-embedded" target="_blank">alternate PPA</a>&nbsp;because it supports more versions of Ubuntu (and by extension, Mint) but is based on the same source tree.</div><div><div class="separator" style="clear: both; text-align: center;"></div>Follow these steps in a shell to install the arm-none-eabi toolchain.</div><pre class="brush: bash">sudo add-apt-repository ppa:terry.guo/gcc-arm-embedded<br />sudo apt-get update<br />sudo apt-get install gcc-arm-none-eabi<br /></pre><div><h4>Compilation/Linking</h4></div><div><br /></div><div>In this section I will give you some background information about compilation and linking. From this simplified discussion, you will understand why a linker script is necessary.</div><div><br /></div><h4>The Anatomy of a Relocatable Object</h4><div><br /></div><div style="text-align: left;"><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-yWAKR-H0n4A/UjXa-cFnwkI/AAAAAAAAAmo/E3YL9QEcoD4/s1600/Great_Dane_and_Chihuahua_Skeletons.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto; text-align: center;"><img border="0" src="http://4.bp.blogspot.com/-yWAKR-H0n4A/UjXa-cFnwkI/AAAAAAAAAmo/E3YL9QEcoD4/s200/Great_Dane_and_Chihuahua_Skeletons.jpg" width="180" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Anatomy</td></tr></tbody></table></div><div>There are numerous steps involved in compiling a C program. The first few steps include preprocessing, tokenization, syntax checking, building a symbol tree and more. Once the compiler has built an architecture-independent representation of your program it will create a relocatable object that contains the necessary instructions to implement your program.<br /><br />I will present a simple program to show the various sections within an object file.</div><div class="separator" style="clear: both; text-align: left;"></div><pre class="brush: c">/*<br /> * Sample Program to Demonstrate Object Sections<br /> */<br /><br />#include &lt;stdint.h&gt;<br /><br />uint32_t i = 0x00; // .bss<br />uint32_t j; // .bss<br />uint32_t k = 100; // .data<br />char *phrase = "Andrew Rossignol"; // .rodata<br /><br />int main(void) { // .text (all executable code)<br /> while(1);<br /> <br /> return 0;<br />}</pre>An object file will have a number of sections, but we are specifically interested in:<br /><ul><li>.bss</li><ul><li>Global variables whose values are 0 (uninitialized or assigned)</li></ul><li>.data</li><ul><li>Global variables whose values are initialized (non-zero)</li></ul><li>.rodata</li><ul><li>Read only globally defined variables</li></ul><li>.text</li><ul><li>Executable machine code</li></ul></ul>The remaining sections contain debugging information that I have safely managed to avoid for the sake of this project.<br /><br />The program above can be compiled and disassembled using the following commands. The -c flag instructs the compiler to produce an object file that is not linked (more on this later). I have set the -g flag to enable debugging symbols. This will make our disassembly listing much easier to work with. Finally, I have disabled optimizations with -O0. The second command simply disassembles all sections and saves to a file.<br /><pre class="brush: bash">arm-none-eabi-gcc -O0 -g -c -nostartfiles main.c -o main.o<br />arm-none-eabi-objdump -DS main.o &gt; main.o.S</pre>If you were to open main.o.S in a text editor, you would see the following disassembly listing. I have omitted the debug sections from this article as it would be quite long.<br /><pre class="brush: plain">main.o: file format elf32-littlearm<br /><br />Disassembly of section .text:<br /><br />00000000 &lt;main&gt;:<br />uint32_t i = 0x00; // .bss<br />uint32_t j; // .bss<br />uint32_t k = 100; // .data<br />char *phrase = "Andrew Rossignol"; // .rodata<br /><br />int main(void) { // .text (all executable code)<br /> 0: e52db004 push {fp} ; (str fp, [sp, #-4]!)<br /> 4: e28db000 add fp, sp, #0<br /> 8: e24dd00c sub sp, sp, #12<br /> int z = 0;<br /> c: e3a03000 mov r3, #0<br /> 10: e50b3008 str r3, [fp, #-8]<br /> while(1);<br /> 14: eafffffe b 14 <main x14=""><br /><br />Disassembly of section .data:<br /><br />00000000 &lt;k&gt;:<br /> 0: 00000064 andeq r0, r0, r4, rrx<br /><br />00000004 &lt;phrase&gt;:<br /> 4: 00000000 andeq r0, r0, r0<br /><br />Disassembly of section .bss:<br /><br />00000000 &lt;i&gt;:<br />uint32_t i = 0x00; // .bss<br />uint32_t j; // .bss<br />uint32_t k = 100; // .data<br />char *phrase = "Andrew Rossignol"; // .rodata<br /><br />int main(void) { // .text (all executable code)<br /> 0: 00000000 andeq r0, r0, r0<br /><br />Disassembly of section .rodata:<br /><br />00000000 &lt;.rodata&gt;:<br /> 0: 72646e41 rsbvc r6, r4, #1040 ; 0x410<br /> 4: 52207765 eorpl r7, r0, #26476544 ; 0x1940000<br /> 8: 6973736f ldmdbvs r3!, {r0, r1, r2, r3, r5, r6, r8, r9, ip, sp, lr}^<br /> c: 6c6f6e67 stclvs 14, cr6, [pc], #-412 ; fffffe78 &lt;phrase+0xfffffe74&gt;<br /> 10: 00000000 andeq r0, r0, r0</main></pre>You will see the phrase "Disassembly of section" numerous times in this file. It is easy to see how the variables and executable code relate between main.c and main.o.S. Take note that that the addresses in each section are starting at 0x00000000. This is because the object has not been "located". Each of these sections must be inserted into Flash Memory or SRAM at specific locations. This is the job of the linker and the linker script.<br /><br /><h4>Linking</h4><div><br /></div>Now that you have an understanding of why linking is important I can explain what a linker script is. When you compile your program, you will end up with numerous object files that must be combined into one executable (memory space). This is known as linking. The linker needs to have an understanding of how big your flash memory is, how much RAM you have and where it should be putting the various sections. It is the job of the linker script to let the GNU Linker know where to locate the various sections of your binary. I will present a linker script that I have written for my C Runtime (more on this later).<br /><pre class="brush: c">/*<br /> * Linker script for SAM4E16E<br /> */<br /><br />MEMORY {<br /> rom (rx) : ORIGIN = 0x00400000, LENGTH = 0x00100000<br /> ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00002000<br />}<br /><br />_stackStart = ALIGN (ORIGIN(ram) + LENGTH(ram), 8);<br /><br />...<br /></pre>In the MEMORY section we tell the linker what types of memory we have available. In the SAM4E we have ROM that is read-execute only. We also have RAM that is read-write-execute. We also indicate the start addresses and length of these memory segments. This information comes from the <a href="http://www.atmel.com/Images/Atmel_11157_32-bit-Cortex-M4-Microcontroller_SAM4E_Datasheet.pdf" target="_blank">SAM4E datasheet</a>&nbsp;(page 58).<br /><br />I have also defined the default value of the stack pointer to be at the end of RAM. The stack on ARM processors grows downwards. This means that I essentially have no heap.<br /><pre class="brush: c">...<br /><br />SECTIONS {<br /> .text : {<br /> *(.vectorTable*);<br /> *(.text*);<br /> *(.rodata*);<br /> } &gt; rom<br /> <br /> . = ALIGN(4);<br /> _startFlashData = .;<br /><br />...<br /></pre>I have defined three sections in my linker script. On line 4, I indicate that I wish to create a section named .text. In this data segment I am inserting a user defined section named 'vectorTable'. You will see this again in the C Runtime. I am also inserting the .text and .rodata sections that I presented earlier. On line 8 I am instructing the linker to insert these three sections into ROM which was defined in the MEMORY area.<br /><br />On lines 10 and 11, I define a variable indicating the start of the data segment in flash. This may sound confusing at first glance. The data section contains globally defined variables that must exist in RAM before the C program executes. There is no way to permanently store these variables in RAM as SRAM is volatile storage. Instead the .data section is stored in flash and then copied to SRAM when the system boots. This is accomplished in the C Runtime (keep reading).<br /><br />The special '.' variable refers to the current address in memory and ALIGN(4) will align the current address to the next even 32-bit address. Thus, we are snapping the current address to the next even 32-bit value. These lines create a "linker defined symbol" that can be referred to in C using the extern keyword or within the linker script itself.<br /><pre class="brush: c">...<br /> .data : AT(_startFlashData) {<br /> . = ALIGN(4);<br /> _startData = .;<br /> *(.data*);<br /> . = ALIGN(4);<br /> _endData = .;<br /> } &gt; ram<br /> <br /> .bss (NOLOAD) : {<br /> . = ALIGN(4);<br /> _startBss = .;<br /> *(.bss*);<br /> . = ALIGN(4);<br /> _endBss = .;<br /> } &gt; ram<br />}<br /><br />...<br /></pre>Next, I am instructing the linker to store the .data section at the address stored in the _startFlashData variable. I define two variables within the .data section, the starting address and the ending address of the .data section in RAM. I will use these symbols in the C Runtime.<br /><br />The last section, .bss, is very similar to the .data section except I am instructing the linker not to store it (NOLOAD) anywhere in the image. This makes sense as the .bss segment consists only of zero valued variables.<br /><br /><h4>C Runtime (crt0)</h4><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-iRjPoloUbNU/UjYy8UQbm7I/AAAAAAAAAn0/tiqMRsmAaMs/s1600/in-the-beginning.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-iRjPoloUbNU/UjYy8UQbm7I/AAAAAAAAAn0/tiqMRsmAaMs/s200/in-the-beginning.jpg" width="240" /></a></div><div>As I mentioned earlier, a C program has a few dependencies that must be satisfied before it can execute. This happens in the C Runtime, also known as crt0. The C Runtime is executed before your program starts. It takes care of setting up the stack pointer, initializing RAM, setting up the standard library and calling your main(). I have taken some hints from the <a href="http://asf.atmel.com/" target="_blank">Atmel Software Framework</a> when writing this code.</div><div class="separator" style="clear: both; text-align: left;">The following is a C Runtime that I have written. In the linker defined symbols I obtain the values of the symbols generated in the linker script.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">I have used a compiler directive (__attribute__) in the Interrupt Vector Table to instruct the compiler to declare the IVT array in its' own section. The IVT must be located at the beginning of flash memory in these processors.</div><pre class="brush: c">/*<br /> * C Runtime for the SAM4E Processor<br /> *<br /> * Initializes the processor by setting the stack pointer, initializing the<br /> * vector table, copying data into sram, clearing bss and calling main.<br /> */<br /><br />#include &lt;stdint.h&gt;<br /><br />/* Linker Generated Symbols ***************************************************/<br /><br />extern uint32_t _stackStart; // Start of the stack<br /><br />extern uint32_t _startFlashData; // Beginning of the .data segment in flash<br />extern uint32_t _startData; // Start of the .data segment in sram<br />extern uint32_t _endData; // End of the .data segment in sram<br /><br />extern uint32_t _startBss; // Beginning of the .bss segment<br />extern uint32_t _endBss; // End of the .bss segment<br /><br />/* Interrupt Vector Table *****************************************************/<br /><br />void crt0(void);<br /><br />__attribute__ ((section(".vectorTable")))<br />const void *VectorTable[] = {<br /> &amp;_stackStart, // Stack Pointer<br /> &amp;crt0 // Reset Vector<br />};<br /><br />/* C Runtime ******************************************************************/<br /><br />extern int main(void);<br /><br />void crt0(void) {<br /> // Copy data<br /> uint32_t *pSrc = &amp;_startFlashData;<br /> uint32_t *pDest = &amp;_startData;<br /> <br /> while(pDest &lt; &amp;_endData) {<br /> *pDest = *pSrc;<br /> <br /> pDest++;<br /> pSrc++;<br /> }<br /> <br /> // Zero bss<br /> pDest = &amp;_startBss;<br /> while(pDest &lt; &amp;_endBss) {<br /> *pDest = 0;<br /> pDest++;<br /> }<br /> <br /> // Call through to main<br /> main();<br /> <br /> // Trap a main that returns<br /> while(1);<br />}<br /></pre><br />The last bit of C code initializes ram and calls main. The first thing I do is copy the .data segment from flash into ram. I write zeros to the entire bss region and then call main.<br /><br />In embedded systems, it is atypical to have a main return. I have added a while(1) catch at the end of the runtime. This ensures that the processor is never in an undefined or unknown state.<br /><br /><h4>Test Program!</h4><div><br /></div><div>I completed this project incrementally. I was testing at each step through the process to ensure that the final executable image contained the correct values at the correct memory addresses. I was able to declare global variables and use them at runtime (tested using the LEDs and by inspection of the disassembly listing).</div><div><br /></div><div>I wrote a header file to describe the SAM4E PIO controller and used it to enable the blue and amber LEDs. I also made the green LED blink with a crude software delay.</div><pre class="brush: c">/*<br /> * Header file for the SAM4E Processor<br /> *<br /> * Defines registers and macros for use of the SAM4E processor.<br /> */<br /><br />#ifndef SAM4E_H<br />#define SAM4E_H<br /><br />/* PIO Controller *************************************************************/<br /><br />// Base Bank Addresses<br />#define PIO_A (uint32_t)0x400E0E00<br />#define PIO_B (uint32_t)0x400E1000<br />#define PIO_C (uint32_t)0x400E1200<br />#define PIO_D (uint32_t)0x400E1400<br />#define PIO_E (uint32_t)0x400E1600<br /><br />// Register Offsets<br />#define PIO_PER (uint32_t)0x00000000<br />#define PIO_PDR (uint32_t)0x00000004<br /><br />...<br /><br />#define PIO_PCISR (uint32_t)0x00000160<br />#define PIO_PCRHR (uint32_t)0x00000164<br /><br />/*<br /> * Sets a PIO register given a bank, an offset and a new value<br /> */<br />#define pio_set_register(bank, offset, value) { \<br /> (*(uint32_t *)(bank + offset)) = value; \<br />}<br /><br />/*<br /> * Gets a PIO register given a bank and an offset<br /> */<br />#define pio_get_register(bank, offset) { \<br /> return *((uint32_t *)(bank + offset)); \<br />}<br /><br />#endif<br /></pre>I make use of this header file in my simple test program. <br /><pre class="brush: c">/*<br /> * Basic SAM4E Test File<br /> */<br /><br />#include &lt;stdint.h&gt;<br /><br />#include "sam4e.h"<br /><br />// Watchdog Defines<br />#define WDT_MR *((uint32_t *)0x400E1854)<br /><br />int main(void) {<br /> // Disable the watchdog<br /> WDT_MR = 0;<br /> <br /> // Configure the PIO lines<br /> pio_set_register(PIO_A, PIO_OER, (1 &lt;&lt; 0));<br /> pio_set_register(PIO_D, PIO_OER, (1 &lt;&lt; 20) | (1 &lt;&lt; 21));<br /> <br /> // Enable the blue and amber LEDs<br /> pio_set_register(PIO_A, PIO_CODR, (1 &lt;&lt; 0));<br /> pio_set_register(PIO_D, PIO_CODR, (1 &lt;&lt; 20));<br /> <br /> // Blink the green LED<br /> while(1) {<br /> pio_set_register(PIO_D, PIO_CODR, (1 &lt;&lt; 21));<br /> <br /> volatile uint32_t cnt = 1;<br /> while(cnt++ &lt; 100000);<br /> <br /> pio_set_register(PIO_D, PIO_SODR, (1 &lt;&lt; 21));<br /> <br /> cnt = 1;<br /> while(cnt++ &lt; 100000);<br /> }<br /> <br /> <br /> return 0;<br />}<br /></pre>Everything works just as I had expected it to. It took me just over a week with a few hours here and there part time to put all of the pieces of this puzzle together.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-9meFlrVOBlo/UjY-l_AWrbI/AAAAAAAAAoE/1FrbGLbx2TM/s1600/andrew-embedded-happy.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-9meFlrVOBlo/UjY-l_AWrbI/AAAAAAAAAoE/1FrbGLbx2TM/s640/andrew-embedded-happy.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The CRT0 Grin :]</td></tr></tbody></table>If you have any questions at all, do not hesitate to leave them in the comments below. I have tried to write this as a guide for someone who was in my position a year ago. I am sure that there are some improvements that I could make to my CRT and I would really like to see them.<br /><br />Here are the <a href="http://docs.google.com/uc?export=download&amp;id=0B8rRA1u9D-X0aHAxdGlrSnNCTmM" target="_blank">source files</a> used for this post.<br /><br />Thanks for reading!Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com18tag:blogger.com,1999:blog-5663160055124969741.post-89610594321273964692013-09-02T17:56:00.001-04:002013-09-08T21:44:57.208-04:00Networking a Home from the 1950s <div class="separator" style="clear: both; text-align: left;">The year is 1957. The world is being taken storm by a wave of new technology. The first artificial satellite <a href="http://en.wikipedia.org/wiki/Sputnik_1">Sputnik 1</a> was launched and Elvis Presley's Jailhouse Rock was holding the number one spot on music charts around the world. All of this happened, of course, long before I was born.</div><br />Along with these exciting <a href="http://en.wikipedia.org/wiki/1957_in_science">historical events</a>, a small home was built in Stoney Creek, Ontario. My parents have recently purchased this home with the intention to renovate it into the 21st century.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-KDktNa6H8hQ/UiSRb4YL74I/AAAAAAAAAjc/w-soww_UJiE/s1600/IMAG0133%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://1.bp.blogspot.com/-KDktNa6H8hQ/UiSRb4YL74I/AAAAAAAAAjc/w-soww_UJiE/s640/IMAG0133%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The front of the tired old home (before new windows).</td></tr></tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-6L4oeOksKRE/UiUDThQhXmI/AAAAAAAAAlI/5n9SHr75hqU/s1600/IMAG0389%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="361" src="http://2.bp.blogspot.com/-6L4oeOksKRE/UiUDThQhXmI/AAAAAAAAAlI/5n9SHr75hqU/s640/IMAG0389%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Date of Manufacture</td></tr></tbody></table>I was tasked with networking the home and I decided to take some pictures along the way. I have retrofitted Cat6 and RG6 into each bedroom and living space to pave the way for Home Theater PCs (HTPCs) and "Smart TVs". We use&nbsp;<a href="http://www.amazon.ca/Channel-Master-4-bay-Antenna-CM4221/dp/B000FVTPX2">Channel Master 4221</a> antennas to receive local terrestrial ATSC signals. The RG6 will distribute this signal into each room. I purchased most of the supplies from the local <a href="http://nutechelectronics.com/">Nutech Electronics</a>&nbsp;and Home Depot.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-DRl-NGtp2OA/UiSTEfRcN2I/AAAAAAAAAjo/7q4wwXUnlDY/s1600/IMAG0380%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://4.bp.blogspot.com/-DRl-NGtp2OA/UiSTEfRcN2I/AAAAAAAAAjo/7q4wwXUnlDY/s640/IMAG0380%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The Cat6 and RG6 lines departing from the electrical panel. Looks good :]</td></tr></tbody></table>Read on to see more details!<br /><br /><a name='more'></a>Thankfully this home has an unfinished basement. We are finishing it with a drop ceiling which allows me to staple cables onto the bottom surface of the floor joists. I started out with a good stapler and round "cable staples". The regular rectangular staples have a bad tendency of crushing cables.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-TGVf21-OWy4/UiSU0fkqUYI/AAAAAAAAAj0/MH05xKS-lVw/s1600/IMAG0386%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://2.bp.blogspot.com/-TGVf21-OWy4/UiSU0fkqUYI/AAAAAAAAAj0/MH05xKS-lVw/s640/IMAG0386%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">A good stapler and staples are key.</td></tr></tbody></table>The first RG6 line is intended for the chimney on the other side of the home where the antennas will be mounted. The second is for the TV in the front living room. I have decided to put two network drops in the living rooms.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-580PT_-XE9o/UiSV8BwMDHI/AAAAAAAAAkA/PoNwmk1eHIc/s1600/IMAG0360%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://4.bp.blogspot.com/-580PT_-XE9o/UiSV8BwMDHI/AAAAAAAAAkA/PoNwmk1eHIc/s640/IMAG0360%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The first run of Cat6 and RG6.</td></tr></tbody></table>All of the RG6 drops are destined for the electrical panel. The network gear will be located in my bedroom next to the server, so all of the network drops terminate at the basement bedroom. I was careful to try to keep the RG6 and Cat6 somewhat separated from the mains AC where possible. In some cases it was simply unavoidable. I am using UTP Cat6 for this job.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-zECnFEcaEMM/UiSWghMrpgI/AAAAAAAAAkM/SYaw-3eKgC8/s1600/IMAG0362%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://3.bp.blogspot.com/-zECnFEcaEMM/UiSWghMrpgI/AAAAAAAAAkM/SYaw-3eKgC8/s640/IMAG0362%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Just the first two Cat6 runs and RG6 connections.</td></tr></tbody></table>I have installed a 1-to-6 splitter to split the antenna signal into each room.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/--P2bMWFU6pU/UiSXNNTug0I/AAAAAAAAAkY/fx0y3_PAm9A/s1600/IMAG0364%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://4.bp.blogspot.com/--P2bMWFU6pU/UiSXNNTug0I/AAAAAAAAAkY/fx0y3_PAm9A/s640/IMAG0364%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The antenna splitter next to the electrical panel.</td></tr></tbody></table>Once I had all of the RG6 lines routed to the electrical panel, I mounted the splitter and installed the power supply for the <a href="http://www.amazon.com/Channel-Master-TITAN2-PREAMPLIFIER-CM7777/dp/B000GGKOG8">Channel Master 7777</a>&nbsp;antenna preamplifier. I was lucky to salvage this CM7777 that had been dropped. There was a small crack on the Power Supply CCA that I was able to repair easily and test.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-9V_ZQy2heXQ/UiUEiot0B7I/AAAAAAAAAlU/g1tDg5uS2-8/s1600/IMAG0400%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://2.bp.blogspot.com/-9V_ZQy2heXQ/UiUEiot0B7I/AAAAAAAAAlU/g1tDg5uS2-8/s640/IMAG0400%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Completed Antenna Distribution</td></tr></tbody></table>I left 25ft. of RG6 cable on the outside of the home to reach the top of the chimney for the future antennas to be mounted. It will be coiled up on the side of the home until after we move in.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-3BmcDKZfrq8/UiUFCPqF-PI/AAAAAAAAAlc/ewUYNY1Ms9E/s1600/IMAG0388%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://3.bp.blogspot.com/-3BmcDKZfrq8/UiUFCPqF-PI/AAAAAAAAAlc/ewUYNY1Ms9E/s640/IMAG0388%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Coiled RG6</td></tr></tbody></table>Once I had all of the Cat6 drops routed into the basement bedroom it was time to install the keystone jacks and mount them on wall plates. An electrician recommended that I use these fantastic mounting plates. They do not require a stud or a junction box to secure the wall plate to. They simply grip the inside of the dry wall. They are called a "Low Voltage Mounting Bracket" and come in various shapes and sizes. I used the <a href="http://www.homedepot.ca/product/1-gang-low-voltage-mounting-bracket-150-rework/954556">CARLON 1 Gang Low Voltage Mounting Bracket</a>&nbsp;from Home Depot.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-FKyhIFKrgh8/UiSbgk6j2EI/AAAAAAAAAkk/skrz-c65imk/s1600/IMAG0376%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://4.bp.blogspot.com/-FKyhIFKrgh8/UiSbgk6j2EI/AAAAAAAAAkk/skrz-c65imk/s640/IMAG0376%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">View from the back of the wall.</td></tr></tbody></table>They mount very easily, you simply make a hole in the drywall and tighten the screws to increase their grip on the drywall.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-NUURMY5mGA4/UiSb2dldOjI/AAAAAAAAAks/gnEl-rapL94/s1600/IMAG0375%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://4.bp.blogspot.com/-NUURMY5mGA4/UiSb2dldOjI/AAAAAAAAAks/gnEl-rapL94/s640/IMAG0375%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">View from the front, inside the bedroom.</td></tr></tbody></table>After I pulled the cable, there were RG6 and Cat6 drops hanging from the basement ceiling ready to be routed into the walls in the rooms above.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-5bCaQ6bFiOg/UiSdpsrKh5I/AAAAAAAAAk4/xYIRX-Y8-9c/s1600/IMAG0381%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://1.bp.blogspot.com/-5bCaQ6bFiOg/UiSdpsrKh5I/AAAAAAAAAk4/xYIRX-Y8-9c/s640/IMAG0381%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">One Cat6 and RG6 drop for each adjacent bedroom.</td></tr></tbody></table>We all have our own cell phones so wiring plain old telephone system (POTS) into each room seemed like a waste. I have simply ran telephone into the basement bedroom where the modem will reside next to the network gear.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-xXc_iodWAnA/UiUFbYEPpWI/AAAAAAAAAlk/xlLatO03BLw/s1600/IMAG0411%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://1.bp.blogspot.com/-xXc_iodWAnA/UiUFbYEPpWI/AAAAAAAAAlk/xlLatO03BLw/s640/IMAG0411%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The right most keystone is for telephone. It is a little sloppy, but the rest are better.</td></tr></tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-MlSoQRMj8ks/UiUF26_VIqI/AAAAAAAAAls/MOWXPHXrYMQ/s1600/IMAG0412%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://4.bp.blogspot.com/-MlSoQRMj8ks/UiUF26_VIqI/AAAAAAAAAls/MOWXPHXrYMQ/s640/IMAG0412%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The front of the partially completed face plate.</td></tr></tbody></table><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-MkLuGKvaje4/UiUF6O5KeEI/AAAAAAAAAl0/eAiIVdJFp6s/s1600/IMAG0418%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://4.bp.blogspot.com/-MkLuGKvaje4/UiUF6O5KeEI/AAAAAAAAAl0/eAiIVdJFp6s/s640/IMAG0418%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The rear of one of the completed face plates in the bedroom on the main floor.</td></tr></tbody></table>Routing the cables from the basement onto the main floor wasn't terribly difficult. T<strike>his home had 14/2 wiring which was replaced with proper 14/3 as part of the renovation so there were already holes in the floors that I could use.</strike><br /><strike><br /></strike>[Correction, thanks Kent Sorensen]<br /><strike><br /></strike>This home was not wired with a ground to each outlet. The wiring was updated to modern Romex cabling that includes a grounded conductor. I was able to use the existing holes to help route the cables onto the main floor.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-5A-1WlFXPXY/UiUGmXBnyeI/AAAAAAAAAmA/FMmDGlRQUIw/s1600/IMAG0421%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://2.bp.blogspot.com/-5A-1WlFXPXY/UiUGmXBnyeI/AAAAAAAAAmA/FMmDGlRQUIw/s640/IMAG0421%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Cat6 and RG6 for the master and spare bedrooms.</td></tr></tbody></table>The results of the job were great. Everything looks excellent and passed a basic cable test. Time will tell how well everything works once we move in at the end of the month.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-UH1ZKww3XCk/UiUHITKMMuI/AAAAAAAAAmI/_ktFnPuu73Y/s1600/IMAG0420%5B1%5D.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://1.bp.blogspot.com/-UH1ZKww3XCk/UiUHITKMMuI/AAAAAAAAAmI/_ktFnPuu73Y/s640/IMAG0420%5B1%5D.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Neat and tidy wiring. :]</td></tr></tbody></table>This has been a long weekend and I am exhausted. I hope that these pictures and experiences will be valuable to someone in a similar position. Thank for reading!Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com10tag:blogger.com,1999:blog-5663160055124969741.post-78770730107249297442013-08-03T14:19:00.002-04:002013-08-28T22:58:33.741-04:00NI LabVIEW HD44780 Driver I have always had a place for LabVIEW in my toolbox. My opinion of it varies from week to week but I am settling on the side of appreciating what it brings to the table. It is an intriguing software package that allows you to write programs using graphical block diagrams. It is targeted primarily for scientists and engineers (read: non-programmers). I am a seasoned programmer with experience in C, C#, Java and a whole pile of other languages so it is fun for me to use LabVIEW and think about it as if I am using C. I taught myself LabVIEW over the past year by working on increasingly complex projects.<br /><br />I recently got my hands on a National Instruments USB-6009 Data Acquisition (DAQ) card and decided to write up a driver for the classic Hitachi HD44780 LCD controller. I used the object oriented features of LabVIEW to implement the driver. This allows multiple instances of the LCD to exist concurrently which means that I can have multiple displays interfaced to my PC simultaneously. The results were great!<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-muwLRuN7jC0/UfwvrARAm9I/AAAAAAAAAgI/UigbEtLRLoU/s1600/Operational+DMC40457+Display.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://3.bp.blogspot.com/-muwLRuN7jC0/UfwvrARAm9I/AAAAAAAAAgI/UigbEtLRLoU/s640/Operational+DMC40457+Display.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The Initialized and Functional Display :]</td></tr></tbody></table><br /><iframe allowfullscreen="" frameborder="0" height="360" src="//www.youtube.com/embed/qYm6n6w3vko" width="640"></iframe> I I released the <a href="http://decibel.ni.com/content/docs/DOC-30492">code on NI Developer Zone</a>. Read on to see how I did it!<br /><a name='more'></a><h3><b><br /></b></h3><h3><b>Read the Datasheet!</b></h3>My first step in tackling any new device is to <b><u>read the datasheet</u></b>. I usually give it a skim the first time through within 10 or 15 minutes to get a general feel for the document. Then I zero in on the relevant sections required for me to understand how to initialize the target device and bring it to life.<br /><br />The Hitachi datasheet was actually quite helpful as it contains a nice command reference. I also found a <a href="http://ecee.colorado.edu/~mcclurel/lcdguide.pdf">user manual</a> written by Optrex Corporation that contained some useful timing diagrams and flowcharts relevant to initialization. I strongly recommend this document to anyone who wishes to bring this display online.<br /><br /><h3><b>Hardware</b></h3>I wired up the DAQ card to the display using the digital lines and one analog output to set the contrast of the display.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-h3YCh_i3nMQ/Ufw4bzuRUGI/AAAAAAAAAgY/08qgROx0Oqs/s1600/DMC40457+USB-6009+Wiring.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-h3YCh_i3nMQ/Ufw4bzuRUGI/AAAAAAAAAgY/08qgROx0Oqs/s640/DMC40457+USB-6009+Wiring.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">USB-6009 Interfaced with the DMC40457 Display</td></tr></tbody></table>I installed header pins into the screw down terminals so that I could attach the display easily.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-57yGq37PQgs/Ufw5dscJQdI/AAAAAAAAAgk/TwxL0ZJbRAs/s1600/Header+Pins+USB-6009.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-57yGq37PQgs/Ufw5dscJQdI/AAAAAAAAAgk/TwxL0ZJbRAs/s640/Header+Pins+USB-6009.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Header Pins</td></tr></tbody></table><h3>Software</h3>I started working on the driver in LabVIEW straight away. This is my first time using a DAQ card so I decided to keep it simple and just vary the contrast of the display. The program is simple with an event loop and an analog write DAQmx task.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-PZ8SLtIrlHE/Uf0cEwp8wTI/AAAAAAAAAg0/FcjOZUmCZ6c/s1600/Contrast+Control.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="370" src="http://2.bp.blogspot.com/-PZ8SLtIrlHE/Uf0cEwp8wTI/AAAAAAAAAg0/FcjOZUmCZ6c/s640/Contrast+Control.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Contrast Control Program, Very Simple</td></tr></tbody></table>Once I had an understanding of how to use the DAQmx API, I started designing the class to model the display. I decided that I would keep track of different configuration aspects of the display and where it is physically connected.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-2C3c2W3VXxM/Uf0pHaY9ILI/AAAAAAAAAhE/zB9cWjPb5SY/s1600/Class+Data.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="492" src="http://3.bp.blogspot.com/-2C3c2W3VXxM/Uf0pHaY9ILI/AAAAAAAAAhE/zB9cWjPb5SY/s640/Class+Data.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The private data members of my class</td></tr></tbody></table>Once I had the data modeled, it was only a matter of implementing the various commands required to initialize and write data to the display.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-jxrR9Xb9SWQ/Uf0_1dUPayI/AAAAAAAAAhU/QnN2m-gEUDY/s1600/Sample+Application+Front+Panel.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="336" src="http://3.bp.blogspot.com/-jxrR9Xb9SWQ/Uf0_1dUPayI/AAAAAAAAAhU/QnN2m-gEUDY/s640/Sample+Application+Front+Panel.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Sample Application Front Panel</td></tr></tbody></table>Here is a screenshot of the source code for the sample application. I have developed the sample to support a 40 character by 4 line display (40x4) which means that there are two HD44780 controllers. This requires two instances of the class to drive them.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-a2dePP60CWE/Uf1AU_--zlI/AAAAAAAAAhc/K8rRIVLw72A/s1600/Sample+Application+Block+Diagram.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="428" src="http://2.bp.blogspot.com/-a2dePP60CWE/Uf1AU_--zlI/AAAAAAAAAhc/K8rRIVLw72A/s640/Sample+Application+Block+Diagram.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Sample Application Block Diagram</td></tr></tbody></table>The writers over at <a href="http://hackaday.com/">Hack a Day</a> have given me a lot of "cred" for my work so I thought I would show my support.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-AXgU_5gR1MI/Uf1G8Wi0jFI/AAAAAAAAAhs/sFUdQO9apuE/s1600/Hack+a+day.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://4.bp.blogspot.com/-AXgU_5gR1MI/Uf1G8Wi0jFI/AAAAAAAAAhs/sFUdQO9apuE/s640/Hack+a+day.JPG" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Hack a Day and Slogan</td></tr></tbody></table>I hope you have enjoyed reading this article. I don't tinker with LabVIEW often but this is probably the most involved project I have worked on.Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com0tag:blogger.com,1999:blog-5663160055124969741.post-68200763461796787832013-07-19T11:39:00.002-04:002013-07-24T19:03:38.753-04:00Programming the Vintage Intel MCS-48 Microcontrollers I have had a box in my parts collection for a few years that contains a variety of interesting vintage components. The most prominent are the Intel 8035L microcontrollers. These microcontrollers are from the <a href="http://en.wikipedia.org/wiki/Intel_MCS-48">Intel MCS-48</a> (commonly known as the 8048) line. They have 64 bytes of RAM and access to 4096 bytes of external program memory. Thankfully they are not the one-time-programmable (OTP) variety so I am free to put them up to any task. These processors have a copyright date of 1977 which puts them at roughly twice my age.<br /><br />There are also a couple of different EPROMS: D27256, with a copyright date of 1984 and D2758 with a copyright date of 1977. Obviously whoever owned these components prior to me was building some interesting embedded systems.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-7q4DL_oo_VA/UelJu7KyeWI/AAAAAAAAAeI/TliEmOKHdGQ/s1600/IMAG0126.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="362" src="http://4.bp.blogspot.com/-7q4DL_oo_VA/UelJu7KyeWI/AAAAAAAAAeI/TliEmOKHdGQ/s640/IMAG0126.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Vintage ICs</td></tr></tbody></table>They are in relatively good shape. I would say that these are socket pulls. They have some signs of prior use (adhesive on the quartz windows). These may have also been "development" units.<br /><br />These components coupled with my new <a href="http://gsyt181.cn/2013/07/new-eprom-tools.html">EPROM tools</a> were enough for me to bring this vintage processor online. I managed to get the classic blinking LED working as shown on the top trace of my oscilloscope.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-BWaLjDwsGX8/UelMTnOmqQI/AAAAAAAAAeY/leoMN76lJnM/s1600/Intel+8035L+Blinking+LED.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-BWaLjDwsGX8/UelMTnOmqQI/AAAAAAAAAeY/leoMN76lJnM/s640/Intel+8035L+Blinking+LED.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Intel 8035L in Action :]</td></tr></tbody></table>Continue reading to see how I did it!<br /><br /><a name='more'></a>The first thing I did was make sure that I could load code onto an EPROM using the MEMprog2 that I ordered. Unfortunately the tools do not support the D2758 EPROMs but they do support the D27256. I decided that I could just tie the upper address bits to ground and it would work fine.<br /><br />Once I knew that I could successfully load code into program memory I had to find a compiler or assembler (read toolchain) of sorts. I decided to become friendly with the hardware and stick with assembly. The&nbsp;<a href="http://asm48.sourceforge.net/">asm48</a> project looked promising. I have used Windows 7 as my development host for this project since the programming tools are running on that platform. I use VirtualBox to keep a few copies of Windows 7 around for IE web development anyway.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-dzRLwZKwAXo/UelPgfxMRfI/AAAAAAAAAeo/lrVrcItBpcs/s1600/Assembly+and+Assembler.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="386" src="http://3.bp.blogspot.com/-dzRLwZKwAXo/UelPgfxMRfI/AAAAAAAAAeo/lrVrcItBpcs/s640/Assembly+and+Assembler.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">My test program (3 bytes) and the output of the assembler.</td></tr></tbody></table>Once I had the program assembled and ready to load, I had to look at the datasheet for more hardware information. Thankfully a fellow by the name <a href="http://home.mnet-online.de/al/">Arnim Läuger</a>&nbsp;has done a fantastic job of compiling numerous MCS-48 family documents into one easy to read PDF. He has made this available under the title <a href="http://home.mnet-online.de/al/mcs-48/mcs-48.pdf">Grokking the MCS-48 System</a>. This document served as an instruction set manual and helped me determine how to connect the program memory to the processor.<br /><br />Early Intel processors combine the address and data bits onto the same lines and use two signals: ALE (Address Latch Enable) and PSEN (Program Store Enable) to signal what state the bus is in. This was done to save costs and keep pin count down. Unfortunately this complicates the connection to an external PROM.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-nJSb4-vfAx4/UelRU4NclWI/AAAAAAAAAe4/XpLcutMEs0E/s1600/Intel+Pinouts.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="424" src="http://3.bp.blogspot.com/-nJSb4-vfAx4/UelRU4NclWI/AAAAAAAAAe4/XpLcutMEs0E/s640/Intel+Pinouts.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Various Device Pinouts (EPROM Left, Processor Right, Discrete Logic Behind)</td></tr></tbody></table>This processor has an interesting Instruction Fetch waveform. I studied this waveform for a couple of hours to determine the best way to interface with the external program memory. I devised that I would need some form of external latch to store the address lines after the bus returned to a floating state. I could use the ALE line to signal when the address was stable and retain that value.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-C3duvPytxuA/UelSDU9bcsI/AAAAAAAAAfA/KF3dfd0agCw/s1600/Instruction+Fetch+Waveform.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="446" src="http://3.bp.blogspot.com/-C3duvPytxuA/UelSDU9bcsI/AAAAAAAAAfA/KF3dfd0agCw/s640/Instruction+Fetch+Waveform.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Instruction Fetch Waveform</td></tr></tbody></table>I looked through my discrete logic collection and came across a bag of 74LS373 devices. I had labelled these "8-Bit 3-State Transparent Latch" at some point in the past. I looked up the datasheet and decided that this device would be suitable.<br /><br />All of this design work was done mentally. I have drawn it up quickly for your benefit.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-Y_mG7QCyuqQ/UelXdQ-JJ4I/AAAAAAAAAfY/_7-4aPNUcvE/s1600/EPROM+Interface+Sketch.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://1.bp.blogspot.com/-Y_mG7QCyuqQ/UelXdQ-JJ4I/AAAAAAAAAfY/_7-4aPNUcvE/s640/EPROM+Interface+Sketch.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">EPROM Interface Sketch</td></tr></tbody></table>If you refer to both the instruction fetch waveform and this crude sketch I can explain the basic theory of operation. The processor will assert the address that it wishes to read from on the bus. When the address is stable on the outputs, the ALE line will go low. I use this signal to latch the address lines into the 74LS373 which is otherwise transparent. The processor then puts the bus into a high impedance (floating) state. When it is ready to read the byte of data from the program memory it will assert PSEN by lowering it. I use this signal to control the output enable of the EPROM. This allows the EPROM to put the value of the data being read onto the bus. The processor will raise PSEN when it is finished putting the bus back into in a floating state. This sort of handshaking action will ensure that there is never contention on the bus.<br /><br />A keen reader will spot the error in my drawing. CS should be OE.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-qtglINkbAkg/UelZphK7-sI/AAAAAAAAAfo/_0vfbufNMBo/s1600/Finished+Processor+EPROM+Interface.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="426" src="http://2.bp.blogspot.com/-qtglINkbAkg/UelZphK7-sI/AAAAAAAAAfo/_0vfbufNMBo/s640/Finished+Processor+EPROM+Interface.jpg" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Assembled Circuit</td></tr></tbody></table>After I had the circuit assembled it was as simple as putting the EPROM in my burner and loading my sample program. I validated that the processor was working by observing the state of the address lines. Since my program is only 3 bytes long only the first two bits of the address lines should change. My hypothesis was correct. The rest of the address lines were idle in a low state.<br /><br />I wrote a slightly more complex program to toggle the state of the output lines and managed to observe a blinking LED!<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-hEA15pxfmTc/UelbUARAWVI/AAAAAAAAAf4/DvbYtIATI90/s1600/LED+Blink+Program.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="422" src="http://4.bp.blogspot.com/-hEA15pxfmTc/UelbUARAWVI/AAAAAAAAAf4/DvbYtIATI90/s640/LED+Blink+Program.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The LED Blink Program</td></tr></tbody></table>I hope you have enjoyed reading this article. This blog outlines the process that I took to get scaled up on this very simple vintage processor. Next steps would be to experiment with interrupts. I would like to also toy with the timer that this processor has.<br /><br />Perhaps my favorite discovery throughout this process was the fact that this setup uses 135mA at 5V. These days we are concerned about getting into the uA range for low power devices.<br /><div><br /></div><div>I am interested in building a clock using Nixie tubes. I may end up using this processor as the brains behind the operation.</div>Andrew Rossignolhttp://www.blogger.com/profile/08702285354900642922noreply@blogger.com10