Tuesday, May 7, 2013

Nerds in Berlin (again)

We've been here in Berlin for a few days - in the salubrious district of Kreuzberg - and slowly getting to understand (some) of the appeal. It has many similarities to Brighton: only a grubby, dirtier, graffiti-strewn version - and no seaside. The last time I came, I hated it. This time, the weather is better, there are more people about, and we've met some really cool characters. Kreuzberg is still a dirty, rotten area of Berlin, but - as with so many places - it's the people that make the city, and we've met some pretty cool ones!

One of the more exciting things about our short visit was visiting the drop-in print making workshop. There are places like this all over Berlin: places that hackerspaces should be aspiring to. It's basically a shop where you turn up, explain what you need to do and they supply the parts, materials, equipment and (where it's needed) expertise to help you get the job done.


The shop is open an manned by staff five days a week; there's a massive indie/punk subculture in Berlin where people make their own t-shirts, fliers and cd/album covers, mostly using screen printing. Everyone seems to know someone who knows screen-printing! So we took ourselves along to see how it all works.

Andrea made up a simple screen by hand-stretching some medium-grade silk over an A4 sized picture frame and stapled it along each edge. Although you can get machine-stretched frames, this hand-made one seemed pretty taught, so we thought we'd just give it a go. At the print-shop they provided the photo-sensitive emulsion. It's ready mixed, so you just need to put a thin film on your screen and leave it to dry.


the photo-emulsion is a bright lime-green colour. When it's "baked" it goes a dark, leafy-green colour.

