Initial commit: PI_mikrokontroler changes

This commit is contained in:
2026-05-09 17:52:28 +02:00
committed by Robert Duszkiewicz
parent d668467c81
commit 3dd3565a6e
550 changed files with 153891 additions and 0 deletions

172
firmware_adxl345_spi/.gitignore vendored Normal file
View File

@@ -0,0 +1,172 @@
# MacOS
.DS_Store
# ---> C++
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
.pdm.toml
.pdm-python
.pdm-build/
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

View File

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

View File

@@ -0,0 +1,46 @@
Thank you for opening an issue on an Adafruit Arduino library repository. To
improve the speed of resolution please review the following guidelines and
common troubleshooting steps below before creating the issue:
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
something isn't working as expected. In many cases the problem is a common issue
that you will more quickly receive help from the forum community. GitHub issues
are meant for known defects in the code. If you don't know if there is a defect
in the code then start with troubleshooting on the forum first.
- **If following a tutorial or guide be sure you didn't miss a step.** Carefully
check all of the steps and commands to run have been followed. Consult the
forum if you're unsure or have questions about steps in a guide/tutorial.
- **For Arduino projects check these very common issues to ensure they don't apply**:
- For uploading sketches or communicating with the board make sure you're using
a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes
very hard to tell the difference between a data and charge cable! Try using the
cable with other devices or swapping to another cable to confirm it is not
the problem.
- **Be sure you are supplying adequate power to the board.** Check the specs of
your board and plug in an external power supply. In many cases just
plugging a board into your computer is not enough to power it and other
peripherals.
- **Double check all soldering joints and connections.** Flakey connections
cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints.
- **Ensure you are using an official Arduino or Adafruit board.** We can't
guarantee a clone board will have the same functionality and work as expected
with this code and don't support them.
If you're sure this issue is a defect in the code and checked the steps above
please fill in the following fields to provide enough troubleshooting information.
You may delete the guideline and text above to just leave the following details:
- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE**
- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO
VERSION HERE**
- List the steps to reproduce the problem below (if possible attach a sketch or
copy the sketch code in too): **LIST REPRO STEPS BELOW**

View File

@@ -0,0 +1,26 @@
Thank you for creating a pull request to contribute to Adafruit's GitHub code!
Before you open the request please review the following guidelines and tips to
help it be more easily integrated:
- **Describe the scope of your change--i.e. what the change does and what parts
of the code were modified.** This will help us understand any risks of integrating
the code.
- **Describe any known limitations with your change.** For example if the change
doesn't apply to a supported platform of the library please mention it.
- **Please run any tests or examples that can exercise your modified code.** We
strive to not break users of the code and running tests/examples helps with this
process.
Thank you again for contributing! We will try to test and integrate the change
as soon as we can, but be aware we have many GitHub repositories to manage and
can't immediately respond to every request. There is no need to bump or check in
on a pull request (it will clutter the discussion of the request).
Also don't be worried if the request is closed or not integrated--sometimes the
priorities of Adafruit's GitHub code (education, ease of use) might not match the
priorities of the pull request. Don't fret, the open source community thrives on
forks and GitHub makes it easy to keep your changes in a forked repo.
After reviewing the guidelines above you can delete this text from the pull request.

View File

@@ -0,0 +1,33 @@
name: Arduino Library CI
on: [pull_request, push, repository_dispatch]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-python@v4
with:
python-version: '3.x'
- uses: actions/checkout@v3
- uses: actions/checkout@v3
with:
repository: adafruit/ci-arduino
path: ci
- name: Install the prerequisites
run: bash ci/actions_install.sh
- name: Check for correct code formatting with clang-format
run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r .
- name: Check for correct documentation with doxygen
env:
GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }}
PRETTYNAME : "Adafruit Bus IO Library"
run: bash ci/doxy_gen_and_deploy.sh
- name: Test the code on supported platforms
run: python3 ci/build_platform.py main_platforms zero feather32u4

View File

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

View File

@@ -0,0 +1,384 @@
#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 Create a register we access over a GenericDevice
* @param genericdevice Generic device to use
* @param reg_addr Register address we will read/write
* @param width Width of the register in bytes (1-4)
* @param byteorder Byte order of register data (LSBFIRST or MSBFIRST)
* @param address_width Width of the register address in bytes (1 or 2)
*/
Adafruit_BusIO_Register::Adafruit_BusIO_Register(
Adafruit_GenericDevice *genericdevice, uint16_t reg_addr, uint8_t width,
uint8_t byteorder, uint8_t address_width) {
_i2cdevice = nullptr;
_spidevice = nullptr;
_genericdevice = genericdevice;
_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);
}
if (_genericdevice) {
return _genericdevice->writeRegister(addrbuffer, _addrwidth, buffer, len);
}
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 number of bytes from a register into a buffer
@param buffer Buffer to read data into
@param len Number of bytes to read into the buffer
@return true on successful read, otherwise false
*/
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);
}
if (_genericdevice) {
return _genericdevice->readRegister(addrbuffer, _addrwidth, buffer, len);
}
return false;
}
/*!
* @brief Read 2 bytes of data from the register location
* @param value Pointer to uint16_t variable to read into
* @return True on successful write (only really useful for I2C as SPI is
* uncheckable)
*/
bool Adafruit_BusIO_Register::read(uint16_t *value) {
if (!read(_buffer, 2)) {
return false;
}
if (_byteorder == LSBFIRST) {
*value = _buffer[1];
*value <<= 8;
*value |= _buffer[0];
} else {
*value = _buffer[0];
*value <<= 8;
*value |= _buffer[1];
}
return true;
}
/*!
* @brief Read 1 byte of data from the register location
* @param value Pointer to uint8_t variable to read into
* @return True on successful write (only really useful for I2C as SPI is
* uncheckable)
*/
bool Adafruit_BusIO_Register::read(uint8_t *value) {
if (!read(_buffer, 1)) {
return false;
}
*value = _buffer[0];
return true;
}
/*!
* @brief Pretty printer for this register
* @param s The Stream to print to, defaults to &Serial
*/
void Adafruit_BusIO_Register::print(Stream *s) {
uint32_t val = read();
s->print("0x");
s->print(val, HEX);
}
/*!
* @brief Pretty printer for this register
* @param s The Stream to print to, defaults to &Serial
*/
void Adafruit_BusIO_Register::println(Stream *s) {
print(s);
s->println();
}
/*!
* @brief Create a slice of the register that we can address without
* touching other bits
* @param reg The Adafruit_BusIO_Register which defines the bus/register
* @param bits The number of bits wide we are slicing
* @param shift The number of bits that our bit-slice is shifted from LSB
*/
Adafruit_BusIO_RegisterBits::Adafruit_BusIO_RegisterBits(
Adafruit_BusIO_Register *reg, uint8_t bits, uint8_t shift) {
_register = reg;
_bits = bits;
_shift = shift;
}
/*!
* @brief Read 4 bytes of data from the register
* @return data The 4 bytes to read
*/
uint32_t Adafruit_BusIO_RegisterBits::read(void) {
uint32_t val = _register->read();
val >>= _shift;
return val & ((1 << (_bits)) - 1);
}
/*!
* @brief Write 4 bytes of data to the register
* @param data The 4 bytes to write
* @return True on successful write (only really useful for I2C as SPI is
* uncheckable)
*/
bool Adafruit_BusIO_RegisterBits::write(uint32_t data) {
uint32_t val = _register->read();
// mask off the data before writing
uint32_t mask = (1 << (_bits)) - 1;
data &= mask;
mask <<= _shift;
val &= ~mask; // remove the current data at that spot
val |= data << _shift; // and add in the new data
return _register->write(val, _register->width());
}
/*!
* @brief The width of the register data, helpful for doing calculations
* @returns The data width used when initializing the register
*/
uint8_t Adafruit_BusIO_Register::width(void) { return _width; }
/*!
* @brief Set the default width of data
* @param width the default width of data read from register
*/
void Adafruit_BusIO_Register::setWidth(uint8_t width) { _width = width; }
/*!
* @brief Set register address
* @param address the address from register
*/
void Adafruit_BusIO_Register::setAddress(uint16_t address) {
_address = address;
}
/*!
* @brief Set the width of register address
* @param address_width the width for register address
*/
void Adafruit_BusIO_Register::setAddressWidth(uint16_t address_width) {
_addrwidth = address_width;
}
#endif // SPI exists

View File

@@ -0,0 +1,117 @@
#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_GenericDevice.h>
#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);
Adafruit_BusIO_Register(Adafruit_GenericDevice *genericdevice,
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);
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
void print(Stream *s = &Serial);
void println(Stream *s = &Serial);
#else
void print(Stream *s);
void println(Stream *s);
#endif
private:
Adafruit_I2CDevice *_i2cdevice;
Adafruit_SPIDevice *_spidevice;
Adafruit_GenericDevice *_genericdevice;
Adafruit_BusIO_SPIRegType _spiregtype;
uint16_t _address;
uint8_t _width, _addrwidth, _byteorder;
uint8_t _buffer[4]; // we won't support anything larger than uint32 for
// non-buffered read
uint32_t _cached = 0;
};
/*!
* @brief The class which defines a slice of bits from within a device register
* (a location to read/write data from)
*/
class Adafruit_BusIO_RegisterBits {
public:
Adafruit_BusIO_RegisterBits(Adafruit_BusIO_Register *reg, uint8_t bits,
uint8_t shift);
bool write(uint32_t value);
uint32_t read(void);
private:
Adafruit_BusIO_Register *_register;
uint8_t _bits, _shift;
};
#endif // SPI exists
#endif // BusIO_Register_h

View File

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

View File

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

View File

@@ -0,0 +1,320 @@
#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, HEX);
#endif
#ifdef ARDUINO_ARCH_MBED
_wire->write(0); // forces a write request instead of a read
#endif
if (_wire->endTransmission() == 0) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F(" Detected"));
#endif
return true;
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F(" Not detected"));
#endif
return false;
}
/*!
* @brief Write a buffer or two to the I2C device. Cannot be more than
* maxBufferSize() bytes.
* @param buffer Pointer to buffer of data to write. This is const to
* ensure the content of this buffer doesn't change.
* @param len Number of bytes from buffer to write
* @param prefix_buffer Pointer to optional array of data to write before
* buffer. Cannot be more than maxBufferSize() bytes. This is const to
* ensure the content of this buffer doesn't change.
* @param prefix_len Number of bytes from prefix buffer to write
* @param stop Whether to send an I2C STOP signal on write
* @return True if write was successful, otherwise false.
*/
bool Adafruit_I2CDevice::write(const uint8_t *buffer, size_t len, bool stop,
const uint8_t *prefix_buffer,
size_t prefix_len) {
if ((len + prefix_len) > maxBufferSize()) {
// currently not guaranteed to work if more than 32 bytes!
// we will need to find out if some platforms have larger
// I2C buffer sizes :/
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F("\tI2CDevice could not write such a large buffer"));
#endif
return false;
}
_wire->beginTransmission(_addr);
// Write the prefix data (usually an address)
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
if (_wire->write(prefix_buffer, prefix_len) != prefix_len) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F("\tI2CDevice failed to write"));
#endif
return false;
}
}
// Write the data itself
if (_wire->write(buffer, len) != len) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println(F("\tI2CDevice failed to write"));
#endif
return false;
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tI2CWRITE @ 0x"));
DEBUG_SERIAL.print(_addr, HEX);
DEBUG_SERIAL.print(F(" :: "));
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
for (uint16_t i = 0; i < prefix_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(prefix_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
}
}
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (i % 32 == 31) {
DEBUG_SERIAL.println();
}
}
if (stop) {
DEBUG_SERIAL.print("\tSTOP");
}
#endif
if (_wire->endTransmission(stop) == 0) {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println();
// DEBUG_SERIAL.println("Sent!");
#endif
return true;
} else {
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.println("\tFailed to send!");
#endif
return false;
}
}
/*!
* @brief Read from I2C into a buffer from the I2C device.
* Cannot be more than maxBufferSize() bytes.
* @param buffer Pointer to buffer of data to read into
* @param len Number of bytes from buffer to read.
* @param stop Whether to send an I2C STOP signal on read
* @return True if read was successful, otherwise false.
*/
bool Adafruit_I2CDevice::read(uint8_t *buffer, size_t len, bool stop) {
size_t pos = 0;
while (pos < len) {
size_t read_len =
((len - pos) > maxBufferSize()) ? maxBufferSize() : (len - pos);
bool read_stop = (pos < (len - read_len)) ? false : stop;
if (!_read(buffer + pos, read_len, read_stop))
return false;
pos += read_len;
}
return true;
}
bool Adafruit_I2CDevice::_read(uint8_t *buffer, size_t len, bool stop) {
#if defined(TinyWireM_h)
size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len);
#elif defined(ARDUINO_ARCH_MEGAAVR)
size_t recv = _wire->requestFrom(_addr, len, stop);
#else
size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len, (uint8_t)stop);
#endif
if (recv != len) {
// Not enough data available to fulfill our obligation!
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tI2CDevice did not receive enough data: "));
DEBUG_SERIAL.println(recv);
#endif
return false;
}
for (uint16_t i = 0; i < len; i++) {
buffer[i] = _wire->read();
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tI2CREAD @ 0x"));
DEBUG_SERIAL.print(_addr, HEX);
DEBUG_SERIAL.print(F(" :: "));
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
return true;
}
/*!
* @brief Write some data, then read some data from I2C into another buffer.
* Cannot be more than maxBufferSize() bytes. The buffers can point to
* same/overlapping locations.
* @param write_buffer Pointer to buffer of data to write from
* @param write_len Number of bytes from buffer to write.
* @param read_buffer Pointer to buffer of data to read into.
* @param read_len Number of bytes from buffer to read.
* @param stop Whether to send an I2C STOP signal between the write and read
* @return True if write & read was successful, otherwise false.
*/
bool Adafruit_I2CDevice::write_then_read(const uint8_t *write_buffer,
size_t write_len, uint8_t *read_buffer,
size_t read_len, bool stop) {
if (!write(write_buffer, write_len, stop)) {
return false;
}
return read(read_buffer, read_len);
}
/*!
* @brief Returns the 7-bit address of this device
* @return The 7-bit address of this device
*/
uint8_t Adafruit_I2CDevice::address(void) { return _addr; }
/*!
* @brief Change the I2C clock speed to desired (relies on
* underlying Wire support!
* @param desiredclk The desired I2C SCL frequency
* @return True if this platform supports changing I2C speed.
* Not necessarily that the speed was achieved!
*/
bool Adafruit_I2CDevice::setSpeed(uint32_t desiredclk) {
#if defined(__AVR_ATmega328__) || \
defined(__AVR_ATmega328P__) // fix arduino core set clock
// calculate TWBR correctly
if ((F_CPU / 18) < desiredclk) {
#ifdef DEBUG_SERIAL
Serial.println(F("I2C.setSpeed too high."));
#endif
return false;
}
uint32_t atwbr = ((F_CPU / desiredclk) - 16) / 2;
if (atwbr > 16320) {
#ifdef DEBUG_SERIAL
Serial.println(F("I2C.setSpeed too low."));
#endif
return false;
}
if (atwbr <= 255) {
atwbr /= 1;
TWSR = 0x0;
} else if (atwbr <= 1020) {
atwbr /= 4;
TWSR = 0x1;
} else if (atwbr <= 4080) {
atwbr /= 16;
TWSR = 0x2;
} else { // if (atwbr <= 16320)
atwbr /= 64;
TWSR = 0x3;
}
TWBR = atwbr;
#ifdef DEBUG_SERIAL
Serial.print(F("TWSR prescaler = "));
Serial.println(pow(4, TWSR));
Serial.print(F("TWBR = "));
Serial.println(atwbr);
#endif
return true;
#elif (ARDUINO >= 157) && !defined(ARDUINO_STM32_FEATHER) && \
!defined(TinyWireM_h)
_wire->setClock(desiredclk);
return true;
#else
(void)desiredclk;
return false;
#endif
}

View File

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

View File

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

View File

