I previously wrote an introduction to the Tessel here where I described a program that blinked out tweets in morse code. For my second project with the Tessel I wanted to use some of the modules that came with it to create a basic security device/camera to take pictures and alert me when something bad is happening.
Modules
The Tessel has 4 different ports to plug modules into, each labeled A, B, C and D. The four modules I decided to use for this project were:
- Camera Module in port A
- Light and Sound in port B
- Accelerometer in port C
- Climate in port D
The accelerometer is used to check if someone is tampering with the camera, also for Earthquakes which are concerningly frequent in New Zealand.
The light and sound detector can be used to detect sudden changes in light or noise level.
The climate detector can detect if the temperate goes above a threshold, for example if the house is on fire.
The camera is used to take pictures of what is happening and save them for later inspection. Originally I wanted to tweet them, I had some problems with the twitter API to do this.
Code
Full Code is available here
I have divided each of the modules into their own sections of code to make it easier to describe. The code which all of the modules require is the tessel library, and the q library for promises (my usual choice of bluebird does not work on the Tessel):
tessel = require('tessel') q = require 'q'
Additionally, I found it useful to reimplement bluebird’s promisify function, which turns a callback into a promise:
promisify = (fn, thisArg = null) -> defer = q.defer() callback = (err, data) -> return defer.reject(err) if err defer.resolve(data) fn.apply(thisArg, [callback]) defer.promise
Accelerometer
To use a module you only have to require its model number from npm, and then call use on the port it is plugged into. For example, the accelerometers model number is accel-mma84 and is plugged into port C, so to access the module I only need to call require(‘accel-mma84’).use(tessel.port[‘C’]). This is so easy!
The core function I need to create is called is_moving and returns true if the Tessel is moving. This uses the get_xyz function to return a promise for the acceleration of the module in is an array of [x,y,z] values. Then, it waits for a second takes a different measurement and if the two are difference above a sensitivity level.
accel = require('accel-mma84').use(tessel.port['C']); get_xyz = -> promisify(accel.getAcceleration, accel)``is_moving = (sensitivity = 0.005) -> xyz1 = null xyz2 = null get_xyz() .then( (xyz) -> xyz1 = xyz q.delay(1000).then(-> get_xyz()) ) .then( (xyz) -> xyz2 = xyz diffx = Math.abs(xyz2[0] - xyz1[0]) diffy = Math.abs(xyz2[1] - xyz1[1]) diffz = Math.abs(xyz2[2] - xyz1[2]) if (diffx + diffy + diffz) > sensitivity true else false )
Ambient Sound and Light
Similar to accelerometer, the is_light_sound_changing function measures the light level and sound level twice, a second apart, and if there are differences above a sensitivity it returns true.
ambient = require('ambient-attx4').use(tessel.port['B']);``get_light_and_sound = -> q.all([promisify(ambient.getLightLevel, ambient),promisify(ambient.getSoundLevel, ambient)])``is_light_sound_changing = (light_sensitivity=0.001, sound_sensitivity=0.005) -> l1 = null l2 = null s1 = null s2 = null get_light_and_sound() .then((light_and_sound) -> l1 = light_and_sound[0] s1 = light_and_sound[1] q.delay(1000).then(-> get_light_and_sound()) ) .then((light_and_sound) -> l2 = light_and_sound[0] s2 = light_and_sound[1] diffl = Math.abs(l1 - l2) diffs = Math.abs(s1 - s2) console.log 'diff', diffl, diffs if diffl > light_sensitivity || diffs > sound_sensitivity true else false )
Climate
The is_burning function measures the temperature and if it is above 30 degrees (which is warm for New Zealand, probably cool for Australia) it returns true.
climate = require('climate-si7020').use(tessel.port['D'])``get_temp = -> promisify(climate.readTemperature, climate)``is_burning = (max_temp=30) -> get_temp() .then( (temp) -> console.log temp if temp > max_temp true else false )
Camera
The turn_camera_on function is used to initialise the camera, and then the take_a_picture function takes a picture and saves it to the computer that the Tessel is plugged in to.
camera = require('camera-vc0706').use(tessel.port['A']);``turn_camera_on = -> console.log "Turn Camera On" camera_ready_defer = q.defer() camera.on('ready', -> console.log "Camera On" camera_ready_defer.resolve(true) ) return camera_ready_defer.promise``take_a_picutre = -> promisify(camera.takePicture, camera) .then( (image) -> console.log "Image Taken:", image name = "picture-#{Date.now()}.jpg" console.log('Picture saving as', name, '...'); process.sendfile(name, image); console.log('done.'); )
The Main Loop
First the camera is turned on, then every 10 seconds it checks whether the light or sound is changing, it is too hot, and whether the Tessel is moving. Then, if any of these are true it logs it and takes a picture.
turn_camera_on() .then( -> setInterval( -> q.all([is_light_sound_changing(), is_burning(), is_moving()]) .spread( (lsc, burning, moving) -> if lsc or burning or moving console.log "Light or Sound Changing" if lsc console.log "Burning" if burning console.log "Moving" if moving take_a_picutre() else console.log "Nothing is happening" ) , 10000) )
Executing it
To execute this code first we have to build the javascript code from the coffeescript.
coffee -c tessel_security_camera.coffee
Now we can run it, and give it a directory to put the taken pictures into.
tessel run tessel_security_camera.js \ --upload-dir=/pictures/folder
Conclusion
The Tessel modules are one of its massive advantages over other embedded development platforms. As you can see above, it is incredibly easy to integrate and use these modules. I think that if you want to quickly make some simple, embedded thing that will just work with the least amount of hassle, then you should definitely consider using the Tessel.