A thin film of paint is dropped into a trough (it's expensive stuff and is difficult to re-use, so only the smallest amount needed is taken out of the pot) and the trough is swiped quickly up both sides of the silk - working vertically, from bottom to top


The aim is not to place a film on the surface of the screen (although this may happen) - it should be as thin as possible and we're just trying to get the emulsion to fill the gaps in the silk. Sometimes the paint doesn't get quite into the corners, which is why the screen needs to be oversized: our frame is for an A4 picture so is actually a little larger than A4, all the way around


While the screen was drying, in a dark, warm room, we set about preparing the artwork. Some PCB circuits were laser-printed onto 100gsm paper (not acetate as we were expecting). The paper was then coated with cooking oil (rapeseed I think) and it magically turned translucent: a handy tip to remember for future screen printing - much cheaper than expensive acetate sheets!

we weren't sure, so coated both sides of our sheet with oil

About half an hour later (or however long it takes to drink a coffee in the sunshine, at the cafe opposite) and the screen was ready for exposing. We placed the image onto the exposure bed, then the screen on top and turned on the vacuum to help remove any air bubbles trapped between the image, screen and glass bed. It took a good ten minutes - but only because the exposure box was absolutely massive!


Matt at BuildBrighton always wanted to make a large exposure bed - but this one may be outside even what he was dreaming of!

A4 on medium-guage silk takes about ten minutes to expose, apparently. So after ten minutes, we took the screen out and blasted it with a high-pressure hose. The screen wasn't as dark as we'd expected (but apparently the emulsion continues reacting in sunlight, so is yet to get a bit darker) but after a minute or so of washing, the magic started to happen. All the "unbaked" emulsion washed away and we were left with a perfect copy of our PCB design on the silk:

(our pcb design was only eurocard sized, so we put a solder mask on the bottom half of the screen)

All in all, an amazing success.
The whole thing cost less than five euros and we got help and learned a lot about the how and why of screen printing. Why the screens need to be larger than the image, why different meshes are needed for printing onto different surfaces and so on.

It was a really interesting few hours and has given us a lot of confidence with our own homebrew screen printing rig. Of course, you can use professionally manufactured screens (there were some machine-stretched screens at the workshop which were tighter than a snare drum skin!) and spend a lot of money getting everything "just so". But you can also fudge it with some cobbled-together gear and a bit of know-how. And that's what got us excited.

All we need to do now is go back and see if we can actually print an image with the screen!


Monday, May 6, 2013

Updated Fridge Calendar Project Completed

Another great project was completed this afternoon at the Tinker's Workshop.  If you have not been following along you can catch up now with this post of the Fridge Calendar project that I just put the finishing touches on.


The design looks great with the new look of the rectangular pieces shown here. 


Here is a good shot of the original design along with the new improved design.  The new version has almost the same number size, smaller over all size, and three times less weight. Plus the months can be displayed not too.


Here's a good shot of all the pieces that are needed for the perpetual fridge calendar. 100 pieces in all were needed to be printed and put together for the project. 


There are 162 total pieces to make the calendar which includes the printed parts and the magnets that are mounted on the back of each piece. These pieces were Super Glued on to the back of each piece from a magnetic roll after cutting them to size with a scissors.  There were 100 pieces to print on my 3D printer in the project which took 36.5 hours to do.  Then add at least another 25-30 hours design time and the project got to be a lot bigger than it looks.  I think the effort was worth it in the end.  Not that I would begin to duplicate this project to start selling them any time soon.  I would think a mold would be the order of the day as it would be a lot cheaper and faster to produce. No matter what this will make a nice addition to my kitchen that my visitors can enjoy as much as I do now. 

Friday, May 3, 2013

The Sphero Peackeeper, a $10,000 Robotic Ball!



Do you have $10,000 laying around. If so, you could be able to purchase the Sphero Peacekeeper from Orbotix! Seriously though, how awesome would it be to have and iPad-controlled 150 pound robotic ball. You could drive it around your yard, chase cars, and torment your pets. Just kidding, this Sphero was just an April Fool's prank!

Thursday, May 2, 2013

The New Fridge Calendar Is Slowly Taking Shape

Today has been another long session of printing 3D parts for the Fridge Calendar project. Twelve hours to be exact.  A total of 100 parts will be needed to complete this project and at this point I am only up to part number forty-three.  This is what it looks like so far.


 It looks great already but printing the same parts over and over gets old very quickly.  Each of the smaller blue and red rectangular pieces  are the same and take 22 minutes each to print using my 3D printer.  The larger rectangular red pieces will display the months for the calender once I have the white inserts made.  These larger pieces take 34 minutes each to print so I am more than happy that I only have eight more to go.  Then on to the inserts for all the pieces.  Luckily each of these are different and will make this portion of the project a bit more interesting.  



The upper photo shows what the new calendar will look like once it is completed and the lower photo is what I have on my fridge right now. The new design looks a lot more up to date with the rectangular design and is a third of the weight of the original design.  Another thing that will be good with the new design is that the numbers and letters are all the same size as the original design but because of the new shape the amount of space that is need to display the calendar is reduced in size.  Hopefully I will complete this project by the weekend and I can get it put up on my fridge at that point.  I'll also keep a running tally of the hours I have on this project and be able to tell you the total cost to make this unusual calendar then too. Check back here to see the finished project. 

Tuesday, April 30, 2013

Vibrobot or Arduino Walker?

Now that we know that a handful of people are interested in the kits, we would like to know what kind. The first idea we had for a kit was a simple vibrobot. The robot would resemble a turtle with a circular battery pack, an LED, and a pager motor. It would use its paper clip legs to move around. On the other hand, we had an idea for an Arduino walker. The walker would resemble a four-legged insect with two servos and an Arduino as part of its back. In addition, an infrared sensor would allow the robot to become "aware" of its environment! So the question is, which one would you rather construct? Please vote below!


Arduino Walker kit or Simple Vibrobot
  
pollcode.com free polls 

Monday, April 29, 2013

Robots Protesting Against Killer Robots?



This is not the usual robotics post. It turns out that in Britain, some robots are campaigning against the killer, fully-automated robots used for warfare. Their argument is that, some day, these robots could confuse an innocent human with a hostile target. Another point that they have is that taking the "human" out of these fully-automated robots will cause major conflict. Conflict such as killing innocent people due to confusion of who's the bad guy and who's not. The question is, will this deadly conflict arise in the not-so-distant future?

Sunday, April 28, 2013

An Update On An Earlier Design

  Some time back I put together a project that I had in my mind for a long long time.  It has been on my refrigerator for just about as long and I am finally updating it to make it that much better.  As with most anyone who works on projects the second or even the third version always turns out better than the first go around. This is the case with this project.


I always liked the idea of having a calendar that I didn't have to throw away after only one year of use.  This is my original design that I put together.  It was made using PVC piping, foam core, and some decals that I printed using my computer and held on the fridge  using magnetic strips.  The only problem with the design was that parts were a lot of work to make and are a little bit heavier than I liked. I had no way of making the parts to display what month it was either.  So back to the drawing board for version two.


I started with my Makerbot 3D printer once again with the version on the left.  Same idea as the first version but I was not impressed with the part being mostly black.  So I reversed the printing of the part and also made it thinner. Which of course made it lighter.  The white looks a lot better.  It was a start.


This next version I scrapped the idea of just black and white and also went with a font that would make the number 6, 8, 9, or 0 work out well as the individual numbers were now printed on one white part and inserted into the red part.  Again the idea of having all the parts made circular did not thrill me much anymore so on to the new version on the right.  I changed the part to what you see here with the inner portion of the red frame being painted black.  This made the numbers stand out even more and would be simple to construct. The parts fit tight without having to glue them together. Always a plus.


With a few more tweaks and the help of Blender 3D graphics this is what the new design will look like once I get all the parts made using my 3D printer.  Each number part and day of the week will be 2 inches wide and 1 1/2 inches high.  The month (in blue) will be three inches wide and 1 1/2 inches high.  As before all parts will have a magnetic strip on the back so that it can easily be used on my fridge once again.  This way each month the calender can be easily changed to show whatever month it is. 
  The only other step will be to make a container to hold all the parts for storage  when the calender is not on my fridge along with possibly special parts to show holidays when it is.  A heart for Valentines day or a Christmas tree for Christmas for example.  I'll have to see what I can make up that will work on the 3D printer.  No matter what this new design will look a lot nicer on my fridge and people will stop asking me why the month is not displayed with everything else. Another plus with this project is that I figured that the total cost for the calender will be around $20 to make on my 3D printer.  A good deal for sure. 
  I'll post new photos of this project once I get further along with it to let you see how it all turns out.

Wan't to sell your electronics projects as kits? If so, join Club Jameco



Do you have an upcoming electronics project that you would like to share or even sell? If so, I highly recommend Club Jameco. Club Jameco is an online club where you can design, share, and sell your electronics projects. Users can join the club, and submit their projects to Jameco. If it gets approved, Jameco will produce it, sell it, and give you a commission for every sale! If you're interested in participating in this electronics club, then visit clubjameco.com!

Thursday, April 25, 2013

Working UV controller

Our UV controller is finally complete.
We didn't have any rotary encoders to hand so fudged it with a rotary potentiometer and some push buttons. If we were to make this again, we'd go for a slightly more sophisticated menu system and use a single rotary encoder with button built in. But this will do for now!




Here's the component layout. There are loads of extra connections for power and ground connections. There's nothing worse than soldering up a board and having to tack two or more wires together because there's nowhere on the board to add on another ground wire or power cable. This time, we made sure there are plenty!



Define CONF_WORD = 0x3f71
Define CLOCK_FREQUENCY = 4
AllDigital

Config PORTA = Input
Config PORTB = Output
Config PORTC = Output
Config PORTD = Output
Config PORTE = Output

Symbol white_led_pin = PORTD.1
Symbol uv_led_pin1 = PORTD.2
Symbol uv_led_pin2 = PORTD.3
Symbol uv_led_pin3 = PORTC.4
Symbol uv_led_pin4 = PORTC.5
Symbol uv_led_pin5 = PORTD.4
Symbol uv_led_pin6 = PORTD.5
Symbol uv_led_pin7 = PORTD.6
Symbol uv_led_pin8 = PORTB.4
Symbol speaker = PORTE.2

PORTB = 0x00
PORTD = 0x00

ADCON1 = 00001110b 'AN0 is analogue, all others digital
Define ADC_CLOCK = 3
Define ADC_SAMPLEUS = 50

Dim state As Byte
Dim lastmenu As Word
Dim menu As Word
Dim tmpw As Word
Dim tmp As Byte
Dim setting As Byte
Dim lastsetting As Byte
Dim uvled As Byte
Dim brightwhite As Byte
Dim brightuv As Byte
Dim buttonpressed As Byte
Dim timermins As Byte
Dim timersecs As Byte
Dim intrcnt As Byte
Dim seccount As Word
Dim redrawtimer As Byte

Dim led_counter As Byte

Define LCD_BITS = 4
Define LCD_DREG = PORTB
Define LCD_DBIT = 0
Define LCD_RSREG = PORTB
Define LCD_RSBIT = 5
Define LCD_RWREG = PORTB
Define LCD_RWBIT = 6
Define LCD_EREG = PORTB
Define LCD_EBIT = 7
Define LCD_READ_BUSY_FLAG = 0
Define LCD_DATAUS = 200
Define LCD_INITMS = 200

init:
     'allow everything to settle
     WaitMs 500

     Lcdinit 0 '0=no cursor
     Lcdcmdout LcdClear
     state = 0
     menu = 0
     lastmenu = 0
     setting = 0
     lastsetting = 0
     uvled = 0
     buttonpressed = 0
     intrcnt = 0
     redrawtimer = 0
     led_counter = 0
     
     Read 2, brightwhite
     Read 3, brightuv

     If brightwhite > 10 Then brightwhite = 10
     If brightuv > 10 Then brightuv = 10

     timermins = 1
     timersecs = 28

     Gosub resettimer

     'create a timer to fire every 1m/s
     '(we use this for the countdown AND to control the LEDs)
     
     T1CON = 0x00
     T1CON.T1OSCEN = 1
     T1CON.TMR1CS = 0
     T1CON.TMR1ON = 0
     Gosub preload_timer

     'enable global and peripheral interrupts
     INTCON.GIE = 1
     INTCON.PEIE = 1
     PIE1.TMR1IE = 1
     PIR1.TMR1IF = 0
     
     state = 0

loop:

     '-----------------------------------------------------------------------
     'read the value from RA0 to see if the user has changed the menu setting
     '-----------------------------------------------------------------------
     Adcin 0, menu
     If menu > lastmenu Then
          tmpw = menu - lastmenu
     Else
          tmpw = lastmenu - menu
     Endif

     If tmpw > 3 Then
          'dial has been moved more than the error in reading the A/C
          'so convert this to a setting value
          If menu >= 0 And menu < 340 Then
               'setting one:
               setting = 1
          Else
               If menu >= 340 And menu < 680 Then
                    'centre position: off
                    setting = 0
               Else
                    If menu >= 680 And menu <= 1024 Then
                         'setting two
                         setting = 2
                    Endif
               Endif
          Endif
     Endif

     '----------------------------------------------------
     'if the setting has changed, update the state machine
     '----------------------------------------------------
     If setting <> lastsetting Then

          'stop the timer
          T1CON.TMR1ON = 0
     
          'turn off any leds (they'll come back on again in a minute)
          Low white_led_pin 'turn off the white LED(s)
          Gosub turn_uv_off

          'something has changed: update the state as necessary
          Select Case setting
               Case 1 'start the uv leds again
               state = 1
               uvled = 1

               Case 0
               state = 0
               uvled = 0 'turn off the UV LEDs
               
               Case 2
               state = 2
               uvled = 0 'make sure uv LEDs are off

          EndSelect
     Endif

     lastsetting = setting
     lastmenu = menu


     '---------------------
     'decide what to do now
     '---------------------
     Select Case state
          Case 0
          'redraw the opening menu
          Gosub drawmenu1a
          state = 3 'wait for user to select a menu option

          Case 1
          'start flashing the UV LEDs, one row at a time
          Gosub resettimer
          Lcdcmdout LcdClear
          Lcdout "Time remaining:"
          Gosub drawtimer
          state = 5
          redrawtimer = 0
          'start timer1 interrupt
          Gosub preload_timer
          T1CON.TMR1ON = 1
          
          Case 2
          'show the white LEDs only
          Read 2, brightwhite
          If brightwhite > 10 Then brightwhite = 10
          Gosub drawmenu2
          Gosub turn_uv_off
          'start timer1 interrupt
          Gosub preload_timer
          T1CON.TMR1ON = 1
          state = 4 'wait for the user to select up/down

          Case 3
          'user can select between brightness and timer or hit select
          Gosub is_button_one_pressed
          If buttonpressed = 1 Then
               Gosub drawmenu1b
               state = 7
          Endif
          
          Gosub is_button_two_pressed
          If buttonpressed = 1 Then
               state = 8
               Gosub drawmenu4
          Endif

          Case 4
          'White LED is running
          'user can press up/down to select the white light brightness
          Gosub is_button_one_pressed
          If buttonpressed = 1 Then
               If brightwhite < 10 Then
                    brightwhite = brightwhite + 1
                    Gosub drawmenu2
               Endif
          Endif

          Gosub is_button_two_pressed
          If buttonpressed = 1 Then
               Low white_led_pin
               Write 2, brightwhite
               Gosub drawsaved
               For tmp = 1 To 10
                    WaitMs 200
               Next tmp
               Gosub drawmenu2
          Endif

          Gosub is_button_three_pressed
          If buttonpressed = 1 Then
               If brightwhite > 0 Then
                    brightwhite = brightwhite - 1
                    Gosub drawmenu2
               Endif
          Endif
                    

          Case 5
          'UV lights are running
          'user can press up/down to increase/decrease the CURRENT time by one sec
          Gosub is_button_one_pressed
          If buttonpressed = 1 Then
               Gosub increase_time_one_sec
               redrawtimer = 1
          Endif

          Gosub is_button_three_pressed
          If buttonpressed = 1 Then
               Gosub decrease_time_one_sec
               redrawtimer = 1
          Endif
          
          If redrawtimer = 1 Then
               redrawtimer = 0
               Gosub drawtimer
          Endif

          'check the timer hasn't hit zero
          If timermins = 0 And timersecs = 0 Then
               'stop the timer
               T1CON.TMR1ON = 0
               'draw zero zero
               Gosub drawtimer
               'time out, do nothing until menu selector moved
               Low white_led_pin
               Gosub turn_uv_off
               state = 6
          Endif
               
          Case 6
          'play a tune
          FreqOut speaker, 440, 200
          FreqOut speaker, 392, 200
          FreqOut speaker, 440, 200
          FreqOut speaker, 392, 200
          state = 13

          Case 7
          'user can select between brightness and timer or hit select
          Gosub is_button_three_pressed
          If buttonpressed = 1 Then
               Gosub drawmenu1a
               state = 3
          Endif
          Gosub is_button_two_pressed
          If buttonpressed = 1 Then
               Read 3, brightuv
               If brightuv > 10 Then brightuv = 10
               Gosub drawmenu3
               state = 9
          Endif

          Case 8
          'set the timer minutes
          Gosub is_button_three_pressed
          If buttonpressed = 1 Then
               state = 10
               Gosub drawmenu4
          Endif
          Gosub is_button_two_pressed
          If buttonpressed = 1 Then
               state = 11
               Gosub drawmenu4
          Endif

          Case 9
          'set the UV brightness
          'user can press up/down to select the UV light brightness
          Gosub is_button_one_pressed
          If buttonpressed = 1 Then
               If brightuv < 10 Then
                    brightuv = brightuv + 1
                    Gosub drawmenu3
               Endif
          Endif

          Gosub is_button_two_pressed
          If buttonpressed = 1 Then
               Gosub turn_uv_off
               Write 3, brightuv
               Gosub drawsaved
               For tmp = 1 To 10
                    WaitMs 200
               Next tmp
               Gosub drawmenu1a
               state = 3
          Endif

          Gosub is_button_three_pressed
          If buttonpressed = 1 Then
               If brightuv > 0 Then
                    brightuv = brightuv - 1
                    Gosub drawmenu3
               Endif
          Endif


          Case 10
          'set the UV timer seconds
          Gosub is_button_one_pressed
          If buttonpressed = 1 Then
               state = 8
               Gosub drawmenu4
          Endif
          Gosub is_button_two_pressed
          If buttonpressed = 1 Then
               state = 12
               Gosub drawmenu4
          Endif
          
          Case 11
          'use up/down to change the timer minutes
          Gosub is_button_one_pressed
          If buttonpressed = 1 Then
               Gosub increase_time_one_min
               Gosub drawmenu4
          Endif
          Gosub is_button_three_pressed
          If buttonpressed = 1 Then
               Gosub decrease_time_one_min
               Gosub drawmenu4
          Endif
          Gosub is_button_two_pressed
          If buttonpressed = 1 Then
               Write 10, timermins
               Write 11, timersecs
               state = 8
               Gosub drawmenu4
          Endif

          Case 12
          'use up/down to change the timer seconds
          Gosub is_button_one_pressed
          If buttonpressed = 1 Then
               Gosub increase_time_one_sec
               Gosub drawmenu4
          Endif
          Gosub is_button_three_pressed
          If buttonpressed = 1 Then
               Gosub decrease_time_one_sec
               Gosub drawmenu4
          Endif
          Gosub is_button_two_pressed
          If buttonpressed = 1 Then
               Write 10, timermins
               Write 11, timersecs
               state = 10
               Gosub drawmenu4
          Endif

          Case 13
          'do nothing: wait for the user to move the menu selector to something else

     EndSelect
