This commit is contained in:
2026-01-15 15:00:21 +08:00
parent e67749e2e4
commit 419f6cc78c
705 changed files with 9981 additions and 35723 deletions

View File

@ -0,0 +1,243 @@
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseColons: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
KeepEmptyLinesAtEOF: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE

View 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**

View 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.

View File

@ -0,0 +1,33 @@
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: Install the prerequisites
run: bash ci/actions_install.sh
- name: Check for correct code formatting with clang-format
run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r .
- name: Check for correct documentation with doxygen
env:
GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }}
PRETTYNAME : "Adafruit Bus IO Library"
run: bash ci/doxy_gen_and_deploy.sh
- name: Test the code on supported platforms
run: python3 ci/build_platform.py main_platforms zero feather32u4

View File

@ -0,0 +1 @@
{"type": "library", "name": "Adafruit BusIO", "version": "1.15.0", "spec": {"owner": "adafruit", "id": 6214, "name": "Adafruit BusIO", "requirements": null, "uri": null}}

View File

@ -0,0 +1,365 @@
#include <Adafruit_BusIO_Register.h>
#if !defined(SPI_INTERFACES_COUNT) || \
(defined(SPI_INTERFACES_COUNT) && (SPI_INTERFACES_COUNT > 0))
/*!
* @brief Create a register we access over an I2C Device (which defines the
* bus and address)
* @param i2cdevice The I2CDevice to use for underlying I2C access
* @param reg_addr The address pointer value for the I2C/SMBus register, can
* be 8 or 16 bits
* @param width The width of the register data itself, defaults to 1 byte
* @param byteorder The byte order of the register (used when width is > 1),
* defaults to LSBFIRST
* @param address_width The width of the register address itself, defaults
* to 1 byte
*/
Adafruit_BusIO_Register::Adafruit_BusIO_Register(Adafruit_I2CDevice *i2cdevice,
uint16_t reg_addr,
uint8_t width,
uint8_t byteorder,
uint8_t address_width) {
_i2cdevice = i2cdevice;
_spidevice = nullptr;
_addrwidth = address_width;
_address = reg_addr;
_byteorder = byteorder;
_width = width;
}
/*!
* @brief Create a register we access over an SPI Device (which defines the
* bus and CS pin)
* @param spidevice The SPIDevice to use for underlying SPI access
* @param reg_addr The address pointer value for the SPI register, can
* be 8 or 16 bits
* @param type The method we use to read/write data to SPI (which is not
* as well defined as I2C)
* @param width The width of the register data itself, defaults to 1 byte
* @param byteorder The byte order of the register (used when width is > 1),
* defaults to LSBFIRST
* @param address_width The width of the register address itself, defaults
* to 1 byte
*/
Adafruit_BusIO_Register::Adafruit_BusIO_Register(Adafruit_SPIDevice *spidevice,
uint16_t reg_addr,
Adafruit_BusIO_SPIRegType type,
uint8_t width,
uint8_t byteorder,
uint8_t address_width) {
_spidevice = spidevice;
_spiregtype = type;
_i2cdevice = nullptr;
_addrwidth = address_width;
_address = reg_addr;
_byteorder = byteorder;
_width = width;
}
/*!
* @brief Create a register we access over an I2C or SPI Device. This is a
* handy function because we can pass in nullptr for the unused interface,
* allowing libraries to mass-define all the registers
* @param i2cdevice The I2CDevice to use for underlying I2C access, if
* nullptr we use SPI
* @param spidevice The SPIDevice to use for underlying SPI access, if
* nullptr we use I2C
* @param reg_addr The address pointer value for the I2C/SMBus/SPI register,
* can be 8 or 16 bits
* @param type The method we use to read/write data to SPI (which is not
* as well defined as I2C)
* @param width The width of the register data itself, defaults to 1 byte
* @param byteorder The byte order of the register (used when width is > 1),
* defaults to LSBFIRST
* @param address_width The width of the register address itself, defaults
* to 1 byte
*/
Adafruit_BusIO_Register::Adafruit_BusIO_Register(
Adafruit_I2CDevice *i2cdevice, Adafruit_SPIDevice *spidevice,
Adafruit_BusIO_SPIRegType type, uint16_t reg_addr, uint8_t width,
uint8_t byteorder, uint8_t address_width) {
_spidevice = spidevice;
_i2cdevice = i2cdevice;
_spiregtype = type;
_addrwidth = address_width;
_address = reg_addr;
_byteorder = byteorder;
_width = width;
}
/*!
* @brief Write a buffer of data to the register location
* @param buffer Pointer to data to write
* @param len Number of bytes to write
* @return True on successful write (only really useful for I2C as SPI is
* uncheckable)
*/
bool Adafruit_BusIO_Register::write(uint8_t *buffer, uint8_t len) {
uint8_t addrbuffer[2] = {(uint8_t)(_address & 0xFF),
(uint8_t)(_address >> 8)};
if (_i2cdevice) {
return _i2cdevice->write(buffer, len, true, addrbuffer, _addrwidth);
}
if (_spidevice) {
if (_spiregtype == ADDRESSED_OPCODE_BIT0_LOW_TO_WRITE) {
// very special case!
// pass the special opcode address which we set as the high byte of the
// regaddr
addrbuffer[0] =
(uint8_t)(_address >> 8) & ~0x01; // set bottom bit low to write
// the 'actual' reg addr is the second byte then
addrbuffer[1] = (uint8_t)(_address & 0xFF);
// the address appears to be a byte longer
return _spidevice->write(buffer, len, addrbuffer, _addrwidth + 1);
}
if (_spiregtype == ADDRBIT8_HIGH_TOREAD) {
addrbuffer[0] &= ~0x80;
}
if (_spiregtype == ADDRBIT8_HIGH_TOWRITE) {
addrbuffer[0] |= 0x80;
}
if (_spiregtype == AD8_HIGH_TOREAD_AD7_HIGH_TOINC) {
addrbuffer[0] &= ~0x80;
addrbuffer[0] |= 0x40;
}
return _spidevice->write(buffer, len, addrbuffer, _addrwidth);
}
return false;
}
/*!
* @brief Write up to 4 bytes of data to the register location
* @param value Data to write
* @param numbytes How many bytes from 'value' to write
* @return True on successful write (only really useful for I2C as SPI is
* uncheckable)
*/
bool Adafruit_BusIO_Register::write(uint32_t value, uint8_t numbytes) {
if (numbytes == 0) {
numbytes = _width;
}
if (numbytes > 4) {
return false;
}
// store a copy
_cached = value;
for (int i = 0; i < numbytes; i++) {
if (_byteorder == LSBFIRST) {
_buffer[i] = value & 0xFF;
} else {
_buffer[numbytes - i - 1] = value & 0xFF;
}
value >>= 8;
}
return write(_buffer, numbytes);
}
/*!
* @brief Read data from the register location. This does not do any error
* checking!
* @return Returns 0xFFFFFFFF on failure, value otherwise
*/
uint32_t Adafruit_BusIO_Register::read(void) {
if (!read(_buffer, _width)) {
return -1;
}
uint32_t value = 0;
for (int i = 0; i < _width; i++) {
value <<= 8;
if (_byteorder == LSBFIRST) {
value |= _buffer[_width - i - 1];
} else {
value |= _buffer[i];
}
}
return value;
}
/*!
* @brief Read cached data from last time we wrote to this register
* @return Returns 0xFFFFFFFF on failure, value otherwise
*/
uint32_t Adafruit_BusIO_Register::readCached(void) { return _cached; }
/*!
* @brief Read a buffer of data from the register location
* @param buffer Pointer to data to read into
* @param len Number of bytes to read
* @return True on successful write (only really useful for I2C as SPI is
* uncheckable)
*/
bool Adafruit_BusIO_Register::read(uint8_t *buffer, uint8_t len) {
uint8_t addrbuffer[2] = {(uint8_t)(_address & 0xFF),
(uint8_t)(_address >> 8)};
if (_i2cdevice) {
return _i2cdevice->write_then_read(addrbuffer, _addrwidth, buffer, len);
}
if (_spidevice) {
if (_spiregtype == ADDRESSED_OPCODE_BIT0_LOW_TO_WRITE) {
// very special case!
// pass the special opcode address which we set as the high byte of the
// regaddr
addrbuffer[0] =
(uint8_t)(_address >> 8) | 0x01; // set bottom bit high to read
// the 'actual' reg addr is the second byte then
addrbuffer[1] = (uint8_t)(_address & 0xFF);
// the address appears to be a byte longer
return _spidevice->write_then_read(addrbuffer, _addrwidth + 1, buffer,
len);
}
if (_spiregtype == ADDRBIT8_HIGH_TOREAD) {
addrbuffer[0] |= 0x80;
}
if (_spiregtype == ADDRBIT8_HIGH_TOWRITE) {
addrbuffer[0] &= ~0x80;
}
if (_spiregtype == AD8_HIGH_TOREAD_AD7_HIGH_TOINC) {
addrbuffer[0] |= 0x80 | 0x40;
}
return _spidevice->write_then_read(addrbuffer, _addrwidth, buffer, len);
}
return false;
}
/*!
* @brief Read 2 bytes of data from the register location
* @param value Pointer to uint16_t variable to read into
* @return True on successful write (only really useful for I2C as SPI is
* uncheckable)
*/
bool Adafruit_BusIO_Register::read(uint16_t *value) {
if (!read(_buffer, 2)) {
return false;
}
if (_byteorder == LSBFIRST) {
*value = _buffer[1];
*value <<= 8;
*value |= _buffer[0];
} else {
*value = _buffer[0];
*value <<= 8;
*value |= _buffer[1];
}
return true;
}
/*!
* @brief Read 1 byte of data from the register location
* @param value Pointer to uint8_t variable to read into
* @return True on successful write (only really useful for I2C as SPI is
* uncheckable)
*/
bool Adafruit_BusIO_Register::read(uint8_t *value) {
if (!read(_buffer, 1)) {
return false;
}
*value = _buffer[0];
return true;
}
/*!
* @brief Pretty printer for this register
* @param s The Stream to print to, defaults to &Serial
*/
void Adafruit_BusIO_Register::print(Stream *s) {
uint32_t val = read();
s->print("0x");
s->print(val, HEX);
}
/*!
* @brief Pretty printer for this register
* @param s The Stream to print to, defaults to &Serial
*/
void Adafruit_BusIO_Register::println(Stream *s) {
print(s);
s->println();
}
/*!
* @brief Create a slice of the register that we can address without
* touching other bits
* @param reg The Adafruit_BusIO_Register which defines the bus/register
* @param bits The number of bits wide we are slicing
* @param shift The number of bits that our bit-slice is shifted from LSB
*/
Adafruit_BusIO_RegisterBits::Adafruit_BusIO_RegisterBits(
Adafruit_BusIO_Register *reg, uint8_t bits, uint8_t shift) {
_register = reg;
_bits = bits;
_shift = shift;
}
/*!
* @brief Read 4 bytes of data from the register
* @return data The 4 bytes to read
*/
uint32_t Adafruit_BusIO_RegisterBits::read(void) {
uint32_t val = _register->read();
val >>= _shift;
return val & ((1 << (_bits)) - 1);
}
/*!
* @brief Write 4 bytes of data to the register
* @param data The 4 bytes to write
* @return True on successful write (only really useful for I2C as SPI is
* uncheckable)
*/
bool Adafruit_BusIO_RegisterBits::write(uint32_t data) {
uint32_t val = _register->read();
// mask off the data before writing
uint32_t mask = (1 << (_bits)) - 1;
data &= mask;
mask <<= _shift;
val &= ~mask; // remove the current data at that spot
val |= data << _shift; // and add in the new data
return _register->write(val, _register->width());
}
/*!
* @brief The width of the register data, helpful for doing calculations
* @returns The data width used when initializing the register
*/
uint8_t Adafruit_BusIO_Register::width(void) { return _width; }
/*!
* @brief Set the default width of data
* @param width the default width of data read from register
*/
void Adafruit_BusIO_Register::setWidth(uint8_t width) { _width = width; }
/*!
* @brief Set register address
* @param address the address from register
*/
void Adafruit_BusIO_Register::setAddress(uint16_t address) {
_address = address;
}
/*!
* @brief Set the width of register address
* @param address_width the width for register address
*/
void Adafruit_BusIO_Register::setAddressWidth(uint16_t address_width) {
_addrwidth = address_width;
}
#endif // SPI exists

View File

@ -0,0 +1,105 @@
#ifndef Adafruit_BusIO_Register_h
#define Adafruit_BusIO_Register_h
#include <Arduino.h>
#if !defined(SPI_INTERFACES_COUNT) || \
(defined(SPI_INTERFACES_COUNT) && (SPI_INTERFACES_COUNT > 0))
#include <Adafruit_I2CDevice.h>
#include <Adafruit_SPIDevice.h>
typedef enum _Adafruit_BusIO_SPIRegType {
ADDRBIT8_HIGH_TOREAD = 0,
/*!<
* ADDRBIT8_HIGH_TOREAD
* When reading a register you must actually send the value 0x80 + register
* address to the device. e.g. To read the register 0x0B the register value
* 0x8B is sent and to write 0x0B is sent.
*/
AD8_HIGH_TOREAD_AD7_HIGH_TOINC = 1,
/*!<
* ADDRBIT8_HIGH_TOWRITE
* When writing to a register you must actually send the value 0x80 +
* the register address to the device. e.g. To write to the register 0x19 the
* register value 0x99 is sent and to read 0x19 is sent.
*/
ADDRBIT8_HIGH_TOWRITE = 2,
/*!<
* ADDRESSED_OPCODE_LOWBIT_TO_WRITE
* Used by the MCP23S series, we send 0x40 |'rd with the opcode
* Then set the lowest bit to write
*/
ADDRESSED_OPCODE_BIT0_LOW_TO_WRITE = 3,
} Adafruit_BusIO_SPIRegType;
/*!
* @brief The class which defines a device register (a location to read/write
* data from)
*/
class Adafruit_BusIO_Register {
public:
Adafruit_BusIO_Register(Adafruit_I2CDevice *i2cdevice, uint16_t reg_addr,
uint8_t width = 1, uint8_t byteorder = LSBFIRST,
uint8_t address_width = 1);
Adafruit_BusIO_Register(Adafruit_SPIDevice *spidevice, uint16_t reg_addr,
Adafruit_BusIO_SPIRegType type, uint8_t width = 1,
uint8_t byteorder = LSBFIRST,
uint8_t address_width = 1);
Adafruit_BusIO_Register(Adafruit_I2CDevice *i2cdevice,
Adafruit_SPIDevice *spidevice,
Adafruit_BusIO_SPIRegType type, uint16_t reg_addr,
uint8_t width = 1, uint8_t byteorder = LSBFIRST,
uint8_t address_width = 1);
bool read(uint8_t *buffer, uint8_t len);
bool read(uint8_t *value);
bool read(uint16_t *value);
uint32_t read(void);
uint32_t readCached(void);
bool write(uint8_t *buffer, uint8_t len);
bool write(uint32_t value, uint8_t numbytes = 0);
uint8_t width(void);
void setWidth(uint8_t width);
void setAddress(uint16_t address);
void setAddressWidth(uint16_t address_width);
void print(Stream *s = &Serial);
void println(Stream *s = &Serial);
private:
Adafruit_I2CDevice *_i2cdevice;
Adafruit_SPIDevice *_spidevice;
Adafruit_BusIO_SPIRegType _spiregtype;
uint16_t _address;
uint8_t _width, _addrwidth, _byteorder;
uint8_t _buffer[4]; // we won't support anything larger than uint32 for
// non-buffered read
uint32_t _cached = 0;
};
/*!
* @brief The class which defines a slice of bits from within a device register
* (a location to read/write data from)
*/
class Adafruit_BusIO_RegisterBits {
public:
Adafruit_BusIO_RegisterBits(Adafruit_BusIO_Register *reg, uint8_t bits,
uint8_t shift);
bool write(uint32_t value);
uint32_t read(void);
private:
Adafruit_BusIO_Register *_register;
uint8_t _bits, _shift;
};
#endif // SPI exists
#endif // BusIO_Register_h

View File

@ -0,0 +1,90 @@
/*
Written with help by Claude!
https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c (at this time
chats are not shareable :(
*/
#include "Adafruit_GenericDevice.h"
/*!
* @brief Create a Generic device with the provided read/write functions
* @param obj Pointer to object instance
* @param read_func Function pointer for reading raw data
* @param write_func Function pointer for writing raw data
* @param readreg_func Function pointer for reading registers (optional)
* @param writereg_func Function pointer for writing registers (optional) */
Adafruit_GenericDevice::Adafruit_GenericDevice(
void *obj, busio_genericdevice_read_t read_func,
busio_genericdevice_write_t write_func,
busio_genericdevice_readreg_t readreg_func,
busio_genericdevice_writereg_t writereg_func) {
_obj = obj;
_read_func = read_func;
_write_func = write_func;
_readreg_func = readreg_func;
_writereg_func = writereg_func;
_begun = false;
}
/*! @brief Simple begin function (doesn't do much at this time)
@return true always
*/
bool Adafruit_GenericDevice::begin(void) {
_begun = true;
return true;
}
/*!
@brief Marks the GenericDevice as no longer in use.
@note: Since this is a GenericDevice, if you are using this with a Serial
object, this does NOT disable serial communication or release the RX/TX pins.
That must be done manually by calling Serial.end().
*/
void Adafruit_GenericDevice::end(void) { _begun = false; }
/*! @brief Write a buffer of data
@param buffer Pointer to buffer of data to write
@param len Number of bytes to write
@return true if write was successful, otherwise false */
bool Adafruit_GenericDevice::write(const uint8_t *buffer, size_t len) {
if (!_begun)
return false;
return _write_func(_obj, buffer, len);
}
/*! @brief Read data into a buffer
@param buffer Pointer to buffer to read data into
@param len Number of bytes to read
@return true if read was successful, otherwise false */
bool Adafruit_GenericDevice::read(uint8_t *buffer, size_t len) {
if (!_begun)
return false;
return _read_func(_obj, buffer, len);
}
/*! @brief Read from a register location
@param addr_buf Buffer containing register address
@param addrsiz Size of register address in bytes
@param buf Buffer to store read data
@param bufsiz Size of data to read in bytes
@return true if read was successful, otherwise false */
bool Adafruit_GenericDevice::readRegister(uint8_t *addr_buf, uint8_t addrsiz,
uint8_t *buf, uint16_t bufsiz) {
if (!_begun || !_readreg_func)
return false;
return _readreg_func(_obj, addr_buf, addrsiz, buf, bufsiz);
}
/*! @brief Write to a register location
@param addr_buf Buffer containing register address
@param addrsiz Size of register address in bytes
@param buf Buffer containing data to write
@param bufsiz Size of data to write in bytes
@return true if write was successful, otherwise false */
bool Adafruit_GenericDevice::writeRegister(uint8_t *addr_buf, uint8_t addrsiz,
const uint8_t *buf,
uint16_t bufsiz) {
if (!_begun || !_writereg_func)
return false;
return _writereg_func(_obj, addr_buf, addrsiz, buf, bufsiz);
}

View File

@ -0,0 +1,56 @@
#ifndef ADAFRUIT_GENERICDEVICE_H
#define ADAFRUIT_GENERICDEVICE_H
#include <Arduino.h>
typedef bool (*busio_genericdevice_read_t)(void *obj, uint8_t *buffer,
size_t len);
typedef bool (*busio_genericdevice_write_t)(void *obj, const uint8_t *buffer,
size_t len);
typedef bool (*busio_genericdevice_readreg_t)(void *obj, uint8_t *addr_buf,
uint8_t addrsiz, uint8_t *data,
uint16_t datalen);
typedef bool (*busio_genericdevice_writereg_t)(void *obj, uint8_t *addr_buf,
uint8_t addrsiz,
const uint8_t *data,
uint16_t datalen);
/*!
* @brief Class for communicating with a device via generic read/write functions
*/
class Adafruit_GenericDevice {
public:
Adafruit_GenericDevice(
void *obj, busio_genericdevice_read_t read_func,
busio_genericdevice_write_t write_func,
busio_genericdevice_readreg_t readreg_func = nullptr,
busio_genericdevice_writereg_t writereg_func = nullptr);
bool begin(void);
void end(void);
bool read(uint8_t *buffer, size_t len);
bool write(const uint8_t *buffer, size_t len);
bool readRegister(uint8_t *addr_buf, uint8_t addrsiz, uint8_t *buf,
uint16_t bufsiz);
bool writeRegister(uint8_t *addr_buf, uint8_t addrsiz, const uint8_t *buf,
uint16_t bufsiz);
protected:
/*! @brief Function pointer for reading raw data from the device */
busio_genericdevice_read_t _read_func;
/*! @brief Function pointer for writing raw data to the device */
busio_genericdevice_write_t _write_func;
/*! @brief Function pointer for reading a 'register' from the device */
busio_genericdevice_readreg_t _readreg_func;
/*! @brief Function pointer for writing a 'register' to the device */
busio_genericdevice_writereg_t _writereg_func;
bool _begun; ///< whether we have initialized yet (in case the function needs
///< to do something)
private:
void *_obj; ///< Pointer to object instance
};
#endif // ADAFRUIT_GENERICDEVICE_H

View File

@ -0,0 +1,317 @@
#include "Adafruit_I2CDevice.h"
//#define DEBUG_SERIAL Serial
/*!
* @brief Create an I2C device at a given address
* @param addr The 7-bit I2C address for the device
* @param theWire The I2C bus to use, defaults to &Wire
*/
Adafruit_I2CDevice::Adafruit_I2CDevice(uint8_t addr, TwoWire *theWire) {
_addr = addr;
_wire = theWire;
_begun = false;
#ifdef ARDUINO_ARCH_SAMD
_maxBufferSize = 250; // as defined in Wire.h's RingBuffer
#elif defined(ESP32)
_maxBufferSize = I2C_BUFFER_LENGTH;
#else
_maxBufferSize = 32;
#endif
}
/*!
* @brief Initializes and does basic address detection
* @param addr_detect Whether we should attempt to detect the I2C address
* with a scan. 99% of sensors/devices don't mind, but once in a while they
* don't respond well to a scan!
* @return True if I2C initialized and a device with the addr found
*/
bool Adafruit_I2CDevice::begin(bool addr_detect) {
_wire->begin();
_begun = true;
if (addr_detect) {
return detected();
}
return true;
}
/*!
* @brief De-initialize device, turn off the Wire interface
*/
void Adafruit_I2CDevice::end(void) {
// Not all port implement Wire::end(), such as
// - ESP8266
// - AVR core without WIRE_HAS_END
// - ESP32: end() is implemented since 2.0.1 which is latest at the moment.
// Temporarily disable for now to give time for user to update.
#if !(defined(ESP8266) || \
(defined(ARDUINO_ARCH_AVR) && !defined(WIRE_HAS_END)) || \
defined(ARDUINO_ARCH_ESP32))
_wire->end();
_begun = false;
#endif
}
/*!
* @brief Scans I2C for the address - note will give a false-positive
* if there's no pullups on I2C
* @return True if I2C initialized and a device with the addr found
*/
bool Adafruit_I2CDevice::detected(void) {
// Init I2C if not done yet
if (!_begun && !begin()) {
return false;
}
// A basic scanner, see if it ACK's
_wire->beginTransmission(_addr);
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("Address 0x"));
DEBUG_SERIAL.print(_addr);
#endif
if (_wire->endTransmission() == 0) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F(" Detected"));
#endif
return true;
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F(" Not detected"));
#endif
return false;
}
/*!
* @brief Write a buffer or two to the I2C device. Cannot be more than
* maxBufferSize() bytes.
* @param buffer Pointer to buffer of data to write. This is const to
* ensure the content of this buffer doesn't change.
* @param len Number of bytes from buffer to write
* @param prefix_buffer Pointer to optional array of data to write before
* buffer. Cannot be more than maxBufferSize() bytes. This is const to
* ensure the content of this buffer doesn't change.
* @param prefix_len Number of bytes from prefix buffer to write
* @param stop Whether to send an I2C STOP signal on write
* @return True if write was successful, otherwise false.
*/
bool Adafruit_I2CDevice::write(const uint8_t *buffer, size_t len, bool stop,
const uint8_t *prefix_buffer,
size_t prefix_len) {
if ((len + prefix_len) > maxBufferSize()) {
// currently not guaranteed to work if more than 32 bytes!
// we will need to find out if some platforms have larger
// I2C buffer sizes :/
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F("\tI2CDevice could not write such a large buffer"));
#endif
return false;
}
_wire->beginTransmission(_addr);
// Write the prefix data (usually an address)
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
if (_wire->write(prefix_buffer, prefix_len) != prefix_len) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F("\tI2CDevice failed to write"));
#endif
return false;
}
}
// Write the data itself
if (_wire->write(buffer, len) != len) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F("\tI2CDevice failed to write"));
#endif
return false;
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tI2CWRITE @ 0x"));
DEBUG_SERIAL.print(_addr, HEX);
DEBUG_SERIAL.print(F(" :: "));
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
for (uint16_t i = 0; i < prefix_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(prefix_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
}
}
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (i % 32 == 31) {
DEBUG_SERIAL.println();
}
}
if (stop) {
DEBUG_SERIAL.print("\tSTOP");
}
#endif
if (_wire->endTransmission(stop) == 0) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println();
// DEBUG_SERIAL.println("Sent!");
#endif
return true;
} else {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println("\tFailed to send!");
#endif
return false;
}
}
/*!
* @brief Read from I2C into a buffer from the I2C device.
* Cannot be more than maxBufferSize() bytes.
* @param buffer Pointer to buffer of data to read into
* @param len Number of bytes from buffer to read.
* @param stop Whether to send an I2C STOP signal on read
* @return True if read was successful, otherwise false.
*/
bool Adafruit_I2CDevice::read(uint8_t *buffer, size_t len, bool stop) {
size_t pos = 0;
while (pos < len) {
size_t read_len =
((len - pos) > maxBufferSize()) ? maxBufferSize() : (len - pos);
bool read_stop = (pos < (len - read_len)) ? false : stop;
if (!_read(buffer + pos, read_len, read_stop))
return false;
pos += read_len;
}
return true;
}
bool Adafruit_I2CDevice::_read(uint8_t *buffer, size_t len, bool stop) {
#if defined(TinyWireM_h)
size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len);
#elif defined(ARDUINO_ARCH_MEGAAVR)
size_t recv = _wire->requestFrom(_addr, len, stop);
#else
size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len, (uint8_t)stop);
#endif
if (recv != len) {
// Not enough data available to fulfill our obligation!
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tI2CDevice did not receive enough data: "));
DEBUG_SERIAL.println(recv);
#endif
return false;
}
for (uint16_t i = 0; i < len; i++) {
buffer[i] = _wire->read();
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tI2CREAD @ 0x"));
DEBUG_SERIAL.print(_addr, HEX);
DEBUG_SERIAL.print(F(" :: "));
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
return true;
}
/*!
* @brief Write some data, then read some data from I2C into another buffer.
* Cannot be more than maxBufferSize() bytes. The buffers can point to
* same/overlapping locations.
* @param write_buffer Pointer to buffer of data to write from
* @param write_len Number of bytes from buffer to write.
* @param read_buffer Pointer to buffer of data to read into.
* @param read_len Number of bytes from buffer to read.
* @param stop Whether to send an I2C STOP signal between the write and read
* @return True if write & read was successful, otherwise false.
*/
bool Adafruit_I2CDevice::write_then_read(const uint8_t *write_buffer,
size_t write_len, uint8_t *read_buffer,
size_t read_len, bool stop) {
if (!write(write_buffer, write_len, stop)) {
return false;
}
return read(read_buffer, read_len);
}
/*!
* @brief Returns the 7-bit address of this device
* @return The 7-bit address of this device
*/
uint8_t Adafruit_I2CDevice::address(void) { return _addr; }
/*!
* @brief Change the I2C clock speed to desired (relies on
* underlying Wire support!
* @param desiredclk The desired I2C SCL frequency
* @return True if this platform supports changing I2C speed.
* Not necessarily that the speed was achieved!
*/
bool Adafruit_I2CDevice::setSpeed(uint32_t desiredclk) {
#if defined(__AVR_ATmega328__) || \
defined(__AVR_ATmega328P__) // fix arduino core set clock
// calculate TWBR correctly
if ((F_CPU / 18) < desiredclk) {
#ifdef DEBUG_SERIAL
Serial.println(F("I2C.setSpeed too high."));
#endif
return false;
}
uint32_t atwbr = ((F_CPU / desiredclk) - 16) / 2;
if (atwbr > 16320) {
#ifdef DEBUG_SERIAL
Serial.println(F("I2C.setSpeed too low."));
#endif
return false;
}
if (atwbr <= 255) {
atwbr /= 1;
TWSR = 0x0;
} else if (atwbr <= 1020) {
atwbr /= 4;
TWSR = 0x1;
} else if (atwbr <= 4080) {
atwbr /= 16;
TWSR = 0x2;
} else { // if (atwbr <= 16320)
atwbr /= 64;
TWSR = 0x3;
}
TWBR = atwbr;
#ifdef DEBUG_SERIAL
Serial.print(F("TWSR prescaler = "));
Serial.println(pow(4, TWSR));
Serial.print(F("TWBR = "));
Serial.println(atwbr);
#endif
return true;
#elif (ARDUINO >= 157) && !defined(ARDUINO_STM32_FEATHER) && \
!defined(TinyWireM_h)
_wire->setClock(desiredclk);
return true;
#else
(void)desiredclk;
return false;
#endif
}

View File

@ -0,0 +1,36 @@
#ifndef Adafruit_I2CDevice_h
#define Adafruit_I2CDevice_h
#include <Arduino.h>
#include <Wire.h>
///< The class which defines how we will talk to this device over I2C
class Adafruit_I2CDevice {
public:
Adafruit_I2CDevice(uint8_t addr, TwoWire *theWire = &Wire);
uint8_t address(void);
bool begin(bool addr_detect = true);
void end(void);
bool detected(void);
bool read(uint8_t *buffer, size_t len, bool stop = true);
bool write(const uint8_t *buffer, size_t len, bool stop = true,
const uint8_t *prefix_buffer = nullptr, size_t prefix_len = 0);
bool write_then_read(const uint8_t *write_buffer, size_t write_len,
uint8_t *read_buffer, size_t read_len,
bool stop = false);
bool setSpeed(uint32_t desiredclk);
/*! @brief How many bytes we can read in a transaction
* @return The size of the Wire receive/transmit buffer */
size_t maxBufferSize() { return _maxBufferSize; }
private:
uint8_t _addr;
TwoWire *_wire;
bool _begun;
size_t _maxBufferSize;
bool _read(uint8_t *buffer, size_t len, bool stop);
};
#endif // Adafruit_I2CDevice_h

