diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..eba1110 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ed021a --- /dev/null +++ b/.gitignore @@ -0,0 +1,220 @@ +################# +## SparkFun Useful stuff +################# + +## AVR Development +*.eep +*.elf +*.lst +*.lss +*.sym +*.d +*.o +*.srec +*.map + +## Notepad++ backup files +*.bak + +## BOM files +*bom* + +## VSCode directories +.vscode + +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +############# +## Eagle +############# + +# Ignore the board and schematic backup files and lock files +*.b#? +*.s#? +*.l#? +*.lck + + +############# +## KiCad +############# + +*cache.lib +*.kicad_pcb-bak +*.net +*.gbr +*.drl + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Mac OS +############# + +.DS_Store + + +############# +## Linux +############# + +# backup files (*.bak on Win) +*~ + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md diff --git a/library.properties b/library.properties index 4b60d0f..4d798bf 100644 --- a/library.properties +++ b/library.properties @@ -2,7 +2,7 @@ name=SparkFun Toolkit version=0.1.0 author=SparkFun Electronics maintainer=SparkFun Electronics -sentence=A utility library that other SparkFun libraries can take advantage of. +sentence=A generic communications utility library that other SparkFun libraries can take advantage of. paragraph= category=Other url=https://github.com/sparkfun/SparkFun_Toolkit diff --git a/src/SparkFun_Toolkit.h b/src/SparkFun_Toolkit.h index 6e6670a..8c1b674 100644 --- a/src/SparkFun_Toolkit.h +++ b/src/SparkFun_Toolkit.h @@ -1 +1,10 @@ -// Code! \ No newline at end of file +/* + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics +*/ + +#pragma once + +#include "sfe_i2c_arduino.h" +// #include "sfe_spi_arduino.h" diff --git a/src/sfe_bus.h b/src/sfe_bus.h index 415218f..c48aad3 100644 --- a/src/sfe_bus.h +++ b/src/sfe_bus.h @@ -1,63 +1,71 @@ /* -sfe_bus.h - -The MIT License (MIT) - -Copyright (c) 2022 SparkFun Electronics -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: The -above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED -"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -The following virtual class provides an abstract communication interface. + SPDX-License-Identifier: MIT + Copyright (c) 2023 SparkFun Electronics + + A pure virtual base class for implementing a common communication interface + in SparkFun products. */ #pragma once -#include +#include + +// Error and warning codes +#define SFE_BUS_OK 0 +#define SFE_BUS_E_UNKNOWN -1 +#define SFE_BUS_E_NULL_PTR -2 +#define SFE_BUS_E_TIMEOUT -3 +#define SFE_BUS_E_NO_RESPONSE -4 +#define SFE_BUS_E_DATA_TOO_LONG -5 +#define SFE_BUS_E_NULL_DEV_SETTINGS -6 +#define SFE_BUS_E_NULL_DATA_BUFFER -7 +#define SFE_BUS_W_UNKNOWN 1 +#define SFE_BUS_W_UNDER_READ 2 +#define SFE_BUS_W_NOT_ENABLED 3 -// To repeatedly use this toolkit, you may need to wrap this class in a namespace. -// namespace sfe_XXX { +/// @brief An abstract Bus address class for enabling multiple types of addresses. +class SFEBusDevSettings{}; // Nothing to see here... -// The following abstract class is used an interface for upstream implementation. -class SfeBus +/// @brief An abstract interface for a communication bus +class SFEBus { public: - /// @brief A simple ping of the device at the given address. - /// @param devAddr Address of the device - virtual bool ping(uint8_t devAddr) = 0; - - /// @brief Write a single byte to the given register - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - virtual bool writeRegisterByte(uint8_t devAddr, uint8_t devReg, uint8_t data) = 0; - - /// @brief Writes a number of bytes starting at the given register's address. - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - virtual int writeRegisterRegion(uint8_t devAddr, uint8_t devReg, const uint8_t *data, uint16_t length) = 0; - - /// @brief Reads a block of data from the given register. - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - virtual int readRegisterRegion(uint8_t addr, uint8_t reg, uint8_t *data, uint16_t numBytes) = 0; -}; + /// @brief Begin bus. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t begin(void) = 0; + + /// @brief End bus. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t end(void) = 0; -//}; + /// @brief Writes a number of bytes starting at the given register address. + /// @param devSettings Settings of the device. + /// @param regAddr The first register address to write to. + /// @param data Data buffer to write to registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, const uint8_t *data, const uint32_t numBytes) = 0; + + /// @brief Reads a number of bytes starting at the given register address. + /// @param devSettings Settings of the device. + /// @param regAddr The first register address to read from. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, const uint32_t numBytes) = 0; + + /// @brief Writes a number of bytes to a device that doesn't use registers for communications. + /// @param devSettings Settings of the device. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, const uint32_t numBytes) = 0; + + /// @brief Reads a number of bytes to a device that doesn't use registers for communications. + /// @param devSettings Settings of the device. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, const uint32_t numBytes) = 0; +}; diff --git a/src/sfe_i2c.cpp b/src/sfe_i2c.cpp deleted file mode 100644 index 8a8c725..0000000 --- a/src/sfe_i2c.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* -sfe_i2c.cpp - -The MIT License (MIT) - -Copyright (c) 2022 SparkFun Electronics -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: The -above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED -"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include "sfe_i2c.h" - -#define maxI2CBufferLength 32 -// What we use for transfer chunk size -#define buffSize maxI2CBufferLength; - -bool SfeI2C::init(TwoWire &wirePort, bool bInit) -{ - - // if we don't have a wire port already - if (!_i2cPort) - { - _i2cPort = &wirePort; - - if (bInit) - _i2cPort->begin(); - } - - return true; -} - -bool SfeI2C::init() -{ - if (!_i2cPort) - return init(Wire); - else - return false; -} - -bool SfeI2C::ping(uint8_t devAddr) -{ - - if (!_i2cPort) - return false; - - _i2cPort->beginTransmission(devAddr); - return _i2cPort->endTransmission() == 0; -} - -bool SfeI2C::writeRegisterByte(uint8_t devAddr, uint8_t devReg, uint8_t dataToWrite) -{ - - if (!_i2cPort) - return false; - - _i2cPort->beginTransmission(devAddr); - _i2cPort->write(devReg); - _i2cPort->write(dataToWrite); - return _i2cPort->endTransmission() == 0; -} - -int SfeI2C::writeRegisterRegion(uint8_t devAddr, uint8_t devReg, const uint8_t *data, uint16_t length) -{ - - _i2cPort->beginTransmission(devAddr); - _i2cPort->write(devReg); - _i2cPort->write(data, (int)length); - - return _i2cPort->endTransmission() ? -1 : 0; // -1 = error, 0 = success -} - -int SfeI2C::readRegisterRegion(uint8_t devAddr, uint8_t devReg, uint8_t *data, uint16_t numBytes) -{ - uint8_t nChunk; - uint16_t nReturned; - - if (!_i2cPort) - return -1; - - int i; // counter in loop - bool bFirstInter = true; // Flag for first iteration - used to send devRegister - - while (numBytes > 0) - { - _i2cPort->beginTransmission(devAddr); - - if (bFirstInter) - { - _i2cPort->write(devReg); - bFirstInter = false; - } - - if (_i2cPort->endTransmission() != 0) - return -1; // error with the end transmission - - // We're chunking in data - keeping the max chunk to kMaxI2CBufferLength - nChunk = numBytes > buffSize ? buffSize : numBytes; - - nReturned = _i2cPort->requestFrom((int)devAddr, (int)nChunk, (int)true); - - // No data returned, no dice - if (nReturned == 0) - return -1; // error - - // Copy the retrieved data chunk to the current index in the data segment - for (i = 0; i < nReturned; i++) - { - *data++ = _i2cPort->read(); - } - - // Decrement the amount of data recieved from the overall data request amount - numBytes = numBytes - nReturned; - - } // end while - - return 0; // Success -} diff --git a/src/sfe_i2c.h b/src/sfe_i2c.h index 80369db..d30b5b0 100644 --- a/src/sfe_i2c.h +++ b/src/sfe_i2c.h @@ -1,79 +1,60 @@ /* -sfe_i2c.h - -The MIT License (MIT) - -Copyright (c) 2022 SparkFun Electronics -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: The -above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED -"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -The following classes specify the behavior for communicating -over Inter-Integrated Circuit (I2C) + SPDX-License-Identifier: MIT + Copyright (c) 2023 SparkFun Electronics */ #pragma once #include "sfe_bus.h" -#include -/* -The SfeI2C device defines behavior for I2C implementation based around the TwoWire class (Wire). -This is Arduino specific. -*/ -class SfeI2C : public SfeBus +#define SFE_BUS_I2C_DEFAULT_ADDRESS 0x00 +#define SFE_BUS_I2C_DEFAULT_I2C_SPEED 100000 +#define SFE_BUS_I2C_BUFFER_SIZE 32 + +// @brief A simple bus address implementation for a generic I2C. +class SFEBusDevSettingsI2C : public SFEBusDevSettings { public: - /// @brief Constructor - SfeI2C(void) : _i2cPort(nullptr) {}; - - /// @brief Method sets up the required I2C settings. This function - /// provides a default I2C Port. - /// @return True on successful execution. - bool init(); - - /// @brief Method sets up the required I2C settings. - /// @param wirePort Port for I2C communcation. - /// @param bInit This flag tracks whether the bus has been initialized. - /// @return True on successful execution. - bool init(TwoWire &wirePort, bool bInit = false); - - /// @brief A simple ping of the device at the given address. - /// @param devAddr Address of the device - bool ping(uint8_t devAddr); - - /// @brief Write a single byte to the given register - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - bool writeRegisterByte(uint8_t devAddr, uint8_t devReg, uint8_t data); - - /// @brief Writes a number of bytes starting at the given register's address. - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - int writeRegisterRegion(uint8_t devAddr, uint8_t devReg, const uint8_t *data, uint16_t length); - - /// @brief Reads a block of data from the given register. - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - int readRegisterRegion(uint8_t addr, uint8_t reg, uint8_t *data, uint16_t numBytes); + uint8_t devAddr = SFE_BUS_I2C_DEFAULT_ADDRESS; // Default I2C Address + uint32_t maxDataRate = SFE_BUS_I2C_DEFAULT_I2C_SPEED; // Default I2C Speed +}; - private: - TwoWire *_i2cPort; +/// @brief An abstract interface for an I2C communication bus +class SFEBusI2C : public SFEBus +{ + public: + /// @brief Pings I2C device and looks for an ACK response. + /// @param devAddr Address to ping. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t ping(const uint8_t devAddr) = 0; + + /// @brief Pings I2C device and looks for an ACK response. + /// @param devSettings Settings of device to ping. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t ping(const SFEBusDevSettings *devSettings) = 0; + + /// @brief Changes the I2C buffer size. + /// @param bufferSize New buffer size. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t setBufferSize(const uint32_t bufferSize) = 0; + + /// @brief Returns the I2C buffer size. + /// @param bufferSize Buffer to return the size of the transmit buffer. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t getBufferSize(uint32_t *bufferSize) = 0; + + /// @brief Changes the Bus transmit frequency. + /// @param frequency New bus frequency. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t setBusFrequency(const uint32_t frequency) = 0; + + /// @brief Returns the Bus transmit frequency. + /// @param frequency Buffer to return the frequency. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t getBusFrequency(uint32_t *frequency) = 0; + + protected: + uint32_t _i2cBufferSize = SFE_BUS_I2C_BUFFER_SIZE; // Default Buffer Size + uint32_t _busFrequency = SFE_BUS_I2C_DEFAULT_I2C_SPEED; // Default Speed is 100kHz }; diff --git a/src/sfe_i2c_arduino.cpp b/src/sfe_i2c_arduino.cpp new file mode 100644 index 0000000..cada1ee --- /dev/null +++ b/src/sfe_i2c_arduino.cpp @@ -0,0 +1,302 @@ +/* + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics +*/ + +#include "sfe_i2c_arduino.h" + +int8_t SFEBusArduinoI2C::begin(void) +{ + return begin(_i2cBus ? _i2cBus : &Wire ); +} + +int8_t SFEBusArduinoI2C::begin(TwoWire &wirePort) +{ + return begin(&wirePort); +} + +int8_t SFEBusArduinoI2C::begin(TwoWire *i2cBus) +{ + if (!i2cBus) + return SFE_BUS_E_NULL_PTR; + + _i2cBus = i2cBus; + + _i2cBus->begin(); + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::end(void) +{ + // Null pointer check. + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + _i2cBus->end(); + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::ping(const uint8_t devAddr) +{ + // Null pointer check. + if (_i2cBus == nullptr) + return SFE_BUS_E_NULL_PTR; + if (!devAddr) + return SFE_BUS_E_NULL_PTR; + // Begin and end transmission to check for ACK response + _i2cBus->beginTransmission(devAddr); + + return _mapError(_i2cBus->endTransmission()); +} + +int8_t SFEBusArduinoI2C::ping(const SFEBusDevSettings *devSettings) +{ + // Null pointer check. + if (!devSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; + + return ping(pAddr->devAddr); +} + +int8_t SFEBusArduinoI2C::writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, + const uint8_t *data, const uint32_t numBytes) +{ + // Null pointer check. + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + // Null pointer check. + if (!devSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; + + uint32_t writeOffset = 0; + uint32_t bytesToSend = numBytes; + int8_t result = 0; + + // Start transmission and send register address. + _i2cBus->beginTransmission(pAddr->devAddr); + _i2cBus->write(regAddr); + + while (bytesToSend > 0) + { + // Limit sendLength to the size of the I2C buffer to send in chunks. + uint8_t sendLength = (bytesToSend > _i2cBufferSize) ? _i2cBufferSize : bytesToSend; + + // Do the write thing. + for (uint8_t i = 0; i < sendLength; i++) + _i2cBus->write(data[writeOffset + i]); + + // If there's still more to send, send a repeat start. + if (bytesToSend > _i2cBufferSize) + { + result = _mapError(_i2cBus->endTransmission(false)); + if (SFE_BUS_OK != result) + return result; + } + + writeOffset += sendLength; + bytesToSend -= sendLength; + } + + return _mapError(_i2cBus->endTransmission()); +} + +int8_t SFEBusArduinoI2C::readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, + const uint32_t numBytes) +{ + // Null pointer check. + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + // Null pointer check. + if (!devSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + // Null pointer check. + if (!data) + return SFE_BUS_E_NULL_DATA_BUFFER; + + SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; + + // Start transmission and send register address. + _i2cBus->beginTransmission(pAddr->devAddr); + _i2cBus->write(regAddr); + + // Repeat start condition, return if there's an error. + int8_t result = _mapError(_i2cBus->endTransmission(false)); + if (SFE_BUS_OK != result) + return result; + + uint32_t bytesLeftToRead = numBytes; + uint32_t readOffset = 0; + + while (bytesLeftToRead > 0) + { + // Limit readLength to the size of the I2C buffer to read in chunks. + uint8_t readLength = (bytesLeftToRead > _i2cBufferSize) ? _i2cBufferSize : bytesLeftToRead; + + // Request bytes, then read them into the data buffer. + uint32_t numRead = _i2cBus->requestFrom(pAddr->devAddr, readLength); + + if (numRead < readLength) + return SFE_BUS_W_UNDER_READ; + + if (_i2cBus->available()) + { + for (uint8_t i = 0; i < readLength; i++) + data[readOffset + i] = _i2cBus->read(); + } + else + return SFE_BUS_E_NO_RESPONSE; + + readOffset += readLength; + bytesLeftToRead -= readLength; + } + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, uint32_t numBytes) +{ + // Null pointer check. + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + // Null pointer check. + if (!devSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; + + uint32_t writeOffset = 0; + uint32_t bytesToSend = numBytes; + int8_t result = 0; + + // Start transmission. + _i2cBus->beginTransmission(pAddr->devAddr); + + while (bytesToSend > 0) + { + // Limit sendLength to the size of the I2C buffer to send in chunks. + uint8_t sendLength = (bytesToSend > _i2cBufferSize) ? _i2cBufferSize : bytesToSend; + + // Do the write thing. + for (uint8_t i = 0; i < sendLength; i++) + _i2cBus->write(data[writeOffset + i]); + + // If there's still more to send, send a repeat start. + if (bytesToSend > _i2cBufferSize) + { + result = _mapError(_i2cBus->endTransmission(false)); + if (SFE_BUS_OK != result) + return result; + } + + writeOffset += sendLength; + bytesToSend -= sendLength; + } + + return _mapError(_i2cBus->endTransmission()); +} + +int8_t SFEBusArduinoI2C::readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, uint32_t numBytes) +{ + // Null pointer check. + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + // Null pointer check. + if (!devSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + SFEBusDevSettingsI2C *pAddr = (SFEBusDevSettingsI2C *)devSettings; + + // Start transmission. + _i2cBus->beginTransmission(pAddr->devAddr); + + // Repeat start condition, return if there's an error. + int8_t result = _mapError(_i2cBus->endTransmission(false)); + if (SFE_BUS_OK != result) + return result; + + uint32_t bytesLeftToRead = numBytes; + uint32_t readOffset = 0; + + while (bytesLeftToRead > 0) + { + // Limit readLength to the size of the I2C buffer to read in chunks. + uint8_t readLength = (bytesLeftToRead > _i2cBufferSize) ? _i2cBufferSize : bytesLeftToRead; + + // Request bytes, then read them into the data buffer. + uint32_t numRead = _i2cBus->requestFrom(pAddr->devAddr, readLength); + + if (numRead < readLength) + return SFE_BUS_W_UNDER_READ; + + if (_i2cBus->available()) + { + for (uint8_t i = 0; i < readLength; i++) + data[readOffset + i] = _i2cBus->read(); + } + else + return SFE_BUS_E_NO_RESPONSE; + + readOffset += readLength; + bytesLeftToRead -= readLength; + } + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::setBufferSize(const uint32_t bufferSize) +{ + _i2cBufferSize = bufferSize; + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::getBufferSize(uint32_t *bufferSize) +{ + *bufferSize = _i2cBufferSize; + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::setBusFrequency(const uint32_t frequency) +{ + if (!_i2cBus) + return SFE_BUS_E_NULL_PTR; + + _i2cBus->setClock(frequency); + _busFrequency = frequency; + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::getBusFrequency(uint32_t *frequency) +{ + *frequency = _busFrequency; + + return SFE_BUS_OK; +} + +int8_t SFEBusArduinoI2C::_mapError(const uint8_t error) +{ + if (!error) + return SFE_BUS_OK; + else if (error == 1) + return SFE_BUS_E_DATA_TOO_LONG; + else if ((error == 2) || (error == 3)) + return SFE_BUS_E_NO_RESPONSE; + else if (error == 5) + return SFE_BUS_E_TIMEOUT; + else + return SFE_BUS_E_UNKNOWN; +} diff --git a/src/sfe_i2c_arduino.h b/src/sfe_i2c_arduino.h new file mode 100644 index 0000000..cad7824 --- /dev/null +++ b/src/sfe_i2c_arduino.h @@ -0,0 +1,108 @@ +/* + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics +*/ + +#pragma once + +#include +#include "sfe_i2c.h" +#include + +/// @brief An I2C communication bus implementation for Arduino +class SFEBusArduinoI2C : public SFEBusI2C +{ + public: + /// @brief Empty Constructor. + SFEBusArduinoI2C(void) : _i2cBus{nullptr}{}; + + /// @brief Passing in a TwoWire Object. + SFEBusArduinoI2C(TwoWire &wirePort) : _i2cBus{&wirePort}{}; + + /// @brief Begin the I2C object with the default Wire object. + /// @return 0 for success, negative for failure, positive for warning. + int8_t begin(void); + + /// @brief Begin the I2C object with the inputted Wire object. + /// @param wirePort I2C object to use for this bus. + /// @return 0 for success, negative for failure, positive for warning. + int8_t begin(TwoWire &wirePort); + + /// @brief Begin the I2C object with the inputted Wire object pointer. + /// @param i2cBus I2C bus pointer. + /// @return 0 for success, negative for failure, positive for warning. + int8_t begin(TwoWire *i2cBus); + + /// @brief End the I2C object. + /// @return 0 for success, negative for failure, positive for warning. + int8_t end(void); + + /// @brief Pings I2C device and looks for an ACK response. + /// @param devAddr Address to ping. + /// @return 0 for success, negative for failure, positive for warning. + int8_t ping(const uint8_t devAddr); + + /// @brief Pings I2C device and looks for an ACK response. + /// @param devSettings Settings object containing the address to ping. + /// @return 0 for success, negative for failure, positive for warning. + int8_t ping(const SFEBusDevSettings *devSettings); + + /// @brief Writes a number of bytes starting at the given register address. + /// @param devSettings I2C Settings object containing the address of the device. + /// @param regAddr The register address to write to. + /// @param data Data buffer to write to registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + int8_t writeRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, const uint8_t *data, const uint32_t numBytes); + + /// @brief Reads a number of bytes starting at the given register address. + /// @param devSettings I2C Settings object containing the address of the device. + /// @param regAddr The first register address to read from. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + int8_t readRegisterBytes(const SFEBusDevSettings *devSettings, const uint8_t regAddr, uint8_t *data, const uint32_t numBytes); + + /// @brief Writes a number of bytes to a device that doesn't use registers for communications. + /// @param devSettings I2C Settings object containing the address of the device. + /// @param data Data buffer to write to registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + int8_t writeBytes(const SFEBusDevSettings *devSettings, const uint8_t *data, const uint32_t numBytes); + + /// @brief Reads a number of bytes to a device that doesn't use registers for communications. + /// @param devSettings I2C Settings object containing the address of the device. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + int8_t readBytes(const SFEBusDevSettings *devSettings, uint8_t *data, const uint32_t numBytes); + + /// @brief Changes the I2C buffer size. + /// @param bufferSize New buffer size. + /// @return 0 for success, negative for failure, positive for warning. + int8_t setBufferSize(const uint32_t bufferSize); + + /// @brief Returns the I2C buffer size. + /// @param bufferSize Buffer to return the buffer... yep. + /// @return 0 for success, negative for failure, positive for warning. + int8_t getBufferSize(uint32_t *bufferSize); + + /// @brief Changes the Bus transmit frequency. + /// @param frequency New bus frequency. + /// @return 0 for success, negative for failure, positive for warning. + int8_t setBusFrequency(const uint32_t frequency); + + /// @brief Returns the Bus transmit frequency. + /// @param frequency Buffer to return the frequency. + /// @return 0 for success, negative for failure, positive for warning. + int8_t getBusFrequency(uint32_t *frequency); + + private: + TwoWire* _i2cBus; + + /// @brief Maps the TwoWire interface error scheme to the common bus error scheme. + /// @param error TwoWire error code. + /// @return 0 for success, negative for failure, positive for warning. + int8_t _mapError(const uint8_t error); +}; diff --git a/src/sfe_spi.cpp b/src/sfe_spi.cpp deleted file mode 100644 index f617ca9..0000000 --- a/src/sfe_spi.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* -sfe_spi.h - -The MIT License (MIT) - -Copyright (c) 2022 SparkFun Electronics -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: The -above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED -"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -#pragma once - -#include "sfe_spi.h" -#define SPI_READ 0x80 - -bool SfeSPI::init(SPIClass &spiPort, SPISettings &busSPISettings, uint8_t cs, bool bInit) -{ - - // if we don't have a SPI port already - if (!_spiPort) - { - _spiPort = &spiPort; - - if (bInit) - _spiPort->begin(); - } - - // SPI settings are needed for every transaction - _sfeSPISettings = busSPISettings; - - // The chip select pin can vary from platform to platform and project to project - // and so it must be given by the user. - if (!cs) - return false; - - _cs = cs; - - return true; -} - -bool SfeSPI::init(uint8_t cs, bool bInit) -{ - - // If the transaction settings are not provided by the user they are built here. - SPISettings spiSettings = SPISettings(3000000, MSB_FIRST, SPI_MODE3); - - // In addition of the port is not provided by the user, it defaults to SPI here. - return init(SPI, spiSettings, cs, bInit); -} - -bool SfeSPI::ping(uint8_t devAddr) -{ - return true; -} - -bool SfeSPI::writeRegisterByte(uint8_t devAddr, uint8_t devReg, uint8_t dataToWrite) -{ - - if (!_spiPort) - return false; - - // Apply settings - _spiPort->beginTransaction(_sfeSPISettings); - // Signal communication start - digitalWrite(_cs, LOW); - - _spiPort->transfer(devReg); - _spiPort->transfer(dataToWrite); - - // End communcation - digitalWrite(_cs, HIGH); - _spiPort->endTransaction(); - - return true; -} - -int SfeSPI::writeRegisterRegion(uint8_t devAddr, uint8_t devReg, const uint8_t *data, uint16_t length) -{ - - int i; - - // Apply settings - _spiPort->beginTransaction(_sfeSPISettings); - // Signal communication start - digitalWrite(_cs, LOW); - _spiPort->transfer(devReg); - - for (i = 0; i < length; i++) - { - _spiPort->transfer(*data++); - } - - // End communication - digitalWrite(_cs, HIGH); - _spiPort->endTransaction(); - return 0; -} - -int SfeSPI::readRegisterRegion(uint8_t devAddr, uint8_t devReg, uint8_t *data, uint16_t numBytes) -{ - if (!_spiPort) - return -1; - - int i; - - // Apply settings - _spiPort->beginTransaction(_sfeSPISettings); - // Signal communication start - digitalWrite(_cs, LOW); - // A leading "1" must be added to transfer with devRegister to indicate a "read" - devReg = (devReg | SPI_READ); - _spiPort->transfer(devReg); - - for (i = 0; i < numBytes; i++) - { - *data++ = _spiPort->transfer(0x00); - } - - // End transaction - digitalWrite(_cs, HIGH); - _spiPort->endTransaction(); - return 0; -} diff --git a/src/sfe_spi.h b/src/sfe_spi.h index 91c51ed..0b8082c 100644 --- a/src/sfe_spi.h +++ b/src/sfe_spi.h @@ -1,6 +1,4 @@ /* -sfe_spi.h - The MIT License (MIT) Copyright (c) 2022 SparkFun Electronics @@ -18,73 +16,21 @@ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -The following class specifies the behavior for communicating -over Serial Peripheral Interface (SPI). - */ #pragma once #include "sfe_bus.h" -#include -/* -The SfeSPI class defines behavior for SPI implementation based around the SPIClass class (SPI). -This is Arduino specific. -Paramaters like "devAddr" are kept although irrelevant to SPI due to the use of the abstract class -as interface, SfeBus. -*/ -class SfeSPI : public SfeBus +/// @brief An abstract interface for an SPI communication bus +class SFE_SPI : public SFEBus { public: - /// @brief Constructor - SfeSPI(void) : _spiPort(nullptr) {}; - - /// @brief Method sets up the required SPI settings. This function - /// provides a default SPI Port. - /// @param cs The chip select pin. - /// @param bInit This flag tracks whether the bus has been initialized. - /// @return True on successful execution. - bool init(uint8_t cs, bool bInit = false); - - /// @brief Method sets up the required SPI settings. - /// @param spiPort Port for SPI communcation. - /// @param busSPISettings Settings for speed, endianness, and spi mode of the SPI bus. - /// @param cs The chip select pin. - /// @param bInit This flag tracks whether the bus has been initialized. - /// @return True on successful execution. - bool init(SPIClass &spiPort, SPISettings &busSPISettings, uint8_t cs, bool bInit = false); - - /// @brief Is a device connected? The SPI ping is not relevant but is defined here to - /// keep consistency with I2C class i.e. provided for the abstract interface. - /// @param devAddr Address of the device (unused) - bool ping(uint8_t devAddr = 0); - - /// @brief Write a single byte to the given register - /// @param devAddr The device's I2C address (ignored). - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - bool writeRegisterByte(uint8_t devAddr, uint8_t devReg, uint8_t data); - - /// @brief Writes a number of bytes starting at the given register's address. - /// @param devAddr The device's I2C address (ignored). - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - int writeRegisterRegion(uint8_t devAddr, uint8_t devReg, const uint8_t *data, uint16_t length); - - /// @brief Reads a block of data from the given register. - /// @param devAddr The device's I2C address (ignored). - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - int readRegisterRegion(uint8_t addr, uint8_t reg, uint8_t *data, uint16_t numBytes); + /// @brief Initialize SPI parameters. + /// @param csPin Chip select pin. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t init(uint8_t csPin) = 0; - private: - SPIClass *_spiPort; - // Settings are used for every transaction. - SPISettings _sfeSPISettings; - uint8_t _cs; + protected: + uint8_t _csPin; }; diff --git a/src/sfe_spi_arduino.cpp b/src/sfe_spi_arduino.cpp new file mode 100644 index 0000000..11bdf2d --- /dev/null +++ b/src/sfe_spi_arduino.cpp @@ -0,0 +1,108 @@ +/* +The MIT License (MIT) + +Copyright (c) 2022 SparkFun Electronics +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: The +above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED +"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "sfe_spi_arduino.h" + +int8_t SFE_SPI_Arduino::init(uint8_t csPin) +{ + // Set default SPI port to SPI + return init(csPin, SPI); +} + +int8_t SFE_SPI_Arduino::init(uint8_t csPin, SPIClass &spiPort, bool beginBus) +{ + // Set default SPI settings to 1MHz, MSB first, and Mode 0 + return init(csPin, SPISettings(1000000, MSBFIRST, SPI_MODE0), spiPort); +} + +int8_t SFE_SPI_Arduino::init(uint8_t csPin, SPISettings spiSettings, SPIClass &spiPort, bool beginBus) +{ + // Store provided values + _csPin = csPin; + _spiSettings = spiSettings; + _spiPort = &spiPort; + + // Null pointer check + if (!_spiPort) + return SFE_BUS_E_NULL_PTR; + + // Begin bus if requested + if(beginBus) + _spiPort->begin(); + + // Ensure CS pin is set to output mode and driving high for now + // Writing high before setting mode helps ensure signal stays high + digitalWrite(_csPin, HIGH); + pinMode(_csPin, OUTPUT); + + // Success + return SFE_BUS_OK; +} + +int8_t SFE_SPI_Arduino::writeRegisters(uint8_t regAddr, const uint8_t *data, uint8_t numBytes) +{ + // Null pointer check + if (!_spiPort) + return SFE_BUS_E_NULL_PTR; + + // Start SPI message + _spiPort->beginTransaction(_spiSettings); + digitalWrite(_csPin, LOW); + + // Write register address with write bit + _spiPort->transfer(regAddr & 0x7F); + + // Write data + _spiPort->transfer((void*) data, numBytes); + + // End SPI message + digitalWrite(_csPin, HIGH); + _spiPort->endTransaction(); + + // Success + return SFE_BUS_OK; +} + +int8_t SFE_SPI_Arduino::readRegisters(uint8_t regAddr, uint8_t *data, uint8_t numBytes) +{ + // Null pointer check + if (!_spiPort) + return SFE_BUS_E_NULL_PTR; + + // Start SPI message + _spiPort->beginTransaction(_spiSettings); + digitalWrite(_csPin, LOW); + + // Write register address with read bit + _spiPort->transfer(regAddr | 0x80); + + // Read data into buffer one byte at a time + for(uint16_t i = 0; i < numBytes; i++) + { + data[i] = _spiPort->transfer(0x00); + } + + // End SPI message + digitalWrite(_csPin, HIGH); + _spiPort->endTransaction(); + + // Success + return SFE_BUS_OK; +} diff --git a/src/sfe_spi_arduino.h b/src/sfe_spi_arduino.h new file mode 100644 index 0000000..6992771 --- /dev/null +++ b/src/sfe_spi_arduino.h @@ -0,0 +1,69 @@ +/* +The MIT License (MIT) + +Copyright (c) 2022 SparkFun Electronics +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: The +above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED +"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#pragma once + + +#include "sfe_spi.h" +#include +#include + +/// @brief An SPI communication bus implementation for Arduino +class SFE_SPI_Arduino : public SFE_SPI +{ + public: + /// @brief Initialize SPI parameters. + /// @param csPin Chip select pin. + /// @return 0 for success, negative for failure, positive for warning. + int8_t init(uint8_t csPin); + + /// @brief Initialize SPI parameters. + /// @param csPin Chip select pin. + /// @param spiPort Arduino SPI port to use. + /// @param beginBus Whether to initialize the I2C bus. + /// @return 0 for success, negative for failure, positive for warning. + int8_t init(uint8_t csPin, SPIClass &spiPort, bool beginBus = false); + + /// @brief Initialize SPI parameters. + /// @param csPin Chip select pin. + /// @param spiSettings Arduino SPISettings to use for transactions. + /// @param spiPort Arduino SPI port to use. + /// @param beginBus Whether to initialize the I2C bus. + /// @return 0 for success, negative for failure, positive for warning. + int8_t init(uint8_t csPin, SPISettings spiSettings, SPIClass &spiPort, bool beginBus = false); + + /// @brief Writes a number of bytes starting at the given register address. + /// @param regAddr The first register address to write to. + /// @param data Data buffer to write to registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + int8_t writeRegisters(uint8_t regAddr, const uint8_t *data, uint8_t numBytes); + + /// @brief Reads a number of bytes starting at the given register address. + /// @param regAddr The first register address to read from. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + int8_t readRegisters(uint8_t regAddr, uint8_t *data, uint8_t numBytes); + + protected: + SPIClass* _spiPort; + SPISettings _spiSettings; +}; diff --git a/test/Example_BME280/BME280_SFEBus.cpp b/test/Example_BME280/BME280_SFEBus.cpp new file mode 100644 index 0000000..d6ae5d1 --- /dev/null +++ b/test/Example_BME280/BME280_SFEBus.cpp @@ -0,0 +1,89 @@ +/* + SPDX-License-Identifier: MIT + + Copyright (c) 2023 SparkFun Electronics +*/ + +#include "BME280_SFEBus.h" + +BME280SFEBus::BME280SFEBus(): _i2cBus{nullptr} +{ + settings.commInterface = I2C_MODE; + + settings.I2CAddress = 0x77; + + settings.runMode = 3; + settings.tStandby = 0; + settings.filter = 0; + settings.tempOverSample = 1; + settings.pressOverSample = 1; + settings.humidOverSample = 1; + settings.tempCorrection = 0.f; +} + +void BME280SFEBus::setBus(SFEBusDevSettingsI2C *theDevSettings, SFEBusArduinoI2C *theBus) +{ + _i2cBus = theBus; + _devSettings = *theDevSettings; +} + + +bool BME280SFEBus::beginI2C(SFEBusArduinoI2C *theBus) +{ + if (theBus == nullptr) + theBus = new SFEBusArduinoI2C(); + + if (!theBus) + return false; + + _devSettings.devAddr = settings.I2CAddress; + + setBus(&_devSettings, theBus); + + + + uint8_t chipID = begin(); + + if(chipID == 0x58) return(true); //Begin normal init with these settings. Should return chip ID of 0x58 for BMP + if(chipID == 0x60) return(true); //Begin normal init with these settings. Should return chip ID of 0x60 for BME + return(false); +} + +float BME280SFEBus::readTempC() +{ + // Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC. + // t_fine carries fine temperature as global value + + //get the reading (adc_T); + uint8_t buffer[3]; + readRegisterRegion(buffer, BME280_TEMPERATURE_MSB_REG, 3); + int32_t adc_T = ((uint32_t)buffer[0] << 12) | ((uint32_t)buffer[1] << 4) | ((buffer[2] >> 4) & 0x0F); + + //By datasheet, calibrate + int64_t var1, var2; + + var1 = ((((adc_T>>3) - ((int32_t)calibration.dig_T1<<1))) * ((int32_t)calibration.dig_T2)) >> 11; + var2 = (((((adc_T>>4) - ((int32_t)calibration.dig_T1)) * ((adc_T>>4) - ((int32_t)calibration.dig_T1))) >> 12) * + ((int32_t)calibration.dig_T3)) >> 14; + t_fine = var1 + var2; + float output = (t_fine * 5 + 128) >> 8; + + output = output / 100 + settings.tempCorrection; + + return output; +} + +bool BME280SFEBus::readRegisterFull(uint8_t regAddr, uint8_t* data, uint32_t numBytes) +{ + _i2cBus->readRegisterBytes((SFEBusDevSettings*)&_devSettings, regAddr, data, numBytes); + + return true; +} + +uint8_t BME280SFEBus::readRegister(uint8_t regAddr) +{ + uint8_t regVal; + readRegisterFull(regAddr, ®Val, 1); + + return regVal; +} \ No newline at end of file diff --git a/test/Example_BME280/BME280_SFEBus.h b/test/Example_BME280/BME280_SFEBus.h new file mode 100644 index 0000000..5c4e4d1 --- /dev/null +++ b/test/Example_BME280/BME280_SFEBus.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +class BME280SFEBus: public BME280 +{ + public: + BME280SFEBus(); + + void setBus(SFEBusDevSettingsI2C *theDevSettings, SFEBusArduinoI2C *theBus); + + bool beginI2C(SFEBusArduinoI2C *theBus = nullptr); + + float readTempC(); + + private: + bool readRegisterFull(uint8_t regAddr, uint8_t* data, uint32_t numBytes); + uint8_t BME280SFEBus::readRegister(uint8_t regAddr); + SFEBusArduinoI2C *_i2cBus; + SFEBusDevSettingsI2C _devSettings; +}; \ No newline at end of file diff --git a/test/Example_BME280/Example_BME280.ino b/test/Example_BME280/Example_BME280.ino new file mode 100644 index 0000000..18cc54e --- /dev/null +++ b/test/Example_BME280/Example_BME280.ino @@ -0,0 +1,32 @@ +#include +#include "BME280_SFEBus.h" + +SFEBusArduinoI2C myBus; +SFEBusDevSettingsI2C myBusSettings; +BME280SFEBus mySensor; + +void setup() { + Serial.begin(115200); + while(!Serial) {delay(100);}; + Serial.println("Connected! Testing Bus Arduino I2C library with a modified BME280 library."); + + // Start the bus. + int8_t result = myBus.begin(); + + // Start the sensor. + + if(!mySensor.beginI2C(&myBus)) + { + Serial.println("The sensor did not respond. Please check wiring. Spinning..."); + while(1); + } + +} + +void loop() { + Serial.print("Temp: "); + Serial.print(mySensor.readTempC(), 2); + Serial.println(" degC"); + + delay(100); +} \ No newline at end of file diff --git a/test/Example_OPT4048/Example_OPT4048.ino b/test/Example_OPT4048/Example_OPT4048.ino new file mode 100644 index 0000000..b195e67 --- /dev/null +++ b/test/Example_OPT4048/Example_OPT4048.ino @@ -0,0 +1,37 @@ +#include +#include "OPT4048_SFEBus.h" + +SFEBusArduinoI2C myBus; +sfeOPT4048ArduinoI2C myColor; + +void setup() +{ + Serial.begin(115200); + while(!Serial){delay(100);}; + Serial.println("OPT4048 Example using SFE Bus"); + + myBus.begin(); + + if(!myColor.begin(&myBus)) + { + Serial.println("OPT4048 not detected - check wiring or that your I2C address is correct"); + while(1); + } + + bool setup = myColor.setBasicSetup(); + + Serial.print("Setup returned: "); + Serial.println(setup); + + Serial.println("Ready to go!"); +} + +void loop() +{ + Serial.print("CIEx: "); + Serial.print(myColor.getCIEx()); + Serial.print(" CIEy: "); + Serial.println(myColor.getCIEy()); + + delay(200); +} \ No newline at end of file diff --git a/test/Example_OPT4048/OPT4048_Registers.h b/test/Example_OPT4048/OPT4048_Registers.h new file mode 100644 index 0000000..91dd2c3 --- /dev/null +++ b/test/Example_OPT4048/OPT4048_Registers.h @@ -0,0 +1,306 @@ +/* +OPT4048_Registers.h + +SparkFun Tristimulus Color Sensor - OPT4048 + +Qwiic 1x1 +* https://www.sparkfun.com/products/ +Qwiic Mini +* https://www.sparkfun.com/products/ + +Repository: +* https://github.com/sparkfun/SparkFun_OPT4048_Arduino_Library + +The MIT License (MIT) + +Copyright (c) 2022 SparkFun Electronics +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: The +above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED +"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +License(http://opensource.org/licenses/MIT). + +The following file defines the registers for the opt4048 IC. +*/ + +#pragma once +#include "Arduino.h" +#include + +#define OPT4048_ADDR_HIGH 0x45 +#define OPT4048_ADDR_LOW 0x44 // DEFAULT +#define OPT4048_ADDR_DEF 0x44 // DEFAULT +#define OPT4048_ADDR_SDA 0x46 +#define OPT4048_ADDR_SCL 0x45 + +#define OPT4048_DEVICE_ID 0x2084 + +// Setttings - use find in your editor for to search for "settings" +// to scroll through all predefined setting types. +/// @brief Range Settings found in Register 0x0A +typedef enum +{ + RANGE_2KLUX2 = 0x00, + RANGE_4KLUX5, + RANGE_9LUX, + RANGE_18LUX, + RANGE_36LUX, + RANGE_72LUX, + RANGE_144LUX, + RANGE_AUTO = 0x0C +} opt4048_range_t; + +/// @brief Converstion Settings for Register 0x0A +typedef enum +{ + CONVERSION_TIME_600US = 0x00, + CONVERSION_TIME_1MS, + CONVERSION_TIME_1MS8, + CONVERSION_TIME_3MS4, + CONVERSION_TIME_6MS5, + CONVERSION_TIME_12MS7, + CONVERSION_TIME_25MS, + CONVERSION_TIME_50MS, + CONVERSION_TIME_100MS, + CONVERSION_TIME_200MS, + CONVERSION_TIME_400MS, + CONVERSION_TIME_800MS + +} opt4048_conversion_time_t; + +/// @brief Operation mode settings found in Register 0x0A +typedef enum +{ + OPERATION_MODE_POWER_DOWN = 0x00, + OPERATION_MODE_AUTO_ONE_SHOT, + OPERATION_MODE_ONE_SHOT, + OPERATION_MODE_CONTINUOUS + +} opt4048_operation_mode_t; + +/// @brief Fault count settings found in Register 0x0A +typedef enum +{ + FAULT_COUNT_1 = 0x00, + FAULT_COUNT_2, + FAULT_COUNT_3, + FAULT_COUNT_8 +} opt4048_fault_count_t; + +/// @brief Threshold channel settings found in Register 0x0B +typedef enum +{ + THRESH_CHANNEL_CH0 = 0x00, + THRESH_CHANNEL_CH1, + THRESH_CHANNEL_CH2, + THRESH_CHANNEL_CH3 +} opt4048_threshold_channel_t; + +/// @brief Interrupt settings found in Register 0x0B +typedef enum +{ + INT_SMBUS_ALERT = 0x00, + INT_DR_NEXT_CHANNEL, + INT_DR_ALL_CHANNELS = 0x03 +} opt4048_int_cfg_t; + +/// @brief OPT4048 Register for Exponent and Result (MSB) for Channel 0 +#define SFE_OPT4048_REGISTER_EXP_RES_CH0 0x00 +typedef union { + struct + { + uint16_t result_msb_ch0 : 12; + uint8_t exponent_ch0 : 4; + }; + uint16_t word; + +} opt4048_reg_exp_res_ch0_t; + +/// @brief OPT4048 Register for Result (LSB), Counter, and CRC for Channel 0 +#define SFE_OPT4048_REGISTER_RES_CNT_CRC_CH0 0x01 +typedef union { + struct + { + uint8_t crc_ch0 : 4; + uint8_t counter_ch0 : 4; + uint8_t result_lsb_ch0 : 8; + }; + uint16_t word; + +} opt4048_reg_res_cnt_crc_ch0_t; + +/// @brief OPT4048 Register for Exponent and Result (MSB) for Channel 1 +#define SFE_OPT4048_REGISTER_EXP_RES_CH1 0x02 +typedef union { + struct + { + uint16_t result_msb_ch1 : 12; + uint8_t exponent_ch1 : 4; + }; + uint16_t word; + +} opt4048_reg_exp_res_ch1_t; + +/// @brief OPT4048 Register for Result (LSB), Counter, and CRC for Channel 1 +#define SFE_OPT4048_REGISTER_RES_CNT_CRC_CH1 0x03 +typedef union { + struct + { + uint8_t crc_ch1 : 4; + uint8_t counter_ch1 : 4; + uint8_t result_lsb_ch1 : 8; + }; + uint16_t word; + +} opt4048_reg_res_cnt_crc_ch1_t; + +/// @brief OPT4048 Register for Exponent and Result (MSB) for Channel 2 +#define SFE_OPT4048_REGISTER_EXP_RES_CH2 0x04 +typedef union { + struct + { + uint16_t result_msb_ch2 : 12; + uint8_t exponent_ch2 : 4; + }; + uint16_t word; + +} opt4048_reg_exp_res_ch2_t; + +/// @brief OPT4048 Register for Result (LSB), Counter, and CRC for Channel 2 +#define SFE_OPT4048_REGISTER_RES_CNT_CRC_CH2 0x05 +typedef union { + struct + { + uint8_t crc_ch2 : 4; + uint8_t counter_ch2 : 4; + uint8_t result_lsb_ch2 : 8; + }; + uint16_t word; + +} opt4048_reg_res_cnt_crc_ch2_t; + +/// @brief OPT4048 Register for Exponent and Result (MSB) for Channel 3 +#define SFE_OPT4048_REGISTER_EXP_RES_CH3 0x06 +typedef union { + struct + { + uint16_t result_msb_ch3 : 12; + uint8_t exponent_ch3 : 4; + }; + uint16_t word; + +} opt4048_reg_exp_res_ch3_t; + +/// @brief OPT4048 Register for Result (LSB), Counter, and CRC for Channel 3 +#define SFE_OPT4048_REGISTER_RES_CNT_CRC_CH3 0x07 +typedef union { + struct + { + uint8_t crc_ch3 : 4; + uint8_t counter_ch3 : 4; + uint8_t result_lsb_ch3 : 8; + }; + uint16_t word; + +} opt4048_reg_res_cnt_crc_ch3_t; + +/// @brief OPT4048 Register for Threshold Exponent and Result - Low +#define SFE_OPT4048_REGISTER_THRESH_L_EXP_RES 0x08 +typedef union { + struct + { + uint16_t thresh_result : 12; + uint8_t thresh_exp : 4; + }; + uint16_t word; + +} opt4048_reg_thresh_exp_res_low_t; + +/// @brief OPT4048 Register for Threshold Exponent and Threshold Result - High +#define SFE_OPT4048_REGISTER_THRESH_H_EXP_RES 0x09 +typedef union { + struct + { + uint16_t thresh_result : 12; + uint8_t thresh_exp : 4; + }; + uint16_t word; + +} opt4048_reg_thresh_exp_res_high_t; + +/// @brief OPT4048 Register that controls the main functions of the device. +#define SFE_OPT4048_REGISTER_CONTROL 0x0A + + +typedef union { + struct + { + uint16_t fault_count : 2; + uint16_t int_pol : 1; + uint16_t latch : 1; + uint16_t op_mode : 2; + uint16_t conversion_time : 4; + uint16_t range : 4; + uint16_t reserved : 1; + uint16_t qwake : 1; + }; + uint16_t word; +} opt4048_reg_control_t; + + +/// @brief OPT4047 Register with settings for the interrupt pin. +#define SFE_OPT4048_REGISTER_INT_CONTROL 0x0B +typedef union { + struct + { + uint16_t i2c_burst : 1; + uint16_t reserved_two : 1; + uint16_t int_cfg : 2; + uint16_t int_dir : 1; + uint16_t threshold_ch_sel : 2; + uint16_t reserved_one : 9; + }; + uint16_t word; + +} opt4048_reg_int_control_t; + + +/// @brief OPT4048 register containing various status flags. +#define SFE_OPT4048_REGISTER_FLAGS 0x0C +typedef union { + struct + { + uint8_t flag_low : 1; + uint8_t flag_high : 1; + uint8_t conv_ready_flag : 1; + uint8_t overload_flag : 1; + uint16_t reserved : 12; + }; + uint16_t word; + +} opt4048_reg_flags_t; + +/// @brief OPT4048 Register containing the device ID. +#define SFE_OPT4048_REGISTER_DEVICE_ID 0x11 +typedef union { + struct + { + uint16_t DIDH : 12; + uint16_t DIDL : 2; + uint16_t reserved : 2; + }; + uint16_t word; + +} opt4048_reg_device_id_t; + diff --git a/test/Example_OPT4048/OPT4048_SFEBus.h b/test/Example_OPT4048/OPT4048_SFEBus.h new file mode 100644 index 0000000..605752d --- /dev/null +++ b/test/Example_OPT4048/OPT4048_SFEBus.h @@ -0,0 +1,1195 @@ +#pragma once + +// #include // REMOVE ME LATER, just for squiggle removal. +// #include "../../src/SparkFun_Toolkit.h" +#include +#include "OPT4048_Registers.h" +#include + +/// @brief Struct used to store the color data from the OPT4048. +typedef struct +{ + uint32_t red; + uint32_t green; + uint32_t blue; + uint32_t white; + uint8_t counterR; // Sample counter + uint8_t counterG; + uint8_t counterB; + uint8_t counterW; + uint8_t CRCR; + uint8_t CRCG; + uint8_t CRCB; + uint8_t CRCW; + +} sfe_color_t; + +/// @brief Union used to re-calculate the CRC for optional double check. +typedef union { + struct + { + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + }; + uint8_t byte; +} crcBits; + +/// @brief Union used to re-calculate the CRC for optional double check. +typedef union { + struct + { + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + }; + uint8_t byte; +} exponBits; + +/// @brief Union used to re-calculate the CRC for optional double check. +typedef union { + struct + { + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; + uint8_t bit8 : 1; + uint8_t bit9 : 1; + uint8_t bit10 : 1; + uint8_t bit11 : 1; + uint8_t bit12 : 1; + uint8_t bit13 : 1; + uint8_t bit14 : 1; + uint8_t bit15 : 1; + uint8_t bit16 : 1; + uint8_t bit17 : 1; + uint8_t bit18 : 1; + uint8_t bit19 : 1; + }; + uint32_t word; +} mantissaBits; + +#define OPT_MATRIX_ROWS 4 +#define OPT_MATRIX_COLS 4 + +template +class OPT4048SFEBusBase +{ + public: + OPT4048SFEBusBase() : _sfeBus{nullptr}, _devSettings{nullptr} {}; + + bool begin(sfeBusDevice *theBus, sfeBusDeviceSettings *devSettings, uint8_t deviceAddress = OPT4048_ADDR_DEF) + { + setCommunicationBus(theBus, devSettings); + return begin(deviceAddress); + } + + bool begin(sfeBusDevice *theBus, uint8_t deviceAddress = OPT4048_ADDR_DEF) + { + setCommunicationBus(theBus); + return begin(deviceAddress); + } + + bool begin(uint8_t deviceAddress = OPT4048_ADDR_DEF) + { + if(!_sfeBus) + setCommunicationBus(); + + setDeviceAddress(deviceAddress); + + _sfeBus->begin(); + + return init(); + } + + /// @brief Pings the device to see if it's there. + /// @return true on successful execution. + bool init(void) + { + if(SFE_BUS_OK != _sfeBus->ping(_devSettings)) + return false; + return isConnected(); + } + + /// @brief Checks that the bus is connected with the OPT4048 by checking + /// it's unique ID. + /// @return True on successful execution. + bool isConnected(void) + { + return (OPT4048_DEVICE_ID == getDeviceID()); + } + + /// @brief Retrieves the the device's ID: 0x24 for the OPT4048. + /// @return Returns the unique ID. + uint16_t getDeviceID(void) + { + uint8_t buff[2]; + uint16_t uniqueId; + opt4048_reg_device_id_t idReg; + + int8_t result = readRegisterRegion(SFE_OPT4048_REGISTER_DEVICE_ID, buff); + if(SFE_BUS_OK != result) + return 0; + + idReg.word = (buff[0] << 8) | buff[1]; + + uniqueId = (idReg.DIDH << 2) | idReg.DIDL; + + return uniqueId; + } + + /// @brief Sets the pointer to the data bus for read and writes. + /// @param theBus This parameter sets the bus. + /// @param deviceSettings The bus device settings. + /// @return 0 for success, negative for failure, positive for warning. + int8_t setCommunicationBus(sfeBusDevice *theBus, sfeBusDeviceSettings *deviceSettings) + { + int8_t result = setCommunicationBus(theBus); + if(SFE_BUS_OK != result) + return result; + + result = setCommunicationDevSettings(deviceSettings); + if(SFE_BUS_OK != result) + return result; + + return SFE_BUS_OK; + } + + /// @brief Sets the pointer to the data bus for read and writes. + /// @param theBus This parameter sets the hardware bus. + /// @return 0 for success, negative for failure, positive for warning. + int8_t setCommunicationBus(sfeBusDevice *theBus = nullptr) + { + if(theBus == nullptr) + theBus = new sfeBusDevice(); + + if(!theBus) + return SFE_BUS_E_NULL_PTR; + + _sfeBus = theBus; + + return SFE_BUS_OK; + } + + /// @brief Sets the pointer to the data bus for read and writes. + /// @param deviceSettings This parameter sets the bus' device settings. + /// @return 0 for success, negative for failure, positive for warning. + int8_t setCommunicationDevSettings(sfeBusDeviceSettings *deviceSettings = nullptr) + { + if(deviceSettings == nullptr) + deviceSettings = new sfeBusDeviceSettings(); + + if(!deviceSettings) + return SFE_BUS_E_NULL_DEV_SETTINGS; + + _devSettings = deviceSettings; + + return SFE_BUS_OK; + } + + /// @brief Sets the pointer to the data bus for read and writes. + /// @param deviceAddress This parameter sets the device address. + /// @return 0 for success, negative for failure, positive for warning. + int8_t setDeviceAddress(const uint8_t deviceAddress) + { + int8_t result = SFE_BUS_OK; + + if(!_devSettings) + result = setCommunicationDevSettings(); + + if(SFE_BUS_OK != result) + return result; + + _devSettings->devAddr = deviceAddress; + + return SFE_BUS_OK; + } + + /// @brief Writes to the data to the given register using the hardware data bus. + /// @param offset The register to write to. + /// @param data The data to write to the register. + /// @param length The number of writes + /// @return 0 for success, negative for failure, positive for warning. + int8_t writeRegisterRegion(const uint8_t offset, const uint8_t *data, const uint16_t length = 2) + { + return _sfeBus->writeRegisterBytes((SFEBusDevSettings*)_devSettings, offset, data, length); + } + + /// @brief Reads data from the specified register using the set data bas. + /// @param offset The register to read from. + /// @param data The pointer to the value to store the value. + /// @param length The number of reads + /// @return 0 for success, negative for failure, positive for warning. + int8_t readRegisterRegion(const uint8_t offset, uint8_t *data, const uint16_t numBytes = 2) + { + return _sfeBus->readRegisterBytes((SFEBusDevSettings*)_devSettings, offset, data, numBytes); + } + + ///////////////////////////////////////////////////////////////////Device Settings + + /// @brief Sets the minimum of settings to get the board running. + /// @return True on successful execution. + bool setBasicSetup(void) + { + int8_t result = SFE_BUS_OK; + + Serial.println("Entered setup."); + result = setRange(RANGE_36LUX); + if(SFE_BUS_OK != result) + return false; + Serial.println("Set range."); + + result = setConversionTime(CONVERSION_TIME_200MS); + if(SFE_BUS_OK != result) + return false; + Serial.println("Set conversion time."); + + result = setOperationMode(OPERATION_MODE_CONTINUOUS); + if(SFE_BUS_OK != result) + return false; + + Serial.println("Set operation mode."); + + return true; + } + + /// @brief Sets the OPT4048's quick wake setting. + /// @param enable The range to set the device to. + /// @return True on successful execution. + bool setQwake(const bool enable = true) + { + uint8_t buff[2]; + int8_t result; + opt4048_reg_control_t controlReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + if(SFE_BUS_OK != result) + return false; + + controlReg.word = (buff[0] << 8) | buff[1]; + controlReg.qwake = (uint8_t)enable; + + buff[0] = controlReg.word >> 8; + buff[1] = controlReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + if(SFE_BUS_OK != result) + return false; + + return true; + } + + /// @brief Retrieves the quick wake status of the OPT4048. + /// @return status of qwake setting. + bool getQwake(void) + { + uint8_t buff[2]; + int8_t result = SFE_BUS_OK; + opt4048_reg_control_t controlReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + if(SFE_BUS_OK != result) + return false; + + controlReg.word = (buff[0] << 8) | buff[1]; + + return (controlReg.qwake ? true : false); + } + + /// @brief Sets the OPT4048's effective sensing range which will effect its resolution. + /// @param range The range to set the device to. + /// RANGE_2KLUX2, + /// RANGE_4KLUX5, + /// RANGE_9LUX, + /// RANGE_18LUX, + /// RANGE_36LUX, + /// RANGE_72LUX, + /// RANGE_144LUX, + /// RANGE_AUTO + /// @return 0 for success, negative for failure, positive for warning. + int8_t setRange(const opt4048_range_t range) + { + uint8_t buff[2]; + int8_t result = SFE_BUS_OK; + opt4048_reg_control_t controlReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + Serial.print("Read result was: "); + Serial.println(result); + if(SFE_BUS_OK != result) + return result; + controlReg.word = (buff[0] << 8) | buff[1]; + controlReg.range = range; + + buff[0] = controlReg.word >> 8; + buff[1] = controlReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + Serial.print("Write result was: "); + Serial.println(result); + + return result; + } + + /// @brief Retrieves the light range in lux of the OPT4048. + /// @return Range of lux able to be measured. + opt4048_range_t getRange(void) + { + uint8_t buff[2]; + opt4048_reg_control_t controlReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + controlReg.word = (buff[0] << 8) | buff[1]; + + return (opt4048_range_t)controlReg.range; + } + + /// @brief Sets the OPT4048's conversion time which will effect its resolution. Longer conversion time + /// will result in higher resolution. + /// @param time The conversion time to set the device to. Possible values: + /// CONVERSION_TIME_600US, + /// CONVERSION_TIME_1MS, + /// CONVERSION_TIME_1MS8, + /// CONVERSION_TIME_3MS4, + /// CONVERSION_TIME_6MS5, + /// CONVERSION_TIME_12MS7, + /// CONVERSION_TIME_25MS, + /// CONVERSION_TIME_50MS, + /// CONVERSION_TIME_100MS, + /// CONVERSION_TIME_200MS, + /// CONVERSION_TIME_400MS, + /// CONVERSION_TIME_800MS + /// @return 0 for success, negative for failure, positive for warning. + int8_t setConversionTime(const opt4048_conversion_time_t time) + { + uint8_t buff[2]; + int8_t result = SFE_BUS_OK; + opt4048_reg_control_t controlReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + if (SFE_BUS_OK != result) + return result; + + controlReg.word = buff[0] << 8 | buff[1]; + + controlReg.conversion_time = time; + + buff[0] = controlReg.word >> 8; + buff[1] = controlReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + return result; + } + + /// @brief Retrieves the conversion time used for the ADC. + /// @return The OPT4048 conversion time. + opt4048_conversion_time_t getConversionTime(void) + { + uint8_t buff[2]; + opt4048_reg_control_t controlReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + controlReg.word = buff[0] << 8 | buff[1]; + + return (opt4048_conversion_time_t)controlReg.conversion_time; + + } + + + /// @brief Sets the OPT4048's operation mode. + /// @param mode The mode to set the device to. Possible Values: + /// OPERATION_MODE_POWER_DOWN, + /// OPERATION_MODE_AUTO_ONE_SHOT, + /// OPERATION_MODE_ONE_SHOT, + /// OPERATION_MODE_CONTINUOUS + /// @return 0 for success, negative for failure, positive for warning. + int8_t setOperationMode(const opt4048_operation_mode_t mode) + { + uint8_t buff[2]; + int8_t result = SFE_BUS_OK; + opt4048_reg_control_t controlReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + if (SFE_BUS_OK != result) + return result; + + controlReg.word = buff[0] << 8 | buff[1]; + + controlReg.op_mode = mode; + + buff[0] = controlReg.word >> 8; + buff[1] = controlReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + return result; + } + + /// @brief Retrieves the set operation mode. + /// @return The OPT4048 conversion time. + opt4048_operation_mode_t getOperationMode(void) + { + uint8_t buff[2]; + opt4048_reg_control_t controlReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + controlReg.word = buff[0] << 8 | buff[1]; + + return (opt4048_operation_mode_t)controlReg.op_mode; + } + + /// @brief Changes the behavior of the interrupt from pin to latch. + /// @param enable True to enable, false to disable. + /// @return True on successful execution. + bool setIntLatch(const bool enable = true) + { + uint8_t buff[2]; + int8_t result; + opt4048_reg_control_t controlReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + if (SFE_BUS_OK != result) + return false; + + controlReg.word = buff[0] << 8 | buff[1]; + + controlReg.latch = enable; + + buff[0] = controlReg.word >> 8; + buff[1] = controlReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + if (SFE_BUS_OK != result) + return false; + + return true; + } + + /// @brief Checks if the interrupt is set to pulse or latch. + /// @return True if set to latch, false if not. + bool getIntLatch(void) + { + uint8_t buff[2]; + opt4048_reg_control_t controlReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + controlReg.word = buff[0] << 8 | buff[1]; + + return (bool)controlReg.latch; + } + + /// @brief Sets the OPT4048's interrupt polarity. + /// @param setHigh True to enable, false to disable. + /// @return True on successful execution. + bool setIntActiveHigh(const bool setHigh = true) + { + uint8_t buff[2]; + int8_t result; + opt4048_reg_control_t intReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + if (SFE_BUS_OK != result) + return false; + + intReg.word = buff[0] << 8 | buff[1]; + + intReg.int_pol = (uint8_t)setHigh; + + buff[0] = intReg.word >> 8; + buff[1] = intReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + if (SFE_BUS_OK != result) + return false; + + return true; + } + + /// @brief Gets the OPT4048's interrupt polarity. + /// @return True if active high. + bool getIntActiveHigh(void) + { + uint8_t buff[2]; + opt4048_reg_control_t intReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + intReg.word = buff[0] << 8 | buff[1]; + + return (bool)intReg.int_pol; + } + + /// @brief Sets the number of faults (light values over or under) before an interrupt is triggered. + /// @param count The number of faults to trigger an interrupt + /// FAULT_COUNT_1, + /// FAULT_COUNT_2, + /// FAULT_COUNT_4, + /// FAULT_COUNT_8 + /// @return True on successful execution. + bool setFaultCount(const opt4048_fault_count_t *count) + { + uint8_t buff[2]; + int8_t result; + opt4048_reg_control_t controlReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + if (SFE_BUS_OK != result) + return false; + + controlReg.word = buff[0] << 8 | buff[1]; + + controlReg.fault_count = *count; + + buff[0] = controlReg.word >> 8; + buff[1] = controlReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + if (SFE_BUS_OK != result) + return false; + + return true; + } + + /// @brief Retrieves the number of faults (light values over or under) before + /// an interrupt is triggered. + /// @return The fault count. + opt4048_fault_count_t getFaultCount(void) + { + uint8_t buff[2]; + opt4048_reg_control_t controlReg; + controlReg.fault_count = 0; + + readRegisterRegion(SFE_OPT4048_REGISTER_CONTROL, buff); + + controlReg.word = buff[0] << 8 | buff[1]; + + return ((opt4048_fault_count_t)controlReg.fault_count); + } + + /// @brief Sets the channel for threshold logic + /// @param channel The channel to set the threshold logic to. + /// THRESH_CHANNEL_CH0, + /// THRESH_CHANNEL_CH1, + /// THRESH_CHANNEL_CH2, + /// THRESH_CHANNEL_CH3 + /// @return True on successful execution. + bool setThresholdChannel(const opt4048_threshold_channel_t *channel) + { + uint8_t buff[2]; + int8_t result; + opt4048_reg_int_control_t intReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + if (SFE_BUS_OK != result) + return false; + + intReg.word = buff[0] << 8 | buff[1]; + + intReg.threshold_ch_sel = *channel; + + buff[0] = intReg.word >> 8; + buff[1] = intReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + if (SFE_BUS_OK != result) + return false; + + return true; + } + + /// @brief Retrives the threshold channel. + /// @return THe selected channel. + opt4048_threshold_channel_t getThresholdChannel(void) + { + uint8_t buff[2]; + opt4048_reg_int_control_t intReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + intReg.word = buff[0] << 8 | buff[1]; + + return ((opt4048_threshold_channel_t)intReg.threshold_ch_sel); + } + + /// @brief Sets the Lux High Value threshold. + /// @param thresh The value in float + /// @return Returns the high threshold. + bool setThresholdHigh(const float *thresh) + { + if (*thresh < 2.15 || *thresh > 144000) + return false; + + uint8_t buff[2]; + int8_t result; + // opt4048_reg_thresh_exp_res_high_t threshReg; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_THRESH_H_EXP_RES, buff); + + if (SFE_BUS_OK != result) + return false; + + return true; + } + + /// @brief Retrieves the Lux High Value threshold. + /// @return Returns the high threshold. + uint16_t getThresholdHigh(void) + { + uint8_t buff[2]; + opt4048_reg_thresh_exp_res_high_t threshReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_THRESH_H_EXP_RES, buff); + + threshReg.word = buff[0] << 8 | buff[1]; + + return threshReg.thresh_result << threshReg.thresh_exp; + } + + /// @brief Sets the Lux Low Value threshold. + /// @param thresh The value in float + /// @return Returns the high threshold. + bool setThresholdLow(const float *thresh) + { + if (*thresh < 2.15 || *thresh > 144000) + return false; + + uint8_t buff[2]; + int8_t result; + // opt4048_reg_thresh_exp_res_high_t threshReg; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_THRESH_L_EXP_RES, buff); + + if (SFE_BUS_OK != result) + return false; + + return true; + } + + /// @brief Retrieves the Lux Low Value threshold. + /// @return Returns the low threshold. + uint16_t getThresholdLow(void) + { + uint8_t buff[2]; + opt4048_reg_thresh_exp_res_low_t threshReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_THRESH_L_EXP_RES, buff); + + threshReg.word = buff[0] << 8 | buff[1]; + + return threshReg.thresh_result << threshReg.thresh_exp; + } + + /// @brief Enable CRC for ADC calues + /// @param set True to enable, false to disable. + /// @return True on successful execution. + bool setCRC(const bool enable = true) + { + if (enable) + crcEnabled = true; + else + crcEnabled = false; + } + + ///////////////////////////////////////////////////////////////////Interrupt Settings + /// @brief Changes the behavior of the interrupt pin to be an INPUT to trigger single shot. + /// @param set True to enable, false to disable. + /// @return True on successful execution. + bool setIntInput(const bool enable = true) + { + uint8_t buff[2]; + int8_t result; + opt4048_reg_int_control_t intReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + if (SFE_BUS_OK !=result) + return false; + + intReg.word = buff[0] << 8 | buff[1]; + + intReg.int_dir = (uint8_t)enable; + + buff[0] = intReg.word >> 8; + buff[1] = intReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + if (SFE_BUS_OK !=result) + return false; + + return true; + } + + /// @brief Gets the interrupt input bit + /// @return True if the interrupt is set to INPUT. + bool getIntInputEnable(void) + { + uint8_t buff[2]; + opt4048_reg_int_control_t intReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + intReg.word = buff[0] << 8 | buff[1]; + + return (bool)intReg.int_dir; + } + + /// @brief Changes the behavior interrupt mechanism after the end of conversion + /// @param mechanism The mechanism to set + /// @return True on successful execution. + bool setIntMechanism(const opt4048_int_cfg_t *mechanism) + { + uint8_t buff[2]; + int8_t result; + opt4048_reg_int_control_t intReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + if (SFE_BUS_OK !=result) + return false; + + intReg.word = buff[0] << 8 | buff[1]; + + intReg.int_cfg = *mechanism; + + buff[0] = intReg.word >> 8; + buff[1] = intReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + if (SFE_BUS_OK !=result) + return false; + + return true; + } + + /// @brief Gets the interrupt mechanism for the OPT4048 + /// @return Returns the setd mechanism. + opt4048_int_cfg_t getIntMechanism(void) + { + uint8_t buff[2]; + opt4048_reg_int_control_t intReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + intReg.word = buff[0] << 8 | buff[1]; + + return ((opt4048_int_cfg_t)intReg.int_cfg); + } + + /// @brief Enable register auto increment . + /// @param enable True to enable, false to disable. + /// @return True on successful execution. + bool setI2CBurst(const bool enable = true) + { + uint8_t buff[2]; + int8_t result; + opt4048_reg_int_control_t intReg; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + if (SFE_BUS_OK !=result) + return false; + + intReg.word = buff[0] << 8 | buff[1]; + + intReg.i2c_burst = (uint8_t)enable; + + buff[0] = intReg.word >> 8; + buff[1] = intReg.word; + + result = writeRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + if (SFE_BUS_OK !=result) + return false; + + return true; + } + + /// @brief Retrieves the I2C burst bit. + /// @return True if I2C burst is set, false otherwise. + bool getI2CBurst(void) + { + uint8_t buff[2]; + opt4048_reg_int_control_t intReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_INT_CONTROL, buff); + + intReg.word = buff[0] << 8 | buff[1]; + + return (bool)intReg.i2c_burst; + } + + /// @brief Retrieves the flag register + /// @return The contents of the flag register + opt4048_reg_flags_t getAllFlags(void) + { + uint8_t buff[2]; + opt4048_reg_flags_t flagReg; + + readRegisterRegion(SFE_OPT4048_REGISTER_FLAGS, buff); + + flagReg.word = buff[0] << 8 | buff[1]; + + return flagReg; + } + + /// @brief Checks the overload flag bit. + /// @return True if the overload flag bit is set, false otherwise + bool getOverloadFlag(void) + { + opt4048_reg_flags_t flagReg; + flagReg = getAllFlags(); + + return (bool)flagReg.overload_flag; + } + + /// @brief Checks the conversion ready flag bit. + /// @return True if that flag bit is set, false otherwise + bool getConvReadyFlag(void) + { + opt4048_reg_flags_t flagReg; + flagReg = getAllFlags(); + + return (bool)flagReg.conv_ready_flag; + } + + /// @brief Checks the too bright flag bit. + /// @return True if that flag bit is set, false otherwise + bool getTooBrightFlag(void) + { + opt4048_reg_flags_t flagReg; + flagReg = getAllFlags(); + + return (bool)flagReg.flag_high; + } + + /// @brief Checks the too dim flag bit. + /// @return True if that flag bit is set, false otherwise + bool getTooDimFlag(void) + { + opt4048_reg_flags_t flagReg; + flagReg = getAllFlags(); + + return (bool)flagReg.flag_low; + } + + ///////////////////////////////////////////////////////////////////Color Information + /// @brief Reads Channel Zero (Red) + /// @return Returns the ADC value of Channel Zero + uint32_t getADCCh0(void) + { + uint8_t buff[4]; + uint32_t mantissa; + opt4048_reg_exp_res_ch0_t adcReg; + opt4048_reg_res_cnt_crc_ch0_t adc1Reg; + + readRegisterRegion(SFE_OPT4048_REGISTER_EXP_RES_CH0, buff, 4); + + adcReg.word = buff[0] << 8 | buff[1]; + adc1Reg.word = buff[2] << 8 | buff[3]; + + mantissa = adcReg.result_msb_ch0 << 8 | adc1Reg.result_lsb_ch0; + + return mantissa << adcReg.exponent_ch0; + } + + /// @brief Reads Channel One (Green) + /// @return Returns the ADC value of Channel One + uint32_t getADCCh1(void) + { + uint8_t buff[4]; + uint32_t mantissa; + opt4048_reg_exp_res_ch0_t adcReg; + opt4048_reg_res_cnt_crc_ch0_t adc1Reg; + + readRegisterRegion(SFE_OPT4048_REGISTER_EXP_RES_CH1, buff, 4); + + adcReg.word = buff[0] << 8 | buff[1]; + adc1Reg.word = buff[2] << 8 | buff[3]; + + mantissa = adcReg.result_msb_ch0 << 8 | adc1Reg.result_lsb_ch0; + + return mantissa << adcReg.exponent_ch0; + } + + /// @brief Reads Channel Two (Blue) + /// @return Returns the ADC value of Channel Two + uint32_t getADCCh2(void) + { + uint8_t buff[4]; + uint32_t mantissa; + opt4048_reg_exp_res_ch0_t adcReg; + opt4048_reg_res_cnt_crc_ch0_t adc1Reg; + + readRegisterRegion(SFE_OPT4048_REGISTER_EXP_RES_CH2, buff, 4); + + adcReg.word = buff[0] << 8 | buff[1]; + adc1Reg.word = buff[2] << 8 | buff[3]; + + mantissa = adcReg.result_msb_ch0 << 8 | adc1Reg.result_lsb_ch0; + + return mantissa << adcReg.exponent_ch0; + } + + /// @brief Reads Channel Three (White) + /// @return Returns the ADC value of Channel Three + uint32_t getADCCh3(void) + { + uint8_t buff[4]; + uint32_t mantissa; + opt4048_reg_exp_res_ch0_t adcReg; + opt4048_reg_res_cnt_crc_ch0_t adc1Reg; + + readRegisterRegion(SFE_OPT4048_REGISTER_EXP_RES_CH3, buff, 4); + + adcReg.word = buff[0] << 8 | buff[1]; + adc1Reg.word = buff[2] << 8 | buff[3]; + + mantissa = adcReg.result_msb_ch0 << 8 | adc1Reg.result_lsb_ch0; + + return mantissa << adcReg.exponent_ch0; + } + + /// @brief Retrieves all ADC values for all channels: Red, Green, Blue, and White. + /// @return Returns the ADC values of the channels. + sfe_color_t getAllADC(void) + { + sfe_color_t color; + + color.red = getADCCh0(); + color.green = getADCCh1(); + color.blue = getADCCh2(); + color.white = getADCCh3(); + + return color; + } + + /// @brief Retrieves all ADC values for all channels: Red, Green, Blue, and White, as well as the sample counter, and + /// the CRC value. + /// @param color Pointer to the color struct to be populated with the channels values. + /// @return Returns true on successful execution, false otherwise. + bool getAllChannelData(sfe_color_t *color) + { + int32_t result; + uint8_t buff[16]; + uint32_t mantissaCh0; + uint32_t mantissaCh1; + uint32_t mantissaCh2; + uint32_t mantissaCh3; + uint32_t adcCodeCh0; + uint32_t adcCodeCh1; + uint32_t adcCodeCh2; + uint32_t adcCodeCh3; + + opt4048_reg_exp_res_ch0_t adc0MSB; + opt4048_reg_res_cnt_crc_ch0_t adc0LSB; + opt4048_reg_exp_res_ch1_t adc1MSB; + opt4048_reg_res_cnt_crc_ch1_t adc1LSB; + opt4048_reg_exp_res_ch2_t adc2MSB; + opt4048_reg_res_cnt_crc_ch2_t adc2LSB; + opt4048_reg_exp_res_ch3_t adc3MSB; + opt4048_reg_res_cnt_crc_ch3_t adc3LSB; + + result = readRegisterRegion(SFE_OPT4048_REGISTER_EXP_RES_CH0, buff, 16); + + if (SFE_BUS_OK != result) + return false; + + adc0MSB.word = buff[0] << 8 | buff[1]; + adc0LSB.word = buff[2] << 8 | buff[3]; + + adc1MSB.word = buff[4] << 8 | buff[5]; + adc1LSB.word = buff[6] << 8 | buff[7]; + + adc2MSB.word = buff[8] << 8 | buff[9]; + adc2LSB.word = buff[10] << 8 | buff[11]; + + adc3MSB.word = buff[12] << 8 | buff[13]; + adc3LSB.word = buff[14] << 8 | buff[15]; + + mantissaCh0 = adc0MSB.result_msb_ch0 << 8 | adc0LSB.result_lsb_ch0; + adcCodeCh0 = mantissaCh0 << adc0MSB.exponent_ch0; + + mantissaCh1 = adc1MSB.result_msb_ch1 << 8 | adc1LSB.result_lsb_ch1; + adcCodeCh1 = mantissaCh1 << adc1MSB.exponent_ch1; + + mantissaCh2 = adc2MSB.result_msb_ch2 << 8 | adc2LSB.result_lsb_ch2; + adcCodeCh2 = mantissaCh2 << adc2MSB.exponent_ch2; + + mantissaCh3 = adc3MSB.result_msb_ch3 << 8 | adc3LSB.result_lsb_ch3; + adcCodeCh3 = mantissaCh3 << adc3MSB.exponent_ch3; + + color->red = adcCodeCh0; + color->green = adcCodeCh1; + color->blue = adcCodeCh2; + color->white = adcCodeCh3; + + color->counterR = adc0LSB.counter_ch0; + color->counterG = adc1LSB.counter_ch1; + color->counterR = adc2LSB.counter_ch2; + color->counterR = adc3LSB.counter_ch3; + + color->CRCR = adc0LSB.crc_ch0; + color->CRCG = adc1LSB.crc_ch1; + color->CRCB = adc2LSB.crc_ch2; + color->CRCW = adc3LSB.crc_ch3; + + return true; + } + + /// @brief Calculates the CRC for the OPT4048. Note that the OPT4048 does this already + /// but this is a way to double check the value. + /// @param mantissa The mantissa value of the ADC + /// @param exponent The exponent value of the ADC + /// @param crc The CRC value of the ADC + /// @return Returns the calculated CRC value. + bool validateCRC(uint32_t mantissa, uint8_t expon, uint8_t crc) + { + if(!crcEnabled) + return false; + + mantissaBits mBits; + exponBits exBits; + crcBits cBits; + crcBits compareAgainst; + + mBits.word = mantissa; + exBits.byte = expon; + cBits.byte = crc; + + compareAgainst.bit0 = exBits.byte xor mantissa xor cBits.byte; + + compareAgainst.bit1 = cBits.bit1 xor cBits.bit3 xor mBits.bit1 xor mBits.bit3 xor mBits.bit5 xor mBits.bit7 xor + mBits.bit9 xor mBits.bit11 xor mBits.bit13 xor mBits.bit15 xor mBits.bit17 xor mBits.bit19 xor + exBits.bit1 xor exBits.bit3; + + compareAgainst.bit2 = + cBits.bit3 xor mBits.bit3 xor mBits.bit7 xor mBits.bit11 xor mBits.bit16 xor mBits.bit18 xor exBits.bit3; + + compareAgainst.bit3 = mBits.bit3 xor mBits.bit11 xor mBits.bit19; + + if (compareAgainst.byte == crc) + return true; + + return false; + } + + /// @brief Retrieves the Lux value. + /// @return Returns the Lux value of the sensor + uint32_t getLux(void) + { + uint32_t adcCh1; + uint32_t lux; + + adcCh1 = getADCCh1(); + lux = adcCh1 * cieMatrix[1][3]; + + return lux; + } + + /// @brief Retrieves the CIE X value of the sensor. + /// @return Returns the CIE X value of the sensor + double getCIEx(void) + { + double x = 0; + double y = 0; + double z = 0; + double CIEx; + static sfe_color_t color; + + getAllChannelData(&color); + + for (int row = 0; row < OPT_MATRIX_ROWS; row++) + { + x += color.red * cieMatrix[row][0]; + y += color.green * cieMatrix[row][1]; + z += color.blue * cieMatrix[row][2]; + } + + CIEx = x / (x + y + z); + + return CIEx; + } + + /// @brief Retrieves the CIE Y value of the sensor. + /// @return Returns the CIE Y value of the sensor + double getCIEy(void) + { + double x = 0; + double y = 0; + double z = 0; + double CIEy; + sfe_color_t color; + + getAllChannelData(&color); + + for (int row = 0; row < OPT_MATRIX_ROWS; row++) + { + x += color.red * cieMatrix[row][0]; + y += color.green * cieMatrix[row][1]; + z += color.blue * cieMatrix[row][2]; + } + + CIEy = y / (x + y + z); + + return CIEy; + } + + /// @brief Retrieves the Correlated Color Temperature (CCT) of the sensor. + /// @return Returns the CCT of the sensor in Kelvin + double getCCT(void) + { + double CIEx; + double CIEy; + double CCT; + + CIEx = getCIEx(); + CIEy = getCIEy(); + + double n = (CIEx - 0.3320) / (0.1858 - CIEy); + + // Formula can be found under the CCT section in the datasheet. + CCT = 432 * pow(n, 3) + 3601 * pow(n, 2) + 6861 * n + 5517; + + return CCT; + } + + protected: + sfeBusDevice *_sfeBus; + sfeBusDeviceSettings *_devSettings; + bool crcEnabled = false; + + const double cieMatrix[OPT_MATRIX_ROWS][OPT_MATRIX_COLS] = { + {.000234892992, -.0000189652390, .0000120811684, 0}, + {.0000407467441, .000198958202, -.0000158848115, .00215}, + {.0000928619404, -.0000169739553, .000674021520, 0}, + {0, 0, 0, 0}}; +}; + +class sfeOPT4048ArduinoI2C : public OPT4048SFEBusBase +{ + /* Nothing to see here, see above. */ +}; \ No newline at end of file diff --git a/test/test.ino b/test/test.ino deleted file mode 100644 index 07be984..0000000 --- a/test/test.ino +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include - -void setup() -{ - -} - -void loop() -{ - -}