Goto loop
End

drawmenu1a:
     Lcdcmdout LcdClear
     Lcdout " UV brightness "
     Lcdcmdout LcdLine2Home
     Lcdout " UV timer <"
Return

drawmenu1b:
     Lcdcmdout LcdClear
     Lcdout " UV brightness <"
     Lcdcmdout LcdLine2Home
     Lcdout " UV timer "
Return

drawmenu2:
     Lcdcmdout LcdClear
     Lcdout "Brightness: "
     If brightwhite < 100 Then Lcdout " "
     If brightwhite < 10 Then Lcdout " "
     Lcdout #brightwhite
Return

drawmenu3:
     Lcdcmdout LcdClear
     Lcdout "Brightness: "
     If brightuv < 100 Then Lcdout " "
     If brightuv < 10 Then Lcdout " "
     Lcdout #brightuv
Return

drawmenu4:
     Lcdcmdout LcdClear
     Lcdout " Minutes: "
     If timermins < 10 Then Lcdout " "
     Lcdout #timermins
     If state = 8 Then Lcdout " <"
     Lcdcmdout LcdLine2Home
     Lcdout " Seconds: "
     If timersecs < 10 Then Lcdout " "
     Lcdout #timersecs
     If state = 10 Then Lcdout " <"
Return

drawsaved:
     Lcdcmdout LcdClear
     Lcdout " Settings saved"
