Browse Source

Add dht module.

devsaurus 7 years ago
parent
commit
927ee7fc5f

+ 6 - 0
components/modules/Kconfig

@@ -60,6 +60,12 @@ config LUA_MODULE_BTHCI
   help
       Includes the simple BlueTooth HCI module.
 
+config LUA_MODULE_DHT
+  bool "DHT11/21/22/AM2301/AM2302 module"
+  default "n"
+  help
+      Includes the dht module.
+
 config LUA_MODULE_ENCODER
   bool "Encoder module"
   default "n"

+ 102 - 0
components/modules/dht.c

@@ -0,0 +1,102 @@
+
+
+#include "module.h"
+#include "lauxlib.h"
+#include <stdint.h>
+
+#include "platform.h"
+
+
+typedef enum {
+  LDHT_OK = 0,
+  LDHT_ERROR_CHECKSUM = -1,
+  LDHT_ERROR_TIMEOUT = -2,
+  LDHT_INVALID_VALUE = -999
+} ldht_result_t;
+
+typedef enum {
+  LDHT11,
+  LDHT2X
+} ldht_type_t;
+
+static int ldht_compute_data11( uint8_t *data, double *temp, double *humi )
+{
+  *humi = data[0];
+  *temp = data[2];
+
+  uint8_t sum = data[0] + data[1] + data[2] + data[3];
+  return sum == data[4] ? LDHT_OK : LDHT_ERROR_CHECKSUM;
+}
+
+static int ldht_compute_data2x( uint8_t *data, double *temp, double *humi )
+{
+  *humi = (data[0] * 256 + data[1]) * 0.1;
+  *temp = ((data[2] & 0x7f) * 256 + data[3]) * 0.1;
+
+  if (data[2] & 0x80)
+    *temp = - *temp;
+
+  uint8_t sum = data[0] + data[1] + data[2] + data[3];
+  return sum == data[4] ? LDHT_OK : LDHT_ERROR_CHECKSUM;
+}
+
+static int ldht_read_generic( lua_State *L, ldht_type_t type )
+{
+  int stack = 0;
+
+  int gpio = luaL_checkint( L, ++stack );
+  luaL_argcheck( L, platform_gpio_output_exists( gpio ), stack, "invalid gpio" );
+
+  uint8_t data[5];
+  if (platform_dht_read( gpio,
+			 type == LDHT11 ? PLATFORM_DHT11_WAKEUP_MS : PLATFORM_DHT2X_WAKEUP_MS,
+			 data ) != PLATFORM_OK) {
+    lua_pushinteger( L, LDHT_ERROR_TIMEOUT );
+    return 1;
+  }
+
+  double temp, humi;
+  int res;
+  switch (type) {
+  case LDHT11:
+    res = ldht_compute_data11( data, &temp, &humi );
+    break;
+  case LDHT2X:
+    res = ldht_compute_data2x( data, &temp, &humi );
+    break;
+  default:
+    res = LDHT_INVALID_VALUE;
+    break;
+  }
+  lua_pushinteger( L, res );
+  lua_pushnumber( L, temp );
+  lua_pushnumber( L, humi );
+  int tempdec = (int)((temp - (int)temp) * 1000);
+  int humidec = (int)((humi - (int)humi) * 1000);
+  lua_pushinteger( L, tempdec );
+  lua_pushinteger( L, humidec );
+
+  return 5;
+}
+
+static int ldht_read11( lua_State *L )
+{
+  return ldht_read_generic( L, LDHT11 );
+}
+
+static int ldht_read2x( lua_State *L )
+{
+  return ldht_read_generic( L, LDHT2X );
+}
+
+
+static const LUA_REG_TYPE dht_map[] = {
+  { LSTRKEY( "read11" ),         LFUNCVAL( ldht_read11 ) },
+  { LSTRKEY( "read2x" ),         LFUNCVAL( ldht_read2x ) },
+  { LSTRKEY( "OK" ),             LNUMVAL( LDHT_OK ) },
+  { LSTRKEY( "ERROR_CHECKSUM" ), LNUMVAL( LDHT_ERROR_CHECKSUM ) },
+  { LSTRKEY( "ERROR_TIMEOUT" ),  LNUMVAL( LDHT_ERROR_TIMEOUT ) },
+  { LNILKEY, LNILVAL }
+};
+
+NODEMCU_MODULE(DHT, "dht", dht_map, NULL);

