├── .editorconfig ├── .github └── workflows │ ├── nodejs.yml │ └── npm-publish.yml ├── .gitignore ├── .idea └── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── binding.gyp ├── examples ├── async-explicit.js ├── duty.js ├── fake-test.js ├── promisse.js ├── sync-explicit.js └── sync-implicit.js ├── lib └── index.js ├── package-lock.json ├── package.json ├── src ├── abstract-gpio.cpp ├── abstract-gpio.h ├── bcm2835 │ ├── bcm2835.c │ └── bcm2835.h ├── dht-sensor.cpp ├── dht-sensor.h ├── node-dht-sensor.cpp ├── util.cpp └── util.h └── test └── node-dht-sensor.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | 8 | [*.{gyp,js,cpp,h}] 9 | charset = utf-8 10 | indent_style = space 11 | indent_size = 2 12 | 13 | /* */ 14 | [/src/bcm2835/**] 15 | charset = unset 16 | end_of_line = unset 17 | insert_final_newline = unset 18 | indent_style = unset 19 | indent_size = unset 20 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | node-version: [16.x, 18.x, 20.x] # Updated Node.js versions 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | cache: 'npm' # Caches npm modules 22 | - run: npm ci 23 | - name: Install build tools (if needed) 24 | run: sudo apt-get install -y build-essential python3 make 25 | - run: npm rebuild node-gyp # Rebuild node-gyp if necessary 26 | - run: npm run build --if-present 27 | - run: npm test 28 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to npm 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: actions/setup-node@v3 11 | with: 12 | node-version: '20.x' 13 | registry-url: 'https://registry.npmjs.org' 14 | - run: npm ci 15 | - run: npm publish 16 | env: 17 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .AppleDouble 3 | 4 | lib-cov 5 | *.seed 6 | *.log 7 | *.csv 8 | *.dat 9 | *.out 10 | *.pid 11 | *.gz 12 | 13 | pids 14 | logs 15 | results 16 | build 17 | 18 | npm-debug.log 19 | node_modules 20 | 21 | .vscode/ 22 | *.tgz 23 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12" 4 | - "10" 5 | - "8" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-dht-sensor 2 | 3 | A simple node.js module for reading temperature and relative humidity using a compatible DHT sensor. 4 | 5 | ![](https://github.com/momenso/node-dht-sensor/workflows/Node.js%20CI/badge.svg) 6 | [![npm](https://img.shields.io/npm/v/node-dht-sensor.svg?label=npm%20package)](https://www.npmjs.com/package/node-dht-sensor) 7 | [![npm](https://img.shields.io/npm/dm/node-dht-sensor.svg)](https://www.npmjs.com/package/node-dht-sensor) 8 | [![LICENSE](https://img.shields.io/github/license/momenso/node-dht-sensor.svg)](https://github.com/momenso/node-dht-sensor/blob/master/LICENSE) 9 | 10 | ## Installation 11 | 12 | ```shell session 13 | $ npm install node-dht-sensor 14 | ``` 15 | 16 | ### Installing on Raspberry Pi 5 (libgpiod requirement) 17 | 18 | When running node-dht-sensor on a Raspberry Pi 5 (or newer), you must install libgpiod (and its development headers) before you build. If you try to install the module with --use_libgpiod=true without having libgpiod-dev installed, the build will fail. 19 | 20 | For Raspberry Pi OS (Debian-based), use: 21 | 22 | ```sh 23 | sudo apt-get update 24 | sudo apt-get install -y libgpiod-dev 25 | ``` 26 | 27 | After installing libgpiod-dev, build node-dht-sensor with: 28 | 29 | ```sh 30 | npm install node-dht-sensor --use_libgpiod=true 31 | ``` 32 | 33 | > Note: Specifying --use_libgpiod=true compiles and links against libgpiod for GPIO access, because the BCM2835 library does not work on Raspberry Pi 5’s architecture. If you omit --use_libgpiod=true, node-dht-sensor defaults to using BCM2835, which is compatible with older Raspberry Pi models. 34 | 35 | ## Usage 36 | 37 | To initialize the sensor, you have to specify the sensor type and the [GPIO pin](https://www.raspberrypi.org/documentation/usage/gpio/) where the sensor is connected to. It should work for DHT11, DHT22 and AM2302 sensors. 38 | 39 | You should use sensorType value to match the sensor as follows: 40 | 41 | | Sensor | sensorType value | 42 | | --------------- | :--------------: | 43 | | DHT11 | 11 | 44 | | DHT22 or AM2302 | 22 | 45 | 46 | If the initialization succeeds when you can call the read function to obtain the latest readout from the sensor. Readout values contains a temperature and a humidity property. 47 | 48 | ### First Example 49 | 50 | ![example1](https://cloud.githubusercontent.com/assets/420851/20246902/1a03bafc-a9a8-11e6-8158-d68928b2e79f.png) 51 | 52 | This sample queries a DHT11 sensor connected to the GPIO 4 and prints out the result on the console. 53 | 54 | ```javascript 55 | var sensor = require("node-dht-sensor"); 56 | 57 | sensor.read(11, 4, function(err, temperature, humidity) { 58 | if (!err) { 59 | console.log(`temp: ${temperature}°C, humidity: ${humidity}%`); 60 | } 61 | }); 62 | ``` 63 | 64 | ### Multiple Sensors Example 65 | 66 | ![example2](https://cloud.githubusercontent.com/assets/420851/20246914/554d72c4-a9a8-11e6-9162-ae51ecdf4212.png) 67 | 68 | The following example shows a method for querying multiple sensors connected to the same Raspberry Pi. For this example, we have two sensors: 69 | 70 | 1. A DHT11 sensor connected to GPIO 17 71 | 2. High-resolution DHT22 sensor connected to GPIO 4 72 | 73 | ```javascript 74 | var sensorLib = require("node-dht-sensor"); 75 | 76 | var app = { 77 | sensors: [ 78 | { 79 | name: "Indoor", 80 | type: 11, 81 | pin: 17 82 | }, 83 | { 84 | name: "Outdoor", 85 | type: 22, 86 | pin: 4 87 | } 88 | ], 89 | read: function() { 90 | for (var sensor in this.sensors) { 91 | var readout = sensorLib.read( 92 | this.sensors[sensor].type, 93 | this.sensors[sensor].pin 94 | ); 95 | console.log( 96 | `[${this.sensors[sensor].name}] ` + 97 | `temperature: ${readout.temperature.toFixed(1)}°C, ` + 98 | `humidity: ${readout.humidity.toFixed(1)}%` 99 | ); 100 | } 101 | setTimeout(function() { 102 | app.read(); 103 | }, 2000); 104 | } 105 | }; 106 | 107 | app.read(); 108 | ``` 109 | 110 | ### Promises API 111 | 112 | Promises API provides an alternative `read` method that returns a Promise object rather than using a callback. The API is accessible via `require('node-dht-sensor').promises`. 113 | 114 | ```javascript 115 | var sensor = require("node-dht-sensor").promises; 116 | 117 | // You can use `initialize` and `setMaxTries` just like before 118 | sensor.setMaxRetries(10); 119 | sensor.initialize(22, 17); 120 | 121 | // You can still use the synchronous version of `read`: 122 | // var readout = sensor.readSync(22, 4); 123 | 124 | sensor.read(22, 17).then( 125 | function(res) { 126 | console.log( 127 | `temp: ${res.temperature.toFixed(1)}°C, ` + 128 | `humidity: ${res.humidity.toFixed(1)}%` 129 | ); 130 | }, 131 | function(err) { 132 | console.error("Failed to read sensor data:", err); 133 | } 134 | ); 135 | ``` 136 | 137 | Using `async/await`: 138 | 139 | ```javascript 140 | const sensor = require("node-dht-sensor").promises; 141 | 142 | async function exec() { 143 | try { 144 | const res = await sensor.read(22, 4); 145 | console.log( 146 | `temp: ${res.temperature.toFixed(1)}°C, ` + 147 | `humidity: ${res.humidity.toFixed(1)}%` 148 | ); 149 | } catch (err) { 150 | console.error("Failed to read sensor data:", err); 151 | } 152 | } 153 | 154 | exec(); 155 | ``` 156 | 157 | ### Test mode 158 | 159 | A _test mode_ of operation is available since version `0.2.0`. In this mode of operation, the library does not communicate with the sensor hardware via the **GPIO** but instead it returns a pre-configured readout value. You can use the test mode during development without the need to have an actual sensor connected. 160 | 161 | To enable the _test mode_, fake values must be defined at initialization. In the example below we specify fixed values for temperature equal to 21°C and humidity equal to 60%. 162 | 163 | ```javascript 164 | sensor.initialize({ 165 | test: { 166 | fake: { 167 | temperature: 21, 168 | humidity: 60 169 | } 170 | } 171 | }); 172 | ``` 173 | 174 | After initialization, we can call the `read` method as usual. 175 | 176 | ```javascript 177 | sensor.read(22, 4, function(err, temperature, humidity) { 178 | if (!err) { 179 | console.log( 180 | `temp: ${temperature.toFixed(1)}°C, ` + 181 | `humidity: ${humidity.toFixed(1)}%` 182 | ); 183 | } 184 | }); 185 | ``` 186 | 187 | And the result will always be the configured readout value defined at initialization. 188 | 189 | ```shell session 190 | $ node examples/fake-test.js 191 | temp: 21.0°C, humidity: 60.0% 192 | $ node examples/fake-test.js 193 | temp: 21.0°C, humidity: 60.0% 194 | ``` 195 | 196 | You can find a complete source code example in [examples/fake-test.js](https://github.com/momenso/node-dht-sensor/blob/master/examples/fake-test.js). 197 | 198 | ### Reference for building from source 199 | 200 | Standard node-gyp commands are used to build the module. So, just make sure you have node and node-gyp as well as the Broadcom library to build the project. 201 | 202 | 1. In case, you don't have node-gyp, install it first: 203 | 204 | ```shell session 205 | $ sudo npm install -g node-gyp 206 | $ sudo update-alternatives --install /usr/bin/node-gyp node-gyp /opt/node-v10.15.3-linux-armv7l/bin/node-gyp 1 207 | ``` 208 | 209 | 2. Generate the configuration files 210 | 211 | ```shell session 212 | $ node-gyp configure 213 | ``` 214 | 215 | 3. Build the component 216 | ```shell session 217 | $ node-gyp build 218 | ``` 219 | 220 | ### Tracing and Debugging 221 | 222 | Verbose output from the module can be enabled by specifying the `--dht_verbose=true` flag when installing the node via npm. 223 | 224 | ```shell session 225 | $ npm install node-dht-sensor --dht_verbose=true 226 | ``` 227 | 228 | if you are interested in enabling trace when building directly from source you can enable the `-Ddht_verbose` flag when running node-gyp configure. 229 | 230 | ```shell session 231 | $ node-gyp configure -- -Ddht_verbose=true 232 | ``` 233 | 234 | ### Appendix A: Quick Node.js installation guide 235 | 236 | There are many ways you can get Node.js installed on your Raspberry Pi. Here is just one way you can do it. 237 | 238 | ```shell session 239 | $ wget https://nodejs.org/dist/v14.15.4/node-v14.15.4-linux-armv7l.tar.xz 240 | $ tar xvfJ node-v14.15.4-linux-armv7l.tar.xz 241 | $ sudo mv node-v14.15.4-linux-armv7l /opt 242 | $ sudo update-alternatives --install /usr/bin/node node /opt/node-v14.15.4-linux-armv7l/bin/node 1 243 | $ sudo update-alternatives --set node /opt/node-v14.15.4-linux-armv7l/bin/node 244 | $ sudo update-alternatives --install /usr/bin/npm npm /opt/node-v14.15.4-linux-armv7l/bin/npm 1 245 | ``` 246 | 247 | Please note that you may have to use armv6l instead of arm7l if you have an early Raspberry Pi model. 248 | 249 | ### References 250 | 251 | [1]: Node.js download - https://nodejs.org/en/download/ 252 | 253 | [2]: BCM2835 - http://www.airspayce.com/mikem/bcm2835/ 254 | 255 | [3]: Node.js native addon build tool - https://github.com/TooTallNate/node-gyp 256 | 257 | [4]: GPIO: Raspbery Pi Models A and B - https://www.raspberrypi.org/documentation/usage/gpio/ 258 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "variables": { 5 | "dht_verbose%": "false", 6 | "use_libgpiod%" : "false" 7 | }, 8 | "target_name": "node_dht_sensor", 9 | "sources": [ 10 | "src/bcm2835/bcm2835.c", 11 | "src/node-dht-sensor.cpp", 12 | "src/dht-sensor.cpp", 13 | "src/util.cpp", 14 | "src/abstract-gpio.cpp", 15 | ], 16 | "include_dirs": [ 17 | "\n" + 5 | " sensorType:\n" + 6 | " 11: For DHT11 sensor.\n" + 7 | " 22: For DHT22 or AM2302 sensors.\n\n" + 8 | " gpipPin:\n" + 9 | " Pin number where the sensor is physically connected to.\n\n" + 10 | " repeats:\n" + 11 | " How many times the read operation will be performed, default: 10\n"; 12 | 13 | if (process.argv.length < 4) { 14 | console.warn(usage); 15 | process.exit(1); 16 | } 17 | 18 | var sensorType = parseInt(process.argv[2], 10); 19 | var gpioPin = parseInt(process.argv[3], 10); 20 | var repeats = parseInt(process.argv[4] || "10", 10); 21 | var count = 0; 22 | var start = 0; 23 | var end = 0; 24 | 25 | var iid = setInterval(function() { 26 | if (++count >= repeats) { 27 | clearInterval(iid); 28 | } 29 | 30 | start = new Date().getTime(); 31 | 32 | sensor.read(sensorType, gpioPin, function(err, temperature, humidity) { 33 | end = new Date().getTime(); 34 | if (err) { 35 | console.warn("" + err); 36 | } else { 37 | console.log( 38 | "temperature: %s°C, humidity: %s%%, time: %dms", 39 | temperature.toFixed(1), 40 | humidity.toFixed(1), 41 | end - start 42 | ); 43 | } 44 | }); 45 | }, 3000); 46 | -------------------------------------------------------------------------------- /examples/duty.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var sensor = require("../lib"); 3 | 4 | var usage = 5 | "USAGE: node duty.js [sensorType] [gpioPin] \n" + 6 | " sensorType:\n" + 7 | " 11: For DHT11 sensor.\n" + 8 | " 22: For DHT22 or AM2302 sensors.\n\n" + 9 | " gpipPin:\n" + 10 | " Pin number where the sensor is physically connected to.\n\n"; 11 | 12 | if (process.argv.length < 4) { 13 | console.warn(usage); 14 | process.exit(1); 15 | } 16 | 17 | var sensorType = parseInt(process.argv[2], 10); 18 | var gpioPin = parseInt(process.argv[3], 10); 19 | var count = 0; 20 | var start = 0; 21 | var end = 0; 22 | 23 | var iid = setInterval(function() { 24 | start = new Date().getTime(); 25 | sensor.read(sensorType, gpioPin, function(err, temperature, humidity) { 26 | end = new Date().getTime(); 27 | if (err) { 28 | console.warn("" + err); 29 | } else { 30 | var elapsed = end - start; 31 | console.log( 32 | "temperature: %s°C, humidity: %s%%, time: %dms", 33 | temperature.toFixed(1), 34 | humidity.toFixed(1), 35 | end - start 36 | ); 37 | fs.appendFile( 38 | "log.csv", 39 | new Date().getTime() + 40 | "," + 41 | temperature.toFixed(2) + 42 | "," + 43 | humidity.toFixed(2) + 44 | "," + 45 | elapsed + 46 | "\n", 47 | function(err) {} 48 | ); 49 | } 50 | }); 51 | }, 1000); // 2500 52 | -------------------------------------------------------------------------------- /examples/fake-test.js: -------------------------------------------------------------------------------- 1 | var sensor = require("../lib"); 2 | 3 | sensor.initialize({ 4 | test: { 5 | fake: { 6 | temperature: 21, 7 | humidity: 60 8 | } 9 | } 10 | }); 11 | 12 | sensor.read(22, 4, function(err, temperature, humidity) { 13 | if (!err) { 14 | console.log( 15 | "temp: " + 16 | temperature.toFixed(1) + 17 | "°C, " + 18 | "humidity: " + 19 | humidity.toFixed(1) + 20 | "%" 21 | ); 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /examples/promisse.js: -------------------------------------------------------------------------------- 1 | var sensor = require("../lib").promises; 2 | 3 | sensor 4 | .read(22, 17) 5 | .then( 6 | function(res) { 7 | console.log( 8 | `temp: ${res.temperature.toFixed(1)}°C, ` + 9 | `humidity: ${res.humidity.toFixed(1)}%` 10 | ); 11 | }, 12 | function(err) { 13 | console.error("Failed to read sensor data:", err); 14 | } 15 | ) 16 | .catch(e => console.error("Error: " + e)); 17 | console.log("done"); 18 | -------------------------------------------------------------------------------- /examples/sync-explicit.js: -------------------------------------------------------------------------------- 1 | // explicit sensor read test 2 | var sensor = require("../lib"); 3 | var usage = 4 | "USAGE: node sync-explicit.js [sensorType] [gpioPin] \n" + 5 | " sensorType:\n" + 6 | " 11: For DHT11 sensor.\n" + 7 | " 22: For DHT22 or AM2302 sensors.\n\n" + 8 | " gpipPin:\n" + 9 | " Pin number where the sensor is physically connected to.\n\n" + 10 | " repeats:\n" + 11 | " How many times the read operation will be performed, default: 10\n"; 12 | 13 | if (process.argv.length < 4) { 14 | console.warn(usage); 15 | process.exit(1); 16 | } 17 | 18 | var sensorType = parseInt(process.argv[2], 10); 19 | var gpioPin = parseInt(process.argv[3], 10); 20 | var repeats = parseInt(process.argv[4] || "10", 10); 21 | var count = 0; 22 | 23 | var iid = setInterval(function() { 24 | if (++count >= repeats) { 25 | clearInterval(iid); 26 | } 27 | var start = new Date().getTime(); 28 | var readout = sensor.read(sensorType, gpioPin); 29 | var end = new Date().getTime(); 30 | console.log( 31 | `temperature: ${readout.temperature.toFixed(1)}°C, ` + 32 | `humidity: ${readout.humidity.toFixed(1)}%, ` + 33 | `valid: ${readout.isValid}, ` + 34 | `errors: ${readout.errors}, ` + 35 | `time: ${end - start}ms` 36 | ); 37 | }, 2500); 38 | -------------------------------------------------------------------------------- /examples/sync-implicit.js: -------------------------------------------------------------------------------- 1 | // implicit sensor read test 2 | var sensor = require("../lib"); 3 | 4 | var usage = 5 | "USAGE: node sync-implicit.js [sensorType] [gpioPin] \n" + 6 | " sensorType:\n" + 7 | " 11: For DHT11 sensor.\n" + 8 | " 22: For DHT22 or AM2302 sensors.\n\n" + 9 | " gpipPin:\n" + 10 | " Pin number where the sensor is physically connected to.\n\n" + 11 | " repeats:\n" + 12 | " How many times the read operation will be performed, default: 10\n"; 13 | 14 | if (process.argv.length < 4) { 15 | console.warn(usage); 16 | process.exit(1); 17 | } 18 | 19 | var sensorType = parseInt(process.argv[2], 10); 20 | var gpioPin = parseInt(process.argv[3], 10); 21 | var repeats = parseInt(process.argv[4] || "10", 10); 22 | var count = 0; 23 | 24 | // initialize sensor 25 | if (!sensor.initialize(sensorType, gpioPin)) { 26 | console.warn("Failed to initialize sensor"); 27 | return; 28 | } 29 | 30 | var iid = setInterval(function() { 31 | if (++count >= repeats) { 32 | clearInterval(iid); 33 | } 34 | var start = new Date().getTime(); 35 | var readout = sensor.read(); 36 | var end = new Date().getTime(); 37 | console.log( 38 | `temperature: ${readout.temperature.toFixed(1)}°C, ` + 39 | `humidity: ${readout.humidity.toFixed(1)}%, ` + 40 | `valid: ${readout.isValid}, ` + 41 | `errors: ${readout.errors}, ` + 42 | `time: ${end - start}ms` 43 | ); 44 | }, 2500); 45 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var sensor = require("../build/Release/node_dht_sensor"); 2 | 3 | var promises = { 4 | initialize: sensor.initialize, 5 | setMaxRetries: sensor.setMaxRetries, 6 | readSync(type, pin) { 7 | return sensor.read(type, pin); 8 | }, 9 | read(type, pin) { 10 | return new Promise(function(resolve, reject) { 11 | sensor.read(type, pin, function(err, temperature, humidity) { 12 | if (err) { 13 | reject(err); 14 | } else { 15 | resolve({ temperature, humidity }); 16 | } 17 | }); 18 | }); 19 | } 20 | }; 21 | 22 | module.exports = { 23 | initialize: sensor.initialize, 24 | read: sensor.read, 25 | setMaxRetries: sensor.setMaxRetries, 26 | promises 27 | }; 28 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-dht-sensor", 3 | "version": "0.4.4", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "node-dht-sensor", 9 | "version": "0.4.4", 10 | "hasInstallScript": true, 11 | "license": "LGPL-3.0", 12 | "dependencies": { 13 | "node-addon-api": "^1.6.3" 14 | }, 15 | "devDependencies": { 16 | "chai": "^4.2.0", 17 | "husky": "^2.2.0", 18 | "mocha": "^7.1.2", 19 | "prettier": "1.17.0", 20 | "pretty-quick": "^1.10.0" 21 | } 22 | }, 23 | "node_modules/@types/normalize-package-data": { 24 | "version": "2.4.0", 25 | "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", 26 | "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", 27 | "dev": true 28 | }, 29 | "node_modules/ansi-colors": { 30 | "version": "3.2.3", 31 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", 32 | "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", 33 | "dev": true, 34 | "engines": { 35 | "node": ">=6" 36 | } 37 | }, 38 | "node_modules/ansi-regex": { 39 | "version": "3.0.1", 40 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", 41 | "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", 42 | "dev": true, 43 | "engines": { 44 | "node": ">=4" 45 | } 46 | }, 47 | "node_modules/ansi-styles": { 48 | "version": "3.2.1", 49 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 50 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 51 | "dev": true, 52 | "dependencies": { 53 | "color-convert": "^1.9.0" 54 | }, 55 | "engines": { 56 | "node": ">=4" 57 | } 58 | }, 59 | "node_modules/anymatch": { 60 | "version": "3.1.1", 61 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 62 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 63 | "dev": true, 64 | "dependencies": { 65 | "normalize-path": "^3.0.0", 66 | "picomatch": "^2.0.4" 67 | }, 68 | "engines": { 69 | "node": ">= 8" 70 | } 71 | }, 72 | "node_modules/argparse": { 73 | "version": "1.0.10", 74 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 75 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 76 | "dev": true, 77 | "dependencies": { 78 | "sprintf-js": "~1.0.2" 79 | } 80 | }, 81 | "node_modules/array-differ": { 82 | "version": "2.1.0", 83 | "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-2.1.0.tgz", 84 | "integrity": "sha512-KbUpJgx909ZscOc/7CLATBFam7P1Z1QRQInvgT0UztM9Q72aGKCunKASAl7WNW0tnPmPyEMeMhdsfWhfmW037w==", 85 | "dev": true, 86 | "engines": { 87 | "node": ">=6" 88 | } 89 | }, 90 | "node_modules/array-union": { 91 | "version": "1.0.2", 92 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 93 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 94 | "dev": true, 95 | "dependencies": { 96 | "array-uniq": "^1.0.1" 97 | }, 98 | "engines": { 99 | "node": ">=0.10.0" 100 | } 101 | }, 102 | "node_modules/array-uniq": { 103 | "version": "1.0.3", 104 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 105 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", 106 | "dev": true, 107 | "engines": { 108 | "node": ">=0.10.0" 109 | } 110 | }, 111 | "node_modules/arrify": { 112 | "version": "1.0.1", 113 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 114 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 115 | "dev": true, 116 | "engines": { 117 | "node": ">=0.10.0" 118 | } 119 | }, 120 | "node_modules/assertion-error": { 121 | "version": "1.1.0", 122 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 123 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 124 | "dev": true, 125 | "engines": { 126 | "node": "*" 127 | } 128 | }, 129 | "node_modules/balanced-match": { 130 | "version": "1.0.0", 131 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 132 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 133 | "dev": true 134 | }, 135 | "node_modules/binary-extensions": { 136 | "version": "2.0.0", 137 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", 138 | "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", 139 | "dev": true, 140 | "engines": { 141 | "node": ">=8" 142 | } 143 | }, 144 | "node_modules/brace-expansion": { 145 | "version": "1.1.11", 146 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 147 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 148 | "dev": true, 149 | "dependencies": { 150 | "balanced-match": "^1.0.0", 151 | "concat-map": "0.0.1" 152 | } 153 | }, 154 | "node_modules/braces": { 155 | "version": "3.0.3", 156 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 157 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 158 | "dev": true, 159 | "license": "MIT", 160 | "dependencies": { 161 | "fill-range": "^7.1.1" 162 | }, 163 | "engines": { 164 | "node": ">=8" 165 | } 166 | }, 167 | "node_modules/browser-stdout": { 168 | "version": "1.3.1", 169 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 170 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 171 | "dev": true 172 | }, 173 | "node_modules/caller-callsite": { 174 | "version": "2.0.0", 175 | "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", 176 | "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", 177 | "dev": true, 178 | "dependencies": { 179 | "callsites": "^2.0.0" 180 | }, 181 | "engines": { 182 | "node": ">=4" 183 | } 184 | }, 185 | "node_modules/caller-path": { 186 | "version": "2.0.0", 187 | "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", 188 | "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", 189 | "dev": true, 190 | "dependencies": { 191 | "caller-callsite": "^2.0.0" 192 | }, 193 | "engines": { 194 | "node": ">=4" 195 | } 196 | }, 197 | "node_modules/callsites": { 198 | "version": "2.0.0", 199 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", 200 | "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", 201 | "dev": true, 202 | "engines": { 203 | "node": ">=4" 204 | } 205 | }, 206 | "node_modules/camelcase": { 207 | "version": "5.3.1", 208 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 209 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 210 | "dev": true, 211 | "engines": { 212 | "node": ">=6" 213 | } 214 | }, 215 | "node_modules/chai": { 216 | "version": "4.2.0", 217 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", 218 | "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", 219 | "dev": true, 220 | "dependencies": { 221 | "assertion-error": "^1.1.0", 222 | "check-error": "^1.0.2", 223 | "deep-eql": "^3.0.1", 224 | "get-func-name": "^2.0.0", 225 | "pathval": "^1.1.0", 226 | "type-detect": "^4.0.5" 227 | }, 228 | "engines": { 229 | "node": ">=4" 230 | } 231 | }, 232 | "node_modules/chalk": { 233 | "version": "2.4.2", 234 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 235 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 236 | "dev": true, 237 | "dependencies": { 238 | "ansi-styles": "^3.2.1", 239 | "escape-string-regexp": "^1.0.5", 240 | "supports-color": "^5.3.0" 241 | }, 242 | "engines": { 243 | "node": ">=4" 244 | } 245 | }, 246 | "node_modules/chalk/node_modules/supports-color": { 247 | "version": "5.5.0", 248 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 249 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 250 | "dev": true, 251 | "dependencies": { 252 | "has-flag": "^3.0.0" 253 | }, 254 | "engines": { 255 | "node": ">=4" 256 | } 257 | }, 258 | "node_modules/check-error": { 259 | "version": "1.0.2", 260 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 261 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 262 | "dev": true, 263 | "engines": { 264 | "node": "*" 265 | } 266 | }, 267 | "node_modules/chokidar": { 268 | "version": "3.3.0", 269 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", 270 | "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", 271 | "dev": true, 272 | "dependencies": { 273 | "anymatch": "~3.1.1", 274 | "braces": "~3.0.2", 275 | "glob-parent": "~5.1.0", 276 | "is-binary-path": "~2.1.0", 277 | "is-glob": "~4.0.1", 278 | "normalize-path": "~3.0.0", 279 | "readdirp": "~3.2.0" 280 | }, 281 | "engines": { 282 | "node": ">= 8.10.0" 283 | }, 284 | "optionalDependencies": { 285 | "fsevents": "~2.1.1" 286 | } 287 | }, 288 | "node_modules/ci-info": { 289 | "version": "2.0.0", 290 | "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", 291 | "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", 292 | "dev": true 293 | }, 294 | "node_modules/cliui": { 295 | "version": "5.0.0", 296 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 297 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 298 | "dev": true, 299 | "dependencies": { 300 | "string-width": "^3.1.0", 301 | "strip-ansi": "^5.2.0", 302 | "wrap-ansi": "^5.1.0" 303 | } 304 | }, 305 | "node_modules/cliui/node_modules/ansi-regex": { 306 | "version": "4.1.1", 307 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", 308 | "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", 309 | "dev": true, 310 | "engines": { 311 | "node": ">=6" 312 | } 313 | }, 314 | "node_modules/cliui/node_modules/string-width": { 315 | "version": "3.1.0", 316 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 317 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 318 | "dev": true, 319 | "dependencies": { 320 | "emoji-regex": "^7.0.1", 321 | "is-fullwidth-code-point": "^2.0.0", 322 | "strip-ansi": "^5.1.0" 323 | }, 324 | "engines": { 325 | "node": ">=6" 326 | } 327 | }, 328 | "node_modules/cliui/node_modules/strip-ansi": { 329 | "version": "5.2.0", 330 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 331 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 332 | "dev": true, 333 | "dependencies": { 334 | "ansi-regex": "^4.1.0" 335 | }, 336 | "engines": { 337 | "node": ">=6" 338 | } 339 | }, 340 | "node_modules/color-convert": { 341 | "version": "1.9.3", 342 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 343 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 344 | "dev": true, 345 | "dependencies": { 346 | "color-name": "1.1.3" 347 | } 348 | }, 349 | "node_modules/color-name": { 350 | "version": "1.1.3", 351 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 352 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 353 | "dev": true 354 | }, 355 | "node_modules/concat-map": { 356 | "version": "0.0.1", 357 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 358 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 359 | "dev": true 360 | }, 361 | "node_modules/cosmiconfig": { 362 | "version": "5.2.0", 363 | "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.0.tgz", 364 | "integrity": "sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g==", 365 | "dev": true, 366 | "dependencies": { 367 | "import-fresh": "^2.0.0", 368 | "is-directory": "^0.3.1", 369 | "js-yaml": "^3.13.0", 370 | "parse-json": "^4.0.0" 371 | }, 372 | "engines": { 373 | "node": ">=4" 374 | } 375 | }, 376 | "node_modules/cross-spawn": { 377 | "version": "6.0.6", 378 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", 379 | "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", 380 | "dev": true, 381 | "license": "MIT", 382 | "dependencies": { 383 | "nice-try": "^1.0.4", 384 | "path-key": "^2.0.1", 385 | "semver": "^5.5.0", 386 | "shebang-command": "^1.2.0", 387 | "which": "^1.2.9" 388 | }, 389 | "engines": { 390 | "node": ">=4.8" 391 | } 392 | }, 393 | "node_modules/debug": { 394 | "version": "3.2.6", 395 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 396 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 397 | "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", 398 | "dev": true, 399 | "dependencies": { 400 | "ms": "^2.1.1" 401 | } 402 | }, 403 | "node_modules/decamelize": { 404 | "version": "1.2.0", 405 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 406 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 407 | "dev": true, 408 | "engines": { 409 | "node": ">=0.10.0" 410 | } 411 | }, 412 | "node_modules/deep-eql": { 413 | "version": "3.0.1", 414 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 415 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 416 | "dev": true, 417 | "dependencies": { 418 | "type-detect": "^4.0.0" 419 | }, 420 | "engines": { 421 | "node": ">=0.12" 422 | } 423 | }, 424 | "node_modules/define-properties": { 425 | "version": "1.1.3", 426 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 427 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 428 | "dev": true, 429 | "dependencies": { 430 | "object-keys": "^1.0.12" 431 | }, 432 | "engines": { 433 | "node": ">= 0.4" 434 | } 435 | }, 436 | "node_modules/diff": { 437 | "version": "3.5.0", 438 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 439 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 440 | "dev": true, 441 | "engines": { 442 | "node": ">=0.3.1" 443 | } 444 | }, 445 | "node_modules/emoji-regex": { 446 | "version": "7.0.3", 447 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 448 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 449 | "dev": true 450 | }, 451 | "node_modules/end-of-stream": { 452 | "version": "1.4.1", 453 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 454 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 455 | "dev": true, 456 | "dependencies": { 457 | "once": "^1.4.0" 458 | } 459 | }, 460 | "node_modules/error-ex": { 461 | "version": "1.3.2", 462 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 463 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 464 | "dev": true, 465 | "dependencies": { 466 | "is-arrayish": "^0.2.1" 467 | } 468 | }, 469 | "node_modules/es-abstract": { 470 | "version": "1.17.5", 471 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", 472 | "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", 473 | "dev": true, 474 | "dependencies": { 475 | "es-to-primitive": "^1.2.1", 476 | "function-bind": "^1.1.1", 477 | "has": "^1.0.3", 478 | "has-symbols": "^1.0.1", 479 | "is-callable": "^1.1.5", 480 | "is-regex": "^1.0.5", 481 | "object-inspect": "^1.7.0", 482 | "object-keys": "^1.1.1", 483 | "object.assign": "^4.1.0", 484 | "string.prototype.trimleft": "^2.1.1", 485 | "string.prototype.trimright": "^2.1.1" 486 | }, 487 | "engines": { 488 | "node": ">= 0.4" 489 | }, 490 | "funding": { 491 | "url": "https://github.com/sponsors/ljharb" 492 | } 493 | }, 494 | "node_modules/es-to-primitive": { 495 | "version": "1.2.1", 496 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 497 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 498 | "dev": true, 499 | "dependencies": { 500 | "is-callable": "^1.1.4", 501 | "is-date-object": "^1.0.1", 502 | "is-symbol": "^1.0.2" 503 | }, 504 | "engines": { 505 | "node": ">= 0.4" 506 | }, 507 | "funding": { 508 | "url": "https://github.com/sponsors/ljharb" 509 | } 510 | }, 511 | "node_modules/escape-string-regexp": { 512 | "version": "1.0.5", 513 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 514 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 515 | "dev": true, 516 | "engines": { 517 | "node": ">=0.8.0" 518 | } 519 | }, 520 | "node_modules/esprima": { 521 | "version": "4.0.1", 522 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 523 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 524 | "dev": true, 525 | "bin": { 526 | "esparse": "bin/esparse.js", 527 | "esvalidate": "bin/esvalidate.js" 528 | }, 529 | "engines": { 530 | "node": ">=4" 531 | } 532 | }, 533 | "node_modules/execa": { 534 | "version": "1.0.0", 535 | "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", 536 | "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", 537 | "dev": true, 538 | "dependencies": { 539 | "cross-spawn": "^6.0.0", 540 | "get-stream": "^4.0.0", 541 | "is-stream": "^1.1.0", 542 | "npm-run-path": "^2.0.0", 543 | "p-finally": "^1.0.0", 544 | "signal-exit": "^3.0.0", 545 | "strip-eof": "^1.0.0" 546 | }, 547 | "engines": { 548 | "node": ">=6" 549 | } 550 | }, 551 | "node_modules/fill-range": { 552 | "version": "7.1.1", 553 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 554 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 555 | "dev": true, 556 | "license": "MIT", 557 | "dependencies": { 558 | "to-regex-range": "^5.0.1" 559 | }, 560 | "engines": { 561 | "node": ">=8" 562 | } 563 | }, 564 | "node_modules/find-up": { 565 | "version": "3.0.0", 566 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 567 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 568 | "dev": true, 569 | "dependencies": { 570 | "locate-path": "^3.0.0" 571 | }, 572 | "engines": { 573 | "node": ">=6" 574 | } 575 | }, 576 | "node_modules/flat": { 577 | "version": "4.1.1", 578 | "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", 579 | "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", 580 | "dev": true, 581 | "license": "BSD-3-Clause", 582 | "dependencies": { 583 | "is-buffer": "~2.0.3" 584 | }, 585 | "bin": { 586 | "flat": "cli.js" 587 | } 588 | }, 589 | "node_modules/fs.realpath": { 590 | "version": "1.0.0", 591 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 592 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 593 | "dev": true 594 | }, 595 | "node_modules/fsevents": { 596 | "version": "2.1.3", 597 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 598 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 599 | "dev": true, 600 | "hasInstallScript": true, 601 | "optional": true, 602 | "os": [ 603 | "darwin" 604 | ], 605 | "engines": { 606 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 607 | } 608 | }, 609 | "node_modules/function-bind": { 610 | "version": "1.1.1", 611 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 612 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 613 | "dev": true 614 | }, 615 | "node_modules/get-caller-file": { 616 | "version": "2.0.5", 617 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 618 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 619 | "dev": true, 620 | "engines": { 621 | "node": "6.* || 8.* || >= 10.*" 622 | } 623 | }, 624 | "node_modules/get-func-name": { 625 | "version": "2.0.2", 626 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", 627 | "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", 628 | "dev": true, 629 | "license": "MIT", 630 | "engines": { 631 | "node": "*" 632 | } 633 | }, 634 | "node_modules/get-stdin": { 635 | "version": "7.0.0", 636 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", 637 | "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", 638 | "dev": true, 639 | "engines": { 640 | "node": ">=8" 641 | } 642 | }, 643 | "node_modules/get-stream": { 644 | "version": "4.1.0", 645 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", 646 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", 647 | "dev": true, 648 | "dependencies": { 649 | "pump": "^3.0.0" 650 | }, 651 | "engines": { 652 | "node": ">=6" 653 | } 654 | }, 655 | "node_modules/glob": { 656 | "version": "7.1.3", 657 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 658 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 659 | "deprecated": "Glob versions prior to v9 are no longer supported", 660 | "dev": true, 661 | "dependencies": { 662 | "fs.realpath": "^1.0.0", 663 | "inflight": "^1.0.4", 664 | "inherits": "2", 665 | "minimatch": "^3.0.4", 666 | "once": "^1.3.0", 667 | "path-is-absolute": "^1.0.0" 668 | }, 669 | "engines": { 670 | "node": "*" 671 | } 672 | }, 673 | "node_modules/glob-parent": { 674 | "version": "5.1.2", 675 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 676 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 677 | "dev": true, 678 | "dependencies": { 679 | "is-glob": "^4.0.1" 680 | }, 681 | "engines": { 682 | "node": ">= 6" 683 | } 684 | }, 685 | "node_modules/growl": { 686 | "version": "1.10.5", 687 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 688 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 689 | "dev": true, 690 | "engines": { 691 | "node": ">=4.x" 692 | } 693 | }, 694 | "node_modules/has": { 695 | "version": "1.0.3", 696 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 697 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 698 | "dev": true, 699 | "dependencies": { 700 | "function-bind": "^1.1.1" 701 | }, 702 | "engines": { 703 | "node": ">= 0.4.0" 704 | } 705 | }, 706 | "node_modules/has-flag": { 707 | "version": "3.0.0", 708 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 709 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 710 | "dev": true, 711 | "engines": { 712 | "node": ">=4" 713 | } 714 | }, 715 | "node_modules/has-symbols": { 716 | "version": "1.0.1", 717 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", 718 | "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", 719 | "dev": true, 720 | "engines": { 721 | "node": ">= 0.4" 722 | }, 723 | "funding": { 724 | "url": "https://github.com/sponsors/ljharb" 725 | } 726 | }, 727 | "node_modules/he": { 728 | "version": "1.2.0", 729 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 730 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 731 | "dev": true, 732 | "bin": { 733 | "he": "bin/he" 734 | } 735 | }, 736 | "node_modules/hosted-git-info": { 737 | "version": "2.8.9", 738 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", 739 | "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", 740 | "dev": true 741 | }, 742 | "node_modules/husky": { 743 | "version": "2.2.0", 744 | "resolved": "https://registry.npmjs.org/husky/-/husky-2.2.0.tgz", 745 | "integrity": "sha512-lG33E7zq6v//H/DQIojPEi1ZL9ebPFt3MxUMD8MR0lrS2ljEPiuUUxlziKIs/o9EafF0chL7bAtLQkcPvXmdnA==", 746 | "dev": true, 747 | "hasInstallScript": true, 748 | "dependencies": { 749 | "cosmiconfig": "^5.2.0", 750 | "execa": "^1.0.0", 751 | "find-up": "^3.0.0", 752 | "get-stdin": "^7.0.0", 753 | "is-ci": "^2.0.0", 754 | "pkg-dir": "^4.1.0", 755 | "please-upgrade-node": "^3.1.1", 756 | "read-pkg": "^5.0.0", 757 | "run-node": "^1.0.0", 758 | "slash": "^2.0.0" 759 | }, 760 | "bin": { 761 | "husky-upgrade": "lib/upgrader/bin.js" 762 | }, 763 | "engines": { 764 | "node": ">=8" 765 | } 766 | }, 767 | "node_modules/ignore": { 768 | "version": "3.3.10", 769 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", 770 | "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", 771 | "dev": true 772 | }, 773 | "node_modules/import-fresh": { 774 | "version": "2.0.0", 775 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", 776 | "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", 777 | "dev": true, 778 | "dependencies": { 779 | "caller-path": "^2.0.0", 780 | "resolve-from": "^3.0.0" 781 | }, 782 | "engines": { 783 | "node": ">=4" 784 | } 785 | }, 786 | "node_modules/inflight": { 787 | "version": "1.0.6", 788 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 789 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 790 | "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", 791 | "dev": true, 792 | "dependencies": { 793 | "once": "^1.3.0", 794 | "wrappy": "1" 795 | } 796 | }, 797 | "node_modules/inherits": { 798 | "version": "2.0.4", 799 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 800 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 801 | "dev": true 802 | }, 803 | "node_modules/is-arrayish": { 804 | "version": "0.2.1", 805 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 806 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 807 | "dev": true 808 | }, 809 | "node_modules/is-binary-path": { 810 | "version": "2.1.0", 811 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 812 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 813 | "dev": true, 814 | "dependencies": { 815 | "binary-extensions": "^2.0.0" 816 | }, 817 | "engines": { 818 | "node": ">=8" 819 | } 820 | }, 821 | "node_modules/is-buffer": { 822 | "version": "2.0.4", 823 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", 824 | "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", 825 | "dev": true, 826 | "engines": { 827 | "node": ">=4" 828 | } 829 | }, 830 | "node_modules/is-callable": { 831 | "version": "1.1.5", 832 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", 833 | "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", 834 | "dev": true, 835 | "engines": { 836 | "node": ">= 0.4" 837 | }, 838 | "funding": { 839 | "url": "https://github.com/sponsors/ljharb" 840 | } 841 | }, 842 | "node_modules/is-ci": { 843 | "version": "2.0.0", 844 | "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", 845 | "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", 846 | "dev": true, 847 | "dependencies": { 848 | "ci-info": "^2.0.0" 849 | }, 850 | "bin": { 851 | "is-ci": "bin.js" 852 | } 853 | }, 854 | "node_modules/is-date-object": { 855 | "version": "1.0.2", 856 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", 857 | "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", 858 | "dev": true, 859 | "engines": { 860 | "node": ">= 0.4" 861 | }, 862 | "funding": { 863 | "url": "https://github.com/sponsors/ljharb" 864 | } 865 | }, 866 | "node_modules/is-directory": { 867 | "version": "0.3.1", 868 | "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", 869 | "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", 870 | "dev": true, 871 | "engines": { 872 | "node": ">=0.10.0" 873 | } 874 | }, 875 | "node_modules/is-extglob": { 876 | "version": "2.1.1", 877 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 878 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 879 | "dev": true, 880 | "engines": { 881 | "node": ">=0.10.0" 882 | } 883 | }, 884 | "node_modules/is-fullwidth-code-point": { 885 | "version": "2.0.0", 886 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 887 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 888 | "dev": true, 889 | "engines": { 890 | "node": ">=4" 891 | } 892 | }, 893 | "node_modules/is-glob": { 894 | "version": "4.0.1", 895 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 896 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 897 | "dev": true, 898 | "dependencies": { 899 | "is-extglob": "^2.1.1" 900 | }, 901 | "engines": { 902 | "node": ">=0.10.0" 903 | } 904 | }, 905 | "node_modules/is-number": { 906 | "version": "7.0.0", 907 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 908 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 909 | "dev": true, 910 | "license": "MIT", 911 | "engines": { 912 | "node": ">=0.12.0" 913 | } 914 | }, 915 | "node_modules/is-regex": { 916 | "version": "1.0.5", 917 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", 918 | "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", 919 | "dev": true, 920 | "dependencies": { 921 | "has": "^1.0.3" 922 | }, 923 | "engines": { 924 | "node": ">= 0.4" 925 | }, 926 | "funding": { 927 | "url": "https://github.com/sponsors/ljharb" 928 | } 929 | }, 930 | "node_modules/is-stream": { 931 | "version": "1.1.0", 932 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 933 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 934 | "dev": true, 935 | "engines": { 936 | "node": ">=0.10.0" 937 | } 938 | }, 939 | "node_modules/is-symbol": { 940 | "version": "1.0.3", 941 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", 942 | "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", 943 | "dev": true, 944 | "dependencies": { 945 | "has-symbols": "^1.0.1" 946 | }, 947 | "engines": { 948 | "node": ">= 0.4" 949 | }, 950 | "funding": { 951 | "url": "https://github.com/sponsors/ljharb" 952 | } 953 | }, 954 | "node_modules/isexe": { 955 | "version": "2.0.0", 956 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 957 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 958 | "dev": true 959 | }, 960 | "node_modules/js-yaml": { 961 | "version": "3.13.1", 962 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 963 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 964 | "dev": true, 965 | "dependencies": { 966 | "argparse": "^1.0.7", 967 | "esprima": "^4.0.0" 968 | }, 969 | "bin": { 970 | "js-yaml": "bin/js-yaml.js" 971 | } 972 | }, 973 | "node_modules/json-parse-better-errors": { 974 | "version": "1.0.2", 975 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 976 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", 977 | "dev": true 978 | }, 979 | "node_modules/locate-path": { 980 | "version": "3.0.0", 981 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 982 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 983 | "dev": true, 984 | "dependencies": { 985 | "p-locate": "^3.0.0", 986 | "path-exists": "^3.0.0" 987 | }, 988 | "engines": { 989 | "node": ">=6" 990 | } 991 | }, 992 | "node_modules/lodash": { 993 | "version": "4.17.21", 994 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 995 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 996 | "dev": true 997 | }, 998 | "node_modules/log-symbols": { 999 | "version": "3.0.0", 1000 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", 1001 | "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", 1002 | "dev": true, 1003 | "dependencies": { 1004 | "chalk": "^2.4.2" 1005 | }, 1006 | "engines": { 1007 | "node": ">=8" 1008 | } 1009 | }, 1010 | "node_modules/lru-cache": { 1011 | "version": "4.1.5", 1012 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 1013 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 1014 | "dev": true, 1015 | "dependencies": { 1016 | "pseudomap": "^1.0.2", 1017 | "yallist": "^2.1.2" 1018 | } 1019 | }, 1020 | "node_modules/minimatch": { 1021 | "version": "3.0.4", 1022 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1023 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1024 | "dev": true, 1025 | "dependencies": { 1026 | "brace-expansion": "^1.1.7" 1027 | }, 1028 | "engines": { 1029 | "node": "*" 1030 | } 1031 | }, 1032 | "node_modules/minimist": { 1033 | "version": "1.2.7", 1034 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", 1035 | "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", 1036 | "dev": true, 1037 | "funding": { 1038 | "url": "https://github.com/sponsors/ljharb" 1039 | } 1040 | }, 1041 | "node_modules/mkdirp": { 1042 | "version": "0.5.5", 1043 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 1044 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 1045 | "dev": true, 1046 | "dependencies": { 1047 | "minimist": "^1.2.5" 1048 | }, 1049 | "bin": { 1050 | "mkdirp": "bin/cmd.js" 1051 | } 1052 | }, 1053 | "node_modules/mocha": { 1054 | "version": "7.2.0", 1055 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", 1056 | "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", 1057 | "dev": true, 1058 | "license": "MIT", 1059 | "dependencies": { 1060 | "ansi-colors": "3.2.3", 1061 | "browser-stdout": "1.3.1", 1062 | "chokidar": "3.3.0", 1063 | "debug": "3.2.6", 1064 | "diff": "3.5.0", 1065 | "escape-string-regexp": "1.0.5", 1066 | "find-up": "3.0.0", 1067 | "glob": "7.1.3", 1068 | "growl": "1.10.5", 1069 | "he": "1.2.0", 1070 | "js-yaml": "3.13.1", 1071 | "log-symbols": "3.0.0", 1072 | "minimatch": "3.0.4", 1073 | "mkdirp": "0.5.5", 1074 | "ms": "2.1.1", 1075 | "node-environment-flags": "1.0.6", 1076 | "object.assign": "4.1.0", 1077 | "strip-json-comments": "2.0.1", 1078 | "supports-color": "6.0.0", 1079 | "which": "1.3.1", 1080 | "wide-align": "1.1.3", 1081 | "yargs": "13.3.2", 1082 | "yargs-parser": "13.1.2", 1083 | "yargs-unparser": "1.6.0" 1084 | }, 1085 | "bin": { 1086 | "_mocha": "bin/_mocha", 1087 | "mocha": "bin/mocha" 1088 | }, 1089 | "engines": { 1090 | "node": ">= 8.10.0" 1091 | }, 1092 | "funding": { 1093 | "type": "opencollective", 1094 | "url": "https://opencollective.com/mochajs" 1095 | } 1096 | }, 1097 | "node_modules/mri": { 1098 | "version": "1.1.4", 1099 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", 1100 | "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", 1101 | "dev": true, 1102 | "engines": { 1103 | "node": ">=4" 1104 | } 1105 | }, 1106 | "node_modules/ms": { 1107 | "version": "2.1.1", 1108 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1109 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 1110 | "dev": true 1111 | }, 1112 | "node_modules/multimatch": { 1113 | "version": "3.0.0", 1114 | "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-3.0.0.tgz", 1115 | "integrity": "sha512-22foS/gqQfANZ3o+W7ST2x25ueHDVNWl/b9OlGcLpy/iKxjCpvcNCM51YCenUi7Mt/jAjjqv8JwZRs8YP5sRjA==", 1116 | "dev": true, 1117 | "dependencies": { 1118 | "array-differ": "^2.0.3", 1119 | "array-union": "^1.0.2", 1120 | "arrify": "^1.0.1", 1121 | "minimatch": "^3.0.4" 1122 | }, 1123 | "engines": { 1124 | "node": ">=6" 1125 | } 1126 | }, 1127 | "node_modules/nice-try": { 1128 | "version": "1.0.5", 1129 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 1130 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", 1131 | "dev": true 1132 | }, 1133 | "node_modules/node-addon-api": { 1134 | "version": "1.6.3", 1135 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.6.3.tgz", 1136 | "integrity": "sha512-FXWH6mqjWgU8ewuahp4spec8LkroFZK2NicOv6bNwZC3kcwZUI8LeZdG80UzTSLLhK4T7MsgNwlYDVRlDdfTDg==" 1137 | }, 1138 | "node_modules/node-environment-flags": { 1139 | "version": "1.0.6", 1140 | "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", 1141 | "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", 1142 | "dev": true, 1143 | "dependencies": { 1144 | "object.getownpropertydescriptors": "^2.0.3", 1145 | "semver": "^5.7.0" 1146 | } 1147 | }, 1148 | "node_modules/normalize-package-data": { 1149 | "version": "2.5.0", 1150 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 1151 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 1152 | "dev": true, 1153 | "dependencies": { 1154 | "hosted-git-info": "^2.1.4", 1155 | "resolve": "^1.10.0", 1156 | "semver": "2 || 3 || 4 || 5", 1157 | "validate-npm-package-license": "^3.0.1" 1158 | } 1159 | }, 1160 | "node_modules/normalize-path": { 1161 | "version": "3.0.0", 1162 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1163 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1164 | "dev": true, 1165 | "engines": { 1166 | "node": ">=0.10.0" 1167 | } 1168 | }, 1169 | "node_modules/npm-run-path": { 1170 | "version": "2.0.2", 1171 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 1172 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 1173 | "dev": true, 1174 | "dependencies": { 1175 | "path-key": "^2.0.0" 1176 | }, 1177 | "engines": { 1178 | "node": ">=4" 1179 | } 1180 | }, 1181 | "node_modules/object-inspect": { 1182 | "version": "1.7.0", 1183 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", 1184 | "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", 1185 | "dev": true, 1186 | "funding": { 1187 | "url": "https://github.com/sponsors/ljharb" 1188 | } 1189 | }, 1190 | "node_modules/object-keys": { 1191 | "version": "1.1.1", 1192 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 1193 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 1194 | "dev": true, 1195 | "engines": { 1196 | "node": ">= 0.4" 1197 | } 1198 | }, 1199 | "node_modules/object.assign": { 1200 | "version": "4.1.0", 1201 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 1202 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 1203 | "dev": true, 1204 | "dependencies": { 1205 | "define-properties": "^1.1.2", 1206 | "function-bind": "^1.1.1", 1207 | "has-symbols": "^1.0.0", 1208 | "object-keys": "^1.0.11" 1209 | }, 1210 | "engines": { 1211 | "node": ">= 0.4" 1212 | } 1213 | }, 1214 | "node_modules/object.getownpropertydescriptors": { 1215 | "version": "2.1.0", 1216 | "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", 1217 | "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", 1218 | "dev": true, 1219 | "dependencies": { 1220 | "define-properties": "^1.1.3", 1221 | "es-abstract": "^1.17.0-next.1" 1222 | }, 1223 | "engines": { 1224 | "node": ">= 0.8" 1225 | }, 1226 | "funding": { 1227 | "url": "https://github.com/sponsors/ljharb" 1228 | } 1229 | }, 1230 | "node_modules/once": { 1231 | "version": "1.4.0", 1232 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1233 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1234 | "dev": true, 1235 | "dependencies": { 1236 | "wrappy": "1" 1237 | } 1238 | }, 1239 | "node_modules/p-finally": { 1240 | "version": "1.0.0", 1241 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 1242 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", 1243 | "dev": true, 1244 | "engines": { 1245 | "node": ">=4" 1246 | } 1247 | }, 1248 | "node_modules/p-limit": { 1249 | "version": "2.2.0", 1250 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", 1251 | "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", 1252 | "dev": true, 1253 | "dependencies": { 1254 | "p-try": "^2.0.0" 1255 | }, 1256 | "engines": { 1257 | "node": ">=6" 1258 | } 1259 | }, 1260 | "node_modules/p-locate": { 1261 | "version": "3.0.0", 1262 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 1263 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 1264 | "dev": true, 1265 | "dependencies": { 1266 | "p-limit": "^2.0.0" 1267 | }, 1268 | "engines": { 1269 | "node": ">=6" 1270 | } 1271 | }, 1272 | "node_modules/p-try": { 1273 | "version": "2.2.0", 1274 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1275 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 1276 | "dev": true, 1277 | "engines": { 1278 | "node": ">=6" 1279 | } 1280 | }, 1281 | "node_modules/parse-json": { 1282 | "version": "4.0.0", 1283 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", 1284 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", 1285 | "dev": true, 1286 | "dependencies": { 1287 | "error-ex": "^1.3.1", 1288 | "json-parse-better-errors": "^1.0.1" 1289 | }, 1290 | "engines": { 1291 | "node": ">=4" 1292 | } 1293 | }, 1294 | "node_modules/path-exists": { 1295 | "version": "3.0.0", 1296 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 1297 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 1298 | "dev": true, 1299 | "engines": { 1300 | "node": ">=4" 1301 | } 1302 | }, 1303 | "node_modules/path-is-absolute": { 1304 | "version": "1.0.1", 1305 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1306 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1307 | "dev": true, 1308 | "engines": { 1309 | "node": ">=0.10.0" 1310 | } 1311 | }, 1312 | "node_modules/path-key": { 1313 | "version": "2.0.1", 1314 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 1315 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 1316 | "dev": true, 1317 | "engines": { 1318 | "node": ">=4" 1319 | } 1320 | }, 1321 | "node_modules/path-parse": { 1322 | "version": "1.0.7", 1323 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1324 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1325 | "dev": true 1326 | }, 1327 | "node_modules/pathval": { 1328 | "version": "1.1.1", 1329 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 1330 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 1331 | "dev": true, 1332 | "engines": { 1333 | "node": "*" 1334 | } 1335 | }, 1336 | "node_modules/picomatch": { 1337 | "version": "2.2.2", 1338 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 1339 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 1340 | "dev": true, 1341 | "engines": { 1342 | "node": ">=8.6" 1343 | }, 1344 | "funding": { 1345 | "url": "https://github.com/sponsors/jonschlinkert" 1346 | } 1347 | }, 1348 | "node_modules/pkg-dir": { 1349 | "version": "4.1.0", 1350 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.1.0.tgz", 1351 | "integrity": "sha512-55k9QN4saZ8q518lE6EFgYiu95u3BWkSajCifhdQjvLvmr8IpnRbhI+UGpWJQfa0KzDguHeeWT1ccO1PmkOi3A==", 1352 | "dev": true, 1353 | "dependencies": { 1354 | "find-up": "^3.0.0" 1355 | }, 1356 | "engines": { 1357 | "node": ">=8" 1358 | } 1359 | }, 1360 | "node_modules/please-upgrade-node": { 1361 | "version": "3.1.1", 1362 | "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz", 1363 | "integrity": "sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==", 1364 | "dev": true, 1365 | "dependencies": { 1366 | "semver-compare": "^1.0.0" 1367 | } 1368 | }, 1369 | "node_modules/prettier": { 1370 | "version": "1.17.0", 1371 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.0.tgz", 1372 | "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==", 1373 | "dev": true, 1374 | "bin": { 1375 | "prettier": "bin-prettier.js" 1376 | }, 1377 | "engines": { 1378 | "node": ">=4" 1379 | } 1380 | }, 1381 | "node_modules/pretty-quick": { 1382 | "version": "1.11.1", 1383 | "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-1.11.1.tgz", 1384 | "integrity": "sha512-kSXCkcETfak7EQXz6WOkCeCqpbC4GIzrN/vaneTGMP/fAtD8NerA9bPhCUqHAks1geo7biZNl5uEMPceeneLuA==", 1385 | "dev": true, 1386 | "license": "MIT", 1387 | "dependencies": { 1388 | "chalk": "^2.3.0", 1389 | "execa": "^0.8.0", 1390 | "find-up": "^2.1.0", 1391 | "ignore": "^3.3.7", 1392 | "mri": "^1.1.0", 1393 | "multimatch": "^3.0.0" 1394 | }, 1395 | "bin": { 1396 | "pretty-quick": "bin/pretty-quick.js" 1397 | }, 1398 | "peerDependencies": { 1399 | "prettier": ">=1.8.0" 1400 | } 1401 | }, 1402 | "node_modules/pretty-quick/node_modules/cross-spawn": { 1403 | "version": "5.1.0", 1404 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 1405 | "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", 1406 | "dev": true, 1407 | "license": "MIT", 1408 | "dependencies": { 1409 | "lru-cache": "^4.0.1", 1410 | "shebang-command": "^1.2.0", 1411 | "which": "^1.2.9" 1412 | } 1413 | }, 1414 | "node_modules/pretty-quick/node_modules/execa": { 1415 | "version": "0.8.0", 1416 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", 1417 | "integrity": "sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==", 1418 | "dev": true, 1419 | "license": "MIT", 1420 | "dependencies": { 1421 | "cross-spawn": "^5.0.1", 1422 | "get-stream": "^3.0.0", 1423 | "is-stream": "^1.1.0", 1424 | "npm-run-path": "^2.0.0", 1425 | "p-finally": "^1.0.0", 1426 | "signal-exit": "^3.0.0", 1427 | "strip-eof": "^1.0.0" 1428 | }, 1429 | "engines": { 1430 | "node": ">=4" 1431 | } 1432 | }, 1433 | "node_modules/pretty-quick/node_modules/find-up": { 1434 | "version": "2.1.0", 1435 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", 1436 | "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", 1437 | "dev": true, 1438 | "dependencies": { 1439 | "locate-path": "^2.0.0" 1440 | }, 1441 | "engines": { 1442 | "node": ">=4" 1443 | } 1444 | }, 1445 | "node_modules/pretty-quick/node_modules/get-stream": { 1446 | "version": "3.0.0", 1447 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 1448 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", 1449 | "dev": true, 1450 | "engines": { 1451 | "node": ">=4" 1452 | } 1453 | }, 1454 | "node_modules/pretty-quick/node_modules/locate-path": { 1455 | "version": "2.0.0", 1456 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", 1457 | "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", 1458 | "dev": true, 1459 | "dependencies": { 1460 | "p-locate": "^2.0.0", 1461 | "path-exists": "^3.0.0" 1462 | }, 1463 | "engines": { 1464 | "node": ">=4" 1465 | } 1466 | }, 1467 | "node_modules/pretty-quick/node_modules/p-limit": { 1468 | "version": "1.3.0", 1469 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", 1470 | "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", 1471 | "dev": true, 1472 | "dependencies": { 1473 | "p-try": "^1.0.0" 1474 | }, 1475 | "engines": { 1476 | "node": ">=4" 1477 | } 1478 | }, 1479 | "node_modules/pretty-quick/node_modules/p-locate": { 1480 | "version": "2.0.0", 1481 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", 1482 | "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", 1483 | "dev": true, 1484 | "dependencies": { 1485 | "p-limit": "^1.1.0" 1486 | }, 1487 | "engines": { 1488 | "node": ">=4" 1489 | } 1490 | }, 1491 | "node_modules/pretty-quick/node_modules/p-try": { 1492 | "version": "1.0.0", 1493 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", 1494 | "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", 1495 | "dev": true, 1496 | "engines": { 1497 | "node": ">=4" 1498 | } 1499 | }, 1500 | "node_modules/pseudomap": { 1501 | "version": "1.0.2", 1502 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 1503 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", 1504 | "dev": true 1505 | }, 1506 | "node_modules/pump": { 1507 | "version": "3.0.0", 1508 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 1509 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 1510 | "dev": true, 1511 | "dependencies": { 1512 | "end-of-stream": "^1.1.0", 1513 | "once": "^1.3.1" 1514 | } 1515 | }, 1516 | "node_modules/read-pkg": { 1517 | "version": "5.1.1", 1518 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.1.1.tgz", 1519 | "integrity": "sha512-dFcTLQi6BZ+aFUaICg7er+/usEoqFdQxiEBsEMNGoipenihtxxtdrQuBXvyANCEI8VuUIVYFgeHGx9sLLvim4w==", 1520 | "dev": true, 1521 | "dependencies": { 1522 | "@types/normalize-package-data": "^2.4.0", 1523 | "normalize-package-data": "^2.5.0", 1524 | "parse-json": "^4.0.0", 1525 | "type-fest": "^0.4.1" 1526 | }, 1527 | "engines": { 1528 | "node": ">=8" 1529 | } 1530 | }, 1531 | "node_modules/readdirp": { 1532 | "version": "3.2.0", 1533 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", 1534 | "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", 1535 | "dev": true, 1536 | "dependencies": { 1537 | "picomatch": "^2.0.4" 1538 | }, 1539 | "engines": { 1540 | "node": ">= 8" 1541 | } 1542 | }, 1543 | "node_modules/require-directory": { 1544 | "version": "2.1.1", 1545 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1546 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 1547 | "dev": true, 1548 | "engines": { 1549 | "node": ">=0.10.0" 1550 | } 1551 | }, 1552 | "node_modules/require-main-filename": { 1553 | "version": "2.0.0", 1554 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 1555 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", 1556 | "dev": true 1557 | }, 1558 | "node_modules/resolve": { 1559 | "version": "1.10.1", 1560 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz", 1561 | "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==", 1562 | "dev": true, 1563 | "dependencies": { 1564 | "path-parse": "^1.0.6" 1565 | } 1566 | }, 1567 | "node_modules/resolve-from": { 1568 | "version": "3.0.0", 1569 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", 1570 | "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", 1571 | "dev": true, 1572 | "engines": { 1573 | "node": ">=4" 1574 | } 1575 | }, 1576 | "node_modules/run-node": { 1577 | "version": "1.0.0", 1578 | "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", 1579 | "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", 1580 | "dev": true, 1581 | "bin": { 1582 | "run-node": "run-node" 1583 | }, 1584 | "engines": { 1585 | "node": ">=4" 1586 | } 1587 | }, 1588 | "node_modules/semver": { 1589 | "version": "5.7.2", 1590 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", 1591 | "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", 1592 | "dev": true, 1593 | "license": "ISC", 1594 | "bin": { 1595 | "semver": "bin/semver" 1596 | } 1597 | }, 1598 | "node_modules/semver-compare": { 1599 | "version": "1.0.0", 1600 | "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", 1601 | "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", 1602 | "dev": true 1603 | }, 1604 | "node_modules/set-blocking": { 1605 | "version": "2.0.0", 1606 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1607 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 1608 | "dev": true 1609 | }, 1610 | "node_modules/shebang-command": { 1611 | "version": "1.2.0", 1612 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1613 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 1614 | "dev": true, 1615 | "dependencies": { 1616 | "shebang-regex": "^1.0.0" 1617 | }, 1618 | "engines": { 1619 | "node": ">=0.10.0" 1620 | } 1621 | }, 1622 | "node_modules/shebang-regex": { 1623 | "version": "1.0.0", 1624 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1625 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 1626 | "dev": true, 1627 | "engines": { 1628 | "node": ">=0.10.0" 1629 | } 1630 | }, 1631 | "node_modules/signal-exit": { 1632 | "version": "3.0.2", 1633 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1634 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 1635 | "dev": true 1636 | }, 1637 | "node_modules/slash": { 1638 | "version": "2.0.0", 1639 | "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", 1640 | "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", 1641 | "dev": true, 1642 | "engines": { 1643 | "node": ">=6" 1644 | } 1645 | }, 1646 | "node_modules/spdx-correct": { 1647 | "version": "3.1.0", 1648 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", 1649 | "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", 1650 | "dev": true, 1651 | "dependencies": { 1652 | "spdx-expression-parse": "^3.0.0", 1653 | "spdx-license-ids": "^3.0.0" 1654 | } 1655 | }, 1656 | "node_modules/spdx-exceptions": { 1657 | "version": "2.2.0", 1658 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 1659 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", 1660 | "dev": true 1661 | }, 1662 | "node_modules/spdx-expression-parse": { 1663 | "version": "3.0.0", 1664 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 1665 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 1666 | "dev": true, 1667 | "dependencies": { 1668 | "spdx-exceptions": "^2.1.0", 1669 | "spdx-license-ids": "^3.0.0" 1670 | } 1671 | }, 1672 | "node_modules/spdx-license-ids": { 1673 | "version": "3.0.4", 1674 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", 1675 | "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", 1676 | "dev": true 1677 | }, 1678 | "node_modules/sprintf-js": { 1679 | "version": "1.0.3", 1680 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1681 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1682 | "dev": true 1683 | }, 1684 | "node_modules/string-width": { 1685 | "version": "2.1.1", 1686 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1687 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1688 | "dev": true, 1689 | "dependencies": { 1690 | "is-fullwidth-code-point": "^2.0.0", 1691 | "strip-ansi": "^4.0.0" 1692 | }, 1693 | "engines": { 1694 | "node": ">=4" 1695 | } 1696 | }, 1697 | "node_modules/string.prototype.trimend": { 1698 | "version": "1.0.1", 1699 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", 1700 | "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", 1701 | "dev": true, 1702 | "dependencies": { 1703 | "define-properties": "^1.1.3", 1704 | "es-abstract": "^1.17.5" 1705 | }, 1706 | "funding": { 1707 | "url": "https://github.com/sponsors/ljharb" 1708 | } 1709 | }, 1710 | "node_modules/string.prototype.trimleft": { 1711 | "version": "2.1.2", 1712 | "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", 1713 | "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", 1714 | "dev": true, 1715 | "dependencies": { 1716 | "define-properties": "^1.1.3", 1717 | "es-abstract": "^1.17.5", 1718 | "string.prototype.trimstart": "^1.0.0" 1719 | }, 1720 | "engines": { 1721 | "node": ">= 0.4" 1722 | }, 1723 | "funding": { 1724 | "url": "https://github.com/sponsors/ljharb" 1725 | } 1726 | }, 1727 | "node_modules/string.prototype.trimright": { 1728 | "version": "2.1.2", 1729 | "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", 1730 | "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", 1731 | "dev": true, 1732 | "dependencies": { 1733 | "define-properties": "^1.1.3", 1734 | "es-abstract": "^1.17.5", 1735 | "string.prototype.trimend": "^1.0.0" 1736 | }, 1737 | "engines": { 1738 | "node": ">= 0.4" 1739 | }, 1740 | "funding": { 1741 | "url": "https://github.com/sponsors/ljharb" 1742 | } 1743 | }, 1744 | "node_modules/string.prototype.trimstart": { 1745 | "version": "1.0.1", 1746 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", 1747 | "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", 1748 | "dev": true, 1749 | "dependencies": { 1750 | "define-properties": "^1.1.3", 1751 | "es-abstract": "^1.17.5" 1752 | }, 1753 | "funding": { 1754 | "url": "https://github.com/sponsors/ljharb" 1755 | } 1756 | }, 1757 | "node_modules/strip-ansi": { 1758 | "version": "4.0.0", 1759 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1760 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1761 | "dev": true, 1762 | "dependencies": { 1763 | "ansi-regex": "^3.0.0" 1764 | }, 1765 | "engines": { 1766 | "node": ">=4" 1767 | } 1768 | }, 1769 | "node_modules/strip-eof": { 1770 | "version": "1.0.0", 1771 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 1772 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", 1773 | "dev": true, 1774 | "engines": { 1775 | "node": ">=0.10.0" 1776 | } 1777 | }, 1778 | "node_modules/strip-json-comments": { 1779 | "version": "2.0.1", 1780 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1781 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1782 | "dev": true, 1783 | "engines": { 1784 | "node": ">=0.10.0" 1785 | } 1786 | }, 1787 | "node_modules/supports-color": { 1788 | "version": "6.0.0", 1789 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", 1790 | "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", 1791 | "dev": true, 1792 | "dependencies": { 1793 | "has-flag": "^3.0.0" 1794 | }, 1795 | "engines": { 1796 | "node": ">=6" 1797 | } 1798 | }, 1799 | "node_modules/to-regex-range": { 1800 | "version": "5.0.1", 1801 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1802 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1803 | "dev": true, 1804 | "license": "MIT", 1805 | "dependencies": { 1806 | "is-number": "^7.0.0" 1807 | }, 1808 | "engines": { 1809 | "node": ">=8.0" 1810 | } 1811 | }, 1812 | "node_modules/type-detect": { 1813 | "version": "4.0.8", 1814 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 1815 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 1816 | "dev": true, 1817 | "engines": { 1818 | "node": ">=4" 1819 | } 1820 | }, 1821 | "node_modules/type-fest": { 1822 | "version": "0.4.1", 1823 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", 1824 | "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", 1825 | "dev": true, 1826 | "engines": { 1827 | "node": ">=6" 1828 | } 1829 | }, 1830 | "node_modules/validate-npm-package-license": { 1831 | "version": "3.0.4", 1832 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 1833 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 1834 | "dev": true, 1835 | "dependencies": { 1836 | "spdx-correct": "^3.0.0", 1837 | "spdx-expression-parse": "^3.0.0" 1838 | } 1839 | }, 1840 | "node_modules/which": { 1841 | "version": "1.3.1", 1842 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1843 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1844 | "dev": true, 1845 | "dependencies": { 1846 | "isexe": "^2.0.0" 1847 | }, 1848 | "bin": { 1849 | "which": "bin/which" 1850 | } 1851 | }, 1852 | "node_modules/which-module": { 1853 | "version": "2.0.0", 1854 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 1855 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", 1856 | "dev": true 1857 | }, 1858 | "node_modules/wide-align": { 1859 | "version": "1.1.3", 1860 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1861 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1862 | "dev": true, 1863 | "dependencies": { 1864 | "string-width": "^1.0.2 || 2" 1865 | } 1866 | }, 1867 | "node_modules/wrap-ansi": { 1868 | "version": "5.1.0", 1869 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 1870 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 1871 | "dev": true, 1872 | "dependencies": { 1873 | "ansi-styles": "^3.2.0", 1874 | "string-width": "^3.0.0", 1875 | "strip-ansi": "^5.0.0" 1876 | }, 1877 | "engines": { 1878 | "node": ">=6" 1879 | } 1880 | }, 1881 | "node_modules/wrap-ansi/node_modules/ansi-regex": { 1882 | "version": "4.1.1", 1883 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", 1884 | "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", 1885 | "dev": true, 1886 | "engines": { 1887 | "node": ">=6" 1888 | } 1889 | }, 1890 | "node_modules/wrap-ansi/node_modules/string-width": { 1891 | "version": "3.1.0", 1892 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1893 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1894 | "dev": true, 1895 | "dependencies": { 1896 | "emoji-regex": "^7.0.1", 1897 | "is-fullwidth-code-point": "^2.0.0", 1898 | "strip-ansi": "^5.1.0" 1899 | }, 1900 | "engines": { 1901 | "node": ">=6" 1902 | } 1903 | }, 1904 | "node_modules/wrap-ansi/node_modules/strip-ansi": { 1905 | "version": "5.2.0", 1906 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1907 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1908 | "dev": true, 1909 | "dependencies": { 1910 | "ansi-regex": "^4.1.0" 1911 | }, 1912 | "engines": { 1913 | "node": ">=6" 1914 | } 1915 | }, 1916 | "node_modules/wrappy": { 1917 | "version": "1.0.2", 1918 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1919 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1920 | "dev": true 1921 | }, 1922 | "node_modules/y18n": { 1923 | "version": "4.0.3", 1924 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", 1925 | "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", 1926 | "dev": true 1927 | }, 1928 | "node_modules/yallist": { 1929 | "version": "2.1.2", 1930 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 1931 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 1932 | "dev": true 1933 | }, 1934 | "node_modules/yargs": { 1935 | "version": "13.3.2", 1936 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", 1937 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", 1938 | "dev": true, 1939 | "dependencies": { 1940 | "cliui": "^5.0.0", 1941 | "find-up": "^3.0.0", 1942 | "get-caller-file": "^2.0.1", 1943 | "require-directory": "^2.1.1", 1944 | "require-main-filename": "^2.0.0", 1945 | "set-blocking": "^2.0.0", 1946 | "string-width": "^3.0.0", 1947 | "which-module": "^2.0.0", 1948 | "y18n": "^4.0.0", 1949 | "yargs-parser": "^13.1.2" 1950 | } 1951 | }, 1952 | "node_modules/yargs-parser": { 1953 | "version": "13.1.2", 1954 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 1955 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 1956 | "dev": true, 1957 | "dependencies": { 1958 | "camelcase": "^5.0.0", 1959 | "decamelize": "^1.2.0" 1960 | } 1961 | }, 1962 | "node_modules/yargs-unparser": { 1963 | "version": "1.6.0", 1964 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", 1965 | "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", 1966 | "dev": true, 1967 | "dependencies": { 1968 | "flat": "^4.1.0", 1969 | "lodash": "^4.17.15", 1970 | "yargs": "^13.3.0" 1971 | }, 1972 | "engines": { 1973 | "node": ">=6" 1974 | } 1975 | }, 1976 | "node_modules/yargs/node_modules/ansi-regex": { 1977 | "version": "4.1.1", 1978 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", 1979 | "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", 1980 | "dev": true, 1981 | "engines": { 1982 | "node": ">=6" 1983 | } 1984 | }, 1985 | "node_modules/yargs/node_modules/string-width": { 1986 | "version": "3.1.0", 1987 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1988 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1989 | "dev": true, 1990 | "dependencies": { 1991 | "emoji-regex": "^7.0.1", 1992 | "is-fullwidth-code-point": "^2.0.0", 1993 | "strip-ansi": "^5.1.0" 1994 | }, 1995 | "engines": { 1996 | "node": ">=6" 1997 | } 1998 | }, 1999 | "node_modules/yargs/node_modules/strip-ansi": { 2000 | "version": "5.2.0", 2001 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 2002 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 2003 | "dev": true, 2004 | "dependencies": { 2005 | "ansi-regex": "^4.1.0" 2006 | }, 2007 | "engines": { 2008 | "node": ">=6" 2009 | } 2010 | } 2011 | } 2012 | } 2013 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "David Momenso", 3 | "contributors": [ 4 | "Kerry Shetline" 5 | ], 6 | "name": "node-dht-sensor", 7 | "description": "Reads data from DHT sensors on Raspberry Pi", 8 | "version": "0.4.5", 9 | "repository": { 10 | "url": "https://github.com/momenso/node-dht-sensor" 11 | }, 12 | "scripts": { 13 | "install": "node-gyp configure", 14 | "postinstall": "node-gyp build", 15 | "test": "mocha" 16 | }, 17 | "main": "./lib", 18 | "files": [ 19 | "/src", 20 | "/lib", 21 | "/binding.gyp" 22 | ], 23 | "dependencies": { 24 | "node-addon-api": "^1.6.3" 25 | }, 26 | "devDependencies": { 27 | "chai": "^4.2.0", 28 | "husky": "^2.2.0", 29 | "mocha": "^7.1.2", 30 | "prettier": "1.17.0", 31 | "pretty-quick": "^1.10.0" 32 | }, 33 | "husky": { 34 | "hooks": { 35 | "pre-commit": "pretty-quick --staged" 36 | } 37 | }, 38 | "license": "LGPL-3.0", 39 | "keywords": [ 40 | "DHT", 41 | "DHT11", 42 | "DHT22", 43 | "AM2302", 44 | "sensor", 45 | "temperature", 46 | "humidity", 47 | "raspberry", 48 | "raspberry pi", 49 | "raspberry pi 5", 50 | "rpi", 51 | "libgpiod", 52 | "gpiod" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /src/abstract-gpio.cpp: -------------------------------------------------------------------------------- 1 | #include "abstract-gpio.h" 2 | 3 | #include 4 | #include "bcm2835/bcm2835.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef USE_LIBGPIOD 11 | #include 12 | #endif 13 | 14 | static bool useGpiod = false; 15 | static GpioDirection lastDirection[MAX_GPIO_PIN_NUMBER + 1]; 16 | #ifdef USE_LIBGPIOD 17 | static gpiod_chip *theChip = NULL; 18 | static gpiod_line *lines[MAX_GPIO_PIN_NUMBER + 1]; 19 | #endif 20 | 21 | static void gpiodCleanUp() 22 | { 23 | #ifdef USE_LIBGPIOD 24 | for (int i = 1; i <= MAX_GPIO_PIN_NUMBER; ++i) 25 | { 26 | if (lines[i] != NULL) 27 | { 28 | gpiod_line_release(lines[i]); 29 | } 30 | } 31 | 32 | gpiod_chip_close(theChip); 33 | #endif 34 | } 35 | 36 | bool gpioInitialize() 37 | { 38 | bool isPi5 = false; 39 | std::ifstream file("/proc/cpuinfo"); 40 | std::string line; 41 | 42 | if (file.is_open()) 43 | { 44 | std::regex pattern(R"(Model\s+:\s+Raspberry Pi (\d+))"); 45 | std::smatch match; 46 | 47 | while (std::getline(file, line)) 48 | { 49 | if (std::regex_search(line, match, pattern) && std::stoi(match[1]) > 4) 50 | { 51 | #ifdef USE_LIBGPIOD 52 | isPi5 = true; 53 | #endif 54 | break; 55 | } 56 | } 57 | 58 | file.close(); 59 | } 60 | 61 | if (isPi5) 62 | { 63 | #ifdef USE_LIBGPIOD 64 | theChip = gpiod_chip_open_by_name("gpiochip0"); 65 | 66 | if (theChip == NULL) 67 | { 68 | #ifdef VERBOSE 69 | puts("libgpiod initialization failed."); 70 | #endif 71 | return false; 72 | } 73 | else 74 | { 75 | #ifdef VERBOSE 76 | puts("libgpiod initialized."); 77 | #endif 78 | std::fill(lines, lines + MAX_GPIO_PIN_NUMBER + 1, (gpiod_line*) NULL); 79 | std::fill(lastDirection, lastDirection + MAX_GPIO_PIN_NUMBER + 1, GPIO_UNSET); 80 | useGpiod = true; 81 | std::atexit(gpiodCleanUp); 82 | return true; 83 | } 84 | #endif 85 | } 86 | else if (!bcm2835_init()) 87 | { 88 | #ifdef VERBOSE 89 | puts("BCM2835 initialization failed."); 90 | #endif 91 | return false; 92 | } 93 | else 94 | { 95 | #ifdef VERBOSE 96 | puts("BCM2835 initialized."); 97 | #endif 98 | return true; 99 | } 100 | } 101 | 102 | #ifdef USE_LIBGPIOD 103 | static gpiod_line* getLine(int pin, GpioDirection direction) 104 | { 105 | if (lines[pin] == NULL || lastDirection[pin] != direction) 106 | { 107 | if (lines[pin] != NULL) 108 | { 109 | gpiod_line_release(lines[pin]); 110 | lines[pin] = NULL; 111 | } 112 | 113 | lines[pin] = gpiod_chip_get_line(theChip, pin); 114 | } 115 | 116 | gpiod_line* line = lines[pin]; 117 | 118 | if (lastDirection[pin] != direction) 119 | { 120 | if (direction == GPIO_IN) 121 | { 122 | gpiod_line_request_input(line, "Consumer"); 123 | } 124 | else 125 | { 126 | gpiod_line_request_output(line, "Consumer", 0); 127 | } 128 | 129 | lastDirection[pin] = direction; 130 | } 131 | 132 | return line; 133 | } 134 | #endif 135 | 136 | void gpioWrite(int pin, GpioPinState state) 137 | { 138 | if (useGpiod) 139 | { 140 | #ifdef USE_LIBGPIOD 141 | gpiod_line_set_value(getLine(pin, GPIO_OUT), state == GPIO_LOW ? 0 : 1); 142 | #endif 143 | } 144 | else 145 | { 146 | if (lastDirection[pin] != GPIO_OUT) 147 | { 148 | bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP); 149 | lastDirection[pin] = GPIO_OUT; 150 | } 151 | 152 | bcm2835_gpio_write(pin, state == GPIO_LOW ? LOW : HIGH); 153 | } 154 | } 155 | 156 | GpioPinState gpioRead(int pin) 157 | { 158 | if (useGpiod) 159 | { 160 | #ifdef USE_LIBGPIOD 161 | return gpiod_line_get_value(getLine(pin, GPIO_IN)) == 0 ? GPIO_LOW : GPIO_HIGH; 162 | #endif 163 | } 164 | else 165 | { 166 | if (lastDirection[pin] != GPIO_IN) 167 | { 168 | bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT); 169 | lastDirection[pin] = GPIO_IN; 170 | } 171 | 172 | return bcm2835_gpio_lev(pin) == LOW ? GPIO_LOW : GPIO_HIGH; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/abstract-gpio.h: -------------------------------------------------------------------------------- 1 | #ifndef ABSTRACT_GPIO 2 | #define ABSTRACT_GPIO 3 | 4 | #define MAX_GPIO_PIN_NUMBER 63 5 | 6 | enum GpioDirection { GPIO_UNSET = -1, GPIO_IN, GPIO_OUT }; 7 | enum GpioPinState { GPIO_READ_FAILED = -1, GPIO_LOW, GPIO_HIGH }; 8 | 9 | bool gpioInitialize(); 10 | void gpioWrite(int pin, GpioPinState state); 11 | GpioPinState gpioRead(int pin); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/bcm2835/bcm2835.c: -------------------------------------------------------------------------------- 1 | /* bcm2835.c 2 | // C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi 3 | // http://elinux.org/RPi_Low-level_peripherals 4 | // http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf 5 | // 6 | // Author: Mike McCauley 7 | // Copyright (C) 2011-2013 Mike McCauley 8 | // $Id: bcm2835.c,v 1.27 2019/07/22 23:04:24 mikem Exp mikem $ 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #define BCK2835_LIBRARY_BUILD 23 | #include "bcm2835.h" 24 | 25 | /* This define enables a little test program (by default a blinking output on pin RPI_GPIO_PIN_11) 26 | // You can do some safe, non-destructive testing on any platform with: 27 | // gcc bcm2835.c -D BCM2835_TEST 28 | // ./a.out 29 | */ 30 | /*#define BCM2835_TEST*/ 31 | 32 | /* Uncommenting this define compiles alternative I2C code for the version 1 RPi 33 | // The P1 header I2C pins are connected to SDA0 and SCL0 on V1. 34 | // By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. 35 | */ 36 | /* #define I2C_V1*/ 37 | 38 | /* Physical address and size of the peripherals block 39 | // May be overridden on RPi2 40 | */ 41 | uint32_t *bcm2835_peripherals_base = (uint32_t *)BCM2835_PERI_BASE; 42 | uint32_t bcm2835_peripherals_size = BCM2835_PERI_SIZE; 43 | 44 | /* Virtual memory address of the mapped peripherals block 45 | */ 46 | uint32_t *bcm2835_peripherals = (uint32_t *)MAP_FAILED; 47 | 48 | /* And the register bases within the peripherals block 49 | */ 50 | volatile uint32_t *bcm2835_gpio = (uint32_t *)MAP_FAILED; 51 | volatile uint32_t *bcm2835_pwm = (uint32_t *)MAP_FAILED; 52 | volatile uint32_t *bcm2835_clk = (uint32_t *)MAP_FAILED; 53 | volatile uint32_t *bcm2835_pads = (uint32_t *)MAP_FAILED; 54 | volatile uint32_t *bcm2835_spi0 = (uint32_t *)MAP_FAILED; 55 | volatile uint32_t *bcm2835_bsc0 = (uint32_t *)MAP_FAILED; 56 | volatile uint32_t *bcm2835_bsc1 = (uint32_t *)MAP_FAILED; 57 | volatile uint32_t *bcm2835_st = (uint32_t *)MAP_FAILED; 58 | volatile uint32_t *bcm2835_aux = (uint32_t *)MAP_FAILED; 59 | volatile uint32_t *bcm2835_spi1 = (uint32_t *)MAP_FAILED; 60 | 61 | 62 | 63 | /* This variable allows us to test on hardware other than RPi. 64 | // It prevents access to the kernel memory, and does not do any peripheral access 65 | // Instead it prints out what it _would_ do if debug were 0 66 | */ 67 | static uint8_t debug = 0; 68 | 69 | /* RPI 4 has different pullup registers - we need to know if we have that type */ 70 | 71 | static uint8_t pud_type_rpi4 = 0; 72 | 73 | /* RPI 4 has different pullup operation - make backwards compat */ 74 | 75 | static uint8_t pud_compat_setting = BCM2835_GPIO_PUD_OFF; 76 | 77 | /* I2C The time needed to transmit one byte. In microseconds. 78 | */ 79 | static int i2c_byte_wait_us = 0; 80 | 81 | /* SPI bit order. BCM2835 SPI0 only supports MSBFIRST, so we instead 82 | * have a software based bit reversal, based on a contribution by Damiano Benedetti 83 | */ 84 | static uint8_t bcm2835_spi_bit_order = BCM2835_SPI_BIT_ORDER_MSBFIRST; 85 | static uint8_t bcm2835_byte_reverse_table[] = 86 | { 87 | 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 88 | 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 89 | 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 90 | 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 91 | 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 92 | 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 93 | 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 94 | 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 95 | 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 96 | 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 97 | 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 98 | 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 99 | 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 100 | 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 101 | 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 102 | 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 103 | 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 104 | 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 105 | 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 106 | 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 107 | 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 108 | 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 109 | 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 110 | 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 111 | 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 112 | 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 113 | 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 114 | 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 115 | 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 116 | 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 117 | 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 118 | 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff 119 | }; 120 | 121 | static uint8_t bcm2835_correct_order(uint8_t b) 122 | { 123 | if (bcm2835_spi_bit_order == BCM2835_SPI_BIT_ORDER_LSBFIRST) 124 | return bcm2835_byte_reverse_table[b]; 125 | else 126 | return b; 127 | } 128 | 129 | /* 130 | // Low level register access functions 131 | */ 132 | 133 | /* Function to return the pointers to the hardware register bases */ 134 | uint32_t* bcm2835_regbase(uint8_t regbase) 135 | { 136 | switch (regbase) 137 | { 138 | case BCM2835_REGBASE_ST: 139 | return (uint32_t *)bcm2835_st; 140 | case BCM2835_REGBASE_GPIO: 141 | return (uint32_t *)bcm2835_gpio; 142 | case BCM2835_REGBASE_PWM: 143 | return (uint32_t *)bcm2835_pwm; 144 | case BCM2835_REGBASE_CLK: 145 | return (uint32_t *)bcm2835_clk; 146 | case BCM2835_REGBASE_PADS: 147 | return (uint32_t *)bcm2835_pads; 148 | case BCM2835_REGBASE_SPI0: 149 | return (uint32_t *)bcm2835_spi0; 150 | case BCM2835_REGBASE_BSC0: 151 | return (uint32_t *)bcm2835_bsc0; 152 | case BCM2835_REGBASE_BSC1: 153 | return (uint32_t *)bcm2835_st; 154 | case BCM2835_REGBASE_AUX: 155 | return (uint32_t *)bcm2835_aux; 156 | case BCM2835_REGBASE_SPI1: 157 | return (uint32_t *)bcm2835_spi1; 158 | 159 | } 160 | return (uint32_t *)MAP_FAILED; 161 | } 162 | 163 | void bcm2835_set_debug(uint8_t d) 164 | { 165 | debug = d; 166 | } 167 | 168 | unsigned int bcm2835_version(void) 169 | { 170 | return BCM2835_VERSION; 171 | } 172 | 173 | /* Read with memory barriers from peripheral 174 | * 175 | */ 176 | uint32_t bcm2835_peri_read(volatile uint32_t* paddr) 177 | { 178 | uint32_t ret; 179 | if (debug) 180 | { 181 | printf("bcm2835_peri_read paddr %p\n", (void *) paddr); 182 | return 0; 183 | } 184 | else 185 | { 186 | __sync_synchronize(); 187 | ret = *paddr; 188 | __sync_synchronize(); 189 | return ret; 190 | } 191 | } 192 | 193 | /* read from peripheral without the read barrier 194 | * This can only be used if more reads to THE SAME peripheral 195 | * will follow. The sequence must terminate with memory barrier 196 | * before any read or write to another peripheral can occur. 197 | * The MB can be explicit, or one of the barrier read/write calls. 198 | */ 199 | uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr) 200 | { 201 | if (debug) 202 | { 203 | printf("bcm2835_peri_read_nb paddr %p\n", paddr); 204 | return 0; 205 | } 206 | else 207 | { 208 | return *paddr; 209 | } 210 | } 211 | 212 | /* Write with memory barriers to peripheral 213 | */ 214 | 215 | void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value) 216 | { 217 | if (debug) 218 | { 219 | printf("bcm2835_peri_write paddr %p, value %08X\n", paddr, value); 220 | } 221 | else 222 | { 223 | __sync_synchronize(); 224 | *paddr = value; 225 | __sync_synchronize(); 226 | } 227 | } 228 | 229 | /* write to peripheral without the write barrier */ 230 | void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value) 231 | { 232 | if (debug) 233 | { 234 | printf("bcm2835_peri_write_nb paddr %p, value %08X\n", 235 | paddr, value); 236 | } 237 | else 238 | { 239 | *paddr = value; 240 | } 241 | } 242 | 243 | /* Set/clear only the bits in value covered by the mask 244 | * This is not atomic - can be interrupted. 245 | */ 246 | void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask) 247 | { 248 | uint32_t v = bcm2835_peri_read(paddr); 249 | v = (v & ~mask) | (value & mask); 250 | bcm2835_peri_write(paddr, v); 251 | } 252 | 253 | /* 254 | // Low level convenience functions 255 | */ 256 | 257 | /* Function select 258 | // pin is a BCM2835 GPIO pin number NOT RPi pin number 259 | // There are 6 control registers, each control the functions of a block 260 | // of 10 pins. 261 | // Each control register has 10 sets of 3 bits per GPIO pin: 262 | // 263 | // 000 = GPIO Pin X is an input 264 | // 001 = GPIO Pin X is an output 265 | // 100 = GPIO Pin X takes alternate function 0 266 | // 101 = GPIO Pin X takes alternate function 1 267 | // 110 = GPIO Pin X takes alternate function 2 268 | // 111 = GPIO Pin X takes alternate function 3 269 | // 011 = GPIO Pin X takes alternate function 4 270 | // 010 = GPIO Pin X takes alternate function 5 271 | // 272 | // So the 3 bits for port X are: 273 | // X / 10 + ((X % 10) * 3) 274 | */ 275 | void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode) 276 | { 277 | /* Function selects are 10 pins per 32 bit word, 3 bits per pin */ 278 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10); 279 | uint8_t shift = (pin % 10) * 3; 280 | uint32_t mask = BCM2835_GPIO_FSEL_MASK << shift; 281 | uint32_t value = mode << shift; 282 | bcm2835_peri_set_bits(paddr, value, mask); 283 | } 284 | 285 | /* Set output pin */ 286 | void bcm2835_gpio_set(uint8_t pin) 287 | { 288 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32; 289 | uint8_t shift = pin % 32; 290 | bcm2835_peri_write(paddr, 1 << shift); 291 | } 292 | 293 | /* Clear output pin */ 294 | void bcm2835_gpio_clr(uint8_t pin) 295 | { 296 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32; 297 | uint8_t shift = pin % 32; 298 | bcm2835_peri_write(paddr, 1 << shift); 299 | } 300 | 301 | /* Set all output pins in the mask */ 302 | void bcm2835_gpio_set_multi(uint32_t mask) 303 | { 304 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4; 305 | bcm2835_peri_write(paddr, mask); 306 | } 307 | 308 | /* Clear all output pins in the mask */ 309 | void bcm2835_gpio_clr_multi(uint32_t mask) 310 | { 311 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4; 312 | bcm2835_peri_write(paddr, mask); 313 | } 314 | 315 | /* Read input pin */ 316 | uint8_t bcm2835_gpio_lev(uint8_t pin) 317 | { 318 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEV0/4 + pin/32; 319 | uint8_t shift = pin % 32; 320 | uint32_t value = bcm2835_peri_read(paddr); 321 | return (value & (1 << shift)) ? HIGH : LOW; 322 | } 323 | 324 | /* See if an event detection bit is set 325 | // Sigh cant support interrupts yet 326 | */ 327 | uint8_t bcm2835_gpio_eds(uint8_t pin) 328 | { 329 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; 330 | uint8_t shift = pin % 32; 331 | uint32_t value = bcm2835_peri_read(paddr); 332 | return (value & (1 << shift)) ? HIGH : LOW; 333 | } 334 | 335 | uint32_t bcm2835_gpio_eds_multi(uint32_t mask) 336 | { 337 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4; 338 | uint32_t value = bcm2835_peri_read(paddr); 339 | return (value & mask); 340 | } 341 | 342 | /* Write a 1 to clear the bit in EDS */ 343 | void bcm2835_gpio_set_eds(uint8_t pin) 344 | { 345 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; 346 | uint8_t shift = pin % 32; 347 | uint32_t value = 1 << shift; 348 | bcm2835_peri_write(paddr, value); 349 | } 350 | 351 | void bcm2835_gpio_set_eds_multi(uint32_t mask) 352 | { 353 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4; 354 | bcm2835_peri_write(paddr, mask); 355 | } 356 | 357 | /* Rising edge detect enable */ 358 | void bcm2835_gpio_ren(uint8_t pin) 359 | { 360 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; 361 | uint8_t shift = pin % 32; 362 | uint32_t value = 1 << shift; 363 | bcm2835_peri_set_bits(paddr, value, value); 364 | } 365 | void bcm2835_gpio_clr_ren(uint8_t pin) 366 | { 367 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; 368 | uint8_t shift = pin % 32; 369 | uint32_t value = 1 << shift; 370 | bcm2835_peri_set_bits(paddr, 0, value); 371 | } 372 | 373 | /* Falling edge detect enable */ 374 | void bcm2835_gpio_fen(uint8_t pin) 375 | { 376 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; 377 | uint8_t shift = pin % 32; 378 | uint32_t value = 1 << shift; 379 | bcm2835_peri_set_bits(paddr, value, value); 380 | } 381 | void bcm2835_gpio_clr_fen(uint8_t pin) 382 | { 383 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; 384 | uint8_t shift = pin % 32; 385 | uint32_t value = 1 << shift; 386 | bcm2835_peri_set_bits(paddr, 0, value); 387 | } 388 | 389 | /* High detect enable */ 390 | void bcm2835_gpio_hen(uint8_t pin) 391 | { 392 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; 393 | uint8_t shift = pin % 32; 394 | uint32_t value = 1 << shift; 395 | bcm2835_peri_set_bits(paddr, value, value); 396 | } 397 | void bcm2835_gpio_clr_hen(uint8_t pin) 398 | { 399 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; 400 | uint8_t shift = pin % 32; 401 | uint32_t value = 1 << shift; 402 | bcm2835_peri_set_bits(paddr, 0, value); 403 | } 404 | 405 | /* Low detect enable */ 406 | void bcm2835_gpio_len(uint8_t pin) 407 | { 408 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; 409 | uint8_t shift = pin % 32; 410 | uint32_t value = 1 << shift; 411 | bcm2835_peri_set_bits(paddr, value, value); 412 | } 413 | void bcm2835_gpio_clr_len(uint8_t pin) 414 | { 415 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; 416 | uint8_t shift = pin % 32; 417 | uint32_t value = 1 << shift; 418 | bcm2835_peri_set_bits(paddr, 0, value); 419 | } 420 | 421 | /* Async rising edge detect enable */ 422 | void bcm2835_gpio_aren(uint8_t pin) 423 | { 424 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; 425 | uint8_t shift = pin % 32; 426 | uint32_t value = 1 << shift; 427 | bcm2835_peri_set_bits(paddr, value, value); 428 | } 429 | void bcm2835_gpio_clr_aren(uint8_t pin) 430 | { 431 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; 432 | uint8_t shift = pin % 32; 433 | uint32_t value = 1 << shift; 434 | bcm2835_peri_set_bits(paddr, 0, value); 435 | } 436 | 437 | /* Async falling edge detect enable */ 438 | void bcm2835_gpio_afen(uint8_t pin) 439 | { 440 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; 441 | uint8_t shift = pin % 32; 442 | uint32_t value = 1 << shift; 443 | bcm2835_peri_set_bits(paddr, value, value); 444 | } 445 | void bcm2835_gpio_clr_afen(uint8_t pin) 446 | { 447 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; 448 | uint8_t shift = pin % 32; 449 | uint32_t value = 1 << shift; 450 | bcm2835_peri_set_bits(paddr, 0, value); 451 | } 452 | 453 | /* Set pullup/down */ 454 | void bcm2835_gpio_pud(uint8_t pud) 455 | { 456 | if( pud_type_rpi4 ) 457 | { 458 | pud_compat_setting = pud; 459 | } 460 | else { 461 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4; 462 | bcm2835_peri_write(paddr, pud); 463 | } 464 | } 465 | 466 | /* Pullup/down clock 467 | // Clocks the value of pud into the GPIO pin 468 | */ 469 | void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on) 470 | { 471 | if( pud_type_rpi4 ) 472 | { 473 | if( on ) 474 | bcm2835_gpio_set_pud( pin, pud_compat_setting); 475 | } 476 | else 477 | { 478 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUDCLK0/4 + pin/32; 479 | uint8_t shift = pin % 32; 480 | bcm2835_peri_write(paddr, (on ? 1 : 0) << shift); 481 | } 482 | } 483 | 484 | /* Read GPIO pad behaviour for groups of GPIOs */ 485 | uint32_t bcm2835_gpio_pad(uint8_t group) 486 | { 487 | if (bcm2835_pads == MAP_FAILED) { 488 | return 0; 489 | } 490 | 491 | volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group; 492 | return bcm2835_peri_read(paddr); 493 | } 494 | 495 | /* Set GPIO pad behaviour for groups of GPIOs 496 | // powerup value for all pads is 497 | // BCM2835_PAD_SLEW_RATE_UNLIMITED | BCM2835_PAD_HYSTERESIS_ENABLED | BCM2835_PAD_DRIVE_8mA 498 | */ 499 | void bcm2835_gpio_set_pad(uint8_t group, uint32_t control) 500 | { 501 | if (bcm2835_pads == MAP_FAILED) { 502 | return; 503 | } 504 | 505 | volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group; 506 | bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD); 507 | } 508 | 509 | /* Some convenient arduino-like functions 510 | // milliseconds 511 | */ 512 | void bcm2835_delay(unsigned int millis) 513 | { 514 | struct timespec sleeper; 515 | 516 | sleeper.tv_sec = (time_t)(millis / 1000); 517 | sleeper.tv_nsec = (long)(millis % 1000) * 1000000; 518 | nanosleep(&sleeper, NULL); 519 | } 520 | 521 | /* microseconds */ 522 | void bcm2835_delayMicroseconds(uint64_t micros) 523 | { 524 | struct timespec t1; 525 | uint64_t start; 526 | 527 | if (debug) 528 | { 529 | /* Cant access sytem timers in debug mode */ 530 | printf("bcm2835_delayMicroseconds %lld\n", (long long int) micros); 531 | return; 532 | } 533 | 534 | /* Calling nanosleep() takes at least 100-200 us, so use it for 535 | // long waits and use a busy wait on the System Timer for the rest. 536 | */ 537 | start = bcm2835_st_read(); 538 | 539 | /* Not allowed to access timer registers (result is not as precise)*/ 540 | if (start==0) 541 | { 542 | t1.tv_sec = 0; 543 | t1.tv_nsec = 1000 * (long)(micros); 544 | nanosleep(&t1, NULL); 545 | return; 546 | } 547 | 548 | if (micros > 450) 549 | { 550 | t1.tv_sec = 0; 551 | t1.tv_nsec = 1000 * (long)(micros - 200); 552 | nanosleep(&t1, NULL); 553 | } 554 | 555 | bcm2835_st_delay(start, micros); 556 | } 557 | 558 | /* 559 | // Higher level convenience functions 560 | */ 561 | 562 | /* Set the state of an output */ 563 | void bcm2835_gpio_write(uint8_t pin, uint8_t on) 564 | { 565 | if (on) 566 | bcm2835_gpio_set(pin); 567 | else 568 | bcm2835_gpio_clr(pin); 569 | } 570 | 571 | /* Set the state of a all 32 outputs in the mask to on or off */ 572 | void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on) 573 | { 574 | if (on) 575 | bcm2835_gpio_set_multi(mask); 576 | else 577 | bcm2835_gpio_clr_multi(mask); 578 | } 579 | 580 | /* Set the state of a all 32 outputs in the mask to the values in value */ 581 | void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask) 582 | { 583 | bcm2835_gpio_set_multi(value & mask); 584 | bcm2835_gpio_clr_multi((~value) & mask); 585 | } 586 | 587 | /* Set the pullup/down resistor for a pin 588 | // 589 | // The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on 590 | // the respective GPIO pins. These registers must be used in conjunction with the GPPUD 591 | // register to effect GPIO Pull-up/down changes. The following sequence of events is 592 | // required: 593 | // 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither 594 | // to remove the current Pull-up/down) 595 | // 2. Wait 150 cycles ? this provides the required set-up time for the control signal 596 | // 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to 597 | // modify ? NOTE only the pads which receive a clock will be modified, all others will 598 | // retain their previous state. 599 | // 4. Wait 150 cycles ? this provides the required hold time for the control signal 600 | // 5. Write to GPPUD to remove the control signal 601 | // 6. Write to GPPUDCLK0/1 to remove the clock 602 | // 603 | // RPi has P1-03 and P1-05 with 1k8 pullup resistor 604 | // 605 | // RPI 4 uses a different PUD method - no clock 606 | 607 | */ 608 | void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud) 609 | { 610 | if( pud_type_rpi4 ) 611 | { 612 | int shiftbits = (pin & 0xf) << 1; 613 | uint32_t bits; 614 | uint32_t pull; 615 | 616 | switch (pud) 617 | { 618 | case BCM2835_GPIO_PUD_OFF: pull = 0; break; 619 | case BCM2835_GPIO_PUD_UP: pull = 1; break; 620 | case BCM2835_GPIO_PUD_DOWN: pull = 2; break; 621 | default: return; 622 | } 623 | 624 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUPPDN0/4 + (pin >> 4); 625 | 626 | bits = bcm2835_peri_read_nb( paddr ); 627 | bits &= ~(3 << shiftbits); 628 | bits |= (pull << shiftbits); 629 | 630 | bcm2835_peri_write_nb( paddr, bits ); 631 | 632 | } else 633 | { 634 | bcm2835_gpio_pud(pud); 635 | delayMicroseconds(10); 636 | bcm2835_gpio_pudclk(pin, 1); 637 | delayMicroseconds(10); 638 | bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF); 639 | bcm2835_gpio_pudclk(pin, 0); 640 | } 641 | 642 | } 643 | 644 | 645 | uint8_t bcm2835_gpio_get_pud(uint8_t pin) 646 | { 647 | uint8_t ret = BCM2835_GPIO_PUD_ERROR; 648 | 649 | if( pud_type_rpi4 ) 650 | { 651 | uint32_t bits; 652 | volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUPPDN0/4 + (pin >> 4); 653 | bits = (bcm2835_peri_read_nb( paddr ) >> ((pin & 0xf)<<1)) & 0x3; 654 | 655 | switch (bits) 656 | { 657 | case 0: ret = BCM2835_GPIO_PUD_OFF; break; 658 | case 1: ret = BCM2835_GPIO_PUD_UP; break; 659 | case 2: ret = BCM2835_GPIO_PUD_DOWN; break; 660 | default: ret = BCM2835_GPIO_PUD_ERROR; 661 | } 662 | } 663 | 664 | return ret; 665 | } 666 | 667 | 668 | int bcm2835_spi_begin(void) 669 | { 670 | volatile uint32_t* paddr; 671 | 672 | if (bcm2835_spi0 == MAP_FAILED) 673 | return 0; /* bcm2835_init() failed, or not root */ 674 | 675 | /* Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them */ 676 | bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); /* CE1 */ 677 | bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); /* CE0 */ 678 | bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); /* MISO */ 679 | bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); /* MOSI */ 680 | bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); /* CLK */ 681 | 682 | /* Set the SPI CS register to the some sensible defaults */ 683 | paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; 684 | bcm2835_peri_write(paddr, 0); /* All 0s */ 685 | 686 | /* Clear TX and RX fifos */ 687 | bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR); 688 | 689 | return 1; // OK 690 | } 691 | 692 | void bcm2835_spi_end(void) 693 | { 694 | /* Set all the SPI0 pins back to input */ 695 | bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); /* CE1 */ 696 | bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); /* CE0 */ 697 | bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); /* MISO */ 698 | bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); /* MOSI */ 699 | bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); /* CLK */ 700 | } 701 | 702 | void bcm2835_spi_setBitOrder(uint8_t order) 703 | { 704 | bcm2835_spi_bit_order = order; 705 | } 706 | 707 | /* defaults to 0, which means a divider of 65536. 708 | // The divisor must be a power of 2. Odd numbers 709 | // rounded down. The maximum SPI clock rate is 710 | // of the APB clock 711 | */ 712 | void bcm2835_spi_setClockDivider(uint16_t divider) 713 | { 714 | volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CLK/4; 715 | bcm2835_peri_write(paddr, divider); 716 | } 717 | 718 | void bcm2835_spi_set_speed_hz(uint32_t speed_hz) 719 | { 720 | uint16_t divider = (uint16_t) ((uint32_t) BCM2835_CORE_CLK_HZ / speed_hz); 721 | divider &= 0xFFFE; 722 | bcm2835_spi_setClockDivider(divider); 723 | } 724 | 725 | void bcm2835_spi_setDataMode(uint8_t mode) 726 | { 727 | volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; 728 | /* Mask in the CPO and CPHA bits of CS */ 729 | bcm2835_peri_set_bits(paddr, mode << 2, BCM2835_SPI0_CS_CPOL | BCM2835_SPI0_CS_CPHA); 730 | } 731 | 732 | /* Writes (and reads) a single byte to SPI */ 733 | uint8_t bcm2835_spi_transfer(uint8_t value) 734 | { 735 | volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; 736 | volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; 737 | uint32_t ret; 738 | 739 | /* This is Polled transfer as per section 10.6.1 740 | // BUG ALERT: what happens if we get interupted in this section, and someone else 741 | // accesses a different peripheral? 742 | // Clear TX and RX fifos 743 | */ 744 | bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); 745 | 746 | /* Set TA = 1 */ 747 | bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); 748 | 749 | /* Maybe wait for TXD */ 750 | while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) 751 | ; 752 | 753 | /* Write to FIFO, no barrier */ 754 | bcm2835_peri_write_nb(fifo, bcm2835_correct_order(value)); 755 | 756 | /* Wait for DONE to be set */ 757 | while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) 758 | ; 759 | 760 | /* Read any byte that was sent back by the slave while we sere sending to it */ 761 | ret = bcm2835_correct_order(bcm2835_peri_read_nb(fifo)); 762 | 763 | /* Set TA = 0, and also set the barrier */ 764 | bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); 765 | 766 | return ret; 767 | } 768 | 769 | /* Writes (and reads) an number of bytes to SPI */ 770 | void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len) 771 | { 772 | volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; 773 | volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; 774 | uint32_t TXCnt=0; 775 | uint32_t RXCnt=0; 776 | 777 | /* This is Polled transfer as per section 10.6.1 778 | // BUG ALERT: what happens if we get interupted in this section, and someone else 779 | // accesses a different peripheral? 780 | */ 781 | 782 | /* Clear TX and RX fifos */ 783 | bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); 784 | 785 | /* Set TA = 1 */ 786 | bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); 787 | 788 | /* Use the FIFO's to reduce the interbyte times */ 789 | while((TXCnt < len)||(RXCnt < len)) 790 | { 791 | /* TX fifo not full, so add some more bytes */ 792 | while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))&&(TXCnt < len )) 793 | { 794 | bcm2835_peri_write_nb(fifo, bcm2835_correct_order(tbuf[TXCnt])); 795 | TXCnt++; 796 | } 797 | /* Rx fifo not empty, so get the next received bytes */ 798 | while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))&&( RXCnt < len )) 799 | { 800 | rbuf[RXCnt] = bcm2835_correct_order(bcm2835_peri_read_nb(fifo)); 801 | RXCnt++; 802 | } 803 | } 804 | /* Wait for DONE to be set */ 805 | while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) 806 | ; 807 | 808 | /* Set TA = 0, and also set the barrier */ 809 | bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); 810 | } 811 | 812 | /* Writes an number of bytes to SPI */ 813 | void bcm2835_spi_writenb(const char* tbuf, uint32_t len) 814 | { 815 | volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; 816 | volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; 817 | uint32_t i; 818 | 819 | /* This is Polled transfer as per section 10.6.1 820 | // BUG ALERT: what happens if we get interupted in this section, and someone else 821 | // accesses a different peripheral? 822 | // Answer: an ISR is required to issue the required memory barriers. 823 | */ 824 | 825 | /* Clear TX and RX fifos */ 826 | bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); 827 | 828 | /* Set TA = 1 */ 829 | bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); 830 | 831 | for (i = 0; i < len; i++) 832 | { 833 | /* Maybe wait for TXD */ 834 | while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) 835 | ; 836 | 837 | /* Write to FIFO, no barrier */ 838 | bcm2835_peri_write_nb(fifo, bcm2835_correct_order(tbuf[i])); 839 | 840 | /* Read from FIFO to prevent stalling */ 841 | while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) 842 | (void) bcm2835_peri_read_nb(fifo); 843 | } 844 | 845 | /* Wait for DONE to be set */ 846 | while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) { 847 | while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) 848 | (void) bcm2835_peri_read_nb(fifo); 849 | }; 850 | 851 | /* Set TA = 0, and also set the barrier */ 852 | bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); 853 | } 854 | 855 | /* Writes (and reads) an number of bytes to SPI 856 | // Read bytes are copied over onto the transmit buffer 857 | */ 858 | void bcm2835_spi_transfern(char* buf, uint32_t len) 859 | { 860 | bcm2835_spi_transfernb(buf, buf, len); 861 | } 862 | 863 | void bcm2835_spi_chipSelect(uint8_t cs) 864 | { 865 | volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; 866 | /* Mask in the CS bits of CS */ 867 | bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS); 868 | } 869 | 870 | void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active) 871 | { 872 | volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; 873 | uint8_t shift = 21 + cs; 874 | /* Mask in the appropriate CSPOLn bit */ 875 | bcm2835_peri_set_bits(paddr, active << shift, 1 << shift); 876 | } 877 | 878 | void bcm2835_spi_write(uint16_t data) 879 | { 880 | #if 0 881 | char buf[2]; 882 | 883 | buf[0] = data >> 8; 884 | buf[1] = data & 0xFF; 885 | 886 | bcm2835_spi_transfern(buf, 2); 887 | #else 888 | volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; 889 | volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; 890 | 891 | /* Clear TX and RX fifos */ 892 | bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); 893 | 894 | /* Set TA = 1 */ 895 | bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); 896 | 897 | /* Maybe wait for TXD */ 898 | while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) 899 | ; 900 | 901 | /* Write to FIFO */ 902 | bcm2835_peri_write_nb(fifo, (uint32_t) data >> 8); 903 | bcm2835_peri_write_nb(fifo, data & 0xFF); 904 | 905 | 906 | /* Wait for DONE to be set */ 907 | while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) 908 | ; 909 | 910 | /* Set TA = 0, and also set the barrier */ 911 | bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); 912 | #endif 913 | } 914 | 915 | int bcm2835_aux_spi_begin(void) 916 | { 917 | volatile uint32_t* enable = bcm2835_aux + BCM2835_AUX_ENABLE/4; 918 | volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4; 919 | volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4; 920 | 921 | if (bcm2835_spi1 == MAP_FAILED) 922 | return 0; /* bcm2835_init() failed, or not root */ 923 | 924 | /* Set the SPI pins to the Alt 4 function to enable SPI1 access on them */ 925 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_36, BCM2835_GPIO_FSEL_ALT4); /* SPI1_CE2_N */ 926 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_35, BCM2835_GPIO_FSEL_ALT4); /* SPI1_MISO */ 927 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_38, BCM2835_GPIO_FSEL_ALT4); /* SPI1_MOSI */ 928 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_40, BCM2835_GPIO_FSEL_ALT4); /* SPI1_SCLK */ 929 | 930 | bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(1000000)); // Default 1MHz SPI 931 | 932 | bcm2835_peri_write(enable, BCM2835_AUX_ENABLE_SPI0); 933 | bcm2835_peri_write(cntl1, 0); 934 | bcm2835_peri_write(cntl0, BCM2835_AUX_SPI_CNTL0_CLEARFIFO); 935 | 936 | return 1; /* OK */ 937 | } 938 | 939 | void bcm2835_aux_spi_end(void) 940 | { 941 | /* Set all the SPI1 pins back to input */ 942 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_36, BCM2835_GPIO_FSEL_INPT); /* SPI1_CE2_N */ 943 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_35, BCM2835_GPIO_FSEL_INPT); /* SPI1_MISO */ 944 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_38, BCM2835_GPIO_FSEL_INPT); /* SPI1_MOSI */ 945 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_40, BCM2835_GPIO_FSEL_INPT); /* SPI1_SCLK */ 946 | } 947 | 948 | #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) 949 | 950 | uint16_t bcm2835_aux_spi_CalcClockDivider(uint32_t speed_hz) 951 | { 952 | uint16_t divider; 953 | 954 | if (speed_hz < (uint32_t) BCM2835_AUX_SPI_CLOCK_MIN) { 955 | speed_hz = (uint32_t) BCM2835_AUX_SPI_CLOCK_MIN; 956 | } else if (speed_hz > (uint32_t) BCM2835_AUX_SPI_CLOCK_MAX) { 957 | speed_hz = (uint32_t) BCM2835_AUX_SPI_CLOCK_MAX; 958 | } 959 | 960 | divider = (uint16_t) DIV_ROUND_UP(BCM2835_CORE_CLK_HZ, 2 * speed_hz) - 1; 961 | 962 | if (divider > (uint16_t) BCM2835_AUX_SPI_CNTL0_SPEED_MAX) { 963 | return (uint16_t) BCM2835_AUX_SPI_CNTL0_SPEED_MAX; 964 | } 965 | 966 | return divider; 967 | } 968 | 969 | static uint32_t spi1_speed; 970 | 971 | void bcm2835_aux_spi_setClockDivider(uint16_t divider) 972 | { 973 | spi1_speed = (uint32_t) divider; 974 | } 975 | 976 | void bcm2835_aux_spi_write(uint16_t data) 977 | { 978 | volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4; 979 | volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4; 980 | volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4; 981 | volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4; 982 | 983 | uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT); 984 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N; 985 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE; 986 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT; 987 | _cntl0 |= 16; // Shift length 988 | 989 | bcm2835_peri_write(cntl0, _cntl0); 990 | bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN); 991 | 992 | while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) 993 | ; 994 | 995 | bcm2835_peri_write(io, (uint32_t) data << 16); 996 | } 997 | 998 | void bcm2835_aux_spi_writenb(const char *tbuf, uint32_t len) { 999 | volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4; 1000 | volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4; 1001 | volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4; 1002 | volatile uint32_t* txhold = bcm2835_spi1 + BCM2835_AUX_SPI_TXHOLD/4; 1003 | volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4; 1004 | 1005 | char *tx = (char *) tbuf; 1006 | uint32_t tx_len = len; 1007 | uint32_t count; 1008 | uint32_t data; 1009 | uint32_t i; 1010 | uint8_t byte; 1011 | 1012 | uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT); 1013 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N; 1014 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE; 1015 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT; 1016 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_VAR_WIDTH; 1017 | 1018 | bcm2835_peri_write(cntl0, _cntl0); 1019 | bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN); 1020 | 1021 | while (tx_len > 0) { 1022 | 1023 | while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) 1024 | ; 1025 | 1026 | count = MIN(tx_len, 3); 1027 | data = 0; 1028 | 1029 | for (i = 0; i < count; i++) { 1030 | byte = (tx != NULL) ? (uint8_t) *tx++ : (uint8_t) 0; 1031 | data |= byte << (8 * (2 - i)); 1032 | } 1033 | 1034 | data |= (count * 8) << 24; 1035 | tx_len -= count; 1036 | 1037 | if (tx_len != 0) { 1038 | bcm2835_peri_write(txhold, data); 1039 | } else { 1040 | bcm2835_peri_write(io, data); 1041 | } 1042 | 1043 | while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY) 1044 | ; 1045 | 1046 | (void) bcm2835_peri_read(io); 1047 | } 1048 | } 1049 | 1050 | void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len) { 1051 | volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4; 1052 | volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4; 1053 | volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4; 1054 | volatile uint32_t* txhold = bcm2835_spi1 + BCM2835_AUX_SPI_TXHOLD/4; 1055 | volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4; 1056 | 1057 | char *tx = (char *)tbuf; 1058 | char *rx = (char *)rbuf; 1059 | uint32_t tx_len = len; 1060 | uint32_t rx_len = len; 1061 | uint32_t count; 1062 | uint32_t data; 1063 | uint32_t i; 1064 | uint8_t byte; 1065 | 1066 | uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT); 1067 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N; 1068 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE; 1069 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT; 1070 | _cntl0 |= BCM2835_AUX_SPI_CNTL0_VAR_WIDTH; 1071 | 1072 | bcm2835_peri_write(cntl0, _cntl0); 1073 | bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN); 1074 | 1075 | while ((tx_len > 0) || (rx_len > 0)) { 1076 | 1077 | while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) && (tx_len > 0)) { 1078 | count = MIN(tx_len, 3); 1079 | data = 0; 1080 | 1081 | for (i = 0; i < count; i++) { 1082 | byte = (tx != NULL) ? (uint8_t) *tx++ : (uint8_t) 0; 1083 | data |= byte << (8 * (2 - i)); 1084 | } 1085 | 1086 | data |= (count * 8) << 24; 1087 | tx_len -= count; 1088 | 1089 | if (tx_len != 0) { 1090 | bcm2835_peri_write(txhold, data); 1091 | } else { 1092 | bcm2835_peri_write(io, data); 1093 | } 1094 | 1095 | } 1096 | 1097 | while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_RX_EMPTY) && (rx_len > 0)) { 1098 | count = MIN(rx_len, 3); 1099 | data = bcm2835_peri_read(io); 1100 | 1101 | if (rbuf != NULL) { 1102 | switch (count) { 1103 | case 3: 1104 | *rx++ = (char)((data >> 16) & 0xFF); 1105 | /*@fallthrough@*/ 1106 | /* no break */ 1107 | case 2: 1108 | *rx++ = (char)((data >> 8) & 0xFF); 1109 | /*@fallthrough@*/ 1110 | /* no break */ 1111 | case 1: 1112 | *rx++ = (char)((data >> 0) & 0xFF); 1113 | } 1114 | } 1115 | 1116 | rx_len -= count; 1117 | } 1118 | 1119 | while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY) && (rx_len > 0)) { 1120 | count = MIN(rx_len, 3); 1121 | data = bcm2835_peri_read(io); 1122 | 1123 | if (rbuf != NULL) { 1124 | switch (count) { 1125 | case 3: 1126 | *rx++ = (char)((data >> 16) & 0xFF); 1127 | /*@fallthrough@*/ 1128 | /* no break */ 1129 | case 2: 1130 | *rx++ = (char)((data >> 8) & 0xFF); 1131 | /*@fallthrough@*/ 1132 | /* no break */ 1133 | case 1: 1134 | *rx++ = (char)((data >> 0) & 0xFF); 1135 | } 1136 | } 1137 | 1138 | rx_len -= count; 1139 | } 1140 | } 1141 | } 1142 | 1143 | void bcm2835_aux_spi_transfern(char *buf, uint32_t len) { 1144 | bcm2835_aux_spi_transfernb(buf, buf, len); 1145 | } 1146 | 1147 | int bcm2835_i2c_begin(void) 1148 | { 1149 | uint16_t cdiv; 1150 | 1151 | if ( bcm2835_bsc0 == MAP_FAILED 1152 | || bcm2835_bsc1 == MAP_FAILED) 1153 | return 0; /* bcm2835_init() failed, or not root */ 1154 | 1155 | #ifdef I2C_V1 1156 | volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; 1157 | /* Set the I2C/BSC0 pins to the Alt 0 function to enable I2C access on them */ 1158 | bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); /* SDA */ 1159 | bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); /* SCL */ 1160 | #else 1161 | volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; 1162 | /* Set the I2C/BSC1 pins to the Alt 0 function to enable I2C access on them */ 1163 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); /* SDA */ 1164 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); /* SCL */ 1165 | #endif 1166 | 1167 | /* Read the clock divider register */ 1168 | cdiv = bcm2835_peri_read(paddr); 1169 | /* Calculate time for transmitting one byte 1170 | // 1000000 = micros seconds in a second 1171 | // 9 = Clocks per byte : 8 bits + ACK 1172 | */ 1173 | i2c_byte_wait_us = ((float)cdiv / BCM2835_CORE_CLK_HZ) * 1000000 * 9; 1174 | 1175 | return 1; 1176 | } 1177 | 1178 | void bcm2835_i2c_end(void) 1179 | { 1180 | #ifdef I2C_V1 1181 | /* Set all the I2C/BSC0 pins back to input */ 1182 | bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); /* SDA */ 1183 | bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); /* SCL */ 1184 | #else 1185 | /* Set all the I2C/BSC1 pins back to input */ 1186 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); /* SDA */ 1187 | bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); /* SCL */ 1188 | #endif 1189 | } 1190 | 1191 | void bcm2835_i2c_setSlaveAddress(uint8_t addr) 1192 | { 1193 | /* Set I2C Device Address */ 1194 | #ifdef I2C_V1 1195 | volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_A/4; 1196 | #else 1197 | volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_A/4; 1198 | #endif 1199 | bcm2835_peri_write(paddr, addr); 1200 | } 1201 | 1202 | /* defaults to 0x5dc, should result in a 166.666 kHz I2C clock frequency. 1203 | // The divisor must be a power of 2. Odd numbers 1204 | // rounded down. 1205 | */ 1206 | void bcm2835_i2c_setClockDivider(uint16_t divider) 1207 | { 1208 | #ifdef I2C_V1 1209 | volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; 1210 | #else 1211 | volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; 1212 | #endif 1213 | bcm2835_peri_write(paddr, divider); 1214 | /* Calculate time for transmitting one byte 1215 | // 1000000 = micros seconds in a second 1216 | // 9 = Clocks per byte : 8 bits + ACK 1217 | */ 1218 | i2c_byte_wait_us = ((float)divider / BCM2835_CORE_CLK_HZ) * 1000000 * 9; 1219 | } 1220 | 1221 | /* set I2C clock divider by means of a baudrate number */ 1222 | void bcm2835_i2c_set_baudrate(uint32_t baudrate) 1223 | { 1224 | uint32_t divider; 1225 | /* use 0xFFFE mask to limit a max value and round down any odd number */ 1226 | divider = (BCM2835_CORE_CLK_HZ / baudrate) & 0xFFFE; 1227 | bcm2835_i2c_setClockDivider( (uint16_t)divider ); 1228 | } 1229 | 1230 | /* Writes an number of bytes to I2C */ 1231 | uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) 1232 | { 1233 | #ifdef I2C_V1 1234 | volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; 1235 | volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; 1236 | volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; 1237 | volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; 1238 | #else 1239 | volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; 1240 | volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; 1241 | volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; 1242 | volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; 1243 | #endif 1244 | 1245 | uint32_t remaining = len; 1246 | uint32_t i = 0; 1247 | uint8_t reason = BCM2835_I2C_REASON_OK; 1248 | 1249 | /* Clear FIFO */ 1250 | bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); 1251 | /* Clear Status */ 1252 | bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); 1253 | /* Set Data Length */ 1254 | bcm2835_peri_write(dlen, len); 1255 | /* pre populate FIFO with max buffer */ 1256 | while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) 1257 | { 1258 | bcm2835_peri_write_nb(fifo, buf[i]); 1259 | i++; 1260 | remaining--; 1261 | } 1262 | 1263 | /* Enable device and start transfer */ 1264 | bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); 1265 | 1266 | /* Transfer is over when BCM2835_BSC_S_DONE */ 1267 | while(!(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE )) 1268 | { 1269 | while ( remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_TXD )) 1270 | { 1271 | /* Write to FIFO */ 1272 | bcm2835_peri_write(fifo, buf[i]); 1273 | i++; 1274 | remaining--; 1275 | } 1276 | } 1277 | 1278 | /* Received a NACK */ 1279 | if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) 1280 | { 1281 | reason = BCM2835_I2C_REASON_ERROR_NACK; 1282 | } 1283 | 1284 | /* Received Clock Stretch Timeout */ 1285 | else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) 1286 | { 1287 | reason = BCM2835_I2C_REASON_ERROR_CLKT; 1288 | } 1289 | 1290 | /* Not all data is sent */ 1291 | else if (remaining) 1292 | { 1293 | reason = BCM2835_I2C_REASON_ERROR_DATA; 1294 | } 1295 | 1296 | bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); 1297 | 1298 | return reason; 1299 | } 1300 | 1301 | /* Read an number of bytes from I2C */ 1302 | uint8_t bcm2835_i2c_read(char* buf, uint32_t len) 1303 | { 1304 | #ifdef I2C_V1 1305 | volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; 1306 | volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; 1307 | volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; 1308 | volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; 1309 | #else 1310 | volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; 1311 | volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; 1312 | volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; 1313 | volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; 1314 | #endif 1315 | 1316 | uint32_t remaining = len; 1317 | uint32_t i = 0; 1318 | uint8_t reason = BCM2835_I2C_REASON_OK; 1319 | 1320 | /* Clear FIFO */ 1321 | bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); 1322 | /* Clear Status */ 1323 | bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); 1324 | /* Set Data Length */ 1325 | bcm2835_peri_write_nb(dlen, len); 1326 | /* Start read */ 1327 | bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ); 1328 | 1329 | /* wait for transfer to complete */ 1330 | while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) 1331 | { 1332 | /* we must empty the FIFO as it is populated and not use any delay */ 1333 | while (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) 1334 | { 1335 | /* Read from FIFO, no barrier */ 1336 | buf[i] = bcm2835_peri_read_nb(fifo); 1337 | i++; 1338 | remaining--; 1339 | } 1340 | } 1341 | 1342 | /* transfer has finished - grab any remaining stuff in FIFO */ 1343 | while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) 1344 | { 1345 | /* Read from FIFO, no barrier */ 1346 | buf[i] = bcm2835_peri_read_nb(fifo); 1347 | i++; 1348 | remaining--; 1349 | } 1350 | 1351 | /* Received a NACK */ 1352 | if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) 1353 | { 1354 | reason = BCM2835_I2C_REASON_ERROR_NACK; 1355 | } 1356 | 1357 | /* Received Clock Stretch Timeout */ 1358 | else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) 1359 | { 1360 | reason = BCM2835_I2C_REASON_ERROR_CLKT; 1361 | } 1362 | 1363 | /* Not all data is received */ 1364 | else if (remaining) 1365 | { 1366 | reason = BCM2835_I2C_REASON_ERROR_DATA; 1367 | } 1368 | 1369 | bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); 1370 | 1371 | return reason; 1372 | } 1373 | 1374 | /* Read an number of bytes from I2C sending a repeated start after writing 1375 | // the required register. Only works if your device supports this mode 1376 | */ 1377 | uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) 1378 | { 1379 | #ifdef I2C_V1 1380 | volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; 1381 | volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; 1382 | volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; 1383 | volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; 1384 | #else 1385 | volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; 1386 | volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; 1387 | volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; 1388 | volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; 1389 | #endif 1390 | uint32_t remaining = len; 1391 | uint32_t i = 0; 1392 | uint8_t reason = BCM2835_I2C_REASON_OK; 1393 | 1394 | /* Clear FIFO */ 1395 | bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); 1396 | /* Clear Status */ 1397 | bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); 1398 | /* Set Data Length */ 1399 | bcm2835_peri_write(dlen, 1); 1400 | /* Enable device and start transfer */ 1401 | bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN); 1402 | bcm2835_peri_write(fifo, regaddr[0]); 1403 | bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); 1404 | 1405 | /* poll for transfer has started */ 1406 | while ( !( bcm2835_peri_read(status) & BCM2835_BSC_S_TA ) ) 1407 | { 1408 | /* Linux may cause us to miss entire transfer stage */ 1409 | if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) 1410 | break; 1411 | } 1412 | 1413 | /* Send a repeated start with read bit set in address */ 1414 | bcm2835_peri_write(dlen, len); 1415 | bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); 1416 | 1417 | /* Wait for write to complete and first byte back. */ 1418 | bcm2835_delayMicroseconds(i2c_byte_wait_us * 3); 1419 | 1420 | /* wait for transfer to complete */ 1421 | while (!(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE)) 1422 | { 1423 | /* we must empty the FIFO as it is populated and not use any delay */ 1424 | while (remaining && bcm2835_peri_read(status) & BCM2835_BSC_S_RXD) 1425 | { 1426 | /* Read from FIFO */ 1427 | buf[i] = bcm2835_peri_read(fifo); 1428 | i++; 1429 | remaining--; 1430 | } 1431 | } 1432 | 1433 | /* transfer has finished - grab any remaining stuff in FIFO */ 1434 | while (remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_RXD)) 1435 | { 1436 | /* Read from FIFO */ 1437 | buf[i] = bcm2835_peri_read(fifo); 1438 | i++; 1439 | remaining--; 1440 | } 1441 | 1442 | /* Received a NACK */ 1443 | if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) 1444 | { 1445 | reason = BCM2835_I2C_REASON_ERROR_NACK; 1446 | } 1447 | 1448 | /* Received Clock Stretch Timeout */ 1449 | else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) 1450 | { 1451 | reason = BCM2835_I2C_REASON_ERROR_CLKT; 1452 | } 1453 | 1454 | /* Not all data is sent */ 1455 | else if (remaining) 1456 | { 1457 | reason = BCM2835_I2C_REASON_ERROR_DATA; 1458 | } 1459 | 1460 | bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); 1461 | 1462 | return reason; 1463 | } 1464 | 1465 | /* Sending an arbitrary number of bytes before issuing a repeated start 1466 | // (with no prior stop) and reading a response. Some devices require this behavior. 1467 | */ 1468 | uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len) 1469 | { 1470 | #ifdef I2C_V1 1471 | volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; 1472 | volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; 1473 | volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; 1474 | volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; 1475 | #else 1476 | volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; 1477 | volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; 1478 | volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; 1479 | volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; 1480 | #endif 1481 | 1482 | uint32_t remaining = cmds_len; 1483 | uint32_t i = 0; 1484 | uint8_t reason = BCM2835_I2C_REASON_OK; 1485 | 1486 | /* Clear FIFO */ 1487 | bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); 1488 | 1489 | /* Clear Status */ 1490 | bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); 1491 | 1492 | /* Set Data Length */ 1493 | bcm2835_peri_write(dlen, cmds_len); 1494 | 1495 | /* pre populate FIFO with max buffer */ 1496 | while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) 1497 | { 1498 | bcm2835_peri_write_nb(fifo, cmds[i]); 1499 | i++; 1500 | remaining--; 1501 | } 1502 | 1503 | /* Enable device and start transfer */ 1504 | bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); 1505 | 1506 | /* poll for transfer has started (way to do repeated start, from BCM2835 datasheet) */ 1507 | while ( !( bcm2835_peri_read(status) & BCM2835_BSC_S_TA ) ) 1508 | { 1509 | /* Linux may cause us to miss entire transfer stage */ 1510 | if(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE) 1511 | break; 1512 | } 1513 | 1514 | remaining = buf_len; 1515 | i = 0; 1516 | 1517 | /* Send a repeated start with read bit set in address */ 1518 | bcm2835_peri_write(dlen, buf_len); 1519 | bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); 1520 | 1521 | /* Wait for write to complete and first byte back. */ 1522 | bcm2835_delayMicroseconds(i2c_byte_wait_us * (cmds_len + 1)); 1523 | 1524 | /* wait for transfer to complete */ 1525 | while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) 1526 | { 1527 | /* we must empty the FIFO as it is populated and not use any delay */ 1528 | while (remaining && bcm2835_peri_read(status) & BCM2835_BSC_S_RXD) 1529 | { 1530 | /* Read from FIFO, no barrier */ 1531 | buf[i] = bcm2835_peri_read_nb(fifo); 1532 | i++; 1533 | remaining--; 1534 | } 1535 | } 1536 | 1537 | /* transfer has finished - grab any remaining stuff in FIFO */ 1538 | while (remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_RXD)) 1539 | { 1540 | /* Read from FIFO */ 1541 | buf[i] = bcm2835_peri_read(fifo); 1542 | i++; 1543 | remaining--; 1544 | } 1545 | 1546 | /* Received a NACK */ 1547 | if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) 1548 | { 1549 | reason = BCM2835_I2C_REASON_ERROR_NACK; 1550 | } 1551 | 1552 | /* Received Clock Stretch Timeout */ 1553 | else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) 1554 | { 1555 | reason = BCM2835_I2C_REASON_ERROR_CLKT; 1556 | } 1557 | 1558 | /* Not all data is sent */ 1559 | else if (remaining) 1560 | { 1561 | reason = BCM2835_I2C_REASON_ERROR_DATA; 1562 | } 1563 | 1564 | bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); 1565 | 1566 | return reason; 1567 | } 1568 | 1569 | /* Read the System Timer Counter (64-bits) */ 1570 | uint64_t bcm2835_st_read(void) 1571 | { 1572 | volatile uint32_t* paddr; 1573 | uint32_t hi, lo; 1574 | uint64_t st; 1575 | 1576 | if (bcm2835_st==MAP_FAILED) 1577 | return 0; 1578 | 1579 | paddr = bcm2835_st + BCM2835_ST_CHI/4; 1580 | hi = bcm2835_peri_read(paddr); 1581 | 1582 | paddr = bcm2835_st + BCM2835_ST_CLO/4; 1583 | lo = bcm2835_peri_read(paddr); 1584 | 1585 | paddr = bcm2835_st + BCM2835_ST_CHI/4; 1586 | st = bcm2835_peri_read(paddr); 1587 | 1588 | /* Test for overflow */ 1589 | if (st == hi) 1590 | { 1591 | st <<= 32; 1592 | st += lo; 1593 | } 1594 | else 1595 | { 1596 | st <<= 32; 1597 | paddr = bcm2835_st + BCM2835_ST_CLO/4; 1598 | st += bcm2835_peri_read(paddr); 1599 | } 1600 | return st; 1601 | } 1602 | 1603 | /* Delays for the specified number of microseconds with offset */ 1604 | void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros) 1605 | { 1606 | uint64_t compare = offset_micros + micros; 1607 | 1608 | while(bcm2835_st_read() < compare) 1609 | ; 1610 | } 1611 | 1612 | /* PWM */ 1613 | 1614 | void bcm2835_pwm_set_clock(uint32_t divisor) 1615 | { 1616 | if ( bcm2835_clk == MAP_FAILED 1617 | || bcm2835_pwm == MAP_FAILED) 1618 | return; /* bcm2835_init() failed or not root */ 1619 | 1620 | /* From Gerts code */ 1621 | divisor &= 0xfff; 1622 | /* Stop PWM clock */ 1623 | bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01); 1624 | bcm2835_delay(110); /* Prevents clock going slow */ 1625 | /* Wait for the clock to be not busy */ 1626 | while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0) 1627 | bcm2835_delay(1); 1628 | /* set the clock divider and enable PWM clock */ 1629 | bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12)); 1630 | bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11); /* Source=osc and enable */ 1631 | } 1632 | 1633 | void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled) 1634 | { 1635 | if ( bcm2835_clk == MAP_FAILED 1636 | || bcm2835_pwm == MAP_FAILED) 1637 | return; /* bcm2835_init() failed or not root */ 1638 | 1639 | uint32_t control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL); 1640 | 1641 | if (channel == 0) 1642 | { 1643 | if (markspace) 1644 | control |= BCM2835_PWM0_MS_MODE; 1645 | else 1646 | control &= ~BCM2835_PWM0_MS_MODE; 1647 | if (enabled) 1648 | control |= BCM2835_PWM0_ENABLE; 1649 | else 1650 | control &= ~BCM2835_PWM0_ENABLE; 1651 | } 1652 | else if (channel == 1) 1653 | { 1654 | if (markspace) 1655 | control |= BCM2835_PWM1_MS_MODE; 1656 | else 1657 | control &= ~BCM2835_PWM1_MS_MODE; 1658 | if (enabled) 1659 | control |= BCM2835_PWM1_ENABLE; 1660 | else 1661 | control &= ~BCM2835_PWM1_ENABLE; 1662 | } 1663 | 1664 | /* If you use the barrier here, wierd things happen, and the commands dont work */ 1665 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control); 1666 | /* bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE); */ 1667 | 1668 | } 1669 | 1670 | void bcm2835_pwm_set_range(uint8_t channel, uint32_t range) 1671 | { 1672 | if ( bcm2835_clk == MAP_FAILED 1673 | || bcm2835_pwm == MAP_FAILED) 1674 | return; /* bcm2835_init() failed or not root */ 1675 | 1676 | if (channel == 0) 1677 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range); 1678 | else if (channel == 1) 1679 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range); 1680 | } 1681 | 1682 | void bcm2835_pwm_set_data(uint8_t channel, uint32_t data) 1683 | { 1684 | if ( bcm2835_clk == MAP_FAILED 1685 | || bcm2835_pwm == MAP_FAILED) 1686 | return; /* bcm2835_init() failed or not root */ 1687 | 1688 | if (channel == 0) 1689 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data); 1690 | else if (channel == 1) 1691 | bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data); 1692 | } 1693 | 1694 | /* Allocate page-aligned memory. */ 1695 | void *malloc_aligned(size_t size) 1696 | { 1697 | void *mem; 1698 | errno = posix_memalign(&mem, BCM2835_PAGE_SIZE, size); 1699 | return (errno ? NULL : mem); 1700 | } 1701 | 1702 | /* Map 'size' bytes starting at 'off' in file 'fd' to memory. 1703 | // Return mapped address on success, MAP_FAILED otherwise. 1704 | // On error print message. 1705 | */ 1706 | static void *mapmem(const char *msg, size_t size, int fd, off_t off) 1707 | { 1708 | void *map = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, off); 1709 | if (map == MAP_FAILED) 1710 | fprintf(stderr, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno)); 1711 | return map; 1712 | } 1713 | 1714 | static void unmapmem(void **pmem, size_t size) 1715 | { 1716 | if (*pmem == MAP_FAILED) return; 1717 | munmap(*pmem, size); 1718 | *pmem = MAP_FAILED; 1719 | } 1720 | 1721 | /* Initialise this library. */ 1722 | int bcm2835_init(void) 1723 | { 1724 | int memfd; 1725 | int ok; 1726 | FILE *fp; 1727 | 1728 | if (debug) 1729 | { 1730 | bcm2835_peripherals = (uint32_t*)BCM2835_PERI_BASE; 1731 | 1732 | bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS/4; 1733 | bcm2835_clk = bcm2835_peripherals + BCM2835_CLOCK_BASE/4; 1734 | bcm2835_gpio = bcm2835_peripherals + BCM2835_GPIO_BASE/4; 1735 | bcm2835_pwm = bcm2835_peripherals + BCM2835_GPIO_PWM/4; 1736 | bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE/4; 1737 | bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4; 1738 | bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; 1739 | bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4; 1740 | bcm2835_aux = bcm2835_peripherals + BCM2835_AUX_BASE/4; 1741 | bcm2835_spi1 = bcm2835_peripherals + BCM2835_SPI1_BASE/4; 1742 | 1743 | return 1; /* Success */ 1744 | } 1745 | 1746 | /* Figure out the base and size of the peripheral address block 1747 | // using the device-tree. Required for RPi2/3/4, optional for RPi 1 1748 | */ 1749 | if ((fp = fopen(BMC2835_RPI2_DT_FILENAME , "rb"))) 1750 | { 1751 | unsigned char buf[16]; 1752 | uint32_t base_address; 1753 | uint32_t peri_size; 1754 | if (fread(buf, 1, sizeof(buf), fp) >= 8) 1755 | { 1756 | base_address = (buf[4] << 24) | 1757 | (buf[5] << 16) | 1758 | (buf[6] << 8) | 1759 | (buf[7] << 0); 1760 | 1761 | peri_size = (buf[8] << 24) | 1762 | (buf[9] << 16) | 1763 | (buf[10] << 8) | 1764 | (buf[11] << 0); 1765 | 1766 | if (!base_address) 1767 | { 1768 | /* looks like RPI 4 */ 1769 | base_address = (buf[8] << 24) | 1770 | (buf[9] << 16) | 1771 | (buf[10] << 8) | 1772 | (buf[11] << 0); 1773 | 1774 | peri_size = (buf[12] << 24) | 1775 | (buf[13] << 16) | 1776 | (buf[14] << 8) | 1777 | (buf[15] << 0); 1778 | } 1779 | /* check for valid known range formats */ 1780 | if ((buf[0] == 0x7e) && 1781 | (buf[1] == 0x00) && 1782 | (buf[2] == 0x00) && 1783 | (buf[3] == 0x00) && 1784 | ((base_address == BCM2835_PERI_BASE) || (base_address == BCM2835_RPI2_PERI_BASE) || (base_address == BCM2835_RPI4_PERI_BASE))) 1785 | { 1786 | bcm2835_peripherals_base = (uint32_t *)base_address; 1787 | bcm2835_peripherals_size = peri_size; 1788 | if( base_address == BCM2835_RPI4_PERI_BASE ) 1789 | { 1790 | pud_type_rpi4 = 1; 1791 | } 1792 | } 1793 | 1794 | } 1795 | 1796 | fclose(fp); 1797 | } 1798 | /* else we are prob on RPi 1 with BCM2835, and use the hardwired defaults */ 1799 | 1800 | /* Now get ready to map the peripherals block 1801 | * If we are not root, try for the new /dev/gpiomem interface and accept 1802 | * the fact that we can only access GPIO 1803 | * else try for the /dev/mem interface and get access to everything 1804 | */ 1805 | memfd = -1; 1806 | ok = 0; 1807 | if (geteuid() == 0) 1808 | { 1809 | /* Open the master /dev/mem device */ 1810 | if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) 1811 | { 1812 | fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n", 1813 | strerror(errno)) ; 1814 | goto exit; 1815 | } 1816 | 1817 | /* Base of the peripherals block is mapped to VM */ 1818 | bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, (off_t)(uint32_t)bcm2835_peripherals_base); 1819 | if (bcm2835_peripherals == MAP_FAILED) goto exit; 1820 | 1821 | /* Now compute the base addresses of various peripherals, 1822 | // which are at fixed offsets within the mapped peripherals block 1823 | // Caution: bcm2835_peripherals is uint32_t*, so divide offsets by 4 1824 | */ 1825 | bcm2835_gpio = bcm2835_peripherals + BCM2835_GPIO_BASE/4; 1826 | bcm2835_pwm = bcm2835_peripherals + BCM2835_GPIO_PWM/4; 1827 | bcm2835_clk = bcm2835_peripherals + BCM2835_CLOCK_BASE/4; 1828 | bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS/4; 1829 | bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE/4; 1830 | bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4; /* I2C */ 1831 | bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; /* I2C */ 1832 | bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4; 1833 | bcm2835_aux = bcm2835_peripherals + BCM2835_AUX_BASE/4; 1834 | bcm2835_spi1 = bcm2835_peripherals + BCM2835_SPI1_BASE/4; 1835 | 1836 | ok = 1; 1837 | } 1838 | else 1839 | { 1840 | /* Not root, try /dev/gpiomem */ 1841 | /* Open the master /dev/mem device */ 1842 | if ((memfd = open("/dev/gpiomem", O_RDWR | O_SYNC) ) < 0) 1843 | { 1844 | fprintf(stderr, "bcm2835_init: Unable to open /dev/gpiomem: %s\n", 1845 | strerror(errno)) ; 1846 | goto exit; 1847 | } 1848 | 1849 | /* Base of the peripherals block is mapped to VM */ 1850 | bcm2835_peripherals_base = 0; 1851 | bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, (off_t)(uint32_t)bcm2835_peripherals_base); 1852 | if (bcm2835_peripherals == MAP_FAILED) goto exit; 1853 | bcm2835_gpio = bcm2835_peripherals; 1854 | ok = 1; 1855 | } 1856 | 1857 | exit: 1858 | if (memfd >= 0) 1859 | close(memfd); 1860 | 1861 | if (!ok) 1862 | bcm2835_close(); 1863 | 1864 | return ok; 1865 | } 1866 | 1867 | /* Close this library and deallocate everything */ 1868 | int bcm2835_close(void) 1869 | { 1870 | if (debug) return 1; /* Success */ 1871 | 1872 | unmapmem((void**) &bcm2835_peripherals, bcm2835_peripherals_size); 1873 | bcm2835_peripherals = MAP_FAILED; 1874 | bcm2835_gpio = MAP_FAILED; 1875 | bcm2835_pwm = MAP_FAILED; 1876 | bcm2835_clk = MAP_FAILED; 1877 | bcm2835_pads = MAP_FAILED; 1878 | bcm2835_spi0 = MAP_FAILED; 1879 | bcm2835_bsc0 = MAP_FAILED; 1880 | bcm2835_bsc1 = MAP_FAILED; 1881 | bcm2835_st = MAP_FAILED; 1882 | bcm2835_aux = MAP_FAILED; 1883 | bcm2835_spi1 = MAP_FAILED; 1884 | return 1; /* Success */ 1885 | } 1886 | 1887 | #ifdef BCM2835_TEST 1888 | /* this is a simple test program that prints out what it will do rather than 1889 | // actually doing it 1890 | */ 1891 | int main(int argc, char **argv) 1892 | { 1893 | /* Be non-destructive */ 1894 | bcm2835_set_debug(1); 1895 | 1896 | if (!bcm2835_init()) 1897 | return 1; 1898 | 1899 | /* Configure some GPIO pins fo some testing 1900 | // Set RPI pin P1-11 to be an output 1901 | */ 1902 | bcm2835_gpio_fsel(RPI_GPIO_P1_11, BCM2835_GPIO_FSEL_OUTP); 1903 | /* Set RPI pin P1-15 to be an input */ 1904 | bcm2835_gpio_fsel(RPI_GPIO_P1_15, BCM2835_GPIO_FSEL_INPT); 1905 | /* with a pullup */ 1906 | bcm2835_gpio_set_pud(RPI_GPIO_P1_15, BCM2835_GPIO_PUD_UP); 1907 | /* And a low detect enable */ 1908 | bcm2835_gpio_len(RPI_GPIO_P1_15); 1909 | /* and input hysteresis disabled on GPIOs 0 to 27 */ 1910 | bcm2835_gpio_set_pad(BCM2835_PAD_GROUP_GPIO_0_27, BCM2835_PAD_SLEW_RATE_UNLIMITED|BCM2835_PAD_DRIVE_8mA); 1911 | 1912 | #if 1 1913 | /* Blink */ 1914 | while (1) 1915 | { 1916 | /* Turn it on */ 1917 | bcm2835_gpio_write(RPI_GPIO_P1_11, HIGH); 1918 | 1919 | /* wait a bit */ 1920 | bcm2835_delay(500); 1921 | 1922 | /* turn it off */ 1923 | bcm2835_gpio_write(RPI_GPIO_P1_11, LOW); 1924 | 1925 | /* wait a bit */ 1926 | bcm2835_delay(500); 1927 | } 1928 | #endif 1929 | 1930 | #if 0 1931 | /* Read input */ 1932 | while (1) 1933 | { 1934 | /* Read some data */ 1935 | uint8_t value = bcm2835_gpio_lev(RPI_GPIO_P1_15); 1936 | printf("read from pin 15: %d\n", value); 1937 | 1938 | /* wait a bit */ 1939 | bcm2835_delay(500); 1940 | } 1941 | #endif 1942 | 1943 | #if 0 1944 | /* Look for a low event detection 1945 | // eds will be set whenever pin 15 goes low 1946 | */ 1947 | while (1) 1948 | { 1949 | if (bcm2835_gpio_eds(RPI_GPIO_P1_15)) 1950 | { 1951 | /* Now clear the eds flag by setting it to 1 */ 1952 | bcm2835_gpio_set_eds(RPI_GPIO_P1_15); 1953 | printf("low event detect for pin 15\n"); 1954 | } 1955 | 1956 | /* wait a bit */ 1957 | bcm2835_delay(500); 1958 | } 1959 | #endif 1960 | 1961 | if (!bcm2835_close()) 1962 | return 1; 1963 | 1964 | return 0; 1965 | } 1966 | #endif 1967 | 1968 | 1969 | 1970 | -------------------------------------------------------------------------------- /src/dht-sensor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "abstract-gpio.h" 5 | #include 6 | 7 | #define MAXTIMINGS 100 8 | #define DHT11 11 9 | #define DHT22 22 10 | #define AM2302 22 11 | 12 | int initialized = 0; 13 | unsigned long long last_read[32] = {}; 14 | float last_temperature[32] = {}; 15 | float last_humidity[32] = {}; 16 | 17 | unsigned long long getTime() 18 | { 19 | struct timeval tv; 20 | gettimeofday(&tv, nullptr); 21 | unsigned long long time = (unsigned long long)(tv.tv_sec) * 1000 + 22 | (unsigned long long)(tv.tv_usec) / 1000; 23 | return time; 24 | } 25 | 26 | long readDHT(int type, int pin, float &temperature, float &humidity) 27 | { 28 | int bitCount = 0; 29 | int timeout; 30 | int bits[MAXTIMINGS]; 31 | int data[MAXTIMINGS / 8] = {}; 32 | 33 | #ifdef VERBOSE 34 | #ifdef DBG_CONSOLE 35 | FILE *pTrace = stdout; 36 | #else 37 | FILE *pTrace = fopen("dht-sensor.log", "a"); 38 | if (pTrace == nullptr) 39 | { 40 | puts("WARNING: unable to initialize trace file, it will be redirected to stdout."); 41 | pTrace = stdout; 42 | } 43 | #endif 44 | #endif 45 | 46 | #ifdef VERBOSE 47 | fprintf(pTrace, "start sensor read (type=%d, pin=%d).\n", type, pin); 48 | #endif 49 | 50 | // throttle sensor reading - if last read was less than 2s then return same 51 | unsigned long long now = getTime(); 52 | if ((last_read[pin] > 0) && (now - last_read[pin] < 3000)) 53 | { 54 | #ifdef VERBOSE 55 | fprintf(pTrace, "*** too early to read again pin %d: %llu\n", pin, now - last_read[pin]); 56 | #endif 57 | temperature = last_temperature[pin]; 58 | humidity = last_humidity[pin]; 59 | return 0; 60 | } 61 | 62 | // request sensor data 63 | gpioWrite(pin, GPIO_HIGH); 64 | usleep(10000); 65 | gpioWrite(pin, GPIO_LOW); 66 | usleep(type == 11 ? 18000 : 2500); 67 | gpioWrite(pin, GPIO_HIGH); 68 | 69 | // wait for sensor response 70 | for (timeout = 0; timeout < 1000000 && gpioRead(pin) == GPIO_LOW; ++timeout); 71 | if (timeout >= 100000) 72 | { 73 | #ifdef VERBOSE 74 | fprintf(pTrace, "*** timeout #1\n"); 75 | #ifdef DBG_CONSOLE 76 | fflush(pTrace); 77 | #else 78 | fclose(pTrace); 79 | #endif 80 | #endif 81 | return -3; 82 | } 83 | 84 | for (timeout = 0; timeout < 1000000 && gpioRead(pin) == GPIO_HIGH; ++timeout); 85 | if (timeout >= 100000) 86 | { 87 | #ifdef VERBOSE 88 | fprintf(pTrace, "*** timeout #2\n"); 89 | #ifdef DBG_CONSOLE 90 | fflush(pTrace); 91 | #else 92 | fclose(pTrace); 93 | #endif 94 | #endif 95 | return -3; 96 | } 97 | 98 | // read data 99 | for (bitCount = 0; bitCount < MAXTIMINGS; ++bitCount) 100 | { 101 | for (timeout = 0; gpioRead(pin) == GPIO_LOW && timeout < 50000; ++timeout); 102 | for (timeout = 0; gpioRead(pin) == GPIO_HIGH && timeout < 50000; ++timeout); 103 | bits[bitCount] = timeout; 104 | if (timeout >= 50000) break; 105 | } 106 | 107 | // data decoding, get widest pulse 108 | int peak = bits[1]; 109 | #ifdef VERBOSE 110 | fprintf(pTrace, "init peak: %d\n", bits[1]); 111 | #endif 112 | for (int i = 2; i < bitCount; ++i) 113 | { 114 | if (peak < bits[i]) 115 | { 116 | peak = bits[i]; 117 | #ifdef VERBOSE 118 | fprintf(pTrace, "update peak: %d (%d)\n", i, bits[i]); 119 | #endif 120 | } 121 | } 122 | 123 | // convert pulses to bits 124 | #ifdef VERBOSE 125 | fprintf(pTrace, "bitCount=%d, peak=%d:\n", bitCount, peak); 126 | #endif 127 | int k = 0; 128 | for (int i = 1; i < bitCount; ++i) 129 | { 130 | data[k] <<= 1; 131 | if ((2 * bits[i] - peak) > 0) 132 | { 133 | data[k] |= 1; 134 | #ifdef VERBOSE 135 | fprintf(pTrace, "1 (%03d) ", bits[i]); 136 | } 137 | else 138 | { 139 | fprintf(pTrace, "0 (%03d) ", bits[i]); 140 | #endif 141 | } 142 | if (i % 8 == 0) 143 | { 144 | k++; 145 | #ifdef VERBOSE 146 | fprintf(pTrace, "\n"); 147 | #endif 148 | } 149 | } 150 | 151 | // crc checking 152 | #ifdef VERBOSE 153 | int crc = ((data[0] + data[1] + data[2] + data[3]) & 0xff); 154 | fprintf(pTrace, "\n=> %x %x %x %x (%x/%x) : %s\n", 155 | data[0], data[1], data[2], data[3], data[4], 156 | crc, (data[4] == crc) ? "OK" : "ERR"); 157 | #endif 158 | 159 | // checks if received data is valid: 160 | // - minimum of 5 bytes (40 bits) 161 | // - has to be whole bytes 162 | // - checksum must match 163 | if ((bitCount > 40) && ((bitCount - 1) % 8 == 0) && 164 | (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xff))) 165 | { 166 | #ifdef VERBOSE 167 | fprintf(pTrace, "sensor type = %d, ", type); 168 | #endif 169 | 170 | if (type == DHT11) 171 | { 172 | #ifdef VERBOSE 173 | printf("temperature = %d, humidity = %d\n", data[2], data[0]); 174 | #endif 175 | temperature = data[2]; 176 | humidity = data[0]; 177 | } 178 | else if (type == DHT22) 179 | { 180 | float f, h; 181 | h = data[0] * 256 + data[1]; 182 | h /= 10.0; 183 | 184 | f = (data[2] & 0x7F) * 256 + data[3]; 185 | f /= 10.0; 186 | if (data[2] & 0x80) f *= -1; 187 | 188 | #ifdef VERBOSE 189 | fprintf(pTrace, "temperature = %.1f, humidity = %.1f\n", f, h); 190 | #endif 191 | temperature = f; 192 | humidity = h; 193 | } 194 | else 195 | { 196 | #ifdef VERBOSE 197 | fprintf(pTrace, "*** unknown sensor data type\n"); 198 | #ifdef DBG_CONSOLE 199 | fflush(pTrace); 200 | #else 201 | fclose(pTrace); 202 | #endif 203 | #endif 204 | return -2; 205 | } 206 | } 207 | else 208 | { 209 | #ifdef VERBOSE 210 | fprintf(pTrace, "*** unexpected data: bits=%d: %d != %d + %d + %d + %d\n", 211 | bitCount, data[4], data[0], data[1], data[2], data[3]); 212 | #ifdef DBG_CONSOLE 213 | fflush(pTrace); 214 | #else 215 | fclose(pTrace); 216 | #endif 217 | #endif 218 | return -1; 219 | } 220 | 221 | #ifdef VERBOSE 222 | fprintf(pTrace, "*** obtained readout successfully.\n"); 223 | #ifdef DBG_CONSOLE 224 | fflush(pTrace); 225 | #else 226 | fclose(pTrace); 227 | #endif 228 | #endif 229 | 230 | // update last readout 231 | last_read[pin] = now; 232 | last_temperature[pin] = temperature; 233 | last_humidity[pin] = humidity; 234 | 235 | return 0; 236 | } 237 | 238 | int initialize() 239 | { 240 | if (gpioInitialize()) 241 | { 242 | initialized = 1; 243 | memset(last_read, 0, sizeof(unsigned long long)*32); 244 | memset(last_temperature, 0, sizeof(float)*32); 245 | memset(last_humidity, 0, sizeof(float)*32); 246 | return 0; 247 | } 248 | 249 | return 1; 250 | } 251 | -------------------------------------------------------------------------------- /src/dht-sensor.h: -------------------------------------------------------------------------------- 1 | #ifndef _DHT_H 2 | #define _DHT_H 3 | 4 | int initialize(); 5 | unsigned long long getTime(); 6 | long readDHT(int type, int pin, float &temperature, float &humidity); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/node-dht-sensor.cpp: -------------------------------------------------------------------------------- 1 | #define NODE_ADDON_API_DISABLE_DEPRECATED 2 | 3 | #include 4 | #include 5 | #include 6 | #include "dht-sensor.h" 7 | #include "util.h" 8 | 9 | extern int initialized; 10 | extern unsigned long long last_read[32]; 11 | extern float last_temperature[32]; 12 | extern float last_humidity[32]; 13 | 14 | std::mutex sensorMutex; 15 | 16 | int _gpio_pin = 4; 17 | int _sensor_type = 11; 18 | int _max_retries = 3; 19 | bool _test_fake_enabled = false; 20 | float _fake_temperature = 0; 21 | float _fake_humidity = 0; 22 | 23 | long readSensor(int type, int pin, float &temperature, float &humidity) 24 | { 25 | if (_test_fake_enabled) 26 | { 27 | temperature = _fake_temperature; 28 | humidity = _fake_humidity; 29 | return 0; 30 | } 31 | else 32 | { 33 | return readDHT(type, pin, temperature, humidity); 34 | } 35 | } 36 | 37 | class ReadWorker : public Napi::AsyncWorker 38 | { 39 | public: 40 | ReadWorker(Napi::Function &callback, int sensor_type, int gpio_pin) 41 | : Napi::AsyncWorker(callback), sensor_type(sensor_type), 42 | gpio_pin(gpio_pin) {} 43 | 44 | void Execute() override 45 | { 46 | sensorMutex.lock(); 47 | 48 | if (Init()) { 49 | Read(); 50 | } else { 51 | SetError("failed to initialize sensor"); 52 | } 53 | 54 | sensorMutex.unlock(); 55 | } 56 | 57 | void OnOK() override 58 | { 59 | Napi::Env env = Env(); 60 | 61 | if (!initialized) 62 | { 63 | SetError("failed to initialize sensor"); 64 | } 65 | else if (failed) 66 | { 67 | SetError("failed to read sensor"); 68 | } 69 | else 70 | { 71 | Callback().Call({env.Null(), 72 | Napi::Number::New(env, temperature), 73 | Napi::Number::New(env, humidity)}); 74 | } 75 | } 76 | 77 | private: 78 | int sensor_type; 79 | int gpio_pin; 80 | bool failed = false; 81 | float temperature = 0; 82 | float humidity = 0; 83 | 84 | bool Init() 85 | { 86 | if (!initialized) 87 | { 88 | initialized = initialize() == 0; 89 | } 90 | 91 | return initialized; 92 | } 93 | 94 | void Read() 95 | { 96 | if (sensor_type != 11 && sensor_type != 22) 97 | { 98 | SetError("sensor type is invalid"); 99 | return; 100 | } 101 | 102 | temperature = last_temperature[gpio_pin], 103 | humidity = last_humidity[gpio_pin]; 104 | int retry = _max_retries; 105 | int result = 0; 106 | while (true) 107 | { 108 | result = readSensor(sensor_type, gpio_pin, temperature, humidity); 109 | if (result == 0 || --retry < 0) 110 | break; 111 | usleep(450000); 112 | } 113 | failed = result != 0; 114 | if (failed) { 115 | SetError("failed to read sensor"); 116 | } 117 | } 118 | }; 119 | 120 | Napi::Value ReadAsync(const Napi::CallbackInfo &info) 121 | { 122 | int sensor_type = info[0].ToNumber().Uint32Value(); 123 | int gpio_pin = info[1].ToNumber().Uint32Value(); 124 | auto callback = info[2].As(); 125 | 126 | auto worker = new ReadWorker(callback, sensor_type, gpio_pin); 127 | worker->Queue(); 128 | 129 | return info.Env().Undefined(); 130 | } 131 | 132 | Napi::Value ReadSync(const Napi::CallbackInfo &info) 133 | { 134 | Napi::Env env = info.Env(); 135 | 136 | int sensor_type; 137 | int gpio_pin; 138 | 139 | if (info.Length() == 2) 140 | { 141 | gpio_pin = info[1].ToNumber().Uint32Value(); 142 | // TODO: validate gpio_pin 143 | 144 | sensor_type = info[0].ToNumber().Uint32Value(); 145 | if (sensor_type != 11 && sensor_type != 22) 146 | { 147 | Napi::TypeError::New(env, "specified sensor type is invalid").ThrowAsJavaScriptException(); 148 | return env.Undefined(); 149 | } 150 | 151 | // initialization (on demand) 152 | if (!initialized) 153 | { 154 | initialized = initialize() == 0; 155 | if (!initialized) 156 | { 157 | Napi::TypeError::New(env, "failed to initialize").ThrowAsJavaScriptException(); 158 | return env.Undefined(); 159 | } 160 | } 161 | } 162 | else 163 | { 164 | sensor_type = _sensor_type; 165 | gpio_pin = _gpio_pin; 166 | } 167 | 168 | float temperature = last_temperature[gpio_pin], 169 | humidity = last_humidity[gpio_pin]; 170 | int retry = _max_retries; 171 | int result = 0; 172 | while (true) 173 | { 174 | result = readSensor(sensor_type, gpio_pin, temperature, humidity); 175 | if (result == 0 || --retry < 0) 176 | break; 177 | usleep(450000); 178 | } 179 | 180 | auto readout = Napi::Object::New(env); 181 | readout.Set("humidity", humidity); 182 | readout.Set("temperature", temperature); 183 | readout.Set("isValid", result == 0); 184 | readout.Set("errors", _max_retries - retry); 185 | 186 | return readout; 187 | } 188 | 189 | Napi::Value Read(const Napi::CallbackInfo &info) 190 | { 191 | Napi::Env env = info.Env(); 192 | 193 | switch (info.Length()) 194 | { 195 | case 0: // no parameters, use synchronous interface 196 | case 2: // sensor type and GPIO pin, use synchronous interface 197 | return ReadSync(info); 198 | case 3: 199 | // sensorType, gpioPin and callback, use asynchronous interface 200 | return ReadAsync(info); 201 | default: 202 | Napi::TypeError::New(env, "invalid number of arguments").ThrowAsJavaScriptException(); 203 | return env.Undefined(); 204 | } 205 | } 206 | 207 | void SetMaxRetries(const Napi::CallbackInfo &info) 208 | { 209 | if (info.Length() != 1) 210 | { 211 | Napi::TypeError::New(info.Env(), "Wrong number of arguments").ThrowAsJavaScriptException(); 212 | return; 213 | } 214 | _max_retries = info[0].ToNumber().Uint32Value(); 215 | } 216 | 217 | void LegacyInitialization(const Napi::CallbackInfo &info) 218 | { 219 | Napi::Env env = info.Env(); 220 | 221 | if (!info[0].IsNumber() || !info[1].IsNumber()) 222 | { 223 | Napi::TypeError::New(env, "Invalid arguments").ThrowAsJavaScriptException(); 224 | return; 225 | } 226 | 227 | int sensor_type = info[0].ToNumber().Uint32Value(); 228 | if (sensor_type != 11 && sensor_type != 22) 229 | { 230 | Napi::TypeError::New(env, "Specified sensor type is not supported").ThrowAsJavaScriptException(); 231 | return; 232 | } 233 | 234 | // update parameters 235 | _sensor_type = sensor_type; 236 | _gpio_pin = info[1].ToNumber().Uint32Value(); 237 | 238 | if (info.Length() >= 3) 239 | { 240 | if (!info[2].IsNumber()) 241 | { 242 | Napi::TypeError::New(env, "Invalid maxRetries parameter").ThrowAsJavaScriptException(); 243 | return; 244 | } 245 | else 246 | { 247 | _max_retries = info[2].ToNumber().Uint32Value(); 248 | } 249 | } 250 | } 251 | 252 | Napi::Value Initialize(const Napi::CallbackInfo &info) 253 | { 254 | Napi::Env env = info.Env(); 255 | 256 | if (info.Length() < 1) 257 | { 258 | Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException(); 259 | return env.Undefined(); 260 | } 261 | else if (info.Length() > 1) 262 | { 263 | LegacyInitialization(info); 264 | return Napi::Boolean::New(env, initialized || initialize() == 0); 265 | } 266 | else if (!info[0].IsObject()) 267 | { 268 | Napi::TypeError::New(env, "Invalid argument: an object is expected").ThrowAsJavaScriptException(); 269 | return env.Undefined(); 270 | } 271 | 272 | Napi::Object options = info[0].ToObject(); 273 | 274 | const auto KEY_TEST = "test"; 275 | const auto KEY_FAKE = "fake"; 276 | const auto KEY_TEMP = "temperature"; 277 | const auto KEY_HUMIDITY = "humidity"; 278 | 279 | Napi::Value testKeyValue = objectGetDefined(options, KEY_TEST); 280 | if (!testKeyValue.IsEmpty()) 281 | { 282 | Napi::Object testKey = testKeyValue.ToObject(); 283 | initialized = 1; 284 | 285 | Napi::Value fakeKeyValue = objectGetDefined(testKey, KEY_FAKE); 286 | if (fakeKeyValue.IsEmpty()) 287 | { 288 | Napi::TypeError::New(env, "Invalid argument: 'options.test.fake' is missing or is not an object").ThrowAsJavaScriptException(); 289 | return env.Undefined(); 290 | } 291 | 292 | Napi::Object fakeKey = fakeKeyValue.ToObject(); 293 | _test_fake_enabled = true; 294 | 295 | if (fakeKey.Has(KEY_TEMP)) 296 | { 297 | Napi::Value temp = fakeKey.Get(KEY_TEMP); 298 | if (!temp.IsNumber()) 299 | { 300 | Napi::TypeError::New(env, "Invalid argument: 'options.test.fake.temperature' must be a number") 301 | .ThrowAsJavaScriptException(); 302 | return env.Undefined(); 303 | } 304 | 305 | _fake_temperature = temp.As().FloatValue(); 306 | } 307 | else 308 | { 309 | Napi::Error::New(env, "Test mode: temperature value must be defined for a fake").ThrowAsJavaScriptException(); 310 | return env.Undefined(); 311 | } 312 | 313 | if (fakeKey.Has(KEY_HUMIDITY)) 314 | { 315 | Napi::Value humidity = fakeKey.Get(KEY_HUMIDITY); 316 | if (!humidity.IsNumber()) 317 | { 318 | Napi::TypeError::New(env, "Invalid argument: 'options.test.fake.humidity' must be a number") 319 | .ThrowAsJavaScriptException(); 320 | return env.Undefined(); 321 | } 322 | 323 | _fake_humidity = humidity.As().FloatValue(); 324 | } 325 | else 326 | { 327 | Napi::Error::New(env, "Test mode: humidity value must be defined for a fake").ThrowAsJavaScriptException(); 328 | return env.Undefined(); 329 | } 330 | } 331 | 332 | return env.Undefined(); 333 | } 334 | 335 | Napi::Object Init(Napi::Env env, Napi::Object exports) 336 | { 337 | exports.Set("read", Napi::Function::New(env, Read)); 338 | exports.Set("initialize", Napi::Function::New(env, Initialize)); 339 | exports.Set("setMaxRetries", Napi::Function::New(env, SetMaxRetries)); 340 | return exports; 341 | } 342 | 343 | NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init); 344 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | Napi::Value objectGetDefined(Napi::Object object, const std::string &key) 4 | { 5 | if (!object.Has(key)) 6 | { 7 | return Napi::Value(); 8 | } 9 | 10 | Napi::Value value = object.Get(key); 11 | if (value.IsNull() || value.IsUndefined()) 12 | { 13 | return Napi::Value(); 14 | } 15 | 16 | return value; 17 | } 18 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTIL_H 2 | #define _UTIL_H 3 | 4 | #include 5 | #include 6 | 7 | // Return empty Napi::Value if `object` doesn't have `key` 8 | // or it is null or undefined. Otherwise return the value in `key`. 9 | Napi::Value objectGetDefined(Napi::Object object, const std::string &key); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /test/node-dht-sensor.js: -------------------------------------------------------------------------------- 1 | var assert = require("chai").assert; 2 | var sensor = require("../lib"); 3 | var psensor = require("../lib").promises; 4 | 5 | const SENSOR_TYPE = parseInt(process.env.SENSOR_TYPE || 11, 10); 6 | const GPIO_PIN = parseInt(process.env.GPIO_PIN || 4, 10); 7 | 8 | describe("Initialize", () => { 9 | describe("Initialize mock sensor", () => { 10 | it("should initialize to provide fake readouts", () => { 11 | sensor.initialize({ 12 | test: { 13 | fake: { 14 | temperature: 42, 15 | humidity: 72 16 | } 17 | } 18 | }); 19 | }); 20 | it("should fail when initializing fake readout without temperature value", () => { 21 | assert.throws( 22 | () => { 23 | sensor.initialize({ 24 | test: { 25 | fake: { 26 | humidity: 60 27 | } 28 | } 29 | }); 30 | }, 31 | Error, 32 | "Test mode: temperature value must be defined for a fake" 33 | ); 34 | }); 35 | it("should fail when initializing fake readout without humidity value", () => { 36 | assert.throws( 37 | () => { 38 | sensor.initialize({ 39 | test: { 40 | fake: { 41 | temperature: 17 42 | } 43 | } 44 | }); 45 | }, 46 | Error, 47 | "Test mode: humidity value must be defined for a fake" 48 | ); 49 | }); 50 | }); 51 | describe("Sensor type and GPIO pin", () => { 52 | it("should throw error if sensor type is not supported", () => { 53 | assert.throws( 54 | () => { 55 | sensor.initialize(0, 0); 56 | }, 57 | TypeError, 58 | "Specified sensor type is not supported" 59 | ); 60 | }); 61 | it("should initialize with valid sensor type and GPIO pin", () => { 62 | sensor.initialize(SENSOR_TYPE, GPIO_PIN); 63 | }); 64 | }); 65 | describe("With invalid arguments", () => { 66 | it("should fail when no arguments are provided", () => { 67 | assert.throws(sensor.initialize, TypeError, "Wrong number of arguments"); 68 | }); 69 | it("should fail when sensor type is not numeric", () => { 70 | assert.throws( 71 | () => { 72 | sensor.initialize("11", 0); 73 | }, 74 | TypeError, 75 | "Invalid arguments" 76 | ); 77 | }); 78 | it("should fail when GPIO pin is not numeric", () => { 79 | assert.throws( 80 | () => { 81 | sensor.initialize(SENSOR_TYPE, "4"); 82 | }, 83 | TypeError, 84 | "Invalid arguments" 85 | ); 86 | }); 87 | it("should fail when maxRetries is not numeric", () => { 88 | assert.throws( 89 | () => { 90 | sensor.initialize(SENSOR_TYPE, GPIO_PIN, "abc"); 91 | }, 92 | TypeError, 93 | "Invalid maxRetries parameter" 94 | ); 95 | }); 96 | }); 97 | }); 98 | 99 | describe("Set max retries", () => { 100 | it("should fail if an argument is not provided", () => { 101 | assert.throws(sensor.setMaxRetries, TypeError, "Wrong number of arguments"); 102 | }); 103 | it("should set max retries", () => { 104 | sensor.setMaxRetries(5); 105 | }); 106 | }); 107 | 108 | describe("Read sensor", () => { 109 | describe("Synchronously", () => { 110 | it("should return a readout when no parameter is provided", () => { 111 | sensor.initialize(SENSOR_TYPE, GPIO_PIN); 112 | var readout = sensor.read(); 113 | 114 | assert.isObject(readout); 115 | assert.hasAllKeys(readout, [ 116 | "temperature", 117 | "humidity", 118 | "isValid", 119 | "errors" 120 | ]); 121 | }); 122 | it("should return a readout when sensor type and GPIO pin are provided", () => { 123 | var readout = sensor.read(SENSOR_TYPE, GPIO_PIN); 124 | assert.isObject(readout); 125 | assert.hasAllKeys(readout, [ 126 | "temperature", 127 | "humidity", 128 | "isValid", 129 | "errors" 130 | ]); 131 | }); 132 | it("should fail when invalid sensor type is specified", () => { 133 | assert.throws( 134 | () => { 135 | sensor.read(3, GPIO_PIN); 136 | }, 137 | TypeError, 138 | "specified sensor type is invalid" 139 | ); 140 | }); 141 | }); 142 | describe("Asynchronously", () => { 143 | it("should obtain temperature and humidity", done => { 144 | sensor.read(SENSOR_TYPE, GPIO_PIN, (err, temperature, humidity) => { 145 | assert.notExists(err); 146 | assert.isNumber(temperature); 147 | assert.isNumber(humidity); 148 | done(); 149 | }); 150 | }); 151 | it("should fail when invalid sensor type is specified", done => { 152 | sensor.read(3, GPIO_PIN, err => { 153 | assert.exists(err); 154 | assert.throws( 155 | () => { 156 | assert.ifError(err, "sensor type is invalid"); 157 | }, 158 | Error, 159 | "sensor type is invalid" 160 | ); 161 | done(); 162 | }); 163 | }); 164 | }); 165 | describe("Asynchronously (promise)", () => { 166 | it("should obtain temperature and humidity", async () => { 167 | const { temperature, humidity } = await psensor.read( 168 | SENSOR_TYPE, 169 | GPIO_PIN 170 | ); 171 | assert.isNumber(temperature); 172 | assert.isNumber(humidity); 173 | }); 174 | it("should fail when invalid sensor type is specified", async () => { 175 | try { 176 | await psensor.read(3, GPIO_PIN); 177 | assert.fail("should have failed"); 178 | } catch (err) { 179 | assert.throws( 180 | () => { 181 | assert.ifError(err, "sensor type is invalid"); 182 | }, 183 | Error, 184 | "sensor type is invalid" 185 | ); 186 | } 187 | }); 188 | }); 189 | describe("With invalid arguments", () => { 190 | it("should fail if too many arguments are provided", () => { 191 | assert.throws( 192 | () => { 193 | sensor.read(1, 2, 3, 4); 194 | }, 195 | TypeError, 196 | "invalid number of arguments" 197 | ); 198 | }); 199 | }); 200 | }); 201 | --------------------------------------------------------------------------------