Archive for the ‘Uncategorized’ Category

7 Segment Pixel library for Arduino

Photo Credit Jeff Jassky. World Maker Faire 2016


Are you dissatisfied with the small size of the largest 7 segment digital displays that are commercially available?  Me too!  The good news is that if you have access to a laser cutter, it’s pretty easy and extremely fun to build your own obnoxiously large, 7 segment displays from scratch, using smart RGB LEDs. Arguably, the most popular of these are the WS2812 variety, AKA NeoPixels from  The laser cutter can help you to cut a backer board to precisely mount your LED lamps inline with the segments.  Most importantly, the laser can be used to cut a baffling layer out of a sheet of some opaque material. In this layer, each segment of every digit is cut out. Imaging the numeral 8 removed from a sheet of material for each digit.  This layer gets samwiched between the lamps layer and the face of the panel, if you choose to add a face.  The baffling layer not only creates the shape of the segments, but it baffles the light from each segment. It keeps the light in one segment from leaking over into adjacent segments.

But I digress!  Way back when I made my first 7 seven segment display, I was surprised that I couldn’t find an Arduino library to help make it easier to show digits on a custom built display such as mine.  There were plenty of 7 segment libraries to drive off the shelf parts, but nothing to help drive a pixel based custom made display.  If you’ve had the same concerns, YOUR SEARCH IS OVER! 😀 I’m pleased to announce the release of my Seven Segment Pixel library for Arduino which can be found on github:

The library uses the awesome Adafruit_NeoPixel library as a dependency.  You can instantiate a display object like so:

Seven_Segment_Pixel display1 = Seven_Segment_Pixel( DIGITS, PIXPERSEG, NUMDELIMS, PIXPERDELIM, NUMPIXELS, PIN, NEO_RGB + NEO_KHZ800 );

Then use the updateDigit function to manipulate a specific digit and it’s color:

 display1.updateDigit( 1, 1, 255, 0 , 0 );;

This would display a red, numeral 1 in the first digit position.  The show() function is just  calls the Adafruit_NeoPixel show() function.

 display1.updateDelimiter( 1, 255, 0 , 0 );;

If I had wired in a colon for a clock, the above would make the colon red.

Please take it for a spin and let me know what you think!



Longest commit message, ever.


Please enjoy a picture of the Franklin Ace 1000. (My family’s first computer). Displayed by at a H.O.P.E. conference in NYC.


First, a little background about Fat Cat Fab Lab and how we use CiviCRM and Stripe. Fat Cat Fab Lab is a makerspace that pays it’s rent almost exclusively with membership dues in the West Village neighborhood of NYC. As you can imagine, rent is high in the city that arguably invented high rent. Also, we built an RFID door system that ties in with our CiviCRM database. I’ve threatened to blog about it here in the 2015 Makerfaire post. An RFID card only allows entry if the associated member is in good standing. What this means is that we’re in a fairly unique situation the in Civi-scape because of our setup. Recurring contributions have to come in, and membership renewals _must_ work flawlessly as a result of those contributions. If not, I will get urgent messages about access cards not working! That said, one of my goals is to help the Stripe CiviCRM extension become a very “membership friendly” one, making use of some of the sexy Stripe features, like subscription editing. Membership upgrade/downgrade is asked for a lot at Fat Cat Fab Lab, so this implementation strives to reduce the associated administrative load for that workflow. I dare say the extension has come a long way from not renewing memberships at all. Good times! 😛 Anyway…moving forward!