View File

@ -0,0 +1,10 @@
#ifndef _ADAFRUIT_I2C_REGISTER_H_
#define _ADAFRUIT_I2C_REGISTER_H_
#include <Adafruit_BusIO_Register.h>
#include <Arduino.h>
typedef Adafruit_BusIO_Register Adafruit_I2CRegister;
typedef Adafruit_BusIO_RegisterBits Adafruit_I2CRegisterBits;
#endif

View File

@ -0,0 +1,508 @@
#include "Adafruit_SPIDevice.h"
//#define DEBUG_SERIAL Serial
/*!
* @brief Create an SPI device with the given CS pin and settings
* @param cspin The arduino pin number to use for chip select
* @param freq The SPI clock frequency to use, defaults to 1MHz
* @param dataOrder The SPI data order to use for bits within each byte,
* defaults to SPI_BITORDER_MSBFIRST
* @param dataMode The SPI mode to use, defaults to SPI_MODE0
* @param theSPI The SPI bus to use, defaults to &theSPI
*/
Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, uint32_t freq,
BusIOBitOrder dataOrder,
uint8_t dataMode, SPIClass *theSPI) {
#ifdef BUSIO_HAS_HW_SPI
_cs = cspin;
_sck = _mosi = _miso = -1;
_spi = theSPI;
_begun = false;
_spiSetting = new SPISettings(freq, dataOrder, dataMode);
_freq = freq;
_dataOrder = dataOrder;
_dataMode = dataMode;
#else
// unused, but needed to suppress compiler warns
(void)cspin;
(void)freq;
(void)dataOrder;
(void)dataMode;
(void)theSPI;
#endif
}
/*!
* @brief Create an SPI device with the given CS pin and settings
* @param cspin The arduino pin number to use for chip select
* @param sckpin The arduino pin number to use for SCK
* @param misopin The arduino pin number to use for MISO, set to -1 if not
* used
* @param mosipin The arduino pin number to use for MOSI, set to -1 if not
* used
* @param freq The SPI clock frequency to use, defaults to 1MHz
* @param dataOrder The SPI data order to use for bits within each byte,
* defaults to SPI_BITORDER_MSBFIRST
* @param dataMode The SPI mode to use, defaults to SPI_MODE0
*/
Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, int8_t sckpin,
int8_t misopin, int8_t mosipin,
uint32_t freq, BusIOBitOrder dataOrder,
uint8_t dataMode) {
_cs = cspin;
_sck = sckpin;
_miso = misopin;
_mosi = mosipin;
#ifdef BUSIO_USE_FAST_PINIO
csPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(cspin));
csPinMask = digitalPinToBitMask(cspin);
if (mosipin != -1) {
mosiPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(mosipin));
mosiPinMask = digitalPinToBitMask(mosipin);
}
if (misopin != -1) {
misoPort = (BusIO_PortReg *)portInputRegister(digitalPinToPort(misopin));
misoPinMask = digitalPinToBitMask(misopin);
}
clkPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(sckpin));
clkPinMask = digitalPinToBitMask(sckpin);
#endif
_freq = freq;
_dataOrder = dataOrder;
_dataMode = dataMode;
_begun = false;
}
/*!
* @brief Release memory allocated in constructors
*/
Adafruit_SPIDevice::~Adafruit_SPIDevice() {
if (_spiSetting)
delete _spiSetting;
}
/*!
* @brief Initializes SPI bus and sets CS pin high
* @return Always returns true because there's no way to test success of SPI
* init
*/
bool Adafruit_SPIDevice::begin(void) {
if (_cs != -1) {
pinMode(_cs, OUTPUT);
digitalWrite(_cs, HIGH);
}
if (_spi) { // hardware SPI
#ifdef BUSIO_HAS_HW_SPI
_spi->begin();
#endif
} else {
pinMode(_sck, OUTPUT);
if ((_dataMode == SPI_MODE0) || (_dataMode == SPI_MODE1)) {
// idle low on mode 0 and 1
digitalWrite(_sck, LOW);
} else {
// idle high on mode 2 or 3
digitalWrite(_sck, HIGH);
}
if (_mosi != -1) {
pinMode(_mosi, OUTPUT);
digitalWrite(_mosi, HIGH);
}
if (_miso != -1) {
pinMode(_miso, INPUT);
}
}
_begun = true;
return true;
}
/*!
* @brief Transfer (send/receive) a buffer over hard/soft SPI, without
* transaction management
* @param buffer The buffer to send and receive at the same time
* @param len The number of bytes to transfer
*/
void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) {
//
// HARDWARE SPI
//
if (_spi) {
#ifdef BUSIO_HAS_HW_SPI
#if defined(SPARK)
_spi->transfer(buffer, buffer, len, nullptr);
#elif defined(STM32)
for (size_t i = 0; i < len; i++) {
_spi->transfer(buffer[i]);
}
#else
_spi->transfer(buffer, len);
#endif
return;
#endif
}
//
// SOFTWARE SPI
//
uint8_t startbit;
if (_dataOrder == SPI_BITORDER_LSBFIRST) {
startbit = 0x1;
} else {
startbit = 0x80;
}
bool towrite, lastmosi = !(buffer[0] & startbit);
uint8_t bitdelay_us = (1000000 / _freq) / 2;
for (size_t i = 0; i < len; i++) {
uint8_t reply = 0;
uint8_t send = buffer[i];
/*
Serial.print("\tSending software SPI byte 0x");
Serial.print(send, HEX);
Serial.print(" -> 0x");
*/
// Serial.print(send, HEX);
for (uint8_t b = startbit; b != 0;
b = (_dataOrder == SPI_BITORDER_LSBFIRST) ? b << 1 : b >> 1) {
if (bitdelay_us) {
delayMicroseconds(bitdelay_us);
}
if (_dataMode == SPI_MODE0 || _dataMode == SPI_MODE2) {
towrite = send & b;
if ((_mosi != -1) && (lastmosi != towrite)) {
#ifdef BUSIO_USE_FAST_PINIO
if (towrite)
*mosiPort = *mosiPort | mosiPinMask;
else
*mosiPort = *mosiPort & ~mosiPinMask;
#else
digitalWrite(_mosi, towrite);
#endif
lastmosi = towrite;
}
#ifdef BUSIO_USE_FAST_PINIO
*clkPort = *clkPort | clkPinMask; // Clock high
#else
digitalWrite(_sck, HIGH);
#endif
if (bitdelay_us) {
delayMicroseconds(bitdelay_us);
}
if (_miso != -1) {
#ifdef BUSIO_USE_FAST_PINIO
if (*misoPort & misoPinMask) {
#else
if (digitalRead(_miso)) {
#endif
reply |= b;
}
}
#ifdef BUSIO_USE_FAST_PINIO
*clkPort = *clkPort & ~clkPinMask; // Clock low
#else
digitalWrite(_sck, LOW);
#endif
} else { // if (_dataMode == SPI_MODE1 || _dataMode == SPI_MODE3)
#ifdef BUSIO_USE_FAST_PINIO
*clkPort = *clkPort | clkPinMask; // Clock high
#else
digitalWrite(_sck, HIGH);
#endif
if (bitdelay_us) {
delayMicroseconds(bitdelay_us);
}
if (_mosi != -1) {
#ifdef BUSIO_USE_FAST_PINIO
if (send & b)
*mosiPort = *mosiPort | mosiPinMask;
else
*mosiPort = *mosiPort & ~mosiPinMask;
#else
digitalWrite(_mosi, send & b);
#endif
}
#ifdef BUSIO_USE_FAST_PINIO
*clkPort = *clkPort & ~clkPinMask; // Clock low
#else
digitalWrite(_sck, LOW);
#endif
if (_miso != -1) {
#ifdef BUSIO_USE_FAST_PINIO
if (*misoPort & misoPinMask) {
#else
if (digitalRead(_miso)) {
#endif
reply |= b;
}
}
}
if (_miso != -1) {
buffer[i] = reply;
}
}
}
return;
}
/*!
* @brief Transfer (send/receive) one byte over hard/soft SPI, without
* transaction management
* @param send The byte to send
* @return The byte received while transmitting
*/
uint8_t Adafruit_SPIDevice::transfer(uint8_t send) {
uint8_t data = send;
transfer(&data, 1);
return data;
}
/*!
* @brief Manually begin a transaction (calls beginTransaction if hardware
* SPI)
*/
void Adafruit_SPIDevice::beginTransaction(void) {
if (_spi) {
#ifdef BUSIO_HAS_HW_SPI
_spi->beginTransaction(*_spiSetting);
#endif
}
}
/*!
* @brief Manually end a transaction (calls endTransaction if hardware SPI)
*/
void Adafruit_SPIDevice::endTransaction(void) {
if (_spi) {
#ifdef BUSIO_HAS_HW_SPI
_spi->endTransaction();
#endif
}
}
/*!
* @brief Assert/Deassert the CS pin if it is defined
* @param value The state the CS is set to
*/
void Adafruit_SPIDevice::setChipSelect(int value) {
if (_cs != -1) {
digitalWrite(_cs, value);
}
}
/*!
* @brief Write a buffer or two to the SPI device, with transaction
* management.
* @brief Manually begin a transaction (calls beginTransaction if hardware
* SPI) with asserting the CS pin
*/
void Adafruit_SPIDevice::beginTransactionWithAssertingCS() {
beginTransaction();
setChipSelect(LOW);
}
/*!
* @brief Manually end a transaction (calls endTransaction if hardware SPI)
* with deasserting the CS pin
*/
void Adafruit_SPIDevice::endTransactionWithDeassertingCS() {
setChipSelect(HIGH);
endTransaction();
}
/*!
* @brief Write a buffer or two to the SPI device, with transaction
* management.
* @param buffer Pointer to buffer of data to write
* @param len Number of bytes from buffer to write
* @param prefix_buffer Pointer to optional array of data to write before
* buffer.
* @param prefix_len Number of bytes from prefix buffer to write
* @return Always returns true because there's no way to test success of SPI
* writes
*/
bool Adafruit_SPIDevice::write(const uint8_t *buffer, size_t len,
const uint8_t *prefix_buffer,
size_t prefix_len) {
beginTransactionWithAssertingCS();
// do the writing
#if defined(ARDUINO_ARCH_ESP32)
if (_spi) {
if (prefix_len > 0) {
_spi->transferBytes((uint8_t *)prefix_buffer, nullptr, prefix_len);
}
if (len > 0) {
_spi->transferBytes((uint8_t *)buffer, nullptr, len);
}
} else
#endif
{
for (size_t i = 0; i < prefix_len; i++) {
transfer(prefix_buffer[i]);
}
for (size_t i = 0; i < len; i++) {
transfer(buffer[i]);
}
}
endTransactionWithDeassertingCS();
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: "));
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
for (uint16_t i = 0; i < prefix_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(prefix_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
}
}
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (i % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
return true;
}
/*!
* @brief Read from SPI into a buffer from the SPI device, with transaction
* management.
* @param buffer Pointer to buffer of data to read into
* @param len Number of bytes from buffer to read.
* @param sendvalue The 8-bits of data to write when doing the data read,
* defaults to 0xFF
* @return Always returns true because there's no way to test success of SPI
* writes
*/
bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
memset(buffer, sendvalue, len); // clear out existing buffer
beginTransactionWithAssertingCS();
transfer(buffer, len);
endTransactionWithDeassertingCS();
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Read: "));
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
return true;
}
/*!
* @brief Write some data, then read some data from SPI into another buffer,
* with transaction management. The buffers can point to same/overlapping
* locations. This does not transmit-receive at the same time!
* @param write_buffer Pointer to buffer of data to write from
* @param write_len Number of bytes from buffer to write.
* @param read_buffer Pointer to buffer of data to read into.
* @param read_len Number of bytes from buffer to read.
* @param sendvalue The 8-bits of data to write when doing the data read,
* defaults to 0xFF
* @return Always returns true because there's no way to test success of SPI
* writes
*/
bool Adafruit_SPIDevice::write_then_read(const uint8_t *write_buffer,
size_t write_len, uint8_t *read_buffer,
size_t read_len, uint8_t sendvalue) {
beginTransactionWithAssertingCS();
// do the writing
#if defined(ARDUINO_ARCH_ESP32)
if (_spi) {
if (write_len > 0) {
_spi->transferBytes((uint8_t *)write_buffer, nullptr, write_len);
}
} else
#endif
{
for (size_t i = 0; i < write_len; i++) {
transfer(write_buffer[i]);
}
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: "));
for (uint16_t i = 0; i < write_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(write_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (write_len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
// do the reading
for (size_t i = 0; i < read_len; i++) {
read_buffer[i] = transfer(sendvalue);
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Read: "));
for (uint16_t i = 0; i < read_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(read_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (read_len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
endTransactionWithDeassertingCS();
return true;
}
/*!
* @brief Write some data and read some data at the same time from SPI
* into the same buffer, with transaction management. This is basicaly a wrapper
* for transfer() with CS-pin and transaction management. This /does/
* transmit-receive at the same time!
* @param buffer Pointer to buffer of data to write/read to/from
* @param len Number of bytes from buffer to write/read.
* @return Always returns true because there's no way to test success of SPI
* writes
*/
bool Adafruit_SPIDevice::write_and_read(uint8_t *buffer, size_t len) {
beginTransactionWithAssertingCS();
transfer(buffer, len);
endTransactionWithDeassertingCS();
return true;
}

View File

@ -0,0 +1,143 @@
#ifndef Adafruit_SPIDevice_h
#define Adafruit_SPIDevice_h
#include <Arduino.h>
#if !defined(SPI_INTERFACES_COUNT) || \
(defined(SPI_INTERFACES_COUNT) && (SPI_INTERFACES_COUNT > 0))
// HW SPI available
#include <SPI.h>
#define BUSIO_HAS_HW_SPI
#else
// SW SPI ONLY
enum { SPI_MODE0, SPI_MODE1, SPI_MODE2, _SPI_MODE4 };
typedef uint8_t SPIClass;
#endif
// some modern SPI definitions don't have BitOrder enum
#if (defined(__AVR__) && !defined(ARDUINO_ARCH_MEGAAVR)) || \
defined(ESP8266) || defined(TEENSYDUINO) || defined(SPARK) || \
defined(ARDUINO_ARCH_SPRESENSE) || defined(MEGATINYCORE) || \
defined(DXCORE) || defined(ARDUINO_AVR_ATmega4809) || \
defined(ARDUINO_AVR_ATmega4808) || defined(ARDUINO_AVR_ATmega3209) || \
defined(ARDUINO_AVR_ATmega3208) || defined(ARDUINO_AVR_ATmega1609) || \
defined(ARDUINO_AVR_ATmega1608) || defined(ARDUINO_AVR_ATmega809) || \
defined(ARDUINO_AVR_ATmega808) || defined(ARDUINO_ARCH_ARC32) || \
defined(ARDUINO_ARCH_XMC) || defined(ARDUINO_SILABS)
typedef enum _BitOrder {
SPI_BITORDER_MSBFIRST = MSBFIRST,
SPI_BITORDER_LSBFIRST = LSBFIRST,
} BusIOBitOrder;
#elif defined(ESP32) || defined(__ASR6501__) || defined(__ASR6502__)
// some modern SPI definitions don't have BitOrder enum and have different SPI
// mode defines
typedef enum _BitOrder {
SPI_BITORDER_MSBFIRST = SPI_MSBFIRST,
SPI_BITORDER_LSBFIRST = SPI_LSBFIRST,
} BusIOBitOrder;
#else
// Some platforms have a BitOrder enum but its named MSBFIRST/LSBFIRST
#define SPI_BITORDER_MSBFIRST MSBFIRST
#define SPI_BITORDER_LSBFIRST LSBFIRST
typedef BitOrder BusIOBitOrder;
#endif
#if defined(__IMXRT1062__) // Teensy 4.x
// *Warning* I disabled the usage of FAST_PINIO as the set/clear operations
// used in the cpp file are not atomic and can effect multiple IO pins
// and if an interrupt happens in between the time the code reads the register
// and writes out the updated value, that changes one or more other IO pins
// on that same IO port, those change will be clobbered when the updated
// values are written back. A fast version can be implemented that uses the
// ports set and clear registers which are atomic.
// typedef volatile uint32_t BusIO_PortReg;
// typedef uint32_t BusIO_PortMask;
//#define BUSIO_USE_FAST_PINIO
#elif defined(ARDUINO_ARCH_XMC)
#undef BUSIO_USE_FAST_PINIO
#elif defined(__AVR__) || defined(TEENSYDUINO)
typedef volatile uint8_t BusIO_PortReg;
typedef uint8_t BusIO_PortMask;
#define BUSIO_USE_FAST_PINIO
#elif defined(ESP8266) || defined(ESP32) || defined(__SAM3X8E__) || \
defined(ARDUINO_ARCH_SAMD)
typedef volatile uint32_t BusIO_PortReg;
typedef uint32_t BusIO_PortMask;
#define BUSIO_USE_FAST_PINIO
#elif (defined(__arm__) || defined(ARDUINO_FEATHER52)) && \
!defined(ARDUINO_ARCH_MBED) && !defined(ARDUINO_ARCH_RP2040) && \
!defined(ARDUINO_SILABS)
typedef volatile uint32_t BusIO_PortReg;
typedef uint32_t BusIO_PortMask;
#if !defined(__ASR6501__) && !defined(__ASR6502__)
#define BUSIO_USE_FAST_PINIO
#endif
#else
#undef BUSIO_USE_FAST_PINIO
#endif
/**! The class which defines how we will talk to this device over SPI **/
class Adafruit_SPIDevice {
public:
#ifdef BUSIO_HAS_HW_SPI
Adafruit_SPIDevice(int8_t cspin, uint32_t freq = 1000000,
BusIOBitOrder dataOrder = SPI_BITORDER_MSBFIRST,
uint8_t dataMode = SPI_MODE0, SPIClass *theSPI = &SPI);
#else
Adafruit_SPIDevice(int8_t cspin, uint32_t freq = 1000000,
BusIOBitOrder dataOrder = SPI_BITORDER_MSBFIRST,
uint8_t dataMode = SPI_MODE0, SPIClass *theSPI = nullptr);
#endif
Adafruit_SPIDevice(int8_t cspin, int8_t sck, int8_t miso, int8_t mosi,
uint32_t freq = 1000000,
BusIOBitOrder dataOrder = SPI_BITORDER_MSBFIRST,
uint8_t dataMode = SPI_MODE0);
~Adafruit_SPIDevice();
bool begin(void);
bool read(uint8_t *buffer, size_t len, uint8_t sendvalue = 0xFF);
bool write(const uint8_t *buffer, size_t len,
const uint8_t *prefix_buffer = nullptr, size_t prefix_len = 0);
bool write_then_read(const uint8_t *write_buffer, size_t write_len,
uint8_t *read_buffer, size_t read_len,
uint8_t sendvalue = 0xFF);
bool write_and_read(uint8_t *buffer, size_t len);
uint8_t transfer(uint8_t send);
void transfer(uint8_t *buffer, size_t len);
void beginTransaction(void);
void endTransaction(void);
void beginTransactionWithAssertingCS();
void endTransactionWithDeassertingCS();
private:
#ifdef BUSIO_HAS_HW_SPI
SPIClass *_spi = nullptr;
SPISettings *_spiSetting = nullptr;
#else
uint8_t *_spi = nullptr;
uint8_t *_spiSetting = nullptr;
#endif
uint32_t _freq;
BusIOBitOrder _dataOrder;
uint8_t _dataMode;
void setChipSelect(int value);
int8_t _cs, _sck, _mosi, _miso;
#ifdef BUSIO_USE_FAST_PINIO
BusIO_PortReg *mosiPort, *clkPort, *misoPort, *csPort;
BusIO_PortMask mosiPinMask, misoPinMask, clkPinMask, csPinMask;
#endif
bool _begun;
};
#endif // Adafruit_SPIDevice_h

View File

@ -0,0 +1,11 @@
# Adafruit Bus IO Library
# https://github.com/adafruit/Adafruit_BusIO
# MIT License
cmake_minimum_required(VERSION 3.5)
idf_component_register(SRCS "Adafruit_I2CDevice.cpp" "Adafruit_BusIO_Register.cpp" "Adafruit_SPIDevice.cpp"
INCLUDE_DIRS "."
REQUIRES arduino)
project(Adafruit_BusIO)

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Adafruit Industries
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.

View File

@ -0,0 +1,8 @@
# Adafruit Bus IO Library [![Build Status](https://github.com/adafruit/Adafruit_BusIO/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_BusIO/actions)
This is a helper library to abstract away I2C & SPI transactions and registers
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
MIT license, all text above must be included in any redistribution

View File

@ -0,0 +1 @@
COMPONENT_ADD_INCLUDEDIRS = .

View File

@ -0,0 +1,219 @@
/*
Advanced example of using bstracted transport for reading and writing
register data from a UART-based device such as a TMC2209
Written with help by Claude!
https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c (at this time
chats are not shareable :(
*/
#include "Adafruit_BusIO_Register.h"
#include "Adafruit_GenericDevice.h"
// Debugging macros
#define DEBUG_SERIAL Serial
#ifdef DEBUG_SERIAL
#define DEBUG_PRINT(x) DEBUG_SERIAL.print(x)
#define DEBUG_PRINTLN(x) DEBUG_SERIAL.println(x)
#define DEBUG_PRINT_HEX(x) \
do { \
if (x < 0x10) \
DEBUG_SERIAL.print('0'); \
DEBUG_SERIAL.print(x, HEX); \
DEBUG_SERIAL.print(' '); \
} while (0)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINT_HEX(x)
#endif
#define TMC2209_IOIN 0x06
class TMC2209_UART {
private:
Stream *_uart_stream;
uint8_t _addr;
static bool uart_read(void *thiz, uint8_t *buffer, size_t len) {
TMC2209_UART *dev = (TMC2209_UART *)thiz;
uint16_t timeout = 100;
while (dev->_uart_stream->available() < len && timeout--) {
delay(1);
}
if (timeout == 0) {
DEBUG_PRINTLN("Read timeout!");
return false;
}
DEBUG_PRINT("Reading: ");
for (size_t i = 0; i < len; i++) {
buffer[i] = dev->_uart_stream->read();
DEBUG_PRINT_HEX(buffer[i]);
}
DEBUG_PRINTLN("");
return true;
}
static bool uart_write(void *thiz, const uint8_t *buffer, size_t len) {
TMC2209_UART *dev = (TMC2209_UART *)thiz;
DEBUG_PRINT("Writing: ");
for (size_t i = 0; i < len; i++) {
DEBUG_PRINT_HEX(buffer[i]);
}
DEBUG_PRINTLN("");
dev->_uart_stream->write(buffer, len);
return true;
}
static bool uart_readreg(void *thiz, uint8_t *addr_buf, uint8_t addrsiz,
uint8_t *data, uint16_t datalen) {
TMC2209_UART *dev = (TMC2209_UART *)thiz;
while (dev->_uart_stream->available())
dev->_uart_stream->read();
uint8_t packet[4] = {0x05, uint8_t(dev->_addr << 1), addr_buf[0], 0x00};
packet[3] = calcCRC(packet, 3);
if (!uart_write(thiz, packet, 4))
return false;
// Read back echo
uint8_t echo[4];
if (!uart_read(thiz, echo, 4))
return false;
// Verify echo
for (uint8_t i = 0; i < 4; i++) {
if (echo[i] != packet[i]) {
DEBUG_PRINTLN("Echo mismatch");
return false;
}
}
uint8_t response[8]; // sync + 0xFF + reg + 4 data bytes + CRC
if (!uart_read(thiz, response, 8))
return false;
// Verify response
if (response[0] != 0x05) {
DEBUG_PRINTLN("Invalid sync byte");
return false;
}
if (response[1] != 0xFF) {
DEBUG_PRINTLN("Invalid reply address");
return false;
}
if (response[2] != addr_buf[0]) {
DEBUG_PRINTLN("Register mismatch");
return false;
}
uint8_t crc = calcCRC(response, 7);
if (crc != response[7]) {
DEBUG_PRINTLN("CRC mismatch");
return false;
}
memcpy(data, &response[3], 4);
return true;
}
static bool uart_writereg(void *thiz, uint8_t *addr_buf, uint8_t addrsiz,
const uint8_t *data, uint16_t datalen) {
TMC2209_UART *dev = (TMC2209_UART *)thiz;
while (dev->_uart_stream->available())
dev->_uart_stream->read();
uint8_t packet[8] = {0x05,
uint8_t(dev->_addr << 1),
uint8_t(addr_buf[0] | 0x80),
data[0],
data[1],
data[2],
data[3],
0x00};
packet[7] = calcCRC(packet, 7);
if (!uart_write(thiz, packet, 8))
return false;
uint8_t echo[8];
if (!uart_read(thiz, echo, 8))
return false;
for (uint8_t i = 0; i < 8; i++) {
if (echo[i] != packet[i]) {
DEBUG_PRINTLN("Write echo mismatch");
return false;
}
}
return true;
}
static uint8_t calcCRC(uint8_t *data, uint8_t length) {
uint8_t crc = 0;
for (uint8_t i = 0; i < length; i++) {
uint8_t currentByte = data[i];
for (uint8_t j = 0; j < 8; j++) {
if ((crc >> 7) ^ (currentByte & 0x01)) {
crc = (crc << 1) ^ 0x07;
} else {
crc = crc << 1;
}
currentByte = currentByte >> 1;
}
}
return crc;
}
public:
TMC2209_UART(Stream *serial, uint8_t addr)
: _uart_stream(serial), _addr(addr) {}
Adafruit_GenericDevice *createDevice() {
return new Adafruit_GenericDevice(this, uart_read, uart_write, uart_readreg,
uart_writereg);
}
};
void setup() {
Serial.begin(115200);
while (!Serial)
;
delay(100);
Serial.println("TMC2209 Generic Device register read/write test!");
Serial1.begin(115200);
TMC2209_UART uart(&Serial1, 0);
Adafruit_GenericDevice *device = uart.createDevice();
device->begin();
// Create register object for IOIN
Adafruit_BusIO_Register ioin_reg(device,
TMC2209_IOIN, // device and register address
4, // width = 4 bytes
MSBFIRST, // byte order
1); // address width = 1 byte
Serial.print("IOIN = 0x");
Serial.println(ioin_reg.read(), HEX);
// Create RegisterBits for VERSION field (bits 31:24)
Adafruit_BusIO_RegisterBits version_bits(
&ioin_reg, 8, 24); // 8 bits wide, starting at bit 24
Serial.println("Reading VERSION...");
uint8_t version = version_bits.read();
Serial.print("VERSION = 0x");
Serial.println(version, HEX);
}
void loop() { delay(1000); }

View File

@ -0,0 +1,98 @@
/*
Abstracted transport for reading and writing data from a UART-based
device such as a TMC2209
Written with help by Claude!
https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c (at this time
chats are not shareable :(
*/
#include "Adafruit_GenericDevice.h"
/**
* Basic UART device class that demonstrates using GenericDevice with a Stream
* interface. This example shows how to wrap a Stream (like HardwareSerial or
* SoftwareSerial) with read/write callbacks that can be used by BusIO's
* register functions.
*/
class UARTDevice {
public:
UARTDevice(Stream *serial) : _serial(serial) {}
// Static callback for writing data to UART
// Called by GenericDevice when data needs to be sent
static bool uart_write(void *thiz, const uint8_t *buffer, size_t len) {
UARTDevice *dev = (UARTDevice *)thiz;
dev->_serial->write(buffer, len);
return true;
}
// Static callback for reading data from UART
// Includes timeout and will return false if not enough data available
static bool uart_read(void *thiz, uint8_t *buffer, size_t len) {
UARTDevice *dev = (UARTDevice *)thiz;
uint16_t timeout = 100;
while (dev->_serial->available() < len && timeout--) {
delay(1);
}
if (timeout == 0) {
return false;
}
for (size_t i = 0; i < len; i++) {
buffer[i] = dev->_serial->read();
}
return true;
}
// Create a GenericDevice instance using our callbacks
Adafruit_GenericDevice *createDevice() {
return new Adafruit_GenericDevice(this, uart_read, uart_write);
}
private:
Stream *_serial; // Underlying Stream instance (HardwareSerial, etc)
};
void setup() {
Serial.begin(115200);
while (!Serial)
;
delay(100);
Serial.println("Generic Device test!");
// Initialize UART for device communication
Serial1.begin(115200);
// Create UART wrapper and BusIO device
UARTDevice uart(&Serial1);
Adafruit_GenericDevice *device = uart.createDevice();
device->begin();
// Test write/read cycle
uint8_t write_buf[4] = {0x5, 0x0, 0x0, 0x48};
uint8_t read_buf[8];
Serial.println("Writing data...");
if (!device->write(write_buf, 4)) {
Serial.println("Write failed!");
return;
}
Serial.println("Reading response...");
if (!device->read(read_buf, 8)) {
Serial.println("Read failed!");
return;
}
// Print response bytes
Serial.print("Got response: ");
for (int i = 0; i < 8; i++) {
Serial.print("0x");
Serial.print(read_buf[i], HEX);
Serial.print(" ");
}
Serial.println();
}
void loop() { delay(1000); }

View File

@ -0,0 +1,21 @@
#include <Adafruit_I2CDevice.h>
Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(0x10);
void setup() {
while (!Serial) { delay(10); }
Serial.begin(115200);
Serial.println("I2C address detection test");
if (!i2c_dev.begin()) {
Serial.print("Did not find device at 0x");
Serial.println(i2c_dev.address(), HEX);
while (1);
}
Serial.print("Device found on address 0x");
Serial.println(i2c_dev.address(), HEX);
}
void loop() {
}

View File

@ -0,0 +1,41 @@
#include <Adafruit_I2CDevice.h>
#define I2C_ADDRESS 0x60
Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(I2C_ADDRESS);
void setup() {
while (!Serial) { delay(10); }
Serial.begin(115200);
Serial.println("I2C device read and write test");
if (!i2c_dev.begin()) {
Serial.print("Did not find device at 0x");
Serial.println(i2c_dev.address(), HEX);
while (1);
}
Serial.print("Device found on address 0x");
Serial.println(i2c_dev.address(), HEX);
uint8_t buffer[32];
// Try to read 32 bytes
i2c_dev.read(buffer, 32);
Serial.print("Read: ");
for (uint8_t i=0; i<32; i++) {
Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
}
Serial.println();
// read a register by writing first, then reading
buffer[0] = 0x0C; // we'll reuse the same buffer
i2c_dev.write_then_read(buffer, 1, buffer, 2, false);
Serial.print("Write then Read: ");
for (uint8_t i=0; i<2; i++) {
Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
}
Serial.println();
}
void loop() {
}

View File

@ -0,0 +1,38 @@
#include <Adafruit_I2CDevice.h>
#include <Adafruit_BusIO_Register.h>
#define I2C_ADDRESS 0x60
Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(I2C_ADDRESS);
void setup() {
while (!Serial) { delay(10); }
Serial.begin(115200);
Serial.println("I2C device register test");
if (!i2c_dev.begin()) {
Serial.print("Did not find device at 0x");
Serial.println(i2c_dev.address(), HEX);
while (1);
}
Serial.print("Device found on address 0x");
Serial.println(i2c_dev.address(), HEX);
Adafruit_BusIO_Register id_reg = Adafruit_BusIO_Register(&i2c_dev, 0x0C, 2, LSBFIRST);
uint16_t id;
id_reg.read(&id);
Serial.print("ID register = 0x"); Serial.println(id, HEX);
Adafruit_BusIO_Register thresh_reg = Adafruit_BusIO_Register(&i2c_dev, 0x01, 2, LSBFIRST);
uint16_t thresh;
thresh_reg.read(&thresh);
Serial.print("Initial threshold register = 0x"); Serial.println(thresh, HEX);
thresh_reg.write(~thresh);
Serial.print("Post threshold register = 0x"); Serial.println(thresh_reg.read(), HEX);
}
void loop() {
}

View File

@ -0,0 +1,38 @@
#include <Adafruit_BusIO_Register.h>
// Define which interface to use by setting the unused interface to NULL!
#define SPIDEVICE_CS 10
Adafruit_SPIDevice *spi_dev = NULL; // new Adafruit_SPIDevice(SPIDEVICE_CS);
#define I2C_ADDRESS 0x5D
Adafruit_I2CDevice *i2c_dev = new Adafruit_I2CDevice(I2C_ADDRESS);
void setup() {
while (!Serial) { delay(10); }
Serial.begin(115200);
Serial.println("I2C or SPI device register test");
if (spi_dev && !spi_dev->begin()) {
Serial.println("Could not initialize SPI device");
}
if (i2c_dev) {
if (i2c_dev->begin()) {
Serial.print("Device found on I2C address 0x");
Serial.println(i2c_dev->address(), HEX);
} else {
Serial.print("Did not find I2C device at 0x");
Serial.println(i2c_dev->address(), HEX);
}
}
Adafruit_BusIO_Register id_reg = Adafruit_BusIO_Register(i2c_dev, spi_dev, ADDRBIT8_HIGH_TOREAD, 0x0F);
uint8_t id=0;
id_reg.read(&id);
Serial.print("ID register = 0x"); Serial.println(id, HEX);
}
void loop() {
}

View File

@ -0,0 +1,29 @@
#include <Adafruit_SPIDevice.h>
#define SPIDEVICE_CS 10
Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 100000, SPI_BITORDER_MSBFIRST, SPI_MODE1);
//Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 13, 12, 11, 100000, SPI_BITORDER_MSBFIRST, SPI_MODE1);
void setup() {
while (!Serial) { delay(10); }
Serial.begin(115200);
Serial.println("SPI device mode test");
if (!spi_dev.begin()) {
Serial.println("Could not initialize SPI device");
while (1);
}
}
void loop() {
Serial.println("\n\nTransfer test");
for (uint16_t x=0; x<=0xFF; x++) {
uint8_t i = x;
Serial.print("0x"); Serial.print(i, HEX);
spi_dev.read(&i, 1, i);
Serial.print("/"); Serial.print(i, HEX);
Serial.print(", ");
delay(25);
}
}

View File

@ -0,0 +1,39 @@
#include <Adafruit_SPIDevice.h>
#define SPIDEVICE_CS 10
Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS);
void setup() {
while (!Serial) { delay(10); }
Serial.begin(115200);
Serial.println("SPI device read and write test");
if (!spi_dev.begin()) {
Serial.println("Could not initialize SPI device");
while (1);
}
uint8_t buffer[32];
// Try to read 32 bytes
spi_dev.read(buffer, 32);
Serial.print("Read: ");
for (uint8_t i=0; i<32; i++) {
Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
}
Serial.println();
// read a register by writing first, then reading
buffer[0] = 0x8F; // we'll reuse the same buffer
spi_dev.write_then_read(buffer, 1, buffer, 2, false);
Serial.print("Write then Read: ");
for (uint8_t i=0; i<2; i++) {
Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
}
Serial.println();
}
void loop() {
}

View File

@ -0,0 +1,192 @@
/***************************************************
This is an example for how to use Adafruit_BusIO_RegisterBits from Adafruit_BusIO library.
Designed specifically to work with the Adafruit RTD Sensor
----> https://www.adafruit.com/products/3328
uisng a MAX31865 RTD-to-Digital Converter
----> https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf
This sensor uses SPI to communicate, 4 pins are required to
interface.
A fifth pin helps to detect when a new conversion is ready.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Example written (2020/3) by Andreas Hardtung/AnHard.
BSD license, all text above must be included in any redistribution
****************************************************/
#include <Adafruit_BusIO_Register.h>
#include <Adafruit_SPIDevice.h>
#define MAX31865_SPI_SPEED (5000000)
#define MAX31865_SPI_BITORDER (SPI_BITORDER_MSBFIRST)
#define MAX31865_SPI_MODE (SPI_MODE1)
#define MAX31865_SPI_CS (10)
#define MAX31865_READY_PIN (2)
Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice( MAX31865_SPI_CS, MAX31865_SPI_SPEED, MAX31865_SPI_BITORDER, MAX31865_SPI_MODE, &SPI); // Hardware SPI
// Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice( MAX31865_SPI_CS, 13, 12, 11, MAX31865_SPI_SPEED, MAX31865_SPI_BITORDER, MAX31865_SPI_MODE); // Software SPI
// MAX31865 chip related *********************************************************************************************
Adafruit_BusIO_Register config_reg = Adafruit_BusIO_Register(&spi_dev, 0x00, ADDRBIT8_HIGH_TOWRITE, 1, MSBFIRST);
Adafruit_BusIO_RegisterBits bias_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 7);
Adafruit_BusIO_RegisterBits auto_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 6);
Adafruit_BusIO_RegisterBits oneS_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 5);
Adafruit_BusIO_RegisterBits wire_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 4);
Adafruit_BusIO_RegisterBits faultT_bits = Adafruit_BusIO_RegisterBits(&config_reg, 2, 2);
Adafruit_BusIO_RegisterBits faultR_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 1);
Adafruit_BusIO_RegisterBits fi50hz_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 0);
Adafruit_BusIO_Register rRatio_reg = Adafruit_BusIO_Register(&spi_dev, 0x01, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
Adafruit_BusIO_RegisterBits rRatio_bits = Adafruit_BusIO_RegisterBits(&rRatio_reg, 15, 1);
Adafruit_BusIO_RegisterBits fault_bit = Adafruit_BusIO_RegisterBits(&rRatio_reg, 1, 0);
Adafruit_BusIO_Register maxRratio_reg = Adafruit_BusIO_Register(&spi_dev, 0x03, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
Adafruit_BusIO_RegisterBits maxRratio_bits = Adafruit_BusIO_RegisterBits(&maxRratio_reg, 15, 1);
Adafruit_BusIO_Register minRratio_reg = Adafruit_BusIO_Register(&spi_dev, 0x05, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
Adafruit_BusIO_RegisterBits minRratio_bits = Adafruit_BusIO_RegisterBits(&minRratio_reg, 15, 1);
Adafruit_BusIO_Register fault_reg = Adafruit_BusIO_Register(&spi_dev, 0x07, ADDRBIT8_HIGH_TOWRITE, 1, MSBFIRST);
Adafruit_BusIO_RegisterBits range_high_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 7);
Adafruit_BusIO_RegisterBits range_low_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 6);
Adafruit_BusIO_RegisterBits refin_high_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 5);
Adafruit_BusIO_RegisterBits refin_low_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 4);
Adafruit_BusIO_RegisterBits rtdin_low_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 3);
Adafruit_BusIO_RegisterBits voltage_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 2);
// Print the details of the configuration register.
void printConfig( void ) {
Serial.print("BIAS: "); if (bias_bit.read() ) Serial.print("ON"); else Serial.print("OFF");
Serial.print(", AUTO: "); if (auto_bit.read() ) Serial.print("ON"); else Serial.print("OFF");
Serial.print(", ONES: "); if (oneS_bit.read() ) Serial.print("ON"); else Serial.print("OFF");
Serial.print(", WIRE: "); if (wire_bit.read() ) Serial.print("3"); else Serial.print("2/4");
Serial.print(", FAULTCLEAR: "); if (faultR_bit.read() ) Serial.print("ON"); else Serial.print("OFF");
Serial.print(", "); if (fi50hz_bit.read() ) Serial.print("50HZ"); else Serial.print("60HZ");
Serial.println();
}
// Check and print faults. Then clear them.
void checkFaults( void ) {
if (fault_bit.read()) {
Serial.print("MAX: "); Serial.println(maxRratio_bits.read());
Serial.print("VAL: "); Serial.println( rRatio_bits.read());
Serial.print("MIN: "); Serial.println(minRratio_bits.read());
if (range_high_fault_bit.read() ) Serial.println("Range high fault");
if ( range_low_fault_bit.read() ) Serial.println("Range low fault");
if (refin_high_fault_bit.read() ) Serial.println("REFIN high fault");
if ( refin_low_fault_bit.read() ) Serial.println("REFIN low fault");
if ( rtdin_low_fault_bit.read() ) Serial.println("RTDIN low fault");
if ( voltage_fault_bit.read() ) Serial.println("Voltage fault");
faultR_bit.write(1); // clear fault
}
}
void setup() {
#if (MAX31865_1_READY_PIN != -1)
pinMode(MAX31865_READY_PIN ,INPUT_PULLUP);
#endif
while (!Serial) { delay(10); }
Serial.begin(115200);
Serial.println("SPI Adafruit_BusIO_RegisterBits test on MAX31865");
if (!spi_dev.begin()) {
Serial.println("Could not initialize SPI device");
while (1);
}
// Set up for automode 50Hz. We don't care about selfheating. We want the highest possible sampling rate.
auto_bit.write(0); // Don't switch filtermode while auto_mode is on.
fi50hz_bit.write(1); // Set filter to 50Hz mode.
faultR_bit.write(1); // Clear faults.
bias_bit.write(1); // In automode we want to have the bias current always on.
delay(5); // Wait until bias current settles down.
// 10.5 time constants of the input RC network is required.
// 10ms worst case for 10kω reference resistor and a 0.1µF capacitor across the RTD inputs.
// Adafruit Module has 0.1µF and only 430/4300ω So here 0.43/4.3ms
auto_bit.write(1); // Now we can set automode. Automatically starting first conversion.
// Test the READY_PIN
#if (defined( MAX31865_READY_PIN ) && (MAX31865_READY_PIN != -1))
int i = 0;
while (digitalRead(MAX31865_READY_PIN) && i++ <= 100) { delay(1); }
if (i >= 100) {
Serial.print("ERROR: Max31865 Pin detection does not work. PIN:");
Serial.println(MAX31865_READY_PIN);
}
#else
delay(100);
#endif
// Set ratio range.
// Setting the temperatures would need some more calculation - not related to Adafruit_BusIO_RegisterBits.
uint16_t ratio = rRatio_bits.read();
maxRratio_bits.write( (ratio < 0x8fffu-1000u) ? ratio + 1000u : 0x8fffu );
minRratio_bits.write( (ratio > 1000u) ? ratio - 1000u : 0u );
printConfig();
checkFaults();
}
void loop() {
#if (defined( MAX31865_READY_PIN ) && (MAX31865_1_READY_PIN != -1))
// Is conversion ready?
if (!digitalRead(MAX31865_READY_PIN))
#else
// Warant conversion is ready.
delay(21); // 21ms for 50Hz-mode. 19ms in 60Hz-mode.
#endif
{
// Read ratio, calculate temperature, scale, filter and print.
Serial.println( rRatio2C( rRatio_bits.read() ) * 100.0f, 0); // Temperature scaled by 100
// Check, print, clear faults.
checkFaults();
}
// Do something else.
//delay(15000);
}
// Module/Sensor related. Here Adafruit PT100 module with a 2_Wire PT100 Class C *****************************
float rRatio2C(uint16_t ratio) {
// A simple linear conversion.
const float R0 = 100.0f;
const float Rref = 430.0f;
const float alphaPT = 0.003850f;
const float ADCmax = (1u << 15) - 1.0f;
const float rscale = Rref / ADCmax;
// Measured temperature in boiling water 101.08°C with factor a = 1 and b = 0. Rref and MAX at about 22±2°C.
// Measured temperature in ice/water bath 0.76°C with factor a = 1 and b = 0. Rref and MAX at about 22±2°C.
//const float a = 1.0f / (alphaPT * R0);
const float a = (100.0f/101.08f) / (alphaPT * R0);
//const float b = 0.0f; // 101.08
const float b = -0.76f; // 100.32 > 101.08
return filterRing( ((ratio * rscale) - R0) * a + b );
}
// General purpose *********************************************************************************************
#define RINGLENGTH 250
float filterRing( float newVal ) {
static float ring[RINGLENGTH] = { 0.0 };
static uint8_t ringIndex = 0;
static bool ringFull = false;
if ( ringIndex == RINGLENGTH ) { ringFull = true; ringIndex = 0; }
ring[ringIndex] = newVal;
uint8_t loopEnd = (ringFull) ? RINGLENGTH : ringIndex + 1;
float ringSum = 0.0f;
for (uint8_t i = 0; i < loopEnd; i++) ringSum += ring[i];
ringIndex++;
return ringSum / loopEnd;
}

