UNPKG

rpio

Version:

High performance GPIO/i2c/PWM/SPI module for Raspberry Pi, Orange Pi, Banana Pi

218 lines (194 loc) 6.63 kB
var rpio = require('../lib/rpio'); /* * Repeatedly read a DHT11 attached to Pin 7 / GPIO 4 and print out the current * temperature and relative humidity if the read was successful. */ var pin = 7; /* * Print debug messages if enabled. */ var debug = false; function dbg() { if (debug) { console.error.apply(console, arguments); } } /* * The DHT11 transmission is made up of 40 "bits", where the length of each * high output determines whether it is a 0 or 1. Per the datasheet, "0" is * 26-28us in length, and "1" 70us. However, in a JS environment we can't be * so precise, and so we determine what is high and low based on a sampling of * the values we received. * * These devices are notoriously unreliable, so this program will run forever * attempting to retrieve and print valid data until the user kills the process. * * For the most portable implementation we read all bits after initialisation * into a big buffer as fast as possible, and then parse afterwards. This is * necessary on e.g. the original Raspberry Pi 1 as the CPU is not fast enough * to do something like: * * while ((curstate = rpio.read(...)) == laststate) * count++; * * for each read, which would be the normal way of doing something like this. */ function read_dht11(vals) { /* * Our read buffer of all the bits sent. Should be plenty. On a * Raspberry Pi 4 a successful read uses up about half of this. */ var buf = new Buffer(50000); /* * Array storing the data bits received from the DHT11 after parsing * the input buffer. */ var data = new Array(); /* * The initialisation sequence as per the datasheet is to start high, * pull low for at least 18ms, then back high and read. The JavaScript * function call overhead ensures that we'll actually be sleeping * longer than that, but it doesn't appear to be a problem. */ rpio.open(pin, rpio.OUTPUT, rpio.HIGH); rpio.write(pin, rpio.LOW); rpio.msleep(18); /* * Enable the pin pull-up then use the mode-setting variant of readbuf() * to read the pin value into the buffer as fast as possible. We only * have around 100us before the DHT11 will start transmitting data so * there needs to be no delay between configuring the pin as an input and * reading the data. * * Configuring the pin for input, enabling the pull-up, then reading the * data would take way too long. Apart from the JavaScript function call * overhead, just enabling the pull-up can require a 150us delay on some * hardware. */ rpio.pud(pin, rpio.PULL_UP); rpio.readbuf(pin, buf, buf.length, true); rpio.close(pin); /* * The data has been received, split the buffer into groups of "1"s. By * measuring the length of each group we can determine whether it is a * low (short) or high (long) bit sent by the DHT11. */ buf.join('').replace(/0+/g, '0').split('0').forEach(function(bits, n) { data.push(bits.length); }); /* * In normal operation we'd expect to see 43 values: the initial high of * the pull-up, a ready signal, 40 bits of data, then a continuous high * to signal end of transmission. Use shift() and pop() to remove the * control bits, leaving just the data. * * In certain circumstances the first bit can be missing, if we didn't * switch to read mode fast enough. However as that's just the high * value configured by the pull-up and not data from the DHT11 we allow * it to be missing. Everything else, even with a correct checksum (can * happen!) we treat as invalid. */ if (data.length < 42 || data.length > 43) { dbg("Bad data read: length=%d", data.length); return false; } /* Remove extra first bit generated by the pull-up */ if (data.length == 43) { data.shift(); } /* Remove data ready bit */ data.shift(); /* Remove end of transmission bit */ data.pop(); /* * Calculate the low and high water marks. As each model of Raspberry * Pi will run at different speeds, the length of each high bit will * vary, so calculate the average and use that to determine what is * "high" and "low". * * The longest "low" seen on a Raspberry Pi 4 is around 135, so the * default low here should be more than sufficient. */ var low = 10000; var high = 0; for (var i = 0; i < data.length; i++) { if (data[i] < low) low = data[i]; if (data[i] > high) high = data[i]; } var avg = (low + high) / 2; /* * The data received from the DHT11 is in 5 groups of 8-bits: * * [0:7] integral relative humidity * [8:15] decimal relative humidity * [16:23] integral temperature * [24:31] decimal temperature * [32:39] checksum * * Parse the bitstream into the supplied "vals" buffer. */ vals.fill(0); for (var i = 0; i < data.length; i++) { var group = parseInt(i/8) /* The data is in big-endian format, shift it in. */ vals[group] <<= 1; /* This should be a high bit, based on the average. */ if (data[i] >= avg) vals[group] |= 1; } /* * Occasionally the final checksum test can be valid even when the * values are clearly bogus. Perform additional sanity checks to * ensure they are within the limitations of the DHT11. */ /* Relative humidity range is 20 - 90% */ if (vals[0] < 20 || vals[0] > 90) { dbg("Bad humidity: %d%%", vals[0]); return false; } /* Temperature range is 0 - 50C */ if (vals[2] > 50) { dbg("Bad temperature: %d", vals[0]); return false; } /* * Validate the checksum and return whether successful or not. The * checksum is simply the value of the other 4 groups combined. * * In theory the total should be masked off to 8-bits, but in testing * this has occasionally resulted in obviously bogus data passing the * test. It's unlikely that valid data will total >255 anyway given * the limitations of the DHT11 and the lack of decimal precision in * many implementations. */ if ((vals[0] + vals[1] + vals[2] + vals[3]) != vals[4]) { dbg("Bad checksum: %d:%d:%d:%d %d", v[0], v[1], v[2], v[3], v[4]); return false; } return true; } /* * Run until the user kills the process, skipping any bad reads. */ var v = Buffer(5); while (true) { /* * On some DHT11 the fractional parts are always 0, but print them out * anyway as some models appear to at least support temperatures. * * On a successful read wait 5 seconds before the next sample. On a * failed read, wait 1 second, which is the minimum time recommended * by the datasheet between samples. */ if (read_dht11(v)) { console.log("Temperature = %d.%dC, Humidity = %d.%d%%", v[2], v[3], v[0], v[1]); rpio.sleep(5); } else { rpio.sleep(1); } }