Here’s a outline of new features and changes in design/approach.

  1. In Webhook.php, removal of any code that cancels a subscription when an at_end_date is reached.
    – Used in CiviCRM when installments are opted for in a recurring contribution, this code in case: invoice.payment.succceed became unnecessary as soon as case: customer.subscription.deleted was added. A subscription with at_end_time set naturally sends a customer.subscription.deleted event to our webhook when it expires. 
  2. Add feature: multiple subscriptions per customer. (the initial reason for this giant commit)
    – The Upgrader.php script depends on a newer version of the stripe-php library, so version 4.3.0 has been added. Now it can ask Stripe for each customers subscription id. The upgrade script supposes that organizations haven’t created any additional subscriptions for customers outside of Civi and that no subscriptions have edited. There is commented bonus code, for orgs that have made subscription edits. Method 2 should be functionally the same for orgs that haven’t edited, it’s just a more circuitous route, and therefore more prone to error.
    – Now there’s a new column in the civicrm_stripe_subscriptions table to store this unique identifier.
  3. Switch to contribution_recurring_id exclusively as a parameter to the API call Contribution repeattransaction.
    – In my experience, using original_invoice_id is unreliable in renewing a membership that has been downgraded.

    Screen Shot 2017-01-15 at 12.03.31 PM

    Excerpt from api.contribution.repeattransasction

    As you can see from the API code for repeattransaction, when contribution_recur_id is supplied, the API Contribution getvalue is called with limit =1, and order descending. This is the _previous_ contribution associated with the recurring id! For that reason, I believe $original_invoice_id might be an unfortunate variable name here. I imagine this could cause confusion for others looking to write a payprocessor extension for CiviCRM….who knows, maybe not.With this approach in mind, it makes sense to add column contribution_recur_id to the civicrm_stripe_subscriptions table and eventually deprecate the original_invoice_id column on installed versions 4.7.1 and earlier.- Upgrader.php will also populate the contribution_recur_id column.

  4. Change the default behavior of CiviDiscount on auto-renew memberships to be an introductory offer, rather than an indefinitely discounted membership. Once I get a time to continue my work on CiviDiscount and get a PR together, if accepted, we can start paying attention to a new boolean parameter intro_offer. This would give admins the choice of behavior per discount code on an auto-renew membership or other recurring contribution.
    – I’ve worked up a rough gift card implementation with CiviDiscount. This code in this commit works with it, employing a new discount_type =3 for gift cards, ( discount_type=2  is for fixed discounts).  Both of these are applied to the customer the same way: though a negative balance. This is one of the recommended ways for applying a gift card to a subscription. When there is a membership involved, this means now we have to act upon a possibly null charge invoice to renew the membership. I considered adjusting the number of terms and ignoring the null charge invoice. But, I thought better of it. The fact is that this negative balance will be applied to anything the customer pays for. So if we apply the entire negative balance to multiple membership terms there could be a double spend situation…screw that!
  5. Implement Stripe subscription editing for recurring contributions. What this means is that when a customers subscription has been edited to new plan in the Stripe dashboard, the existing recurring contribution is canceled and new one is started. A new pending contribution is created at the new level. In reality we don’t know the actual amount of this contribution, because Stripe prorates the remaining time at the old level and creates a new line item for the new level according to the amount of days left until the customers billing date. We’re basically waiting for the dust to settle and recording the contribution when it comes in.  I had considered just editing the current recurring contribution to the new level, but as a CiviCRM site-admin, seeing a record of previous levels tells me a story and is important information.  Thank you Eileen McNaughton, for letting me pester you with endless question about this and other things!   If there is a membership involved with this recurring contribution change, the members level is changed automatically. This is possible because we’re now prepending plan ids with membertype_X and adding the membership name after CiviCRM in the plan description. I don’t see any other way of making this happen…and I believe it’s worth doing if your organization depends on it’s different memberships for survival. In Stripe, plan id cannot be edited. The good news is that this code also checks to see if membertype_X- is prepended to the plan description, an attribute that can be edited. This means existing users of the extension who want the feature can retrofit their Stripe plans by looking up their member type id’s in their civicrm_membership_type table and prepending them to the corresponding plans in stripe. If existing users of this extension don’t care about the member level changing feature, they can safely ignore it.Ultimately I see subscription editing as something initiated within CiviCRM instead of the Stripe UI. Hopefully some code can be recycled. Maybe we’d have a list of existing plans that we can change the customer to.  

Thanks for reading..I hope the community finds this as useful as we do!

Fat Cat Fab Lab at Maker Faire 2015!

Making for it’s own sake I find is usually the most fun. Sometimes, most times I find, we make things that we need and that are practical.  This project fell somewhere in the middle.  People that know me, know that I’m proud to be one of the co-founders of Fat Cat Fab Lab.  It’s an exciting space with a great community of people. We needed something to bring to the 2015 New York Maker Faire.  I wanted to show off what our CNC router and laser cutter can do.  It also gave us an opportunity to show off a previous project as well: our custom built RFID door system.   This beast is a working demo of our door system.