View File

@ -0,0 +1,34 @@
#include <Adafruit_BusIO_Register.h>
#include <Adafruit_SPIDevice.h>
#define SPIDEVICE_CS 10
Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS);
void setup() {
while (!Serial) { delay(10); }
Serial.begin(115200);
Serial.println("SPI device register test");
if (!spi_dev.begin()) {
Serial.println("Could not initialize SPI device");
while (1);
}
Adafruit_BusIO_Register id_reg = Adafruit_BusIO_Register(&spi_dev, 0x0F, ADDRBIT8_HIGH_TOREAD);
uint8_t id = 0;
id_reg.read(&id);
Serial.print("ID register = 0x"); Serial.println(id, HEX);
Adafruit_BusIO_Register thresh_reg = Adafruit_BusIO_Register(&spi_dev, 0x0C, ADDRBIT8_HIGH_TOREAD, 2, LSBFIRST);
uint16_t thresh = 0;
thresh_reg.read(&thresh);
Serial.print("Initial threshold register = 0x"); Serial.println(thresh, HEX);
thresh_reg.write(~thresh);
Serial.print("Post threshold register = 0x"); Serial.println(thresh_reg.read(), HEX);
}
void loop() {
}

View File

@ -0,0 +1,9 @@
; name=Adafruit BusIO
; version=1.15.0
; author=Adafruit
; maintainer=Adafruit <info@adafruit.com>
; sentence=This is a library for abstracting away I2C and SPI interfacing
; paragraph=This is a library for abstracting away I2C and SPI interfacing
; category=Signal Input/Output
; url=https://github.com/adafruit/Adafruit_BusIO
; architectures=*

View 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**

View 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.

View 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 MLX90614 Arduino Library"
run: bash ci/doxy_gen_and_deploy.sh

View File

@ -0,0 +1 @@
{"type": "library", "name": "Adafruit MLX90614 Library", "version": "2.1.5", "spec": {"owner": "adafruit", "id": 782, "name": "Adafruit MLX90614 Library", "requirements": null, "uri": null}}

View File

@ -0,0 +1,175 @@
/***************************************************
This is a library for the MLX90614 Temp Sensor
Designed specifically to work with the MLX90614 sensors in the
adafruit shop
----> https://www.adafruit.com/products/1747 (3V)
----> https://www.adafruit.com/products/1748 (5V)
These sensors use I2C to communicate, 2 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include "Adafruit_MLX90614.h"
Adafruit_MLX90614::~Adafruit_MLX90614() {
if (i2c_dev)
delete i2c_dev;
}
/**
* @brief Begin the I2C connection
* @param addr I2C address for the device.
* @param wire Pointer to Wire instance
* @return True if the device was successfully initialized, otherwise false.
*/
bool Adafruit_MLX90614::begin(uint8_t addr, TwoWire *wire) {
_addr = addr; // needed for CRC
if (i2c_dev)
delete i2c_dev;
i2c_dev = new Adafruit_I2CDevice(addr, wire);
return i2c_dev->begin();
}
/**
* @brief Read the raw value from the emissivity register
*
* @return uint16_t The unscaled emissivity value or '0' if reading failed
*/
uint16_t Adafruit_MLX90614::readEmissivityReg(void) {
return read16(MLX90614_EMISS);
}
/**
* @brief Write the raw unscaled emissivity value to the emissivity register
*
* @param ereg The unscaled emissivity value
*/
void Adafruit_MLX90614::writeEmissivityReg(uint16_t ereg) {
write16(MLX90614_EMISS, 0); // erase
delay(10);
write16(MLX90614_EMISS, ereg);
delay(10);
}
/**
* @brief Read the emissivity value from the sensor's register and scale
*
* @return double The emissivity value, ranging from 0.1 - 1.0 or NAN if reading
* failed
*/
double Adafruit_MLX90614::readEmissivity(void) {
uint16_t ereg = read16(MLX90614_EMISS);
if (ereg == 0)
return NAN;
return ((double)ereg) / 65535.0;
}
/**
* @brief Set the emissivity value
*
* @param emissivity The emissivity value to use, between 0.1 and 1.0
*/
void Adafruit_MLX90614::writeEmissivity(double emissivity) {
uint16_t ereg = (uint16_t)(0xffff * emissivity);
writeEmissivityReg(ereg);
}
/**
* @brief Get the current temperature of an object in degrees Farenheit
*
* @return double The temperature in degrees Farenheit or NAN if reading failed
*/
double Adafruit_MLX90614::readObjectTempF(void) {
return (readTemp(MLX90614_TOBJ1) * 9 / 5) + 32;
}
/**
* @brief Get the current ambient temperature in degrees Farenheit
*
* @return double The temperature in degrees Farenheit or NAN if reading failed
*/
double Adafruit_MLX90614::readAmbientTempF(void) {
return (readTemp(MLX90614_TA) * 9 / 5) + 32;
}
/**
* @brief Get the current temperature of an object in degrees Celcius
*
* @return double The temperature in degrees Celcius or NAN if reading failed
*/
double Adafruit_MLX90614::readObjectTempC(void) {
return readTemp(MLX90614_TOBJ1);
}
/**
* @brief Get the current ambient temperature in degrees Celcius
*
* @return double The temperature in degrees Celcius or NAN if reading failed
*/
double Adafruit_MLX90614::readAmbientTempC(void) {
return readTemp(MLX90614_TA);
}
float Adafruit_MLX90614::readTemp(uint8_t reg) {
float temp;
temp = read16(reg);
if (temp == 0)
return NAN;
temp *= .02;
temp -= 273.15;
return temp;
}
/*********************************************************************/
uint16_t Adafruit_MLX90614::read16(uint8_t a) {
uint8_t buffer[3];
buffer[0] = a;
// read two bytes of data + pec
bool status = i2c_dev->write_then_read(buffer, 1, buffer, 3);
if (!status)
return 0;
// return data, ignore pec
return uint16_t(buffer[0]) | (uint16_t(buffer[1]) << 8);
}
byte Adafruit_MLX90614::crc8(byte *addr, byte len)
// The PEC calculation includes all bits except the START, REPEATED START, STOP,
// ACK, and NACK bits. The PEC is a CRC-8 with polynomial X8+X2+X1+1.
{
byte crc = 0;
while (len--) {
byte inbyte = *addr++;
for (byte i = 8; i; i--) {
byte carry = (crc ^ inbyte) & 0x80;
crc <<= 1;
if (carry)
crc ^= 0x7;
inbyte <<= 1;
}
}
return crc;
}
void Adafruit_MLX90614::write16(uint8_t a, uint16_t v) {
uint8_t buffer[4];
buffer[0] = _addr << 1;
buffer[1] = a;
buffer[2] = v & 0xff;
buffer[3] = v >> 8;
uint8_t pec = crc8(buffer, 4);
buffer[0] = buffer[1];
buffer[1] = buffer[2];
buffer[2] = buffer[3];
buffer[3] = pec;
i2c_dev->write(buffer, 4);
}

View File

@ -0,0 +1,68 @@
/***************************************************
This is a library for the MLX90614 Temp Sensor
Designed specifically to work with the MLX90614 sensors in the
adafruit shop
----> https://www.adafruit.com/products/1747 (3V)
----> https://www.adafruit.com/products/1748 (5V)
These sensors use I2C to communicate, 2 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit in any redistribution
****************************************************/
#include <Adafruit_I2CDevice.h>
#include <Arduino.h>
#define MLX90614_I2CADDR 0x5A
// RAM
#define MLX90614_RAWIR1 0x04
#define MLX90614_RAWIR2 0x05
#define MLX90614_TA 0x06
#define MLX90614_TOBJ1 0x07
#define MLX90614_TOBJ2 0x08
// EEPROM
#define MLX90614_TOMAX 0x20
#define MLX90614_TOMIN 0x21
#define MLX90614_PWMCTRL 0x22
#define MLX90614_TARANGE 0x23
#define MLX90614_EMISS 0x24
#define MLX90614_CONFIG 0x25
#define MLX90614_ADDR 0x2E
#define MLX90614_ID1 0x3C
#define MLX90614_ID2 0x3D
#define MLX90614_ID3 0x3E
#define MLX90614_ID4 0x3F
/**
* @brief Class to read from and control a MLX90614 Temp Sensor
*
*/
class Adafruit_MLX90614 {
public:
~Adafruit_MLX90614();
bool begin(uint8_t addr = MLX90614_I2CADDR, TwoWire *wire = &Wire);
double readObjectTempC(void);
double readAmbientTempC(void);
double readObjectTempF(void);
double readAmbientTempF(void);
uint16_t readEmissivityReg(void);
void writeEmissivityReg(uint16_t ereg);
double readEmissivity(void);
void writeEmissivity(double emissivity);
private:
Adafruit_I2CDevice *i2c_dev = NULL; ///< Pointer to I2C bus interface
float readTemp(uint8_t reg);
uint16_t read16(uint8_t addr);
void write16(uint8_t addr, uint16_t data);
byte crc8(byte *addr, byte len);
uint8_t _addr;
};

View File

