Monday, January 21, 2013

Arduino Garage Door Opener - No Rolling Codes

Most people trying to do a garage door opener with an Arduino often try and use the wireless modules. It means they have to figure out rolling codes, and other things, to make it happen.

However, most garage door installations have a hard-wired, internal button to work the system.  If you have one of these, it's easy to connect an Arduino to the garage door, without anything high tech.  All you will need is a 5v relay, solder, wire, and the usual Arduino with an available output pin.

First, remove the button from the wall.




There will be two wires connected to the back of it, a red one, and a white one.  Disconnect these, remembering where they are supposed to go (you will be putting this back on the wall after you are done).


 Remove the circuit board from the device.  This is so you can identify the switch being used to open and close the garage door.  (You can also connect it to the locking/vacation button, or even the light button).  Once you have it removed, locate the switch, and then locate the soldering joints where the switch connects on the back side of the opener.  In mine, It was a SPDT (four connectors), but two connected to the + and two connected to the feed.  Solder a new wire into these solder joints.


Always make sure you test the unit for functionality (do it before you connect everything back up to ensure the new wires work).


Once you have that tested, reconnect the wires and make sure the button(s) you used functioned as normal.  If they do, simply connect the two new wires you soldered in and see if it will trigger the door.  It should, if you've done it properly.

This gives you a simple interface to the opener.  To complete the project, simply use a 5v relay connected to a digital I/O pin (set to output mode).  Pull it low automatically, and when you need to open/close the garage door, drive it high for 500ms, then set it low again.

I'd strongly suggest using some magnetic reed switches to determine if the garage door is open, closed, or in a state in between.  I did this already in my Homebrew Security Project, and described how it worked in the code design and assembly section.  It alerts me if the door opens or closes.  It means that, say I'm at work, and a son opens the garage door and forgets it, I can (in theory) trigger the door to close.  On the other hand, a buddy calls me up and needs to borrow a tool while I'm in another state, I can pull up the camera, verify who he is, and trigger the garage door to open for him.

Eventually, another task I'd like to complete is to use automatic door locks to lock the house up as needed.  All part of the security project.
http://blog.silverhawk.net/2013/03/added-pcb-for-alarm-expansion-card.html
After trying to wire this into the arduino, I had the wonderful experience of breaking EVERYTHING because the wiring failed (always bending cat5e will cause it to eventually break), and decided to actually have a PCB created that would give me better plugs using computer header connectors.  Turns out, it wasn't that difficult using Eagle and BatchPCB to have another company manufacture the board, but I still have yet to test the resulting board.  We'll see when it gets here!

Power Control - Another Arduino Tool from SparkFun

SparkFun listed a new enhancement for your Arduino-controlled projects - power control, without exposing anything.  This is perfect for controlling on-and-off timing of Christmas lights, message board signs, or even equipment.  The URL :

https://www.sparkfun.com/products/10747

Tuesday, January 15, 2013

Creating an Arduino Weather Center

I had some quick thoughts for a weather station addition to the Arduino, and didn't have anywhere else to put them.

Barometric Pressure :

Sparkfun has a breakout board that you can easily tie into your Arduino project.

https://www.sparkfun.com/products/11282

Not sure how to use it yet as I don't have one.  (I'll figure it out if someone wants to contribute one to the cause.)

Temperature :

This is the easiest one of all.  There are numerous additions, but it's simple enough to use a thermistor on an analog input, and then calculate the formula.  It's actually fairly accurate.

Humidity :

Again, Sparkfun to the rescue.  They have a breakout board for a humidity sensor :

https://www.sparkfun.com/products/9569

Once more, I've never tried it so I really don't know how to implement, but if someone wants to contribute one, I'll figure it out.

Wind Speed :

3D print an anemometer, and tie it to a DC motor or an optical rotary encoder.  It's a rather simple principle.  I grabbed a 3D file definition from Thingiverse (http://www.thingiverse.com/thing:19439), and had a buddy print it out :


Notice that there were some holes in it.  Once you have the basic form, use some epoxy-resin to give a smooth outside shape and fill in any holes.  Simply tie this to either a rotary encoder (higher resolution lets you detect slower wind speeds) and count the ticks as it rotates.

Or, with a DC motor, check the voltage output of the DC motor.

