Advertisement
Guest User

Untitled

a guest
Nov 18th, 2020
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  *
  3.  * ABElectronics Expander Pi - 32 Digital IO, 8 ADC, 2 DAC and a real-time clock
  4.  * For use with the Expander Pi
  5.  * Version 1.0 Created 19/06/2017
  6.  *
  7.  * Requires rpio to be installed, install with: npm install rpio
  8.  *
  9.  */
  10. var rpio = require("rpio");
  11. const Constants = require("./constants");
  12. const Bme680Data = require("./bme680Data");
  13. const CalibrationData = require("./calibrationData");
  14. const constants = new Constants();
  15. const bme680Data = new Bme680Data();
  16.  
  17. /**
  18.  * IO Class
  19.  */
  20. ExpanderPiIO = (function () {
  21.   // Define registers values from datasheet
  22.   const IODIRA = 0x00; // IO direction A - 1= input 0 = output
  23.   const IODIRB = 0x01; // IO direction B - 1= input 0 = output
  24.   // Input polarity A - If a bit is set, the corresponding GPIO register bit will reflect the inverted value on the pin.
  25.   const IPOLA = 0x02;
  26.   // Input polarity B - If a bit is set, the corresponding GPIO register bit will reflect the inverted value on the pin.
  27.   const IPOLB = 0x03;
  28.   // The GPINTEN register controls the interrupt-onchange feature for each pin on port A.
  29.   const GPINTENA = 0x04;
  30.   // The GPINTEN register controls the interrupt-onchange feature for each pin on port B.
  31.   const GPINTENB = 0x05;
  32.   // Default value for port A - These bits set the compare value for pins configured for interrupt-on-change.
  33.   // If the associated pin level is the opposite from the register bit, an interrupt occurs.
  34.   const DEFVALA = 0x06;
  35.   // Default value for port B - These bits set the compare value for pins configured for interrupt-on-change. If the associated pin level is the
  36.   // opposite from the register bit, an interrupt occurs.
  37.   const DEFVALB = 0x07;
  38.   // Interrupt control register for port A.  If 1 interrupt is fired when the pin matches the default value, if 0 the interrupt is fired on state change
  39.   const INTCONA = 0x08;
  40.   // Interrupt control register for port B.  If 1 interrupt is fired when the pin matches the default value, if 0 the interrupt is fired on state change
  41.   const INTCONB = 0x09;
  42.   const IOCON = 0x0a; // see datasheet for configuration register
  43.   const GPPUA = 0x0c; // pull-up resistors for port A
  44.   const GPPUB = 0x0d; // pull-up resistors for port B
  45.   // The INTF register reflects the interrupt condition on the port A pins of any pin that is enabled for interrupts. A set bit indicates that the
  46.   // associated pin caused the interrupt.
  47.   const INTFA = 0x0e;
  48.   // The INTF register reflects the interrupt condition on the port B pins of any pin that is enabled for interrupts. A set bit indicates that the
  49.   // associated pin caused the interrupt.
  50.   const INTFB = 0x0f;
  51.   // The INTCAP register captures the GPIO port A value at the time the interrupt occurred.
  52.   const INTCAPA = 0x10;
  53.   // The INTCAP register captures the GPIO port B value at the time the interrupt occurred.
  54.   const INTCAPB = 0x11;
  55.   const GPIOA = 0x12; // Data port A
  56.   const GPIOB = 0x13; // Data port B
  57.   const OLATA = 0x14; // Output latches A
  58.   const OLATB = 0x15; // Output latches B
  59.  
  60.   // variables
  61.   ExpanderPiIO.prototype.portADir = 0x00; // port a direction
  62.   ExpanderPiIO.prototype.portBDir = 0x00; // port b direction
  63.   ExpanderPiIO.prototype.portAVal = 0x00; // port a value
  64.   ExpanderPiIO.prototype.portBVal = 0x00; // port b value
  65.   ExpanderPiIO.prototype.portAPullup = 0x00; // port a pull-up resistors
  66.   ExpanderPiIO.prototype.portBPullup = 0x00; // port a pull-up resistors
  67.   ExpanderPiIO.prototype.portAPolarity = 0x00; // input polarity for port a
  68.   ExpanderPiIO.prototype.portBPolarity = 0x00; // input polarity for port b
  69.   ExpanderPiIO.prototype.intA = 0x00; // interrupt control for port a
  70.   ExpanderPiIO.prototype.intB = 0x00; // interrupt control for port a
  71.   // initial configuration - see IOCON page in the MCP23017 datasheet for more information.
  72.   ExpanderPiIO.prototype.config = 0x22;
  73.  
  74.   ExpanderPiIO.prototype.i2caddress = 0x20;
  75.  
  76.   /**
  77.    * Initialize the I2C bus based on the supplied address
  78.    * Load the default configuration, all pins are inputs with pull- ups disabled
  79.    */
  80.   function ExpanderPiIO() {
  81.     rpio.i2cBegin();
  82.     rpio.i2cSetSlaveAddress(this.i2caddress);
  83.     this.i2cWriteByte(IOCON, this.config);
  84.     this.portAVal = this.i2cReadByte(GPIOA);
  85.     this.portBVal = this.i2cReadByte(GPIOB);
  86.     this.i2cWriteByte(IODIRA, 0xff);
  87.     this.i2cWriteByte(IODIRB, 0xff);
  88.     this.setPortPullups(0, 0x00);
  89.     this.setPortPullups(1, 0x00);
  90.     this.invertPort(0, 0x00);
  91.     this.invertPort(1, 0x00);
  92.   }
  93.  
  94.   /**
  95.    * private functions
  96.    */
  97.  
  98.   ExpanderPiIO.prototype.i2cReadByte = function (val) {
  99.     var txbuf = new Buffer([val]);
  100.     var rxbuf = new Buffer(1);
  101.     rpio.i2cSetSlaveAddress(this.i2caddress);
  102.     rpio.i2cWrite(txbuf);
  103.     rpio.i2cRead(rxbuf, 1);
  104.     return rxbuf[0];
  105.   };
  106.  
  107.   ExpanderPiIO.prototype.i2cWriteByte = function (register, val) {
  108.     rpio.i2cSetSlaveAddress(this.i2caddress);
  109.     var txbuf = new Buffer([register, val]);
  110.     var rxbuf = new Buffer(1);
  111.     rpio.i2cWrite(txbuf);
  112.   };
  113.  
  114.   updateByte = function (oldByte, bit, value) {
  115.     // internal function for setting the value of a single bit within a byte
  116.     var newByte = 0;
  117.     if (value == false) {
  118.       newByte = oldByte & ~(1 << bit);
  119.     } else {
  120.       newByte = oldByte | (1 << bit);
  121.     }
  122.     return newByte;
  123.   };
  124.  
  125.   function checkBit(num, bit) {
  126.     return (num >> bit) % 2 != 0;
  127.   }
  128.  
  129.   /**
  130.    * public functions
  131.    */
  132.  
  133.   /**
  134.    * Set IO direction for an individual pin
  135.    * @param  {number} pin - 1 to 16
  136.    * @param  {number} direction - 1 = input, 0 = output
  137.    */
  138.   ExpanderPiIO.prototype.setPinDirection = function (pin, direction) {
  139.     pin = pin - 1;
  140.     if (pin < 8) {
  141.       this.portADir = updateByte(this.portADir, pin, direction);
  142.       this.i2cWriteByte(IODIRA, portADir);
  143.     } else {
  144.       this.portBDir = updateByte(this.portBDir, pin - 8, direction);
  145.       this.i2cWriteByte(IODIRB, portBDir);
  146.     }
  147.   };
  148.  
  149.   /**
  150.    * Set direction for an IO port
  151.    * @param  {number} port - 0 = pins 1 to 8, 1 = pins 8 to 16
  152.    * @param  {number} direction - 0 to 255. For each bit 1 = input, 0 = output
  153.    */
  154.   ExpanderPiIO.prototype.setPortDirection = function (port, direction) {
  155.     if (port == 1) {
  156.       this.i2cWriteByte(IODIRB, direction);
  157.       this.portBDir = direction;
  158.     } else {
  159.       this.i2cWriteByte(IODIRA, direction);
  160.       this.portADir = direction;
  161.     }
  162.   };
  163.  
  164.   /**
  165.    * Set the internal 100K pull-up resistors for an individual pin
  166.    * @param  {number} pin - 1 to 16
  167.    * @param  {number} value - 1 = enabled, 0 = disabled
  168.    */
  169.   ExpanderPiIO.prototype.setPinPullup = function (pin, value) {
  170.     pin = pin - 1;
  171.     if (pin < 8) {
  172.       this.portAPullup = updateByte(this.portAPullup, pin, value);
  173.       this.i2cWriteByte(GPPUA, this.portAPullup);
  174.     } else {
  175.       this.portBPullup = updateByte(this.portBPullup, pin - 8, value);
  176.       this.i2cWriteByte(GPPUB, this.portBPullup);
  177.     }
  178.   };
  179.  
  180.   /**
  181.    * Set the internal 100K pull-up resistors for the selected IO port
  182.    * @param  {number} port - 0 = pins 1 to 8, 1 = pins 8 to 16
  183.    * @param  {number} value - 0 to 255. For each bit 1 = input, 0 = output
  184.    */
  185.   ExpanderPiIO.prototype.setPortPullups = function (port, value) {
  186.     if (port == 1) {
  187.       this.portBPullup = value;
  188.       this.i2cWriteByte(GPPUB, value);
  189.     } else {
  190.       this.portAPullup = value;
  191.       this.i2cWriteByte(GPPUA, value);
  192.     }
  193.   };
  194.  
  195.   /**
  196.    * Write to an individual pin
  197.    * @param  {number} pin - 1 to 16
  198.    * @param  {number} value - 1 = enabled, 0 = disabled
  199.    */
  200.   ExpanderPiIO.prototype.writePin = function (pin, value) {
  201.     //
  202.     //  write to an individual pin 1 - 16
  203.     //
  204.     pin = pin - 1;
  205.     if (pin < 8) {
  206.       this.portAVal = updateByte(this.portAVal, pin, value);
  207.       this.i2cWriteByte(GPIOA, this.portAVal);
  208.     } else {
  209.       this.portBVal = updateByte(this.portBVal, pin - 8, value);
  210.       this.i2cWriteByte(GPIOB, this.portBVal);
  211.     }
  212.   };
  213.  
  214.   /**
  215.    * Write to all pins on the selected port
  216.    * @param  {number} port - 0 = pins 1 to 8, 1 = pins 8 to 16
  217.    * @param  {number} value - number between 0 and 255 or 0x00 and 0xFF
  218.    */
  219.   ExpanderPiIO.prototype.writePort = function (port, value) {
  220.     if (port == 1) {
  221.       this.i2cWriteByte(GPIOB, value);
  222.       this.portBVal = value;
  223.     } else {
  224.       this.i2cWriteByte(GPIOA, value);
  225.       this.portAVal = value;
  226.     }
  227.   };
  228.  
  229.   /**
  230.    * read the value of an individual pin 1 - 16
  231.    * @param  {number} pin - 1 to 16
  232.    * @returns {number} - 0 = logic level low, 1 = logic level high
  233.    */
  234.   ExpanderPiIO.prototype.readPin = function (pin) {
  235.     pin = pin - 1;
  236.     if (pin < 8) {
  237.       this.portAVal = this.i2cReadByte(GPIOA);
  238.       return checkBit(this.portAVal, pin);
  239.     } else {
  240.       pin = pin - 8;
  241.       this.portBVal = this.i2cReadByte(GPIOB);
  242.       return checkBit(this.portBVal, pin);
  243.     }
  244.   };
  245.  
  246.   /**
  247.    * Read all pins on the selected port
  248.    * @param  {number} port - port 0 = pins 1 to 8, port 1 = pins 8 to 16
  249.    * @returns {number} - number between 0 and 255 or 0x00 and 0xFF
  250.    */
  251.   ExpanderPiIO.prototype.readPort = function (port) {
  252.     if (port == 1) {
  253.       this.portBVal = this.i2cReadByte(GPIOB);
  254.       return this.portBVal;
  255.     } else {
  256.       this.portAVal = this.i2cReadByte(GPIOA);
  257.       return this.portAVal;
  258.     }
  259.   };
  260.  
  261.   /**
  262.    * Invert the polarity of the pins on a selected port
  263.    * @param  {number} port - port 0 = pins 1 to 8, port 1 = pins 8 to 16
  264.    * @param  {number} polarity - 0 = same logic state of the input pin, 1 = inverted logic state of the input pin
  265.    */
  266.   ExpanderPiIO.prototype.invertPort = function (port, polarity) {
  267.     if (port == 1) {
  268.       this.i2cWriteByte(IPOLB, polarity);
  269.       this.portBPolarity = polarity;
  270.     } else {
  271.       this.i2cWriteByte(IPOLA, polarity);
  272.       this.portAPolarity = polarity;
  273.     }
  274.   };
  275.  
  276.   /**
  277.    * Invert the polarity of the selected pin
  278.    * @param  {number} pin - 1 to 16
  279.    * @param  {number} polarity - 0 = same logic state of the input pin, 1 = inverted logic state of the input pin
  280.    */
  281.   ExpanderPiIO.prototype.invertPin = function (pin, polarity) {
  282.     pin = pin - 1;
  283.     if (pin < 8) {
  284.       this.portAPolarity = updateByte(this.portAPolarity, pin, polarity);
  285.       this.i2cWriteByte(IPOLA, this.portAPolarity);
  286.     } else {
  287.       this.portBPolarity = updateByte(this.portBPolarity, pin - 8, polarity);
  288.       this.i2cWriteByte(IPOLB, this.portBPolarity);
  289.     }
  290.   };
  291.  
  292.   /**
  293.    * Mirror interrupts across INTA and INTB
  294.    * @param  {number} value - 1 = The INT pins are internally connected, 0 = The INT pins are not connected. INTA is associated with PortA and INTB is associated with PortB
  295.    */
  296.   ExpanderPiIO.prototype.mirrorInterrupts = function (value) {
  297.     if (value == 0) {
  298.       this.config = updateByte(this.config, 6, 0);
  299.     }
  300.     if (value == 1) {
  301.       this.config = updateByte(this.config, 6, 1);
  302.     }
  303.     this.i2cWriteByte(IOCON, this.config);
  304.   };
  305.  
  306.   /**
  307.    * This sets the polarity of the INT output pins
  308.    * @param  {number} value - 1 = Active-high. 0 = Active-low.
  309.    */
  310.   ExpanderPiIO.prototype.setInterruptPolarity = function (value) {
  311.     if (value == 0) {
  312.       this.config = updateByte(this.config, 1, 0);
  313.     }
  314.     if (value == 1) {
  315.       this.config = updateByte(this.config, 1, 1);
  316.     }
  317.     this.i2cWriteByte(IOCON, this.config);
  318.   };
  319.  
  320.   /**
  321.    * Sets the type of interrupt for each pin on the selected port
  322.    * @param  {number} port - port 0 = pins 1 to 8, port 1 = pins 8 to 16
  323.    * @param  {number} value - 0 to 255.  For each bit 1 = interrupt is fired when the pin matches the default value, 0 = the interrupt is fired on state change.
  324.    */
  325.   ExpanderPiIO.prototype.setInterruptType = function (port, value) {
  326.     if (port == 0) {
  327.       this.i2cWriteByte(INTCONA, value);
  328.     } else {
  329.       this.i2cWriteByte(INTCONB, value);
  330.     }
  331.   };
  332.  
  333.   /**
  334.    * These bits set the compare value for pins configured for interrupt-on-change on the selected port.
  335.    * If the associated pin level is the opposite from the register bit, an interrupt occurs.
  336.    * @param  {number} port - port 0 = pins 1 to 8, port 1 = pins 8 to 16
  337.    * @param  {number} value - 0 to 255 or 0x00 and 0xFF
  338.    */
  339.   ExpanderPiIO.prototype.setInterruptDefaults = function (port, value) {
  340.     if (port == 0) {
  341.       this.i2cWriteByte(DEFVALA, value);
  342.     } else {
  343.       this.i2cWriteByte(DEFVALB, value);
  344.     }
  345.   };
  346.  
  347.   /**
  348.    * Enable interrupts for the pins on the selected port
  349.    * @param  {number} port - port 0 = pins 1 to 8, port 1 = pins 8 to 16
  350.    * @param  {number} value - 0 and 255 or 0x00 and 0xFF
  351.    */
  352.   ExpanderPiIO.prototype.setInterruptOnPort = function (port, value) {
  353.     if (port == 0) {
  354.       this.i2cWriteByte(GPINTENA, value);
  355.       intA = value;
  356.     } else {
  357.       this.i2cWriteByte(GPINTENB, value);
  358.       intB = value;
  359.     }
  360.   };
  361.  
  362.   /**
  363.    * Enable interrupts for the selected pin
  364.    * @param  {number} pin - 1 to 16
  365.    * @param  {number} value - 0 = interrupt disabled, 1 = interrupt enabled
  366.    */
  367.   ExpanderPiIO.prototype.setInterruptOnPin = function (pin, value) {
  368.     pin = pin - 1;
  369.     if (pin < 8) {
  370.       this.intA = updateByte(this.intA, pin, value);
  371.       this.i2cWriteByte(GPINTENA, this.intA);
  372.     } else {
  373.       this.intB = updateByte(this.intB, pin - 8, value);
  374.       this.i2cWriteByte(GPINTENB, this.intB);
  375.     }
  376.   };
  377.  
  378.   /**
  379.    * Read the interrupt status for the pins on the selected port
  380.    * @param  {number} port - 0 = pins 1 to 8, port 1 = pins 9 to 16
  381.    * @returns  {number} - 0 to 255. Interrupt status, each pin represents one bit.
  382.    */
  383.   ExpanderPiIO.prototype.readInterruptStatus = function (port) {
  384.     if (port == 0) {
  385.       return this.i2cReadByte(INTFA);
  386.     } else {
  387.       return this.i2cReadByte(INTFB);
  388.     }
  389.   };
  390.  
  391.   /**
  392.    * Read the value from the selected port at the time of the last interrupt trigger
  393.    * @param  {number} port - port 0 = pins 1 to 8, port 1 = pins 8 to 16
  394.    * @returns  {number} - 0 to 255. Interrupt status, each pin represents one bit.
  395.    */
  396.   ExpanderPiIO.prototype.readInterruptCapture = function (port) {
  397.     if (port == 0) {
  398.       return this.i2cReadByte(INTCAPA);
  399.     } else {
  400.       return this.i2cReadByte(INTCAPB);
  401.     }
  402.   };
  403.  
  404.   /**
  405.    * Set the interrupts A and B to 0
  406.    */
  407.   ExpanderPiIO.prototype.resetInterrupts = function () {
  408.     this.readInterruptCapture(0);
  409.     this.readInterruptCapture(1);
  410.   };
  411.  
  412.   return ExpanderPiIO;
  413. })();
  414.  
  415. module.exports = ExpanderPiIO;
  416.  
  417. /**
  418.  * RTC Class
  419.  */
  420. ExpanderPiRTC = (function () {
  421.   // Define registers values from datasheet
  422.   const SECONDS = 0x00;
  423.   const MINUTES = 0x01;
  424.   const HOURS = 0x02;
  425.   const DAYOFWEEK = 0x03;
  426.   const DAY = 0x04;
  427.   const MONTH = 0x05;
  428.   const YEAR = 0x06;
  429.   const CONTROL = 0x07;
  430.  
  431.   // variables
  432.   var rtcAddress = 0x68; // I2C address
  433.   // initial configuration - square wave and output disabled, frequency set
  434.   // to 32.768KHz.
  435.   var config = 0x03;
  436.   // the DS1307 does not store the current century so that has to be added on
  437.   // manually.
  438.   var century = 2000;
  439.  
  440.   /**
  441.    * Initialise the RTC I2C connection and set the default configuration to the control register
  442.    */
  443.   function ExpanderPiRTC() {
  444.     rpio.i2cBegin();
  445.     rpio.i2cSetSlaveAddress(rtcAddress);
  446.     i2cWriteByte(CONTROL, config);
  447.   }
  448.  
  449.   // Private functions
  450.  
  451.   /**
  452.    * Write a single byte to the I2C bus.
  453.    * @param  {number} register - Target register
  454.    * @param  {number} val - Value to be written
  455.    */
  456.   function i2cWriteByte(register, val) {
  457.     var txbuf = new Buffer([register, val]);
  458.     rpio.i2cSetSlaveAddress(rtcAddress);
  459.     rpio.i2cWrite(txbuf);
  460.   }
  461.  
  462.   /**
  463.    * Update a single bit within a variable
  464.    * @param  {number} oldByte - Variable to be updated
  465.    * @param  {number} bit - The location of the bit to be changed
  466.    * @param  {boolean} value - The new value for the bit.  true or false
  467.    * @returns {number} - Updated value
  468.    */
  469.   function updateByte(oldByte, bit, value) {
  470.     var newByte = 0;
  471.     if (value == false) {
  472.       newByte = oldByte & ~(1 << bit);
  473.     } else {
  474.       newByte = oldByte | (1 << bit);
  475.     }
  476.     return newByte;
  477.   }
  478.  
  479.   /**
  480.    * Convert a BCD formatted number to decimal.
  481.    * @param  {number} val - BCD value
  482.    * @returns {number} - Decimal value
  483.    */
  484.   function bcdToDec(val) {
  485.     return val - 6 * (val >> 4);
  486.   }
  487.  
  488.   /**
  489.    * Convert a decimal to BCD formatted number.
  490.    * @param  {number} val - Decimal value
  491.    * @returns {number} - BCD value
  492.    */
  493.   function decToBcd(val) {
  494.     return ((val / 10) << 4) | val % 10;
  495.   }
  496.  
  497.   /**
  498.    * Calculate the current century
  499.    * @param  {number} val - Year
  500.    */
  501.   function getCentury(val) {
  502.     if (val.length > 2) {
  503.       var y = val[0] + val[1];
  504.       century = int(y) * 100;
  505.     }
  506.   }
  507.  
  508.   // public functions
  509.  
  510.   /**
  511.    * Set the date and time on the RTC
  512.    * @param  {Date} date - Use a javascript Date object
  513.    */
  514.   ExpanderPiRTC.prototype.setDate = function (date) {
  515.     getCentury(date.getFullYear());
  516.     i2cWriteByte(SECONDS, decToBcd(date.getSeconds()));
  517.     i2cWriteByte(MINUTES, decToBcd(date.getMinutes()));
  518.     i2cWriteByte(HOURS, decToBcd(date.getHours()));
  519.     i2cWriteByte(DAYOFWEEK, decToBcd(date.getDay()));
  520.     i2cWriteByte(DAY, decToBcd(date.getDate()));
  521.     i2cWriteByte(MONTH, decToBcd(date.getMonth() + 1));
  522.     i2cWriteByte(YEAR, decToBcd(date.getFullYear() - century));
  523.   };
  524.  
  525.   /**
  526.    * Read the date and time from the RTC
  527.    * @returns  {Date} - Returns the date as a javascript Date object
  528.    */
  529.   ExpanderPiRTC.prototype.readDate = function () {
  530.     var txbuf = new Buffer(1);
  531.     var rxbuf = new Buffer(7);
  532.     txbuf[0] = 0;
  533.     rpio.i2cSetSlaveAddress(rtcAddress);
  534.     rpio.i2cWrite(txbuf);
  535.     rpio.i2cRead(rxbuf, 7);
  536.  
  537.     var d = new Date(
  538.       bcdToDec(rxbuf[6]) + century,
  539.       bcdToDec(rxbuf[5]),
  540.       bcdToDec(rxbuf[4]),
  541.       bcdToDec(rxbuf[2]),
  542.       bcdToDec(rxbuf[1]),
  543.       bcdToDec(rxbuf[0]),
  544.       0
  545.     );
  546.  
  547.     return d;
  548.   };
  549.  
  550.   /**
  551.    * Enable the output pin
  552.    */
  553.   ExpanderPiRTC.prototype.enableOutput = function () {
  554.     config = updateByte(config, 7, 1);
  555.     config = updateByte(config, 4, 1);
  556.     i2cWriteByte(CONTROL, config);
  557.   };
  558.  
  559.   /**
  560.    * Disable the output pin
  561.    */
  562.   ExpanderPiRTC.prototype.disableOutput = function () {
  563.     config = updateByte(config, 7, 0);
  564.     config = updateByte(config, 4, 0);
  565.     i2cWriteByte(CONTROL, config);
  566.   };
  567.  
  568.   /**
  569.    * Set the frequency of the output pin square- wave
  570.    * @param  {number} frequency - 1 = 1Hz, 2 = 4.096KHz, 3 = 8.192KHz, 4 = 32.768KHz
  571.    */
  572.   ExpanderPiRTC.prototype.setFrequency = function (frequency) {
  573.     switch (frequency) {
  574.       case 1:
  575.         config = updateByte(config, 0, 0);
  576.         config = updateByte(config, 1, 0);
  577.         break;
  578.       case 2:
  579.         config = updateByte(config, 0, 1);
  580.         config = updateByte(config, 1, 0);
  581.         break;
  582.       case 3:
  583.         config = updateByte(config, 0, 0);
  584.         config = updateByte(config, 1, 1);
  585.         break;
  586.       case 4:
  587.         config = updateByte(config, 0, 1);
  588.         config = updateByte(config, 1, 1);
  589.         break;
  590.       default:
  591.         throw new Error("Argument Out Of Range");
  592.     }
  593.     i2cWriteByte(CONTROL, config);
  594.   };
  595.  
  596.   /**
  597.    * Write to the memory on the DS1307
  598.    * The DS1307 contains 56 - Byte, battery - backed RAM with Unlimited Writes
  599.    * @param  {number} address - 0x08 to 0x3F
  600.    * @param  {Uint8Array} valuearray - byte array containing data to be written to memory. Length can not exceed the avaiable address space.
  601.    */
  602.   ExpanderPiRTC.prototype.writeMemory = function (address, valuearray) {
  603.     if (address + valuearray.length <= 0x3f) {
  604.       if (address >= 0x08 && address <= 0x3f) {
  605.         // create a new array with the address at the start of the array
  606.         var data = new Uint8Array(valuearray.length + 1);
  607.         data[0] = address;
  608.         // copy the data from the valuearray into data
  609.         for (var a = 0; a < data.length; a++) {
  610.           data[a + 1] = valuearray[a];
  611.         }
  612.  
  613.         // write the array to the RTC memory
  614.         rpio.i2cSetSlaveAddress(rtcAddress);
  615.         rpio.i2cWrite(data);
  616.       } else {
  617.         throw new Error("Memory address outside of range: 0x08 to 0x3F");
  618.       }
  619.     } else {
  620.       throw new Error("Array is larger than the available memory space");
  621.     }
  622.   };
  623.  
  624.   /**
  625.    * Read from the memory on the DS1307
  626.    * The DS1307 contains 56 - Byte, battery - backed RAM with Unlimited Writes
  627.    * @param  {Number} address - 0x08 to 0x3F
  628.    * @param  {Number} length - Up to 32 bytes.  length can not exceed the avaiable address space.
  629.    * @returns  {Uint8Array} - Returns an array of the data read from memory
  630.    */
  631.   ExpanderPiRTC.prototype.readMemory = function (address, length) {
  632.     if (address >= 0x08 && address <= 0x3f) {
  633.       if (address <= 0x3f - length) {
  634.         var txbuf = new Uint8Array(1);
  635.         var rxbuf = new Uint8Array(length);
  636.         txbuf[0] = address;
  637.  
  638.         rpio.i2cSetSlaveAddress(rtcAddress);
  639.         rpio.i2cWrite(txbuf);
  640.         rpio.i2cRead(rxbuf, length);
  641.  
  642.         return rxbuf;
  643.       } else {
  644.         throw new Error("Memory overflow error: address + length exceeds 0x3F");
  645.       }
  646.     } else {
  647.       throw new Error("Memory address outside of range: 0x08 to 0x3F");
  648.     }
  649.   };
  650.  
  651.   return ExpanderPiRTC;
  652. })();
  653.  
  654. module.exports = ExpanderPiRTC;
  655.  
  656. /**
  657.  * RTC Class
  658.  */
  659. ExpanderPiBME = (function () {
  660.   // variables
  661.   var bmeAddress = 0x76; // I2C address
  662.   var chip_id = null;
  663.   var power_mode = null;
  664.   var ambient_temperature = null;
  665.   var offset_temp_in_t_fine = null;
  666.   let calibrationData = null;
  667.   /**
  668.    * Initialise the BME connection
  669.    **/
  670.   function ExpanderPiBME() {
  671.     rpio.i2cBegin();
  672.     rpio.i2cSetSlaveAddress(bmeAddress);
  673.   }
  674.  
  675.   // Private functions
  676.  
  677.   function setBits(register, mask, position, value) {
  678.     let temp = i2cReadByte(register, 1)[0];
  679.     temp &= ~mask;
  680.     temp |= value << position;
  681.     i2cWriteByte(register, temp);
  682.   }
  683.  
  684.   function i2cWriteByte(register, val) {
  685.     var txbuf = new Buffer([register, val]);
  686.     rpio.i2cSetSlaveAddress(bmeAddress);
  687.     rpio.i2cWrite(txbuf);
  688.   }
  689.  
  690.   function i2cReadByte(cmd, length) {
  691.     var txbuf = new Buffer([cmd]);
  692.     var rxbuf = new Buffer(length);
  693.     rpio.i2cSetSlaveAddress(bmeAddress);
  694.     rpio.i2cWrite(txbuf);
  695.     rpio.i2cRead(rxbuf, length);
  696.     return rxbuf;
  697.   }
  698.  
  699.   function setTempOffset(value) {
  700.     if (value === 0) {
  701.       offset_temp_in_t_fine = 0;
  702.     } else {
  703.       offset_temp_in_t_fine = parseInt(
  704.         ((parseInt(Math.abs(value) * 100) << 8) - 128) / 5
  705.       );
  706.       if (value < 0) {
  707.         offset_temp_in_t_fine = -offset_temp_in_t_fine;
  708.       }
  709.     }
  710.   }
  711.  
  712.   function setGasStatus(value) {
  713.     bme680Data.gas_settings.run_gas = value;
  714.     setBits(
  715.       constants.CONF_ODR_RUN_GAS_NBC_ADDR,
  716.       constants.RUN_GAS_MSK,
  717.       constants.RUN_GAS_POS,
  718.       value
  719.     );
  720.   }
  721.  
  722.   function setFilter(value) {
  723.     bme680Data.tph_settings.filter = value;
  724.     setBits(
  725.       constants.CONF_ODR_FILT_ADDR,
  726.       constants.FILTER_MSK,
  727.       constants.FILTER_POS,
  728.       value
  729.     );
  730.   }
  731.  
  732.   function setHumidityOversample(value) {
  733.     bme680Data.tph_settings.os_hum = value;
  734.     setBits(
  735.       constants.CONF_OS_H_ADDR,
  736.       constants.OSH_MSK,
  737.       constants.OSH_POS,
  738.       value
  739.     );
  740.   }
  741.  
  742.   function setPressureOversample(value) {
  743.     bme680Data.tph_settings.os_pres = value;
  744.     setBits(
  745.       constants.CONF_T_P_MODE_ADDR,
  746.       constants.OSP_MSK,
  747.       constants.OSP_POS,
  748.       value
  749.     );
  750.   }
  751.  
  752.   function setTemperatureOversample(value) {
  753.     bme680Data.tph_settings.os_temp = value;
  754.     setBits(
  755.       constants.CONF_T_P_MODE_ADDR,
  756.       constants.OST_MSK,
  757.       constants.OST_POS,
  758.       value
  759.     );
  760.   }
  761.  
  762.   function softReset() {
  763.     i2cWriteByte(constants.SOFT_RESET_ADDR, constants.SOFT_RESET_CMD);
  764.   }
  765.  
  766.   function getPowerMode() {
  767.     return i2cReadByte(constants.CONF_T_P_MODE_ADDR, 1)[0];
  768.   }
  769.  
  770.   function setPowerMode(value, blocking = false, maxPollTimeMs = null) {
  771.     if (value !== constants.SLEEP_MODE && value !== constants.FORCED_MODE) {
  772.       throw new Error("Power mode should be one of SLEEP_MODE or FORCED_MODE");
  773.     }
  774.  
  775.     if (!maxPollTimeMs) {
  776.       maxPollTimeMs = 10000 * constants.POLL_PERIOD_MS;
  777.     }
  778.     power_mode = value;
  779.     bme680Data.power_mode = power_mode;
  780.     setBits(
  781.       constants.CONF_T_P_MODE_ADDR,
  782.       constants.MODE_MSK,
  783.       constants.MODE_POS,
  784.       value
  785.     );
  786.     //
  787.     let cpt = 0;
  788.     const intervalPowerModeSwitch = setInterval(() => {
  789.       const currentPowerMode = getPowerMode();
  790.       if (!blocking || currentPowerMode === power_mode) {
  791.         clearInterval(intervalPowerModeSwitch);
  792.         return power_mode;
  793.       }
  794.       cpt++;
  795.       if (cpt * constants.POLL_PERIOD_MS >= maxPollTimeMs) {
  796.         clearInterval(intervalPowerModeSwitch);
  797.         return new Error(
  798.           `Power mode could not be updated after a delay of ${
  799.             cpt * constants.POLL_PERIOD_MS
  800.           } ms`
  801.         );
  802.       }
  803.     }, constants.POLL_PERIOD_MS);
  804.   }
  805.  
  806.   function getCalibrationData() {
  807.     calibrationData = new CalibrationData();
  808.     let calibration = Buffer.concat([
  809.       i2cReadByte(constants.COEFF_ADDR1, constants.COEFF_ADDR1_LEN),
  810.       i2cReadByte(constants.COEFF_ADDR2, constants.COEFF_ADDR2_LEN),
  811.     ]);
  812.     let heat_range = i2cReadByte(constants.ADDR_RES_HEAT_RANGE_ADDR, 1)[0];
  813.     let heat_value = CalibrationData.twos_comp(
  814.       i2cReadByte(constants.ADDR_RES_HEAT_VAL_ADDR, 1)[0],
  815.       8
  816.     );
  817.     let sw_error = CalibrationData.twos_comp(
  818.       i2cReadByte(constants.ADDR_RANGE_SW_ERR_ADDR, 1)[0],
  819.       8
  820.     );
  821.  
  822.     calibrationData.setFromArray(calibration);
  823.     calibrationData.setOther(heat_range, heat_value, sw_error);
  824.   }
  825.   function calcGasResistance(gas_res_adc, gas_range) {
  826.     let var1 = 1340.0 + 5.0 * calibrationData.range_sw_err;
  827.     let var2 = var1 * (1.0 + constants.lookupTable1[gas_range] / 100.0);
  828.     let var3 = 1.0 + constants.lookupTable2[gas_range] / 100.0;
  829.     return (
  830.       1.0 /
  831.       (var3 *
  832.         0.000000125 *
  833.         (1 << gas_range) *
  834.         ((gas_res_adc - 512.0) / var2 + 1.0))
  835.     );
  836.   }
  837.  
  838.   function calcHumidity(humidity_adc) {
  839.     let temp_scaled = (calibrationData.t_fine * 5 + 128) >> 8;
  840.     let var1 =
  841.       humidity_adc -
  842.       calibrationData.par_h1 * 16 -
  843.       (Math.floor((temp_scaled * calibrationData.par_h3) / 100) >> 1);
  844.     let var2 =
  845.       (calibrationData.par_h2 *
  846.         (Math.floor((temp_scaled * calibrationData.par_h4) / 100) +
  847.           Math.floor(
  848.             ((temp_scaled *
  849.               Math.floor((temp_scaled * calibrationData.par_h5) / 100)) >>
  850.               6) /
  851.               100
  852.           ) +
  853.           1 * 16384)) >>
  854.       10;
  855.     let var3 = var1 * var2;
  856.     let var4 = calibrationData.par_h6 << 7;
  857.     var4 = Math.floor(var4 + (temp_scaled * calibrationData.par_h7) / 100) >> 4;
  858.     let var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
  859.     let var6 = (var4 * var5) >> 1;
  860.     let calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
  861.  
  862.     return Math.min(Math.max(calc_hum, 0), 100000);
  863.   }
  864.  
  865.   function calcPressure(pressure_adc) {
  866.     let var1 = (calibrationData.t_fine >> 1) - 64000;
  867.     let var2 =
  868.       ((((var1 >> 2) * (var1 >> 2)) >> 11) * calibrationData.par_p6) >> 2;
  869.     var2 = var2 + ((var1 * calibrationData.par_p5) << 1);
  870.     var2 = (var2 >> 2) + (calibrationData.par_p4 << 16);
  871.     var1 =
  872.       (((((var1 >> 2) * (var1 >> 2)) >> 13) * (calibrationData.par_p3 << 5)) >>
  873.         3) +
  874.       ((calibrationData.par_p2 * var1) >> 1);
  875.     var1 = var1 >> 18;
  876.  
  877.     var1 = ((32768 + var1) * calibrationData.par_p1) >> 15;
  878.     let calc_pressure = 1048576 - pressure_adc;
  879.     calc_pressure = (calc_pressure - (var2 >> 12)) * 3125;
  880.  
  881.     if (calc_pressure >= 1 << 31) {
  882.       calc_pressure = Math.floor(calc_pressure / var1) << 1;
  883.     } else {
  884.       calc_pressure = Math.floor((calc_pressure << 1) / var1);
  885.     }
  886.  
  887.     var1 =
  888.       (calibrationData.par_p9 *
  889.         (((calc_pressure >> 3) * (calc_pressure >> 3)) >> 13)) >>
  890.       12;
  891.     var2 = ((calc_pressure >> 2) * calibrationData.par_p8) >> 13;
  892.  
  893.     let var3 =
  894.       ((calc_pressure >> 8) *
  895.         (calc_pressure >> 8) *
  896.         (calc_pressure >> 8) *
  897.         calibrationData.par_p10) >>
  898.       17;
  899.  
  900.     calc_pressure =
  901.       calc_pressure +
  902.       ((var1 + var2 + var3 + (calibrationData.par_p7 << 7)) >> 4);
  903.  
  904.     return calc_pressure;
  905.   }
  906.  
  907.   function calcHeaterDuration(duration) {
  908.     if (duration < 0xfc0) {
  909.       let factor = 0;
  910.  
  911.       while (duration > 0x3f) {
  912.         duration /= 4;
  913.         factor += 1;
  914.       }
  915.  
  916.       return Number.parseInt(duration + factor * 64);
  917.     }
  918.     return 0xff;
  919.   }
  920.  
  921.   function calcTemperature(temperature_adc) {
  922.     const var1 = (temperature_adc >> 3) - (calibrationData.par_t1 << 1);
  923.     const var2 = (var1 * calibrationData.par_t2) >> 11;
  924.     let var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
  925.     var3 = (var3 * (calibrationData.par_t3 << 4)) >> 14;
  926.  
  927.     // Save teperature data for pressure calculations
  928.     calibrationData.t_fine = var2 + var3 + offset_temp_in_t_fine;
  929.     return (calibrationData.t_fine * 5 + 128) >> 8;
  930.   }
  931.  
  932.   function calcHeaterResistance(temperature) {
  933.     temperature = Math.min(Math.max(temperature, 200), 400);
  934.     let var1 = ((ambient_temperature * calibrationData.par_gh3) / 1000) * 256;
  935.     let var2 =
  936.       (calibrationData.par_gh1 + 784) *
  937.       ((((calibrationData.par_gh2 + 154009) * temperature * 5) / 100 +
  938.         3276800) /
  939.         10);
  940.     let var3 = var1 + var2 / 2;
  941.     let var4 = var3 / (calibrationData.res_heat_range + 4);
  942.     let var5 = 131 * calibrationData.res_heat_val + 65536;
  943.     let heatr_res_x100 = (var4 / var5 - 250) * 34;
  944.     return (heatr_res_x100 + 50) / 100;
  945.   }
  946.  
  947.   function selectGasHeaterProfile(value) {
  948.     if (value > constants.NBCONV_MAX || value < constants.NBCONV_MIN) {
  949.       throw new Error(
  950.         `Profile '${value}' should be between ${constants.NBCONV_MIN} and ${constants.NBCONV_MAX}`
  951.       );
  952.     }
  953.     bme680Data.gas_settings.nb_conv = value;
  954.     setBits(
  955.       constants.CONF_ODR_RUN_GAS_NBC_ADDR,
  956.       constants.NBCONV_MSK,
  957.       constants.NBCONV_POS,
  958.       value
  959.     );
  960.   }
  961.  
  962.   function setGasHeaterDuration(value, nb_profile = 0) {
  963.     if (nb_profile > constants.NBCONV_MAX || value < constants.NBCONV_MIN) {
  964.       throw new Error(
  965.         `Profile '${nb_profile}' should be between ${constants.NBCONV_MIN} and ${constants.NBCONV_MAX}`
  966.       );
  967.     }
  968.     bme680Data.gas_settings.heatr_dur = value;
  969.     let temp = calcHeaterDuration(bme680Data.gas_settings.heatr_dur);
  970.     i2cWriteByte(constants.GAS_WAIT0_ADDR + nb_profile, temp);
  971.   }
  972.  
  973.   function setGasHeaterTemperature(value, nb_profile = 0) {
  974.     if (nb_profile > constants.NBCONV_MAX || value < constants.NBCONV_MIN) {
  975.       throw new Error(
  976.         `Profile '${nb_profile}' should be between ${constants.NBCONV_MIN} and ${constants.NBCONV_MAX}`
  977.       );
  978.     }
  979.  
  980.     bme680Data.gas_settings.heatr_temp = value;
  981.     let temp = Number.parseInt(
  982.       calcHeaterResistance(bme680Data.gas_settings.heatr_temp)
  983.     );
  984.     i2cWriteByte(constants.RES_HEAT0_ADDR + nb_profile, temp);
  985.   }
  986.  
  987.   async function getSensorData() {
  988.     setPowerMode(constants.FORCED_MODE);
  989.  
  990.     let i = 0,
  991.       status;
  992.     do {
  993.       status = i2cReadByte(constants.FIELD0_ADDR, 1)[0];
  994.       await new Promise((resolve) => {
  995.         setTimeout(() => {
  996.           resolve();
  997.         }, constants.POLL_PERIOD_MS);
  998.       });
  999.       i++;
  1000.     } while (i < 1000 && !(status & constants.NEW_DATA_MSK));
  1001.  
  1002.     let regs = i2cReadByte(constants.FIELD0_ADDR, constants.FIELD_LENGTH);
  1003.  
  1004.     bme680Data.data.status = regs[0] & constants.NEW_DATA_MSK;
  1005.     // Contains the nb_profile used to obtain the current measurement
  1006.     bme680Data.data.gas_index = regs[0] & constants.GAS_INDEX_MSK;
  1007.     bme680Data.data.meas_index = regs[1];
  1008.  
  1009.     const adc_pres = (regs[2] << 12) | (regs[3] << 4) | (regs[4] >> 4);
  1010.     const adc_temp = (regs[5] << 12) | (regs[6] << 4) | (regs[7] >> 4);
  1011.     const adc_hum = (regs[8] << 8) | regs[9];
  1012.     const adc_gas_res = (regs[13] << 2) | (regs[14] >> 6);
  1013.     const gas_range = regs[14] & constants.GAS_RANGE_MSK;
  1014.  
  1015.     bme680Data.data.status |= regs[14] & constants.GASM_VALID_MSK;
  1016.     bme680Data.data.status |= regs[14] & constants.HEAT_STAB_MSK;
  1017.  
  1018.     bme680Data.data.heat_stable =
  1019.       (bme680Data.data.status & constants.HEAT_STAB_MSK) > 0;
  1020.  
  1021.     let temperature = calcTemperature(adc_temp);
  1022.     bme680Data.data.temperature = temperature / 100.0;
  1023.     ambient_temperature = temperature; // Saved for heater calc;
  1024.     bme680Data.ambient_temperature = ambient_temperature;
  1025.  
  1026.     bme680Data.data.pressure = calcPressure(adc_pres) / 100.0;
  1027.     bme680Data.data.humidity = calcHumidity(adc_hum) / 1000.0;
  1028.     bme680Data.data.gas_resistance = calcGasResistance(adc_gas_res, gas_range);
  1029.     bme680Data.calibration_data = calibrationData;
  1030.     return bme680Data;
  1031.   }
  1032.  
  1033.   ExpanderPiBME.prototype.initialize = async function () {
  1034.     chip_id = i2cReadByte(constants.CHIP_ID_ADDR, 1)[0];
  1035.     if (chip_id !== constants.CHIP_ID) {
  1036.       const invalidChipIdError = `BME680 Not Found. Invalid CHIP ID: ${chip_id}`;
  1037.       console.error(invalidChipIdError);
  1038.       throw new Error(invalidChipIdError);
  1039.     }
  1040.  
  1041.     bme680Data.chip_id = constants.CHIP_ID;
  1042.     softReset();
  1043.     setPowerMode(constants.SLEEP_MODE);
  1044.     getCalibrationData();
  1045.     setHumidityOversample(constants.OS_2X);
  1046.     setPressureOversample(constants.OS_4X);
  1047.     setTemperatureOversample(constants.OS_8X);
  1048.     setFilter(constants.FILTER_SIZE_3);
  1049.     setGasStatus(constants.ENABLE_GAS_MEAS);
  1050.     setTempOffset(0);
  1051.  
  1052.     await getSensorData();
  1053.  
  1054.     setGasHeaterTemperature(320);
  1055.     setGasHeaterDuration(150);
  1056.     selectGasHeaterProfile(0);
  1057.   };
  1058.  
  1059.   ExpanderPiBME.prototype.getSensorData = async function () {
  1060.     return await getSensorData();
  1061.   };
  1062.   ExpanderPiBME.prototype.setTempOffset = function (temp) {
  1063.     setTempOffset(temp);
  1064.   };
  1065.  
  1066.   return ExpanderPiBME;
  1067. })();
  1068.  
  1069. module.exports = ExpanderPiBME;
  1070.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement