R_1_0_23
This commit is contained in:
parent
30c9aad1c1
commit
3a8222b311
BIN
lib/Adafruit_ADS1X15-master.zip
Normal file
BIN
lib/Adafruit_ADS1X15-master.zip
Normal file
Binary file not shown.
46
lib/Adafruit_ADS1X15/.github/ISSUE_TEMPLATE.md
vendored
Normal file
46
lib/Adafruit_ADS1X15/.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
Thank you for opening an issue on an Adafruit Arduino library repository. To
|
||||||
|
improve the speed of resolution please review the following guidelines and
|
||||||
|
common troubleshooting steps below before creating the issue:
|
||||||
|
|
||||||
|
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
|
||||||
|
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
|
||||||
|
something isn't working as expected. In many cases the problem is a common issue
|
||||||
|
that you will more quickly receive help from the forum community. GitHub issues
|
||||||
|
are meant for known defects in the code. If you don't know if there is a defect
|
||||||
|
in the code then start with troubleshooting on the forum first.
|
||||||
|
|
||||||
|
- **If following a tutorial or guide be sure you didn't miss a step.** Carefully
|
||||||
|
check all of the steps and commands to run have been followed. Consult the
|
||||||
|
forum if you're unsure or have questions about steps in a guide/tutorial.
|
||||||
|
|
||||||
|
- **For Arduino projects check these very common issues to ensure they don't apply**:
|
||||||
|
|
||||||
|
- For uploading sketches or communicating with the board make sure you're using
|
||||||
|
a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes
|
||||||
|
very hard to tell the difference between a data and charge cable! Try using the
|
||||||
|
cable with other devices or swapping to another cable to confirm it is not
|
||||||
|
the problem.
|
||||||
|
|
||||||
|
- **Be sure you are supplying adequate power to the board.** Check the specs of
|
||||||
|
your board and plug in an external power supply. In many cases just
|
||||||
|
plugging a board into your computer is not enough to power it and other
|
||||||
|
peripherals.
|
||||||
|
|
||||||
|
- **Double check all soldering joints and connections.** Flakey connections
|
||||||
|
cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints.
|
||||||
|
|
||||||
|
- **Ensure you are using an official Arduino or Adafruit board.** We can't
|
||||||
|
guarantee a clone board will have the same functionality and work as expected
|
||||||
|
with this code and don't support them.
|
||||||
|
|
||||||
|
If you're sure this issue is a defect in the code and checked the steps above
|
||||||
|
please fill in the following fields to provide enough troubleshooting information.
|
||||||
|
You may delete the guideline and text above to just leave the following details:
|
||||||
|
|
||||||
|
- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE**
|
||||||
|
|
||||||
|
- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO
|
||||||
|
VERSION HERE**
|
||||||
|
|
||||||
|
- List the steps to reproduce the problem below (if possible attach a sketch or
|
||||||
|
copy the sketch code in too): **LIST REPRO STEPS BELOW**
|
||||||
26
lib/Adafruit_ADS1X15/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
26
lib/Adafruit_ADS1X15/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Thank you for creating a pull request to contribute to Adafruit's GitHub code!
|
||||||
|
Before you open the request please review the following guidelines and tips to
|
||||||
|
help it be more easily integrated:
|
||||||
|
|
||||||
|
- **Describe the scope of your change--i.e. what the change does and what parts
|
||||||
|
of the code were modified.** This will help us understand any risks of integrating
|
||||||
|
the code.
|
||||||
|
|
||||||
|
- **Describe any known limitations with your change.** For example if the change
|
||||||
|
doesn't apply to a supported platform of the library please mention it.
|
||||||
|
|
||||||
|
- **Please run any tests or examples that can exercise your modified code.** We
|
||||||
|
strive to not break users of the code and running tests/examples helps with this
|
||||||
|
process.
|
||||||
|
|
||||||
|
Thank you again for contributing! We will try to test and integrate the change
|
||||||
|
as soon as we can, but be aware we have many GitHub repositories to manage and
|
||||||
|
can't immediately respond to every request. There is no need to bump or check in
|
||||||
|
on a pull request (it will clutter the discussion of the request).
|
||||||
|
|
||||||
|
Also don't be worried if the request is closed or not integrated--sometimes the
|
||||||
|
priorities of Adafruit's GitHub code (education, ease of use) might not match the
|
||||||
|
priorities of the pull request. Don't fret, the open source community thrives on
|
||||||
|
forks and GitHub makes it easy to keep your changes in a forked repo.
|
||||||
|
|
||||||
|
After reviewing the guidelines above you can delete this text from the pull request.
|
||||||
32
lib/Adafruit_ADS1X15/.github/workflows/githubci.yml
vendored
Normal file
32
lib/Adafruit_ADS1X15/.github/workflows/githubci.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
name: Arduino Library CI
|
||||||
|
|
||||||
|
on: [pull_request, push, repository_dispatch]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: adafruit/ci-arduino
|
||||||
|
path: ci
|
||||||
|
|
||||||
|
- name: pre-install
|
||||||
|
run: bash ci/actions_install.sh
|
||||||
|
|
||||||
|
- name: test platforms
|
||||||
|
run: python3 ci/build_platform.py main_platforms
|
||||||
|
|
||||||
|
- name: clang
|
||||||
|
run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r .
|
||||||
|
|
||||||
|
- name: doxygen
|
||||||
|
env:
|
||||||
|
GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }}
|
||||||
|
PRETTYNAME : "Adafruit ADS1X15 ADC Driver Library"
|
||||||
|
run: bash ci/doxy_gen_and_deploy.sh
|
||||||
413
lib/Adafruit_ADS1X15/Adafruit_ADS1X15.cpp
Normal file
413
lib/Adafruit_ADS1X15/Adafruit_ADS1X15.cpp
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@file Adafruit_ADS1X15.cpp
|
||||||
|
@author K.Townsend (Adafruit Industries)
|
||||||
|
|
||||||
|
@mainpage Adafruit ADS1X15 ADC Breakout Driver
|
||||||
|
|
||||||
|
@section intro_sec Introduction
|
||||||
|
|
||||||
|
This is a library for the Adafruit ADS1X15 ADC breakout boards.
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
@section author Author
|
||||||
|
|
||||||
|
Written by Kevin "KTOWN" Townsend for Adafruit Industries.
|
||||||
|
|
||||||
|
@section HISTORY
|
||||||
|
|
||||||
|
v1.0 - First release
|
||||||
|
v1.1 - Added ADS1115 support - W. Earl
|
||||||
|
v2.0 - Refactor - C. Nelson
|
||||||
|
|
||||||
|
@section license License
|
||||||
|
|
||||||
|
BSD license, all text here must be included in any redistribution
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
#include "Adafruit_ADS1X15.h"
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Instantiates a new ADS1015 class w/appropriate properties
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
Adafruit_ADS1015::Adafruit_ADS1015() {
|
||||||
|
m_bitShift = 4;
|
||||||
|
m_gain = GAIN_TWOTHIRDS; /* +/- 6.144V range (limited to VDD +0.3V max!) */
|
||||||
|
m_dataRate = RATE_ADS1015_1600SPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Instantiates a new ADS1115 class w/appropriate properties
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
Adafruit_ADS1115::Adafruit_ADS1115() {
|
||||||
|
m_bitShift = 0;
|
||||||
|
m_gain = GAIN_TWOTHIRDS; /* +/- 6.144V range (limited to VDD +0.3V max!) */
|
||||||
|
m_dataRate = RATE_ADS1115_128SPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Sets up the HW (reads coefficients values, etc.)
|
||||||
|
|
||||||
|
@param i2c_addr I2C address of device
|
||||||
|
@param wire I2C bus
|
||||||
|
|
||||||
|
@return true if successful, otherwise false
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
bool Adafruit_ADS1X15::begin(uint8_t i2c_addr, TwoWire *wire) {
|
||||||
|
m_i2c_dev = new Adafruit_I2CDevice(i2c_addr, wire);
|
||||||
|
return m_i2c_dev->begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Sets the gain and input voltage range
|
||||||
|
|
||||||
|
@param gain gain setting to use
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
void Adafruit_ADS1X15::setGain(adsGain_t gain) { m_gain = gain; }
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Gets a gain and input voltage range
|
||||||
|
|
||||||
|
@return the gain setting
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
adsGain_t Adafruit_ADS1X15::getGain() { return m_gain; }
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Sets the data rate
|
||||||
|
|
||||||
|
@param rate the data rate to use
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
void Adafruit_ADS1X15::setDataRate(uint16_t rate) { m_dataRate = rate; }
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Gets the current data rate
|
||||||
|
|
||||||
|
@return the data rate
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
uint16_t Adafruit_ADS1X15::getDataRate() { return m_dataRate; }
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Gets a single-ended ADC reading from the specified channel
|
||||||
|
|
||||||
|
@param channel ADC channel to read
|
||||||
|
|
||||||
|
@return the ADC reading
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
int16_t Adafruit_ADS1X15::readADC_SingleEnded(uint8_t channel) {
|
||||||
|
if (channel > 3) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
startADCReading(MUX_BY_CHANNEL[channel], /*continuous=*/false);
|
||||||
|
|
||||||
|
// Wait for the conversion to complete
|
||||||
|
while (!conversionComplete())
|
||||||
|
;
|
||||||
|
|
||||||
|
// Read the conversion results
|
||||||
|
return getLastConversionResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Reads the conversion results, measuring the voltage
|
||||||
|
difference between the P (AIN0) and N (AIN1) input. Generates
|
||||||
|
a signed value since the difference can be either
|
||||||
|
positive or negative.
|
||||||
|
|
||||||
|
@return the ADC reading
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
int16_t Adafruit_ADS1X15::readADC_Differential_0_1() {
|
||||||
|
startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_0_1, /*continuous=*/false);
|
||||||
|
|
||||||
|
// Wait for the conversion to complete
|
||||||
|
while (!conversionComplete())
|
||||||
|
;
|
||||||
|
|
||||||
|
// Read the conversion results
|
||||||
|
return getLastConversionResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Reads the conversion results, measuring the voltage
|
||||||
|
difference between the P (AIN0) and N (AIN3) input. Generates
|
||||||
|
a signed value since the difference can be either
|
||||||
|
positive or negative.
|
||||||
|
@return the ADC reading
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
int16_t Adafruit_ADS1X15::readADC_Differential_0_3() {
|
||||||
|
startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_0_3, /*continuous=*/false);
|
||||||
|
|
||||||
|
// Wait for the conversion to complete
|
||||||
|
while (!conversionComplete())
|
||||||
|
;
|
||||||
|
|
||||||
|
// Read the conversion results
|
||||||
|
return getLastConversionResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Reads the conversion results, measuring the voltage
|
||||||
|
difference between the P (AIN1) and N (AIN3) input. Generates
|
||||||
|
a signed value since the difference can be either
|
||||||
|
positive or negative.
|
||||||
|
@return the ADC reading
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
int16_t Adafruit_ADS1X15::readADC_Differential_1_3() {
|
||||||
|
startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_1_3, /*continuous=*/false);
|
||||||
|
|
||||||
|
// Wait for the conversion to complete
|
||||||
|
while (!conversionComplete())
|
||||||
|
;
|
||||||
|
|
||||||
|
// Read the conversion results
|
||||||
|
return getLastConversionResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Reads the conversion results, measuring the voltage
|
||||||
|
difference between the P (AIN2) and N (AIN3) input. Generates
|
||||||
|
a signed value since the difference can be either
|
||||||
|
positive or negative.
|
||||||
|
|
||||||
|
@return the ADC reading
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
int16_t Adafruit_ADS1X15::readADC_Differential_2_3() {
|
||||||
|
startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_2_3, /*continuous=*/false);
|
||||||
|
|
||||||
|
// Wait for the conversion to complete
|
||||||
|
while (!conversionComplete())
|
||||||
|
;
|
||||||
|
|
||||||
|
// Read the conversion results
|
||||||
|
return getLastConversionResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Sets up the comparator to operate in basic mode, causing the
|
||||||
|
ALERT/RDY pin to assert (go from high to low) when the ADC
|
||||||
|
value exceeds the specified threshold.
|
||||||
|
|
||||||
|
This will also set the ADC in continuous conversion mode.
|
||||||
|
|
||||||
|
@param channel ADC channel to use
|
||||||
|
@param threshold comparator threshold
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
void Adafruit_ADS1X15::startComparator_SingleEnded(uint8_t channel,
|
||||||
|
int16_t threshold) {
|
||||||
|
// Start with default values
|
||||||
|
uint16_t config =
|
||||||
|
ADS1X15_REG_CONFIG_CQUE_1CONV | // Comparator enabled and asserts on 1
|
||||||
|
// match
|
||||||
|
ADS1X15_REG_CONFIG_CLAT_LATCH | // Latching mode
|
||||||
|
ADS1X15_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low (default val)
|
||||||
|
ADS1X15_REG_CONFIG_CMODE_TRAD | // Traditional comparator (default val)
|
||||||
|
ADS1X15_REG_CONFIG_MODE_CONTIN | // Continuous conversion mode
|
||||||
|
ADS1X15_REG_CONFIG_MODE_CONTIN; // Continuous conversion mode
|
||||||
|
|
||||||
|
// Set PGA/voltage range
|
||||||
|
config |= m_gain;
|
||||||
|
|
||||||
|
// Set data rate
|
||||||
|
config |= m_dataRate;
|
||||||
|
|
||||||
|
config |= MUX_BY_CHANNEL[channel];
|
||||||
|
|
||||||
|
// Set the high threshold register
|
||||||
|
// Shift 12-bit results left 4 bits for the ADS1015
|
||||||
|
writeRegister(ADS1X15_REG_POINTER_HITHRESH, threshold << m_bitShift);
|
||||||
|
|
||||||
|
// Write config register to the ADC
|
||||||
|
writeRegister(ADS1X15_REG_POINTER_CONFIG, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief In order to clear the comparator, we need to read the
|
||||||
|
conversion results. This function reads the last conversion
|
||||||
|
results without changing the config value.
|
||||||
|
|
||||||
|
@return the last ADC reading
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
int16_t Adafruit_ADS1X15::getLastConversionResults() {
|
||||||
|
// Read the conversion results
|
||||||
|
uint16_t res = readRegister(ADS1X15_REG_POINTER_CONVERT) >> m_bitShift;
|
||||||
|
if (m_bitShift == 0) {
|
||||||
|
return (int16_t)res;
|
||||||
|
} else {
|
||||||
|
// Shift 12-bit results right 4 bits for the ADS1015,
|
||||||
|
// making sure we keep the sign bit intact
|
||||||
|
if (res > 0x07FF) {
|
||||||
|
// negative number - extend the sign to 16th bit
|
||||||
|
res |= 0xF000;
|
||||||
|
}
|
||||||
|
return (int16_t)res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Return the current fs range for the configured gain
|
||||||
|
|
||||||
|
@return the fsRange for the configured gain, or zero.
|
||||||
|
Zero should not be possible thereby indicating error
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
float Adafruit_ADS1X15::getFsRange() {
|
||||||
|
// see data sheet Table 3
|
||||||
|
switch (m_gain) {
|
||||||
|
case GAIN_TWOTHIRDS:
|
||||||
|
return 6.144f;
|
||||||
|
break;
|
||||||
|
case GAIN_ONE:
|
||||||
|
return 4.096f;
|
||||||
|
break;
|
||||||
|
case GAIN_TWO:
|
||||||
|
return 2.048f;
|
||||||
|
break;
|
||||||
|
case GAIN_FOUR:
|
||||||
|
return 1.024f;
|
||||||
|
break;
|
||||||
|
case GAIN_EIGHT:
|
||||||
|
return 0.512f;
|
||||||
|
break;
|
||||||
|
case GAIN_SIXTEEN:
|
||||||
|
return 0.256f;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Compute volts for the given raw counts.
|
||||||
|
|
||||||
|
@param counts the ADC reading in raw counts
|
||||||
|
|
||||||
|
@return the ADC reading in volts
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
float Adafruit_ADS1X15::computeVolts(int16_t counts) {
|
||||||
|
return counts * (getFsRange() / (32768 >> m_bitShift));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Non-blocking start conversion function
|
||||||
|
|
||||||
|
Call getLastConversionResults() once conversionComplete() returns true.
|
||||||
|
In continuous mode, getLastConversionResults() will always return the
|
||||||
|
latest result.
|
||||||
|
ALERT/RDY pin is set to RDY mode, and a 8us pulse is generated every
|
||||||
|
time new data is ready.
|
||||||
|
|
||||||
|
@param mux mux field value
|
||||||
|
@param continuous continuous if set, otherwise single shot
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
void Adafruit_ADS1X15::startADCReading(uint16_t mux, bool continuous) {
|
||||||
|
// Start with default values
|
||||||
|
uint16_t config =
|
||||||
|
ADS1X15_REG_CONFIG_CQUE_1CONV | // Set CQUE to any value other than
|
||||||
|
// None so we can use it in RDY mode
|
||||||
|
ADS1X15_REG_CONFIG_CLAT_NONLAT | // Non-latching (default val)
|
||||||
|
ADS1X15_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low (default val)
|
||||||
|
ADS1X15_REG_CONFIG_CMODE_TRAD; // Traditional comparator (default val)
|
||||||
|
|
||||||
|
if (continuous) {
|
||||||
|
config |= ADS1X15_REG_CONFIG_MODE_CONTIN;
|
||||||
|
} else {
|
||||||
|
config |= ADS1X15_REG_CONFIG_MODE_SINGLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set PGA/voltage range
|
||||||
|
config |= m_gain;
|
||||||
|
|
||||||
|
// Set data rate
|
||||||
|
config |= m_dataRate;
|
||||||
|
|
||||||
|
// Set channels
|
||||||
|
config |= mux;
|
||||||
|
|
||||||
|
// Set 'start single-conversion' bit
|
||||||
|
config |= ADS1X15_REG_CONFIG_OS_SINGLE;
|
||||||
|
|
||||||
|
// Write config register to the ADC
|
||||||
|
writeRegister(ADS1X15_REG_POINTER_CONFIG, config);
|
||||||
|
|
||||||
|
// Set ALERT/RDY to RDY mode.
|
||||||
|
writeRegister(ADS1X15_REG_POINTER_HITHRESH, 0x8000);
|
||||||
|
writeRegister(ADS1X15_REG_POINTER_LOWTHRESH, 0x0000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Returns true if conversion is complete, false otherwise.
|
||||||
|
|
||||||
|
@return True if conversion is complete, false otherwise.
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
bool Adafruit_ADS1X15::conversionComplete() {
|
||||||
|
return (readRegister(ADS1X15_REG_POINTER_CONFIG) & 0x8000) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Writes 16-bits to the specified destination register
|
||||||
|
|
||||||
|
@param reg register address to write to
|
||||||
|
@param value value to write to register
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
void Adafruit_ADS1X15::writeRegister(uint8_t reg, uint16_t value) {
|
||||||
|
buffer[0] = reg;
|
||||||
|
buffer[1] = value >> 8;
|
||||||
|
buffer[2] = value & 0xFF;
|
||||||
|
m_i2c_dev->write(buffer, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Read 16-bits from the specified destination register
|
||||||
|
|
||||||
|
@param reg register address to read from
|
||||||
|
|
||||||
|
@return 16 bit register value read
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
uint16_t Adafruit_ADS1X15::readRegister(uint8_t reg) {
|
||||||
|
buffer[0] = reg;
|
||||||
|
m_i2c_dev->write(buffer, 1);
|
||||||
|
m_i2c_dev->read(buffer, 2);
|
||||||
|
return ((buffer[0] << 8) | buffer[1]);
|
||||||
|
}
|
||||||
202
lib/Adafruit_ADS1X15/Adafruit_ADS1X15.h
Normal file
202
lib/Adafruit_ADS1X15/Adafruit_ADS1X15.h
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@file Adafruit_ADS1X15.h
|
||||||
|
|
||||||
|
This is a library for the Adafruit ADS1X15 ADC breakout boards.
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit and open-source hardware by purchasing
|
||||||
|
products from Adafruit!
|
||||||
|
|
||||||
|
Written by Kevin "KTOWN" Townsend for Adafruit Industries.
|
||||||
|
|
||||||
|
BSD license, all text here must be included in any redistribution
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
#ifndef __ADS1X15_H__
|
||||||
|
#define __ADS1X15_H__
|
||||||
|
|
||||||
|
#include <Adafruit_I2CDevice.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
/*=========================================================================
|
||||||
|
I2C ADDRESS/BITS
|
||||||
|
-----------------------------------------------------------------------*/
|
||||||
|
#define ADS1X15_ADDRESS (0x48) ///< 1001 000 (ADDR = GND)
|
||||||
|
/*=========================================================================*/
|
||||||
|
|
||||||
|
/*=========================================================================
|
||||||
|
POINTER REGISTER
|
||||||
|
-----------------------------------------------------------------------*/
|
||||||
|
#define ADS1X15_REG_POINTER_MASK (0x03) ///< Point mask
|
||||||
|
#define ADS1X15_REG_POINTER_CONVERT (0x00) ///< Conversion
|
||||||
|
#define ADS1X15_REG_POINTER_CONFIG (0x01) ///< Configuration
|
||||||
|
#define ADS1X15_REG_POINTER_LOWTHRESH (0x02) ///< Low threshold
|
||||||
|
#define ADS1X15_REG_POINTER_HITHRESH (0x03) ///< High threshold
|
||||||
|
/*=========================================================================*/
|
||||||
|
|
||||||
|
/*=========================================================================
|
||||||
|
CONFIG REGISTER
|
||||||
|
-----------------------------------------------------------------------*/
|
||||||
|
#define ADS1X15_REG_CONFIG_OS_MASK (0x8000) ///< OS Mask
|
||||||
|
#define ADS1X15_REG_CONFIG_OS_SINGLE \
|
||||||
|
(0x8000) ///< Write: Set to start a single-conversion
|
||||||
|
#define ADS1X15_REG_CONFIG_OS_BUSY \
|
||||||
|
(0x0000) ///< Read: Bit = 0 when conversion is in progress
|
||||||
|
#define ADS1X15_REG_CONFIG_OS_NOTBUSY \
|
||||||
|
(0x8000) ///< Read: Bit = 1 when device is not performing a conversion
|
||||||
|
|
||||||
|
#define ADS1X15_REG_CONFIG_MUX_MASK (0x7000) ///< Mux Mask
|
||||||
|
#define ADS1X15_REG_CONFIG_MUX_DIFF_0_1 \
|
||||||
|
(0x0000) ///< Differential P = AIN0, N = AIN1 (default)
|
||||||
|
#define ADS1X15_REG_CONFIG_MUX_DIFF_0_3 \
|
||||||
|
(0x1000) ///< Differential P = AIN0, N = AIN3
|
||||||
|
#define ADS1X15_REG_CONFIG_MUX_DIFF_1_3 \
|
||||||
|
(0x2000) ///< Differential P = AIN1, N = AIN3
|
||||||
|
#define ADS1X15_REG_CONFIG_MUX_DIFF_2_3 \
|
||||||
|
(0x3000) ///< Differential P = AIN2, N = AIN3
|
||||||
|
#define ADS1X15_REG_CONFIG_MUX_SINGLE_0 (0x4000) ///< Single-ended AIN0
|
||||||
|
#define ADS1X15_REG_CONFIG_MUX_SINGLE_1 (0x5000) ///< Single-ended AIN1
|
||||||
|
#define ADS1X15_REG_CONFIG_MUX_SINGLE_2 (0x6000) ///< Single-ended AIN2
|
||||||
|
#define ADS1X15_REG_CONFIG_MUX_SINGLE_3 (0x7000) ///< Single-ended AIN3
|
||||||
|
|
||||||
|
constexpr uint16_t MUX_BY_CHANNEL[] = {
|
||||||
|
ADS1X15_REG_CONFIG_MUX_SINGLE_0, ///< Single-ended AIN0
|
||||||
|
ADS1X15_REG_CONFIG_MUX_SINGLE_1, ///< Single-ended AIN1
|
||||||
|
ADS1X15_REG_CONFIG_MUX_SINGLE_2, ///< Single-ended AIN2
|
||||||
|
ADS1X15_REG_CONFIG_MUX_SINGLE_3 ///< Single-ended AIN3
|
||||||
|
}; ///< MUX config by channel
|
||||||
|
|
||||||
|
#define ADS1X15_REG_CONFIG_PGA_MASK (0x0E00) ///< PGA Mask
|
||||||
|
#define ADS1X15_REG_CONFIG_PGA_6_144V (0x0000) ///< +/-6.144V range = Gain 2/3
|
||||||
|
#define ADS1X15_REG_CONFIG_PGA_4_096V (0x0200) ///< +/-4.096V range = Gain 1
|
||||||
|
#define ADS1X15_REG_CONFIG_PGA_2_048V \
|
||||||
|
(0x0400) ///< +/-2.048V range = Gain 2 (default)
|
||||||
|
#define ADS1X15_REG_CONFIG_PGA_1_024V (0x0600) ///< +/-1.024V range = Gain 4
|
||||||
|
#define ADS1X15_REG_CONFIG_PGA_0_512V (0x0800) ///< +/-0.512V range = Gain 8
|
||||||
|
#define ADS1X15_REG_CONFIG_PGA_0_256V (0x0A00) ///< +/-0.256V range = Gain 16
|
||||||
|
|
||||||
|
#define ADS1X15_REG_CONFIG_MODE_MASK (0x0100) ///< Mode Mask
|
||||||
|
#define ADS1X15_REG_CONFIG_MODE_CONTIN (0x0000) ///< Continuous conversion mode
|
||||||
|
#define ADS1X15_REG_CONFIG_MODE_SINGLE \
|
||||||
|
(0x0100) ///< Power-down single-shot mode (default)
|
||||||
|
|
||||||
|
#define ADS1X15_REG_CONFIG_RATE_MASK (0x00E0) ///< Data Rate Mask
|
||||||
|
|
||||||
|
#define ADS1X15_REG_CONFIG_CMODE_MASK (0x0010) ///< CMode Mask
|
||||||
|
#define ADS1X15_REG_CONFIG_CMODE_TRAD \
|
||||||
|
(0x0000) ///< Traditional comparator with hysteresis (default)
|
||||||
|
#define ADS1X15_REG_CONFIG_CMODE_WINDOW (0x0010) ///< Window comparator
|
||||||
|
|
||||||
|
#define ADS1X15_REG_CONFIG_CPOL_MASK (0x0008) ///< CPol Mask
|
||||||
|
#define ADS1X15_REG_CONFIG_CPOL_ACTVLOW \
|
||||||
|
(0x0000) ///< ALERT/RDY pin is low when active (default)
|
||||||
|
#define ADS1X15_REG_CONFIG_CPOL_ACTVHI \
|
||||||
|
(0x0008) ///< ALERT/RDY pin is high when active
|
||||||
|
|
||||||
|
#define ADS1X15_REG_CONFIG_CLAT_MASK \
|
||||||
|
(0x0004) ///< Determines if ALERT/RDY pin latches once asserted
|
||||||
|
#define ADS1X15_REG_CONFIG_CLAT_NONLAT \
|
||||||
|
(0x0000) ///< Non-latching comparator (default)
|
||||||
|
#define ADS1X15_REG_CONFIG_CLAT_LATCH (0x0004) ///< Latching comparator
|
||||||
|
|
||||||
|
#define ADS1X15_REG_CONFIG_CQUE_MASK (0x0003) ///< CQue Mask
|
||||||
|
#define ADS1X15_REG_CONFIG_CQUE_1CONV \
|
||||||
|
(0x0000) ///< Assert ALERT/RDY after one conversions
|
||||||
|
#define ADS1X15_REG_CONFIG_CQUE_2CONV \
|
||||||
|
(0x0001) ///< Assert ALERT/RDY after two conversions
|
||||||
|
#define ADS1X15_REG_CONFIG_CQUE_4CONV \
|
||||||
|
(0x0002) ///< Assert ALERT/RDY after four conversions
|
||||||
|
#define ADS1X15_REG_CONFIG_CQUE_NONE \
|
||||||
|
(0x0003) ///< Disable the comparator and put ALERT/RDY in high state (default)
|
||||||
|
/*=========================================================================*/
|
||||||
|
|
||||||
|
/** Gain settings */
|
||||||
|
typedef enum {
|
||||||
|
GAIN_TWOTHIRDS = ADS1X15_REG_CONFIG_PGA_6_144V,
|
||||||
|
GAIN_ONE = ADS1X15_REG_CONFIG_PGA_4_096V,
|
||||||
|
GAIN_TWO = ADS1X15_REG_CONFIG_PGA_2_048V,
|
||||||
|
GAIN_FOUR = ADS1X15_REG_CONFIG_PGA_1_024V,
|
||||||
|
GAIN_EIGHT = ADS1X15_REG_CONFIG_PGA_0_512V,
|
||||||
|
GAIN_SIXTEEN = ADS1X15_REG_CONFIG_PGA_0_256V
|
||||||
|
} adsGain_t;
|
||||||
|
|
||||||
|
/** Data rates */
|
||||||
|
#define RATE_ADS1015_128SPS (0x0000) ///< 128 samples per second
|
||||||
|
#define RATE_ADS1015_250SPS (0x0020) ///< 250 samples per second
|
||||||
|
#define RATE_ADS1015_490SPS (0x0040) ///< 490 samples per second
|
||||||
|
#define RATE_ADS1015_920SPS (0x0060) ///< 920 samples per second
|
||||||
|
#define RATE_ADS1015_1600SPS (0x0080) ///< 1600 samples per second (default)
|
||||||
|
#define RATE_ADS1015_2400SPS (0x00A0) ///< 2400 samples per second
|
||||||
|
#define RATE_ADS1015_3300SPS (0x00C0) ///< 3300 samples per second
|
||||||
|
|
||||||
|
#define RATE_ADS1115_8SPS (0x0000) ///< 8 samples per second
|
||||||
|
#define RATE_ADS1115_16SPS (0x0020) ///< 16 samples per second
|
||||||
|
#define RATE_ADS1115_32SPS (0x0040) ///< 32 samples per second
|
||||||
|
#define RATE_ADS1115_64SPS (0x0060) ///< 64 samples per second
|
||||||
|
#define RATE_ADS1115_128SPS (0x0080) ///< 128 samples per second (default)
|
||||||
|
#define RATE_ADS1115_250SPS (0x00A0) ///< 250 samples per second
|
||||||
|
#define RATE_ADS1115_475SPS (0x00C0) ///< 475 samples per second
|
||||||
|
#define RATE_ADS1115_860SPS (0x00E0) ///< 860 samples per second
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Sensor driver for the Adafruit ADS1X15 ADC breakouts.
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
class Adafruit_ADS1X15 {
|
||||||
|
protected:
|
||||||
|
// Instance-specific properties
|
||||||
|
Adafruit_I2CDevice *m_i2c_dev; ///< I2C bus device
|
||||||
|
uint8_t m_bitShift; ///< bit shift amount
|
||||||
|
adsGain_t m_gain; ///< ADC gain
|
||||||
|
uint16_t m_dataRate; ///< Data rate
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool begin(uint8_t i2c_addr = ADS1X15_ADDRESS, TwoWire *wire = &Wire);
|
||||||
|
int16_t readADC_SingleEnded(uint8_t channel);
|
||||||
|
int16_t readADC_Differential_0_1();
|
||||||
|
int16_t readADC_Differential_0_3();
|
||||||
|
int16_t readADC_Differential_1_3();
|
||||||
|
int16_t readADC_Differential_2_3();
|
||||||
|
void startComparator_SingleEnded(uint8_t channel, int16_t threshold);
|
||||||
|
int16_t getLastConversionResults();
|
||||||
|
float getFsRange();
|
||||||
|
float computeVolts(int16_t counts);
|
||||||
|
void setGain(adsGain_t gain);
|
||||||
|
adsGain_t getGain();
|
||||||
|
void setDataRate(uint16_t rate);
|
||||||
|
uint16_t getDataRate();
|
||||||
|
|
||||||
|
void startADCReading(uint16_t mux, bool continuous);
|
||||||
|
|
||||||
|
bool conversionComplete();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writeRegister(uint8_t reg, uint16_t value);
|
||||||
|
uint16_t readRegister(uint8_t reg);
|
||||||
|
uint8_t buffer[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Sensor driver for the Adafruit ADS1015 ADC breakout.
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
class Adafruit_ADS1015 : public Adafruit_ADS1X15 {
|
||||||
|
public:
|
||||||
|
Adafruit_ADS1015();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Sensor driver for the Adafruit ADS1115 ADC breakout.
|
||||||
|
*/
|
||||||
|
/**************************************************************************/
|
||||||
|
class Adafruit_ADS1115 : public Adafruit_ADS1X15 {
|
||||||
|
public:
|
||||||
|
Adafruit_ADS1115();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
23
lib/Adafruit_ADS1X15/README.md
Normal file
23
lib/Adafruit_ADS1X15/README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Adafruit_ADS1015 [](http://adafruit.github.io/Adafruit_ADS1X15/html/index.html)
|
||||||
|
|
||||||
|
|
||||||
|
Driver for TI's ADS1X15: 12 and 16-bit Differential or Single-Ended ADC with PGA and Comparator
|
||||||
|
|
||||||
|
## Info
|
||||||
|
|
||||||
|
This family of ADCs provide 4 single-ended or 2 differential channels.
|
||||||
|
Each has a programmable gain amplifier from 2/3 up to 16x. Available
|
||||||
|
in 12 or 16 bit versions:
|
||||||
|
|
||||||
|
* [ADS1015 12-bit ADC](https://www.adafruit.com/product/1083)
|
||||||
|
* [ADS1115 16-bit ADC](https://www.adafruit.com/product/1085)
|
||||||
|
|
||||||
|
The chip's fairly small so it comes on a breakout board with ferrites to keep the AVDD and AGND quiet. Interfacing is done via I2C. The address can be changed to one of four options (see the datasheet table 5) so you can have up to 4 ADS1x15's connected on a single 2-wire I2C bus for 16 single ended inputs.
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code, please
|
||||||
|
support Adafruit and open-source hardware by purchasing products from
|
||||||
|
[Adafruit](https://www.adafruit.com)!
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
BSD license, all text above must be included in any redistribution.
|
||||||
44
lib/Adafruit_ADS1X15/examples/comparator/comparator.ino
Normal file
44
lib/Adafruit_ADS1X15/examples/comparator/comparator.ino
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include <Adafruit_ADS1X15.h>
|
||||||
|
|
||||||
|
// Adafruit_ADS1115 ads; /* Use this for the 16-bit version */
|
||||||
|
Adafruit_ADS1015 ads; /* Use this for the 12-bit version */
|
||||||
|
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
Serial.println("Hello!");
|
||||||
|
|
||||||
|
Serial.println("Single-ended readings from AIN0 with >3.0V comparator");
|
||||||
|
Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115)");
|
||||||
|
Serial.println("Comparator Threshold: 1000 (3.000V)");
|
||||||
|
|
||||||
|
// The ADC input range (or gain) can be changed via the following
|
||||||
|
// functions, but be careful never to exceed VDD +0.3V max, or to
|
||||||
|
// exceed the upper and lower limits if you adjust the input range!
|
||||||
|
// ADS1015 ADS1115
|
||||||
|
// ------- -------
|
||||||
|
// ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV 0.1875mV (default)
|
||||||
|
// ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV
|
||||||
|
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV 0.0625mV
|
||||||
|
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV 0.03125mV
|
||||||
|
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV 0.015625mV
|
||||||
|
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV 0.0078125mV
|
||||||
|
|
||||||
|
if (!ads.begin()) {
|
||||||
|
Serial.println("Failed to initialize ADS.");
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
// Setup 3V comparator on channel 0
|
||||||
|
ads.startComparator_SingleEnded(0, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
int16_t adc0;
|
||||||
|
|
||||||
|
// Comparator will only de-assert after a read
|
||||||
|
adc0 = ads.getLastConversionResults();
|
||||||
|
Serial.print("AIN0: "); Serial.println(adc0);
|
||||||
|
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
70
lib/Adafruit_ADS1X15/examples/continuous/continuous.ino
Normal file
70
lib/Adafruit_ADS1X15/examples/continuous/continuous.ino
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include <Adafruit_ADS1X15.h>
|
||||||
|
|
||||||
|
// Adafruit_ADS1115 ads; /* Use this for the 16-bit version */
|
||||||
|
Adafruit_ADS1015 ads; /* Use this for the 12-bit version */
|
||||||
|
|
||||||
|
// Pin connected to the ALERT/RDY signal for new sample notification.
|
||||||
|
constexpr int READY_PIN = 3;
|
||||||
|
|
||||||
|
// This is required on ESP32 to put the ISR in IRAM. Define as
|
||||||
|
// empty for other platforms. Be careful - other platforms may have
|
||||||
|
// other requirements.
|
||||||
|
#ifndef IRAM_ATTR
|
||||||
|
#define IRAM_ATTR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
volatile bool new_data = false;
|
||||||
|
void IRAM_ATTR NewDataReadyISR() {
|
||||||
|
new_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
Serial.println("Hello!");
|
||||||
|
|
||||||
|
Serial.println("Getting differential reading from AIN0 (P) and AIN1 (N)");
|
||||||
|
Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115)");
|
||||||
|
|
||||||
|
// The ADC input range (or gain) can be changed via the following
|
||||||
|
// functions, but be careful never to exceed VDD +0.3V max, or to
|
||||||
|
// exceed the upper and lower limits if you adjust the input range!
|
||||||
|
// ADS1015 ADS1115
|
||||||
|
// ------- -------
|
||||||
|
// ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV 0.1875mV (default)
|
||||||
|
// ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV
|
||||||
|
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV 0.0625mV
|
||||||
|
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV 0.03125mV
|
||||||
|
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV 0.015625mV
|
||||||
|
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV 0.0078125mV
|
||||||
|
|
||||||
|
if (!ads.begin()) {
|
||||||
|
Serial.println("Failed to initialize ADS.");
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pinMode(READY_PIN, INPUT);
|
||||||
|
// With default COMP_POL=0, get a rising edge every time a new sample is ready.
|
||||||
|
attachInterrupt(digitalPinToInterrupt(READY_PIN), NewDataReadyISR, RISING);
|
||||||
|
|
||||||
|
// Start continuous conversions.
|
||||||
|
ads.startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_0_1, /*continuous=*/true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
// If we don't have new data, skip this iteration.
|
||||||
|
if (!new_data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t results = ads.getLastConversionResults();
|
||||||
|
|
||||||
|
Serial.print("Differential: "); Serial.print(results); Serial.print("("); Serial.print(ads.computeVolts(results)); Serial.println("V)");
|
||||||
|
|
||||||
|
new_data = false;
|
||||||
|
|
||||||
|
// In a real application we probably don't want to do a delay here if we are doing interrupt-based sampling, but we have a delay
|
||||||
|
// in this example to avoid writing too much data to the serial port.
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
44
lib/Adafruit_ADS1X15/examples/differential/differential.ino
Normal file
44
lib/Adafruit_ADS1X15/examples/differential/differential.ino
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include <Adafruit_ADS1X15.h>
|
||||||
|
|
||||||
|
// Adafruit_ADS1115 ads; /* Use this for the 16-bit version */
|
||||||
|
Adafruit_ADS1015 ads; /* Use this for the 12-bit version */
|
||||||
|
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
Serial.println("Hello!");
|
||||||
|
|
||||||
|
Serial.println("Getting differential reading from AIN0 (P) and AIN1 (N)");
|
||||||
|
Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115)");
|
||||||
|
|
||||||
|
// The ADC input range (or gain) can be changed via the following
|
||||||
|
// functions, but be careful never to exceed VDD +0.3V max, or to
|
||||||
|
// exceed the upper and lower limits if you adjust the input range!
|
||||||
|
// ADS1015 ADS1115
|
||||||
|
// ------- -------
|
||||||
|
// ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV 0.1875mV (default)
|
||||||
|
// ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV
|
||||||
|
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV 0.0625mV
|
||||||
|
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV 0.03125mV
|
||||||
|
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV 0.015625mV
|
||||||
|
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV 0.0078125mV
|
||||||
|
|
||||||
|
if (!ads.begin()) {
|
||||||
|
Serial.println("Failed to initialize ADS.");
|
||||||
|
while (1);
|
||||||
|
}}
|
||||||
|
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
int16_t results;
|
||||||
|
|
||||||
|
/* Be sure to update this value based on the IC and the gain settings! */
|
||||||
|
float multiplier = 3.0F; /* ADS1015 @ +/- 6.144V gain (12-bit results) */
|
||||||
|
//float multiplier = 0.1875F; /* ADS1115 @ +/- 6.144V gain (16-bit results) */
|
||||||
|
|
||||||
|
results = ads.readADC_Differential_0_1();
|
||||||
|
|
||||||
|
Serial.print("Differential: "); Serial.print(results); Serial.print("("); Serial.print(results * multiplier); Serial.println("mV)");
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
50
lib/Adafruit_ADS1X15/examples/nonblocking/nonblocking.ino
Normal file
50
lib/Adafruit_ADS1X15/examples/nonblocking/nonblocking.ino
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include <Adafruit_ADS1X15.h>
|
||||||
|
|
||||||
|
// Adafruit_ADS1115 ads; /* Use this for the 16-bit version */
|
||||||
|
Adafruit_ADS1015 ads; /* Use this for the 12-bit version */
|
||||||
|
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
Serial.println("Hello!");
|
||||||
|
|
||||||
|
Serial.println("Getting differential reading from AIN0 (P) and AIN1 (N)");
|
||||||
|
Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115)");
|
||||||
|
|
||||||
|
// The ADC input range (or gain) can be changed via the following
|
||||||
|
// functions, but be careful never to exceed VDD +0.3V max, or to
|
||||||
|
// exceed the upper and lower limits if you adjust the input range!
|
||||||
|
// ADS1015 ADS1115
|
||||||
|
// ------- -------
|
||||||
|
// ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV 0.1875mV (default)
|
||||||
|
// ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV
|
||||||
|
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV 0.0625mV
|
||||||
|
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV 0.03125mV
|
||||||
|
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV 0.015625mV
|
||||||
|
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV 0.0078125mV
|
||||||
|
|
||||||
|
if (!ads.begin()) {
|
||||||
|
Serial.println("Failed to initialize ADS.");
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the first conversion.
|
||||||
|
ads.startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_0_1, /*continuous=*/false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
// If we don't have new data, skip this iteration.
|
||||||
|
if (!ads.conversionComplete()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t results = ads.getLastConversionResults();
|
||||||
|
|
||||||
|
Serial.print("Differential: "); Serial.print(results); Serial.print("("); Serial.print(ads.computeVolts(results)); Serial.println("V)");
|
||||||
|
|
||||||
|
// Start another conversion.
|
||||||
|
ads.startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_0_1, /*continuous=*/false);
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
54
lib/Adafruit_ADS1X15/examples/singleended/singleended.ino
Normal file
54
lib/Adafruit_ADS1X15/examples/singleended/singleended.ino
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <Adafruit_ADS1X15.h>
|
||||||
|
|
||||||
|
//Adafruit_ADS1115 ads; /* Use this for the 16-bit version */
|
||||||
|
Adafruit_ADS1015 ads; /* Use this for the 12-bit version */
|
||||||
|
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
Serial.println("Hello!");
|
||||||
|
|
||||||
|
Serial.println("Getting single-ended readings from AIN0..3");
|
||||||
|
Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115)");
|
||||||
|
|
||||||
|
// The ADC input range (or gain) can be changed via the following
|
||||||
|
// functions, but be careful never to exceed VDD +0.3V max, or to
|
||||||
|
// exceed the upper and lower limits if you adjust the input range!
|
||||||
|
// ADS1015 ADS1115
|
||||||
|
// ------- -------
|
||||||
|
// ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV 0.1875mV (default)
|
||||||
|
// ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV
|
||||||
|
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV 0.0625mV
|
||||||
|
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV 0.03125mV
|
||||||
|
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV 0.015625mV
|
||||||
|
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV 0.0078125mV
|
||||||
|
|
||||||
|
if (!ads.begin()) {
|
||||||
|
Serial.println("Failed to initialize ADS.");
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
int16_t adc0, adc1, adc2, adc3;
|
||||||
|
float volts0, volts1, volts2, volts3;
|
||||||
|
|
||||||
|
adc0 = ads.readADC_SingleEnded(0);
|
||||||
|
adc1 = ads.readADC_SingleEnded(1);
|
||||||
|
adc2 = ads.readADC_SingleEnded(2);
|
||||||
|
adc3 = ads.readADC_SingleEnded(3);
|
||||||
|
|
||||||
|
volts0 = ads.computeVolts(adc0);
|
||||||
|
volts1 = ads.computeVolts(adc1);
|
||||||
|
volts2 = ads.computeVolts(adc2);
|
||||||
|
volts3 = ads.computeVolts(adc3);
|
||||||
|
|
||||||
|
Serial.println("-----------------------------------------------------------");
|
||||||
|
Serial.print("AIN0: "); Serial.print(adc0); Serial.print(" "); Serial.print(volts0); Serial.println("V");
|
||||||
|
Serial.print("AIN1: "); Serial.print(adc1); Serial.print(" "); Serial.print(volts1); Serial.println("V");
|
||||||
|
Serial.print("AIN2: "); Serial.print(adc2); Serial.print(" "); Serial.print(volts2); Serial.println("V");
|
||||||
|
Serial.print("AIN3: "); Serial.print(adc3); Serial.print(" "); Serial.print(volts3); Serial.println("V");
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
14
lib/Adafruit_ADS1X15/keywords.txt
Normal file
14
lib/Adafruit_ADS1X15/keywords.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Adafruit_ADS1015 KEYWORD1
|
||||||
|
Adafruit_ADS1115 KEYWORD1
|
||||||
|
begin KEYWORD2
|
||||||
|
readADC_SingleEnded KEYWORD2
|
||||||
|
readADC_Differential_0_1 KEYWORD2
|
||||||
|
readADC_Differential_2_3 KEYWORD2
|
||||||
|
startComparator_SingleEnded KEYWORD2
|
||||||
|
getLastConversionResults KEYWORD2
|
||||||
|
getFsRange KEYWORD1
|
||||||
|
computeVolts KEYWORD2
|
||||||
|
setGain KEYWORD2
|
||||||
|
getGain KEYWORD2
|
||||||
|
setDataRate KEYWORD2
|
||||||
|
getDataRate KEYWORD2
|
||||||
10
lib/Adafruit_ADS1X15/library.properties
Normal file
10
lib/Adafruit_ADS1X15/library.properties
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
name=Adafruit ADS1X15
|
||||||
|
version=2.6.0
|
||||||
|
author=Adafruit
|
||||||
|
maintainer=Adafruit <info@adafruit.com>
|
||||||
|
sentence=Arduino library for ADS1015/1115 ADCs.
|
||||||
|
paragraph=Arduino library for ADS1015/1115 12/16-bit Differential or Single-Ended ADCs with PGA and Comparator
|
||||||
|
category=Signal Input/Output
|
||||||
|
url=https://github.com/adafruit/Adafruit_ADS1X15
|
||||||
|
architectures=*
|
||||||
|
depends=Adafruit BusIO
|
||||||
26
lib/Adafruit_ADS1X15/license.txt
Normal file
26
lib/Adafruit_ADS1X15/license.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Software License Agreement (BSD License)
|
||||||
|
|
||||||
|
Copyright (c) 2012, Adafruit Industries
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holders nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
BIN
lib/Adafruit_INA219-master.zip
Normal file
BIN
lib/Adafruit_INA219-master.zip
Normal file
Binary file not shown.
46
lib/Adafruit_INA219/.github/ISSUE_TEMPLATE.md
vendored
Normal file
46
lib/Adafruit_INA219/.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
Thank you for opening an issue on an Adafruit Arduino library repository. To
|
||||||
|
improve the speed of resolution please review the following guidelines and
|
||||||
|
common troubleshooting steps below before creating the issue:
|
||||||
|
|
||||||
|
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
|
||||||
|
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
|
||||||
|
something isn't working as expected. In many cases the problem is a common issue
|
||||||
|
that you will more quickly receive help from the forum community. GitHub issues
|
||||||
|
are meant for known defects in the code. If you don't know if there is a defect
|
||||||
|
in the code then start with troubleshooting on the forum first.
|
||||||
|
|
||||||
|
- **If following a tutorial or guide be sure you didn't miss a step.** Carefully
|
||||||
|
check all of the steps and commands to run have been followed. Consult the
|
||||||
|
forum if you're unsure or have questions about steps in a guide/tutorial.
|
||||||
|
|
||||||
|
- **For Arduino projects check these very common issues to ensure they don't apply**:
|
||||||
|
|
||||||
|
- For uploading sketches or communicating with the board make sure you're using
|
||||||
|
a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes
|
||||||
|
very hard to tell the difference between a data and charge cable! Try using the
|
||||||
|
cable with other devices or swapping to another cable to confirm it is not
|
||||||
|
the problem.
|
||||||
|
|
||||||
|
- **Be sure you are supplying adequate power to the board.** Check the specs of
|
||||||
|
your board and plug in an external power supply. In many cases just
|
||||||
|
plugging a board into your computer is not enough to power it and other
|
||||||
|
peripherals.
|
||||||
|
|
||||||
|
- **Double check all soldering joints and connections.** Flakey connections
|
||||||
|
cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints.
|
||||||
|
|
||||||
|
- **Ensure you are using an official Arduino or Adafruit board.** We can't
|
||||||
|
guarantee a clone board will have the same functionality and work as expected
|
||||||
|
with this code and don't support them.
|
||||||
|
|
||||||
|
If you're sure this issue is a defect in the code and checked the steps above
|
||||||
|
please fill in the following fields to provide enough troubleshooting information.
|
||||||
|
You may delete the guideline and text above to just leave the following details:
|
||||||
|
|
||||||
|
- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE**
|
||||||
|
|
||||||
|
- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO
|
||||||
|
VERSION HERE**
|
||||||
|
|
||||||
|
- List the steps to reproduce the problem below (if possible attach a sketch or
|
||||||
|
copy the sketch code in too): **LIST REPRO STEPS BELOW**
|
||||||
26
lib/Adafruit_INA219/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
26
lib/Adafruit_INA219/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Thank you for creating a pull request to contribute to Adafruit's GitHub code!
|
||||||
|
Before you open the request please review the following guidelines and tips to
|
||||||
|
help it be more easily integrated:
|
||||||
|
|
||||||
|
- **Describe the scope of your change--i.e. what the change does and what parts
|
||||||
|
of the code were modified.** This will help us understand any risks of integrating
|
||||||
|
the code.
|
||||||
|
|
||||||
|
- **Describe any known limitations with your change.** For example if the change
|
||||||
|
doesn't apply to a supported platform of the library please mention it.
|
||||||
|
|
||||||
|
- **Please run any tests or examples that can exercise your modified code.** We
|
||||||
|
strive to not break users of the code and running tests/examples helps with this
|
||||||
|
process.
|
||||||
|
|
||||||
|
Thank you again for contributing! We will try to test and integrate the change
|
||||||
|
as soon as we can, but be aware we have many GitHub repositories to manage and
|
||||||
|
can't immediately respond to every request. There is no need to bump or check in
|
||||||
|
on a pull request (it will clutter the discussion of the request).
|
||||||
|
|
||||||
|
Also don't be worried if the request is closed or not integrated--sometimes the
|
||||||
|
priorities of Adafruit's GitHub code (education, ease of use) might not match the
|
||||||
|
priorities of the pull request. Don't fret, the open source community thrives on
|
||||||
|
forks and GitHub makes it easy to keep your changes in a forked repo.
|
||||||
|
|
||||||
|
After reviewing the guidelines above you can delete this text from the pull request.
|
||||||
32
lib/Adafruit_INA219/.github/workflows/githubci.yml
vendored
Normal file
32
lib/Adafruit_INA219/.github/workflows/githubci.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
name: Arduino Library CI
|
||||||
|
|
||||||
|
on: [pull_request, push, repository_dispatch]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: adafruit/ci-arduino
|
||||||
|
path: ci
|
||||||
|
|
||||||
|
- name: pre-install
|
||||||
|
run: bash ci/actions_install.sh
|
||||||
|
|
||||||
|
- name: test platforms
|
||||||
|
run: python3 ci/build_platform.py main_platforms
|
||||||
|
|
||||||
|
- name: clang
|
||||||
|
run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r .
|
||||||
|
|
||||||
|
- name: doxygen
|
||||||
|
env:
|
||||||
|
GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }}
|
||||||
|
PRETTYNAME : "Adafruit INA219 Arduino Library"
|
||||||
|
run: bash ci/doxy_gen_and_deploy.sh
|
||||||
8
lib/Adafruit_INA219/.gitignore
vendored
Normal file
8
lib/Adafruit_INA219/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# osx
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# doxygen
|
||||||
|
Doxyfile*
|
||||||
|
doxygen_sqlite3.db
|
||||||
|
html
|
||||||
|
*.tmp
|
||||||
486
lib/Adafruit_INA219/Adafruit_INA219.cpp
Normal file
486
lib/Adafruit_INA219/Adafruit_INA219.cpp
Normal file
@ -0,0 +1,486 @@
|
|||||||
|
/*!
|
||||||
|
* @file Adafruit_INA219.cpp
|
||||||
|
*
|
||||||
|
* @mainpage Adafruit INA219 current/power monitor IC
|
||||||
|
*
|
||||||
|
* @section intro_sec Introduction
|
||||||
|
*
|
||||||
|
* Driver for the INA219 current sensor
|
||||||
|
*
|
||||||
|
* This is a library for the Adafruit INA219 breakout
|
||||||
|
* ----> https://www.adafruit.com/product/904
|
||||||
|
*
|
||||||
|
* Adafruit invests time and resources providing this open source code,
|
||||||
|
* please support Adafruit and open-source hardware by purchasing
|
||||||
|
* products from Adafruit!
|
||||||
|
*
|
||||||
|
* @section author Author
|
||||||
|
*
|
||||||
|
* Written by Bryan Siepert and Kevin "KTOWN" Townsend for Adafruit Industries.
|
||||||
|
*
|
||||||
|
* @section license License
|
||||||
|
*
|
||||||
|
* BSD license, all text here must be included in any redistribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
#include "Adafruit_INA219.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Instantiates a new INA219 class
|
||||||
|
* @param addr the I2C address the device can be found on. Default is 0x40
|
||||||
|
*/
|
||||||
|
Adafruit_INA219::Adafruit_INA219(uint8_t addr) {
|
||||||
|
ina219_i2caddr = addr;
|
||||||
|
ina219_currentDivider_mA = 0;
|
||||||
|
ina219_powerMultiplier_mW = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief INA219 class destructor
|
||||||
|
*/
|
||||||
|
Adafruit_INA219::~Adafruit_INA219() { delete i2c_dev; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Sets up the HW (defaults to 32V and 2A for calibration values)
|
||||||
|
* @param theWire the TwoWire object to use
|
||||||
|
* @return true: success false: Failed to start I2C
|
||||||
|
*/
|
||||||
|
bool Adafruit_INA219::begin(TwoWire *theWire) {
|
||||||
|
if (!i2c_dev) {
|
||||||
|
i2c_dev = new Adafruit_I2CDevice(ina219_i2caddr, theWire);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i2c_dev->begin()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
init();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief begin I2C and set up the hardware
|
||||||
|
*/
|
||||||
|
void Adafruit_INA219::init() {
|
||||||
|
// Set chip to large range config values to start
|
||||||
|
setCalibration_32V_2A();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Gets the raw bus voltage (16-bit signed integer, so +-32767)
|
||||||
|
* @return the raw bus voltage reading
|
||||||
|
*/
|
||||||
|
int16_t Adafruit_INA219::getBusVoltage_raw() {
|
||||||
|
uint16_t value;
|
||||||
|
|
||||||
|
Adafruit_BusIO_Register bus_voltage_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_BUSVOLTAGE, 2, MSBFIRST);
|
||||||
|
_success = bus_voltage_reg.read(&value);
|
||||||
|
|
||||||
|
// Shift to the right 3 to drop CNVR and OVF and multiply by LSB
|
||||||
|
return (int16_t)((value >> 3) * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Gets the raw shunt voltage (16-bit signed integer, so +-32767)
|
||||||
|
* @return the raw shunt voltage reading
|
||||||
|
*/
|
||||||
|
int16_t Adafruit_INA219::getShuntVoltage_raw() {
|
||||||
|
uint16_t value;
|
||||||
|
Adafruit_BusIO_Register shunt_voltage_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_SHUNTVOLTAGE, 2, MSBFIRST);
|
||||||
|
_success = shunt_voltage_reg.read(&value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Gets the raw current value (16-bit signed integer, so +-32767)
|
||||||
|
* @return the raw current reading
|
||||||
|
*/
|
||||||
|
int16_t Adafruit_INA219::getCurrent_raw() {
|
||||||
|
uint16_t value;
|
||||||
|
|
||||||
|
// Sometimes a sharp load will reset the INA219, which will
|
||||||
|
// reset the cal register, meaning CURRENT and POWER will
|
||||||
|
// not be available ... avoid this by always setting a cal
|
||||||
|
// value even if it's an unfortunate extra step
|
||||||
|
Adafruit_BusIO_Register calibration_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_CALIBRATION, 2, MSBFIRST);
|
||||||
|
calibration_reg.write(ina219_calValue, 2);
|
||||||
|
|
||||||
|
// Now we can safely read the CURRENT register!
|
||||||
|
Adafruit_BusIO_Register current_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_CURRENT, 2, MSBFIRST);
|
||||||
|
_success = current_reg.read(&value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Gets the raw power value (16-bit signed integer, so +-32767)
|
||||||
|
* @return raw power reading
|
||||||
|
*/
|
||||||
|
int16_t Adafruit_INA219::getPower_raw() {
|
||||||
|
uint16_t value;
|
||||||
|
|
||||||
|
// Sometimes a sharp load will reset the INA219, which will
|
||||||
|
// reset the cal register, meaning CURRENT and POWER will
|
||||||
|
// not be available ... avoid this by always setting a cal
|
||||||
|
// value even if it's an unfortunate extra step
|
||||||
|
Adafruit_BusIO_Register calibration_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_CALIBRATION, 2, MSBFIRST);
|
||||||
|
calibration_reg.write(ina219_calValue, 2);
|
||||||
|
|
||||||
|
// Now we can safely read the POWER register!
|
||||||
|
Adafruit_BusIO_Register power_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_POWER, 2, MSBFIRST);
|
||||||
|
_success = power_reg.read(&value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Gets the shunt voltage in mV (so +-327mV)
|
||||||
|
* @return the shunt voltage converted to millivolts
|
||||||
|
*/
|
||||||
|
float Adafruit_INA219::getShuntVoltage_mV() {
|
||||||
|
int16_t value;
|
||||||
|
value = getShuntVoltage_raw();
|
||||||
|
return value * 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Gets the bus voltage in volts
|
||||||
|
* @return the bus voltage converted to volts
|
||||||
|
*/
|
||||||
|
float Adafruit_INA219::getBusVoltage_V() {
|
||||||
|
int16_t value = getBusVoltage_raw();
|
||||||
|
return value * 0.001;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Gets the current value in mA, taking into account the
|
||||||
|
* config settings and current LSB
|
||||||
|
* @return the current reading convereted to milliamps
|
||||||
|
*/
|
||||||
|
float Adafruit_INA219::getCurrent_mA() {
|
||||||
|
float valueDec = getCurrent_raw();
|
||||||
|
valueDec /= ina219_currentDivider_mA;
|
||||||
|
return valueDec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Gets the power value in mW, taking into account the
|
||||||
|
* config settings and current LSB
|
||||||
|
* @return power reading converted to milliwatts
|
||||||
|
*/
|
||||||
|
float Adafruit_INA219::getPower_mW() {
|
||||||
|
float valueDec = getPower_raw();
|
||||||
|
valueDec *= ina219_powerMultiplier_mW;
|
||||||
|
return valueDec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Configures to INA219 to be able to measure up to 32V and 2A
|
||||||
|
* of current. Each unit of current corresponds to 100uA, and
|
||||||
|
* each unit of power corresponds to 2mW. Counter overflow
|
||||||
|
* occurs at 3.2A.
|
||||||
|
* @note These calculations assume a 0.1 ohm resistor is present
|
||||||
|
*/
|
||||||
|
void Adafruit_INA219::setCalibration_32V_2A() {
|
||||||
|
// By default we use a pretty huge range for the input voltage,
|
||||||
|
// which probably isn't the most appropriate choice for system
|
||||||
|
// that don't use a lot of power. But all of the calculations
|
||||||
|
// are shown below if you want to change the settings. You will
|
||||||
|
// also need to change any relevant register settings, such as
|
||||||
|
// setting the VBUS_MAX to 16V instead of 32V, etc.
|
||||||
|
|
||||||
|
// VBUS_MAX = 32V (Assumes 32V, can also be set to 16V)
|
||||||
|
// VSHUNT_MAX = 0.32 (Assumes Gain 8, 320mV, can also be 0.16, 0.08,
|
||||||
|
// 0.04) RSHUNT = 0.1 (Resistor value in ohms)
|
||||||
|
|
||||||
|
// 1. Determine max possible current
|
||||||
|
// MaxPossible_I = VSHUNT_MAX / RSHUNT
|
||||||
|
// MaxPossible_I = 3.2A
|
||||||
|
|
||||||
|
// 2. Determine max expected current
|
||||||
|
// MaxExpected_I = 2.0A
|
||||||
|
|
||||||
|
// 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit)
|
||||||
|
// MinimumLSB = MaxExpected_I/32767
|
||||||
|
// MinimumLSB = 0.000061 (61uA per bit)
|
||||||
|
// MaximumLSB = MaxExpected_I/4096
|
||||||
|
// MaximumLSB = 0,000488 (488uA per bit)
|
||||||
|
|
||||||
|
// 4. Choose an LSB between the min and max values
|
||||||
|
// (Preferrably a roundish number close to MinLSB)
|
||||||
|
// CurrentLSB = 0.0001 (100uA per bit)
|
||||||
|
|
||||||
|
// 5. Compute the calibration register
|
||||||
|
// Cal = trunc (0.04096 / (Current_LSB * RSHUNT))
|
||||||
|
// Cal = 4096 (0x1000)
|
||||||
|
|
||||||
|
ina219_calValue = 4096;
|
||||||
|
|
||||||
|
// 6. Calculate the power LSB
|
||||||
|
// PowerLSB = 20 * CurrentLSB
|
||||||
|
// PowerLSB = 0.002 (2mW per bit)
|
||||||
|
|
||||||
|
// 7. Compute the maximum current and shunt voltage values before overflow
|
||||||
|
//
|
||||||
|
// Max_Current = Current_LSB * 32767
|
||||||
|
// Max_Current = 3.2767A before overflow
|
||||||
|
//
|
||||||
|
// If Max_Current > Max_Possible_I then
|
||||||
|
// Max_Current_Before_Overflow = MaxPossible_I
|
||||||
|
// Else
|
||||||
|
// Max_Current_Before_Overflow = Max_Current
|
||||||
|
// End If
|
||||||
|
//
|
||||||
|
// Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT
|
||||||
|
// Max_ShuntVoltage = 0.32V
|
||||||
|
//
|
||||||
|
// If Max_ShuntVoltage >= VSHUNT_MAX
|
||||||
|
// Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX
|
||||||
|
// Else
|
||||||
|
// Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage
|
||||||
|
// End If
|
||||||
|
|
||||||
|
// 8. Compute the Maximum Power
|
||||||
|
// MaximumPower = Max_Current_Before_Overflow * VBUS_MAX
|
||||||
|
// MaximumPower = 3.2 * 32V
|
||||||
|
// MaximumPower = 102.4W
|
||||||
|
|
||||||
|
// Set multipliers to convert raw current/power values
|
||||||
|
ina219_currentDivider_mA = 10; // Current LSB = 100uA per bit (1000/100 = 10)
|
||||||
|
ina219_powerMultiplier_mW = 2; // Power LSB = 1mW per bit (2/1)
|
||||||
|
|
||||||
|
// Set Calibration register to 'Cal' calculated above
|
||||||
|
Adafruit_BusIO_Register calibration_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_CALIBRATION, 2, MSBFIRST);
|
||||||
|
calibration_reg.write(ina219_calValue, 2);
|
||||||
|
|
||||||
|
// Set Config register to take into account the settings above
|
||||||
|
uint16_t config = INA219_CONFIG_BVOLTAGERANGE_32V |
|
||||||
|
INA219_CONFIG_GAIN_8_320MV | INA219_CONFIG_BADCRES_12BIT |
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_1S_532US |
|
||||||
|
INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS;
|
||||||
|
Adafruit_BusIO_Register config_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_CONFIG, 2, MSBFIRST);
|
||||||
|
_success = config_reg.write(config, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Set power save mode according to parameters
|
||||||
|
* @param on
|
||||||
|
* boolean value
|
||||||
|
*/
|
||||||
|
void Adafruit_INA219::powerSave(bool on) {
|
||||||
|
Adafruit_BusIO_Register config_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_CONFIG, 2, MSBFIRST);
|
||||||
|
|
||||||
|
Adafruit_BusIO_RegisterBits mode_bits =
|
||||||
|
Adafruit_BusIO_RegisterBits(&config_reg, 3, 0);
|
||||||
|
if (on) {
|
||||||
|
_success = mode_bits.write(INA219_CONFIG_MODE_POWERDOWN);
|
||||||
|
} else {
|
||||||
|
_success = mode_bits.write(INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Configures to INA219 to be able to measure up to 32V and 1A
|
||||||
|
* of current. Each unit of current corresponds to 40uA, and each
|
||||||
|
* unit of power corresponds to 800uW. Counter overflow occurs at
|
||||||
|
* 1.3A.
|
||||||
|
* @note These calculations assume a 0.1 ohm resistor is present
|
||||||
|
*/
|
||||||
|
void Adafruit_INA219::setCalibration_32V_1A() {
|
||||||
|
// By default we use a pretty huge range for the input voltage,
|
||||||
|
// which probably isn't the most appropriate choice for system
|
||||||
|
// that don't use a lot of power. But all of the calculations
|
||||||
|
// are shown below if you want to change the settings. You will
|
||||||
|
// also need to change any relevant register settings, such as
|
||||||
|
// setting the VBUS_MAX to 16V instead of 32V, etc.
|
||||||
|
|
||||||
|
// VBUS_MAX = 32V (Assumes 32V, can also be set to 16V)
|
||||||
|
// VSHUNT_MAX = 0.32 (Assumes Gain 8, 320mV, can also be 0.16, 0.08, 0.04)
|
||||||
|
// RSHUNT = 0.1 (Resistor value in ohms)
|
||||||
|
|
||||||
|
// 1. Determine max possible current
|
||||||
|
// MaxPossible_I = VSHUNT_MAX / RSHUNT
|
||||||
|
// MaxPossible_I = 3.2A
|
||||||
|
|
||||||
|
// 2. Determine max expected current
|
||||||
|
// MaxExpected_I = 1.0A
|
||||||
|
|
||||||
|
// 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit)
|
||||||
|
// MinimumLSB = MaxExpected_I/32767
|
||||||
|
// MinimumLSB = 0.0000305 (30.5uA per bit)
|
||||||
|
// MaximumLSB = MaxExpected_I/4096
|
||||||
|
// MaximumLSB = 0.000244 (244uA per bit)
|
||||||
|
|
||||||
|
// 4. Choose an LSB between the min and max values
|
||||||
|
// (Preferrably a roundish number close to MinLSB)
|
||||||
|
// CurrentLSB = 0.0000400 (40uA per bit)
|
||||||
|
|
||||||
|
// 5. Compute the calibration register
|
||||||
|
// Cal = trunc (0.04096 / (Current_LSB * RSHUNT))
|
||||||
|
// Cal = 10240 (0x2800)
|
||||||
|
|
||||||
|
ina219_calValue = 10240;
|
||||||
|
|
||||||
|
// 6. Calculate the power LSB
|
||||||
|
// PowerLSB = 20 * CurrentLSB
|
||||||
|
// PowerLSB = 0.0008 (800uW per bit)
|
||||||
|
|
||||||
|
// 7. Compute the maximum current and shunt voltage values before overflow
|
||||||
|
//
|
||||||
|
// Max_Current = Current_LSB * 32767
|
||||||
|
// Max_Current = 1.31068A before overflow
|
||||||
|
//
|
||||||
|
// If Max_Current > Max_Possible_I then
|
||||||
|
// Max_Current_Before_Overflow = MaxPossible_I
|
||||||
|
// Else
|
||||||
|
// Max_Current_Before_Overflow = Max_Current
|
||||||
|
// End If
|
||||||
|
//
|
||||||
|
// ... In this case, we're good though since Max_Current is less than
|
||||||
|
// MaxPossible_I
|
||||||
|
//
|
||||||
|
// Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT
|
||||||
|
// Max_ShuntVoltage = 0.131068V
|
||||||
|
//
|
||||||
|
// If Max_ShuntVoltage >= VSHUNT_MAX
|
||||||
|
// Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX
|
||||||
|
// Else
|
||||||
|
// Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage
|
||||||
|
// End If
|
||||||
|
|
||||||
|
// 8. Compute the Maximum Power
|
||||||
|
// MaximumPower = Max_Current_Before_Overflow * VBUS_MAX
|
||||||
|
// MaximumPower = 1.31068 * 32V
|
||||||
|
// MaximumPower = 41.94176W
|
||||||
|
|
||||||
|
// Set multipliers to convert raw current/power values
|
||||||
|
ina219_currentDivider_mA = 25; // Current LSB = 40uA per bit (1000/40 = 25)
|
||||||
|
ina219_powerMultiplier_mW = 0.8f; // Power LSB = 800uW per bit
|
||||||
|
|
||||||
|
// Set Calibration register to 'Cal' calculated above
|
||||||
|
Adafruit_BusIO_Register calibration_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_CALIBRATION, 2, MSBFIRST);
|
||||||
|
calibration_reg.write(ina219_calValue, 2);
|
||||||
|
|
||||||
|
// Set Config register to take into account the settings above
|
||||||
|
uint16_t config = INA219_CONFIG_BVOLTAGERANGE_32V |
|
||||||
|
INA219_CONFIG_GAIN_8_320MV | INA219_CONFIG_BADCRES_12BIT |
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_1S_532US |
|
||||||
|
INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS;
|
||||||
|
Adafruit_BusIO_Register config_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_CONFIG, 2, MSBFIRST);
|
||||||
|
_success = config_reg.write(config, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief set device to alibration which uses the highest precision for
|
||||||
|
* current measurement (0.1mA), at the expense of
|
||||||
|
* only supporting 16V at 400mA max.
|
||||||
|
*/
|
||||||
|
void Adafruit_INA219::setCalibration_16V_400mA() {
|
||||||
|
|
||||||
|
// Calibration which uses the highest precision for
|
||||||
|
// current measurement (0.1mA), at the expense of
|
||||||
|
// only supporting 16V at 400mA max.
|
||||||
|
|
||||||
|
// VBUS_MAX = 16V
|
||||||
|
// VSHUNT_MAX = 0.04 (Assumes Gain 1, 40mV)
|
||||||
|
// RSHUNT = 0.1 (Resistor value in ohms)
|
||||||
|
|
||||||
|
// 1. Determine max possible current
|
||||||
|
// MaxPossible_I = VSHUNT_MAX / RSHUNT
|
||||||
|
// MaxPossible_I = 0.4A
|
||||||
|
|
||||||
|
// 2. Determine max expected current
|
||||||
|
// MaxExpected_I = 0.4A
|
||||||
|
|
||||||
|
// 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit)
|
||||||
|
// MinimumLSB = MaxExpected_I/32767
|
||||||
|
// MinimumLSB = 0.0000122 (12uA per bit)
|
||||||
|
// MaximumLSB = MaxExpected_I/4096
|
||||||
|
// MaximumLSB = 0.0000977 (98uA per bit)
|
||||||
|
|
||||||
|
// 4. Choose an LSB between the min and max values
|
||||||
|
// (Preferrably a roundish number close to MinLSB)
|
||||||
|
// CurrentLSB = 0.00005 (50uA per bit)
|
||||||
|
|
||||||
|
// 5. Compute the calibration register
|
||||||
|
// Cal = trunc (0.04096 / (Current_LSB * RSHUNT))
|
||||||
|
// Cal = 8192 (0x2000)
|
||||||
|
|
||||||
|
ina219_calValue = 8192;
|
||||||
|
|
||||||
|
// 6. Calculate the power LSB
|
||||||
|
// PowerLSB = 20 * CurrentLSB
|
||||||
|
// PowerLSB = 0.001 (1mW per bit)
|
||||||
|
|
||||||
|
// 7. Compute the maximum current and shunt voltage values before overflow
|
||||||
|
//
|
||||||
|
// Max_Current = Current_LSB * 32767
|
||||||
|
// Max_Current = 1.63835A before overflow
|
||||||
|
//
|
||||||
|
// If Max_Current > Max_Possible_I then
|
||||||
|
// Max_Current_Before_Overflow = MaxPossible_I
|
||||||
|
// Else
|
||||||
|
// Max_Current_Before_Overflow = Max_Current
|
||||||
|
// End If
|
||||||
|
//
|
||||||
|
// Max_Current_Before_Overflow = MaxPossible_I
|
||||||
|
// Max_Current_Before_Overflow = 0.4
|
||||||
|
//
|
||||||
|
// Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT
|
||||||
|
// Max_ShuntVoltage = 0.04V
|
||||||
|
//
|
||||||
|
// If Max_ShuntVoltage >= VSHUNT_MAX
|
||||||
|
// Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX
|
||||||
|
// Else
|
||||||
|
// Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage
|
||||||
|
// End If
|
||||||
|
//
|
||||||
|
// Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX
|
||||||
|
// Max_ShuntVoltage_Before_Overflow = 0.04V
|
||||||
|
|
||||||
|
// 8. Compute the Maximum Power
|
||||||
|
// MaximumPower = Max_Current_Before_Overflow * VBUS_MAX
|
||||||
|
// MaximumPower = 0.4 * 16V
|
||||||
|
// MaximumPower = 6.4W
|
||||||
|
|
||||||
|
// Set multipliers to convert raw current/power values
|
||||||
|
ina219_currentDivider_mA = 20; // Current LSB = 50uA per bit (1000/50 = 20)
|
||||||
|
ina219_powerMultiplier_mW = 1.0f; // Power LSB = 1mW per bit
|
||||||
|
|
||||||
|
// Set Calibration register to 'Cal' calculated above
|
||||||
|
Adafruit_BusIO_Register calibration_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_CALIBRATION, 2, MSBFIRST);
|
||||||
|
calibration_reg.write(ina219_calValue, 2);
|
||||||
|
// Set Config register to take into account the settings above
|
||||||
|
uint16_t config = INA219_CONFIG_BVOLTAGERANGE_16V |
|
||||||
|
INA219_CONFIG_GAIN_1_40MV | INA219_CONFIG_BADCRES_12BIT |
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_1S_532US |
|
||||||
|
INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS;
|
||||||
|
|
||||||
|
Adafruit_BusIO_Register config_reg =
|
||||||
|
Adafruit_BusIO_Register(i2c_dev, INA219_REG_CONFIG, 2, MSBFIRST);
|
||||||
|
_success = config_reg.write(config, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Provides the the underlying return value from the last operation
|
||||||
|
* called on the device.
|
||||||
|
* @return true: Last operation was successful false: Last operation failed
|
||||||
|
* @note For function calls that have intermediary device operations,
|
||||||
|
* e.g. calibration before read/write, only the final operation's
|
||||||
|
* result is stored.
|
||||||
|
*/
|
||||||
|
bool Adafruit_INA219::success() { return _success; }
|
||||||
198
lib/Adafruit_INA219/Adafruit_INA219.h
Normal file
198
lib/Adafruit_INA219/Adafruit_INA219.h
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
/*!
|
||||||
|
* @file Adafruit_INA219.h
|
||||||
|
*
|
||||||
|
* This is a library for the Adafruit INA219 breakout board
|
||||||
|
* ----> https://www.adafruit.com/product/904
|
||||||
|
*
|
||||||
|
* Adafruit invests time and resources providing this open source code,
|
||||||
|
* please support Adafruit and open-source hardware by purchasing
|
||||||
|
* products from Adafruit!
|
||||||
|
*
|
||||||
|
* Written by Bryan Siepert and Kevin "KTOWN" Townsend for Adafruit Industries.
|
||||||
|
*
|
||||||
|
* BSD license, all text here must be included in any redistribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LIB_ADAFRUIT_INA219_
|
||||||
|
#define _LIB_ADAFRUIT_INA219_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include <Adafruit_BusIO_Register.h>
|
||||||
|
#include <Adafruit_I2CDevice.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
/** calculated I2C address: 0 = GND, 1 = V+ **/
|
||||||
|
/* The address is controlled by the A0 and A1 inputs on the INA219:
|
||||||
|
*
|
||||||
|
* Calculated address: b100ABCD
|
||||||
|
* A0 controls C and D: GND = 00, V+ = 01, SDA = 10, SCL = 11
|
||||||
|
* A1 controls A and B: GND = 00, V+ = 01, SDA = 10, SCL = 11
|
||||||
|
*
|
||||||
|
* E.g. if A0 is tied to ground and A1 is tied to V+,
|
||||||
|
* the resulting address is b1000100 = 0x44
|
||||||
|
*
|
||||||
|
* SDA and SCL options aren't implemented.
|
||||||
|
*/
|
||||||
|
#define INA219_CALC_ADDRESS(INA_ADDR0, INA_ADDR1) \
|
||||||
|
(0x40 | (INA_ADDR0 != 0 ? 0x01 : 0x00) | (INA_ADDR1 != 0 ? 0x04 : 0x00))
|
||||||
|
|
||||||
|
/** default I2C address **/
|
||||||
|
#define INA219_ADDRESS (0x40) // 1000000 (A0+A1=GND)
|
||||||
|
|
||||||
|
/** read **/
|
||||||
|
#define INA219_READ (0x01)
|
||||||
|
|
||||||
|
/*=========================================================================
|
||||||
|
CONFIG REGISTER (R/W)
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/** config register address **/
|
||||||
|
#define INA219_REG_CONFIG (0x00)
|
||||||
|
|
||||||
|
/** reset bit **/
|
||||||
|
#define INA219_CONFIG_RESET (0x8000) // Reset Bit
|
||||||
|
|
||||||
|
/** mask for bus voltage range **/
|
||||||
|
#define INA219_CONFIG_BVOLTAGERANGE_MASK (0x2000) // Bus Voltage Range Mask
|
||||||
|
|
||||||
|
/** bus voltage range values **/
|
||||||
|
enum {
|
||||||
|
INA219_CONFIG_BVOLTAGERANGE_16V = (0x0000), // 0-16V Range
|
||||||
|
INA219_CONFIG_BVOLTAGERANGE_32V = (0x2000), // 0-32V Range
|
||||||
|
};
|
||||||
|
|
||||||
|
/** mask for gain bits **/
|
||||||
|
#define INA219_CONFIG_GAIN_MASK (0x1800) // Gain Mask
|
||||||
|
|
||||||
|
/** values for gain bits **/
|
||||||
|
enum {
|
||||||
|
INA219_CONFIG_GAIN_1_40MV = (0x0000), // Gain 1, 40mV Range
|
||||||
|
INA219_CONFIG_GAIN_2_80MV = (0x0800), // Gain 2, 80mV Range
|
||||||
|
INA219_CONFIG_GAIN_4_160MV = (0x1000), // Gain 4, 160mV Range
|
||||||
|
INA219_CONFIG_GAIN_8_320MV = (0x1800), // Gain 8, 320mV Range
|
||||||
|
};
|
||||||
|
|
||||||
|
/** mask for bus ADC resolution bits **/
|
||||||
|
#define INA219_CONFIG_BADCRES_MASK (0x0780)
|
||||||
|
|
||||||
|
/** values for bus ADC resolution **/
|
||||||
|
enum {
|
||||||
|
INA219_CONFIG_BADCRES_9BIT = (0x0000), // 9-bit bus res = 0..511
|
||||||
|
INA219_CONFIG_BADCRES_10BIT = (0x0080), // 10-bit bus res = 0..1023
|
||||||
|
INA219_CONFIG_BADCRES_11BIT = (0x0100), // 11-bit bus res = 0..2047
|
||||||
|
INA219_CONFIG_BADCRES_12BIT = (0x0180), // 12-bit bus res = 0..4097
|
||||||
|
INA219_CONFIG_BADCRES_12BIT_2S_1060US =
|
||||||
|
(0x0480), // 2 x 12-bit bus samples averaged together
|
||||||
|
INA219_CONFIG_BADCRES_12BIT_4S_2130US =
|
||||||
|
(0x0500), // 4 x 12-bit bus samples averaged together
|
||||||
|
INA219_CONFIG_BADCRES_12BIT_8S_4260US =
|
||||||
|
(0x0580), // 8 x 12-bit bus samples averaged together
|
||||||
|
INA219_CONFIG_BADCRES_12BIT_16S_8510US =
|
||||||
|
(0x0600), // 16 x 12-bit bus samples averaged together
|
||||||
|
INA219_CONFIG_BADCRES_12BIT_32S_17MS =
|
||||||
|
(0x0680), // 32 x 12-bit bus samples averaged together
|
||||||
|
INA219_CONFIG_BADCRES_12BIT_64S_34MS =
|
||||||
|
(0x0700), // 64 x 12-bit bus samples averaged together
|
||||||
|
INA219_CONFIG_BADCRES_12BIT_128S_69MS =
|
||||||
|
(0x0780), // 128 x 12-bit bus samples averaged together
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/** mask for shunt ADC resolution bits **/
|
||||||
|
#define INA219_CONFIG_SADCRES_MASK \
|
||||||
|
(0x0078) // Shunt ADC Resolution and Averaging Mask
|
||||||
|
|
||||||
|
/** values for shunt ADC resolution **/
|
||||||
|
enum {
|
||||||
|
INA219_CONFIG_SADCRES_9BIT_1S_84US = (0x0000), // 1 x 9-bit shunt sample
|
||||||
|
INA219_CONFIG_SADCRES_10BIT_1S_148US = (0x0008), // 1 x 10-bit shunt sample
|
||||||
|
INA219_CONFIG_SADCRES_11BIT_1S_276US = (0x0010), // 1 x 11-bit shunt sample
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_1S_532US = (0x0018), // 1 x 12-bit shunt sample
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_2S_1060US =
|
||||||
|
(0x0048), // 2 x 12-bit shunt samples averaged together
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_4S_2130US =
|
||||||
|
(0x0050), // 4 x 12-bit shunt samples averaged together
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_8S_4260US =
|
||||||
|
(0x0058), // 8 x 12-bit shunt samples averaged together
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_16S_8510US =
|
||||||
|
(0x0060), // 16 x 12-bit shunt samples averaged together
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_32S_17MS =
|
||||||
|
(0x0068), // 32 x 12-bit shunt samples averaged together
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_64S_34MS =
|
||||||
|
(0x0070), // 64 x 12-bit shunt samples averaged together
|
||||||
|
INA219_CONFIG_SADCRES_12BIT_128S_69MS =
|
||||||
|
(0x0078), // 128 x 12-bit shunt samples averaged together
|
||||||
|
};
|
||||||
|
|
||||||
|
/** mask for operating mode bits **/
|
||||||
|
#define INA219_CONFIG_MODE_MASK (0x0007) // Operating Mode Mask
|
||||||
|
|
||||||
|
/** values for operating mode **/
|
||||||
|
enum {
|
||||||
|
INA219_CONFIG_MODE_POWERDOWN = 0x00, /**< power down */
|
||||||
|
INA219_CONFIG_MODE_SVOLT_TRIGGERED = 0x01, /**< shunt voltage triggered */
|
||||||
|
INA219_CONFIG_MODE_BVOLT_TRIGGERED = 0x02, /**< bus voltage triggered */
|
||||||
|
INA219_CONFIG_MODE_SANDBVOLT_TRIGGERED =
|
||||||
|
0x03, /**< shunt and bus voltage triggered */
|
||||||
|
INA219_CONFIG_MODE_ADCOFF = 0x04, /**< ADC off */
|
||||||
|
INA219_CONFIG_MODE_SVOLT_CONTINUOUS = 0x05, /**< shunt voltage continuous */
|
||||||
|
INA219_CONFIG_MODE_BVOLT_CONTINUOUS = 0x06, /**< bus voltage continuous */
|
||||||
|
INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS =
|
||||||
|
0x07, /**< shunt and bus voltage continuous */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** shunt voltage register **/
|
||||||
|
#define INA219_REG_SHUNTVOLTAGE (0x01)
|
||||||
|
|
||||||
|
/** bus voltage register **/
|
||||||
|
#define INA219_REG_BUSVOLTAGE (0x02)
|
||||||
|
|
||||||
|
/** power register **/
|
||||||
|
#define INA219_REG_POWER (0x03)
|
||||||
|
|
||||||
|
/** current register **/
|
||||||
|
#define INA219_REG_CURRENT (0x04)
|
||||||
|
|
||||||
|
/** calibration register **/
|
||||||
|
#define INA219_REG_CALIBRATION (0x05)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Class that stores state and functions for interacting with INA219
|
||||||
|
* current/power monitor IC
|
||||||
|
*/
|
||||||
|
class Adafruit_INA219 {
|
||||||
|
public:
|
||||||
|
Adafruit_INA219(uint8_t addr = INA219_ADDRESS);
|
||||||
|
~Adafruit_INA219();
|
||||||
|
bool begin(TwoWire *theWire = &Wire);
|
||||||
|
void setCalibration_32V_2A();
|
||||||
|
void setCalibration_32V_1A();
|
||||||
|
void setCalibration_16V_400mA();
|
||||||
|
float getBusVoltage_V();
|
||||||
|
float getShuntVoltage_mV();
|
||||||
|
float getCurrent_mA();
|
||||||
|
float getPower_mW();
|
||||||
|
void powerSave(bool on);
|
||||||
|
bool success();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Adafruit_I2CDevice *i2c_dev = NULL;
|
||||||
|
|
||||||
|
bool _success;
|
||||||
|
|
||||||
|
uint8_t ina219_i2caddr = -1;
|
||||||
|
uint32_t ina219_calValue;
|
||||||
|
// The following multipliers are used to convert raw current and power
|
||||||
|
// values to mA and mW, taking into account the current config settings
|
||||||
|
uint32_t ina219_currentDivider_mA;
|
||||||
|
float ina219_powerMultiplier_mW;
|
||||||
|
|
||||||
|
void init();
|
||||||
|
int16_t getBusVoltage_raw();
|
||||||
|
int16_t getShuntVoltage_raw();
|
||||||
|
int16_t getCurrent_raw();
|
||||||
|
int16_t getPower_raw();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
16
lib/Adafruit_INA219/README.md
Normal file
16
lib/Adafruit_INA219/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Adafruit INA219 Library [](https://github.com/adafruit/Adafruit_INA219/actions)[](http://adafruit.github.io/Adafruit_INA219/html/index.html)
|
||||||
|
|
||||||
|
<a href="https://www.adafruit.com/products/904"><img src="assets/board.jpg" width="500px" /></a>
|
||||||
|
|
||||||
|
This is a library for the Adafruit INA219 high side DC current sensor boards:
|
||||||
|
* https://www.adafruit.com/products/904
|
||||||
|
* https://www.adafruit.com/product/3650
|
||||||
|
|
||||||
|
Check out the links above for our tutorials and wiring diagrams. This chip uses I2C to communicate.
|
||||||
|
|
||||||
|
To install, use the Arduino Library Manager and search for 'Adafruit INA219' and install the library.
|
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
|
||||||
|
|
||||||
|
Written by Ktown for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
BIN
lib/Adafruit_INA219/assets/board.jpg
Normal file
BIN
lib/Adafruit_INA219/assets/board.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 252 KiB |
127
lib/Adafruit_INA219/code-of-conduct.md
Normal file
127
lib/Adafruit_INA219/code-of-conduct.md
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
# Adafruit Community Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and leaders pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, gender identity and expression, level or type of
|
||||||
|
experience, education, socio-economic status, nationality, personal appearance,
|
||||||
|
race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
We are committed to providing a friendly, safe and welcoming environment for
|
||||||
|
all.
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Be kind and courteous to others
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Collaborating with other community members
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and sexual attention or advances
|
||||||
|
* The use of inappropriate images, including in a community member's avatar
|
||||||
|
* The use of inappropriate language, including in a community member's nickname
|
||||||
|
* Any spamming, flaming, baiting or other attention-stealing behavior
|
||||||
|
* Excessive or unwelcome helping; answering outside the scope of the question
|
||||||
|
asked
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate
|
||||||
|
|
||||||
|
The goal of the standards and moderation guidelines outlined here is to build
|
||||||
|
and maintain a respectful community. We ask that you don’t just aim to be
|
||||||
|
"technically unimpeachable", but rather try to be your best self.
|
||||||
|
|
||||||
|
We value many things beyond technical expertise, including collaboration and
|
||||||
|
supporting others within our community. Providing a positive experience for
|
||||||
|
other community members can have a much more significant impact than simply
|
||||||
|
providing the correct answer.
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project leaders are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project leaders have the right and responsibility to remove, edit, or
|
||||||
|
reject messages, comments, commits, code, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any community member for other behaviors that they deem
|
||||||
|
inappropriate, threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Moderation
|
||||||
|
|
||||||
|
Instances of behaviors that violate the Adafruit Community Code of Conduct
|
||||||
|
may be reported by any member of the community. Community members are
|
||||||
|
encouraged to report these situations, including situations they witness
|
||||||
|
involving other community members.
|
||||||
|
|
||||||
|
You may report in the following ways:
|
||||||
|
|
||||||
|
In any situation, you may send an email to <support@adafruit.com>.
|
||||||
|
|
||||||
|
On the Adafruit Discord, you may send an open message from any channel
|
||||||
|
to all Community Helpers by tagging @community helpers. You may also send an
|
||||||
|
open message from any channel, or a direct message to @kattni#1507,
|
||||||
|
@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or
|
||||||
|
@Andon#8175.
|
||||||
|
|
||||||
|
Email and direct message reports will be kept confidential.
|
||||||
|
|
||||||
|
In situations on Discord where the issue is particularly egregious, possibly
|
||||||
|
illegal, requires immediate action, or violates the Discord terms of service,
|
||||||
|
you should also report the message directly to Discord.
|
||||||
|
|
||||||
|
These are the steps for upholding our community’s standards of conduct.
|
||||||
|
|
||||||
|
1. Any member of the community may report any situation that violates the
|
||||||
|
Adafruit Community Code of Conduct. All reports will be reviewed and
|
||||||
|
investigated.
|
||||||
|
2. If the behavior is an egregious violation, the community member who
|
||||||
|
committed the violation may be banned immediately, without warning.
|
||||||
|
3. Otherwise, moderators will first respond to such behavior with a warning.
|
||||||
|
4. Moderators follow a soft "three strikes" policy - the community member may
|
||||||
|
be given another chance, if they are receptive to the warning and change their
|
||||||
|
behavior.
|
||||||
|
5. If the community member is unreceptive or unreasonable when warned by a
|
||||||
|
moderator, or the warning goes unheeded, they may be banned for a first or
|
||||||
|
second offense. Repeated offenses will result in the community member being
|
||||||
|
banned.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct and the enforcement policies listed above apply to all
|
||||||
|
Adafruit Community venues. This includes but is not limited to any community
|
||||||
|
spaces (both public and private), the entire Adafruit Discord server, and
|
||||||
|
Adafruit GitHub repositories. Examples of Adafruit Community spaces include
|
||||||
|
but are not limited to meet-ups, audio chats on the Adafruit Discord, or
|
||||||
|
interaction at a conference.
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. As a community
|
||||||
|
member, you are representing our community, and are expected to behave
|
||||||
|
accordingly.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 1.4, available at
|
||||||
|
<https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>,
|
||||||
|
and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html).
|
||||||
|
|
||||||
|
For other projects adopting the Adafruit Community Code of
|
||||||
|
Conduct, please contact the maintainers of those projects for enforcement.
|
||||||
|
If you wish to use this code of conduct for your own project, consider
|
||||||
|
explicitly mentioning your moderation policy or making a copy with your
|
||||||
|
own moderation policy so as to avoid confusion.
|
||||||
54
lib/Adafruit_INA219/examples/getcurrent/getcurrent.ino
Normal file
54
lib/Adafruit_INA219/examples/getcurrent/getcurrent.ino
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_INA219.h>
|
||||||
|
|
||||||
|
Adafruit_INA219 ina219;
|
||||||
|
|
||||||
|
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial) {
|
||||||
|
// will pause Zero, Leonardo, etc until serial console opens
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Hello!");
|
||||||
|
|
||||||
|
// Initialize the INA219.
|
||||||
|
// By default the initialization will use the largest range (32V, 2A). However
|
||||||
|
// you can call a setCalibration function to change this range (see comments).
|
||||||
|
if (! ina219.begin()) {
|
||||||
|
Serial.println("Failed to find INA219 chip");
|
||||||
|
while (1) { delay(10); }
|
||||||
|
}
|
||||||
|
// To use a slightly lower 32V, 1A range (higher precision on amps):
|
||||||
|
//ina219.setCalibration_32V_1A();
|
||||||
|
// Or to use a lower 16V, 400mA range (higher precision on volts and amps):
|
||||||
|
//ina219.setCalibration_16V_400mA();
|
||||||
|
|
||||||
|
Serial.println("Measuring voltage and current with INA219 ...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
float shuntvoltage = 0;
|
||||||
|
float busvoltage = 0;
|
||||||
|
float current_mA = 0;
|
||||||
|
float loadvoltage = 0;
|
||||||
|
float power_mW = 0;
|
||||||
|
|
||||||
|
shuntvoltage = ina219.getShuntVoltage_mV();
|
||||||
|
busvoltage = ina219.getBusVoltage_V();
|
||||||
|
current_mA = ina219.getCurrent_mA();
|
||||||
|
power_mW = ina219.getPower_mW();
|
||||||
|
loadvoltage = busvoltage + (shuntvoltage / 1000);
|
||||||
|
|
||||||
|
Serial.print("Bus Voltage: "); Serial.print(busvoltage); Serial.println(" V");
|
||||||
|
Serial.print("Shunt Voltage: "); Serial.print(shuntvoltage); Serial.println(" mV");
|
||||||
|
Serial.print("Load Voltage: "); Serial.print(loadvoltage); Serial.println(" V");
|
||||||
|
Serial.print("Current: "); Serial.print(current_mA); Serial.println(" mA");
|
||||||
|
Serial.print("Power: "); Serial.print(power_mW); Serial.println(" mW");
|
||||||
|
Serial.println("");
|
||||||
|
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
262
lib/Adafruit_INA219/examples/ina_poweroled/ina_poweroled.ino
Normal file
262
lib/Adafruit_INA219/examples/ina_poweroled/ina_poweroled.ino
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
// Feather Power Meter
|
||||||
|
//
|
||||||
|
// Small Feather-based power monitor using an INA219 breakout and
|
||||||
|
// monochrome OLED display.
|
||||||
|
//
|
||||||
|
// Author: Tony DiCola (modded by ladyada)
|
||||||
|
//
|
||||||
|
// Released under a MIT license: http://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_NeoPixel.h>
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <Adafruit_INA219.h>
|
||||||
|
|
||||||
|
// Configure orientation of the display.
|
||||||
|
// 0 = none, 1 = 90 degrees clockwise, 2 = 180 degrees, 3 = 270 degrees CW
|
||||||
|
#define ROTATION 0
|
||||||
|
|
||||||
|
#define NEO_PIN 6
|
||||||
|
|
||||||
|
// Create NeoPixel, OLED and INA219 globals.
|
||||||
|
Adafruit_SSD1306 display;
|
||||||
|
Adafruit_INA219 ina219;
|
||||||
|
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(144, NEO_PIN, NEO_GRB + NEO_KHZ800);
|
||||||
|
|
||||||
|
// Keep track of total time and milliamp measurements for milliamp-hour computation.
|
||||||
|
uint32_t total_sec = 0;
|
||||||
|
float total_mA = 0.0;
|
||||||
|
|
||||||
|
uint8_t counter = 0;
|
||||||
|
void pixel_show_and_powerupdate() {
|
||||||
|
pixels.show();
|
||||||
|
if (counter == 0) {
|
||||||
|
update_power_display();
|
||||||
|
} else {
|
||||||
|
counter = (counter+1) % 10 ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial) {
|
||||||
|
// will pause Zero, Leonardo, etc until serial console opens
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
pixels.begin();
|
||||||
|
pixels.show(); // Initialize all pixels to 'off'
|
||||||
|
// Try to initialize the INA219
|
||||||
|
if (! ina219.begin()) {
|
||||||
|
Serial.println("Failed to find INA219 chip");
|
||||||
|
while (1) { delay(10); }
|
||||||
|
}
|
||||||
|
// By default the INA219 will be calibrated with a range of 32V, 2A.
|
||||||
|
// However uncomment one of the below to change the range. A smaller
|
||||||
|
// range can't measure as large of values but will measure with slightly
|
||||||
|
// better precision.
|
||||||
|
//ina219.setCalibration_32V_1A();
|
||||||
|
//ina219.setCalibration_16V_400mA();
|
||||||
|
|
||||||
|
// Setup the OLED display.
|
||||||
|
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
|
||||||
|
Wire.setClock(400000);
|
||||||
|
|
||||||
|
// Clear the display.
|
||||||
|
display.clearDisplay();
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
// Set rotation.
|
||||||
|
display.setRotation(ROTATION);
|
||||||
|
|
||||||
|
// Set text size and color.
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.setTextColor(WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Some example procedures showing how to display to the pixels:
|
||||||
|
colorWipe(pixels.Color(255, 0, 0), 50); // Red
|
||||||
|
colorWipe(pixels.Color(0, 255, 0), 50); // Green
|
||||||
|
colorWipe(pixels.Color(0, 0, 255), 50); // Blue
|
||||||
|
//colorWipe(pixels.Color(0, 0, 0, 255), 50); // White RGBW
|
||||||
|
// Send a theater pixel chase in...
|
||||||
|
theaterChase(pixels.Color(127, 127, 127), 50); // White
|
||||||
|
theaterChase(pixels.Color(127, 0, 0), 50); // Red
|
||||||
|
theaterChase(pixels.Color(0, 0, 127), 50); // Blue
|
||||||
|
|
||||||
|
rainbow(20);
|
||||||
|
rainbowCycle(20);
|
||||||
|
theaterChaseRainbow(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Fill the dots one after the other with a color
|
||||||
|
void colorWipe(uint32_t c, uint8_t wait) {
|
||||||
|
for(uint16_t i=0; i<pixels.numPixels(); i++) {
|
||||||
|
pixels.setPixelColor(i, c);
|
||||||
|
pixel_show_and_powerupdate();
|
||||||
|
delay(wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rainbow(uint8_t wait) {
|
||||||
|
uint16_t i, j;
|
||||||
|
|
||||||
|
for(j=0; j<256; j++) {
|
||||||
|
for(i=0; i<pixels.numPixels(); i++) {
|
||||||
|
pixels.setPixelColor(i, Wheel((i+j) & 255));
|
||||||
|
}
|
||||||
|
pixel_show_and_powerupdate();
|
||||||
|
delay(wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slightly different, this makes the rainbow equally distributed throughout
|
||||||
|
void rainbowCycle(uint8_t wait) {
|
||||||
|
uint16_t i, j;
|
||||||
|
|
||||||
|
for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
|
||||||
|
for(i=0; i< pixels.numPixels(); i++) {
|
||||||
|
pixels.setPixelColor(i, Wheel(((i * 256 / pixels.numPixels()) + j) & 255));
|
||||||
|
}
|
||||||
|
pixel_show_and_powerupdate();
|
||||||
|
delay(wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Theatre-style crawling lights.
|
||||||
|
void theaterChase(uint32_t c, uint8_t wait) {
|
||||||
|
for (int j=0; j<10; j++) { //do 10 cycles of chasing
|
||||||
|
for (int q=0; q < 3; q++) {
|
||||||
|
for (uint16_t i=0; i < pixels.numPixels(); i=i+3) {
|
||||||
|
pixels.setPixelColor(i+q, c); //turn every third pixel on
|
||||||
|
}
|
||||||
|
pixel_show_and_powerupdate();
|
||||||
|
|
||||||
|
delay(wait);
|
||||||
|
|
||||||
|
for (uint16_t i=0; i < pixels.numPixels(); i=i+3) {
|
||||||
|
pixels.setPixelColor(i+q, 0); //turn every third pixel off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Theatre-style crawling lights with rainbow effect
|
||||||
|
void theaterChaseRainbow(uint8_t wait) {
|
||||||
|
for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel
|
||||||
|
for (int q=0; q < 3; q++) {
|
||||||
|
for (uint16_t i=0; i < pixels.numPixels(); i=i+3) {
|
||||||
|
pixels.setPixelColor(i+q, Wheel( (i+j) % 255)); //turn every third pixel on
|
||||||
|
}
|
||||||
|
pixel_show_and_powerupdate();
|
||||||
|
|
||||||
|
delay(wait);
|
||||||
|
|
||||||
|
for (uint16_t i=0; i < pixels.numPixels(); i=i+3) {
|
||||||
|
pixels.setPixelColor(i+q, 0); //turn every third pixel off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input a value 0 to 255 to get a color value.
|
||||||
|
// The colours are a transition r - g - b - back to r.
|
||||||
|
uint32_t Wheel(byte WheelPos) {
|
||||||
|
WheelPos = 255 - WheelPos;
|
||||||
|
if(WheelPos < 85) {
|
||||||
|
return pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
|
||||||
|
}
|
||||||
|
if(WheelPos < 170) {
|
||||||
|
WheelPos -= 85;
|
||||||
|
return pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
|
||||||
|
}
|
||||||
|
WheelPos -= 170;
|
||||||
|
return pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void update_power_display() {
|
||||||
|
// Read voltage and current from INA219.
|
||||||
|
float shuntvoltage = ina219.getShuntVoltage_mV();
|
||||||
|
float busvoltage = ina219.getBusVoltage_V();
|
||||||
|
float current_mA = ina219.getCurrent_mA();
|
||||||
|
|
||||||
|
// Compute load voltage, power, and milliamp-hours.
|
||||||
|
float loadvoltage = busvoltage + (shuntvoltage / 1000);
|
||||||
|
float power_mW = loadvoltage * current_mA;
|
||||||
|
(void)power_mW;
|
||||||
|
|
||||||
|
total_mA += current_mA;
|
||||||
|
total_sec += 1;
|
||||||
|
float total_mAH = total_mA / 3600.0;
|
||||||
|
(void)total_mAH;
|
||||||
|
|
||||||
|
// Update display.
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setCursor(0,0);
|
||||||
|
|
||||||
|
// Mode 0, display volts and amps.
|
||||||
|
printSIValue(loadvoltage, "V:", 2, 10);
|
||||||
|
display.setCursor(0, 16);
|
||||||
|
printSIValue(current_mA/1000.0, "A:", 5, 10);
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void printSIValue(float value, const char* units, int precision, int maxWidth) {
|
||||||
|
// Print a value in SI units with the units left justified and value right justified.
|
||||||
|
// Will switch to milli prefix if value is below 1.
|
||||||
|
|
||||||
|
// Add milli prefix if low value.
|
||||||
|
if (fabs(value) < 1.0) {
|
||||||
|
display.print('m');
|
||||||
|
maxWidth -= 1;
|
||||||
|
value *= 1000.0;
|
||||||
|
precision = max(0, precision-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print units.
|
||||||
|
display.print(units);
|
||||||
|
maxWidth -= strlen(units);
|
||||||
|
|
||||||
|
// Leave room for negative sign if value is negative.
|
||||||
|
if (value < 0.0) {
|
||||||
|
maxWidth -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find how many digits are in value.
|
||||||
|
int digits = ceil(log10(fabs(value)));
|
||||||
|
if (fabs(value) < 1.0) {
|
||||||
|
digits = 1; // Leave room for 0 when value is below 0.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle if not enough width to display value, just print dashes.
|
||||||
|
if (digits > maxWidth) {
|
||||||
|
// Fill width with dashes (and add extra dash for negative values);
|
||||||
|
for (int i=0; i < maxWidth; ++i) {
|
||||||
|
display.print('-');
|
||||||
|
}
|
||||||
|
if (value < 0.0) {
|
||||||
|
display.print('-');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute actual precision for printed value based on space left after
|
||||||
|
// printing digits and decimal point. Clamp within 0 to desired precision.
|
||||||
|
int actualPrecision = constrain(maxWidth-digits-1, 0, precision);
|
||||||
|
|
||||||
|
// Compute how much padding to add to right justify.
|
||||||
|
int padding = maxWidth-digits-1-actualPrecision;
|
||||||
|
for (int i=0; i < padding; ++i) {
|
||||||
|
display.print(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, print the value!
|
||||||
|
display.print(value, actualPrecision);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
10
lib/Adafruit_INA219/library.properties
Normal file
10
lib/Adafruit_INA219/library.properties
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
name=Adafruit INA219
|
||||||
|
version=1.2.3
|
||||||
|
author=Adafruit
|
||||||
|
maintainer=Adafruit <info@adafruit.com>
|
||||||
|
sentence=INA219 Current Sensor
|
||||||
|
paragraph=INA219 Current Sensor
|
||||||
|
category=Sensors
|
||||||
|
url=https://github.com/adafruit/Adafruit_INA219
|
||||||
|
architectures=*
|
||||||
|
depends=Adafruit NeoPixel, Adafruit GFX Library, Adafruit SSD1306, Adafruit BusIO
|
||||||
26
lib/Adafruit_INA219/license.txt
Normal file
26
lib/Adafruit_INA219/license.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Software License Agreement (BSD License)
|
||||||
|
|
||||||
|
Copyright (c) 2012, Adafruit Industries
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holders nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@ -12,3 +12,10 @@
|
|||||||
platform = espressif32
|
platform = espressif32
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
knolleary/PubSubClient@^2.8
|
||||||
|
bblanchon/ArduinoJson@^7.4.2
|
||||||
|
claws/BH1750@^1.3.0
|
||||||
|
arduino-libraries/NTPClient@^3.2.1
|
||||||
|
adafruit/Adafruit ADS1X15@^2.4.2
|
||||||
|
adafruit/Adafruit INA219@^1.2.0
|
||||||
|
|||||||
862
src/main.cpp
862
src/main.cpp
@ -1,18 +1,860 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <BH1750.h>
|
||||||
|
#include <NTPClient.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include <Update.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <Adafruit_ADS1X15.h>
|
||||||
|
#include <Adafruit_INA219.h>
|
||||||
|
#include "esp_sleep.h"
|
||||||
|
#include "esp_wifi.h" // esp_wifi_set_ps
|
||||||
|
|
||||||
// put function declarations here:
|
#define CURRENT_VERSION "R_1_0_23"
|
||||||
int myFunction(int, int);
|
|
||||||
|
// ---------- Low Power ----------
|
||||||
|
#define LOW_POWER_MODE 1
|
||||||
|
#define LP_SLEEP_GUARD_MS 20
|
||||||
|
#define SENSOR_PWR_PIN -1 // 전원 게이팅 핀 없으면 -1
|
||||||
|
|
||||||
|
// ---------- Publish Interval 기본값 ----------
|
||||||
|
#define PUBLISH_INTERVAL_MS_DEFAULT 1000UL // MQTT 발행 간격 기본(부팅 후)
|
||||||
|
#define MQTT_KEEPALIVE_SEC 60
|
||||||
|
#define SLEEP_SLICE_MS 5000UL // 긴 대기 쪼개기(keepalive/명령 수신용)
|
||||||
|
|
||||||
|
// ---------- Duty Cycle 기본값 ----------
|
||||||
|
#define DUTY_SLEEP_MS_DEFAULT 10000UL // 슬립 윈도우(수면 간격)
|
||||||
|
#define DUTY_AWAKE_MS_DEFAULT 2000UL // 깨어있는 윈도우(발행 유지 시간)
|
||||||
|
|
||||||
|
// -------- OTA --------
|
||||||
|
#define OTA_SERVER "https://esp32.qmsguide.synology.me"
|
||||||
|
#define OTA_VERSION_FILE "/housecontrol.txt"
|
||||||
|
|
||||||
|
// -------- Pins --------
|
||||||
|
#define SDA_PIN 21
|
||||||
|
#define SCL_PIN 22
|
||||||
|
#define RELAY1 16 // 12V LED 메인 (active-low)
|
||||||
|
#define RELAY2 17 // 12V LED 서브, R1과 항상 동기 (active-low, 파생)
|
||||||
|
#define RELAY3 18 // 5V LED (active-low)
|
||||||
|
#define LED_TX 23
|
||||||
|
#define LED_I2C 26
|
||||||
|
#define LED_WIFI 27
|
||||||
|
#define BTN_R1 32
|
||||||
|
#define BTN_R2 33
|
||||||
|
const unsigned long DEBOUNCE_MS = 30;
|
||||||
|
|
||||||
|
// ---- Relay polarity (true=active-low, false=active-high)
|
||||||
|
#define RELAY1_ACTIVE_LOW true
|
||||||
|
#define RELAY2_ACTIVE_LOW true
|
||||||
|
#define RELAY3_ACTIVE_LOW false
|
||||||
|
|
||||||
|
// 공통 릴레이 제어/상태 유틸
|
||||||
|
inline void relay_write(uint8_t pin, bool active_low, bool on) {
|
||||||
|
digitalWrite(pin, on ? (active_low ? LOW : HIGH)
|
||||||
|
: (active_low ? HIGH : LOW));
|
||||||
|
}
|
||||||
|
inline bool relay_is_on(uint8_t pin, bool active_low) {
|
||||||
|
int lv = digitalRead(pin);
|
||||||
|
return active_low ? (lv == LOW) : (lv == HIGH);
|
||||||
|
}
|
||||||
|
inline bool relay_active_low_for_pin(uint8_t pin) {
|
||||||
|
if (pin == RELAY1) return RELAY1_ACTIVE_LOW;
|
||||||
|
if (pin == RELAY2) return RELAY2_ACTIVE_LOW;
|
||||||
|
if (pin == RELAY3) return RELAY3_ACTIVE_LOW;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ★ R2 파생 로직: R1 상태를 R2에 그대로 복제
|
||||||
|
inline void updateDerivedRelays() {
|
||||||
|
bool r1_on = relay_is_on(RELAY1, RELAY1_ACTIVE_LOW);
|
||||||
|
relay_write(RELAY2, RELAY2_ACTIVE_LOW, r1_on);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Button {
|
||||||
|
uint8_t pin;
|
||||||
|
bool lastStable;
|
||||||
|
bool lastReading;
|
||||||
|
unsigned long lastChange;
|
||||||
|
};
|
||||||
|
Button btnR1{ BTN_R1, HIGH, HIGH, 0 };
|
||||||
|
Button btnR2{ BTN_R2, HIGH, HIGH, 0 };
|
||||||
|
|
||||||
|
// -------- I2C Addresses --------
|
||||||
|
#define INA219_ADDR_A 0x40
|
||||||
|
#define INA219_ADDR_B 0x41
|
||||||
|
#define ADS1115_ADDR 0x48
|
||||||
|
#define BH1750_ADDR 0x23
|
||||||
|
|
||||||
|
// -------- ADS1115 보정 --------
|
||||||
|
const float A_SCALE[4] = { 1.00f, 1.00f, 1.00f, 1.00f };
|
||||||
|
const float A_OFFSET[4] = { 0.35f, 0.35f, 0.14f, 0.00f };
|
||||||
|
|
||||||
|
// ===================== XY-MD04 (RS485 Modbus-RTU, auto-direction) ======================
|
||||||
|
// GPIO4/5 오토-디렉션(DE/RE 없음)
|
||||||
|
#define RS485_RX_PIN 4
|
||||||
|
#define RS485_TX_PIN 5
|
||||||
|
inline void rs485_tx_mode(bool) {} // auto-dir 모듈 가정(무동작)
|
||||||
|
|
||||||
|
// MD04 파라미터(온습도: 인풋레지스터 0x0001/0x0002, ID=1)
|
||||||
|
#define MD04_SLAVE_ID 1
|
||||||
|
#define MD04_FUNC_READ 0x04 // Input Register
|
||||||
|
#define MD04_REG_T 0x0001
|
||||||
|
#define MD04_REG_H 0x0002
|
||||||
|
#define MD04_POLL_MS 1200
|
||||||
|
|
||||||
|
// RS-485 타이밍
|
||||||
|
#define RS485_MAXBUF 64
|
||||||
|
#define RS485_TIMEOUT_MS 800
|
||||||
|
#define POST_TX_GUARD_US 12000
|
||||||
|
#define INTER_READ_DELAY_MS 15
|
||||||
|
#define RS485_RETRY 1
|
||||||
|
|
||||||
|
HardwareSerial& RS485 = Serial2;
|
||||||
|
float md04_airT = NAN, md04_airH = NAN;
|
||||||
|
uint32_t md04_errcnt = 0, lastMd04Poll = 0;
|
||||||
|
bool md04_ok = false;
|
||||||
|
|
||||||
|
// -------- WiFi / MQTT --------
|
||||||
|
struct WiFiCandidate { const char* ssid; const char* password; };
|
||||||
|
WiFiCandidate candidates[] = {
|
||||||
|
{ "ChoiBK", "#Info96716" },
|
||||||
|
{ "ChoiBK_35", "#Info96716" },
|
||||||
|
{ "WAVLINK-N_868C", "Info96716" }
|
||||||
|
};
|
||||||
|
const int candidateCount = sizeof(candidates) / sizeof(candidates[0]);
|
||||||
|
|
||||||
|
const char* mqtt_broker = "qideun.com";
|
||||||
|
const int mqtt_port = 1883;
|
||||||
|
const char* mqtt_topic_control = "sokuree/house/control";
|
||||||
|
const char* mqtt_topic_status = "sokuree/house/status";
|
||||||
|
|
||||||
|
// -------- Objects --------
|
||||||
|
WiFiClient espClient;
|
||||||
|
PubSubClient client(espClient);
|
||||||
|
WiFiUDP ntpUDP;
|
||||||
|
NTPClient timeClient(ntpUDP, "pool.ntp.org", 9 * 3600, 60000);
|
||||||
|
BH1750 lightMeter(BH1750::CONTINUOUS_HIGH_RES_MODE);
|
||||||
|
Adafruit_ADS1X15 ads;
|
||||||
|
Adafruit_INA219 inaA(INA219_ADDR_A), inaB(INA219_ADDR_B);
|
||||||
|
|
||||||
|
// -------- timers / intervals --------
|
||||||
|
unsigned long lastPublishTime = 0, lastWiFiReconnectAttempt = 0, lastMqttReconnectAttempt = 0;
|
||||||
|
int mqttReconnectAttempts = 0;
|
||||||
|
const int maxMqttReconnectAttempts = 5;
|
||||||
|
|
||||||
|
uint32_t publishIntervalMs = PUBLISH_INTERVAL_MS_DEFAULT;
|
||||||
|
uint32_t duty_sleep_ms = DUTY_SLEEP_MS_DEFAULT;
|
||||||
|
uint32_t duty_awake_ms = DUTY_AWAKE_MS_DEFAULT;
|
||||||
|
uint32_t duty_awake_until_ms = 0; // AWAKE 윈도우 종료 시각
|
||||||
|
|
||||||
|
// -------- energy --------
|
||||||
|
double dailyWh1 = 0.0, dailyWh2 = 0.0;
|
||||||
|
int lastDay = -1;
|
||||||
|
uint32_t lastEnergyMs = 0;
|
||||||
|
|
||||||
|
// -------- I2C health --------
|
||||||
|
volatile int lastDiscReason = -1;
|
||||||
|
uint32_t lastI2cCheckMs = 0, i2cErrorCount = 0;
|
||||||
|
bool i2cFaultLatched = false;
|
||||||
|
|
||||||
|
// -------- I2C device health --------
|
||||||
|
bool bh1750_ok = true, ads_ok = true, inaA_ok = true, inaB_ok = true;
|
||||||
|
uint32_t bh1750_err = 0, ads_err = 0, inaA_err = 0, inaB_err = 0;
|
||||||
|
uint32_t lastI2cErrReportMs = 0;
|
||||||
|
|
||||||
|
// ---------- Forward Declarations ----------
|
||||||
|
void checkForOTAUpdate();
|
||||||
|
bool buttonPressed(Button& b);
|
||||||
|
void toggleRelay(uint8_t relayPin);
|
||||||
|
uint16_t crc16_modbus(const uint8_t* data, size_t len);
|
||||||
|
bool waitWiFi(uint32_t ms);
|
||||||
|
void connectToBestAP();
|
||||||
|
void connectToMqtt();
|
||||||
|
void publishStatus();
|
||||||
|
void setPublishIntervalMs(uint32_t ms);
|
||||||
|
void setDutySleepMs(uint32_t ms);
|
||||||
|
void setDutyAwakeMs(uint32_t ms);
|
||||||
|
void md04_poll_if_due();
|
||||||
|
static inline void i2c_error_report_1s();
|
||||||
|
|
||||||
|
// ----------------------------- Helpers -----------------------------
|
||||||
|
static inline bool i2cPing(uint8_t addr) {
|
||||||
|
Wire.beginTransmission(addr);
|
||||||
|
return (Wire.endTransmission() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t crc16_modbus(const uint8_t* data, size_t len) {
|
||||||
|
uint16_t crc = 0xFFFF;
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
crc ^= data[i];
|
||||||
|
for (int b = 0; b < 8; b++) crc = (crc & 1) ? ((crc >> 1) ^ 0xA001) : (crc >> 1);
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void sensor_power(bool sleep_on) {
|
||||||
|
#if (SENSOR_PWR_PIN >= 0)
|
||||||
|
pinMode(SENSOR_PWR_PIN, OUTPUT);
|
||||||
|
digitalWrite(SENSOR_PWR_PIN, sleep_on ? LOW : HIGH);
|
||||||
|
#endif
|
||||||
|
// BH1750 Power save
|
||||||
|
Wire.beginTransmission(BH1750_ADDR);
|
||||||
|
Wire.write(sleep_on ? 0x00 : 0x01);
|
||||||
|
Wire.endTransmission();
|
||||||
|
if (!sleep_on) {
|
||||||
|
Wire.beginTransmission(BH1750_ADDR);
|
||||||
|
Wire.write(0x10);
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 연결 상태에 따라: 연결이면 delay(모뎀슬립), 비연결이면 light sleep
|
||||||
|
inline void cooperative_sleep_block(uint32_t ms_block) {
|
||||||
|
if (ms_block < 10) {
|
||||||
|
client.loop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t remain = ms_block;
|
||||||
|
while (remain > 0) {
|
||||||
|
uint32_t slice = (remain > SLEEP_SLICE_MS) ? SLEEP_SLICE_MS : remain;
|
||||||
|
sensor_power(true);
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
delay(slice);
|
||||||
|
} else {
|
||||||
|
esp_sleep_enable_timer_wakeup((uint64_t)slice * 1000ULL);
|
||||||
|
esp_light_sleep_start();
|
||||||
|
}
|
||||||
|
sensor_power(false);
|
||||||
|
client.loop();
|
||||||
|
remain -= slice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2cBusRecover() {
|
||||||
|
Wire.end();
|
||||||
|
pinMode(SCL_PIN, INPUT_PULLUP);
|
||||||
|
pinMode(SDA_PIN, INPUT_PULLUP);
|
||||||
|
delay(2);
|
||||||
|
for (int i = 0; i < 9 && digitalRead(SDA_PIN) == LOW; ++i) {
|
||||||
|
pinMode(SCL_PIN, OUTPUT);
|
||||||
|
digitalWrite(SCL_PIN, LOW);
|
||||||
|
delayMicroseconds(6);
|
||||||
|
digitalWrite(SCL_PIN, HIGH);
|
||||||
|
delayMicroseconds(6);
|
||||||
|
pinMode(SCL_PIN, INPUT_PULLUP);
|
||||||
|
delayMicroseconds(6);
|
||||||
|
}
|
||||||
|
pinMode(SDA_PIN, OUTPUT);
|
||||||
|
digitalWrite(SDA_PIN, LOW);
|
||||||
|
delayMicroseconds(6);
|
||||||
|
pinMode(SCL_PIN, OUTPUT);
|
||||||
|
digitalWrite(SCL_PIN, HIGH);
|
||||||
|
delayMicroseconds(6);
|
||||||
|
digitalWrite(SDA_PIN, HIGH);
|
||||||
|
delayMicroseconds(6);
|
||||||
|
|
||||||
|
Wire.begin(SDA_PIN, SCL_PIN);
|
||||||
|
Wire.setClock(100000);
|
||||||
|
#if defined(ESP_ARDUINO_VERSION)
|
||||||
|
Wire.setTimeOut(50);
|
||||||
|
#endif
|
||||||
|
lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
|
||||||
|
ads.begin(ADS1115_ADDR);
|
||||||
|
ads.setGain(GAIN_ONE);
|
||||||
|
inaA.begin();
|
||||||
|
inaB.begin();
|
||||||
|
i2cErrorCount++;
|
||||||
|
i2cFaultLatched = true;
|
||||||
|
digitalWrite(LED_I2C, HIGH);
|
||||||
|
Serial.println("[I2C] bus recovered & sensors reinit");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool waitWiFi(uint32_t ms) {
|
||||||
|
uint32_t t0 = millis();
|
||||||
|
while (WiFi.status() != WL_CONNECTED && (millis() - t0) < ms) delay(100);
|
||||||
|
return WiFi.status() == WL_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 버튼 디바운스
|
||||||
|
bool buttonPressed(Button& b) {
|
||||||
|
bool rd = digitalRead(b.pin);
|
||||||
|
unsigned long now = millis();
|
||||||
|
if (rd != b.lastReading) {
|
||||||
|
b.lastReading = rd;
|
||||||
|
b.lastChange = now;
|
||||||
|
}
|
||||||
|
if ((now - b.lastChange) > DEBOUNCE_MS) {
|
||||||
|
if (rd != b.lastStable) {
|
||||||
|
b.lastStable = rd;
|
||||||
|
if (rd == LOW) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 릴레이 토글(극성 반영)
|
||||||
|
// ★ R1만 직접 토글, R2는 항상 R1을 따라가도록 파생
|
||||||
|
void toggleRelay(uint8_t relayPin) {
|
||||||
|
bool active_low = relay_active_low_for_pin(relayPin);
|
||||||
|
bool on = relay_is_on(relayPin, active_low);
|
||||||
|
relay_write(relayPin, active_low, !on);
|
||||||
|
if (relayPin == RELAY1) {
|
||||||
|
updateDerivedRelays();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- OTA ----------
|
||||||
|
void checkForOTAUpdate() {
|
||||||
|
if (WiFi.status() != WL_CONNECTED) return;
|
||||||
|
HTTPClient http;
|
||||||
|
String versionURL = String(OTA_SERVER) + OTA_VERSION_FILE;
|
||||||
|
http.begin(versionURL);
|
||||||
|
int httpCode = http.GET();
|
||||||
|
if (httpCode == HTTP_CODE_OK) {
|
||||||
|
String response = http.getString();
|
||||||
|
response.trim();
|
||||||
|
int sep = response.indexOf('|');
|
||||||
|
if (sep != -1) {
|
||||||
|
String serverVersion = response.substring(0, sep);
|
||||||
|
String serverBinName = response.substring(sep + 1);
|
||||||
|
if (serverVersion != CURRENT_VERSION) {
|
||||||
|
HTTPClient binHttp;
|
||||||
|
binHttp.begin(String(OTA_SERVER) + "/" + serverBinName);
|
||||||
|
int binCode = binHttp.GET();
|
||||||
|
if (binCode == HTTP_CODE_OK) {
|
||||||
|
int contentLength = binHttp.getSize();
|
||||||
|
WiFiClient* stream = binHttp.getStreamPtr();
|
||||||
|
if (Update.begin(contentLength)) {
|
||||||
|
size_t written = Update.writeStream(*stream);
|
||||||
|
if (written == contentLength && Update.end() && Update.isFinished()) ESP.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binHttp.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- RS485 / Modbus ----------------------------- */
|
||||||
|
static void build_read_req(uint8_t* out, uint8_t sid, uint8_t func, uint16_t reg, uint16_t qty, bool crcLH) {
|
||||||
|
out[0] = sid; out[1] = func;
|
||||||
|
out[2] = (uint8_t)(reg >> 8); out[3] = (uint8_t)(reg & 0xFF);
|
||||||
|
out[4] = (uint8_t)(qty >> 8); out[5] = (uint8_t)(qty & 0xFF);
|
||||||
|
uint16_t crc = crc16_modbus(out, 6);
|
||||||
|
if (crcLH) { out[6] = (uint8_t)(crc & 0xFF); out[7] = (uint8_t)(crc >> 8); }
|
||||||
|
else { out[6] = (uint8_t)(crc >> 8); out[7] = (uint8_t)(crc & 0xFF); }
|
||||||
|
}
|
||||||
|
static bool same8(const uint8_t* a, const uint8_t* b) {
|
||||||
|
for (int i = 0; i < 8; i++) if (a[i] != b[i]) return false; return true;
|
||||||
|
}
|
||||||
|
static bool md04_read_one(uint8_t sid, uint16_t reg, int16_t& outVal) {
|
||||||
|
uint8_t reqLH[8], reqHL[8];
|
||||||
|
build_read_req(reqLH, sid, 0x04, reg, 1, true);
|
||||||
|
build_read_req(reqHL, sid, 0x04, reg, 1, false);
|
||||||
|
for (int pass = 0; pass < 2; ++pass) {
|
||||||
|
const uint8_t* req = (pass == 0) ? reqLH : reqHL;
|
||||||
|
while (RS485.available()) RS485.read();
|
||||||
|
rs485_tx_mode(true); RS485.write(req, 8); RS485.flush(); rs485_tx_mode(false);
|
||||||
|
delayMicroseconds(POST_TX_GUARD_US);
|
||||||
|
uint8_t rx[RS485_MAXBUF]; size_t n = 0; unsigned long t0 = millis(); bool echoDropped = false;
|
||||||
|
while ((millis() - t0) < RS485_TIMEOUT_MS) {
|
||||||
|
while (RS485.available() && n < RS485_MAXBUF) rx[n++] = RS485.read();
|
||||||
|
if (!echoDropped && n >= 8 && same8(rx, req)) { memmove(rx, rx + 8, n - 8); n -= 8; echoDropped = true; }
|
||||||
|
if (n >= 7) {
|
||||||
|
if (rx[0] == sid && rx[1] == 0x04 && rx[2] == 0x02) {
|
||||||
|
uint16_t crc_calc = crc16_modbus(rx, 5);
|
||||||
|
uint16_t crc_recv = (uint16_t)rx[5] | ((uint16_t)rx[6] << 8);
|
||||||
|
if (crc_calc == crc_recv) { outVal = (int16_t)((rx[3] << 8) | rx[4]); return true; }
|
||||||
|
}
|
||||||
|
if (n >= 5 && rx[0] == sid && rx[1] == 0x84) return false;
|
||||||
|
}
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool md04_transaction() {
|
||||||
|
bool ok = false; int16_t t10 = 0, h10 = 0;
|
||||||
|
for (int attempt = 0; attempt <= RS485_RETRY; ++attempt) {
|
||||||
|
bool okT = md04_read_one(MD04_SLAVE_ID, MD04_REG_T, t10);
|
||||||
|
delay(INTER_READ_DELAY_MS);
|
||||||
|
bool okH = md04_read_one(MD04_SLAVE_ID, MD04_REG_H, h10);
|
||||||
|
if (okT && okH) {
|
||||||
|
md04_airT = (float)t10 / 10.0f;
|
||||||
|
md04_airH = (float)((uint16_t)h10) / 10.0f;
|
||||||
|
md04_ok = isfinite(md04_airT) && isfinite(md04_airH);
|
||||||
|
ok = md04_ok; break;
|
||||||
|
} else md04_errcnt++;
|
||||||
|
}
|
||||||
|
if (!ok) md04_ok = false;
|
||||||
|
return md04_ok;
|
||||||
|
}
|
||||||
|
void md04_poll_if_due() {
|
||||||
|
uint32_t now = millis();
|
||||||
|
if (now - lastMd04Poll < MD04_POLL_MS) return;
|
||||||
|
lastMd04Poll = now; md04_transaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- WiFi events ----------------------------- */
|
||||||
|
void onWiFiEvent(arduino_event_id_t event, arduino_event_info_t info) {
|
||||||
|
if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) {
|
||||||
|
lastDiscReason = info.wifi_sta_disconnected.reason;
|
||||||
|
digitalWrite(LED_WIFI, HIGH);
|
||||||
|
Serial.printf("[WiFi] DISCONNECTED reason=%d\n", lastDiscReason);
|
||||||
|
} else if (event == ARDUINO_EVENT_WIFI_STA_CONNECTED) {
|
||||||
|
Serial.printf("[WiFi] CONNECTED bssid=%02X:%02X:%02X:%02X:%02X:%02X ch=%d\n",
|
||||||
|
info.wifi_sta_connected.bssid[0], info.wifi_sta_connected.bssid[1], info.wifi_sta_connected.bssid[2],
|
||||||
|
info.wifi_sta_connected.bssid[3], info.wifi_sta_connected.bssid[4], info.wifi_sta_connected.bssid[5],
|
||||||
|
info.wifi_sta_connected.channel);
|
||||||
|
} else if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) {
|
||||||
|
digitalWrite(LED_WIFI, LOW);
|
||||||
|
Serial.print("[WiFi] GOT IP "); Serial.println(WiFi.localIP());
|
||||||
|
WiFi.printDiag(Serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- MQTT ----------------------------- */
|
||||||
|
void setPublishIntervalMs(uint32_t ms) {
|
||||||
|
if (ms < 500UL) ms = 500UL;
|
||||||
|
if (ms > 600000UL) ms = 600000UL;
|
||||||
|
publishIntervalMs = ms;
|
||||||
|
Serial.printf("[CFG] publishIntervalMs=%lu ms\n", (unsigned long)publishIntervalMs);
|
||||||
|
}
|
||||||
|
void setDutySleepMs(uint32_t ms) {
|
||||||
|
if (ms < 500UL) ms = 500UL;
|
||||||
|
if (ms > 600000UL) ms = 600000UL;
|
||||||
|
duty_sleep_ms = ms;
|
||||||
|
Serial.printf("[CFG] duty_sleep_ms=%lu ms\n", (unsigned long)duty_sleep_ms);
|
||||||
|
}
|
||||||
|
void setDutyAwakeMs(uint32_t ms) {
|
||||||
|
if (ms < 200UL) ms = 200UL;
|
||||||
|
if (ms > 60000UL) ms = 60000UL;
|
||||||
|
duty_awake_ms = ms;
|
||||||
|
Serial.printf("[CFG] duty_awake_ms=%lu ms\n", (unsigned long)duty_awake_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleMQTTMessage(const String& message) {
|
||||||
|
bool updated = false;
|
||||||
|
|
||||||
|
// 릴레이 제어: R1과 R2는 항상 동기 (R2 파생)
|
||||||
|
if (message == "11" || message == "21") { // R1 ON, R2도 자동 ON
|
||||||
|
relay_write(RELAY1, RELAY1_ACTIVE_LOW, true);
|
||||||
|
updateDerivedRelays();
|
||||||
|
updated = true;
|
||||||
|
} else if (message == "10" || message == "20") { // R1 OFF, R2도 자동 OFF
|
||||||
|
relay_write(RELAY1, RELAY1_ACTIVE_LOW, false);
|
||||||
|
updateDerivedRelays();
|
||||||
|
updated = true;
|
||||||
|
} else if (message == "31") { // R3 ON (5V LED)
|
||||||
|
relay_write(RELAY3, RELAY3_ACTIVE_LOW, true);
|
||||||
|
updated = true;
|
||||||
|
} else if (message == "30") { // R3 OFF
|
||||||
|
relay_write(RELAY3, RELAY3_ACTIVE_LOW, false);
|
||||||
|
updated = true;
|
||||||
|
} else if (message == "100") {
|
||||||
|
checkForOTAUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish interval
|
||||||
|
else if (message.startsWith("PUB,")) {
|
||||||
|
long sec = message.substring(4).toInt();
|
||||||
|
if (sec > 0) setPublishIntervalMs((uint32_t)sec * 1000UL);
|
||||||
|
} else if (message.startsWith("PUBMS,")) {
|
||||||
|
long ms = message.substring(6).toInt();
|
||||||
|
if (ms > 0) setPublishIntervalMs((uint32_t)ms);
|
||||||
|
} else if (message == "PUB?") {
|
||||||
|
StaticJsonDocument<96> d;
|
||||||
|
d["pub_ms"] = (int)publishIntervalMs;
|
||||||
|
d["sleep_ms"] = (int)duty_sleep_ms;
|
||||||
|
d["awake_ms"] = (int)duty_awake_ms;
|
||||||
|
char b[96]; size_t l = serializeJson(d, b);
|
||||||
|
client.publish("sokuree/house/status/cfg", b, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duty cycle
|
||||||
|
else if (message.startsWith("SLEEP,")) {
|
||||||
|
long sec = message.substring(6).toInt();
|
||||||
|
if (sec > 0) setDutySleepMs((uint32_t)sec * 1000UL);
|
||||||
|
} else if (message.startsWith("SLEEPMS,")) {
|
||||||
|
long ms = message.substring(8).toInt();
|
||||||
|
if (ms > 0) setDutySleepMs((uint32_t)ms);
|
||||||
|
} else if (message.startsWith("AWAKE,")) {
|
||||||
|
long sec = message.substring(6).toInt();
|
||||||
|
if (sec > 0) setDutyAwakeMs((uint32_t)sec * 1000UL);
|
||||||
|
} else if (message.startsWith("AWAKEMS,")) {
|
||||||
|
long ms = message.substring(8).toInt();
|
||||||
|
if (ms > 0) setDutyAwakeMs((uint32_t)ms);
|
||||||
|
} else if (message == "DUTY?") {
|
||||||
|
StaticJsonDocument<96> d;
|
||||||
|
d["sleep_ms"] = (int)duty_sleep_ms;
|
||||||
|
d["awake_ms"] = (int)duty_awake_ms;
|
||||||
|
d["pub_ms"] = (int)publishIntervalMs;
|
||||||
|
char b[96]; size_t l = serializeJson(d, b);
|
||||||
|
client.publish("sokuree/house/status/cfg", b, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated) delay(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callback(char* topic, byte* payload, unsigned int length) {
|
||||||
|
String msg; msg.reserve(length);
|
||||||
|
for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
|
||||||
|
handleMQTTMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connectToMqtt() {
|
||||||
|
if (!WiFi.isConnected() || client.connected()) return;
|
||||||
|
uint64_t chipId = ESP.getEfuseMac();
|
||||||
|
char clientId[32];
|
||||||
|
snprintf(clientId, sizeof(clientId), "ESP32_%04X", (uint16_t)(chipId & 0xFFFF));
|
||||||
|
if (client.connect(clientId)) {
|
||||||
|
client.subscribe(mqtt_topic_control);
|
||||||
|
mqttReconnectAttempts = 0;
|
||||||
|
} else {
|
||||||
|
mqttReconnectAttempts++;
|
||||||
|
if (mqttReconnectAttempts >= maxMqttReconnectAttempts) ESP.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- ADS helpers ----------------------------- */
|
||||||
|
float readADS_V(uint8_t ch) {
|
||||||
|
int16_t r[5];
|
||||||
|
for (int i = 0; i < 5; i++) { r[i] = ads.readADC_SingleEnded(ch); delay(2); }
|
||||||
|
for (int i = 1; i < 5; i++) {
|
||||||
|
int16_t k = r[i], j = i - 1;
|
||||||
|
while (j >= 0 && r[j] > k) { r[j + 1] = r[j]; j--; }
|
||||||
|
r[j + 1] = k;
|
||||||
|
}
|
||||||
|
float v = ads.computeVolts(r[2]);
|
||||||
|
if (v < 0.0f) v = 0.0f;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- I2C 1초 에러 요약 ----------------------------- */
|
||||||
|
static inline void i2c_error_report_1s() {
|
||||||
|
uint32_t now = millis();
|
||||||
|
if (now - lastI2cErrReportMs < 1000UL) return;
|
||||||
|
lastI2cErrReportMs = now;
|
||||||
|
|
||||||
|
if (!bh1750_ok || !ads_ok || !inaA_ok || !inaB_ok || i2cFaultLatched) {
|
||||||
|
Serial.print("[I2C][ERR] modules=");
|
||||||
|
bool first = true;
|
||||||
|
auto pf = [&](const char* name) {
|
||||||
|
if (!first) Serial.print(", ");
|
||||||
|
Serial.print(name); first = false;
|
||||||
|
};
|
||||||
|
if (!bh1750_ok) pf("BH1750");
|
||||||
|
if (!ads_ok) pf("ADS1115");
|
||||||
|
if (!inaA_ok) pf("INA219_A");
|
||||||
|
if (!inaB_ok) pf("INA219_B");
|
||||||
|
if (i2cFaultLatched) { if (!first) Serial.print(", "); Serial.print("BUS_FAULT"); first = false; }
|
||||||
|
|
||||||
|
Serial.print(" | cnts { bh="); Serial.print(bh1750_err);
|
||||||
|
Serial.print(", ads="); Serial.print(ads_err);
|
||||||
|
Serial.print(", inaA="); Serial.print(inaA_err);
|
||||||
|
Serial.print(", inaB="); Serial.print(inaB_err);
|
||||||
|
Serial.print(", bus="); Serial.print(i2cErrorCount);
|
||||||
|
Serial.println(" }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- Publish Status ----------------------------- */
|
||||||
|
void publishStatus() {
|
||||||
|
// I2C 상태 점검 & 복구
|
||||||
|
if (millis() - lastI2cCheckMs > 2000) {
|
||||||
|
lastI2cCheckMs = millis();
|
||||||
|
static uint8_t consec_ping_fail = 0;
|
||||||
|
bool ping_ok = i2cPing(ADS1115_ADDR) && i2cPing(INA219_ADDR_A)
|
||||||
|
&& i2cPing(INA219_ADDR_B) && i2cPing(BH1750_ADDR);
|
||||||
|
if (!ping_ok) {
|
||||||
|
consec_ping_fail++;
|
||||||
|
if (consec_ping_fail >= 2) { Serial.println("[I2C] ping failed twice -> recover"); i2cBusRecover(); consec_ping_fail = 0; }
|
||||||
|
} else {
|
||||||
|
consec_ping_fail = 0; i2cFaultLatched = false; digitalWrite(LED_I2C, LOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 핑 OK 플래그
|
||||||
|
bh1750_ok = i2cPing(BH1750_ADDR);
|
||||||
|
ads_ok = i2cPing(ADS1115_ADDR);
|
||||||
|
inaA_ok = i2cPing(INA219_ADDR_A);
|
||||||
|
inaB_ok = i2cPing(INA219_ADDR_B);
|
||||||
|
|
||||||
|
if (!bh1750_ok) bh1750_err++;
|
||||||
|
if (!ads_ok) ads_err++;
|
||||||
|
if (!inaA_ok) inaA_err++;
|
||||||
|
if (!inaB_ok) inaB_err++;
|
||||||
|
|
||||||
|
// BH1750
|
||||||
|
float lux = NAN;
|
||||||
|
if (bh1750_ok) {
|
||||||
|
lux = lightMeter.readLightLevel();
|
||||||
|
if (!isfinite(lux) || lux < 0) { bh1750_ok = false; bh1750_err++; lux = NAN; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// INA219
|
||||||
|
float bv1 = NAN, c1 = NAN, bv2 = NAN, c2 = NAN;
|
||||||
|
if (inaA_ok) { bv1 = inaA.getBusVoltage_V(); c1 = inaA.getCurrent_mA();
|
||||||
|
if (!isfinite(bv1) || !isfinite(c1)) { inaA_ok = false; inaA_err++; }
|
||||||
|
}
|
||||||
|
if (inaB_ok) { bv2 = inaB.getBusVoltage_V(); c2 = inaB.getCurrent_mA();
|
||||||
|
if (!isfinite(bv2) || !isfinite(c2)) { inaB_ok = false; inaB_err++; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADS1115
|
||||||
|
auto safe_readADS_V = [&](uint8_t ch) -> float {
|
||||||
|
if (!ads_ok) return NAN;
|
||||||
|
float v = readADS_V(ch);
|
||||||
|
if (!isfinite(v) || v < 0) { ads_ok = false; ads_err++; return NAN; }
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
float a_rawV[4] = { NAN, NAN, NAN, NAN };
|
||||||
|
for (int i = 0; i < 4; i++) a_rawV[i] = safe_readADS_V(i);
|
||||||
|
|
||||||
|
float a_mapV[4] = { NAN, NAN, NAN, NAN };
|
||||||
|
for (int i = 0; i < 4; i++) if (isfinite(a_rawV[i])) {
|
||||||
|
a_mapV[i] = a_rawV[i] * A_SCALE[i] + A_OFFSET[i];
|
||||||
|
if (!isfinite(a_mapV[i])) a_mapV[i] = NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 배터리 %
|
||||||
|
auto batpct = [&](float v) {
|
||||||
|
if (!isfinite(v)) return NAN;
|
||||||
|
float p = (v - 3.3f) / (4.2f - 3.3f) * 100.0f;
|
||||||
|
if (p < 0) p = 0; if (p > 100) p = 100;
|
||||||
|
return roundf(p * 10) / 10.0f;
|
||||||
|
};
|
||||||
|
float batpct0 = isfinite(a_mapV[0]) ? batpct(a_mapV[0]) : NAN;
|
||||||
|
float batpct1 = isfinite(a_mapV[1]) ? batpct(a_mapV[1]) : NAN;
|
||||||
|
float batpct2 = isfinite(a_mapV[2]) ? batpct(a_mapV[2]) : NAN;
|
||||||
|
|
||||||
|
// 전력/에너지 적산
|
||||||
|
float pwr1_mW = (isfinite(bv1) && isfinite(c1)) ? (bv1 * c1) : NAN;
|
||||||
|
float pwr2_mW = (isfinite(bv2) && isfinite(c2)) ? (bv2 * c2) : NAN;
|
||||||
|
uint32_t nowMs = millis();
|
||||||
|
if (lastEnergyMs == 0) lastEnergyMs = nowMs;
|
||||||
|
float dt_h = (nowMs - lastEnergyMs) / 3600000.0f;
|
||||||
|
if (dt_h > (10.0f / 3600.0f)) dt_h = (10.0f / 3600.0f);
|
||||||
|
lastEnergyMs = nowMs;
|
||||||
|
|
||||||
|
timeClient.update();
|
||||||
|
time_t rawTime = timeClient.getEpochTime();
|
||||||
|
struct tm ti; localtime_r(&rawTime, &ti);
|
||||||
|
int currentDay = ti.tm_mday;
|
||||||
|
if (lastDay == -1) lastDay = currentDay;
|
||||||
|
if (currentDay != lastDay) { dailyWh1 = 0.0; dailyWh2 = 0.0; lastDay = currentDay; }
|
||||||
|
if (isfinite(pwr1_mW)) dailyWh1 += (pwr1_mW / 1000.0f) * dt_h;
|
||||||
|
if (isfinite(pwr2_mW)) dailyWh2 += (pwr2_mW / 1000.0f) * dt_h;
|
||||||
|
|
||||||
|
// ★ 파생 릴레이 동기 (R1 변경 후 혹시 모를 불일치 방지)
|
||||||
|
updateDerivedRelays();
|
||||||
|
|
||||||
|
// ===== 발행 JSON =====
|
||||||
|
StaticJsonDocument<1024> doc;
|
||||||
|
doc["ver"] = CURRENT_VERSION;
|
||||||
|
doc["r1"] = relay_is_on(RELAY1, RELAY1_ACTIVE_LOW) ? "ON" : "OFF";
|
||||||
|
doc["r2"] = relay_is_on(RELAY2, RELAY2_ACTIVE_LOW) ? "ON" : "OFF";
|
||||||
|
doc["r3"] = relay_is_on(RELAY3, RELAY3_ACTIVE_LOW) ? "ON" : "OFF";
|
||||||
|
|
||||||
|
// 조도
|
||||||
|
if (isfinite(lux)) doc["lux"] = (int)lux; else doc["lux"] = nullptr;
|
||||||
|
|
||||||
|
// INA219
|
||||||
|
if (isfinite(bv1)) doc["bv1"] = roundf(bv1 * 100) / 100.0f; else doc["bv1"] = nullptr;
|
||||||
|
if (isfinite(c1)) doc["c1"] = roundf(c1 * 100) / 100.0f; else doc["c1"] = nullptr;
|
||||||
|
if (isfinite(bv2)) doc["bv2"] = roundf(bv2 * 100) / 100.0f; else doc["bv2"] = nullptr;
|
||||||
|
if (isfinite(c2)) doc["c2"] = roundf(c2 * 100) / 100.0f; else doc["c2"] = nullptr;
|
||||||
|
|
||||||
|
// ADS 맵V
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
char key[3]; snprintf(key, sizeof(key), "a%d", i);
|
||||||
|
if (isfinite(a_mapV[i])) doc[key] = roundf(a_mapV[i] * 100) / 100.0f;
|
||||||
|
else doc[key] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 배터리 %
|
||||||
|
if (isfinite(batpct0)) doc["batp0"] = batpct0; else doc["batp0"] = nullptr;
|
||||||
|
if (isfinite(batpct1)) doc["batp1"] = batpct1; else doc["batp1"] = nullptr;
|
||||||
|
if (isfinite(batpct2)) doc["batp2"] = batpct2; else doc["batp2"] = nullptr;
|
||||||
|
|
||||||
|
// 전력/에너지
|
||||||
|
if (isfinite(pwr1_mW)) doc["pwr1"] = roundf(pwr1_mW * 100) / 100.0f; else doc["pwr1"] = nullptr;
|
||||||
|
if (isfinite(pwr2_mW)) doc["pwr2"] = roundf(pwr2_mW * 100) / 100.0f; else doc["pwr2"] = nullptr;
|
||||||
|
doc["dwh1"] = roundf(dailyWh1 * 10000) / 10000.0f;
|
||||||
|
doc["dwh2"] = roundf(dailyWh2 * 10000) / 10000.0f;
|
||||||
|
|
||||||
|
char buffer[768];
|
||||||
|
size_t len = serializeJson(doc, buffer);
|
||||||
|
digitalWrite(LED_TX, HIGH);
|
||||||
|
client.publish(mqtt_topic_status, buffer, len);
|
||||||
|
digitalWrite(LED_TX, LOW);
|
||||||
|
Serial.print("Status sent: "); Serial.println(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- Setup / Loop ----------------------------- */
|
||||||
|
void connectToBestAP() {
|
||||||
|
WiFi.persistent(false);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.disconnect(true, true);
|
||||||
|
delay(60);
|
||||||
|
int n = WiFi.scanNetworks(false, true);
|
||||||
|
struct Found { const char* ssid; const char* pass; int ch; uint8_t bssid[6]; int rssi; bool found; }
|
||||||
|
best = { nullptr, nullptr, 0, { 0 }, -1000, false },
|
||||||
|
alt = { nullptr, nullptr, 0, { 0 }, -1000, false };
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
String s = WiFi.SSID(i); int r = WiFi.RSSI(i); int ch = WiFi.channel(i);
|
||||||
|
const uint8_t* b = WiFi.BSSID(i);
|
||||||
|
for (int j = 0; j < candidateCount; ++j)
|
||||||
|
if (s == candidates[j].ssid) {
|
||||||
|
if (!best.found || r > best.rssi) {
|
||||||
|
if (best.found) alt = best;
|
||||||
|
best = { candidates[j].ssid, candidates[j].password, ch, { 0 }, r, true };
|
||||||
|
if (b) memcpy(best.bssid, b, 6);
|
||||||
|
} else if (!alt.found || r > alt.rssi) {
|
||||||
|
alt = { candidates[j].ssid, candidates[j].password, ch, { 0 }, r, true };
|
||||||
|
if (b) memcpy(alt.bssid, b, 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto tryConnect = [&](const Found& f) -> bool {
|
||||||
|
if (!f.found) return false;
|
||||||
|
lastDiscReason = -1;
|
||||||
|
Serial.printf("[WiFi] try lock %s ch%d rssi%d\n", f.ssid, f.ch, f.rssi);
|
||||||
|
WiFi.begin(f.ssid, f.pass, f.ch, f.bssid);
|
||||||
|
if (waitWiFi(6000)) return true;
|
||||||
|
Serial.printf("[WiFi] lock failed status=%d reason=%d\n", WiFi.status(), lastDiscReason);
|
||||||
|
lastDiscReason = -1;
|
||||||
|
Serial.printf("[WiFi] retry generic %s\n", f.ssid);
|
||||||
|
WiFi.disconnect(true, true);
|
||||||
|
delay(80);
|
||||||
|
WiFi.begin(f.ssid, f.pass);
|
||||||
|
if (waitWiFi(8000)) return true;
|
||||||
|
Serial.printf("[WiFi] generic failed status=%d reason=%d\n", WiFi.status(), lastDiscReason);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
bool ok = false;
|
||||||
|
if (best.found) ok = tryConnect(best);
|
||||||
|
if (!ok && alt.found) ok = tryConnect(alt);
|
||||||
|
if (!ok) { Serial.printf("[WiFi] fallback generic %s\n", candidates[0].ssid);
|
||||||
|
WiFi.begin(candidates[0].ssid, candidates[0].password); waitWiFi(8000);
|
||||||
|
}
|
||||||
|
WiFi.scanDelete();
|
||||||
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// put your setup code here, to run once:
|
Serial.begin(115200);
|
||||||
int result = myFunction(2, 3);
|
|
||||||
|
pinMode(RELAY1, OUTPUT);
|
||||||
|
pinMode(RELAY2, OUTPUT);
|
||||||
|
pinMode(RELAY3, OUTPUT);
|
||||||
|
|
||||||
|
// ★ 초기상태: 모두 OFF로 통일 (극성 반영)
|
||||||
|
relay_write(RELAY1, RELAY1_ACTIVE_LOW, false);
|
||||||
|
relay_write(RELAY2, RELAY2_ACTIVE_LOW, false);
|
||||||
|
relay_write(RELAY3, RELAY3_ACTIVE_LOW, false);
|
||||||
|
updateDerivedRelays(); // R1 OFF → R2 OFF
|
||||||
|
|
||||||
|
pinMode(LED_TX, OUTPUT); digitalWrite(LED_TX, LOW);
|
||||||
|
pinMode(LED_I2C, OUTPUT); digitalWrite(LED_I2C, LOW);
|
||||||
|
pinMode(LED_WIFI, OUTPUT); digitalWrite(LED_WIFI, HIGH);
|
||||||
|
pinMode(BTN_R1, INPUT_PULLUP);
|
||||||
|
pinMode(BTN_R2, INPUT_PULLUP);
|
||||||
|
|
||||||
|
Wire.begin(SDA_PIN, SCL_PIN);
|
||||||
|
Wire.setClock(100000);
|
||||||
|
#if defined(ESP_ARDUINO_VERSION)
|
||||||
|
Wire.setTimeOut(50);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
|
||||||
|
ads.begin(ADS1115_ADDR); ads.setGain(GAIN_ONE);
|
||||||
|
inaA.begin(); inaB.begin();
|
||||||
|
|
||||||
|
RS485.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||||
|
|
||||||
|
WiFi.setSleep(true);
|
||||||
|
esp_wifi_set_ps(WIFI_PS_MAX_MODEM);
|
||||||
|
WiFi.onEvent(onWiFiEvent);
|
||||||
|
connectToBestAP();
|
||||||
|
|
||||||
|
client.setServer(mqtt_broker, mqtt_port);
|
||||||
|
client.setCallback(callback);
|
||||||
|
client.setKeepAlive(MQTT_KEEPALIVE_SEC);
|
||||||
|
client.setBufferSize(768);
|
||||||
|
|
||||||
|
timeClient.begin();
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
if (WiFi.status() == WL_CONNECTED && timeClient.update()) break;
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
digitalWrite(LED_WIFI, (WiFi.status() == WL_CONNECTED) ? LOW : HIGH);
|
||||||
|
|
||||||
|
sensor_power(false); // 웨이크 상태 시작
|
||||||
|
|
||||||
|
// 첫 측정/발행
|
||||||
|
publishStatus();
|
||||||
|
lastPublishTime = millis();
|
||||||
|
|
||||||
|
// 첫 AWAKE 윈도우 시작
|
||||||
|
duty_awake_until_ms = millis() + duty_awake_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void connectToMqtt();
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
// put your main code here, to run repeatedly:
|
unsigned long now = millis();
|
||||||
}
|
|
||||||
|
|
||||||
// put function definitions here:
|
// 버튼 즉시 처리 (두 버튼 모두 R1+R2 토글로 사용)
|
||||||
int myFunction(int x, int y) {
|
if (buttonPressed(btnR1)) { toggleRelay(RELAY1); publishStatus(); }
|
||||||
return x + y;
|
if (buttonPressed(btnR2)) { toggleRelay(RELAY1); publishStatus(); }
|
||||||
}
|
|
||||||
|
// WiFi LED
|
||||||
|
digitalWrite(LED_WIFI, (WiFi.status() == WL_CONNECTED) ? LOW : HIGH);
|
||||||
|
|
||||||
|
// 연결 유지/재연결
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
if (now - lastWiFiReconnectAttempt > 5000UL) {
|
||||||
|
lastWiFiReconnectAttempt = now; WiFi.mode(WIFI_STA); connectToBestAP();
|
||||||
|
}
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
if (!client.connected()) {
|
||||||
|
if (now - lastMqttReconnectAttempt > 5000) {
|
||||||
|
lastMqttReconnectAttempt = now; connectToMqtt();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client.loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// RS485 폴링(비동기)
|
||||||
|
md04_poll_if_due();
|
||||||
|
|
||||||
|
// I2C 에러 요약(1초 주기)
|
||||||
|
i2c_error_report_1s();
|
||||||
|
|
||||||
|
// ===== Duty-cycle: AWAKE 구간 → 발행 / AWAKE 끝나면 SLEEP 블록 =====
|
||||||
|
bool in_burst = ((int32_t)(duty_awake_until_ms - millis()) > 0);
|
||||||
|
|
||||||
|
if (in_burst) {
|
||||||
|
// 깨어있는 동안: PUB 주기로 반복 발행
|
||||||
|
if (millis() - lastPublishTime > publishIntervalMs) {
|
||||||
|
lastPublishTime = millis(); publishStatus();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// AWAKE 종료 → 슬립 블록 수행
|
||||||
|
cooperative_sleep_block(duty_sleep_ms);
|
||||||
|
|
||||||
|
// 슬립 끝: 즉시 1회 발행
|
||||||
|
publishStatus(); lastPublishTime = millis();
|
||||||
|
|
||||||
|
// 다음 AWAKE 윈도우 시작
|
||||||
|
duty_awake_until_ms = millis() + duty_awake_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user