Of the two options, an optical rotary encoder may have less wear and tear, and give you a measurement that is accurate if you average the last three readings (because you don't know how long between the detents you were when starting the count or ending the count).  Not as accurate as a DC motor, but still a workable number (and easier to use - just tie the encoder to the interrupt, use the interrupt function to increment an internal count, and that will tell you how far you've rotated).  A real-time clock might be necessitated if you want true accuracy (e.g. https://www.sparkfun.com/products/99 ).

However, that mandates requiring the use of the interrupt pin, or putting a counter in the circuit.  I'd rather keep things simple, and also (rather than keeping a running sample) would rather have an instantaneous result.  I don't want a real-time clock, so I'm going with the DC motor design.  There are two kinds of DC motors.  A motor with brushes will be the easiest circuit to implement, but it also has a little friction in it due to it's design.  A motor without brushes will have less friction (and therefore a smaller measurement of wind speeds will be available), but their output is not constant (voltage will drop/spike over time).

I'm going with a Brushless DC motor, and adding in a capacitor inline to filter out the spikes.  That is then fed into a voltage divider to give me exactly what I need on an Arduino analog input pin.

Just as an FYI, a DC motor is also a generator (but there is an efficiency loss when you reverse it).  Turning the motor won't provide the same DC input that is required to drive the motor.  But, it's still a transducer, and it works.

Wind Direction :

For the wind direction, a 360 degree potentiometer (with no stops) would give you what you need, just look at the resistance.  No need for hall-effects sensors or anything.  You will need to calibrate it with an offset though (once you know true north).  A great 360 degree potentiometer would be available for about $15, from http://www.digikey.com/product-detail/en/6630S1D-B28-A103/6630S1D-B28-A103-ND/3534161 (You can buy in bulk to get cheaper prices, but I'd hope you are in the manufacturing line of work).

Many people think a rotary encoder would work here fairly well, but I disagree.  Unless your device is permanently powered, or you have a re-calibration mechanism set up, you'll start to lose accuraccy as soon as you start losing power to the device and things rotate.  In other words, don't use a rotary encoder for something like this.  A potentiometer will give you an instantaneous measurement you can use, even if you lose power and get it back.

Rainfall / Snowfall :

I haven't figured this one out yet.  If anyone has any ideas, feel free to drop me a line!

Friday, January 11, 2013

The hacky way to motion detecion

Previously in our Project Homebrew Security System , we worked on trying motion detection and capture using a flood light hack after I had tried the Linux "motion" package, and also after I'd also tried "ZoneMinder", and had some ugly results with frame rates being bad.  I abandoned the motion flood light hack (yes, it works) because of two things - I didn't want to buy motion detection lights, and I didn't want to install new power lines to be able to run them.  I wanted them close to the cameras, and I didn't necessarily want the lights to turn on hiding the LED's that accompany a security camera.

Turns out, there are two absolute requirements for the motion implementation :
  1. Motion state changes (e.g. motion has been detected, or is all clear).  This one caused me to abandon the "motion" package, because of the lack of sensitivity and color-based detection algorythms.  The noisier something is without actual motion, the less likely you'll be to respond, so it had to be accurate, and not as noisy.
  2. Motion video capture for review.  Both ZoneMinder  and motion failed here, both coming in at a rate of three frames per second.  I needed a higher quality.

Some nice-to-haves :

  1. Automatic replication of videos to off-site location.
  2. Motion detection should be close by the camera.

State Changes

Luckily, the TrendNet TV-IP322P cameras have a setup section called "Event Server".  For notifications, we need the HTTP section.  It has chunks for notifying based on motion, and two GPIO pins through an HTTP web call.  I threw a quick CGI onto my security server that checks and makes sure the MAC address of the device is authorized to send the status change, and then the CGI can push that state change into Nagios to where I get real-time notifications via SMS, e-mail, etc, when motion is detected.  First requirement, check.

Then I realized that it never cleared.  That means the check went into a critical state, but never went back into an OK state.  Talking to TrendNet, it's not possible, nor are they willing to let me at their firmware to extend it.  Aside from that incapability, I have an idea to get a clearing, but need the next requirement to make it work : video capture to a network server.

Quality Capture

Rather than have the cgi trigger video capture (which I could simply grab via a wget to the URL's), it was easiest to actually configure a CIFS share (Windows world or Samba in Linux - they both work great here). Once the share is set up, in the TrendNet interface, click on "Event Server", then "Network Storage" :


In this interface, configure it to match your CIFS file server settings.

State Change - Getting an "All Clear"

Back to the state changes, now that we have video being copied up to the media server (quite nice, getting videos we can pull up quickly from one source on any device), we can finish the state changes.  There are two ways to do this : (1) Write a cron job that looks for modification times of the resulting uploaded files, and (2) add serious logging to Samba.  The first one is the simplest, but also more noisy when motion is being detected (because of the way Samba creates the files).  The second way will be more accurate, but also is more in-depth in it's configuration.

Watch the uploaded directory :

As I always hate being too complex, I tried the simplest way first.  Remember, I had to try this before I realized it was a little bit noisy.  We create a cron job that scans the video directory for the camera looking for recent videos and if a recent one has no more being written, send the "all clear" for that camera.  Basically, the job has to do the following :

  1. Get the most recent video file written by the camera.  In the TrendNet case, it was the CIFS share, followed by the date, followed by the hour, e.g. :

    /path/to/mount/garagecam/20130109/21/215208.mp4
    Would have been written on January 9th of 2013 at 9:52 P.M., and this is what we look for.
  2. If any of these files were written to within the last two minutes, check that they are still not being written to.  If they are, ignore the file as video is still being captured.
  3. If the files were written to in the last minute, but aren't written, make sure we don't have any new files, and if so, fire off the "all clear".
Suddenly, I have both the motion-is-happening events, and the okay-no-motion-anymore events being triggered.  Plus, I can get the motion-is-happening events to include a link to the currently happening video.  That's a nice feature for those with mobile phones that want to see what triggered the alert from a remote location!

Later, after getting multiple motion-detection events and all-clears for a single event (yes, The TrendNet does this), I decided to try the second option.

Add Serious Samba Logging and Watch the Log :

This was a bit more complex.  I was looking originally for a "hook" that would run after a file has been uploaded, but never did find one.  In the end, I added a few lines to the smb.conf file's "[global]" section that enabled a full audit :
    
    # AUDIT LOGGING
            vfs objects = full_audit
            full_audit:prefix = %u|%I|%m|%S
            full_audit:success = pwrite
            full_audit:failure = none
            full_audit:facility = local6
            full_audit:priority = NOTICE
    
Then, I added the log entry to /etc/rsyslog.conf :
    
    local6.*            /var/log/samba/audit.log
    
Restarting both Samba and rsyslog :
    
    /etc/init.d/smb restart
    /etc/init.d/rsyslog restart
    
And instantly, lots of messages in my /var/log/samba/audit.log file!  One thing to note here, is that the TrendNet IP camera kept firing off pwrite's, you get a continuous stream of messages in the audit file as it's writing to the video file.  I found a slightly better configuration for this same thing (no cron job, but named pipes) and you can do the same.  The link to the post is http://www.silverhawk.net/2013/10/catching-trendnet-end-of-motion-signals.html .

At that point, your cron job simply looks for any files and also checks to see if samba is still writing to the file via /var/log/samba/audit.log .  It's a bit more complex, but it is less noisy.


Offsite Automatic Backups

If you need offsite, automatic backups, I'd suggest using Samba under Linux as your CIFS software, and a while-loop shell script using inotifywait pointing to the directory where things will be dropped.  The inotifywait binary should come in the inotify-tools RPM package, so make sure that's installed.  The script should immediately start to copy the video file to a remote server when it starts to be written.

Next up, we visit setting up a portal .

Saturday, January 5, 2013

Creating a 3TB Filesystem in Linux

I moved my media drive out of the portable enclosure, and into the chassis, and ran into a problem.

The filesystem was showing as available, but was unable to mount.  Wondering what was going on, I ran :

fdisk -l /dev/sdb

and had a nice, wonderful message about the DOS partition table being unable to handle disks larger than some arbitrary amount.  Note, this is a new drive that I had mkfs.ext2'd on it a week and a half ago (it was a Christmas present - my wife ROCKS!), so it was (in theory) already having data on it.  I still had the original data ready to go, but couldn't get that mounted, and opted to look into doing this properly.

That lead me to actually re-create the filesystem on it, complete with a new partition table.  This time, instead of using fdisk and mkfs.ext2, I opted for ext4 (still want to turn off journalling) and parted (after removing the partition) :

(parted) mklabel gpt
Warning: The existing disk label on /dev/sdb will be destroyed and all data on this disk will be lost. Do you want to continue?
Yes/No? yes
(parted) mkpart primary ext2 0% 100%
(parted) quit
Information: You may need to update /etc/fstab.

This allowed me to start to create the filesystem after a quick check :


[root@hostname mnt]# fdisk -l /dev/sdb

WARNING: GPT (GUID Partition Table) detected on '/dev/sdb'! The util fdisk doesn't support GPT. Use GNU Parted.


Disk /dev/sdb: 3000.6 GB, 3000592982016 bytes
255 heads, 63 sectors/track, 364801 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk identifier: 0x00000000

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1      267350  2147483647+  ee  GPT
Partition 1 does not start on physical sector boundary.
[root@hostname mnt]#

So, now it's time to create the filesystem :
 
[root@hostname mnt]# mkfs.ext4 /dev/sdb1
mke2fs 1.41.12 (17-May-2010)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=1 blocks, Stripe width=0 blocks
183148544 inodes, 732566272 blocks
36628313 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
22357 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
        4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
        102400000, 214990848, 512000000, 550731776, 644972544

Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 28 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
[root@hostname mnt]#

So, the filesystem is created.  But, EXT4 filesystems have journalling enabled by default.  I don't like this, so I must disable it :

[root@hostname mnt]# tune2fs -o journal_data_writeback /dev/sdb1
tune2fs 1.41.12 (17-May-2010)
[root@hostname mnt]# tune2fs -O ^has_journal /dev/sdb1
tune2fs 1.41.12 (17-May-2010)
[root@hostname mnt]# dumpe2fs /dev/sdb1 |grep 'Filesystem features'
dumpe2fs 1.41.12 (17-May-2010)
Filesystem features:      ext_attr resize_inode dir_index filetype extent flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize
[root@hostname mnt]#


As long as you don't see has_journal in the output, journalling has been disabled.  The next step is to get the filesystem's unique ID :

[root@hostname mnt]# blkid /dev/sdb1
/dev/sdb1: UUID="1ec5551c-10eb-4bad-9232-5710613ba4d2" TYPE="ext4"
[root@hostname mnt]#

Next, ensure we have the mount entry in /etc/fstab :

[root@hostname mnt]# cat /etc/fstab |grep 1ec5551c-10eb-4bad-9232-5710613ba4d2
UUID=1ec5551c-10eb-4bad-9232-5710613ba4d2 /home/media   ext4    defaults     1 2
[root@hostname mnt]#

In some cases, it's wise to reboot the machine and make sure you can still mount it up.  I did that since this is the home media server, and everything looks good:

[root@hostname ~]# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/VolGroup-lv_root
                       50G   17G   31G  35% /
tmpfs                 3.8G     0  3.8G   0% /dev/shm
/dev/sda1             485M   90M  370M  20% /boot
/dev/mapper/VolGroup-lv_home
                      400G  347G   33G  92% /home
/dev/sdb1             2.7T   73M  2.6T   1% /home/media
[root@hostname ~]#

That means it's time to rsync things back over.  Hooray!

Linux : Creating a Video Countdown

I had some videos that I was trying to alter, and I needed a "countdown" of about 15 seconds based on one of the frames from the video.  I ended up doing a lot of hacks, but finally pinned it down to the following steps.
 
First, you need to extract the audio chunk that you want to use, and edit it to the exact time you would like.  You can use Audacity to edit the sound to how you want it, fading in and out, etc, but if you want to start with an autio chunk from the video you are using, you can extract it doing :

 mplayer -dumpaudio -dumpfile audio_track.mp3 video_to_extract_from.avi

 lame --decode audio_track.mp3  audio_track.wav

The first one extracts into an MP3 file, the second one converts that to a .WAV file (Audacity in my case wasn't compiled with MP3 support).

Next, you need to choose your background.  You should use one with the same dimensions as the video you are attaching it to, so you might want to extract each frame from the video and choose one of those.  For example :

ffmpeg -i video_to_extract_from.avi frame%8d.jpg

Once done, you need to pick the frame you wanted.  I found it, and then renamed it to "base_image.jpg".  After that, you can create the countdown frames using the following (may have to tweak everything to get the countdown into the right place, and the colors right):

FRAME_BACKGROUND='base_image.jpg';TOTAL_SECONDS=15;FPS=29.97;STROKE_WIDTH=2;STROKE_COLOR='#18FF';TEXT_COLOR=white;RECTANGLE_FILL='#000C';RECTANGLE_POSITION=400,200,650,400;TIME_POINTSIZE=32;POSITION_TIME=+485+325;BREAK_TEXT_SIZE=24;POSITION_BREAK_TEXT=+439+241;FRAMES=`echo "$TOTAL_SECONDS * $FPS"|bc`;for FRAME in `seq -w 1 $FRAMES`; do TIME=`perl -e "print sprintf('%02.1f','$TOTAL_SECONDS' - ('$FRAME' * '$TOTAL_SECONDS' / '$FRAMES'))"`;convert $FRAME_BACKGROUND -fill $RECTANGLE_FILL -draw "rectangle $RECTANGLE_POSITION" -stroke $STROKE_COLOR -strokewidth $STROKE_WIDTH -pointsize $BREAK_TEXT_SIZE -annotate $POSITION_BREAK_TEXT 'Break Ends In:' -stroke none -fill $TEXT_COLOR -pointsize $BREAK_TEXT_SIZE -annotate $POSITION_BREAK_TEXT 'Break Ends In:' -stroke $STROKE_COLOR -strokewidth $STROKE_WIDTH -pointsize $TIME_POINTSIZE -annotate $POSITION_TIME "$TIME secs" -stroke none -fill $TEXT_COLOR -pointsize $TIME_POINTSIZE -annotate $POSITION_TIME "$TIME secs" image$FRAME.jpg; done

This will generate the new frames.  Next, you need to combine them into one video using :

ffmpeg -r 30 -f image2 -i image%03d.jpg -i ../track.wav video.avi

You may have to tweak the process to get it just the way you like, but it should work.