@ -0,0 +1,51 @@
# Adafruit-MLX90614-Library [![Build Status](https://github.com/adafruit/Adafruit-MLX90614-Library/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit-MLX90614-Library/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit-MLX90614-Library/html/index.html)
This is a library for the MLX90614 temperature sensor
<a href="https://www.adafruit.com/products/1747"><img src="https://cdn-shop.adafruit.com/970x728/1747-00.jpg" width="500px"></a>
Designed and tested to work with the MLX90614 sensors in the adafruit shop
* https://www.adafruit.com/products/1747 3V version
* https://www.adafruit.com/products/1748 5V version
Check out the links above for our tutorials and wiring diagrams
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
# Installation
To install, use the Arduino Library Manager and search for "Adafruit-MLX90614-Library" and install the library.
# Contributing
Contributions are welcome! Please read our [Code of Conduct](https://github.com/adafruit/Adafruit-MLX90614-Library/blob/master/CODE_OF_CONDUCT.md>)
before contributing to help this project stay welcoming.
## Documentation and doxygen
Documentation is produced by doxygen. Contributions should include documentation for any new code added.
Some examples of how to use doxygen can be found in these guide pages:
https://learn.adafruit.com/the-well-automated-arduino-library/doxygen
https://learn.adafruit.com/the-well-automated-arduino-library/doxygen-tips
## Formatting and clang-format
This library uses [`clang-format`](https://releases.llvm.org/download.html) to standardize the formatting of `.cpp` and `.h` files.
Contributions should be formatted using `clang-format`:
The `-i` flag will make the changes to the file.
```bash
clang-format -i *.cpp *.h
```
If you prefer to make the changes yourself, running `clang-format` without the `-i` flag will print out a formatted version of the file. You can save this to a file and diff it against the original to see the changes.
Note that the formatting output by `clang-format` is what the automated formatting checker will expect. Any diffs from this formatting will result in a failed build until they are addressed. Using the `-i` flag is highly recommended.
### clang-format resources
* [Binary builds and source available on the LLVM downloads page](https://releases.llvm.org/download.html)
* [Documentation and IDE integration](https://clang.llvm.org/docs/ClangFormat.html)
## About this Driver
Written by Limor Fried for Adafruit Industries.
BSD license, check license.txt for more information
All text above must be included in any redistribution

View 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 dont 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 communitys 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.

View File

@ -0,0 +1,47 @@
/*
* See app note:
* https://www.melexis.com/en/documents/documentation/application-notes/application-note-mlx90614-changing-emissivity-setting
*
* 1. Write 0x0000 to address 0x04 (erase the EEPROM cell)
* 2. Write the new value to address 0x04
* 3. Read the value in address 0x04 in order to check that the correct value is stored
* 4. Restart the module
*
*/
#include <Adafruit_MLX90614.h>
//== CHANGE THIS ============
double new_emissivity = 0.95;
//===========================
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
void setup() {
// Serial.begin(9600);
// while (!Serial);
Serial.println("Adafruit MLX90614 Emissivity Setter.\n");
// init sensor
if (!mlx.begin()) {
Serial.println("Error connecting to MLX sensor. Check wiring.");
while (1);
};
// read current emissivity
Serial.print("Current emissivity = "); Serial.println(mlx.readEmissivity());
// set new emissivity
Serial.print("Setting emissivity = "); Serial.println(new_emissivity);
mlx.writeEmissivity(new_emissivity); // this does the 0x0000 erase write
// read back
Serial.print("New emissivity = "); Serial.println(mlx.readEmissivity());
// done
Serial.print("DONE. Restart the module.");
}
void loop() {
}

View File

@ -0,0 +1,46 @@
/***************************************************
This is a library example for the MLX90614 Temp Sensor
Designed specifically to work with the MLX90614 sensors in the
adafruit shop
----> https://www.adafruit.com/products/1747 3V version
----> https://www.adafruit.com/products/1748 5V version
These sensors use I2C to communicate, 2 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include <Adafruit_MLX90614.h>
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("Adafruit MLX90614 test");
if (!mlx.begin()) {
Serial.println("Error connecting to MLX sensor. Check wiring.");
while (1);
};
Serial.print("Emissivity = "); Serial.println(mlx.readEmissivity());
Serial.println("================================================");
}
void loop() {
Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempC());
Serial.print("*C\tObject = "); Serial.print(mlx.readObjectTempC()); Serial.println("*C");
Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempF());
Serial.print("*F\tObject = "); Serial.print(mlx.readObjectTempF()); Serial.println("*F");
Serial.println();
delay(500);
}

View File

@ -0,0 +1,10 @@
; name=Adafruit MLX90614 Library
; version=2.1.5
; author=Adafruit
; maintainer=Adafruit <info@adafruit.com>
; sentence=Arduino library for the MLX90614 sensors in the Adafruit shop
; paragraph=Arduino library for the MLX90614 sensors in the Adafruit shop
; category=Sensors
; url=https://github.com/adafruit/Adafruit-MLX90614-Library
; architectures=*
; depends=Adafruit BusIO

View File

@ -0,0 +1,26 @@
Software License Agreement (BSD License)
Copyright (c) 2020 Limor Fried for 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.

View File

@ -0,0 +1,15 @@
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- due
# - zero # SAMD covered by M4
# - leonardo # AVR covered by UNO
- m4
# - esp32 # errors on OneWire => util/crc16.h vs rom/crc.h
- esp8266
# - mega2560 # AVR covered by UNO
unittest:
# These dependent libraries will be installed
libraries:
- "OneWire"

1
lib/DallasTemperature/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto

View File

@ -0,0 +1,13 @@
name: Arduino-lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: arduino/arduino-lint-action@v1
with:
library-manager: update
# compliance: strict

View File

@ -0,0 +1,17 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
runTest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- run: |
gem install arduino_ci
arduino_ci.rb

View File

@ -0,0 +1,18 @@
name: JSON check
on:
push:
paths:
- '**.json'
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: json-syntax-check
uses: limitusus/json-syntax-check@v1
with:
pattern: "\\.json$"

16
lib/DallasTemperature/.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
.idea
classes
target
out
build
*.iml
*.ipr
*.iws
*.log
*.war
.idea
.project
.classpath
.settings
.gradle
.vscode

View File

@ -0,0 +1 @@
{"type": "library", "name": "DallasTemperature", "version": "3.11.0", "spec": {"owner": "milesburton", "id": 54, "name": "DallasTemperature", "requirements": null, "uri": null}}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,346 @@
#ifndef DallasTemperature_h
#define DallasTemperature_h
#define DALLASTEMPLIBVERSION "3.8.1" // To be deprecated -> TODO remove in 4.0.0
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// set to true to include code for new and delete operators
#ifndef REQUIRESNEW
#define REQUIRESNEW false
#endif
// set to true to include code implementing alarm search functions
#ifndef REQUIRESALARMS
#define REQUIRESALARMS true
#endif
#include <inttypes.h>
#ifdef __STM32F1__
#include <OneWireSTM.h>
#else
#include <OneWire.h>
#endif
// Model IDs
#define DS18S20MODEL 0x10 // also DS1820
#define DS18B20MODEL 0x28 // also MAX31820
#define DS1822MODEL 0x22
#define DS1825MODEL 0x3B // also MAX31850
#define DS28EA00MODEL 0x42
// Error Codes
// See https://github.com/milesburton/Arduino-Temperature-Control-Library/commit/ac1eb7f56e3894e855edc3353be4bde4aa838d41#commitcomment-75490966 for the 16bit implementation. Reverted due to microcontroller resource constraints.
#define DEVICE_DISCONNECTED_C -127
#define DEVICE_DISCONNECTED_F -196.6
#define DEVICE_DISCONNECTED_RAW -7040
#define DEVICE_FAULT_OPEN_C -254
#define DEVICE_FAULT_OPEN_F -425.199982
#define DEVICE_FAULT_OPEN_RAW -32512
#define DEVICE_FAULT_SHORTGND_C -253
#define DEVICE_FAULT_SHORTGND_F -423.399994
#define DEVICE_FAULT_SHORTGND_RAW -32384
#define DEVICE_FAULT_SHORTVDD_C -252
#define DEVICE_FAULT_SHORTVDD_F -421.599976
#define DEVICE_FAULT_SHORTVDD_RAW -32256
// For readPowerSupply on oneWire bus
// definition of nullptr for C++ < 11, using official workaround:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf
#if __cplusplus < 201103L
const class
{
public:
template <class T>
operator T *() const {
return 0;
}
template <class C, class T>
operator T C::*() const {
return 0;
}
private:
void operator&() const;
} nullptr = {};
#endif
typedef uint8_t DeviceAddress[8];
class DallasTemperature {
public:
DallasTemperature();
DallasTemperature(OneWire*);
DallasTemperature(OneWire*, uint8_t);
void setOneWire(OneWire*);
void setPullupPin(uint8_t);
// initialise bus
void begin(void);
// returns the number of devices found on the bus
uint8_t getDeviceCount(void);
// returns the number of DS18xxx Family devices on bus
uint8_t getDS18Count(void);
// returns true if address is valid
bool validAddress(const uint8_t*);
// returns true if address is of the family of sensors the lib supports.
bool validFamily(const uint8_t* deviceAddress);
// finds an address at a given index on the bus
bool getAddress(uint8_t*, uint8_t);
// attempt to determine if the device at the given address is connected to the bus
bool isConnected(const uint8_t*);
// attempt to determine if the device at the given address is connected to the bus
// also allows for updating the read scratchpad
bool isConnected(const uint8_t*, uint8_t*);
// read device's scratchpad
bool readScratchPad(const uint8_t*, uint8_t*);
// write device's scratchpad
void writeScratchPad(const uint8_t*, const uint8_t*);
// read device's power requirements
bool readPowerSupply(const uint8_t* deviceAddress = nullptr);
// get global resolution
uint8_t getResolution();
// set global resolution to 9, 10, 11, or 12 bits
void setResolution(uint8_t);
// returns the device resolution: 9, 10, 11, or 12 bits
uint8_t getResolution(const uint8_t*);
// set resolution of a device to 9, 10, 11, or 12 bits
bool setResolution(const uint8_t*, uint8_t,
bool skipGlobalBitResolutionCalculation = false);
// sets/gets the waitForConversion flag
void setWaitForConversion(bool);
bool getWaitForConversion(void);
// sets/gets the checkForConversion flag
void setCheckForConversion(bool);
bool getCheckForConversion(void);
struct request_t {
bool result;
unsigned long timestamp;
operator bool() {
return result;
}
};
// sends command for all devices on the bus to perform a temperature conversion
request_t requestTemperatures(void);
// sends command for one device to perform a temperature conversion by address
request_t requestTemperaturesByAddress(const uint8_t*);
// sends command for one device to perform a temperature conversion by index
request_t requestTemperaturesByIndex(uint8_t);
// returns temperature raw value (12 bit integer of 1/128 degrees C)
int32_t getTemp(const uint8_t*);
// returns temperature in degrees C
float getTempC(const uint8_t*);
// returns temperature in degrees F
float getTempF(const uint8_t*);
// Get temperature for device index (slow)
float getTempCByIndex(uint8_t);
// Get temperature for device index (slow)
float getTempFByIndex(uint8_t);
// returns true if the bus requires parasite power
bool isParasitePowerMode(void);
// Is a conversion complete on the wire? Only applies to the first sensor on the wire.
bool isConversionComplete(void);
static uint16_t millisToWaitForConversion(uint8_t);
uint16_t millisToWaitForConversion();
// Sends command to one device to save values from scratchpad to EEPROM by index
// Returns true if no errors were encountered, false indicates failure
bool saveScratchPadByIndex(uint8_t);
// Sends command to one or more devices to save values from scratchpad to EEPROM
// Returns true if no errors were encountered, false indicates failure
bool saveScratchPad(const uint8_t* = nullptr);
// Sends command to one device to recall values from EEPROM to scratchpad by index
// Returns true if no errors were encountered, false indicates failure
bool recallScratchPadByIndex(uint8_t);
// Sends command to one or more devices to recall values from EEPROM to scratchpad
// Returns true if no errors were encountered, false indicates failure
bool recallScratchPad(const uint8_t* = nullptr);
// Sets the autoSaveScratchPad flag
void setAutoSaveScratchPad(bool);
// Gets the autoSaveScratchPad flag
bool getAutoSaveScratchPad(void);
#if REQUIRESALARMS
typedef void AlarmHandler(const uint8_t*);
// sets the high alarm temperature for a device
// accepts a int8_t. valid range is -55C - 125C
void setHighAlarmTemp(const uint8_t*, int8_t);
// sets the low alarm temperature for a device
// accepts a int8_t. valid range is -55C - 125C
void setLowAlarmTemp(const uint8_t*, int8_t);
// returns a int8_t with the current high alarm temperature for a device
// in the range -55C - 125C
int8_t getHighAlarmTemp(const uint8_t*);
// returns a int8_t with the current low alarm temperature for a device
// in the range -55C - 125C
int8_t getLowAlarmTemp(const uint8_t*);
// resets internal variables used for the alarm search
void resetAlarmSearch(void);
// search the wire for devices with active alarms
bool alarmSearch(uint8_t*);
// returns true if ia specific device has an alarm
bool hasAlarm(const uint8_t*);
// returns true if any device is reporting an alarm on the bus
bool hasAlarm(void);
// runs the alarm handler for all devices returned by alarmSearch()
void processAlarms(void);
// sets the alarm handler
void setAlarmHandler(const AlarmHandler *);
// returns true if an AlarmHandler has been set
bool hasAlarmHandler();
#endif
// if no alarm handler is used the two bytes can be used as user data
// example of such usage is an ID.
// note if device is not connected it will fail writing the data.
// note if address cannot be found no error will be reported.
// in short use carefully
void setUserData(const uint8_t*, int16_t);
void setUserDataByIndex(uint8_t, int16_t);
int16_t getUserData(const uint8_t*);
int16_t getUserDataByIndex(uint8_t);
// convert from Celsius to Fahrenheit
static float toFahrenheit(float);
// convert from Fahrenheit to Celsius
static float toCelsius(float);
// convert from raw to Celsius
static float rawToCelsius(int32_t);
// convert from Celsius to raw
static int16_t celsiusToRaw(float);
// convert from raw to Fahrenheit
static float rawToFahrenheit(int32_t);
#if REQUIRESNEW
// initialize memory area
void* operator new(unsigned int);
// delete memory reference
void operator delete(void*);
#endif
void blockTillConversionComplete(uint8_t);
void blockTillConversionComplete(uint8_t, unsigned long);
void blockTillConversionComplete(uint8_t, request_t);
private:
typedef uint8_t ScratchPad[9];
// parasite power on or off
bool parasite;
// external pullup
bool useExternalPullup;
uint8_t pullupPin;
// used to determine the delay amount needed to allow for the
// temperature conversion to take place
uint8_t bitResolution;
// used to requestTemperature with or without delay
bool waitForConversion;
// used to requestTemperature to dynamically check if a conversion is complete
bool checkForConversion;
// used to determine if values will be saved from scratchpad to EEPROM on every scratchpad write
bool autoSaveScratchPad;
// count of devices on the bus
uint8_t devices;
// count of DS18xxx Family devices on bus
uint8_t ds18Count;
// Take a pointer to one wire instance
OneWire* _wire;
// reads scratchpad and returns the raw temperature
int32_t calculateTemperature(const uint8_t*, uint8_t*);
// Returns true if all bytes of scratchPad are '\0'
bool isAllZeros(const uint8_t* const scratchPad, const size_t length = 9);
// External pullup control
void activateExternalPullup(void);
void deactivateExternalPullup(void);
#if REQUIRESALARMS
// required for alarmSearch
uint8_t alarmSearchAddress[8];
int8_t alarmSearchJunction;
uint8_t alarmSearchExhausted;
// the alarm handler function pointer
AlarmHandler *_AlarmHandler;
#endif
};
#endif

View File

@ -0,0 +1,80 @@
[![Arduino CI](https://github.com/milesburton/Arduino-Temperature-Control-Library/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![Arduino-lint](https://github.com/milesburton/Arduino-Temperature-Control-Library/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/AS5600/actions/workflows/arduino-lint.yml)
[![JSON check](https://github.com/milesburton/Arduino-Temperature-Control-Library/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/AS5600/actions/workflows/jsoncheck.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/milesburton/Arduino-Temperature-Control-Library/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/milesburton/Arduino-Temperature-Control-Library.svg?maxAge=3600)](https://github.com/milesburton/Arduino-Temperature-Control-Library/releases)
# Arduino Library for Maxim Temperature Integrated Circuits
## Usage
This library supports the following devices :
* DS18B20
* DS18S20 - Please note there appears to be an issue with this series.
* DS1822
* DS1820
* MAX31820
* MAX31850
You will need a pull-up resistor of about 5 KOhm between the 1-Wire data line
and your 5V power. If you are using the DS18B20, ground pins 1 and 3. The
centre pin is the data line '1-wire'.
In case of temperature conversion problems (result is `-85`), strong pull-up setup may be necessary. See section
_Powering the DS18B20_ in
[DS18B20 datasheet](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf) (page 7)
and use `DallasTemperature(OneWire*, uint8_t)` constructor.
We have included a "REQUIRESNEW" and "REQUIRESALARMS" definition. If you
want to slim down the code feel free to use either of these by including
#define REQUIRESNEW
or
#define REQUIRESALARMS
at the top of DallasTemperature.h
Finally, please include OneWire from Paul Stoffregen in the library manager before you begin.
## Credits
The OneWire code has been derived from
http://www.arduino.cc/playground/Learning/OneWire.
Miles Burton <miles@mnetcs.com> originally developed this library.
Tim Newsome <nuisance@casualhacker.net> added support for multiple sensors on
the same bus.
Guil Barros [gfbarros@bappos.com] added getTempByAddress (v3.5)
Note: these are implemented as getTempC(address) and getTempF(address)
Rob Tillaart [rob.tillaart@gmail.com] added async modus (v3.7.0)
## Website
Additional documentation may be found here
https://www.milesburton.com/Dallas_Temperature_Control_Library
# License
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

View File

@ -0,0 +1,161 @@
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// arrays to hold device addresses
DeviceAddress insideThermometer, outsideThermometer;
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
// Start up the library
sensors.begin();
// locate devices on the bus
Serial.print("Found ");
Serial.print(sensors.getDeviceCount(), DEC);
Serial.println(" devices.");
// search for devices on the bus and assign based on an index.
if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");
if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1");
// show the addresses we found on the bus
Serial.print("Device 0 Address: ");
printAddress(insideThermometer);
Serial.println();
Serial.print("Device 0 Alarms: ");
printAlarms(insideThermometer);
Serial.println();
Serial.print("Device 1 Address: ");
printAddress(outsideThermometer);
Serial.println();
Serial.print("Device 1 Alarms: ");
printAlarms(outsideThermometer);
Serial.println();
Serial.println("Setting alarm temps...");
// alarm when temp is higher than 30C
sensors.setHighAlarmTemp(insideThermometer, 30);
// alarm when temp is lower than -10C
sensors.setLowAlarmTemp(insideThermometer, -10);
// alarm when temp is higher than 31C
sensors.setHighAlarmTemp(outsideThermometer, 31);
// alarn when temp is lower than 27C
sensors.setLowAlarmTemp(outsideThermometer, 27);
Serial.print("New Device 0 Alarms: ");
printAlarms(insideThermometer);
Serial.println();
Serial.print("New Device 1 Alarms: ");
printAlarms(outsideThermometer);
Serial.println();
}
// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}
// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
float tempC = sensors.getTempC(deviceAddress);
Serial.print("Temp C: ");
Serial.print(tempC);
Serial.print(" Temp F: ");
Serial.print(DallasTemperature::toFahrenheit(tempC));
}
void printAlarms(uint8_t deviceAddress[])
{
char temp;
temp = sensors.getHighAlarmTemp(deviceAddress);
Serial.print("High Alarm: ");
Serial.print(temp, DEC);
Serial.print("C/");
Serial.print(DallasTemperature::toFahrenheit(temp));
Serial.print("F | Low Alarm: ");
temp = sensors.getLowAlarmTemp(deviceAddress);
Serial.print(temp, DEC);
Serial.print("C/");
Serial.print(DallasTemperature::toFahrenheit(temp));
Serial.print("F");
}
// main function to print information about a device
void printData(DeviceAddress deviceAddress)
{
Serial.print("Device Address: ");
printAddress(deviceAddress);
Serial.print(" ");
printTemperature(deviceAddress);
Serial.println();
}
void checkAlarm(DeviceAddress deviceAddress)
{
if (sensors.hasAlarm(deviceAddress))
{
Serial.print("ALARM: ");
printData(deviceAddress);
}
}
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures();
Serial.println("DONE");
// Method 1:
// check each address individually for an alarm condition
checkAlarm(insideThermometer);
checkAlarm(outsideThermometer);
/*
// Alternate method:
// Search the bus and iterate through addresses of devices with alarms
// space for the alarm device's address
DeviceAddress alarmAddr;
Serial.println("Searching for alarms...");
// resetAlarmSearch() must be called before calling alarmSearch()
sensors.resetAlarmSearch();
// alarmSearch() returns 0 when there are no devices with alarms
while (sensors.alarmSearch(alarmAddr))
{
Serial.print("ALARM: ");
printData(alarmAddr);
}
*/
}

View File

@ -0,0 +1,143 @@
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// arrays to hold device addresses
DeviceAddress insideThermometer, outsideThermometer;
// function that will be called when an alarm condition exists during DallasTemperatures::processAlarms();
void newAlarmHandler(const uint8_t* deviceAddress)
{
Serial.println("Alarm Handler Start");
printAlarmInfo(deviceAddress);
printTemp(deviceAddress);
Serial.println();
Serial.println("Alarm Handler Finish");
}
void printCurrentTemp(DeviceAddress deviceAddress)
{
printAddress(deviceAddress);
printTemp(deviceAddress);
Serial.println();
}
void printAddress(const DeviceAddress deviceAddress)
{
Serial.print("Address: ");
for (uint8_t i = 0; i < 8; i++)
{
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
Serial.print(" ");
}
void printTemp(const DeviceAddress deviceAddress)
{
float tempC = sensors.getTempC(deviceAddress);
if (tempC != DEVICE_DISCONNECTED_C)
{
Serial.print("Current Temp C: ");
Serial.print(tempC);
}
else Serial.print("DEVICE DISCONNECTED");
Serial.print(" ");
}
void printAlarmInfo(const DeviceAddress deviceAddress)
{
char temp;
printAddress(deviceAddress);
temp = sensors.getHighAlarmTemp(deviceAddress);
Serial.print("High Alarm: ");
Serial.print(temp, DEC);
Serial.print("C");
Serial.print(" Low Alarm: ");
temp = sensors.getLowAlarmTemp(deviceAddress);
Serial.print(temp, DEC);
Serial.print("C");
Serial.print(" ");
}
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
// Start up the library
sensors.begin();
// locate devices on the bus
Serial.print("Found ");
Serial.print(sensors.getDeviceCount(), DEC);
Serial.println(" devices.");
// search for devices on the bus and assign based on an index
if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");
if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1");
Serial.print("Device insideThermometer ");
printAlarmInfo(insideThermometer);
Serial.println();
Serial.print("Device outsideThermometer ");
printAlarmInfo(outsideThermometer);
Serial.println();
// set alarm ranges
Serial.println("Setting alarm temps...");
sensors.setHighAlarmTemp(insideThermometer, 26);
sensors.setLowAlarmTemp(insideThermometer, 22);
sensors.setHighAlarmTemp(outsideThermometer, 25);
sensors.setLowAlarmTemp(outsideThermometer, 21);
Serial.print("New insideThermometer ");
printAlarmInfo(insideThermometer);
Serial.println();
Serial.print("New outsideThermometer ");
printAlarmInfo(outsideThermometer);
Serial.println();
// attach alarm handler
sensors.setAlarmHandler(&newAlarmHandler);
}
void loop(void)
{
// ask the devices to measure the temperature
sensors.requestTemperatures();
// if an alarm condition exists as a result of the most recent
// requestTemperatures() request, it exists until the next time
// requestTemperatures() is called AND there isn't an alarm condition
// on the device
if (sensors.hasAlarm())
{
Serial.println("Oh noes! There is at least one alarm on the bus.");
}
// call alarm handler function defined by sensors.setAlarmHandler
// for each device reporting an alarm
sensors.processAlarms();
if (!sensors.hasAlarm())
{
// just print out the current temperature
printCurrentTemp(insideThermometer);
printCurrentTemp(outsideThermometer);
}
delay(1000);
}

View File

@ -0,0 +1,35 @@
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino, while external pullup P-MOSFET gate into port 3
#define ONE_WIRE_BUS 2
#define ONE_WIRE_PULLUP 3
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire, ONE_WIRE_PULLUP);
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
// Start up the library
sensors.begin();
}
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
for (int i = 0; i < sensors.getDeviceCount(); i++) {
Serial.println("Temperature for Device " + String(i) + " is: " + String(sensors.getTempCByIndex(i)));
}
}

View File

@ -0,0 +1,43 @@
#include <OneWire.h>
#include <DallasTemperature.h>
OneWire ds18x20[] = { 3, 7 };
const int oneWireCount = sizeof(ds18x20) / sizeof(OneWire);
DallasTemperature sensor[oneWireCount];
void setup(void) {
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature Multiple Bus Control Library Simple Demo");
Serial.print("============Ready with ");
Serial.print(oneWireCount);
Serial.println(" Sensors================");
// Start up the library on all defined bus-wires
DeviceAddress deviceAddress;
for (int i = 0; i < oneWireCount; i++) {
sensor[i].setOneWire(&ds18x20[i]);
sensor[i].begin();
if (sensor[i].getAddress(deviceAddress, 0)) sensor[i].setResolution(deviceAddress, 12);
}
}
void loop(void) {
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
for (int i = 0; i < oneWireCount; i++) {
sensor[i].requestTemperatures();
}
Serial.println("DONE");
delay(1000);
for (int i = 0; i < oneWireCount; i++) {
float temperature = sensor[i].getTempCByIndex(0);
Serial.print("Temperature for the sensor ");
Serial.print(i);
Serial.print(" is ");
Serial.println(temperature);
}
Serial.println();
}

View File

@ -0,0 +1,148 @@
// Include the libraries we need
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
#define TEMPERATURE_PRECISION 9
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// arrays to hold device addresses
DeviceAddress insideThermometer, outsideThermometer;
// Assign address manually. The addresses below will need to be changed
// to valid device addresses on your bus. Device address can be retrieved
// by using either oneWire.search(deviceAddress) or individually via
// sensors.getAddress(deviceAddress, index)
// DeviceAddress insideThermometer = { 0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0 };
// DeviceAddress outsideThermometer = { 0x28, 0x3F, 0x1C, 0x31, 0x2, 0x0, 0x0, 0x2 };
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
// Start up the library
sensors.begin();
// locate devices on the bus
Serial.print("Locating devices...");
Serial.print("Found ");
Serial.print(sensors.getDeviceCount(), DEC);
Serial.println(" devices.");
// report parasite power requirements
Serial.print("Parasite power is: ");
if (sensors.isParasitePowerMode()) Serial.println("ON");
else Serial.println("OFF");
// Search for devices on the bus and assign based on an index. Ideally,
// you would do this to initially discover addresses on the bus and then
// use those addresses and manually assign them (see above) once you know
// the devices on your bus (and assuming they don't change).
//
// method 1: by index
if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");
if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1");
// method 2: search()
// search() looks for the next device. Returns 1 if a new address has been
// returned. A zero might mean that the bus is shorted, there are no devices,
// or you have already retrieved all of them. It might be a good idea to
// check the CRC to make sure you didn't get garbage. The order is
// deterministic. You will always get the same devices in the same order
//
// Must be called before search()
//oneWire.reset_search();
// assigns the first address found to insideThermometer
//if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
// assigns the seconds address found to outsideThermometer
//if (!oneWire.search(outsideThermometer)) Serial.println("Unable to find address for outsideThermometer");
// show the addresses we found on the bus
Serial.print("Device 0 Address: ");
printAddress(insideThermometer);
Serial.println();
Serial.print("Device 1 Address: ");
printAddress(outsideThermometer);
Serial.println();
// set the resolution to 9 bit per device
sensors.setResolution(insideThermometer, TEMPERATURE_PRECISION);
sensors.setResolution(outsideThermometer, TEMPERATURE_PRECISION);
Serial.print("Device 0 Resolution: ");
Serial.print(sensors.getResolution(insideThermometer), DEC);
Serial.println();
Serial.print("Device 1 Resolution: ");
Serial.print(sensors.getResolution(outsideThermometer), DEC);
Serial.println();
}
// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
// zero pad the address if necessary
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}
// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
float tempC = sensors.getTempC(deviceAddress);
if (tempC == DEVICE_DISCONNECTED_C)
{
Serial.println("Error: Could not read temperature data");
return;
}
Serial.print("Temp C: ");
Serial.print(tempC);
Serial.print(" Temp F: ");
Serial.print(DallasTemperature::toFahrenheit(tempC));
}
// function to print a device's resolution
void printResolution(DeviceAddress deviceAddress)
{
Serial.print("Resolution: ");
Serial.print(sensors.getResolution(deviceAddress));
Serial.println();
}
// main function to print information about a device
void printData(DeviceAddress deviceAddress)
{
Serial.print("Device Address: ");
printAddress(deviceAddress);
Serial.print(" ");
printTemperature(deviceAddress);
Serial.println();
}
/*
Main function, calls the temperatures in a loop.
*/
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures();
Serial.println("DONE");
// print the device information
printData(insideThermometer);
printData(outsideThermometer);
}

View File

@ -0,0 +1,106 @@
//
// FILE: SaveRecallScratchPad.ino
// AUTHOR: GitKomodo
// VERSION: 0.0.1
// PURPOSE: Show DallasTemperature lib functionality to
// save/recall ScratchPad values to/from EEPROM
//
// HISTORY:
// 0.0.1 = 2020-02-18 initial version
//
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress deviceAddress;
void setup()
{
Serial.begin(9600);
Serial.println(__FILE__);
Serial.println("Dallas Temperature Demo");
sensors.begin();
// Get ID of first sensor (at index 0)
sensors.getAddress(deviceAddress, 0);
// By default configuration and alarm/userdata registers are also saved to EEPROM
// when they're changed. Sensors recall these values automatically when powered up.
// Turn OFF automatic saving of configuration and alarm/userdata registers to EEPROM
sensors.setAutoSaveScratchPad(false);
// Change configuration and alarm/userdata registers on the scratchpad
int8_t resolution = 12;
sensors.setResolution(deviceAddress, resolution);
int16_t userdata = 24680;
sensors.setUserData(deviceAddress, userdata);
// Save configuration and alarm/userdata registers to EEPROM
sensors.saveScratchPad(deviceAddress);
// saveScratchPad can also be used without a parameter to save the configuration
// and alarm/userdata registers of ALL connected sensors to EEPROM:
//
// sensors.saveScratchPad();
//
// Or the configuration and alarm/userdata registers of a sensor can be saved to
// EEPROM by index:
//
// sensors.saveScratchPadByIndex(0);
// Print current values on the scratchpad (resolution = 12, userdata = 24680)
printValues();
}
void loop() {
// Change configuration and alarm/userdata registers on the scratchpad
int8_t resolution = 10;
sensors.setResolution(deviceAddress, resolution);
int16_t userdata = 12345;
sensors.setUserData(deviceAddress, userdata);
// Print current values on the scratchpad (resolution = 10, userdata = 12345)
printValues();
delay(2000);
// Recall configuration and alarm/userdata registers from EEPROM
sensors.recallScratchPad(deviceAddress);
// recallScratchPad can also be used without a parameter to recall the configuration
// and alarm/userdata registers of ALL connected sensors from EEPROM:
//
// sensors.recallScratchPad();
//
// Or the configuration and alarm/userdata registers of a sensor can be recalled
// from EEPROM by index:
//
// sensors.recallScratchPadByIndex(0);
// Print current values on the scratchpad (resolution = 12, userdata = 24680)
printValues();
delay(2000);
}
void printValues() {
Serial.println();
Serial.println("Current values on the scratchpad:");
Serial.print("Resolution:\t");
Serial.println(sensors.getResolution(deviceAddress));
Serial.print("User data:\t");
Serial.println(sensors.getUserData(deviceAddress));
}

View File

@ -0,0 +1,47 @@
//
// This sketch does not use the ALARM registers and uses those 2 bytes as a counter
// these 2 bytes can be used for other purposes as well e.g. last temperature or
// a specific ID.
//
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
int count = 0;
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
// Start up the library
sensors.begin();
}
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
Serial.print("Temperature for the device 1 (index 0) is: ");
Serial.println(sensors.getTempCByIndex(0));
count++;
sensors.setUserDataByIndex(0, count);
int x = sensors.getUserDataByIndex(0);
Serial.println(count);
}

View File

@ -0,0 +1,51 @@
// Include the libraries we need
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
/*
* The setup function. We only start the sensors here
*/
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
// Start up the library
sensors.begin();
}
/*
* Main function, get and show the temperature
*/
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
// After we got the temperatures, we can print them here.
// We use the function ByIndex, and as an example get the temperature from the first sensor only.
float tempC = sensors.getTempCByIndex(0);
// Check if reading was successful
if (tempC != DEVICE_DISCONNECTED_C)
{
Serial.print("Temperature for the device 1 (index 0) is: ");
Serial.println(tempC);
}
else
{
Serial.println("Error: Could not read temperature data");
}
}

View File

@ -0,0 +1,121 @@
// Include the libraries we need
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// arrays to hold device address
DeviceAddress insideThermometer;
/*
* Setup function. Here we do the basics
*/
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
// locate devices on the bus
Serial.print("Locating devices...");
sensors.begin();
Serial.print("Found ");
Serial.print(sensors.getDeviceCount(), DEC);
Serial.println(" devices.");
// report parasite power requirements
Serial.print("Parasite power is: ");
if (sensors.isParasitePowerMode()) Serial.println("ON");
else Serial.println("OFF");
// Assign address manually. The addresses below will beed to be changed
// to valid device addresses on your bus. Device address can be retrieved
// by using either oneWire.search(deviceAddress) or individually via
// sensors.getAddress(deviceAddress, index)
// Note that you will need to use your specific address here
//insideThermometer = { 0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0 };
// Method 1:
// Search for devices on the bus and assign based on an index. Ideally,
// you would do this to initially discover addresses on the bus and then
// use those addresses and manually assign them (see above) once you know
// the devices on your bus (and assuming they don't change).
if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");
// method 2: search()
// search() looks for the next device. Returns 1 if a new address has been
// returned. A zero might mean that the bus is shorted, there are no devices,
// or you have already retrieved all of them. It might be a good idea to
// check the CRC to make sure you didn't get garbage. The order is
// deterministic. You will always get the same devices in the same order
//
// Must be called before search()
//oneWire.reset_search();
// assigns the first address found to insideThermometer
//if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
// show the addresses we found on the bus
Serial.print("Device 0 Address: ");
printAddress(insideThermometer);
Serial.println();
// set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
sensors.setResolution(insideThermometer, 9);
Serial.print("Device 0 Resolution: ");
Serial.print(sensors.getResolution(insideThermometer), DEC);
Serial.println();
}
// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
// method 1 - slower
//Serial.print("Temp C: ");
//Serial.print(sensors.getTempC(deviceAddress));
//Serial.print(" Temp F: ");
//Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit
// method 2 - faster
float tempC = sensors.getTempC(deviceAddress);
if (tempC == DEVICE_DISCONNECTED_C)
{
Serial.println("Error: Could not read temperature data");
return;
}
Serial.print("Temp C: ");
Serial.print(tempC);
Serial.print(" Temp F: ");
Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
}
/*
* Main function. It will request the tempC from the sensors and display on Serial.
*/
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
// It responds almost immediately. Let's print out the data
printTemperature(insideThermometer); // Use a simple function to print out the data
}
// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}

View File

@ -0,0 +1,129 @@
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
#define TEMPERATURE_PRECISION 9 // Lower resolution
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
int numberOfDevices; // Number of temperature devices found
DeviceAddress tempDeviceAddress; // We'll use this variable to store a found device address
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
// Start up the library
sensors.begin();
// Grab a count of devices on the wire
numberOfDevices = sensors.getDeviceCount();
// locate devices on the bus
Serial.print("Locating devices...");
Serial.print("Found ");
Serial.print(numberOfDevices, DEC);
Serial.println(" devices.");
// report parasite power requirements
Serial.print("Parasite power is: ");
if (sensors.isParasitePowerMode()) Serial.println("ON");
else Serial.println("OFF");
// Loop through each device, print out address
for (int i = 0; i < numberOfDevices; i++)
{
// Search the wire for address
if (sensors.getAddress(tempDeviceAddress, i))
{
Serial.print("Found device ");
Serial.print(i, DEC);
Serial.print(" with address: ");
printAddress(tempDeviceAddress);
Serial.println();
Serial.print("Setting resolution to ");
Serial.println(TEMPERATURE_PRECISION, DEC);
// set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions)
sensors.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION);
Serial.print("Resolution actually set to: ");
Serial.print(sensors.getResolution(tempDeviceAddress), DEC);
Serial.println();
} else {
Serial.print("Found ghost device at ");
Serial.print(i, DEC);
Serial.print(" but could not detect address. Check power and cabling");
}
}
}
// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
// method 1 - slower
//Serial.print("Temp C: ");
//Serial.print(sensors.getTempC(deviceAddress));
//Serial.print(" Temp F: ");
//Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit
// method 2 - faster
float tempC = sensors.getTempC(deviceAddress);
if (tempC == DEVICE_DISCONNECTED_C)
{
Serial.println("Error: Could not read temperature data");
return;
}
Serial.print("Temp C: ");
Serial.print(tempC);
Serial.print(" Temp F: ");
Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
}
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
// Loop through each device, print out temperature data
for (int i = 0; i < numberOfDevices; i++)
{
// Search the wire for address
if (sensors.getAddress(tempDeviceAddress, i))
{
// Output the device ID
Serial.print("Temperature for device: ");
Serial.println(i, DEC);
// It responds almost immediately. Let's print out the data
printTemperature(tempDeviceAddress); // Use a simple function to print out the data
}
//else ghost device! Check your power requirements and cabling
}
}
// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}

