tag:blogger.com,1999:blog-25843273726245651912024-03-18T10:11:32.661+00:00Nerd Clubwhere geeky stuff and nerdy ideas come togetherChrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.comBlogger820125tag:blogger.com,1999:blog-2584327372624565191.post-72818328471015232102017-07-29T01:51:00.002+01:002017-07-29T01:51:42.094+01:00Hall sensor array quick postJust a quick post in response to a couple of questions we've been getting for a schematic for a hall sensor array, being driven directly off the Arduino i/o pins.<br />
<br />
<div style="display: block; font-family: "helvetica" , "arial" , sans-serif; font-size: 14px; font-stretch: normal; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 12px auto 6px auto;">
<a href="https://www.scribd.com/document/354985435/Hall-Sensor-Array#from_embed" style="text-decoration: underline;" title="View Hall Sensor Array on Scribd">Hall Sensor Array</a> by <a href="https://www.scribd.com/user/72007968/chris-holden2495#from_embed" style="text-decoration: underline;" title="View chris_holden2495's profile on Scribd">chris_holden2495</a> on Scribd</div>
<iframe class="scribd_iframe_embed" data-aspect-ratio="1.4146341463414633" data-auto-height="false" frameborder="0" height="600" id="doc_73964" scrolling="no" src="https://www.scribd.com/embeds/354985435/content?start_page=1&view_mode=scroll&access_key=key-vQc5edToAUIMntgmrBGY&show_recommendations=true" width="100%"></iframe>
<br />
If you place your A3144 hall sensors in a grid pattern, with the "flat" part of the sensor facing down (with the slightly bevelled face pointing upwards) then all of the centre pins should be connected together (a common ground).<br />
<br />
Each of the left-hand pins should be connected horizontally to the sensors immediately left/right of them. Each of the right hand pins should be connected vertically to the sensors immediately above/below them.<br />
<br />
The left-hand pins are the power supply for the sensors.<br />
The right hand pins are the open-drain collectors (outputs) of the sensors.<br />
<br />
We try to put the output pins (from the sensors) on our Arduino D2-D9 pins and make them inputs with interal pullups. We try to connect the output pins to power the sensors onto the Arduino D10-D17 pins and make them outputs, because D13 usually has an LED connected to it - you can still power a hall sensor from this pin, but using D13 as an input can be problematic.<br />
<br />
Hope this clears a few questions up!<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com1tag:blogger.com,1999:blog-2584327372624565191.post-5272619136511436052017-06-23T13:04:00.001+01:002017-06-23T13:04:37.505+01:00Back in the saddleWell it's been a funny couple of months.<br />
Firstly all kinds of personal and family issues meant I was travelling the country and not really spending much time at home nor in the workshop bungalow.<br />
<br />
Then along came a General Election.<br />
I've always taken an interest in politics, and always been a firm believer in engaging with people - whatever their political persuasion - to get people discussing policies and what parties actually do, rather than the usual X-Factor style personality vote that many elections have become.<br />
<br />
So when the UK elections threw up a hung Parliament, the conversation didn't just stop. In fact, at the time of writing, things haven't exactly been sorted out yet. The Tories have yet to confirm a deal with the DUP to keep hold of power and there could be another General Election any time soon (although its unlikely to be before the upcoming summer recess).<br />
<br />
On top of this, work still needs to get done to pay the bills.<br />
So there's not been much time for messing about in the bungalow, making stuff with the 3d printer or cutting stuff out with the laser cutter.<br />
<br />
But that's all about to change.<br />
Enough with politics. Enough with being angry.<br />
It's time to get making stuff again!Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-39904475508756349052017-05-15T21:37:00.001+01:002017-05-15T21:37:29.462+01:00Light up guitar demonstrationSo the light-up guitar is complete and ready to try out.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht2ZHcXxmw2ii-OC_WvBHwixwXcWyQVbpW5srwo09js2K2iCJBNd6ZiXZFdgUN3cb-2kV-rWOtq5s9HOw13LxEXFvWsLgFcLUIwqAyJ6ERrCIWvvT-nCTJFklI_Zsh_sfrmCG4M_AMmzCW/s1600/20170515_174524.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht2ZHcXxmw2ii-OC_WvBHwixwXcWyQVbpW5srwo09js2K2iCJBNd6ZiXZFdgUN3cb-2kV-rWOtq5s9HOw13LxEXFvWsLgFcLUIwqAyJ6ERrCIWvvT-nCTJFklI_Zsh_sfrmCG4M_AMmzCW/s320/20170515_174524.jpg" width="320" /></a></div>
<br />
The chords and scales mode works really well.<br />
And playing blues licks (with the blues scale selected) is great fun. As many guitarists already know, when playing a 12-bar pattern, it's quite common to play a major pentatonic melody over the one (I) chord, and a minor pentatonic melody over the four (IV) and five (V) chords. This is what gives the blues its distinctive "blues-y" sound (for you music theorists, it's the clash of a minor third over a major triad with an added flat 7th that <i>really</i> makes the blues sound like the blues, but the principle is the same).<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/YCpqrujzkAk?rel=0&controls=0&showinfo=0" width="490"></iframe>
</div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<i>By just noodling around playing notes from a scale you can get a nice blues-y guitar sound; coupled with a bit of rhythm, there's a very real danger of it actually sounding melodic!</i></div>
<i><br /></i>
<br />
Flicking the major/minor switch makes playing/finding chord tones really easy. Root notes are always in red. Thirds are in pale blue. When in "major" mode, the fourth degree of the scale is bright purple. So find a purple note, and the major third is immediately behind it. Slide into (or hammer on to) that note and you instantly get that blues sound.<br />
<br />
My own personal arsenal of blues licks is limited to about three licks. But even then, playing those couple of licks with the fretboard lit up makes understanding how and why they work much easier. You can actually see when you're bouncing around the major/minor third, you can see as well as hear the effect of sliding or bending to a flat seventh, you can see why a particular lick sounds good over the four chord (because it has a fourth of the scale in it) and doesn't always fit well over the five chord.<br />
<br />
From a learning point of view, it's a real success.<br />
From a guitar point of view.... well, not so much.<br />
<br />
For a start, I didn't do a very good job of clamping the fingerboard absolutely flat when gluing it to the neck. In fact, there's a tiny bit of a back-bow in the neck once it's on the guitar.<br />
Which means I've had to really jack up the bridge and put on some super-heavy gauge strings, to try to get the neck to bow "forwards" a little bit.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrvHIDd7LJqYRrgJOHy2hqgP1YPg02C63iyBuOBOR2h2rJTapNbGypLPHAZpf5T3R36bqp6BqRVevf5-himO5DL5OygGU98zJkHyx31HUaw4qbSmC1tPtpT7nIWDgRsdsC8H0lco6yoKvP/s1600/20170510_135908.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrvHIDd7LJqYRrgJOHy2hqgP1YPg02C63iyBuOBOR2h2rJTapNbGypLPHAZpf5T3R36bqp6BqRVevf5-himO5DL5OygGU98zJkHyx31HUaw4qbSmC1tPtpT7nIWDgRsdsC8H0lco6yoKvP/s320/20170510_135908.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<i>with the straight edge sitting on the neck, you can see a slight gap between the ruler edge and the frets on the left, furthest away from the body. Unfortunately, this means the neck has a slight back-bow at around the fifth fret, causing lower notes to "fret out" and buzz really badly.</i><br />
<i><br /></i>So although the backbow can (just about) be compensated for, it's at the cost of really heavy strings (13s on the highest E). Which, as you can imagine, makes playing blues-y licks, with lots of vibrato and string bending really tricky.<br />
<br />
There's also a dodgy earth problem that needs finding and addressing.<br />
It's barely noticeable when using a clean-tone amp. But add in a bit of crunch (I use the overdrive switch on my amp but the same effect happens with any high-gain effect pedal with any kind of treble boost) and suddenly you can hear the additional electronics in the guitar signal.<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/zXM4JVUjIyQ?rel=0&controls=0&showinfo=0" width="490"></iframe>
</div>
<br />
When using the menu, there are audible clicks in the audio signal. But the best is when the LEDs light up. The PWM signal driving the LEDs sounds like someone blowing into an open-ended piece of drainpipe. Very peculiar!<br />
<br />
So there we have it - a working light-up guitar. Of sorts.<br />
It lights up. It makes finding and playing scales easy.<br />
But it's an absolute pain to actually play.<br />
<br />
Keith will just have to hang on a little bit longer - I'm already started on a new guitar neck which I'll make sure is created flat and not bowed. And maybe we'll try moving the electronics out of the guitar body and hooking up to a separate, dedicated box of tricks, well away from the pickups and anything else that might affect the audio signal....<br />
<br />
<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com1tag:blogger.com,1999:blog-2584327372624565191.post-6493430996825313352017-05-08T23:35:00.002+01:002017-05-08T23:43:55.098+01:00Thwarted at the last!After swapping out a couple of dodgy Nokia LCDs and some last-minute rewiring (to enable a 2000mAh lipo battery to power the whole thing as well as be charged from a regular phone charger) the back of our guitar scratchplate was looking a bit of a mess (ok, Keith, <i>your</i> guitar scratchplate)<br />
<br />
We went from this....<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjr1r1i469SWRCWZ0j8MJhdfvIZ6XNf0uRHGdp1luA-sjmntUNAgVg8CW8yJFlqz80YkSiHPxIQ-FllZe0z7_3Q3yViMR0ruHGQwDbrtQLBjrZgpuewC8W3JQh7pfQ2ZyC-cpxSKZui_15x/s1600/20170501_114533.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjr1r1i469SWRCWZ0j8MJhdfvIZ6XNf0uRHGdp1luA-sjmntUNAgVg8CW8yJFlqz80YkSiHPxIQ-FllZe0z7_3Q3yViMR0ruHGQwDbrtQLBjrZgpuewC8W3JQh7pfQ2ZyC-cpxSKZui_15x/s320/20170501_114533.jpg" width="320" /></a></div>
<br />
....to this!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtsbMaPch9k4CwsZiTYaDZjNATXSwqc9Yg-QX9PVIY4b-NOusOq3uvgO0JAQLYTEtgSallDhfv-sYaswvGdaTagYkSzpsoFBupEKDiB5wWUUHt_S4a_o12CrBRJEABCAoqNzTu5m2W5P5C/s1600/20170507_002057.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtsbMaPch9k4CwsZiTYaDZjNATXSwqc9Yg-QX9PVIY4b-NOusOq3uvgO0JAQLYTEtgSallDhfv-sYaswvGdaTagYkSzpsoFBupEKDiB5wWUUHt_S4a_o12CrBRJEABCAoqNzTu5m2W5P5C/s320/20170507_002057.jpg" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
There's a lot to cram in there.<br />
And even after doing some extra routing on the body<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbQgTXZntJKjM6-1zH-jivbbz7NdbwubtbPJNOeWQY8PtBfMO7K4zjmFuQYBWkDtbM-fTKuinUFjvAn2DnfQidiDCms4cAjJINk-9Xj34eMVb6E-wFH3TkkQ-4z4D6aiWfEvaynxbKyJe6/s1600/20170508_170108.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbQgTXZntJKjM6-1zH-jivbbz7NdbwubtbPJNOeWQY8PtBfMO7K4zjmFuQYBWkDtbM-fTKuinUFjvAn2DnfQidiDCms4cAjJINk-9Xj34eMVb6E-wFH3TkkQ-4z4D6aiWfEvaynxbKyJe6/s320/20170508_170108.jpg" width="320" /></a></div>
<br />
and tidying up the wiring (a little bit)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLVKdZJYLq6DBeIlr5JqAAw-Zy_inF51umebg_RUizs1SlmB0pBIF17FNYq6QTw5lIQtJzGKDRL9okIiwexinX9LbFDU7J2PtusB1SekIWZh5iMjjtf6THUKIbEfdg-kc2Vm4r4qxxeF4y/s1600/20170508_223443.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLVKdZJYLq6DBeIlr5JqAAw-Zy_inF51umebg_RUizs1SlmB0pBIF17FNYq6QTw5lIQtJzGKDRL9okIiwexinX9LbFDU7J2PtusB1SekIWZh5iMjjtf6THUKIbEfdg-kc2Vm4r4qxxeF4y/s320/20170508_223443.jpg" width="320" /></a></div>
<br />
it finally came to assembling everything for the final dry fit.<br />
Sadly, it didn't quite fit!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZfmi9fbRxL3bP9UBKz4PkVdC9V7XOUuOA0uSLiuDpWJbphzRSdrF41F-XDbhljpS_Gar6Y9-e2JVcICnrhdkbS1ZAFwi_bo8mHBL4esg-CA8TV_ttoHf6D6v2QcvDJkAZV9I58VI8fPRq/s1600/20170508_225439.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZfmi9fbRxL3bP9UBKz4PkVdC9V7XOUuOA0uSLiuDpWJbphzRSdrF41F-XDbhljpS_Gar6Y9-e2JVcICnrhdkbS1ZAFwi_bo8mHBL4esg-CA8TV_ttoHf6D6v2QcvDJkAZV9I58VI8fPRq/s320/20170508_225439.jpg" width="320" /></a></div>
<br />
And it's a little bit late in the day to be running the router and hacking out lumps of wood from the body. So that'll have to wait 'til the morning. In the meantime, here's a final test, to make sure that the new wiring still works properly:<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/LeyVTG_gMpY?rel=0&controls=0&showinfo=0" width="490"></iframe>
</div>
<br />
Everything works well;<br />
The phone jack acts as a data connection for both midi and bluetooth data (not demonstrated in this video) as well as a charging point. The power switch allows you to flip between mains powered (the battery charges while connected) or battery powered. The second tone control has sneakily been replaced with a rotary encoder to navigate the menu. The major/minor flip switch lets you quickly and easily change between major and minor scales, while keeping the root notes highlighted in the same colour (red). All in all, it's pretty exciting.<br />
<br />
Just a shame there was no time to get the router out and finish it off fully!<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-8518236590223265382017-05-04T00:33:00.000+01:002017-05-04T00:33:03.700+01:00Unity and TextMeshProI guess, since <a href="https://blogs.unity3d.com/2017/03/20/textmesh-pro-joins-unity/">Unity "bought up" TextMeshPro</a> and made it available for free with version 5.5 of Unity back in February, I'm probably one of the last to jump on board and give it a go.<br />
<br />
Between messing about with Arduino, guitars and hall sensors, I've been playing about with Unity a bit too. I tried getting away from the whole "create the next awesome 3d blockbuster" and am looking to create simple, mobile-friendly games that should provide 10-30 minutes playtime. A bit like the old ZX Spectrum games of yesteryear (though I shudder to think how many hours I spent trying to get Mr Head and Mr Heels to work together properly in 1986).<br />
<br />
Anyway, a simple 2D turn-based strategy game is currently in development (although it'll probably never be anything more than a vehicle for trying out different coding techniques,so don't expect a release date any time soon) and - like most game framework prototypes - is filled with coder art, empty graphics placeholders and <i>lots</i> of console debug messages.<br />
<br />
As anyone who has worked with Steve will know, it's the graphics and look-and-feel that can elevate something pretty mediocre into something amazing. And that's what happened last night, when I swapped out my simple Unity GUI Text for some TextMeshPro glyphs.<br />
<br />
Here's an example. It's dynamic text.<br />
At certain points in the turn-based game, your turn can be interrupted by your opponent (a classic example of this is when a player leaves a character on "overwatch" - meaning "if anything crosses your field of view, shoot at it immediately").<br />
<br />
Whenever a player is interrupted, we wanted a nice big on-screen display to inform them of what was going on. A simple 72-point dynamic text box just didn't seem to cut it. But swap out the text object for a TextMeshPro mesh and suddenly things look a lot prettier, very quickly<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidCCKewLLoYsn8TjPIVOE0GRMx2iLptezk4bXq16fhlpvcmuZYR5bPRVKtT9kKp49145Q5UFD2V0gT-SwMofMNE09FCBPzW6SMFT5Tj_rE5ZYoBJGaF59HhmZ6z3usrQhxMOGGm-hADXGa/s1600/textmeshpro.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="145" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidCCKewLLoYsn8TjPIVOE0GRMx2iLptezk4bXq16fhlpvcmuZYR5bPRVKtT9kKp49145Q5UFD2V0gT-SwMofMNE09FCBPzW6SMFT5Tj_rE5ZYoBJGaF59HhmZ6z3usrQhxMOGGm-hADXGa/s320/textmeshpro.png" width="320" /></a></div>
<br />
Just simple stuff like putting a gradient on some text can make it look so much better than a simple flat font. And TextMeshPro lets you define up to four gradient points (to get that shiny metallic look on each letter if you like).<br />
<br />
Also, when you scale a text object up in Unity (for example, to create a "bounce-up" effect) even TTF fonts become pixellated at larger sizes. No such problem with TextMeshPro!<br />
<br />
It's such a massive improvement on the original UI Text, it's no wonder Unity bought it up and now offer it for free as a Unity Essential. It's a great asset and a quick and simple way of making even boring stuff look pretty onscreen!<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-32314277616677040452017-05-03T01:06:00.001+01:002017-05-03T01:06:39.326+01:00Assembling the light-up guitarThings have slowed down a little bit, as assembling the guitar has proved to be a little more difficult than designing the electronics and writing the firmware!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOI2SQFZsh2b5wsuQpYjNbiDF2zo-Tn9EbN63NLR8RLmOZCqaURLLL0ktW5-mVWBnf-aVI-WqLfnSaQn5UXDiKxkSbTWLWaZ0Xd3quxEGbpS1cFLWp7BRhsNMBi7dA7pldjsuxYy77PUYO/s1600/20170501_114533.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOI2SQFZsh2b5wsuQpYjNbiDF2zo-Tn9EbN63NLR8RLmOZCqaURLLL0ktW5-mVWBnf-aVI-WqLfnSaQn5UXDiKxkSbTWLWaZ0Xd3quxEGbpS1cFLWp7BRhsNMBi7dA7pldjsuxYy77PUYO/s320/20170501_114533.jpg" width="320" /></a></div>
<br />
The first thing to do was make a note of the existing connections - just in case anything accidentally gets snipped or un-soldered. Then we whipped the scratch plate off and set about it with a Dremel and a small routing bit to make a window for our LCD display.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUsz_3tpOgVSuTquAbOJs4-f552QzqmT45iHnX5tkU1yBJbVMxjf-pwimNyXreRJBZMh0EFeGifd0NDOvVgGE6wz-cw7wa2yZXVbYz55gksuLXQxbEgiXPFgeE_0hYe9ZVrXRnuN3ImJbf/s1600/20170501_115305.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUsz_3tpOgVSuTquAbOJs4-f552QzqmT45iHnX5tkU1yBJbVMxjf-pwimNyXreRJBZMh0EFeGifd0NDOvVgGE6wz-cw7wa2yZXVbYz55gksuLXQxbEgiXPFgeE_0hYe9ZVrXRnuN3ImJbf/s320/20170501_115305.jpg" width="320" /></a></div>
<br />
That triple-layered plastic made a right mess! Copious amounts of hot glue ensures our screen shouldn't be going anywhere, any time soon.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2rsmJ9p_gu-RePObgp9N_4II36EpWZHdfkYJEWylTHUV0OJYzBDCIv4V6DVoHSOiUziciD-y7qJTR9euXneMLP2nGep02LnIdN3ecRiT9dzy-JdMp_ajJ6JHCH0SB3e0yOMUY1wyoUlYd/s1600/20170501_163913.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2rsmJ9p_gu-RePObgp9N_4II36EpWZHdfkYJEWylTHUV0OJYzBDCIv4V6DVoHSOiUziciD-y7qJTR9euXneMLP2nGep02LnIdN3ecRiT9dzy-JdMp_ajJ6JHCH0SB3e0yOMUY1wyoUlYd/s320/20170501_163913.jpg" width="320" /></a></div>
<br />
We took out the second tone knob (the one that's almost always left fully dialled to ten) and replaced with a fixed-value resistor. After all, the tone knob on these cheap Strat copies doesn't boost any frequencies, it can only choke them. And what's a Strat if it's not got that bright, jangly sound? Why would you want to dial that down anyway? So off came the pot, to be replaced with a fixed 220k resistor. In it's place we fitted a rotary encoder.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgwHVu4480sNn_xECy-UdcnxMyAbGCwo4HlQRScgrHQqwITfXVuS2G-U4_xnlXWWjo_UYIQRfMK7CYjDzRaBogVSIzRo5cMAecZb5HWA7XZR8KhIZKXZEenNXm9R-I7ggoqcx5vny9ZEw0/s1600/20170501_175610.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgwHVu4480sNn_xECy-UdcnxMyAbGCwo4HlQRScgrHQqwITfXVuS2G-U4_xnlXWWjo_UYIQRfMK7CYjDzRaBogVSIzRo5cMAecZb5HWA7XZR8KhIZKXZEenNXm9R-I7ggoqcx5vny9ZEw0/s320/20170501_175610.jpg" width="240" /></a></div>
<br />
After cutting down the shaft and putting the tone cap back on, you'd never even know it had been modified if you weren't looking closely! Also added a stereo jack socket which we'll use to connect to the power/ground/RX pins of the microcontroller to allow data to be pushed into it at a later date.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6S5r7OpUzIS1NwBiOu_G7XNgiGzq7zpSZ6gLNgOUKnDyw8emXEwRZ6ANh43uO_NbFdghu56FO_2jPiEY0i6EtZ6k6XgGLs8L2dRZE6Hbu5xjRFTEcfsv9lUTYLmsGH11aFX7_PVnww-3l/s1600/20170501_175622.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6S5r7OpUzIS1NwBiOu_G7XNgiGzq7zpSZ6gLNgOUKnDyw8emXEwRZ6ANh43uO_NbFdghu56FO_2jPiEY0i6EtZ6k6XgGLs8L2dRZE6Hbu5xjRFTEcfsv9lUTYLmsGH11aFX7_PVnww-3l/s320/20170501_175622.jpg" width="320" /></a></div>
<br />
Now for a really important part - lining up the neck and bridge. On a previous build, we missed this step out; fixed the neck, drilled the holes for the bridge, installed the tremolo system and found that the bass strings were almost in the middle of the neck and the high treble strings were fretting out, as the high E string was barely on the neck!<br />
<br />
No chance of that happening this time around - some lengths of cotton allowed us to see where the strings would end up, to ensure we got the bridge in the right position this time.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaEveTLOIDo2j-fxVZFkB7nBQEZIoV0Z1oMzLh_1I7rLgzcabLjXZP-NdycvmmdhZ6tGHV2l79LbT088C4wQD_vmgtpZFCy7j3scrmfgl41qo0JL3zugM0pd5mmYV4gHi7Lu9C1MGq2wkK/s1600/20170502_213713.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaEveTLOIDo2j-fxVZFkB7nBQEZIoV0Z1oMzLh_1I7rLgzcabLjXZP-NdycvmmdhZ6tGHV2l79LbT088C4wQD_vmgtpZFCy7j3scrmfgl41qo0JL3zugM0pd5mmYV4gHi7Lu9C1MGq2wkK/s320/20170502_213713.jpg" width="320" /></a></div>
<br />
You can see where, under the scratchplate, we had to do some extra routing to create a cavity for the LCD screen and 3000mAh lipo battery. A quick test fit made sure that everything was going to fit inside the cavities when assembled.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6-myX_m2iVeOZDCpYikRhda2xnMOyKiVUot0F3o-EMYNdZxArfwzbhNs0Pnn6vnYXlVdN72SRIMeYEaR8QsReCyTGmdLHsEsEO0dQX3eookZqFIoYIeuHvUh6NZG6tlWR5nbFJAiDCHJz/s1600/20170502_221306.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6-myX_m2iVeOZDCpYikRhda2xnMOyKiVUot0F3o-EMYNdZxArfwzbhNs0Pnn6vnYXlVdN72SRIMeYEaR8QsReCyTGmdLHsEsEO0dQX3eookZqFIoYIeuHvUh6NZG6tlWR5nbFJAiDCHJz/s320/20170502_221306.jpg" width="320" /></a></div>
<br />
The firmware isn't quite finished yet (need to add in a couple more fancy patterns and recreate our earlier <a href="http://nerdclub-uk.blogspot.co.uk/2016/02/guitar-eq-on-neck.html">volume meter effect</a>) so we're not quite ready to close it up and fit the strings. But that time is coming. It's coming real soon......<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-26795140771575853212017-04-29T01:10:00.000+01:002017-04-29T01:10:09.925+01:00A quick thanks to Jason for the MIDI-in schematic (and opto-isolators)If you're having trouble with your MIDI (as we were, trying to get our input working through an opto-isolator) there's only one person with the wealth of experience worth asking and that's <a href="http://hotchk155.blogspot.co.uk/">Jason</a>. He quickly identified that the random spare-part left-over opto-isolator we were trying to use for our MIDI-In simply wasn't up to the job.<br />
<br />
Apparently there isn't a great range of opto-isolators to work from for handling MIDI. Most are simply used as switches. And while its' true that serial data is just flipping a switch really quickly, we need an opto-isolator that can switch on and off quickly enough to keep up with the data rate.<br />
<br />
Jason kindly gave us a couple of 6N138 isolators and an updated schematic to work from.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho9F58MLmNQ4YCoI5p5RTZYxobS1qcD5p209bK0wlS0qmyz9UMACbS30w763w-gWA7ia_cnyEWyFxyRERnvN77T8gps7YWxv0FHtnlu9grA9v1SsubeX0WRL6LRvsU7BZP_Y65Rrgb45Lv/s1600/Image7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="224" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho9F58MLmNQ4YCoI5p5RTZYxobS1qcD5p209bK0wlS0qmyz9UMACbS30w763w-gWA7ia_cnyEWyFxyRERnvN77T8gps7YWxv0FHtnlu9grA9v1SsubeX0WRL6LRvsU7BZP_Y65Rrgb45Lv/s320/Image7.png" width="320" /></a></div>
<br />
The isolator neatly inverts the MIDI logic (in MIDI a zero is a high signal and a one is low) as well as holding the RX line high when idling - just how all good UART peripherals like it.<br />
<br />
With this slight alteration we got our MIDI In working reliably - with the added bonus that we can now hook up and MIDI source, powered from anywhere; the isolator means there's no need for a common ground or similar reference. Which is just as well - to date we've tested our light-up guitar fretboard by loading MIDI files on the laptop and playing them through a usb-to-midi interface conntect to a second usb port. This means the serial port and the MIDI signal have both been generated from the same source.<br />
<br />
But the ultimate test of our MIDI capabilities will be when we plug the guitar into a portable keyboard, press a key and see which of our frets lights up!<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-43732462691842414632017-04-28T00:22:00.000+01:002017-04-28T00:22:09.364+01:00Displaying MIDI notes as fret positionsTo convert scales and patterns into LED combinations we've made quite extensive use of spreadsheets. Boring old boxes of numbers have made displaying patterns of dots much easier.<br />
<br />
The guitar fingerboard LEDs have been wired starting at fret 15, low string E, passing through each string of the 15th fret (E, A, G, D, B, E) then continuing with LED number six representing 14th fret low E, LED seven is the 14th fret A string and so on.<br />
<br />
So we first drew up our patterns in a spreadsheet, coloured the boxes (to make them easier to identify) then transferred these patterns into byte arrays.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-G9a8bF2LYyNvW30sCE-o1xGKkITu8-dvC3urg6gFjrqcKyM2VwBApNYC6F23T_v3X6MQeRF8WfHnkjsIQRMe5scUhSa0XK_HWWtXDqMyczhvkma5-HCi9EWrb7e5kNgE1T-90nDf2EQM/s1600/scales.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-G9a8bF2LYyNvW30sCE-o1xGKkITu8-dvC3urg6gFjrqcKyM2VwBApNYC6F23T_v3X6MQeRF8WfHnkjsIQRMe5scUhSa0XK_HWWtXDqMyczhvkma5-HCi9EWrb7e5kNgE1T-90nDf2EQM/s320/scales.png" width="320" /></a></div>
<br />
Anyone familiar with guitar scales might see the pentatonic minor box one starting with the A root note on the fifth fret in the image above.<br />
<br />
After giving each note of the scale a "colour palette index", we stored each pair of strings in a single byte. So the low E string is the upper nibble of the first byte, the A string the lower nibble of the first byte, the D string is the upper nibble of the second byte and G is the lower nibble. The B string is the third byte upper nibble and lastly the high E string is represented by the lower nibble of the third byte.<br />
<br />
So reading the scale chart from the bottom right corner, reading upwards, then from right to left, our fifteenth fret LED colour index values are 7(E), 2(A), 0(D), 0(G), 4(B), 7(e) (zero represents no dot to be displayed). The fourteenth colour index values are 0, 0, 5, 1, 0, 0.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBqtJ3WHf99PXLJ7KIxzk4qX70PwmrqRU7jz9BxnkS2Goa-L6lE4AHnRuXmVvt69PFQHUataisKkC2i2ky29HMpu6VOTqb3ZNKxIwSv69GeoUf9x4n5Q_gKnIp9TSo1Pi7LDUXtCIkqIh3/s1600/fretnotes_code.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBqtJ3WHf99PXLJ7KIxzk4qX70PwmrqRU7jz9BxnkS2Goa-L6lE4AHnRuXmVvt69PFQHUataisKkC2i2ky29HMpu6VOTqb3ZNKxIwSv69GeoUf9x4n5Q_gKnIp9TSo1Pi7LDUXtCIkqIh3/s320/fretnotes_code.png" width="320" /></a></div>
<br />
You can see that by splitting each fret/string position into a single half-byte 0-F, when storing the values in hexadecimal, we can actually "see" the dots in the code. The first three byte values, for the six strings on the fifteenth fret are 0x72, 0x00, 0x47. The correlates directly with the dot patterns in the spreadsheet 7(E), 2(A), 0(D), 0(G), 4(B), 7(e)<br />
<br />
Using a similar technique, we listed the note values of every fret position for every string.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSSws_zJnqXwlAgW62Gl4aGXrSK_kMXew1QIVaPxHVl8LgOK_sSujCnTzT-D171X47O4EBsDiwmh2ZcBNb8ZFtiiiU9muWIiuWnpFPrywfb5nifDZ17uru_ntzH_Dso-R69stkmvvBymfY/s1600/fretnotes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="262" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSSws_zJnqXwlAgW62Gl4aGXrSK_kMXew1QIVaPxHVl8LgOK_sSujCnTzT-D171X47O4EBsDiwmh2ZcBNb8ZFtiiiU9muWIiuWnpFPrywfb5nifDZ17uru_ntzH_Dso-R69stkmvvBymfY/s320/fretnotes.png" width="320" /></a></div>
<br />
Then after a quick Google look-up, recorded the MIDI note value for each note at each fret position, for each string. Recording the MIDI values in the same order we've wired the LEDs, our array of values begins 31, 36, 41, 46, 50, 55<br />
<br />
These are the midi note values for G2, C3, F3, Bb3, D4, G4 which happen to be the notes on the guitar at the fifteenth fret.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWCRbEy5XAENnzepKk2-2SbLemirruXcoAIs7u-2pSB7kxzWMJAUq7K15jGYMgFS3qhgy8zA-bFWDXh-2X9CTxIHkGiEH2hQUSnu-C86R3W7mxAR-8kNZKPp6G1hOzH4UZVCr33Qu4wuLE/s1600/Image11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="81" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWCRbEy5XAENnzepKk2-2SbLemirruXcoAIs7u-2pSB7kxzWMJAUq7K15jGYMgFS3qhgy8zA-bFWDXh-2X9CTxIHkGiEH2hQUSnu-C86R3W7mxAR-8kNZKPp6G1hOzH4UZVCr33Qu4wuLE/s320/Image11.png" width="320" /></a></div>
<br />
Since the seven LED is at the low E string position on the 14th fret, our array of MID note positions continues 30, 35, 40, 45, 49, 54. We continue recording each note value in our MIDI array, all the way up from fret 15 to fret zero (open strings).<br />
<br />
<div style="background-color: black; color: lime; font-family: "courier" , "courier new" , , "fixed" , sans-serif , "sans"; font-size: 8pt; padding: 20px;">
const byte midi_notes[] PROGMEM = {<br />
31,36,41,46,50,55,<br />
30,35,40,45,49,54,<br />
29,34,39,44,48,53,<br />
28,33,38,43,47,52,<br />
27,32,37,42,46,51,<br />
26,31,36,41,45,50,<br />
25,30,35,40,44,49,<br />
24,29,34,39,43,48,<br />
23,28,33,38,42,47,<br />
22,27,32,37,41,46,<br />
21,26,31,36,40,45,<br />
20,25,30,35,39,44,<br />
19,24,29,34,38,43,<br />
18,23,28,33,37,42,<br />
17,22,27,32,36,41,<br />
16,21,26,31,35,40 <br />
};</div>
<br />
Now when we receive a MIDI note value, we can loop through this array to work out which LED corresponds to that note and light it up (or, if it's a note off message, turn it off).<br />
<br />
<div style="background-color: black; color: lime; font-family: "courier" , "courier new" , , "fixed" , sans-serif , "sans"; font-size: 8pt; padding: 20px;">
void addNoteToFretboard(byte note, int col_index){<br />
// loop through all the midi note values in the array<br />
// and wherever there's a match, light up that LED with<br />
// the appropriate colour index<br />
byte p;<br />
CRGB c = RGBColour(col_index);<br />
<br />
for(int i=0; i<96; i++){<br />
p = pgm_read_byte(&midi_notes[i]); <br />
if(p==note){<br />
led[i] = c;<br />
}<br />
}<br />
FastLED.show();<br />
}</div>
<br />
<div style="text-align: center;">
<i>(in the example above, we have a function into which we pass an index number and it returns a CRGB value for a specific colour)</i></div>
<br />
When hooked up to a MIDI source, the result looks something like this:<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/3-kNjNeTOqs?rel=0&controls=0&showinfo=0" width="490"></iframe>
</div>
<br />
<br />
That's the opening sequence to Metallica's Enter Sandman. If you already know how to play the song, you'll recognise the familiar patterns around the 5th, 6th and 7th frets. For some notes, more than one dot appears. That's because there's more than one place where you can fret the note. Matching colours mean "it's the same note" so you'll see a red dot appear at both the 6th fret low E string and 1st fret A string at the same time. You can choose whether to play the riff on the 6th/7th fret, or on the 1st and second - it's up to you!<br />
<br />
At the minute there's no differentiation between channels - you need to turn channels on and off using your MIDI sequencer - so when we wound the song on a bit and played it complete with distorted guitar, bass notes and drums/hi-hat, it looked a little bit crazy!<br />
<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/g373Re2o0lA?rel=0&controls=0&showinfo=0" width="490"></iframe>
</div>
<br />
<br />
However, as a performance piece, we think it looks pretty cool too!<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-91366050426803947002017-04-27T00:20:00.000+01:002017-04-27T00:20:14.721+01:00Messing about with MIDI and RealTerm vs PuttyWhile our guitar neck glue is going off, and since we've pretty much got most of the LED goodness working (that'll probably have to be demonstrated in a later post, once everything has been stuck together) we thought the intervening time could be spent adding some extra cool functionality to our light-up guitar.<br />
<br />
To date it allows you to select pentatonic or diatonic scales in any key, include extra notes (such as the "blue note" and flatted/major thirds in the minor pentatonic, major fourths in the major pentatonic and so on). You can also have it display all the major C.A.G.E.D chord shapes in any key too (we really should have made a demo video before this post!)<br />
<br />
EDIT: made a quick demo video. Here you go -<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/eeIb-dtCOuI?rel=0&controls=0&showinfo=0" width="490"></iframe><br /></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<i>In "offline mode" you can display different scales and CAGED chord shapes in any key. A simple flick switch will allow you to quickly change between major and minor (for those pesky I-to-IV and IV chord changes in a 12-bar song!) And, of course, there are some fancy patterns just for show as well!</i></div>
<br />
Now most guitarists who know how to play guitar recognise that it's not just a Guitar Hero clone - where frets light up and if you match them with your fingers, you'll play a tune. By lighting up a particular scale (and different target notes from that scale) you can improvise blues-based solos more easily just by wandering between the dots. But for anyone wanting to play along to their favourite song, we figured it'd be a massive amount of work creating some software that allows you to input tab in such a format that you can then send to the guitar to get the frets to light up.<br />
<br />
So when MIDI was suggested, we figured.... hmmmm.<br />
It's always tricky knowing which of a possible five places you should place a dot for a specific note on a guitar. But if we could take the incoming MIDI signal and simply light up all possible alternatives, it would leave the player to decide which fret they found most comfortable to reach for (instead of some crazy algorithm going rogue and forcing you to whizz up and down the guitar neck at lightning speed!)<br />
<br />
So we needed to get to grips with handling MIDI in signals (not the infinitely easier generating MIDI out messages). The first thing was to hook up a usb-to-MIDI device so we had an independent way of generating MIDI messages.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXWtF9vyWnn3vgwYAdrvXpr_ccEB3OGAHJupC67H1bL1jXQsQ1c8B5kn_CCJgRC8uXgCR6T1Z3d1mpFLOyFwMALQq1MmDChRSTGACJgJp4HIyUY36OBpbegth2z5BWq4CVM_-2L4BtlwLW/s1600/estudio-usb-to-midi-interface-1024x1024.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXWtF9vyWnn3vgwYAdrvXpr_ccEB3OGAHJupC67H1bL1jXQsQ1c8B5kn_CCJgRC8uXgCR6T1Z3d1mpFLOyFwMALQq1MmDChRSTGACJgJp4HIyUY36OBpbegth2z5BWq4CVM_-2L4BtlwLW/s320/estudio-usb-to-midi-interface-1024x1024.jpg" width="320" /></a></div>
Now MIDI uses inverted logic (zero is represented by a HIGH signal, a one is a LOW signal) but other than that, it's simply serial data sent at a funny baud rate. 31250 bps to be precise.<br />
<br />
So we downloaded a free MIDI sequencer (<a href="http://www.anvilstudio.com/">Anvil Studio</a> looks gnarly and draws slowly, but it can create, edit and play MIDI files to a MIDI device, so it was good enough for this test) and grabbed something a bit more sophisticated than our usual serial favourite Putty - a little program called <a href="https://realterm.sourceforge.io/">RealTerm</a>.<br />
<br />
Putty is fine for sniffing data, but RealTerm makes playing with serial data a doddle. Not only can it display the raw incoming data, but it has a myriad of options for decoding it too. We went with hex[space] so that we could visualise the MIDI values in hex, instead of trying to decode non-printable ASCII characters (as Putty likes to do). It's much easier to see what a value like 0xB4 means than a checkerboard patterned glyph!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRxBoxe3cUo8_-voGEdCc13HcHSQ1LRxFCgvjxWWOu3XaETfMEBB7mE3yFurFoQw_PDtWNJwrBF8-fdIczVsRLDP0mGVTUILR1tGFTKyzZhDOVi3jYR_DucVjnKznOGoawjcJR8IxVMiIS/s1600/Image1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRxBoxe3cUo8_-voGEdCc13HcHSQ1LRxFCgvjxWWOu3XaETfMEBB7mE3yFurFoQw_PDtWNJwrBF8-fdIczVsRLDP0mGVTUILR1tGFTKyzZhDOVi3jYR_DucVjnKznOGoawjcJR8IxVMiIS/s320/Image1.png" width="320" /></a></div>
<br />
<br />
We hooked up our MIDI out to a socket and introduced an opto-isolator to both isolate and invert the MIDI signal from our usb-to-MIDI device. The circuit was something similar to this one (with a few resistor values changed to match values we had lying around)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoSqc_QjjQGEAmjkeY8OPmgYMkknVknpCUS86D_4z3vkeNlgzR6vZJiTYu5EDbqENMpU42mXmVVp7pcH0z5G6U14gFdgS5a-9c4nrQ3BdT2L1b4GoExbA_5TUcEwTvToTLpqTPIzoaf9ER/s1600/midi_in.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoSqc_QjjQGEAmjkeY8OPmgYMkknVknpCUS86D_4z3vkeNlgzR6vZJiTYu5EDbqENMpU42mXmVVp7pcH0z5G6U14gFdgS5a-9c4nrQ3BdT2L1b4GoExbA_5TUcEwTvToTLpqTPIzoaf9ER/s320/midi_in.png" width="320" /></a></div>
<br />
The strange this is, we got no data from the circuit. Absolutely nothing.<br />
After fiddling about for about an hour and getting nowhere, we took a bit of a risk. Since the MIDI signal was being generated by a laptop usb port, we figured it wouldn't be more than 5v. So we connected the MIDI out directly to our serial in.....<br />
Instead of a puff of blue smoke, we got serial data appearing in RealTerm. Success! (although we did have to invert the signal, with midi pin 4 connected to ground and midi pin 5 to our serial RX.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdZVN4GXyJB3mFhQRiZ90CUxHqdAOV6QALzGm0a83OpF0sJZKfseC46Yp5sbvfhZEp-4FFjwODn7iacQ_5gIm4O8IQQs8PRuf5IjMdHAcXJsEoSqTA3AtV3_OkzJMS4IzIal2UX-H3c2V6/s1600/midi_capture.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="136" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdZVN4GXyJB3mFhQRiZ90CUxHqdAOV6QALzGm0a83OpF0sJZKfseC46Yp5sbvfhZEp-4FFjwODn7iacQ_5gIm4O8IQQs8PRuf5IjMdHAcXJsEoSqTA3AtV3_OkzJMS4IzIal2UX-H3c2V6/s320/midi_capture.png" width="320" /></a></div>
<br />
It might look like gibberish, but it's pure, raw, MIDI data!<br />
The excellent MIDI resource site (<a href="https://www.midi.org/specifications/item/table-1-summary-of-midi-message">https://www.midi.org/specifications/item/table-1-summary-of-midi-message</a>) gave us an easy way of decoding the data - and thanks to RealTerm's hex view, we could even do it onscreen!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtItCuQDt4qOvKm5rLysL1hcemVp-hzL4HoycrSglFQhXKdCW-0xksambu6PpR8Xo588NRcs8M2b-HAbNdZVjOi4V_nXwV1ivwjPXwvqFhL_haxT0Jo7_m6O_1lM97yA1Tlgps9duEyM6Q/s1600/Image6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtItCuQDt4qOvKm5rLysL1hcemVp-hzL4HoycrSglFQhXKdCW-0xksambu6PpR8Xo588NRcs8M2b-HAbNdZVjOi4V_nXwV1ivwjPXwvqFhL_haxT0Jo7_m6O_1lM97yA1Tlgps9duEyM6Q/s320/Image6.png" width="316" /></a></div>
<br />
The MIDI messages we're interested in are the note on and note off ones. All other messages can be discarded. Most (but not necessarily all) MIDI messages arrive in three-byte packets. Luckily, the MIDI format also makes it really easy to decode.<br />
<br />
The first byte of a MIDI message "packet" always has the first/MSB set to one. The first byte is also the "command" byte, telling us what action is to be carried out. Subsequent bytes, containing data values always have the first/MSB bit cleared (set to zero). So it's dead easy to find the start of each message - just read in bytes from the serial port and as soon as a byte has it's first bit set, you know you're at the first byte of a new packet.<br />
<br />
We can see from our received MIDI data, we get a repeating "B" message, every three bytes at the very start. This is the MIDI device sending a "control change" message for each channel being used by our MIDI sequencer (since 0x0B in hex is 1011 in binary, which matches the pattern for "control change" in the MIDI specifications). The second character of the first byte is the channel number (from 0-15). The following two bytes are information about how the controller is set up (volume, left/right balance etc).<br />
<br />
For the messages we're interested in, we can just look for a message beginning with 9 (note on) or 8 (note off). If we dump our serial data capture into notepad and split the data into three-byte packets, it becomes much easier to read.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnKnxV1jSrKrxZQrmhfVMo3XTX3175b9G5GCLAMKpVNIONW1YT7xeBzaWI8ufae9ow3BXIQdFIHvM_wvqhARn3Fba4D1SoEUKzQqnplEzLvqmLWMrlI4K9YDLkS99Nou_dmA5K1wwceAmG/s1600/midi-notes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnKnxV1jSrKrxZQrmhfVMo3XTX3175b9G5GCLAMKpVNIONW1YT7xeBzaWI8ufae9ow3BXIQdFIHvM_wvqhARn3Fba4D1SoEUKzQqnplEzLvqmLWMrlI4K9YDLkS99Nou_dmA5K1wwceAmG/s320/midi-notes.png" width="320" /></a></div>
<br />
Our first musical MIDI instruction is 90 28 7F<br />
<br />
This decodes as:<br />
<br />
<ul>
<li>9 = note on</li>
<li>0 = channel zero</li>
<li>0x28 = decimal 40 = octave1 note E</li>
<li>0x7F = full volume</li>
</ul>
<br />
<br />
(note that normally we'd expect anything "full" to be 0xFF but in MIDI messages, the data bytes always have a leading bit zero, so the maximum value we can achieve is 0x7F)<br />
<br />
And as anyone who has ever played "Enter Sandman" on a guitar will tell you, the first note struck is always the low E string. Unless you're Bill Bailey....<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/j2n0cd2kUVQ?rel=0&controls=0&showinfo=0" width="490"></iframe><br /></div>
<br />
Things look quite promising so far - so what's next?<br />
The next message is 90 34 7F. That's another full-volume, note on message, this time for note 0x34 (which is decimal 52, or octave 2 note E)<br />
<br />
Following that comes 90 37 7F. Another full-volume note-on message, for note 0x37 (decimal 55, or octave 2 note G).<br />
<br />
Now comes our first note off message - 80 28 50. It's that first note, low E. Which tells us that all three note have been ringing out until this time. Again, anyone familiar with the opening bars of Enter Sandman (that bit with the clean guitar tone at the start) will recognise that this is the case.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMl2SQQfBWlubaGs_Zlns8DSnkadLlGjQM4MFnsbxb5O2gs-5jp4c3YYBhexfY8VMNTyguXD8IsOA7cIGs64Xlt21KfN9dh84whgtb1RdgWOTjarn7pcc7Lhb6k2UolQGFgbAtmNFNMNa8/s1600/Image2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="75" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMl2SQQfBWlubaGs_Zlns8DSnkadLlGjQM4MFnsbxb5O2gs-5jp4c3YYBhexfY8VMNTyguXD8IsOA7cIGs64Xlt21KfN9dh84whgtb1RdgWOTjarn7pcc7Lhb6k2UolQGFgbAtmNFNMNa8/s320/Image2.png" width="320" /></a></div>
<br />
The reason the low E stops is because, when playing this on a guitar, you normally fret the low E string to play the two-note decending riff. Sure enough, the next messages representing the next two notes are<br />
<br />
90 2E 7F<br />
90 2D 7F<br />
<br />
Even without decoding these to work out what the notes are, we can see that there's a half-step change in tone; whatever note 0x2E is, the next note is 0x2D, one half-step/semi-tone below it. Being familiar with the tune, we already know that this is correct!<br />
<br />
What's interesting to note (although, to be honest, maybe not unless you're a bit of a music nerd) is that if these notes had been generated by a guitar, there'd be a note off between the decending tones. That's because both notes are played on the same string. There's no way that you could get both notes playing, unless they were played on two different strings (and just about everyone who's ever played Enter Sandman on a guitar normally plays it by moving down a fret on the same string). In our MIDI playback, somehow both notes are ringing out at the same time. It's only a few bytes further on that we see the note off message for 0x2E, followed a few bytes later by the note off message for 0x2D.<br />
<br />
Our screenshot belies the fact that these on/off messages can appear one after the other, almost instantaneously. But it's just interesting to note that this tune was probably played out on a MIDI keyboard (where it's quite possible to sustain two notes next to each other) and the player's inability to get their fingers out of the way quickly enough has lead to some notes sustaining for slightly longer than they would if played on a different instrument.<br />
<br />
Anyway, all that aside, we've now got a way of reading incoming MIDI messages and decoding them into note on and note off messages. In the next post, we'll look at how we can use this data to display the notes on our guitar neck....<br />
<br />
<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-70165933808437357902017-04-26T22:27:00.001+01:002017-04-26T22:27:06.625+01:00Fixing the light-up guitar neckOne of the great things about having moved into the workshop bungalow is that I can try out ideas quickly and easily and designing through repetition is a doddle. In the past, I'd have to plan what I wanted to do/make then visit the unit, make it, bring it home and just hope it worked - at least until I could get to the unit a second time.<br />
<br />
Now I can design something and whizz it out on the laser in minutes, not days. So when I got the RGB LEDs soldered up for Keith's guitar, I could play about with a few designs for sticking the fingerboard to the guitar neck.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1bMbC8BFOs-0l0mqmZ7vblqKLNgk3mrGmM-ipz75Zn7-3bJT4_LIY0G-6yIEesSCQhD_UqNqgrutIfQjVR7l7Ul3ay57_WVCyZhfDsuq87-2FOmX5gn11xjFeRRgKqpMtAXkY3SpGKTRW/s1600/20170423_203730.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1bMbC8BFOs-0l0mqmZ7vblqKLNgk3mrGmM-ipz75Zn7-3bJT4_LIY0G-6yIEesSCQhD_UqNqgrutIfQjVR7l7Ul3ay57_WVCyZhfDsuq87-2FOmX5gn11xjFeRRgKqpMtAXkY3SpGKTRW/s320/20170423_203730.jpg" width="320" /></a></div>
<br />
<br />
If a design didn't quite work or fit properly, it took just moments to knock out another, amended version. It took maybe three or four goes to make some mdf standoffs to fit around the LEDs on the reverse side of the fingerboard<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUqBfTB1PIryKu83l3pYqf9tXmuT_-ertMiYGnQMXrFn_IXPtGxrQkju9YasgYxoIunwKJDeMf8Me_QtmaNTqIJBO3gMHy5HcuHjC64Om-uIkdjUCt1w4XzDjfdK3mPFvhrmPlk6ib6zRI/s1600/20170423_212312.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUqBfTB1PIryKu83l3pYqf9tXmuT_-ertMiYGnQMXrFn_IXPtGxrQkju9YasgYxoIunwKJDeMf8Me_QtmaNTqIJBO3gMHy5HcuHjC64Om-uIkdjUCt1w4XzDjfdK3mPFvhrmPlk6ib6zRI/s320/20170423_212312.jpg" width="320" /></a></div>
<br />
Because I'd positioned the LEDs by hand, rather than using a template or a PCB (where the position/location of the LEDs is fixed) they didn't exactly line up with the drawing I made the mdf template from. But it only took a bit of fiddling about to get a working fit.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT-CXE4dJwMWSSnkMn9jYnZT02tpWkRCnz9yTJBSC-b6NBglrrKLXFlCkzKjycgYEOuJzc8DGm6tdL7UBYQFvV6yAqDgtnm7YJLMrWlx5jp2a0ntUAc0FaOmxgoJOo4pJ2YAf9rwm-qAvH/s1600/20170423_204212.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT-CXE4dJwMWSSnkMn9jYnZT02tpWkRCnz9yTJBSC-b6NBglrrKLXFlCkzKjycgYEOuJzc8DGm6tdL7UBYQFvV6yAqDgtnm7YJLMrWlx5jp2a0ntUAc0FaOmxgoJOo4pJ2YAf9rwm-qAvH/s320/20170423_204212.jpg" width="320" /></a></div>
<br />
Next we used some epoxy glue to fix one side of the mdf to the fingerboard, and clamped it down to a board to get the fingerboard as flat as possible.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd7Idex2gdADDQuWHkTh03zdPrwlJDUJa9rKamQdVrYOg0ITnK7bsdSIstaT4KrBRXgl79JyfWFHt4uAJn2cr-RGv0WihesXauiDo9YFxf2cDIb0b8F-4Fa0gkBPN8bnsiuTW0p0xuhGej/s1600/20170425_170954_HDR.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd7Idex2gdADDQuWHkTh03zdPrwlJDUJa9rKamQdVrYOg0ITnK7bsdSIstaT4KrBRXgl79JyfWFHt4uAJn2cr-RGv0WihesXauiDo9YFxf2cDIb0b8F-4Fa0gkBPN8bnsiuTW0p0xuhGej/s320/20170425_170954_HDR.jpg" width="320" /></a></div>
<br />
Although not fully cured after a few hours, the glue had set enough to allow us to move the fingerboard and glue it to the guitar neck.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2Cbb9JeuW4swMgsXYv4SeCI__jOBKPEOGKlgm4GwkWKFjF0_5eOGNKSEi8EbFYS_h9RZVCwqLmgOd3tcxQ0CZBQIqAvL57F-3ssov5vZy700049GJHUc8i7dTc4utZRZgvNGDgWZQhz48/s1600/20170425_212059.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2Cbb9JeuW4swMgsXYv4SeCI__jOBKPEOGKlgm4GwkWKFjF0_5eOGNKSEi8EbFYS_h9RZVCwqLmgOd3tcxQ0CZBQIqAvL57F-3ssov5vZy700049GJHUc8i7dTc4utZRZgvNGDgWZQhz48/s320/20170425_212059.jpg" width="320" /></a></div>
<br />
Now it's just a case of leaving overnight and see how things look in the morning! We used epoxy rather than PVA since many of the luthiers recommend it for it's stability. Apparently PVA can shrink over time, which might cause the fingerboard to pull or bow. Epoxy doesn't suffer from this (although "proper" luthiers recommend against epoxy as it makes a future repair almost impossible - if this thing goes wrong, the entire fingerboard will need to be sanded off!)<br />
<br />
And there we have it - a light-up guitar neck, ready to be fit to the rest of the instrument. Because of the additional 2mm height added under the fingerboard, we're going to have to raise our bridge by the same amount (otherwise the strings will buzz as they "fret out" on the higher frets). A simple shim under the bridge raises it by 2mm, but this also means we're going to have to screw it down hard, losing the tremelo operation (having a moving trem might distort the shim under the bridge over time). Luckily the pickups can be adjusted by more than 2mm, so we've simply raised the nut, strings, bridge and pickups all by 2mm and the playability shouldn't be affected.<br />
<br />
<br />
We'll have to assemble the rest of the guitar for testing.......<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-27477687050715993812017-04-23T23:16:00.002+01:002017-04-23T23:16:29.128+01:00Save some Arduino RAM when using strings with the F macroAnyone who has ever written out debug messages to themselves while developing on Arduino will know, add too many and all your mcu RAM gets chewed up pretty quickly.<br />
<br />
In "production" code, it's quite common to flash an LED to indicate what's going on, but that gets pretty tedious to debug when you're making lots of changes to your code as you develop, so it becomes common to little complex routines will little Serial.Println statements, to show where in the logic control you're up to.<br />
<br />
A you might write something like<br />
<br />
<div style="background-color: black; color: lime; font-family: courier, courier new, fixed-width, fixed, sans-serif, sans; font-size: 8pt; padding: 10px;">
while(something){<br />
Serial.println("Here's what's going on");<br />
}</div>
<br />
after a few of these (ok, maybe like a couple of dozen or more) you'll find your RAM usage creeping up. Debugging code that uses wasteful libraries means either re-writing someone else's code (negating the benefits of a library-based development system) or reducing the message length (until you're doing little more than an alpha-numeric equivalent to flashing an LED).<br />
<br />
More experienced users might write something like<br />
<br />
<div style="background-color: black; color: lime; font-family: courier, courier new, fixed-width, fixed, sans-serif, sans; font-size: 8pt; padding: 10px;">
while(something){<br />
Serial.println(F("Here's what's going on"));<br />
}</div>
<br />
<br />
At first the difference is difficult to spot. But that all important F macro (which isn't particularly well documented in Arduino help files) makes a massive difference. What that does is write your string message to program ROM rather than fill your RAM with pointers to character arrays that the Arduino string class uses.<br />
<br />
Replace all instances of "my string" with F("my string") and you'll find your RAM usage plummets (while your program ROM size increases by roughly the same amount as you've saved with RAM).<br />
<br />
We recently played about with the excellent nokia5110-compatible LCD library and built a rotary-encoder based menu system (for the light-up guitar I promised Keith). There are lots of strings of text used, and - sure enough - after coding a few menus, our RAM usage was on the up<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7uLt3smUudlhIbD81UGXHmCjuHCImFOCcyxrT731mrJ1rxGeOMyw36UwlZmkIjfw1eBR1IjVnEtE1wtE5cfzMYEEMPmfNZKd0TcwwgCwZu-FVUVWJXU-acZL5_a3tmZ1CAnaxb_ucF9mc/s1600/Untitled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="317" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7uLt3smUudlhIbD81UGXHmCjuHCImFOCcyxrT731mrJ1rxGeOMyw36UwlZmkIjfw1eBR1IjVnEtE1wtE5cfzMYEEMPmfNZKd0TcwwgCwZu-FVUVWJXU-acZL5_a3tmZ1CAnaxb_ucF9mc/s320/Untitled.png" width="320" /></a></div>
<br />
<br />
While it seems trivial to add the F macro jut before each of our strings, in this particular case, it wouldn't actually work. See, our LcdString function accepts not an Arduino-type string object, but a pointer to a character array.<br />
<br />
So if we tried to write LcdString(F(" string "));<br />
it simply wouldn't work (the compile returns a data type mismatch error.<br />
<br />
The answer is a quick-and-dirty function into which we can pass a string and return a character array, which we can then pass into our LcdString function.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz8wZoKJB7jYXdDk0-PiZjGStuMhSNQJ96lDeJLO_Gr825Yp2ZdC4IEPlDT59gC8FuL1dWx3TABk_iMIEcDOgmF7Mj8F-5RyfK-FoFR6M3NYgojwv_ivBFILZUwGfYmtOHmWaBbSWfw3f7/s1600/Untitled2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="317" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz8wZoKJB7jYXdDk0-PiZjGStuMhSNQJ96lDeJLO_Gr825Yp2ZdC4IEPlDT59gC8FuL1dWx3TABk_iMIEcDOgmF7Mj8F-5RyfK-FoFR6M3NYgojwv_ivBFILZUwGfYmtOHmWaBbSWfw3f7/s320/Untitled2.png" width="320" /></a></div>
<br />
Now we can write our string calls using the F-macro (to push the strings into program ROM space and free up RAM) but still pass them as character arrays into the functions that prefer character arrays over the Arduino string class.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJb3jbcZrojoYSS46qlUTlCjJYWEVVLqWn6NeT8PHwhZ1QPB94qPHEl_Kj2nRUTYrmXKOmwiu2ZhVLMTx6PvjER38mNnSQGQ_Gx36NRfBSyWqnyqUBQBNUypvcRuHfi8qYF93CgknDfvnK/s1600/Untitled3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="317" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJb3jbcZrojoYSS46qlUTlCjJYWEVVLqWn6NeT8PHwhZ1QPB94qPHEl_Kj2nRUTYrmXKOmwiu2ZhVLMTx6PvjER38mNnSQGQ_Gx36NRfBSyWqnyqUBQBNUypvcRuHfi8qYF93CgknDfvnK/s320/Untitled3.png" width="320" /></a></div>
<br />
In our menu test, we managed to conserve over 360 bytes just by implementing the F-macro using a string2char function. Given the atmega328 has 2kb of RAM but a massive 32kB of ROM, wherever possible we try to push our strings into ROM.<br />
<br />
We managed to reduce our RAM usage by over a third (34%) for a modest 2% increase in program space. Given there's more to this project than just the menu system, we'd take any chance to reclaim back over 17% of the total available RAM for use in the rest of the program!<br />
<br />
So next time you're getting close to using up all your RAM, find all those little debug messages and wrap them in the F-macro. And if you're passing strings into other functions, you can still use it and simply pass your F-strings into the string2char function if the function prefers a character array.<br />
<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com2tag:blogger.com,1999:blog-2584327372624565191.post-83912642167044937742017-04-22T11:55:00.002+01:002017-04-22T11:57:46.610+01:00Making a light-up-guitar for KeithHaving spent a few days in Dublin, I got chatting to my brother-in-law Keith. He was asking about the guitar project we worked on a while back and we swapped tales about learning (or failing to learn) the pentatonic scales and target notes properly.<br />
<br />
When I got back I promised I'd build him a guitar to demonstrate how it all worked. Which was grand, except since moving into the workshop bungalow, I've not been able to find the massive PCBs to connect up the WS2812B RGB LEDs.<br />
<br />
Not wanting to go back on promise meant only one thing - hand-solderingall 96 of the little buggers with a pair of tweezers and some thin-gauge wire. I thought I'd left wire-wrapping behind in the 90s! Luckily there were a couple of laser-cut fingerboards left over from building the last few guitars about a year ago, so I set about super-gluing some LEDs to the underside.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOviWgMZHY8aVF6ulaIC0ZGee3cYCOBBgGkoxPt1JmFxOWdPHZgvev4NpZ6Js5EjY6FWCZ8OmNztlP1QiYQ__2EFDkN7BURM3_HA6veBe1W94ZObXxpIPUOdPTjAoLrlP-xHkUIImw433f/s1600/20170421_113538.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOviWgMZHY8aVF6ulaIC0ZGee3cYCOBBgGkoxPt1JmFxOWdPHZgvev4NpZ6Js5EjY6FWCZ8OmNztlP1QiYQ__2EFDkN7BURM3_HA6veBe1W94ZObXxpIPUOdPTjAoLrlP-xHkUIImw433f/s320/20170421_113538.jpg" width="320" /></a></div>
<br />
I got the idea from messing about with the electronic board game; having PCBs built for them would be prohibitively expensive, so we swapped the circuit boards for strips of copper tape and hand-building with loose components. By placing the LEDs the right way around, I figured I could connect the data_in and data_out pins together easily, then just join all the power and ground pins to two strips of copper tape on each fret.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbti6S_UZzS1SnFC3KkgUXmQv69eylDXWSMQKGsaN2I9ve-h-jIfomyMthtCvHom9vPUOhxK9APuPSWVihanXKOYydtichd7Jk9Tk8OdzFc2P7mCeTkY4IMqCG-SZSPoUx8DQlQyfZodf_/s1600/20170421_154825.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbti6S_UZzS1SnFC3KkgUXmQv69eylDXWSMQKGsaN2I9ve-h-jIfomyMthtCvHom9vPUOhxK9APuPSWVihanXKOYydtichd7Jk9Tk8OdzFc2P7mCeTkY4IMqCG-SZSPoUx8DQlQyfZodf_/s320/20170421_154825.jpg" width="320" /></a></div>
<br />
It took nearly two days of positioning, soldering, testing, debugging, re-soldering - in between other work - but the end result was quite impressive<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP6Dnmeq56hUmjuarSlfIUy6mICXyTYs-Wl1Irusm71tMVKm_xPsib4FS1FAPRfgW_GSAv-j1fYEVzttHt_7DxkNSRN8yrOA9m7x5D_7bg9C7SbHfO5wCvrKJHOj1IEzdtXS5SZt8qz3nl/s1600/20170422_103242.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP6Dnmeq56hUmjuarSlfIUy6mICXyTYs-Wl1Irusm71tMVKm_xPsib4FS1FAPRfgW_GSAv-j1fYEVzttHt_7DxkNSRN8yrOA9m7x5D_7bg9C7SbHfO5wCvrKJHOj1IEzdtXS5SZt8qz3nl/s320/20170422_103242.jpg" width="320" /></a></div>
<br />
Connections between each strip of lights was made up the centre of the fingerboard so the outer sides could be glued to the guitar neck; since the truss rod is down the middle of the neck we wouldn't be gluing the centre of the fingerboard anyway.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikbImIK9GFY1LJwksYKi6gD8WYnogfVnA_oS3ogsTV40u7aKvOtBawo0qiFpE9lYL3HySeiYPkr2CVAsypBb6jLsUB7vy7EVcl_efT_ouVnkxvA1BAb07Xt3iMWbcs6paoiyWfd0dBSdGH/s1600/20170422_080911.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikbImIK9GFY1LJwksYKi6gD8WYnogfVnA_oS3ogsTV40u7aKvOtBawo0qiFpE9lYL3HySeiYPkr2CVAsypBb6jLsUB7vy7EVcl_efT_ouVnkxvA1BAb07Xt3iMWbcs6paoiyWfd0dBSdGH/s320/20170422_080911.jpg" width="320" /></a></div>
<br />
<br />
A quick rainbow sketch on an Arduino with the FastLED library and we had a rather attractive display. Never mind lighting up frets, learning scales and showing how to play the guitar - I quite fancy another one of these with just the rainbow pattern. I might not be the best performer at the next Open Mic Night at the Pebbles, but I'll certainly be the brightest!<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/g5uG_m3z3cI?rel=0&controls=0&showinfo=0" width="490"></iframe>
</div>
Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-59591882663439126362017-04-20T15:07:00.001+01:002017-04-20T15:07:05.695+01:00Unity, raycasting and line of sight between objectsWe're putting together a simple 2D/top-down game that makes extensive use of "line-of-sight" rules as players move around the game world. There are a few ways you can check for line-of-sight but they almost always involve drawing a line between two points, then seeing which objects (if any) intersect this line.<br />
<br />
If we were coding this in any other language for any other system, that's probably how we'd do it anyway; create an equation to describe the line between the two points, then "walk along" the length of the line, one pixel/unit at a time, checking to see if the x/y position of any other object in the gameworld is close enough to the line to be considered intersecting with it.<br />
<br />
Unity provides this functionality already with its Physics.Raycast function.<br />
<br />
Provide the function with a start point vector and a direction vector and it imagines an infinitely long line (the "ray") from the origin, and returns the first object that the ray collides with (if any). There's also Physics.RaycastAll which does the same thing, but returns an array of all objects hit by the ray.<br />
<br />
We made use of the RaycastAll function in our line-of-sight checks (although we're building a 2D game, the same principles of 3D development still exist, we just treat everything as if it were all on the same Z-plane). We put three moving objects into our game world, with one of them hidden behind a wall. We then updated the position of each object and ran our line-of-sight checks from the moving object to all other objects in the world. The checks involved raycasting from the movign object to each other object (in turn) and checking the array of collisions.<br />
<br />
If the array was empty, there were no detected obstacles along the line, and so we said that there was a line-of-sight between the two (in future development we'll have to include things like facing and field-of-vision and so on, but for now we're just trying to decide if an obstacle exists between two points). If any obstacle was returned in the array, we said that no line-of-sight existed between the two objects.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz9c96DbGCzMrcsBZxaWf1_f4QXVYn4nx1uUyA59CMn5eu0bkX8_5PjKJiGjZZ1kOGDy-KyJtdTgNTWWgFOluwbPNd2Gep6_Af9iwVOYiqTn6TnW8HWrm_8EiUHbagO8sSaz22bXHau94x/s1600/Image1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz9c96DbGCzMrcsBZxaWf1_f4QXVYn4nx1uUyA59CMn5eu0bkX8_5PjKJiGjZZ1kOGDy-KyJtdTgNTWWgFOluwbPNd2Gep6_Af9iwVOYiqTn6TnW8HWrm_8EiUHbagO8sSaz22bXHau94x/s320/Image1.png" width="320" /></a></div>
<br />
<br />
On the face of it, a simple Raycast function might do the job, as we're only interested - at this stage - in the binary option of "is there an obstacle between these two points". But we wanted to use the RaycastAll function to return ALL objects so that in future we might be able to assign "visibility" to different obstacles. Some obstacles may, for example, be see-through, but we still want them to act as an obstacle for purposes other than viewing. A classic example might be a glass window: you can see through it but it also acts as a physical barrier.<br />
<br />
So we don't just want our line-of-sight function to return false if any old obstacle exists between two points - we want to inspect each obstacle type between the points and decide whether or not to include them in our line-of-sight check. So instead of Physics.Raycast, we used Physics.RaycastAll.<br />
<br />
Everything seemed to be working just fine for a while; our hidden object remained hidden and the visible object revealed itself in good time. The function correctly identified whether or not there was a line-of-sight between all of the objects. Then something funny happened - despite there being a pefectly clear run between our first two objects, the LOS function started returning false<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigJuE3YYbTXQQl-DapMsMg2UdVcmkifaYYzWgmY-SZKijbnsSeStdbZGwZASEXzRYNeofmyn4XeUhRPKT-vAZztYpxA8tYMX5Np_1n5AImP9uI_wxhpRH1slW4f8irK_mLNb3wf70staDM/s1600/Image2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="208" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigJuE3YYbTXQQl-DapMsMg2UdVcmkifaYYzWgmY-SZKijbnsSeStdbZGwZASEXzRYNeofmyn4XeUhRPKT-vAZztYpxA8tYMX5Np_1n5AImP9uI_wxhpRH1slW4f8irK_mLNb3wf70staDM/s320/Image2.png" width="320" /></a></div>
<br />
<br />
Even more peculiarly, sometimes the function returned true (is there a line of sight between these two objects) and sometimes false, depending on which object we used as the source and which was the destination. Yet as we hadn't yet introduced rotation or facing into our function, it didn't make sense that an obstacle was found if we went from A to B but none were found if we went from B to A.<br />
<br />
After much puzzling and re-reading the Unity documentation, we eventually worked out the problem. Our ray was continuing beyond the object being tested. So although we thought were asking "are there any obstacles along a ray between these two points?" the function was actually returning "are there any objects along an infinitely long ray, starting at point A and continuing in the direction towards point B?"<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjngEJY8CviT46kQZla6ShFHFeYXyGJayUaHxV4CD_AvSzBy738oXXWgF0Qy-a5pjlIhO-YNuCMN_WwBySbzZXIMSYIhZtQYYxmWy32BVcBlOmn_SeLJO-ibYWj5ijkK2RqL9rGgfN7dILK/s1600/Image3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="208" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjngEJY8CviT46kQZla6ShFHFeYXyGJayUaHxV4CD_AvSzBy738oXXWgF0Qy-a5pjlIhO-YNuCMN_WwBySbzZXIMSYIhZtQYYxmWy32BVcBlOmn_SeLJO-ibYWj5ijkK2RqL9rGgfN7dILK/s320/Image3.png" width="320" /></a></div>
<br />
<br />
Of course, as soon as we moved an object so that there was a wall <i>behind</i> it, the function found the wall. The ray passed through the second object, struck the wall behind and said "yes, I found an obstacle along that ray".<br />
<br />
What we needed to do was limit the length of the ray;<br />
The RaycastAll function has an overload which allows you to enter a start point, a direction and a magnitude (maximum length of the ray). We created our ray be subtracting the gameworld co-ordinates of the source object from the co-ordinates of the destination object. This creates a vector describing the path between the two objects. We use this vector as our ray. Having created the ray, we then used the magnitude of the direction vector as the length of the ray.<br />
<br />
As soon as we limited the length of the ray to match the length of the vector describing the direction from one object to the other,the function worked as expected, both "forwards" and "backwards" (i.e. it didn't match which object we used as the source and which was the destination).<br />
<br />
<div style="background-color: black; color: lime; font-family: courier, courier new, fixed-width, fixed, sans-serif, sans; font-size: 8pt; padding: 20px;">
bool hasLOS(GameObject source, GameObject dest){
<br />
// firstly cast a ray between the two objects and see if there are any
<br />
// obstacles inbetween (some obstacles have "partial visibility" in which
<br />
// case we may or may not want to include as a "hit")
<br />
<br />
RaycastHit[] hits;
<br />
bool obj_hit = false;
<br />
<br />
Vector3 dir = dest.transform.position - source.transform.position;
<br />
Ray ry = new Ray ();
<br />
ry.origin = source.transform.position;
<br />
ry.direction = dir;
<br />
<br />
hits = Physics.RaycastAll (ry, dir.magnitude);
<br />
Debug.DrawRay (source.transform.position, dir, Color.cyan, 4.0f);
<br />
<br />
foreach(RaycastHit hit in hits){
<br />
// here we could look at an attached script (if one exists) on the object and
<br />
// decide whether or not this should actually constitute a hit
<br />
Debug.Log("LOS test hit from "+source.transform.position+" to "+dest.transform.position+" = "+hit.transform.parent.gameObject.name);
<br />
obj_hit = true;
<br />
}
<br />
<br />
return(!obj_hit);
<br />
}
</div>
<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
Within the foreach loop we can put some further testing to decide whether or not the obstacle has an effect. So in the case of firing a bullet at a target which is on the other side of a glass wall, we could call the function and ignore the glass object when testing for line of sight (can we see the object behind the glass) but include the object as an obstacle when using the same function to decide if, say, a bullet were to be fired from one object at another.<br />
<br />
The same result could be achieved using trigonometry (lots of tan/cos functions) but Unity does provide lots of nice, easy, helper functions, such as Raycast and RaycastAll. Thanks Unity!<br />
<div>
<br /></div>
<div>
<br /></div>
Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com1tag:blogger.com,1999:blog-2584327372624565191.post-47351346317391645432017-04-16T22:44:00.000+01:002017-04-21T16:47:10.520+01:00Serial UART hub/network with master/slave devicesWe recently had cause to build a simple serial/UART "network" of slave devices. We had a single "controller" device (which receives data from a PC and broadcasts it along the bus) and a number of similar "slave" type devices.<br />
<br />
Normally, when it comes to multiple devices along a bus, we'd be thinking of either SPI (broadcast the message to all devices with an identifier in the message to which the appropriate devices repond) or I2C (each device could have its own unique hardware ID to which we address the messages).<br />
<br />
But for a recent project we were asked if we could create a serial/UART bus. At first it seemed quite straight forward - simply tie all the TX lines of the "slaves" together and connect to the "RX" of the "master" and invert; tie all the RX lines of the "slaves" to each other and connect them to the "TX" of the master device.<br />
<br />
The basic idea is that the master would broadcast a message to all devices, including a device ID in the message. When any device receives an end-of-message marker, it looks at the device ID. If the message is not intended for that device, it simply ignores it.<br />
<br />
The theory works great.<br />
Sometimes in hardware it works just fine.<br />
But sometimes it goes horribly wrong.<br />
<br />
Now of course if two devices try talking at once, you just get garbled nonsense (so at the end of each message we include a simple XOR sum to check if a message is valid). So this set-up only works if you can be sure that only one device is going to try to use the bus at any one time.<br />
<br />
But sometimes we were getting devices resetting. Not all of them, and not all at the same time. Just some devices, sometimes. Which in turn indicates that one device is trying to drive a line high, while another is trying to drive it low. When this happens, we're effectively creating a dead-short between power and ground; so it's no wonder that the devices are resetting!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYO5JIgoHU-FLWsZ2E-AR3CDYfzqYa2lMexWM0UFJRaqPZGjNtfdr1n0YZ0zqj1bfjGQ2r4YDJNPllV5IWHa21VVsCWQ2jQ_WM_avtd-Jdh28_drNtoArpBOLbV78XAYCh5dJ8iQG37MHf/s1600/txrx.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="127" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYO5JIgoHU-FLWsZ2E-AR3CDYfzqYa2lMexWM0UFJRaqPZGjNtfdr1n0YZ0zqj1bfjGQ2r4YDJNPllV5IWHa21VVsCWQ2jQ_WM_avtd-Jdh28_drNtoArpBOLbV78XAYCh5dJ8iQG37MHf/s320/txrx.png" width="320" /></a></div>
<br />
By simply putting a diode on each of the TX lines and a pull-up resistor on the "master" RX line we can overcome this problem easily. Now, when a device tries to drive a TX line high, the current can't get through the diode. But the pull-up resistor lets the TX line (connected to the RX of the master) float high. So the end result is the same.<br />
<br />
But if another device drives the TX line low, it's enough to overcome the pull-up resistor, so the entire TX bus goes low (and the master RX line goes low). If one device tries to drive the TX line high and another low, the TX line goes low. The data at the other end might get garbled, but the important thing is that we don't get slave devices resetting.<br />
<br />
It's basically the same idea used with SPI communications - drive a line low, release it to let it float high. But if we can't guarantee that our slave devices aren't going to try to drive the TX line high, the diode simply blocks that behaviour. When no devices are pulling the TX line low, it floats high anyway (which is the idle state of a UART transmitter anyway).<br />
<br />
Simple.<br />
But a trick worth knowing!<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com2tag:blogger.com,1999:blog-2584327372624565191.post-7909554144172572582017-04-15T18:29:00.002+01:002017-04-15T18:29:20.806+01:00Creating primitives and textures in UnityI love Unity. I love that you can write code and compile it to multiple platforms. I love that you can "hit up" the Asset Store and have a game working in a couple of hours. At least, a simple game.<br />
<br />
But one of the things I've always fancied doing with Unity was have it load levels (from a web server perhaps) and create rooms and playing areas dynamically. We've played about with doing just that using pre-bought assets (it's not as easy as you think, if you're working on a grid-based system, since most assets have their origin in the dead centre, not on one corner!)<br />
<br />
So as a bit of an experiment, we played about with creating a map "plane" from primitives, onto which we'll dynamically load textures. So at the start of the "game" there's nothing on screen - then a few script calls and we'll create some primitive shapes (after all, most floors and walls are not much more than simple rectangles) and apply some textures.<br />
<br />
It's worth noting that we're creating a 2D top-down type map, even though we're using 3D shapes (the 3d shapes allow us to work with complex principles such as rotation and line-of-sight later on down the line).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUnav962JyJxfIgYsJmzUIt0HhMIxnBeCfmVk_US5fS8KXBPjGoLkXS7jE5crOMfZccuj7VifR79AQ0jIv8XZOznzv2VHMBXYWSBzXX74Sj7W2bR-HF27SL6NrDin8vTZ8YBMhXqe7sSVr/s1600/Image3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUnav962JyJxfIgYsJmzUIt0HhMIxnBeCfmVk_US5fS8KXBPjGoLkXS7jE5crOMfZccuj7VifR79AQ0jIv8XZOznzv2VHMBXYWSBzXX74Sj7W2bR-HF27SL6NrDin8vTZ8YBMhXqe7sSVr/s320/Image3.png" width="320" /></a></div>
<br />
We've set up our camera as orthographic and have it pointing straight down. We also added a directional light and made this a child of the camera - effectively following it as it moves over the map. We also created a "gameWorld" empty gameobject just to hold all our dynamically generated content, in case we need to turn the global world on/off for some reason in the future.<br />
<br />
Now a couple of scripts to actually generate our primitive shapes and to apply textures to them. We're working on a grid-based map and each object we create in our game-world will be placed from the bottom-left corner:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhd5I_qeRualKgwy3GYuVkQ-UOXGwCYnhaJwpQ0V4UbkKvSRQy_p5r66SvCV2VYFr5ErEV5qw2LD40tUKjAadlJwISJmPfYXcvNHZNvE_7jfWJbFc_WFtQAgyHjHugML0sAAzHNbew0rTTq/s1600/Image4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhd5I_qeRualKgwy3GYuVkQ-UOXGwCYnhaJwpQ0V4UbkKvSRQy_p5r66SvCV2VYFr5ErEV5qw2LD40tUKjAadlJwISJmPfYXcvNHZNvE_7jfWJbFc_WFtQAgyHjHugML0sAAzHNbew0rTTq/s320/Image4.png" width="320" /></a></div>
<br />
<br />
But when you create a gameobject in Unity, the origin of the object is smack-bang in the centre. Which makes getting everything to line up in a grid a bit of a pain (especially if the objects are not perfectly square).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVDTsC2kCpTfAdbhsew18GuuZAp4IwT7SJUxBaLeecH1YTazZ0m2NJj8zOtZwmZZT5bi4CeILQu-MebYMHCfaIjuFV0t1K_nfjPLI-G-Ed1-mDFYxt3MHZJDl-L0-0siGEbAudesCSRR5L/s1600/Image6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVDTsC2kCpTfAdbhsew18GuuZAp4IwT7SJUxBaLeecH1YTazZ0m2NJj8zOtZwmZZT5bi4CeILQu-MebYMHCfaIjuFV0t1K_nfjPLI-G-Ed1-mDFYxt3MHZJDl-L0-0siGEbAudesCSRR5L/s320/Image6.png" width="320" /></a></div>
<br />
So whenever we create an object that we want to align on our grid, we "wrap it up" inside an empty gameobject and set the local x/y co-ordinates to half the height/width of the object. This way we can place our floors and walls without having to keep applying an offset to get the origin somewhere near the bottom-left corner.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjO14HHX4rM7UDDaJbQ2O6MOEwu9C34pir4wyyqnpr__9ss2yQFi1ixjnXDQGALMUaz5Y0x5DEBC_fnHBDI3KFTKBX4LNOBpsNCE3RP2YNyy1tc0IdvXqyigVhpYXc9yp7Gr62xiQO4rd8L/s1600/Image8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjO14HHX4rM7UDDaJbQ2O6MOEwu9C34pir4wyyqnpr__9ss2yQFi1ixjnXDQGALMUaz5Y0x5DEBC_fnHBDI3KFTKBX4LNOBpsNCE3RP2YNyy1tc0IdvXqyigVhpYXc9yp7Gr62xiQO4rd8L/s320/Image8.png" width="320" /></a></div>
<i><br /></i>
<i>With the gameobject in worldspace, placed at 0,0 half of the floor tile is beyond our 0,0 position (ok, it's only a quarter section, but you get the idea)</i><br />
<i><br /></i>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLt28HiTOUecxcvfyUjPFPh1sn4ebhJ0q2sBlHfSfu9Ltj1R3P_kTEDkyEaUFVnd-u2qbMf8yVustlfEniJhWks9A_bis49SQSHd9tQ8oIv3huABE_sOXQBy4u2Yx4Au7elrXRaeeyO-lC/s1600/Image9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLt28HiTOUecxcvfyUjPFPh1sn4ebhJ0q2sBlHfSfu9Ltj1R3P_kTEDkyEaUFVnd-u2qbMf8yVustlfEniJhWks9A_bis49SQSHd9tQ8oIv3huABE_sOXQBy4u2Yx4Au7elrXRaeeyO-lC/s320/Image9.png" width="320" /></a></div>
<br />
<i>By placing the tile inside an empty game object, we can place the parent at 0,0 and offset the child by half the height/width and get our tile to appear where we want it "in world space".</i><br />
<br />
<div style="background-color: black; color: lime; font-family: "courier" , "courier new" , , "fixed" , "sans"; font-size: 8pt; padding: 20px;">
using System.Collections;
<br />
using System.Collections.Generic;
<br />
using UnityEngine;
<br />
<br />
public class object_creator : MonoBehaviour {
<br />
<br />
Material mat;
<br />
Shader shdr;
<br />
<br />
// if you're using one-square to one-unity-unit keep track of it here
<br />
// (in earlier versions, a 0.5 scaled plane - 5Uunits - represented a board
<br />
// of 8x8 grid, in which case square size would be 5/8 = 0.625)
<br />
private float square_size = 1f;
<br />
<br />
// Use this for initialization
<br />
void Start () {
<br />
<br />
}
<br />
<br />
void Awake(){
<br />
shdr = Shader.Find ("Sprites/Default");
<br />
if (shdr) {
<br />
mat = new Material (shdr);
<br />
} else {
<br />
Debug.Log ("wtf");
<br />
}
<br />
}
<br />
<br />
// Update is called once per frame
<br />
void Update () {
<br />
<br />
}
<br />
<br />
public GameObject createObject(string objName, GameObject objParent, float x, float y, float z, float size_x, float size_y, float size_height){
<br />
<br />
// creates a primitive (cube) wrapped inside an empty game object
<br />
// which is placed at the gameworld position x,y
<br />
<br />
// the position of the (empty) game object is such that the origin is in the
<br />
// bottom-left corner (not the centre as is usual with gameobjects)
<br />
GameObject piece = new GameObject();
<br />
piece.name = objName;
<br />
piece.transform.parent = objParent.transform;
<br />
piece.transform.localPosition = new Vector3 (x, z, y);
<br />
piece.transform.Translate(new Vector3(-square_size/2, 0, -square_size/2));
<br />
<br />
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
<br />
cube.transform.parent = piece.transform;
<br />
cube.transform.localPosition = new Vector3 (size_x/2, 0, size_y/2);
<br />
cube.transform.localScale = new Vector3 (size_x, size_height, size_y);
<br />
<br />
return(piece);
<br />
}
<br />
<br />
public void setTexture(GameObject o, string imageName){
<br />
// get the child object in "o" with the name "Cube"
<br />
// (this is the actual shape, the game object is the container)
<br />
GameObject p = o.transform.FindChild("Cube").gameObject;
<br />
// download the texture for this object
<br />
string url="http://your_url/" + imageName + ".png";
<br />
StartCoroutine (downloadImage(url, p));
<br />
}
<br />
<br />
IEnumerator downloadImage(string url, GameObject o){
<br />
if (url.Length > 0) {
<br />
Debug.Log ("loading from " + url);
<br />
WWW www = new WWW (url);
<br />
yield return www;
<br />
Texture2D tex = new Texture2D (www.texture.width, www.texture.height);
<br />
www.LoadImageIntoTexture(tex);
<br />
o.GetComponent<Renderer> ().material = mat;
<br />
o.GetComponent<Renderer> ().material.mainTexture = tex;
<br />
o.GetComponent<Renderer> ().material.shader = shdr;
<br />
Debug.Log ("Texture set");
<br />
}
<br />
}
<br />
}
</div>
<br />
<br />
Our "object creator" script is referenced by our "game controller" script.<br />
When any primitive is created, it needs to be given a material to apply to it; so we create a global material, based on the "sprites/default" shader. This same material can be applied to all our primitive shapes. With a material applied, we can then change the texture property of each shape, with a newly-downloaded image, if necessary.<br />
<br />
<div style="background-color: black; color: cyan; font-family: "courier new" , "courier" , , "fixed" , "sans"; font-size: 8pt; padding: 20px;">
using System.Collections;
<br />
using System.Collections.Generic;
<br />
using UnityEngine;
<br />
<br />
public class game_controller : MonoBehaviour {
<br />
<br />
public GameObject world;
<br />
public object_creator oc;
<br />
<br />
// Use this for initialization
<br />
void Start () {
<br />
GameObject o;
<br />
o = oc.createObject ("b1", world, 0f, 0f, 0f, 8f, 8f, 0.05f);
<br />
oc.setTexture(o,"board1");
<br />
<br />
GameObject o2 = oc.createObject ("b2", world, 8f, 0f, 0f, 8f, 8f, 0.05f);
<br />
oc.setTexture(o2,"board2");
<br />
}
<br />
<br />
<br />
// Update is called once per frame
<br />
void Update () {
<br />
<br />
}
<br />
}
</div>
<br />
This script creates two "map tiles" each 8x8 units in size. It places the first at 0,0 and the second at 8,0 (immediately to the right of the first one). The script downloads the image board1.png and applies it to the first tile, and downloads the png image board2.png and applies it to the second tile.<br />
<br />
The end result looks something like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUMVq-RNG7VkJIIJ-uOFHgml8eZ-CL3XDB8SQr4i14pkyVRsvFLP02F8FFTrgRShRNP5xBuzcRvfjsIPBjLwaAqf0bm_vyA0IJLqtAAFF-QyHQOWPFVBeiG1IW4h0n_rBuaU7KuNbQbojy/s1600/Image10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUMVq-RNG7VkJIIJ-uOFHgml8eZ-CL3XDB8SQr4i14pkyVRsvFLP02F8FFTrgRShRNP5xBuzcRvfjsIPBjLwaAqf0bm_vyA0IJLqtAAFF-QyHQOWPFVBeiG1IW4h0n_rBuaU7KuNbQbojy/s320/Image10.png" width="320" /></a></div>
<br />
When we place an object at 0,0 (in world space) it appears in the first square, from the bottom-left corner of the map. If we change the co-ordinates of the object to 3,4 in world space, it appears four squares in and five squares up from our "board origin" in the bottom-left corner of the map (remember our map starts at zero, so at x3, the object should appear on the fourth square in).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYpL5Y41z2yAtr9SJoMVSoGklnHE3GFpXO9uDPY03Kbh2jDhPG1gTap4rKQVnyG8GKowkf9kj4KjjNv_YvMs-zjmwbzsZQna9LFjt2PJ95tipGxLNYj8KQwVUAdwQJc1H1L6j7fuqSTW3D/s1600/Image11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="165" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYpL5Y41z2yAtr9SJoMVSoGklnHE3GFpXO9uDPY03Kbh2jDhPG1gTap4rKQVnyG8GKowkf9kj4KjjNv_YvMs-zjmwbzsZQna9LFjt2PJ95tipGxLNYj8KQwVUAdwQJc1H1L6j7fuqSTW3D/s320/Image11.png" width="320" /></a></div>
<br />
A liberal sprinkling of iTween functions and a simple download-map-data-via-xml and we're on the way to creating a top-down game which can load map layout data (and sprites/images) from a website - online map editing here we come!<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-84328509284497219762017-04-09T23:24:00.000+01:002017-04-09T23:24:50.550+01:00AVR atmega328 PORTC not working AVCCOne of the things I've personally struggled with, switching between Arduino and PIC is the way the Arduino IDE/language deals with digital pins. I like to use terms like PORTB.5 (the sixth pin on portB) rather than the Arduino-specific "pin 13". Of course you can use direct port access with Arduino, but the convention is to address each individual pin using the crazy sequential numbering system.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2nN2HNYGYUpja2UivUjPcOkadA4j_wKWtji8dTQy2IHdmv5POXHmQp3c3NOnVc2jJohmfGapVwCPr_HgMESktehdsUWhlN_dKIJ3uC5822cRy64h8NOXMSaHLp9IZCQVY7LojKNxn8cz2/s1600/atmega328-tqfp-arduino-pinout.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2nN2HNYGYUpja2UivUjPcOkadA4j_wKWtji8dTQy2IHdmv5POXHmQp3c3NOnVc2jJohmfGapVwCPr_HgMESktehdsUWhlN_dKIJ3uC5822cRy64h8NOXMSaHLp9IZCQVY7LojKNxn8cz2/s320/atmega328-tqfp-arduino-pinout.jpg" width="307" /></a></div>
<br />
<br />
I've been working with a couple of guys on a custom "Arduino" board - in actual fact, it's little more than an ATMega328P AVR chip on a custom PCB; necessary only because we wanted to use 8 inputs, 8 outputs, SPI and a single, reversible pin for serial communication. At first we wanted to use an Arduino Pro Mini but no matter which way we tried to route things, we always ended up with pins 10-17 (yep, digital 17) as inputs with pull-up resistors enabled.<br />
<br />
As most Arduino users know, on most Arduino boards, pin 13 has an onboard LED. Which means we can't use it as an input (since the inline resistor on the LED is pulling the input pin low despite the internal pull-up).<br />
<br />
We also wanted to use a full-bridge rectifier to protect our little delicate AVR chips (they really don't like being powered up in reverse and can easily let out the blue magic smoke if you get the power and ground pins the wrong way around!)<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkKOI_5muVV0AxLr5J9a-E_BnlXS4Ti8c6yUItYHLpjcwEoUYLWxGfD9h3AfHGoqOZzadXKR60FDWYSuTVivcA6ny3kV6fWPLh_WrddauxpWuaQU6aYid5JyLGShxo-F5LXNqQ5K0uWD5e/s1600/Image1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="297" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkKOI_5muVV0AxLr5J9a-E_BnlXS4Ti8c6yUItYHLpjcwEoUYLWxGfD9h3AfHGoqOZzadXKR60FDWYSuTVivcA6ny3kV6fWPLh_WrddauxpWuaQU6aYid5JyLGShxo-F5LXNqQ5K0uWD5e/s320/Image1.png" width="320" /></a></div>
<br />
So we figured that the best idea would be a custom board with an AVR atmega328, with connectors for our inputs and outputs (routed to the nearest pins on the mcu, not necessarily in the digital pin number sequence) and multiple connectors for power and ground connected not to the AVR chip, but to pins 2 and 3 of the rectifier. The output of the rectifier is then connected to the AVR chip (pin1 to ground, pin 4 to power). This gives us power sockets which can be connected without worrying about the polarity of the power source.<br />
<br />
So everything appeared to be working just fine - the chip booted up and sent data over serial, irrespective of the polarity of the power supply. We tested all the inputs and could see that they were all working. But we were surprised to see that some of our outputs simply didn't work; the serial debug log indicated that the inputs were being read correctly, but the outputs simply failed to go high.<br />
<br />
We'd moved some pins around, putting our inputs onto the lower numbered pins with outputs on pins 10-17 (in case we ever wanted to return to the Arduino pre-made boards and needed to use the i/o pin with an LED connected to it). But it turned out that every one of our output pins numbered above 13 was not working. That's A0 (digital pin 14) A1 (pin 15) A2 (pin 16) and A3 (pin 17).<br />
<br />
We've used pins numbered 14 - 19 as digital i/o in the past; pins 20-21 can be set to digital inputs but not outputs, but we've had no trouble in the past making A2 light up an LED, for example. But there was something not right with our isolated AVR chip on our custom board....<br />
<br />
It took some desoldering and a while testing for continuity before we discovered a hairline fracture in the trace connecting Vcc to the AVcc pins. It turns out that you need power connected to the AVcc pin for any of PORTC to work as digital outputs.<br />
<br />
And it also turns out that PORTC happen to include the Arduino digital pins 14 (C0) through to 19 (C5). So without power on our AVcc pin, pins 14-19 fail to work as outputs.<br />
<br />
A quick bit of tack-soldering and short length of wire and everything worked perfectly! So there you have it - if your digital pins 14-19 fail to work as outputs, double-check your connection between Vcc and AVcc; it's not just some useless "alternative" connection, it does actually serve a purpose!<br />
<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-39177652840809352122017-04-05T22:31:00.003+01:002017-04-05T22:31:50.169+01:00Not all A3114 hall sensors are the same - who knew?We were playing about with hall sensors again this week. We've used hall sensors a lot in the past, and had a bunch of A3114 sensors left over from previous projects. But there were only a couple left and the massive bag of left-overs was somewhere in a box in the black hole that the bungalow workshop has quickly become.<br />
<br />
A few clicks on AliExpress later and we had some more A3114 sensors sent within just five days. We threw the lot together in a little component drawer and got on with making our project.<br />
<br />
Hall sensors are often used as limit switches, but don't suffer from the problems that mechanical switches often do in dusty environments - namely there are no moving parts to get gunked up with dust, and no way the switch can get jammed. But when we tried using them, we got some weird results.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSXPHX1Rcg-D42GMkq3Uzn591AqK1kvRd_0a0XO7Y-iNYPUsyyrPmB9UJuGQjw7x0_RzOrLYXuc6uOMhpz__WHZW16AceluOken8ofDotmOD5zeVPmomQbgf8A8HH-fbVYOGTu9dzbZhfs/s1600/hall_sensor.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSXPHX1Rcg-D42GMkq3Uzn591AqK1kvRd_0a0XO7Y-iNYPUsyyrPmB9UJuGQjw7x0_RzOrLYXuc6uOMhpz__WHZW16AceluOken8ofDotmOD5zeVPmomQbgf8A8HH-fbVYOGTu9dzbZhfs/s320/hall_sensor.jpg" width="179" /></a></div>
<br />
<br />
Some hall sensors plain simply didn't work.<br />
Some triggered from about three inches away!<br />
Some worked as we expected, triggering when a neodymium magnet approached from about 5mm away. And some acted less like switches and more like variable/analogue devices, with the output increasing in intensity as the magnet was moved closer.<br />
<br />
To get to the bottom of things we created a simple hall sensor tester from a battery, an LED and a socket (into which we plugged our different hall sensors to try them out).<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/eQlMrVrSB6M?rel=0&controls=0&showinfo=0" width="490"></iframe>
<br />
The first sensor in the video demonstrates how we expected the hall sensors to work; introduce a magnet and at a certain distance, the sensor acts like a switch and the LED lights up (in the video it appears to fade up quickly, but that's the camera auto-light-adjustment; in real life it switches almost instantly).<br />
<br />
The last sensor in the video - although not immediately obvious in the film - appeared to work a tiny, tiny amount; if you looked right inside the LED, a tiny little dot of light was just about perceptible, when the magnet was right up against the sensor.<br />
<br />
The second sensor in the video had us puzzled.<br />
Not because it triggers from a long way away, but because it appears to have an almost-analogue-like behaviour - the intensity of the light increases/decreases as the magnet is moved towards/away from the sensor. The reason this was particularly puzzling is because A3114 sensors are supposed to have an inbuilt <i>hysteresis</i>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh65sVSv4ruQ-pSgsEuHO9tD46MRisCNfAlVzWEE45D0uUrRbBUDQiwdnnCWYAbYrWsrs29jTrOO9m7fiyWhmdx_iGpQzwm8KGmsMRt0VJhmFfO6yN94Nyrqsgpq4M7Xr9SpMLkbwh6LQy2/s1600/fig4a.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh65sVSv4ruQ-pSgsEuHO9tD46MRisCNfAlVzWEE45D0uUrRbBUDQiwdnnCWYAbYrWsrs29jTrOO9m7fiyWhmdx_iGpQzwm8KGmsMRt0VJhmFfO6yN94Nyrqsgpq4M7Xr9SpMLkbwh6LQy2/s320/fig4a.jpg" width="320" /></a></div>
<br />
<br />
The A3114 is supposed to have a "trigger" and "release" magnetic flux density with a "dead band" which reduces any "chatter" that might occur just at the point where the switch would normally activate (similar to the bounce in a mechanical switch).<br />
<br />
Yet the second sensor in the video doesn't display either a trigger or a release threshold - the intensity of the LED changes in relation to the distance from the sensor. Which makes us wonder - what on earth kind of sensor is it?!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiK90FyJaAOO-dekqBExgqMTsnbRrdTxuot3ESms__7SRmNLzsFOwKgzCBGfUN2jIVDqKRnolzCK_t7shbWzIATMIhp92HGj5Exs2yplZr6Sz4F6qsGMiZ25-Q2eaXVyeacgMPIYH90USDL/s1600/Image1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="239" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiK90FyJaAOO-dekqBExgqMTsnbRrdTxuot3ESms__7SRmNLzsFOwKgzCBGfUN2jIVDqKRnolzCK_t7shbWzIATMIhp92HGj5Exs2yplZr6Sz4F6qsGMiZ25-Q2eaXVyeacgMPIYH90USDL/s320/Image1.jpg" width="320" /></a></div>
<br />
On closer inspection, we found that the sensors that worked as we expected them to were labelled 3114/515 and 3114/OH15.<br />
<br />
The newer sensors are labelled 3114/402.<br />
Which suggests that not all 3114 hall sensors are the same.<br />
<br />
Who knew?<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com1tag:blogger.com,1999:blog-2584327372624565191.post-22363378638002876932017-03-31T23:52:00.001+01:002017-03-31T23:56:04.013+01:00Resurrecting more old posts - word clock with Arduino and TinyRTC DS1307 moduleI've always loved the idea of a word clock; a few of us have even built a couple over the years (though I don't actually manage to ever keep hold of the ones I've made). Mostly they're a fun way to prove our multiplexing/charlie-plexing is working properly. Sometimes just to demonstrate how to use a MAX7219 chip.<br />
<br />
Recently we had to convert some old code over to Arduino, to use a MAX7219 LED driver. We'd had some trouble getting a 4-way 7-segment LED display to work properly with the MAX7219 chips - mostly because we were using common anode displays and the driver chips are best suited to common cathode (or was it the other way around...?). So we thought it'd be a good idea to strip the bigger project back to individual pieces; and for this task we focussed on just making the MAX7219 chips work.<br />
<br />
A <a href="http://nerdclub-uk.blogspot.co.uk/2016/10/messing-about-with-max7219-common-anode.html">few months back</a> we'd done some playing around with Arduino and MAX7219. This time we cascaded two chips and connected each MAX7219 to a single LED matrix. A bit of copy-n-paste coding later and we had a working example using the Arduino MAX Library<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYd3tVzxG5hFAhF0Gvi90Twr4e4I9tlg7Rqt22NE5fBiE0D7Xs5sWseRCKZQZK23Kcw2e3TzSZW6q9HYPvG3ZEwQiH9SObZAob_lsz3t6sldB7jaqhbW8Au41D-ET8y60IkvZPoFpeAJFC/s1600/20170328_180423.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYd3tVzxG5hFAhF0Gvi90Twr4e4I9tlg7Rqt22NE5fBiE0D7Xs5sWseRCKZQZK23Kcw2e3TzSZW6q9HYPvG3ZEwQiH9SObZAob_lsz3t6sldB7jaqhbW8Au41D-ET8y60IkvZPoFpeAJFC/s320/20170328_180423.jpg" width="320" /></a></div>
<br />
Having just moved a load of stuff into the new workshop bungalow, we had boxes and boxes of components hanging around - to hand were some RTC modules from a few years back; this gave us a perfect opportunity to put one to use<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdPbTaDPJEKd5GOWtoBKhLbOfSbhgZXG4rzAg3H8Qfm_NvvNCy21QATmTEHYlH_shp5DqPMcapoDbMgPDExQ1kFmP3mKjlR0asBdCcKxxTT82oXL1LqFb4OSRYK6BjaaFbNpF3Z9JHEciO/s1600/20170330_152543.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdPbTaDPJEKd5GOWtoBKhLbOfSbhgZXG4rzAg3H8Qfm_NvvNCy21QATmTEHYlH_shp5DqPMcapoDbMgPDExQ1kFmP3mKjlR0asBdCcKxxTT82oXL1LqFb4OSRYK6BjaaFbNpF3Z9JHEciO/s320/20170330_152543.jpg" width="320" /></a></div>
<br />
The <a href="https://www.elecrow.com/wiki/index.php?title=Tiny_RTC">RTC Library</a> had us reading the current date/time off the TinyRTC module in just minutes!<br />
<br />
<div style="background-color: black; color: lime; font-family: "courier new" , "courier" ,; font-size: 8pt; padding: 20px;">
#include <Wire.h>
<br />
#include "RTClib.h"
<br />
RTC_DS1307 RTC;
<br />
<br />
void setup () {
<br />
Serial.begin(9600);
<br />
Wire.begin();
<br />
RTC.begin();
<br />
if (! RTC.isrunning()) {
<br />
Serial.println("RTC is NOT running!");
<br />
// following line sets the RTC to the date & time this sketch was compiled
<br />
RTC.adjust(DateTime(__DATE__, __TIME__));
<br />
}
<br />
}
<br />
void loop () {
<br />
DateTime now = RTC.now();
<br />
Serial.print(now.year(), DEC);
<br />
Serial.print('/');
<br />
Serial.print(now.month(), DEC);
<br />
Serial.print('/');
<br />
Serial.print(now.day(), DEC);
<br />
Serial.print(' ');
<br />
Serial.print(now.hour(), DEC);
<br />
Serial.print(':');
<br />
Serial.print(now.minute(), DEC);
<br />
Serial.print(':');
<br />
Serial.print(now.second(), DEC);
<br />
Serial.println();
<br />
delay(1000);
<br />
}</div>
<br />
<br />
After putting the two matrix boards side-by-side all we needed to do was read the current time over I2C and decide which LEDs needed to light up when. Here's the pattern of letters/words we came up with:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn_bCBLNfljw_YIKptyAaakTVtSCw6XLVGI7aZI1ErCHCSPR6_GIiad5ha26-t1rEmYQM_fHBWjzfgceTLYDSPok_1c4RrEPrj7ndi54qqKQMHcPX2GXzqbtr2UO-W8VCJh1ocfGf7G3AY/s1600/Image1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn_bCBLNfljw_YIKptyAaakTVtSCw6XLVGI7aZI1ErCHCSPR6_GIiad5ha26-t1rEmYQM_fHBWjzfgceTLYDSPok_1c4RrEPrj7ndi54qqKQMHcPX2GXzqbtr2UO-W8VCJh1ocfGf7G3AY/s320/Image1.png" width="320" /></a></div>
<br />
As we're effectively using a larger 16x8 matrix, we had plenty of extra letters after writing out the hours and minutes, so went to town with improving the precision of our clock.<br />
<br />
Normally a word clock might say something like "it is ten past two" and display this until the time had changed to the next five-minute segment (in this case, "it is quarter past two"). We decided that we'd improve the precision by adding in "nearly" and "just gone" elements to each five-minute segment.<br />
<br />
So, within two minutes of a time, we'd describe it as "nearly". For example at 14:04 we'd say "it is nearly five past two". Similarly, for up to two minutes after a time, we'd describe it is "just gone". So at 14:31 we'd say "it has just gone half past two".<br />
<br />
This gives us the ability to tell the time to within two minutes, using language that many of us commonly use every day (although, as Nick pointed out, a margin of error of two minutes still leaves plenty time to miss your bus if you're not careful!)<br />
<br />
Here's the code we came up with.<br />
It's written to be readable/understandable rather than particularly "clever" (for example, we'd normally use an array for multi-line variables, like R[1][3] rather than multiple "single" variables, like R1C3 but as we've been getting a few questions of late on things we'd consider to be pretty simple, we tried to keep the code as understandable as possible!)<br />
<br />
<div style="background-color: black; color: lime; font-family: courier, courier new, fixed, sans; font-size: 8pt; padding: 10px;">
<br />
#include "Wire.h"
<br />
#define DS1307_I2C_ADDRESS 0x68
<br />
<br />
int dataIn = 2;
<br />
int load = 3;
<br />
int clk = 4;
<br />
int maxInUse = 2; //change this variable to set how many MAX7219's you'll use
<br />
<br />
// define max7219 registers
<br />
byte max7219_reg_noop = 0x00;
<br />
byte max7219_reg_digit0 = 0x01;
<br />
byte max7219_reg_digit1 = 0x02;
<br />
byte max7219_reg_digit2 = 0x03;
<br />
byte max7219_reg_digit3 = 0x04;
<br />
byte max7219_reg_digit4 = 0x05;
<br />
byte max7219_reg_digit5 = 0x06;
<br />
byte max7219_reg_digit6 = 0x07;
<br />
byte max7219_reg_digit7 = 0x08;
<br />
byte max7219_reg_decodeMode = 0x09;
<br />
byte max7219_reg_intensity = 0x0a;
<br />
byte max7219_reg_scanLimit = 0x0b;
<br />
byte max7219_reg_shutdown = 0x0c;
<br />
byte max7219_reg_displayTest = 0x0f;
<br />
<br />
int e = 0; // just a variable
<br />
<br />
<br />
// these are used to run in test mode
<br />
bool test_mode = false;
<br />
int curr_min = 0;
<br />
int curr_hr = 0;
<br />
<br />
<br />
// Convert normal decimal numbers to binary coded decimal
<br />
byte decToBcd(byte val){
<br />
return ( (val/10*16) + (val%10) );
<br />
}
<br />
<br />
// Convert binary coded decimal to normal decimal numbers
<br />
byte bcdToDec(byte val){
<br />
return ( (val/16*10) + (val%16) );
<br />
}
<br />
<br />
void setDateDs1307(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year){
<br />
Wire.beginTransmission(DS1307_I2C_ADDRESS);
<br />
Wire.write(0);
<br />
Wire.write(decToBcd(second)); // 0 to bit 7 starts the clock
<br />
Wire.write(decToBcd(minute));
<br />
Wire.write(decToBcd(hour)); // If you want 12 hour am/pm you need to set
<br />
// bit 6 (also need to change readDateDs1307)
<br />
Wire.write(decToBcd(dayOfWeek));
<br />
Wire.write(decToBcd(dayOfMonth));
<br />
Wire.write(decToBcd(month));
<br />
Wire.write(decToBcd(year));
<br />
Wire.endTransmission();
<br />
}
<br />
<br />
// Gets the date and time from the ds1307
<br />
void getDateDs1307(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year){
<br />
// Reset the register pointer
<br />
Wire.beginTransmission(DS1307_I2C_ADDRESS);
<br />
Wire.write(0);
<br />
Wire.endTransmission();
<br />
<br />
Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
<br />
<br />
// A few of these need masks because certain bits are control bits
<br />
byte b;
<br />
b=Wire.read();
<br />
Serial.print(b,HEX);
<br />
*second = bcdToDec(b & 0x7f);
<br />
<br />
b=Wire.read();
<br />
Serial.print(b,HEX);
<br />
*minute = bcdToDec(b);
<br />
<br />
b=Wire.read();
<br />
Serial.print(b,HEX);
<br />
*hour = bcdToDec(b & 0x3f); // Need to change this if 12 hour am/pm
<br />
<br />
b=Wire.read();
<br />
Serial.print(b,HEX);
<br />
*dayOfWeek = bcdToDec(b);
<br />
<br />
b=Wire.read();
<br />
Serial.print(b,HEX);
<br />
*dayOfMonth = bcdToDec(b);
<br />
<br />
b=Wire.read();
<br />
Serial.print(b,HEX);
<br />
*month = bcdToDec(b);
<br />
<br />
b=Wire.read();
<br />
Serial.print(b,HEX);
<br />
*year = bcdToDec(b);
<br />
<br />
Serial.print(" ");
<br />
<br />
}
<br />
<br />
<br />
void putByte(byte data) {
<br />
byte i = 8;
<br />
byte mask;
<br />
while(i > 0) {
<br />
mask = 0x01 << (i - 1);
<br />
digitalWrite( clk, LOW);
<br />
if (data & mask){
<br />
digitalWrite(dataIn, HIGH);
<br />
}else{
<br />
digitalWrite(dataIn, LOW);
<br />
}
<br />
digitalWrite(clk, HIGH);
<br />
--i;
<br />
}
<br />
<br />
}
<br />
<br />
void maxAll (byte reg, byte col) { // initialize all MAX7219's in the system
<br />
int c = 0;
<br />
digitalWrite(load, LOW); // begin
<br />
for ( c =1; c<= maxInUse; c++) {
<br />
putByte(reg); // specify register
<br />
putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
<br />
}
<br />
//digitalWrite(load, LOW);
<br />
digitalWrite(load,HIGH);
<br />
}
<br />
<br />
void maxOne(byte maxNr, byte reg, byte col) {
<br />
//maxOne is for addressing different MAX7219's,
<br />
//while having a couple of them cascaded
<br />
<br />
int c = 0;
<br />
digitalWrite(load, LOW); // begin
<br />
<br />
for ( c = maxInUse; c > maxNr; c--) {
<br />
putByte(0); // means no operation
<br />
putByte(0); // means no operation
<br />
}
<br />
<br />
putByte(reg); // specify register
<br />
putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
<br />
<br />
for ( c =maxNr-1; c >= 1; c--) {
<br />
putByte(0); // means no operation
<br />
putByte(0); // means no operation
<br />
}
<br />
<br />
//digitalWrite(load, LOW); // and load da stuff
<br />
digitalWrite(load,HIGH);
<br />
}
<br />
<br />
void showClockOutput(byte hour, byte minute, byte second, byte dayOfMonth, byte month, byte year){
<br />
Serial.print(hour, DEC);
<br />
Serial.print(":");
<br />
Serial.print(minute, DEC);
<br />
Serial.print(":");
<br />
Serial.print(second, DEC);
<br />
Serial.print(" ");
<br />
Serial.print(dayOfMonth, DEC);
<br />
Serial.print("/");
<br />
Serial.print(month, DEC);
<br />
Serial.print("/");
<br />
Serial.print(year, DEC);
<br />
Serial.println();
<br />
}
<br />
<br />
<br />
void setup () {
<br />
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
<br />
<br />
pinMode(dataIn, OUTPUT);
<br />
pinMode(clk, OUTPUT);
<br />
pinMode(load, OUTPUT);
<br />
<br />
//initiation of the max 7219
<br />
maxAll(max7219_reg_scanLimit, 0x07);
<br />
maxAll(max7219_reg_decodeMode, 0x00); // using an led matrix (not digits)
<br />
maxAll(max7219_reg_shutdown, 0x01); // not in shutdown mode
<br />
maxAll(max7219_reg_displayTest, 0x00); // no display test
<br />
for (e=1; e<=8; e++) { // empty registers, turn all LEDs off
<br />
maxAll(e,0);
<br />
}
<br />
maxAll(max7219_reg_intensity, 0x0f & 0x0f);
<br />
<br />
// initialise the RTC module
<br />
// (inc setting the time if necessary)
<br />
<br />
Wire.begin();
<br />
bool set_date=true;
<br />
if(set_date==false){
<br />
<br />
// change these values as necessary
<br />
second = 10;
<br />
minute = 46;
<br />
hour = 16;
<br />
dayOfWeek = 4;
<br />
dayOfMonth = 30;
<br />
month = 3;
<br />
year = 17;
<br />
setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
<br />
}
<br />
<br />
Serial.begin(9600);
<br />
Serial.println("Let's go");
<br />
<br />
}
<br />
<br />
void loop () {
<br />
<br />
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
<br />
getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
<br />
showClockOutput( hour, minute, second, dayOfMonth, month, year);
<br />
<br />
int r;
<br />
int r1c1, r1c2;
<br />
int r2c1, r2c2;
<br />
int r3c1, r3c2;
<br />
int r4c1, r4c2;
<br />
int r5c1, r5c2;
<br />
int r6c1, r6c2;
<br />
int r7c1, r7c2;
<br />
int r8c1, r8c2;
<br />
<br />
if(test_mode==true){
<br />
curr_min++;
<br />
if(curr_min > 59){ curr_hr++; curr_min=0;}
<br />
if(curr_hr > 23 ){ curr_hr=0; }
<br />
<br />
hour = curr_hr;
<br />
minute = curr_min;
<br />
}
<br />
<br />
// from the time, work out whether we start with its or it has
<br />
// (its nearly or it has just past)
<br />
r=0;
<br />
<br />
r1c1 = B11100000; r1c2 = 0x00;
<br />
r2c1 = 0x00; r2c2 = 0x00;
<br />
r3c1 = 0x00; r3c2 = 0x00;
<br />
r4c1 = 0x00; r4c2 = 0x00;
<br />
r5c1 = 0x00; r5c2 = 0x00;
<br />
r6c1 = 0x00; r6c2 = 0x00;
<br />
r7c1 = 0x00; r7c2 = 0x00;
<br />
r8c1 = 0x00; r8c2 = 0x00;
<br />
<br />
if(minute==1 || minute==2 || minute==6 || minute==7 || minute==11 || minute==12 || minute==16 || minute==17){ r=1; }
<br />
if(minute==3 || minute==4 || minute==8 || minute==9 || minute==13 || minute==14 || minute==18 || minute==19){ r=2; }
<br />
if(minute==21 || minute==22 || minute==26 || minute==27 || minute==31 || minute==32 || minute==36 || minute==37){ r=1; }
<br />
if(minute==23 || minute==24 || minute==28 || minute==29 || minute==33 || minute==34 || minute==38 || minute==39){ r=2; }
<br />
if(minute==41 || minute==42 || minute==46 || minute==47 || minute==51 || minute==52 || minute==56 || minute==57){ r=1; }
<br />
if(minute==43 || minute==44 || minute==48 || minute==49 || minute==53 || minute==54 || minute==58 || minute==59){ r=2; }
<br />
<br />
// just before or just after major clock minutes, light up the
<br />
// word "nearly" or "just past"
<br />
if(r==1){ r1c1 = B11011101; r1c2 = B11101111; }
<br />
if(r==2){ r1c1 = B11100000; r1c2 = B00000000; }
<br />
if(r==2){ r2c1 = B11111100; }
<br />
<br />
// maybe we could use the word "just gone" for one minute past and drop the "just"
<br />
// for two minutes past?
<br />
r=0;
<br />
if(minute==2 || minute==7 || minute==12 || minute==17){ r=1; }
<br />
if(minute==22 || minute==27 || minute==32 || minute==37){ r=1; }
<br />
if(minute==42 || minute==47 || minute==52 || minute==57){ r=1; }
<br />
if(r==1){
<br />
// mask out the word "just"
<br />
r1c1 = r1c1 & B11111110;
<br />
r1c2 = r1c2 & B00011111;
<br />
}
<br />
<br />
// at quarter past and quarter to the hour, light up the word quarter
<br />
if(minute >=13 && minute<=17){ r2c1+=1; r2c2 = B11111100; }
<br />
if(minute >=43 && minute<=47){ r2c1+=1; r2c2 = B11111100; }
<br />
<br />
// at twenty to and twenty past the hour, light up the word "twenty"
<br />
// (also at twenty-five to and twenty-five past)
<br />
if(minute >=18 && minute<=27){ r3c1 = B11111100; }
<br />
if(minute >=33 && minute<=42){ r3c1 = B11111100; }
<br />
<br />
// light up the word "five" at not only five to/past but also
<br />
// twenty-five to and twenty-five past
<br />
if(minute >=3 && minute<=7) { r3c1 +=1; r3c2 = B11100000;}
<br />
if(minute >=53 && minute<=57){ r3c1 +=1; r3c2 = B11100000;}
<br />
if(minute >=23 && minute<=27){ r3c1 +=1; r3c2 = B11100000;}
<br />
if(minute >=33 && minute<=37){ r3c1 +=1; r3c2 = B11100000;}
<br />
<br />
// at ten past/to the hour, light up the word ten
<br />
if(minute >=8 && minute <=12){ r3c2 = B00001110;}
<br />
if(minute >=48 && minute <=52){ r3c2 = B00001110;}
<br />
<br />
// at around half past...
<br />
if(minute >=28 && minute <=32){ r4c1 = B11110000;}
<br />
<br />
// display the either the word "past" or "two"
<br />
if(minute > 2 && minute <= 32){ r4c1 += B00000011; r4c2 = B11000000; }
<br />
if(minute > 32 && minute < 58) { r4c1 += B00001100; }
<br />
<br />
// this is for on the hour, o'clock
<br />
if(minute >=58 || minute <=2) { r8c1 = B11111110; }
<br />
<br />
// show the correct hour(s)
<br />
// (why compare to 32 and not 30 for half past the hour?
<br />
// because at 11:32 we want it still to read "it has just
<br />
// gone half past 11" - not half past twelve!
<br />
<br />
// just before/after one o'clock
<br />
if((hour==0 && minute > 32) || (hour == 1 && minute <=32)){ r7c2 = B00000111; }
<br />
if((hour==12 && minute > 32) || (hour == 13 && minute <=32)){ r7c2 = B00000111; }
<br />
<br />
// just before/after two o'clock
<br />
if((hour== 1 && minute > 32) || (hour == 2 && minute <=32)) { r5c1 = B11100000; }
<br />
if((hour==13 && minute > 32) || (hour == 14 && minute <=32)) { r5c1 = B11100000; }
<br />
<br />
// just before/after three o'clock
<br />
if((hour== 2 && minute > 32) || (hour == 3 && minute <=32)) { r4c2 += B00011111; }
<br />
if((hour==14 && minute > 32) || (hour == 15 && minute <=32)) { r4c2 += B00011111; }
<br />
<br />
// just before/after four o'clock
<br />
if((hour== 3 && minute > 32) || (hour == 4 && minute <=32)) { r5c1 = B00011110; }
<br />
if((hour==15 && minute > 32) || (hour == 16 && minute <=32)) { r5c1 = B00011110; }
<br />
<br />
// just before/after five o'clock
<br />
if((hour==4 && minute > 32) || (hour == 5 && minute <=32)){ r5c1 += 1; r5c2 = B11100000; }
<br />
if((hour==16 && minute > 32) || (hour == 17 && minute <=32)){ r5c1 += 1; r5c2 = B11100000; }
<br />
<br />
// just before/after six o'clock
<br />
if((hour==5 && minute > 32) || (hour == 6 && minute <=32)){ r6c1 = B11100000; }
<br />
if((hour==17 && minute > 32) || (hour == 18 && minute <=32)){ r6c1 = B11100000; }
<br />
<br />
// just before/after seven o'clock
<br />
if((hour==6 && minute > 32) || (hour == 7 && minute <=32)){ r5c2 = B00011111; }
<br />
if((hour==18 && minute > 32) || (hour == 19 && minute <=32)){ r5c2 = B00011111; }
<br />
<br />
// just before/after eight o'clock
<br />
if((hour==7 && minute > 32) || (hour == 8 && minute <=32)){ r6c1 = B00011111; }
<br />
if((hour==19 && minute > 32) || (hour == 20 && minute <=32)){ r6c1 = B00011111; }
<br />
<br />
// just before/after nine o'clock
<br />
if((hour==8 && minute > 32) || (hour == 9 && minute <=32)){ r6c2 = B11110000; }
<br />
if((hour==20 && minute > 32) || (hour == 21 && minute <=32)){ r6c2 = B11110000; }
<br />
<br />
// just before/after ten o'clock
<br />
if((hour==9 && minute > 32) || (hour == 10 && minute <=32)){ r6c2 = B00001110; }
<br />
if((hour==21 && minute > 32) || (hour == 22 && minute <=32)){ r6c2 = B00001110; }
<br />
<br />
// just before/after eleven o'clock
<br />
if((hour==10 && minute > 32) || (hour == 11 && minute <=32)){ r7c1 = B11111100; }
<br />
if((hour==22 && minute > 32) || (hour == 23 && minute <=32)){ r7c1 = B11111100; }
<br />
<br />
// this is just before/after twelve (noon)
<br />
if((hour==11 && minute > 32) || (hour == 12 && minute <=32)){ r7c1 = B00000011; r7c2 = B11110000; }
<br />
<br />
// this is just before/after midnight
<br />
if((hour==23 && minute > 32) || (hour == 0 && minute <=32)){ r8c1 = B00000001; r8c2 = B11111110; }
<br />
<br />
<br />
// now light up the LEDs
<br />
maxOne(1,1,r1c1);
<br />
maxOne(1,2,r2c1);
<br />
maxOne(1,3,r3c1);
<br />
maxOne(1,4,r4c1);
<br />
maxOne(1,5,r5c1);
<br />
maxOne(1,6,r6c1);
<br />
maxOne(1,7,r7c1);
<br />
maxOne(1,8,r8c1);
<br />
<br />
maxOne(2,1,r1c2);
<br />
maxOne(2,2,r2c2);
<br />
maxOne(2,3,r3c2);
<br />
maxOne(2,4,r4c2);
<br />
maxOne(2,5,r5c2);
<br />
maxOne(2,6,r6c2);
<br />
maxOne(2,7,r7c2);
<br />
maxOne(2,8,r8c2);
<br />
<br />
delay(2000);
<br />
<br />
}
<br />
<br /></div>
<br />
<br />
Normally, when telling the time, anything before 30 minutes is described as "<i>past </i>the hour" and anything after is "<i>to</i> the following hour". But because we're allowing for "a few minutes past a specific time point" we had to allow for up to 32 minutes to be described as "<i>past</i>" (so 15:32 would be written as just gone half <i>past</i> three). Similarly, up to two minutes before the o'clock position would be described as "nearly x o'clock" so any checks against minutes are 0-32 and 33-58 (not against 30 and 59 as might be expected).<br />
<br />
With the LEDs wired up and the code working, we fired up the laser cutter to create a fascia. We cut the protective film from one side of some clear perspex and sprayed it with black paint....<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3gjtqKvPO9Jz9FTwtz53FakrKOKN1PJUpyruIxgINSEbdnv-q38KTp9QXw89oE3KeRyD3ulysUVs9jpChxGBmhA05Q4NUOGR1jBBu_ibhtvakdasAYVwjQfTcCuCXHDgWMzMMCSlYERsM/s1600/20170330_151648.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3gjtqKvPO9Jz9FTwtz53FakrKOKN1PJUpyruIxgINSEbdnv-q38KTp9QXw89oE3KeRyD3ulysUVs9jpChxGBmhA05Q4NUOGR1jBBu_ibhtvakdasAYVwjQfTcCuCXHDgWMzMMCSlYERsM/s320/20170330_151648.jpg" width="320" /></a></div>
<br />
....then laser-etched the letters onto the paint. As this was to be the back of the display, the lettering had to be mirrored.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixpfsu8QU_4HuaVWTMhFxgDN4BQ_z_a80t3vEd4KZt6TtcqsBlW26_QyFQu8k9JYh5DVluIn7qO0cB6F4BM4Jg3flY5OGPiE1bcLWRflEL-gh6a0OSUHQA7taKvzOsYBI86Az_PcpRWFxs/s1600/Image9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="161" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixpfsu8QU_4HuaVWTMhFxgDN4BQ_z_a80t3vEd4KZt6TtcqsBlW26_QyFQu8k9JYh5DVluIn7qO0cB6F4BM4Jg3flY5OGPiE1bcLWRflEL-gh6a0OSUHQA7taKvzOsYBI86Az_PcpRWFxs/s320/Image9.png" width="320" /></a></div>
<br />
<br />
Amazingly, the clock booted up and worked first time.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7meioj0NP698aP2vfDujNPfNQhHS1n-x2II-xCxCRdSnMER5bsJwWZT8JULutA2HCffo2q7sRWTBitGxjK7EMei2VYZxJfL9ADo1Qr5NebT-qBu__87WbnxHp1IlpIo0L4MuGgDWGZmkh/s1600/20170331_132422.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7meioj0NP698aP2vfDujNPfNQhHS1n-x2II-xCxCRdSnMER5bsJwWZT8JULutA2HCffo2q7sRWTBitGxjK7EMei2VYZxJfL9ADo1Qr5NebT-qBu__87WbnxHp1IlpIo0L4MuGgDWGZmkh/s320/20170331_132422.jpg" width="320" /></a></div>
<br />
So we added a test mode to increase the minutes every couple of seconds, so we can see it cycle through all available times in just a few minutes instead of having to stay awake (and gawp at the clock face without a break) for 24 hours or more!<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/F2f5_WosgyM?rel=0&controls=0&showinfo=0" width="490"></iframe>
</div>
<br />
Maybe if we build another one we might try smoked acrylic so the unlit letters are less prominent when they're not in use?<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com1tag:blogger.com,1999:blog-2584327372624565191.post-8217104489335225922017-03-27T21:02:00.000+01:002017-03-27T21:02:06.320+01:00Recreating MAX7219 functionality with an ArduinoIn recent weeks a few of our older projects have been resurrected and we've had a few emailed questions about them. Number one tends to be "you built this with a PIC can you send me the Arduino code?"<br />
<br />
The short answer is "no". The longer answer is "well, maybe, one day, when one of us needs something similar for some project".<br />
<br />
The latest project to garner interest is our "<a href="http://nerdclub-uk.blogspot.co.uk/2016/01/bluetooth-dartboard-scoring-device.html">dartsboard scorer</a>" using some massive 7-segment LEDs. It's been in a box for the last six months, but having recently got the workshop bungalow into some kind of useable state, I thought I might set up the dartboard and get it out again.<br />
<br />
One thing that always bugged me (and everyone else who used it if I'm honest) is that the brightness of the LEDs fluctuates depending on the number of segments lit up. It's all because we couldn't use our MAX7219 chips as the supply voltage needs to be in the region of 9v-12v and the LEDs are common anode types (the max7219 chips work best with common cathode displays).<br />
<br />
So I figured it's time we put the display right. And in doing so, maybe answering a couple of questions about the darts scorer - mostly "can you do it on an Arduino?" So here goes - we're going to be multiplexing the segments of the display(s) so that each segment draws the same amount of current and stays lit for the same duration; in theory that should make each segment appear with the same brightness.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9sgoKCxU4y1qGXboY7Pod56AylyShmaCR2MsjCfwGOP6Y4gEsZSylVjy9PFx5MkUFL3i0HOkxPmWSV88L9XzOE6nQYMuqC3BKOfpSV4r6g5986lKckaPfDgjYI-fqx9Nc97puRKLkvINN/s1600/20170327_171337.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9sgoKCxU4y1qGXboY7Pod56AylyShmaCR2MsjCfwGOP6Y4gEsZSylVjy9PFx5MkUFL3i0HOkxPmWSV88L9XzOE6nQYMuqC3BKOfpSV4r6g5986lKckaPfDgjYI-fqx9Nc97puRKLkvINN/s320/20170327_171337.jpg" width="320" /></a></div>
<br />
<br />
As before, we're using a ULN2803A sink array. Each 7-segment display will get it's own (Arduino) controller chip (bare ATMega328 chips) and we'll tell it which number to display by sending data to it over a simple three-wire SPI/I2C connector.<br />
<br />
We'll store the value we want to display in a variable. Then create a "pattern" to display on the 7-segment LED. So if we wanted to show the value 4:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVi6XHIDLJzx_E5SuyaatnpOPHErW3oiwJqlizGLoh_-_Z4jBKiE666UAJLlk2aH4gI3Y4Qs-aWzh7Xby9jtWoVI8Mb5eOqFtI3iOm6VfRdEhV8ozz09XClfyObqlKM3YXfgY-1ww5st-F/s1600/Image2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVi6XHIDLJzx_E5SuyaatnpOPHErW3oiwJqlizGLoh_-_Z4jBKiE666UAJLlk2aH4gI3Y4Qs-aWzh7Xby9jtWoVI8Mb5eOqFtI3iOm6VfRdEhV8ozz09XClfyObqlKM3YXfgY-1ww5st-F/s320/Image2.png" width="320" /></a></div>
We'd want to light up segments 2, 3, 6 and 7.<br />
So our pattern (reading right-to-left) would be 01100110.<br />
Similarly to display the number 7 we'd light up 1,2,3 and 6.<br />
The pattern for number seven would be 00100111.<br />
<br />
So in the main loop of our code, we'll look at bits 0-7 of our pattern variable and illuminate the appropriate LED segment (or not as necessary). All other segments will be turned off - so only one segment is lit at any one time and all active segments are illuminated for the same duration.<br />
<br />
<div style="background-color: black; color: lime; font-family: courier, courier new, fixed-width, fixed, sans; font-size: 8pt; padding: 20px;">
<br />
int k=2;
<br />
int last_k=2;
<br />
int mask_pattern = B00001101;
<br />
int value_to_display=0;
<br />
<br />
int byte_received=0;
<br />
int spi_cs=10;
<br />
int spi_data=11;
<br />
int spi_clk=12;
<br />
<br />
int last_cs=1;
<br />
int last_data=1;
<br />
int last_clk=1;
<br />
int byte_buffer=0;
<br />
int bits_received=0;
<br />
<br />
int a;
<br />
int b;
<br />
int c;
<br />
int d;
<br />
<br />
<br />
void setMaskPattern(int p){
<br />
switch(p){
<br />
case 0:
<br />
mask_pattern = B00111111; break;
<br />
<br />
case 1:
<br />
mask_pattern = B00000110; break;
<br />
<br />
case 2:
<br />
mask_pattern = B01011011; break;
<br />
<br />
case 3:
<br />
mask_pattern = B01001111; break;
<br />
<br />
case 4:
<br />
mask_pattern = B01100110; break;
<br />
<br />
case 5:
<br />
mask_pattern = B01101101; break;
<br />
<br />
case 6:
<br />
mask_pattern = B01111101; break;
<br />
<br />
case 7:
<br />
mask_pattern = B00100111; break;
<br />
<br />
case 8:
<br />
mask_pattern = B01111111; break;
<br />
<br />
case 9:
<br />
mask_pattern = B01101111; break;
<br />
<br />
case 99:
<br />
mask_pattern = 0x00; break;
<br />
<br />
}
<br />
<br />
}
<br />
<br />
void clearBuffer(){
<br />
byte_buffer=0;
<br />
bits_received=0;
<br />
}
<br />
<br />
void setup() {
<br />
for(int i=2; i<=8; i++){
<br />
pinMode(i,OUTPUT);
<br />
digitalWrite(i,LOW);
<br />
}
<br />
<br />
pinMode(spi_cs,INPUT_PULLUP);
<br />
pinMode(spi_data,INPUT_PULLUP);
<br />
pinMode(spi_clk,INPUT_PULLUP);
<br />
<br />
clearBuffer();
<br />
setMaskPattern(99);
<br />
<br />
}
<br />
<br />
void loop() {
<br />
<br />
<br />
// --------------------------------------------------
<br />
// multiplex the LEDs as fast as we can
<br />
// (up to 3ms is ok, 5ms creates a visible flicker)
<br />
// --------------------------------------------------
<br />
digitalWrite(last_k,LOW);
<br />
k=k+1;
<br />
if(k>8){ k=2;}
<br />
<br />
b=1 << (k-2);
<br />
if(mask_pattern & b){
<br />
digitalWrite(k,HIGH);
<br />
}
<br />
last_k=k;
<br />
<br />
<br />
// ---------------------------------------------
<br />
// if we've received any data on the SPI bus,
<br />
// update the number to display
<br />
// ---------------------------------------------
<br />
b=digitalRead(spi_cs);
<br />
if(b==LOW){
<br />
if(last_cs!=LOW){
<br />
// this is a falling edge - prepare the data values
<br />
clearBuffer();
<br />
}else{
<br />
<br />
// monitor the CLK line
<br />
a=digitalRead(spi_clk);
<br />
if(a==LOW){
<br />
<br />
if(last_clk!=LOW){
<br />
// this is a falling edge, get the data
<br />
c=digitalRead(spi_data);
<br />
if(c==HIGH){
<br />
d = 1;
<br />
d = d << bits_received;
<br />
byte_buffer = byte_buffer + d;
<br />
}
<br />
bits_received++;
<br />
<br />
}
<br />
}
<br />
last_clk=a;
<br />
<br />
}
<br />
<br />
}else{
<br />
<br />
if(last_cs==LOW){
<br />
// this is releasing the CS line, so put the value
<br />
// from the buffer onto the display
<br />
<br />
if(bits_received > 3){
<br />
value_to_display = byte_buffer;
<br />
if(value_to_display > 9){ value_to_display=99;}
<br />
setMaskPattern(value_to_display);
<br />
}
<br />
}
<br />
<br />
}
<br />
<br />
last_cs=b;
<br />
<br />
}
</div>
<br />
We've also got a bit of "pin polling" going on, looking for data coming in over I2C on pins 8,9 and 10. So when our CS line goes low, we reset everything, read some incoming data and when CS drifts high, choose a new pattern to make different segments of the LED display to light up. Of course this could (should?) be put onto an interrupt, but as the controller has nothing else to do, it won't hurt to poll the pins.<br />
<br />
<div style="display: block; font-family: "helvetica" , "arial" , sans-serif; font-size: 14px; font-stretch: normal; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; margin: 12px auto 6px auto;">
<a href="https://www.scribd.com/document/343199328/7-seg-spi#from_embed" style="text-decoration: underline;" title="View 7-seg-spi on Scribd">7-seg-spi</a> by <a href="https://www.scribd.com/user/72007968/chris-holden2495#from_embed" style="text-decoration: underline;" title="View chris_holden2495's profile on Scribd">chris_holden2495</a> on Scribd</div>
<iframe class="scribd_iframe_embed" data-aspect-ratio="1.4146341463414633" data-auto-height="false" frameborder="0" height="600" id="doc_66579" scrolling="no" src="https://www.scribd.com/embeds/343199328/content?start_page=1&view_mode=scroll&access_key=key-DWG1bWPwcZliBSbhYGqe&show_recommendations=false" width="100%"></iframe>
<br />
<br />
<br />
Which means we need a "controller" to send data to the display - the following code simply increments a counter from zero through to ten and displays the appropriate digit on the 7-segment LED.<br />
<br />
<div style="background-color: black; color: lime; font-family: courier, courier new, fixed-width, fixed, sans; font-size: 8pt; padding: 20px;">
<br />
int byte_to_send;
<br />
int spi_cs=10;
<br />
int spi_data=11;
<br />
int spi_clk=12;
<br />
<br />
int mask=0;
<br />
int k;
<br />
<br />
void sendByte(int byte_value){
<br />
// we assert the CS line (pull it low) to tell
<br />
// the target to start listening - but first need
<br />
// to make sure that the clock line is also idle
<br />
<br />
digitalWrite(spi_clk,HIGH);
<br />
digitalWrite(spi_cs,LOW);
<br />
<br />
// give it a moment
<br />
delay(1);
<br />
<br />
// now we set the data pin to indicate each bit
<br />
// in the value we want to send
<br />
for(int i=0; i<8; i++){
<br />
mask = 1;
<br />
mask = mask << i;
<br />
k = byte_value & mask;
<br />
if(k==0){
<br />
digitalWrite(spi_data,LOW);
<br />
}else{
<br />
digitalWrite(spi_data,HIGH);
<br />
}
<br />
<br />
// give it a moment
<br />
delay(1);
<br />
<br />
// drive the clock line low
<br />
digitalWrite(spi_clk,LOW);
<br />
<br />
// give it a moment
<br />
delay(2);
<br />
<br />
// return the clock line to idle
<br />
digitalWrite(spi_clk,HIGH);
<br />
<br />
}
<br />
<br />
// release the CS line (send it high)
<br />
digitalWrite(spi_cs,HIGH);
<br />
<br />
}
<br />
<br />
void setup() {
<br />
// put your setup code here, to run once:
<br />
pinMode(spi_cs,OUTPUT);
<br />
pinMode(spi_data,OUTPUT);
<br />
pinMode(spi_clk,OUTPUT);
<br />
<br />
// we're using pull-ups on the other end
<br />
// so everything should idle high
<br />
digitalWrite(spi_cs,HIGH);
<br />
digitalWrite(spi_data,HIGH);
<br />
digitalWrite(spi_clk,HIGH);
<br />
<br />
}
<br />
<br />
void loop() {
<br />
// put your main code here, to run repeatedly:
<br />
<br />
byte_to_send++;
<br />
if(byte_to_send > 10){ byte_to_send=0;}
<br />
sendByte(byte_to_send);
<br />
delay(2000);
<br />
<br />
}
</div>
<br />
<br />
The result looks something like this:<br />
<br />
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/lKr1yIIua7k?rel=0&controls=0&showinfo=0" width="490"></iframe>
</div>
<div style="text-align: center;">
<br /></div>
<br />
Now obviously this isn't the full code for our updated Darts Scorer, but should be enough to get your own project off the ground - swapping out a MAX7219 with an atmega328 and a ULN2803A transistor array (if you don't use the decimal point you can get away with a ULN2003).<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-12515719225410648322017-03-22T23:37:00.001+00:002017-03-22T23:37:31.719+00:00Bluetooth with Unity for iOS and AndroidIn recent weeks we've had quite a bit of activity on an old "how to do bluetooth" page on the blog. A lot of people have asked for a zip file or git link so they can just clone it into their projects.<br />
<br />
Firstly, that's not quite how this blog works; stuff posted here is a learning aid - probably just an aide-memoire for the few contributors who actually make the stuff in the first place - rather than a repository of open source code. In fact, I get quite resentful of ignorant people who post (often abusive) comments demanding the entire source code project, instead of reading the post and learning how to do it for themselves. It's not a view shared by everyone at Nerd Towers, but for me, any comment beginning "TL;DR" is meaningless drivel.<br />
<br />
With that rant out of the way, we recently had need to create a simple bluetooth-enabled app (for a Unity Meetup event next week, demonstrating how to connect Unity/software to hardware/real-world devices). So it seemed like a good time to review the bluetooth Unity library and hopefully clarify a few things that seem to have tripped a few readers up...<br />
<br />
The idea here is to simplify using the bluetooth library as much as possible. As the "list all devices and pick one" part of the last post caused such trouble, we've cut it out completely and tried to keep everything together in one place/script. This means we're looking for a specific named device and we'll automatically connect to it, then send and receive data via a couple of text boxes.<br />
<br />
Once you've got this working, you should be able to modify the script as necessary to pass values to/from your own functions to make it work in your own Unity project.<br />
<br />
Right, first up, import the BTLE library from the Asset Store.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdDG9kLmBGyZHLVfoWRYEbPZl7ImddIPQ6stcSL1ArtS3MxhHp8NF-LDwh2M7IqbmhpExvGYyw8h8QFxUZWNtKZ0_KQjlrYYGO2xdRcgcriIpJcxCyjc554IGFwmeupZqA6SAqVVbjSttk/s1600/Image1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdDG9kLmBGyZHLVfoWRYEbPZl7ImddIPQ6stcSL1ArtS3MxhHp8NF-LDwh2M7IqbmhpExvGYyw8h8QFxUZWNtKZ0_KQjlrYYGO2xdRcgcriIpJcxCyjc554IGFwmeupZqA6SAqVVbjSttk/s320/Image1.png" width="320" /></a></div>
<br />
And create a panel in the Unity IDE<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVyy997go5dMwtm4iGPRX3EwvvOa6P5T1Ubxh-KZJj3qYNTIdquETiyvzZvVHfjmzEre6LVVCjZGrp0Ve9tjZSLrIMEQox7l340LASFkyaj1_gBaJ31KeiU4R4n5fEz3cwXWhb-fKI9VTD/s1600/Image2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVyy997go5dMwtm4iGPRX3EwvvOa6P5T1Ubxh-KZJj3qYNTIdquETiyvzZvVHfjmzEre6LVVCjZGrp0Ve9tjZSLrIMEQox7l340LASFkyaj1_gBaJ31KeiU4R4n5fEz3cwXWhb-fKI9VTD/s320/Image2.png" width="320" /></a></div>
<br />
<br />
Create a new, blank game object then create a script btle_controller.cs. Drag and drop the script onto the blank game object<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVJO83zYBdCuBoBT8ImZKEXqxL4DiL0HDscgmNtTyIFHtjmh0toa7j-6tlAiSwSqKFe29qH_aop6WmuiIKCnsxCQkz7Fovswrww_P2qJuyiFQAminhLRSZmmeWMkX2TL3yKWjpeJll0mye/s1600/Image3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVJO83zYBdCuBoBT8ImZKEXqxL4DiL0HDscgmNtTyIFHtjmh0toa7j-6tlAiSwSqKFe29qH_aop6WmuiIKCnsxCQkz7Fovswrww_P2qJuyiFQAminhLRSZmmeWMkX2TL3yKWjpeJll0mye/s320/Image3.png" width="320" /></a></div>
<br />
Create a text object in the panel (this will be our debug window)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2dwpxzWYoGgGR_R721BVFUguLU-lrSvfvSy1VZp5xr5QOAfQFeK4nrq3mD_jcfEgsyGIohYDZJtOGV5QKbdFg3_rtuXylyxAujwp7UbRFkPY6SuxoZ32xhprKiDJZlbgm0FW_tsiIf9ih/s1600/Image4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2dwpxzWYoGgGR_R721BVFUguLU-lrSvfvSy1VZp5xr5QOAfQFeK4nrq3mD_jcfEgsyGIohYDZJtOGV5QKbdFg3_rtuXylyxAujwp7UbRFkPY6SuxoZ32xhprKiDJZlbgm0FW_tsiIf9ih/s320/Image4.png" width="320" /></a></div>
<br />
Copy and paste this code into the script:<br />
<br />
<div style="background-color: black; color: lime; font-family: courier, courier-new, courier new, fixed-width, sans; font-size: 8pt; padding: 10px;">
using System.Collections;
<br />
using System.Collections.Generic;
<br />
using UnityEngine;
<br />
using UnityEngine.UI;
<br />
using System.Text;
<br />
<br />
public class btle_controller : MonoBehaviour {
<br />
<br />
// -----------------------------------------------------------------
<br />
// change these to match the bluetooth device you're connecting to:
<br />
// -----------------------------------------------------------------
<br />
// private string _FullUID = "713d****-503e-4c75-ba94-3148f18d941e"; // redbear module pattern
<br />
private string _FullUID = "0000****-0000-1000-8000-00805f9b34fb"; // BLE-CC41a module pattern
<br />
private string _serviceUUID = "ffe0";
<br />
private string _readCharacteristicUUID = "ffe1";
<br />
private string _writeCharacteristicUUID = "ffe1";
<br />
private string deviceToConnectTo = "ChrisBLE";
<br />
<br />
public bool isConnected=false;
<br />
private bool _readFound=false;
<br />
private bool _writeFound=false;
<br />
private string _connectedID = null;
<br />
<br />
private Dictionary<string, string> _peripheralList;
<br />
private float _subscribingTimeout = 0f;
<br />
<br />
public Text txtDebug;
<br />
public GameObject uiPanel;
<br />
public Text txtSend;
<br />
public Text txtReceive;
<br />
public Button btnSend;
<br />
<br />
<br />
// Use this for initialization
<br />
void Start () {
<br />
btnSend.onClick.AddListener (sendData);
<br />
uiPanel.SetActive (false);
<br />
txtDebug.text+="\nInitialising bluetooth \n";
<br />
BluetoothLEHardwareInterface.Initialize (true, false, () => {},
<br />
(error) => {}
<br />
);
<br />
Invoke ("scan", 1f);
<br />
}
<br />
<br />
// Update is called once per frame
<br />
void Update () {
<br />
if (_readFound && _writeFound) {
<br />
_readFound = false;
<br />
_writeFound = false;
<br />
_subscribingTimeout = 1.0f;
<br />
}
<br />
<br />
if (_subscribingTimeout > 0f) {
<br />
_subscribingTimeout -= Time.deltaTime;
<br />
if (_subscribingTimeout <= 0f) {
<br />
_subscribingTimeout = 0f;
<br />
BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress (
<br />
_connectedID, FullUUID (_serviceUUID), FullUUID (_readCharacteristicUUID),
<br />
(deviceAddress, notification) => {
<br />
},
<br />
(deviceAddress2, characteristic, data) => {
<br />
BluetoothLEHardwareInterface.Log ("id: " + _connectedID);
<br />
if (deviceAddress2.CompareTo (_connectedID) == 0) {
<br />
BluetoothLEHardwareInterface.Log (string.Format ("data length: {0}", data.Length));
<br />
if (data.Length == 0) {
<br />
// do nothing
<br />
} else {
<br />
string s = ASCIIEncoding.UTF8.GetString (data);
<br />
BluetoothLEHardwareInterface.Log ("data: " + s);
<br />
receiveText (s);
<br />
}
<br />
}
<br />
});
<br />
<br />
}
<br />
}
<br />
}
<br />
<br />
void receiveText(string s){
<br />
txtDebug.text += "Received: " + s + " \n";
<br />
txtReceive.text = s;
<br />
}
<br />
<br />
void sendDataBluetooth(string sData){
<br />
if (sData.Length > 0) {
<br />
byte[] bytes = ASCIIEncoding.UTF8.GetBytes (sData);
<br />
if (bytes.Length > 0) {
<br />
sendBytesBluetooth (bytes);
<br />
}
<br />
}
<br />
}
<br />
<br />
void sendBytesBluetooth(byte[] data){
<br />
BluetoothLEHardwareInterface.Log (string.Format ("data length: {0} uuid {1}", data.Length.ToString (), FullUUID (_writeCharacteristicUUID)));
<br />
BluetoothLEHardwareInterface.WriteCharacteristic (_connectedID, FullUUID(_serviceUUID), FullUUID(_writeCharacteristicUUID),
<br />
data, data.Length, true, (characteristicUUID)=> {
<br />
BluetoothLEHardwareInterface.Log("Write succeeded");
<br />
}
<br />
);
<br />
}
<br />
<br />
<br />
void scan(){
<br />
<br />
// the first callback will only get called the first time this device is seen
<br />
// this is because it gets added to a list in the BluetoothDeviceScript
<br />
// after that only the second callback will get called and only if there is
<br />
// advertising data available
<br />
txtDebug.text+=("Starting scan \r\n");
<br />
BluetoothLEHardwareInterface.ScanForPeripheralsWithServices (null, (address, name) => {
<br />
AddPeripheral (name, address);
<br />
}, (address, name, rssi, advertisingInfo) => {});
<br />
<br />
}
<br />
<br />
void AddPeripheral (string name, string address){
<br />
txtDebug.text+=("Found "+name+" \r\n");
<br />
<br />
if (_peripheralList == null) {
<br />
_peripheralList = new Dictionary<string, string> ();
<br />
}
<br />
if (!_peripheralList.ContainsKey (address)) {
<br />
_peripheralList [address] = name;
<br />
if (name.Trim().ToLower() == deviceToConnectTo.Trim().ToLower()) {
<br />
//txtDebug.text += "Found our device, stop scanning \n";
<br />
//BluetoothLEHardwareInterface.StopScan ();
<br />
<br />
txtDebug.text += "Connecting to " + address + "\n";
<br />
connectBluetooth (address);
<br />
} else {
<br />
txtDebug.text += "Not what we're looking for \n";
<br />
}
<br />
} else {
<br />
txtDebug.text += "No address found \n";
<br />
}
<br />
}
<br />
<br />
void connectBluetooth(string addr){
<br />
BluetoothLEHardwareInterface.ConnectToPeripheral (addr, (address) => {
<br />
},
<br />
(address, serviceUUID) => {
<br />
},
<br />
(address, serviceUUID, characteristicUUID) => {
<br />
<br />
// discovered characteristic
<br />
if (IsEqual (serviceUUID, _serviceUUID)) {
<br />
_connectedID = address;
<br />
isConnected = true;
<br />
<br />
if (IsEqual (characteristicUUID, _readCharacteristicUUID)) {
<br />
_readFound = true;
<br />
}
<br />
if (IsEqual (characteristicUUID, _writeCharacteristicUUID)) {
<br />
_writeFound = true;
<br />
}
<br />
<br />
txtDebug.text += "Connected";
<br />
BluetoothLEHardwareInterface.StopScan ();
<br />
uiPanel.SetActive (true);
<br />
<br />
}
<br />
}, (address) => {
<br />
<br />
// this will get called when the device disconnects
<br />
// be aware that this will also get called when the disconnect
<br />
// is called above. both methods get call for the same action
<br />
// this is for backwards compatibility
<br />
isConnected = false;
<br />
});
<br />
<br />
}
<br />
<br />
<br />
void sendData(){
<br />
string s = txtSend.text.ToString ();
<br />
sendDataBluetooth (s);
<br />
}
<br />
<br />
// -------------------------------------------------------
<br />
// some helper functions for handling connection strings
<br />
// -------------------------------------------------------
<br />
string FullUUID (string uuid) {
<br />
return _FullUID.Replace ("****", uuid);
<br />
}
<br />
<br />
bool IsEqual(string uuid1, string uuid2){
<br />
if (uuid1.Length == 4) {
<br />
uuid1 = FullUUID (uuid1);
<br />
}
<br />
if (uuid2.Length == 4) {
<br />
uuid2 = FullUUID (uuid2);
<br />
}
<br />
return (uuid1.ToUpper().CompareTo(uuid2.ToUpper()) == 0);
<br />
}
<br />
<br />
}
</div>
<br />
<br />
... and hook up all the controls to the public variables. Starting with the debug text object, drag and drop this into the public variable slot in the Unity IDE.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijq2whL8bceMPo-0agWuX0zJG7WPbvHi28bHhRc2bNfk9JVVmlbsW8oTqa0Hlp4IAFDwDk3retMXsSBv46evS_pEAL8YqfCPbNfictG2Xt-v2dhh3neeKVwklkB3FDfQNgs3nxsW1Gohs1/s1600/Image5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijq2whL8bceMPo-0agWuX0zJG7WPbvHi28bHhRc2bNfk9JVVmlbsW8oTqa0Hlp4IAFDwDk3retMXsSBv46evS_pEAL8YqfCPbNfictG2Xt-v2dhh3neeKVwklkB3FDfQNgs3nxsW1Gohs1/s320/Image5.png" width="320" /></a></div>
<br />
Now create a second panel (we made ours dark so you can see it clearly) and link this up to the script variable by dragging and dropping it in the Unity IDE.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkPCqrY1GSYHBy-TVCDYO6ARbUasP0E2ONcJG0JjwhWltRDR2lzplu02joBvSv7DtXxPrTv8b7w98tXnLln9ohYndxWN2z8-r5W7wqAR5Wax1inGd8bdIPN_7HNJZnFf3Ys0incHNDCYOe/s1600/Image6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkPCqrY1GSYHBy-TVCDYO6ARbUasP0E2ONcJG0JjwhWltRDR2lzplu02joBvSv7DtXxPrTv8b7w98tXnLln9ohYndxWN2z8-r5W7wqAR5Wax1inGd8bdIPN_7HNJZnFf3Ys0incHNDCYOe/s320/Image6.png" width="320" /></a></div>
<br />
Create an input object (which we'll type in to send data) and a text object, as children of the second panel (this panel gets disabled until the Bluetooth device has connected). Plop a button on there too while you're about it. As before, hook up all the on-screen game objects to the script by dragging and dropping.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjl9Ko49bJho3FwuWFATT4EEfedy3qOJ_v0-UZmboc6n58yn7DERz46IzzSSBPNTlMH8fKikxmWLRNgqaXikt33rdnHNo3fSffaNjYhQNIoZqHrpOfwF156EQipvavuwhzZzfrqUO0VHhrL/s1600/Image7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjl9Ko49bJho3FwuWFATT4EEfedy3qOJ_v0-UZmboc6n58yn7DERz46IzzSSBPNTlMH8fKikxmWLRNgqaXikt33rdnHNo3fSffaNjYhQNIoZqHrpOfwF156EQipvavuwhzZzfrqUO0VHhrL/s320/Image7.png" width="320" /></a></div>
<br />
We tested our project using Unity Remote on an LG G3 phone and a simple <a href="http://nerdclub-uk.blogspot.co.uk/2016/02/working-with-cheap-bluetooth-btle4.html">Arduino/BLE-CC41a</a> combo (pushing data onto the serial port to send/receive over bluetooth).<br />
<br />
<div style="background-color: red; color: white; padding: 20px;">
IMPORTANT:<br />
If you're running the BTLE4 library on a device running Android 6.0 or later, you'll need to add an extra line to the AndroidManifest.xml file
</div>
<br />
<br />
<div style="background-color: black; color: cyan; font-family: courier new, courier, fixed-width, fixed, sans; font-size: 8pt; padding: 10px;">
<uses-permission: android:name="android.permission.ACCESS_COARSE_LOCATION" />
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU7e02Eh6Z9gLVEtKx7uW-3CoaSA_XfiH7usZ5QvS1wAIPlfFdkEAMcu6_Z-Mf0XE4-u1I4-dcqAYOLyYyKLARjYM1wnB0eMRXu5Hqy3S2CwB2Xda1lfrr3IXurakphV8T9IHUFmFh2c3H/s1600/Image8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="151" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU7e02Eh6Z9gLVEtKx7uW-3CoaSA_XfiH7usZ5QvS1wAIPlfFdkEAMcu6_Z-Mf0XE4-u1I4-dcqAYOLyYyKLARjYM1wnB0eMRXu5Hqy3S2CwB2Xda1lfrr3IXurakphV8T9IHUFmFh2c3H/s320/Image8.png" width="320" /></a></div>
<br />
Now fire up your app (we found it only worked on an actual device, running it in test-mode on the PC did nothing- even with bluetooth enabled on the laptop) and send sending/receiving data over bluetooth!<br />
<br />
<br />
Note:<br />
If you're getting nothing, and no errors during compile, it's quite likely that the UUID patterns are incorrect. Use some software such as BLEGattList (for Android) on your device to query not only which bluetooth devices are available, but also which UUIDs they are using.<br />
<br />
We found that the bottom device, listed as "unknown service" beginning 0000ffe0 with a single read/write characteristic with the leading digits 0000ffe1 gave us the values to "plug in" to our code: the service was FFE0, the read characteristic FFE1 and the write characteristic was also FFE1.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcAIG6jQiiBQN11HpiD314BSihjjEhzyxeaasoLZ4d-dAhCgnQgfmFfdpnvFW1WZWQuXwx4XW1lKCOB7lAKNH2wdo6vOzEvp8gLoej0nuicLvpIHnCOVWsmvcXHU-ruT2ZLkKujnZ8Lpj6/s1600/Screenshot_2016-02-22-18-10-37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcAIG6jQiiBQN11HpiD314BSihjjEhzyxeaasoLZ4d-dAhCgnQgfmFfdpnvFW1WZWQuXwx4XW1lKCOB7lAKNH2wdo6vOzEvp8gLoej0nuicLvpIHnCOVWsmvcXHU-ruT2ZLkKujnZ8Lpj6/s1600/Screenshot_2016-02-22-18-10-37.png" /></a></div>
This seems pretty standard across almost every one of these cheap bluetooth modules that mimic the BLE-CC41a devices. We did find an old RedBear bluetooth module which we got working, but the FullUID string had to be changed to something completely unrecognisable, and the read and write characteristics had different values (from memory something like 0002 for reading and 0001 for writing).<br />
<br />
Anyway, there it is - very much a cut-down, quick and dirty way of getting your device to talk to bluetooth modules, all from a single script. Please don't ask for zip files or full source code - the BTLE plugin is available on the asset store, it costs about a tenner and is well worth spending a few quid to support a fellow programmer. With that installed you can copy and paste a single script, assign some variables and off you go!<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com10tag:blogger.com,1999:blog-2584327372624565191.post-36464266060818858432017-03-16T12:16:00.001+00:002017-03-16T12:16:51.243+00:003d printed peristaltic pumpWhile playing about with some ideas recently we pondered just how powerful a pump had to be to raise a column of water a set height (using, say, aquarium tube of 4.7mm diameter to a height of about 400mm).<br />
<br />
We wondered if it would be possible to submerge a pump into a liquid and use inductive chargers (like those often used in Adafruit products to wireless charge lipo batteries) to power the pump. So the liquid could be in a container with the pump in it, which you then place on a separate base. All the clever stuff goes on in the base, and we just use inductive power to turn the pump on and off. It's probably not possible. But this was one of those "learn as you do it" type projects.<br />
<br />
For a start, we've already discounted an impeller type design - while you can run an impeller pump off a small hobby motor, they usually have to run at quite high voltage, and they consume quite a bit of current (around half an amp or more, which is way more than any wireless system would be able to provide).<br />
<br />
At this stage, we're not even convinced that wireless is even feasible. But we did get to thinking that some kind of peristaltic pump, driven by a small, geared, low current stepper motor might be worth investigating. Plus we've a spanky new 3d printer just sitting there waiting to be given it's first "proper" job!<br />
<br />
The first thing was to design our peristaltic pump; for this we used Inkscape (originally with a view to making the pump from layers of laser cut acrylic).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxwmAmm2vHa8VtUdVsmu4f1R7tHm7NiCYL8LVRIZMPWbalTmcjiq-YlxD1lj0HDYP37wwuHpIGTj8pe0MdtKUjyFXXaLJ4tSFPvunCw4QBWlpq62NS37W3OkKQ8EzyzFN5U-yhnNC7ME0u/s1600/pump_make1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="184" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxwmAmm2vHa8VtUdVsmu4f1R7tHm7NiCYL8LVRIZMPWbalTmcjiq-YlxD1lj0HDYP37wwuHpIGTj8pe0MdtKUjyFXXaLJ4tSFPvunCw4QBWlpq62NS37W3OkKQ8EzyzFN5U-yhnNC7ME0u/s320/pump_make1.png" width="320" /></a></div>
<br />
Then after importing the svg shapes into Blender and quite a bit of buggering about (extrude, then alt+A or something to convert a spline-based extrusion into a 3d geometry mesh, applying a few boolean operations and so on) we had some pretty impressive looking models.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfvQTQFPKFItujfVjbPVY-dF9UPwH0ti0v9dVxwkXiSI1_dMMAlfE1RqSS2YWF4TMdWmDzNQI5BUyFR3yyIycima2VlBupaI9cCrXuZeCYTjo5qX5cZlcJnfeiamlfq6J04smCYTfEV7sY/s1600/pump_make2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfvQTQFPKFItujfVjbPVY-dF9UPwH0ti0v9dVxwkXiSI1_dMMAlfE1RqSS2YWF4TMdWmDzNQI5BUyFR3yyIycima2VlBupaI9cCrXuZeCYTjo5qX5cZlcJnfeiamlfq6J04smCYTfEV7sY/s320/pump_make2.png" width="320" /></a></div>
<br />
There was something about removing duplicates and triangulating the shapes (edit mode, control something, control + N) that we forgot to document, but then the whole thing was ready to export as an STL file. A few clicks later and our models appeared in UP! Studio<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRNpEvBIffWDFSUzBCgu18TM9Wpzdd7YQAXllBQV65zd-NnWO40LZLbuTMLVYqtWOhH8I-S1syS9LEaV-B6WVNFBLYySsRgAjosJty44F_O_Z9vp2qz2aOEd-zXRILnoTviSjJsT-24HFg/s1600/pump_make3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRNpEvBIffWDFSUzBCgu18TM9Wpzdd7YQAXllBQV65zd-NnWO40LZLbuTMLVYqtWOhH8I-S1syS9LEaV-B6WVNFBLYySsRgAjosJty44F_O_Z9vp2qz2aOEd-zXRILnoTviSjJsT-24HFg/s320/pump_make3.png" width="320" /></a></div>
<br />
After the first export, the design appeared tiny. So we exported again from Blender, this time setting the scale setting in the export dialogue to 1000 (Blender's default units are metres but despite our shapes being described as 0.005 for 5mm for example, we still had to multiply up by 1000 to get them to the right size).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL12l2sIgRPXL7UcHAP5XO21Rs5eLHDdgbd07Ee5V2TjXG1hoIAz9Aravo8i8p9whiuuLCzFtUaRGM6t4rogkub6H5vdQA5YnYF-D1uoKCqVGJ37Z0IVL-PvyzyyJbDFZx6L6YfZr5tufo/s1600/20170312_143720_HDR.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL12l2sIgRPXL7UcHAP5XO21Rs5eLHDdgbd07Ee5V2TjXG1hoIAz9Aravo8i8p9whiuuLCzFtUaRGM6t4rogkub6H5vdQA5YnYF-D1uoKCqVGJ37Z0IVL-PvyzyyJbDFZx6L6YfZr5tufo/s320/20170312_143720_HDR.jpg" width="320" /></a></div>
<br />
We're still learning how 3d printing works but it was still a bit of a surprise (and a bit disappointing) to see our raft curling off the bed so early on in the first print. Stopping the print and trying again only caused the nozzle to block (which took about an hour to sort out in the end). So for the second print, we left all the doors on the printer closed and set it running. As long as the printer was pulling filament off the spool, we were happy to leave it printing "blindly".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8enIL7jIc3Rhf6WGmBrBp4dvPFGbiUP-DDJAveWY2_z0r3ZWDLnGdNKeYlBQpwxYin26m9-dRBlwYWq5HiwBQe1bV4J6rySUewPHXLRe-AbN6Sn91St2MUiPfmUtfiTyDLlxqpPyMSsiz/s1600/20170312_174345.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8enIL7jIc3Rhf6WGmBrBp4dvPFGbiUP-DDJAveWY2_z0r3ZWDLnGdNKeYlBQpwxYin26m9-dRBlwYWq5HiwBQe1bV4J6rySUewPHXLRe-AbN6Sn91St2MUiPfmUtfiTyDLlxqpPyMSsiz/s320/20170312_174345.jpg" width="320" /></a></div>
<br />
The final print was satisfyingly stinky when the UP! Mini beeped to tell us it had finished. The raft stayed stuck to the bed but there was still a tiny amount of warping in the print. The workshop was pretty cold (the UP! Studio said the nozzle started at 12deg C when we first booted the software up) so maybe that was it. Even so, the parts fitted together perfectly snugly.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHfkI9OLO3_meUN_fHDMgQXFwudowM69iDzixKWwVCKk1ZjQgipdOReXXgoV0EHoJhOL8WeLVYxpXrNWCFDBZ2a2a_dAZeaeEVffySQTD0wDJXcdwXYhHsueE8js4qoDSXvjMr8pcpTNN5/s1600/20170312_180606.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHfkI9OLO3_meUN_fHDMgQXFwudowM69iDzixKWwVCKk1ZjQgipdOReXXgoV0EHoJhOL8WeLVYxpXrNWCFDBZ2a2a_dAZeaeEVffySQTD0wDJXcdwXYhHsueE8js4qoDSXvjMr8pcpTNN5/s320/20170312_180606.jpg" width="320" /></a></div>
<br />
We drew the shaft hole exactly 3mm high and 5mm wide as per the dimension drawing for the 28BYJ-48 stepper motor. The triangular part of our pump pushed over the shaft with a bit of force- it was a very snug fit; some might say perfectly scaled!<br />
<br />
We connected up our stepper motor to a ULN2803A transistor array and threw together some simple code to make it power the coils in the correct sequence to get the motor to turn. We wrapped some 6mm aquarium pipe around the outside of the wheels and set the stepper motor spinning.<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="276" src="https://www.youtube-nocookie.com/embed/8cuzOSbce3A?rel=0&controls=0&showinfo=0" width="490"></iframe>
</div>
<br />
While everything appears to run fine, the 3d print holds up and it all looks good in principle, as soon as we added the (admittedly fairly stiff pipe) to the pump, it stalled the motor! Even at 12v, our little stepper just didn't have the grunt to squash the wheels against the sides of the tube. And that's without there being any fluid in the system!<br />
<br />
So it looks like we're either going to have to use a bigger/better/stronger motor or softer/squashier tube before this goes anywhere near a bucket of water...<br />
<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com1tag:blogger.com,1999:blog-2584327372624565191.post-9258369104414971152017-03-11T00:48:00.000+00:002017-03-11T00:48:16.956+00:00Testing our UP! Mini printer calibrationThis afternoon we set the UP! Mini 3d printer up in its final home, in the bungalow workshop. While the workshop isn't exactly finished, having loads of computers, tools, electronics equipment and boxes of wires hanging around the house has been getting pretty tiresome of late.<br />
<br />
So I set up the "big computer" in the bungalow and gave the 3d printer a quick go to check it still worked ok after being joggled around. Thankfully - and as Steve insists is common with UP! printers - it just worked!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZDad9GOGPZ7QotdqIu6TUA5xJrEK71znlZoqVx-PuYD6rIOA1BNUSg2mDTVVp9crrtl-pto86pIzkGkpYCcJSUexHTbiR_ezvBQj7mGNuAGS5SLzKsUkHGpUd5lYpnWiuANL1Z4qCk4gY/s1600/20170307_201711.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZDad9GOGPZ7QotdqIu6TUA5xJrEK71znlZoqVx-PuYD6rIOA1BNUSg2mDTVVp9crrtl-pto86pIzkGkpYCcJSUexHTbiR_ezvBQj7mGNuAGS5SLzKsUkHGpUd5lYpnWiuANL1Z4qCk4gY/s320/20170307_201711.jpg" width="320" /></a></div>
<div style="text-align: center;">
<i>great expanses of long empty shelves - won't stay empty for long!</i></div>
<i><br /></i>
After <a href="http://nerdclub-uk.blogspot.co.uk/2017/02/calibrating-ls3020-hpc-laser-cutter.html">calibrating our laser cutter</a> a few days ago (and finding it wasn't cutting exactly to size without a bit of tweaking) we figured it best to see how accurately the Up! Mini was printing.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYM-alkInZhohiCq8n2jceIyLG5aoLUehNqCZQP-8vp5rh5J4IdctAczTZX2oHTlrEMdYzNWLQIfLXUeRbPay5qAFzu3_WPD5Bgy0DfRKAPwyhwC0_2p_wcZhAiTvxF1ew99CGQUGpEBB_/s1600/3dstudio.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYM-alkInZhohiCq8n2jceIyLG5aoLUehNqCZQP-8vp5rh5J4IdctAczTZX2oHTlrEMdYzNWLQIfLXUeRbPay5qAFzu3_WPD5Bgy0DfRKAPwyhwC0_2p_wcZhAiTvxF1ew99CGQUGpEBB_/s320/3dstudio.png" width="320" /></a></div>
<br />
Without messing about creating a 3d model and slicing it up, I just added a simple cube in the Up! Studio software and scaled it to as close to 14mm as I could get (it ended up around 14.02mm). 10 minutes later and a little white cube was ready.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh16bFlL8eieB88MRPqRGYB_2BhAmVUvjF-QV9s8XL09bUhAnXt3mLAYxHR8D44kX2siSc6Kruatnj3_uMbx5x3o3PTt7UUK8MdHBcpGKroZKJeMNVE43RUdY8_a9fpjBmR3ZDdwBHsy90k/s1600/20170307_202539.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh16bFlL8eieB88MRPqRGYB_2BhAmVUvjF-QV9s8XL09bUhAnXt3mLAYxHR8D44kX2siSc6Kruatnj3_uMbx5x3o3PTt7UUK8MdHBcpGKroZKJeMNVE43RUdY8_a9fpjBmR3ZDdwBHsy90k/s320/20170307_202539.jpg" width="320" /></a></div>
<br />
Along one side and the height of cube appeared to be within a reasonable tolerance<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3UYDpYfwMq0CC_1ZsllcymNX9HfnRB43TzY-xL4OK30GnqYyrrjoWMi6GR0BampfiFybhgeBEk9kDUW2Qwz0wGiY5iuHDu6_s2P-Wg9BRUESoMAOROiiaG5gy9EZCXKCPZtXECWbdSEi9/s1600/20170307_202124.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3UYDpYfwMq0CC_1ZsllcymNX9HfnRB43TzY-xL4OK30GnqYyrrjoWMi6GR0BampfiFybhgeBEk9kDUW2Qwz0wGiY5iuHDu6_s2P-Wg9BRUESoMAOROiiaG5gy9EZCXKCPZtXECWbdSEi9/s320/20170307_202124.jpg" width="320" /></a></div>
<br />
Our printer has a resolution of 0.2mm so even at 14.14mm we're still pretty happy that the print is within our margin of error.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiojGE7t4H6Yy_9I2ZwM-1kyGDirHhtn4hZOYhZXJfqWHYVtoTejcKZcU3AHI711lKMioMTA8ApAv7_Kxz6f6GE9vbp_xUknIfgb_2ZtSmGXViIOoDvjxJpAdewy1ZY2sX9mxXzCXcfrSWa/s1600/20170307_202151.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiojGE7t4H6Yy_9I2ZwM-1kyGDirHhtn4hZOYhZXJfqWHYVtoTejcKZcU3AHI711lKMioMTA8ApAv7_Kxz6f6GE9vbp_xUknIfgb_2ZtSmGXViIOoDvjxJpAdewy1ZY2sX9mxXzCXcfrSWa/s320/20170307_202151.jpg" width="320" /></a></div>
<br />
Measuring the height of the cube (across the banding) showed it to also be within reasonable margin of error.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgloYo4WYBbGaCztUBrAh3iMmKAO6a23T4T-4mo4M1W3fZmdD7VRvn3bik-2x46nUYfUjI6TFAynaoQyMOM4JhlitSWGdtARkUjakbY9SAUHuKpnd8XTreTIpB_zGCeAuoPmALWKH-zLQoL/s1600/20170307_202322.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgloYo4WYBbGaCztUBrAh3iMmKAO6a23T4T-4mo4M1W3fZmdD7VRvn3bik-2x46nUYfUjI6TFAynaoQyMOM4JhlitSWGdtARkUjakbY9SAUHuKpnd8XTreTIpB_zGCeAuoPmALWKH-zLQoL/s320/20170307_202322.jpg" width="320" /></a></div>
<br />
At first this reading caused a little concern, as it's less than the 14mm required. But it's also within 0.17mm of the required distance - less than the height of a single layer. So it's pretty good. After all, the variation between designed and measured values were 0.17mm, 0.06mm and 0.12mm<br />
<br />
If we'd had that degree of accuracy from our initial laser cut testing, we'd have been more than happy! So until we print something that's completely way out of whack, it look like the UP! Mini 3d printer is producing decent prints - almost straight out of the box.<br />
<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-67477930828241345742017-03-10T00:27:00.000+00:002017-03-10T00:27:17.857+00:00UP! Mini 3d printerSo this arrived a few days ago.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhx_1sKUMqrnpWe95xx5XrSiaShZBI7BViBJFd5bDqcm8uP1IyhUtQliLwPDuvQMcczhJdAX3jGKIFVCJOwT91vQadAGgfLHyMEGkoBm36CLfEB9BATTqo9arEjwKaMHhKV44_nX5waOwTn/s1600/UP-Mini-3D-printer-review-pic.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhx_1sKUMqrnpWe95xx5XrSiaShZBI7BViBJFd5bDqcm8uP1IyhUtQliLwPDuvQMcczhJdAX3jGKIFVCJOwT91vQadAGgfLHyMEGkoBm36CLfEB9BATTqo9arEjwKaMHhKV44_nX5waOwTn/s320/UP-Mini-3D-printer-review-pic.jpg" width="320" /></a></div>
<br />
Steve has an UP! 3d printer and can't recommend it enough. We were looking for an introduction to 3d printing, but couldn't really justify throwing a grand at what would effectively be a fancy toy just to play about with.<br />
<br />
But when five UP! Mini printers appeared on eBay for less than two hundred quid each, we figured it was time to dive in and see what this 3d printing thing is that all the cool kids are banging on about.<br />
<br />
The printer had come from a "printer farm" (apparently) and had been maintained by a "professional". Quite what this means, we're still unsure. We were warned that the printer head might need purging and that there was no support offered. Having seen a few of the BuildBrighton lot battle for months with their 3d printers, this made us a little nervous - but Steve was insistent that out of all the 3d printers out there, UP! was a pretty pain-free entry to the 3d printing world.<br />
<br />
So we installed the software and booted the thing up.<br />
After hitting extrude out came a greyish streak of goo. It turns out the printer was printing with black ABS before we got it. Having pushed some white into it, the grey goo lasted about a minute before slowly turning a chewing-gum white. At least we'd loaded and managed to extrude the filament!<br />
<br />
The UP! software makes setting up the printer pretty easy. Click "initialise" and the print head/bed goes to their home positions (thanks to a number of limit switches). Click "maintain" and you can check the bed levelling.<br />
<br />
This involves moving the head to one of nine different points. As per the instructions, we used a folded piece of paper as a feeler gauge and moved the bed until it was just gripping the paper. Then loaded a model and hit print.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDM0sQnXXr26Yc5rbQ1u06nujs_ZdmJh6oP6bsXdv_x7ZrPXPHw-9Q64Mx0u93Zm1DnQSaE1svlYmXAtFJ6v3o2WP_xP-UNhvR2KcHhU8KExCqp5deraNPz0yCY6zm0ZcXH2w2dPUiYB8M/s1600/20170306_194502.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDM0sQnXXr26Yc5rbQ1u06nujs_ZdmJh6oP6bsXdv_x7ZrPXPHw-9Q64Mx0u93Zm1DnQSaE1svlYmXAtFJ6v3o2WP_xP-UNhvR2KcHhU8KExCqp5deraNPz0yCY6zm0ZcXH2w2dPUiYB8M/s320/20170306_194502.jpg" width="320" /></a></div>
<br />
This, apparently, is the first layer raft. But after printing this much, the printer sort of gave up. Actually, that's not really fair - the printer carried on regardless, it's just that nothing came out of the nozzle! It turns out we'd managed to bung the nozzle up on the first go!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjldtYrLs981mpuTUrp6WHLGLp-z8rLXTYHWFq2iQ4lE15K-xc4WKehcw7zt4LB2DKIKd3zUFi9aLj6uGg7YOBOCImRWy7qoBq6jPVjyMNXpmdwOjWIpUgSH3a-F_GqYFBSMIPAY3bjbrwc/s1600/20170306_194538.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjldtYrLs981mpuTUrp6WHLGLp-z8rLXTYHWFq2iQ4lE15K-xc4WKehcw7zt4LB2DKIKd3zUFi9aLj6uGg7YOBOCImRWy7qoBq6jPVjyMNXpmdwOjWIpUgSH3a-F_GqYFBSMIPAY3bjbrwc/s320/20170306_194538.jpg" width="320" /></a></div>
<br />
Luckily the printer came with an attachment for removing the nozzle.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJxTfGNonZdplY0rBfaxcQhV7MgN5WaPF3Qlb_mnhutmmmn3HGolMZql2gnM4y6HWDI2SXBH48gzkQwb8seI9czmqJlGvFC3I2F3WKXyCOhzm1bgIMeg1-l8r1Zw7VQ8fZ1OEK0pvLUatu/s1600/20170306_194528.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJxTfGNonZdplY0rBfaxcQhV7MgN5WaPF3Qlb_mnhutmmmn3HGolMZql2gnM4y6HWDI2SXBH48gzkQwb8seI9czmqJlGvFC3I2F3WKXyCOhzm1bgIMeg1-l8r1Zw7VQ8fZ1OEK0pvLUatu/s320/20170306_194528.jpg" width="320" /></a></div>
<br />
Unfortunately, it didn't fit!<br />
It turns out that our printer is using a V3 nozzle, not a V2. Ours is 8mm o/d and has an internal thread. They're far less common than the original V2 type nozzle that just about every other 3d printer uses!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHSEYqZ3bvSRMl7slQvlMtcQCo2ev63OunaEtDilnnajVbhzxAl5-N-oVXcmBIsaxkEAzCyEOf5QCOa9oOJt4DIUPXEHluWMkwlMz9h3sSaZ25HD3xFauxikZgZI667cqhxdDdHchJNDg_/s1600/up-nozzle-600x600.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHSEYqZ3bvSRMl7slQvlMtcQCo2ev63OunaEtDilnnajVbhzxAl5-N-oVXcmBIsaxkEAzCyEOf5QCOa9oOJt4DIUPXEHluWMkwlMz9h3sSaZ25HD3xFauxikZgZI667cqhxdDdHchJNDg_/s320/up-nozzle-600x600.jpg" width="320" /></a></div>
<br />
With the printer up to temperature, we carefully argued about who was going to be responsible for breaking the machine before we'd even managed to get a first print off it. Rock paper scissors settled it. I lost.<br />
<br />
The nozzle actually came off pretty cleanly - the outside was covered in black gunk and inside a white blob of ABS blocked the nozzle hole completely. It took about an hour of soaking in acetone, picking at with a pin and pulling the ABS with some super-fine tweezers and drilling with a 0.3mm bit (broke two of the buggers in the process) to get the nozzle completely clear.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiFBJ3O3Pxav8661zuXu4L_GxNpME8j-Nxz4msCd79IVN3qsXwom2_W0QCdw0bIlJ3Jdke9FG03QOn6zXETH9p7HPAi7FuEJJsepYVWIiqp0bP9hd3U0cYYP7blLMgXUfGr49NzCKLim8h/s1600/20170306_210945.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiFBJ3O3Pxav8661zuXu4L_GxNpME8j-Nxz4msCd79IVN3qsXwom2_W0QCdw0bIlJ3Jdke9FG03QOn6zXETH9p7HPAi7FuEJJsepYVWIiqp0bP9hd3U0cYYP7blLMgXUfGr49NzCKLim8h/s320/20170306_210945.jpg" width="320" /></a></div>
<br />
With the nozzle cleared, and midnight fast approaching, we decided to give it one quick print before calling it a day, whatever the outcome. I grabbed a little dragon off Thingiverse and loaded into the UP! Studio software. Before printing, Nick insisted we should recalibrate the bed. Steve suggested that we might have crashed the bed into the nozzle (which could be the cause of the blockage).<br />
<br />
So this time, we set the nozzle height so that it gripped the paper, then backed it off by 0.1mm on all nine points across the bed. Then I insisted that we leave all the doors closed (on the UP! Mini) and let the printer get on with its thing, instead of us crowding around it to see how it's getting on...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEir98-W2Cx6dOgPAkxwSEq8NdQOm4p46jmobpJoEAswsbJTQ2_K3385smQK1z0_5etaeGPONohfba5wbzL5ZajqX2p5qgfsYi6yr_KfA150-eNZzSykSbnVQ5mjhSN5G65E5Vq-Uv06XgSy/s1600/20170306_232914.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEir98-W2Cx6dOgPAkxwSEq8NdQOm4p46jmobpJoEAswsbJTQ2_K3385smQK1z0_5etaeGPONohfba5wbzL5ZajqX2p5qgfsYi6yr_KfA150-eNZzSykSbnVQ5mjhSN5G65E5Vq-Uv06XgSy/s320/20170306_232914.jpg" width="320" /></a></div>
<br />
About half-way through the print, we couldn't resist lifting the lid and taking a peek inside. Something wasn't right.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2wZCdrm3HvYYpRC8nUqY3i0FY1C9U76FtDL3Y-2tCP4MU5RooDdZv5M2ilylEy_dI5c1ImmEgkdL3NOeKydNbz0nlGCujDnN5xbTYtRL6Uk3QgMnaw7ZUvTN1BndWxOHzX9HMFYXa4K9w/s1600/20170306_231935.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2wZCdrm3HvYYpRC8nUqY3i0FY1C9U76FtDL3Y-2tCP4MU5RooDdZv5M2ilylEy_dI5c1ImmEgkdL3NOeKydNbz0nlGCujDnN5xbTYtRL6Uk3QgMnaw7ZUvTN1BndWxOHzX9HMFYXa4K9w/s320/20170306_231935.jpg" width="320" /></a></div>
<br />
We'd set the fill to 65% yet our model was hollow inside. And there was lots of "stringing". We had a nice solid raft, and the ABS was flowing freely but something just didn't ring true.<br />
<br />
Nick asked why the dragon on screen was pink and red. It turns out that the model we'd downloaded had <i>all</i> it's normals facing the wrong way! As easy as clicking "fix" in UP! Studio and the onscreen dragon turned blue.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBeVvfsfjv2CgL48Zxa8hJbg-Cpmp15qBONfLdSXeLCLqB8XEGUJ2dScJa_p0zqPcgZPcX6125wYdlZnzn_l4zsJRQZb_pLYLjOOuJXPiBcGJEUq9pAAuwa87NgCMiQTbQx86R74P34ubt/s1600/Image1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="167" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBeVvfsfjv2CgL48Zxa8hJbg-Cpmp15qBONfLdSXeLCLqB8XEGUJ2dScJa_p0zqPcgZPcX6125wYdlZnzn_l4zsJRQZb_pLYLjOOuJXPiBcGJEUq9pAAuwa87NgCMiQTbQx86R74P34ubt/s320/Image1.png" width="320" /></a></div>
<br />
So we hit print, closed the printer up and left it to do it's thing. The print time went up from 6 minutes to about 19 minutes. About half-way through we peeked inside again, just to make sure everything looked as it should - things were a lot more promising this time!<br />
<br />
At the end of the print, the UP! Mini beeped loudly and we took our first 3d model off the print bed.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMCUmfRGWnMrqoa0dyl5Ee-Pyo6twOznlKaghkgs40M5eQHWVgazEp_kfBgs_PIm_KIzrXcA32rUbCQQ7xUWi2lSYlsEehXP78l9hcKEAkh_YniBZgF_FLw5qxtGA-BEx6v7ISx_1DkLeG/s1600/20170306_233534.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMCUmfRGWnMrqoa0dyl5Ee-Pyo6twOznlKaghkgs40M5eQHWVgazEp_kfBgs_PIm_KIzrXcA32rUbCQQ7xUWi2lSYlsEehXP78l9hcKEAkh_YniBZgF_FLw5qxtGA-BEx6v7ISx_1DkLeG/s320/20170306_233534.jpg" width="320" /></a></div>
<br />
Ta-da! Success!<br />
A little 3d printed dragon.<br />
<br />
The UP! Mini has been surprisingly simple to set up and get working. Having seen quite a few of the BuildBrighton lot fighting with their Prusa homebrew models, spewing reams and reams of spaghetti and spending hours trying to get the bed level, getting the model to stick, blocking and unblocking the nozzle, only for a slightly wonky, half-recognisable shape to appear, the UP! Mini was amazingly easy to use.<br />
<br />
As Steve likes to remind other 3d printer users, with his UP! printer, he loads a model, hits print and out it comes - no messing about with calibration and bed sticking problems every time; his printer may have been expensive compared to some home-made/kit versions, but it "just works".<br />
<br />
Hopefully our UP! Mini will perform as well as these early tests suggest it should!<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com2tag:blogger.com,1999:blog-2584327372624565191.post-51381220759100733032017-03-08T07:30:00.001+00:002017-03-08T07:30:47.914+00:00Legoland BerlinLast weekend a few of us went and had a weekend in Berlin. We didn't have time to visit all the different hackspaces/makerspaces there (at last count, about ten in just the one city!) but did end up near Legoland.<br />
<br />
Unlike the UK counterpart, it's not a massive theme park with rides and a sprawling outdoor park (it's built in the middle of a city for a start!) but is still quite impressive and worth a visit - particularly if one of your party happens to be a five-year-old boy who has just had a birthday.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic3iw0mi04P0RyMeYhnvbgMdYVsmAVHTKeSM0zKUpowqXGNYMXcrqJj-RUwUtzV2j9HWRVNWD6OXudGAYGKYZVEoMDHj9SPAcP3L9l4qVZscAmfgYI2pjDUAXOBYIh5KUGGT94hHipp4lj/s1600/20170303_142341.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic3iw0mi04P0RyMeYhnvbgMdYVsmAVHTKeSM0zKUpowqXGNYMXcrqJj-RUwUtzV2j9HWRVNWD6OXudGAYGKYZVEoMDHj9SPAcP3L9l4qVZscAmfgYI2pjDUAXOBYIh5KUGGT94hHipp4lj/s320/20170303_142341.jpg" width="320" /></a></div>
<br />
In the (underground) entrance are some very impressive city-scapes, including a recreation of the Berlin City circa 1989, complete with working underground railway system.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimjkf08cdKZIX1ynAIV4GoiJXv2btTKEM-mDvZLhcW1BTiMdQ-mpZhCvAxvRQK_5f5Yi4xCSaqoXYDxeLhp6aeopxrrny6TsJ0fM7EA5qR2bpPhQHfHcByegZpXQ8v4fbvYr0UEldVs6tr/s1600/20170303_142421.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimjkf08cdKZIX1ynAIV4GoiJXv2btTKEM-mDvZLhcW1BTiMdQ-mpZhCvAxvRQK_5f5Yi4xCSaqoXYDxeLhp6aeopxrrny6TsJ0fM7EA5qR2bpPhQHfHcByegZpXQ8v4fbvYr0UEldVs6tr/s320/20170303_142421.jpg" width="320" /></a></div>
<br />
<br />
The whole set-up is rather impressive.<br />
And - unlike the cliched stereotype of a typical German - it's not devoid of humour; there's even a graffitti-covered wall which - at the press of a button - falls over to the cheers of the crowds.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBqiBRNqDgd8YDvcbscl-gHDbWyJXhVJzfHyVJZxF-iMG7Z1zq4k-UujBCKSR4UAOusHlS0TL7mNzSmEdz9Xub6CoVsRVOClPqUNEnKQJVVn1U9lDUpQco9DqNpEgRAfncmkcp8HQOkrfr/s1600/20170303_142536.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBqiBRNqDgd8YDvcbscl-gHDbWyJXhVJzfHyVJZxF-iMG7Z1zq4k-UujBCKSR4UAOusHlS0TL7mNzSmEdz9Xub6CoVsRVOClPqUNEnKQJVVn1U9lDUpQco9DqNpEgRAfncmkcp8HQOkrfr/s320/20170303_142536.jpg" width="320" /></a></div>
<br />
Moments later a tiny David Hasselhoff belts out "Looking for Freedom" atop a Lego crane, complete with twinkling LEDs. The whole thing is fantastically tongue-in-cheek - not exactly the kind of thing we've been taught to expect from our German friends!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyc6NSqqDAinvbP9nvC202g2BWPeMFrTFvBo9K3hGH1t3VQKQsMEfEewNmXeVkAsfguE549wacFDzNAZLJw_l4nQ7DE7kkVIj2IBw6b1nG5t6uOtKvY5XgPI-rPp29j5FGxb4ZpGMB4jG9/s1600/20170303_160401.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyc6NSqqDAinvbP9nvC202g2BWPeMFrTFvBo9K3hGH1t3VQKQsMEfEewNmXeVkAsfguE549wacFDzNAZLJw_l4nQ7DE7kkVIj2IBw6b1nG5t6uOtKvY5XgPI-rPp29j5FGxb4ZpGMB4jG9/s320/20170303_160401.jpg" width="320" /></a></div>
<br />
Of course the science and engineering section held our interest far longer than the Ninja Go and Batman franchised areas; servo-driven animations, flashing LEDs and loads of real-life moments in history captured in miniature made this a trip to remember!<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com0tag:blogger.com,1999:blog-2584327372624565191.post-65452716521825713982017-03-07T08:49:00.003+00:002017-03-07T08:49:52.622+00:00ExpressPCB acquires RobotRoom Copper ConnectionThis morning, David from RobotRoom sent us an email so full of exciting news, upbeat and happy smiles all around, you could almost hear Katrina and the Waves singing "Walking on Sunshine".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhexhYFDcF53mTAUMGcWEhE0P-nRxUFhVCxlrT2ybIaUdsbo4ZQgGVEKykhf4kS0XQMD9ucVH2v1w6IAUECM9kOGhawWTMNaWjyjfxgnl_H8l7tAFzoCi_ilh8rBe6ooiJS1rAi6IthnaB8/s1600/Image3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhexhYFDcF53mTAUMGcWEhE0P-nRxUFhVCxlrT2ybIaUdsbo4ZQgGVEKykhf4kS0XQMD9ucVH2v1w6IAUECM9kOGhawWTMNaWjyjfxgnl_H8l7tAFzoCi_ilh8rBe6ooiJS1rAi6IthnaB8/s320/Image3.png" width="320" /></a></div>
<br />
ExpressPCB has acquired Copper Connection.<br />
We put our head(s) in our collective hands.<br />
<br />
ExpressPCB - as a few of us have insisted over the years - is, far and away, the quickest and easiest software for producing quality PCBs. It's simple, quick to learn, has no fancy plugins, but the schematic and PCB layout applications integrate so nicely together that it's almost a joy to be able to lay down a circuit board from a schematic in next to no time.<br />
<br />
The one area that ExpressPCB was lacking was output (or exporting). ExpressPCB is free software provided by a PCB manufacturing company. They don't make it easy for you to produce your own designs or to send files generated by their software to other manufacturers. And why would they - after all they've provided the software for free, it's not unreasonable that they tie the user into using them to produce the boards!<br />
<br />
For homebrew boards, you can print your design from ExpressPCB onto toner transfer paper and make them yourself at home - the company obviously has no problem with that (it's only fair that you get to produce a prototype board before committing to a production run). Which is fine for hobbyists using through-hole components (as many hobbyists do, as they transfer designs from a breadboard to a PCB).<br />
<br />
When designing for surface mount boards, however, things get a bit trickier. If our design was entirely SMT, we'd "print" the board to a PDF (using CutePDF) then open the pdf in inkscape, mirror, then print (so after toner-transferring the design, it would appear the "correct way around" on the copper). An alternative approach is to design a library of components that are all mirrored (with pin one at the top right instead of the top left) and we've also done this successfully in the past. But it's also all to easy to print and etch an entire SMT board only to realise - too late - that the entire design needs flipping to be useable!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGbZcgzneC_dCJwRl6wEo42D_MZB8uEmOLGNTloRmOEHi2Qp5Dorkek9UPC0GTo9yluvpY7zNuD7eSnjXe3VF3MRkvL__UEoPgmzwYxAOGGBHUTtIkd_loDvgx50fnc2j23xMkZQIbUi18/s1600/Image4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGbZcgzneC_dCJwRl6wEo42D_MZB8uEmOLGNTloRmOEHi2Qp5Dorkek9UPC0GTo9yluvpY7zNuD7eSnjXe3VF3MRkvL__UEoPgmzwYxAOGGBHUTtIkd_loDvgx50fnc2j23xMkZQIbUi18/s320/Image4.png" width="320" /></a></div>
<br />
One of the really nice features of RobotRoom's Copper Connection is the print facility to produce printouts either for inspection/proof (the design is printed the correct way around) or for transfer etching (the design is automatically mirrored so that it appears the right way around after doing the toner transfer process).<br />
<br />
As mentioned, it's not an insurmountable problem to export and flip your designs from ExpressPCB if you don't have Copper Connection (or if, as we suspect might happen in the future, the print option disappears in upcoming versions of the software).<br />
<br />
But the absolute best thing about Copper Connection - and it's whole selling point for users of ExpressPCB (and probably the first thing to go now that ExpressPCB has acquired RobotRoom) is the Gerber/Excellon export.<br />
<br />
Without Copper Connection we'd have had to pay hundreds of pounds for "professional level" licences for Eagle or DipTrace to producer Gerber files in order to get our PCBs manufactured by factories in China. Unlike some, we've never had a problem with any of the gerber files produced by Copper Connection - our PCBs have been a doddle to create (thanks to the excellent ExpressPCB software) and a two-click process to turn into Gerbers (thanks to Robot Room's Copper Connection).<br />
<br />
It stands to reason that export to Gerber will either be the first thing to go from Copper Connection, else maybe a "pro level" licence to cover the cost of producing the software (after all ExpressPCB aren't going to want to give software away for free that lets you take your designs to any old manufacturer).<br />
<br />
Of course David from RobotRoom is excited that his produce has been acquired by ExpressPCB. Of course ExpressPCB are happy to have taken out a "competitor" to their software. But we can't help but feel that it'll mean the end of our cheapskate way of producing PCBs - both home-etched and pro manufactured - without quite a bit of messing about (or, worse still, learning Eagle!)<br />
<br />Chrishttp://www.blogger.com/profile/08274133286396866480noreply@blogger.com1