ADS1115 analog gain signal module - how to run it with espruino?

Posted on
Page
of 4
/ 4
Last Next
  • Hi, I would like to use ADS1115 module with espruino, it is working by I2C, under arduino is nice library
    https://github.com/adafruit/Adafruit_ADS­1X15 ,
    I was trying this library to work with stm32 maple leaflabs R3+ and it was working with it well.
    I wonder how to use this module with espruino?
    Any chance you could help me to solve it?

  • Sure, I think your first port of call would be to try and replace the readRegister/writeRegister commands that were in arduino:

    static void writeRegister(uint8_t i2cAddress, uint8_t reg, uint16_t value) {
      Wire.beginTransmission(i2cAddress);
      i2cwrite((uint8_t)reg);
      i2cwrite((uint8_t)(value>>8));
      i2cwrite((uint8_t)(value & 0xFF));
      Wire.endTransmission();
    }
    
    static uint16_t readRegister(uint8_t i2cAddress, uint8_t reg) {
      Wire.beginTransmission(i2cAddress);
      i2cwrite(ADS1015_REG_POINTER_CONVERT);
      Wire.endTransmission();
      Wire.requestFrom(i2cAddress, (uint8_t)2);
      return ((i2cread() << 8) | i2cread());  
    }
    

    In Espruino I reckon they should look like this:

    var REG = {
       MASK : 3,
        CONVERT : 0,
        CONFIG : 1,
        LOWTHRESH : 2,
        HITHRESH : 3
    };
    
    function ADS1X15(i2c) {
      this.i2c = i2c;
      this.addr = 0x48;
    }
    
    ADS1X15.prototype.writeRegister = function(reg, value) {
      this.i2c.writeTo(this.addr, reg, value>>8, value);  
    };
    
    ADS1X15.prototype.readRegister = function(reg) {
      this.i2c.writeTo(this.addr, reg);  
      var d = this.i2c.readFrom(this.addr, 2);
      return (d[0] << 8) | d[1];  
    };
    
    // then try with
    I2C1.setup({ scl : ..., sda: ...} );
    var ads = new ADS1X15(I2C1);
    console.log(ads.readRegister(REG.CONVERT­));
    

    That might actually print the conversion value... But for a fully working ADC you'll need to look at readADC_SingleEnded and copy what that's doing.

    It does use a delay there, so I'd be tempted to use a callback to return the values instead, as it means Espruino can get on with other stuff in the mean time:

    ... getADC = function(callback) {
      // ..
      setTimeout(function() {
        var a = ...;
        callback(a);
      }, );
    }
    

    Maybe define a 'config' variable instead of all the #defines as well,

    var CONFIG = {
    OS_MASK      : (0x8000),
    OS_SINGLE    : (0x8000),  // Write: Set to start a single-conversion
    OS_BUSY      : (0x0000),  // Read: Bit = 0 when conversion is in progress
    OS_NOTBUSY   : (0x8000),  // Read: Bit = 1 when device is not performing a conversion
                
    MUX_MASK     : (0x7000),
    MUX_DIFF_0_1 : (0x0000),  // Differential P = AIN0, N = AIN1 (default)
    MUX_DIFF_0_3 : (0x1000),  // Differential P = AIN0, N = AIN3
    MUX_DIFF_1_3 : (0x2000),  // Differential P = AIN1, N = AIN3
    MUX_DIFF_2_3 : (0x3000),  // Differential P = AIN2, N = AIN3
    MUX_SINGLE_0 : (0x4000),  // Single-ended AIN0
    MUX_SINGLE_1 : (0x5000),  // Single-ended AIN1
    MUX_SINGLE_2 : (0x6000),  // Single-ended AIN2
    MUX_SINGLE_3 : (0x7000),  // Single-ended AIN3
                
    PGA_MASK     : (0x0E00),
    PGA_6_144V   : (0x0000),  // +/-6.144V range = Gain 2/3
    PGA_4_096V   : (0x0200),  // +/-4.096V range = Gain 1
    PGA_2_048V   : (0x0400),  // +/-2.048V range = Gain 2 (default)
    PGA_1_024V   : (0x0600),  // +/-1.024V range = Gain 4
    PGA_0_512V   : (0x0800),  // +/-0.512V range = Gain 8
    PGA_0_256V   : (0x0A00),  // +/-0.256V range = Gain 16
                
    MODE_MASK    : (0x0100),
    MODE_CONTIN  : (0x0000),  // Continuous conversion mode
    MODE_SINGLE  : (0x0100),  // Power-down single-shot mode (default)
              
    DR_MASK      : (0x00E0),  
    DR_128SPS    : (0x0000),  // 128 samples per second
    DR_250SPS    : (0x0020),  // 250 samples per second
    DR_490SPS    : (0x0040),  // 490 samples per second
    DR_920SPS    : (0x0060),  // 920 samples per second
    DR_1600SPS   : (0x0080),  // 1600 samples per second (default)
    DR_2400SPS   : (0x00A0),  // 2400 samples per second
    DR_3300SPS   : (0x00C0),  // 3300 samples per second
             
    CMODE_MASK   : (0x0010),
    CMODE_TRAD   : (0x0000),  // Traditional comparator with hysteresis (default)
    CMODE_WINDOW : (0x0010),  // Window comparator
              
    CPOL_MASK    : (0x0008),
    CPOL_ACTVLOW : (0x0000),  // ALERT/RDY pin is low when active (default)
    CPOL_ACTVHI  : (0x0008),  // ALERT/RDY pin is high when active
            
    CLAT_MASK    : (0x0004),  // Determines if ALERT/RDY pin latches once asserted
    CLAT_NONLAT  : (0x0000),  // Non-latching comparator (default)
    CLAT_LATCH   : (0x0004),  // Latching comparator
               
    CQUE_MASK    : (0x0003),
    CQUE_1CONV   : (0x0000),  // Assert ALERT/RDY after one conversions
    CQUE_2CONV   : (0x0001),  // Assert ALERT/RDY after two conversions
    CQUE_4CONV   : (0x0002),  // Assert ALERT/RDY after four conversions
    CQUE_NONE    : (0x0003),  // Disable the comparator and put ALERT/RDY in high state (default)
    };
    

    hope that helps!

  • Thank you for your effort, I am beginner with espruino and python, so don't understand much of your code, a bit better doing that with C, I mean it is more easy to me how to place the parts of the code and implement libraries, here for now it is a bit of cosmos to me

    Is there any tutorial to show how to implement new modules, libraries, and place code properly in Web IDE?

  • There's this one?

    For now, I would copy and paste this into the right-hand side of the Web IDE:

    var CONFIG = {
    OS_MASK      : (0x8000),
    OS_SINGLE    : (0x8000),  // Write: Set to start a single-conversion
    OS_BUSY      : (0x0000),  // Read: Bit = 0 when conversion is in progress
    OS_NOTBUSY   : (0x8000),  // Read: Bit = 1 when device is not performing a conversion
                
    MUX_MASK     : (0x7000),
    MUX_DIFF_0_1 : (0x0000),  // Differential P = AIN0, N = AIN1 (default)
    MUX_DIFF_0_3 : (0x1000),  // Differential P = AIN0, N = AIN3
    MUX_DIFF_1_3 : (0x2000),  // Differential P = AIN1, N = AIN3
    MUX_DIFF_2_3 : (0x3000),  // Differential P = AIN2, N = AIN3
    MUX_SINGLE_0 : (0x4000),  // Single-ended AIN0
    MUX_SINGLE_1 : (0x5000),  // Single-ended AIN1
    MUX_SINGLE_2 : (0x6000),  // Single-ended AIN2
    MUX_SINGLE_3 : (0x7000),  // Single-ended AIN3
                
    PGA_MASK     : (0x0E00),
    PGA_6_144V   : (0x0000),  // +/-6.144V range = Gain 2/3
    PGA_4_096V   : (0x0200),  // +/-4.096V range = Gain 1
    PGA_2_048V   : (0x0400),  // +/-2.048V range = Gain 2 (default)
    PGA_1_024V   : (0x0600),  // +/-1.024V range = Gain 4
    PGA_0_512V   : (0x0800),  // +/-0.512V range = Gain 8
    PGA_0_256V   : (0x0A00),  // +/-0.256V range = Gain 16
                
    MODE_MASK    : (0x0100),
    MODE_CONTIN  : (0x0000),  // Continuous conversion mode
    MODE_SINGLE  : (0x0100),  // Power-down single-shot mode (default)
              
    DR_MASK      : (0x00E0),  
    DR_128SPS    : (0x0000),  // 128 samples per second
    DR_250SPS    : (0x0020),  // 250 samples per second
    DR_490SPS    : (0x0040),  // 490 samples per second
    DR_920SPS    : (0x0060),  // 920 samples per second
    DR_1600SPS   : (0x0080),  // 1600 samples per second (default)
    DR_2400SPS   : (0x00A0),  // 2400 samples per second
    DR_3300SPS   : (0x00C0),  // 3300 samples per second
             
    CMODE_MASK   : (0x0010),
    CMODE_TRAD   : (0x0000),  // Traditional comparator with hysteresis (default)
    CMODE_WINDOW : (0x0010),  // Window comparator
              
    CPOL_MASK    : (0x0008),
    CPOL_ACTVLOW : (0x0000),  // ALERT/RDY pin is low when active (default)
    CPOL_ACTVHI  : (0x0008),  // ALERT/RDY pin is high when active
            
    CLAT_MASK    : (0x0004),  // Determines if ALERT/RDY pin latches once asserted
    CLAT_NONLAT  : (0x0000),  // Non-latching comparator (default)
    CLAT_LATCH   : (0x0004),  // Latching comparator
               
    CQUE_MASK    : (0x0003),
    CQUE_1CONV   : (0x0000),  // Assert ALERT/RDY after one conversions
    CQUE_2CONV   : (0x0001),  // Assert ALERT/RDY after two conversions
    CQUE_4CONV   : (0x0002),  // Assert ALERT/RDY after four conversions
    CQUE_NONE    : (0x0003),  // Disable the comparator and put ALERT/RDY in high state (default)
    };
    
    var REG = {
       MASK : 3,
        CONVERT : 0,
        CONFIG : 1,
        LOWTHRESH : 2,
        HITHRESH : 3
    };
    function ADS1X15(i2c) {
      this.i2c = i2c;
      this.addr = 0x48;
    }
    ADS1X15.prototype.writeRegister = function(reg, value) {
      this.i2c.writeTo(this.addr, reg, value>>8, value);  
    };
    ADS1X15.prototype.readRegister = function(reg) {
      this.i2c.writeTo(this.addr, reg);  
      var d = this.i2c.readFrom(this.addr, 2);
      return (d[0] << 8) | d[1];  
    };
    
    ADS1X15.prototype.getADC = function(channel, callback) {
      var config = CONFIG.CQUE_NONE    | // Disable the comparator (default val)
                        CONFIG.CLAT_NONLAT  | // Non-latching (default val)
                        CONFIG.CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
                        CONFIG.CMODE_TRAD   | // Traditional comparator (default val)
                        CONFIG.DR_1600SPS   | // 1600 samples per second (default)
                        CONFIG.MODE_SINGLE;   // Single-shot mode (default)
    
      // Set PGA/voltage range
      // TODO: config |= CONFIG.PGA_something
    
      // Set single-ended input channel
      config |= [CONFIG.MUX_SINGLE_0,CONFIG.MUX_SINGLE_1­,CONFIG.MUX_SINGLE_2,CONFIG.MUX_SINGLE_3­][channel];
    
      // Set 'start single-conversion' bit
      config |= CONFIG.OS_SINGLE;
    
      // Write config register to the ADC
      this.writeRegister(REG.POINTER_CONFIG, config);
    
      // Wait for the conversion to complete
      var ads = this;
      setTimeout(function() {
        // Read the conversion results
        // Shift 12-bit results right 4 bits for the ADS1015
        callback(ads.readRegister(REG.POINTER_CO­NVERT)); 
      }, 8);
    };
    
    // then try with
    I2C1.setup({ scl : ..., sda: ...} );
    var ads = new ADS1X15(I2C1);
    
    ads.getADC(0, function(val) {
      console.log("Reading: "+val);
    });
    

    Change I2C1.setup pins to whatever you're using, and then see what happens...

    It might be that it works, but I haven't been able to test it.

    When it is working properly, it's easy enough to convert it into a module - just remove the few 'testing' lines at the end and add exports.connect = function(i2c) { return new ADS1X15(i2c); }, then you're sorted.

  • will check it at the evening, no errors in WEB IDE ;)

  • I've got that error when wanted to upload it into board

    WARNING: Expecting a number or something iterable, got undefined
    WARNING: Expecting a number or something iterable, got undefined
    =undefined
    Uncaught InternalError: Timeout on I2C Write Transmit Mode 2
     at line 2 col 55
          this.i2c.writeTo(this.addr, reg, value>>8, value);
                                                           ^
    in function "writeRegister" called from line 15 col 52
          this.writeRegister(REG.POINTER_CONFIG, config);
                                                        ^
    in function "getADC" called from line 3 col 6
        });
          ^
    
  • I2C1.setup({ scl : ..., sda: ...} );

    Did you fill in which I2C pins you have it connected to there?

  • Yes I did try to choose both pins from both ports (I2C1 and I2C2) from my maple leaflabs,
    I put there scl: D5, sda: D9 - for I2C1 and scl: D29, sda: D30 for I2C2 option.
    Also tried to mark them that way scl: PB6, sda: PB7 and for I2c2 port, scl: PB10, sda: PB11.
    but still got this error

    WARNING: Expecting a number or something iterable, got undefined
    WARNING: Expecting a number or something iterable, got undefined
    =undefined
    WARNING: Expecting a number or something iterable, got undefined
    WARNING: Expecting a number or something iterable, got undefined
    Reading: 0
    
  • Have you tried pull resistors on the ic2 scl & sda to to vdd?
    Are you powering the ADS1115 from 5v or 3.3v?
    Not sure if your maple leaflabs i2c pins are 5v tolerant?

  • It's a bug in the code I posted - as I don't have anything here to test with. REG.POINTER_CONFIG should be REG.CONFIG. If you change that in the code it might work?

    Same with REG.POINTER_CONVERT to REG.CONVERT

  • code works ;)
    it return one value, but only once, when I upload the code by Web IDE
    do you know which function will let me to print values from one of 4 channels in which ADS1115 is equipped?
    and how to use gain settings like it is in arduino library for this module?

  • Have you tried calling the following code again:

    ads.getADC(0, function(val) {
      console.log("Reading: "+val);
    });
    

    The first argument is a value between 0 and 3 (for each input).

    As with other things in Espruino, the code works with a callback (so Espruino can do other things while it's waiting for the ADC). For setting the gain, use the following code, but with the ads.setGain(...), where ... is a value in mv (6144, 4096, 2048, 1024, 512 or 256). The value is the full swing, so 256 = +/- 0.256v

    var CONFIG = {
    OS_MASK      : (0x8000),
    OS_SINGLE    : (0x8000),  // Write: Set to start a single-conversion
    OS_BUSY      : (0x0000),  // Read: Bit = 0 when conversion is in progress
    OS_NOTBUSY   : (0x8000),  // Read: Bit = 1 when device is not performing a conversion
                
    MUX_MASK     : (0x7000),
    MUX_DIFF_0_1 : (0x0000),  // Differential P = AIN0, N = AIN1 (default)
    MUX_DIFF_0_3 : (0x1000),  // Differential P = AIN0, N = AIN3
    MUX_DIFF_1_3 : (0x2000),  // Differential P = AIN1, N = AIN3
    MUX_DIFF_2_3 : (0x3000),  // Differential P = AIN2, N = AIN3
    MUX_SINGLE_0 : (0x4000),  // Single-ended AIN0
    MUX_SINGLE_1 : (0x5000),  // Single-ended AIN1
    MUX_SINGLE_2 : (0x6000),  // Single-ended AIN2
    MUX_SINGLE_3 : (0x7000),  // Single-ended AIN3
                
    PGA_MASK     : (0x0E00),
    PGA_6_144V   : (0x0000),  // +/-6.144V range = Gain 2/3
    PGA_4_096V   : (0x0200),  // +/-4.096V range = Gain 1
    PGA_2_048V   : (0x0400),  // +/-2.048V range = Gain 2 (default)
    PGA_1_024V   : (0x0600),  // +/-1.024V range = Gain 4
    PGA_0_512V   : (0x0800),  // +/-0.512V range = Gain 8
    PGA_0_256V   : (0x0A00),  // +/-0.256V range = Gain 16
                
    MODE_MASK    : (0x0100),
    MODE_CONTIN  : (0x0000),  // Continuous conversion mode
    MODE_SINGLE  : (0x0100),  // Power-down single-shot mode (default)
              
    DR_MASK      : (0x00E0),  
    DR_128SPS    : (0x0000),  // 128 samples per second
    DR_250SPS    : (0x0020),  // 250 samples per second
    DR_490SPS    : (0x0040),  // 490 samples per second
    DR_920SPS    : (0x0060),  // 920 samples per second
    DR_1600SPS   : (0x0080),  // 1600 samples per second (default)
    DR_2400SPS   : (0x00A0),  // 2400 samples per second
    DR_3300SPS   : (0x00C0),  // 3300 samples per second
             
    CMODE_MASK   : (0x0010),
    CMODE_TRAD   : (0x0000),  // Traditional comparator with hysteresis (default)
    CMODE_WINDOW : (0x0010),  // Window comparator
              
    CPOL_MASK    : (0x0008),
    CPOL_ACTVLOW : (0x0000),  // ALERT/RDY pin is low when active (default)
    CPOL_ACTVHI  : (0x0008),  // ALERT/RDY pin is high when active
            
    CLAT_MASK    : (0x0004),  // Determines if ALERT/RDY pin latches once asserted
    CLAT_NONLAT  : (0x0000),  // Non-latching comparator (default)
    CLAT_LATCH   : (0x0004),  // Latching comparator
               
    CQUE_MASK    : (0x0003),
    CQUE_1CONV   : (0x0000),  // Assert ALERT/RDY after one conversions
    CQUE_2CONV   : (0x0001),  // Assert ALERT/RDY after two conversions
    CQUE_4CONV   : (0x0002),  // Assert ALERT/RDY after four conversions
    CQUE_NONE    : (0x0003),  // Disable the comparator and put ALERT/RDY in high state (default)
    };
    var GAINS = {
      6144 : CONFIG.PGA_6_144V,  // +/-6.144V range = Gain 2/3
      4096 : CONFIG.PGA_4_096V,  // +/-4.096V range = Gain 1
      2048 : CONFIG.PGA_2_048V,  // +/-2.048V range = Gain 2 (default)
      1024 : CONFIG.PGA_1_024V,  // +/-1.024V range = Gain 4
      512 : CONFIG.PGA_0_512V,  // +/-0.512V range = Gain 8
      256 : CONFIG.PGA_0_256V,  // +/-0.256V range = Gain 16;
    };
    var REG = {
      MASK : 3,
      CONVERT : 0,
      CONFIG : 1,
      LOWTHRESH : 2,
      HITHRESH : 3
    };
    function ADS1X15(i2c) {
      this.i2c = i2c;
      this.addr = 0x48;
      this.gain = 0;
    }
    
    ADS1X15.prototype.writeRegister = function(reg, value) {
      this.i2c.writeTo(this.addr, reg, value>>8, value);  
    };
    ADS1X15.prototype.readRegister = function(reg) {
      this.i2c.writeTo(this.addr, reg);  
      var d = this.i2c.readFrom(this.addr, 2);
      return (d[0] << 8) | d[1];  
    };
    /// set the gain, with a value in mv (6144, 4096, 2048, 1024, 512 or 256). The value is the full swing, so 256 = +/- 0.256v
    ADS1X15.prototype.setGain = function(gain) {
      if (!(gain in GAINS)) throw new Error("Gain "+gain+" not found");
      this.gain = gain;
    };
    ADS1X15.prototype.getADC = function(channel, callback) {
      var config = CONFIG.CQUE_NONE    | // Disable the comparator (default val)
                        CONFIG.CLAT_NONLAT  | // Non-latching (default val)
                        CONFIG.CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
                        CONFIG.CMODE_TRAD   | // Traditional comparator (default val)
                        CONFIG.DR_1600SPS   | // 1600 samples per second (default)
                        CONFIG.MODE_SINGLE;   // Single-shot mode (default)
      // Set PGA/voltage range
      config |= this.gain;
      // Set single-ended input channel
      config |= [CONFIG.MUX_SINGLE_0,CONFIG.MUX_SINGLE_1­,CONFIG.MUX_SINGLE_2,CONFIG.MUX_SINGLE_3­][channel];
      // Set 'start single-conversion' bit
      config |= CONFIG.OS_SINGLE;
      // Write config register to the ADC
      this.writeRegister(REG.CONFIG, config);
      // Wait for the conversion to complete
      var ads = this;
      setTimeout(function() {
        // Read the conversion results
        // Shift 12-bit results right 4 bits for the ADS1015
        callback(ads.readRegister(REG.CONVERT));­ 
      }, 8);
    };
    
    
    // then try with
    I2C1.setup({ scl : ..., sda: ...} );
    var ads = new ADS1X15(I2C1);
    ads.setGain(256); // +/- 0.256mV
    ads.getADC(0, function(val) {
      console.log("Reading: "+val);
    });
    
  • thank you Gordon, it is working ;) play with Espruino is much easier than C and PIC modules I see ;)
    could you show me some simple construction about callback?

  • I'm not sure what to suggest. This is just like setTimeout.

    Maybe you want to read 4 values every second and write them to the console, you could do:

    function everySecond() {
     ads.getADC(0, function(val0) {
      ads.getADC(1, function(val1) {
       ads.getADC(2, function(val2) {
        ads.getADC(3, function(val3) {
         console.log(val0,val1,val2,val3);
        });
       });
      });
     });
    }
    
    setInterval(everySecond, 1000);
    

    Basically you call the function and it returns almost immediately. However it calls the function you supply at some point later on.

  • I have also just put the ADS1X15 module onto the website, so you no longer need all the code I posted. You just need:

    I2C1.setup({ scl : ..., sda: ...} );
    var ads = require("ADS1X15").connect(I2C1); // <--- this now pulls everything from the internet
    ads.setGain(256); // +/- 0.256mV
    ads.getADC(0, function(val) {
      console.log("Reading: "+val);
    });
    
  • thank you for a lot of your work Gordon, now it would be much much easier for others ;)

    Do you know how to set print or console.log function to show float value like from analogRead but only to show 3 places after .dot ? eg. (1.234V)

  • Try:

    console.log((val*0.256/32768).toFixed(3)­ + "V");
    

    You'll probably have to change 0.256 depending on the current gain of your ADC. The .toFixed(3) turns it into a string with 3 decimal places.

  • Gordon do you know how to get those ads 4 channels values print on HD44780?
    your console.log construction is working in the console but didn't figure out yet
    how to print those values there, it looks like it should be a different way than
    analogRead values?

  • I've got it ;)

  • Ok I'm having a senior moment I've clearly misunderstood how prog gain works
    I've been banging away at this all afternoon and not getting anywhere.
    Tried 2 different ADS1115 (16bit)

    I'm tying to measure 0 to 3.3v analog in
    So started with gain setting is 4096 +/- 4.096V
    So if I understand this correctly an input voltage of 2.048 should give 50% of range 32768 and 4.096v would give 65536

    I can't get an val that correlates in any way.
    I've tried other gains same result.
    Any ideas welcomed with thanks.

    Lawrence

  • Update this gives me the correct voltage in but I don't understand why?
    Gain is 4096 & voltage multiplier 6.144

    I2C1.setup({ scl: B6, sda: B7 });
    var ads = require("ADS1X15").connect(I2C1);
    ads.setGain(4096); 
    
    setInterval(function(){
      ads.getADC(3, function(val) {
          console.log("val:"+val+" v:"+(val*6.144/32768).toFixed(3) + "v");     
      });
    },3000);
    
  • If gain is set to 4096 (accoriding to Table 3 on page 13 of TI ADS1115 datasheet:

    • measurement range is from -4.096V..4.095999V
    • values are (always) -32768..0..32767
      (1 value unit equals to 0.000125V)

    Therefore, for 2.048V you should get a value of 16384, which is at half in the positive half of the value range... 0..16384..32767

    6.144/32768 = 0.0001875 - about 50% off from the above calculated 4.096 related 0.000125 value, therefore, I 'assume', the gain setting is not working correctly... it took 6.144 because:

    I did look at the 'last' 'module' code and my language concious did not 'like' property names of the GAINS object that are pure numbers: lines 57..62 - even though it obviously works partially, because you do not mention the built-in error (...! in GAINS) has not caugth on). BUT: ... | 4096 is | 2 ^ 12 or | 0b0001'0000"0000'0000 or | 0x1000 - just sets bit 12 (counting from 0) outside of the PGA_MASK... not what you want. Obviously, it did not mess up, at least not totally. Since it does not throw an error it clears all (gain mattering) bit(s) - see PGA_MASK - and that ets the gain to 2/3: -6.144..0..+6.144 volts, which explains the 6.144 / 32768 term that worked for you. Make the following changes, and things should just work as expected:

    Lines 56..63:

    var GAINS = {
      G6144 : CONFIG.PGA_6_144V,  // +/-6.144V range = Gain 2/3
      G4096 : CONFIG.PGA_4_096V,  // +/-4.096V range = Gain 1
      G2048 : CONFIG.PGA_2_048V,  // +/-2.048V range = Gain 2 (default)
      G1024 : CONFIG.PGA_1_024V,  // +/-1.024V range = Gain 4
      G512 : CONFIG.PGA_0_512V,  // +/-0.512V range = Gain 8
      G256 : CONFIG.PGA_0_256V,  // +/-0.256V range = Gain 16;
    };
    

    Lines 71..75:

    function ADS1X15(i2c) {
      this.i2c = i2c;
      this.addr = 0x48;
      this.gain = 2048;
    }
    

    Lines 80..84:

    ADS1X15.prototype.readRegister = function(reg) {
      this.i2c.writeTo(this.addr, reg);  
      var d = this.i2c.readFrom(this.addr, 2);
      return ((d[0] << 8) | d[1]) / 32768;  // returning value in volts
    };
    

    Lines 85..89:

    // set the gain, with a value in mv (6144, 4096, 2048, 1024, 512 or 256). The value is the full swing, so 256 = +/- 0.256v
    ADS1X15.prototype.setGain = function(gain) {
      if (!(("G" + gain) in GAINS)) throw new Error("Gain "+gain+" not found");
      this.gain = gain;
    };
    

    Lines 97 and 98:

      // Set PGA/voltage range
      config |= GAINS["G"+this.gain];
    

    Usage:

    I2C1.setup({ scl: B6, sda: B7 });
    var ads = require("ADS1X15").connect(I2C1);
    ads.setGain(4096); 
    setInterval(function(){
      ads.getADC(3, function(val) {
          console.log(v.toFixed(3) + "V");     
      });
    },3000);
    

    Now there are a few memory saving options that can be applied...

    a) Throw GAINS away. There is already an object in place that holds the gains: CONFIG... therefore, lines 87. and 98. each become 2 lines and read:

       var g = "PGA_" + ("0" + this.gain + " V").substr(-5); 
       if (!(g.substr(0,5) + "_" +  g.substr(5)) in CONFIG)) throw new Error("Gain "+gain+" not found");
    
      var g = "PGA_" + ("0" + this.gain + " V").substr(-5); 
      config |= CONFIG[g.substr(0,5) + "_" +  g.substr(5)];
    

    b) Simplify the gains part in CONFIG like you had it in GAINS and it forming the property names becomes inline instead of an extra composition line for string g:

    Lines 18..23:

    PGA_6144mV: 0x0000,  // +/-6.144V range = Gain 2/3
    PGA_4096mV: 0x0200,  // +/-4.096V range = Gain 1
    PGA_2048mV: 0x0400,  // +/-2.048V range = Gain 2 (default)
    PGA_1024mV: 0x0600,  // +/-1.024V range = Gain 4
    PGA_512mV: 0x0800,  // +/-0.512V range = Gain 8
    PGA_256mV: 0x0A00,  // +/-0.256V range = Gain 16
    
        if (!("PGA_" + this.gain + "mV") in CONFIG)) throw new Error("Gain "+gain+" not found");
    
        config |= CONFIG["PGA_" + this.gain + "mV"];
    

    Btw, I wondered why the config is set all the times, is it not sufficient to set it once? Could make it sense to have 4 configs (3, or 2 in - 1 or 2 - diff mode)?

  • @allObjects your real star. Thanks so much for taking the time to dig this one out. So poor assumptions on my part didn't help. I made the fixes to inline code @Gordon used initially (it was a straight port from Arduino Lib). As for modifying the module I can't even find it on Git let alone fork it!

    For the optimization I will leave that to @Gordon.
    One thing Lines 80..84: think it's best to return the raw value and allow user to scale to suite.

    Thanks again
    Lawrence

  • @LawrenceGrif, np, urvw. Since the division is always by 32768 and the raw value is config dependent, I did not see any value in delivering the raw value...

    Ports from Arduino have their challenges: it is a very good start. In this case, I have the feeling that not all things made it through - first functionl, and second Espruino typical / spirit. The code is in an intermediary stage. For usage different from defaults, only gain made it.

    For me, Espruino typical is taking advantage of JavaScript's dynamics / no compile and easy to use (litteral) object-orientation. For that I expect to pass optionally a config (modifying) object at setup time as well as at read / conversion time. The current code looks like a lot of compile language with all the symbols in the CONFIG object - which are just space-eat-aways in Espruino. Yes, it can make the code very readable and its usage 'simpler', but for quite a high price. For keeping both - frugal resource use under constraint and easy config under no constraints - and applying either only one or both depending the situation, splitting the code into two modules ADS1x15 and ADS1x5Cfg a good solution:

    1. ADS1x15 module is bare metal operation code wit optional acceptance of a config value in masked config register format: {cfg:0x####, msk:0x####}. cfg includes the bits to set msk is the (combined) mask.
    2. ADS1x15 module provides code to easily (and safely) create the config object from scratch or applies changes on the retrievable register object.

    I can see applications where changing gain between readings of the very same channel makes sense: 1st read is to find the value range, second read is for more digits for that channel's final read... assuming that the value is slow changin / time does not play that a signifcant role.

    Example code for passing config or config modification information at connect time - adjusting default created registry object with 4096 (= 0x0200) gain:

    // var ads = new ADS1x15(i2c,0x48,{cfg:0x0200, msk:0x0E00});
    var ads = ADS1x15Mod.connect(i2c,0x48,{cfg:0x0200,­ msk:0x0E00});
    // var ads = require("ADS1x15").connect(i2c,0x48,{cfg­:0x0200, msk:0x0E00});
    

    Example code for passing config or config modification information at conversion time - adjusting default created registry object with 2048 (= 0x0200) - back to default - gain:

    ads.getADC(1,function(v){ console.log("CH1: " + v + "V"); },{cfg:0x0400, msk:0x0E00});
    

    Example code for bare operations module (0x8583 sets defaults):

    .
    ..
    ...
    var ADS1x15 = function(i2c,addr,cfg) {
      this.i2c = i2c;
      this.addr = addr;
      this.reg = 0x8583;
      if (cfg) { this.cfg(cfg); }
    }, p = ADS1x15.prototype;
    
    p.cfg = function(cfg) {
      this.reg = (this.reg  & (0xFFFF ^ cfg.msk)) || (cfg.cfg & cfg.msk);
    };
    
    p.writeReg = function(reg, val) {
      this.i2c.writeTo(this.addr, reg, val>>8, val);  
    };
    
    p.readReg = function(reg) {
      this.i2c.writeTo(this.addr, reg);  
      var d = this.i2c.readFrom(this.addr, 2);
      return (d[0] << 8) | d[1];  
    };
    
    p.getADC = function(channel, callback,cfg) {
      if (cfg) { this.cfg(cfg); }
      this.writeReg(1, this.cfg || (channel << 12)));
      var _this = this;
      setTimeout(function(){ callback(_this.readReg(0)/32768); }, 8);
    };
    
    ADS1x15xMod = 
    { connect: function(i2c,addr,cfg) { 
        return new ADS1x1x(i2c,addr,cfg);
      }
    }
    // export(ADS1x15Mod);
    ...
    ..
    .
    

    Btw, noticed that there is no need to do bit shifting on result for AD10XX series. The read raw value hast just a less fine granulation (resolution)... smart choice from TI: ADS1x1x are pin compatible with the ADS101x series being just less accurate...

    As mentioned earlier, I'd have a gain config for each channel (gain flexibility saves hardware...). Therefore, gain may be a thing to handle differently from passing other config values.

    Since sequencing is predictable by design of the app - assuming all of the design is controllable - busy checking and other more sophisticate things to make operation safe would only bloat the bare metal module.

    The config helper / support module may show as a separate post...

    PS: Code only partially verified... have no ADS1x1x at hand... :( ... :)

  • @allObjects as a way of saying thanks would be happy to get an ADS1X15 to you for testing. Would that be OK with you? If so direct mail and I'll arrange.

    Lawrence

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

ADS1115 analog gain signal module - how to run it with espruino?

Posted by Avatar for bigplik @bigplik

Actions