View File

@ -0,0 +1,77 @@
//
// FILE: Timing.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.0.3
// PURPOSE: show performance of DallasTemperature lib
// compared to datasheet times per resolution
//
// HISTORY:
// 0.0.1 2017-07-25 initial version
// 0.0.2 2020-02-13 updates to work with current lib version
// 0.0.3 2020-02-20 added timing measurement of setResolution
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensor(&oneWire);
uint32_t start, stop;
void setup()
{
Serial.begin(9600);
Serial.println(__FILE__);
Serial.print("DallasTemperature Library version: ");
Serial.println(DALLASTEMPLIBVERSION);
sensor.begin();
}
void loop()
{
float ti[4] = { 94, 188, 375, 750 };
Serial.println();
Serial.println("Test takes about 30 seconds for 4 resolutions");
Serial.println("RES\tTIME\tACTUAL\tGAIN");
for (int r = 9; r < 13; r++)
{
start = micros();
sensor.setResolution(r);
Serial.println(micros() - start);
start = micros();
sensor.setResolution(r);
Serial.println(micros() - start);
uint32_t duration = run(20);
float avgDuration = duration / 20.0;
Serial.print(r);
Serial.print("\t");
Serial.print(ti[r - 9]);
Serial.print("\t");
Serial.print(avgDuration, 2);
Serial.print("\t");
Serial.print(avgDuration * 100 / ti[r - 9], 1);
Serial.println("%");
}
delay(1000);
}
uint32_t run(int runs)
{
float t;
start = millis();
for (int i = 0; i < runs; i++)
{
sensor.requestTemperatures();
t = sensor.getTempCByIndex(0);
}
stop = millis();
return stop - start;
}

View File

@ -0,0 +1,45 @@
//
// FILE: TwoPin_DS18B20.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: two pins for two sensors demo
// DATE: 2014-06-13
// URL: http://forum.arduino.cc/index.php?topic=216835.msg1764333#msg1764333
//
// Released to the public domain
//
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS_1 2
#define ONE_WIRE_BUS_2 4
OneWire oneWire_in(ONE_WIRE_BUS_1);
OneWire oneWire_out(ONE_WIRE_BUS_2);
DallasTemperature sensor_inhouse(&oneWire_in);
DallasTemperature sensor_outhouse(&oneWire_out);
void setup(void)
{
Serial.begin(9600);
Serial.println("Dallas Temperature Control Library Demo - TwoPin_DS18B20");
sensor_inhouse.begin();
sensor_outhouse.begin();
}
void loop(void)
{
Serial.print("Requesting temperatures...");
sensor_inhouse.requestTemperatures();
sensor_outhouse.requestTemperatures();
Serial.println(" done");
Serial.print("Inhouse: ");
Serial.println(sensor_inhouse.getTempCByIndex(0));
Serial.print("Outhouse: ");
Serial.println(sensor_outhouse.getTempCByIndex(0));
}

View File

@ -0,0 +1,115 @@
//
// FILE: UserDataDemo.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: use of alarm field as user identification demo
// DATE: 2019-12-23
// URL:
//
// Released to the public domain
//
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
uint8_t deviceCount = 0;
// Add 4 prepared sensors to the bus
// use the UserDataWriteBatch demo to prepare 4 different labeled sensors
struct
{
int id;
DeviceAddress addr;
} T[4];
float getTempByID(int id)
{
for (uint8_t index = 0; index < deviceCount; index++)
{
if (T[index].id == id)
{
return sensors.getTempC(T[index].addr);
}
}
return -999;
}
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
// zero pad the address if necessary
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}
void setup(void)
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println("Dallas Temperature Demo");
sensors.begin();
// count devices
deviceCount = sensors.getDeviceCount();
Serial.print("#devices: ");
Serial.println(deviceCount);
// Read ID's per sensor
// and put them in T array
for (uint8_t index = 0; index < deviceCount; index++)
{
// go through sensors
sensors.getAddress(T[index].addr, index);
T[index].id = sensors.getUserData(T[index].addr);
}
// Check all 4 sensors are set
for (uint8_t index = 0; index < deviceCount; index++)
{
Serial.println();
Serial.println(T[index].id);
printAddress(T[index].addr);
Serial.println();
}
Serial.println();
}
void loop(void)
{
Serial.println();
Serial.print(millis());
Serial.println("\treq temp");
sensors.requestTemperatures();
Serial.print(millis());
Serial.println("\tGet temp by address");
for (int i = 0; i < 4; i++)
{
Serial.print(millis());
Serial.print("\t temp:\t");
Serial.println(sensors.getTempC(T[i].addr));
}
Serial.print(millis());
Serial.println("\tGet temp by ID"); // assume ID = 0, 1, 2, 3
for (int id = 0; id < 4; id++)
{
Serial.print(millis());
Serial.print("\t temp:\t");
Serial.println(getTempByID(id));
}
delay(1000);
}
// END OF FILE

View File

@ -0,0 +1,107 @@
//
// FILE: UserDataWriteBatch.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: use of alarm field as user identification demo
// DATE: 2019-12-23
// URL:
//
// Released to the public domain
//
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
uint8_t deviceCount = 0;
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
// zero pad the address if necessary
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}
void setup(void)
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println("Write user ID to DS18B20\n");
sensors.begin();
// count devices
deviceCount = sensors.getDeviceCount();
Serial.print("#devices: ");
Serial.println(deviceCount);
Serial.println();
Serial.println("current ID's");
for (uint8_t index = 0; index < deviceCount; index++)
{
DeviceAddress t;
sensors.getAddress(t, index);
printAddress(t);
Serial.print("\t\tID: ");
int id = sensors.getUserData(t);
Serial.println(id);
}
Serial.println();
Serial.print("Enter ID for batch: ");
int c = 0;
int id = 0;
while (c != '\n' && c != '\r')
{
c = Serial.read();
switch (c)
{
case '0'...'9':
id *= 10;
id += (c - '0');
break;
default:
break;
}
}
Serial.println();
Serial.println(id);
Serial.println();
Serial.println("Start labeling ...");
for (uint8_t index = 0; index < deviceCount; index++)
{
Serial.print(".");
DeviceAddress t;
sensors.getAddress(t, index);
sensors.setUserData(t, id);
}
Serial.println();
Serial.println();
Serial.println("Show results ...");
for (uint8_t index = 0; index < deviceCount; index++)
{
DeviceAddress t;
sensors.getAddress(t, index);
printAddress(t);
Serial.print("\t\tID: ");
int id = sensors.getUserData(t);
Serial.println(id);
}
Serial.println("Done ...");
}
void loop(void) {}
// END OF FILE

View File

@ -0,0 +1,66 @@
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
void setup(void)
{
// start serial port
Serial.begin(115200);
Serial.println("Dallas Temperature Control Library - Async Demo");
Serial.println("\nDemo shows the difference in length of the call\n\n");
// Start up the library
sensors.begin();
}
void loop(void)
{
// Request temperature conversion (traditional)
Serial.println("Before blocking requestForConversion");
unsigned long start = millis();
sensors.requestTemperatures();
unsigned long stop = millis();
Serial.println("After blocking requestForConversion");
Serial.print("Time used: ");
Serial.println(stop - start);
// get temperature
Serial.print("Temperature: ");
Serial.println(sensors.getTempCByIndex(0));
Serial.println("\n");
// Request temperature conversion - non-blocking / async
Serial.println("Before NON-blocking/async requestForConversion");
start = millis();
sensors.setWaitForConversion(false); // makes it async
sensors.requestTemperatures();
sensors.setWaitForConversion(true);
stop = millis();
Serial.println("After NON-blocking/async requestForConversion");
Serial.print("Time used: ");
Serial.println(stop - start);
// 9 bit resolution by default
// Note the programmer is responsible for the right delay
// we could do something usefull here instead of the delay
int resolution = 9;
delay(750 / (1 << (12 - resolution)));
// get temperature
Serial.print("Temperature: ");
Serial.println(sensors.getTempCByIndex(0));
Serial.println("\n\n\n\n");
delay(5000);
}

View File

@ -0,0 +1,80 @@
//
// Sample of using Async reading of Dallas Temperature Sensors
//
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
DeviceAddress tempDeviceAddress;
int resolution = 12;
unsigned long lastTempRequest = 0;
int delayInMillis = 0;
float temperature = 0.0;
int idle = 0;
//
// SETUP
//
void setup(void)
{
Serial.begin(115200);
Serial.println("Dallas Temperature Control Library - Async Demo");
Serial.print("Library Version: ");
Serial.println(DALLASTEMPLIBVERSION);
Serial.println("\n");
sensors.begin();
sensors.getAddress(tempDeviceAddress, 0);
sensors.setResolution(tempDeviceAddress, resolution);
sensors.setWaitForConversion(false);
sensors.requestTemperatures();
delayInMillis = 750 / (1 << (12 - resolution));
lastTempRequest = millis();
pinMode(13, OUTPUT);
}
void loop(void)
{
if (millis() - lastTempRequest >= delayInMillis) // waited long enough??
{
digitalWrite(13, LOW);
Serial.print(" Temperature: ");
temperature = sensors.getTempCByIndex(0);
Serial.println(temperature, resolution - 8);
Serial.print(" Resolution: ");
Serial.println(resolution);
Serial.print("Idle counter: ");
Serial.println(idle);
Serial.println();
idle = 0;
// immediately after fetching the temperature we request a new sample
// in the async modus
// for the demo we let the resolution change to show differences
resolution++;
if (resolution > 12) resolution = 9;
sensors.setResolution(tempDeviceAddress, resolution);
sensors.requestTemperatures();
delayInMillis = 750 / (1 << (12 - resolution));
lastTempRequest = millis();
}
digitalWrite(13, HIGH);
// we can do usefull things here
// for the demo we just count the idle time in millis
delay(1);
idle++;
}

View File

@ -0,0 +1,67 @@
//
// FILE: oneWireSearch.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.02
// PURPOSE: scan for 1-Wire devices + code snippet generator
// DATE: 2015-june-30
// URL: http://forum.arduino.cc/index.php?topic=333923
//
// inspired by http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html
//
// Released to the public domain
//
// 0.1.00 initial version
// 0.1.01 first published version
// 0.1.02 small output changes
#include <OneWire.h>
void setup()
{
Serial.begin(115200);
Serial.println("//\n// Start oneWireSearch.ino \n//");
for (uint8_t pin = 2; pin < 13; pin++)
{
findDevices(pin);
}
Serial.println("\n//\n// End oneWireSearch.ino \n//");
}
void loop()
{
}
uint8_t findDevices(int pin)
{
OneWire ow(pin);
uint8_t address[8];
uint8_t count = 0;
if (ow.search(address))
{
Serial.print("\nuint8_t pin");
Serial.print(pin, DEC);
Serial.println("[][8] = {");
do {
count++;
Serial.println(" {");
for (uint8_t i = 0; i < 8; i++)
{
Serial.print("0x");
if (address[i] < 0x10) Serial.print("0");
Serial.print(address[i], HEX);
if (i < 7) Serial.print(", ");
}
Serial.println(" },");
} while (ow.search(address));
Serial.println("};");
Serial.print("// nr devices found: ");
Serial.println(count);
}
return count;
}

View File

@ -0,0 +1,92 @@
//
// FILE: readPowerSupply.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo
// DATE: 2020-02-10
//
// Released to the public domain
//
// Include the libraries we need
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// arrays to hold device addresses
DeviceAddress insideThermometer, outsideThermometer;
// Assign address manually. The addresses below will beed to be changed
// to valid device addresses on your bus. Device address can be retrieved
// by using either oneWire.search(deviceAddress) or individually via
// sensors.getAddress(deviceAddress, index)
// DeviceAddress insideThermometer = { 0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0 };
// DeviceAddress outsideThermometer = { 0x28, 0x3F, 0x1C, 0x31, 0x2, 0x0, 0x0, 0x2 };
int devCount = 0;
/*
* The setup function. We only start the sensors here
*/
void setup(void)
{
Serial.begin(115200);
Serial.println("Arduino Temperature Control Library Demo - readPowerSupply");
sensors.begin();
devCount = sensors.getDeviceCount();
Serial.print("#devices: ");
Serial.println(devCount);
// report parasite power requirements
Serial.print("Parasite power is: ");
if (sensors.readPowerSupply()) Serial.println("ON"); // no address means "scan all devices for parasite mode"
else Serial.println("OFF");
// Search for devices on the bus and assign based on an index.
if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");
if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1");
// show the addresses we found on the bus
Serial.print("Device 0 Address: ");
printAddress(insideThermometer);
Serial.println();
Serial.print("Power = parasite: ");
Serial.println(sensors.readPowerSupply(insideThermometer));
Serial.println();
Serial.println();
Serial.print("Device 1 Address: ");
printAddress(outsideThermometer);
Serial.println();
Serial.print("Power = parasite: ");
Serial.println(sensors.readPowerSupply(outsideThermometer));
Serial.println();
Serial.println();
}
// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
// zero pad the address if necessary
if (deviceAddress[i] < 0x10) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}
// empty on purpose
void loop(void)
{
}
// END OF FILE

View File

@ -0,0 +1,87 @@
#######################################
# Syntax Coloring Map For DallasTemperature
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
DallasTemperature KEYWORD1
OneWire KEYWORD1
AlarmHandler KEYWORD1
DeviceAddress KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
setOneWire KEYWORD2
setPullupPin KEYWORD2
setResolution KEYWORD2
getResolution KEYWORD2
getTemp KEYWORD2
getTempC KEYWORD2
toFahrenheit KEYWORD2
getTempF KEYWORD2
getTempCByIndex KEYWORD2
getTempFByIndex KEYWORD2
rawToCelsius KEYWORD2
rawToFahrenheit KEYWORD2
setWaitForConversion KEYWORD2
getWaitForConversion KEYWORD2
requestTemperatures KEYWORD2
requestTemperaturesByAddress KEYWORD2
requestTemperaturesByIndex KEYWORD2
setCheckForConversion KEYWORD2
getCheckForConversion KEYWORD2
isConversionComplete KEYWORD2
millisToWaitForConversion KEYWORD2
isParasitePowerMode KEYWORD2
begin KEYWORD2
getDeviceCount KEYWORD2
getDS18Count KEYWORD2
getAddress KEYWORD2
validAddress KEYWORD2
validFamily KEYWORD2
isConnected KEYWORD2
readScratchPad KEYWORD2
writeScratchPad KEYWORD2
readPowerSupply KEYWORD2
saveScratchPadByIndex KEYWORD2
saveScratchPad KEYWORD2
recallScratchPadByIndex KEYWORD2
recallScratchPad KEYWORD2
setAutoSaveScratchPad KEYWORD2
getAutoSaveScratchPad KEYWORD2
setHighAlarmTemp KEYWORD2
setLowAlarmTemp KEYWORD2
getHighAlarmTemp KEYWORD2
getLowAlarmTemp KEYWORD2
resetAlarmSearch KEYWORD2
alarmSearch KEYWORD2
hasAlarm KEYWORD2
toCelsius KEYWORD2
processAlarms KEYWORD2
setAlarmHandler KEYWORD2
hasAlarmHandler KEYWORD2
setUserData KEYWORD2
setUserDataByIndex KEYWORD2
getUserData KEYWORD2
getUserDataByIndex KEYWORD2
calculateTemperature KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
DEVICE_DISCONNECTED_C LITERAL1
DEVICE_DISCONNECTED_F LITERAL1
DEVICE_DISCONNECTED_RAW LITERAL1
DEVICE_FAULT_OPEN_C LITERAL1
DEVICE_FAULT_OPEN_F LITERAL1
DEVICE_FAULT_OPEN_RAW LITERAL1
DEVICE_FAULT_SHORTGND_C LITERAL1
DEVICE_FAULT_SHORTGND_F LITERAL1
DEVICE_FAULT_SHORTGND_RAW LITERAL1
DEVICE_FAULT_SHORTVDD_C LITERAL1
DEVICE_FAULT_SHORTVDD_F LITERAL1
DEVICE_FAULT_SHORTVDD_RAW LITERAL1

View File

@ -0,0 +1,38 @@
// {
// "name": "DallasTemperature",
// "keywords": "onewire, 1-wire, bus, sensor, temperature",
// "description": "Arduino Library for Dallas Temperature ICs (DS18B20, DS18S20, DS1822, DS1820, MAX31850)",
// "repository":
// {
// "type": "git",
// "url": "https://github.com/milesburton/Arduino-Temperature-Control-Library.git"
// },
// "authors":
// [
// {
// "name": "Miles Burton",
// "email": "miles@mnetcs.com",
// "url": "http://www.milesburton.com",
// "maintainer": true
// },
// {
// "name": "Tim Newsome",
// "email": "nuisance@casualhacker.net"
// },
// {
// "name": "Guil Barros",
// "email": "gfbarros@bappos.com"
// },
// {
// "name": "Rob Tillaart",
// "email": "rob.tillaart@gmail.com"
// }
// ],
// "dependencies":
// {
// "paulstoffregen/OneWire": "^2.3.5"
// },
// "version": "3.11.0",
// "frameworks": "arduino",
// "platforms": "*"
// }

View File

@ -0,0 +1,10 @@
name=DallasTemperature
version=3.11.0
author=Miles Burton <miles@mnetcs.com>, Tim Newsome <nuisance@casualhacker.net>, Guil Barros <gfbarros@bappos.com>, Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Miles Burton <miles@mnetcs.com>
sentence=Arduino Library for Dallas Temperature ICs
paragraph=Supports DS18B20, DS18S20, DS1822, DS1820, MAX31850
category=Sensors
url=https://github.com/milesburton/Arduino-Temperature-Control-Library
architectures=*
depends=OneWire

33
lib/EspSoftwareSerial/.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
#Ignore thumbnails created by Windows
Thumbs.db
#Ignore files built by Visual Studio
*.obj
*.exe
*.pdb
*.user
*.aps
*.pch
*.vspscc
*_i.c
*_p.c
*.ncb
*.suo
*.tlb
*.tlh
*.bak
*.cache
*.ilk
*.log
[Bb]in
[Dd]ebug*/
*.lib
*.sbr
obj/
[Rr]elease*/
_ReSharper*/
[Tt]est[Rr]esult*
.vs/
#Nuget packages folder
packages/
__vm/

View File

@ -0,0 +1 @@
{"type": "library", "name": "EspSoftwareSerial", "version": "6.17.1", "spec": {"owner": "plerup", "id": 168, "name": "EspSoftwareSerial", "requirements": null, "uri": null}}

View File

@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -0,0 +1,169 @@
# EspSoftwareSerial
## Implementation of the Arduino software serial library for the ESP8266 / ESP32 family
This fork implements interrupt service routine best practice.
In the receive interrupt, instead of blocking for whole bytes
at a time - voiding any near-realtime behavior of the CPU - only level
change and timestamp are recorded. The more time consuming phase
detection and byte assembly are done in the main code.
Except at high bitrates, depending on other ongoing activity,
interrupts in particular, this software serial adapter
supports full duplex receive and send. At high bitrates (115200bps)
send bit timing can be improved at the expense of blocking concurrent
full duplex receives, with the `SoftwareSerial::enableIntTx(false)` function call.
The same functionality is given as the corresponding AVR library but
several instances can be active at the same time. Speed up to 115200 baud
is supported. Besides a constructor compatible to the AVR SoftwareSerial class,
and updated constructor that takes no arguments exists, instead the `begin()`
function can handle the pin assignments and logic inversion.
It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer.
This way, it is a better drop-in replacement for the hardware serial APIs on the ESP MCUs.
Please note that due to the fact that the ESPs always have other activities
ongoing, there will be some inexactness in interrupt timings. This may
lead to inevitable, but few, bit errors when having heavy data traffic
at high baud rates.
This library supports ESP8266, ESP32, ESP32-S2 and ESP32-C3 devices.
## Resource optimization
The memory footprint can be optimized to just fit the amount of expected
incoming asynchronous data.
For this, the `SoftwareSerial` constructor provides two arguments. First, the
octet buffer capacity for assembled received octets can be set. Read calls are
satisfied from this buffer, freeing it in return.
Second, the signal edge detection buffer of 32bit fields can be resized.
One octet may require up to to 10 fields, but fewer may be needed,
depending on the bit pattern. Any read or write calls check this buffer
to assemble received octets, thus promoting completed octets to the octet
buffer, freeing fields in the edge detection buffer.
Look at the swsertest.ino example. There, on reset, ASCII characters ' ' to 'z'
are sent. This happens not as a block write, but in a single write call per
character. As the example uses a local loopback wire, every outgoing bit is
immediately received back. Therefore, any single write call causes up to
10 fields - depending on the exact bit pattern - to be occupied in the signal
edge detection buffer. In turn, as explained before, each single write call
also causes received bit assembly to be performed, promoting these bits from
the signal edge detection buffer to the octet buffer as soon as possible.
Explaining by way of contrast, if during a a single write call, perhaps because
of using block writing, more than a single octet is received, there will be a
need for more than 10 fields in the signal edge detection buffer.
The necessary capacity of the octet buffer only depends on the amount of incoming
data until the next read call.
For the swsertest.ino example, this results in the following optimized
constructor arguments to spend only the minimum RAM on buffers required:
The octet buffer capacity (`bufCapacity`) is 95 (93 characters net plus two tolerance).
The signal edge detection buffer capacity (`isrBufCapacity`) is 11, as each
single octet can have up to 11 bits on the wire,
which are immediately received during the write, and each
write call causes the signal edge detection to promote the previously sent and
received bits to the octet buffer.
In a more generalized scenario, calculate the bits (use message size in octets
times 10) that may be asynchronously received to determine the value for
`isrBufCapacity` in the constructor. Also use the number of received octets
that must be buffered for reading as the value of `bufCapacity`.
The more frequently your code calls write or read functions, the greater the
chances are that you can reduce the `isrBufCapacity` footprint without losing data,
and each time you call read to fetch from the octet buffer, you reduce the
need for space there.
## SoftwareSerialConfig and parity
The configuration of the data stream is done via a `SoftwareSerialConfig`
argument to `begin()`. Word lengths can be set to between 5 and 8 bits, parity
can be N(one), O(dd) or E(ven) and 1 or 2 stop bits can be used. The default is
`SWSERIAL_8N1` using 8 bits, no parity and 1 stop bit but any combination can
be used, e.g. `SWSERIAL_7E2`. If using EVEN or ODD parity, any parity errors
can be detected with the `readParity()` and `parityEven()` or `parityOdd()`
functions respectively. Note that the result of `readParity()` always applies
to the preceding `read()` or `peek()` call, and is undefined if they report
no data or an error.
To allow flexible 9-bit and data/addressing protocols, the additional parity
modes MARK and SPACE are also available. Furthermore, the parity mode can be
individually set in each call to `write()`.
This allows a simple implementation of protocols where the parity bit is used to
distinguish between data and addresses/commands ("9-bit" protocols). First set
up SoftwareSerial with parity mode SPACE, e.g. `SWSERIAL_8S1`. This will add a
parity bit to every byte sent, setting it to logical zero (SPACE parity).
To detect incoming bytes with the parity bit set (MARK parity), use the
`readParity()` function. To send a byte with the parity bit set, just add
`MARK` as the second argument when writing, e.g. `write(ch, SWSERIAL_PARITY_MARK)`.
## Checking for correct pin selection / configuration
In general, most pins on the ESP8266 and ESP32 devices can be used by SoftwareSerial,
however each device has a number of pins that have special functions or require careful
handling to prevent undesirable situations, for example they are connected to the
on-board SPI flash memory or they are used to determine boot and programming modes
after powerup or brownouts. These pins are not able to be configured by this library.
The exact list for each device can be found in the
[ESP32 data sheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
in sections 2.2 (Pin Descriptions) and 2.4 (Strapping pins). There is a discussion
dedicated to the use of GPIO12 in this
[note about GPIO12](https://github.com/espressif/esp-idf/tree/release/v3.2/examples/storage/sd_card#note-about-gpio12).
Refer to the `isValidGPIOpin()`, `isValidRxGPIOpin()` and `isValidTxGPIOpin()`
functions for the GPIO restrictions enforced by this library by default.
The easiest and safest method is to test the object returned at runtime, to see if
it is valid. For example:
```
#include <SoftwareSerial.h>
#define MYPORT_TX 12
#define MYPORT_RX 13
SoftwareSerial myPort;
[...]
Serial.begin(115200); // Standard hardware serial port
myPort.begin(38400, SWSERIAL_8N1, MYPORT_RX, MYPORT_TX, false);
if (!myPort) { // If the object did not initialize, then its configuration is invalid
Serial.println("Invalid SoftwareSerial pin configuration, check config");
while (1) { // Don't continue with invalid configuration
delay (1000);
}
}
[...]
```
## Using and updating EspSoftwareSerial in the esp8266com/esp8266 Arduino build environment
EspSoftwareSerial is both part of the BSP download for ESP8266 in Arduino,
and it is set up as a Git submodule in the esp8266 source tree,
specifically in `.../esp8266/libraries/SoftwareSerial` when using a Github
repository clone in your Arduino sketchbook hardware directory.
This supersedes any version of EspSoftwareSerial installed for instance via
the Arduino library manager, it is not required to install EspSoftwareSerial
for the ESP8266 separately at all, but doing so has ill effect.
The responsible maintainer of the esp8266 repository has kindly shared the
following command line instructions to use, if one wishes to manually
update EspSoftwareSerial to a newer release than pulled in via the ESP8266 Arduino BSP:
To update esp8266/arduino SoftwareSerial submodule to lastest master:
Clean it (optional):
```shell
$ rm -rf libraries/SoftwareSerial
$ git submodule update --init
```
Now update it:
```shell
$ cd libraries/SoftwareSerial
$ git checkout master
$ git pull
```

View File

@ -0,0 +1,71 @@
#include "SoftwareSerial.h"
#ifndef D5
#if defined(ESP8266)
#define D8 (15)
#define D5 (14)
#define D7 (13)
#define D6 (12)
#define RX (3)
#define TX (1)
#elif defined(ESP32)
#define D8 (5)
#define D5 (18)
#define D7 (23)
#define D6 (19)
#define RX (3)
#define TX (1)
#endif
#endif
SoftwareSerial swSer;
#ifdef ESP8266
auto logSer = SoftwareSerial(-1, TX);
auto hwSer = Serial;
#else
auto logSer = Serial;
auto hwSer = Serial1;
#endif
constexpr uint32_t TESTBPS = 115200;
void setup() {
delay(2000);
#ifdef ESP8266
hwSer.begin(TESTBPS, SERIAL_8N1);
hwSer.swap();
#else
hwSer.begin(TESTBPS, SERIAL_8N1, D6, D5);
#endif
logSer.begin(115200);
logSer.println(PSTR("\nOne Wire Half Duplex Bitpattern and Datarate Test"));
swSer.begin(TESTBPS, SWSERIAL_8N1, D6, D5);
swSer.enableIntTx(true);
logSer.println(PSTR("Tx on swSer"));
}
uint8_t val = 0xff;
void loop() {
swSer.write((uint8_t)0x00);
swSer.write(val);
swSer.write(val);
auto start = ESP.getCycleCount();
int rxCnt = 0;
while (ESP.getCycleCount() - start < ESP.getCpuFreqMHz() * 1000000 / 10) {
if (hwSer.available()) {
auto rxVal = hwSer.read();
if ((!rxCnt && rxVal) || (rxCnt && rxVal != val)) {
logSer.printf(PSTR("Rx bit error: tx = 0x%02x, rx = 0x%02x\n"), val, rxVal);
}
++rxCnt;
}
}
if (rxCnt != 3) {
logSer.printf(PSTR("Rx cnt error, tx = 0x%02x\n"), val);
}
++val;
if (!val) {
logSer.println("Starting over");
}
}

View File

@ -0,0 +1,279 @@
#include <SoftwareSerial.h>
// On ESP8266:
// Local SoftwareSerial loopback, connect D5 (rx) and D6 (tx).
// For local hardware loopback, connect D5 to D8 (tx), D6 to D7 (rx).
// For hardware send/sink, connect D7 (rx) and D8 (tx).
// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking
// interrupts severely impacts the ability of the SoftwareSerial devices to operate concurrently
// and/or in duplex mode.
// Operating in software serial full duplex mode, runs at 19200bps and few errors (~2.5%).
// Operating in software serial half duplex mode (both loopback and repeater),
// runs at 57600bps with nearly no errors.
// Operating loopback in full duplex, and repeater in half duplex, runs at 38400bps with nearly no errors.
// On ESP32:
// For SoftwareSerial or hardware send/sink, connect D5 (rx) and D6 (tx).
// Hardware Serial2 defaults to D4 (rx), D3 (tx).
// For local hardware loopback, connect D5 (rx) to D3 (tx), D6 (tx) to D4 (rx).
#ifndef D5
#if defined(ESP8266)
#define D8 (15)
#define D5 (14)
#define D7 (13)
#define D6 (12)
#define RX (3)
#define TX (1)
#elif defined(ESP32)
#define D8 (5)
#define D5 (18)
#define D7 (23)
#define D6 (19)
#define RX (3)
#define TX (1)
#endif
#endif
// Pick only one of HWLOOPBACK, HWSOURCESWSINK, or HWSOURCESINK
//#define HWLOOPBACK 1
//#define HWSOURCESWSINK 1
//#define HWSOURCESINK 1
#define HALFDUPLEX 1
#ifdef ESP32
constexpr int IUTBITRATE = 19200;
#else
constexpr int IUTBITRATE = 19200;
#endif
#if defined(ESP8266)
constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1;
constexpr SerialConfig hwSerialConfig = SERIAL_8E1;
#elif defined(ESP32)
constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1;
constexpr uint32_t hwSerialConfig = SERIAL_8E1;
#else
constexpr unsigned swSerialConfig = 3;
#endif
constexpr bool invert = false;
constexpr int BLOCKSIZE = 16; // use fractions of 256
unsigned long start;
const char effTxTxt[] PROGMEM = "eff. tx: ";
const char effRxTxt[] PROGMEM = "eff. rx: ";
int txCount;
int rxCount;
int expected;
int rxErrors;
int rxParityErrors;
constexpr int ReportInterval = IUTBITRATE / 8;
#if defined(ESP8266)
#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK)
HardwareSerial& hwSerial(Serial);
SoftwareSerial serialIUT;
SoftwareSerial logger;
#elif defined(HWSOURCESINK)
HardwareSerial& serialIUT(Serial);
SoftwareSerial logger;
#else
SoftwareSerial serialIUT;
HardwareSerial& logger(Serial);
#endif
#elif defined(ESP32)
#if defined(HWLOOPBACK) || defined (HWSOURCESWSINK)
HardwareSerial& hwSerial(Serial2);
SoftwareSerial serialIUT;
#elif defined(HWSOURCESINK)
HardwareSerial& serialIUT(Serial2);
#else
SoftwareSerial serialIUT;
#endif
HardwareSerial& logger(Serial);
#else
SoftwareSerial serialIUT(14, 12);
HardwareSerial& logger(Serial);
#endif
void setup() {
#if defined(ESP8266)
#if defined(HWLOOPBACK) || defined(HWSOURCESINK) || defined(HWSOURCESWSINK)
Serial.begin(IUTBITRATE, hwSerialConfig, SERIAL_FULL, 1, invert);
Serial.swap();
Serial.setRxBufferSize(2 * BLOCKSIZE);
logger.begin(9600, SWSERIAL_8N1, -1, TX);
#else
logger.begin(9600);
#endif
#if !defined(HWSOURCESINK)
serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE);
#ifdef HALFDUPLEX
serialIUT.enableIntTx(false);
#endif
#endif
#elif defined(ESP32)
#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK)
Serial2.begin(IUTBITRATE, hwSerialConfig, D4, D3, invert);
Serial2.setRxBufferSize(2 * BLOCKSIZE);
#elif defined(HWSOURCESINK)
serialIUT.begin(IUTBITRATE, hwSerialConfig, D5, D6, invert);
serialIUT.setRxBufferSize(2 * BLOCKSIZE);
#endif
#if !defined(HWSOURCESINK)
serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE);
#ifdef HALFDUPLEX
serialIUT.enableIntTx(false);
#endif
#endif
logger.begin(9600);
#else
#if !defined(HWSOURCESINK)
serialIUT.begin(IUTBITRATE);
#endif
logger.begin(9600);
#endif
logger.println(PSTR("Loopback example for EspSoftwareSerial"));
start = micros();
txCount = 0;
rxCount = 0;
rxErrors = 0;
rxParityErrors = 0;
expected = -1;
}
unsigned char c = 0;
void loop() {
#ifdef HALFDUPLEX
char block[BLOCKSIZE];
#endif
char inBuf[BLOCKSIZE];
for (int i = 0; i < BLOCKSIZE; ++i) {
#ifndef HALFDUPLEX
#ifdef HWSOURCESWSINK
hwSerial.write(c);
#else
serialIUT.write(c);
#endif
#ifdef HWLOOPBACK
int avail = hwSerial.available();
while ((0 == (i % 8)) && avail > 0) {
int inCnt = hwSerial.read(inBuf, min(avail, min(BLOCKSIZE, hwSerial.availableForWrite())));
hwSerial.write(inBuf, inCnt);
avail -= inCnt;
}
#endif
#else
block[i] = c;
#endif
c = (c + 1) % 256;
++txCount;
}
#ifdef HALFDUPLEX
#ifdef HWSOURCESWSINK
hwSerial.write(block, BLOCKSIZE);
#else
serialIUT.write(block, BLOCKSIZE);
#endif
#endif
#ifdef HWSOURCESINK
#if defined(ESP8266)
if (serialIUT.hasOverrun()) { logger.println(PSTR("serialIUT.overrun")); }
#endif
#else
if (serialIUT.overflow()) { logger.println(PSTR("serialIUT.overflow")); }
#endif
int inCnt;
uint32_t deadlineStart;
#ifdef HWLOOPBACK
// starting deadline for the first bytes to become readable
deadlineStart = ESP.getCycleCount();
inCnt = 0;
while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) {
int avail = hwSerial.available();
inCnt += hwSerial.read(&inBuf[inCnt], min(avail, min(BLOCKSIZE - inCnt, hwSerial.availableForWrite())));
if (inCnt >= BLOCKSIZE) { break; }
// wait for more outstanding bytes to trickle in
if (avail) deadlineStart = ESP.getCycleCount();
}
hwSerial.write(inBuf, inCnt);
#endif
// starting deadline for the first bytes to come in
deadlineStart = ESP.getCycleCount();
inCnt = 0;
while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 8 * ESP.getCpuFreqMHz()) {
int avail;
if (0 != (swSerialConfig & 070))
avail = serialIUT.available();
else
avail = serialIUT.read(inBuf, BLOCKSIZE);
for (int i = 0; i < avail; ++i)
{
unsigned char r;
if (0 != (swSerialConfig & 070))
r = serialIUT.read();
else
r = inBuf[i];
if (expected == -1) { expected = r; }
else {
expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4));
}
if (r != expected) {
++rxErrors;
expected = -1;
}
#ifndef HWSOURCESINK
if (serialIUT.readParity() != (static_cast<bool>(swSerialConfig & 010) ? serialIUT.parityOdd(r) : serialIUT.parityEven(r)))
{
++rxParityErrors;
}
#elif defined(ESP8266)
// current ESP8266 API does not flag parity errors separately
if (serialIUT.hasRxError())
{
++rxParityErrors;
}
#endif
++rxCount;
++inCnt;
}
if (inCnt >= BLOCKSIZE) { break; }
// wait for more outstanding bytes to trickle in
if (avail) deadlineStart = ESP.getCycleCount();
}
const uint32_t interval = micros() - start;
if (txCount >= ReportInterval && interval) {
uint8_t wordBits = (5 + swSerialConfig % 4) + static_cast<bool>(swSerialConfig & 070) + 1 + ((swSerialConfig & 0300) ? 1 : 0);
logger.println(String(PSTR("tx/rx: ")) + txCount + PSTR("/") + rxCount);
const long txCps = txCount * (1000000.0 / interval);
const long rxCps = rxCount * (1000000.0 / interval);
logger.print(String(FPSTR(effTxTxt)) + wordBits * txCps + PSTR("bps, ")
+ effRxTxt + wordBits * rxCps + PSTR("bps, ")
+ rxErrors + PSTR(" errors (") + 100.0 * rxErrors / (!rxErrors ? 1 : rxCount) + PSTR("%)"));
if (0 != (swSerialConfig & 070))
{
logger.print(PSTR(" (")); logger.print(rxParityErrors); logger.println(PSTR(" parity errors)"));
}
else
{
logger.println();
}
txCount = 0;
rxCount = 0;
rxErrors = 0;
rxParityErrors = 0;
expected = -1;
// resync
delay(1000UL * 12 * BLOCKSIZE / IUTBITRATE * 16);
serialIUT.flush();
start = micros();
}
}