Return

resettimer:
     Read 10, timermins
     Read 11, timersecs

     If timermins = 255 Then timermins = 1
     If timersecs = 255 Then timersecs = 30

     If timermins = 0 And timersecs = 0 Then
          timermins = 1
          timersecs = 30
     Endif
Return

increase_time_one_sec:
     timersecs = timersecs + 1
     If timersecs > 59 Then
          timersecs = 0
          timermins = timermins + 1
     Endif
Return

decrease_time_one_sec:
     If timersecs > 0 Then
          timersecs = timersecs - 1
     Else
          If timermins > 0 Then
               timersecs = 59
               timermins = timermins - 1
          Else
               'timer is already at zero
               timersecs = 0
               timermins = 0
          Endif
     Endif
Return

increase_time_one_min:
     If timermins < 99 Then
          timermins = timermins + 1
     Endif
Return

decrease_time_one_min:
     If timermins > 0 Then
          timermins = timermins - 1
     Endif
Return

drawtimer:
     Lcdcmdout LcdLine2Home
     Lcdout " "
     If timermins < 10 Then Lcdout " "
     Lcdout #timermins
     Lcdout "m "
     If timersecs < 10 Then Lcdout " "
     Lcdout #timersecs
     Lcdout "sec"
Return

is_button_one_pressed:
     buttonpressed = 0
     If PORTA.1 = 1 Then
          'debounce the button press
          WaitMs 1
          If PORTA.1 = 1 Then
               While PORTA.1 = 1
                    'do nothing
               Wend
               buttonpressed = 1
          Endif
     Endif
