Browse Source

added support for ads1015 (#2231)

* ads1015 is supported, up to 4 devices can be connected at the same time

* removed debug, updated documentation

* changed to oop API

* added __gc to handle active timer cleanup

* reworked argument validation and error reporting

* stack is no longer messed up after __del
Pawel Jasinski 6 years ago
parent
commit
f87d68ff8f
2 changed files with 638 additions and 442 deletions
  1. 514 391
      app/modules/ads1115.c
  2. 124 51
      docs/en/modules/ads1115.md

+ 514 - 391
app/modules/ads1115.c

@@ -8,441 +8,564 @@
 #include "lauxlib.h"
 #include "platform.h"
 #include "osapi.h"
+#include "c_stdlib.h"
+
+
+//***************************************************************************
+// CHIP
+//***************************************************************************
+#define ADS1115_ADS1015 ( 15)
+#define ADS1115_ADS1115 (115)
 
 //***************************************************************************
 // I2C ADDRESS DEFINITON
 //***************************************************************************
 
-#define ADS1115_I2C_ADDR_GND		(0x48)
-#define ADS1115_I2C_ADDR_VDD		(0x49)
-#define ADS1115_I2C_ADDR_SDA		(0x4A)
-#define ADS1115_I2C_ADDR_SCL		(0x4B)
+#define ADS1115_I2C_ADDR_GND        (0x48)
+#define ADS1115_I2C_ADDR_VDD        (0x49)
+#define ADS1115_I2C_ADDR_SDA        (0x4A)
+#define ADS1115_I2C_ADDR_SCL        (0x4B)
+#define IS_I2C_ADDR_VALID(addr) (((addr) & 0xFC) == 0x48)
 
 //***************************************************************************
 // POINTER REGISTER
 //***************************************************************************
 
-#define ADS1115_POINTER_MASK		(0x03)
-#define ADS1115_POINTER_CONVERSION	(0x00)
-#define ADS1115_POINTER_CONFIG		(0x01)
-#define ADS1115_POINTER_THRESH_LOW	(0x02)
-#define ADS1115_POINTER_THRESH_HI	(0x03)
+#define ADS1115_POINTER_MASK        (0x03)
+#define ADS1115_POINTER_CONVERSION  (0x00)
+#define ADS1115_POINTER_CONFIG      (0x01)
+#define ADS1115_POINTER_THRESH_LOW  (0x02)
+#define ADS1115_POINTER_THRESH_HI   (0x03)
 
 //***************************************************************************
 // CONFIG REGISTER
 //***************************************************************************
 
-#define ADS1115_OS_MASK				(0x8000)
-#define ADS1115_OS_NON				(0x0000)
-#define ADS1115_OS_SINGLE			(0x8000)	// Write: Set to start a single-conversion
-#define ADS1115_OS_BUSY				(0x0000)	// Read: Bit = 0 when conversion is in progress
-#define ADS1115_OS_NOTBUSY			(0x8000)	// Read: Bit = 1 when device is not performing a conversion
-
-#define ADS1115_MUX_MASK			(0x7000)
-#define ADS1115_MUX_DIFF_0_1		(0x0000)	// Differential P = AIN0, N = AIN1 (default)
-#define ADS1115_MUX_DIFF_0_3		(0x1000)	// Differential P = AIN0, N = AIN3
-#define ADS1115_MUX_DIFF_1_3		(0x2000)	// Differential P = AIN1, N = AIN3
-#define ADS1115_MUX_DIFF_2_3		(0x3000)	// Differential P = AIN2, N = AIN3
-#define ADS1115_MUX_SINGLE_0		(0x4000)	// Single-ended AIN0
-#define ADS1115_MUX_SINGLE_1		(0x5000)	// Single-ended AIN1
-#define ADS1115_MUX_SINGLE_2		(0x6000)	// Single-ended AIN2
-#define ADS1115_MUX_SINGLE_3		(0x7000)	// Single-ended AIN3
-
-#define ADS1115_PGA_MASK			(0x0E00)
-#define ADS1115_PGA_6_144V			(0x0000)	// +/-6.144V range = Gain 2/3
-#define ADS1115_PGA_4_096V			(0x0200)	// +/-4.096V range = Gain 1
-#define ADS1115_PGA_2_048V			(0x0400)	// +/-2.048V range = Gain 2 (default)
-#define ADS1115_PGA_1_024V			(0x0600)	// +/-1.024V range = Gain 4
-#define ADS1115_PGA_0_512V			(0x0800)	// +/-0.512V range = Gain 8
-#define ADS1115_PGA_0_256V			(0x0A00)	// +/-0.256V range = Gain 16
-
-#define ADS1115_MODE_MASK			(0x0100)
-#define ADS1115_MODE_CONTIN			(0x0000)	// Continuous conversion mode
-#define ADS1115_MODE_SINGLE			(0x0100)	// Power-down single-shot mode (default)
-
-#define ADS1115_DR_MASK				(0x00E0)
-#define ADS1115_DR_8SPS				(0x0000)	// 8 samples per second
-#define ADS1115_DR_16SPS			(0x0020)	// 16 samples per second
-#define ADS1115_DR_32SPS			(0x0040)	// 32 samples per second
-#define ADS1115_DR_64SPS			(0x0060)	// 64 samples per second
-#define ADS1115_DR_128SPS			(0x0080)	// 128 samples per second (default)
-#define ADS1115_DR_250SPS			(0x00A0)	// 250 samples per second
-#define ADS1115_DR_475SPS			(0x00C0)	// 475 samples per second
-#define ADS1115_DR_860SPS			(0x00E0)	// 860 samples per second
-
-#define ADS1115_CMODE_MASK			(0x0010)
-#define ADS1115_CMODE_TRAD			(0x0000)	// Traditional comparator with hysteresis (default)
-#define ADS1115_CMODE_WINDOW		(0x0010)	// Window comparator
-
-#define ADS1115_CPOL_MASK			(0x0008)
-#define ADS1115_CPOL_ACTVLOW		(0x0000)	// ALERT/RDY pin is low when active (default)
-#define ADS1115_CPOL_ACTVHI			(0x0008)	// ALERT/RDY pin is high when active
-
-#define ADS1115_CLAT_MASK			(0x0004)	// Determines if ALERT/RDY pin latches once asserted
-#define ADS1115_CLAT_NONLAT			(0x0000)	// Non-latching comparator (default)
-#define ADS1115_CLAT_LATCH			(0x0004)	// Latching comparator
-
-#define ADS1115_CQUE_MASK			(0x0003)
-#define ADS1115_CQUE_1CONV			(0x0000)	// Assert ALERT/RDY after one conversions
-#define ADS1115_CQUE_2CONV			(0x0001)	// Assert ALERT/RDY after two conversions
-#define ADS1115_CQUE_4CONV			(0x0002)	// Assert ALERT/RDY after four conversions
-#define ADS1115_CQUE_NONE			(0x0003)	// Disable the comparator and put ALERT/RDY in high state (default)
+#define ADS1115_OS_MASK             (0x8000)
+#define ADS1115_OS_NON              (0x0000)
+#define ADS1115_OS_SINGLE           (0x8000)    // Write: Set to start a single-conversion
+#define ADS1115_OS_BUSY             (0x0000)    // Read: Bit = 0 when conversion is in progress
+#define ADS1115_OS_NOTBUSY          (0x8000)    // Read: Bit = 1 when device is not performing a conversion
+
+#define ADS1115_MUX_MASK            (0x7000)
+#define ADS1115_MUX_DIFF_0_1        (0x0000)    // Differential P = AIN0, N = AIN1 (default)
+#define ADS1115_MUX_DIFF_0_3        (0x1000)    // Differential P = AIN0, N = AIN3
+#define ADS1115_MUX_DIFF_1_3        (0x2000)    // Differential P = AIN1, N = AIN3
+#define ADS1115_MUX_DIFF_2_3        (0x3000)    // Differential P = AIN2, N = AIN3
+#define ADS1115_MUX_SINGLE_0        (0x4000)    // Single-ended AIN0
+#define ADS1115_MUX_SINGLE_1        (0x5000)    // Single-ended AIN1
+#define ADS1115_MUX_SINGLE_2        (0x6000)    // Single-ended AIN2
+#define ADS1115_MUX_SINGLE_3        (0x7000)    // Single-ended AIN3
+#define IS_CHANNEL_VALID(channel) (((channel) & 0x8FFF) == 0)
+
+#define ADS1115_PGA_MASK            (0x0E00)
+#define ADS1115_PGA_6_144V          (0x0000)    // +/-6.144V range = Gain 2/3
+#define ADS1115_PGA_4_096V          (0x0200)    // +/-4.096V range = Gain 1
+#define ADS1115_PGA_2_048V          (0x0400)    // +/-2.048V range = Gain 2 (default)
+#define ADS1115_PGA_1_024V          (0x0600)    // +/-1.024V range = Gain 4
+#define ADS1115_PGA_0_512V          (0x0800)    // +/-0.512V range = Gain 8
+#define ADS1115_PGA_0_256V          (0x0A00)    // +/-0.256V range = Gain 16
+
+#define ADS1115_MODE_MASK           (0x0100)
+#define ADS1115_MODE_CONTIN         (0x0000)    // Continuous conversion mode
+#define ADS1115_MODE_SINGLE         (0x0100)    // Power-down single-shot mode (default)
+
+#define ADS1115_DR_MASK             (0x00E0)
+#define ADS1115_DR_8SPS             (   8)
+#define ADS1115_DR_16SPS            (  16)
+#define ADS1115_DR_32SPS            (  32)
+#define ADS1115_DR_64SPS            (  64)
+#define ADS1115_DR_128SPS           ( 128)
+#define ADS1115_DR_250SPS           ( 250)
+#define ADS1115_DR_475SPS           ( 475)
+#define ADS1115_DR_490SPS           ( 490)
+#define ADS1115_DR_860SPS           ( 860)
+#define ADS1115_DR_920SPS           ( 920)
+#define ADS1115_DR_1600SPS          (1600)
+#define ADS1115_DR_2400SPS          (2400)
+#define ADS1115_DR_3300SPS          (3300)
+
+#define ADS1115_CMODE_MASK          (0x0010)
+#define ADS1115_CMODE_TRAD          (0x0000)    // Traditional comparator with hysteresis (default)
+#define ADS1115_CMODE_WINDOW        (0x0010)    // Window comparator
+
+#define ADS1115_CPOL_MASK           (0x0008)
+#define ADS1115_CPOL_ACTVLOW        (0x0000)    // ALERT/RDY pin is low when active (default)
+#define ADS1115_CPOL_ACTVHI         (0x0008)    // ALERT/RDY pin is high when active
+
+#define ADS1115_CLAT_MASK           (0x0004)    // Determines if ALERT/RDY pin latches once asserted
+#define ADS1115_CLAT_NONLAT         (0x0000)    // Non-latching comparator (default)
+#define ADS1115_CLAT_LATCH          (0x0004)    // Latching comparator
+
+#define ADS1115_CQUE_MASK           (0x0003)
+#define ADS1115_CQUE_1CONV          (0x0000)    // Assert ALERT/RDY after one conversions
+#define ADS1115_CQUE_2CONV          (0x0001)    // Assert ALERT/RDY after two conversions
+#define ADS1115_CQUE_4CONV          (0x0002)    // Assert ALERT/RDY after four conversions
+#define ADS1115_CQUE_NONE           (0x0003)    // Disable the comparator and put ALERT/RDY in high state (default)
+
+#define ADS1115_DEFAULT_CONFIG_REG  (0x8583)    // Config register value after reset
 
 //***************************************************************************
 
 static const uint8_t ads1115_i2c_id = 0;
 static const uint8_t general_i2c_addr = 0x00;
 static const uint8_t ads1115_i2c_reset = 0x06;
-static uint8_t ads1115_i2c_addr = ADS1115_I2C_ADDR_GND;
-static uint16_t ads1115_os = ADS1115_OS_SINGLE;
-static uint16_t ads1115_gain = ADS1115_PGA_6_144V;
-static uint16_t ads1115_samples = ADS1115_DR_128SPS;
-static uint16_t ads1115_channel = ADS1115_MUX_SINGLE_0;
-static uint16_t ads1115_comp = ADS1115_CQUE_NONE;
-static uint16_t ads1115_mode = ADS1115_MODE_SINGLE;
-static uint16_t ads1115_threshold_low = 0x8000;
-static uint16_t ads1115_threshold_hi = 0x7FFF;
-static uint16_t ads1115_config = 0x8583;
-static uint16_t ads1115_conversion = 0;
-static double ads1115_volt = 0;
-os_timer_t ads1115_timer;	// timer for conversion delay
-int ads1115_timer_ref; // callback when readout is ready
-
-static int ads1115_lua_readoutdone(void);
-
-static uint8_t write_reg(uint8_t reg, uint16_t config) {
-	platform_i2c_send_start(ads1115_i2c_id);
-	platform_i2c_send_address(ads1115_i2c_id, ads1115_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
-	platform_i2c_send_byte(ads1115_i2c_id, reg);
-	platform_i2c_send_byte(ads1115_i2c_id, (uint8_t)(config >> 8));
-	platform_i2c_send_byte(ads1115_i2c_id, (uint8_t)(config & 0xFF));
-	platform_i2c_send_stop(ads1115_i2c_id);
+static const char metatable_name[] = "ads1115.device";
+static const char unexpected_value[] = "unexpected value";
+
+typedef struct {
+    uint8_t i2c_addr;
+    uint8_t chip_id;
+    uint16_t gain;
+    uint16_t samples_value; // sample per second
+    uint16_t samples;       // register value
+    uint16_t comp;
+    uint16_t mode;
+    uint16_t threshold_low;
+    uint16_t threshold_hi;
+    uint16_t config;
+    int timer_ref;
+    os_timer_t timer;
+} ads_ctrl_ud_t;
+
+
+static int ads1115_lua_readoutdone(void * param);
+static int ads1115_lua_register(lua_State *L, uint8_t chip_id);
+
+static uint8_t write_reg(uint8_t ads_addr, uint8_t reg, uint16_t config) {
+    platform_i2c_send_start(ads1115_i2c_id);
+    platform_i2c_send_address(ads1115_i2c_id, ads_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
+    platform_i2c_send_byte(ads1115_i2c_id, reg);
+    platform_i2c_send_byte(ads1115_i2c_id, (uint8_t)(config >> 8));
+    platform_i2c_send_byte(ads1115_i2c_id, (uint8_t)(config & 0xFF));
+    platform_i2c_send_stop(ads1115_i2c_id);
 }
 
-static uint16_t read_reg(uint8_t reg) {
-	platform_i2c_send_start(ads1115_i2c_id);
-	platform_i2c_send_address(ads1115_i2c_id, ads1115_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
-	platform_i2c_send_byte(ads1115_i2c_id, reg);
-	platform_i2c_send_stop(ads1115_i2c_id);
-	platform_i2c_send_start(ads1115_i2c_id);
-	platform_i2c_send_address(ads1115_i2c_id, ads1115_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER);
-	uint16_t buf = (platform_i2c_recv_byte(ads1115_i2c_id, 1) << 8);
-	buf += platform_i2c_recv_byte(ads1115_i2c_id, 0);
-	platform_i2c_send_stop(ads1115_i2c_id);
-	return buf;
+static uint16_t read_reg(uint8_t ads_addr, uint8_t reg) {
+    platform_i2c_send_start(ads1115_i2c_id);
+    platform_i2c_send_address(ads1115_i2c_id, ads_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
+    platform_i2c_send_byte(ads1115_i2c_id, reg);
+    platform_i2c_send_stop(ads1115_i2c_id);
+    platform_i2c_send_start(ads1115_i2c_id);
+    platform_i2c_send_address(ads1115_i2c_id, ads_addr, PLATFORM_I2C_DIRECTION_RECEIVER);
+    uint16_t buf = (platform_i2c_recv_byte(ads1115_i2c_id, 1) << 8);
+    buf += platform_i2c_recv_byte(ads1115_i2c_id, 0);
+    platform_i2c_send_stop(ads1115_i2c_id);
+    return buf;
 }
 
 // convert ADC value to voltage corresponding to PGA settings
-static double get_volt(uint16_t value) {
-	
-	double volt = 0;
-	
-	switch (ads1115_gain) {
-		case (ADS1115_PGA_6_144V):
-			volt = (int16_t)value * 0.1875;
-			break;
-		case (ADS1115_PGA_4_096V):
-			volt = (int16_t)value * 0.125;
-			break;
-		case (ADS1115_PGA_2_048V):
-			volt = (int16_t)value * 0.0625;
-			break;
-		case (ADS1115_PGA_1_024V):
-			volt = (int16_t)value * 0.03125;
-			break;
-		case (ADS1115_PGA_0_512V):
-			volt = (int16_t)value * 0.015625;
-			break;
-		case (ADS1115_PGA_0_256V):
-			volt = (int16_t)value * 0.0078125;
-			break;
-	}
-	
-	return volt;
+static double get_volt(uint16_t gain, uint16_t value) {
+
+    double volt = 0;
+
+    switch (gain) {
+        case (ADS1115_PGA_6_144V):
+            volt = (int16_t)value * 0.1875;
+            break;
+        case (ADS1115_PGA_4_096V):
+            volt = (int16_t)value * 0.125;
+            break;
+        case (ADS1115_PGA_2_048V):
+            volt = (int16_t)value * 0.0625;
+            break;
+        case (ADS1115_PGA_1_024V):
+            volt = (int16_t)value * 0.03125;
+            break;
+        case (ADS1115_PGA_0_512V):
+            volt = (int16_t)value * 0.015625;
+            break;
+        case (ADS1115_PGA_0_256V):
+            volt = (int16_t)value * 0.0078125;
+            break;
+    }
+
+    return volt;
+}
+
+// validates and convert threshold in volt to ADC value corresponding to PGA settings
+// returns true if valid
+static uint8_t get_value(uint16_t gain, uint16_t channel, int16_t *volt) {
+    switch (gain) {
+        case (ADS1115_PGA_6_144V):
+            if ((*volt >= 6144) || (*volt < -6144) || ((*volt < 0) && (channel >> 14))) return 0;
+            *volt = *volt / 0.1875;
+            break;
+        case (ADS1115_PGA_4_096V):
+            if ((*volt >= 4096) || (*volt < -4096) || ((*volt < 0) && (channel >> 14))) return 0;
+            *volt = *volt / 0.125;
+            break;
+        case (ADS1115_PGA_2_048V):
+            if ((*volt >= 2048) || (*volt < -2048) || ((*volt < 0) && (channel >> 14))) return 0;
+            *volt = *volt / 0.0625;
+            break;
+        case (ADS1115_PGA_1_024V):
+            if ((*volt >= 1024) || (*volt < -1024) || ((*volt < 0) && (channel >> 14))) return 0;
+            *volt = *volt / 0.03125;
+            break;
+        case (ADS1115_PGA_0_512V):
+            if ((*volt >= 512) || (*volt < -512) || ((*volt < 0) && (channel >> 14))) return 0;
+            *volt = *volt / 0.015625;
+            break;
+        case (ADS1115_PGA_0_256V):
+            if ((*volt >= 256) || (*volt < -256) || ((*volt < 0) && (channel >> 14))) return 0;
+            *volt = *volt / 0.0078125;
+            break;
+    }
+    return 1;
+}
+
+
+// Reset of all devices
+// Lua:     ads1115.reset()
+static int ads1115_lua_reset(lua_State *L) {
+    platform_i2c_send_start(ads1115_i2c_id);
+    platform_i2c_send_address(ads1115_i2c_id, general_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
+    platform_i2c_send_byte(ads1115_i2c_id, ads1115_i2c_reset);
+    platform_i2c_send_stop(ads1115_i2c_id);
+    return 0;
+}
+
+// Register an ADS device
+// Lua:     ads1115.ADS1115(I2C_ID, ADDRESS)
+static int ads1115_lua_register_1115(lua_State *L) {
+    return ads1115_lua_register(L, ADS1115_ADS1115);
 }
 
-// convert threshold in volt to ADC value corresponding to PGA settings
-static uint8_t get_value(int16_t *volt) {
-	
-	switch (ads1115_gain) {
-		case (ADS1115_PGA_6_144V):
-			if ((*volt >= 6144) || (*volt < -6144) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
-			*volt = *volt / 0.1875;
-			break;
-		case (ADS1115_PGA_4_096V):
-			if ((*volt >= 4096) || (*volt < -4096) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
-			*volt = *volt / 0.125;
-			break;
-		case (ADS1115_PGA_2_048V):
-			if ((*volt >= 2048) || (*volt < -2048) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
-			*volt = *volt / 0.0625;
-			break;
-		case (ADS1115_PGA_1_024V):
-			if ((*volt >= 1024) || (*volt < -1024) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
-			*volt = *volt / 0.03125;
-			break;
-		case (ADS1115_PGA_0_512V):
-			if ((*volt >= 512) || (*volt < -512) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
-			*volt = *volt / 0.015625;
-			break;
-		case (ADS1115_PGA_0_256V):
-			if ((*volt >= 256) || (*volt < -256) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
-			*volt = *volt / 0.0078125;
-			break;
-	}
-	
-	return 0;
+static int ads1115_lua_register_1015(lua_State *L) {
+    return ads1115_lua_register(L, ADS1115_ADS1015);
 }
 
-// Initializes ADC
-// Lua:		ads11115.setup(ADDRESS)
-static int ads1115_lua_setup(lua_State *L) {
-	
-	// check variables
-	if (!lua_isnumber(L, 1)) {
-		return luaL_error(L, "wrong arg range");
-	}
-	
-	ads1115_i2c_addr = luaL_checkinteger(L, 1);
-	if (!((ads1115_i2c_addr == ADS1115_I2C_ADDR_GND) || (ads1115_i2c_addr == ADS1115_I2C_ADDR_VDD) || (ads1115_i2c_addr == ADS1115_I2C_ADDR_SDA) || (ads1115_i2c_addr == ADS1115_I2C_ADDR_SCL))) {
-		return luaL_error(L, "Invalid argument: adddress");
-	}
-	
-	platform_i2c_send_start(ads1115_i2c_id);
-	platform_i2c_send_address(ads1115_i2c_id, general_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
-	platform_i2c_send_byte(ads1115_i2c_id, ads1115_i2c_reset);
-	platform_i2c_send_stop(ads1115_i2c_id);
-	
-	// check for device on i2c bus
-	if (read_reg(ADS1115_POINTER_CONFIG) != 0x8583) {
-		return luaL_error(L, "found no device");
-	}
-	
-	return 0;
+static int ads1115_lua_register(lua_State *L, uint8_t chip_id) {
+    uint8_t i2c_id = luaL_checkinteger(L, 1);
+    luaL_argcheck(L, 0 == i2c_id, 1, "i2c_id must be 0");
+    uint8_t i2c_addr = luaL_checkinteger(L, 2);
+    luaL_argcheck(L, IS_I2C_ADDR_VALID(i2c_addr), 2, unexpected_value);
+    uint16_t config_read = read_reg(i2c_addr, ADS1115_POINTER_CONFIG);
+    if (config_read == 0xFFFF) {
+        return luaL_error(L, "found no device");
+    }
+    if (config_read != ADS1115_DEFAULT_CONFIG_REG) {
+        return luaL_error(L, "unexpected config value (%p) please reset device before calling this function", config_read);
+    }
+    ads_ctrl_ud_t *ads_ctrl = (ads_ctrl_ud_t *)lua_newuserdata(L, sizeof(ads_ctrl_ud_t));
+    if (NULL == ads_ctrl) {
+        return luaL_error(L, "ads1115 malloc: out of memory");
+    }
+    luaL_getmetatable(L, metatable_name);
+    lua_setmetatable(L, -2);
+    ads_ctrl->chip_id = chip_id;
+    ads_ctrl->i2c_addr = i2c_addr;
+    ads_ctrl->gain = ADS1115_PGA_6_144V;
+    ads_ctrl->samples = ADS1115_DR_128SPS;
+    ads_ctrl->samples_value = chip_id == ADS1115_ADS1115 ? 128 : 1600;
+    ads_ctrl->comp = ADS1115_CQUE_NONE;
+    ads_ctrl->mode = ADS1115_MODE_SINGLE;
+    ads_ctrl->threshold_low = 0x8000;
+    ads_ctrl->threshold_hi = 0x7FFF;
+    ads_ctrl->config = ADS1115_DEFAULT_CONFIG_REG;
+    ads_ctrl->timer_ref = LUA_NOREF;
+    return 1;
 }
 
-// Change ADC settings
-// Lua: 	ads1115.setting(GAIN,SAMPLES,CHANNEL,MODE[,CONVERSION_RDY][,COMPARATOR,THRESHOLD_LOW,THRESHOLD_HI])
+// Change the ADC device settings
+// Lua:     ads1115.device:settings(GAIN,SAMPLES,CHANNEL,MODE[,CONVERSION_RDY][,COMPARATOR,THRESHOLD_LOW,THRESHOLD_HI[,COMP_MODE])
 static int ads1115_lua_setting(lua_State *L) {
-	
-	// check variables
-	if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4)) {
-		return luaL_error(L, "wrong arg range");
-	}
-	
-	ads1115_gain = luaL_checkinteger(L, 1);
-	if (!((ads1115_gain == ADS1115_PGA_6_144V) || (ads1115_gain == ADS1115_PGA_4_096V) || (ads1115_gain == ADS1115_PGA_2_048V) || (ads1115_gain == ADS1115_PGA_1_024V) || (ads1115_gain == ADS1115_PGA_0_512V) || (ads1115_gain == ADS1115_PGA_0_256V))) {
-		return luaL_error(L, "Invalid argument: gain");
-	}
-	
-	ads1115_samples = luaL_checkinteger(L, 2);
-	if (!((ads1115_samples == ADS1115_DR_8SPS) || (ads1115_samples == ADS1115_DR_16SPS) || (ads1115_samples == ADS1115_DR_32SPS) || (ads1115_samples == ADS1115_DR_64SPS) || (ads1115_samples == ADS1115_DR_128SPS) || (ads1115_samples == ADS1115_DR_250SPS) || (ads1115_samples == ADS1115_DR_475SPS) || (ads1115_samples == ADS1115_DR_860SPS))) {
-		return luaL_error(L, "Invalid argument: samples");
-	}
-	
-	ads1115_channel = luaL_checkinteger(L, 3);
-	if (!((ads1115_channel == ADS1115_MUX_SINGLE_0) || (ads1115_channel == ADS1115_MUX_SINGLE_1) || (ads1115_channel == ADS1115_MUX_SINGLE_2) || (ads1115_channel == ADS1115_MUX_SINGLE_3) || (ads1115_channel == ADS1115_MUX_DIFF_0_1) || (ads1115_channel == ADS1115_MUX_DIFF_0_3) || (ads1115_channel == ADS1115_MUX_DIFF_1_3) || (ads1115_channel == ADS1115_MUX_DIFF_2_3))) {
-		return luaL_error(L, "Invalid argument: channel");
-	}
-	
-	ads1115_mode = luaL_checkinteger(L, 4);
-	if (!((ads1115_mode == ADS1115_MODE_SINGLE) || (ads1115_mode == ADS1115_MODE_CONTIN))) {
-		return luaL_error(L, "Invalid argument: mode");
-	}
-	
-	if (ads1115_mode == ADS1115_MODE_SINGLE) {
-		ads1115_os = ADS1115_OS_SINGLE;
-	} else {
-		ads1115_os = ADS1115_OS_NON;
-	}
-	
-	ads1115_comp = ADS1115_CQUE_NONE;
-	
-	// Parse optional parameters
-	if (lua_isnumber(L, 5) && !(lua_isnumber(L, 6) || lua_isnumber(L, 7))) {
-		
-		//	conversion ready mode
-		ads1115_comp = luaL_checkinteger(L, 5);
-		if (!((ads1115_comp == ADS1115_CQUE_1CONV) || (ads1115_comp == ADS1115_CQUE_2CONV) || (ads1115_comp == ADS1115_CQUE_4CONV))) {
-			return luaL_error(L, "Invalid argument: conversion ready mode");
-		}
-		
-		ads1115_threshold_low = 0x7FFF;
-		ads1115_threshold_hi = 0x8000;
-		
-		write_reg(ADS1115_POINTER_THRESH_LOW, ads1115_threshold_low);
-		write_reg(ADS1115_POINTER_THRESH_HI, ads1115_threshold_hi);
-		
-	} else if (lua_isnumber(L, 5) && lua_isnumber(L, 6) && lua_isnumber(L, 7)) {
-		
-		//	comparator mode
-		ads1115_comp = luaL_checkinteger(L, 5);
-		if (!((ads1115_comp == ADS1115_CQUE_1CONV) || (ads1115_comp == ADS1115_CQUE_2CONV) || (ads1115_comp == ADS1115_CQUE_4CONV))) {
-			return luaL_error(L, "Invalid argument: comparator mode");
-		}
-		
-		ads1115_threshold_low = luaL_checkinteger(L, 5);
-		ads1115_threshold_hi = luaL_checkinteger(L, 6);
-		
-		if ((int16_t)ads1115_threshold_low > (int16_t)ads1115_threshold_hi) {
-			return luaL_error(L, "Invalid argument: threshold_low > threshold_hi");
-		}
-		
-		if (get_value(&ads1115_threshold_low)) {
-			return luaL_error(L, "Invalid argument: threshold_low");
-		}
-		
-		if (get_value(&ads1115_threshold_hi)) {
-			return luaL_error(L, "Invalid argument: threshold_hi");
-		}
-		
-		write_reg(ADS1115_POINTER_THRESH_LOW, ads1115_threshold_low);
-		write_reg(ADS1115_POINTER_THRESH_HI, ads1115_threshold_hi);
-		
-	}
-	
-	ads1115_config = (ads1115_os | ads1115_channel | ads1115_gain | ads1115_mode | ads1115_samples | ADS1115_CMODE_TRAD | ADS1115_CPOL_ACTVLOW | ADS1115_CLAT_NONLAT | ads1115_comp);
-	
-	write_reg(ADS1115_POINTER_CONFIG, ads1115_config);
-	
-	return 0;
+    int argc = lua_gettop(L);
+    if (argc != 5 && argc != 6 && argc != 8 && argc != 9) { // user data counts
+        luaL_error(L, "invalid number of arguments to 'setting'");
+    }
+    ads_ctrl_ud_t *ads_ctrl = luaL_checkudata(L, 1, metatable_name);
+    // gain
+    uint16_t gain = luaL_checkinteger(L, 2);
+    luaL_argcheck(L, (gain == ADS1115_PGA_6_144V) || (gain == ADS1115_PGA_4_096V) || (gain == ADS1115_PGA_2_048V) ||
+                     (gain == ADS1115_PGA_1_024V) || (gain == ADS1115_PGA_0_512V) || (gain == ADS1115_PGA_0_256V),
+                  2, unexpected_value);
+    ads_ctrl->gain = gain;
+    // samples
+    uint16_t samples_value = luaL_checkinteger(L, 3);
+    uint16_t samples = 0;
+    if (ads_ctrl->chip_id == ADS1115_ADS1115) {
+        switch(samples_value) {
+            case ADS1115_DR_8SPS:
+                samples = 0;
+                break;
+            case ADS1115_DR_16SPS:
+                samples = 0x20;
+                break;
+            case ADS1115_DR_32SPS:
+                samples = 0x40;
+                break;
+            case ADS1115_DR_64SPS:
+                samples = 0x60;
+                break;
+            case ADS1115_DR_128SPS: // default
+                samples = 0x80;
+                break;
+            case ADS1115_DR_250SPS:
+                samples = 0xA0;
+                break;
+            case ADS1115_DR_475SPS:
+                samples = 0xC0;
+                break;
+            case ADS1115_DR_860SPS:
+                samples = 0xE0;
+                break;
+            default:
+                luaL_argerror(L, 3, unexpected_value);
+        }
+    } else { // ADS1115_ADS1015
+        switch(samples_value) {
+            case ADS1115_DR_128SPS:
+                samples = 0;
+                break;
+            case ADS1115_DR_250SPS:
+                samples = 0x20;
+                break;
+            case ADS1115_DR_490SPS:
+                samples = 0x40;
+                break;
+            case ADS1115_DR_920SPS:
+                samples = 0x60;
+                break;
+            case ADS1115_DR_1600SPS: // default
+                samples = 0x80;
+                break;
+            case ADS1115_DR_2400SPS:
+                samples = 0xA0;
+                break;
+            case ADS1115_DR_3300SPS:
+                samples = 0xC0;
+                break;
+            default:
+                luaL_argerror(L, 3, unexpected_value);
+        }
+    }
+    ads_ctrl->samples = samples;
+    ads_ctrl->samples_value = samples_value;
+    // channel
+    uint16_t channel = luaL_checkinteger(L, 4);
+    luaL_argcheck(L, IS_CHANNEL_VALID(channel), 4, unexpected_value);
+    // mode
+    uint16_t mode = luaL_checkinteger(L, 5);
+    luaL_argcheck(L, (mode == ADS1115_MODE_SINGLE) || (mode == ADS1115_MODE_CONTIN), 5, unexpected_value);
+    ads_ctrl->mode = mode;
+    uint16_t os = mode == ADS1115_MODE_SINGLE ? ADS1115_OS_SINGLE : ADS1115_OS_NON;
+
+    uint16_t comp = ADS1115_CQUE_NONE;
+    // Parse optional parameters
+    if (argc > 5) {
+        // comparator or conversion count
+        comp = luaL_checkinteger(L, 6);
+        luaL_argcheck(L, (comp == ADS1115_CQUE_1CONV) || (comp == ADS1115_CQUE_2CONV) || (comp == ADS1115_CQUE_4CONV),
+                      6, unexpected_value);
+        uint16_t threshold_low = 0x7FFF;
+        uint16_t threshold_hi = 0x8000;
+        if (argc > 6) {
+            // comparator thresholds
+            threshold_low = luaL_checkinteger(L, 7);
+            threshold_hi = luaL_checkinteger(L, 8);
+            luaL_argcheck(L, (int16_t)threshold_low <= (int16_t)threshold_hi, 7, "threshold_low > threshold_hi");
+            luaL_argcheck(L, get_value(gain, channel, &threshold_low), 7, unexpected_value);
+            luaL_argcheck(L, get_value(gain, channel, &threshold_hi), 8, unexpected_value);
+        }
+        ads_ctrl->threshold_low = threshold_low;
+        ads_ctrl->threshold_hi = threshold_hi;
+        NODE_DBG("ads1115 low: %04x\n", threshold_low);
+        NODE_DBG("ads1115 hi : %04x\n", threshold_hi);
+        write_reg(ads_ctrl->i2c_addr, ADS1115_POINTER_THRESH_LOW, threshold_low);
+        write_reg(ads_ctrl->i2c_addr, ADS1115_POINTER_THRESH_HI, threshold_hi);
+    }
+    ads_ctrl->comp = comp;
+
+    uint16_t comparator_mode = ADS1115_CMODE_TRAD;
+    if (argc == 9) {
+        comparator_mode = luaL_checkinteger(L, 9);
+        luaL_argcheck(L, (comparator_mode == ADS1115_CMODE_WINDOW) || (comparator_mode == ADS1115_CMODE_TRAD),
+                      9, unexpected_value);
+    }
+
+    uint16_t config = (os | channel | gain | mode | samples | comparator_mode | ADS1115_CPOL_ACTVLOW | ADS1115_CLAT_NONLAT | comp);
+    ads_ctrl->config = config;
+
+    NODE_DBG("ads1115 config: %04x\n", ads_ctrl->config);
+    write_reg(ads_ctrl->i2c_addr, ADS1115_POINTER_CONFIG, config);
+    return 0;
 }
 
-// Read the conversion register from the ADC
-// Lua: 	ads1115.startread(function(volt, voltdec, adc) print(volt,voltdec,adc) end)
+// Read the conversion register from the ADC device
+// Lua:     ads1115.device:startread(function(volt, voltdec, adc) print(volt,voltdec,adc) end)
 static int ads1115_lua_startread(lua_State *L) {
-	
-	if (((ads1115_comp == ADS1115_CQUE_1CONV) || (ads1115_comp == ADS1115_CQUE_2CONV) || (ads1115_comp == ADS1115_CQUE_4CONV)) && (ads1115_threshold_low == 0x7FFF) && (ads1115_threshold_hi == 0x8000)) {
-		
-		if (ads1115_mode == ADS1115_MODE_SINGLE) {
-			write_reg(ADS1115_POINTER_CONFIG, ads1115_config);
-		}
-		
-		return 0;
-		
-	} else {
-		
-		luaL_argcheck(L, (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION), 1, "Must be function");
-		lua_pushvalue(L, 1);
-		ads1115_timer_ref = luaL_ref(L, LUA_REGISTRYINDEX);
-	
-		if (ads1115_mode == ADS1115_MODE_SINGLE) {
-			write_reg(ADS1115_POINTER_CONFIG, ads1115_config);
-		}
-	
-		// Start a timer to wait until ADC conversion is done
-		os_timer_disarm (&ads1115_timer);
-		os_timer_setfn (&ads1115_timer, (os_timer_func_t *)ads1115_lua_readoutdone, NULL);
-	
-		switch (ads1115_samples) {
-			case (ADS1115_DR_8SPS):
-				os_timer_arm (&ads1115_timer, 150, 0);
-				break;
-			case (ADS1115_DR_16SPS):
-				os_timer_arm (&ads1115_timer, 75, 0);
-				break;
-			case (ADS1115_DR_32SPS):
-				os_timer_arm (&ads1115_timer, 35, 0);
-				break;
-			case (ADS1115_DR_64SPS):
-				os_timer_arm (&ads1115_timer, 20, 0);
-				break;
-			case (ADS1115_DR_128SPS):
-				os_timer_arm (&ads1115_timer, 10, 0);
-				break;
-			case (ADS1115_DR_250SPS):
-				os_timer_arm (&ads1115_timer, 5, 0);
-				break;
-			case (ADS1115_DR_475SPS):
-				os_timer_arm (&ads1115_timer, 3, 0);
-				break;
-			case (ADS1115_DR_860SPS):
-				os_timer_arm (&ads1115_timer, 2, 0);
-				break;
-		}
-	
-		return 0;
-	}
+    ads_ctrl_ud_t *ads_ctrl = luaL_checkudata(L, 1, metatable_name);
+
+    if (((ads_ctrl->comp == ADS1115_CQUE_1CONV) ||
+         (ads_ctrl->comp == ADS1115_CQUE_2CONV) ||
+         (ads_ctrl->comp == ADS1115_CQUE_4CONV)) &&
+        (ads_ctrl->threshold_low == 0x7FFF) &&
+        (ads_ctrl->threshold_hi == 0x8000)) {
+        // conversion ready mode
+        if (ads_ctrl->mode == ADS1115_MODE_SINGLE) {
+            NODE_DBG("ads1115 trigger config: %04x", ads_ctrl->config);
+            write_reg(ads_ctrl->i2c_addr, ADS1115_POINTER_CONFIG, ads_ctrl->config);
+        }
+        return 0;
+    }
+
+    luaL_argcheck(L, (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION), 2, "Must be function");
+    lua_pushvalue(L, 2);
+    ads_ctrl->timer_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+    if (ads_ctrl->mode == ADS1115_MODE_SINGLE) {
+        write_reg(ads_ctrl->i2c_addr, ADS1115_POINTER_CONFIG, ads_ctrl->config);
+    }
+
+    // Start a timer to wait until ADC conversion is done
+    os_timer_disarm(&ads_ctrl->timer);
+    os_timer_setfn(&ads_ctrl->timer, (os_timer_func_t *)ads1115_lua_readoutdone, (void *)ads_ctrl);
+
+    int msec = 1; // ADS1115_DR_1600SPS, ADS1115_DR_2400SPS, ADS1115_DR_3300SPS
+    switch (ads_ctrl->samples_value) {
+        case ADS1115_DR_8SPS:
+            msec = 150;
+            break;
+        case ADS1115_DR_16SPS:
+            msec = 75;
+            break;
+        case ADS1115_DR_32SPS:
+            msec = 35;
+            break;
+        case ADS1115_DR_64SPS:
+            msec = 20;
+            break;
+        case ADS1115_DR_128SPS:
+            msec = 10;
+            break;
+        case ADS1115_DR_250SPS:
+            msec = 5;
+            break;
+        case ADS1115_DR_475SPS:
+        case ADS1115_DR_490SPS:
+            msec = 3;
+            break;
+        case ADS1115_DR_860SPS:
+        case ADS1115_DR_920SPS:
+            msec = 2;
+    }
+    os_timer_arm(&ads_ctrl->timer, msec, 0);
+    return 0;
 }
 
 // adc conversion timer callback
-static int ads1115_lua_readoutdone(void) {
-	
-	ads1115_conversion = read_reg(ADS1115_POINTER_CONVERSION);
-	ads1115_volt = get_volt(ads1115_conversion);
-	int ads1115_voltdec = (int)((ads1115_volt - (int)ads1115_volt) * 1000);
-	ads1115_voltdec = ads1115_voltdec>0?ads1115_voltdec:0-ads1115_voltdec;
-	
-	lua_State *L = lua_getstate();
-	os_timer_disarm (&ads1115_timer);
-	
-	lua_rawgeti (L, LUA_REGISTRYINDEX, ads1115_timer_ref);
-	luaL_unref (L, LUA_REGISTRYINDEX, ads1115_timer_ref);
-	ads1115_timer_ref = LUA_NOREF;
-	
-	lua_pushnumber(L, ads1115_volt);
-	lua_pushinteger(L, ads1115_voltdec);
-	lua_pushinteger(L, ads1115_conversion);
-	
-	lua_call (L, 3, 0);
+static int ads1115_lua_readoutdone(void * param) {
+    ads_ctrl_ud_t * ads_ctrl = (ads_ctrl_ud_t *)param;
+
+    uint16_t ads1115_conversion = read_reg(ads_ctrl->i2c_addr, ADS1115_POINTER_CONVERSION);
+    double ads1115_volt = get_volt(ads_ctrl->gain, ads1115_conversion);
+    int ads1115_voltdec = (int)((ads1115_volt - (int)ads1115_volt) * 1000);
+    ads1115_voltdec = ads1115_voltdec > 0 ? ads1115_voltdec : 0 - ads1115_voltdec;
+
+    lua_State *L = lua_getstate();
+    os_timer_disarm(&ads_ctrl->timer);
+
+    lua_rawgeti(L, LUA_REGISTRYINDEX, ads_ctrl->timer_ref);
+    luaL_unref(L, LUA_REGISTRYINDEX, ads_ctrl->timer_ref);
+    ads_ctrl->timer_ref = LUA_NOREF;
+
+    lua_pushnumber(L, ads1115_volt);
+    lua_pushinteger(L, ads1115_voltdec);
+    lua_pushinteger(L, ads1115_conversion);
+
+    lua_call(L, 3, 0);
 }
 
-// Read the conversion register from the ADC
-// Lua: 	volt,voltdec,adc = ads1115.read()
+// Read the conversion register from the ADC device
+// Lua:     volt,voltdec,adc = ads1115.device:read()
 static int ads1115_lua_read(lua_State *L) {
-	
-	ads1115_conversion = read_reg(ADS1115_POINTER_CONVERSION);
-	ads1115_volt = get_volt(ads1115_conversion);
-	int ads1115_voltdec = (int)((ads1115_volt - (int)ads1115_volt) * 1000);
-	ads1115_voltdec = ads1115_voltdec>0?ads1115_voltdec:0-ads1115_voltdec;
-	
-	lua_pushnumber(L, ads1115_volt);
-	lua_pushinteger(L, ads1115_voltdec);
-	lua_pushinteger(L, ads1115_conversion);
-	
-	return 3;
+    ads_ctrl_ud_t *ads_ctrl = luaL_checkudata(L, 1, metatable_name);
+
+    uint16_t ads1115_conversion = read_reg(ads_ctrl->i2c_addr, ADS1115_POINTER_CONVERSION);
+    double ads1115_volt = get_volt(ads_ctrl->gain, ads1115_conversion);
+    int ads1115_voltdec = (int)((ads1115_volt - (int)ads1115_volt) * 1000);
+    ads1115_voltdec = ads1115_voltdec > 0 ? ads1115_voltdec : 0 - ads1115_voltdec;
+
+    lua_pushnumber(L, ads1115_volt);
+    lua_pushinteger(L, ads1115_voltdec);
+    lua_pushinteger(L, ads1115_conversion);
+
+    return 3;
+}
+
+static int ads1115_lua_delete(lua_State *L) {
+    ads_ctrl_ud_t *ads_ctrl = luaL_checkudata(L, 1, metatable_name);
+    if (ads_ctrl->timer_ref != LUA_NOREF) {
+        os_timer_disarm(&ads_ctrl->timer);
+        luaL_unref(L, LUA_REGISTRYINDEX, ads_ctrl->timer_ref);
+    }
+    return 0;
 }
 
 static const LUA_REG_TYPE ads1115_map[] = {
-	{	LSTRKEY( "setup" ),			LFUNCVAL(ads1115_lua_setup)		},
-	{	LSTRKEY( "setting" ),		LFUNCVAL(ads1115_lua_setting)	},
-	{	LSTRKEY( "startread" ),		LFUNCVAL(ads1115_lua_startread)	},
-	{	LSTRKEY( "read" ),			LFUNCVAL(ads1115_lua_read)		},
-	{	LSTRKEY( "ADDR_GND" ),		LNUMVAL(ADS1115_I2C_ADDR_GND)	},
-	{	LSTRKEY( "ADDR_VDD" ),		LNUMVAL(ADS1115_I2C_ADDR_VDD)	},
-	{	LSTRKEY( "ADDR_SDA" ),		LNUMVAL(ADS1115_I2C_ADDR_SDA)	},
-	{	LSTRKEY( "ADDR_SCL" ),		LNUMVAL(ADS1115_I2C_ADDR_SCL)	},
-	{	LSTRKEY( "SINGLE_SHOT" ),	LNUMVAL(ADS1115_MODE_SINGLE)	},
-	{	LSTRKEY( "CONTINUOUS" ),	LNUMVAL(ADS1115_MODE_CONTIN)	},
-	{	LSTRKEY( "DIFF_0_1" ),		LNUMVAL(ADS1115_MUX_DIFF_0_1)	},
-	{	LSTRKEY( "DIFF_0_3" ),		LNUMVAL(ADS1115_MUX_DIFF_0_3)	},
-	{	LSTRKEY( "DIFF_1_3" ),		LNUMVAL(ADS1115_MUX_DIFF_1_3)	},
-	{	LSTRKEY( "DIFF_2_3" ),		LNUMVAL(ADS1115_MUX_DIFF_2_3)	},
-	{	LSTRKEY( "SINGLE_0" ),		LNUMVAL(ADS1115_MUX_SINGLE_0)	},
-	{	LSTRKEY( "SINGLE_1" ),		LNUMVAL(ADS1115_MUX_SINGLE_1)	},
-	{	LSTRKEY( "SINGLE_2" ),		LNUMVAL(ADS1115_MUX_SINGLE_2)	},
-	{	LSTRKEY( "SINGLE_3" ),		LNUMVAL(ADS1115_MUX_SINGLE_3)	},
-	{	LSTRKEY( "GAIN_6_144V" ),	LNUMVAL(ADS1115_PGA_6_144V)		},
-	{	LSTRKEY( "GAIN_4_096V" ),	LNUMVAL(ADS1115_PGA_4_096V)		},
-	{	LSTRKEY( "GAIN_2_048V" ),	LNUMVAL(ADS1115_PGA_2_048V)		},
-	{	LSTRKEY( "GAIN_1_024V" ),	LNUMVAL(ADS1115_PGA_1_024V)		},
-	{	LSTRKEY( "GAIN_0_512V" ),	LNUMVAL(ADS1115_PGA_0_512V)		},
-	{	LSTRKEY( "GAIN_0_256V" ),	LNUMVAL(ADS1115_PGA_0_256V)		},
-	{	LSTRKEY( "DR_8SPS" ),		LNUMVAL(ADS1115_DR_8SPS)		},
-	{	LSTRKEY( "DR_16SPS" ),		LNUMVAL(ADS1115_DR_16SPS)		},
-	{	LSTRKEY( "DR_32SPS" ),		LNUMVAL(ADS1115_DR_32SPS)		},
-	{	LSTRKEY( "DR_64SPS" ),		LNUMVAL(ADS1115_DR_64SPS)		},
-	{	LSTRKEY( "DR_128SPS" ),		LNUMVAL(ADS1115_DR_128SPS)		},
-	{	LSTRKEY( "DR_250SPS" ),		LNUMVAL(ADS1115_DR_250SPS)		},
-	{	LSTRKEY( "DR_475SPS" ),		LNUMVAL(ADS1115_DR_475SPS)		},
-	{	LSTRKEY( "DR_860SPS" ),		LNUMVAL(ADS1115_DR_860SPS)		},
-	{	LSTRKEY( "CONV_RDY_1" ),	LNUMVAL(ADS1115_CQUE_1CONV)		},
-	{	LSTRKEY( "CONV_RDY_2" ),	LNUMVAL(ADS1115_CQUE_2CONV)		},
-	{	LSTRKEY( "CONV_RDY_4" ),	LNUMVAL(ADS1115_CQUE_4CONV)		},
-	{	LSTRKEY( "COMP_1CONV" ),	LNUMVAL(ADS1115_CQUE_1CONV)		},
-	{	LSTRKEY( "COMP_2CONV" ),	LNUMVAL(ADS1115_CQUE_2CONV)		},
-	{	LSTRKEY( "COMP_4CONV" ),	LNUMVAL(ADS1115_CQUE_4CONV)		},
-	{	LNILKEY, LNILVAL											}
+
+    {   LSTRKEY( "ads1115" ),       LFUNCVAL(ads1115_lua_register_1115) },
+    {   LSTRKEY( "ads1015" ),       LFUNCVAL(ads1115_lua_register_1015) },
+    {   LSTRKEY( "reset" ),         LFUNCVAL(ads1115_lua_reset)     },
+    {   LSTRKEY( "ADDR_GND" ),      LNUMVAL(ADS1115_I2C_ADDR_GND)   },
+    {   LSTRKEY( "ADDR_VDD" ),      LNUMVAL(ADS1115_I2C_ADDR_VDD)   },
+    {   LSTRKEY( "ADDR_SDA" ),      LNUMVAL(ADS1115_I2C_ADDR_SDA)   },
+    {   LSTRKEY( "ADDR_SCL" ),      LNUMVAL(ADS1115_I2C_ADDR_SCL)   },
+    {   LSTRKEY( "SINGLE_SHOT" ),   LNUMVAL(ADS1115_MODE_SINGLE)    },
+    {   LSTRKEY( "CONTINUOUS" ),    LNUMVAL(ADS1115_MODE_CONTIN)    },
+    {   LSTRKEY( "DIFF_0_1" ),      LNUMVAL(ADS1115_MUX_DIFF_0_1)   },
+    {   LSTRKEY( "DIFF_0_3" ),      LNUMVAL(ADS1115_MUX_DIFF_0_3)   },
+    {   LSTRKEY( "DIFF_1_3" ),      LNUMVAL(ADS1115_MUX_DIFF_1_3)   },
+    {   LSTRKEY( "DIFF_2_3" ),      LNUMVAL(ADS1115_MUX_DIFF_2_3)   },
+    {   LSTRKEY( "SINGLE_0" ),      LNUMVAL(ADS1115_MUX_SINGLE_0)   },
+    {   LSTRKEY( "SINGLE_1" ),      LNUMVAL(ADS1115_MUX_SINGLE_1)   },
+    {   LSTRKEY( "SINGLE_2" ),      LNUMVAL(ADS1115_MUX_SINGLE_2)   },
+    {   LSTRKEY( "SINGLE_3" ),      LNUMVAL(ADS1115_MUX_SINGLE_3)   },
+    {   LSTRKEY( "GAIN_6_144V" ),   LNUMVAL(ADS1115_PGA_6_144V)     },
+    {   LSTRKEY( "GAIN_4_096V" ),   LNUMVAL(ADS1115_PGA_4_096V)     },
+    {   LSTRKEY( "GAIN_2_048V" ),   LNUMVAL(ADS1115_PGA_2_048V)     },
+    {   LSTRKEY( "GAIN_1_024V" ),   LNUMVAL(ADS1115_PGA_1_024V)     },
+    {   LSTRKEY( "GAIN_0_512V" ),   LNUMVAL(ADS1115_PGA_0_512V)     },
+    {   LSTRKEY( "GAIN_0_256V" ),   LNUMVAL(ADS1115_PGA_0_256V)     },
+    {   LSTRKEY( "DR_8SPS" ),       LNUMVAL(ADS1115_DR_8SPS)        },
+    {   LSTRKEY( "DR_16SPS" ),      LNUMVAL(ADS1115_DR_16SPS)       },
+    {   LSTRKEY( "DR_32SPS" ),      LNUMVAL(ADS1115_DR_32SPS)       },
+    {   LSTRKEY( "DR_64SPS" ),      LNUMVAL(ADS1115_DR_64SPS)       },
+    {   LSTRKEY( "DR_128SPS" ),     LNUMVAL(ADS1115_DR_128SPS)      },
+    {   LSTRKEY( "DR_250SPS" ),     LNUMVAL(ADS1115_DR_250SPS)      },
+    {   LSTRKEY( "DR_475SPS" ),     LNUMVAL(ADS1115_DR_475SPS)      },
+    {   LSTRKEY( "DR_490SPS" ),     LNUMVAL(ADS1115_DR_490SPS)      },
+    {   LSTRKEY( "DR_860SPS" ),     LNUMVAL(ADS1115_DR_860SPS)      },
+    {   LSTRKEY( "DR_920SPS" ),     LNUMVAL(ADS1115_DR_920SPS)      },
+    {   LSTRKEY( "DR_1600SPS" ),    LNUMVAL(ADS1115_DR_1600SPS)     },
+    {   LSTRKEY( "DR_2400SPS" ),    LNUMVAL(ADS1115_DR_2400SPS)     },
+    {   LSTRKEY( "DR_3300SPS" ),    LNUMVAL(ADS1115_DR_3300SPS)     },
+    {   LSTRKEY( "CONV_RDY_1" ),    LNUMVAL(ADS1115_CQUE_1CONV)     },
+    {   LSTRKEY( "CONV_RDY_2" ),    LNUMVAL(ADS1115_CQUE_2CONV)     },
+    {   LSTRKEY( "CONV_RDY_4" ),    LNUMVAL(ADS1115_CQUE_4CONV)     },
+    {   LSTRKEY( "COMP_1CONV" ),    LNUMVAL(ADS1115_CQUE_1CONV)     },
+    {   LSTRKEY( "COMP_2CONV" ),    LNUMVAL(ADS1115_CQUE_2CONV)     },
+    {   LSTRKEY( "COMP_4CONV" ),    LNUMVAL(ADS1115_CQUE_4CONV)     },
+    {   LSTRKEY( "CMODE_TRAD"),     LNUMVAL(ADS1115_CMODE_TRAD)     },
+    {   LSTRKEY( "CMODE_WINDOW"),   LNUMVAL(ADS1115_CMODE_WINDOW)   },
+    {   LNILKEY, LNILVAL                                            }
+};
+
+static const LUA_REG_TYPE ads1115_instance_map[] = {
+    {   LSTRKEY( "setting" ),       LFUNCVAL(ads1115_lua_setting)   },
+    {   LSTRKEY( "startread" ),     LFUNCVAL(ads1115_lua_startread) },
+    {   LSTRKEY( "read" ),          LFUNCVAL(ads1115_lua_read)      },
+    {   LSTRKEY( "__index" ),       LROVAL(ads1115_instance_map)    },
+    {   LSTRKEY( "__gc" ),          LFUNCVAL(ads1115_lua_delete)    },
+    {   LNILKEY, LNILVAL                                            }
 };
 
-NODEMCU_MODULE(ADS1115, "ads1115", ads1115_map, NULL);
+
+int luaopen_ads1115(lua_State *L) {
+    luaL_rometatable(L, metatable_name, (void *)ads1115_instance_map);
+    return 0;
+}
+
+NODEMCU_MODULE(ADS1115, "ads1115", ads1115_map, luaopen_ads1115);

+ 124 - 51
docs/en/modules/ads1115.md

@@ -3,17 +3,96 @@
 | :----- | :-------------------- | :---------- | :------ |
 | 2017-04-24 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [ads1115.c](../../../app/modules/ads1115.c)|
 
-This module provides access to the ADS1115 16-Bit analog-to-digital converter.
+This module provides access to the ADS1115 (16-Bit) and ADS1015 (12-Bit) analog-to-digital converters.
+Other chips from the same family (ADS1113, ADS1114, ADS1013 and ADS1014) are likely to work. Missing hardware features will be silently ignored.
+
+This module supports multiple devices connected to I²C bus. The devices of different types can be mixed.
+The addressing of ADS family allows for maximum of 4 devices connected to the same I²C bus.
 
 !!! caution
 
 	The **ABSOLUTE MAXIMUM RATINGS** for all analog inputs are `–0.3V to VDD+0.3V` referred to GND.
 
-## ads1115.read()
+
+
+## ads1115.ads1115()
+Registers ADS1115 (ADS1113, ADS1114) device.
+
+#### Syntax
+`ads1115.ADS1115(I2C_ID, I2C_ADDR)`
+
+#### Parameters
+- `I2C_ID` - always 0
+- `ADDRESS` - I²C address of a device
+	* `ads1115.ADDR_GND`
+	* `ads1115.ADDR_VDD`
+	* `ads1115.ADDR_SDA`
+	* `ads1115.ADDR_SCL`
+
+#### Returns
+Registered `device` object
+
+#### Example
+```lua
+local id, sda, scl = 0, 6, 5
+i2c.setup(id, sda, scl, i2c.SLOW)
+ads1115.reset()
+adc1 = ads1115.ads1115(id, ads1115.ADDR_GND)
+```
+
+
+## ads1115.ads1015()
+Registers ADS1015 (ADS1013, ADS1014) device.
+
+#### Syntax
+`ads1115.ads1015(I2C_ID, I2C_ADDR)`
+
+#### Parameters
+- `I2C_ID` - always 0
+- `ADDRESS` - I²C address of a device
+	* `ads1115.ADDR_GND`
+	* `ads1115.ADDR_VDD`
+	* `ads1115.ADDR_SDA`
+	* `ads1115.ADDR_SCL`
+
+#### Returns
+Registered `device` object
+
+#### Example
+```lua
+local id, sda, scl = 0, 6, 5
+i2c.setup(id, sda, scl, i2c.SLOW)
+ads1115.reset()
+adc1 = ads1115.ads1015(id, ads1115.ADDR_VDD)
+adc2 = ads1115.ads1115(id, ads1115.ADDR_SDA)
+```
+## ads1115.reset()
+Reset all devices connected to I²C interface.
+
+### Syntax
+ads1115.reset()
+
+#### Parameters
+none
+
+#### Returns
+`nil`
+
+#### Example
+```lua
+local id, alert_pin, sda, scl = 0, 7, 6, 5
+i2c.setup(id, sda, scl, i2c.SLOW)
+ads1115.reset()
+```
+
+
+# ADS Device
+
+## ads1115.device:read()
 Gets the result stored in the register of a previously issued conversion, e.g. in continuous mode or with a conversion ready interrupt.
 
 #### Syntax
-`volt, volt_dec, adc = ads1115.read()`
+`volt, volt_dec, adc = device:read()`
 
 #### Parameters
 none
@@ -25,39 +104,43 @@ none
 
 !!! note
 
-	If using float firmware then `volt` is a floating point number. On an integer firmware, the final value has to be concatenated from `volt` and `volt_dec`.
+	If using float firmware then `volt` is a floating point number. On an integer firmware, the final value has to be concatenated from `volt` and `volt_dec`. Both values `volt` and `volt_dec` contains sign.
 
 #### Example
 ```lua
 local id, alert_pin, sda, scl = 0, 7, 6, 5
 i2c.setup(id, sda, scl, i2c.SLOW)
-ads1115.setup(ads1115.ADDR_GND)
+ads1115.reset()
+adc1 = ads1115.ads1115(id, ads1115.ADDR_GND)
 
 -- continuous mode
-ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS)
+adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS)
 -- read adc result with read()
-volt, volt_dec, adc = ads1115.read()
+volt, volt_dec, adc = ads1:read()
 print(volt, volt_dec, adc)
 
 -- comparator
-ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS, ads1115.COMP_1CONV, 1000, 2000)
+adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS, ads1115.COMP_1CONV, 1000, 2000)
 local function comparator(level, when)
 	-- read adc result with read() when threshold reached
-	volt, volt_dec, adc = ads1115.read()
+	gpio.trig(alert_pin)
+	volt, volt_dec, adc = ads1:read()
 	print(volt, volt_dec, adc)
 end
 gpio.mode(alert_pin, gpio.INT)
 gpio.trig(alert_pin, "both", comparator)
+
 -- read adc result with read()
-volt, volt_dec, adc = ads1115.read()
+volt, volt_dec, adc = ads1115:read()
 print(volt, volt_dec, adc)
 ```
 
-## ads1115.setting()
+
+## ads1115.device:setting()
 Configuration settings for the ADC.
 
 #### Syntax
-`ads1115.setting(GAIN, SAMPLES, CHANNEL, MODE[, CONVERSION_RDY][, COMPARATOR, THRESHOLD_LOW, THRESHOLD_HI])`
+`device:setting(GAIN, SAMPLES, CHANNEL, MODE[, CONVERSION_RDY][, COMPARATOR, THRESHOLD_LOW, THRESHOLD_HI[,COMP_MODE]])`
 
 #### Parameters
 - `GAIN` Programmable gain amplifier
@@ -68,14 +151,19 @@ Configuration settings for the ADC.
 	* `ads1115.GAIN_0_512V` 8x Gain
 	* `ads1115.GAIN_0_256V` 16x Gain
 - `SAMPLES` Data rate in samples per second
-	* `ads1115.DR_8SPS`
-	* `ads1115.DR_16SPS`
-	* `ads1115.DR_32SPS`
-	* `ads1115.DR_64SPS`
+	* `ads1115.DR_8SPS` ADS1115 only
+	* `ads1115.DR_16SPS` ADS1115 only
+	* `ads1115.DR_32SPS` ADS1115 only
+	* `ads1115.DR_64SPS` ADS1115 only
 	* `ads1115.DR_128SPS`
 	* `ads1115.DR_250SPS`
-	* `ads1115.DR_475SPS`
-	* `ads1115.DR_860SPS`
+	* `ads1115.DR_475SPS` ADS1115 only
+	* `ads1115.DR_490SPS` ADS1015 only
+	* `ads1115.DR_860SPS` ADS1115 only
+	* `ads1115.DR_920SPS` ADS1015 only
+	* `ads1115.DR_1600SPS` ADS1015 only
+	* `ads1115.DR_2400SPS` ADS1015 only
+	* `ads1115.DR_3300SPS` ADS1015 only
 - `CHANNEL` Input multiplexer for single-ended or differential measurement
 	* `ads1115.SINGLE_0` channel 0 to GND
 	* `ads1115.SINGLE_1` channel 1 to GND
@@ -102,31 +190,11 @@ Configuration settings for the ADC.
 - `THRESHOLD_HI`
 	* `0` - `+ GAIN_MAX` in mV for single-ended inputs
 	* `- GAIN_MAX` - `+ GAIN_MAX` in mV for differential inputs
+- `COMP_MODE` Comparator mode
+	* `ads1115.CMODE_TRAD` traditional comparator mode (with hysteresis)
+	* `ads1115.CMODE_WINDOW` window comparator mode
 
-#### Returns
-`nil`
-
-#### Example
-```lua
-local id, sda, scl = 0, 6, 5
-i2c.setup(id, sda, scl, i2c.SLOW)
-ads1115.setup(ads1115.ADDR_GND)
-
-ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT)
-```
-
-## ads1115.setup()
-Initializes the device on the defined I²C device address.
-
-#### Syntax
-`ads1115.setup(ADDRESS)`
-
-#### Parameters
-- `ADDRESS`
-	* `ads1115.ADDR_GND`
-	* `ads1115.ADDR_VDD`
-	* `ads1115.ADDR_SDA`
-	* `ads1115.ADDR_SCL`
+note: Comparator and conversion ready are always configured to non-latching, active low.
 
 #### Returns
 `nil`
@@ -135,15 +203,18 @@ Initializes the device on the defined I²C device address.
 ```lua
 local id, sda, scl = 0, 6, 5
 i2c.setup(id, sda, scl, i2c.SLOW)
+ads1115.reset()
+adc1 = ads1115.ads1015(id, ads1115.ADDR_GND)
 
-ads1115.setup(ads1115.ADDR_GND)
+adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_3300SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT)
 ```
 
-## ads1115.startread()
+
+## ads1115.device:startread()
 Starts the ADC reading for single-shot mode and after the conversion is done it will invoke an optional callback function in which the ADC conversion result can be obtained.
 
 #### Syntax
-`ads1115.startread([CALLBACK])`
+`device:startread([CALLBACK])`
 
 #### Parameters
 - `CALLBACK` callback function which will be invoked after the adc conversion is done
@@ -156,21 +227,23 @@ Starts the ADC reading for single-shot mode and after the conversion is done it
 ```lua
 local id, alert_pin, sda, scl = 0, 7, 6, 5
 i2c.setup(id, sda, scl, i2c.SLOW)
-ads1115.setup(ads1115.ADDR_GND)
+ads1115.reset()
+adc1 = ads1115.ads1115(id, ads1115.ADDR_VDD)
 
 -- single shot
-ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT)
+adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT)
 -- start adc conversion and get result in callback after conversion is ready
-ads1115.startread(function(volt, volt_dec, adc) print(volt, volt_dec, adc) end)
+adc1:startread(function(volt, volt_dec, adc) print(volt, volt_dec, adc) end)
 
 -- conversion ready
-ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT, ads1115.CONV_RDY_1)
+adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT, ads1115.CONV_RDY_1)
 local function conversion_ready(level, when)
-	volt, volt_dec, adc = ads1115.read()
+	gpio.trig(alert_pin)
+	volt, volt_dec, adc = adc1:read()
 	print(volt, volt_dec, adc)
 end
 gpio.mode(alert_pin, gpio.INT)
 gpio.trig(alert_pin, "down", conversion_ready)
 -- start conversion and get result with read() after conversion ready pin asserts
-ads1115.startread()
+adc1:startread()
 ```