View File

@ -0,0 +1,59 @@
#include "SoftwareSerial.h"
#ifndef D5
#if defined(ESP8266)
#define D5 (14)
#define D6 (12)
#elif defined(ESP32)
#define D5 (18)
#define D6 (19)
#endif
#endif
SoftwareSerial swSer1;
SoftwareSerial swSer2;
void setup() {
delay(2000);
Serial.begin(115200);
Serial.println(PSTR("\nOne Wire Half Duplex Serial Tester"));
swSer1.begin(115200, SWSERIAL_8N1, D6, D6, false, 256);
// high speed half duplex, turn off interrupts during tx
swSer1.enableIntTx(false);
swSer2.begin(115200, SWSERIAL_8N1, D5, D5, false, 256);
// high speed half duplex, turn off interrupts during tx
swSer2.enableIntTx(false);
}
void loop() {
Serial.println(PSTR("\n\nTesting on swSer1"));
Serial.print(PSTR("Enter something to send using swSer1."));
checkSwSerial(&swSer1);
Serial.println(PSTR("\n\nTesting on swSer2"));
Serial.print(PSTR("Enter something to send using swSer2."));
checkSwSerial(&swSer2);
}
void checkSwSerial(SoftwareSerial* ss) {
byte ch;
while (!Serial.available());
ss->enableTx(true);
while (Serial.available()) {
ch = Serial.read();
ss->write(ch);
}
ss->enableTx(false);
// wait 1 second for the reply from SOftwareSerial if any
delay(1000);
if (ss->available()) {
Serial.print(PSTR("\nResult:"));
while (ss->available()) {
ch = (byte)ss->read();
Serial.print(ch < 0x10 ? PSTR(" 0") : PSTR(" "));
Serial.print(ch, HEX);
}
Serial.println();
}
}

View File

@ -0,0 +1,199 @@
#include <SoftwareSerial.h>
// On ESP8266:
// SoftwareSerial loopback for remote source (loopback.ino), or hardware loopback.
// Connect source D5 (rx) to local D8 (tx), source D6 (tx) to local D7 (rx).
// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking
// interrupts severely impacts the ability of the SoftwareSerial devices to operate concurrently
// and/or in duplex mode.
// On ESP32:
// For software or hardware loopback, connect source rx to local D8 (tx), source tx to local D7 (rx).
#ifndef D5
#if defined(ESP8266)
#define D8 (15)
#define D5 (14)
#define D7 (13)
#define D6 (12)
#define RX (3)
#define TX (1)
#elif defined(ESP32)
#define D8 (5)
#define D5 (18)
#define D7 (23)
#define D6 (19)
#define RX (3)
#define TX (1)
#endif
#endif
#define HWLOOPBACK 1
#define HALFDUPLEX 1
#ifdef ESP32
constexpr int IUTBITRATE = 19200;
#else
constexpr int IUTBITRATE = 19200;
#endif
#if defined(ESP8266)
constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1;
constexpr SerialConfig hwSerialConfig = SERIAL_8E1;
#elif defined(ESP32)
constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1;
constexpr uint32_t hwSerialConfig = SERIAL_8E1;
#else
constexpr unsigned swSerialConfig = 3;
#endif
constexpr bool invert = false;
constexpr int BLOCKSIZE = 16; // use fractions of 256
unsigned long start;
const char bitRateTxt[] PROGMEM = "Effective data rate: ";
int rxCount;
int seqErrors;
int parityErrors;
int expected;
constexpr int ReportInterval = IUTBITRATE / 8;
#if defined(ESP8266)
#if defined(HWLOOPBACK)
HardwareSerial& repeater(Serial);
SoftwareSerial logger;
#else
SoftwareSerial repeater;
HardwareSerial& logger(Serial);
#endif
#elif defined(ESP32)
#if defined(HWLOOPBACK)
HardwareSerial& repeater(Serial2);
#else
SoftwareSerial repeater;
#endif
HardwareSerial& logger(Serial);
#else
SoftwareSerial repeater(14, 12);
HardwareSerial& logger(Serial);
#endif
void setup() {
#if defined(ESP8266)
#if defined(HWLOOPBACK)
repeater.begin(IUTBITRATE, hwSerialConfig, SERIAL_FULL, 1, invert);
repeater.swap();
repeater.setRxBufferSize(2 * BLOCKSIZE);
logger.begin(9600, SWSERIAL_8N1, -1, TX);
#else
repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE);
#ifdef HALFDUPLEX
repeater.enableIntTx(false);
#endif
logger.begin(9600);
#endif
#elif defined(ESP32)
#if defined(HWLOOPBACK)
repeater.begin(IUTBITRATE, hwSerialConfig, D7, D8, invert);
repeater.setRxBufferSize(2 * BLOCKSIZE);
#else
repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE);
#ifdef HALFDUPLEX
repeater.enableIntTx(false);
#endif
#endif
logger.begin(9600);
#else
repeater.begin(IUTBITRATE);
logger.begin(9600);
#endif
logger.println(PSTR("Repeater example for EspSoftwareSerial"));
start = micros();
rxCount = 0;
seqErrors = 0;
parityErrors = 0;
expected = -1;
}
void loop() {
#ifdef HWLOOPBACK
#if defined(ESP8266)
if (repeater.hasOverrun()) { logger.println(PSTR("repeater.overrun")); }
#endif
#else
if (repeater.overflow()) { logger.println(PSTR("repeater.overflow")); }
#endif
#ifdef HALFDUPLEX
char block[BLOCKSIZE];
#endif
// starting deadline for the first bytes to come in
uint32_t deadlineStart = ESP.getCycleCount();
int inCnt = 0;
while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) {
int avail = repeater.available();
for (int i = 0; i < avail; ++i)
{
int r = repeater.read();
if (r == -1) { logger.println(PSTR("read() == -1")); }
if (expected == -1) { expected = r; }
else {
expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4));
}
if (r != expected) {
++seqErrors;
expected = -1;
}
#ifndef HWLOOPBACK
if (repeater.readParity() != (static_cast<bool>(swSerialConfig & 010) ? repeater.parityOdd(r) : repeater.parityEven(r)))
{
++parityErrors;
}
#elif defined(ESP8266)
// current ESP8266 API does not flag parity errors separately
if (repeater.hasRxError())
{
++parityErrors;
}
#endif
++rxCount;
#ifdef HALFDUPLEX
block[inCnt] = r;
#else
repeater.write(r);
#endif
if (++inCnt >= BLOCKSIZE) { break; }
}
if (inCnt >= BLOCKSIZE) { break; }
// wait for more outstanding bytes to trickle in
if (avail) deadlineStart = ESP.getCycleCount();
}
#ifdef HALFDUPLEX
repeater.write(block, inCnt);
#endif
if (rxCount >= ReportInterval) {
auto end = micros();
unsigned long interval = end - start;
long cps = rxCount * (1000000.0 / interval);
long seqErrorsps = seqErrors * (1000000.0 / interval);
logger.print(String(FPSTR(bitRateTxt)) + 10 * cps + PSTR("bps, ")
+ seqErrorsps + PSTR("cps seq. errors (") + 100.0 * seqErrors / rxCount + PSTR("%)"));
#ifndef HWLOOPBACK
if (0 != (swSerialConfig & 070))
{
logger.print(PSTR(" (")); logger.print(parityErrors); logger.println(PSTR(" parity errors)"));
}
else
#endif
{
logger.println();
}
start = end;
rxCount = 0;
seqErrors = 0;
parityErrors = 0;
expected = -1;
}
}

View File

@ -0,0 +1,113 @@
#include <SoftwareSerial.h>
SoftwareSerial swSer;
byte buf[10] = { 0xFA, 0xAF,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xED };
const byte cmd[10] PROGMEM = { 0xFA, 0xAF,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xED };
void setup() {
delay(2000);
Serial.begin(115200);
Serial.println(PSTR("\nAlpha 1S Servo Tester"));
swSer.begin(115200, SWSERIAL_8N1, 12, 12, false, 256);
}
void loop() {
for (int i = 1; i <= 32; i++) {
GetVersion(i);
delay(100);
}
SetLED(1, 0);
GoPos(1, 0, 50);
delay(1000);
GoPos(1, 90, 50);
delay(1000);
GoPos(1, 100, 50);
delay(1000);
SetLED(1, 1);
delay(2000);
}
void GetVersion(byte id) {
memcpy_P(buf, cmd, 10);
buf[0] = 0xFC;
buf[1] = 0xCF;
buf[2] = id;
buf[3] = 0x01;
SendCommand();
}
void GoPos(byte id, byte Pos, byte Time) {
memcpy_P(buf, cmd, 10);
buf[2] = id;
buf[3] = 0x01;
buf[4] = Pos;
buf[5] = Time;
buf[6] = 0x00;
buf[7] = Time;
SendCommand();
}
void GetPos(byte id) {
memcpy_P(buf, cmd, 10);
buf[2] = id;
buf[3] = 0x02;
SendCommand();
}
void SetLED(byte id, byte mode) {
memcpy_P(buf, cmd, 10);
buf[2] = id;
buf[3] = 0x04;
buf[4] = mode;
SendCommand();
}
void SendCommand() {
SendCommand(true);
}
void SendCommand(bool checkResult) {
byte sum = 0;
for (int i = 2; i < 8; i++) {
sum += buf[i];
}
buf[8] = sum;
ShowCommand();
swSer.flush();
swSer.enableTx(true);
swSer.write(buf, 10);
swSer.enableTx(false);
if (checkResult) checkReturn();
}
void ShowCommand() {
Serial.print(millis());
Serial.print(PSTR(" OUT>>"));
for (int i = 0; i < 10; i++) {
Serial.print(buf[i] < 0x10 ? PSTR(" 0") : PSTR(" "));
Serial.print(buf[i], HEX);
}
Serial.println();
}
void checkReturn() {
unsigned long startMs = millis();
while (((millis() - startMs) < 500) && (!swSer.available()));
if (swSer.available()) {
Serial.print(millis());
Serial.print(PSTR(" IN>>>"));
while (swSer.available()) {
byte ch = (byte)swSer.read();
Serial.print((ch < 0x10 ? PSTR(" 0") : PSTR(" ")));
Serial.print(ch, HEX);
}
Serial.println();
}
}

View File

@ -0,0 +1,79 @@
// On ESP8266:
// At 80MHz runs up 57600ps, and at 160MHz CPU frequency up to 115200bps with only negligible errors.
// Connect pin 13 to 15.
// For verification and as a example for how to use SW serial on the USB to PC connection,
// which allows the use of HW Serial on GPIO13 and GPIO15 instead, #define SWAPSERIAL below.
// Notice how the bitrates are also swapped then between RX/TX and GPIO13/GPIO15.
// Builtin debug output etc. must be stopped on HW Serial in this case, as it would interfere with the
// external communication on GPIO13/GPIO15.
#include <SoftwareSerial.h>
#ifndef D5
#if defined(ESP8266)
#define D8 (15)
#define D5 (14)
#define D7 (13)
#define D6 (12)
#define RX (3)
#define TX (1)
#elif defined(ESP32)
#define D8 (5)
#define D5 (18)
#define D7 (23)
#define D6 (19)
#define RX (3)
#define TX (1)
#endif
#endif
#ifdef ESP32
#define BAUD_RATE 57600
#else
#define BAUD_RATE 57600
#endif
#undef SWAPSERIAL
#ifndef SWAPSERIAL
auto& usbSerial = Serial;
SoftwareSerial testSerial;
#else
SoftwareSerial usbSerial;
auto& testSerial = Serial;
#endif
void setup() {
#ifndef SWAPSERIAL
usbSerial.begin(115200);
// Important: the buffer size optimizations here, in particular the isrBufSize (11) that is only sufficiently
// large to hold a single word (up to start - 8 data - parity - stop), are on the basis that any char written
// to the loopback SoftwareSerial adapter gets read before another write is performed.
// Block writes with a size greater than 1 would usually fail. Do not copy this into your own project without
// reading the documentation.
testSerial.begin(BAUD_RATE, SWSERIAL_8N1, D7, D8, false, 95, 11);
#else
testSerial.begin(115200);
testSerial.setDebugOutput(false);
testSerial.swap();
usbSerial.begin(BAUD_RATE, SWSERIAL_8N1, RX, TX, false, 95);
#endif
usbSerial.println(PSTR("\nSoftware serial test started"));
for (char ch = ' '; ch <= 'z'; ch++) {
testSerial.write(ch);
}
testSerial.println();
}
void loop() {
while (testSerial.available() > 0) {
usbSerial.write(testSerial.read());
yield();
}
while (usbSerial.available() > 0) {
testSerial.write(usbSerial.read());
yield();
}
}

View File

@ -0,0 +1,43 @@
#######################################
# Syntax Coloring Map for SoftwareSerial
# (esp8266)
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
SoftwareSerial KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
baudRate KEYWORD2
setTransmitEnablePin KEYWORD2
enableIntTx KEYWORD2
overflow KEYWORD2
available KEYWORD2
peek KEYWORD2
read KEYWORD2
flush KEYWORD2
write KEYWORD2
enableRx KEYWORD2
enableTx KEYWORD2
listen KEYWORD2
end KEYWORD2
isListening KEYWORD2
stopListening KEYWORD2
onReceive KEYWORD2
perform_work KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
SW_SERIAL_UNUSED_PIN LITERAL1
SWSERIAL_5N1 LITERAL1
SWSERIAL_6N1 LITERAL1
SWSERIAL_7N1 LITERAL1
SWSERIAL_8N1 LITERAL1

View File

@ -0,0 +1,26 @@
// {
// "name": "EspSoftwareSerial",
// "version": "6.17.1",
// "description": "Implementation of the Arduino software serial for ESP8266/ESP32.",
// "keywords": [
// "serial", "io", "softwareserial"
// ],
// "repository":
// {
// "type": "git",
// "url": "https://github.com/plerup/espsoftwareserial"
// },
// "authors": [
// {
// "name": "Dirk Kaar"
// },
// {
// "name": "Peter Lerup"
// }
// ],
// "license": "LGPL-2.1+",
// "frameworks": "arduino",
// "platforms": [
// "espressif8266", "espressif32"
// ]
// }

View File

@ -0,0 +1,9 @@
; name=EspSoftwareSerial
; version=6.17.1
; author=Dirk Kaar, Peter Lerup
; maintainer=Dirk Kaar <dok@dok-net.net>
; sentence=Implementation of the Arduino software serial for ESP8266/ESP32.
; paragraph=
; category=Signal Input/Output
; url=https://github.com/plerup/espsoftwareserial/
; architectures=esp8266,esp32

View File