Return

is_button_two_pressed:
     buttonpressed = 0
     If PORTA.2 = 1 Then
          'debounce the button press
          WaitMs 1
          If PORTA.2 = 1 Then
               While PORTA.2 = 1
                    'do nothing
               Wend
               buttonpressed = 1
          Endif
     Endif
Return

is_button_three_pressed:
     buttonpressed = 0
     If PORTA.3 = 1 Then
          'debounce the button press
          WaitMs 1
          If PORTA.3 = 1 Then
               While PORTA.3 = 1
                    'do nothing
               Wend
               buttonpressed = 1
          Endif
     Endif
Return

preload_timer:
     'at 4mhz (crystal) we have 1m clock cycles per second
     'if we count up to 1,000 that's 1 millisecond
     'so let's preload timer1 with 65,535 - 1,000 = 64,535
     '(which in hex is 0xFC17)
     TMR1H = 0xfc
     TMR1L = 0x17
Return

turn_uv_off:
     Low uv_led_pin1
     Low uv_led_pin2
     Low uv_led_pin3
     Low uv_led_pin4
     Low uv_led_pin5
     Low uv_led_pin6
     Low uv_led_pin7
     Low uv_led_pin8
Return

turn_uv_on:
     High uv_led_pin1
     High uv_led_pin2
     High uv_led_pin3
     High uv_led_pin4
     High uv_led_pin5
     High uv_led_pin6
     High uv_led_pin7
     High uv_led_pin8