+ 205 - 0
components/platform/dht.c

@@ -0,0 +1,205 @@
+
+/* ****************************************************************************
+ *
+ * ESP32 platform interface for DHT temperature & humidity sensors
+ *
+ * Copyright (c) 2017, Arnim Laeuger
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ****************************************************************************/
+
+#include "platform.h"
+
+#include "driver/rmt.h"
+#include "driver/gpio.h"
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "esp_log.h"
+
+
+#undef DHT_DEBUG
+
+// RX idle threshold [us]
+// needs to be larger than any duration occurring during bit slots
+// datasheet specs up to 200us for "Bus master has released time"
+#define DHT_DURATION_RX_IDLE (250)
+
+// zero bit duration threshold [us]
+// a high phase
+// * shorter than this is detected as a zero bit
+// * longer than this is detected as a one bit
+#define DHT_DURATION_ZERO (50)
+
+
+// grouped information for RMT management
+static struct {
+  int channel;
+  RingbufHandle_t rb;
+  int gpio;
+} dht_rmt = {-1, NULL, -1};
+
+
+static void dht_deinit( void )
+{
+  // drive idle level 1
+  gpio_set_level( dht_rmt.gpio, 1 );
+
+  rmt_rx_stop( dht_rmt.channel );
+  rmt_driver_uninstall( dht_rmt.channel );
+
+  platform_rmt_release( dht_rmt.channel );
+
+  // invalidate channel and gpio assignments
+  dht_rmt.channel = -1;
+  dht_rmt.gpio = -1;
+}
+
+static int dht_init( uint8_t gpio_num )
+{
+  // acquire an RMT module for RX
+  if ((dht_rmt.channel = platform_rmt_allocate( 1 )) >= 0) {
+
+#ifdef DHT_DEBUG
+    ESP_LOGI("dht", "RMT RX channel: %d", dht_rmt.channel);
+#endif
+
+    rmt_config_t rmt_rx;
+    rmt_rx.channel = dht_rmt.channel;
+    rmt_rx.gpio_num = gpio_num;
+    rmt_rx.clk_div = 80;  // base period is 1us
+    rmt_rx.mem_block_num = 1;
+    rmt_rx.rmt_mode = RMT_MODE_RX;
+    rmt_rx.rx_config.filter_en = true;
+    rmt_rx.rx_config.filter_ticks_thresh = 30;
+    rmt_rx.rx_config.idle_threshold = DHT_DURATION_RX_IDLE;
+    if (rmt_config( &rmt_rx ) == ESP_OK) {
+      if (rmt_driver_install( rmt_rx.channel, 512, 0 ) == ESP_OK) {
+
+        rmt_get_ringbuf_handler( dht_rmt.channel, &dht_rmt.rb );
+
+        dht_rmt.gpio = gpio_num;
+
+        // use gpio for TX direction
+        // drive idle level 1
+        gpio_set_level( gpio_num, 1 );
+        gpio_pullup_dis( gpio_num );
+        gpio_pulldown_dis( gpio_num );
+        gpio_set_direction( gpio_num, GPIO_MODE_INPUT_OUTPUT_OD );
+        gpio_set_intr_type( gpio_num, GPIO_INTR_DISABLE );
+
+        return PLATFORM_OK;
+
+      }
+    }
+
+    platform_rmt_release( dht_rmt.channel );
+  }
+
+  return PLATFORM_ERR;
+}
+
+
+int platform_dht_read( uint8_t gpio_num, uint8_t wakeup_ms, uint8_t *data )
+{
+  if (dht_init( gpio_num ) != PLATFORM_OK)
+    return PLATFORM_ERR;
+
+  // send start signal and arm RX channel
+  TickType_t xDelay = pdMS_TO_TICKS( wakeup_ms ) > 0 ? pdMS_TO_TICKS( wakeup_ms ) : 1;
+  gpio_set_level( gpio_num, 0 );         // pull wire low
+  vTaskDelay( xDelay );                  // time low phase
+  rmt_rx_start( dht_rmt.channel, true ); // arm RX channel
+  gpio_set_level( gpio_num, 1 );         // release wire
+
+  // wait for incoming bit stream
+  size_t rx_size;
+  rmt_item32_t* rx_items = (rmt_item32_t *)xRingbufferReceive( dht_rmt.rb, &rx_size, pdMS_TO_TICKS( 100 ) );
+
+  // default is "no error"
+  // error conditions have to overwrite this with PLATFORM_ERR
+  int res = PLATFORM_OK;
+
+  if (rx_items) {
+
+#ifdef DHT_DEBUG
+    ESP_LOGI("dht", "rx_items received: %d", rx_size);
+    for (size_t i = 0; i < rx_size / 4; i++) {
+      ESP_LOGI("dht", "level: %d, duration %d", rx_items[i].level0, rx_items[i].duration0);
+      ESP_LOGI("dht", "level: %d, duration %d", rx_items[i].level1, rx_items[i].duration1);
+    }
+#endif
+
+    // we expect 40 bits of payload plus a response bit and two edges for start and stop signals
+    // each bit on the wire consumes 2 rmt samples (and 2 rmt samples stretch over 4 bytes)
+    if (rx_size >= (5*8 + 1+1) * 4) {
+
+      // check framing
+      if (rx_items[ 0].level0 == 1 &&                            // rising edge of the start signal
+          rx_items[ 0].level1 == 0 && rx_items[1].level0 == 1 && // response signal
+          rx_items[41].level1 == 0) {                            // falling edge of stop signal
+
+        // run through the bytes
+        for (size_t byte = 0; byte < 5 && res == PLATFORM_OK; byte++) {
+          size_t bit_pos = 1 + byte*8;
+          data[byte] = 0;
+
+          // decode the bits inside a byte
+          for (size_t bit = 0; bit < 8; bit++, bit_pos++) {
+            if (rx_items[bit_pos].level1 != 0) {
+              // not a falling edge, terminate decoding
+              res = PLATFORM_ERR;
+              break;
+            }
+            // ignore duration of low level
+
+            // data is sent MSB first
+            data[byte] <<= 1;
+            if (rx_items[bit_pos + 1].level0 == 1 && rx_items[bit_pos + 1].duration0 > DHT_DURATION_ZERO)
+              data[byte] |= 1;
+          }
+
+#ifdef DHT_DEBUG
+          ESP_LOGI("dht", "data 0x%02x", data[byte]);
+#endif
+        }
+
+        // all done
+
+      } else {
+        // framing mismatch on start, response, or stop signals
+        res = PLATFORM_ERR;
+      }
+
+    } else {
+      // too few bits received
+      res = PLATFORM_ERR;
+    }
+
+    vRingbufferReturnItem( dht_rmt.rb, (void *)rx_items );
+  } else {
+    // time out occurred, this indicates an unconnected / misconfigured bus
+    res = PLATFORM_ERR;
+  }
+
+  dht_deinit();
+
+  return res;
+}