@ -0,0 +1,679 @@
/*
SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32.
Copyright (c) 2015-2016 Peter Lerup. All rights reserved.
Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "SoftwareSerial.h"
#include <Arduino.h>
#ifndef ESP32
uint32_t SoftwareSerial::m_savedPS = 0;
#else
portMUX_TYPE SoftwareSerial::m_interruptsMux = portMUX_INITIALIZER_UNLOCKED;
#endif
inline void IRAM_ATTR SoftwareSerial::disableInterrupts()
{
#ifndef ESP32
m_savedPS = xt_rsil(15);
#else
taskENTER_CRITICAL(&m_interruptsMux);
#endif
}
inline void IRAM_ATTR SoftwareSerial::restoreInterrupts()
{
#ifndef ESP32
xt_wsr_ps(m_savedPS);
#else
taskEXIT_CRITICAL(&m_interruptsMux);
#endif
}
constexpr uint8_t BYTE_ALL_BITS_SET = ~static_cast<uint8_t>(0);
SoftwareSerial::SoftwareSerial() {
m_isrOverflow = false;
m_rxGPIOPullUpEnabled = true;
m_txGPIOOpenDrain = false;
}
SoftwareSerial::SoftwareSerial(int8_t rxPin, int8_t txPin, bool invert)
{
m_isrOverflow = false;
m_rxGPIOPullUpEnabled = true;
m_txGPIOOpenDrain = false;
m_rxPin = rxPin;
m_txPin = txPin;
m_invert = invert;
}
SoftwareSerial::~SoftwareSerial() {
end();
}
#if __GNUC__ >= 10
constexpr
#endif
bool SoftwareSerial::isValidGPIOpin(int8_t pin) const {
#if defined(ESP8266)
return (pin >= 0 && pin <= 16) && !isFlashInterfacePin(pin);
#elif defined(ESP32)
// Remove the strapping pins as defined in the datasheets, they affect bootup and other critical operations
// Remmove the flash memory pins on related devices, since using these causes memory access issues.
#ifdef CONFIG_IDF_TARGET_ESP32
// Datasheet https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf,
// Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32/_images/esp32-devkitC-v4-pinout.jpg
return (pin == 1) || (pin >= 3 && pin <= 5) ||
(pin >= 12 && pin <= 15) ||
(!psramFound() && pin >= 16 && pin <= 17) ||
(pin >= 18 && pin <= 19) ||
(pin >= 21 && pin <= 23) || (pin >= 25 && pin <= 27) || (pin >= 32 && pin <= 39);
#elif CONFIG_IDF_TARGET_ESP32S2
// Datasheet https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf,
// Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/_images/esp32-s2_saola1-pinout.jpg
return (pin >= 1 && pin <= 21) || (pin >= 33 && pin <= 44);
#elif CONFIG_IDF_TARGET_ESP32C3
// Datasheet https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf,
// Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/_images/esp32-c3-devkitm-1-v1-pinout.jpg
return (pin >= 0 && pin <= 1) || (pin >= 3 && pin <= 7) || (pin >= 18 && pin <= 21);
#else
return pin >= 0;
#endif
#else
return pin >= 0;
#endif
}
#if __GNUC__ >= 10
constexpr
#endif
bool SoftwareSerial::isValidRxGPIOpin(int8_t pin) const {
return isValidGPIOpin(pin)
#if defined(ESP8266)
&& (pin != 16)
#endif
;
}
#if __GNUC__ >= 10
constexpr
#endif
bool SoftwareSerial::isValidTxGPIOpin(int8_t pin) const {
return isValidGPIOpin(pin)
#if defined(ESP32)
#ifdef CONFIG_IDF_TARGET_ESP32
&& (pin < 34)
#elif CONFIG_IDF_TARGET_ESP32S2
&& (pin <= 45)
#elif CONFIG_IDF_TARGET_ESP32C3
// no restrictions
#endif
#endif
;
}
#if __GNUC__ >= 10
constexpr
#endif
bool SoftwareSerial::hasRxGPIOPullUp(int8_t pin) const {
#if defined(ESP32)
return !(pin >= 34 && pin <= 39);
#else
(void)pin;
return true;
#endif
}
void SoftwareSerial::setRxGPIOPinMode() {
if (m_rxValid) {
pinMode(m_rxPin, hasRxGPIOPullUp(m_rxPin) && m_rxGPIOPullUpEnabled ? INPUT_PULLUP : INPUT);
}
}
void SoftwareSerial::setTxGPIOPinMode() {
if (m_txValid) {
pinMode(m_txPin, m_txGPIOOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT);
}
}
void SoftwareSerial::begin(uint32_t baud, SoftwareSerialConfig config,
int8_t rxPin, int8_t txPin,
bool invert, int bufCapacity, int isrBufCapacity) {
if (-1 != rxPin) m_rxPin = rxPin;
if (-1 != txPin) m_txPin = txPin;
m_oneWire = (m_rxPin == m_txPin);
m_invert = invert;
m_dataBits = 5 + (config & 07);
m_parityMode = static_cast<SoftwareSerialParity>(config & 070);
m_stopBits = 1 + ((config & 0300) ? 1 : 0);
m_pduBits = m_dataBits + static_cast<bool>(m_parityMode) + m_stopBits;
m_bitTicks = (microsToTicks(1000000UL) + baud / 2) / baud;
m_intTxEnabled = true;
if (isValidRxGPIOpin(m_rxPin)) {
m_rxReg = portInputRegister(digitalPinToPort(m_rxPin));
m_rxBitMask = digitalPinToBitMask(m_rxPin);
m_buffer.reset(new circular_queue<uint8_t>((bufCapacity > 0) ? bufCapacity : 64));
if (m_parityMode)
{
m_parityBuffer.reset(new circular_queue<uint8_t>((m_buffer->capacity() + 7) / 8));
m_parityInPos = m_parityOutPos = 1;
}
m_isrBuffer.reset(new circular_queue<uint32_t, SoftwareSerial*>((isrBufCapacity > 0) ?
isrBufCapacity : m_buffer->capacity() * (2 + m_dataBits + static_cast<bool>(m_parityMode))));
if (m_buffer && (!m_parityMode || m_parityBuffer) && m_isrBuffer) {
m_rxValid = true;
setRxGPIOPinMode();
}
}
if (isValidTxGPIOpin(m_txPin)) {
#if !defined(ESP8266)
m_txReg = portOutputRegister(digitalPinToPort(m_txPin));
#endif
m_txBitMask = digitalPinToBitMask(m_txPin);
m_txValid = true;
if (!m_oneWire) {
setTxGPIOPinMode();
digitalWrite(m_txPin, !m_invert);
}
}
enableRx(true);
}
void SoftwareSerial::end()
{
enableRx(false);
m_txValid = false;
if (m_buffer) {
m_buffer.reset();
}
m_parityBuffer.reset();
if (m_isrBuffer) {
m_isrBuffer.reset();
}
}
uint32_t SoftwareSerial::baudRate() {
return 1000000UL / ticksToMicros(m_bitTicks);
}
void SoftwareSerial::setTransmitEnablePin(int8_t txEnablePin) {
if (isValidTxGPIOpin(txEnablePin)) {
m_txEnableValid = true;
m_txEnablePin = txEnablePin;
pinMode(m_txEnablePin, OUTPUT);
digitalWrite(m_txEnablePin, LOW);
}
else {
m_txEnableValid = false;
}
}
void SoftwareSerial::enableIntTx(bool on) {
m_intTxEnabled = on;
}
void SoftwareSerial::enableRxGPIOPullUp(bool on) {
m_rxGPIOPullUpEnabled = on;
setRxGPIOPinMode();
}
void SoftwareSerial::enableTxGPIOOpenDrain(bool on) {
m_txGPIOOpenDrain = on;
setTxGPIOPinMode();
}
void SoftwareSerial::enableTx(bool on) {
if (m_txValid && m_oneWire) {
if (on) {
enableRx(false);
setTxGPIOPinMode();
digitalWrite(m_txPin, !m_invert);
}
else {
setRxGPIOPinMode();
enableRx(true);
}
}
}
void SoftwareSerial::enableRx(bool on) {
if (m_rxValid && on != m_rxEnabled) {
if (on) {
m_rxLastBit = m_pduBits - 1;
// Init to stop bit level and current tick
m_isrLastTick = (microsToTicks(micros()) | 1) ^ m_invert;
if (m_bitTicks >= microsToTicks(1000000UL / 74880UL))
attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast<void (*)(void*)>(rxBitISR), this, CHANGE);
else
attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast<void (*)(void*)>(rxBitSyncISR), this, m_invert ? RISING : FALLING);
}
else {
detachInterrupt(digitalPinToInterrupt(m_rxPin));
}
m_rxEnabled = on;
}
}
int SoftwareSerial::read() {
if (!m_rxValid) { return -1; }
if (!m_buffer->available()) {
rxBits();
if (!m_buffer->available()) { return -1; }
}
auto val = m_buffer->pop();
if (m_parityBuffer)
{
m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos;
m_parityOutPos <<= 1;
if (!m_parityOutPos)
{
m_parityOutPos = 1;
m_parityBuffer->pop();
}
}
return val;
}
int SoftwareSerial::read(uint8_t* buffer, size_t size) {
if (!m_rxValid) { return 0; }
int avail;
if (0 == (avail = m_buffer->pop_n(buffer, size))) {
rxBits();
avail = m_buffer->pop_n(buffer, size);
}
if (!avail) return 0;
if (m_parityBuffer) {
uint32_t parityBits = avail;
while (m_parityOutPos >>= 1) ++parityBits;
m_parityOutPos = (1 << (parityBits % 8));
m_parityBuffer->pop_n(nullptr, parityBits / 8);
}
return avail;
}
size_t SoftwareSerial::readBytes(uint8_t* buffer, size_t size) {
if (!m_rxValid || !size) { return 0; }
size_t count = 0;
auto start = millis();
do {
auto readCnt = read(&buffer[count], size - count);
count += readCnt;
if (count >= size) break;
if (readCnt) {
start = millis();
}
else {
optimistic_yield(1000UL);
}
} while (millis() - start < _timeout);
return count;
}
int SoftwareSerial::available() {
if (!m_rxValid) { return 0; }
rxBits();
int avail = m_buffer->available();
if (!avail) {
optimistic_yield(10000UL);
}
return avail;
}
void SoftwareSerial::lazyDelay() {
// Reenable interrupts while delaying to avoid other tasks piling up
if (!m_intTxEnabled) { restoreInterrupts(); }
const auto expired = microsToTicks(micros()) - m_periodStart;
const int32_t remaining = m_periodDuration - expired;
const int32_t ms = remaining > 0 ? static_cast<int32_t>(ticksToMicros(remaining) / 1000L) : 0;
if (ms > 0)
{
delay(ms);
}
else
{
optimistic_yield(10000UL);
}
// Assure that below-ms part of delays are not elided
preciseDelay();
// Disable interrupts again if applicable
if (!m_intTxEnabled) { disableInterrupts(); }
}
void IRAM_ATTR SoftwareSerial::preciseDelay() {
uint32_t ticks;
uint32_t expired;
do {
ticks = microsToTicks(micros());
expired = ticks - m_periodStart;
} while (static_cast<int32_t>(m_periodDuration - expired) > 0);
m_periodDuration = 0;
m_periodStart = ticks;
}
void IRAM_ATTR SoftwareSerial::writePeriod(
uint32_t dutyCycle, uint32_t offCycle, bool withStopBit) {
preciseDelay();
if (dutyCycle)
{
#if defined(ESP8266)
if (16 == m_txPin) {
GP16O = 1;
}
else {
GPOS = m_txBitMask;
}
#else
*m_txReg |= m_txBitMask;
#endif
m_periodDuration += dutyCycle;
if (offCycle || (withStopBit && !m_invert)) {
if (!withStopBit || m_invert) {
preciseDelay();
}
else {
lazyDelay();
}
}
}
if (offCycle)
{
#if defined(ESP8266)
if (16 == m_txPin) {
GP16O = 0;
}
else {
GPOC = m_txBitMask;
}
#else
*m_txReg &= ~m_txBitMask;
#endif
m_periodDuration += offCycle;
if (withStopBit && m_invert) lazyDelay();
}
}
size_t SoftwareSerial::write(uint8_t byte) {
return write(&byte, 1);
}
size_t SoftwareSerial::write(uint8_t byte, SoftwareSerialParity parity) {
return write(&byte, 1, parity);
}
size_t SoftwareSerial::write(const uint8_t* buffer, size_t size) {
return write(buffer, size, m_parityMode);
}
size_t IRAM_ATTR SoftwareSerial::write(const uint8_t* buffer, size_t size, SoftwareSerialParity parity) {
if (m_rxValid) { rxBits(); }
if (!m_txValid) { return -1; }
if (m_txEnableValid) {
digitalWrite(m_txEnablePin, HIGH);
}
// Stop bit: if inverted, LOW, otherwise HIGH
bool b = !m_invert;
uint32_t dutyCycle = 0;
uint32_t offCycle = 0;
if (!m_intTxEnabled) {
// Disable interrupts in order to get a clean transmit timing
disableInterrupts();
}
const uint32_t dataMask = ((1UL << m_dataBits) - 1);
bool withStopBit = true;
m_periodDuration = 0;
m_periodStart = microsToTicks(micros());
for (size_t cnt = 0; cnt < size; ++cnt) {
uint8_t byte = pgm_read_byte(buffer + cnt) & dataMask;
// push LSB start-data-parity-stop bit pattern into uint32_t
// Stop bits: HIGH
uint32_t word = ~0UL;
// inverted parity bit, performance tweak for xor all-bits-set word
if (parity && m_parityMode)
{
uint32_t parityBit;
switch (parity)
{
case SWSERIAL_PARITY_EVEN:
// from inverted, so use odd parity
parityBit = byte;
parityBit ^= parityBit >> 4;
parityBit &= 0xf;
parityBit = (0x9669 >> parityBit) & 1;
break;
case SWSERIAL_PARITY_ODD:
// from inverted, so use even parity
parityBit = byte;
parityBit ^= parityBit >> 4;
parityBit &= 0xf;
parityBit = (0x6996 >> parityBit) & 1;
break;
case SWSERIAL_PARITY_MARK:
parityBit = 0;
break;
case SWSERIAL_PARITY_SPACE:
// suppresses warning parityBit uninitialized
default:
parityBit = 1;
break;
}
word ^= parityBit;
}
word <<= m_dataBits;
word |= byte;
// Start bit: LOW
word <<= 1;
if (m_invert) word = ~word;
for (int i = 0; i <= m_pduBits; ++i) {
bool pb = b;
b = word & (1UL << i);
if (!pb && b) {
writePeriod(dutyCycle, offCycle, withStopBit);
withStopBit = false;
dutyCycle = offCycle = 0;
}
if (b) {
dutyCycle += m_bitTicks;
}
else {
offCycle += m_bitTicks;
}
}
withStopBit = true;
}
writePeriod(dutyCycle, offCycle, true);
if (!m_intTxEnabled) {
// restore the interrupt state if applicable
restoreInterrupts();
}
if (m_txEnableValid) {
digitalWrite(m_txEnablePin, LOW);
}
return size;
}
void SoftwareSerial::flush() {
if (!m_rxValid) { return; }
m_buffer->flush();
if (m_parityBuffer)
{
m_parityInPos = m_parityOutPos = 1;
m_parityBuffer->flush();
}
}
bool SoftwareSerial::overflow() {
bool res = m_overflow;
m_overflow = false;
return res;
}
int SoftwareSerial::peek() {
if (!m_rxValid) { return -1; }
if (!m_buffer->available()) {
rxBits();
if (!m_buffer->available()) return -1;
}
auto val = m_buffer->peek();
if (m_parityBuffer) m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos;
return val;
}
void SoftwareSerial::rxBits() {
#ifdef ESP8266
if (m_isrOverflow.load()) {
m_overflow = true;
m_isrOverflow.store(false);
}
#else
if (m_isrOverflow.exchange(false)) {
m_overflow = true;
}
#endif
m_isrBuffer->for_each(m_isrBufferForEachDel);
// A stop bit can go undetected if leading data bits are at same level
// and there was also no next start bit yet, so one word may be pending.
// Check that there was no new ISR data received in the meantime, inserting an
// extraneous stop level bit out of sequence breaks rx.
if (m_rxLastBit < m_pduBits - 1) {
const uint32_t detectionTicks = (m_pduBits - 1 - m_rxLastBit) * m_bitTicks;
if (!m_isrBuffer->available() && microsToTicks(micros()) - m_isrLastTick > detectionTicks) {
// Produce faux stop bit level, prevents start bit maldetection
// tick's LSB is repurposed for the level bit
rxBits(((m_isrLastTick + detectionTicks) | 1) ^ m_invert);
}
}
}
void SoftwareSerial::rxBits(const uint32_t isrTick) {
const bool level = (m_isrLastTick & 1) ^ m_invert;
// error introduced by edge value in LSB of isrTick is negligible
uint32_t ticks = isrTick - m_isrLastTick;
m_isrLastTick = isrTick;
uint32_t bits = ticks / m_bitTicks;
if (ticks % m_bitTicks > (m_bitTicks >> 1)) ++bits;
while (bits > 0) {
// start bit detection
if (m_rxLastBit >= (m_pduBits - 1)) {
// leading edge of start bit?
if (level) break;
m_rxLastBit = -1;
--bits;
continue;
}
// data bits
if (m_rxLastBit < (m_dataBits - 1)) {
uint8_t dataBits = min(bits, static_cast<uint32_t>(m_dataBits - 1 - m_rxLastBit));
m_rxLastBit += dataBits;
bits -= dataBits;
m_rxCurByte >>= dataBits;
if (level) { m_rxCurByte |= (BYTE_ALL_BITS_SET << (8 - dataBits)); }
continue;
}
// parity bit
if (m_parityMode && m_rxLastBit == (m_dataBits - 1)) {
++m_rxLastBit;
--bits;
m_rxCurParity = level;
continue;
}
// stop bits
// Store the received value in the buffer unless we have an overflow
// if not high stop bit level, discard word
if (bits >= static_cast<uint32_t>(m_pduBits - 1 - m_rxLastBit) && level) {
m_rxCurByte >>= (sizeof(uint8_t) * 8 - m_dataBits);
if (!m_buffer->push(m_rxCurByte)) {
m_overflow = true;
}
else {
if (m_parityBuffer)
{
if (m_rxCurParity) {
m_parityBuffer->pushpeek() |= m_parityInPos;
}
else {
m_parityBuffer->pushpeek() &= ~m_parityInPos;
}
m_parityInPos <<= 1;
if (!m_parityInPos)
{
m_parityBuffer->push();
m_parityInPos = 1;
}
}
}
}
m_rxLastBit = m_pduBits - 1;
// reset to 0 is important for masked bit logic
m_rxCurByte = 0;
m_rxCurParity = false;
break;
}
}
void IRAM_ATTR SoftwareSerial::rxBitISR(SoftwareSerial* self) {
uint32_t curTick = microsToTicks(micros());
bool level = *self->m_rxReg & self->m_rxBitMask;
// Store level and tick in the buffer unless we have an overflow
// tick's LSB is repurposed for the level bit
if (!self->m_isrBuffer->push((curTick | 1U) ^ !level)) self->m_isrOverflow.store(true);
}
void IRAM_ATTR SoftwareSerial::rxBitSyncISR(SoftwareSerial* self) {
uint32_t start = microsToTicks(micros());
uint32_t wait = self->m_bitTicks - microsToTicks(2U);
bool level = self->m_invert;
// Store level and tick in the buffer unless we have an overflow
// tick's LSB is repurposed for the level bit
if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ !level)) self->m_isrOverflow.store(true);
for (uint32_t i = 0; i < self->m_pduBits; ++i) {
while (microsToTicks(micros()) - start < wait) {};
wait += self->m_bitTicks;
// Store level and tick in the buffer unless we have an overflow
// tick's LSB is repurposed for the level bit
if (static_cast<bool>(*self->m_rxReg & self->m_rxBitMask) != level)
{
if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ level)) self->m_isrOverflow.store(true);
level = !level;
}
}
}
void SoftwareSerial::onReceive(Delegate<void(int available), void*> handler) {
receiveHandler = handler;
}
void SoftwareSerial::perform_work() {
if (!m_rxValid) { return; }
rxBits();
if (receiveHandler) {
int avail = m_buffer->available();
if (avail) { receiveHandler(avail); }
}
}

View File

@ -0,0 +1,301 @@
/*
SoftwareSerial.h
SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32.
Copyright (c) 2015-2016 Peter Lerup. All rights reserved.
Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __SoftwareSerial_h
#define __SoftwareSerial_h
#include "circular_queue/circular_queue.h"
#include <Stream.h>
enum SoftwareSerialParity : uint8_t {
SWSERIAL_PARITY_NONE = 000,
SWSERIAL_PARITY_EVEN = 020,
SWSERIAL_PARITY_ODD = 030,
SWSERIAL_PARITY_MARK = 040,
SWSERIAL_PARITY_SPACE = 070,
};
enum SoftwareSerialConfig {
SWSERIAL_5N1 = SWSERIAL_PARITY_NONE,
SWSERIAL_6N1,
SWSERIAL_7N1,
SWSERIAL_8N1,
SWSERIAL_5E1 = SWSERIAL_PARITY_EVEN,
SWSERIAL_6E1,
SWSERIAL_7E1,
SWSERIAL_8E1,
SWSERIAL_5O1 = SWSERIAL_PARITY_ODD,
SWSERIAL_6O1,
SWSERIAL_7O1,
SWSERIAL_8O1,
SWSERIAL_5M1 = SWSERIAL_PARITY_MARK,
SWSERIAL_6M1,
SWSERIAL_7M1,
SWSERIAL_8M1,
SWSERIAL_5S1 = SWSERIAL_PARITY_SPACE,
SWSERIAL_6S1,
SWSERIAL_7S1,
SWSERIAL_8S1,
SWSERIAL_5N2 = 0200 | SWSERIAL_PARITY_NONE,
SWSERIAL_6N2,
SWSERIAL_7N2,
SWSERIAL_8N2,
SWSERIAL_5E2 = 0200 | SWSERIAL_PARITY_EVEN,
SWSERIAL_6E2,
SWSERIAL_7E2,
SWSERIAL_8E2,
SWSERIAL_5O2 = 0200 | SWSERIAL_PARITY_ODD,
SWSERIAL_6O2,
SWSERIAL_7O2,
SWSERIAL_8O2,
SWSERIAL_5M2 = 0200 | SWSERIAL_PARITY_MARK,
SWSERIAL_6M2,
SWSERIAL_7M2,
SWSERIAL_8M2,
SWSERIAL_5S2 = 0200 | SWSERIAL_PARITY_SPACE,
SWSERIAL_6S2,
SWSERIAL_7S2,
SWSERIAL_8S2,
};
/// This class is compatible with the corresponding AVR one, however,
/// the constructor takes no arguments, for compatibility with the
/// HardwareSerial class.
/// Instead, the begin() function handles pin assignments and logic inversion.
/// It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer.
/// Bitrates up to at least 115200 can be used.
class SoftwareSerial : public Stream {
public:
SoftwareSerial();
/// Ctor to set defaults for pins.
/// @param rxPin the GPIO pin used for RX
/// @param txPin -1 for onewire protocol, GPIO pin used for twowire TX
SoftwareSerial(int8_t rxPin, int8_t txPin = -1, bool invert = false);
SoftwareSerial(const SoftwareSerial&) = delete;
SoftwareSerial& operator= (const SoftwareSerial&) = delete;
virtual ~SoftwareSerial();
/// Configure the SoftwareSerial object for use.
/// @param baud the TX/RX bitrate
/// @param config sets databits, parity, and stop bit count
/// @param rxPin -1 or default: either no RX pin, or keeps the rxPin set in the ctor
/// @param txPin -1 or default: either no TX pin (onewire), or keeps the txPin set in the ctor
/// @param invert true: uses invert line level logic
/// @param bufCapacity the capacity for the received bytes buffer
/// @param isrBufCapacity 0: derived from bufCapacity. The capacity of the internal asynchronous
/// bit receive buffer, a suggested size is bufCapacity times the sum of
/// start, data, parity and stop bit count.
void begin(uint32_t baud, SoftwareSerialConfig config,
int8_t rxPin, int8_t txPin, bool invert,
int bufCapacity = 64, int isrBufCapacity = 0);
void begin(uint32_t baud, SoftwareSerialConfig config,
int8_t rxPin, int8_t txPin) {
begin(baud, config, rxPin, txPin, m_invert);
}
void begin(uint32_t baud, SoftwareSerialConfig config,
int8_t rxPin) {
begin(baud, config, rxPin, m_txPin, m_invert);
}
void begin(uint32_t baud, SoftwareSerialConfig config = SWSERIAL_8N1) {
begin(baud, config, m_rxPin, m_txPin, m_invert);
}
uint32_t baudRate();
/// Transmit control pin.
void setTransmitEnablePin(int8_t txEnablePin);
/// Enable (default) or disable interrupts during tx.
void enableIntTx(bool on);
/// Enable (default) or disable internal rx GPIO pull-up.
void enableRxGPIOPullUp(bool on);
/// Enable or disable (default) tx GPIO output mode.
void enableTxGPIOOpenDrain(bool on);
bool overflow();
int available() override;
#if defined(ESP8266)
int availableForWrite() override {
#else
int availableForWrite() {
#endif
if (!m_txValid) return 0;
return 1;
}
int peek() override;
int read() override;
/// @returns The verbatim parity bit associated with the last successful read() or peek() call
bool readParity()
{
return m_lastReadParity;
}
/// @returns The calculated bit for even parity of the parameter byte
static bool parityEven(uint8_t byte) {
byte ^= byte >> 4;
byte &= 0xf;
return (0x6996 >> byte) & 1;
}
/// @returns The calculated bit for odd parity of the parameter byte
static bool parityOdd(uint8_t byte) {
byte ^= byte >> 4;
byte &= 0xf;
return (0x9669 >> byte) & 1;
}
/// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout
int read(uint8_t* buffer, size_t size)
#if defined(ESP8266)
override
#endif
;
/// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout
int read(char* buffer, size_t size) {
return read(reinterpret_cast<uint8_t*>(buffer), size);
}
/// @returns The number of bytes read into buffer, up to size. Times out if the limit set through
/// Stream::setTimeout() is reached.
size_t readBytes(uint8_t* buffer, size_t size) override;
/// @returns The number of bytes read into buffer, up to size. Times out if the limit set through
/// Stream::setTimeout() is reached.
size_t readBytes(char* buffer, size_t size) override {
return readBytes(reinterpret_cast<uint8_t*>(buffer), size);
}
void flush() override;
size_t write(uint8_t byte) override;
size_t write(uint8_t byte, SoftwareSerialParity parity);
size_t write(const uint8_t* buffer, size_t size) override;
size_t write(const char* buffer, size_t size) {
return write(reinterpret_cast<const uint8_t*>(buffer), size);
}
size_t write(const uint8_t* buffer, size_t size, SoftwareSerialParity parity);
size_t write(const char* buffer, size_t size, SoftwareSerialParity parity) {
return write(reinterpret_cast<const uint8_t*>(buffer), size, parity);
}
operator bool() const {
return (-1 == m_rxPin || m_rxValid) && (-1 == m_txPin || m_txValid) && !(-1 == m_rxPin && m_oneWire);
}
/// Disable or enable interrupts on the rx pin.
void enableRx(bool on);
/// One wire control.
void enableTx(bool on);
// AVR compatibility methods.
bool listen() { enableRx(true); return true; }
void end();
bool isListening() { return m_rxEnabled; }
bool stopListening() { enableRx(false); return true; }
/// Set an event handler for received data.
void onReceive(Delegate<void(int available), void*> handler);
/// Run the internal processing and event engine. Can be iteratively called
/// from loop, or otherwise scheduled.
void perform_work();
using Print::write;
private:
// It's legal to exceed the deadline, for instance,
// by enabling interrupts.
void lazyDelay();
// Synchronous precise delay
void preciseDelay();
// If withStopBit is set, either cycle contains a stop bit.
// If dutyCycle == 0, the level is not forced to HIGH.
// If offCycle == 0, the level remains unchanged from dutyCycle.
void writePeriod(
uint32_t dutyCycle, uint32_t offCycle, bool withStopBit);
constexpr bool isValidGPIOpin(int8_t pin) const;
constexpr bool isValidRxGPIOpin(int8_t pin) const;
constexpr bool isValidTxGPIOpin(int8_t pin) const;
// result is only defined for a valid Rx GPIO pin
constexpr bool hasRxGPIOPullUp(int8_t pin) const;
// safely set the pin mode for the Rx GPIO pin
void setRxGPIOPinMode();
// safely set the pin mode for the Tx GPIO pin
void setTxGPIOPinMode();
/* check m_rxValid that calling is safe */
void rxBits();
void rxBits(const uint32_t isrTick);
static void disableInterrupts();
static void restoreInterrupts();
static void rxBitISR(SoftwareSerial* self);
static void rxBitSyncISR(SoftwareSerial* self);
static inline uint32_t microsToTicks(uint32_t micros) {
return micros << 1;
}
static inline uint32_t ticksToMicros(uint32_t ticks) {
return ticks >> 1;
}
// Member variables
int8_t m_rxPin = -1;
volatile uint32_t* m_rxReg;
uint32_t m_rxBitMask;
int8_t m_txPin = -1;
#if !defined(ESP8266)
volatile uint32_t* m_txReg;
#endif
uint32_t m_txBitMask;
int8_t m_txEnablePin = -1;
uint8_t m_dataBits;
bool m_oneWire;
bool m_rxValid = false;
bool m_rxEnabled = false;
bool m_txValid = false;
bool m_txEnableValid = false;
bool m_invert;
/// PDU bits include data, parity and stop bits; the start bit is not counted.
uint8_t m_pduBits;
bool m_intTxEnabled;
bool m_rxGPIOPullUpEnabled;
bool m_txGPIOOpenDrain;
SoftwareSerialParity m_parityMode;
uint8_t m_stopBits;
bool m_lastReadParity;
bool m_overflow = false;
uint32_t m_bitTicks;
uint8_t m_parityInPos;
uint8_t m_parityOutPos;
int8_t m_rxLastBit; // 0 thru (m_pduBits - m_stopBits - 1): data/parity bits. -1: start bit. (m_pduBits - 1): stop bit.
uint8_t m_rxCurByte = 0;
std::unique_ptr<circular_queue<uint8_t> > m_buffer;
std::unique_ptr<circular_queue<uint8_t> > m_parityBuffer;
uint32_t m_periodStart;
uint32_t m_periodDuration;
#ifndef ESP32
static uint32_t m_savedPS;
#else
static portMUX_TYPE m_interruptsMux;
#endif
// the ISR stores the relative bit times in the buffer. The inversion corrected level is used as sign bit (2's complement):
// 1 = positive including 0, 0 = negative.
std::unique_ptr<circular_queue<uint32_t, SoftwareSerial*> > m_isrBuffer;
const Delegate<void(uint32_t&&), SoftwareSerial*> m_isrBufferForEachDel = { [](SoftwareSerial* self, uint32_t&& isrTick) { self->rxBits(isrTick); }, this };
std::atomic<bool> m_isrOverflow;
uint32_t m_isrLastTick;
bool m_rxCurParity = false;
Delegate<void(int available), void*> receiveHandler;
};
#endif // __SoftwareSerial_h

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,567 @@
/*
MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate
class
Copyright (c) 2019-2020 Dirk O. Kaar. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __MULTIDELEGATE_H
#define __MULTIDELEGATE_H
#include <iterator>
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
#include <atomic>
#else
#include "circular_queue/ghostl.h"
#endif
#if defined(ESP8266)
#include <interrupts.h>
using esp8266::InterruptLock;
#elif defined(ARDUINO)
class InterruptLock {
public:
InterruptLock() {
noInterrupts();
}
~InterruptLock() {
interrupts();
}
};
#else
#include <mutex>
#endif
namespace
{
template< typename Delegate, typename R, bool ISQUEUE = false, typename... P>
struct CallP
{
static R execute(Delegate& del, P... args)
{
return del(std::forward<P...>(args...));
}
};
template< typename Delegate, bool ISQUEUE, typename... P>
struct CallP<Delegate, void, ISQUEUE, P...>
{
static bool execute(Delegate& del, P... args)
{
del(std::forward<P...>(args...));
return true;
}
};
template< typename Delegate, typename R, bool ISQUEUE = false>
struct Call
{
static R execute(Delegate& del)
{
return del();
}
};
template< typename Delegate, bool ISQUEUE>
struct Call<Delegate, void, ISQUEUE>
{
static bool execute(Delegate& del)
{
del();
return true;
}
};
}
namespace delegate
{
namespace detail
{
template< typename Delegate, typename R, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32, typename... P>
class MultiDelegatePImpl
{
public:
MultiDelegatePImpl() = default;
~MultiDelegatePImpl()
{
*this = nullptr;
}
MultiDelegatePImpl(const MultiDelegatePImpl&) = delete;
MultiDelegatePImpl& operator=(const MultiDelegatePImpl&) = delete;
MultiDelegatePImpl(MultiDelegatePImpl&& md)
{
first = md.first;
last = md.last;
unused = md.unused;
nodeCount = md.nodeCount;
md.first = nullptr;
md.last = nullptr;
md.unused = nullptr;
md.nodeCount = 0;
}
MultiDelegatePImpl(const Delegate& del)
{
add(del);
}
MultiDelegatePImpl(Delegate&& del)
{
add(std::move(del));
}
MultiDelegatePImpl& operator=(MultiDelegatePImpl&& md)
{
first = md.first;
last = md.last;
unused = md.unused;
nodeCount = md.nodeCount;
md.first = nullptr;
md.last = nullptr;
md.unused = nullptr;
md.nodeCount = 0;
return *this;
}
MultiDelegatePImpl& operator=(std::nullptr_t)
{
if (last)
last->mNext = unused;
if (first)
unused = first;
while (unused)
{
auto to_delete = unused;
unused = unused->mNext;
delete(to_delete);
}
return *this;
}
MultiDelegatePImpl& operator+=(const Delegate& del)
{
add(del);
return *this;
}
MultiDelegatePImpl& operator+=(Delegate&& del)
{
add(std::move(del));
return *this;
}
protected:
struct Node_t
{
~Node_t()
{
mDelegate = nullptr; // special overload in Delegate
}
Node_t* mNext = nullptr;
Delegate mDelegate;
};
Node_t* first = nullptr;
Node_t* last = nullptr;
Node_t* unused = nullptr;
size_t nodeCount = 0;
// Returns a pointer to an unused Node_t,
// or if none are available allocates a new one,
// or nullptr if limit is reached
Node_t* IRAM_ATTR get_node_unsafe()
{
Node_t* result = nullptr;
// try to get an item from unused items list
if (unused)
{
result = unused;
unused = unused->mNext;
}
// if no unused items, and count not too high, allocate a new one
else if (nodeCount < QUEUE_CAPACITY)
{
#if defined(ESP8266) || defined(ESP32)
result = new (std::nothrow) Node_t;
#else
result = new Node_t;
#endif
if (result)
++nodeCount;
}
return result;
}
void recycle_node_unsafe(Node_t* node)
{
node->mDelegate = nullptr; // special overload in Delegate
node->mNext = unused;
unused = node;
}
#ifndef ARDUINO
std::mutex mutex_unused;
#endif
public:
class iterator : public std::iterator<std::forward_iterator_tag, Delegate>
{
public:
Node_t* current = nullptr;
Node_t* prev = nullptr;
const Node_t* stop = nullptr;
iterator(MultiDelegatePImpl& md) : current(md.first), stop(md.last) {}
iterator() = default;
iterator(const iterator&) = default;
iterator& operator=(const iterator&) = default;
iterator& operator=(iterator&&) = default;
operator bool() const
{
return current && stop;
}
bool operator==(const iterator& rhs) const
{
return current == rhs.current;
}
bool operator!=(const iterator& rhs) const
{
return !operator==(rhs);
}
Delegate& operator*() const
{
return current->mDelegate;
}
Delegate* operator->() const
{
return &current->mDelegate;
}
iterator& operator++() // prefix
{
if (current && stop != current)
{
prev = current;
current = current->mNext;
}
else
current = nullptr; // end
return *this;
}
iterator& operator++(int) // postfix
{
iterator tmp(*this);
operator++();
return tmp;
}
};
iterator begin()
{
return iterator(*this);
}
iterator end() const
{
return iterator();
}
const Delegate* IRAM_ATTR add(const Delegate& del)
{
return add(Delegate(del));
}
const Delegate* IRAM_ATTR add(Delegate&& del)
{
if (!del)
return nullptr;
#ifdef ARDUINO
InterruptLock lockAllInterruptsInThisScope;
#else
std::lock_guard<std::mutex> lock(mutex_unused);
#endif
Node_t* item = ISQUEUE ? get_node_unsafe() :
#if defined(ESP8266) || defined(ESP32)
new (std::nothrow) Node_t;
#else
new Node_t;
#endif
if (!item)
return nullptr;
item->mDelegate = std::move(del);
item->mNext = nullptr;
if (last)
last->mNext = item;
else
first = item;
last = item;
return &item->mDelegate;
}
iterator erase(iterator it)
{
if (!it)
return end();
#ifdef ARDUINO
InterruptLock lockAllInterruptsInThisScope;
#else
std::lock_guard<std::mutex> lock(mutex_unused);
#endif
auto to_recycle = it.current;
if (last == it.current)
last = it.prev;
it.current = it.current->mNext;
if (it.prev)
{
it.prev->mNext = it.current;
}
else
{
first = it.current;
}
if (ISQUEUE)
recycle_node_unsafe(to_recycle);
else
delete to_recycle;
return it;
}
bool erase(const Delegate* const del)
{
auto it = begin();
while (it)
{
if (del == &(*it))
{
erase(it);
return true;
}
++it;
}
return false;
}
operator bool() const
{
return first;
}
R operator()(P... args)
{
auto it = begin();
if (!it)
return {};
static std::atomic<bool> fence(false);
// prevent recursive calls
#if defined(ARDUINO) && !defined(ESP32)
if (fence.load()) return {};
fence.store(true);
#else
if (fence.exchange(true)) return {};
#endif
R result;
do
{
result = CallP<Delegate, R, ISQUEUE, P...>::execute(*it, args...);
if (result && ISQUEUE)
it = erase(it);
else
++it;
#if defined(ESP8266) || defined(ESP32)
// running callbacks might last too long for watchdog etc.
optimistic_yield(10000);
#endif
} while (it);
fence.store(false);
return result;
}
};
template< typename Delegate, typename R = void, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32>
class MultiDelegateImpl : public MultiDelegatePImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>
{
public:
using MultiDelegatePImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>::MultiDelegatePImpl;
R operator()()
{
auto it = this->begin();
if (!it)
return {};
static std::atomic<bool> fence(false);
// prevent recursive calls
#if defined(ARDUINO) && !defined(ESP32)
if (fence.load()) return {};
fence.store(true);
#else
if (fence.exchange(true)) return {};
#endif
R result;
do
{
result = Call<Delegate, R, ISQUEUE>::execute(*it);
if (result && ISQUEUE)
it = this->erase(it);
else
++it;
#if defined(ESP8266) || defined(ESP32)
// running callbacks might last too long for watchdog etc.
optimistic_yield(10000);
#endif
} while (it);
fence.store(false);
return result;
}
};
template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> class MultiDelegate;
template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P>
class MultiDelegate<Delegate, R(P...), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegatePImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY, P...>
{
public:
using MultiDelegatePImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY, P...>::MultiDelegatePImpl;
};
template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY>
class MultiDelegate<Delegate, R(), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegateImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>
{
public:
using MultiDelegateImpl<Delegate, R, ISQUEUE, QUEUE_CAPACITY>::MultiDelegateImpl;
};
template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P>
class MultiDelegate<Delegate, void(P...), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegatePImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY, P...>
{
public:
using MultiDelegatePImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY, P...>::MultiDelegatePImpl;
void operator()(P... args)
{
auto it = this->begin();
if (!it)
return;
static std::atomic<bool> fence(false);
// prevent recursive calls
#if defined(ARDUINO) && !defined(ESP32)
if (fence.load()) return;
fence.store(true);
#else
if (fence.exchange(true)) return;
#endif
do
{
CallP<Delegate, void, ISQUEUE, P...>::execute(*it, args...);
if (ISQUEUE)
it = this->erase(it);
else
++it;
#if defined(ESP8266) || defined(ESP32)
// running callbacks might last too long for watchdog etc.
optimistic_yield(10000);
#endif
} while (it);
fence.store(false);
}
};
template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY>
class MultiDelegate<Delegate, void(), ISQUEUE, QUEUE_CAPACITY> : public MultiDelegateImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY>
{
public:
using MultiDelegateImpl<Delegate, void, ISQUEUE, QUEUE_CAPACITY>::MultiDelegateImpl;
void operator()()
{
auto it = this->begin();
if (!it)
return;
static std::atomic<bool> fence(false);
// prevent recursive calls
#if defined(ARDUINO) && !defined(ESP32)
if (fence.load()) return;
fence.store(true);
#else
if (fence.exchange(true)) return;
#endif
do
{
Call<Delegate, void, ISQUEUE>::execute(*it);
if (ISQUEUE)
it = this->erase(it);
else
++it;
#if defined(ESP8266) || defined(ESP32)
// running callbacks might last too long for watchdog etc.
optimistic_yield(10000);
#endif
} while (it);
fence.store(false);
}
};
}
}
/**
The MultiDelegate class template can be specialized to either a queue or an event multiplexer.
It is designed to be used with Delegate, the efficient runtime wrapper for C function ptr and C++ std::function.
@tparam Delegate specifies the concrete type that MultiDelegate bases the queue or event multiplexer on.
@tparam ISQUEUE modifies the generated MultiDelegate class in subtle ways. In queue mode (ISQUEUE == true),
the value of QUEUE_CAPACITY enforces the maximum number of simultaneous items the queue can contain.
This is exploited to minimize the use of new and delete by reusing already allocated items, thus
reducing heap fragmentation. In event multiplexer mode (ISQUEUE = false), new and delete are
used for allocation of the event handler items.
If the result type of the function call operator of Delegate is void, calling a MultiDelegate queue
removes each item after calling it; a Multidelegate event multiplexer keeps event handlers until
explicitly removed.
If the result type of the function call operator of Delegate is non-void, in a MultiDelegate queue
the type-conversion to bool of that result determines if the item is immediately removed or kept
after each call: if true is returned, the item is removed. A Multidelegate event multiplexer keeps event
handlers until they are explicitly removed.
@tparam QUEUE_CAPACITY is only used if ISQUEUE == true. Then, it sets the maximum capacity that the queue dynamically
allocates from the heap. Unused items are not returned to the heap, but are managed by the MultiDelegate
instance during its own lifetime for efficiency.
*/
template< typename Delegate, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32>
class MultiDelegate : public delegate::detail::MultiDelegate<Delegate, typename Delegate::target_type, ISQUEUE, QUEUE_CAPACITY>
{
public:
using delegate::detail::MultiDelegate<Delegate, typename Delegate::target_type, ISQUEUE, QUEUE_CAPACITY>::MultiDelegate;
};
#endif // __MULTIDELEGATE_H