Return

On Interrupt
     Save System
     If PIR1.TMR1IF = 1 Then
          PIR1.TMR1IF = 0
          Gosub preload_timer
          seccount = seccount + 1
          If seccount >= 1000 Then
               seccount = 0
               Gosub decrease_time_one_sec
               redrawtimer = 1
          Endif

          led_counter = led_counter + 1
          If led_counter > 10 Then led_counter = 0

          Select Case state
               Case 4
               If led_counter >= brightwhite And brightwhite < 10 Then
                    Low white_led_pin
               Else
                    High white_led_pin
               Endif

               Case 5
               If led_counter >= brightuv And brightuv < 10 Then
                    Gosub turn_uv_off
               Else
                    Gosub turn_uv_on
               Endif
          EndSelect

     Endif
Resume


Here's a video showing the final UV controller:



In the end we went for a 10ms PWM on the LEDs. Originally we thought 30 would be fine, but it introduced noticeable flicker on the LEDs when running normally. Although the camera shows flicker on the white LEDs, in reality they appear as uniformly lit.

The video demonstrates:

  • variable UV brightness
  • changing the countdown time-out value
  • an amazingly melodic tune when the countdown has elapsed 
  • using variable brightness white LEDs for aligning silkscreen masks before exposure


That's all the electronics done - now we need to find a suitable box to enclose it all in and actually start making some silkscreens!

Using PIC Timer1 for PWM

At the heart of our UV controller is the Timer1 module and some clever use of PWM.
Let's look at PWM first of all - this is how we keep the lights on. The basic idea is this - persistence of vision gives us the illusion that an LED is light either dimly or brightly, when in fact it's flickering on and off so quickly that we can't see it flashing. The more time it spends on, the brighter it appears:




This is how most people imagine PWM working - in a "serial" manner (one instruction after another, with a suitable delay built in). For an 80% duty cycle, for example, the code might go something like this:

LED_on
Wait 1ms

LED_on
Wait 1ms
LED_on
Wait 1ms
LED_on
Wait 1ms
LED_off
Wait 1ms

Or even, more simply
LED_on
Wait 4ms
LED_off
Wait 1ms

and just repeat/loop.
In fact, a lot of servo controllers work this way - which is where the theoretical limit of 8 serially controlled servos per board comes from. A lot of times you see this for pwm - the code exactly matches the timing graph: send a pin high, wait the appropriate of time, send it low, wait out the remainder of the total cycle time.

And it works. But it's not the best way to control stuff via PWM. Let's say we've got some other stuff going on (like reading an analogue input, writing to a character display and so on). What happens now?

LED_on
Wait 4ms
LED_off
Wait 1ms
Check the analogue input
Write to the display
Loop

The problem is that doing all the other stuff, not just flickering the LEDS, takes up a lot of time. So our LEDs are on for 4ms and off for 1ms, and on for 4 out of 5 milliseconds gives us a duty cycle of 80% over 5ms. But if all this other stuff takes a few milliseconds to complete too:

On for 4ms
Off for 1ms
Stays off for 20ms while we check the inputs, update the LCD display and so on
Our 80% duty cycle is now actually on for 4ms, off for 1ms, stays off for 20ms, so on for 4 out of 25ms gives us a duty cycle of just 16%. And, because we've increased the total amount of time between each cycle, the flickering of the LEDs, on and off, becomes noticeable to the eye.

What we need is a way of getting the LEDs to update themselves, while we dedicate as much mcu power and time to the other tasks as necessary. What we need, in short, is a timer interrupt!