I wanted to give a very special shout out and thank you to Jimmy Diresta, who helped out with some last minute welding of an internal frame.  If you haven’t already, I highly recommend you subscribe to his youtube channel right away.  Hours of entertainment and learning await you!   He’s a gifted, prolific maker who’s style of video is so compelling, full of personality, and fun to watch.   Plus, he’s just a good guy!  He’s super gracious with his time, especially when it comes to people who want to learn.   Thank you, Jimmy!

We’ve been using Autodesk Fusion 360 at FCFL.  Integrated CAD/CAM is notoriously expensive. Fusion 360 is a boon for educational organizations for whom the licensing terms _seem_ to indicate it will be free forever.  Forever is a long time…we’ll see!  It’s exciting because the product gets updates all the time that bring in new features.  Sometimes if might feel a little “too exciting” when you’re not sure if the update will also bring instability. 😉   It does crash from time to time, but also does a great job at auto-recovering work that was open during the crash.   Kudos Autodesk, and thank you!  Look out for upcoming Fusion 360 classes at  Fat Cat Fab Lab.

I was inspired by some CNCed laminated bamboo material that’s been kicking around the lab.  Using that inspiration, this is the first test cut of some shapes I wanted to incorporate into the design.


This is just to show the internal CNCed parts that provide space for the frame, door-strike, request-to-exit button, cabling etc.



On top I used some 1/2 plywood as biscuits.  I came up with my own registration method for double sided CNC projects.  Did it work well? Yes.  Was it fast? Not so much.  😛   Check out the Core77 youtube channel for a super slick way of registering your work on the machine.  Thanks Jimmy, for hipping me to that.  😉


This turned out to be a great success, and of great interest to other makerspaces who want the same functionality.  The RFID card numbers are stored in our member database.  We use CiviCRM, which is a major windfall for non-profits!  Anyway, the cards get scanned at “door clients” placed at each entrance, and the card numbers are checked against the database.  If the card id exists, the system then checks if that member is in good standing or not.  😉  Automation ftw!  Clients are made of Arduino Yuns, and Adafruit NFC shields programmed by volunteer Gerard Baron.  Thanks Gerard!  I handled the server component which is written in Python….this was super fun!



Statistic Counting Clock

This was an extremely fun job I did for an organization called Brazil Against Violence.  Among some other signage, they wanted a statistic counting clock.  What the heck is that!?  It’s a digital clock, that keeps track of different metrics, based on statistics gathered by an organization called Fundo Brazil.



The most fun parts were discovering how 7 segment displays work in terms of software, and implementing my own using Neopixels and Arduino.  Designing the the physical part was fun too.  I used the laser cutter to make baffling layers that sit between the LEDs and the front surface.


A killer feature for school principals

Photo by Kevin Dooley.

Photo by Kevin Dooley. CC License.

In 11 years of administering phone systems,  almost once a year I get asked this question by a principal of a school, “How can I change the school phone menu to tell parents that school is closed due to bad weather?  From home!”  Or something to that effect.   Traditionally, proprietary phone system manufacturers tend not expose these features to regular users.  I’d end up having to say, “I’m sorry, this isn’t possible without giving you full administrative access and special training.”    As a responsible IT consultant, this isn’t something I’d feel too comfortable with.  And frankly, neither were the principals when I let them know how the systems work.  They wouldn’t want the ability to mess up the whole thing.  That’s a lot of rope!


Enter, Freeswitch!  It’s like a ball of clay that you mold into pretty much anything you can imagine telephony-wise.   I love to say, “Yes.” to my clients and Freeswitch helps me do it.


So how is it done, you ask?  By leveraging the existing voicemail system!  The IVR menu can be programmed to play the active greeting of any voicemail box programmatically, before it continues on to the regular IVR menu spiel.   It’s a perfect little sandbox, controllable by regular users that can’t break the IVR menu or it’s functions.

As long as a principal knows how to record a voicemail greeting, they’re golden!

You can also stack them up.  Say you want to dedicate an announcement message to your development director to give out information to parents about an upcoming fundraiser.  No problem.   😉


Thwarting robocallers with Freeswitch

Who doesn’t love an automated call informing us that the warranty on a car, of a brand that we do not own, is about to expire. Or that we’ve won a fabulous Caribbean vacation. Good times! Fortunately, with Freeswitch it is pretty simple to set up a whitelist to allow certain calls to ring through to an extension, while forcing unknown callers or callers with no caller ID to hit an IVR menu, sometimes referred to in phone-system jargon, as an auto-attendant. From there the callers can be asked to “Press one to continue.” within the IVR menu. I’ve been using this setup for over a year and it’s been FANTASTIC!