View File

@ -0,0 +1,393 @@
/*
circular_queue.h - Implementation of a lock-free circular queue for EspSoftwareSerial.
Copyright (c) 2019 Dirk O. Kaar. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __circular_queue_h
#define __circular_queue_h
#ifdef ARDUINO
#include <Arduino.h>
#endif
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
#include <atomic>
#include <memory>
#include <algorithm>
#include "Delegate.h"
using std::min;
#else
#include "ghostl.h"
#endif
#if !defined(ESP32) && !defined(ESP8266)
#define IRAM_ATTR
#endif
/*!
@brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO).
This implementation is lock-free between producer and consumer for the available(), peek(),
pop(), and push() type functions.
*/
template< typename T, typename ForEachArg = void >
class circular_queue
{
public:
/*!
@brief Constructs a valid, but zero-capacity dummy queue.
*/
circular_queue() : m_bufSize(1)
{
m_inPos.store(0);
m_outPos.store(0);
}
/*!
@brief Constructs a queue of the given maximum capacity.
*/
circular_queue(const size_t capacity) : m_bufSize(capacity + 1), m_buffer(new T[m_bufSize])
{
m_inPos.store(0);
m_outPos.store(0);
}
circular_queue(circular_queue&& cq) :
m_bufSize(cq.m_bufSize), m_buffer(cq.m_buffer), m_inPos(cq.m_inPos.load()), m_outPos(cq.m_outPos.load())
{}
~circular_queue()
{
m_buffer.reset();
}
circular_queue(const circular_queue&) = delete;
circular_queue& operator=(circular_queue&& cq)
{
m_bufSize = cq.m_bufSize;
m_buffer = cq.m_buffer;
m_inPos.store(cq.m_inPos.load());
m_outPos.store(cq.m_outPos.load());
}
circular_queue& operator=(const circular_queue&) = delete;
/*!
@brief Get the numer of elements the queue can hold at most.
*/
size_t capacity() const
{
return m_bufSize - 1;
}
/*!
@brief Resize the queue. The available elements in the queue are preserved.
This is not lock-free and concurrent producer or consumer access
will lead to corruption.
@return True if the new capacity could accommodate the present elements in
the queue, otherwise nothing is done and false is returned.
*/
bool capacity(const size_t cap);
/*!
@brief Discard all data in the queue.
*/
void flush()
{
m_outPos.store(m_inPos.load());
}
/*!
@brief Get a snapshot number of elements that can be retrieved by pop.
*/
size_t available() const
{
int avail = static_cast<int>(m_inPos.load() - m_outPos.load());
if (avail < 0) avail += m_bufSize;
return avail;
}
/*!
@brief Get the remaining free elementes for pushing.
*/
size_t available_for_push() const
{
int avail = static_cast<int>(m_outPos.load() - m_inPos.load()) - 1;
if (avail < 0) avail += m_bufSize;
return avail;
}
/*!
@brief Peek at the next element pop will return without removing it from the queue.
@return An rvalue copy of the next element that can be popped. If the queue is empty,
return an rvalue copy of the element that is pending the next push.
*/
T peek() const
{
const auto outPos = m_outPos.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
return m_buffer[outPos];
}
/*!
@brief Peek at the next pending input value.
@return A reference to the next element that can be pushed.
*/
inline T& IRAM_ATTR pushpeek() __attribute__((always_inline))
{
const auto inPos = m_inPos.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
return m_buffer[inPos];
}
/*!
@brief Release the next pending input value, accessible by pushpeek(), into the queue.
@return true if the queue accepted the value, false if the queue
was full.
*/
inline bool IRAM_ATTR push() __attribute__((always_inline))
{
const auto inPos = m_inPos.load(std::memory_order_acquire);
const size_t next = (inPos + 1) % m_bufSize;
if (next == m_outPos.load(std::memory_order_relaxed)) {
return false;
}
std::atomic_thread_fence(std::memory_order_acquire);
m_inPos.store(next, std::memory_order_release);
return true;
}
/*!
@brief Move the rvalue parameter into the queue.
@return true if the queue accepted the value, false if the queue
was full.
*/
inline bool IRAM_ATTR push(T&& val) __attribute__((always_inline))
{
const auto inPos = m_inPos.load(std::memory_order_acquire);
const size_t next = (inPos + 1) % m_bufSize;
if (next == m_outPos.load(std::memory_order_relaxed)) {
return false;
}
std::atomic_thread_fence(std::memory_order_acquire);
m_buffer[inPos] = std::move(val);
std::atomic_thread_fence(std::memory_order_release);
m_inPos.store(next, std::memory_order_release);
return true;
}
/*!
@brief Push a copy of the parameter into the queue.
@return true if the queue accepted the value, false if the queue
was full.
*/
inline bool IRAM_ATTR push(const T& val) __attribute__((always_inline))
{
T v(val);
return push(std::move(v));
}
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
/*!
@brief Push copies of multiple elements from a buffer into the queue,
in order, beginning at buffer's head.
@return The number of elements actually copied into the queue, counted
from the buffer head.
*/
size_t push_n(const T* buffer, size_t size);
#endif
/*!
@brief Pop the next available element from the queue.
@return An rvalue copy of the popped element, or a default
value of type T if the queue is empty.
*/
T pop();
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
/*!
@brief Pop multiple elements in ordered sequence from the queue to a buffer.
If buffer is nullptr, simply discards up to size elements from the queue.
@return The number of elements actually popped from the queue to
buffer.
*/
size_t pop_n(T* buffer, size_t size);
#endif
/*!
@brief Iterate over and remove each available element from queue,
calling back fun with an rvalue reference of every single element.
*/
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
void for_each(const Delegate<void(T&&), ForEachArg>& fun);
#else
void for_each(Delegate<void(T&&), ForEachArg> fun);
#endif
/*!
@brief In reverse order, iterate over, pop and optionally requeue each available element from the queue,
calling back fun with a reference of every single element.
Requeuing is dependent on the return boolean of the callback function. If it
returns true, the requeue occurs.
*/
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
bool for_each_rev_requeue(const Delegate<bool(T&), ForEachArg>& fun);
#else
bool for_each_rev_requeue(Delegate<bool(T&), ForEachArg> fun);
#endif
protected:
const T defaultValue = {};
size_t m_bufSize;
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
std::unique_ptr<T[]> m_buffer;
#else
std::unique_ptr<T> m_buffer;
#endif
std::atomic<size_t> m_inPos;
std::atomic<size_t> m_outPos;
};
template< typename T, typename ForEachArg >
bool circular_queue<T, ForEachArg>::capacity(const size_t cap)
{
if (cap + 1 == m_bufSize) return true;
else if (available() > cap) return false;
std::unique_ptr<T[] > buffer(new T[cap + 1]);
const auto available = pop_n(buffer, cap);
m_buffer.reset(buffer);
m_bufSize = cap + 1;
std::atomic_thread_fence(std::memory_order_release);
m_inPos.store(available, std::memory_order_relaxed);
m_outPos.store(0, std::memory_order_release);
return true;
}
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
template< typename T, typename ForEachArg >
size_t circular_queue<T, ForEachArg>::push_n(const T* buffer, size_t size)
{
const auto inPos = m_inPos.load(std::memory_order_acquire);
const auto outPos = m_outPos.load(std::memory_order_relaxed);
size_t blockSize = (outPos > inPos) ? outPos - 1 - inPos : (outPos == 0) ? m_bufSize - 1 - inPos : m_bufSize - inPos;
blockSize = min(size, blockSize);
if (!blockSize) return 0;
int next = (inPos + blockSize) % m_bufSize;
std::atomic_thread_fence(std::memory_order_acquire);
auto dest = m_buffer.get() + inPos;
std::copy_n(std::make_move_iterator(buffer), blockSize, dest);
size = min(size - blockSize, outPos > 1 ? static_cast<size_t>(outPos - next - 1) : 0);
next += size;
dest = m_buffer.get();
std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest);
std::atomic_thread_fence(std::memory_order_release);
m_inPos.store(next, std::memory_order_release);
return blockSize + size;
}
#endif
template< typename T, typename ForEachArg >
T circular_queue<T, ForEachArg>::pop()
{
const auto outPos = m_outPos.load(std::memory_order_acquire);
if (m_inPos.load(std::memory_order_relaxed) == outPos) return defaultValue;
std::atomic_thread_fence(std::memory_order_acquire);
auto val = std::move(m_buffer[outPos]);
std::atomic_thread_fence(std::memory_order_release);
m_outPos.store((outPos + 1) % m_bufSize, std::memory_order_release);
return val;
}
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
template< typename T, typename ForEachArg >
size_t circular_queue<T, ForEachArg>::pop_n(T* buffer, size_t size) {
size_t avail = size = min(size, available());
if (!avail) return 0;
const auto outPos = m_outPos.load(std::memory_order_acquire);
size_t n = min(avail, static_cast<size_t>(m_bufSize - outPos));
std::atomic_thread_fence(std::memory_order_acquire);
if (buffer) {
buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer);
avail -= n;
std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer);
}
std::atomic_thread_fence(std::memory_order_release);
m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release);
return size;
}
#endif
template< typename T, typename ForEachArg >
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
void circular_queue<T, ForEachArg>::for_each(const Delegate<void(T&&), ForEachArg>& fun)
#else
void circular_queue<T, ForEachArg>::for_each(Delegate<void(T&&), ForEachArg> fun)
#endif
{
auto outPos = m_outPos.load(std::memory_order_acquire);
const auto inPos = m_inPos.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
while (outPos != inPos)
{
fun(std::move(m_buffer[outPos]));
std::atomic_thread_fence(std::memory_order_release);
outPos = (outPos + 1) % m_bufSize;
m_outPos.store(outPos, std::memory_order_release);
}
}
template< typename T, typename ForEachArg >
#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
bool circular_queue<T, ForEachArg>::for_each_rev_requeue(const Delegate<bool(T&), ForEachArg>& fun)
#else
bool circular_queue<T, ForEachArg>::for_each_rev_requeue(Delegate<bool(T&), ForEachArg> fun)
#endif
{
auto inPos0 = circular_queue<T, ForEachArg>::m_inPos.load(std::memory_order_acquire);
auto outPos = circular_queue<T, ForEachArg>::m_outPos.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (outPos == inPos0) return false;
auto pos = inPos0;
auto outPos1 = inPos0;
const auto posDecr = circular_queue<T, ForEachArg>::m_bufSize - 1;
do {
pos = (pos + posDecr) % circular_queue<T, ForEachArg>::m_bufSize;
T&& val = std::move(circular_queue<T, ForEachArg>::m_buffer[pos]);
if (fun(val))
{
outPos1 = (outPos1 + posDecr) % circular_queue<T, ForEachArg>::m_bufSize;
if (outPos1 != pos) circular_queue<T, ForEachArg>::m_buffer[outPos1] = std::move(val);
}
} while (pos != outPos);
circular_queue<T, ForEachArg>::m_outPos.store(outPos1, std::memory_order_release);
return true;
}
#endif // __circular_queue_h

View File

@ -0,0 +1,200 @@
/*
circular_queue_mp.h - Implementation of a lock-free circular queue for EspSoftwareSerial.
Copyright (c) 2019 Dirk O. Kaar. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __circular_queue_mp_h
#define __circular_queue_mp_h
#include "circular_queue.h"
#ifdef ESP8266
#include "interrupts.h"
#else
#include <mutex>
#endif
/*!
@brief Instance class for a multi-producer, single-consumer circular queue / ring buffer (FIFO).
This implementation is lock-free between producers and consumer for the available(), peek(),
pop(), and push() type functions, but is guarded to safely allow only a single producer
at any instant.
*/
template< typename T, typename ForEachArg = void >
class circular_queue_mp : protected circular_queue<T, ForEachArg>
{
public:
circular_queue_mp() = default;
circular_queue_mp(const size_t capacity) : circular_queue<T, ForEachArg>(capacity)
{}
circular_queue_mp(circular_queue<T, ForEachArg>&& cq) : circular_queue<T, ForEachArg>(std::move(cq))
{}
using circular_queue<T, ForEachArg>::operator=;
using circular_queue<T, ForEachArg>::capacity;
using circular_queue<T, ForEachArg>::flush;
using circular_queue<T, ForEachArg>::available;
using circular_queue<T, ForEachArg>::available_for_push;
using circular_queue<T, ForEachArg>::peek;
using circular_queue<T, ForEachArg>::pop;
using circular_queue<T, ForEachArg>::pop_n;
using circular_queue<T, ForEachArg>::for_each;
using circular_queue<T, ForEachArg>::for_each_rev_requeue;
/*!
@brief Resize the queue. The available elements in the queue are preserved.
This is not lock-free, but safe, concurrent producer or consumer access
is guarded.
@return True if the new capacity could accommodate the present elements in
the queue, otherwise nothing is done and false is returned.
*/
bool capacity(const size_t cap)
{
#ifdef ESP8266
esp8266::InterruptLock lock;
#else
std::lock_guard<std::mutex> lock(m_pushMtx);
#endif
return circular_queue<T, ForEachArg>::capacity(cap);
}
bool IRAM_ATTR push() = delete;
/*!
@brief Move the rvalue parameter into the queue, guarded
for multiple concurrent producers.
@return true if the queue accepted the value, false if the queue
was full.
*/
bool IRAM_ATTR push(T&& val)
{
#ifdef ESP8266
esp8266::InterruptLock lock;
#else
std::lock_guard<std::mutex> lock(m_pushMtx);
#endif
return circular_queue<T, ForEachArg>::push(std::move(val));
}
/*!
@brief Push a copy of the parameter into the queue, guarded
for multiple concurrent producers.
@return true if the queue accepted the value, false if the queue
was full.
*/
bool IRAM_ATTR push(const T& val)
{
#ifdef ESP8266
esp8266::InterruptLock lock;
#else
std::lock_guard<std::mutex> lock(m_pushMtx);
#endif
return circular_queue<T, ForEachArg>::push(val);
}
/*!
@brief Push copies of multiple elements from a buffer into the queue,
in order, beginning at buffer's head. This is guarded for
multiple producers, push_n() is atomic.
@return The number of elements actually copied into the queue, counted
from the buffer head.
*/
size_t push_n(const T* buffer, size_t size)
{
#ifdef ESP8266
esp8266::InterruptLock lock;
#else
std::lock_guard<std::mutex> lock(m_pushMtx);
#endif
return circular_queue<T, ForEachArg>::push_n(buffer, size);
}
/*!
@brief Pops the next available element from the queue, requeues
it immediately.
@return A reference to the just requeued element, or the default
value of type T if the queue is empty.
*/
T& pop_requeue();
/*!
@brief Iterate over, pop and optionally requeue each available element from the queue,
calling back fun with a reference of every single element.
Requeuing is dependent on the return boolean of the callback function. If it
returns true, the requeue occurs.
*/
bool for_each_requeue(const Delegate<bool(T&), ForEachArg>& fun);
#ifndef ESP8266
protected:
std::mutex m_pushMtx;
#endif
};
template< typename T, typename ForEachArg >
T& circular_queue_mp<T, ForEachArg>::pop_requeue()
{
#ifdef ESP8266
esp8266::InterruptLock lock;
#else
std::lock_guard<std::mutex> lock(m_pushMtx);
#endif
const auto outPos = circular_queue<T, ForEachArg>::m_outPos.load(std::memory_order_acquire);
const auto inPos = circular_queue<T, ForEachArg>::m_inPos.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (inPos == outPos) return circular_queue<T, ForEachArg>::defaultValue;
T& val = circular_queue<T, ForEachArg>::m_buffer[inPos] = std::move(circular_queue<T, ForEachArg>::m_buffer[outPos]);
const auto bufSize = circular_queue<T, ForEachArg>::m_bufSize;
std::atomic_thread_fence(std::memory_order_release);
circular_queue<T, ForEachArg>::m_outPos.store((outPos + 1) % bufSize, std::memory_order_relaxed);
circular_queue<T, ForEachArg>::m_inPos.store((inPos + 1) % bufSize, std::memory_order_release);
return val;
}
template< typename T, typename ForEachArg >
bool circular_queue_mp<T, ForEachArg>::for_each_requeue(const Delegate<bool(T&), ForEachArg>& fun)
{
auto inPos0 = circular_queue<T, ForEachArg>::m_inPos.load(std::memory_order_acquire);
auto outPos = circular_queue<T, ForEachArg>::m_outPos.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (outPos == inPos0) return false;
do {
T&& val = std::move(circular_queue<T, ForEachArg>::m_buffer[outPos]);
if (fun(val))
{
#ifdef ESP8266
esp8266::InterruptLock lock;
#else
std::lock_guard<std::mutex> lock(m_pushMtx);
#endif
std::atomic_thread_fence(std::memory_order_release);
auto inPos = circular_queue<T, ForEachArg>::m_inPos.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
circular_queue<T, ForEachArg>::m_buffer[inPos] = std::move(val);
std::atomic_thread_fence(std::memory_order_release);
circular_queue<T, ForEachArg>::m_inPos.store((inPos + 1) % circular_queue<T, ForEachArg>::m_bufSize, std::memory_order_release);
}
else
{
std::atomic_thread_fence(std::memory_order_release);
}
outPos = (outPos + 1) % circular_queue<T, ForEachArg>::m_bufSize;
circular_queue<T, ForEachArg>::m_outPos.store(outPos, std::memory_order_release);
} while (outPos != inPos0);
return true;
}
#endif // __circular_queue_mp_h

View File

@ -0,0 +1,94 @@
/*
ghostl.h - Implementation of a bare-bones, mostly no-op, C++ STL shell
that allows building some Arduino ESP8266/ESP32
libraries on Aruduino AVR.
Copyright (c) 2019 Dirk O. Kaar. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __ghostl_h
#define __ghostl_h
#if defined(ARDUINO_ARCH_SAMD)
#include <atomic>
#endif
using size_t = decltype(sizeof(char));
namespace std
{
#if !defined(ARDUINO_ARCH_SAMD)
typedef enum memory_order {
memory_order_relaxed,
memory_order_acquire,
memory_order_release,
memory_order_seq_cst
} memory_order;
template< typename T > class atomic {
private:
T value;
public:
atomic() {}
atomic(T desired) { value = desired; }
void store(T desired, std::memory_order = std::memory_order_seq_cst) volatile noexcept { value = desired; }
T load(std::memory_order = std::memory_order_seq_cst) const volatile noexcept { return value; }
};
inline void atomic_thread_fence(std::memory_order order) noexcept {}
template< typename T > T&& move(T& t) noexcept { return static_cast<T&&>(t); }
#endif
template< typename T, size_t long N > struct array
{
T _M_elems[N];
decltype(sizeof(0)) size() const { return N; }
T& operator[](decltype(sizeof(0)) i) { return _M_elems[i]; }
const T& operator[](decltype(sizeof(0)) i) const { return _M_elems[i]; }
};
template< typename T > class unique_ptr
{
public:
using pointer = T*;
unique_ptr() noexcept : ptr(nullptr) {}
unique_ptr(pointer p) : ptr(p) {}
pointer operator->() const noexcept { return ptr; }
T& operator[](decltype(sizeof(0)) i) const { return ptr[i]; }
void reset(pointer p = pointer()) noexcept
{
delete ptr;
ptr = p;
}
T& operator*() const { return *ptr; }
private:
pointer ptr;
};
template< typename T > using function = T*;
using nullptr_t = decltype(nullptr);
template<typename T>
struct identity {
typedef T type;
};
template <typename T>
inline T&& forward(typename identity<T>::type& t) noexcept
{
return static_cast<typename identity<T>::type&&>(t);
}
}
#endif // __ghostl_h

View File

@ -0,0 +1 @@
{"type": "library", "name": "InterpolationLib", "version": "1.0.2", "spec": {"owner": "luisllamasbinaburo", "id": 6733, "name": "InterpolationLib", "requirements": null, "uri": null}}

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,83 @@
# Arduino Interpolation Library
Arduino library that provides interpolation methods step, linear, smooth, catmull spline and constrained spline.
All methods recieves X-Values and Y-Values, the size of this arrays and the X point to interpolate, and return the estimated Y at the point X.
## Modes
### Step
Simple step interpolation. Estimated value is Yn-1 or Yn. The relative 'change point' int he interval (or threshold) is an optional parameter. 0.0 means change at the start of the interval, 1.0 at the and of the interval, and 0.5 in the mid point interval.
![Image](https://github.com/luisllamasbinaburo/Arduino-Interpolation/blob/master/images/arduino-interpolation-step.png)
### Linear
Linear interpolation. Adicional parameter controls the interpolation out of the provided X-Values arrays.
![Image](https://github.com/luisllamasbinaburo/Arduino-Interpolation/blob/master/images/arduino-interpolation-linear.png)
### Smooth
Applies a cubic smooth step between value changes
![Image](https://github.com/luisllamasbinaburo/Arduino-Interpolation/blob/master/images/arduino-interpolation-smooth.png)
### Catmull spline
Typical Catmull spline interpolation
![Image](https://github.com/luisllamasbinaburo/Arduino-Interpolation/blob/master/images/arduino-interpolation-catmull-spline.png)
### Constrained spline
A special kind of spline that doesn't overshoot
![Image](https://github.com/luisllamasbinaburo/Arduino-Interpolation/blob/master/images/arduino-interpolation-constrained-spline.png)
## Example
```c++
#include "InterpolationLib.h"
const int numValues = 10;
double xValues[10] = { 5, 12, 30, 50, 60, 70, 74, 84, 92, 100 };
double yValues[10] = { 150, 200, 200, 200, 180, 100, 100, 150, 220, 320 };
void setup()
{
while (!Serial) { ; }
Serial.begin(115200);
for (float xValue = 0; xValue <= 110; xValue += .25)
{
Serial.print(Interpolation::Step(xValues, yValues, numValues, xValue, 0.0));
Serial.print(',');
Serial.print(Interpolation::Step(xValues, yValues, numValues, xValue, 0.5));
Serial.print(',');
Serial.print(Interpolation::Step(xValues, yValues, numValues, xValue, 1.0));
Serial.print(',');
Serial.print(Interpolation::SmoothStep(xValues, yValues, numValues, xValue));
Serial.print(',');
Serial.print(Interpolation::Linear(xValues, yValues, numValues, xValue, false));
Serial.print(',');
Serial.print(Interpolation::Linear(xValues, yValues, numValues, xValue, true));
Serial.print(',');
Serial.print(Interpolation::CatmullSpline(xValues, yValues, numValues, xValue));
Serial.print(',');
Serial.println(Interpolation::ConstrainedSpline(xValues, yValues, numValues, xValue));
}
}
void loop()
{
}
```
## Auxiliar tools
Aditional utils
### Float map
A simple map function that uses templates, so it works with integer (like normal 'map' function), float, double, or any other comparable type.
```c++
Interpolation::Map<float>(2.0, 0.0, 10, 100, 200)
```
### Range generator
A simple utility that generates static double arrays with fixed size, and values between 'min' and 'max'. Useful for fast generating
homogeneously distributed X-values arrays to use as parameter in the interpolations methods.
```c++
double* ptr = Range<size>::Generate(min, max);
```

View File

@ -0,0 +1,41 @@
/***************************************************
Copyright (c) 2019 Luis Llamas
(www.luisllamas.es)
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License
****************************************************/
#include "InterpolationLib.h"
const int numValues = 10;
double xValues[10] = { 5, 12, 30, 50, 60, 70, 74, 84, 92, 100 };
double yValues[10] = { 150, 200, 200, 200, 180, 100, 100, 150, 220, 320 };
void setup()
{
while (!Serial) { ; }
Serial.begin(115200);
for (float xValue = 0; xValue <= 110; xValue += .25)
{
Serial.print(Interpolation::Step(xValues, yValues, numValues, xValue, 0.0));
Serial.print(',');
Serial.print(Interpolation::Step(xValues, yValues, numValues, xValue, 0.5));
Serial.print(',');
Serial.print(Interpolation::Step(xValues, yValues, numValues, xValue, 1.0));
Serial.print(',');
Serial.print(Interpolation::SmoothStep(xValues, yValues, numValues, xValue));
Serial.print(',');
Serial.print(Interpolation::Linear(xValues, yValues, numValues, xValue, false));
Serial.print(',');
Serial.print(Interpolation::Linear(xValues, yValues, numValues, xValue, true));
Serial.print(',');
Serial.print(Interpolation::CatmullSpline(xValues, yValues, numValues, xValue));
Serial.print(',');
Serial.println(Interpolation::ConstrainedSpline(xValues, yValues, numValues, xValue));
}
}
void loop()
{
}

View File

@ -0,0 +1,20 @@
/***************************************************
Copyright (c) 2019 Luis Llamas
(www.luisllamas.es)
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License
****************************************************/
#include "InterpolationLib.h"
void setup()
{
while (!Serial) { ; }
Serial.begin(115200);
Serial.print(Interpolation::Map<float>(2.0, 0.0, 10, 100, 200));
}
void loop()
{
}

View File

@ -0,0 +1,24 @@
/***************************************************
Copyright (c) 2019 Luis Llamas
(www.luisllamas.es)
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License
****************************************************/
#include "InterpolationLib.h"
void setup()
{
while (!Serial) { ; }
Serial.begin(115200);
double* ptr = Range<10>::Generate(3, 4);
for (auto i = 0; i < 10; i++)
{
Serial.println(ptr[i]);
}
}
void loop()
{
}

Some files were not shown because too many files have changed in this diff Show More