Over the weekend and last night, I’ve put together a demo of using sensors and triggers with the LogoChip. The demo uses one of the built-in A/D converters to monitor a CdS photocell, and then trigger audio playback on a Radio Shack 276-1323 recording module.
The code watches for the light level to dim and then triggers one of two recordings, the idea being that someone is probably standing over the box looking into it. It’s actually a bit complex, because I keep a running average of the recent brightness, to make it easier to tell whether a sudden drop is a significant event or just noise variation on the sensor. LogoChip Logo doesn’t have terribly good support for arrays, and it was fairly interesting writing ring-buffer code.
Adapting the Playback Modules
[INSERT PHOTO OF CIRCUIT IN BOX]
The little PCBs on the sides are the Radio Shack audio playback modules. Each has an offboard record button and an onboard playback button. I desoldered the 9V battery clip that came with the units and soldered on leads to take power from my PCB, to cut down the number of batteries I had to install. I just now realized I had mistakenly plugged the power into the regulated 5V instead of the unregulated 9V, so that may explain why the playback was relatively quiet. It also speaks well for a wide supply voltage range on the modules.
The playback buttons are NO with pullup resistors, and ground their line when closed. Because they’re rubber pushbuttons with graphite contacts the press directly onto the PCBs, there were no leads to solder my remote trigger wires onto, so I wrapped them around the lower leads (toward the middle of the PCB) of the pullup resistors, R3.
[INSERT CLOSEUP PICTURE OF MODULE PCB]
Then I plugged the remote triggers into ports C6 and C7 on the LogoChip. Since the playback buttons on the modules are active low, I had to reverse my software logic and set the ports high on initialization, then pull them low to activate.
; Configure the audio trigger I/O port. to init-squawk-port ; Set left and right triggers off (active low, so set high) setbit squawk-left-bit squawk-port setbit squawk-right-bit squawk-port ; Set left and right triggers as outputs clearbit squawk-left-bit squawk-ddr clearbit squawk-right-bit squawk-ddr end ; Trigger the external playback modules ; Parameter: which module to trigger to squawk :squawk-bit ; Trigger it (active low, so set low) clearbit :squawk-bit squawk-port ; Wait a smidge to be sure it registers wait 1 ; Clear the trigger (active low, so set high) setbit :squawk-bit squawk-port end
The Software
[INSERT DETAILED DESCRIPTION OF RUNNING AVERAGE FUNCTION]
I learned several important lessons from this programming exercise:
- The A/D converter needs longer than .1 seconds between readings–at that repeat rate, I was getting garbage every time. .2 seconds appears to have been working reliably.
- The LogoChip Language Reference gives the positions of global variables
m
andn
incorrectly, which led to a buffer overflow in my code. Globaln
is actually position 0 andm
is 1, not 1 and 2 as stated. - LogoChip Logo does bizarre things to arithmetic expressions. I have no idea how it was evaluating
setsum-bright sum-bright - global (array-base + array-index) + bright
because I couldn’t figure out any sequence of evaluation that led to the results it was giving me. I solved it with parentheses:
setsum-bright (sum-bright - global (array-base + array-index)) + bright
but it’d be nice to understand what was happening.
The Code
Here are the programs:
- squawk.txt – Test code to play back sound samples
- light-level.txt – Test code to monitor and report light levels
- light-demo.txt – Program to monitor light levels and trigger audio playback
Here’s the code:
; Keith Neufeld ; 27-Feb-2006 ; ; LogoChip program to demo measuring ambient light level ; with CdS photocell wired from LogoChip port A1 to +5V ; and 1K resistor wired from A1 to GND. ; ; When light level suddenly becomes dimmer, trigger audio playback ; using Radio Shack 276-1323 20-second recording modules ; with play buttons wired to LogoChip ports C6 and C7. ; Play buttons are active low. ; Port assignments for the photocell and audio units constants [ [cds-port porta] [cds-ddr porta-ddr] [cds-bit 1] [squawk-port portc] [squawk-ddr portc-ddr] [squawk-left-bit 6] [squawk-right-bit 7] ] ; Array for averaging brightness global [array0 array1 array2 array3 array4 array5 array6 array7 array-index] constants [ [array-base 2] [array-len 8] ] ; Current brightness, sum of recent brightness, average recent brightness global [bright sum-bright avg-bright] ; Calculated brightness values that indicate dimming global [threshold-shady threshold-dark] ; At powerup, run the main program. to powerup startup end ; At startup, clear the array storage space, initialize some ; global variables, and configure the I/O ports. to startup init-array init-vars init-squawk-port init-cds-port prs "starting watch-brightness end ; Loop forever, taking samples and watching for dimming. global [already-shady already-dark] to watch-brightness loop [ ; Calculate what fractions of previous brightness ; constitute being dim calc-thresholds ; Get current brightness read-bright ; The following logic for different brightness conditions ; is carefully arranged to make a successful test exclude ; success on subsequent tests. I would have preferred to ; write this using ; ; if[ block 1 ] ; elseif [ block 2 ] ; elseif [ block 3 ] ; ; but LogoChip Logo doesn't support that, and I got cranky ; nesting the conditional blocks three deep using the syntax ; that _is_ supported. ; ; Caveat maintainer. ; If we're getting brighter, if bright > avg-bright [ ; Start paying attention to getting dimmer again setalready-dark 0 setalready-shady 0 ] ; If we're significantly darker than before, if bright < threshold-dark [ ; And we haven't already done something about it, if not already-dark [ ; Then cause an event dim-a-lot-event ; And remember that we're already dark ; and don't need to trigger another event. setalready-dark 1 setalready-shady 1 ] ] ; If we're a little darker than before, if bright < threshold-shady [ ; And we haven't already done something about it, if not already-shady [ ; Then cause an event dim-a-little-event ; And remember that we're already shady ; and don't need to trigger another event. setalready-shady 1 ] ] ] end ; Event routine for when slight dimming is detected to dim-a-little-event prs "|dimming a little| ; Play the message from the right speaker. squawk squawk-right-bit ; Give it a couple of seconds to finish. wait 20 end ; Event routine for when significant dimming is detected to dim-a-lot-event prs "|dimming a lot| ; Play the message from the left speaker. squawk squawk-left-bit ; Give it a couple of seconds to finish. wait 20 end ; Clear the array. to init-array setarray-index 0 repeat array-len [ setglobal (array-base + array-index) 0 setarray-index (array-index + 1) % array-len ] end ; Initialize global variables. to init-vars setsum-bright 0 setalready-shady 0 setalready-dark 0 end ; Configure the audio trigger I/O port. to init-squawk-port ; Set left and right triggers off (active low, so set high) setbit squawk-left-bit squawk-port setbit squawk-right-bit squawk-port ; Set left and right triggers as outputs clearbit squawk-left-bit squawk-ddr clearbit squawk-right-bit squawk-ddr end ; Configure the photocell I/O port. to init-cds-port ; Set photocell port as input setbit cds-bit cds-ddr ; Give time for the operator to get their hand out of the way ; and find out how bright the room can be repeat 10 [ ; Read the current brightness and store rolling average read-bright ;prs "average ;print avg-bright ] ;prs "|final average| ;print avg-bright end ; Trigger the external playback modules ; Parameter: which module to trigger to squawk :squawk-bit ; Trigger it (active low, so set low) clearbit :squawk-bit squawk-port ; Wait a smidge to be sure it registers wait 1 ; Clear the trigger (active low, so set high) setbit :squawk-bit squawk-port end ; Read the brightness and work it into a rolling average. ; Uses a ring buffer to compute the rolling average. If you know what ; that means, there's not much I can teach you about LogoChip Logo. to read-bright ; Read current brightness setbright read-ad cds-bit ;prs "|current brightness| ;print bright ; Roll off oldest value from rolling average and add current ; Parentheses around first two operands are necessary-- ; returns random result without setsum-bright (sum-bright - global (array-base + array-index)) + bright ; Save current brightness and advance history pointer setglobal (array-base + array-index) bright setarray-index (array-index + 1) % array-len ; Calculate recent average brightness, rounding the fraction setavg-bright (sum-bright + (array-len / 2)) / array-len ; A/D converter seems to require .2 sec between readings wait 2 end ; Calculate thresholds of dimming based on recent average brightness. ; Multiply then divide because fractions become 0 in integer arithmetic. to calc-thresholds setthreshold-shady (9 * avg-bright) / 10 ;prs "|shady threshold| ;print threshold-shady setthreshold-dark (5 * avg-bright) / 10 end