+ 8 - 0
components/platform/include/platform.h

@@ -164,6 +164,14 @@ uint8_t platform_onewire_crc8( const uint8_t *addr, uint8_t len );
 bool platform_onewire_check_crc16( const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc );
 uint16_t platform_onewire_crc16( const uint8_t* input, uint16_t len, uint16_t crc );
 
+// *****************************************************************************
+// DHT platform interface
+#define PLATFORM_DHT11_WAKEUP_MS 20
+#define PLATFORM_DHT2X_WAKEUP_MS 1
+
+int platform_dht_read( uint8_t gpio_num, uint8_t wakeup_ms, uint8_t *data );
+
+
 // Internal flash erase/write functions
 
 uint32_t platform_flash_get_sector_of_address( uint32_t addr );

+ 82 - 0
docs/en/modules/dht.md

@@ -0,0 +1,82 @@
+# DHT Module
+| Since  | Origin / Contributor  | Maintainer  | Source  |
+| :----- | :-------------------- | :---------- | :------ |
+| 2017-03-30 | [Arnim Läuger](https://github.com/devsaurus) | [Arnim Läuger](https://github.com/devsaurus) | [dht](../../../components/modules/dht.c)|
+
+## Constants
+Constants for various functions.
+
+`dht.OK`, `dht.ERROR_CHECKSUM`, `dht.ERROR_TIMEOUT` represent the potential values for the DHT read status
+
+## dht.read11()
+Read DHT11 humidity temperature combo sensor.
+
+#### Syntax
+`dht.read11(pin)`
+
+#### Parameters
+`pin` 0~33, IO index
+
+#### Returns
+- `status` as defined in Constants
+- `temp` temperature
+- `humi` humidity
+- `temp_dec` temperature decimal (always 0)
+- `humi_dec` humidity decimal (always 0)
+
+#### Example
+```lua
+pin = 4
+status, temp, humi = dht.read11(pin)
+if status == dht.OK then
+    print("DHT Temperature:"..temp..";".."Humidity:"..humi)
+
+elseif status == dht.ERROR_CHECKSUM then
+    print( "DHT Checksum error." )
+elseif status == dht.ERROR_TIMEOUT then
+    print( "DHT timed out." )
+end
+```
+
+## dht.read2x()
+Read DHT21/22/33/43 and AM2301/2302/2303 humidity temperature combo sensors.
+
+#### Syntax
+`dht.read2x(pin)`
+
+#### Parameters
+`pin` 0~33, IO index
+
+#### Returns
+- `status` as defined in Constants
+- `temp` temperature (see note below)
+- `humi` humidity (see note below)
+- `temp_dec` temperature decimal
+- `humi_dec` humidity decimal
+
+!!! note
+
+    If using float firmware then `temp` and `humi` are floating point numbers. On an integer firmware, the final values have to be concatenated from `temp` and `temp_dec` / `humi` and `hum_dec`.
+
+#### Example
+```lua
+pin = 4
+status, temp, humi, temp_dec, humi_dec = dht.read2x(pin)
+if status == dht.OK then
+    -- Integer firmware using this example
+    print(string.format("DHT Temperature:%d.%03d;Humidity:%d.%03d\r\n",
+          math.floor(temp),
+          temp_dec,
+          math.floor(humi),
+          humi_dec
+    ))
+
+    -- Float firmware using this example
+    print("DHT Temperature:"..temp..";".."Humidity:"..humi)
+
+elseif status == dht.ERROR_CHECKSUM then
+    print( "DHT Checksum error." )
+elseif status == dht.ERROR_TIMEOUT then
+    print( "DHT timed out." )
+end
+```

+ 0 - 7
lua_modules/dht_lib/README.md

@@ -1,7 +0,0 @@
-Support for this Lua module has been discontinued.
-
-Equivalent functionality is available from the dht module in the NodeMCU
-firmware code base. Refer to `docs/en/modules/dht.md` for API
-documentation.
-
-The original Lua code can be found in the [git repository](https://github.com/nodemcu/nodemcu-firmware/tree/2fbd5ed509964a16057b22e00aa8469d6a522d73/lua_modules/dht_lib).

+ 1 - 0
mkdocs.yml

@@ -33,6 +33,7 @@ pages:
     - Modules:
         - 'bit':         'en/modules/bit.md'
         - 'bthci':       'en/modules/bthci.md'
+        - 'dht':         'en/modules/dht.md'
         - 'file':        'en/modules/file.md'
         - 'i2c':         'en/modules/i2c.md'
         - 'node':        'en/modules/node.md'