Let’s see how it works!

We’re assuming that we already have an IVR menu in place at extension 5001. It plays our audio file that says “You’re reached [person X], press one to continue.” And the action for pressing one transfers the caller to our desired extension or ring group.

Numbers have been changed to protect the innocent. For the purposes of this example let’s assume the following:

That our DID number is 212-555-1212. And we have a couple of friends that are not within the 212 area code that we want to explicitly whitelist. Again, for example’s sake, their numbers are 201-ABC-DEFG and 203-BCD-EFGH.

<extension name="12125551212">
        <condition field="destination_number" expression="^(12125551212)$">
                <action application="set" data="dialed_ext=$1"/>
        <condition field="caller_id_number" expression="^(\+1|1)?(212\d{7})|\+1201ABCDEFG|\+1203BCDEFGH$">
                <action application="ring_ready"/>
                <action application="set" data="call_timeout=18"/>
                <action application="set" data="hangup_after_bridge=true"/>
                <action application="set" data="continue_on_fail=true"/>
                <action application="bridge" data="sofia/internal/1001@${domain_name}"/>
                <action application="answer"/>
                <action application="info"/>
                <action application="sleep" data="1000"/>
                <action application="voicemail" data="default $${domain} 1001"/>
                <anti-action application="ring_ready"/>
                <anti-action application="set" data="call_timeout=10"/>
                <anti-action application="set" data="hangup_after_bridge=true"/>
                <anti-action application="set" data="continue_on_fail=true"/>
                <anti-action application="bridge" data="sofia/internal/5001@${domain_name}"/>
                <anti-action application="answer"/>
                <anti-action application="info"/>
                <anti-action application="sleep" data="1000"/>


This xml snippet is from out default.xml dialplan.  Calls to our DID get routed here and satisfy the regex for this extension shown. Anyone from our local area code 212 gets through. And also our friends when their caller ID is not blocked.  In other words, if the caller is in the whitelist, they get transferred to extension 1001. If not, they get transferred to extension 5001 and after that, possibly a very hot and dark place with the gnashing of the teeth etc. etc.     😉



Needs-Help Button using Arduino and Freeswitch

This is a project for my grandmother, who turned 100 this year. Yes, really! Unfortunately, Grandma has been having some health problems of late. You know, as you do when you’re a 100!   Thankfully she’s home from the hospital and doing well.  While my folks live with her and generally have someone with her all the time. Sometimes they need to do some work outside, like mow the lawn or go grocery shopping while they are the ones on duty, so to speak.   I thought, wouldn’t it be great if there was a super easy way for her to let someone know that she needs help if she is by herself momentarily.   Something like this might afford my parents a bit more freedom and piece of mind. Enter, the Needs-Help Button!  Name subject to change until a better one emerges.  Suggestions welcome!  It’s using a key fob single button remote transmitter and momentary receiver from Adafruit.   These are connected to an Arduino Micro that sends a character string to it’s USB serial port.  A Python daemon listens to the serial port on the Alix board computer running Voyage Linux and everyone’s favorite Open Source PBX/soft-switch, Freeswitch.   Here it is in action:

Ideally, I think it should cycle through a list of numbers, first starting with some internal extensions at the house.    The call recipients can cancel the cycle by acknowledging the call for help by pressing star-9.


I had considered doing this on a Raspberry Pi but I had read about a maximum uptime issue with it.   Maybe it’s not a problem anymore but as you can imagine I wanted a pretty solid platform for this.  In the future I could also see this system being used for VOIP calls to the “old country” so it will be nice to pick up a $10 license for the G.729 codec.  Unfortunately they only offer x86 and x86_64 architectures….not ARM yet.


Intercom system using Freeswitch and Arduino

Recently I was asked if I could install a new intercom system to a building that had recently been converted for apartment dwelling.   After looking around for one that fit the clients needs, I realized I could build one that was less expensive and extremely functional with technology that I know pretty well.  I love it when I can get crossover from my maker life into my work life!    I knew I could get Freeswitch to fire off a shell script using DTMF tones.    The bind_meta_app handles this for us.  Here’s the line in my dial plan that listens for DTMF.