In our UV controller we're going to use our timer to do two things. Firstly it'll keep track of time (since we have a countdown timer from switching on the UV LEDs so that we don't overexpose any screens/boards). But secondly, we'll use it to as a "clock" for our PWM. 

Instead of saying "turn on, do nothing while a set time elapses, turn off, do nothing while more time passes" we're going to use an interrupt, which is more akin to: "turn on the LEDs, then I'm off over here to do something else - give me a shout when that time is up". After the "on" part of the PWM time has elapsed "turn off the LEDs, then give me a shout when it's time to turn them on again".

In fact, we're going to use a slightly hybrid version of the two approaches. We're using an interrupt to run some code every 1 millisecond. At this point we'll ask "should the LEDs be on of off?" and act accordingly. We'll also keep a counter running and increase it by one; when the counter hits 1000, we know that exactly one second has passed, and adjust our countdown time by one second.  Here's how we set up Timer1 in Oshonsoft:


'create a timer to fire every 1m/s
'(we use this for the countdown AND to control the LEDs)

T1CON = 0x00
T1CON.T1OSCEN = 1
T1CON.TMR1CS = 0
T1CON.TMR1ON = 0
Gosub preload_timer

'enable global and peripheral interrupts
INTCON.GIE = 1
INTCON.PEIE = 1
PIE1.TMR1IE = 1
'clear the timer-has-tripped interrupt flag
PIR1.TMR1IF = 0


Our sub-routine preload_timer simply sets a value so that we get the timer interrupt to fire every millisecond. This can take a bit of thinking about, and there are "make-it-easy" calculators all over the 'net but they can sometimes just confuse the matter. Simply put, Timer1 is actually a 16-bit counter. Every clock instruction it increases by one. When it reaches 65,535 and rolls over to zero, the Timer1 interrupt is called. That's basically it.

What we need to do is work out what number to start counting from, so that when the mcu hits 65535 limit and rolls over to zero, exactly the right number of clock instructions have been carried out to indicate one second has elapsed. The number we need to count up to is critically dependent on the speed we're running the mcu at (the crystal value we're running it from). In our case, we're using a 4Mhz crystal.

A 4Mhz crystal means 1 million instructions are carried out every second. So 1000 instruction cycles (one instruction takes 4 clock cycles remember!) occur every millisecond. If we set our Timer1 value so that it only counts up to 1,000 instead of 65,535 before firing the interrupt, we should get a 1ms interrupt. Instead of starting at zero, we need to preload our Timer1 value with 65535-1000 = 64535. In hex, 64535 is 0xFC17

preload_timer:
'at 4mhz (crystal) we have 1m clock cycles per second
'if we count up to 1,000 that's 1 millisecond
'so let's preload timer1 with 65,535 - 1,000 = 64,535
'(which in hex is 0xFC17)
TMR1H = 0xfc
TMR1L = 0x17
Return

Now, every millisecond, wherever we are in our program, and whatever we're doing, everything will get parked and code execution jumps to the interrupt routine. When we're done here, we'll jump back to exactly where we were in the code, before we were so rudely interrupted. The interrupt routine needs to keep track of the number of milliseconds passed, and update the LEDs as necessary:


On Interrupt
     Save System
     
     'If it's Timer1 that caused the interrupt:
     If PIR1.TMR1IF = 1 Then
          'set the new timer value
          Gosub preload_timer
         
          'clear the interrupt flag
          PIR1.TMR1IF = 0
         
          'increase our (milli)second timer
          seccount = seccount + 1
          'if one whole second has passed, update the clock
          If seccount >= 1000 Then
               seccount = 0
               Gosub decrease_time_one_sec
               redrawtimer = 1
          Endif
          
          'increase our LED PWM wave
          led_counter = led_counter + 1
          If led_counter > 30 Then led_counter = 0

          'decide whether the LED(s) should be on or off
          Select Case state
               Case 4
               If led_counter >= brightwhite And brightwhite < 30 Then
                    Low white_led_pin
               Else
                    High white_led_pin
               Endif

               Case 5
               If led_counter >= brightuv And brightuv < 30 Then
                    Gosub turn_uv_off
               Else
                    Gosub turn_uv_on
               Endif
          EndSelect

     Endif
Resume

In this way, we still have PWM controlled LEDs - they are on for part of a time and off for a specific set time - but instead of just hanging around doing nothing between the on and off phases, we're freed up to run other code and the LEDs will just "look after themselves".

As well as a millisecond counter, we'll just keep an "LED brightness counter" which also increases every millisecond - but only counts up to 30 then resets to zero instead of 1,000 (as our clock counter does).  In our code, we're using a brightness level from zero to 30. All we do is see if the current LED brightness counter is less than the required LED brightness and if it is, turn the LEDs on, otherwise turn them off.

This means that one PWM cycle lasts 30 milliseconds (we don't want to make this too large else visible flickering will appear on the LEDs). We don't have a fine degree of control over the "duty cycle". We can't, for example, set it to 1%, but we have up to 30 different values we can use which equate to
1/30 = about 3%
2/30 = about 7%
3/30 = about 10%
4/30 = about 13%
and so on.

Enough with the theory - let's get this coded up and some new firmware burned onto our almost-working hardware!

Wednesday, April 24, 2013

Working UV controller (sort of)

After making a PCB and finding that it didn't work, and then making up a breadboard version of the circuit and making it work, we're back to making a PCB version of the UV controller circuit - and hoping that it works this time!


Here's a new controller PCB with some simplified firmware on it, simply writing a message to the character LCD. With a bit of jiggery-pokery we managed to get the display to work on our new PCB.


Wiring up the rest of the circuit and putting it on it's own dedicated power supply (running it off a little PIC programmer isn't going to be a good idea if all these LEDs suddenly spring into life!) and everything is still working fine:


