├── flags.json ├── options.json ├── .vscode └── launch.json ├── lib └── Execute.js ├── package.json ├── LICENSE ├── .gitignore ├── index.js └── README.md /flags.json: -------------------------------------------------------------------------------- 1 | [ 2 | "raw", 3 | "verbose", 4 | "fullscreen", 5 | "nopreview", 6 | "vstab", 7 | "hflip", 8 | "vflip", 9 | "timestamp", 10 | "datetime" 11 | ] 12 | -------------------------------------------------------------------------------- /options.json: -------------------------------------------------------------------------------- 1 | [ 2 | "output", 3 | "width", 4 | "height", 5 | "quality", 6 | "latest", 7 | "timeout", 8 | "thumb", 9 | "demo", 10 | "encoding", 11 | "timelapse", 12 | "framerate", 13 | "rotation", 14 | "preview", 15 | "opacity", 16 | "annotate", 17 | "exif", 18 | "brightness", 19 | "contrast", 20 | "shutter", 21 | "saturation", 22 | "roi", 23 | "ISO", 24 | "exposure" 25 | ] 26 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Playground", 11 | "program": "${workspaceRoot}/playground/test.js" 12 | }, 13 | { 14 | "type": "node", 15 | "request": "launch", 16 | "name": "Launch Program", 17 | "program": "${workspaceRoot}/index.js" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /lib/Execute.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | 3 | class Execute { 4 | static run(fullCmd, options = {}) { 5 | return new Promise((resolve, reject) => { 6 | exec(fullCmd, options, (error, stdout, stderr) => { 7 | if (stderr || error) { 8 | reject(stderr || error); 9 | } 10 | resolve(stdout); 11 | }); 12 | }); 13 | } 14 | 15 | static cmd(base, params) { 16 | if (!params && base) { 17 | return base; 18 | } 19 | if (params.constructor !== Array) { 20 | throw new Error('params must be an Array'); 21 | } 22 | 23 | return base + ' ' + params.join(' '); 24 | } 25 | } 26 | 27 | module.exports = Execute; 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pi-camera", 3 | "version": "1.7.0", 4 | "description": "A very lightweight promise based Node.js wrapper for the native Raspberry Pi Camera CLI tools.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "engines": { 10 | "node": ">=6.10.0" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/stetsmando/pi-camera.git" 15 | }, 16 | "keywords": [ 17 | "Raspberry", 18 | "Pi", 19 | "Camera", 20 | "Picture", 21 | "Photo", 22 | "Video", 23 | "raspicam", 24 | "raspistill", 25 | "raspivid", 26 | "picamera", 27 | "pi-camera" 28 | ], 29 | "author": "Stetson Pierce ", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/stetsmando/pi-camera/issues" 33 | }, 34 | "homepage": "https://github.com/stetsmando/pi-camera#readme" 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Stetson Pierce 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # Custom 61 | playground/ -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Execute = require('./lib/Execute'); 2 | const FLAGS = require('./flags'); 3 | const OPTS = require('./options'); 4 | 5 | class PiCamera { 6 | constructor(config) { 7 | // Ensure config is an object 8 | config = config || {}; 9 | 10 | if (!config.mode) { 11 | throw new Error('PiCamera requires a mode to be created'); 12 | } 13 | 14 | // Set the mode prop 15 | this.mode = config.mode; 16 | 17 | // Remove mode and set config 18 | delete config.mode; 19 | this.config = config; 20 | } 21 | 22 | /* 23 | * Getter 24 | */ 25 | get(prop) { 26 | if (!this.config[prop]) { 27 | throw new Error(`${ prop } isn't set on current object!`); 28 | } 29 | 30 | return this.config[prop]; 31 | } 32 | 33 | /* 34 | * Setter 35 | */ 36 | set(prop, value) { 37 | this.config[prop] = value; 38 | 39 | return this.config[prop]; 40 | } 41 | 42 | // Take a picture 43 | snap() { 44 | if (this.mode !== 'photo') { 45 | throw new Error(`snap() can only be called when Pi-Camera is in 'photo' mode`); 46 | } 47 | 48 | return Execute.run(Execute.cmd('raspistill', this.configToArray())); 49 | } 50 | 51 | // Take a picture and return it in Data URL format (data:image/jpg;base64,...) 52 | async snapDataUrl(maxBuffer = 1024*1024*10) { 53 | 54 | if (this.mode !== 'photo') { 55 | throw new Error(`snapDataUrl() can only be called when Pi-Camera is in 'photo' mode`); 56 | } 57 | 58 | this.config.output = '-'; 59 | const image = await Execute.run(Execute.cmd('raspistill', this.configToArray()), {encoding: 'binary', maxBuffer: maxBuffer}); 60 | let data = Buffer.from(image, 'binary').toString('base64'); 61 | return 'data:image/jpg;base64,' + data; 62 | } 63 | 64 | // Record a video 65 | record() { 66 | if (this.mode !== 'video') { 67 | throw new Error(`record() can only be called when Pi-Camera is in 'video' mode`); 68 | } 69 | 70 | return Execute.run(Execute.cmd('raspivid', this.configToArray())); 71 | } 72 | } 73 | 74 | // Takes internal config object and return array version 75 | PiCamera.prototype.configToArray = function() { 76 | let configArray = [] 77 | Object.keys(this.config).forEach((key, i) => { 78 | // Only include flags if they're set to true 79 | if (FLAGS.includes(key) && this.config[key]) { 80 | configArray.push(`--${key}`) 81 | } else if (OPTS.includes(key)) { 82 | configArray.push(`--${key}`, this.config[key]) 83 | } 84 | }) 85 | 86 | return configArray; 87 | } 88 | 89 | module.exports = PiCamera; 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pi-Camera 2 | A _very_ lightweight promise based Node.js wrapper for the [native Raspberry Pi Camera CLI tools](https://www.raspberrypi.org/documentation/usage/camera/raspicam/README.md). 3 | 4 | _Note: This was written with the intent of NOT being a hand holding library, but rather just give you access to execute commands on the camera in a flexible way._ 5 | 6 | ## Want to Contribute or Help Out? 7 | Feel free to head over to the GitHub page for Pi-Camera and submit comments, issues, pulls, and whatever else you'd like. I plan on adding features as I need them for my own projects so if something isn't happening fast enough for you why not fix it? (: 8 | 9 | ## Installation 10 | ```javascript 11 | // NPM 5 12 | npm install pi-camera 13 | 14 | // Older NPM versions 15 | npm install pi-camera --save 16 | ``` 17 | 18 | ## Basic usage 19 | ### Raspistill (Image Capture) 20 | ```javascript 21 | const PiCamera = require('pi-camera'); 22 | const myCamera = new PiCamera({ 23 | mode: 'photo', 24 | output: `${ __dirname }/test.jpg`, 25 | width: 640, 26 | height: 480, 27 | nopreview: true, 28 | }); 29 | 30 | myCamera.snap() 31 | .then((result) => { 32 | // Your picture was captured 33 | }) 34 | .catch((error) => { 35 | // Handle your error 36 | }); 37 | ``` 38 | 39 | You may use the snapDataUrl method for capturing a photo in Data-URL format (i.e. for embedding it in a website) without saving it to disk. 40 | ```javascript 41 | const PiCamera = require('pi-camera'); 42 | const myCamera = new PiCamera({ 43 | mode: 'photo', 44 | width: 640, 45 | height: 480, 46 | nopreview: true, 47 | }); 48 | 49 | myCamera.snapDataUrl() 50 | .then((result) => { 51 | // Your picture was captured 52 | console.log(''); 53 | }) 54 | .catch((error) => { 55 | // Handle your error 56 | }); 57 | ``` 58 | 59 | ### Raspivid (Video Capture) 60 | ```javascript 61 | const PiCamera = require('pi-camera'); 62 | const myCamera = new PiCamera({ 63 | mode: 'video', 64 | output: `${ __dirname }/video.h264`, 65 | width: 1920, 66 | height: 1080, 67 | timeout: 5000, // Record for 5 seconds 68 | nopreview: true, 69 | }); 70 | 71 | myCamera.record() 72 | .then((result) => { 73 | // Your video was captured 74 | }) 75 | .catch((error) => { 76 | // Handle your error 77 | }); 78 | ``` 79 | 80 | __Something worth considering is that the Camera module captures videos in a .h264 format, which is raw and uncompressed. Most players do not support this format so you might want to convert your files into something like .mp4. You can read more about it [here](https://www.raspberrypi.org/documentation/usage/camera/raspicam/raspivid.md).__ 81 | 82 | ## Flags and Options 83 | The Raspistill and Raspivid commands support a good number of parameters and options. 84 | 85 | Currently they're all stored in the same files, so you'll need to do your do diligence and make sure you're using the correct options and flags for what you're trying to do. A good list of them can be found [here](https://www.raspberrypi.org/documentation/raspbian/applications/camera.md) 86 | 87 | ### **What's the difference between Flags and Options?** 88 | Good question! 89 | 90 | Flags are portions of the `Raspistill` and `Raspivid` commands that are passed and require no additional input to fuction like so: 91 | ```bash 92 | # NOTE: Not a working command 93 | raspistill --nopreview --raw --hflip --vflip 94 | ``` 95 | 96 | Options are portions of the `Raspistill` and `Raspivid` commands that are passed and require additional input to fuction like so: 97 | ```bash 98 | # NOTE: Not a working command 99 | raspistill --output some/path/here --width 1080 --height 720 100 | ``` 101 | 102 | While the command line tools support many flags and options they're not all configured in this lib. If you discover one that you need but isn't supported, consider testing it, adding it and making a PR. 103 | --------------------------------------------------------------------------------