• @allObjects thanks for your help with this. So what was the actual problem at the end of all this?

    Could we roll it into a fix for the module on github?

    Using the numeric value in GAINS rather than a non-numeric is intentional, because it then uses a lot less memory (both parts should fit in one JsVar). The !(gain in GAINS) test should still work as well - I just tested - so I'm not quite sure which change actually fixed this?

    Also, you mention using the CONFIG object, but it's actually carefully arranged such that the minifier is able to totally remove it... So the nonminified version remains easy to edit, while the minified version is really compact.

    If you were to access CONFIG like CONFIG["PGA_" + this.gain + "mV"] then I'm pretty sure the minified code would end up an awful lot bigger.

  • @Gordon, line 98 is the culprit. config |= GAINS[this.gain]; (vs config |= this.gain;) will do it.

    ...and for the sake of consistency (of storing and usage application passed values vs derived values), line 74 has to read this.gain = 2048; (vs this.gain = 0;).

    ...Config ... carefully arranged... to totally remove needs a separate discussion.

  • Created pullrequest with fix in use (in covert) and init (constructor). Used allObjects git ID. My original one got messed up.

  • That's great, thanks! I've just pulled it in and I'll get it updated on the website this week.

    Bit of a stupid mistake on my part! Nice idea keeping the gain as an int though, because we could then add a new function getVolts or something that can easily re-scale the value that's returned.

  • Obviously I can't test, but am I right in thinking that the actual voltage should be value * ads.gain / 32768000?

  • ...absolutely! (My earlier - now dropped - code was wrong in this respect).

    Initally, my guts wanted to go for your formula as well - and was also part of the reason to store the users provided value with a skalar and proportianly reference to the actual value vs irregular config value - but my reasoning got messed up for some other reason and overrode my guts...

    The other part of reasoning was: if the object stores the translated property, it cannot be asked what the config was, because the GAINS 'table' object is inaccessible form the outsite and would require an extra method (getGain) to get the gain value back...

    Above paragraph is a segue way into the postponed discussion of the value of the CONFIG object as integrated part of the runtime module:

    Since the CONFIG object is not accessible for the app, it is useless... sounds harsh, but is true. I like the idea of having a nicely crafted CONFIG / options object with well named properties... but it's unavailability to the app makes not just useless, but worse: a resource freezer. It is very well compacting, but still of no use. It requires a new module... That was the reason to put config stuff into a separate module with methods to pull the desired values. Therefore, I'd still would like to go back to a ADS1x15BB - bare bones - and a ADS1x15Cfg - config - module, and the bare bones module with the optional config parm available in connect() and getADC(); I would keep it interface compatible with existing ADS1x15 module - in respect to connect(), getADC(), and (if insisted on) setGain() - to have it interchangeable, but slim down the internals.

  • Yes, in the unminified code it's a total waste of resources, but the idea is that 95% of users will be using the minified module as-is, so for them it's vital that it's efficient.

    The unminified version will at least be easy to use and add things to (after all the source is easily available) - it just means you can't extend it as-is very easily.

    It's a shame I can't do:

    ifndef MINIFIED
    exports.CONFIG = CONFIG;
    endif
    

    It'd let you develop stuff with the unminified module if you wanted it, while still allowing a compact minified version.

  • Both @Gordon @allObjects how easy would it be to implement the read differential inputs feature? Which is there in the Arduino Lib. That would round the module out and work well for load cells/strain gauges and other inputs from sensors that use a bridge.

  • Very easy - it's almost copy/paste.

    So this is the existing code: https://github.com/espruino/EspruinoDocs­/blob/master/devices/ADS1X15.js#L115-L13­8

    And it's copied from https://github.com/adafruit/Adafruit_ADS­1X15/blob/master/Adafruit_ADS1015.cpp#L1­43-L189

    So honestly it's just a matter of seeing what's changed in this code and making a new function: https://github.com/adafruit/Adafruit_ADS­1X15/blob/master/Adafruit_ADS1015.cpp#L1­99-L240

    It'd be a lot easier if someone who had one did it though, because I'll probably make some stupid mistake again :)

  • Ok I will give it a go as a standalone function as the module/Github pulls is all way beyond me.

  • @Gordon

    Obviously I can't test, but am I right in thinking that the actual voltage should be value * ads.gain / 32768000?

    That is correct have been using it for last few days.

  • @LawrenceGrif, place this in the settings - project - sandbox folder under the file ADS1x15Ext.js and load it in your app for testing... and let me know if it is any good...

    /* Copyright (c) 2015 Gordon Williams, Pur3 Ltd. See the file LICENSE for copying permission. */
    /* 
    Module for the ADS1015 4 channel ADC, single ended or differential read
    
    ` ` `
    // single-ended ADC read - channel 0:
    I2C1.setup({ scl : ..., sda: ...} );
    var ads = new ADS1X15(I2C1);
    ads.setGain(256); // +/- 0.256V
    ads.getADC(0, function(val) {
      console.log("Read single-ended ADC value: "+val);
    });
    ` ` `
    
    ` ` `
    // differential ADC read - channels [0,1]:
    I2C1.setup({ scl : ..., sda: ...} );
    var ads = new ADS1X15(I2C1);
    ads.setGain(4096); // +/- 4.096V
    ads.getADC([0,1], function(val) {
      console.log("Read differential ADC value: "+val);
    });
    ` ` `
    
     */
    
    /* Register values. Most of these aren't used
    and this is hidden, so it'll get optimised out
    when minified */
    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 DIFFS = {
      0 : { 1 : MUX_DIFF_0_1,   // Differential P = AIN0, N = AIN1 (default)
            3 : MUX_DIFF_0_3 }, // Differential P = AIN0, N = AIN3
      1 : { 3 : MUX_DIFF_1_3 }, // Differential P = AIN1, N = AIN3
      2 : { 3 : MUX_DIFF_2_3 }  // Differential P = AIN2, N = AIN3
    };
    var REG = {
      MASK : 3,
      CONVERT : 0,
      CONFIG : 1,
      LOWTHRESH : 2,
      HITHRESH : 3
    };
    function ADS1X15(i2c) {
      this.i2c = i2c;
      this.addr = 0x48;
      this.gain = 2048;
    }
    // used internally for writing to the ADC
    ADS1X15.prototype.writeRegister = function(reg, value) {
      this.i2c.writeTo(this.addr, reg, value>>8, value);  
    };
    // used internally for reading from the ADC
    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 I2C address. 
    By default it's 0x48, but it could also be 0x4B if the ADDR pin is 1  */
    ADS1X15.prototype.setAddr = function(addr) { this.addr = addr; };
    /* 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;
    };
    /* Get an ADC reading and call `callback` with it as a 16 bit value. 
    `channel` is a value between 0 and 3. */
    ADS1X15.prototype.getADC = function(channelSpec, 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)
                 ;
      // single ended (channelSpec is a number) or differential (channelSpec is array w/ valid channel duo)
      if (!isNaN(channelSpec)) { // Set single-ended input channel
        config |= [CONFIG.MUX_SINGLE_0,CONFIG.MUX_SINGLE_1­,CONFIG.MUX_SINGLE_2,CONFIG.MUX_SINGLE_3­][channelSpec];
      } else { // Set differential input channels from channelSpec
        var dif = DIFFS[channelSpec[0]];
        dif = (dif) ? dif[channelSpec[1]] : dif;
        if (typeof dif === "undefined") throw new Error("Invalid differential channelSpec " + channelSpec);
        config |= dif;
      }           
      // Set PGA/voltage range
      config |= GAINS[this.gain];
      // 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
        var d = ads.readRegister(REG.CONVERT);
        if (d&0x8000) d-=65536; // sign
        callback(d); 
      }, 8);
    };
    
    // Create an instance of ADS1X15
    exports.connect = function(/*=I2C*/i2c) {
      return new ADS1X15(i2c);
    };
    

    Usage:
    first argument - channelSpec - in getADC() is either:

    • a single channel number: 0..3 for single ended read, or
    • a valid duo of channels as an array: [0,1], [0,3], [1,3], or [2,3] for differential read

    (changed lines since first pub of extension: 145, 147)

  • Thanks! just found a small bug in this (CONFIG. was missing in some lines). I actually put a gist here.

    And you can use it like:

    var ads = require("https://gist.githubusercontent.­com/gfwilliams/da8f09f91c8d0011cbae/raw/­5a9e01ce6187f0243f4944b57c0bc1d9f4658c1f­/ADS1X15.js").connect(I2C1);
    
  • @Gordon, thanks,... ( I see... in the DIFFS... ouch!...)

    Working on the getVolts() thinghy... or do you have a better name, for example just: getV():

  • It's actually already in GitHub - I added it this morning: https://github.com/espruino/EspruinoDocs­/blob/master/devices/ADS1X15.js#L141

    Although currently it only works with the non-differential.

    Unfortunately it's not in the Gist, but if @LawrenceGrif can confirm your code works I can pull it into the main repo.

  • Incorportated getADCVoltage with channelSpec for both single-ended and differential. Waiting for @LawrenceGrif's confirmation.

    Wantering if such things could be somehow tested by emulation... w/ DI / IoC / in a pico container... (not Pico).

    I'm thinking... what javascript testing framework are you using, if any? ... I see now more and more challenges coming up that get more and more resource binding to get fixed... code base growth, platform number growth...

    With spi injection, this could be nicely tested... so we need communication emulators that can have plugins and plugin variations - (test) data driven(?) - for the different devices behavior...

    For the ADS1X15 there would be a I2C_ADS1X15 'stubber'...

  • ... but you'd end up writing a virtual ADS1X15 - and then your test is only as good as the virtual implementation?

    I guess potentially one way to handle this would be to have a few volunteers who were willing to sacrifice a Raspberry Pi and some STM32-based board. They could then connect that board up to the Pi, make a test harness with whatever hardware they wanted and some test code, and put it on the internet - the Pi could automatically download each binary, flash the STM32, run the test, and report back the result.

  • Ok just tried

    var ads = require("https://gist.githubusercontent.­com/gfwilliams/da8f09f91c8d0011cbae/raw/­82a514a0450f28b00393a507f62ca68156777a97­/ADS1X15.js");
    

    Uncaught Error: Function "setGain" not found!
    at line 1 col 5

  • Maybe try adding the .connect(...) to it.

  • Err woops I didn't notice the errors pls see attahced


    1 Attachment

    • ide_erros.png
  • Ok, for now - just go into 'Settings', then 'Minification', then make sure Module Minification is set to No Minification

  • IDE was/is set No Minification

  • For both Module Minification and Minification? Because those errors are definitely coming from the minifier, and I get exactly the same ones when I turn it on, and they disappear when I turn it off.

  • Arr OK popup errors gone still back to

    Uncaught Error: Function "setGain" not found! at line 1 col 5
    ads.setGain(_gain); // +/- 0.256mV

  • Did you add .connect(...) like I said? Try:

    I2C1.setup({ scl: B6, sda: B7 });
    var ads = require("https://gist.githubusercontent.­com/gfwilliams/da8f09f91c8d0011cbae/raw/­5a9e01ce6187f0243f4944b57c0bc1d9f4658c1f­/ADS1X15.js").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);
    
  • 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