@@ -0,0 +1,512 @@
#include "Adafruit_SPIDevice.h"
// #define DEBUG_SERIAL Serial
#ifdef BUSIO_USE_FAST_PINIO
#define BUSIO_SET_CLOCK_LOW() (*clkPort = *clkPort & ~clkPinMask)
#define BUSIO_SET_CLOCK_HIGH() (*clkPort = *clkPort | clkPinMask)
#define BUSIO_READ_MISO() (*misoPort & misoPinMask)
#define BUSIO_WRITE_MOSI(value) \
do { \
if (value) \
*mosiPort = *mosiPort | mosiPinMask; \
else \
*mosiPort = *mosiPort & ~mosiPinMask; \
} while (0)
#else
#define BUSIO_SET_CLOCK_LOW() digitalWrite(_sck, LOW)
#define BUSIO_SET_CLOCK_HIGH() digitalWrite(_sck, HIGH)
#define BUSIO_READ_MISO() digitalRead(_miso)
#define BUSIO_WRITE_MOSI(value) digitalWrite(_mosi, value)
#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 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)) {
BUSIO_WRITE_MOSI(towrite);
lastmosi = towrite;
}
BUSIO_SET_CLOCK_HIGH();
if (bitdelay_us) {
delayMicroseconds(bitdelay_us);
}
if (_miso != -1) {
if (BUSIO_READ_MISO())
reply |= b;
}
BUSIO_SET_CLOCK_LOW();
} else if (_dataMode == SPI_MODE3) {
if (_mosi != -1) { // transmit on falling edge
BUSIO_WRITE_MOSI(send & b);
}
BUSIO_SET_CLOCK_LOW();
if (bitdelay_us) {
delayMicroseconds(bitdelay_us);
}
BUSIO_SET_CLOCK_HIGH();
if (bitdelay_us) {
delayMicroseconds(bitdelay_us);
}
if (_miso != -1) { // read on rising edge
if (BUSIO_READ_MISO()) {
reply |= b;
}
}
} else { // || _dataMode == SPI_MODE1)
BUSIO_SET_CLOCK_HIGH();
if (bitdelay_us) {
delayMicroseconds(bitdelay_us);
}
if (_mosi != -1) {
BUSIO_WRITE_MOSI(send & b);
}
BUSIO_SET_CLOCK_LOW();
if (_miso != -1) {
if (BUSIO_READ_MISO()) {
reply |= b;
}
}
}
}
if (_miso != -1) {
buffer[i] = reply;
}
}
return;
}
/*!
* @brief Transfer (send/receive) one byte over hard/soft SPI, without
* transaction management
* @param send The byte to send
* @return The byte received while transmitting
*/
uint8_t Adafruit_SPIDevice::transfer(uint8_t send) {
uint8_t data = send;
transfer(&data, 1);
return data;
}
/*!
* @brief Manually begin a transaction (calls beginTransaction if hardware
* SPI)
*/
void Adafruit_SPIDevice::beginTransaction(void) {
if (_spi) {
#ifdef BUSIO_HAS_HW_SPI
_spi->beginTransaction(*_spiSetting);
#endif
}
}
/*!
* @brief Manually end a transaction (calls endTransaction if hardware SPI)
*/
void Adafruit_SPIDevice::endTransaction(void) {
if (_spi) {
#ifdef BUSIO_HAS_HW_SPI
_spi->endTransaction();
#endif
}
}
/*!
* @brief Assert/Deassert the CS pin if it is defined
* @param value The state the CS is set to
*/
void Adafruit_SPIDevice::setChipSelect(int value) {
if (_cs != -1) {
digitalWrite(_cs, value);
}
}
/*!
* @brief Write a buffer or two to the SPI device, with transaction
* management.
* @brief Manually begin a transaction (calls beginTransaction if hardware
* SPI) with asserting the CS pin
*/
void Adafruit_SPIDevice::beginTransactionWithAssertingCS() {
beginTransaction();
setChipSelect(LOW);
}
/*!
* @brief Manually end a transaction (calls endTransaction if hardware SPI)
* with deasserting the CS pin
*/
void Adafruit_SPIDevice::endTransactionWithDeassertingCS() {
setChipSelect(HIGH);
endTransaction();
}
/*!
* @brief Write a buffer or two to the SPI device, with transaction
* management.
* @param buffer Pointer to buffer of data to write
* @param len Number of bytes from buffer to write
* @param prefix_buffer Pointer to optional array of data to write before
* buffer.
* @param prefix_len Number of bytes from prefix buffer to write
* @return Always returns true because there's no way to test success of SPI
* writes
*/
bool Adafruit_SPIDevice::write(const uint8_t *buffer, size_t len,
const uint8_t *prefix_buffer,
size_t prefix_len) {
beginTransactionWithAssertingCS();
// do the writing
#if defined(ARDUINO_ARCH_ESP32)
if (_spi) {
if (prefix_len > 0) {
_spi->transferBytes((uint8_t *)prefix_buffer, nullptr, prefix_len);
}
if (len > 0) {
_spi->transferBytes((uint8_t *)buffer, nullptr, len);
}
} else
#endif
{
for (size_t i = 0; i < prefix_len; i++) {
transfer(prefix_buffer[i]);
}
for (size_t i = 0; i < len; i++) {
transfer(buffer[i]);
}
}
endTransactionWithDeassertingCS();
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: "));
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
for (uint16_t i = 0; i < prefix_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(prefix_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
}
}
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (i % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
return true;
}
/*!
* @brief Read from SPI into a buffer from the SPI device, with transaction
* management.
* @param buffer Pointer to buffer of data to read into
* @param len Number of bytes from buffer to read.
* @param sendvalue The 8-bits of data to write when doing the data read,
* defaults to 0xFF
* @return Always returns true because there's no way to test success of SPI
* writes
*/
bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
memset(buffer, sendvalue, len); // clear out existing buffer
beginTransactionWithAssertingCS();
transfer(buffer, len);
endTransactionWithDeassertingCS();
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Read: "));
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
return true;
}
/*!
* @brief Write some data, then read some data from SPI into another buffer,
* with transaction management. The buffers can point to same/overlapping
* locations. This does not transmit-receive at the same time!
* @param write_buffer Pointer to buffer of data to write from
* @param write_len Number of bytes from buffer to write.
* @param read_buffer Pointer to buffer of data to read into.
* @param read_len Number of bytes from buffer to read.
* @param sendvalue The 8-bits of data to write when doing the data read,
* defaults to 0xFF
* @return Always returns true because there's no way to test success of SPI
* writes
*/
bool Adafruit_SPIDevice::write_then_read(const uint8_t *write_buffer,
size_t write_len, uint8_t *read_buffer,
size_t read_len, uint8_t sendvalue) {
beginTransactionWithAssertingCS();
// do the writing
#if defined(ARDUINO_ARCH_ESP32)
if (_spi) {
if (write_len > 0) {
_spi->transferBytes((uint8_t *)write_buffer, nullptr, write_len);
}
} else
#endif
{
for (size_t i = 0; i < write_len; i++) {
transfer(write_buffer[i]);
}
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: "));
for (uint16_t i = 0; i < write_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(write_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (write_len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
// do the reading
for (size_t i = 0; i < read_len; i++) {
read_buffer[i] = transfer(sendvalue);
}
#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Read: "));
for (uint16_t i = 0; i < read_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(read_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (read_len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
endTransactionWithDeassertingCS();
return true;
}
/*!
* @brief Write some data and read some data at the same time from SPI
* into the same buffer, with transaction management. This is basicaly a wrapper
* for transfer() with CS-pin and transaction management. This /does/
* transmit-receive at the same time!
* @param buffer Pointer to buffer of data to write/read to/from
* @param len Number of bytes from buffer to write/read.
* @return Always returns true because there's no way to test success of SPI
* writes
*/
bool Adafruit_SPIDevice::write_and_read(uint8_t *buffer, size_t len) {
beginTransactionWithAssertingCS();
transfer(buffer, len);
endTransactionWithDeassertingCS();
return true;
}

View File

@@ -0,0 +1,149 @@
#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_MODE3 };
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)
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(__MBED__) || defined(__ZEPHYR__)
// Boards based on RTOS cores like mbed or Zephyr are not going to expose the
// low level registers needed for fast pin manipulation
#undef 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_RP2040) && !defined(ARDUINO_SILABS) && \
!defined(ARDUINO_UNOR4_MINIMA) && !defined(ARDUINO_UNOR4_WIFI) && \
!defined(PORTDUINO)
typedef volatile uint32_t BusIO_PortReg;
typedef uint32_t BusIO_PortMask;
#if !defined(__ASR6501__) && !defined(__ASR6502__)
#define BUSIO_USE_FAST_PINIO
#endif
#else
#undef BUSIO_USE_FAST_PINIO
#endif
/**! The class which defines how we will talk to this device over SPI **/
class Adafruit_SPIDevice {
public:
#ifdef BUSIO_HAS_HW_SPI
Adafruit_SPIDevice(int8_t cspin, uint32_t freq = 1000000,
BusIOBitOrder dataOrder = SPI_BITORDER_MSBFIRST,
uint8_t dataMode = SPI_MODE0, SPIClass *theSPI = &SPI);
#else
Adafruit_SPIDevice(int8_t cspin, uint32_t freq = 1000000,
BusIOBitOrder dataOrder = SPI_BITORDER_MSBFIRST,
uint8_t dataMode = SPI_MODE0, SPIClass *theSPI = nullptr);
#endif
Adafruit_SPIDevice(int8_t cspin, int8_t sck, int8_t miso, int8_t mosi,
uint32_t freq = 1000000,
BusIOBitOrder dataOrder = SPI_BITORDER_MSBFIRST,
uint8_t dataMode = SPI_MODE0);
~Adafruit_SPIDevice();
bool begin(void);
bool read(uint8_t *buffer, size_t len, uint8_t sendvalue = 0xFF);
bool write(const uint8_t *buffer, size_t len,
const uint8_t *prefix_buffer = nullptr, size_t prefix_len = 0);
bool write_then_read(const uint8_t *write_buffer, size_t write_len,
uint8_t *read_buffer, size_t read_len,
uint8_t sendvalue = 0xFF);
bool write_and_read(uint8_t *buffer, size_t len);
uint8_t transfer(uint8_t send);
void transfer(uint8_t *buffer, size_t len);
void beginTransaction(void);
void endTransaction(void);
void beginTransactionWithAssertingCS();
void endTransactionWithDeassertingCS();
private:
#ifdef BUSIO_HAS_HW_SPI
SPIClass *_spi = nullptr;
SPISettings *_spiSetting = nullptr;
#else
uint8_t *_spi = nullptr;
uint8_t *_spiSetting = nullptr;
#endif
uint32_t _freq;
BusIOBitOrder _dataOrder;
uint8_t _dataMode;
void setChipSelect(int value);
int8_t _cs, _sck, _mosi, _miso;
#ifdef BUSIO_USE_FAST_PINIO
BusIO_PortReg *mosiPort, *clkPort, *misoPort, *csPort;
BusIO_PortMask mosiPinMask, misoPinMask, clkPinMask, csPinMask;
#endif
bool _begun;
};
#endif // Adafruit_SPIDevice_h

View File

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

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Adafruit Industries
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,43 @@
#include <Adafruit_BusIO_Register.h>
#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 register test");
if (!i2c_dev.begin()) {
Serial.print("Did not find device at 0x");
Serial.println(i2c_dev.address(), HEX);
while (1)
;
}
Serial.print("Device found on address 0x");
Serial.println(i2c_dev.address(), HEX);
Adafruit_BusIO_Register id_reg =
Adafruit_BusIO_Register(&i2c_dev, 0x0C, 2, LSBFIRST);
uint16_t id;
id_reg.read(&id);
Serial.print("ID register = 0x");
Serial.println(id, HEX);
Adafruit_BusIO_Register thresh_reg =
Adafruit_BusIO_Register(&i2c_dev, 0x01, 2, LSBFIRST);
uint16_t thresh;
thresh_reg.read(&thresh);
Serial.print("Initial threshold register = 0x");
Serial.println(thresh, HEX);
thresh_reg.write(~thresh);
Serial.print("Post threshold register = 0x");
Serial.println(thresh_reg.read(), HEX);
}
void loop() {}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
{"type": "library", "name": "ArduinoJson", "version": "7.4.3", "spec": {"owner": "bblanchon", "id": 64, "name": "ArduinoJson", "requirements": null, "uri": null}}

View File

@@ -0,0 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#include "src/ArduinoJson.h"

View File

@@ -0,0 +1,10 @@
The MIT License (MIT)
---------------------
Copyright © 2014-2026, Benoit BLANCHON
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,153 @@
<p align="center">
<a href="https://arduinojson.org/"><img alt="ArduinoJson" src="https://arduinojson.org/images/logo.svg" width="200" /></a>
</p>
---
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bblanchon/ArduinoJson/ci.yml?branch=7.x&logo=github)](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A7.x)
[![Continuous Integration](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/7.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/7.x)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
[![Coveralls branch](https://img.shields.io/coveralls/github/bblanchon/ArduinoJson/7.x?logo=coveralls)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=7.x)
[![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github&color=orange)](https://github.com/bblanchon/ArduinoJson/stargazers)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github&color=orange)](https://github.com/sponsors/bblanchon)
ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
## Features
* [JSON deserialization](https://arduinojson.org/v7/api/json/deserializejson/)
* [Optionally decodes UTF-16 escape sequences to UTF-8](https://arduinojson.org/v7/api/config/decode_unicode/)
* [Optionally supports comments in the input](https://arduinojson.org/v7/api/config/enable_comments/)
* [Optionally filters the input to keep only desired values](https://arduinojson.org/v7/api/json/deserializejson/#filtering)
* Supports single quotes as a string delimiter
* Compatible with [NDJSON](http://ndjson.org/) and [JSON Lines](https://jsonlines.org/)
* [JSON serialization](https://arduinojson.org/v7/api/json/serializejson/)
* [Can write to a buffer or a stream](https://arduinojson.org/v7/api/json/serializejson/)
* [Optionally indents the document (prettified JSON)](https://arduinojson.org/v7/api/json/serializejsonpretty/)
* [MessagePack serialization](https://arduinojson.org/v7/api/msgpack/serializemsgpack/)
* [MessagePack deserialization](https://arduinojson.org/v7/api/msgpack/deserializemsgpack/)
* Efficient
* [Twice smaller than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Almost 10% faster than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Deduplicates strings](https://arduinojson.org/news/2020/08/01/version-6-16-0/)
* Versatile
* Supports [custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v7/how-to/use-external-ram-on-esp32/)
* Supports [`String`](https://arduinojson.org/v7/api/config/enable_arduino_string/), [`std::string`](https://arduinojson.org/v7/api/config/enable_std_string/), and [`std::string_view`](https://arduinojson.org/v7/api/config/enable_string_view/)
* Supports [`Stream`](https://arduinojson.org/v7/api/config/enable_arduino_stream/) and [`std::istream`/`std::ostream`](https://arduinojson.org/v7/api/config/enable_std_stream/)
* Supports [Flash strings](https://arduinojson.org/v7/api/config/enable_progmem/)
* Supports [custom readers](https://arduinojson.org/v7/api/json/deserializejson/#custom-reader) and [custom writers](https://arduinojson.org/v7/api/json/serializejson/#custom-writer)
* Supports [custom converters](https://arduinojson.org/news/2021/05/04/version-6-18-0/)
* Portable
* Usable on any C++ project (not limited to Arduino)
* Compatible with C++11, C++14 and C++17
* Support for C++98/C++03 available on [ArduinoJson 6.20.x](https://github.com/bblanchon/ArduinoJson/tree/6.20.x)
* Zero warnings with `-Wall -Wextra -pedantic` and `/W4`
* [Header-only library](https://en.wikipedia.org/wiki/Header-only)
* Works with virtually any board
* Arduino boards: [Uno](https://amzn.to/38aL2ik), [Due](https://amzn.to/36YkWi2), [Micro](https://amzn.to/35WkdwG), [Nano](https://amzn.to/2QTvwRX), [Mega](https://amzn.to/36XWhuf), [Yun](https://amzn.to/30odURc), [Leonardo](https://amzn.to/36XWjlR)...
* Espressif chips: [ESP8266](https://amzn.to/36YluV8), [ESP32](https://amzn.to/2G4pRCB)
* Lolin (WeMos) boards: [D1 mini](https://amzn.to/2QUpz7q), [D1 Mini Pro](https://amzn.to/36UsGSs)...
* Teensy boards: [4.0](https://amzn.to/30ljXGq), [3.2](https://amzn.to/2FT0EuC), [2.0](https://amzn.to/2QXUMXj)
* Particle boards: [Argon](https://amzn.to/2FQHa9X), [Boron](https://amzn.to/36WgLUd), [Electron](https://amzn.to/30vEc4k), [Photon](https://amzn.to/387F9Cd)...
* Texas Instruments boards: [MSP430](https://amzn.to/30nJWgg)...
* Soft cores: [Nios II](https://en.wikipedia.org/wiki/Nios_II)...
* Tested on all major development environments
* [Arduino IDE](https://www.arduino.cc/en/Main/Software)
* [Atmel Studio](http://www.atmel.com/microsite/atmel-studio/)
* [Atollic TrueSTUDIO](https://atollic.com/truestudio/)
* [Energia](http://energia.nu/)
* [IAR Embedded Workbench](https://www.iar.com/iar-embedded-workbench/)
* [Keil uVision](http://www.keil.com/)
* [MPLAB X IDE](http://www.microchip.com/mplab/mplab-x-ide)
* [Particle](https://www.particle.io/)
* [PlatformIO](http://platformio.org/)
* [Sloeber plugin for Eclipse](https://eclipse.baeyens.it/)
* [Visual Micro](http://www.visualmicro.com/)
* [Visual Studio](https://www.visualstudio.com/)
* [Even works with online compilers like wandbox.org](https://wandbox.org/permlink/RlZSKy17DjJ6HcdN)
* [CMake friendly](https://arduinojson.org/v7/how-to/use-arduinojson-with-cmake/)
* Well designed
* [Elegant API](http://arduinojson.org/v7/example/)
* [Thread-safe](https://en.wikipedia.org/wiki/Thread_safety)
* Self-contained (no external dependency)
* `const` friendly
* [`for` friendly](https://arduinojson.org/v7/api/jsonobject/begin_end/)
* [TMP friendly](https://en.wikipedia.org/wiki/Template_metaprogramming)
* Handles [integer overflows](https://arduinojson.org/v7/api/jsonvariant/as/#integer-overflows)
* Well tested
* [Unit test coverage close to 100%](https://coveralls.io/github/bblanchon/ArduinoJson?branch=7.x)
* Continuously tested on
* [Visual Studio 2017, 2019, 2022](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/7.x)
* [GCC 4.8, 5, 6, 7, 8, 9, 10, 11, 12](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
* [Clang 7 to 19](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
* [Continuously fuzzed with Google OSS Fuzz](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
* Passes all default checks of [clang-tidy](https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/)
* Well documented
* [Tutorials](https://arduinojson.org/v7/doc/deserialization/)
* [Examples](https://arduinojson.org/v7/example/)
* [How-tos](https://arduinojson.org/v7/example/)
* [FAQ](https://arduinojson.org/v7/faq/)
* [Troubleshooter](https://arduinojson.org/v7/troubleshooter/)
* [Book](https://arduinojson.org/book/)
* [Changelog](CHANGELOG.md)
* Vibrant user community
* Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories)
* [Used in hundreds of projects](https://www.hackster.io/search?i=projects&q=arduinojson)
* [Responsive support](https://github.com/bblanchon/ArduinoJson/issues?q=is%3Aissue+is%3Aclosed)
## Quickstart
### Deserialization
Here is a program that parses a JSON document with ArduinoJson.
```c++
const char* json = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
JsonDocument doc;
deserializeJson(doc, json);
const char* sensor = doc["sensor"];
long time = doc["time"];
double latitude = doc["data"][0];
double longitude = doc["data"][1];
```
See the [tutorial on arduinojson.org](https://arduinojson.org/v7/doc/deserialization/)
### Serialization
Here is a program that generates a JSON document with ArduinoJson:
```c++
JsonDocument doc;
doc["sensor"] = "gps";
doc["time"] = 1351824120;
doc["data"][0] = 48.756080;
doc["data"][1] = 2.302038;
serializeJson(doc, Serial);
// This prints:
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
```
See the [tutorial on arduinojson.org](https://arduinojson.org/v7/doc/serialization/)
## Sponsors
ArduinoJson is thankful to its sponsors. Please give them a visit; they deserve it!
<p>
<a href="https://github.com/1technophile" rel="sponsored">
<img alt="1technophile" src="https://avatars.githubusercontent.com/u/12672732?s=40&v=4">
</a>
<a href="https://github.com/LArkema" rel="sponsored">
<img alt="LArkema" src="https://avatars.githubusercontent.com/u/38381313?s=40&v=4">
</a>
</p>
If you run a commercial project that embeds ArduinoJson, think about [sponsoring the library's development](https://github.com/sponsors/bblanchon): it ensures the code that your products rely on stays actively maintained. It can also give your project some exposure to the makers' community.
If you are an individual user and want to support the development (or give a sign of appreciation), consider purchasing the book [Mastering ArduinoJson](https://arduinojson.org/book/)&nbsp;❤, or simply [cast a star](https://github.com/bblanchon/ArduinoJson/stargazers)&nbsp;⭐.

View File

@@ -0,0 +1,152 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
//
// This example shows how to store your project configuration in a file.
// It uses the SD library but can be easily modified for any other file-system.
//
// The file contains a JSON document with the following content:
// {
// "hostname": "examples.com",
// "port": 2731
// }
//
// To run this program, you need an SD card connected to the SPI bus as follows:
// * MOSI <-> pin 11
// * MISO <-> pin 12
// * CLK <-> pin 13
// * CS <-> pin 4
//
// https://arduinojson.org/v7/example/config/
#include <ArduinoJson.h>
#include <SD.h>
#include <SPI.h>
// Our configuration structure.
struct Config {
char hostname[64];
int port;
};
const char* filename = "/config.txt"; // <- SD library uses 8.3 filenames
Config config; // <- global configuration object
// Loads the configuration from a file
void loadConfiguration(const char* filename, Config& config) {
// Open file for reading
File file = SD.open(filename);
// Allocate a temporary JsonDocument
JsonDocument doc;
// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, file);
if (error)
Serial.println(F("Failed to read file, using default configuration"));
// Copy values from the JsonDocument to the Config
config.port = doc["port"] | 2731;
strlcpy(config.hostname, // <- destination
doc["hostname"] | "example.com", // <- source
sizeof(config.hostname)); // <- destination's capacity
// Close the file (Curiously, File's destructor doesn't close the file)
file.close();
}
// Saves the configuration to a file
void saveConfiguration(const char* filename, const Config& config) {
// Delete existing file, otherwise the configuration is appended to the file
SD.remove(filename);
// Open file for writing
File file = SD.open(filename, FILE_WRITE);
if (!file) {
Serial.println(F("Failed to create file"));
return;
}
// Allocate a temporary JsonDocument
JsonDocument doc;
// Set the values in the document
doc["hostname"] = config.hostname;
doc["port"] = config.port;
// Serialize JSON to file
if (serializeJson(doc, file) == 0) {
Serial.println(F("Failed to write to file"));
}
// Close the file
file.close();
}
// Prints the content of a file to the Serial
void printFile(const char* filename) {
// Open file for reading
File file = SD.open(filename);
if (!file) {
Serial.println(F("Failed to read file"));
return;
}
// Extract each characters by one by one
while (file.available()) {
Serial.print((char)file.read());
}
Serial.println();
// Close the file
file.close();
}
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// Initialize SD library
const int chipSelect = 4;
while (!SD.begin(chipSelect)) {
Serial.println(F("Failed to initialize SD library"));
delay(1000);
}
// Should load default config if run for the first time
Serial.println(F("Loading configuration..."));
loadConfiguration(filename, config);
// Create configuration file
Serial.println(F("Saving configuration..."));
saveConfiguration(filename, config);
// Dump config file
Serial.println(F("Print config file..."));
printFile(filename);
}
void loop() {
// not used in this example
}
// Performance issue?
// ------------------
//
// File is an unbuffered stream, which is not optimal for ArduinoJson.
// See: https://arduinojson.org/v7/how-to/improve-speed/
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization or deserialization problem.
//
// The book "Mastering ArduinoJson" contains a case study of a project that has
// a complex configuration with nested members.
// Contrary to this example, the project in the book uses the SPIFFS filesystem.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

View File

@@ -0,0 +1,64 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
//
// This example shows how to use DeserializationOption::Filter
//
// https://arduinojson.org/v7/example/filter/
#include <ArduinoJson.h>
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// The huge input: an extract from OpenWeatherMap response
auto input_json = F(
"{\"cod\":\"200\",\"message\":0,\"list\":[{\"dt\":1581498000,\"main\":{"
"\"temp\":3.23,\"feels_like\":-3.63,\"temp_min\":3.23,\"temp_max\":4.62,"
"\"pressure\":1014,\"sea_level\":1014,\"grnd_level\":1010,\"humidity\":"
"58,\"temp_kf\":-1.39},\"weather\":[{\"id\":800,\"main\":\"Clear\","
"\"description\":\"clear "
"sky\",\"icon\":\"01d\"}],\"clouds\":{\"all\":0},\"wind\":{\"speed\":6."
"19,\"deg\":266},\"sys\":{\"pod\":\"d\"},\"dt_txt\":\"2020-02-12 "
"09:00:00\"},{\"dt\":1581508800,\"main\":{\"temp\":6.09,\"feels_like\":-"
"1.07,\"temp_min\":6.09,\"temp_max\":7.13,\"pressure\":1015,\"sea_"
"level\":1015,\"grnd_level\":1011,\"humidity\":48,\"temp_kf\":-1.04},"
"\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear "
"sky\",\"icon\":\"01d\"}],\"clouds\":{\"all\":9},\"wind\":{\"speed\":6."
"64,\"deg\":268},\"sys\":{\"pod\":\"d\"},\"dt_txt\":\"2020-02-12 "
"12:00:00\"}],\"city\":{\"id\":2643743,\"name\":\"London\",\"coord\":{"
"\"lat\":51.5085,\"lon\":-0.1257},\"country\":\"GB\",\"population\":"
"1000000,\"timezone\":0,\"sunrise\":1581492085,\"sunset\":1581527294}}");
// The filter: it contains "true" for each value we want to keep
JsonDocument filter;
filter["list"][0]["dt"] = true;
filter["list"][0]["main"]["temp"] = true;
// Deserialize the document
JsonDocument doc;
deserializeJson(doc, input_json, DeserializationOption::Filter(filter));
// Print the result
serializeJsonPretty(doc, Serial);
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// deserialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on deserialization.
// It begins with a simple example, like the one above, and then adds more
// features like deserializing directly from a file or an HTTP request.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

View File

@@ -0,0 +1,65 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
//
// This example shows how to generate a JSON document with ArduinoJson.
//
// https://arduinojson.org/v7/example/generator/
#include <ArduinoJson.h>
void setup() {
// Initialize Serial port
Serial.begin(9600);
while (!Serial)
continue;
// Allocate the JSON document
JsonDocument doc;
// Add values in the document
doc["sensor"] = "gps";
doc["time"] = 1351824120;
// Add an array
JsonArray data = doc["data"].to<JsonArray>();
data.add(48.756080);
data.add(2.302038);
// Generate the minified JSON and send it to the Serial port
serializeJson(doc, Serial);
// The above line prints:
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
// Start a new line
Serial.println();
// Generate the prettified JSON and send it to the Serial port
serializeJsonPretty(doc, Serial);
// The above line prints:
// {
// "sensor": "gps",
// "time": 1351824120,
// "data": [
// 48.756080,
// 2.302038
// ]
// }
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on serialization.
// It begins with a simple example, like the one above, and then adds more
// features like serializing directly to a file or an HTTP request.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

View File

@@ -0,0 +1,125 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
//
// This example shows how to parse a JSON document in an HTTP response.
// It uses the Ethernet library, but can be easily adapted for Wifi.
//
// It performs a GET resquest on https://arduinojson.org/example.json
// Here is the expected response:
// {
// "sensor": "gps",
// "time": 1351824120,
// "data": [
// 48.756080,
// 2.302038
// ]
// }
//
// https://arduinojson.org/v7/example/http-client/
#include <ArduinoJson.h>
#include <Ethernet.h>
#include <SPI.h>
void setup() {
// Initialize Serial port
Serial.begin(9600);
while (!Serial)
continue;
// Initialize Ethernet library
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
if (!Ethernet.begin(mac)) {
Serial.println(F("Failed to configure Ethernet"));
return;
}
delay(1000);
Serial.println(F("Connecting..."));
// Connect to HTTP server
EthernetClient client;
client.setTimeout(10000);
if (!client.connect("arduinojson.org", 80)) {
Serial.println(F("Connection failed"));
return;
}
Serial.println(F("Connected!"));
// Send HTTP request
client.println(F("GET /example.json HTTP/1.0"));
client.println(F("Host: arduinojson.org"));
client.println(F("Connection: close"));
if (client.println() == 0) {
Serial.println(F("Failed to send request"));
client.stop();
return;
}
// Check HTTP status
char status[32] = {0};
client.readBytesUntil('\r', status, sizeof(status));
// It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK"
if (strcmp(status + 9, "200 OK") != 0) {
Serial.print(F("Unexpected response: "));
Serial.println(status);
client.stop();
return;
}
// Skip HTTP headers
char endOfHeaders[] = "\r\n\r\n";
if (!client.find(endOfHeaders)) {
Serial.println(F("Invalid response"));
client.stop();
return;
}
// Allocate the JSON document
JsonDocument doc;
// Parse JSON object
DeserializationError error = deserializeJson(doc, client);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
client.stop();
return;
}
// Extract values
Serial.println(F("Response:"));
Serial.println(doc["sensor"].as<const char*>());
Serial.println(doc["time"].as<long>());
Serial.println(doc["data"][0].as<float>(), 6);
Serial.println(doc["data"][1].as<float>(), 6);
// Disconnect
client.stop();
}
void loop() {
// not used in this example
}
// Performance issue?
// ------------------
//
// EthernetClient is an unbuffered stream, which is not optimal for ArduinoJson.
// See: https://arduinojson.org/v7/how-to/improve-speed/
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on deserialization
// showing how to parse the response from GitHub's API. In the last chapter,
// it shows how to parse the huge documents from OpenWeatherMap
// and Reddit.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

View File

@@ -0,0 +1,65 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
//
// This example shows how to deserialize a JSON document with ArduinoJson.
//
// https://arduinojson.org/v7/example/parser/
#include <ArduinoJson.h>
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// Allocate the JSON document
JsonDocument doc;
// JSON input string.
const char* json =
"{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, json);
// Test if parsing succeeds
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
// Fetch the values
//
// Most of the time, you can rely on the implicit casts.
// In other case, you can do doc["time"].as<long>();
const char* sensor = doc["sensor"];
long time = doc["time"];
double latitude = doc["data"][0];
double longitude = doc["data"][1];
// Print the values
Serial.println(sensor);
Serial.println(time);
Serial.println(latitude, 6);
Serial.println(longitude, 6);
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// deserialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on deserialization.
// It begins with a simple example, like the one above, and then adds more
// features like deserializing directly from a file or an HTTP request.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

View File

@@ -0,0 +1,118 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
//
// This example shows how to implement an HTTP server that sends a JSON document
// in the response.
// It uses the Ethernet library but can be easily adapted for Wifi.
//
// The JSON document contains the values of the analog and digital pins.
// It looks like that:
// {
// "analog": [0, 76, 123, 158, 192, 205],
// "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0]
// }
//
// https://arduinojson.org/v7/example/http-server/
#include <ArduinoJson.h>
#include <Ethernet.h>
#include <SPI.h>
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
EthernetServer server(80);
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// Initialize Ethernet libary
if (!Ethernet.begin(mac)) {
Serial.println(F("Failed to initialize Ethernet library"));
return;
}
// Start to listen
server.begin();
Serial.println(F("Server is ready."));
Serial.print(F("Please connect to http://"));
Serial.println(Ethernet.localIP());
}
void loop() {
// Wait for an incomming connection
EthernetClient client = server.available();
// Do we have a client?
if (!client)
return;
Serial.println(F("New client"));
// Read the request (we ignore the content in this example)
while (client.available())
client.read();
// Allocate a temporary JsonDocument
JsonDocument doc;
// Create the "analog" array
JsonArray analogValues = doc["analog"].to<JsonArray>();
for (int pin = 0; pin < 6; pin++) {
// Read the analog input
int value = analogRead(pin);
// Add the value at the end of the array
analogValues.add(value);
}
// Create the "digital" array
JsonArray digitalValues = doc["digital"].to<JsonArray>();
for (int pin = 0; pin < 14; pin++) {
// Read the digital input
int value = digitalRead(pin);
// Add the value at the end of the array
digitalValues.add(value);
}
Serial.print(F("Sending: "));
serializeJson(doc, Serial);
Serial.println();
// Write response headers
client.println(F("HTTP/1.0 200 OK"));
client.println(F("Content-Type: application/json"));
client.println(F("Connection: close"));
client.print(F("Content-Length: "));
client.println(measureJsonPretty(doc));
client.println();
// Write JSON document
serializeJsonPretty(doc, client);
// Disconnect
client.stop();
}
// Performance issue?
// ------------------
//
// EthernetClient is an unbuffered stream, which is not optimal for ArduinoJson.
// See: https://arduinojson.org/v7/how-to/improve-speed/
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on serialization.
// It begins with a simple example, then adds more features like serializing
// directly to a file or an HTTP client.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

View File

@@ -0,0 +1,106 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
//
// This example shows how to send a JSON document to a UDP socket.
// At regular interval, it sends a UDP packet that contains the status of
// analog and digital pins.
// It looks like that:
// {
// "analog": [0, 76, 123, 158, 192, 205],
// "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0]
// }
//
// If you want to test this program, you need to be able to receive the UDP
// packets.
// For example, you can run netcat on your computer
// $ ncat -ulp 8888
// See https://nmap.org/ncat/
//
// https://arduinojson.org/v7/example/udp-beacon/
#include <ArduinoJson.h>
#include <Ethernet.h>
#include <SPI.h>
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress remoteIp(192, 168, 0, 108); // <- EDIT!!!!
unsigned short remotePort = 8888;
unsigned short localPort = 8888;
EthernetUDP udp;
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// Initialize Ethernet libary
if (!Ethernet.begin(mac)) {
Serial.println(F("Failed to initialize Ethernet library"));
return;
}
// Enable UDP
udp.begin(localPort);
}
void loop() {
// Allocate a temporary JsonDocument
JsonDocument doc;
// Create the "analog" array
JsonArray analogValues = doc["analog"].to<JsonArray>();
for (int pin = 0; pin < 6; pin++) {
// Read the analog input
int value = analogRead(pin);
// Add the value at the end of the array
analogValues.add(value);
}
// Create the "digital" array
JsonArray digitalValues = doc["digital"].to<JsonArray>();
for (int pin = 0; pin < 14; pin++) {
// Read the digital input
int value = digitalRead(pin);
// Add the value at the end of the array
digitalValues.add(value);
}
// Log
Serial.print(F("Sending to "));
Serial.print(remoteIp);
Serial.print(F(" on port "));
Serial.println(remotePort);
serializeJson(doc, Serial);
// Send UDP packet
udp.beginPacket(remoteIp, remotePort);
serializeJson(doc, udp);
udp.println();
udp.endPacket();
// Wait
delay(10000);
}
// Performance issue?
// ------------------
//
// EthernetUDP is an unbuffered stream, which is not optimal for ArduinoJson.
// See: https://arduinojson.org/v7/how-to/improve-speed/
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on serialization.
// It begins with a simple example, then adds more features like serializing
// directly to a file or any stream.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

View File

@@ -0,0 +1,61 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
//
// This example shows how to deserialize a MessagePack document with
// ArduinoJson.
//
// https://arduinojson.org/v7/example/msgpack-parser/
#include <ArduinoJson.h>
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// Allocate the JSON document
JsonDocument doc;
// The MessagePack input string
uint8_t input[] = {131, 166, 115, 101, 110, 115, 111, 114, 163, 103, 112, 115,
164, 116, 105, 109, 101, 206, 80, 147, 50, 248, 164, 100,
97, 116, 97, 146, 203, 64, 72, 96, 199, 58, 188, 148,
112, 203, 64, 2, 106, 146, 230, 33, 49, 169};
// This MessagePack document contains:
// {
// "sensor": "gps",
// "time": 1351824120,
// "data": [48.75608, 2.302038]
// }
// Parse the input
DeserializationError error = deserializeMsgPack(doc, input);
// Test if parsing succeeded
if (error) {
Serial.print("deserializeMsgPack() failed: ");
Serial.println(error.f_str());
return;
}
// Fetch the values
//
// Most of the time, you can rely on the implicit casts.
// In other case, you can do doc["time"].as<long>();
const char* sensor = doc["sensor"];
long time = doc["time"];
double latitude = doc["data"][0];
double longitude = doc["data"][1];
// Print the values
Serial.println(sensor);
Serial.println(time);
Serial.println(latitude, 6);
Serial.println(longitude, 6);
}
void loop() {
// not used in this example
}

View File

@@ -0,0 +1,63 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
//
// This example shows the different ways you can use Flash strings with
// ArduinoJson.
//
// Use Flash strings sparingly, because ArduinoJson duplicates them in the
// JsonDocument. Prefer plain old char*, as they are more efficient in term of
// code size, speed, and memory usage.
//
// https://arduinojson.org/v7/example/progmem/
#include <ArduinoJson.h>
void setup() {
JsonDocument doc;
// You can use a Flash String as your JSON input.
// WARNING: the strings in the input will be duplicated in the JsonDocument.
deserializeJson(doc, F("{\"sensor\":\"gps\",\"time\":1351824120,"
"\"data\":[48.756080,2.302038]}"));
// You can use a Flash String as a key to get a member from JsonDocument
// No duplication is done.
long time = doc[F("time")];
// You can use a Flash String as a key to set a member of a JsonDocument
// WARNING: the content of the Flash String will be duplicated in the
// JsonDocument.
doc[F("time")] = time;
// You can set a Flash String as the content of a JsonVariant
// WARNING: the content of the Flash String will be duplicated in the
// JsonDocument.
doc["sensor"] = F("gps");
// It works with serialized() too:
doc["sensor"] = serialized(F("\"gps\""));
doc["sensor"] = serialized(F("\xA3gps"), 3);
// You can compare the content of a JsonVariant to a Flash String
if (doc["sensor"] == F("gps")) {
// ...
}
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any memory
// problem.
//
// The book "Mastering ArduinoJson" contains a quick C++ course that explains
// how your microcontroller stores strings in memory. It also tells why you
// should not abuse Flash strings with ArduinoJson.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

View File

@@ -0,0 +1,76 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
//
// This example shows the different ways you can use String with ArduinoJson.
//
// Use String objects sparingly, because ArduinoJson duplicates them in the
// JsonDocument. Prefer plain old char[], as they are more efficient in term of
// code size, speed, and memory usage.
//
// https://arduinojson.org/v7/example/string/
#include <ArduinoJson.h>
void setup() {
JsonDocument doc;
// You can use a String as your JSON input.
// WARNING: the string in the input will be duplicated in the JsonDocument.
String input =
"{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
deserializeJson(doc, input);
// You can use a String as a key to get a member from JsonDocument
// No duplication is done.
long time = doc[String("time")];
// You can use a String as a key to set a member of a JsonDocument
// WARNING: the content of the String will be duplicated in the JsonDocument.
doc[String("time")] = time;
// You can get the content of a JsonVariant as a String
// No duplication is done, at least not in the JsonDocument.
String sensor = doc["sensor"];
// Unfortunately, the following doesn't work (issue #118):
// sensor = doc["sensor"]; // <- error "ambiguous overload for 'operator='"
// As a workaround, you need to replace by:
sensor = doc["sensor"].as<String>();
// You can set a String as the content of a JsonVariant
// WARNING: the content of the String will be duplicated in the JsonDocument.
doc["sensor"] = sensor;
// It works with serialized() too:
doc["sensor"] = serialized(sensor);
// You can also concatenate strings
// WARNING: the content of the String will be duplicated in the JsonDocument.
doc[String("sen") + "sor"] = String("gp") + "s";
// You can compare the content of a JsonObject with a String
if (doc["sensor"] == sensor) {
// ...
}
// Lastly, you can print the resulting JSON to a String
String output;
serializeJson(doc, output);
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any problem.
//
// The book "Mastering ArduinoJson" contains a quick C++ course that explains
// how your microcontroller stores strings in memory. On several occasions, it
// shows how you can avoid String in your program.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

View File

@@ -0,0 +1,23 @@
{
"name": "ArduinoJson",
"keywords": "json, rest, http, web",
"description": "A simple and efficient JSON library for embedded C++. ⭐ 7124 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.",
"homepage": "https://arduinojson.org/?utm_source=meta&utm_medium=library.json",
"repository": {
"type": "git",
"url": "https://github.com/bblanchon/ArduinoJson.git"
},
"version": "7.4.3",
"authors": {
"name": "Benoit Blanchon",
"url": "https://blog.benoitblanchon.fr"
},
"export": {
"include": ["src", "examples", "LICENSE.txt", "ArduinoJson.h"]
},
"frameworks": "*",
"platforms": "*",
"build": {
"libArchive": false
}
}

View File

@@ -0,0 +1,11 @@
name=ArduinoJson
version=7.4.3
author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=A simple and efficient JSON library for embedded C++.
paragraph=⭐ 7124 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.
category=Data Processing
url=https://arduinojson.org/?utm_source=meta&utm_medium=library.properties
architectures=*
repository=https://github.com/bblanchon/ArduinoJson.git
license=MIT

View File

@@ -0,0 +1,17 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#ifdef __cplusplus
# include "ArduinoJson.hpp"
using namespace ArduinoJson;
#else
#error ArduinoJson requires a C++ compiler, please change file extension to .cc or .cpp
#endif

View File

@@ -0,0 +1,65 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_VER < 1910)
# error ArduinoJson requires C++11 or newer. Configure your compiler for C++11 or downgrade ArduinoJson to 6.20.
#endif
#include "ArduinoJson/Configuration.hpp"
// Include Arduino.h before stdlib.h to avoid conflict with atexit()
// https://github.com/bblanchon/ArduinoJson/pull/1693#issuecomment-1001060240
#if ARDUINOJSON_ENABLE_ARDUINO_STRING || ARDUINOJSON_ENABLE_ARDUINO_STREAM || \
ARDUINOJSON_ENABLE_ARDUINO_PRINT || \
(ARDUINOJSON_ENABLE_PROGMEM && defined(ARDUINO))
# include <Arduino.h>
#endif
#if !ARDUINOJSON_DEBUG
# ifdef __clang__
# pragma clang system_header
# elif defined __GNUC__
# pragma GCC system_header
# endif
#endif
// Remove true and false macros defined by some cores, such as Arduino Due's
// See issues #2181 and arduino/ArduinoCore-sam#50
#ifdef true
# undef true
#endif
#ifdef false
# undef false
#endif
#include "ArduinoJson/Array/JsonArray.hpp"
#include "ArduinoJson/Object/JsonObject.hpp"
#include "ArduinoJson/Variant/JsonVariantConst.hpp"
#include "ArduinoJson/Document/JsonDocument.hpp"
#include "ArduinoJson/Array/ArrayImpl.hpp"
#include "ArduinoJson/Array/ElementProxy.hpp"
#include "ArduinoJson/Array/Utilities.hpp"
#include "ArduinoJson/Collection/CollectionImpl.hpp"
#include "ArduinoJson/Memory/ResourceManagerImpl.hpp"
#include "ArduinoJson/Object/MemberProxy.hpp"
#include "ArduinoJson/Object/ObjectImpl.hpp"
#include "ArduinoJson/Variant/ConverterImpl.hpp"
#include "ArduinoJson/Variant/JsonVariantCopier.hpp"
#include "ArduinoJson/Variant/VariantCompare.hpp"
#include "ArduinoJson/Variant/VariantImpl.hpp"
#include "ArduinoJson/Variant/VariantRefBaseImpl.hpp"
#include "ArduinoJson/Json/JsonDeserializer.hpp"
#include "ArduinoJson/Json/JsonSerializer.hpp"
#include "ArduinoJson/Json/PrettyJsonSerializer.hpp"
#include "ArduinoJson/MsgPack/MsgPackBinary.hpp"
#include "ArduinoJson/MsgPack/MsgPackDeserializer.hpp"
#include "ArduinoJson/MsgPack/MsgPackExtension.hpp"
#include "ArduinoJson/MsgPack/MsgPackSerializer.hpp"
#include "ArduinoJson/compatibility.hpp"

View File

@@ -0,0 +1,66 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Collection/CollectionData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class ArrayData : public CollectionData {
public:
VariantData* addElement(ResourceManager* resources);
static VariantData* addElement(ArrayData* array, ResourceManager* resources) {
if (!array)
return nullptr;
return array->addElement(resources);
}
template <typename T>
bool addValue(const T& value, ResourceManager* resources);
template <typename T>
static bool addValue(ArrayData* array, const T& value,
ResourceManager* resources) {
if (!array)
return false;
return array->addValue(value, resources);
}
VariantData* getOrAddElement(size_t index, ResourceManager* resources);
VariantData* getElement(size_t index, const ResourceManager* resources) const;
static VariantData* getElement(const ArrayData* array, size_t index,
const ResourceManager* resources) {
if (!array)
return nullptr;
return array->getElement(index, resources);
}
void removeElement(size_t index, ResourceManager* resources);
static void removeElement(ArrayData* array, size_t index,
ResourceManager* resources) {
if (!array)
return;
array->removeElement(index, resources);
}
void remove(iterator it, ResourceManager* resources) {
CollectionData::removeOne(it, resources);
}
static void remove(ArrayData* array, iterator it,
ResourceManager* resources) {
if (array)
return array->remove(it, resources);
}
private:
iterator at(size_t index, const ResourceManager* resources) const;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,79 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Array/ArrayData.hpp>
#include <ArduinoJson/Variant/VariantCompare.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline ArrayData::iterator ArrayData::at(
size_t index, const ResourceManager* resources) const {
auto it = createIterator(resources);
while (!it.done() && index) {
it.next(resources);
--index;
}
return it;
}
inline VariantData* ArrayData::addElement(ResourceManager* resources) {
auto slot = resources->allocVariant();
if (!slot)
return nullptr;
CollectionData::appendOne(slot, resources);
return slot.ptr();
}
inline VariantData* ArrayData::getOrAddElement(size_t index,
ResourceManager* resources) {
auto it = createIterator(resources);
while (!it.done() && index > 0) {
it.next(resources);
index--;
}
if (it.done())
index++;
VariantData* element = it.data();
while (index > 0) {
element = addElement(resources);
if (!element)
return nullptr;
index--;
}
return element;
}
inline VariantData* ArrayData::getElement(
size_t index, const ResourceManager* resources) const {
return at(index, resources).data();
}
inline void ArrayData::removeElement(size_t index, ResourceManager* resources) {
remove(at(index, resources), resources);
}
template <typename T>
inline bool ArrayData::addValue(const T& value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(resources != nullptr);
auto slot = resources->allocVariant();
if (!slot)
return false;
JsonVariant variant(slot.ptr(), resources);
if (!variant.set(value)) {
resources->freeVariant(slot);
return false;
}
CollectionData::appendOne(slot, resources);
return true;
}
// Returns the size (in bytes) of an array with n elements.
constexpr size_t sizeofArray(size_t n) {
return n * ResourceManager::slotSize;
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,75 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Variant/VariantRefBase.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
// A proxy class to get or set an element of an array.
// https://arduinojson.org/v7/api/jsonarray/subscript/
template <typename TUpstream>
class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
public VariantOperators<ElementProxy<TUpstream>> {
friend class VariantAttorney;
friend class VariantRefBase<ElementProxy<TUpstream>>;
template <typename, typename>
friend class MemberProxy;
template <typename>
friend class ElementProxy;
public:
ElementProxy(TUpstream upstream, size_t index)
: upstream_(upstream), index_(index) {}
ElementProxy& operator=(const ElementProxy& src) {
this->set(src);
return *this;
}
template <typename T>
ElementProxy& operator=(const T& src) {
this->set(src);
return *this;
}
template <typename T>
ElementProxy& operator=(T* src) {
this->set(src);
return *this;
}
private:
// clang-format off
ElementProxy(const ElementProxy& src) // Error here? See https://arduinojson.org/v7/proxy-non-copyable/
: upstream_(src.upstream_), index_(src.index_) {}
// clang-format on
ResourceManager* getResourceManager() const {
return VariantAttorney::getResourceManager(upstream_);
}
FORCE_INLINE VariantData* getData() const {
return VariantData::getElement(
VariantAttorney::getData(upstream_), index_,
VariantAttorney::getResourceManager(upstream_));
}
VariantData* getOrCreateData() const {
auto data = VariantAttorney::getOrCreateData(upstream_);
if (!data)
return nullptr;
return data->getOrAddElement(
index_, VariantAttorney::getResourceManager(upstream_));
}
TUpstream upstream_;
size_t index_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,219 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Array/ElementProxy.hpp>
#include <ArduinoJson/Array/JsonArrayConst.hpp>
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
class JsonObject;
// A reference to an array in a JsonDocument
// https://arduinojson.org/v7/api/jsonarray/
class JsonArray : public detail::VariantOperators<JsonArray> {
friend class detail::VariantAttorney;
public:
using iterator = JsonArrayIterator;
// Constructs an unbound reference.
JsonArray() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonArray(detail::ArrayData* data, detail::ResourceManager* resources)
: data_(data), resources_(resources) {}
// Returns a JsonVariant pointing to the array.
// https://arduinojson.org/v7/api/jsonvariant/
operator JsonVariant() {
void* data = data_; // prevent warning cast-align
return JsonVariant(reinterpret_cast<detail::VariantData*>(data),
resources_);
}
// Returns a read-only reference to the array.
// https://arduinojson.org/v7/api/jsonarrayconst/
operator JsonArrayConst() const {
return JsonArrayConst(data_, resources_);
}
// Appends a new (empty) element to the array.
// Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsonarray/add/
template <typename T, detail::enable_if_t<
!detail::is_same<T, JsonVariant>::value, int> = 0>
T add() const {
return add<JsonVariant>().to<T>();
}
// Appends a new (null) element to the array.
// Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsonarray/add/
template <typename T, detail::enable_if_t<
detail::is_same<T, JsonVariant>::value, int> = 0>
JsonVariant add() const {
return JsonVariant(detail::ArrayData::addElement(data_, resources_),
resources_);
}
// Appends a value to the array.
// https://arduinojson.org/v7/api/jsonarray/add/
template <typename T>
bool add(const T& value) const {
return detail::ArrayData::addValue(data_, value, resources_);
}
// Appends a value to the array.
// https://arduinojson.org/v7/api/jsonarray/add/
template <typename T,
detail::enable_if_t<!detail::is_const<T>::value, int> = 0>
bool add(T* value) const {
return detail::ArrayData::addValue(data_, value, resources_);
}
// Returns an iterator to the first element of the array.
// https://arduinojson.org/v7/api/jsonarray/begin/
iterator begin() const {
if (!data_)
return iterator();
return iterator(data_->createIterator(resources_), resources_);
}
// Returns an iterator following the last element of the array.
// https://arduinojson.org/v7/api/jsonarray/end/
iterator end() const {
return iterator();
}
// Copies an array.
// https://arduinojson.org/v7/api/jsonarray/set/
bool set(JsonArrayConst src) const {
if (!data_)
return false;
clear();
for (auto element : src) {
if (!add(element))
return false;
}
return true;
}
// Removes the element at the specified iterator.
// https://arduinojson.org/v7/api/jsonarray/remove/
void remove(iterator it) const {
detail::ArrayData::remove(data_, it.iterator_, resources_);
}
// Removes the element at the specified index.
// https://arduinojson.org/v7/api/jsonarray/remove/
void remove(size_t index) const {
detail::ArrayData::removeElement(data_, index, resources_);
}
// Removes the element at the specified index.
// https://arduinojson.org/v7/api/jsonarray/remove/
template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
void remove(const TVariant& variant) const {
if (variant.template is<size_t>())
remove(variant.template as<size_t>());
}
// Removes all the elements of the array.
// https://arduinojson.org/v7/api/jsonarray/clear/
void clear() const {
detail::ArrayData::clear(data_, resources_);
}
// Gets or sets the element at the specified index.
// https://arduinojson.org/v7/api/jsonarray/subscript/
template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
detail::ElementProxy<JsonArray> operator[](T index) const {
return {*this, size_t(index)};
}
// Gets or sets the element at the specified index.
// https://arduinojson.org/v7/api/jsonarray/subscript/
template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
detail::ElementProxy<JsonArray> operator[](const TVariant& variant) const {
if (variant.template is<size_t>())
return {*this, variant.template as<size_t>()};
else
return {*this, size_t(-1)};
}
operator JsonVariantConst() const {
return JsonVariantConst(collectionToVariant(data_), resources_);
}
// Returns true if the reference is unbound.
// https://arduinojson.org/v7/api/jsonarray/isnull/
bool isNull() const {
return data_ == 0;
}
// Returns true if the reference is bound.
// https://arduinojson.org/v7/api/jsonarray/isnull/
operator bool() const {
return data_ != 0;
}
// Returns the depth (nesting level) of the array.
// https://arduinojson.org/v7/api/jsonarray/nesting/
size_t nesting() const {
return detail::VariantData::nesting(collectionToVariant(data_), resources_);
}
// Returns the number of elements in the array.
// https://arduinojson.org/v7/api/jsonarray/size/
size_t size() const {
return data_ ? data_->size(resources_) : 0;
}
// DEPRECATED: use add<JsonVariant>() instead
ARDUINOJSON_DEPRECATED("use add<JsonVariant>() instead")
JsonVariant add() const {
return add<JsonVariant>();
}
// DEPRECATED: use add<JsonArray>() instead
ARDUINOJSON_DEPRECATED("use add<JsonArray>() instead")
JsonArray createNestedArray() const {
return add<JsonArray>();
}
// DEPRECATED: use add<JsonObject>() instead
ARDUINOJSON_DEPRECATED("use add<JsonObject>() instead")
JsonObject createNestedObject() const;
// DEPRECATED: always returns zero
ARDUINOJSON_DEPRECATED("always returns zero")
size_t memoryUsage() const {
return 0;
}
private:
detail::ResourceManager* getResourceManager() const {
return resources_;
}
detail::VariantData* getData() const {
return collectionToVariant(data_);
}
detail::VariantData* getOrCreateData() const {
return collectionToVariant(data_);
}
detail::ArrayData* data_;
detail::ResourceManager* resources_;
};
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,133 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Array/JsonArrayIterator.hpp>
#include <ArduinoJson/Variant/VariantAttorney.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
class JsonObject;
// A read-only reference to an array in a JsonDocument
// https://arduinojson.org/v7/api/jsonarrayconst/
class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
friend class JsonArray;
friend class detail::VariantAttorney;
public:
using iterator = JsonArrayConstIterator;
// Returns an iterator to the first element of the array.
// https://arduinojson.org/v7/api/jsonarrayconst/begin/
iterator begin() const {
if (!data_)
return iterator();
return iterator(data_->createIterator(resources_), resources_);
}
// Returns an iterator to the element following the last element of the array.
// https://arduinojson.org/v7/api/jsonarrayconst/end/
iterator end() const {
return iterator();
}
// Creates an unbound reference.
JsonArrayConst() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonArrayConst(const detail::ArrayData* data,
const detail::ResourceManager* resources)
: data_(data), resources_(resources) {}
// Returns the element at the specified index.
// https://arduinojson.org/v7/api/jsonarrayconst/subscript/
template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
JsonVariantConst operator[](T index) const {
return JsonVariantConst(
detail::ArrayData::getElement(data_, size_t(index), resources_),
resources_);
}
// Returns the element at the specified index.
// https://arduinojson.org/v7/api/jsonarrayconst/subscript/
template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
JsonVariantConst operator[](const TVariant& variant) const {
if (variant.template is<size_t>())
return operator[](variant.template as<size_t>());
else
return JsonVariantConst();
}
operator JsonVariantConst() const {
return JsonVariantConst(getData(), resources_);
}
// Returns true if the reference is unbound.
// https://arduinojson.org/v7/api/jsonarrayconst/isnull/
bool isNull() const {
return data_ == 0;
}
// Returns true if the reference is bound.
// https://arduinojson.org/v7/api/jsonarrayconst/isnull/
operator bool() const {
return data_ != 0;
}
// Returns the depth (nesting level) of the array.
// https://arduinojson.org/v7/api/jsonarrayconst/nesting/
size_t nesting() const {
return detail::VariantData::nesting(getData(), resources_);
}
// Returns the number of elements in the array.
// https://arduinojson.org/v7/api/jsonarrayconst/size/
size_t size() const {
return data_ ? data_->size(resources_) : 0;
}
// DEPRECATED: always returns zero
ARDUINOJSON_DEPRECATED("always returns zero")
size_t memoryUsage() const {
return 0;
}
private:
const detail::VariantData* getData() const {
return collectionToVariant(data_);
}
const detail::ArrayData* data_;
const detail::ResourceManager* resources_;
};
// Compares the content of two arrays.
// Returns true if the two arrays are equal.
inline bool operator==(JsonArrayConst lhs, JsonArrayConst rhs) {
if (!lhs && !rhs)
return true;
if (!lhs || !rhs)
return false;
auto a = lhs.begin();
auto b = rhs.begin();
for (;;) {
if (a == b) // same pointer or both null
return true;
if (a == lhs.end() || b == rhs.end())
return false;
if (*a != *b)
return false;
++a;
++b;
}
}
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,96 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Variant/JsonVariant.hpp>
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
template <typename T>
class Ptr {
public:
Ptr(T value) : value_(value) {}
T* operator->() {
return &value_;
}
T& operator*() {
return value_;
}
private:
T value_;
};
class JsonArrayIterator {
friend class JsonArray;
public:
JsonArrayIterator() {}
explicit JsonArrayIterator(detail::ArrayData::iterator iterator,
detail::ResourceManager* resources)
: iterator_(iterator), resources_(resources) {}
JsonVariant operator*() {
return JsonVariant(iterator_.data(), resources_);
}
Ptr<JsonVariant> operator->() {
return operator*();
}
bool operator==(const JsonArrayIterator& other) const {
return iterator_ == other.iterator_;
}
bool operator!=(const JsonArrayIterator& other) const {
return iterator_ != other.iterator_;
}
JsonArrayIterator& operator++() {
iterator_.next(resources_);
return *this;
}
private:
detail::ArrayData::iterator iterator_;
detail::ResourceManager* resources_;
};
class JsonArrayConstIterator {
friend class JsonArray;
public:
JsonArrayConstIterator() {}
explicit JsonArrayConstIterator(detail::ArrayData::iterator iterator,
const detail::ResourceManager* resources)
: iterator_(iterator), resources_(resources) {}
JsonVariantConst operator*() const {
return JsonVariantConst(iterator_.data(), resources_);
}
Ptr<JsonVariantConst> operator->() {
return operator*();
}
bool operator==(const JsonArrayConstIterator& other) const {
return iterator_ == other.iterator_;
}
bool operator!=(const JsonArrayConstIterator& other) const {
return iterator_ != other.iterator_;
}
JsonArrayConstIterator& operator++() {
iterator_.next(resources_);
return *this;
}
private:
detail::ArrayData::iterator iterator_;
const detail::ResourceManager* resources_;
};
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,112 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Array/JsonArray.hpp>
#include <ArduinoJson/Document/JsonDocument.hpp>
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Copies a value to a JsonVariant.
// This is a degenerated form of copyArray() to stop the recursion.
template <typename T, detail::enable_if_t<!detail::is_array<T>::value, int> = 0>
inline bool copyArray(const T& src, JsonVariant dst) {
return dst.set(src);
}
// Copies values from an array to a JsonArray or a JsonVariant.
// https://arduinojson.org/v7/api/misc/copyarray/
template <typename T, size_t N, typename TDestination,
detail::enable_if_t<
!detail::is_base_of<JsonDocument, TDestination>::value, int> = 0>
inline bool copyArray(T (&src)[N], const TDestination& dst) {
return copyArray(src, N, dst);
}
// Copies values from an array to a JsonArray or a JsonVariant.
// https://arduinojson.org/v7/api/misc/copyarray/
template <typename T, typename TDestination,
detail::enable_if_t<
!detail::is_base_of<JsonDocument, TDestination>::value, int> = 0>
inline bool copyArray(const T* src, size_t len, const TDestination& dst) {
bool ok = true;
for (size_t i = 0; i < len; i++) {
ok &= copyArray(src[i], dst.template add<JsonVariant>());
}
return ok;
}
// Copies a string to a JsonVariant.
// This is a degenerated form of copyArray() to handle strings.
template <typename TDestination>
inline bool copyArray(const char* src, size_t, const TDestination& dst) {
return dst.set(src);
}
// Copies values from an array to a JsonDocument.
// https://arduinojson.org/v7/api/misc/copyarray/
template <typename T>
inline bool copyArray(const T& src, JsonDocument& dst) {
return copyArray(src, dst.to<JsonArray>());
}
// Copies an array to a JsonDocument.
// https://arduinojson.org/v7/api/misc/copyarray/
template <typename T>
inline bool copyArray(const T* src, size_t len, JsonDocument& dst) {
return copyArray(src, len, dst.to<JsonArray>());
}
// Copies a value from a JsonVariant.
// This is a degenerated form of copyArray() to stop the recursion.
template <typename T, detail::enable_if_t<!detail::is_array<T>::value, int> = 0>
inline size_t copyArray(JsonVariantConst src, T& dst) {
dst = src.as<T>();
return 1;
}
// Copies values from a JsonArray or JsonVariant to an array.
// https://arduinojson.org/v7/api/misc/copyarray/
template <typename T, size_t N>
inline size_t copyArray(JsonArrayConst src, T (&dst)[N]) {
return copyArray(src, dst, N);
}
// Copies values from a JsonArray or JsonVariant to an array.
// https://arduinojson.org/v7/api/misc/copyarray/
template <typename T>
inline size_t copyArray(JsonArrayConst src, T* dst, size_t len) {
size_t i = 0;
for (JsonArrayConst::iterator it = src.begin(); it != src.end() && i < len;
++it)
copyArray(*it, dst[i++]);
return i;
}
// Copies a string from a JsonVariant.
// This is a degenerated form of copyArray() to handle strings.
template <size_t N>
inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) {
JsonString s = src;
size_t len = N - 1;
if (len > s.size())
len = s.size();
memcpy(dst, s.c_str(), len);
dst[len] = 0;
return 1;
}
// Copies values from a JsonDocument to an array.
// https://arduinojson.org/v7/api/misc/copyarray/
template <
typename TSource, typename T,
detail::enable_if_t<detail::is_array<T>::value &&
detail::is_base_of<JsonDocument, TSource>::value,
int> = 0>
inline size_t copyArray(const TSource& src, T& dst) {
return copyArray(src.template as<JsonArrayConst>(), dst);
}
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,122 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <stddef.h> // size_t
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantData;
class ResourceManager;
class CollectionIterator {
friend class CollectionData;
public:
CollectionIterator() : slot_(nullptr), currentId_(NULL_SLOT) {}
void next(const ResourceManager* resources);
bool done() const {
return slot_ == nullptr;
}
bool operator==(const CollectionIterator& other) const {
return slot_ == other.slot_;
}
bool operator!=(const CollectionIterator& other) const {
return slot_ != other.slot_;
}
VariantData* operator->() {
ARDUINOJSON_ASSERT(slot_ != nullptr);
return data();
}
VariantData& operator*() {
ARDUINOJSON_ASSERT(slot_ != nullptr);
return *data();
}
const VariantData& operator*() const {
ARDUINOJSON_ASSERT(slot_ != nullptr);
return *data();
}
VariantData* data() {
return reinterpret_cast<VariantData*>(slot_);
}
const VariantData* data() const {
return reinterpret_cast<const VariantData*>(slot_);
}
private:
CollectionIterator(VariantData* slot, SlotId slotId);
VariantData* slot_;
SlotId currentId_, nextId_;
};
class CollectionData {
SlotId head_ = NULL_SLOT;
SlotId tail_ = NULL_SLOT;
public:
// Placement new
static void* operator new(size_t, void* p) noexcept {
return p;
}
static void operator delete(void*, void*) noexcept {}
using iterator = CollectionIterator;
iterator createIterator(const ResourceManager* resources) const;
size_t size(const ResourceManager*) const;
size_t nesting(const ResourceManager*) const;
void clear(ResourceManager* resources);
static void clear(CollectionData* collection, ResourceManager* resources) {
if (!collection)
return;
collection->clear(resources);
}
SlotId head() const {
return head_;
}
protected:
void appendOne(Slot<VariantData> slot, const ResourceManager* resources);
void appendPair(Slot<VariantData> key, Slot<VariantData> value,
const ResourceManager* resources);
void removeOne(iterator it, ResourceManager* resources);
void removePair(iterator it, ResourceManager* resources);
private:
Slot<VariantData> getPreviousSlot(VariantData*, const ResourceManager*) const;
};
inline const VariantData* collectionToVariant(
const CollectionData* collection) {
const void* data = collection; // prevent warning cast-align
return reinterpret_cast<const VariantData*>(data);
}
inline VariantData* collectionToVariant(CollectionData* collection) {
void* data = collection; // prevent warning cast-align
return reinterpret_cast<VariantData*>(data);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,137 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Collection/CollectionData.hpp>
#include <ArduinoJson/Memory/Alignment.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantCompare.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline CollectionIterator::CollectionIterator(VariantData* slot, SlotId slotId)
: slot_(slot), currentId_(slotId) {
nextId_ = slot_ ? slot_->next() : NULL_SLOT;
}
inline void CollectionIterator::next(const ResourceManager* resources) {
ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT);
slot_ = resources->getVariant(nextId_);
currentId_ = nextId_;
if (slot_)
nextId_ = slot_->next();
}
inline CollectionData::iterator CollectionData::createIterator(
const ResourceManager* resources) const {
return iterator(resources->getVariant(head_), head_);
}
inline void CollectionData::appendOne(Slot<VariantData> slot,
const ResourceManager* resources) {
if (tail_ != NULL_SLOT) {
auto tail = resources->getVariant(tail_);
tail->setNext(slot.id());
tail_ = slot.id();
} else {
head_ = slot.id();
tail_ = slot.id();
}
}
inline void CollectionData::appendPair(Slot<VariantData> key,
Slot<VariantData> value,
const ResourceManager* resources) {
key->setNext(value.id());
if (tail_ != NULL_SLOT) {
auto tail = resources->getVariant(tail_);
tail->setNext(key.id());
tail_ = value.id();
} else {
head_ = key.id();
tail_ = value.id();
}
}
inline void CollectionData::clear(ResourceManager* resources) {
auto next = head_;
while (next != NULL_SLOT) {
auto currId = next;
auto slot = resources->getVariant(next);
next = slot->next();
resources->freeVariant({slot, currId});
}
head_ = NULL_SLOT;
tail_ = NULL_SLOT;
}
inline Slot<VariantData> CollectionData::getPreviousSlot(
VariantData* target, const ResourceManager* resources) const {
auto prev = Slot<VariantData>();
auto currentId = head_;
while (currentId != NULL_SLOT) {
auto currentSlot = resources->getVariant(currentId);
if (currentSlot == target)
break;
prev = Slot<VariantData>(currentSlot, currentId);
currentId = currentSlot->next();
}
return prev;
}
inline void CollectionData::removeOne(iterator it, ResourceManager* resources) {
if (it.done())
return;
auto curr = it.slot_;
auto prev = getPreviousSlot(curr, resources);
auto next = curr->next();
if (prev)
prev->setNext(next);
else
head_ = next;
if (next == NULL_SLOT)
tail_ = prev.id();
resources->freeVariant({it.slot_, it.currentId_});
}
inline void CollectionData::removePair(ObjectData::iterator it,
ResourceManager* resources) {
if (it.done())
return;
auto keySlot = it.slot_;
auto valueId = it.nextId_;
auto valueSlot = resources->getVariant(valueId);
// remove value slot
keySlot->setNext(valueSlot->next());
resources->freeVariant({valueSlot, valueId});
// remove key slot
removeOne(it, resources);
}
inline size_t CollectionData::nesting(const ResourceManager* resources) const {
size_t maxChildNesting = 0;
for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
size_t childNesting = it->nesting(resources);
if (childNesting > maxChildNesting)
maxChildNesting = childNesting;
}
return maxChildNesting + 1;
}
inline size_t CollectionData::size(const ResourceManager* resources) const {
size_t count = 0;
for (auto it = createIterator(resources); !it.done(); it.next(resources))
count++;
return count;
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,285 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
// Support std::istream and std::ostream
// https://arduinojson.org/v7/config/enable_std_stream/
#ifndef ARDUINOJSON_ENABLE_STD_STREAM
# ifdef __has_include
# if __has_include(<istream>) && \
__has_include(<ostream>) && \
!defined(min) && \
!defined(max)
# define ARDUINOJSON_ENABLE_STD_STREAM 1
# else
# define ARDUINOJSON_ENABLE_STD_STREAM 0
# endif
# else
# ifdef ARDUINO
# define ARDUINOJSON_ENABLE_STD_STREAM 0
# else
# define ARDUINOJSON_ENABLE_STD_STREAM 1
# endif
# endif
#endif
// Support std::string
// https://arduinojson.org/v7/config/enable_std_string/
#ifndef ARDUINOJSON_ENABLE_STD_STRING
# ifdef __has_include
# if __has_include(<string>) && !defined(min) && !defined(max)
# define ARDUINOJSON_ENABLE_STD_STRING 1
# else
# define ARDUINOJSON_ENABLE_STD_STRING 0
# endif
# else
# ifdef ARDUINO
# define ARDUINOJSON_ENABLE_STD_STRING 0
# else
# define ARDUINOJSON_ENABLE_STD_STRING 1
# endif
# endif
#endif
// Support for std::string_view
#ifndef ARDUINOJSON_ENABLE_STRING_VIEW
# ifdef __has_include
# if __has_include(<string_view>) && __cplusplus >= 201703L
# define ARDUINOJSON_ENABLE_STRING_VIEW 1
# else
# define ARDUINOJSON_ENABLE_STRING_VIEW 0
# endif
# else
# define ARDUINOJSON_ENABLE_STRING_VIEW 0
# endif
#endif
// Pointer size: a heuristic to set sensible defaults
#ifndef ARDUINOJSON_SIZEOF_POINTER
# if defined(__SIZEOF_POINTER__)
# define ARDUINOJSON_SIZEOF_POINTER __SIZEOF_POINTER__
# elif defined(_WIN64) && _WIN64
# define ARDUINOJSON_SIZEOF_POINTER 8 // 64 bits
# else
# define ARDUINOJSON_SIZEOF_POINTER 4 // assume 32 bits otherwise
# endif
#endif
// Store floating-point values with float (0) or double (1)
// https://arduinojson.org/v7/config/use_double/
#ifndef ARDUINOJSON_USE_DOUBLE
# if ARDUINOJSON_SIZEOF_POINTER >= 4 // 32 & 64 bits systems
# define ARDUINOJSON_USE_DOUBLE 1
# else
# define ARDUINOJSON_USE_DOUBLE 0
# endif
#endif
// Store integral values with long (0) or long long (1)
// https://arduinojson.org/v7/config/use_long_long/
#ifndef ARDUINOJSON_USE_LONG_LONG
# if ARDUINOJSON_SIZEOF_POINTER >= 4 // 32 & 64 bits systems
# define ARDUINOJSON_USE_LONG_LONG 1
# else
# define ARDUINOJSON_USE_LONG_LONG 0
# endif
#endif
// Limit nesting as the stack is likely to be small
// https://arduinojson.org/v7/config/default_nesting_limit/
#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT
# define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10
#endif
// Number of bytes to store a slot id
// https://arduinojson.org/v7/config/slot_id_size/
#ifndef ARDUINOJSON_SLOT_ID_SIZE
# if ARDUINOJSON_SIZEOF_POINTER <= 2
// 8-bit and 16-bit archs => up to 255 slots
# define ARDUINOJSON_SLOT_ID_SIZE 1
# elif ARDUINOJSON_SIZEOF_POINTER == 4
// 32-bit arch => up to 65535 slots
# define ARDUINOJSON_SLOT_ID_SIZE 2
# else
// 64-bit arch => up to 4294967295 slots
# define ARDUINOJSON_SLOT_ID_SIZE 4
# endif
#endif
// Capacity of each variant pool (in slots)
#ifndef ARDUINOJSON_POOL_CAPACITY
# if ARDUINOJSON_SLOT_ID_SIZE == 1
# define ARDUINOJSON_POOL_CAPACITY 16 // 96 bytes
# elif ARDUINOJSON_SLOT_ID_SIZE == 2
# define ARDUINOJSON_POOL_CAPACITY 128 // 1024 bytes
# else
# define ARDUINOJSON_POOL_CAPACITY 256 // 4096 bytes
# endif
#endif
// Initial capacity of the pool list
#ifndef ARDUINOJSON_INITIAL_POOL_COUNT
# define ARDUINOJSON_INITIAL_POOL_COUNT 4
#endif
// Automatically call shrinkToFit() from deserializeXxx()
// Disabled by default on 8-bit platforms because it's not worth the increase in
// code size
#ifndef ARDUINOJSON_AUTO_SHRINK
# if ARDUINOJSON_SIZEOF_POINTER <= 2
# define ARDUINOJSON_AUTO_SHRINK 0
# else
# define ARDUINOJSON_AUTO_SHRINK 1
# endif
#endif
// Number of bytes to store the length of a string
// https://arduinojson.org/v7/config/string_length_size/
#ifndef ARDUINOJSON_STRING_LENGTH_SIZE
# if ARDUINOJSON_SIZEOF_POINTER <= 2
# define ARDUINOJSON_STRING_LENGTH_SIZE 1 // up to 255 characters
# else
# define ARDUINOJSON_STRING_LENGTH_SIZE 2 // up to 65535 characters
# endif
#endif
#ifdef ARDUINO
// Enable support for Arduino's String class
// https://arduinojson.org/v7/config/enable_arduino_string/
# ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING
# define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
# endif
// Enable support for Arduino's Stream class
// https://arduinojson.org/v7/config/enable_arduino_stream/
# ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM
# define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1
# endif
// Enable support for Arduino's Print class
# ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT
# define ARDUINOJSON_ENABLE_ARDUINO_PRINT 1
# endif
// Enable support for PROGMEM
// https://arduinojson.org/v7/config/enable_progmem/
# ifndef ARDUINOJSON_ENABLE_PROGMEM
# define ARDUINOJSON_ENABLE_PROGMEM 1
# endif
#else // ARDUINO
// Disable support for Arduino's String class
// https://arduinojson.org/v7/config/enable_arduino_string/
# ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING
# define ARDUINOJSON_ENABLE_ARDUINO_STRING 0
# endif
// Disable support for Arduino's Stream class
// https://arduinojson.org/v7/config/enable_arduino_stream/
# ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM
# define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0
# endif
// Disable support for Arduino's Print class
# ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT
# define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0
# endif
// Enable PROGMEM support on AVR only
// https://arduinojson.org/v7/config/enable_progmem/
# ifndef ARDUINOJSON_ENABLE_PROGMEM
# ifdef __AVR__
# define ARDUINOJSON_ENABLE_PROGMEM 1
# else
# define ARDUINOJSON_ENABLE_PROGMEM 0
# endif
# endif
#endif // ARDUINO
// Convert unicode escape sequence (\u0123) to UTF-8
// https://arduinojson.org/v7/config/decode_unicode/
#ifndef ARDUINOJSON_DECODE_UNICODE
# define ARDUINOJSON_DECODE_UNICODE 1
#endif
// Ignore comments in input
// https://arduinojson.org/v7/config/enable_comments/
#ifndef ARDUINOJSON_ENABLE_COMMENTS
# define ARDUINOJSON_ENABLE_COMMENTS 0
#endif
// Support NaN in JSON
// https://arduinojson.org/v7/config/enable_nan/
#ifndef ARDUINOJSON_ENABLE_NAN
# define ARDUINOJSON_ENABLE_NAN 0
#endif
// Support Infinity in JSON
// https://arduinojson.org/v7/config/enable_infinity/
#ifndef ARDUINOJSON_ENABLE_INFINITY
# define ARDUINOJSON_ENABLE_INFINITY 0
#endif
// Control the exponentiation threshold for big numbers
// CAUTION: cannot be more that 1e9 !!!!
// https://arduinojson.org/v7/config/positive_exponentiation_threshold/
#ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD
# define ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD 1e7
#endif
// Control the exponentiation threshold for small numbers
// https://arduinojson.org/v7/config/negative_exponentiation_threshold/
#ifndef ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD
# define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5
#endif
#ifndef ARDUINOJSON_LITTLE_ENDIAN
# if defined(_MSC_VER) || \
(defined(__BYTE_ORDER__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
defined(__LITTLE_ENDIAN__) || defined(__i386) || defined(__x86_64)
# define ARDUINOJSON_LITTLE_ENDIAN 1
# else
# define ARDUINOJSON_LITTLE_ENDIAN 0
# endif
#endif
#ifndef ARDUINOJSON_ENABLE_ALIGNMENT
# if defined(__AVR)
# define ARDUINOJSON_ENABLE_ALIGNMENT 0
# else
# define ARDUINOJSON_ENABLE_ALIGNMENT 1
# endif
#endif
#ifndef ARDUINOJSON_TAB
# define ARDUINOJSON_TAB " "
#endif
#ifndef ARDUINOJSON_STRING_BUFFER_SIZE
# define ARDUINOJSON_STRING_BUFFER_SIZE 32
#endif
#ifndef ARDUINOJSON_DEBUG
# ifdef __PLATFORMIO_BUILD_DEBUG__
# define ARDUINOJSON_DEBUG 1
# else
# define ARDUINOJSON_DEBUG 0
# endif
#endif
#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_DOUBLE
# define ARDUINOJSON_USE_EXTENSIONS 1
#else
# define ARDUINOJSON_USE_EXTENSIONS 0
#endif
#if defined(nullptr)
# error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr
// See https://github.com/bblanchon/ArduinoJson/issues/1355
#endif

View File

@@ -0,0 +1,106 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/pgmspace_generic.hpp>
#include <ArduinoJson/Polyfills/preprocessor.hpp>
#if ARDUINOJSON_ENABLE_STD_STREAM
# include <ostream>
#endif
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
class DeserializationError {
public:
enum Code {
Ok,
EmptyInput,
IncompleteInput,
InvalidInput,
NoMemory,
TooDeep
};
DeserializationError() {}
DeserializationError(Code c) : code_(c) {}
// Compare with DeserializationError
friend bool operator==(const DeserializationError& lhs,
const DeserializationError& rhs) {
return lhs.code_ == rhs.code_;
}
friend bool operator!=(const DeserializationError& lhs,
const DeserializationError& rhs) {
return lhs.code_ != rhs.code_;
}
// Compare with Code
friend bool operator==(const DeserializationError& lhs, Code rhs) {
return lhs.code_ == rhs;
}
friend bool operator==(Code lhs, const DeserializationError& rhs) {
return lhs == rhs.code_;
}
friend bool operator!=(const DeserializationError& lhs, Code rhs) {
return lhs.code_ != rhs;
}
friend bool operator!=(Code lhs, const DeserializationError& rhs) {
return lhs != rhs.code_;
}
// Returns true if there is an error
explicit operator bool() const {
return code_ != Ok;
}
// Returns internal enum, useful for switch statement
Code code() const {
return code_;
}
const char* c_str() const {
static const char* messages[] = {
"Ok", "EmptyInput", "IncompleteInput",
"InvalidInput", "NoMemory", "TooDeep"};
ARDUINOJSON_ASSERT(static_cast<size_t>(code_) <
sizeof(messages) / sizeof(messages[0]));
return messages[code_];
}
#if ARDUINOJSON_ENABLE_PROGMEM
const __FlashStringHelper* f_str() const {
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s0, "Ok");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s1, "EmptyInput");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s2, "IncompleteInput");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s3, "InvalidInput");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s4, "NoMemory");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s5, "TooDeep");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(const char*, messages,
{s0, s1, s2, s3, s4, s5});
return reinterpret_cast<const __FlashStringHelper*>(
detail::pgm_read(messages + code_));
}
#endif
private:
Code code_;
};
#if ARDUINOJSON_ENABLE_STD_STREAM
inline std::ostream& operator<<(std::ostream& s,
const DeserializationError& e) {
s << e.c_str();
return s;
}
inline std::ostream& operator<<(std::ostream& s, DeserializationError::Code c) {
s << DeserializationError(c).c_str();
return s;
}
#endif
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,35 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Deserialization/Filter.hpp>
#include <ArduinoJson/Deserialization/NestingLimit.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TFilter>
struct DeserializationOptions {
TFilter filter;
DeserializationOption::NestingLimit nestingLimit;
};
template <typename TFilter>
inline DeserializationOptions<TFilter> makeDeserializationOptions(
TFilter filter, DeserializationOption::NestingLimit nestingLimit = {}) {
return {filter, nestingLimit};
}
template <typename TFilter>
inline DeserializationOptions<TFilter> makeDeserializationOptions(
DeserializationOption::NestingLimit nestingLimit, TFilter filter) {
return {filter, nestingLimit};
}
inline DeserializationOptions<AllowAllFilter> makeDeserializationOptions(
DeserializationOption::NestingLimit nestingLimit = {}) {
return {{}, nestingLimit};
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,77 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Variant/JsonVariant.hpp>
#include <ArduinoJson/Variant/VariantAttorney.hpp>
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
namespace DeserializationOption {
class Filter {
public:
#if ARDUINOJSON_AUTO_SHRINK
explicit Filter(JsonDocument& doc) : variant_(doc) {
doc.shrinkToFit();
}
#endif
explicit Filter(JsonVariantConst variant) : variant_(variant) {}
bool allow() const {
return variant_;
}
bool allowArray() const {
return variant_ == true || variant_.is<JsonArrayConst>();
}
bool allowObject() const {
return variant_ == true || variant_.is<JsonObjectConst>();
}
bool allowValue() const {
return variant_ == true;
}
template <typename TKey>
Filter operator[](const TKey& key) const {
if (variant_ == true) // "true" means "allow recursively"
return *this;
JsonVariantConst member = variant_[key];
return Filter(member.isNull() ? variant_["*"] : member);
}
private:
JsonVariantConst variant_;
};
} // namespace DeserializationOption
namespace detail {
struct AllowAllFilter {
bool allow() const {
return true;
}
bool allowArray() const {
return true;
}
bool allowObject() const {
return true;
}
bool allowValue() const {
return true;
}
template <typename TKey>
AllowAllFilter operator[](const TKey&) const {
return AllowAllFilter();
}
};
} // namespace detail
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,32 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
namespace DeserializationOption {
class NestingLimit {
public:
NestingLimit() : value_(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {}
explicit NestingLimit(uint8_t n) : value_(n) {}
NestingLimit decrement() const {
ARDUINOJSON_ASSERT(value_ > 0);
return NestingLimit(static_cast<uint8_t>(value_ - 1));
}
bool reached() const {
return value_ == 0;
}
private:
uint8_t value_;
};
} // namespace DeserializationOption
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,74 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/utility.hpp>
#include <stdlib.h> // for size_t
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
// The default reader is a simple wrapper for Readers that are not copyable
template <typename TSource, typename Enable = void>
struct Reader {
public:
Reader(TSource& source) : source_(&source) {}
int read() {
// clang-format off
return source_->read(); // Error here? See https://arduinojson.org/v7/invalid-input/
// clang-format on
}
size_t readBytes(char* buffer, size_t length) {
return source_->readBytes(buffer, length);
}
private:
TSource* source_;
};
template <typename TSource, typename Enable = void>
struct BoundedReader {
// no default implementation because we need to pass the size to the
// constructor
};
ARDUINOJSON_END_PRIVATE_NAMESPACE
#include <ArduinoJson/Deserialization/Readers/IteratorReader.hpp>
#include <ArduinoJson/Deserialization/Readers/RamReader.hpp>
#include <ArduinoJson/Deserialization/Readers/VariantReader.hpp>
#if ARDUINOJSON_ENABLE_ARDUINO_STREAM
# include <ArduinoJson/Deserialization/Readers/ArduinoStreamReader.hpp>
#endif
#if ARDUINOJSON_ENABLE_ARDUINO_STRING
# include <ArduinoJson/Deserialization/Readers/ArduinoStringReader.hpp>
#endif
#if ARDUINOJSON_ENABLE_PROGMEM
# include <ArduinoJson/Deserialization/Readers/FlashReader.hpp>
#endif
#if ARDUINOJSON_ENABLE_STD_STREAM
# include <ArduinoJson/Deserialization/Readers/StdStreamReader.hpp>
#endif
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TInput>
Reader<remove_reference_t<TInput>> makeReader(TInput&& input) {
return Reader<remove_reference_t<TInput>>{detail::forward<TInput>(input)};
}
template <typename TChar>
BoundedReader<TChar*> makeReader(TChar* input, size_t inputSize) {
return BoundedReader<TChar*>{input, inputSize};
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,30 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <Arduino.h>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TSource>
struct Reader<TSource, enable_if_t<is_base_of<Stream, TSource>::value>> {
public:
explicit Reader(Stream& stream) : stream_(&stream) {}
int read() {
// don't use stream_->read() as it ignores the timeout
char c;
return stream_->readBytes(&c, 1) ? static_cast<unsigned char>(c) : -1;
}
size_t readBytes(char* buffer, size_t length) {
return stream_->readBytes(buffer, length);
}
private:
Stream* stream_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,18 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <Arduino.h>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TSource>
struct Reader<TSource, enable_if_t<is_base_of<::String, TSource>::value>>
: BoundedReader<const char*> {
explicit Reader(const ::String& s)
: BoundedReader<const char*>(s.c_str(), s.length()) {}
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,56 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Polyfills/pgmspace.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <>
struct Reader<const __FlashStringHelper*, void> {
const char* ptr_;
public:
explicit Reader(const __FlashStringHelper* ptr)
: ptr_(reinterpret_cast<const char*>(ptr)) {}
int read() {
return pgm_read_byte(ptr_++);
}
size_t readBytes(char* buffer, size_t length) {
memcpy_P(buffer, ptr_, length);
ptr_ += length;
return length;
}
};
template <>
struct BoundedReader<const __FlashStringHelper*, void> {
const char* ptr_;
const char* end_;
public:
explicit BoundedReader(const __FlashStringHelper* ptr, size_t size)
: ptr_(reinterpret_cast<const char*>(ptr)), end_(ptr_ + size) {}
int read() {
if (ptr_ < end_)
return pgm_read_byte(ptr_++);
else
return -1;
}
size_t readBytes(char* buffer, size_t length) {
size_t available = static_cast<size_t>(end_ - ptr_);
if (available < length)
length = available;
memcpy_P(buffer, ptr_, length);
ptr_ += length;
return length;
}
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,42 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Polyfills/type_traits.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TIterator>
class IteratorReader {
TIterator ptr_, end_;
public:
explicit IteratorReader(TIterator begin, TIterator end)
: ptr_(begin), end_(end) {}
int read() {
if (ptr_ < end_)
return static_cast<unsigned char>(*ptr_++);
else
return -1;
}
size_t readBytes(char* buffer, size_t length) {
size_t i = 0;
while (i < length && ptr_ < end_)
buffer[i++] = *ptr_++;
return i;
}
};
template <typename TSource>
struct Reader<TSource, void_t<typename TSource::const_iterator>>
: IteratorReader<typename TSource::const_iterator> {
explicit Reader(const TSource& source)
: IteratorReader<typename TSource::const_iterator>(source.begin(),
source.end()) {}
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,49 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Polyfills/type_traits.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename T>
struct IsCharOrVoid {
static const bool value =
is_same<T, void>::value || is_same<T, char>::value ||
is_same<T, unsigned char>::value || is_same<T, signed char>::value;
};
template <typename T>
struct IsCharOrVoid<const T> : IsCharOrVoid<T> {};
template <typename TSource>
struct Reader<TSource*, enable_if_t<IsCharOrVoid<TSource>::value>> {
const char* ptr_;
public:
explicit Reader(const void* ptr)
: ptr_(ptr ? reinterpret_cast<const char*>(ptr) : "") {}
int read() {
return static_cast<unsigned char>(*ptr_++);
}
size_t readBytes(char* buffer, size_t length) {
for (size_t i = 0; i < length; i++)
buffer[i] = *ptr_++;
return length;
}
};
template <typename TSource>
struct BoundedReader<TSource*, enable_if_t<IsCharOrVoid<TSource>::value>>
: public IteratorReader<const char*> {
public:
explicit BoundedReader(const void* ptr, size_t len)
: IteratorReader<const char*>(reinterpret_cast<const char*>(ptr),
reinterpret_cast<const char*>(ptr) + len) {}
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,29 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <istream>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TSource>
struct Reader<TSource, enable_if_t<is_base_of<std::istream, TSource>::value>> {
public:
explicit Reader(std::istream& stream) : stream_(&stream) {}
int read() {
return stream_->get();
}
size_t readBytes(char* buffer, size_t length) {
stream_->read(buffer, static_cast<std::streamsize>(length));
return static_cast<size_t>(stream_->gcount());
}
private:
std::istream* stream_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,19 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Object/MemberProxy.hpp>
#include <ArduinoJson/Variant/JsonVariantConst.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TVariant>
struct Reader<TVariant, enable_if_t<IsVariant<TVariant>::value>>
: Reader<char*, void> {
explicit Reader(const TVariant& x)
: Reader<char*, void>(x.template as<const char*>()) {}
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,79 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Deserialization/DeserializationError.hpp>
#include <ArduinoJson/Deserialization/DeserializationOptions.hpp>
#include <ArduinoJson/Deserialization/Reader.hpp>
#include <ArduinoJson/Polyfills/utility.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
// A meta-function that returns the first type of the parameter pack
// or void if empty
template <typename...>
struct first_or_void {
using type = void;
};
template <typename T, typename... Rest>
struct first_or_void<T, Rest...> {
using type = T;
};
// A meta-function that returns true if T is a valid destination type for
// deserialize()
template <class T>
using is_deserialize_destination =
bool_constant<is_base_of<JsonDocument, remove_cv_t<T>>::value ||
IsVariant<T>::value>;
template <typename TDestination>
inline void shrinkJsonDocument(TDestination&) {
// no-op by default
}
#if ARDUINOJSON_AUTO_SHRINK
inline void shrinkJsonDocument(JsonDocument& doc) {
doc.shrinkToFit();
}
#endif
template <template <typename> class TDeserializer, typename TDestination,
typename TReader, typename TOptions>
DeserializationError doDeserialize(TDestination&& dst, TReader reader,
TOptions options) {
auto data = VariantAttorney::getOrCreateData(dst);
if (!data)
return DeserializationError::NoMemory;
auto resources = VariantAttorney::getResourceManager(dst);
dst.clear();
auto err = TDeserializer<TReader>(resources, reader)
.parse(*data, options.filter, options.nestingLimit);
shrinkJsonDocument(dst);
return err;
}
template <
template <typename> class TDeserializer, typename TDestination,
typename TStream, typename... Args,
enable_if_t< // issue #1897
!is_integral<typename first_or_void<Args...>::type>::value, int> = 0>
DeserializationError deserialize(TDestination&& dst, TStream&& input,
Args... args) {
return doDeserialize<TDeserializer>(
dst, makeReader(detail::forward<TStream>(input)),
makeDeserializationOptions(args...));
}
template <template <typename> class TDeserializer, typename TDestination,
typename TChar, typename Size, typename... Args,
enable_if_t<is_integral<Size>::value, int> = 0>
DeserializationError deserialize(TDestination&& dst, TChar* input,
Size inputSize, Args... args) {
return doDeserialize<TDeserializer>(dst, makeReader(input, size_t(inputSize)),
makeDeserializationOptions(args...));
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,426 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Array/ElementProxy.hpp>
#include <ArduinoJson/Memory/Allocator.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Object/JsonObject.hpp>
#include <ArduinoJson/Object/MemberProxy.hpp>
#include <ArduinoJson/Polyfills/utility.hpp>
#include <ArduinoJson/Variant/JsonVariantConst.hpp>
#include <ArduinoJson/Variant/VariantTo.hpp>
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// A JSON document.
// https://arduinojson.org/v7/api/jsondocument/
class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
friend class detail::VariantAttorney;
public:
explicit JsonDocument(Allocator* alloc = detail::DefaultAllocator::instance())
: resources_(alloc) {}
// Copy-constructor
JsonDocument(const JsonDocument& src) : JsonDocument(src.allocator()) {
set(src);
}
// Move-constructor
JsonDocument(JsonDocument&& src)
: JsonDocument(detail::DefaultAllocator::instance()) {
swap(*this, src);
}
// Construct from variant, array, or object
template <typename T,
detail::enable_if_t<detail::IsVariant<T>::value ||
detail::is_same<T, JsonArray>::value ||
detail::is_same<T, JsonArrayConst>::value ||
detail::is_same<T, JsonObject>::value ||
detail::is_same<T, JsonObjectConst>::value,
int> = 0>
JsonDocument(const T& src,
Allocator* alloc = detail::DefaultAllocator::instance())
: JsonDocument(alloc) {
set(src);
}
JsonDocument& operator=(JsonDocument src) {
swap(*this, src);
return *this;
}
template <typename T>
JsonDocument& operator=(const T& src) {
set(src);
return *this;
}
Allocator* allocator() const {
return resources_.allocator();
}
// Reduces the capacity of the memory pool to match the current usage.
// https://arduinojson.org/v7/api/jsondocument/shrinktofit/
void shrinkToFit() {
resources_.shrinkToFit();
}
// Casts the root to the specified type.
// https://arduinojson.org/v7/api/jsondocument/as/
template <typename T>
T as() {
return getVariant().template as<T>();
}
// Casts the root to the specified type.
// https://arduinojson.org/v7/api/jsondocument/as/
template <typename T>
T as() const {
return getVariant().template as<T>();
}
// Empties the document and resets the memory pool
// https://arduinojson.org/v7/api/jsondocument/clear/
void clear() {
resources_.clear();
data_.reset();
}
// Returns true if the root is of the specified type.
// https://arduinojson.org/v7/api/jsondocument/is/
template <typename T>
bool is() {
return getVariant().template is<T>();
}
// Returns true if the root is of the specified type.
// https://arduinojson.org/v7/api/jsondocument/is/
template <typename T>
bool is() const {
return getVariant().template is<T>();
}
// Returns true if the root is null.
// https://arduinojson.org/v7/api/jsondocument/isnull/
bool isNull() const {
return getVariant().isNull();
}
// Returns trues if the memory pool was too small.
// https://arduinojson.org/v7/api/jsondocument/overflowed/
bool overflowed() const {
return resources_.overflowed();
}
// Returns the depth (nesting level) of the array.
// https://arduinojson.org/v7/api/jsondocument/nesting/
size_t nesting() const {
return data_.nesting(&resources_);
}
// Returns the number of elements in the root array or object.
// https://arduinojson.org/v7/api/jsondocument/size/
size_t size() const {
return data_.size(&resources_);
}
// Copies the specified document.
// https://arduinojson.org/v7/api/jsondocument/set/
bool set(const JsonDocument& src) {
return to<JsonVariant>().set(src.as<JsonVariantConst>());
}
// Replaces the root with the specified value.
// https://arduinojson.org/v7/api/jsondocument/set/
template <
typename T,
detail::enable_if_t<!detail::is_base_of<JsonDocument, T>::value, int> = 0>
bool set(const T& src) {
return to<JsonVariant>().set(src);
}
// Replaces the root with the specified value.
// https://arduinojson.org/v7/api/jsondocument/set/
template <typename TChar,
detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
bool set(TChar* src) {
return to<JsonVariant>().set(src);
}
// Clears the document and converts it to the specified type.
// https://arduinojson.org/v7/api/jsondocument/to/
template <typename T>
typename detail::VariantTo<T>::type to() {
clear();
return getVariant().template to<T>();
}
// DEPRECATED: use obj["key"].is<T>() instead
// https://arduinojson.org/v7/api/jsondocument/containskey/
template <typename TChar>
ARDUINOJSON_DEPRECATED("use doc[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const {
return data_.getMember(detail::adaptString(key), &resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsondocument/containskey/
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
bool containsKey(const TString& key) const {
return data_.getMember(detail::adaptString(key), &resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsondocument/containskey/
template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
bool containsKey(const TVariant& key) const {
return containsKey(key.template as<const char*>());
}
// Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
detail::MemberProxy<JsonDocument&, detail::AdaptedString<TString>> operator[](
const TString& key) {
return {*this, detail::adaptString(key)};
}
// Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value &&
!detail::is_const<TChar>::value,
int> = 0>
detail::MemberProxy<JsonDocument&, detail::AdaptedString<TChar*>> operator[](
TChar* key) {
return {*this, detail::adaptString(key)};
}
// Gets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
JsonVariantConst operator[](const TString& key) const {
return JsonVariantConst(
data_.getMember(detail::adaptString(key), &resources_), &resources_);
}
// Gets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value &&
!detail::is_const<TChar>::value,
int> = 0>
JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst(
data_.getMember(detail::adaptString(key), &resources_), &resources_);
}
// Gets or sets a root array's element.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
detail::ElementProxy<JsonDocument&> operator[](T index) {
return {*this, size_t(index)};
}
// Gets a root array's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
JsonVariantConst operator[](size_t index) const {
return JsonVariantConst(data_.getElement(index, &resources_), &resources_);
}
// Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
JsonVariantConst operator[](const TVariant& key) const {
if (key.template is<JsonString>())
return operator[](key.template as<JsonString>());
if (key.template is<size_t>())
return operator[](key.template as<size_t>());
return {};
}
// Appends a new (empty) element to the root array.
// Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsondocument/add/
template <typename T, detail::enable_if_t<
!detail::is_same<T, JsonVariant>::value, int> = 0>
T add() {
return add<JsonVariant>().to<T>();
}
// Appends a new (null) element to the root array.
// Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsondocument/add/
template <typename T, detail::enable_if_t<
detail::is_same<T, JsonVariant>::value, int> = 0>
JsonVariant add() {
return JsonVariant(data_.addElement(&resources_), &resources_);
}
// Appends a value to the root array.
// https://arduinojson.org/v7/api/jsondocument/add/
template <typename TValue>
bool add(const TValue& value) {
return data_.addValue(value, &resources_);
}
// Appends a value to the root array.
// https://arduinojson.org/v7/api/jsondocument/add/
template <typename TChar,
detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
bool add(TChar* value) {
return data_.addValue(value, &resources_);
}
// Removes an element of the root array.
// https://arduinojson.org/v7/api/jsondocument/remove/
template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
void remove(T index) {
detail::VariantData::removeElement(getData(), size_t(index),
getResourceManager());
}
// Removes a member of the root object.
// https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value &&
!detail::is_const<TChar>::value,
int> = 0>
void remove(TChar* key) {
detail::VariantData::removeMember(getData(), detail::adaptString(key),
getResourceManager());
}
// Removes a member of the root object.
// https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
void remove(const TString& key) {
detail::VariantData::removeMember(getData(), detail::adaptString(key),
getResourceManager());
}
// Removes a member of the root object or an element of the root array.
// https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
void remove(const TVariant& key) {
if (key.template is<const char*>())
remove(key.template as<const char*>());
if (key.template is<size_t>())
remove(key.template as<size_t>());
}
operator JsonVariant() {
return getVariant();
}
operator JsonVariantConst() const {
return getVariant();
}
friend void swap(JsonDocument& a, JsonDocument& b) {
swap(a.resources_, b.resources_);
swap_(a.data_, b.data_);
}
// DEPRECATED: use add<JsonVariant>() instead
ARDUINOJSON_DEPRECATED("use add<JsonVariant>() instead")
JsonVariant add() {
return add<JsonVariant>();
}
// DEPRECATED: use add<JsonArray>() instead
ARDUINOJSON_DEPRECATED("use add<JsonArray>() instead")
JsonArray createNestedArray() {
return add<JsonArray>();
}
// DEPRECATED: use doc[key].to<JsonArray>() instead
template <typename TChar>
ARDUINOJSON_DEPRECATED("use doc[key].to<JsonArray>() instead")
JsonArray createNestedArray(TChar* key) {
return operator[](key).template to<JsonArray>();
}
// DEPRECATED: use doc[key].to<JsonArray>() instead
template <typename TString>
ARDUINOJSON_DEPRECATED("use doc[key].to<JsonArray>() instead")
JsonArray createNestedArray(const TString& key) {
return operator[](key).template to<JsonArray>();
}
// DEPRECATED: use add<JsonObject>() instead
ARDUINOJSON_DEPRECATED("use add<JsonObject>() instead")
JsonObject createNestedObject() {
return add<JsonObject>();
}
// DEPRECATED: use doc[key].to<JsonObject>() instead
template <typename TChar>
ARDUINOJSON_DEPRECATED("use doc[key].to<JsonObject>() instead")
JsonObject createNestedObject(TChar* key) {
return operator[](key).template to<JsonObject>();
}
// DEPRECATED: use doc[key].to<JsonObject>() instead
template <typename TString>
ARDUINOJSON_DEPRECATED("use doc[key].to<JsonObject>() instead")
JsonObject createNestedObject(const TString& key) {
return operator[](key).template to<JsonObject>();
}
// DEPRECATED: always returns zero
ARDUINOJSON_DEPRECATED("always returns zero")
size_t memoryUsage() const {
return 0;
}
private:
JsonVariant getVariant() {
return JsonVariant(&data_, &resources_);
}
JsonVariantConst getVariant() const {
return JsonVariantConst(&data_, &resources_);
}
detail::ResourceManager* getResourceManager() {
return &resources_;
}
detail::VariantData* getData() {
return &data_;
}
const detail::VariantData* getData() const {
return &data_;
}
detail::VariantData* getOrCreateData() {
return &data_;
}
detail::ResourceManager resources_;
detail::VariantData data_;
};
inline void convertToJson(const JsonDocument& src, JsonVariant dst) {
dst.set(src.as<JsonVariantConst>());
}
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,40 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class EscapeSequence {
public:
// Optimized for code size on a 8-bit AVR
static char escapeChar(char c) {
const char* p = escapeTable(true);
while (p[0] && p[1] != c) {
p += 2;
}
return p[0];
}
// Optimized for code size on a 8-bit AVR
static char unescapeChar(char c) {
const char* p = escapeTable(false);
for (;;) {
if (p[0] == '\0')
return 0;
if (p[0] == c)
return p[1];
p += 2;
}
}
private:
static const char* escapeTable(bool isSerializing) {
return &"//''\"\"\\\\b\bf\fn\nr\rt\t"[isSerializing ? 4 : 0];
}
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,720 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Deserialization/deserialize.hpp>
#include <ArduinoJson/Json/EscapeSequence.hpp>
#include <ArduinoJson/Json/Latch.hpp>
#include <ArduinoJson/Json/Utf16.hpp>
#include <ArduinoJson/Json/Utf8.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Numbers/parseNumber.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Polyfills/utility.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TReader>
class JsonDeserializer {
public:
JsonDeserializer(ResourceManager* resources, TReader reader)
: stringBuilder_(resources),
foundSomething_(false),
latch_(reader),
resources_(resources) {}
template <typename TFilter>
DeserializationError parse(VariantData& variant, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
err = parseVariant(variant, filter, nestingLimit);
if (!err && latch_.last() != 0 && variant.isFloat()) {
// We don't detect trailing characters earlier, so we need to check now
return DeserializationError::InvalidInput;
}
return err;
}
private:
char current() {
return latch_.current();
}
void move() {
latch_.clear();
}
bool eat(char charToSkip) {
if (current() != charToSkip)
return false;
move();
return true;
}
template <typename TFilter>
DeserializationError::Code parseVariant(
VariantData& variant, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
err = skipSpacesAndComments();
if (err)
return err;
switch (current()) {
case '[':
if (filter.allowArray())
return parseArray(variant.toArray(), filter, nestingLimit);
else
return skipArray(nestingLimit);
case '{':
if (filter.allowObject())
return parseObject(variant.toObject(), filter, nestingLimit);
else
return skipObject(nestingLimit);
case '\"':
case '\'':
if (filter.allowValue())
return parseStringValue(variant);
else
return skipQuotedString();
case 't':
if (filter.allowValue())
variant.setBoolean(true);
return skipKeyword("true");
case 'f':
if (filter.allowValue())
variant.setBoolean(false);
return skipKeyword("false");
case 'n':
// the variant should already by null, except if the same object key was
// used twice, as in {"a":1,"a":null}
return skipKeyword("null");
default:
if (filter.allowValue())
return parseNumericValue(variant);
else
return skipNumericValue();
}
}
DeserializationError::Code skipVariant(
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
err = skipSpacesAndComments();
if (err)
return err;
switch (current()) {
case '[':
return skipArray(nestingLimit);
case '{':
return skipObject(nestingLimit);
case '\"':
case '\'':
return skipQuotedString();
case 't':
return skipKeyword("true");
case 'f':
return skipKeyword("false");
case 'n':
return skipKeyword("null");
default:
return skipNumericValue();
}
}
template <typename TFilter>
DeserializationError::Code parseArray(
ArrayData& array, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
if (nestingLimit.reached())
return DeserializationError::TooDeep;
// Skip opening braket
ARDUINOJSON_ASSERT(current() == '[');
move();
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
// Empty array?
if (eat(']'))
return DeserializationError::Ok;
TFilter elementFilter = filter[0UL];
// Read each value
for (;;) {
if (elementFilter.allow()) {
// Allocate slot in array
VariantData* value = array.addElement(resources_);
if (!value)
return DeserializationError::NoMemory;
// 1 - Parse value
err = parseVariant(*value, elementFilter, nestingLimit.decrement());
if (err)
return err;
} else {
err = skipVariant(nestingLimit.decrement());
if (err)
return err;
}
// 2 - Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
// 3 - More values?
if (eat(']'))
return DeserializationError::Ok;
if (!eat(','))
return DeserializationError::InvalidInput;
}
}
DeserializationError::Code skipArray(
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
if (nestingLimit.reached())
return DeserializationError::TooDeep;
// Skip opening braket
ARDUINOJSON_ASSERT(current() == '[');
move();
// Read each value
for (;;) {
// 1 - Skip value
err = skipVariant(nestingLimit.decrement());
if (err)
return err;
// 2 - Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
// 3 - More values?
if (eat(']'))
return DeserializationError::Ok;
if (!eat(','))
return DeserializationError::InvalidInput;
}
}
template <typename TFilter>
DeserializationError::Code parseObject(
ObjectData& object, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
if (nestingLimit.reached())
return DeserializationError::TooDeep;
// Skip opening brace
ARDUINOJSON_ASSERT(current() == '{');
move();
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
// Empty object?
if (eat('}'))
return DeserializationError::Ok;
// Read each key value pair
for (;;) {
// Parse key
err = parseKey();
if (err)
return err;
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
// Colon
if (!eat(':'))
return DeserializationError::InvalidInput;
JsonString key = stringBuilder_.str();
TFilter memberFilter = filter[key];
if (memberFilter.allow()) {
auto member = object.getMember(adaptString(key), resources_);
if (!member) {
auto keyVariant = object.addPair(&member, resources_);
if (!keyVariant)
return DeserializationError::NoMemory;
stringBuilder_.save(keyVariant);
} else {
member->clear(resources_);
}
// Parse value
err = parseVariant(*member, memberFilter, nestingLimit.decrement());
if (err)
return err;
} else {
err = skipVariant(nestingLimit.decrement());
if (err)
return err;
}
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
// More keys/values?
if (eat('}'))
return DeserializationError::Ok;
if (!eat(','))
return DeserializationError::InvalidInput;
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
}
}
DeserializationError::Code skipObject(
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
if (nestingLimit.reached())
return DeserializationError::TooDeep;
// Skip opening brace
ARDUINOJSON_ASSERT(current() == '{');
move();
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
// Empty object?
if (eat('}'))
return DeserializationError::Ok;
// Read each key value pair
for (;;) {
// Skip key
err = skipKey();
if (err)
return err;
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
// Colon
if (!eat(':'))
return DeserializationError::InvalidInput;
// Skip value
err = skipVariant(nestingLimit.decrement());
if (err)
return err;
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
// More keys/values?
if (eat('}'))
return DeserializationError::Ok;
if (!eat(','))
return DeserializationError::InvalidInput;
err = skipSpacesAndComments();
if (err)
return err;
}
}
DeserializationError::Code parseKey() {
stringBuilder_.startString();
if (isQuote(current())) {
return parseQuotedString();
} else {
return parseNonQuotedString();
}
}
DeserializationError::Code parseStringValue(VariantData& variant) {
DeserializationError::Code err;
stringBuilder_.startString();
err = parseQuotedString();
if (err)
return err;
stringBuilder_.save(&variant);
return DeserializationError::Ok;
}
DeserializationError::Code parseQuotedString() {
#if ARDUINOJSON_DECODE_UNICODE
Utf16::Codepoint codepoint;
DeserializationError::Code err;
#endif
const char stopChar = current();
move();
for (;;) {
char c = current();
move();
if (c == stopChar)
break;
if (c == '\0')
return DeserializationError::IncompleteInput;
if (c == '\\') {
c = current();
if (c == '\0')
return DeserializationError::IncompleteInput;
if (c == 'u') {
#if ARDUINOJSON_DECODE_UNICODE
move();
uint16_t codeunit;
err = parseHex4(codeunit);
if (err)
return err;
if (codepoint.append(codeunit))
Utf8::encodeCodepoint(codepoint.value(), stringBuilder_);
#else
stringBuilder_.append('\\');
#endif
continue;
}
// replace char
c = EscapeSequence::unescapeChar(c);
if (c == '\0')
return DeserializationError::InvalidInput;
move();
}
stringBuilder_.append(c);
}
if (!stringBuilder_.isValid())
return DeserializationError::NoMemory;
return DeserializationError::Ok;
}
DeserializationError::Code parseNonQuotedString() {
char c = current();
ARDUINOJSON_ASSERT(c);
if (canBeInNonQuotedString(c)) { // no quotes
do {
move();
stringBuilder_.append(c);
c = current();
} while (canBeInNonQuotedString(c));
} else {
return DeserializationError::InvalidInput;
}
if (!stringBuilder_.isValid())
return DeserializationError::NoMemory;
return DeserializationError::Ok;
}
DeserializationError::Code skipKey() {
if (isQuote(current())) {
return skipQuotedString();
} else {
return skipNonQuotedString();
}
}
DeserializationError::Code skipQuotedString() {
const char stopChar = current();
move();
for (;;) {
char c = current();
move();
if (c == stopChar)
break;
if (c == '\0')
return DeserializationError::IncompleteInput;
if (c == '\\') {
if (current() != '\0')
move();
}
}
return DeserializationError::Ok;
}
DeserializationError::Code skipNonQuotedString() {
char c = current();
while (canBeInNonQuotedString(c)) {
move();
c = current();
}
return DeserializationError::Ok;
}
DeserializationError::Code parseNumericValue(VariantData& result) {
uint8_t n = 0;
char c = current();
while (canBeInNumber(c) && n < 63) {
move();
buffer_[n++] = c;
c = current();
}
buffer_[n] = 0;
auto number = parseNumber(buffer_);
switch (number.type()) {
case NumberType::UnsignedInteger:
if (result.setInteger(number.asUnsignedInteger(), resources_))
return DeserializationError::Ok;
else
return DeserializationError::NoMemory;
case NumberType::SignedInteger:
if (result.setInteger(number.asSignedInteger(), resources_))
return DeserializationError::Ok;
else
return DeserializationError::NoMemory;
case NumberType::Float:
if (result.setFloat(number.asFloat(), resources_))
return DeserializationError::Ok;
else
return DeserializationError::NoMemory;
#if ARDUINOJSON_USE_DOUBLE
case NumberType::Double:
if (result.setFloat(number.asDouble(), resources_))
return DeserializationError::Ok;
else
return DeserializationError::NoMemory;
#endif
default:
return DeserializationError::InvalidInput;
}
}
DeserializationError::Code skipNumericValue() {
char c = current();
while (canBeInNumber(c)) {
move();
c = current();
}
return DeserializationError::Ok;
}
DeserializationError::Code parseHex4(uint16_t& result) {
result = 0;
for (uint8_t i = 0; i < 4; ++i) {
char digit = current();
if (!digit)
return DeserializationError::IncompleteInput;
uint8_t value = decodeHex(digit);
if (value > 0x0F)
return DeserializationError::InvalidInput;
result = uint16_t((result << 4) | value);
move();
}
return DeserializationError::Ok;
}
static inline bool isBetween(char c, char min, char max) {
return min <= c && c <= max;
}
static inline bool canBeInNumber(char c) {
return isBetween(c, '0', '9') || c == '+' || c == '-' || c == '.' ||
#if ARDUINOJSON_ENABLE_NAN || ARDUINOJSON_ENABLE_INFINITY
isBetween(c, 'A', 'Z') || isBetween(c, 'a', 'z');
#else
c == 'e' || c == 'E';
#endif
}
static inline bool canBeInNonQuotedString(char c) {
return isBetween(c, '0', '9') || isBetween(c, '_', 'z') ||
isBetween(c, 'A', 'Z');
}
static inline bool isQuote(char c) {
return c == '\'' || c == '\"';
}
static inline uint8_t decodeHex(char c) {
if (c < 'A')
return uint8_t(c - '0');
c = char(c & ~0x20); // uppercase
return uint8_t(c - 'A' + 10);
}
DeserializationError::Code skipSpacesAndComments() {
for (;;) {
switch (current()) {
// end of string
case '\0':
return foundSomething_ ? DeserializationError::IncompleteInput
: DeserializationError::EmptyInput;
// spaces
case ' ':
case '\t':
case '\r':
case '\n':
move();
continue;
#if ARDUINOJSON_ENABLE_COMMENTS
// comments
case '/':
move(); // skip '/'
switch (current()) {
// block comment
case '*': {
move(); // skip '*'
bool wasStar = false;
for (;;) {
char c = current();
if (c == '\0')
return DeserializationError::IncompleteInput;
if (c == '/' && wasStar) {
move();
break;
}
wasStar = c == '*';
move();
}
break;
}
// trailing comment
case '/':
// no need to skip "//"
for (;;) {
move();
char c = current();
if (c == '\0')
return DeserializationError::IncompleteInput;
if (c == '\n')
break;
}
break;
// not a comment, just a '/'
default:
return DeserializationError::InvalidInput;
}
break;
#endif
default:
foundSomething_ = true;
return DeserializationError::Ok;
}
}
}
DeserializationError::Code skipKeyword(const char* s) {
while (*s) {
char c = current();
if (c == '\0')
return DeserializationError::IncompleteInput;
if (*s != c)
return DeserializationError::InvalidInput;
++s;
move();
}
return DeserializationError::Ok;
}
StringBuilder stringBuilder_;
bool foundSomething_;
Latch<TReader> latch_;
ResourceManager* resources_;
char buffer_[64]; // using a member instead of a local variable because it
// ended in the recursive path after compiler inlined the
// code
};
ARDUINOJSON_END_PRIVATE_NAMESPACE
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Parses a JSON input, filters, and puts the result in a JsonDocument.
// https://arduinojson.org/v7/api/json/deserializejson/
template <typename TDestination, typename... Args,
detail::enable_if_t<
detail::is_deserialize_destination<TDestination>::value, int> = 0>
inline DeserializationError deserializeJson(TDestination&& dst,
Args&&... args) {
using namespace detail;
return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst),
detail::forward<Args>(args)...);
}
// Parses a JSON input, filters, and puts the result in a JsonDocument.
// https://arduinojson.org/v7/api/json/deserializejson/
template <typename TDestination, typename TChar, typename... Args,
detail::enable_if_t<
detail::is_deserialize_destination<TDestination>::value, int> = 0>
inline DeserializationError deserializeJson(TDestination&& dst, TChar* input,
Args&&... args) {
using namespace detail;
return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst),
input, detail::forward<Args>(args)...);
}
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,165 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Json/TextFormatter.hpp>
#include <ArduinoJson/Serialization/measure.hpp>
#include <ArduinoJson/Serialization/serialize.hpp>
#include <ArduinoJson/Variant/VariantDataVisitor.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TWriter>
class JsonSerializer : public VariantDataVisitor<size_t> {
public:
static const bool producesText = true;
JsonSerializer(TWriter writer, const ResourceManager* resources)
: formatter_(writer), resources_(resources) {}
size_t visit(const ArrayData& array) {
write('[');
auto slotId = array.head();
while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId);
slot->accept(*this, resources_);
slotId = slot->next();
if (slotId != NULL_SLOT)
write(',');
}
write(']');
return bytesWritten();
}
size_t visit(const ObjectData& object) {
write('{');
auto slotId = object.head();
bool isKey = true;
while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId);
slot->accept(*this, resources_);
slotId = slot->next();
if (slotId != NULL_SLOT)
write(isKey ? ':' : ',');
isKey = !isKey;
}
write('}');
return bytesWritten();
}
template <typename T>
enable_if_t<is_floating_point<T>::value, size_t> visit(T value) {
formatter_.writeFloat(value);
return bytesWritten();
}
size_t visit(const char* value) {
formatter_.writeString(value);
return bytesWritten();
}
size_t visit(JsonString value) {
formatter_.writeString(value.c_str(), value.size());
return bytesWritten();
}
size_t visit(RawString value) {
formatter_.writeRaw(value.data(), value.size());
return bytesWritten();
}
size_t visit(JsonInteger value) {
formatter_.writeInteger(value);
return bytesWritten();
}
size_t visit(JsonUInt value) {
formatter_.writeInteger(value);
return bytesWritten();
}
size_t visit(bool value) {
formatter_.writeBoolean(value);
return bytesWritten();
}
size_t visit(nullptr_t) {
formatter_.writeRaw("null");
return bytesWritten();
}
protected:
size_t bytesWritten() const {
return formatter_.bytesWritten();
}
void write(char c) {
formatter_.writeRaw(c);
}
void write(const char* s) {
formatter_.writeRaw(s);
}
private:
TextFormatter<TWriter> formatter_;
protected:
const ResourceManager* resources_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Produces a minified JSON document.
// https://arduinojson.org/v7/api/json/serializejson/
template <
typename TDestination,
detail::enable_if_t<!detail::is_pointer<TDestination>::value, int> = 0>
size_t serializeJson(JsonVariantConst source, TDestination& destination) {
using namespace detail;
return serialize<JsonSerializer>(source, destination);
}
// Produces a minified JSON document.
// https://arduinojson.org/v7/api/json/serializejson/
inline size_t serializeJson(JsonVariantConst source, void* buffer,
size_t bufferSize) {
using namespace detail;
return serialize<JsonSerializer>(source, buffer, bufferSize);
}
// Computes the length of the document that serializeJson() produces.
// https://arduinojson.org/v7/api/json/measurejson/
inline size_t measureJson(JsonVariantConst source) {
using namespace detail;
return measure<JsonSerializer>(source);
}
#if ARDUINOJSON_ENABLE_STD_STREAM
template <typename T,
detail::enable_if_t<
detail::is_convertible<T, JsonVariantConst>::value, int> = 0>
inline std::ostream& operator<<(std::ostream& os, const T& source) {
serializeJson(source, os);
return os;
}
#endif
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,56 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Polyfills/assert.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TReader>
class Latch {
public:
Latch(TReader reader) : reader_(reader), loaded_(false) {
#if ARDUINOJSON_DEBUG
ended_ = false;
#endif
}
void clear() {
loaded_ = false;
}
int last() const {
return current_;
}
FORCE_INLINE char current() {
if (!loaded_) {
load();
}
return current_;
}
private:
void load() {
ARDUINOJSON_ASSERT(!ended_);
int c = reader_.read();
#if ARDUINOJSON_DEBUG
if (c <= 0)
ended_ = true;
#endif
current_ = static_cast<char>(c > 0 ? c : 0);
loaded_ = true;
}
TReader reader_;
char current_; // NOLINT(clang-analyzer-optin.cplusplus.UninitializedObject)
// Not initialized in constructor (+10 bytes on AVR)
bool loaded_;
#if ARDUINOJSON_DEBUG
bool ended_;
#endif
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,110 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Configuration.hpp>
#include <ArduinoJson/Json/JsonSerializer.hpp>
#include <ArduinoJson/Serialization/measure.hpp>
#include <ArduinoJson/Serialization/serialize.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TWriter>
class PrettyJsonSerializer : public JsonSerializer<TWriter> {
using base = JsonSerializer<TWriter>;
public:
PrettyJsonSerializer(TWriter writer, const ResourceManager* resources)
: base(writer, resources), nesting_(0) {}
size_t visit(const ArrayData& array) {
auto it = array.createIterator(base::resources_);
if (!it.done()) {
base::write("[\r\n");
nesting_++;
while (!it.done()) {
indent();
it->accept(*this, base::resources_);
it.next(base::resources_);
base::write(it.done() ? "\r\n" : ",\r\n");
}
nesting_--;
indent();
base::write("]");
} else {
base::write("[]");
}
return this->bytesWritten();
}
size_t visit(const ObjectData& object) {
auto it = object.createIterator(base::resources_);
if (!it.done()) {
base::write("{\r\n");
nesting_++;
bool isKey = true;
while (!it.done()) {
if (isKey)
indent();
it->accept(*this, base::resources_);
it.next(base::resources_);
if (isKey)
base::write(": ");
else
base::write(it.done() ? "\r\n" : ",\r\n");
isKey = !isKey;
}
nesting_--;
indent();
base::write("}");
} else {
base::write("{}");
}
return this->bytesWritten();
}
using base::visit;
private:
void indent() {
for (uint8_t i = 0; i < nesting_; i++)
base::write(ARDUINOJSON_TAB);
}
uint8_t nesting_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Produces JsonDocument to create a prettified JSON document.
// https://arduinojson.org/v7/api/json/serializejsonpretty/
template <
typename TDestination,
detail::enable_if_t<!detail::is_pointer<TDestination>::value, int> = 0>
inline size_t serializeJsonPretty(JsonVariantConst source,
TDestination& destination) {
using namespace ArduinoJson::detail;
return serialize<PrettyJsonSerializer>(source, destination);
}
// Produces JsonDocument to create a prettified JSON document.
// https://arduinojson.org/v7/api/json/serializejsonpretty/
inline size_t serializeJsonPretty(JsonVariantConst source, void* buffer,
size_t bufferSize) {
using namespace ArduinoJson::detail;
return serialize<PrettyJsonSerializer>(source, buffer, bufferSize);
}
// Computes the length of the document that serializeJsonPretty() produces.
// https://arduinojson.org/v7/api/json/measurejsonpretty/
inline size_t measureJsonPretty(JsonVariantConst source) {
using namespace ArduinoJson::detail;
return measure<PrettyJsonSerializer>(source);
}
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,177 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <stdint.h>
#include <string.h> // for strlen
#include <ArduinoJson/Json/EscapeSequence.hpp>
#include <ArduinoJson/Numbers/FloatParts.hpp>
#include <ArduinoJson/Numbers/JsonInteger.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/attributes.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Serialization/CountingDecorator.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TWriter>
class TextFormatter {
public:
explicit TextFormatter(TWriter writer) : writer_(writer) {}
TextFormatter& operator=(const TextFormatter&) = delete;
// Returns the number of bytes sent to the TWriter implementation.
size_t bytesWritten() const {
return writer_.count();
}
void writeBoolean(bool value) {
if (value)
writeRaw("true");
else
writeRaw("false");
}
void writeString(const char* value) {
ARDUINOJSON_ASSERT(value != NULL);
writeRaw('\"');
while (*value)
writeChar(*value++);
writeRaw('\"');
}
void writeString(const char* value, size_t n) {
ARDUINOJSON_ASSERT(value != NULL);
writeRaw('\"');
while (n--)
writeChar(*value++);
writeRaw('\"');
}
void writeChar(char c) {
char specialChar = EscapeSequence::escapeChar(c);
if (specialChar) {
writeRaw('\\');
writeRaw(specialChar);
} else if (c) {
writeRaw(c);
} else {
writeRaw("\\u0000");
}
}
template <typename T>
void writeFloat(T value) {
writeFloat(JsonFloat(value), sizeof(T) >= 8 ? 9 : 6);
}
void writeFloat(JsonFloat value, int8_t decimalPlaces) {
if (isnan(value))
return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");
#if ARDUINOJSON_ENABLE_INFINITY
if (value < 0.0) {
writeRaw('-');
value = -value;
}
if (isinf(value))
return writeRaw("Infinity");
#else
if (isinf(value))
return writeRaw("null");
if (value < 0.0) {
writeRaw('-');
value = -value;
}
#endif
auto parts = decomposeFloat(value, decimalPlaces);
writeInteger(parts.integral);
if (parts.decimalPlaces)
writeDecimals(parts.decimal, parts.decimalPlaces);
if (parts.exponent) {
writeRaw('e');
writeInteger(parts.exponent);
}
}
template <typename T>
enable_if_t<is_signed<T>::value> writeInteger(T value) {
using unsigned_type = make_unsigned_t<T>;
unsigned_type unsigned_value;
if (value < 0) {
writeRaw('-');
unsigned_value = unsigned_type(unsigned_type(~value) + 1);
} else {
unsigned_value = unsigned_type(value);
}
writeInteger(unsigned_value);
}
template <typename T>
enable_if_t<is_unsigned<T>::value> writeInteger(T value) {
char buffer[22];
char* end = buffer + sizeof(buffer);
char* begin = end;
// write the string in reverse order
do {
*--begin = char(value % 10 + '0');
value = T(value / 10);
} while (value);
// and dump it in the right order
writeRaw(begin, end);
}
void writeDecimals(uint32_t value, int8_t width) {
// buffer should be big enough for all digits and the dot
char buffer[16];
char* end = buffer + sizeof(buffer);
char* begin = end;
// write the string in reverse order
while (width--) {
*--begin = char(value % 10 + '0');
value /= 10;
}
*--begin = '.';
// and dump it in the right order
writeRaw(begin, end);
}
void writeRaw(const char* s) {
writer_.write(reinterpret_cast<const uint8_t*>(s), strlen(s));
}
void writeRaw(const char* s, size_t n) {
writer_.write(reinterpret_cast<const uint8_t*>(s), n);
}
void writeRaw(const char* begin, const char* end) {
writer_.write(reinterpret_cast<const uint8_t*>(begin),
static_cast<size_t>(end - begin));
}
template <size_t N>
void writeRaw(const char (&s)[N]) {
writer_.write(reinterpret_cast<const uint8_t*>(s), N - 1);
}
void writeRaw(char c) {
writer_.write(static_cast<uint8_t>(c));
}
protected:
CountingDecorator<TWriter> writer_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,67 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <stdint.h> // uint16_t, uint32_t
// The high surrogate may be uninitialized if the pair is invalid,
// we choose to ignore the problem to reduce the size of the code
// Garbage in => Garbage out
#if defined(__GNUC__)
# if __GNUC__ >= 7
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
# endif
#endif
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
namespace Utf16 {
inline bool isHighSurrogate(uint16_t codeunit) {
return codeunit >= 0xD800 && codeunit < 0xDC00;
}
inline bool isLowSurrogate(uint16_t codeunit) {
return codeunit >= 0xDC00 && codeunit < 0xE000;
}
class Codepoint {
public:
Codepoint() : highSurrogate_(0), codepoint_(0) {}
bool append(uint16_t codeunit) {
if (isHighSurrogate(codeunit)) {
highSurrogate_ = codeunit & 0x3FF;
return false;
}
if (isLowSurrogate(codeunit)) {
codepoint_ =
uint32_t(0x10000 + ((highSurrogate_ << 10) | (codeunit & 0x3FF)));
return true;
}
codepoint_ = codeunit;
return true;
}
uint32_t value() const {
return codepoint_;
}
private:
uint16_t highSurrogate_;
uint32_t codepoint_;
};
} // namespace Utf16
ARDUINOJSON_END_PRIVATE_NAMESPACE
#if defined(__GNUC__)
# if __GNUC__ >= 8
# pragma GCC diagnostic pop
# endif
#endif

View File

@@ -0,0 +1,46 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
namespace Utf8 {
template <typename TStringBuilder>
inline void encodeCodepoint(uint32_t codepoint32, TStringBuilder& str) {
// this function was optimize for code size on AVR
if (codepoint32 < 0x80) {
str.append(char(codepoint32));
} else {
// a buffer to store the string in reverse
char buf[5];
char* p = buf;
*(p++) = 0;
*(p++) = char((codepoint32 | 0x80) & 0xBF);
uint16_t codepoint16 = uint16_t(codepoint32 >> 6);
if (codepoint16 < 0x20) { // 0x800
*(p++) = char(codepoint16 | 0xC0);
} else {
*(p++) = char((codepoint16 | 0x80) & 0xBF);
codepoint16 = uint16_t(codepoint16 >> 6);
if (codepoint16 < 0x10) { // 0x10000
*(p++) = char(codepoint16 | 0xE0);
} else {
*(p++) = char((codepoint16 | 0x80) & 0xBF);
codepoint16 = uint16_t(codepoint16 >> 6);
*(p++) = char(codepoint16 | 0xF0);
}
}
while (*(--p)) {
str.append(*p);
}
}
}
} // namespace Utf8
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,60 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <stddef.h> // size_t
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
#if ARDUINOJSON_ENABLE_ALIGNMENT
inline bool isAligned(size_t value) {
const size_t mask = sizeof(void*) - 1;
size_t addr = value;
return (addr & mask) == 0;
}
inline size_t addPadding(size_t bytes) {
const size_t mask = sizeof(void*) - 1;
return (bytes + mask) & ~mask;
}
template <size_t bytes>
struct AddPadding {
static const size_t mask = sizeof(void*) - 1;
static const size_t value = (bytes + mask) & ~mask;
};
#else
inline bool isAligned(size_t) {
return true;
}
inline size_t addPadding(size_t bytes) {
return bytes;
}
template <size_t bytes>
struct AddPadding {
static const size_t value = bytes;
};
#endif
template <typename T>
inline bool isAligned(T* ptr) {
return isAligned(reinterpret_cast<size_t>(ptr));
}
template <typename T>
inline T* addPadding(T* p) {
size_t address = addPadding(reinterpret_cast<size_t>(p));
return reinterpret_cast<T*>(address);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,49 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <stdlib.h> // malloc, free
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
class Allocator {
public:
virtual void* allocate(size_t size) = 0;
virtual void deallocate(void* ptr) = 0;
virtual void* reallocate(void* ptr, size_t new_size) = 0;
protected:
~Allocator() = default;
};
namespace detail {
class DefaultAllocator : public Allocator {
public:
void* allocate(size_t size) override {
return malloc(size);
}
void deallocate(void* ptr) override {
free(ptr);
}
void* reallocate(void* ptr, size_t new_size) override {
return realloc(ptr, new_size);
}
static Allocator* instance() {
static DefaultAllocator allocator;
return &allocator;
}
private:
DefaultAllocator() = default;
~DefaultAllocator() = default;
};
} // namespace detail
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,110 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/Allocator.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/integer.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
using SlotId = uint_t<ARDUINOJSON_SLOT_ID_SIZE * 8>;
using SlotCount = SlotId;
const SlotId NULL_SLOT = SlotId(-1);
template <typename T>
class Slot {
public:
Slot() : ptr_(nullptr), id_(NULL_SLOT) {}
Slot(T* p, SlotId id) : ptr_(p), id_(id) {
ARDUINOJSON_ASSERT((p == nullptr) == (id == NULL_SLOT));
}
explicit operator bool() const {
return ptr_ != nullptr;
}
SlotId id() const {
return id_;
}
T* ptr() const {
return ptr_;
}
T* operator->() const {
ARDUINOJSON_ASSERT(ptr_ != nullptr);
return ptr_;
}
private:
T* ptr_;
SlotId id_;
};
template <typename T>
class MemoryPool {
public:
void create(SlotCount cap, Allocator* allocator) {
ARDUINOJSON_ASSERT(cap > 0);
slots_ = reinterpret_cast<T*>(allocator->allocate(slotsToBytes(cap)));
capacity_ = slots_ ? cap : 0;
usage_ = 0;
}
void destroy(Allocator* allocator) {
if (slots_)
allocator->deallocate(slots_);
slots_ = nullptr;
capacity_ = 0;
usage_ = 0;
}
Slot<T> allocSlot() {
if (!slots_)
return {};
if (usage_ >= capacity_)
return {};
auto index = usage_++;
return {slots_ + index, SlotId(index)};
}
T* getSlot(SlotId id) const {
ARDUINOJSON_ASSERT(id < usage_);
return slots_ + id;
}
void clear() {
usage_ = 0;
}
void shrinkToFit(Allocator* allocator) {
auto newSlots = reinterpret_cast<T*>(
allocator->reallocate(slots_, slotsToBytes(usage_)));
if (newSlots) {
slots_ = newSlots;
capacity_ = usage_;
}
}
SlotCount usage() const {
return usage_;
}
static SlotCount bytesToSlots(size_t n) {
return static_cast<SlotCount>(n / sizeof(T));
}
static size_t slotsToBytes(SlotCount n) {
return n * sizeof(T);
}
private:
SlotCount capacity_;
SlotCount usage_;
T* slots_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,214 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/utility.hpp>
#include <string.h> // memcpy
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
using PoolCount = SlotId;
template <typename T>
class MemoryPoolList {
struct FreeSlot {
SlotId next;
};
static_assert(sizeof(FreeSlot) <= sizeof(T), "T is too small");
public:
using Pool = MemoryPool<T>;
MemoryPoolList() = default;
~MemoryPoolList() {
ARDUINOJSON_ASSERT(count_ == 0);
}
friend void swap(MemoryPoolList& a, MemoryPoolList& b) {
bool aUsedPreallocated = a.pools_ == a.preallocatedPools_;
bool bUsedPreallocated = b.pools_ == b.preallocatedPools_;
// Who is using preallocated pools?
if (aUsedPreallocated && bUsedPreallocated) {
// both of us => swap preallocated pools
for (PoolCount i = 0; i < ARDUINOJSON_INITIAL_POOL_COUNT; i++)
swap_(a.preallocatedPools_[i], b.preallocatedPools_[i]);
} else if (bUsedPreallocated) {
// only b => copy b's preallocated pools and give him a's pointer
for (PoolCount i = 0; i < b.count_; i++)
a.preallocatedPools_[i] = b.preallocatedPools_[i];
b.pools_ = a.pools_;
a.pools_ = a.preallocatedPools_;
} else if (aUsedPreallocated) {
// only a => copy a's preallocated pools and give him b's pointer
for (PoolCount i = 0; i < a.count_; i++)
b.preallocatedPools_[i] = a.preallocatedPools_[i];
a.pools_ = b.pools_;
b.pools_ = b.preallocatedPools_;
} else {
// neither => swap pointers
swap_(a.pools_, b.pools_);
}
swap_(a.count_, b.count_);
swap_(a.capacity_, b.capacity_);
swap_(a.freeList_, b.freeList_);
}
MemoryPoolList& operator=(MemoryPoolList&& src) {
ARDUINOJSON_ASSERT(count_ == 0);
if (src.pools_ == src.preallocatedPools_) {
memcpy(preallocatedPools_, src.preallocatedPools_,
sizeof(preallocatedPools_));
pools_ = preallocatedPools_;
} else {
pools_ = src.pools_;
src.pools_ = nullptr;
}
count_ = src.count_;
capacity_ = src.capacity_;
src.count_ = 0;
src.capacity_ = 0;
return *this;
}
Slot<T> allocSlot(Allocator* allocator) {
// try to allocate from free list
if (freeList_ != NULL_SLOT) {
return allocFromFreeList();
}
// try to allocate from last pool (other pools are full)
if (count_) {
auto slot = allocFromLastPool();
if (slot)
return slot;
}
// create a new pool and try again
auto pool = addPool(allocator);
if (!pool)
return {};
return allocFromLastPool();
}
void freeSlot(Slot<T> slot) {
reinterpret_cast<FreeSlot*>(slot.ptr())->next = freeList_;
freeList_ = slot.id();
}
T* getSlot(SlotId id) const {
if (id == NULL_SLOT)
return nullptr;
auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY);
auto indexInPool = SlotId(id % ARDUINOJSON_POOL_CAPACITY);
ARDUINOJSON_ASSERT(poolIndex < count_);
return pools_[poolIndex].getSlot(indexInPool);
}
void clear(Allocator* allocator) {
for (PoolCount i = 0; i < count_; i++)
pools_[i].destroy(allocator);
count_ = 0;
freeList_ = NULL_SLOT;
if (pools_ != preallocatedPools_) {
allocator->deallocate(pools_);
pools_ = preallocatedPools_;
capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
}
}
SlotCount usage() const {
SlotCount total = 0;
for (PoolCount i = 0; i < count_; i++)
total = SlotCount(total + pools_[i].usage());
return total;
}
size_t size() const {
return Pool::slotsToBytes(usage());
}
void shrinkToFit(Allocator* allocator) {
if (count_ > 0)
pools_[count_ - 1].shrinkToFit(allocator);
if (pools_ != preallocatedPools_ && count_ != capacity_) {
pools_ = static_cast<Pool*>(
allocator->reallocate(pools_, count_ * sizeof(Pool)));
ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail
capacity_ = count_;
}
}
private:
Slot<T> allocFromFreeList() {
ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT);
auto id = freeList_;
auto slot = getSlot(freeList_);
freeList_ = reinterpret_cast<FreeSlot*>(slot)->next;
return {slot, id};
}
Slot<T> allocFromLastPool() {
ARDUINOJSON_ASSERT(count_ > 0);
auto poolIndex = SlotId(count_ - 1);
auto slot = pools_[poolIndex].allocSlot();
if (!slot)
return {};
return {slot.ptr(),
SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())};
}
Pool* addPool(Allocator* allocator) {
if (count_ == capacity_ && !increaseCapacity(allocator))
return nullptr;
auto pool = &pools_[count_++];
SlotCount poolCapacity = ARDUINOJSON_POOL_CAPACITY;
if (count_ == maxPools) // last pool is smaller because of NULL_SLOT
poolCapacity--;
pool->create(poolCapacity, allocator);
return pool;
}
bool increaseCapacity(Allocator* allocator) {
if (capacity_ == maxPools)
return false;
void* newPools;
auto newCapacity = PoolCount(capacity_ * 2);
if (pools_ == preallocatedPools_) {
newPools = allocator->allocate(newCapacity * sizeof(Pool));
if (!newPools)
return false;
memcpy(newPools, preallocatedPools_, sizeof(preallocatedPools_));
} else {
newPools = allocator->reallocate(pools_, newCapacity * sizeof(Pool));
if (!newPools)
return false;
}
pools_ = static_cast<Pool*>(newPools);
capacity_ = newCapacity;
return true;
}
Pool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT];
Pool* pools_ = preallocatedPools_;
PoolCount count_ = 0;
PoolCount capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
SlotId freeList_ = NULL_SLOT;
public:
static const PoolCount maxPools =
PoolCount(NULL_SLOT / ARDUINOJSON_POOL_CAPACITY + 1);
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,131 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/Allocator.hpp>
#include <ArduinoJson/Memory/MemoryPoolList.hpp>
#include <ArduinoJson/Memory/StringPool.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/utility.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantData;
class VariantWithId;
class ResourceManager {
union SlotData {
VariantData variant;
#if ARDUINOJSON_USE_EXTENSIONS
VariantExtension extension;
#endif
};
public:
constexpr static size_t slotSize = sizeof(SlotData);
ResourceManager(Allocator* allocator = DefaultAllocator::instance())
: allocator_(allocator), overflowed_(false) {}
~ResourceManager() {
stringPool_.clear(allocator_);
variantPools_.clear(allocator_);
}
ResourceManager(const ResourceManager&) = delete;
ResourceManager& operator=(const ResourceManager& src) = delete;
friend void swap(ResourceManager& a, ResourceManager& b) {
swap(a.stringPool_, b.stringPool_);
swap(a.variantPools_, b.variantPools_);
swap_(a.allocator_, b.allocator_);
swap_(a.overflowed_, b.overflowed_);
}
Allocator* allocator() const {
return allocator_;
}
size_t size() const {
return variantPools_.size() + stringPool_.size();
}
bool overflowed() const {
return overflowed_;
}
Slot<VariantData> allocVariant();
void freeVariant(Slot<VariantData> slot);
VariantData* getVariant(SlotId id) const;
#if ARDUINOJSON_USE_EXTENSIONS
Slot<VariantExtension> allocExtension();
void freeExtension(SlotId slot);
VariantExtension* getExtension(SlotId id) const;
#endif
template <typename TAdaptedString>
StringNode* saveString(TAdaptedString str) {
if (str.isNull())
return 0;
auto node = stringPool_.add(str, allocator_);
if (!node)
overflowed_ = true;
return node;
}
void saveString(StringNode* node) {
stringPool_.add(node);
}
template <typename TAdaptedString>
StringNode* getString(const TAdaptedString& str) const {
return stringPool_.get(str);
}
StringNode* createString(size_t length) {
auto node = StringNode::create(length, allocator_);
if (!node)
overflowed_ = true;
return node;
}
StringNode* resizeString(StringNode* node, size_t length) {
node = StringNode::resize(node, length, allocator_);
if (!node)
overflowed_ = true;
return node;
}
void destroyString(StringNode* node) {
StringNode::destroy(node, allocator_);
}
void dereferenceString(const char* s) {
stringPool_.dereference(s, allocator_);
}
void clear() {
variantPools_.clear(allocator_);
overflowed_ = false;
stringPool_.clear(allocator_);
}
void shrinkToFit() {
variantPools_.shrinkToFit(allocator_);
}
private:
Allocator* allocator_;
bool overflowed_;
StringPool stringPool_;
MemoryPoolList<SlotData> variantPools_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,52 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Collection/CollectionData.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Polyfills/alias_cast.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline Slot<VariantData> ResourceManager::allocVariant() {
auto p = variantPools_.allocSlot(allocator_);
if (!p) {
overflowed_ = true;
return {};
}
return {new (&p->variant) VariantData, p.id()};
}
inline void ResourceManager::freeVariant(Slot<VariantData> variant) {
variant->clear(this);
variantPools_.freeSlot({alias_cast<SlotData*>(variant.ptr()), variant.id()});
}
inline VariantData* ResourceManager::getVariant(SlotId id) const {
return reinterpret_cast<VariantData*>(variantPools_.getSlot(id));
}
#if ARDUINOJSON_USE_EXTENSIONS
inline Slot<VariantExtension> ResourceManager::allocExtension() {
auto p = variantPools_.allocSlot(allocator_);
if (!p) {
overflowed_ = true;
return {};
}
return {&p->extension, p.id()};
}
inline void ResourceManager::freeExtension(SlotId id) {
auto p = getExtension(id);
variantPools_.freeSlot({reinterpret_cast<SlotData*>(p), id});
}
inline VariantExtension* ResourceManager::getExtension(SlotId id) const {
return &variantPools_.getSlot(id)->extension;
}
#endif
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,79 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/ResourceManager.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class StringBuffer {
public:
StringBuffer(ResourceManager* resources) : resources_(resources) {}
~StringBuffer() {
if (node_)
resources_->destroyString(node_);
}
char* reserve(size_t capacity) {
if (node_ && capacity > node_->length) {
// existing buffer is too small, we need to reallocate
resources_->destroyString(node_);
node_ = nullptr;
}
if (!node_)
node_ = resources_->createString(capacity);
if (!node_)
return nullptr;
size_ = capacity;
node_->data[capacity] = 0; // null-terminate the string
return node_->data;
}
JsonString str() const {
ARDUINOJSON_ASSERT(node_ != nullptr);
return JsonString(node_->data, node_->length);
}
void save(VariantData* data) {
ARDUINOJSON_ASSERT(node_ != nullptr);
const char* s = node_->data;
if (isTinyString(s, size_))
data->setTinyString(adaptString(s, size_));
else
data->setOwnedString(commitStringNode());
}
void saveRaw(VariantData* data) {
data->setRawString(commitStringNode());
}
private:
StringNode* commitStringNode() {
ARDUINOJSON_ASSERT(node_ != nullptr);
node_->data[size_] = 0;
auto node = resources_->getString(adaptString(node_->data, size_));
if (node) {
node->references++;
return node;
}
if (node_->length != size_) {
node = resources_->resizeString(node_, size_);
ARDUINOJSON_ASSERT(node != nullptr); // realloc to smaller can't fail
} else {
node = node_;
}
node_ = nullptr;
resources_->saveString(node);
return node;
}
ResourceManager* resources_;
StringNode* node_ = nullptr;
size_t size_ = 0;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,88 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/ResourceManager.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class StringBuilder {
public:
static const size_t initialCapacity = 31;
StringBuilder(ResourceManager* resources) : resources_(resources) {}
~StringBuilder() {
if (node_)
resources_->destroyString(node_);
}
void startString() {
size_ = 0;
if (!node_)
node_ = resources_->createString(initialCapacity);
}
void save(VariantData* variant) {
ARDUINOJSON_ASSERT(variant != nullptr);
ARDUINOJSON_ASSERT(node_ != nullptr);
char* p = node_->data;
if (isTinyString(p, size_)) {
variant->setTinyString(adaptString(p, size_));
return;
}
p[size_] = 0;
StringNode* node = resources_->getString(adaptString(p, size_));
if (!node) {
node = resources_->resizeString(node_, size_);
ARDUINOJSON_ASSERT(node != nullptr); // realloc to smaller can't fail
resources_->saveString(node);
node_ = nullptr; // next time we need a new string
} else {
node->references++;
}
variant->setOwnedString(node);
}
void append(const char* s) {
while (*s)
append(*s++);
}
void append(const char* s, size_t n) {
while (n-- > 0) // TODO: memcpy
append(*s++);
}
void append(char c) {
if (node_ && size_ == node_->length)
node_ = resources_->resizeString(node_, size_ * 2U + 1);
if (node_)
node_->data[size_++] = c;
}
bool isValid() const {
return node_ != nullptr;
}
size_t size() const {
return size_;
}
JsonString str() const {
ARDUINOJSON_ASSERT(node_ != nullptr);
node_->data[size_] = 0;
return JsonString(node_->data, size_);
}
private:
ResourceManager* resources_;
StringNode* node_ = nullptr;
size_t size_ = 0;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,75 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/Allocator.hpp>
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/integer.hpp>
#include <ArduinoJson/Polyfills/limits.hpp>
#include <stddef.h> // offsetof
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
struct StringNode {
// Use the same type as SlotId to store the reference count
// (there can never be more references than slots)
using references_type = uint_t<ARDUINOJSON_SLOT_ID_SIZE * 8>;
using length_type = uint_t<ARDUINOJSON_STRING_LENGTH_SIZE * 8>;
struct StringNode* next;
references_type references;
length_type length;
char data[1];
static constexpr size_t maxLength = numeric_limits<length_type>::highest();
static constexpr size_t sizeForLength(size_t n) {
return n + 1 + offsetof(StringNode, data);
}
static StringNode* create(size_t length, Allocator* allocator) {
if (length > maxLength)
return nullptr;
auto size = sizeForLength(length);
if (size < length) // integer overflow
return nullptr; // (not testable on 64-bit)
auto node = reinterpret_cast<StringNode*>(allocator->allocate(size));
if (node) {
node->length = length_type(length);
node->references = 1;
}
return node;
}
static StringNode* resize(StringNode* node, size_t length,
Allocator* allocator) {
ARDUINOJSON_ASSERT(node != nullptr);
StringNode* newNode;
if (length <= maxLength)
newNode = reinterpret_cast<StringNode*>(
allocator->reallocate(node, sizeForLength(length)));
else
newNode = nullptr;
if (newNode)
newNode->length = length_type(length);
else
allocator->deallocate(node);
return newNode;
}
static void destroy(StringNode* node, Allocator* allocator) {
allocator->deallocate(node);
}
};
// Returns the size (in bytes) of an string with n characters.
constexpr size_t sizeofString(size_t n) {
return StringNode::sizeForLength(n);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,102 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/Allocator.hpp>
#include <ArduinoJson/Memory/StringNode.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/utility.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class StringPool {
public:
StringPool() = default;
StringPool(const StringPool&) = delete;
void operator=(StringPool&& src) = delete;
~StringPool() {
ARDUINOJSON_ASSERT(strings_ == nullptr);
}
friend void swap(StringPool& a, StringPool& b) {
swap_(a.strings_, b.strings_);
}
void clear(Allocator* allocator) {
while (strings_) {
auto node = strings_;
strings_ = node->next;
StringNode::destroy(node, allocator);
}
}
size_t size() const {
size_t total = 0;
for (auto node = strings_; node; node = node->next)
total += sizeofString(node->length);
return total;
}
template <typename TAdaptedString>
StringNode* add(TAdaptedString str, Allocator* allocator) {
ARDUINOJSON_ASSERT(str.isNull() == false);
auto node = get(str);
if (node) {
node->references++;
return node;
}
size_t n = str.size();
node = StringNode::create(n, allocator);
if (!node)
return nullptr;
stringGetChars(str, node->data, n);
node->data[n] = 0; // force NUL terminator
add(node);
return node;
}
void add(StringNode* node) {
ARDUINOJSON_ASSERT(node != nullptr);
node->next = strings_;
strings_ = node;
}
template <typename TAdaptedString>
StringNode* get(const TAdaptedString& str) const {
for (auto node = strings_; node; node = node->next) {
if (stringEquals(str, adaptString(node->data, node->length)))
return node;
}
return nullptr;
}
void dereference(const char* s, Allocator* allocator) {
StringNode* prev = nullptr;
for (auto node = strings_; node; node = node->next) {
if (node->data == s) {
if (--node->references == 0) {
if (prev)
prev->next = node->next;
else
strings_ = node->next;
StringNode::destroy(node, allocator);
}
return;
}
prev = node;
}
}
private:
StringNode* strings_ = nullptr;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,71 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Strings/StringAdapters.hpp>
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// A special type of data that can be used to insert pregenerated JSON portions.
template <typename T>
class SerializedValue {
public:
explicit SerializedValue(T str) : str_(str) {}
operator T() const {
return str_;
}
const char* data() const {
return str_.c_str();
}
size_t size() const {
// CAUTION: the old Arduino String doesn't have size()
return str_.length();
}
private:
T str_;
};
template <typename TChar>
class SerializedValue<TChar*> {
public:
explicit SerializedValue(TChar* p, size_t n) : data_(p), size_(n) {}
operator TChar*() const {
return data_;
}
TChar* data() const {
return data_;
}
size_t size() const {
return size_;
}
private:
TChar* data_;
size_t size_;
};
using RawString = SerializedValue<const char*>;
template <typename T>
inline SerializedValue<T> serialized(T str) {
return SerializedValue<T>(str);
}
template <typename TChar>
inline SerializedValue<TChar*> serialized(TChar* p) {
return SerializedValue<TChar*>(p, detail::adaptString(p).size());
}
template <typename TChar>
inline SerializedValue<TChar*> serialized(TChar* p, size_t n) {
return SerializedValue<TChar*>(p, n);
}
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,97 @@
#pragma once
#include <ArduinoJson/Variant/Converter.hpp>
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
class MsgPackBinary {
public:
MsgPackBinary() : data_(nullptr), size_(0) {}
explicit MsgPackBinary(const void* c, size_t size) : data_(c), size_(size) {}
const void* data() const {
return data_;
}
size_t size() const {
return size_;
}
private:
const void* data_;
size_t size_;
};
template <>
struct Converter<MsgPackBinary> : private detail::VariantAttorney {
static void toJson(MsgPackBinary src, JsonVariant dst) {
auto data = VariantAttorney::getData(dst);
if (!data)
return;
auto resources = getResourceManager(dst);
data->clear(resources);
if (src.data()) {
size_t headerSize = src.size() >= 0x10000 ? 5
: src.size() >= 0x100 ? 3
: 2;
auto str = resources->createString(src.size() + headerSize);
if (str) {
resources->saveString(str);
auto ptr = reinterpret_cast<uint8_t*>(str->data);
switch (headerSize) {
case 2:
ptr[0] = uint8_t(0xc4);
ptr[1] = uint8_t(src.size() & 0xff);
break;
case 3:
ptr[0] = uint8_t(0xc5);
ptr[1] = uint8_t(src.size() >> 8 & 0xff);
ptr[2] = uint8_t(src.size() & 0xff);
break;
case 5:
ptr[0] = uint8_t(0xc6);
ptr[1] = uint8_t(src.size() >> 24 & 0xff);
ptr[2] = uint8_t(src.size() >> 16 & 0xff);
ptr[3] = uint8_t(src.size() >> 8 & 0xff);
ptr[4] = uint8_t(src.size() & 0xff);
break;
default:
ARDUINOJSON_ASSERT(false);
}
memcpy(ptr + headerSize, src.data(), src.size());
data->setRawString(str);
return;
}
}
}
static MsgPackBinary fromJson(JsonVariantConst src) {
auto data = getData(src);
if (!data)
return {};
auto rawstr = data->asRawString();
auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str());
auto n = rawstr.size();
if (n >= 2 && p[0] == 0xc4) { // bin 8
size_t size = p[1];
if (size + 2 == n)
return MsgPackBinary(p + 2, size);
} else if (n >= 3 && p[0] == 0xc5) { // bin 16
size_t size = size_t(p[1] << 8) | p[2];
if (size + 3 == n)
return MsgPackBinary(p + 3, size);
} else if (n >= 5 && p[0] == 0xc6) { // bin 32
size_t size =
size_t(p[1] << 24) | size_t(p[2] << 16) | size_t(p[3] << 8) | p[4];
if (size + 5 == n)
return MsgPackBinary(p + 5, size);
}
return {};
}
static bool checkJson(JsonVariantConst src) {
return fromJson(src).data() != nullptr;
}
};
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,487 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Deserialization/deserialize.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/StringBuffer.hpp>
#include <ArduinoJson/MsgPack/endianness.hpp>
#include <ArduinoJson/MsgPack/ieee754.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TReader>
class MsgPackDeserializer {
public:
MsgPackDeserializer(ResourceManager* resources, TReader reader)
: resources_(resources),
reader_(reader),
stringBuffer_(resources),
foundSomething_(false) {}
template <typename TFilter>
DeserializationError parse(VariantData& variant, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
err = parseVariant(&variant, filter, nestingLimit);
return foundSomething_ ? err : DeserializationError::EmptyInput;
}
private:
template <typename TFilter>
DeserializationError::Code parseVariant(
VariantData* variant, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
uint8_t header[5];
err = readBytes(header, 1);
if (err)
return err;
const uint8_t& code = header[0];
foundSomething_ = true;
bool allowValue = filter.allowValue();
if (allowValue) {
// callers pass a null pointer only when value must be ignored
ARDUINOJSON_ASSERT(variant != 0);
}
if (code >= 0xcc && code <= 0xd3) {
auto width = uint8_t(1U << ((code - 0xcc) % 4));
if (allowValue)
return readInteger(variant, width, code >= 0xd0);
else
return skipBytes(width);
}
switch (code) {
case 0xc0:
// already null
return DeserializationError::Ok;
case 0xc1:
return DeserializationError::InvalidInput;
case 0xc2:
case 0xc3:
if (allowValue)
variant->setBoolean(code == 0xc3);
return DeserializationError::Ok;
case 0xca:
if (allowValue)
return readFloat<float>(variant);
else
return skipBytes(4);
case 0xcb:
if (allowValue)
return readDouble<double>(variant);
else
return skipBytes(8);
}
if (code <= 0x7f || code >= 0xe0) { // fixint
if (allowValue)
variant->setInteger(static_cast<int8_t>(code), resources_);
return DeserializationError::Ok;
}
uint8_t sizeBytes = 0;
size_t size = 0;
bool isExtension = code >= 0xc7 && code <= 0xc9;
switch (code) {
case 0xc4: // bin 8
case 0xc7: // ext 8
case 0xd9: // str 8
sizeBytes = 1;
break;
case 0xc5: // bin 16
case 0xc8: // ext 16
case 0xda: // str 16
case 0xdc: // array 16
case 0xde: // map 16
sizeBytes = 2;
break;
case 0xc6: // bin 32
case 0xc9: // ext 32
case 0xdb: // str 32
case 0xdd: // array 32
case 0xdf: // map 32
sizeBytes = 4;
break;
}
if (code >= 0xd4 && code <= 0xd8) { // fixext
size = size_t(1) << (code - 0xd4);
isExtension = true;
}
switch (code & 0xf0) {
case 0x90: // fixarray
case 0x80: // fixmap
size = code & 0x0F;
break;
}
switch (code & 0xe0) {
case 0xa0: // fixstr
size = code & 0x1f;
break;
}
if (sizeBytes) {
err = readBytes(header + 1, sizeBytes);
if (err)
return err;
uint32_t size32 = 0;
for (uint8_t i = 0; i < sizeBytes; i++)
size32 = (size32 << 8) | header[i + 1];
size = size_t(size32);
if (size < size32) // integer overflow
return DeserializationError::NoMemory; // (not testable on 32/64-bit)
}
// array 16, 32 and fixarray
if (code == 0xdc || code == 0xdd || (code & 0xf0) == 0x90)
return readArray(variant, size, filter, nestingLimit);
// map 16, 32 and fixmap
if (code == 0xde || code == 0xdf || (code & 0xf0) == 0x80)
return readObject(variant, size, filter, nestingLimit);
// str 8, 16, 32 and fixstr
if (code == 0xd9 || code == 0xda || code == 0xdb || (code & 0xe0) == 0xa0) {
if (allowValue)
return readString(variant, size);
else
return skipBytes(size);
}
if (isExtension)
size++; // to include the type
if (allowValue)
return readRawString(variant, header, uint8_t(1 + sizeBytes), size);
else
return skipBytes(size);
}
DeserializationError::Code readByte(uint8_t& value) {
int c = reader_.read();
if (c < 0)
return DeserializationError::IncompleteInput;
value = static_cast<uint8_t>(c);
return DeserializationError::Ok;
}
DeserializationError::Code readBytes(void* p, size_t n) {
if (reader_.readBytes(reinterpret_cast<char*>(p), n) == n)
return DeserializationError::Ok;
return DeserializationError::IncompleteInput;
}
template <typename T>
DeserializationError::Code readBytes(T& value) {
return readBytes(&value, sizeof(value));
}
DeserializationError::Code skipBytes(size_t n) {
for (; n; --n) {
if (reader_.read() < 0)
return DeserializationError::IncompleteInput;
}
return DeserializationError::Ok;
}
DeserializationError::Code readInteger(VariantData* variant, uint8_t width,
bool isSigned) {
uint8_t buffer[8];
auto err = readBytes(buffer, width);
if (err)
return err;
union {
int64_t signedValue;
uint64_t unsignedValue;
};
if (isSigned)
signedValue = static_cast<int8_t>(buffer[0]); // propagate sign bit
else
unsignedValue = static_cast<uint8_t>(buffer[0]);
for (uint8_t i = 1; i < width; i++)
unsignedValue = (unsignedValue << 8) | buffer[i];
if (isSigned) {
auto truncatedValue = static_cast<JsonInteger>(signedValue);
if (truncatedValue == signedValue) {
if (!variant->setInteger(truncatedValue, resources_))
return DeserializationError::NoMemory;
}
// else set null on overflow
} else {
auto truncatedValue = static_cast<JsonUInt>(unsignedValue);
if (truncatedValue == unsignedValue)
if (!variant->setInteger(truncatedValue, resources_))
return DeserializationError::NoMemory;
// else set null on overflow
}
return DeserializationError::Ok;
}
template <typename T>
enable_if_t<sizeof(T) == 4, DeserializationError::Code> readFloat(
VariantData* variant) {
DeserializationError::Code err;
T value;
err = readBytes(value);
if (err)
return err;
fixEndianness(value);
variant->setFloat(value, resources_);
return DeserializationError::Ok;
}
template <typename T>
enable_if_t<sizeof(T) == 8, DeserializationError::Code> readDouble(
VariantData* variant) {
DeserializationError::Code err;
T value;
err = readBytes(value);
if (err)
return err;
fixEndianness(value);
if (variant->setFloat(value, resources_))
return DeserializationError::Ok;
else
return DeserializationError::NoMemory;
}
template <typename T>
enable_if_t<sizeof(T) == 4, DeserializationError::Code> readDouble(
VariantData* variant) {
DeserializationError::Code err;
uint8_t i[8]; // input is 8 bytes
T value; // output is 4 bytes
uint8_t* o = reinterpret_cast<uint8_t*>(&value);
err = readBytes(i, 8);
if (err)
return err;
doubleToFloat(i, o);
fixEndianness(value);
variant->setFloat(value, resources_);
return DeserializationError::Ok;
}
DeserializationError::Code readString(VariantData* variant, size_t n) {
DeserializationError::Code err;
err = readString(n);
if (err)
return err;
stringBuffer_.save(variant);
return DeserializationError::Ok;
}
DeserializationError::Code readString(size_t n) {
char* p = stringBuffer_.reserve(n);
if (!p)
return DeserializationError::NoMemory;
return readBytes(p, n);
}
DeserializationError::Code readRawString(VariantData* variant,
const void* header,
uint8_t headerSize, size_t n) {
auto totalSize = size_t(headerSize + n);
if (totalSize < n) // integer overflow
return DeserializationError::NoMemory; // (not testable on 64-bit)
char* p = stringBuffer_.reserve(totalSize);
if (!p)
return DeserializationError::NoMemory;
memcpy(p, header, headerSize);
auto err = readBytes(p + headerSize, n);
if (err)
return err;
stringBuffer_.saveRaw(variant);
return DeserializationError::Ok;
}
template <typename TFilter>
DeserializationError::Code readArray(
VariantData* variant, size_t n, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
if (nestingLimit.reached())
return DeserializationError::TooDeep;
bool allowArray = filter.allowArray();
ArrayData* array;
if (allowArray) {
ARDUINOJSON_ASSERT(variant != 0);
array = &variant->toArray();
} else {
array = 0;
}
TFilter elementFilter = filter[0U];
for (; n; --n) {
VariantData* value;
if (elementFilter.allow()) {
ARDUINOJSON_ASSERT(array != 0);
value = array->addElement(resources_);
if (!value)
return DeserializationError::NoMemory;
} else {
value = 0;
}
err = parseVariant(value, elementFilter, nestingLimit.decrement());
if (err)
return err;
}
return DeserializationError::Ok;
}
template <typename TFilter>
DeserializationError::Code readObject(
VariantData* variant, size_t n, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
if (nestingLimit.reached())
return DeserializationError::TooDeep;
ObjectData* object;
if (filter.allowObject()) {
ARDUINOJSON_ASSERT(variant != 0);
object = &variant->toObject();
} else {
object = 0;
}
for (; n; --n) {
err = readKey();
if (err)
return err;
JsonString key = stringBuffer_.str();
TFilter memberFilter = filter[key.c_str()];
VariantData* member = 0;
if (memberFilter.allow()) {
ARDUINOJSON_ASSERT(object != 0);
auto keyVariant = object->addPair(&member, resources_);
if (!keyVariant)
return DeserializationError::NoMemory;
stringBuffer_.save(keyVariant);
}
err = parseVariant(member, memberFilter, nestingLimit.decrement());
if (err)
return err;
}
return DeserializationError::Ok;
}
DeserializationError::Code readKey() {
DeserializationError::Code err;
uint8_t code;
err = readByte(code);
if (err)
return err;
if ((code & 0xe0) == 0xa0)
return readString(code & 0x1f);
if (code >= 0xd9 && code <= 0xdb) {
uint8_t sizeBytes = uint8_t(1U << (code - 0xd9));
uint32_t size = 0;
for (uint8_t i = 0; i < sizeBytes; i++) {
err = readByte(code);
if (err)
return err;
size = (size << 8) | code;
}
return readString(size);
}
return DeserializationError::InvalidInput;
}
ResourceManager* resources_;
TReader reader_;
StringBuffer stringBuffer_;
bool foundSomething_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Parses a MessagePack input and puts the result in a JsonDocument.
// https://arduinojson.org/v7/api/msgpack/deserializemsgpack/
template <typename TDestination, typename... Args,
detail::enable_if_t<
detail::is_deserialize_destination<TDestination>::value, int> = 0>
inline DeserializationError deserializeMsgPack(TDestination&& dst,
Args&&... args) {
using namespace detail;
return deserialize<MsgPackDeserializer>(detail::forward<TDestination>(dst),
detail::forward<Args>(args)...);
}
// Parses a MessagePack input and puts the result in a JsonDocument.
// https://arduinojson.org/v7/api/msgpack/deserializemsgpack/
template <typename TDestination, typename TChar, typename... Args,
detail::enable_if_t<
detail::is_deserialize_destination<TDestination>::value, int> = 0>
inline DeserializationError deserializeMsgPack(TDestination&& dst, TChar* input,
Args&&... args) {
using namespace detail;
return deserialize<MsgPackDeserializer>(detail::forward<TDestination>(dst),
input,
detail::forward<Args>(args)...);
}
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,120 @@
#pragma once
#include <ArduinoJson/Variant/Converter.hpp>
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
class MsgPackExtension {
public:
MsgPackExtension() : data_(nullptr), size_(0), type_(0) {}
explicit MsgPackExtension(int8_t type, const void* data, size_t size)
: data_(data), size_(size), type_(type) {}
int8_t type() const {
return type_;
}
const void* data() const {
return data_;
}
size_t size() const {
return size_;
}
private:
const void* data_;
size_t size_;
int8_t type_;
};
template <>
struct Converter<MsgPackExtension> : private detail::VariantAttorney {
static void toJson(MsgPackExtension src, JsonVariant dst) {
auto data = VariantAttorney::getData(dst);
if (!data)
return;
auto resources = getResourceManager(dst);
data->clear(resources);
if (src.data()) {
uint8_t format, sizeBytes;
if (src.size() >= 0x10000) {
format = 0xc9; // ext 32
sizeBytes = 4;
} else if (src.size() >= 0x100) {
format = 0xc8; // ext 16
sizeBytes = 2;
} else if (src.size() == 16) {
format = 0xd8; // fixext 16
sizeBytes = 0;
} else if (src.size() == 8) {
format = 0xd7; // fixext 8
sizeBytes = 0;
} else if (src.size() == 4) {
format = 0xd6; // fixext 4
sizeBytes = 0;
} else if (src.size() == 2) {
format = 0xd5; // fixext 2
sizeBytes = 0;
} else if (src.size() == 1) {
format = 0xd4; // fixext 1
sizeBytes = 0;
} else {
format = 0xc7; // ext 8
sizeBytes = 1;
}
auto str = resources->createString(src.size() + 2 + sizeBytes);
if (str) {
resources->saveString(str);
auto ptr = reinterpret_cast<uint8_t*>(str->data);
*ptr++ = uint8_t(format);
for (uint8_t i = 0; i < sizeBytes; i++)
*ptr++ = uint8_t(src.size() >> (sizeBytes - i - 1) * 8 & 0xff);
*ptr++ = uint8_t(src.type());
memcpy(ptr, src.data(), src.size());
data->setRawString(str);
return;
}
}
}
static MsgPackExtension fromJson(JsonVariantConst src) {
auto data = getData(src);
if (!data)
return {};
auto rawstr = data->asRawString();
if (rawstr.size() == 0)
return {};
auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str());
size_t payloadSize = 0;
uint8_t headerSize = 0;
const uint8_t& code = p[0];
if (code >= 0xd4 && code <= 0xd8) { // fixext 1
headerSize = 2;
payloadSize = size_t(1) << (code - 0xd4);
}
if (code >= 0xc7 && code <= 0xc9) {
uint8_t sizeBytes = uint8_t(1 << (code - 0xc7));
for (uint8_t i = 0; i < sizeBytes; i++)
payloadSize = (payloadSize << 8) | p[1 + i];
headerSize = uint8_t(2 + sizeBytes);
}
if (rawstr.size() == headerSize + payloadSize)
return MsgPackExtension(int8_t(p[headerSize - 1]), p + headerSize,
payloadSize);
return {};
}
static bool checkJson(JsonVariantConst src) {
return fromJson(src).data() != nullptr;
}
};
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,244 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/MsgPack/endianness.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Serialization/CountingDecorator.hpp>
#include <ArduinoJson/Serialization/measure.hpp>
#include <ArduinoJson/Serialization/serialize.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TWriter>
class MsgPackSerializer : public VariantDataVisitor<size_t> {
public:
static const bool producesText = false;
MsgPackSerializer(TWriter writer, const ResourceManager* resources)
: writer_(writer), resources_(resources) {}
template <typename T>
enable_if_t<is_floating_point<T>::value && sizeof(T) == 4, size_t> visit(
T value32) {
if (canConvertNumber<JsonInteger>(value32)) {
JsonInteger truncatedValue = JsonInteger(value32);
if (value32 == T(truncatedValue))
return visit(truncatedValue);
}
writeByte(0xCA);
writeInteger(value32);
return bytesWritten();
}
template <typename T>
ARDUINOJSON_NO_SANITIZE("float-cast-overflow")
enable_if_t<is_floating_point<T>::value && sizeof(T) == 8, size_t> visit(
T value64) {
float value32 = float(value64);
if (value32 == value64)
return visit(value32);
writeByte(0xCB);
writeInteger(value64);
return bytesWritten();
}
size_t visit(const ArrayData& array) {
size_t n = array.size(resources_);
if (n < 0x10) {
writeByte(uint8_t(0x90 + n));
} else if (n < 0x10000) {
writeByte(0xDC);
writeInteger(uint16_t(n));
} else {
writeByte(0xDD);
writeInteger(uint32_t(n));
}
auto slotId = array.head();
while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId);
slot->accept(*this, resources_);
slotId = slot->next();
}
return bytesWritten();
}
size_t visit(const ObjectData& object) {
size_t n = object.size(resources_);
if (n < 0x10) {
writeByte(uint8_t(0x80 + n));
} else if (n < 0x10000) {
writeByte(0xDE);
writeInteger(uint16_t(n));
} else {
writeByte(0xDF);
writeInteger(uint32_t(n));
}
auto slotId = object.head();
while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId);
slot->accept(*this, resources_);
slotId = slot->next();
}
return bytesWritten();
}
size_t visit(const char* value) {
return visit(JsonString(value));
}
size_t visit(JsonString value) {
ARDUINOJSON_ASSERT(!value.isNull());
auto n = value.size();
if (n < 0x20) {
writeByte(uint8_t(0xA0 + n));
} else if (n < 0x100) {
writeByte(0xD9);
writeInteger(uint8_t(n));
} else if (n < 0x10000) {
writeByte(0xDA);
writeInteger(uint16_t(n));
} else {
writeByte(0xDB);
writeInteger(uint32_t(n));
}
writeBytes(reinterpret_cast<const uint8_t*>(value.c_str()), n);
return bytesWritten();
}
size_t visit(RawString value) {
writeBytes(reinterpret_cast<const uint8_t*>(value.data()), value.size());
return bytesWritten();
}
size_t visit(JsonInteger value) {
if (value > 0) {
visit(static_cast<JsonUInt>(value));
} else if (value >= -0x20) {
writeInteger(int8_t(value));
} else if (value >= -0x80) {
writeByte(0xD0);
writeInteger(int8_t(value));
} else if (value >= -0x8000) {
writeByte(0xD1);
writeInteger(int16_t(value));
}
#if ARDUINOJSON_USE_LONG_LONG
else if (value >= -0x80000000LL)
#else
else
#endif
{
writeByte(0xD2);
writeInteger(int32_t(value));
}
#if ARDUINOJSON_USE_LONG_LONG
else {
writeByte(0xD3);
writeInteger(int64_t(value));
}
#endif
return bytesWritten();
}
size_t visit(JsonUInt value) {
if (value <= 0x7F) {
writeInteger(uint8_t(value));
} else if (value <= 0xFF) {
writeByte(0xCC);
writeInteger(uint8_t(value));
} else if (value <= 0xFFFF) {
writeByte(0xCD);
writeInteger(uint16_t(value));
}
#if ARDUINOJSON_USE_LONG_LONG
else if (value <= 0xFFFFFFFF)
#else
else
#endif
{
writeByte(0xCE);
writeInteger(uint32_t(value));
}
#if ARDUINOJSON_USE_LONG_LONG
else {
writeByte(0xCF);
writeInteger(uint64_t(value));
}
#endif
return bytesWritten();
}
size_t visit(bool value) {
writeByte(value ? 0xC3 : 0xC2);
return bytesWritten();
}
size_t visit(nullptr_t) {
writeByte(0xC0);
return bytesWritten();
}
private:
size_t bytesWritten() const {
return writer_.count();
}
void writeByte(uint8_t c) {
writer_.write(c);
}
void writeBytes(const uint8_t* p, size_t n) {
writer_.write(p, n);
}
template <typename T>
void writeInteger(T value) {
fixEndianness(value);
writeBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
}
CountingDecorator<TWriter> writer_;
const ResourceManager* resources_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Produces a MessagePack document.
// https://arduinojson.org/v7/api/msgpack/serializemsgpack/
template <
typename TDestination,
detail::enable_if_t<!detail::is_pointer<TDestination>::value, int> = 0>
inline size_t serializeMsgPack(JsonVariantConst source, TDestination& output) {
using namespace ArduinoJson::detail;
return serialize<MsgPackSerializer>(source, output);
}
// Produces a MessagePack document.
// https://arduinojson.org/v7/api/msgpack/serializemsgpack/
inline size_t serializeMsgPack(JsonVariantConst source, void* output,
size_t size) {
using namespace ArduinoJson::detail;
return serialize<MsgPackSerializer>(source, output, size);
}
// Computes the length of the document that serializeMsgPack() produces.
// https://arduinojson.org/v7/api/msgpack/measuremsgpack/
inline size_t measureMsgPack(JsonVariantConst source) {
using namespace ArduinoJson::detail;
return measure<MsgPackSerializer>(source);
}
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -0,0 +1,46 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Polyfills/type_traits.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
#if ARDUINOJSON_LITTLE_ENDIAN
inline void swapBytes(uint8_t& a, uint8_t& b) {
uint8_t t(a);
a = b;
b = t;
}
inline void fixEndianness(uint8_t* p, integral_constant<size_t, 8>) {
swapBytes(p[0], p[7]);
swapBytes(p[1], p[6]);
swapBytes(p[2], p[5]);
swapBytes(p[3], p[4]);
}
inline void fixEndianness(uint8_t* p, integral_constant<size_t, 4>) {
swapBytes(p[0], p[3]);
swapBytes(p[1], p[2]);
}
inline void fixEndianness(uint8_t* p, integral_constant<size_t, 2>) {
swapBytes(p[0], p[1]);
}
inline void fixEndianness(uint8_t*, integral_constant<size_t, 1>) {}
template <typename T>
inline void fixEndianness(T& value) {
fixEndianness(reinterpret_cast<uint8_t*>(&value),
integral_constant<size_t, sizeof(T)>());
}
#else
template <typename T>
inline void fixEndianness(T&) {}
#endif
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,18 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline void doubleToFloat(const uint8_t d[8], uint8_t f[4]) {
f[0] = uint8_t((d[0] & 0xC0) | (d[0] << 3 & 0x3f) | (d[1] >> 5));
f[1] = uint8_t((d[1] << 3) | (d[2] >> 5));
f[2] = uint8_t((d[2] << 3) | (d[3] >> 5));
f[3] = uint8_t((d[3] << 3) | (d[4] >> 5));
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -0,0 +1,42 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2026, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Configuration.hpp>
#include <ArduinoJson/Polyfills/preprocessor.hpp>
#include <ArduinoJson/version.hpp>
#ifndef ARDUINOJSON_VERSION_NAMESPACE
# define ARDUINOJSON_VERSION_NAMESPACE \
ARDUINOJSON_CONCAT5( \
ARDUINOJSON_VERSION_MACRO, \
ARDUINOJSON_BIN2ALPHA(ARDUINOJSON_ENABLE_PROGMEM, \
ARDUINOJSON_USE_LONG_LONG, \
ARDUINOJSON_USE_DOUBLE, 1), \
ARDUINOJSON_BIN2ALPHA( \
ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \
ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE), \
ARDUINOJSON_SLOT_ID_SIZE, ARDUINOJSON_STRING_LENGTH_SIZE)
#endif
#define ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE \
namespace ArduinoJson { \
inline namespace ARDUINOJSON_VERSION_NAMESPACE {
#define ARDUINOJSON_END_PUBLIC_NAMESPACE \
} \
}
#define ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE \
namespace ArduinoJson { \
inline namespace ARDUINOJSON_VERSION_NAMESPACE { \
namespace detail {
#define ARDUINOJSON_END_PRIVATE_NAMESPACE \
} \
} \
}

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