V2.1
This commit is contained in:
243
lib/Adafruit BusIO/.clang-format
Normal file
243
lib/Adafruit BusIO/.clang-format
Normal 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
|
||||
46
lib/Adafruit BusIO/.github/ISSUE_TEMPLATE.md
vendored
Normal file
46
lib/Adafruit BusIO/.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
Thank you for opening an issue on an Adafruit Arduino library repository. To
|
||||
improve the speed of resolution please review the following guidelines and
|
||||
common troubleshooting steps below before creating the issue:
|
||||
|
||||
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
|
||||
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
|
||||
something isn't working as expected. In many cases the problem is a common issue
|
||||
that you will more quickly receive help from the forum community. GitHub issues
|
||||
are meant for known defects in the code. If you don't know if there is a defect
|
||||
in the code then start with troubleshooting on the forum first.
|
||||
|
||||
- **If following a tutorial or guide be sure you didn't miss a step.** Carefully
|
||||
check all of the steps and commands to run have been followed. Consult the
|
||||
forum if you're unsure or have questions about steps in a guide/tutorial.
|
||||
|
||||
- **For Arduino projects check these very common issues to ensure they don't apply**:
|
||||
|
||||
- For uploading sketches or communicating with the board make sure you're using
|
||||
a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes
|
||||
very hard to tell the difference between a data and charge cable! Try using the
|
||||
cable with other devices or swapping to another cable to confirm it is not
|
||||
the problem.
|
||||
|
||||
- **Be sure you are supplying adequate power to the board.** Check the specs of
|
||||
your board and plug in an external power supply. In many cases just
|
||||
plugging a board into your computer is not enough to power it and other
|
||||
peripherals.
|
||||
|
||||
- **Double check all soldering joints and connections.** Flakey connections
|
||||
cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints.
|
||||
|
||||
- **Ensure you are using an official Arduino or Adafruit board.** We can't
|
||||
guarantee a clone board will have the same functionality and work as expected
|
||||
with this code and don't support them.
|
||||
|
||||
If you're sure this issue is a defect in the code and checked the steps above
|
||||
please fill in the following fields to provide enough troubleshooting information.
|
||||
You may delete the guideline and text above to just leave the following details:
|
||||
|
||||
- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE**
|
||||
|
||||
- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO
|
||||
VERSION HERE**
|
||||
|
||||
- List the steps to reproduce the problem below (if possible attach a sketch or
|
||||
copy the sketch code in too): **LIST REPRO STEPS BELOW**
|
||||
26
lib/Adafruit BusIO/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
26
lib/Adafruit BusIO/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
Thank you for creating a pull request to contribute to Adafruit's GitHub code!
|
||||
Before you open the request please review the following guidelines and tips to
|
||||
help it be more easily integrated:
|
||||
|
||||
- **Describe the scope of your change--i.e. what the change does and what parts
|
||||
of the code were modified.** This will help us understand any risks of integrating
|
||||
the code.
|
||||
|
||||
- **Describe any known limitations with your change.** For example if the change
|
||||
doesn't apply to a supported platform of the library please mention it.
|
||||
|
||||
- **Please run any tests or examples that can exercise your modified code.** We
|
||||
strive to not break users of the code and running tests/examples helps with this
|
||||
process.
|
||||
|
||||
Thank you again for contributing! We will try to test and integrate the change
|
||||
as soon as we can, but be aware we have many GitHub repositories to manage and
|
||||
can't immediately respond to every request. There is no need to bump or check in
|
||||
on a pull request (it will clutter the discussion of the request).
|
||||
|
||||
Also don't be worried if the request is closed or not integrated--sometimes the
|
||||
priorities of Adafruit's GitHub code (education, ease of use) might not match the
|
||||
priorities of the pull request. Don't fret, the open source community thrives on
|
||||
forks and GitHub makes it easy to keep your changes in a forked repo.
|
||||
|
||||
After reviewing the guidelines above you can delete this text from the pull request.
|
||||
33
lib/Adafruit BusIO/.github/workflows/githubci.yml
vendored
Normal file
33
lib/Adafruit BusIO/.github/workflows/githubci.yml
vendored
Normal 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
|
||||
|
||||
1
lib/Adafruit BusIO/.piopm
Normal file
1
lib/Adafruit BusIO/.piopm
Normal 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}}
|
||||
365
lib/Adafruit BusIO/Adafruit_BusIO_Register.cpp
Normal file
365
lib/Adafruit BusIO/Adafruit_BusIO_Register.cpp
Normal 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
|
||||
105
lib/Adafruit BusIO/Adafruit_BusIO_Register.h
Normal file
105
lib/Adafruit BusIO/Adafruit_BusIO_Register.h
Normal 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
|
||||
90
lib/Adafruit BusIO/Adafruit_GenericDevice.cpp
Normal file
90
lib/Adafruit BusIO/Adafruit_GenericDevice.cpp
Normal 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);
|
||||
}
|
||||
56
lib/Adafruit BusIO/Adafruit_GenericDevice.h
Normal file
56
lib/Adafruit BusIO/Adafruit_GenericDevice.h
Normal 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
|
||||
317
lib/Adafruit BusIO/Adafruit_I2CDevice.cpp
Normal file
317
lib/Adafruit BusIO/Adafruit_I2CDevice.cpp
Normal 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
|
||||
}
|
||||
36
lib/Adafruit BusIO/Adafruit_I2CDevice.h
Normal file
36
lib/Adafruit BusIO/Adafruit_I2CDevice.h
Normal 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
|
||||
10
lib/Adafruit BusIO/Adafruit_I2CRegister.h
Normal file
10
lib/Adafruit BusIO/Adafruit_I2CRegister.h
Normal 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
|
||||
508
lib/Adafruit BusIO/Adafruit_SPIDevice.cpp
Normal file
508
lib/Adafruit BusIO/Adafruit_SPIDevice.cpp
Normal 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;
|
||||
}
|
||||
143
lib/Adafruit BusIO/Adafruit_SPIDevice.h
Normal file
143
lib/Adafruit BusIO/Adafruit_SPIDevice.h
Normal 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
|
||||
11
lib/Adafruit BusIO/CMakeLists.txt
Normal file
11
lib/Adafruit BusIO/CMakeLists.txt
Normal 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)
|
||||
21
lib/Adafruit BusIO/LICENSE
Normal file
21
lib/Adafruit BusIO/LICENSE
Normal 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.
|
||||
8
lib/Adafruit BusIO/README.md
Normal file
8
lib/Adafruit BusIO/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Adafruit Bus IO Library [](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
|
||||
1
lib/Adafruit BusIO/component.mk
Normal file
1
lib/Adafruit BusIO/component.mk
Normal file
@ -0,0 +1 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS = .
|
||||
@ -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); }
|
||||
@ -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); }
|
||||
@ -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() {
|
||||
|
||||
}
|
||||
41
lib/Adafruit BusIO/examples/i2c_readwrite/i2c_readwrite.ino
Normal file
41
lib/Adafruit BusIO/examples/i2c_readwrite/i2c_readwrite.ino
Normal 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() {
|
||||
|
||||
}
|
||||
38
lib/Adafruit BusIO/examples/i2c_registers/i2c_registers.ino
Normal file
38
lib/Adafruit BusIO/examples/i2c_registers/i2c_registers.ino
Normal 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() {
|
||||
|
||||
}
|
||||
@ -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() {
|
||||
|
||||
}
|
||||
29
lib/Adafruit BusIO/examples/spi_modetest/spi_modetest.ino
Normal file
29
lib/Adafruit BusIO/examples/spi_modetest/spi_modetest.ino
Normal 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);
|
||||
}
|
||||
}
|
||||
39
lib/Adafruit BusIO/examples/spi_readwrite/spi_readwrite.ino
Normal file
39
lib/Adafruit BusIO/examples/spi_readwrite/spi_readwrite.ino
Normal 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() {
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
34
lib/Adafruit BusIO/examples/spi_registers/spi_registers.ino
Normal file
34
lib/Adafruit BusIO/examples/spi_registers/spi_registers.ino
Normal 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() {
|
||||
|
||||
}
|
||||
9
lib/Adafruit BusIO/library.properties
Normal file
9
lib/Adafruit BusIO/library.properties
Normal 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=*
|
||||
46
lib/Adafruit MLX90614 Library/.github/ISSUE_TEMPLATE.md
vendored
Normal file
46
lib/Adafruit MLX90614 Library/.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
Thank you for opening an issue on an Adafruit Arduino library repository. To
|
||||
improve the speed of resolution please review the following guidelines and
|
||||
common troubleshooting steps below before creating the issue:
|
||||
|
||||
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
|
||||
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
|
||||
something isn't working as expected. In many cases the problem is a common issue
|
||||
that you will more quickly receive help from the forum community. GitHub issues
|
||||
are meant for known defects in the code. If you don't know if there is a defect
|
||||
in the code then start with troubleshooting on the forum first.
|
||||
|
||||
- **If following a tutorial or guide be sure you didn't miss a step.** Carefully
|
||||
check all of the steps and commands to run have been followed. Consult the
|
||||
forum if you're unsure or have questions about steps in a guide/tutorial.
|
||||
|
||||
- **For Arduino projects check these very common issues to ensure they don't apply**:
|
||||
|
||||
- For uploading sketches or communicating with the board make sure you're using
|
||||
a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes
|
||||
very hard to tell the difference between a data and charge cable! Try using the
|
||||
cable with other devices or swapping to another cable to confirm it is not
|
||||
the problem.
|
||||
|
||||
- **Be sure you are supplying adequate power to the board.** Check the specs of
|
||||
your board and plug in an external power supply. In many cases just
|
||||
plugging a board into your computer is not enough to power it and other
|
||||
peripherals.
|
||||
|
||||
- **Double check all soldering joints and connections.** Flakey connections
|
||||
cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints.
|
||||
|
||||
- **Ensure you are using an official Arduino or Adafruit board.** We can't
|
||||
guarantee a clone board will have the same functionality and work as expected
|
||||
with this code and don't support them.
|
||||
|
||||
If you're sure this issue is a defect in the code and checked the steps above
|
||||
please fill in the following fields to provide enough troubleshooting information.
|
||||
You may delete the guideline and text above to just leave the following details:
|
||||
|
||||
- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE**
|
||||
|
||||
- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO
|
||||
VERSION HERE**
|
||||
|
||||
- List the steps to reproduce the problem below (if possible attach a sketch or
|
||||
copy the sketch code in too): **LIST REPRO STEPS BELOW**
|
||||
26
lib/Adafruit MLX90614 Library/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
26
lib/Adafruit MLX90614 Library/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
Thank you for creating a pull request to contribute to Adafruit's GitHub code!
|
||||
Before you open the request please review the following guidelines and tips to
|
||||
help it be more easily integrated:
|
||||
|
||||
- **Describe the scope of your change--i.e. what the change does and what parts
|
||||
of the code were modified.** This will help us understand any risks of integrating
|
||||
the code.
|
||||
|
||||
- **Describe any known limitations with your change.** For example if the change
|
||||
doesn't apply to a supported platform of the library please mention it.
|
||||
|
||||
- **Please run any tests or examples that can exercise your modified code.** We
|
||||
strive to not break users of the code and running tests/examples helps with this
|
||||
process.
|
||||
|
||||
Thank you again for contributing! We will try to test and integrate the change
|
||||
as soon as we can, but be aware we have many GitHub repositories to manage and
|
||||
can't immediately respond to every request. There is no need to bump or check in
|
||||
on a pull request (it will clutter the discussion of the request).
|
||||
|
||||
Also don't be worried if the request is closed or not integrated--sometimes the
|
||||
priorities of Adafruit's GitHub code (education, ease of use) might not match the
|
||||
priorities of the pull request. Don't fret, the open source community thrives on
|
||||
forks and GitHub makes it easy to keep your changes in a forked repo.
|
||||
|
||||
After reviewing the guidelines above you can delete this text from the pull request.
|
||||
32
lib/Adafruit MLX90614 Library/.github/workflows/githubci.yml
vendored
Normal file
32
lib/Adafruit MLX90614 Library/.github/workflows/githubci.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Arduino Library CI
|
||||
|
||||
on: [pull_request, push, repository_dispatch]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: adafruit/ci-arduino
|
||||
path: ci
|
||||
|
||||
- name: pre-install
|
||||
run: bash ci/actions_install.sh
|
||||
|
||||
- name: test platforms
|
||||
run: python3 ci/build_platform.py main_platforms
|
||||
|
||||
- name: clang
|
||||
run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r .
|
||||
|
||||
- name: doxygen
|
||||
env:
|
||||
GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }}
|
||||
PRETTYNAME : "Adafruit MLX90614 Arduino Library"
|
||||
run: bash ci/doxy_gen_and_deploy.sh
|
||||
1
lib/Adafruit MLX90614 Library/.piopm
Normal file
1
lib/Adafruit MLX90614 Library/.piopm
Normal 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}}
|
||||
175
lib/Adafruit MLX90614 Library/Adafruit_MLX90614.cpp
Normal file
175
lib/Adafruit MLX90614 Library/Adafruit_MLX90614.cpp
Normal 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);
|
||||
}
|
||||
68
lib/Adafruit MLX90614 Library/Adafruit_MLX90614.h
Normal file
68
lib/Adafruit MLX90614 Library/Adafruit_MLX90614.h
Normal 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;
|
||||
};
|
||||
51
lib/Adafruit MLX90614 Library/README.md
Normal file
51
lib/Adafruit MLX90614 Library/README.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Adafruit-MLX90614-Library [](https://github.com/adafruit/Adafruit-MLX90614-Library/actions)[](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
|
||||
127
lib/Adafruit MLX90614 Library/code-of-conduct.md
Normal file
127
lib/Adafruit MLX90614 Library/code-of-conduct.md
Normal file
@ -0,0 +1,127 @@
|
||||
# Adafruit Community Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and leaders pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level or type of
|
||||
experience, education, socio-economic status, nationality, personal appearance,
|
||||
race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
We are committed to providing a friendly, safe and welcoming environment for
|
||||
all.
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Be kind and courteous to others
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Collaborating with other community members
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and sexual attention or advances
|
||||
* The use of inappropriate images, including in a community member's avatar
|
||||
* The use of inappropriate language, including in a community member's nickname
|
||||
* Any spamming, flaming, baiting or other attention-stealing behavior
|
||||
* Excessive or unwelcome helping; answering outside the scope of the question
|
||||
asked
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate
|
||||
|
||||
The goal of the standards and moderation guidelines outlined here is to build
|
||||
and maintain a respectful community. We ask that you don’t just aim to be
|
||||
"technically unimpeachable", but rather try to be your best self.
|
||||
|
||||
We value many things beyond technical expertise, including collaboration and
|
||||
supporting others within our community. Providing a positive experience for
|
||||
other community members can have a much more significant impact than simply
|
||||
providing the correct answer.
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project leaders are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project leaders have the right and responsibility to remove, edit, or
|
||||
reject messages, comments, commits, code, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any community member for other behaviors that they deem
|
||||
inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Moderation
|
||||
|
||||
Instances of behaviors that violate the Adafruit Community Code of Conduct
|
||||
may be reported by any member of the community. Community members are
|
||||
encouraged to report these situations, including situations they witness
|
||||
involving other community members.
|
||||
|
||||
You may report in the following ways:
|
||||
|
||||
In any situation, you may send an email to <support@adafruit.com>.
|
||||
|
||||
On the Adafruit Discord, you may send an open message from any channel
|
||||
to all Community Helpers by tagging @community helpers. You may also send an
|
||||
open message from any channel, or a direct message to @kattni#1507,
|
||||
@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or
|
||||
@Andon#8175.
|
||||
|
||||
Email and direct message reports will be kept confidential.
|
||||
|
||||
In situations on Discord where the issue is particularly egregious, possibly
|
||||
illegal, requires immediate action, or violates the Discord terms of service,
|
||||
you should also report the message directly to Discord.
|
||||
|
||||
These are the steps for upholding our community’s standards of conduct.
|
||||
|
||||
1. Any member of the community may report any situation that violates the
|
||||
Adafruit Community Code of Conduct. All reports will be reviewed and
|
||||
investigated.
|
||||
2. If the behavior is an egregious violation, the community member who
|
||||
committed the violation may be banned immediately, without warning.
|
||||
3. Otherwise, moderators will first respond to such behavior with a warning.
|
||||
4. Moderators follow a soft "three strikes" policy - the community member may
|
||||
be given another chance, if they are receptive to the warning and change their
|
||||
behavior.
|
||||
5. If the community member is unreceptive or unreasonable when warned by a
|
||||
moderator, or the warning goes unheeded, they may be banned for a first or
|
||||
second offense. Repeated offenses will result in the community member being
|
||||
banned.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct and the enforcement policies listed above apply to all
|
||||
Adafruit Community venues. This includes but is not limited to any community
|
||||
spaces (both public and private), the entire Adafruit Discord server, and
|
||||
Adafruit GitHub repositories. Examples of Adafruit Community spaces include
|
||||
but are not limited to meet-ups, audio chats on the Adafruit Discord, or
|
||||
interaction at a conference.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. As a community
|
||||
member, you are representing our community, and are expected to behave
|
||||
accordingly.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 1.4, available at
|
||||
<https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>,
|
||||
and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html).
|
||||
|
||||
For other projects adopting the Adafruit Community Code of
|
||||
Conduct, please contact the maintainers of those projects for enforcement.
|
||||
If you wish to use this code of conduct for your own project, consider
|
||||
explicitly mentioning your moderation policy or making a copy with your
|
||||
own moderation policy so as to avoid confusion.
|
||||
@ -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() {
|
||||
}
|
||||
46
lib/Adafruit MLX90614 Library/examples/mlxtest/mlxtest.ino
Normal file
46
lib/Adafruit MLX90614 Library/examples/mlxtest/mlxtest.ino
Normal 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);
|
||||
}
|
||||
10
lib/Adafruit MLX90614 Library/library.properties
Normal file
10
lib/Adafruit MLX90614 Library/library.properties
Normal 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
|
||||
26
lib/Adafruit MLX90614 Library/license.txt
Normal file
26
lib/Adafruit MLX90614 Library/license.txt
Normal 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.
|
||||
15
lib/DallasTemperature/.arduino-ci.yml
Normal file
15
lib/DallasTemperature/.arduino-ci.yml
Normal 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
1
lib/DallasTemperature/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
* text=auto
|
||||
13
lib/DallasTemperature/.github/workflows/arduino-lint.yml
vendored
Normal file
13
lib/DallasTemperature/.github/workflows/arduino-lint.yml
vendored
Normal 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
|
||||
17
lib/DallasTemperature/.github/workflows/arduino_test_runner.yml
vendored
Normal file
17
lib/DallasTemperature/.github/workflows/arduino_test_runner.yml
vendored
Normal 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
|
||||
18
lib/DallasTemperature/.github/workflows/jsoncheck.yml
vendored
Normal file
18
lib/DallasTemperature/.github/workflows/jsoncheck.yml
vendored
Normal 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
16
lib/DallasTemperature/.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
.idea
|
||||
classes
|
||||
target
|
||||
out
|
||||
build
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
*.log
|
||||
*.war
|
||||
.idea
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
.gradle
|
||||
.vscode
|
||||
1
lib/DallasTemperature/.piopm
Normal file
1
lib/DallasTemperature/.piopm
Normal file
@ -0,0 +1 @@
|
||||
{"type": "library", "name": "DallasTemperature", "version": "3.11.0", "spec": {"owner": "milesburton", "id": 54, "name": "DallasTemperature", "requirements": null, "uri": null}}
|
||||
1122
lib/DallasTemperature/DallasTemperature.cpp
Normal file
1122
lib/DallasTemperature/DallasTemperature.cpp
Normal file
File diff suppressed because it is too large
Load Diff
346
lib/DallasTemperature/DallasTemperature.h
Normal file
346
lib/DallasTemperature/DallasTemperature.h
Normal 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
|
||||
80
lib/DallasTemperature/README.md
Normal file
80
lib/DallasTemperature/README.md
Normal file
@ -0,0 +1,80 @@
|
||||
[](https://github.com/marketplace/actions/arduino_ci)
|
||||
[](https://github.com/RobTillaart/AS5600/actions/workflows/arduino-lint.yml)
|
||||
[](https://github.com/RobTillaart/AS5600/actions/workflows/jsoncheck.yml)
|
||||
[](https://github.com/milesburton/Arduino-Temperature-Control-Library/blob/master/LICENSE)
|
||||
[](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
|
||||
161
lib/DallasTemperature/examples/Alarm/Alarm.ino
Normal file
161
lib/DallasTemperature/examples/Alarm/Alarm.ino
Normal 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);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
143
lib/DallasTemperature/examples/AlarmHandler/AlarmHandler.ino
Normal file
143
lib/DallasTemperature/examples/AlarmHandler/AlarmHandler.ino
Normal 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);
|
||||
}
|
||||
@ -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)));
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
148
lib/DallasTemperature/examples/Multiple/Multiple.ino
Normal file
148
lib/DallasTemperature/examples/Multiple/Multiple.ino
Normal 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);
|
||||
}
|
||||
@ -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));
|
||||
|
||||
}
|
||||
47
lib/DallasTemperature/examples/SetUserData/SetUserData.ino
Normal file
47
lib/DallasTemperature/examples/SetUserData/SetUserData.ino
Normal 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);
|
||||
}
|
||||
51
lib/DallasTemperature/examples/Simple/Simple.ino
Normal file
51
lib/DallasTemperature/examples/Simple/Simple.ino
Normal 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");
|
||||
}
|
||||
}
|
||||
121
lib/DallasTemperature/examples/Single/Single.ino
Normal file
121
lib/DallasTemperature/examples/Single/Single.ino
Normal 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);
|
||||
}
|
||||
}
|
||||
129
lib/DallasTemperature/examples/Tester/Tester.ino
Normal file
129
lib/DallasTemperature/examples/Tester/Tester.ino
Normal 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);
|
||||
}
|
||||
}
|
||||
77
lib/DallasTemperature/examples/Timing/Timing.ino
Normal file
77
lib/DallasTemperature/examples/Timing/Timing.ino
Normal 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;
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
115
lib/DallasTemperature/examples/UserDataDemo/UserDataDemo.ino
Normal file
115
lib/DallasTemperature/examples/UserDataDemo/UserDataDemo.ino
Normal 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
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
@ -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++;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
87
lib/DallasTemperature/keywords.txt
Normal file
87
lib/DallasTemperature/keywords.txt
Normal 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
|
||||
38
lib/DallasTemperature/library.json
Normal file
38
lib/DallasTemperature/library.json
Normal 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": "*"
|
||||
// }
|
||||
10
lib/DallasTemperature/library.properties
Normal file
10
lib/DallasTemperature/library.properties
Normal 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
33
lib/EspSoftwareSerial/.gitignore
vendored
Normal 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/
|
||||
1
lib/EspSoftwareSerial/.piopm
Normal file
1
lib/EspSoftwareSerial/.piopm
Normal file
@ -0,0 +1 @@
|
||||
{"type": "library", "name": "EspSoftwareSerial", "version": "6.17.1", "spec": {"owner": "plerup", "id": 168, "name": "EspSoftwareSerial", "requirements": null, "uri": null}}
|
||||
502
lib/EspSoftwareSerial/LICENSE
Normal file
502
lib/EspSoftwareSerial/LICENSE
Normal 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!
|
||||
169
lib/EspSoftwareSerial/README.md
Normal file
169
lib/EspSoftwareSerial/README.md
Normal 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
|
||||
```
|
||||
71
lib/EspSoftwareSerial/examples/bitpattern/bitpattern.ino
Normal file
71
lib/EspSoftwareSerial/examples/bitpattern/bitpattern.ino
Normal 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");
|
||||
}
|
||||
}
|
||||
279
lib/EspSoftwareSerial/examples/loopback/loopback.ino
Normal file
279
lib/EspSoftwareSerial/examples/loopback/loopback.ino
Normal 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();
|
||||
}
|
||||
}
|
||||
59
lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino
Normal file
59
lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino
Normal 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();
|
||||
}
|
||||
}
|
||||
199
lib/EspSoftwareSerial/examples/repeater/repeater.ino
Normal file
199
lib/EspSoftwareSerial/examples/repeater/repeater.ino
Normal 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;
|
||||
}
|
||||
}
|
||||
113
lib/EspSoftwareSerial/examples/servoTester/servoTester.ino
Normal file
113
lib/EspSoftwareSerial/examples/servoTester/servoTester.ino
Normal 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();
|
||||
}
|
||||
}
|
||||
79
lib/EspSoftwareSerial/examples/swsertest/swsertest.ino
Normal file
79
lib/EspSoftwareSerial/examples/swsertest/swsertest.ino
Normal 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();
|
||||
}
|
||||
}
|
||||
43
lib/EspSoftwareSerial/keywords.txt
Normal file
43
lib/EspSoftwareSerial/keywords.txt
Normal 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
|
||||
26
lib/EspSoftwareSerial/library.json
Normal file
26
lib/EspSoftwareSerial/library.json
Normal 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"
|
||||
// ]
|
||||
// }
|
||||
9
lib/EspSoftwareSerial/library.properties
Normal file
9
lib/EspSoftwareSerial/library.properties
Normal 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
|
||||
679
lib/EspSoftwareSerial/src/SoftwareSerial.cpp
Normal file
679
lib/EspSoftwareSerial/src/SoftwareSerial.cpp
Normal 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); }
|
||||
}
|
||||
}
|
||||
301
lib/EspSoftwareSerial/src/SoftwareSerial.h
Normal file
301
lib/EspSoftwareSerial/src/SoftwareSerial.h
Normal 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
|
||||
2130
lib/EspSoftwareSerial/src/circular_queue/Delegate.h
Normal file
2130
lib/EspSoftwareSerial/src/circular_queue/Delegate.h
Normal file
File diff suppressed because it is too large
Load Diff
567
lib/EspSoftwareSerial/src/circular_queue/MultiDelegate.h
Normal file
567
lib/EspSoftwareSerial/src/circular_queue/MultiDelegate.h
Normal 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 ¤t->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
|
||||
393
lib/EspSoftwareSerial/src/circular_queue/circular_queue.h
Normal file
393
lib/EspSoftwareSerial/src/circular_queue/circular_queue.h
Normal 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
|
||||
200
lib/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h
Normal file
200
lib/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h
Normal 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
|
||||
94
lib/EspSoftwareSerial/src/circular_queue/ghostl.h
Normal file
94
lib/EspSoftwareSerial/src/circular_queue/ghostl.h
Normal 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
|
||||
1
lib/InterpolationLib/.piopm
Normal file
1
lib/InterpolationLib/.piopm
Normal file
@ -0,0 +1 @@
|
||||
{"type": "library", "name": "InterpolationLib", "version": "1.0.2", "spec": {"owner": "luisllamasbinaburo", "id": 6733, "name": "InterpolationLib", "requirements": null, "uri": null}}
|
||||
201
lib/InterpolationLib/LICENSE
Normal file
201
lib/InterpolationLib/LICENSE
Normal 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.
|
||||
83
lib/InterpolationLib/README.md
Normal file
83
lib/InterpolationLib/README.md
Normal 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.
|
||||

|
||||
|
||||
### Linear
|
||||
Linear interpolation. Adicional parameter controls the interpolation out of the provided X-Values arrays.
|
||||

|
||||
|
||||
### Smooth
|
||||
Applies a cubic smooth step between value changes
|
||||

|
||||
|
||||
### Catmull spline
|
||||
Typical Catmull spline interpolation
|
||||

|
||||
|
||||
### Constrained spline
|
||||
A special kind of spline that doesn't overshoot
|
||||

|
||||
|
||||
|
||||
## 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);
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -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()
|
||||
{
|
||||
}
|
||||
20
lib/InterpolationLib/examples/Map/Map.ino
Normal file
20
lib/InterpolationLib/examples/Map/Map.ino
Normal 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()
|
||||
{
|
||||
}
|
||||
24
lib/InterpolationLib/examples/Range/Range.ino
Normal file
24
lib/InterpolationLib/examples/Range/Range.ino
Normal 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
Reference in New Issue
Block a user