<action application="bind_meta_app" data="9 b i transfer::-bleg open_sesame XML features"/>

Visitors dial a two digit apartment number.  This rings the tenant’s cell phone using a VOIP service.  Call recipients press star 9 to trigger the Arduino to trip the relay which sends current to the door strike.  Mind you that the ‘b’ after the 9  (for b-leg) means only the call recipients can trigger the relay. We can’t have people letting themselves in!

Then in features.xml:

<extension name="open_sesame">
       <condition field"destination_number" expression="^open_sesame$">
       <action application="answer"/>
       <action application="playback" data="voicemail/door_opening.wav"/>
       <action application="event" data="${system(/bin/echo -n YOURSTRINGHERE  >> /dev/ttyACM0)}"/>
       <action application="hangup"/>

This echos a string to the serial port on the Arduino. Code on the Arduino listens for certain strings that can trigger any number of 4 relays. The -n switch means no return character, which is a different ascii code we don’t want to hear. The lovely voice tells the call recipient that the door is opening. I love devices that tell you what they’re doing…so friendly!

In my Arduino sketches I have a habit of adding in a couple of LED blinks to the setup function so that I know if it has been reset by power loss or whatnot. It turns out that helped me figure out that I had landed in a huge pile of whatnot.  If you echo strings directly to the serial port of an older pre-Leonardo Arduino, you tickle the hardware reset and it reboots.    Normally that would be used by the Arduino software to reset the board after you’ve uploaded your new sketch.  After much chin scratching and googleing I learned a 47K Ohm resistor between the 3v and ground pins disables this.

I was shopping for vandal proof outdoor telephones and realized…hey there’s no shortage of those on Ebay. And they cost 1/3rd of a traditional vandal proof speaker-phone with dial pad! Thanks to Ian Hendrickson-Smith for the inspiration on bottom artwork. 😉  The client _loves_ the payphone.  The building actually has Keith Haring murals all throughout, so it kinda fits vibe of the place.

IMG_0016 IMG_0017-2


It turns out this was a ‘smart’ payphone that a certain company was not able to remotely program for me.  Not only did I need it to make free calls but I needed it to be able to make 2 digit calls instead of the usually 10 or 11 digit dialing that they are programmed for in the field.    In the end I just purchased a replacement phone board from said company, one that is used in converting payphones for home use.    At first it worked better as an AM radio that is until I discovered ferrite cores for filtering out these signals.    Also their keypad didn’t fit my upper housing so I ended up having to figure out how to make my current keypad fit their phone board.  Once I got my keypad apart I could see how the matrix worked.  I think they must have just invented circuit board ‘via’s and were trying to incorporate them wherever they could!  It was the 80’s.  On the phone board side I could clearly see the traces to the DTMF chip.  After a bit of googleing I found the data sheet on that.  I matched up the corresponding rows and columns and was good to go.

IMG_0019-2 IMG_0022 IMG_0034-2


The Freeswitch software is running on an Alix board, a low power embedded x86 platform normally used for wifi access points. It has an 500Mhz AMD Geode processor and 256Mb ram and it handles Freeswitch without issue.   My dialplan only allows calls from the front door so I can rest assured that it will only be making one call at a time.   No worries on unexpected load.

Alix board, Arduino UNO with Seeed Studio Relay Shield, Grandstream ATA and AC Power.

Alix 3D2 board, Arduino UNO with Seeed Studio Relay Shield, Grandstream ATA and AC Power.


The client maintains a Google Doc spreadsheet which has apartment numbers and corresponding cell numbers.  I have written a bash script that runs on the device daily.  It scrapes the Google Doc in CSV format and rebuilds the dialplan based on this information.  This seemed preferable to running a LAMP interface on the low-power device.   Here’s the script if you want to use it.

Thanks for reading!


Protecting a physically exposed extension with Freeswitch

Sometimes you want or need an extension in a public place that needs to block outgoing calls some of the time. For instance one client has a fax machine that also has a handset built into the unit. There’s no way to disable the handset unless you use a super cumbersome locking mechanism built into the firmware. One approach might be to use a secret dialing prefix for this one extension. However, dialed numbers appear on the fax confirmation printouts so a simple dialplan_prefix-as_access_code might not stay secret for very long. My solution was to have a secret extension that the user dials before sending. This flips a bit and allows one call to be made. After the call is made the bit is flipped back. Freeswitch custom variables are destroyed after a call has ended so we cannot pass on our bit with the dialplan alone. We need a little help. If you’re already using mysql or postgres you might consider storing it there. I was not in this camp so I just opted for a file.