The only problem now, of course, is we're running simplified firmware which just writes "Hello" onto the character LCD - the darlington arrays aren't being set so random lines of UV LEDs currently light up each time the circuit is powered up.


But it's encouraging to see that the whole thing still (appears to be) working. Even with a couple of rows of LEDs lit (we'll only ever have one row lit at any one time, and strobe them quickly using PWM to control the brightness) the message on screen remains the same, and we put a start-up sequence in place so that we could see if the PIC is rebooting. It's possible that when a row of LEDs lights up it might create a dip in the voltage supply and reset the microcontroller. A few smoothing caps on the supply line(s) - you can see one on the supply to the LCD in the photo above) and everything is good - no mcu resetting, no ripples, nothing.

So in theory all that remains is to put the "proper" firmware onto the PIC and shove the whole lot into an box and we're ready to try creating our own miniature silkscreen for PCB printing....

16x2 character LCD R/W pin is important

Today's lesson is don't be a jerk and always breadboard your prototypes first. It doesn't matter how clever you think you are (read: I think I am) nor how many successful circuits you've made in the past, test stuff before committing to a PCB design. It's easy to say that now....

Despite protestations from a few others, I was confident that the uv controller circuit was relatively simple and didn't need breadboarding. After all, we've made a few different circuits with character LCDs, we've made pcbs with transistors and speakers on acting as crude amplifiers. We've made no end of twisty-pots-as-input type devices. It's surely just a case of pulling all these bits together? Why waste time plugging wires into a board when you know it'll work anyway....?

Well, the first attempt just didn't work, and it took me about two hours to discover why - eventually (and reluctantly) making up the entire circuit on a breadboard to do some debugging.

As is often the case, the problem came down to a faulty assumption. I was assuming that because we never read data from the character display, only write to it, that the R/W pin didn't need to be controlled from the PIC - just tie it to ground (the datasheet said 0=write, 1=read) and could save a pin on (or rather save time running a trace back to) the mcu. The only change I made on the breadboard prototype (keeping the original firmware from the non-working board) was that the RW pin was connected (in firmware I left the code for controlling it in the first time around, just never bothered connecting the r/w pin on the LCD to the PIC). Everything else was exactly the same as before.


And there you have it - a working character LCD (complete with blinking LED to show us that the PIC is alive and running code!). And all because of a faulty assumption and a determination to save a few minutes assembly by leaving out the R/W pin.

That worked out well then.

Tuesday, April 23, 2013

UV lightbox controller

Another update on our UV lightbox controller. We coded up some firmware, finished soldering the board, added the buttons and potentiometers and connected up the 16x2 character LCD.


The speaker was added as something of an afterthought, but it might be nice to have a sounder when the timer expires to indicate the end of UV exposure time. The LCD has a 0.1" pin header soldered across pins 1-16 and is connected via an IDE ribbon cable soldered directly to the PCB. We powered up the board and....


...no dice. After tweaking the contrast on the display, we saw only a bunch of empty blocks. For this reason, we're not posting the schematics or firmware here, until something is working! But we know for sure that at least the backlight is connected correctly.


But just as soon as it is, we'll have a follow up post with more details!

Laser cutting mirorred acrylic

When cutting mirrored acrylic, always cut with the mirrored side down. This means you need to flip the text so it cuts out the right way around! It only took us one attempt to get it wrong before cutting out a massive 58 different names for wedding table place holders.


28 minutes of solid lasering at 18mm/sec and 15mA and the entire A4 sheet was done


Another eight minutes and 58 bases were cut out from black acrylic too. The end result(s) look something like this:


The font used was Cheri and the letters were separated in Inkscape and pushed together to make one continuous shape ready for cutting. About an hour's work, and we've all the place-names to make one couple even happier on their big day!

Making through-hole LEDs from SMT versions

Often when people make the move from through-hole components to SMT, there's no going back. Why bother with all that messy drilling and flipping boards over and soldering and trimming legs, when you can simply plop a miniaturised component onto the board with a bit of paste and solder it up in a fraction of the time?

But because our prototype PCB for the board game module is single-sided, we're having to solder little wires from the "under" side of the board to the "top" to solder our tiny little SMT LEDs to. In short, we're making through-hole style LEDs from some 1206-sized SMT versions



This allows us to mount each LED in the tiny space alongside each of the (5mm high) pushbuttons. Regular LEDs - even the small 3mm versions - are far too tall and we didn't fancy trying mounting the LEDs on their side to get them to fit, so the obvious answer was to make some ultra-low profile through-hole LEDs.


The final board, soldered up and ready for testing:


In the final (production) version, we're going to make the PCB double-sided and put SMT pads on the top surface alongside the pushbuttons. This should not only make assembly easier, but much quicker in the long run!