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!
Tuesday, April 30, 2013
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!
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:
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!
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
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
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.
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!
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
Wait 1ms
LED_on
Wait 1ms
Wait 1ms
LED_off
Wait 1ms
Or even, more simply
LED_on
Wait 4ms
LED_off
Wait 1ms
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
'(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
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....
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.
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!
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
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!
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!
Sunday, April 21, 2013
Justin's LED flashing UFO birthday card
Ok, maybe the red LED is just a little brighter - or perhaps should have been put on one of the "wider" or closer-looking beams. Or maybe replaced with a green/yellow LED. Or maybe had a higher value inline resistor or something.
But the end result is pretty cool....
But the end result is pretty cool....
Friday, April 19, 2013
Designs to be made for the first SimpleBotics robot
Three years ago, I came up with an idea of a business centered around robotics. So when I go off to college, I could start a company (Just like Jobs and Woz). The result was SimpleBotics. As of now, I believe it is appropriate to start thinking about an actual product. This is why I, along with a few others, will be taking the necessary steps in making this product actually exist. The real question is, will you buy one? Please vote below
UFO birthday card with flashing LEDs - final pictures
Last night, at BuildBrighton, we helped Justin to get his flashing-LEDs-and-UFOs birthday card ready for this weekend. And, as ever, Steve made it not just look like a finished "product" but like the most awesome you wished you owned yourself. Where most of us would just slap a sticker on the top and call it done, Steve went the extra mile to make it really special!
The key to making this look so awesome was the thin layer of styrene immediately over the top of the LEDs - providing a single, flat surface for the image to sit on top of.
A photo-quality print is lined up and fixed over the entire board using Photo Mount spray...
... and the excess trimmed off. The print on the surface is pale enough that the LEDs shine through the relatively lightweight (90gsm?) photo paper meaning no nasty cutouts or holes required in the image surface.
Instead of just stopping there, Steve went to the trouble of making a striking green frame for the image, complete with sliding on-off switch to stop the battery from draining too quickly.
With a 5v regulator onboard, the LEDs can be powered from a single PP3 9v battery.
Having mounted the image onto the uprights, a simple - but effective - acrylic border is placed around the outside of the photo and fixed in place with hot glue.
The key to making this look so awesome was the thin layer of styrene immediately over the top of the LEDs - providing a single, flat surface for the image to sit on top of.
A photo-quality print is lined up and fixed over the entire board using Photo Mount spray...
... and the excess trimmed off. The print on the surface is pale enough that the LEDs shine through the relatively lightweight (90gsm?) photo paper meaning no nasty cutouts or holes required in the image surface.
Instead of just stopping there, Steve went to the trouble of making a striking green frame for the image, complete with sliding on-off switch to stop the battery from draining too quickly.
With a 5v regulator onboard, the LEDs can be powered from a single PP3 9v battery.
Having mounted the image onto the uprights, a simple - but effective - acrylic border is placed around the outside of the photo and fixed in place with hot glue.
The final (amazing-looking) finished product. Hopefully we've done enough to help Justin earn a few brownie points for this one ;-)
LED UFO birthday greetings card
Thursday nights are BuildBrighton Open Nights, which means getting stuck in and helping out with other people's projects a lot of the time. Last night it happened to be Justin who needed a little help completing a fancy greetings card. It's his girlfriend's birthday soon and which girl couldn't help but be impressed by a hand-made birthday card? With UFOs on it. And flashing LEDs? (no, seriously, she loves this kind of stuff).
So Justin picked an image of the interwebs and set about making a design. He found a picture of some UFOs and decided to have a number of LEDs light up in a sequence, to give the image of "tractor beams" rising up from the ground:
The basic idea is that at any one time, all the same numbered LEDs are on together. So firstly all the ones come on, then a few hundred milliseconds later, all the twos, then all the threes and so on. This gives each beam it's own animation, while allowing multiple beams to be controlled from a single PIC output pin.
It's going to be a battery operated card, and - unlike an earlier, similar project - we've convinced Justin to invest some time in making a PCB rather than just poking LEDs into the card and soldering them all up with wire on the back! We're trying to avoid through hole components here, because of the little spiky legs on the back - so its SMT all the way; for LEDs, resistors and microcontroller. Initially we thought of using the 16F1825 with it's low pin count, but in the end opted for a 16F628a because it runs from an internal oscillator (no need for an external, chunky crystal) at the lower rate of 4Mhz, not 32Mhz as per the '1825. Nothing on the card is time critical (it has no serial comms, for example) and, generally, the lower clock speed means lower power consumption when running (so hopefully those LEDs can stay flashing for a little bit longer).
Normally we wouldn't bother making up a schematic for something like this - it's just bunches of LEDs being driven directly from the output pin of a PIC. But the offset/staggered nature could cause a few headaches when laying out the board, so out came ExpressPCB...
While it's a really simple circuit, the key thing is that when this is linked to the PCB layout software in ExpressPCB, it, rather handily, lights up all connected pads when you click on them. This makes laying out our PCB so much easier than just trying to do it all in one go off the top your head! The numbering is a bit weird - we used 1,2,3,4 for the first "row" of LEDs, then 11,12,13,14 for the next row and so on. So when we come to run the code, we'll switch on 1,2,3,4 together, then 11,12,13,14 together, then 21,22,23,24 - basically the "tens column" of the number represents the current row to be lit.
Because the LEDs need to line up with a printed image, it was important to position each LED exactly in the right place. We used our old friend Inksc(r)ape to do this. First, we imported the bitmap to be used and resized it to the size of our eurocard copper board (100mm x 160mm). Then we simply draw 1206 sized rectangles and positioned them over the image:
The painstakingly slow bit was selecting each LEDs, noting it's position (in mm) and transferring these positions to the 1206 LEDs drawn in ExpressPCB.
It was slow going, but we got there in the end!
Because the schematic and the PCB layout were linked, whenever a single pad on one LED was clicked, all the others that it needed to be connected to lit up. This made routing the traces a little bit easier, and also ensured that no LED was inadvertently left out
To make assembly easier later on down the line, we positioned every LED with the ground pin on the same side; it would have made routing easier not to do this, but there would be nothing worse than ending up with a failed board because one or more LEDs were soldered the wrong way around!
So after about an hour or so of routing and re-routing traces, we ended up with a single-sided PCB with no jumpers (since the board will be hand assembled, vias were a big no-no as they'd introduce more nasty spiky bits where bits of wires had to be soldered)
Because we're using press-n-peel (which reverses an image during transfer) AND entirely surface mount components, the whole PCB needs to be flipped before printing (so after transfer, it's back the right way around)
That's all the hard work done! Now it's just a matter of etching the board and sticking the LEDs onto it! Here's the board fresh from the ferric chloride:
The PIC was programmed using a SOIC-clip and a homemade programming cable (the PIC was already mounted onto the PCB before it was programmed).
And there you have it. A cycling, flashing LED battery-operated UFO-themed greetings card. And all for a few hours work.
As it was getting late by the time the board was assembled and working, Steve took it away to produce the sticker front and hid all the scary looking electronics away. Hopefully a follow up post will show the final, working card in all it's glory.....
So Justin picked an image of the interwebs and set about making a design. He found a picture of some UFOs and decided to have a number of LEDs light up in a sequence, to give the image of "tractor beams" rising up from the ground:
The basic idea is that at any one time, all the same numbered LEDs are on together. So firstly all the ones come on, then a few hundred milliseconds later, all the twos, then all the threes and so on. This gives each beam it's own animation, while allowing multiple beams to be controlled from a single PIC output pin.
It's going to be a battery operated card, and - unlike an earlier, similar project - we've convinced Justin to invest some time in making a PCB rather than just poking LEDs into the card and soldering them all up with wire on the back! We're trying to avoid through hole components here, because of the little spiky legs on the back - so its SMT all the way; for LEDs, resistors and microcontroller. Initially we thought of using the 16F1825 with it's low pin count, but in the end opted for a 16F628a because it runs from an internal oscillator (no need for an external, chunky crystal) at the lower rate of 4Mhz, not 32Mhz as per the '1825. Nothing on the card is time critical (it has no serial comms, for example) and, generally, the lower clock speed means lower power consumption when running (so hopefully those LEDs can stay flashing for a little bit longer).
Normally we wouldn't bother making up a schematic for something like this - it's just bunches of LEDs being driven directly from the output pin of a PIC. But the offset/staggered nature could cause a few headaches when laying out the board, so out came ExpressPCB...
(we've drawn every bunch of LEDs as four, although sometimes on the card, only two or three LEDs light up at a time - it was just easier to draw them all in - copy and paste went mad - and then just not include them on the PCB later)
While it's a really simple circuit, the key thing is that when this is linked to the PCB layout software in ExpressPCB, it, rather handily, lights up all connected pads when you click on them. This makes laying out our PCB so much easier than just trying to do it all in one go off the top your head! The numbering is a bit weird - we used 1,2,3,4 for the first "row" of LEDs, then 11,12,13,14 for the next row and so on. So when we come to run the code, we'll switch on 1,2,3,4 together, then 11,12,13,14 together, then 21,22,23,24 - basically the "tens column" of the number represents the current row to be lit.
Because the LEDs need to line up with a printed image, it was important to position each LED exactly in the right place. We used our old friend Inksc(r)ape to do this. First, we imported the bitmap to be used and resized it to the size of our eurocard copper board (100mm x 160mm). Then we simply draw 1206 sized rectangles and positioned them over the image:
The painstakingly slow bit was selecting each LEDs, noting it's position (in mm) and transferring these positions to the 1206 LEDs drawn in ExpressPCB.
It was slow going, but we got there in the end!
Because the schematic and the PCB layout were linked, whenever a single pad on one LED was clicked, all the others that it needed to be connected to lit up. This made routing the traces a little bit easier, and also ensured that no LED was inadvertently left out
To make assembly easier later on down the line, we positioned every LED with the ground pin on the same side; it would have made routing easier not to do this, but there would be nothing worse than ending up with a failed board because one or more LEDs were soldered the wrong way around!
So after about an hour or so of routing and re-routing traces, we ended up with a single-sided PCB with no jumpers (since the board will be hand assembled, vias were a big no-no as they'd introduce more nasty spiky bits where bits of wires had to be soldered)
The PCB as drawn in ExpressPCB
Because we're using press-n-peel (which reverses an image during transfer) AND entirely surface mount components, the whole PCB needs to be flipped before printing (so after transfer, it's back the right way around)
That's all the hard work done! Now it's just a matter of etching the board and sticking the LEDs onto it! Here's the board fresh from the ferric chloride:
(there's a bit of pitting in places on the board - our new Brother 3012 isn't a patch on the old Xerox Phaser for printing toner-transfer images)
Justin made a great job of soldering all the components in place. Using a bit of wire connected to a 3v coincell battery he made sure all the LEDs were connected the right way around and working before the last stage of the project - programming the PIC.
The firmware is incredibly simple. Simply flash PORTB.0 high, wait a few milliseconds, send PORTB.0 low and PORTB.1 high - repeat for each pin 0-6 and cycle forever. Rather than use an if statement to determine which pin to light up, we used bit-shifting and wrote a single byte value to the entire port:
Define CONF_WORD = 0x3f18
Define CLOCK_FREQUENCY = 4
AllDigital
Config PORTB = Output
PORTB = 0x00
Dim counter As Byte
Dim tmp As Byte
WaitMs 1000
init:
counter = 0
loop:
tmp = 1
tmp = ShiftLeft(tmp, counter)
PORTB = tmp
counter = counter + 1
If counter > 6 Then counter = 0
WaitMs 200
Goto loop
End
Define CLOCK_FREQUENCY = 4
AllDigital
Config PORTB = Output
PORTB = 0x00
Dim counter As Byte
Dim tmp As Byte
WaitMs 1000
init:
counter = 0
loop:
tmp = 1
tmp = ShiftLeft(tmp, counter)
PORTB = tmp
counter = counter + 1
If counter > 6 Then counter = 0
WaitMs 200
Goto loop
End
The PIC was programmed using a SOIC-clip and a homemade programming cable (the PIC was already mounted onto the PCB before it was programmed).
And there you have it. A cycling, flashing LED battery-operated UFO-themed greetings card. And all for a few hours work.
As it was getting late by the time the board was assembled and working, Steve took it away to produce the sticker front and hid all the scary looking electronics away. Hopefully a follow up post will show the final, working card in all it's glory.....
Subscribe to:
Posts (Atom)