<extension name="enable a fax out" >
    <condition field="destination_number" expression="^(6789)$">
       <action application="answer"/>
        <action application="sleep" data="500"/>
        <action application="playback" data="ivr/ivr-you_may.wav"/>
        <action application="playback" data="ivr/ivr-send_fax_now.wav"/>
        <action application="set" data="${system(/bin/echo 1 > /var/fax/faxsetting)} "/>
        <action application="set" data="faxout_status=${system(/bin/cat   /var/fax/faxsetting)} "/>
        <action application="sleep" data="1000"/>
        <action application="log" data="point1 Faxout status set to [${destination_number}] , ${faxout_status}"/>
        <action application="playback" data="voicemail/vm-goodbye.wav"/>
        <action application="sleep" data="550"/>
        <action application="hangup"/>

After you’ve dialed the obscure extension number, you’re allowed one outgoing call from extension 1016. We’ve set our bit to 1.

<extension name="faxout route  route, x1016">
     <condition field="caller_id_number" expression="^(1016)$" require-nested="true"/>
     <condition field="destination_number" expression="^91(1{0,1}\d{10})$">
          <action application="set" data="faxout_status=${system(/bin/cat /var/fax/faxsetting)} "/>
          <action application="log" data="point2, faxout status set to ${faxout_status}"/>
          <action application="set" data="my_dest=${destination_number:2}"/>
          <action application="log" data="my_dest is set to ${my_dest}"/>
          <action application="execute_extension" data="1016_${faxout_status}"/>

Here we’ve put our bit from the file into a variable and route accordingly.

If it’s set to 1 we route here:

<extension name="1016_1" >
      <condition field="destination_number" expression="^(1016_1)$">
           <action application="set" data="${system(/bin/echo 0 > /var/fax/faxsetting)} "/>
           <action application="export" data="suppress_cng=true"/>
           <action application="set" data="sip_h_X-accountcode=${accountcode}"/>
           <action application="set" data="sip_h_X-Tag="/>
           <action application="set" data="call_direction=outbound"/>
           <action application="set" data="hangup_after_bridge=true"/>
           <action application="set" data="effective_caller_id_number=YOURCALLERIDHERE"/>
           <action application="set" data="inherit_codec=true"/>
           <action application="set" data="continue_on_fail=true"/>
           <action application="log" data="my_dest is set to ${my_dest}"/>
           <action application="bridge" data="sofia/gateway/flowroute/1${my_dest}"/>

If not then send them here:

<extension name="1016_0" >
      <condition field="destination_number" expression="^(1016_0)$">
           <action application="answer"/>
           <action application="sleep" data="500"/>
           <action application="playback" data="ivr/ivr-not_have_permission.wav"/>
           <action application="playback" data="voicemail/vm-goodbye.wav"/>
           <action application="sleep" data="550"/>
           <action application="hangup"/>


If you have another way to handle this situation, or have any improvements feel free to comment!


Arduino for 6th Graders

One of the most rewarding and equally challenging things I have accomplished was teaching a pilot class at Ascension School:  Arduino for 6th graders.  My goodness this takes a monumental amount of energy!   Generally, I think the fear as educators, is that getting kids past the nature and syntax of C is asking too much.  My experience was, it is not!  I do not believe everything we present to kids has to look like it was made for kids.   Funny how exciting the humble blink sketch is.  This is not lost on kids!   Word to educators however: Arduinos ship with the Blink sketch on them, for testing I imagine.  So that can be anti-climactic if it’s already blinking when students plug in their boards.  Better to upload a blank sketch, so kids experience that sense of accomplishment!  I was very happy that one student who’d been generally having a hard time in school found something he was really good at.   The school purchased 6 ArdX Experimenters Kits from Adafruit.  I had the kids working in groups of 3 for the most part.


I found that one stumbling block turned out to be understanding the inner workings of the breadboard. I ended up making some wiring diagrams with Fritzing and printing them out. This shows some of the printouts in transparent folders. I gave the kids dry-erase markers and their task was to trace the path of current through the circuit.  From here I could get a feeling for who had it and who hadn’t gotten it yet.