├── images └── .gitkeep ├── .gitignore ├── package.json ├── README.md └── camera.js /images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raspeye", 3 | "version": "0.0.1", 4 | "description": "Raspberry Pi camera streaming & timelapsing", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/weworkweplay/raspeye-rpi" 8 | }, 9 | "scripts": { 10 | "start": "node camera.js" 11 | }, 12 | "dependencies": { 13 | "request": "*" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RaspEye 2 | Create a low-cost webcam with the Raspberry Pi and the default RPi Camera board. By default, it stores your pictures on its SD-card, but there's an option to set an API URL and have all images uploaded to your server. 3 | 4 | [We published an example server and front-end setup here.](https://github.com/weworkweplay/raspeye-web) 5 | 6 | ## Setup 7 | 1. Download NOOBS to SD-card 8 | 2. Install Raspbian 9 | 3. Install Node (http://weworkweplay.com/play/raspberry-pi-nodejs/ - step 2) 10 | 4. Install Forever (`$ sudo -i npm install -g forever`) 11 | 5. Clone this repository onto your Raspberry Pi. 12 | 6. Install dependencies (`$ npm install`) 13 | 7. If you want to post the images to your server, edit the API-variable in `camera.js` 14 | 8. To have it run on startup, open crontab configuration: `$ crontab -u pi -e` 15 | 9. Add the following line: `@reboot /usr/bin/sudo -u pi -H /usr/local/bin/forever start -a /home/pi/camera.js` 16 | 17 | Note that if you're using a Raspberry Pi A, you have no ethernet port and you'll have to go through some trouble to set up automatic WiFi-connecting via a dongle if you want to push to a server. 18 | 19 | If you turn off image uploading, all images stay on the SD-card, the app will probably crash when the card is full. 20 | 21 | When booting, the app goes in burst mode. It'll take an image as soon as the camera is ready during three minutes, so when you're looking through the livestream you can position your camera and adjust it easily. You can turn it off by changing the `burstMode` to `false`. 22 | -------------------------------------------------------------------------------- /camera.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'), 2 | request = require('request'), 3 | fs = require('fs'), 4 | exec = require('child_process').exec, 5 | api = 'http://link.to.api/', 6 | auth = { 7 | username: 'demo', 8 | password: 'demo' 9 | }, 10 | takePicture, 11 | uploadPicture, 12 | syncPictures; 13 | 14 | /** 15 | * File upload 16 | * Delete file on 200 OK 17 | * 18 | * @param string fileName 19 | * @return void 20 | */ 21 | uploadPicture = function (fileName, callback) { 22 | if (api.length === 0) { 23 | return false; 24 | } 25 | 26 | fs.exists(fileName, function (exists) { 27 | if (!exists) { 28 | return false; 29 | } 30 | 31 | fs.createReadStream(fileName).pipe(request.post(api + '/upload/', function (error, response, body) { 32 | if (response.statusCode === 200) { 33 | fs.unlink(fileName, function (err) {}); 34 | } 35 | 36 | takePicture(); 37 | }).auth(auth.username, auth.password, true)); 38 | }); 39 | }; 40 | 41 | /** 42 | * Execute default raspistill command, saving picture in /images/ 43 | * 44 | * @return void 45 | */ 46 | takePicture = function (callback) { 47 | var now = new Date(), 48 | fileName = '/home/pi/images/' + now.getTime() + '.jpg'; 49 | 50 | exec('raspistill -o ' + fileName + ' -w 1920 -h 1080 -q 15', function (err, stdin, stdout) { 51 | if (!err) { 52 | uploadPicture(fileName); 53 | } 54 | }); 55 | }; 56 | 57 | /** 58 | * Find files that didn't get deleted (sign of failed upload) and try to upload them again 59 | * 60 | * @return void 61 | */ 62 | syncPictures = function () { 63 | fs.readdir('/home/pi/images/', function (err, files) { 64 | if (err) { 65 | return false; 66 | } 67 | 68 | if (files.length > 0) { 69 | var queueUpload = function (i) { 70 | setTimeout(function () { 71 | uploadPicture('/home/pi/images/' + files[i]); 72 | }, i * 200); 73 | }; 74 | 75 | for (var i = 0; i < files.length; i++) { 76 | queueUpload(i); 77 | } 78 | } 79 | }); 80 | }; 81 | 82 | // Ready for take-off! 83 | syncPictures(); 84 | takePicture(); 85 | --------------------------------------------------------------------------------