├── .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 | 
6 | [](https://www.npmjs.com/package/node-dht-sensor)
7 | [](https://www.npmjs.com/package/node-dht-sensor)
8 | [](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 | 
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 | 
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 |
--------------------------------------------------------------------------------