diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..f74d4a187
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,19 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with UTF-8 encoding for every file
+[*]
+end_of_line = lf
+charset = utf-8
+
+# For all source code
+[*.{c,cpp,h,ino,sh}]
+insert_final_newline = true
+indent_style = tab
+indent_size = 2
+
+# Tab indentation also for makefiles
+[Makefile]
+indent_style = tab
diff --git a/.gitattributes b/.gitattributes
index 463573798..415abee25 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,5 +1,5 @@
# Set default behaviour, in case users don't have core.autocrlf set.
-* text=auto
+* text eol=lf
# Explicitly declare text files we want to always be normalized and converted
# to native line endings on checkout.
diff --git a/.gitignore b/.gitignore
index 980b4fce7..c99392e32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,7 @@ stino.settings
MyPrivateConfig.h
Documentation/html
doxygen_sqlite3.db
+Makefile.inc
+build
+bin
+*.sublime-workspace
diff --git a/.mystools/.bundle_runtime.sh b/.mystools/.bundle_runtime.sh
new file mode 100755
index 000000000..ebf09b7be
--- /dev/null
+++ b/.mystools/.bundle_runtime.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+###
+#
+# Tool runtime shim that provides a simple API for running
+# tool bundles and convenience functions for other tool developers.
+#
+##
+log() { IFS=" "; >&2 printf "%s\n" "$*"; }
+info() { IFS=" "; >&2 printf "\e[92m%s\e[m\n" "$*"; }
+warn() { IFS=" "; >&2 printf "\e[93m%s\e[m\n" "$*"; }
+err() { IFS=" "; >&2 printf "\e[91m%s\e[m\n" "$*"; exit 1; }
+
+is_supported_os()
+{
+ [[ ${1} == darwin* ]] || [[ ${1} == linux-gnu* ]] || [[ ${1} == freebsd ]] || [[ ${1} == msys ]] || [[ ${1} == cygwin ]]
+}
+
+is_installed()
+{
+ which "${1}" > /dev/null 2>&1
+}
+
+tools_dir()
+{
+ git_config_tools_dir='git config mysensors.toolsdir'
+
+ # Set mysensors.toolsdir in git config if it hasn't been set
+ if [[ ! $($git_config_tools_dir) || ! -d "$($git_config_tools_dir)" ]]; then
+ $git_config_tools_dir "$( cd "$(dirname "${BASH_SOURCE[0]}")"; git rev-parse --show-prefix )"
+ git config alias.mystoolspath '![ -z "${GIT_PREFIX}" ] || cd ${GIT_PREFIX}; echo $(git rev-parse --show-cdup)$(git config mysensors.toolsdir)'
+ fi
+
+ $git_config_tools_dir
+}
+
+configure_git_tool_aliases()
+{
+ find "$(git mystoolspath)" -name run.sh -print0 | while IFS= read -r -d '' bundle; do
+ local tool="$(basename "$(dirname "${bundle}")")"
+ git config alias.${tool} '!f() { $(git mystoolspath)'${tool}'/run.sh $@; }; f'
+ done
+}
+
+bootstrap_cksum()
+{
+ echo $(git ls-files -s -- $(git mystoolspath)bootstrap-dev.sh | cut -d' ' -f2)
+}
+
+bootstrap_version()
+{
+ git_config_bootstrap_version='git config mysensors.bootstrap-cksum'
+ if [[ $1 == --set ]]; then
+ $git_config_bootstrap_version $(bootstrap_cksum)
+ else
+ echo $($git_config_bootstrap_version)
+ fi
+}
+
+environment_outdated()
+{
+ [[ $(bootstrap_version) != $(bootstrap_cksum) ]]
+}
+
+modifiedSourceFiles()
+{
+ against=$(git rev-parse --verify HEAD >/dev/null 2>&1 && echo HEAD || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)
+ git diff $1 --diff-filter=AM --name-only $against | grep -E '.*\.(c|cpp|h|hpp|ino)$'
+}
+
+stagedSourceFiles()
+{
+ modifiedSourceFiles '--cached'
+}
+
+runBundle()
+{
+ local bundle="$(dirname "${0}")"
+ local tool=$(basename "${bundle}")
+
+ local CMD_OPTIONS=$( TOOLCONFIG="${bundle}/config" "${bundle}/options.sh" )
+
+ $tool $CMD_OPTIONS "$@"
+}
+
+###
+# Common environment variables
+#
+$(git rev-parse --is-inside-work-tree --quiet >/dev/null 2>&1) || err "Working directory is not a git repository. aborting..."
+
+if [[ $(basename "${0}") != *bootstrap* ]] && environment_outdated ; then
+ err "Your environment is out of date... Re-run $(git mystoolspath)bootstrap-dev.sh and try again"
+fi
+
+GITREPO=$(git rev-parse --show-toplevel)
+GITDIR=$(git rev-parse --git-dir)
+TOOLSDIR="$(tools_dir)"
diff --git a/.mystools/README.md b/.mystools/README.md
new file mode 100644
index 000000000..19a674251
--- /dev/null
+++ b/.mystools/README.md
@@ -0,0 +1,260 @@
+# Development Tools
+
+### Overview
+
+This directory hosts MySensors development tools. The following
+conventions are employed to facilitate consistent re-use/invocation
+across modalitiies (e.g. local development, continuous integration,
+editor linters, etc.)
+
+1. All common tools are hosted and managed in
+ the tools directory (used for both local development
+ and continuous integration)
+2. Each tool comprises a directory, akin to a bundle,
+ that encapsulates declarative command-line options,
+ configuration files and a run script
+3. A single bootstrap script configures a
+ development environment
+4. A lightweight runtime provides a common set of
+ convenience functions and variables to all scripts
+5. Support for all MySensors development environments
+
+### Usage
+
+The [boostrap-dev](bootstrap-dev.sh) script completely configures a
+development environment. The boostrap-dev script validates development
+prerequisites such as verifying the git repo ```origin``` &
+```upstream``` remotes, tools needed for the git client hooks,
+installing local commit hooks, and creating git aliases, etc.
+
+```shell-script
+$ cd MySensors # git repo
+$ .mystools/bootstrap-dev.sh
+Checking operating system support: darwin16...
+Checking github 'origin' & 'upstream' remotes...
+Checking tool/utility prerequisites...
+Installing git client-side hooks...
+Configuring git aliases for running mysensor tool bundles...
+Successfully configured your repo for MySensors development... Thanks for your support!
+$
+```
+**Note:** If the bootstrap can not find required command-line
+utilities, you will be requested to install the required tools and
+re-run bootstrap.sh. See [Installation instructions for prerequisite
+tools](#installtools).
+
+Once the bootstrapping process is complete, a git alias for each tool
+is available to conveniently run the tool with the pre-configured
+mysensors options and settings (no need to remember all those
+command-line options or where the configuration files reside).
+Furthermore, you can run the command against staged files, unstaged
+modified files or a list of files common to most git commands.
+
+```shell-script
+$ # Run cppcheck on an individual file
+$ git cppcheck core/MyMessage.cpp
+$
+$ # Run cppcheck on all changed files
+$ git cppcheck
+$
+$ # Run astyle on staged files
+$ git astyle --cached
+```
+
+Available tool aliases:
+
+* git cppcheck [--cached] [files]
+* git astyle [--cached] [files]
+
+Finally, to maintain code quality and a legible git revision history,
+two git hooks are installed that will trigger whenever you commit
+changes to your local repo. The hooks will examine your changes to
+ensure they satisfy the [MySensors Code Contribution
+Guidelines](https://www.mysensors.org/download/contributing) before
+you push your changes to GitHub for merging by the core MySensors
+team. Gitler will also enforce the coding guidelines so the hooks are
+provided to reduce development cycle times by detecting standards
+variances locally before pushing to GitHub.
+
+### Installation instructions for prerequisite tools
+
+This first time you run the bootstrap script, it may inform you that
+certain development tools are missing from your path or system:
+
+```
+Checking operating system support: darwin16...
+Checking github 'origin' & 'upstream' remotes...
+Checking tool/utility prerequisites...
+astyle not installed or not in current path.
+cppcheck not installed or not in current path.
+One or more required tools not found. Install required tools and re-run .mystools/bootstrap-dev.sh
+```
+
+To finish the bootstrap process, you will need to install the required
+tools for your specific operating system as follows. Currently we use
+Astyle 2.0.5 or later and Cppcheck 1.76 or later. Once you have
+installed AStyle and Cppcheck, re-run bootstrap-dev.sh to finish
+configuring your development environment.
+
+##### macOS
+
+```brew install astyle cppcheck```
+
+#### Linux Xenial or later
+
+```apt-get install astyle cppcheck```
+
+#### Linux Trusty or earlier
+
+##### Note: The apt versions are too old on Trusty so follow the [Linux - Build and Install from Source](#buildFromSource) instructions
+
+##### Windows - GitHub Git Shell
+
+###### *IMPORTANT: Be sure to launch PowerShell As Administrator*
+
+```
+### Install AStyle
+
+# Download
+iwr 'https://sourceforge.net/projects/astyle/files/astyle/astyle%202.05.1/AStyle_2.05.1_windows.zip/download' -UserAgent [Microsoft.PowerShell.Commands.PSUserAgent]::FireFox -OutFile astyle.2.05.zip
+
+# Unzip the filed & move the C:\Program Files
+expand-archive astyle.2.05.zip
+mv .\astyle.2.05 'C:\Program Files\AStyle\'
+
+# Add AStyle to your path
+[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\Program Files\AStyle\bin", [EnvironmentVariableTarget]::Machine)
+
+### Install Cppcheck (either 64-bit or 32-bit depending upon your version of Windows - pick one below)
+
+# 64-bit
+iwr 'http://github.com/danmar/cppcheck/releases/download/1.76.1/cppcheck-1.76.1-x64-Setup.msi' -UserAgent [Microsoft.PowerShell.Commands.PSUserAgent]::FireFox -OutFile cppcheck-1.76.1-Setup.msi
+
+# 32-bit
+iwr 'http://github.com/danmar/cppcheck/releases/download/1.76.1/cppcheck-1.76.1-x86-Setup.msi' -UserAgent [Microsoft.PowerShell.Commands.PSUserAgent]::FireFox -OutFile cppcheck-1.76.1-Setup.msi
+
+# Launch installer to install Cppcheck
+& .\cppcheck-1.76.1-Setup.msi
+
+### Add Cppcheck to your path
+[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\Program Files\Cppcheck", [EnvironmentVariableTarget]::Machine)
+
+### At this point you need to reboot for the path changes to take effect
+```
+
+##### Windows - bash
+
+###### NOTE: At the time of this writing, the apt vresions of cppcheck and astyle are too old. Follow the [Linux - Build and Install from Source](#buildFromSource) instructions
+
+##### Windows - Cygwin
+```
+Run Either Cygwin Setup-x86-64.exe or Setup-x86.exe depending upon your OS. Select and install astyle and cppcheck
+```
+
+##### Linux - Build and Install from Source
+
+```
+### Install AStyle
+
+# Download
+curl -L 'https://sourceforge.net/projects/astyle/files/astyle/astyle%202.05.1/astyle_2.05.1_linux.tar.gz/download' | tar xvz
+
+# Compile and install
+cd astyle/build/gcc && sudo make shared release shared static install
+
+### Install Cppcheck
+
+# Download
+curl -L 'https://sourceforge.net/projects/cppcheck/files/cppcheck/1.76.1/cppcheck-1.76.1.tar.gz/download' | tar xvz
+
+# Compile and install
+cd cppcheck-1.76.1
+sudo apt-get install libpcre++-dev
+sudo make SRCDIR=build CFGDIR=/usr/share/cppcheck HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" install
+
+```
+
+### Implementation Details
+
+*This section is intended for developers curious about the
+implementation details or interested in extending the development
+environment to support additional tools, aliases, etc.*
+
+A lightweight [runtime](.bundle_runtime.sh) (less than 70 lines of
+code) provides a simple API to consistently run the MySensors toolset
+across modalities including, but not limited to, local development and
+continuous integration. The core concept is that each tool
+encapsulates all runtime configuration settings in a simple,
+stand-alone bundle, that can be executed by the bundle runtime. The
+runtime provides an API to execute a tool bundle along with
+convenience functions and environment variables common to all tools.
+
+Each tool bundle is laid out as follows:
+
+```
+${TOOLSDIR} [e.g. ${GITREPO}/.mystools}]
+.../tool [e.g. cppcheck, astyle]
+....../run.sh [tool run script]
+....../options.sh [command-line options]
+....../config [supporting config files]
+........./some.cfg
+```
+
+All bundle runtime dependencies are defined withing the namespace /
+context of a git repo so users are free to rename or move repos or
+even use multiple copies of the same repo without encountering
+intra-repo tool settings/configuration conflicts.
+
+During the bootstrap process, certain keys and aliases are defined
+that the runtime leverages.
+
+#####git config keys
+
+* mysensors.toolsdir = .mystools (defined as a repo-relative path - location agnostic)
+* mysensors.bootstrap-cksum = \
+
+#####git aliases
+
+* mystoolspath = *returns the absolute path to the tools dir*
+* \ = *(e.g. cppcheck) runs the tool bundle*
+
+ NOTE: The \ aliases are auto-generated by enumerating the bundles located
+in *mystoolspath*.
+
+While the ```git args``` API is designed to cover many the
+common use cases, tool and hook authors may need to source the
+[runtime](.bundle_runtime.sh) into their script context in order to
+use the convenience functions and environment variables as follows:
+
+```shell-script
+. "$(git config mystoolspath).bundle_runtime.sh"
+```
+
+The runtime shim will instantiate the following environment variables
+and convenience functions in the script context for use within a tool
+or script.
+
+```
+TOOLSDIR - absolute path to repo tools dir. Maintained in
+ a location-agnostic fashion (e.g. if the user
+ moves the repo, changes tools directory within
+ repo, etc.
+GITREPO - absolute path to the git repo
+GITDIR - absolute path to the repo .git directory
+
+runBundle() - Run a given tool / bundle
+is_installed() - true if a given utility is installed
+supported_os() - true if running on a supported os
+log() - log a message in default color to console
+warn() - log a message in yellow to console
+err() - log a message in red and exit with non-zero status
+```
+
+Many of the aforementioned details can be safely ignored if you want
+to add a new development tool to the toolset. Simply create a bundle
+that follows the layout above, declare the tool command-line options
+in the [bundle]/options.sh and any configuration files in
+[bundle]/config/. While it should not be necessary to alter the
+bundle execution behavior beyond declaring command-line options and
+configuration files, you can override it by modifing the [bundle]
+run.sh script.
diff --git a/.mystools/astyle/config/style.cfg b/.mystools/astyle/config/style.cfg
new file mode 100644
index 000000000..363270ef4
--- /dev/null
+++ b/.mystools/astyle/config/style.cfg
@@ -0,0 +1,23 @@
+# Coding standard for MySensors core library (see https://www.mysensors.org/download/contributing)
+
+# Curly braces should follow "One True Brace Style" format
+style=1tbs
+
+# All control statements ('if', 'for', 'while' etc.) should include curly braces {}. Even one-liners.
+add-brackets
+
+# Tab-character for white space/indentation
+indent=tab
+
+# All lines should be kept at 100 character width or less
+max-code-length=100
+break-after-logical
+
+# Preprocessor blocks should be indented
+#indent-preproc-block
+
+# Preprocessor definitions should be indented if linebroken
+indent-preproc-define
+
+# C++ comments should be indented to source level, and not be kept in first column
+indent-col1-comments
diff --git a/.mystools/astyle/options.sh b/.mystools/astyle/options.sh
new file mode 100755
index 000000000..64f922d51
--- /dev/null
+++ b/.mystools/astyle/options.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+OPTIONS="--options="${TOOLCONFIG}"/style.cfg -n"
+
+echo $OPTIONS
diff --git a/.mystools/astyle/run.sh b/.mystools/astyle/run.sh
new file mode 100755
index 000000000..81d18f68e
--- /dev/null
+++ b/.mystools/astyle/run.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Instantiate the bundle runtime shim
+. "$(git mystoolspath).bundle_runtime.sh"
+
+# Astyle takes one or more files as an argument.
+# --cached or for changed
+if [[ $# = 0 ]]; then
+ for file in $(modifiedSourceFiles); do
+ runBundle $file
+ done
+elif [[ $1 = '--cached' ]]; then
+ for file in $(stagedSourceFiles); do
+ runBundle $file
+ done
+else
+ eval "set -- $(git rev-parse --sq --prefix "$GIT_PREFIX" "$@")"
+ runBundle "$@"
+fi
diff --git a/.mystools/bootstrap-dev.sh b/.mystools/bootstrap-dev.sh
new file mode 100755
index 000000000..33c689dd7
--- /dev/null
+++ b/.mystools/bootstrap-dev.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+###
+# OVERVIEW:
+# This script bootstraps the MySensors development environment.
+#
+# USAGE:
+# git clone https://github.com/mysensors/MySensors.git
+# cd MySensors
+# .mystools/bootstrap-dev.sh
+##
+
+##
+# Import common utility functions and environment variables
+. "$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd)/.bundle_runtime.sh"
+
+check_tool_prerequisite()
+{
+ function ver { printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' ' '); }
+
+ if is_installed ${1} ; then
+ #local version=$(${1} --version 2>&1 | sed -e 's/[[:alpha:]|(|[:space:]]//g')
+ local version=$(${1} --version 2>&1 | sed -ne 's/[^0-9]*\(\([0-9]\.\)\{0,4\}[0-9][^.]\).*/\1/p')
+ if [ $(ver ${version}) -lt $(ver ${2}) ]; then
+ warn "Found ${1} ${version} however, version ${2} or greater is required..."
+ return 1
+ fi
+ else
+ warn "${1} not installed or not in current path."
+ return 1
+ fi
+}
+
+check_git_remote()
+{
+ local url=$( git config --get remote.${1}.url )
+ [[ $url == ${2} ]]
+}
+
+install_hooks()
+{
+ for hook in "${1}"/*.sh; do
+ local hookname=$(basename $hook)
+ $(cd "${GITDIR}/hooks"; ln -s -f "../../${TOOLSDIR}hooks/${hookname}" "${hookname%.sh}")
+ done
+}
+
+###
+# Main entry
+#
+# 1. Check that we are bootstrapping a supported OS/environment
+# 2. Validate github remotes include valid upstream and origin
+# 3. Check for client commit hook prerequisites
+# 4. Install client commit hook prerequisites
+# 5. Define aliases for conveniently running tool bundles
+##
+
+mysrepo="https://github.com/mysensors/MySensors.git"
+
+#1
+log "Checking operating system support: ${OSTYPE}..."
+is_supported_os ${OSTYPE} || {
+ err "OS ${OSTYPE} is unknown/unsupported, won't install anything"
+}
+
+#2
+log "Checking github 'origin' & 'upstream' remotes..."
+check_git_remote "origin" "${mysrepo}" && warn "Git \"origin\" should point to your github fork, not the github MySensors repo ${mysrep}"
+
+check_git_remote "upstream" "${mysrepo}" || {
+ warn "Git \"upstream\" remote not found or incorrectly defined. Configuring remote upstream --> ${mysrepo}..."
+ git remote add upstream "${mysrepo}" || err "git remote add ${mysrep} failed due to error $?"
+}
+
+#3
+log "Checking tool/utility prerequisites..."
+check_tool_prerequisite "astyle" "2.0.5" || err "Install AStyle 2.0.5 or greater and re-run ${0}"
+check_tool_prerequisite "cppcheck" "1.76" || err "Install Cppcheck 1.76 or greater and re-run ${0}"
+check_tool_prerequisite "git" "2.0" || err "Install git 2.0 or greater and re-run ${0}"
+
+#4
+log "Installing client-side git hooks..."
+install_hooks "$(git mystoolspath)hooks" || err "Failed to install git hooks due to error $?..."
+
+#5
+log "Configuring git aliases for running mysensor tools..."
+configure_git_tool_aliases || err "Failed to create git aliases due to error $?..."
+
+bootstrap_version "--set"
+
+info "Successfully configured your repo for MySensors development... Thanks for your support!"
diff --git a/.mystools/cppcheck/config/avr.xml b/.mystools/cppcheck/config/avr.xml
new file mode 100644
index 000000000..f2ba74526
--- /dev/null
+++ b/.mystools/cppcheck/config/avr.xml
@@ -0,0 +1,17 @@
+
+
+ 8
+ signed
+
+ 2
+ 2
+ 4
+ 8
+ 4
+ 4
+ 8
+ 2
+ 2
+ 2
+
+
\ No newline at end of file
diff --git a/.mystools/cppcheck/config/includes.cfg b/.mystools/cppcheck/config/includes.cfg
new file mode 100644
index 000000000..a57a7f30a
--- /dev/null
+++ b/.mystools/cppcheck/config/includes.cfg
@@ -0,0 +1,4 @@
+.
+drivers/Linux
+drivers/AES
+drivers/RPi
diff --git a/.mystools/cppcheck/config/suppressions.cfg b/.mystools/cppcheck/config/suppressions.cfg
new file mode 100644
index 000000000..57465da1e
--- /dev/null
+++ b/.mystools/cppcheck/config/suppressions.cfg
@@ -0,0 +1,5 @@
+toomanyconfigs
+missingInclude
+missingIncludeSystem
+ConfigurationNotChecked
+unmatchedSuppression
diff --git a/.mystools/cppcheck/config/unix32.xml b/.mystools/cppcheck/config/unix32.xml
new file mode 100644
index 000000000..bc4ca0e89
--- /dev/null
+++ b/.mystools/cppcheck/config/unix32.xml
@@ -0,0 +1,17 @@
+
+
+ 8
+ unsigned
+
+ 2
+ 4
+ 4
+ 8
+ 4
+ 8
+ 12
+ 4
+ 4
+ 2
+
+
diff --git a/.mystools/cppcheck/options.sh b/.mystools/cppcheck/options.sh
new file mode 100755
index 000000000..e6b40235c
--- /dev/null
+++ b/.mystools/cppcheck/options.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+OPTIONS="--quiet \
+ --error-exitcode=1 \
+ --force \
+ --enable=style,information \
+ --library=${LIBRARY:-avr} \
+ --platform="${TOOLCONFIG}"/${PLATFORM:-avr.xml} \
+ --includes-file="${TOOLCONFIG}"/includes.cfg \
+ --suppressions-list="${TOOLCONFIG}"/suppressions.cfg"
+
+echo $OPTIONS
diff --git a/.mystools/cppcheck/run.sh b/.mystools/cppcheck/run.sh
new file mode 100755
index 000000000..f674d4586
--- /dev/null
+++ b/.mystools/cppcheck/run.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Instantiate the tool runtime shim
+. "$(git mystoolspath).bundle_runtime.sh"
+
+# cppcheck takes one or more files as an argument.
+# --cached or for changed
+if [[ $# = 0 ]]; then
+ for file in $(modifiedSourceFiles); do
+ runBundle $file
+ done
+elif [[ $1 = '--cached' ]]; then
+ for file in $(stagedSourceFiles); do
+ runBundle $file
+ done
+else
+ eval "set -- $(git rev-parse --sq --prefix "$GIT_PREFIX" "$@")"
+ runBundle "$@"
+fi
diff --git a/.mystools/hooks/pre-commit.sh b/.mystools/hooks/pre-commit.sh
new file mode 100755
index 000000000..146d85f2d
--- /dev/null
+++ b/.mystools/hooks/pre-commit.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# instantiate mysensors tool runtime shim
+. "$(git mystoolspath).bundle_runtime.sh"
+
+###
+# astyle
+#
+errorsDetected=0
+for file in $(stagedSourceFiles); do
+ git astyle $file >/dev/null
+ if ! git diff-files --quiet $file >&2; then
+ warn "$file has been updated to match the MySensors core coding style."
+ errorsDetected=1
+ fi
+done
+
+[ $errorsDetected == 1 ] && err "Styling updates applied. Review the changes and use 'git add' to update your staged files."
+
+###
+# cppcheck
+#
+git cppcheck --cached || err "Correct the errors until cppcheck passes using 'git cppcheck --cached'."
+
+exit 0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d1617fd8f..ca7cd1614 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1 +1 @@
-To get started, sign the Contributor License Agreement.
\ No newline at end of file
+To get started, sign the Contributor License Agreement.
diff --git a/Documentation/Doxyfile b/Doxyfile
similarity index 100%
rename from Documentation/Doxyfile
rename to Doxyfile
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..742dd82d0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,154 @@
+#############################################################################
+#
+# Makefile for MySensors
+#
+#
+# The arduino library build part were inspired by
+# Arduino-Makefile project, Copyright (C) 2012 Sudar
+#
+# Description:
+# ------------
+# use make all and make install to install the gateway
+#
+
+CONFIG_FILE=Makefile.inc
+
+include $(CONFIG_FILE)
+
+CPPFLAGS+=-Ofast -g -Wall -Wextra
+DEPFLAGS=-MT $@ -MMD -MP
+
+GATEWAY_BIN=mysgw
+GATEWAY=$(BINDIR)/$(GATEWAY_BIN)
+GATEWAY_C_SOURCES=$(wildcard drivers/Linux/*.c)
+GATEWAY_CPP_SOURCES=$(wildcard drivers/Linux/*.cpp) examples_linux/mysgw.cpp
+GATEWAY_OBJECTS=$(patsubst %.c,$(BUILDDIR)/%.o,$(GATEWAY_C_SOURCES)) $(patsubst %.cpp,$(BUILDDIR)/%.o,$(GATEWAY_CPP_SOURCES))
+
+INCLUDES=-I. -I./core -I./drivers/Linux
+
+ifeq ($(SOC),$(filter $(SOC),BCM2835 BCM2836))
+RPI_C_SOURCES=$(wildcard drivers/RPi/*.c)
+RPI_CPP_SOURCES=$(wildcard drivers/RPi/*.cpp)
+GATEWAY_OBJECTS+=$(patsubst %.c,$(BUILDDIR)/%.o,$(RPI_C_SOURCES)) $(patsubst %.cpp,$(BUILDDIR)/%.o,$(RPI_CPP_SOURCES))
+
+INCLUDES+=-I./drivers/RPi
+endif
+
+# Gets include flags for library
+get_library_includes = $(if $(and $(wildcard $(1)/src), $(wildcard $(1)/library.properties)), \
+ -I$(1)/src, \
+ $(addprefix -I,$(1) $(wildcard $(1)/utility)))
+
+# Gets all sources with given extension (param2) for library (path = param1)
+# for old (1.0.x) layout looks in . and "utility" directories
+# for new (1.5.x) layout looks in src and recursively its subdirectories
+get_library_files = $(if $(and $(wildcard $(1)/src), $(wildcard $(1)/library.properties)), \
+ $(call rwildcard,$(1)/src/,*.$(2)), \
+ $(wildcard $(1)/*.$(2) $(1)/utility/*.$(2)))
+
+ifdef ARDUINO_LIB_DIR
+ARDUINO=arduino
+ARDUINO_LIBS:=$(shell find $(ARDUINO_LIB_DIR) -mindepth 1 -maxdepth 1 -type d)
+ARDUINO_INCLUDES:=$(foreach lib, $(ARDUINO_LIBS), $(call get_library_includes,$(lib)))
+ARDUINO_LIB_CPP_SRCS:=$(foreach lib, $(ARDUINO_LIBS), $(call get_library_files,$(lib),cpp))
+ARDUINO_LIB_C_SRCS:=$(foreach lib, $(ARDUINO_LIBS), $(call get_library_files,$(lib),c))
+ARDUINO_LIB_AS_SRCS:=$(foreach lib, $(ARDUINO_LIBS), $(call get_library_files,$(lib),S))
+ARDUINO_LIB_OBJS=$(patsubst $(ARDUINO_LIB_DIR)/%.cpp,$(BUILDDIR)/arduinolibs/%.cpp.o,$(ARDUINO_LIB_CPP_SRCS)) \
+ $(patsubst $(ARDUINO_LIB_DIR)/%.c,$(BUILDDIR)/arduinolibs/%.c.o,$(ARDUINO_LIB_C_SRCS)) \
+ $(patsubst $(ARDUINO_LIB_DIR)/%.S,$(BUILDDIR)/arduinolibs/%.S.o,$(ARDUINO_LIB_AS_SRCS))
+
+INCLUDES+=$(ARDUINO_INCLUDES)
+DEPS+=$(ARDUINO_LIB_OBJS:.o=.d)
+endif
+
+DEPS+=$(GATEWAY_OBJECTS:.o=.d)
+
+.PHONY: all createdir cleanconfig clean install uninstall
+
+all: createdir $(ARDUINO) $(GATEWAY)
+
+createdir:
+ @mkdir -p $(BUILDDIR) $(BINDIR)
+
+# Arduino libraries Build
+$(ARDUINO): CPPFLAGS+=-DARDUINO=100
+$(ARDUINO): $(ARDUINO_LIB_OBJS)
+ @printf "[Done building Arduino Libraries]\n"
+
+# Gateway Build
+$(GATEWAY): $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS)
+ $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS)
+
+# Include all .d files
+-include $(DEPS)
+
+$(BUILDDIR)/arduinolibs/%.cpp.o: $(ARDUINO_LIB_DIR)/%.cpp
+ @mkdir -p $(dir $@)
+ $(CXX) $(DEPFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
+
+$(BUILDDIR)/arduinolibs/%.c.o: $(ARDUINO_LIB_DIR)/%.c
+ @mkdir -p $(dir $@)
+ $(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -c $< -o $@
+
+$(BUILDDIR)/arduinolibs/%.S.o: $(ARDUINO_LIB_DIR)/%.S
+ @mkdir -p $(dir $@)
+ $(CC) $(DEPFLAGS) $(CPPFLAGS) $(ASFLAGS) $(INCLUDES) -c $< -o $@
+
+$(BUILDDIR)/%.o: %.cpp
+ @mkdir -p $(dir $@)
+ $(CXX) $(DEPFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
+
+$(BUILDDIR)/%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -c $< -o $@
+
+# clear configuration files
+cleanconfig:
+ @echo "[Cleaning configuration]"
+ rm -rf $(CONFIG_FILE)
+
+# clear build files
+clean:
+ @echo "[Cleaning]"
+ rm -rf $(BUILDDIR) $(BINDIR)
+
+$(CONFIG_FILE):
+ @echo "[Running configure]"
+ @./configure --no-clean
+
+install: all install-gateway install-initscripts
+
+install-gateway:
+ @echo "Installing $(GATEWAY) to ${DESTDIR}$(GATEWAY_DIR)"
+ @install -m 0755 $(GATEWAY) ${DESTDIR}$(GATEWAY_DIR)
+
+install-initscripts:
+ifeq ($(INIT_SYSTEM), systemd)
+ install -m0644 initscripts/mysgw.systemd ${DESTDIR}/etc/systemd/system/mysgw.service
+ @sed -i -e "s|%gateway_dir%|${GATEWAY_DIR}|g" ${DESTDIR}/etc/systemd/system/mysgw.service
+ systemctl daemon-reload
+ @echo "MySensors gateway has been installed, to add to the boot run:"
+ @echo " sudo systemctl enable mysgw.service"
+ @echo "To start the gateway run:"
+ @echo " sudo systemctl start mysgw.service"
+else ifeq ($(INIT_SYSTEM), sysvinit)
+ install -m0755 initscripts/mysgw.sysvinit ${DESTDIR}/etc/init.d/mysgw
+ @sed -i -e "s|%gateway_dir%|${GATEWAY_DIR}|g" ${DESTDIR}/etc/init.d/mysgw
+ @echo "MySensors gateway has been installed, to add to the boot run:"
+ @echo " sudo update-rc.d mysgw defaults"
+ @echo "To start the gateway run:"
+ @echo " sudo service mysgw start"
+endif
+
+uninstall:
+ifeq ($(INIT_SYSTEM), systemd)
+ @echo "Stopping daemon mysgw (ignore errors)"
+ -@systemctl stop mysgw.service
+ @echo "removing files"
+ rm /etc/systemd/system/mysgw.service $(GATEWAY_DIR)/$(GATEWAY_BIN)
+else ifeq ($(INIT_SYSTEM), sysvinit)
+ @echo "Stopping daemon mysgw (ignore errors)"
+ -@service mysgw stop
+ @echo "removing files"
+ rm /etc/init.d/mysgw $(GATEWAY_DIR)/$(GATEWAY_BIN)
+endif
diff --git a/MyConfig.h b/MyConfig.h
index 2a9c5da74..16f6da722 100644
--- a/MyConfig.h
+++ b/MyConfig.h
@@ -2,7 +2,7 @@
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
- * repeater and gateway builds a routing tables in EEPROM which keeps track of the
+ * repeater and gateway builds a routing tables in RAM or EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad
@@ -67,6 +67,15 @@
#ifndef MY_BAUD_RATE
#define MY_BAUD_RATE 115200
#endif
+/**
+* @def MY_SERIAL_OUTPUT_SIZE
+* @brief Max. characters for serial output.
+*/
+#ifndef MY_SERIAL_OUTPUT_SIZE
+#define MY_SERIAL_OUTPUT_SIZE (120u)
+#endif
+
+
// Disables over-the-air reset of node
//#define MY_DISABLE_REMOTE_RESET
@@ -79,54 +88,98 @@
//#define MY_RADIO_NRF24
//#define MY_RADIO_RFM69
+//#define MY_RADIO_RFM95
//#define MY_RS485
+/**
+* @def MY_RAM_ROUTING_TABLE_FEATURE
+* @brief If enabled, the routing table is kept in RAM (if memory allows) and saved in regular intervals.
+* note: AVR has limited memory, use with care
+*/
+#define MY_RAM_ROUTING_TABLE_FEATURE
+
+/**
+* @def MY_ROUTING_TABLE_SAVE_INTERVAL_MS
+* @brief Interval to dump content of routing table to eeprom
+*/
+#ifndef MY_ROUTING_TABLE_SAVE_INTERVAL_MS
+#define MY_ROUTING_TABLE_SAVE_INTERVAL_MS (30*60*1000ul)
+#endif
/**
* @def MY_TRANSPORT_SANITY_CHECK
-* @brief If enabled, node will check transport in regular intervals to detect HW issues and re-initialize in case of failure. This feature is enabled for all repeater nodes (incl. GW)
+* @brief If enabled, node will check transport in regular intervals to detect HW issues and re-initialize in case of failure.
+* This feature is enabled for all repeater nodes (incl. GW)
*/
//#define MY_TRANSPORT_SANITY_CHECK
+
+/**
+* @def MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS
+* @brief Interval (in ms) for transport sanity checks
+*/
+#ifndef MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS
+#define MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS (15*60*1000ul)
+#endif
/**
-* @def MY_TRANSPORT_SANITY_CHECK_INTERVAL
-* @brief Interval (in ms) of transport sanity checks
+* @def MY_TRANSPORT_DISCOVERY_INTERVAL_MS
+* @brief This is a gateway-only feature: Interval (in ms) to issue network discovery checks
*/
-#ifndef MY_TRANSPORT_SANITY_CHECK_INTERVAL
-#define MY_TRANSPORT_SANITY_CHECK_INTERVAL ((uint32_t)60000)
+#ifndef MY_TRANSPORT_DISCOVERY_INTERVAL_MS
+#define MY_TRANSPORT_DISCOVERY_INTERVAL_MS (20*60*1000ul)
#endif
+
+/**
+ *@def MY_TRANSPORT_UPLINK_CHECK_DISABLED
+ *@brief If set, uplink check to GW is disabled during transport initialisation
+ */
+//#define MY_TRANSPORT_UPLINK_CHECK_DISABLED
+
+/**
+ *@def MY_TRANSPORT_MAX_TX_FAILURES
+ *@brief Set to override max. consecutive TX failures until SNP is initiated
+ */
+//#define MY_TRANSPORT_MAX_TX_FAILURES (10u)
+
/**
* @def MY_REGISTRATION_FEATURE
* @brief If enabled, node has to register to gateway/controller before allowed to send sensor data.
*/
#define MY_REGISTRATION_FEATURE
- /**
- * @def MY_REGISTRATION_RETRIES
- * @brief Number of registration retries if no reply received from GW/controller
- */
+/**
+* @def MY_REGISTRATION_RETRIES
+* @brief Number of registration retries if no reply received from GW/controller
+*/
#ifndef MY_REGISTRATION_RETRIES
-#define MY_REGISTRATION_RETRIES 3
+#define MY_REGISTRATION_RETRIES (3u)
#endif
- /**
- * @def MY_REGISTRATION_DEFAULT
- * @brief Node registration default - this applies if no registration response is recieved from controller
- */
-
+/**
+* @def MY_REGISTRATION_DEFAULT
+* @brief Node registration default - this applies if no registration response is received from controller
+*/
#define MY_REGISTRATION_DEFAULT true
- /**
- * @def MY_REGISTRATION_CONTROLLER
- * @brief If enabled, node registration request has to be handled by controller
- */
- // #define MY_REGISTRATION_CONTROLLER
+/**
+* @def MY_REGISTRATION_CONTROLLER
+* @brief If enabled, node registration request has to be handled by controller
+*/
+// #define MY_REGISTRATION_CONTROLLER
- /**
- * @def MY_CORE_COMPATIBILITY_CHECK
- * @brief If enabled, library compatibility is checked during node registration. Incompatible libraries are unable to send sensor data.
- */
+/**
+* @def MY_CORE_COMPATIBILITY_CHECK
+* @brief If enabled, library compatibility is checked during node registration. Incompatible libraries are unable to send sensor data.
+*/
#define MY_CORE_COMPATIBILITY_CHECK
+/**
+* @def MY_TRANSPORT_WAIT_READY_MS
+* @brief Timeout in MS until transport is ready during startup, set to 0 for no timeout
+*/
+#ifndef MY_TRANSPORT_WAIT_READY_MS
+#define MY_TRANSPORT_WAIT_READY_MS (0ul)
+#endif
+
/**
* @def MY_NODE_ID
* @brief Node id defaults to AUTO (tries to fetch id from controller).
@@ -149,18 +202,29 @@
*/
//#define MY_PARENT_NODE_IS_STATIC
-// Enables repeater functionality (relays messages from other nodes)
-// #define MY_REPEATER_FEATURE
+/**
+* @def MY_REPEATER_FEATURE
+* @brief Enables repeater functionality (relays messages from other nodes)
+*/
+//#define MY_REPEATER_FEATURE
+
+/**
+* @def MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS
+* @brief Timeout (in ms) to re-establish link if node is send to sleep and transport is not ready.
+*/
+#ifndef MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS
+#define MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS (10*1000ul)
+#endif
/**
- * @def MY_SMART_SLEEP_WAIT_DURATION
- * @brief The wait period before going to sleep when using smartSleep-functions.
+ * @def MY_SMART_SLEEP_WAIT_DURATION_MS
+ * @brief The wait period (in ms) before going to sleep when using smartSleep-functions.
*
* This period has to be long enough for controller to be able to send out
* potential buffered messages.
*/
-#ifndef MY_SMART_SLEEP_WAIT_DURATION
-#define MY_SMART_SLEEP_WAIT_DURATION 500
+#ifndef MY_SMART_SLEEP_WAIT_DURATION_MS
+#define MY_SMART_SLEEP_WAIT_DURATION_MS (500ul)
#endif
/**********************************
@@ -199,7 +263,7 @@
* @brief Max buffersize needed for messages coming from controller.
*/
#ifndef MY_GATEWAY_MAX_RECEIVE_LENGTH
-#define MY_GATEWAY_MAX_RECEIVE_LENGTH 100
+#define MY_GATEWAY_MAX_RECEIVE_LENGTH (100u)
#endif
/**
@@ -207,7 +271,7 @@
* @brief Max buffer size when sending messages.
*/
#ifndef MY_GATEWAY_MAX_SEND_LENGTH
-#define MY_GATEWAY_MAX_SEND_LENGTH 120
+#define MY_GATEWAY_MAX_SEND_LENGTH (120u)
#endif
/**
@@ -215,7 +279,7 @@
* @brief Max number of parallel clients (sever mode).
*/
#ifndef MY_GATEWAY_MAX_CLIENTS
-#define MY_GATEWAY_MAX_CLIENTS 1
+#define MY_GATEWAY_MAX_CLIENTS (1u)
#endif
@@ -223,25 +287,22 @@
/**********************************
* Information LEDs blinking
***********************************/
-// This feature enables LEDs blinking on message receive, transmit
-// or if some error occurred. This was commonly used only in gateways,
-// but now can be used in any sensor node. Also the LEDs can now be
-// disabled in the gateway.
-
-//#define MY_LEDS_BLINKING_FEATURE
+// If one of the following is defined here, or in the sketch, the pin will be used for the
+// corresponding led function.
+// They have to be enabled here (or in your sketch). Replace x with the pin number you have the LED on.
+//
+// NOTE!! that on some platforms (for example sensebender GW) the hardware variant can enable LEDs by default,
+// These defaults can be overridden by defining one of these.
+//#define MY_DEFAULT_ERR_LED_PIN x
+//#define MY_DEFAULT_TX_LED_PIN x
+//#define MY_DEFAULT_RX_LED_PIN x
-// The following setting allows you to inverse the blinking feature MY_LEDS_BLINKING_FEATURE
+// The following setting allows you to inverse the LED blinking
// When MY_WITH_LEDS_BLINKING_INVERSE is enabled LEDSs are normally turned on and switches
// off when blinking
//#define MY_WITH_LEDS_BLINKING_INVERSE
-// The following defines can be used to set the port pin, that the LED is connected to
-// If one of the following is defined here, or in the sketch, MY_LEDS_BLINKING_FEATURE will be
-// enabled by default. (Replace x with the pin number you have the LED on)
-//#define MY_DEFAULT_ERR_LED_PIN x
-//#define MY_DEFAULT_TX_LED_PIN x
-//#define MY_DEFAULT_RX_LED_PIN x
/**********************************************
* Gateway inclusion button/mode configuration
@@ -262,11 +323,11 @@
* @brief The default input pin used for the inclusion mode button.
*/
#ifndef MY_INCLUSION_MODE_BUTTON_PIN
- #if defined(ARDUINO_ARCH_ESP8266)
- #define MY_INCLUSION_MODE_BUTTON_PIN 5
- #else
- #define MY_INCLUSION_MODE_BUTTON_PIN 3
- #endif
+#if defined(ARDUINO_ARCH_ESP8266)
+#define MY_INCLUSION_MODE_BUTTON_PIN 5
+#else
+#define MY_INCLUSION_MODE_BUTTON_PIN 3
+#endif
#endif
/**
@@ -316,6 +377,15 @@
*/
//#define MY_SIGNING_REQUEST_SIGNATURES
+/**
+ * @def MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL
+ * @brief Enable this to have gateway require all nodes in the network to sign messages sent to it. @ref MY_SIGNING_REQUEST_SIGNATURES must also be set.
+ *
+ * Use this for maximum security, but be aware that every single node will have to be personalized before they can be used.
+ * Note that if this is enabled, and whitelisting is also enabled, whitelisting will also be in effect for all nodes.
+ */
+//#define MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL
+
/**
* @def MY_VERIFICATION_TIMEOUT_MS
* @brief Define a suitable timeout for a signature verification session
@@ -382,6 +452,18 @@
#define MY_RS485_MAX_MESSAGE_LENGTH 40
#endif
+/**
+ * @def MY_RS485_DE_PIN
+ * @brief RS485 driver enable pin.
+ */
+//#define MY_RS485_DE_PIN 2
+
+/**
+ * @def MY_RS485_HWSERIAL
+ * @brief Enable this if RS485 is connected to a hardware serial port.
+ */
+//#define MY_RS485_HWSERIAL Serial1
+
/**********************************
* NRF24L01P Driver Defaults
***********************************/
@@ -406,13 +488,15 @@
* @brief Default RF24 chip enable pin setting. Override in sketch if needed.
*/
#ifndef MY_RF24_CE_PIN
- #if defined(ARDUINO_ARCH_ESP8266)
- #define MY_RF24_CE_PIN 4
- #elif defined(ARDUINO_ARCH_SAMD)
- #define MY_RF24_CE_PIN 27
- #else
- #define MY_RF24_CE_PIN 9
- #endif
+#if defined(ARDUINO_ARCH_ESP8266)
+#define MY_RF24_CE_PIN 4
+#elif defined(ARDUINO_ARCH_SAMD)
+#define MY_RF24_CE_PIN 27
+#elif defined(LINUX_ARCH_RASPBERRYPI)
+#define MY_RF24_CE_PIN 22
+#else
+#define MY_RF24_CE_PIN 9
+#endif
#endif
/**
@@ -420,13 +504,33 @@
* @brief Default RF24 chip select pin setting. Override in sketch if needed.
*/
#ifndef MY_RF24_CS_PIN
- #if defined(ARDUINO_ARCH_ESP8266)
- #define MY_RF24_CS_PIN 15
- #elif defined(ARDUINO_ARCH_SAMD)
- #define MY_RF24_CS_PIN 3
- #else
- #define MY_RF24_CS_PIN 10
- #endif
+#if defined(ARDUINO_ARCH_ESP8266)
+#define MY_RF24_CS_PIN 15
+#elif defined(ARDUINO_ARCH_SAMD)
+#define MY_RF24_CS_PIN 3
+#elif defined(LINUX_ARCH_RASPBERRYPI)
+#define MY_RF24_CS_PIN 24
+#else
+#define MY_RF24_CS_PIN 10
+#endif
+#endif
+
+/**
+* @def MY_RX_MESSAGE_BUFFER_FEATURE
+* @brief This enabled the receiving buffer feature.
+*
+* This feature is currently not supported for RFM69 and RS485, for RF24 MY_RF24_IRQ_PIN has to be defined.
+*/
+//#define MY_RX_MESSAGE_BUFFER_FEATURE
+
+/**
+ * @def MY_RX_MESSAGE_BUFFER_SIZE
+ * @brief Declare the amount of incoming messages that can be buffered.
+ */
+#ifdef MY_RX_MESSAGE_BUFFER_FEATURE
+#ifndef MY_RX_MESSAGE_BUFFER_SIZE
+#define MY_RX_MESSAGE_BUFFER_SIZE (20)
+#endif
#endif
/**
@@ -483,14 +587,6 @@
#define MY_RF24_ADDR_WIDTH 5
#endif
-/**
- * @def MY_RF24_SANITY_CHECK
- * @brief RF24 sanity check to verify functional RF module
- *
- * This reads back and compares configuration registers. Disable if using non-P modules
- */
-#define MY_RF24_SANITY_CHECK
-
// Enable SOFTSPI for NRF24L01, useful for the W5100 Ethernet module
//#define MY_SOFTSPI
@@ -543,9 +639,9 @@
* @brief Set to true if @ref MY_IS_RFM69HW is set.
*/
#ifdef MY_IS_RFM69HW
- #define MY_RFM69HW true
+#define MY_RFM69HW true
#else
- #define MY_RFM69HW false
+#define MY_RFM69HW false
#endif
/**
@@ -577,16 +673,97 @@
* @brief RF69 IRQ pin number.
*/
#ifndef MY_RF69_IRQ_NUM
- #if defined(ARDUINO_ARCH_ESP8266)
- #define MY_RF69_IRQ_NUM MY_RF69_IRQ_PIN
- #else
- #define MY_RF69_IRQ_NUM RF69_IRQ_NUM
- #endif
+#if defined(ARDUINO_ARCH_ESP8266)
+#define MY_RF69_IRQ_NUM RF69_IRQ_PIN
+#else
+#define MY_RF69_IRQ_NUM RF69_IRQ_NUM
+#endif
#endif
// Enables RFM69 encryption (all nodes and gateway must have this enabled, and all must be personalized with the same AES key)
//#define MY_RFM69_ENABLE_ENCRYPTION
+/**********************************
+* RFM95 driver defaults
+***********************************/
+
+/**
+ * @def MY_RFM95_FREQUENCY
+ * @brief RFM95 frequency
+ *
+ * This must match the hardware version of the RFM95 radio.
+ */
+#ifndef MY_RFM95_FREQUENCY
+#define MY_RFM95_FREQUENCY (868.1f)
+#endif
+/**
+* @def MY_RFM95_MODEM_CONFIGRUATION
+* @brief RFM95 modem configuration, see table
+*
+* BW = Bandwidth in kHz
+* CR = Error correction code
+* SF = Spreading factor, chips / symbol
+*
+* | CONFIG | BW | CR | SF | Comment
+* |------------------------|-------|-----|------|-----------------------------
+* | RFM95_BW125CR45SF128 | 125 | 4/5 | 128 | Default, medium range
+* | RFM95_BW500CR45SF128 | 500 | 4/5 | 128 | Fast, short range
+* | RFM95_BW31_25CR48SF512 | 31.25 | 4/8 | 512 | Slow, long range
+* | RFM95_BW125CR48SF4096 | 125 | 4/8 | 4096 | Slow, long range
+*
+*/
+
+#ifndef MY_RFM95_MODEM_CONFIGRUATION
+// default
+#define MY_RFM95_MODEM_CONFIGRUATION RFM95_BW125CR45SF128
+#endif
+
+/**
+ * @def MY_RFM95_RST_PIN
+ * @brief RFM95 reset pin, uncomment if used
+ */
+//#define MY_RFM95_RST_PIN RFM95_RST_PIN
+
+/**
+ * @def MY_RFM95_IRQ_PIN
+ * @brief RFM95 IRQ pin
+ */
+#ifndef MY_RFM95_IRQ_PIN
+#define MY_RFM95_IRQ_PIN RFM95_IRQ_PIN
+#endif
+
+/**
+ * @def MY_RFM95_SPI_CS
+ * @brief RFM95 SPI chip select pin
+ */
+#ifndef MY_RFM95_SPI_CS
+#define MY_RFM95_SPI_CS RFM95_SPI_CS
+#endif
+
+/**
+ * @def MY_RFM95_TX_POWER
+ * @brief RFM95 TX power level.
+ */
+#ifndef MY_RFM95_TX_POWER
+#define MY_RFM95_TX_POWER 13
+#endif
+
+/**
+* @def MY_RFM95_ATC_MODE_DISABLED
+* @brief Enable to disable ATC mode
+*/
+//#define MY_RFM95_ATC_MODE_DISABLED
+
+/**
+* @def MY_RFM95_ATC_TARGET_RSSI
+* @brief Traget RSSI level for ATC mode
+*/
+#ifndef MY_RFM95_ATC_TARGET_RSSI
+#define MY_RFM95_ATC_TARGET_RSSI (-60)
+#endif
+
+
+
/**************************************
* Ethernet Gateway Transport Defaults
***************************************/
@@ -595,14 +772,19 @@
//#define MY_GATEWAY_W5100
//#define MY_GATEWAY_ENC28J60
//#define MY_GATEWAY_ESP8266
+//#define MY_GATEWAY_LINUX
/**
* @def MY_PORT
* @brief The Ethernet TCP/UDP port to open on controller or gateway.
*/
#ifndef MY_PORT
+#ifdef MY_GATEWAY_MQTT_CLIENT
+#define MY_PORT 1883
+#else
#define MY_PORT 5003
#endif
+#endif
// Static ip address of gateway (if this is disabled, DHCP will be used)
//#define MY_IP_ADDRESS 192,168,178,66
@@ -632,6 +814,10 @@
// If MY_CONTROLLER_IP_ADDRESS is left un-defined, gateway acts as server allowing incoming connections.
//#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 254
+/**************************************
+* Node Locking
+***************************************/
+
/**
* @defgroup MyLockgrp MyNodeLock
* @ingroup internals
@@ -688,18 +874,89 @@
#endif
/** @}*/ // Node lock group
+/**********************************
+* ESP8266 Defaults
+***********************************/
+
+/**
+ * @def MY_ESP8266_SERIAL_MODE
+ * @brief Serial modes: SERIAL_FULL, SERIAL_RX_ONLY, SERIAL_TX_ONLY
+ *
+ * SERIAL_FULL: Default mode.
+ * SERIAL_TX_ONLY: allows to use RX (GPIO3) as a general purpose input/output.
+ * SERIAL_RX_ONLY: allows to use TX (GPIO1) as a general purpose input/output.
+ */
+#ifndef MY_ESP8266_SERIAL_MODE
+#define MY_ESP8266_SERIAL_MODE SERIAL_FULL
#endif
+/**************************************
+* Linux Settings
+***************************************/
+
+/**
+ * @def MY_LINUX_SERIAL_PORT
+ * @brief Serial device port
+ */
+#ifndef MY_LINUX_SERIAL_PORT
+#define MY_LINUX_SERIAL_PORT "/dev/ttyACM0"
+#endif
+
+/**
+ * @def MY_IS_SERIAL_PTY
+ * @brief Set serial as a pseudo terminal.
+ *
+ * Enable this if you need to connect to a controller running on the same device.
+ */
+//#define MY_IS_SERIAL_PTY
+
+/**
+ * @def MY_LINUX_SERIAL_PTY
+ * @brief Symlink name for the PTY device.
+ */
+#ifndef MY_LINUX_SERIAL_PTY
+#define MY_LINUX_SERIAL_PTY "/dev/ttyMySensorsGateway"
+#endif
+
+/**
+ * @def MY_LINUX_SERIAL_GROUPNAME
+ * @brief Grant access to the specified system group for the serial device.
+ */
+//#define MY_LINUX_SERIAL_GROUPNAME "tty"
+
+/**
+ * @def MY_LINUX_CONFIG_FILE
+ * @brief Set the filepath for the gateway config file
+ *
+ * For now the configuration file is only used to store the emulated eeprom state
+ */
+#ifndef MY_LINUX_CONFIG_FILE
+#define MY_LINUX_CONFIG_FILE "/etc/mysensors.dat"
+#endif
+
+#endif // MyConfig_h
+
// Doxygen specific constructs, not included when built normally
// This is used to enable disabled macros/definitions to be included in the documentation as well.
#if DOXYGEN
#define MY_SIGNING_ATSHA204
#define MY_SIGNING_SOFT
#define MY_SIGNING_REQUEST_SIGNATURES
+#define MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
+#define MY_RS485_HWSERIAL
#define MY_IS_RFM69HW
#define MY_PARENT_NODE_IS_STATIC
#define MY_REGISTRATION_CONTROLLER
+#define MY_TRANSPORT_UPLINK_CHECK_DISABLED
#define MY_DEBUG_VERBOSE_RF24
#define MY_TRANSPORT_SANITY_CHECK
+#define MY_RX_MESSAGE_BUFFER_FEATURE
+#define MY_RX_MESSAGE_BUFFER_SIZE
+#define MY_NODE_LOCK_FEATURE
+#define MY_REPEATER_FEATURE
+#define MY_LINUX_SERIAL_GROUPNAME
+#define MY_IS_SERIAL_PTY
+#define MY_RFM95_ATC_MODE_DISABLED
+#define MY_RFM95_RST_PIN
#endif
diff --git a/MySensors.h b/MySensors.h
index 189990e24..3c5bf3b15 100644
--- a/MySensors.h
+++ b/MySensors.h
@@ -40,61 +40,58 @@
* @def MY_NODE_TYPE
* @brief Contain a string describing the class of sketch/node (gateway/repeater/sensor).
*/
-#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60) || defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_MQTT_CLIENT)
- #define MY_GATEWAY_FEATURE
- #define MY_IS_GATEWAY (true)
- #define MY_NODE_TYPE "gateway"
+#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60) || defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX) || defined(MY_GATEWAY_MQTT_CLIENT)
+#define MY_GATEWAY_FEATURE
+#define MY_IS_GATEWAY (true)
+#define MY_NODE_TYPE "GW"
#elif defined(MY_REPEATER_FEATURE)
- #define MY_IS_GATEWAY (false)
- #define MY_NODE_TYPE "repeater"
+#define MY_IS_GATEWAY (false)
+#define MY_NODE_TYPE "REPEATER"
#else
- #define MY_IS_GATEWAY (false)
- #define MY_NODE_TYPE "sensor"
+#define MY_IS_GATEWAY (false)
+#define MY_NODE_TYPE "NODE"
#endif
// Enable radio "feature" if one of the radio types was enabled
-#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485)
- #define MY_RADIO_FEATURE
+#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RADIO_RFM95) || defined(MY_RS485)
+#define MY_SENSOR_NETWORK
#endif
// HARDWARE
#if defined(ARDUINO_ARCH_ESP8266)
- // Remove PSTR macros from debug prints
- #undef PSTR
- #define PSTR(x) (x)
- //#undef F
- //#define F(x) (x)
- #include "core/MyHwESP8266.cpp"
+#include "core/MyHwESP8266.cpp"
#elif defined(ARDUINO_ARCH_AVR)
- #include "core/MyHwATMega328.cpp"
+#include "drivers/AVR/DigitalWriteFast/digitalWriteFast.h"
+#include "core/MyHwATMega328.cpp"
#elif defined(ARDUINO_ARCH_SAMD)
- #include "core/MyHwSAMD.cpp"
+#include "core/MyHwSAMD.cpp"
+#elif defined(__linux__)
+#ifdef LINUX_ARCH_RASPBERRYPI
+#include "core/MyHwRPi.cpp"
+#else
+#include "core/MyHwLinuxGeneric.cpp"
+#endif
#endif
// LEDS
-#if !defined(MY_DEFAULT_ERR_LED_PIN) & defined(MY_HW_ERR_LED_PIN)
- #define MY_DEFAULT_ERR_LED_PIN MY_HW_ERR_LED_PIN
+#if !defined(MY_DEFAULT_ERR_LED_PIN) && defined(MY_HW_ERR_LED_PIN)
+#define MY_DEFAULT_ERR_LED_PIN MY_HW_ERR_LED_PIN
#endif
#if !defined(MY_DEFAULT_TX_LED_PIN) && defined(MY_HW_TX_LED_PIN)
- #define MY_DEFAULT_TX_LED_PIN MY_HW_TX_LED_PIN
+#define MY_DEFAULT_TX_LED_PIN MY_HW_TX_LED_PIN
#endif
#if !defined(MY_DEFAULT_RX_LED_PIN) && defined(MY_HW_TX_LED_PIN)
- #define MY_DEFAULT_RX_LED_PIN MY_HW_TX_LED_PIN
-#endif
-
-// Not necessary to include blinking feature if no LED's are defined!
-#if defined(MY_LEDS_BLINKING_FEATURE) && !defined(MY_DEFAULT_RX_LED_PIN) && !defined(MY_DEFAULT_TX_LED_PIN) && !defined(MY_ERR_LED_PIN)
- #undef MY_LEDS_BLINKING_FEATURE
+#define MY_DEFAULT_RX_LED_PIN MY_HW_TX_LED_PIN
#endif
-// Enable LED BLINKING FEATURE, if there are any LEDs defined.
-#if defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_ERR_LED) || defined(MY_DEFAULT_TX_LED_PIN)
- #define MY_LEDS_BLINKING_FEATURE
+#if defined(MY_LEDS_BLINKING_FEATURE)
+#error MY_LEDS_BLINKING_FEATURE is now removed from MySensors core,\
+define MY_DEFAULT_ERR_LED_PIN, MY_DEFAULT_TX_LED_PIN or\
+MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs
#endif
-
/**
* @def MY_DEFAULT_LED_BLINK_PERIOD
* @brief Default LEDs blinking period in milliseconds.
@@ -102,44 +99,11 @@
#ifndef MY_DEFAULT_LED_BLINK_PERIOD
#define MY_DEFAULT_LED_BLINK_PERIOD 300
#endif
-/**
- * @def MY_DEFAULT_RX_LED_PIN
- * @brief The RX LED default pin.
- */
-#ifndef MY_DEFAULT_RX_LED_PIN
- #if defined(ARDUINO_ARCH_ESP8266)
- #define MY_DEFAULT_RX_LED_PIN 8
- #else
- #define MY_DEFAULT_RX_LED_PIN 6
- #endif
-#endif
-/**
- * @def MY_DEFAULT_TX_LED_PIN
- * @brief The TX LED default pin.
- */
-#ifndef MY_DEFAULT_TX_LED_PIN
- #if defined(ARDUINO_ARCH_ESP8266)
- #define MY_DEFAULT_TX_LED_PIN 9
- #else
- #define MY_DEFAULT_TX_LED_PIN 5
- #endif
-#endif
-/**
- * @def MY_DEFAULT_ERR_LED_PIN
- * @brief The Error LED default pin.
- */
-#ifndef MY_DEFAULT_ERR_LED_PIN
- #if defined(ARDUINO_ARCH_ESP8266)
- #define MY_DEFAULT_ERR_LED_PIN 7
- #else
- #define MY_DEFAULT_ERR_LED_PIN 4
- #endif
-#endif
-#if defined(MY_LEDS_BLINKING_FEATURE)
- #include "core/MyLeds.cpp"
+#if defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_TX_LED_PIN) || defined(MY_DEFAULT_ERR_LED_PIN)
+#include "core/MyLeds.cpp"
#else
- #include "core/MyLeds.h"
+#include "core/MyLeds.h"
#endif
#include "core/MyIndication.cpp"
@@ -147,145 +111,226 @@
// INCLUSION MODE
#if defined(MY_INCLUSION_MODE_FEATURE)
- #include "core/MyInclusionMode.cpp"
+#include "core/MyInclusionMode.cpp"
#endif
// SIGNING
#if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT)
- #define MY_SIGNING_FEATURE
+#define MY_SIGNING_FEATURE
#endif
#include "core/MySigning.cpp"
#include "drivers/ATSHA204/sha256.cpp"
#if defined(MY_SIGNING_FEATURE)
- // SIGNING COMMON FUNCTIONS
- #if defined(MY_SIGNING_ATSHA204) && defined(MY_SIGNING_SOFT)
- #error Only one signing engine can be activated
- #endif
+// SIGNING COMMON FUNCTIONS
+#if defined(MY_SIGNING_ATSHA204) && defined(MY_SIGNING_SOFT)
+#error Only one signing engine can be activated
+#endif
+#if defined(MY_SIGNING_ATSHA204) && defined(__linux__)
+#error No support for ATSHA204 on this platform
+#endif
- #if defined(MY_SIGNING_ATSHA204)
- #include "core/MySigningAtsha204.cpp"
- #include "drivers/ATSHA204/ATSHA204.cpp"
- #elif defined(MY_SIGNING_SOFT)
- #include "core/MySigningAtsha204Soft.cpp"
- #endif
+#if defined(MY_SIGNING_ATSHA204)
+#include "core/MySigningAtsha204.cpp"
+#include "drivers/ATSHA204/ATSHA204.cpp"
+#elif defined(MY_SIGNING_SOFT)
+#include "core/MySigningAtsha204Soft.cpp"
+#endif
#endif
// FLASH
#if defined(MY_OTA_FIRMWARE_FEATURE)
- #include "drivers/SPIFlash/SPIFlash.cpp"
- #include "core/MyOTAFirmwareUpdate.cpp"
+#include "drivers/SPIFlash/SPIFlash.cpp"
+#include "core/MyOTAFirmwareUpdate.cpp"
#endif
// GATEWAY - TRANSPORT
+#if defined(MY_CONTROLLER_IP_ADDRESS) || defined(MY_CONTROLLER_URL_ADDRESS)
+#define MY_GATEWAY_CLIENT_MODE
+#endif
+#if defined(MY_USE_UDP) && !defined(MY_GATEWAY_CLIENT_MODE)
+#error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS for UDP
+#endif
+
#if defined(MY_GATEWAY_MQTT_CLIENT)
- #if defined(MY_RADIO_FEATURE)
- // We assume that a gateway having a radio also should act as repeater
- #define MY_REPEATER_FEATURE
- #endif
- // GATEWAY - COMMON FUNCTIONS
- // We only support MQTT Client using W5100 and ESP8266 at the moment
- #if !(defined(MY_CONTROLLER_URL_ADDRESS) || defined(MY_CONTROLLER_IP_ADDRESS))
- #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS
- #endif
-
- #if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX)
- #error You must specify a topic publish prefix MY_MQTT_PUBLISH_TOPIC_PREFIX for this MQTT client
- #endif
- #if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX)
- #error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client
- #endif
- #if !defined(MY_MQTT_CLIENT_ID)
- #error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client
- #endif
-
- #include "drivers/PubSubClient/PubSubClient.cpp"
- #include "core/MyGatewayTransport.cpp"
- #include "core/MyGatewayTransportMQTTClient.cpp"
+#if defined(MY_SENSOR_NETWORK)
+// We assume that a gateway having a radio also should act as repeater
+#define MY_REPEATER_FEATURE
+#endif
+// GATEWAY - COMMON FUNCTIONS
+// We support MQTT Client using W5100, ESP8266 and Linux
+#if !defined(MY_GATEWAY_CLIENT_MODE)
+#error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS
+#endif
+
+#if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX)
+#error You must specify a topic publish prefix MY_MQTT_PUBLISH_TOPIC_PREFIX for this MQTT client
+#endif
+#if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX)
+#error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client
+#endif
+
+#if !defined(MY_MQTT_CLIENT_ID)
+#error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client
+#endif
+
+#include "core/MyGatewayTransport.cpp"
+#include "core/MyProtocolMySensors.cpp"
+
+#if defined(MY_GATEWAY_LINUX)
+#include "drivers/Linux/EthernetClient.h"
+#include "drivers/Linux/EthernetServer.h"
+#include "drivers/Linux/IPAddress.h"
+#endif
+#include "drivers/PubSubClient/PubSubClient.cpp"
+#include "core/MyGatewayTransportMQTTClient.cpp"
#elif defined(MY_GATEWAY_FEATURE)
- // GATEWAY - COMMON FUNCTIONS
- #include "core/MyGatewayTransport.cpp"
-
- // We currently only support one protocol at the moment, enable it.
- #include "core/MyProtocolMySensors.cpp"
-
- // GATEWAY - CONFIGURATION
- #if defined(MY_RADIO_FEATURE)
- // We assume that a gateway having a radio also should act as repeater
- #define MY_REPEATER_FEATURE
- #endif
- #if defined(MY_CONTROLLER_IP_ADDRESS)
- #define MY_GATEWAY_CLIENT_MODE
- #endif
- #if !defined(MY_PORT)
- #error You must define MY_PORT (controller or gatway port to open)
- #endif
- #if defined(MY_GATEWAY_ESP8266)
- // GATEWAY - ESP8266
- #include "core/MyGatewayTransportEthernet.cpp"
- #elif defined(MY_GATEWAY_W5100)
- // GATEWAY - W5100
- #include "core/MyGatewayTransportEthernet.cpp"
- #elif defined(MY_GATEWAY_ENC28J60)
- // GATEWAY - ENC28J60
- #if defined(MY_USE_UDP)
- #error UDP mode is not available for ENC28J60
- #endif
- #include "core/MyGatewayTransportEthernet.cpp"
- #elif defined(MY_GATEWAY_SERIAL)
- // GATEWAY - SERIAL
- #include "core/MyGatewayTransportSerial.cpp"
- #endif
+// GATEWAY - COMMON FUNCTIONS
+#include "core/MyGatewayTransport.cpp"
+
+#include "core/MyProtocolMySensors.cpp"
+
+// GATEWAY - CONFIGURATION
+#if defined(MY_SENSOR_NETWORK)
+// We assume that a gateway having a radio also should act as repeater
+#define MY_REPEATER_FEATURE
+#endif
+#if !defined(MY_PORT)
+#error You must define MY_PORT (controller or gatway port to open)
+#endif
+#if defined(MY_GATEWAY_ESP8266)
+// GATEWAY - ESP8266
+#include "core/MyGatewayTransportEthernet.cpp"
+#elif defined(MY_GATEWAY_LINUX)
+// GATEWAY - Generic Linux
+#include "drivers/Linux/EthernetClient.h"
+#include "drivers/Linux/EthernetServer.h"
+#include "drivers/Linux/IPAddress.h"
+#include "core/MyGatewayTransportEthernet.cpp"
+#elif defined(MY_GATEWAY_W5100)
+// GATEWAY - W5100
+#include "core/MyGatewayTransportEthernet.cpp"
+#elif defined(MY_GATEWAY_ENC28J60)
+// GATEWAY - ENC28J60
+#if defined(MY_USE_UDP)
+#error UDP mode is not available for ENC28J60
+#endif
+#include "core/MyGatewayTransportEthernet.cpp"
+#elif defined(MY_GATEWAY_SERIAL)
+// GATEWAY - SERIAL
+#include "core/MyGatewayTransportSerial.cpp"
+#endif
+#endif
+
+// RAM ROUTING TABLE
+#if defined(MY_RAM_ROUTING_TABLE_FEATURE) && defined(MY_REPEATER_FEATURE)
+// activate feature based on architecture
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_SAMD) || defined(LINUX_ARCH_RASPBERRYPI)
+#define MY_RAM_ROUTING_TABLE_ENABLED
+#elif defined(ARDUINO_ARCH_AVR)
+// memory limited, enable with care
+// #define MY_RAM_ROUTING_TABLE_ENABLED
+#endif
+#endif
+
+#if defined(MY_REPEATER_FEATURE)
+#define MY_TRANSPORT_SANITY_CHECK
+#endif
+
+
+#if defined(MY_TRANSPORT_DONT_CARE_MODE)
+#error This directive is deprecated, set MY_TRANSPORT_WAIT_READY_MS instead!
#endif
// RADIO
-#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485)
- // SOFTSPI
- #ifdef MY_SOFTSPI
- #if defined(ARDUINO_ARCH_ESP8266)
- #error Soft SPI is not available on ESP8266
- #endif
- #include "drivers/AVR/DigitalIO/DigitalIO.h"
- #endif
-
- #include "core/MyTransport.cpp"
- #if (defined(MY_RADIO_NRF24) && defined(MY_RADIO_RFM69)) || (defined(MY_RADIO_NRF24) && defined(MY_RS485)) || (defined(MY_RADIO_RFM69) && defined(MY_RS485))
- #error Only one forward link driver can be activated
- #endif
- #if defined(MY_RADIO_NRF24)
- #if defined(MY_RF24_ENABLE_ENCRYPTION)
- #include "drivers/AES/AES.cpp"
- #endif
- #include "drivers/RF24/RF24.cpp"
- #include "core/MyTransportNRF24.cpp"
- #elif defined(MY_RS485)
- #include "drivers/AltSoftSerial/AltSoftSerial.cpp"
- #include "core/MyTransportRS485.cpp"
- #elif defined(MY_RADIO_RFM69)
- #include "drivers/RFM69/RFM69.cpp"
- #include "core/MyTransportRFM69.cpp"
- #endif
+#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RADIO_RFM95) ||defined(MY_RS485)
+// SOFTSPI
+#ifdef MY_SOFTSPI
+#if defined(ARDUINO_ARCH_ESP8266)
+#error Soft SPI is not available on ESP8266
+#endif
+#include "drivers/AVR/DigitalIO/DigitalIO.h"
+#endif
+
+#if defined(MY_RADIO_NRF24) && defined(__linux__) && !defined(LINUX_ARCH_RASPBERRYPI)
+#error No support for nRF24 radio on this platform
+#endif
+
+#include "core/MyTransport.cpp"
+
+// count enabled transports
+#if defined(MY_RADIO_NRF24)
+#define __RF24CNT 1
+#else
+#define __RF24CNT 0
+#endif
+#if defined(MY_RADIO_RFM69)
+#define __RFM69CNT 1
+#else
+#define __RFM69CNT 0
+#endif
+#if defined(MY_RADIO_RFM95)
+#define __RFM95CNT 1
+#else
+#define __RFM95CNT 0
+#endif
+#if defined(MY_RS485)
+#define __RS485CNT 1
+#else
+#define __RS485CNT 0
+#endif
+
+
+#if (__RF24CNT + __RFM69CNT + __RFM95CNT + __RS485CNT > 1)
+#error Only one forward link driver can be activated
+#endif
+
+#if defined(MY_RADIO_NRF24)
+#if defined(MY_RF24_ENABLE_ENCRYPTION)
+#include "drivers/AES/AES.cpp"
+#endif
+#include "drivers/RF24/RF24.cpp"
+#include "core/MyTransportNRF24.cpp"
+#elif defined(MY_RS485)
+#if !defined(MY_RS485_HWSERIAL)
+#if defined(__linux__)
+#error You must specify MY_RS485_HWSERIAL for RS485 transport
+#endif
+#include "drivers/AltSoftSerial/AltSoftSerial.cpp"
+#endif
+#include "core/MyTransportRS485.cpp"
+#elif defined(MY_RADIO_RFM69)
+#include "drivers/RFM69/RFM69.cpp"
+#include "core/MyTransportRFM69.cpp"
+#elif defined(MY_RADIO_RFM95)
+#include "drivers/RFM95/RFM95.cpp"
+#include "core/MyTransportRFM95.cpp"
+#endif
+#endif
+
+#if defined(MY_PARENT_NODE_IS_STATIC) && (MY_PARENT_NODE_ID == AUTO)
+#error Parent is static but no parent ID defined.
#endif
// Make sure to disable child features when parent feature is disabled
-#if !defined(MY_RADIO_FEATURE)
- #undef MY_OTA_FIRMWARE_FEATURE
- #undef MY_REPEATER_FEATURE
- #undef MY_SIGNING_NODE_WHITELISTING
- #undef MY_SIGNING_FEATURE
+#if !defined(MY_SENSOR_NETWORK)
+#undef MY_OTA_FIRMWARE_FEATURE
+#undef MY_REPEATER_FEATURE
+#undef MY_SIGNING_NODE_WHITELISTING
+#undef MY_SIGNING_FEATURE
#endif
#if !defined(MY_GATEWAY_FEATURE)
- #undef MY_INCLUSION_MODE_FEATURE
- #undef MY_INCLUSION_BUTTON_FEATURE
+#undef MY_INCLUSION_MODE_FEATURE
+#undef MY_INCLUSION_BUTTON_FEATURE
#endif
#if !defined(MY_CORE_ONLY)
- #if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RADIO_FEATURE)
- #error No forward link or gateway feature activated. This means nowhere to send messages! Pretty pointless.
- #endif
+#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_SENSOR_NETWORK)
+#error No forward link or gateway feature activated. This means nowhere to send messages! Pretty pointless.
+#endif
#endif
#include "core/MyCapabilities.h"
@@ -295,11 +340,13 @@
#include
#if !defined(MY_CORE_ONLY)
- #if defined(ARDUINO_ARCH_ESP8266)
- #include "core/MyMainESP8266.cpp"
- #else
- #include "core/MyMainDefault.cpp"
- #endif
+#if defined(ARDUINO_ARCH_ESP8266)
+#include "core/MyMainESP8266.cpp"
+#elif defined(__linux__)
+#include "core/MyMainLinux.cpp"
+#else
+#include "core/MyMainDefault.cpp"
+#endif
#endif
#endif
@@ -307,4 +354,5 @@
// This is used to enable disabled macros/definitions to be included in the documentation as well.
#if DOXYGEN
#define MY_GATEWAY_FEATURE
+#define MY_LEDS_BLINKING_FEATURE //!< \deprecated use MY_DEFAULT_RX_LED_PIN, MY_DEFAULT_TX_LED_PIN and/or MY_DEFAULT_ERR_LED_PIN instead **** DEPRECATED, DO NOT USE ****
#endif
diff --git a/README.md b/README.md
index 4dad5a3f9..ee6593b32 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,17 @@
-Arduino
-=======
-
-MySensors Arduino Library v2.0.0
+MySensors Library v2.1.0
Please visit www.mysensors.org for more information
Doxygen
-------
-[master](https://ci.mysensors.org/job/MySensorsArduino/branch/master/Doxygen_HTML/index.html) [development](https://ci.mysensors.org/job/MySensorsArduino/branch/development/Doxygen_HTML/index.html)
+[master](https://ci.mysensors.org/job/Verifiers/job/MySensors/branch/master/Doxygen_HTML/index.html) [development](https://ci.mysensors.org/job/Verifiers/job/MySensors/branch/development/Doxygen_HTML/index.html)
CI statuses
-----------
-Current build status of master branch: [![Build Status](https://ci.mysensors.org/job/Verifiers/job/MySensorsArduino/job/master/badge/icon)](https://ci.mysensors.org/job/Verifiers/job/MySensorsArduino/job/master/)
-
-Current build status of development branch: [![Build Status](https://ci.mysensors.org/job/Verifiers/job/MySensorsArduino/job/development/badge/icon)](https://ci.mysensors.org/job/Verifiers/job/MySensorsArduino/job/development/)
-
-Current build status of master branch (nightly build): [![Build Status](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightly/job/master/badge/icon)](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightly/job/master/)
+Current build status of master branch: [![Build Status](https://ci.mysensors.org/job/Verifiers/job/MySensors/job/master/badge/icon)](https://ci.mysensors.org/job/Verifiers/job/MySensors/job/master/)
-Current build status of development branch (nightly build): [![Build Status](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightly/job/development/badge/icon)](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightly/job/development/)
+Current build status of development branch: [![Build Status](https://ci.mysensors.org/job/Verifiers/job/MySensors/job/development/badge/icon)](https://ci.mysensors.org/job/Verifiers/job/MySensors/job/development/)
Current build status of master branch (nightly build of Arduino IDE): [![Build Status](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/master/badge/icon)](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/master/)
-Current build status of development branch (nightly build of Arduino IDE): [![Build Status](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/development/badge/icon)](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/development/)
+Current build status of development branch (nightly build of Arduino IDE): [![Build Status](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/development/badge/icon)](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/development/)
\ No newline at end of file
diff --git a/configure b/configure
new file mode 100755
index 000000000..7fee365a8
--- /dev/null
+++ b/configure
@@ -0,0 +1,497 @@
+#!/bin/bash
+# This version is heavily based on the work of mz-fuzzy (https://github.com/mz-fuzzy)
+# adapted to work with MySensors project.
+# Original work: https://github.com/TMRh20/RF24/blob/master/configure
+
+function help {
+cat < CPU defining/optimizing flags to be used. [configure autodetected]
+ --extra-cflags= Extra C flags passed to C compilation. []
+ --extra-cxxflags= Extra C++ flags passed to C++ compilation. []
+ --extra-ldflags= Extra C flags passed to linking. []
+ --c_compiler= C compiler. [arm-linux-gnueabihf-gcc][gcc]
+ --cxx_compiler= C++ compiler [arm-linux-gnueabihf-g++][g++]
+ --build-dir= Compiler directory to store object files. [build]
+ --bin-dir= Compiler directory to store binary files. [bin]
+ --arduino-lib-dir= Arduino library directory.
+ --no-clean Don't clean previous build artifacts.
+
+Installation options:
+ --prefix= Installation prefix path. [/usr/local]
+ --gateway-dir= Gateway files installation directory. [PREFIX/bin]
+
+MySensors options:
+ --my-debug=[enable|disable] Enables or disables MySensors core debugging. [enable]
+ --my-config-file= Config file path. [/etc/mysensors.dat]
+ --my-gateway=[ethernet|serial|mqtt]
+ Gateway type, set to none to disable gateway feature. [ethernet]
+ --my-node-id= Disable gateway feature and run as a node with given id.
+ --my-controller-url-address=
+ Controller or MQTT broker url.
+ --my-controller-ip-address=
+ Controller or MQTT broker ip.
+ --my-port= The port to keep open on gateway mode.
+ If gateway is set to mqtt, it sets the broker port.
+ --my-serial-port= Serial port. [/dev/ttyACM0]
+ --my-serial-baudrate= Serial baud rate. [115200]
+ --my-serial-is-pty Set the serial port to be a pseudo terminal. Use this if you want
+ to connect to a controller running on the same device.
+ --my-serial-pty= Symlink name for the PTY device. [/dev/ttyMySensorsGateway]
+ --my-serial-groupname=
+ Grant access to the specified system group for the serial device.
+ --my-mqtt-client-id= MQTT client id.
+ --my-mqtt-publish-topic-prefix=
+ MQTT publish topic prefix.
+ --my-mqtt-subscribe-topic-prefix=
+ MQTT subscribe topic prefix.
+ --my-transport=[none|nrf24|rs485|rfm95]
+ Transport type, set to none to disable transport feature. [nrf24]
+ --my-rf24-channel=<0-125> RF channel for the sensor net, 0-125. [76]
+ --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_LOW]
+ RF24 PA level. [RF24_PA_MAX]
+ --my-rf24-irq-pin= Pin number connected to nRF24L01 IRQ pin.
+ --my-rf24-encryption-enabled
+ Enables RF24 encryption.
+ All nodes and gateway must have this enabled, and all must be
+ personalized with the same AES key
+ --my-rx-message-buffer-size=
+ Buffer size for incoming messages when using rf24 interrupts. [20]
+ --my-rs485-serial-port=
+ RS485 serial port. You must provide a port.
+ --my-rs485-baudrate= RS485 baudrate. [9600]
+ --my-rs485-de-pin= Pin number connected to RS485 driver enable pin.
+ --my-rs485-max-msg-length=
+ The maximum message length used for RS485.
+ --my-leds-err-pin= Error LED pin.
+ --my-leds-rx-pin= Receive LED pin.
+ --my-leds-tx-pin= Transmit LED pin.
+ --my-leds-blinking-inverse Inverse the blinking feature.
+ --my-signing=[none|software]
+ Message signing. [none]
+ --my-signing-debug Enable signing related debug.
+ --my-signing-request-signatures
+ Enable signature request from nodes that in turn requested
+ gateway signature.
+ --my-signing-request-gw-signatures-from-all
+ Require all nodes in the network to sign messages sent to the
+ gateway.
+
+EOF
+}
+
+function die {
+ echo "[ERROR] $1"
+ exit $2
+}
+
+function detect_rpi_revision {
+ # get PI Revision from cpuinfo
+ local pirev=$(eval "cat /proc/cpuinfo 2>/dev/null | grep Revision | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$$//'")
+ echo ${pirev}
+}
+
+function detect_machine {
+ local cpu=$(eval "uname -m 2>/dev/null")
+ local machine=$(eval "cat /sys/firmware/devicetree/base/model 2>/dev/null")
+ local hardware=$(eval "grep sunxi_platform /sys/class/sunxi_info/sys_info 2>/dev/null | sed 's/^.*: \(.*\)$/\1/'")
+ if [ -z "$hardware" ]; then
+ local hardware=$(eval "grep Hardware /proc/cpuinfo 2>/dev/null | sed 's/^.*: \(.*\)$/\1/'")
+ fi
+ local soc="unknown"
+ local tp="unknown"
+
+ if [ -z "$cpu" ]; then
+ cpu="unknown"
+ fi
+
+ case $hardware in
+ BCM2708)
+ soc="BCM2835"
+ if [[ $machine == "Raspberry"* ]]; then
+ tp="RPi"
+ fi
+ ;;
+ BCM2709)
+ soc="BCM2836"
+ if [[ $machine == "Raspberry"* ]]; then
+ local rev=($(detect_rpi_revision))
+ if [[ $rev == "a02082" || $rev == "a22082" ]]; then
+ tp="RPi3"
+ else
+ tp="Rpi2"
+ fi
+ fi
+ ;;
+ sun4i|Sun4iw1p1)
+ soc="A10"
+ ;;
+ sun5i|Sun4iw2p1)
+ soc="A13"
+ ;;
+ Sun4iw2p2)
+ soc="A12"
+ ;;
+ Sun4iw2p3)
+ soc="A10s"
+ ;;
+ sun6i|Sun8iw1p1)
+ soc="A31"
+ ;;
+ Sun8iw1p2)
+ soc="A31s"
+ ;;
+ sun7i|Sun8iw2p1)
+ soc="A20"
+ if [[ $machine == "Banana Pi"* ]]; then
+ tp="BananaPi"
+ elif [[ $machine == "Banana Pro"* ]]; then
+ tp="BananaPro"
+ fi
+ ;;
+ sun8i|Sun8iw7p1)
+ soc="H3"
+ ;;
+ Sun8iw3p1)
+ soc="A23"
+ ;;
+ Sun8iw5p1)
+ soc="A33"
+ ;;
+ Sun8iw6p1)
+ soc="A83t"
+ ;;
+ sun9i|Sun9iw1p1)
+ soc="A80"
+ ;;
+ Sun9iw1p2)
+ soc="A80t"
+ ;;
+ sun50i|Sun50iw1p1)
+ soc="A64"
+ ;;
+ 'Generic AM33XX'*)
+ soc="AM33XX"
+ ;;
+ *)
+ soc="unknown"
+ esac
+ echo "${soc} ${tp} ${cpu}"
+}
+
+function gcc_cpu_flags {
+ local soc=$1
+ case $soc in
+ BCM2835)
+ flags="-march=armv6zk -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard"
+ ;;
+ BCM2836)
+ flags="-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard"
+ ;;
+ AM33XX)
+ flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard"
+ ;;
+ A10)
+ flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard"
+ ;;
+ A13)
+ flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard"
+ ;;
+ A20)
+ flags="-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard"
+ ;;
+ H3)
+ flags="-march=armv8-a -mtune=cortex-a53 -mfpu=neon-vfpv4 -mfloat-abi=hard"
+ ;;
+ *)
+ flags=""
+ esac
+ echo ${flags}
+}
+
+# Default values
+debug=enable
+gateway_type=ethernet
+transport_type=nrf24
+signing=none
+signing_request_signatures=false
+
+params="SOC CFLAGS CXXFLAGS CPPFLAGS LDFLAGS PREFIX CC CXX ARDUINO_LIB_DIR BUILDDIR BINDIR GATEWAY_DIR INIT_SYSTEM"
+
+for opt do
+ if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then
+ help
+ exit 0
+ fi
+ optarg="${opt#*=}"
+ case "$opt" in
+ --soc=*)
+ SOC="$optarg"
+ ;;
+ --platform-type=*)
+ PT="$optarg"
+ ;;
+ --cpu-flags=*)
+ CPUFLAGS="$optarg"
+ ;;
+ --extra-cflags=*)
+ CFLAGS="$optarg"
+ ;;
+ --extra-cxxflags=*)
+ CXXFLAGS="$optarg"
+ ;;
+ --extra-ldflags=*)
+ LDFLAGS="$optarg"
+ ;;
+ --c_compiler=*)
+ CC="$optarg"
+ ;;
+ --cxx_compiler=*)
+ CXX="$optarg"
+ ;;
+ --arduino-lib-dir=*)
+ ARDUINO_LIB_DIR=$optarg
+ ;;
+ --build-dir=*)
+ BUILDDIR="$optarg"
+ ;;
+ --bin-dir=*)
+ BINDIR="$optarg"
+ ;;
+ --no-clean*)
+ NO_CLEAN="1"
+ ;;
+ --prefix=*)
+ PREFIX="$optarg"
+ ;;
+ --exec-prefix=*)
+ PREFIX="$optarg"
+ ;;
+ --no_init_system*)
+ NO_INIT="1"
+ ;;
+ --gateway-dir=*)
+ GATEWAY_DIR="$optarg"
+ ;;
+ --my-debug=*)
+ debug=${optarg}
+ ;;
+ --my-gateway=*)
+ gateway_type=${optarg}
+ ;;
+ --my-node-id=*)
+ gateway_type="none";
+ CPPFLAGS="-DMY_NODE_ID=${optarg} $CPPFLAGS"
+ ;;
+ --my-config-file=*)
+ CPPFLAGS="-DMY_LINUX_CONFIG_FILE=\\\"${optarg}\\\" $CPPFLAGS"
+ ;;
+ --my-transport=*)
+ transport_type=${optarg}
+ ;;
+ --my-radio=*)
+ echo "Warning: --my-radio is deprecated, please use --my-transport"
+ transport_type=${optarg}
+ ;;
+ --my-serial-port=*)
+ CPPFLAGS="-DMY_LINUX_SERIAL_PORT=\\\"${optarg}\\\" $CPPFLAGS"
+ ;;
+ --my-serial-baudrate=*)
+ CPPFLAGS="-DMY_BAUD_RATE=${optarg} $CPPFLAGS"
+ ;;
+ --my-serial-is-pty*)
+ CPPFLAGS="-DMY_IS_SERIAL_PTY $CPPFLAGS"
+ ;;
+ --my-serial-pty=*)
+ CPPFLAGS="-DMY_LINUX_SERIAL_PTY=\\\"${optarg}\\\" $CPPFLAGS"
+ ;;
+ --my-serial-groupname=*)
+ CPPFLAGS="-DMY_LINUX_SERIAL_GROUPNAME=\\\"${optarg}\\\" $CPPFLAGS"
+ ;;
+ --my-rf24-channel=*)
+ CPPFLAGS="-DMY_RF24_CHANNEL=${optarg} $CPPFLAGS"
+ ;;
+ --my-rf24-pa-level=*)
+ CPPFLAGS="-DMY_RF24_PA_LEVEL=${optarg} $CPPFLAGS"
+ ;;
+ --my-controller-url-address=*)
+ CPPFLAGS="-DMY_CONTROLLER_URL_ADDRESS=\\\"${optarg}\\\" $CPPFLAGS"
+ ;;
+ --my-controller-ip-address=*)
+ controller_ip=`echo ${optarg//./,}`
+ CPPFLAGS="-DMY_CONTROLLER_IP_ADDRESS=${controller_ip} $CPPFLAGS"
+ ;;
+ --my-port=*)
+ CPPFLAGS="-DMY_PORT=${optarg} $CPPFLAGS"
+ ;;
+ --my-mqtt-client-id=*)
+ CPPFLAGS="-DMY_MQTT_CLIENT_ID=\\\"${optarg}\\\" $CPPFLAGS"
+ ;;
+ --my-mqtt-publish-topic-prefix=*)
+ CPPFLAGS="-DMY_MQTT_PUBLISH_TOPIC_PREFIX=\\\"${optarg}\\\" $CPPFLAGS"
+ ;;
+ --my-mqtt-subscribe-topic-prefix=*)
+ CPPFLAGS="-DMY_MQTT_SUBSCRIBE_TOPIC_PREFIX=\\\"${optarg}\\\" $CPPFLAGS"
+ ;;
+ --my-rf24-irq-pin=*)
+ CPPFLAGS="-DMY_RX_MESSAGE_BUFFER_FEATURE -DMY_RF24_IRQ_PIN=${optarg} $CPPFLAGS"
+ ;;
+ --my-rf24-encryption-enabled*)
+ CPPFLAGS="-DMY_RF24_ENABLE_ENCRYPTION $CPPFLAGS"
+ ;;
+ --my-rx-message-buffer-size=*)
+ CPPFLAGS="-DMY_RX_MESSAGE_BUFFER_SIZE=${optarg} $CPPFLAGS"
+ ;;
+ --my-rs485-serial-port=*)
+ CPPFLAGS="-DMY_RS485_HWSERIAL=\\\"${optarg}\\\" $CPPFLAGS"
+ ;;
+ --my-rs485-baudrate=*)
+ CPPFLAGS="-DMY_RS485_BAUD_RATE=${optarg} $CPPFLAGS"
+ ;;
+ --my-rs485-de-pin=*)
+ CPPFLAGS="-DMY_RS485_DE_PIN=${optarg} $CPPFLAGS"
+ ;;
+ --my-rs485-max-msg-length=*)
+ CPPFLAGS="-DMY_RS485_MAX_MESSAGE_LENGTH=${optarg} $CPPFLAGS"
+ ;;
+ --my-leds-err-pin=*)
+ CPPFLAGS="-DMY_DEFAULT_ERR_LED_PIN=${optarg} $CPPFLAGS"
+ ;;
+ --my-leds-rx-pin=*)
+ CPPFLAGS="-DMY_DEFAULT_RX_LED_PIN=${optarg} $CPPFLAGS"
+ ;;
+ --my-leds-tx-pin=*)
+ CPPFLAGS="-DMY_DEFAULT_TX_LED_PIN=${optarg} $CPPFLAGS"
+ ;;
+ --my-leds-blinking-inverse*)
+ CPPFLAGS="-DMY_WITH_LEDS_BLINKING_INVERSE $CPPFLAGS"
+ ;;
+ --my-signing=*)
+ signing=${optarg}
+ ;;
+ --my-signing-debug*)
+ CPPFLAGS="-DMY_DEBUG_VERBOSE_SIGNING $CPPFLAGS"
+ ;;
+ --my-signing-request-signatures*)
+ signing_request_signatures=true
+ ;;
+ --my-signing-request-gw-signatures-from-all*)
+ signing_request_signatures=true
+ CPPFLAGS="-DMY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL $CPPFLAGS"
+ ;;
+ *)
+ echo "[WARNING] Unknown option detected:$opt, ignored"
+ ;;
+ esac
+done
+
+PREFIX=${PREFIX:-/usr/local}
+BUILDDIR=${BUILDDIR:-build}
+BINDIR=${BINDIR:-bin}
+GATEWAY_DIR=${GATEWAY_DIR:-${PREFIX}/bin}
+CC=${CC:-gcc}
+CXX=${CXX:-g++}
+
+if [ -z "${SOC}" ]; then
+ echo "[SECTION] Detecting target machine."
+ info=($(detect_machine))
+ SOC=${info[0]}
+ TYPE=${info[1]}
+ CPU=${info[2]}
+ echo "[OK] machine detected: SoC=${SOC}, Type=${TYPE}, CPU=${CPU}."
+fi
+
+if [ -z "${CPUFLAGS}" ]; then
+ CPUFLAGS=$(gcc_cpu_flags $SOC)
+fi
+
+if [[ $SOC == "BCM2835" || $SOC == "BCM2836" ]]; then
+ CPPFLAGS="-DLINUX_ARCH_RASPBERRYPI $CPPFLAGS"
+fi
+
+if [[ ${debug} == "enable" ]]; then
+ CPPFLAGS="-DMY_DEBUG $CPPFLAGS"
+fi
+
+if [[ ${gateway_type} == "none" ]]; then
+ # Node mode selected
+ :
+elif [[ ${gateway_type} == "ethernet" ]]; then
+ CPPFLAGS="-DMY_GATEWAY_LINUX $CPPFLAGS"
+elif [[ ${gateway_type} == "serial" ]]; then
+ CPPFLAGS="-DMY_GATEWAY_SERIAL $CPPFLAGS"
+elif [[ ${gateway_type} == "mqtt" ]]; then
+ CPPFLAGS="-DMY_GATEWAY_LINUX -DMY_GATEWAY_MQTT_CLIENT $CPPFLAGS"
+else
+ echo "Invalid gateway type."
+ echo "Aborting."
+ exit 1
+fi
+
+if [[ ${transport_type} == "none" ]]; then
+ # Transport disabled
+ :
+elif [[ ${transport_type} == "nrf24" ]]; then
+ CPPFLAGS="-DMY_RADIO_NRF24 $CPPFLAGS"
+elif [[ ${transport_type} == "rs485" ]]; then
+ CPPFLAGS="-DMY_RS485 $CPPFLAGS"
+elif [[ ${transport_type} == "rfm95" ]]; then
+ CPPFLAGS="-DMY_RADIO_RFM95 $CPPFLAGS"
+else
+ echo "Invalid transport type."
+ echo "Aborting."
+ exit 1
+fi
+
+if [[ ${signing} == "none" ]]; then
+ # Signing disabled
+ :
+elif [[ ${signing} == "software" ]]; then
+ CPPFLAGS="-DMY_SIGNING_SOFT $CPPFLAGS"
+ if [[ ${signing_request_signatures} == true ]]; then
+ CPPFLAGS="-DMY_SIGNING_REQUEST_SIGNATURES $CPPFLAGS"
+ fi
+else
+ echo "Invalid message signing option."
+ echo "Aborting."
+ exit 1
+fi
+
+LDFLAGS="-pthread $LDFLAGS"
+CPPFLAGS="$CPUFLAGS $CPPFLAGS"
+
+if [ "${NO_INIT}" ]; then
+ echo "[OK] no init system chosen."
+elif [ -x /usr/bin/systemctl ] || [ -x /bin/systemctl ]; then
+ INIT_SYSTEM=systemd
+ echo "[OK] init system detected: systemd"
+elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
+ INIT_SYSTEM=sysvinit
+ echo "[OK] init system detected: sysvinit"
+else
+ echo "[FAILED] unknown init system"
+fi
+
+echo "[SECTION] Saving configuration."
+echo -n "" > Makefile.inc
+for param in ${params}; do
+ if [[ ${!param} ]]; then
+ echo "${param}=${!param}" >> Makefile.inc
+ fi
+done
+
+if [ -z "${NO_CLEAN}" ]; then
+ echo "[SECTION] Cleaning previous builds."
+ make clean >/dev/null
+fi
+
+
+echo "[OK] Finished."
diff --git a/core/MyCapabilities.h b/core/MyCapabilities.h
index 2ec468d79..3b45fecac 100644
--- a/core/MyCapabilities.h
+++ b/core/MyCapabilities.h
@@ -22,55 +22,63 @@
#define MyCapabilities_h
#if defined(MY_DISABLE_REMOTE_RESET)
- #define MY_CAP_RESET "N"
+#define MY_CAP_RESET "N"
#else
- #define MY_CAP_RESET "R"
+#define MY_CAP_RESET "R"
#endif
#if defined(MY_OTA_FIRMWARE_FEATURE)
- #define MY_CAP_OTA_FW "O"
+#define MY_CAP_OTA_FW "O"
#else
- #define MY_CAP_OTA_FW "N"
+#define MY_CAP_OTA_FW "N"
#endif
#if defined(MY_RADIO_NRF24)
- #define MY_CAP_RADIO "N"
+#define MY_CAP_RADIO "N"
#elif defined(MY_RADIO_RFM69)
- #define MY_CAP_RADIO "R"
+#define MY_CAP_RADIO "R"
+#elif defined(MY_RADIO_RFM95)
+#define MY_CAP_RADIO "L"
#elif defined(MY_RS485)
- #define MY_CAP_RADIO "S"
+#define MY_CAP_RADIO "S"
#else
- #define MY_CAP_RADIO "-"
+#define MY_CAP_RADIO "-"
#endif
#if defined(MY_GATEWAY_FEATURE)
- #define MY_CAP_TYPE "G"
+#define MY_CAP_TYPE "G"
#elif defined(MY_REPEATER_FEATURE)
- #define MY_CAP_TYPE "R"
+#define MY_CAP_TYPE "R"
#else
- #define MY_CAP_TYPE "N"
+#define MY_CAP_TYPE "N"
#endif
#if defined(ARDUINO_ARCH_SAMD)
- #define MY_CAP_ARCH "S"
+#define MY_CAP_ARCH "S"
#elif defined(ARDUINO_ARCH_ESP8266)
- #define MY_CAP_ARCH "E"
+#define MY_CAP_ARCH "E"
#elif defined(ARDUINO_ARCH_AVR)
- #define MY_CAP_ARCH "A"
+#define MY_CAP_ARCH "A"
#else
- #define MY_CAP_ARCH "-"
+#define MY_CAP_ARCH "-"
#endif
#if defined(MY_SIGNING_ATSHA204)
- #define MY_CAP_SIGN "A"
+#define MY_CAP_SIGN "A"
#elif defined(MY_SIGNING_SOFT)
- #define MY_CAP_SIGN "S"
+#define MY_CAP_SIGN "S"
#else
- #define MY_CAP_SIGN "-"
+#define MY_CAP_SIGN "-"
+#endif
+
+#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
+#define MY_CAP_RXBUF "Q"
+#else
+#define MY_CAP_RXBUF "-"
#endif
-#define MY_CAPABILITIES MY_CAP_RESET MY_CAP_RADIO MY_CAP_OTA_FW MY_CAP_TYPE MY_CAP_ARCH MY_CAP_SIGN
+#define MY_CAPABILITIES MY_CAP_RESET MY_CAP_RADIO MY_CAP_OTA_FW MY_CAP_TYPE MY_CAP_ARCH MY_CAP_SIGN MY_CAP_RXBUF
-#endif /* MyGatewayTransportEthernet_h */
+#endif /* MyCapabilities_h */
diff --git a/core/MyEepromAddresses.h b/core/MyEepromAddresses.h
index 3c94be3bf..11502332b 100644
--- a/core/MyEepromAddresses.h
+++ b/core/MyEepromAddresses.h
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad
- * Copyright (C) 2013-2015 Sensnology AB
+ * Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
@@ -17,28 +17,73 @@
* version 2 as published by the Free Software Foundation.
*/
+/**
+* @file MyEepromAddresses.h
+* @brief Eeprom addresses for MySensors library data
+*
+* @defgroup MyEepromAddressesgrp MyEepromAddresses
+* @ingroup internals
+* @{
+*
+*/
+
+
#ifndef MyEepromAddresses_h
#define MyEepromAddresses_h
-// EEPROM start address for mysensors library data
+
+// EEPROM variable sizes, in bytes
+#define SIZE_NODE_ID (1) //!< Size node ID
+#define SIZE_PARENT_NODE_ID (1) //!< Size parent node ID
+#define SIZE_DISTANCE (1) //!< Size GW distance
+#define SIZE_ROUTES (256) //!< Size routing table
+#define SIZE_CONTROLLER_CONFIG (24) //!< Size controller config
+#define SIZE_FIRMWARE_TYPE (2) //!< Size firmware type
+#define SIZE_FIRMWARE_VERSION (2) //!< Size firmware version
+#define SIZE_FIRMWARE_BLOCKS (2) //!< Size firmware blocks
+#define SIZE_FIRMWARE_CRC (2) //!< Size firmware CRC
+#define SIZE_SIGNING_REQUIREMENT_TABLE (32) //!< Size signing requirement table
+#define SIZE_WHITELIST_REQUIREMENT_TABLE (32) //!< Size whitelist requirement table
+#define SIZE_SIGNING_SOFT_HMAC_KEY (32) //!< Size soft signing HMAC key
+#define SIZE_SIGNING_SOFT_SERIAL (9) //!< Size soft signing serial
+#define SIZE_RF_ENCRYPTION_AES_KEY (16) //!< Size RF AES encryption key
+#define SIZE_NODE_LOCK_COUNTER (1) //!< Size node lock counter
+
+
+/** @brief EEPROM start address */
#define EEPROM_START 0
-// EEPROM location of node id
+/** @brief Address node ID */
#define EEPROM_NODE_ID_ADDRESS EEPROM_START
-// EEPROM location of parent id
-#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1)
-// EEPROM location of distance to gateway
-#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1)
-#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
-#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller)
-#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24)
-#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2)
-#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2)
-#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2)
-#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2)
-#define EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32)
-#define EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS (EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS+32) // This is set with SecurityPersonalizer.ino
-#define EEPROM_SIGNING_SOFT_SERIAL_ADDRESS (EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS+32) // This is set with SecurityPersonalizer.ino
-#define EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS (EEPROM_SIGNING_SOFT_SERIAL_ADDRESS+9) // This is set with SecurityPersonalizer.ino
-#define EEPROM_NODE_LOCK_COUNTER (EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS+16)
-#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_NODE_LOCK_COUNTER+1) // First free address for sketch static configuration
-
-#endif
+/** @brief Address parent node ID */
+#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_NODE_ID_ADDRESS + SIZE_NODE_ID)
+/** @brief Address distance to GW */
+#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS + SIZE_PARENT_NODE_ID)
+/** @brief Address routing table */
+#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS + SIZE_DISTANCE)
+/** @brief Address configuration bytes sent by controller */
+#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS + SIZE_ROUTES)
+/** @brief Address firmware type */
+#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS + SIZE_CONTROLLER_CONFIG)
+/** @brief Address firmware version */
+#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS + SIZE_FIRMWARE_TYPE)
+/** @brief Address firmware blocks */
+#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS + SIZE_FIRMWARE_VERSION)
+/** @brief Address firmware CRC */
+#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS + SIZE_FIRMWARE_BLOCKS)
+/** @brief Address signing requirement table */
+#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS + SIZE_FIRMWARE_CRC)
+/** @brief Address whitelist requirement table */
+#define EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS + SIZE_SIGNING_REQUIREMENT_TABLE)
+/** @brief Address soft signing HMAC key. This is set with @ref SecurityPersonalizer.ino */
+#define EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS (EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS + SIZE_WHITELIST_REQUIREMENT_TABLE)
+/** @brief Address soft signing serial key. This is set with @ref SecurityPersonalizer.ino */
+#define EEPROM_SIGNING_SOFT_SERIAL_ADDRESS (EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS + SIZE_SIGNING_SOFT_HMAC_KEY)
+/** @brief Address RF AES encryption key. This is set with @ref SecurityPersonalizer.ino */
+#define EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS (EEPROM_SIGNING_SOFT_SERIAL_ADDRESS + SIZE_SIGNING_SOFT_SERIAL)
+/** @brief Address node lock couner. This is set with @ref SecurityPersonalizer.ino */
+#define EEPROM_NODE_LOCK_COUNTER (EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS + SIZE_RF_ENCRYPTION_AES_KEY)
+/** @brief First free address for sketch static configuration */
+#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_NODE_LOCK_COUNTER + SIZE_NODE_LOCK_COUNTER)
+
+#endif // MyEepromAddresses_h
+
+/** @}*/
diff --git a/core/MyGatewayTransport.cpp b/core/MyGatewayTransport.cpp
index a7838aa82..b37bb6b5b 100644
--- a/core/MyGatewayTransport.cpp
+++ b/core/MyGatewayTransport.cpp
@@ -20,9 +20,13 @@
#include "MyGatewayTransport.h"
extern bool transportSendRoute(MyMessage &message);
+
+// global variables
extern MyMessage _msg;
+extern MyMessage _msgTmp;
-inline void gatewayTransportProcess() {
+inline void gatewayTransportProcess(void)
+{
if (gatewayTransportAvailable()) {
_msg = gatewayTransportReceive();
if (_msg.destination == GATEWAY_ADDRESS) {
@@ -31,21 +35,22 @@ inline void gatewayTransportProcess() {
if (mGetRequestAck(_msg)) {
// Copy message
_msgTmp = _msg;
- mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop)
+ mSetRequestAck(_msgTmp,
+ false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(_msgTmp, true);
- _msgTmp.sender = _nc.nodeId;
+ _msgTmp.sender = getNodeId();
_msgTmp.destination = _msg.sender;
gatewayTransportSend(_msgTmp);
}
if (mGetCommand(_msg) == C_INTERNAL) {
if (_msg.type == I_VERSION) {
// Request for version. Create the response
- gatewayTransportSend(buildGw(_msg, I_VERSION).set(MYSENSORS_LIBRARY_VERSION));
- #ifdef MY_INCLUSION_MODE_FEATURE
+ gatewayTransportSend(buildGw(_msgTmp, I_VERSION).set(MYSENSORS_LIBRARY_VERSION));
+#ifdef MY_INCLUSION_MODE_FEATURE
} else if (_msg.type == I_INCLUSION_MODE) {
// Request to change inclusion mode
inclusionModeSet(atoi(_msg.data) == 1);
- #endif
+#endif
} else {
_processInternalMessages();
}
@@ -56,9 +61,9 @@ inline void gatewayTransportProcess() {
}
}
} else {
- #if defined(MY_RADIO_FEATURE)
- transportSendRoute(_msg);
- #endif
+#if defined(MY_SENSOR_NETWORK)
+ transportSendRoute(_msg);
+#endif
}
}
}
diff --git a/core/MyGatewayTransport.h b/core/MyGatewayTransport.h
index d34bd31ac..4342f6b58 100644
--- a/core/MyGatewayTransport.h
+++ b/core/MyGatewayTransport.h
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes.
*
* Created by Tomas Hozza
- * Copyright (C) 2015 Tomas Hozza
+ * Copyright (C) 2015 Tomas Hozza
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
@@ -21,10 +21,13 @@
#define MyGatewayTransport_h
#include "MyProtocol.h"
+#include "MySensorsCore.h"
+
+#define MSG_GW_STARTUP_COMPLETE "Gateway startup complete."
// Common gateway functions
-void gatewayTransportProcess();
+void gatewayTransportProcess(void);
// Gateway "interface" functions
@@ -32,7 +35,7 @@ void gatewayTransportProcess();
/**
* initialize the driver
*/
-bool gatewayTransportInit();
+bool gatewayTransportInit(void);
/**
* Send message to controller
@@ -42,11 +45,11 @@ bool gatewayTransportSend(MyMessage &message);
/*
* Check if a new message is available from controller
*/
-bool gatewayTransportAvailable();
+bool gatewayTransportAvailable(void);
/*
* Pick up last message received from controller
*/
-MyMessage& gatewayTransportReceive();
+MyMessage& gatewayTransportReceive(void);
#endif /* MyGatewayTransportEthernet_h */
diff --git a/core/MyGatewayTransportEthernet.cpp b/core/MyGatewayTransportEthernet.cpp
index 2db3b5ac8..693939d7f 100644
--- a/core/MyGatewayTransportEthernet.cpp
+++ b/core/MyGatewayTransportEthernet.cpp
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes.
*
* Created by Tomas Hozza
- * Copyright (C) 2015 Tomas Hozza
+ * Copyright (C) 2015 Tomas Hozza
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
@@ -19,353 +19,406 @@
#include "MyGatewayTransport.h"
+// global variables
+extern MyMessage _msgTmp;
+
#if defined(MY_CONTROLLER_IP_ADDRESS)
- IPAddress _ethernetControllerIP(MY_CONTROLLER_IP_ADDRESS);
+IPAddress _ethernetControllerIP(MY_CONTROLLER_IP_ADDRESS);
#endif
#if defined(MY_IP_ADDRESS)
- IPAddress _ethernetGatewayIP(MY_IP_ADDRESS);
+IPAddress _ethernetGatewayIP(MY_IP_ADDRESS);
#endif
-byte _ethernetGatewayMAC[] = { MY_MAC_ADDRESS };
+uint8_t _ethernetGatewayMAC[] = { MY_MAC_ADDRESS };
uint16_t _ethernetGatewayPort = MY_PORT;
MyMessage _ethernetMsg;
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
-// gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
-
-typedef struct
-{
- char string[MY_GATEWAY_MAX_RECEIVE_LENGTH];
- uint8_t idx;
+typedef struct {
+ char string[MY_GATEWAY_MAX_RECEIVE_LENGTH];
+ uint8_t idx;
} inputBuffer;
#if defined(MY_GATEWAY_ESP8266)
- // Some re-defines to make code more readable below
- #define EthernetServer WiFiServer
- #define EthernetClient WiFiClient
- #define EthernetUDP WiFiUDP
+// Some re-defines to make code more readable below
+#define EthernetServer WiFiServer
+#define EthernetClient WiFiClient
+#define EthernetUDP WiFiUDP
- #if defined(MY_IP_ADDRESS)
- IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
- IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
- #endif
- static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS];
+#if defined(MY_IP_ADDRESS)
+IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
+IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
+#endif
#endif
#if defined(MY_USE_UDP)
- EthernetUDP _ethernetServer;
+EthernetUDP _ethernetServer;
+#elif defined(MY_GATEWAY_LINUX)
+EthernetServer _ethernetServer(_ethernetGatewayPort, MY_GATEWAY_MAX_CLIENTS);
+#elif defined(MY_GATEWAY_CLIENT_MODE)
+// Nothing to do here
#else
- EthernetServer _ethernetServer(_ethernetGatewayPort);
+EthernetServer _ethernetServer(_ethernetGatewayPort);
#endif
-
-#if defined(MY_GATEWAY_ESP8266)
- static EthernetClient clients[MY_GATEWAY_MAX_CLIENTS];
- static inputBuffer inputString[MY_GATEWAY_MAX_CLIENTS];
+#if defined(MY_GATEWAY_CLIENT_MODE)
+static EthernetClient client = EthernetClient();
+static inputBuffer inputString;
+#elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX)
+static EthernetClient clients[MY_GATEWAY_MAX_CLIENTS];
+static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS];
+static inputBuffer inputString[MY_GATEWAY_MAX_CLIENTS];
#else
- static EthernetClient client = EthernetClient();
- static inputBuffer inputString;
+static EthernetClient client = EthernetClient();
+static inputBuffer inputString;
#endif
-
#ifndef MY_IP_ADDRESS
- void gatewayTransportRenewIP();
+void gatewayTransportRenewIP();
#endif
// On W5100 boards with SPI_EN exposed we can use the real SPI bus together with radio
// (if we enable it during usage)
#ifdef MY_W5100_SPI_EN
- void _w5100_spi_en(bool enable)
- {
- if (enable)
- {
- // Pull up pin
- pinMode(MY_W5100_SPI_EN, INPUT);
- digitalWrite(MY_W5100_SPI_EN, HIGH);
- }
- else
- {
- // Ground pin
- pinMode(MY_W5100_SPI_EN, OUTPUT);
- digitalWrite(MY_W5100_SPI_EN, LOW);
- }
+void _w5100_spi_en(bool enable)
+{
+ if (enable) {
+ // Pull up pin
+ hwPinMode(MY_W5100_SPI_EN, INPUT);
+ hwDigitalWrite(MY_W5100_SPI_EN, HIGH);
+ } else {
+ // Ground pin
+ hwPinMode(MY_W5100_SPI_EN, OUTPUT);
+ hwDigitalWrite(MY_W5100_SPI_EN, LOW);
}
+}
#else
- #define _w5100_spi_en(x)
+#define _w5100_spi_en(x)
#endif
-
-
-bool gatewayTransportInit() {
+bool gatewayTransportInit(void)
+{
_w5100_spi_en(true);
- #if defined(MY_GATEWAY_ESP8266)
- #if defined(MY_ESP8266_SSID)
- // Turn off access point
- WiFi.mode (WIFI_STA);
- #if defined(MY_ESP8266_HOSTNAME)
- WiFi.hostname(MY_ESP8266_HOSTNAME);
- #endif
- (void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD);
- #ifdef MY_IP_ADDRESS
- WiFi.config(_ethernetGatewayIP, _gatewayIp, _subnetIp);
- #endif
- while (WiFi.status() != WL_CONNECTED)
- {
- delay(500);
- MY_SERIALDEVICE.print(".");
- yield();
- }
- MY_SERIALDEVICE.print(F("IP: "));
- MY_SERIALDEVICE.println(WiFi.localIP());
- #endif
-
- #else
- #ifdef MY_IP_ADDRESS
- Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP);
- #else
- // Get IP address from DHCP
- if (!Ethernet.begin(_ethernetGatewayMAC)) {
- MY_SERIALDEVICE.print("DHCP FAILURE...");
- _w5100_spi_en(false);
- return false;
- }
- #endif
- MY_SERIALDEVICE.print(F("IP: "));
- MY_SERIALDEVICE.println(Ethernet.localIP());
- // give the Ethernet interface a second to initialize
- delay(1000);
- #endif
-
- #ifdef MY_USE_UDP
- _ethernetServer.begin(_ethernetGatewayPort);
- #else
- // we have to use pointers due to the constructor of EthernetServer
- _ethernetServer.begin();
- #endif /* USE_UDP */
+#if defined(MY_GATEWAY_ESP8266)
+#if defined(MY_ESP8266_SSID)
+ // Turn off access point
+ WiFi.mode (WIFI_STA);
+#if defined(MY_ESP8266_HOSTNAME)
+ WiFi.hostname(MY_ESP8266_HOSTNAME);
+#endif
+#ifdef MY_IP_ADDRESS
+ WiFi.config(_ethernetGatewayIP, _gatewayIp, _subnetIp);
+#endif
+ (void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD);
+ while (WiFi.status() != WL_CONNECTED) {
+ wait(500);
+ MY_SERIALDEVICE.print(F("."));
+ }
+ MY_SERIALDEVICE.print(F("IP: "));
+ MY_SERIALDEVICE.println(WiFi.localIP());
+#endif
+#elif defined(MY_GATEWAY_LINUX)
+ // Nothing to do here
+#else
+#ifdef MY_IP_ADDRESS
+ Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP);
+#else
+ // Get IP address from DHCP
+ if (!Ethernet.begin(_ethernetGatewayMAC)) {
+ MY_SERIALDEVICE.print(F("DHCP FAILURE..."));
+ _w5100_spi_en(false);
+ return false;
+ }
+#endif
+ MY_SERIALDEVICE.print(F("IP: "));
+ MY_SERIALDEVICE.println(Ethernet.localIP());
+ // give the Ethernet interface a second to initialize
+ delay(1000);
+#endif /* MY_GATEWAY_ESP8266 */
+
+#ifdef MY_USE_UDP
+ _ethernetServer.begin(_ethernetGatewayPort);
+#elif defined(MY_GATEWAY_CLIENT_MODE)
+#if defined(MY_CONTROLLER_URL_ADDRESS)
+ if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
+#else
+ if (client.connect(_ethernetControllerIP, MY_PORT)) {
+#endif
+ debug(PSTR("Eth: connect\n"));
+ _w5100_spi_en(false);
+ gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
+ _w5100_spi_en(true);
+ presentNode();
+ } else {
+ client.stop();
+ debug(PSTR("Eth: Failed to connect\n"));
+ }
+#else
+#if defined(MY_GATEWAY_LINUX) && defined(MY_IP_ADDRESS)
+ _ethernetServer.begin(_ethernetGatewayIP);
+#else
+ // we have to use pointers due to the constructor of EthernetServer
+ _ethernetServer.begin();
+#endif
+#endif /* USE_UDP */
_w5100_spi_en(false);
return true;
}
bool gatewayTransportSend(MyMessage &message)
{
- bool ret = true;
+ int nbytes = 0;
char *_ethernetMsg = protocolFormat(message);
- setIndication(INDICATION_GW_TX);
+ setIndication(INDICATION_GW_TX);
_w5100_spi_en(true);
- #if defined(MY_CONTROLLER_IP_ADDRESS)
- #if defined(MY_USE_UDP)
- _ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT);
- _ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg));
- // returns 1 if the packet was sent successfully
- ret = _ethernetServer.endPacket();
- #else
- EthernetClient client;
- #if defined(MY_CONTROLLER_URL_ADDRESS)
- if (client.connected() || client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
- #else
- if (client.connected() || client.connect(_ethernetControllerIP, MY_PORT)) {
- #endif
- client.write(_ethernetMsg, strlen(_ethernetMsg));
- }
- else {
- // connecting to the server failed!
- ret = false;
- }
- #endif
- #else
- // Send message to connected clients
- #if defined(MY_GATEWAY_ESP8266)
- for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++)
- {
- if (clients[i] && clients[i].connected())
- {
- clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg));
- }
- }
- #else
- _ethernetServer.write(_ethernetMsg);
- #endif
- #endif
+#if defined(MY_GATEWAY_CLIENT_MODE)
+#if defined(MY_USE_UDP)
+#if defined(MY_CONTROLLER_URL_ADDRESS)
+ _ethernetServer.beginPacket(MY_CONTROLLER_URL_ADDRESS, MY_PORT);
+#else
+ _ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT);
+#endif
+ _ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg));
+ // returns 1 if the packet was sent successfully
+ nbytes = _ethernetServer.endPacket();
+#else
+ if (!client.connected()) {
+ client.stop();
+#if defined(MY_CONTROLLER_URL_ADDRESS)
+ if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
+#else
+ if (client.connect(_ethernetControllerIP, MY_PORT)) {
+#endif
+ debug(PSTR("Eth: connect\n"));
+ _w5100_spi_en(false);
+ gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
+ _w5100_spi_en(true);
+ presentNode();
+ } else {
+ // connecting to the server failed!
+ debug(PSTR("Eth: Failed to connect\n"));
+ _w5100_spi_en(false);
+ return false;
+ }
+ }
+ nbytes = client.write(_ethernetMsg, strlen(_ethernetMsg));
+#endif
+#else
+ // Send message to connected clients
+#if defined(MY_GATEWAY_ESP8266)
+ for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i] && clients[i].connected()) {
+ nbytes += clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg));
+ }
+ }
+#else
+ nbytes = _ethernetServer.write(_ethernetMsg);
+#endif
+#endif /* MY_GATEWAY_CLIENT_MODE */
_w5100_spi_en(false);
- return ret;
-
+ return (nbytes > 0);
}
-
-#if defined(MY_GATEWAY_ESP8266)
- bool _readFromClient(uint8_t i) {
- while (clients[i].connected() && clients[i].available()) {
- char inChar = clients[i].read();
- if (inputString[i].idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
- // if newline then command is complete
- if (inChar == '\n' || inChar == '\r') {
- // Add string terminator and prepare for the next message
- inputString[i].string[inputString[i].idx] = 0;
- debug(PSTR("Client %d: %s\n"), i, inputString[i].string);
- inputString[i].idx = 0;
- if (protocolParse(_ethernetMsg, inputString[i].string)) {
- return true;
- }
-
- } else {
- // add it to the inputString:
- inputString[i].string[inputString[i].idx++] = inChar;
+#if (defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX)) && !defined(MY_GATEWAY_CLIENT_MODE)
+bool _readFromClient(uint8_t i)
+{
+ while (clients[i].connected() && clients[i].available()) {
+ const char inChar = clients[i].read();
+ if (inputString[i].idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
+ // if newline then command is complete
+ if (inChar == '\n' || inChar == '\r') {
+ // Add string terminator and prepare for the next message
+ inputString[i].string[inputString[i].idx] = 0;
+ debug(PSTR("Client %d: %s\n"), i, inputString[i].string);
+ inputString[i].idx = 0;
+ if (protocolParse(_ethernetMsg, inputString[i].string)) {
+ return true;
}
+
} else {
- // Incoming message too long. Throw away
- debug(PSTR("Client %d: Message too long\n"), i);
- inputString[i].idx = 0;
- // Finished with this client's message. Next loop() we'll see if there's more to read.
- break;
+ // add it to the inputString:
+ inputString[i].string[inputString[i].idx++] = inChar;
}
+ } else {
+ // Incoming message too long. Throw away
+ debug(PSTR("Client %d: Message too long\n"), i);
+ inputString[i].idx = 0;
+ // Finished with this client's message. Next loop() we'll see if there's more to read.
+ break;
}
- return false;
}
+ return false;
+}
#else
- bool _readFromClient() {
- while (client.connected() && client.available()) {
- char inChar = client.read();
- if (inputString.idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
- // if newline then command is complete
- if (inChar == '\n' || inChar == '\r') {
- // Add string terminator and prepare for the next message
- inputString.string[inputString.idx] = 0;
- debug(PSTR("Eth: %s\n"), inputString.string);
- inputString.idx = 0;
- if (protocolParse(_ethernetMsg, inputString.string)) {
- return true;
- }
-
- } else {
- // add it to the inputString:
- inputString.string[inputString.idx++] = inChar;
+bool _readFromClient(void)
+{
+ while (client.connected() && client.available()) {
+ const char inChar = client.read();
+ if (inputString.idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
+ // if newline then command is complete
+ if (inChar == '\n' || inChar == '\r') {
+ // Add string terminator and prepare for the next message
+ inputString.string[inputString.idx] = 0;
+ debug(PSTR("Eth: %s\n"), inputString.string);
+ inputString.idx = 0;
+ if (protocolParse(_ethernetMsg, inputString.string)) {
+ return true;
}
+
} else {
- // Incoming message too long. Throw away
- debug(PSTR("Eth: Message too long\n"));
- inputString.idx = 0;
- // Finished with this client's message. Next loop() we'll see if there's more to read.
- break;
+ // add it to the inputString:
+ inputString.string[inputString.idx++] = inChar;
}
+ } else {
+ // Incoming message too long. Throw away
+ debug(PSTR("Eth: Message too long\n"));
+ inputString.idx = 0;
+ // Finished with this client's message. Next loop() we'll see if there's more to read.
+ break;
}
- return false;
}
+ return false;
+}
#endif
-bool gatewayTransportAvailable()
+bool gatewayTransportAvailable(void)
{
_w5100_spi_en(true);
- #if !defined(MY_IP_ADDRESS) && defined(MY_GATEWAY_W5100)
- // renew IP address using DHCP
- gatewayTransportRenewIP();
- #endif
-
- #ifdef MY_USE_UDP
+#if !defined(MY_IP_ADDRESS) && defined(MY_GATEWAY_W5100)
+ // renew IP address using DHCP
+ gatewayTransportRenewIP();
+#endif
- int packet_size = _ethernetServer.parsePacket();
+#ifdef MY_USE_UDP
+ int packet_size = _ethernetServer.parsePacket();
- if (packet_size) {
- //debug(PSTR("UDP packet available. Size:%d\n"), packet_size);
- setIndication(INDICATION_GW_RX);
- #if defined(MY_GATEWAY_ESP8266)
- _ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
- inputString[0].string[packet_size] = 0;
- debug(PSTR("UDP packet received: %s\n"), inputString[0].string);
- return protocolParse(_ethernetMsg, inputString[0].string);
- #else
- _ethernetServer.read(inputString.string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
- inputString.string[packet_size] = 0;
- debug(PSTR("UDP packet received: %s\n"), inputString.string);
- _w5100_spi_en(false);
- return protocolParse(_ethernetMsg, inputString.string);
- #endif
+ if (packet_size) {
+ //debug(PSTR("UDP packet available. Size:%d\n"), packet_size);
+#if defined(MY_GATEWAY_ESP8266)
+ _ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
+ inputString[0].string[packet_size] = 0;
+ debug(PSTR("UDP packet received: %s\n"), inputString[0].string);
+ const bool ok = protocolParse(_ethernetMsg, inputString[0].string);
+#else
+ _ethernetServer.read(inputString.string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
+ inputString.string[packet_size] = 0;
+ debug(PSTR("UDP packet received: %s\n"), inputString.string);
+ _w5100_spi_en(false);
+ const bool ok = protocolParse(_ethernetMsg, inputString.string);
+#endif
+ if (ok) {
+ setIndication(INDICATION_GW_RX);
}
- #else
- #if defined(MY_GATEWAY_ESP8266)
- // ESP8266: Go over list of clients and stop any that are no longer connected.
- // If the server has a new client connection it will be assigned to a free slot.
- bool allSlotsOccupied = true;
- for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
- if (!clients[i].connected()) {
- if (clientsConnected[i]) {
- debug(PSTR("Client %d disconnected\n"), i);
- clients[i].stop();
- }
- //check if there are any new clients
- if (_ethernetServer.hasClient()) {
- clients[i] = _ethernetServer.available();
- inputString[i].idx = 0;
- debug(PSTR("Client %d connected\n"), i);
- gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
- if (presentation)
- presentation();
- }
- }
- bool connected = clients[i].connected();
- clientsConnected[i] = connected;
- allSlotsOccupied &= connected;
- }
- if (allSlotsOccupied && _ethernetServer.hasClient()) {
- //no free/disconnected spot so reject
- debug(PSTR("No free slot available\n"));
- EthernetClient c = _ethernetServer.available();
- c.stop();
- }
- // Loop over clients connect and read available data
- for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
- if (_readFromClient(i)) {
- setIndication(INDICATION_GW_RX);
- _w5100_spi_en(false);
- return true;
- }
+ return ok;
+ }
+#elif defined(MY_GATEWAY_CLIENT_MODE)
+ if (!client.connected()) {
+ client.stop();
+#if defined(MY_CONTROLLER_URL_ADDRESS)
+ if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
+#else
+ if (client.connect(_ethernetControllerIP, MY_PORT)) {
+#endif
+ debug(PSTR("Eth: connect\n"));
+ _w5100_spi_en(false);
+ gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
+ _w5100_spi_en(true);
+ presentNode();
+ } else {
+ debug(PSTR("Eth: Failed to connect\n"));
+ _w5100_spi_en(false);
+ return false;
+ }
+ }
+ if (_readFromClient()) {
+ setIndication(INDICATION_GW_RX);
+ _w5100_spi_en(false);
+ return true;
+ }
+#else
+#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX)
+ // ESP8266: Go over list of clients and stop any that are no longer connected.
+ // If the server has a new client connection it will be assigned to a free slot.
+ bool allSlotsOccupied = true;
+ for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
+ if (!clients[i].connected()) {
+ if (clientsConnected[i]) {
+ debug(PSTR("Client %d disconnected\n"), i);
+ clients[i].stop();
}
- #else
- // W5100/ENC module does not have hasClient-method. We can only serve one client at the time.
- EthernetClient newclient = _ethernetServer.available();
- // if a new client connects make sure to dispose any previous existing sockets
- if (newclient) {
- if (client != newclient) {
- client.stop();
- client = newclient;
- debug(PSTR("Eth: connect\n"));
- _w5100_spi_en(false);
- gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
- _w5100_spi_en(true);
- if (presentation)
- presentation();
- }
+ //check if there are any new clients
+ if (_ethernetServer.hasClient()) {
+ clients[i] = _ethernetServer.available();
+ inputString[i].idx = 0;
+ debug(PSTR("Client %d connected\n"), i);
+ gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
+ // Send presentation of locally attached sensors (and node if applicable)
+ presentNode();
}
- if (client) {
- if (!client.connected()) {
- debug(PSTR("Eth: disconnect\n"));
- client.stop();
- } else {
- if (_readFromClient()) {
- setIndication(INDICATION_GW_RX);
- _w5100_spi_en(false);
- return true;
- }
- }
+ }
+ bool connected = clients[i].connected();
+ clientsConnected[i] = connected;
+ allSlotsOccupied &= connected;
+ }
+ if (allSlotsOccupied && _ethernetServer.hasClient()) {
+ //no free/disconnected spot so reject
+ debug(PSTR("No free slot available\n"));
+ EthernetClient c = _ethernetServer.available();
+ c.stop();
+ }
+ // Loop over clients connect and read available data
+ for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
+ if (_readFromClient(i)) {
+ setIndication(INDICATION_GW_RX);
+ _w5100_spi_en(false);
+ return true;
+ }
+ }
+#else
+ // W5100/ENC module does not have hasClient-method. We can only serve one client at the time.
+ EthernetClient newclient = _ethernetServer.available();
+ // if a new client connects make sure to dispose any previous existing sockets
+ if (newclient) {
+ if (client != newclient) {
+ client.stop();
+ client = newclient;
+ debug(PSTR("Eth: connect\n"));
+ _w5100_spi_en(false);
+ gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
+ _w5100_spi_en(true);
+ presentNode();
+ }
+ }
+ if (client) {
+ if (!client.connected()) {
+ debug(PSTR("Eth: disconnect\n"));
+ client.stop();
+ } else {
+ if (_readFromClient()) {
+ setIndication(INDICATION_GW_RX);
+ _w5100_spi_en(false);
+ return true;
}
- #endif
- #endif
+ }
+ }
+#endif /* MY_GATEWAY_ESP8266 */
+#endif
_w5100_spi_en(false);
return false;
}
-MyMessage& gatewayTransportReceive()
+MyMessage& gatewayTransportReceive(void)
{
// Return the last parsed message
return _ethernetMsg;
}
-
-#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266)
-void gatewayTransportRenewIP()
+#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266) && !defined(MY_GATEWAY_LINUX)
+void gatewayTransportRenewIP(void)
{
/* renew/rebind IP address
0 - nothing happened
@@ -378,8 +431,9 @@ void gatewayTransportRenewIP()
unsigned long now = hwMillis();
// http://playground.arduino.cc/Code/TimingRollover
- if ((long)(now - next_time) < 0)
+ if ((long)(now - next_time) < 0) {
return;
+ }
if (Ethernet.maintain() & ~(0x06)) {
debug(PSTR("IP was not renewed correctly\n"));
/* Error occured -> IP was not renewed */
@@ -388,4 +442,4 @@ void gatewayTransportRenewIP()
_w5100_spi_en(false);
next_time = now + MY_IP_RENEWAL_INTERVAL;
}
-#endif /* IP_ADDRESS_DHCP */
+#endif
diff --git a/core/MyGatewayTransportMQTTClient.cpp b/core/MyGatewayTransportMQTTClient.cpp
index f6c36e7d1..9016f2621 100644
--- a/core/MyGatewayTransportMQTTClient.cpp
+++ b/core/MyGatewayTransportMQTTClient.cpp
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad
-* Copyright (C) 2013-2015 Sensnology AB
+* Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
@@ -20,23 +20,26 @@
// Topic structure: MY_MQTT_PUBLISH_TOPIC_PREFIX/NODE-ID/SENSOR-ID/CMD-TYPE/ACK-FLAG/SUB-TYPE
+#include "MyGatewayTransport.h"
#if defined MY_CONTROLLER_IP_ADDRESS
- IPAddress _brokerIp(MY_CONTROLLER_IP_ADDRESS);
+IPAddress _brokerIp(MY_CONTROLLER_IP_ADDRESS);
#endif
#if defined(MY_GATEWAY_ESP8266)
- #define EthernetClient WiFiClient
- #if defined(MY_IP_ADDRESS)
- IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
- IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
- #endif
+#define EthernetClient WiFiClient
+#if defined(MY_IP_ADDRESS)
+IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
+IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
+#endif
+#elif defined(MY_GATEWAY_LINUX)
+// Nothing to do here
#else
- byte _MQTT_clientMAC[] = { MY_MAC_ADDRESS };
+uint8_t _MQTT_clientMAC[] = { MY_MAC_ADDRESS };
#endif
#if defined(MY_IP_ADDRESS)
- IPAddress _MQTT_clientIp(MY_IP_ADDRESS);
+IPAddress _MQTT_clientIp(MY_IP_ADDRESS);
#endif
static EthernetClient _MQTT_ethClient;
@@ -45,107 +48,37 @@ static bool _MQTT_connecting = true;
static bool _MQTT_available = false;
static MyMessage _MQTT_msg;
-uint8_t protocolH2i(char c) {
- uint8_t i = 0;
- if (c <= '9')
- i += c - '0';
- else if (c >= 'a')
- i += c - 'a' + 10;
- else
- i += c - 'A' + 10;
- return i;
-}
-
-
-bool gatewayTransportSend(MyMessage &message) {
- if (!_MQTT_client.connected())
+bool gatewayTransportSend(MyMessage &message)
+{
+ if (!_MQTT_client.connected()) {
return false;
+ }
setIndication(INDICATION_GW_TX);
- char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH];
- char _convBuffer[MAX_PAYLOAD * 2 + 1];
- snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR(MY_MQTT_PUBLISH_TOPIC_PREFIX "/%d/%d/%d/%d/%d"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type);
- debug(PSTR("Sending message on topic: %s\n"), _fmtBuffer);
- return _MQTT_client.publish(_fmtBuffer, message.getString(_convBuffer));
+ char *topic = protocolFormatMQTTTopic(MY_MQTT_PUBLISH_TOPIC_PREFIX, message);
+ debug(PSTR("Sending message on topic: %s\n"), topic);
+ return _MQTT_client.publish(topic, message.getString(_convBuffer));
}
-void incomingMQTT(char* topic, byte* payload, unsigned int length) {
+void incomingMQTT(char* topic, uint8_t* payload, unsigned int length)
+{
debug(PSTR("Message arrived on topic: %s\n"), topic);
- char *str, *p;
- uint8_t i = 0;
- uint8_t bvalue[MAX_PAYLOAD];
- uint8_t blen = 0;
- uint8_t command = 0;
- for (str = strtok_r(topic, "/", &p); str && i <= 5;
- str = strtok_r(NULL, "/", &p)) {
- switch (i) {
- case 0: {
- // Topic prefix
- if (strcmp(str, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) != 0) {
- // Message not for us or malformed!
- return;
- }
- break;
- }
- case 1: {
- // Node id
- _MQTT_msg.destination = atoi(str);
- break;
- }
- case 2: {
- // Sensor id
- _MQTT_msg.sensor = atoi(str);
- break;
- }
- case 3: {
- // Command type
- command = atoi(str);
- mSetCommand(_MQTT_msg, command);
- break;
- }
- case 4: {
- // Ack flag
- mSetRequestAck(_MQTT_msg, atoi(str) ? 1 : 0);
- break;
- }
- case 5: {
- // Sub type
- _MQTT_msg.type = atoi(str);
- // Add payload
- if (command == C_STREAM) {
- blen = 0;
- uint8_t val;
- while (*payload) {
- val = protocolH2i(*payload++) << 4;
- val += protocolH2i(*payload++);
- bvalue[blen] = val;
- blen++;
- }
- _MQTT_msg.set(bvalue, blen);
- }
- else {
- char* ca;
- ca = (char *)payload;
- ca += length;
- *ca = '\0';
- _MQTT_msg.set((const char*)payload);
- }
- _MQTT_available = true;
- }
- }
- i++;
- }
+ _MQTT_available = protocolMQTTParse(_MQTT_msg, topic, payload, length);
}
-
-bool reconnectMQTT() {
+bool reconnectMQTT(void)
+{
debug(PSTR("Attempting MQTT connection...\n"));
// Attempt to connect
if (_MQTT_client.connect(MY_MQTT_CLIENT_ID
#if defined(MY_MQTT_USER) && defined(MY_MQTT_PASSWORD)
- , MY_MQTT_USER, MY_MQTT_PASSWORD
+ , MY_MQTT_USER, MY_MQTT_PASSWORD
#endif
- )) {
+ )) {
debug(PSTR("MQTT connected\n"));
+
+ // Send presentation of locally attached sensors (and node if applicable)
+ presentNode();
+
// Once connected, publish an announcement...
//_MQTT_client.publish("outTopic","hello world");
// ... and resubscribe
@@ -155,54 +88,60 @@ bool reconnectMQTT() {
return false;
}
-bool gatewayTransportConnect() {
- #if defined(MY_GATEWAY_ESP8266)
- while (WiFi.status() != WL_CONNECTED) {
- delay(500); // delay calls yield
- MY_SERIALDEVICE.print(".");
- }
- MY_SERIALDEVICE.print("IP: ");
- MY_SERIALDEVICE.println(WiFi.localIP());
- #else
- #ifdef MY_IP_ADDRESS
- Ethernet.begin(_MQTT_clientMAC, _MQTT_clientIp);
- #else
- // Get IP address from DHCP
- if (!Ethernet.begin(_MQTT_clientMAC)) {
- MY_SERIALDEVICE.print("DHCP FAILURE...");
- _MQTT_connecting = false;
- return false;
- }
- #endif
- MY_SERIALDEVICE.print("IP: ");
- MY_SERIALDEVICE.println(Ethernet.localIP());
- // give the Ethernet interface a second to initialize
- delay(1000);
- #endif
+bool gatewayTransportConnect(void)
+{
+#if defined(MY_GATEWAY_ESP8266)
+ while (WiFi.status() != WL_CONNECTED) {
+ wait(500);
+ MY_SERIALDEVICE.print(F("."));
+ }
+ MY_SERIALDEVICE.print(F("IP: "));
+ MY_SERIALDEVICE.println(WiFi.localIP());
+#elif defined(MY_GATEWAY_LINUX)
+#if defined(MY_IP_ADDRESS)
+ //TODO
+#endif
+#else
+#ifdef MY_IP_ADDRESS
+ Ethernet.begin(_MQTT_clientMAC, _MQTT_clientIp);
+#else
+ // Get IP address from DHCP
+ if (!Ethernet.begin(_MQTT_clientMAC)) {
+ MY_SERIALDEVICE.print(F("DHCP FAILURE..."));
+ _MQTT_connecting = false;
+ return false;
+ }
+#endif
+ MY_SERIALDEVICE.print(F("IP: "));
+ MY_SERIALDEVICE.println(Ethernet.localIP());
+ // give the Ethernet interface a second to initialize
+ delay(1000);
+#endif
return true;
}
-bool gatewayTransportInit() {
+bool gatewayTransportInit(void)
+{
_MQTT_connecting = true;
- #if defined(MY_CONTROLLER_IP_ADDRESS)
- _MQTT_client.setServer(_brokerIp, MY_PORT);
- #else
- _MQTT_client.setServer(MY_CONTROLLER_URL_ADDRESS, MY_PORT);
- #endif
-
- _MQTT_client.setCallback(incomingMQTT);
-
- #if defined(MY_GATEWAY_ESP8266)
- // Turn off access point
- WiFi.mode(WIFI_STA);
- #if defined(MY_ESP8266_HOSTNAME)
- WiFi.hostname(MY_ESP8266_HOSTNAME);
- #endif
- (void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD);
- #ifdef MY_IP_ADDRESS
- WiFi.config(_MQTT_clientIp, _gatewayIp, _subnetIp);
- #endif
- #endif
+#if defined(MY_CONTROLLER_IP_ADDRESS)
+ _MQTT_client.setServer(_brokerIp, MY_PORT);
+#else
+ _MQTT_client.setServer(MY_CONTROLLER_URL_ADDRESS, MY_PORT);
+#endif
+
+ _MQTT_client.setCallback(incomingMQTT);
+
+#if defined(MY_GATEWAY_ESP8266)
+ // Turn off access point
+ WiFi.mode(WIFI_STA);
+#if defined(MY_ESP8266_HOSTNAME)
+ WiFi.hostname(MY_ESP8266_HOSTNAME);
+#endif
+ (void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD);
+#ifdef MY_IP_ADDRESS
+ WiFi.config(_MQTT_clientIp, _gatewayIp, _subnetIp);
+#endif
+#endif
gatewayTransportConnect();
@@ -210,30 +149,27 @@ bool gatewayTransportInit() {
return true;
}
-
-
-bool gatewayTransportAvailable() {
-
- if (_MQTT_connecting)
+bool gatewayTransportAvailable(void)
+{
+ if (_MQTT_connecting) {
return false;
-
+ }
//keep lease on dhcp address
//Ethernet.maintain();
if (!_MQTT_client.connected()) {
//reinitialise client
- if (gatewayTransportConnect())
+ if (gatewayTransportConnect()) {
reconnectMQTT();
+ }
return false;
}
_MQTT_client.loop();
return _MQTT_available;
}
-MyMessage & gatewayTransportReceive() {
+MyMessage & gatewayTransportReceive(void)
+{
// Return the last parsed message
_MQTT_available = false;
return _MQTT_msg;
}
-
-
-
diff --git a/core/MyGatewayTransportSerial.cpp b/core/MyGatewayTransportSerial.cpp
index e3ea5fb3e..b54fa9241 100644
--- a/core/MyGatewayTransportSerial.cpp
+++ b/core/MyGatewayTransportSerial.cpp
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad
- * Copyright (C) 2013-2015 Sensnology AB
+ * Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
@@ -17,42 +17,49 @@
* version 2 as published by the Free Software Foundation.
*/
-
#include "MyConfig.h"
#include "MyProtocol.h"
#include "MyGatewayTransport.h"
#include "MyMessage.h"
#include "MyProtocol.h"
+// global variables
+extern MyMessage _msgTmp;
char _serialInputString[MY_GATEWAY_MAX_RECEIVE_LENGTH]; // A buffer for incoming commands from serial interface
-int _serialInputPos;
+uint8_t _serialInputPos;
MyMessage _serialMsg;
-
-bool gatewayTransportSend(MyMessage &message) {
- setIndication(INDICATION_GW_TX);
+bool gatewayTransportSend(MyMessage &message)
+{
+ setIndication(INDICATION_GW_TX);
MY_SERIALDEVICE.print(protocolFormat(message));
// Serial print is always successful
return true;
}
-bool gatewayTransportInit() {
- gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
+bool gatewayTransportInit(void)
+{
+ (void)gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
+ // Send presentation of locally attached sensors (and node if applicable)
+ presentNode();
return true;
}
-
-bool gatewayTransportAvailable() {
+bool gatewayTransportAvailable(void)
+{
while (MY_SERIALDEVICE.available()) {
// get the new byte:
- char inChar = (char) MY_SERIALDEVICE.read();
+ const char inChar = (char)MY_SERIALDEVICE.read();
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (_serialInputPos < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
if (inChar == '\n') {
_serialInputString[_serialInputPos] = 0;
- bool ok = protocolParse(_serialMsg, _serialInputString);
+ const bool ok = protocolParse(_serialMsg, _serialInputString);
+ if (ok) {
+ setIndication(INDICATION_GW_RX);
+ }
_serialInputPos = 0;
return ok;
} else {
@@ -68,7 +75,8 @@ bool gatewayTransportAvailable() {
return false;
}
-MyMessage & gatewayTransportReceive() {
+MyMessage & gatewayTransportReceive(void)
+{
// Return the last parsed message
return _serialMsg;
}
diff --git a/core/MyHw.h b/core/MyHw.h
index 1224f6629..3b4cfbae3 100644
--- a/core/MyHw.h
+++ b/core/MyHw.h
@@ -1,4 +1,4 @@
-/**
+/*
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
@@ -17,6 +17,12 @@
* version 2 as published by the Free Software Foundation.
*/
+/**
+* @file MyHw.h
+*
+* MySensors hardware abstraction layer
+*/
+
#ifndef MyHw_h
#define MyHw_h
@@ -30,23 +36,103 @@
// Implement these as functions or macros
/*
-#define hwDigitalWrite(__pin, __value)
#define hwInit() MY_SERIALDEVICE.begin(BAUD_RATE)
#define hwWatchdogReset() wdt_reset()
#define hwReboot() wdt_enable(WDTO_15MS); while (1)
#define hwMillis() millis()
+#define hwDigitalWrite(__pin, __value)
+#define hwDigitalRead(__pin)
+#define hwPinMode(__pin, __value)
+
void hwReadConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfig(int adr, uint8_t value);
uint8_t hwReadConfig(int adr);
*/
+/**
+ * Sleep for a defined time, using minimum power.
+ * @param ms Time to sleep, in [ms].
+ * @return Nonsense, please ignore.
+ */
int8_t hwSleep(unsigned long ms);
+
+/**
+ * Sleep for a defined time, using minimum power, or until woken by interrupt.
+ * @param interrupt Interrupt number, which can wake the mcu from sleep.
+ * @param mode Interrupt mode, as passed to attachInterrupt.
+ * @param ms Time to sleep, in [ms].
+ * @return -1 when woken by timer, or interrupt number when woken by interrupt.
+ */
int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms);
-int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms);
+
+/**
+ * Sleep for a defined time, using minimum power, or until woken by one of the interrupts.
+ * @param interrupt1 Interrupt1 number, which can wake the mcu from sleep.
+ * @param mode1 Interrupt1 mode, as passed to attachInterrupt.
+ * @param interrupt2 Interrupt2 number, which can wake the mcu from sleep.
+ * @param mode2 Interrupt2 mode, as passed to attachInterrupt.
+ * @param ms Time to sleep, in [ms].
+ * @return -1 when woken by timer, or interrupt number when woken by interrupt.
+ */
+int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2,
+ unsigned long ms);
+
+#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
+/**
+ * CPU voltage
+ * @return CPU voltage in mV
+ */
+uint16_t hwCPUVoltage();
+
+/**
+ * CPU frequency
+ * @return CPU frequency in 1/10Mhz
+ */
+uint16_t hwCPUFrequency();
+
+/**
+ * Free memory
+ * @return free memory in bytes
+ */
+uint16_t hwFreeMem();
+#endif
+
#ifdef MY_DEBUG
- void hwDebugPrint(const char *fmt, ... );
+void hwDebugPrint(const char *fmt, ... );
#endif
+/**
+ * @def MY_CRITICAL_SECTION
+ * @brief Creates a block of code that is guaranteed to be executed atomically.
+ * Upon entering the block all interrupts are disabled, and re-enabled upon
+ * exiting the block from any exit path.
+ * A typical example that requires atomic access is a 16 (or more) bit variable
+ * that is shared between the main execution path and an ISR, on an 8-bit
+ * platform (e.g AVR):
+ * @code
+ * volatile uint16_t val = 0;
+ *
+ * void interrupHandler()
+ * {
+ * val = ~val;
+ * }
+ *
+ * void loop()
+ * {
+ * uint16_t copy_val;
+ * MY_CRITICAL_SECTION
+ * {
+ * copy_val = val;
+ * }
+ * }
+ * @endcode
+ * All code within the MY_CRITICAL_SECTION block will be protected from being
+ * interrupted during execution.
+ */
+#ifdef DOXYGEN
+#define MY_CRITICAL_SECTION
+#endif /* DOXYGEN */
+
#endif // #ifdef MyHw_h
diff --git a/core/MyHwATMega328.cpp b/core/MyHwATMega328.cpp
index 9aad4fd1d..50c2e5115 100644
--- a/core/MyHwATMega328.cpp
+++ b/core/MyHwATMega328.cpp
@@ -21,15 +21,35 @@
#include "MyHwATMega328.h"
-volatile int8_t pinIntTrigger = 0;
+#define INVALID_INTERRUPT_NUM (0xFFu)
-void wakeUp() //place to send the interrupts
+volatile uint8_t _wokeUpByInterrupt =
+ INVALID_INTERRUPT_NUM; // Interrupt number that woke the mcu.
+volatile uint8_t _wakeUp1Interrupt =
+ INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp1-callback.
+volatile uint8_t _wakeUp2Interrupt =
+ INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp2-callback.
+
+void wakeUp1() //place to send the interrupts
{
- pinIntTrigger = 1;
+ detachInterrupt(_wakeUp1Interrupt);
+ if (_wakeUp2Interrupt != INVALID_INTERRUPT_NUM) {
+ detachInterrupt(_wakeUp2Interrupt);
+ }
+ _wokeUpByInterrupt = _wakeUp1Interrupt;
}
void wakeUp2() //place to send the second interrupts
{
- pinIntTrigger = 2;
+ detachInterrupt(_wakeUp2Interrupt);
+ if (_wakeUp1Interrupt != INVALID_INTERRUPT_NUM) {
+ detachInterrupt(_wakeUp1Interrupt);
+ }
+ _wokeUpByInterrupt = _wakeUp2Interrupt;
+}
+
+bool interruptWakeUp()
+{
+ return _wokeUpByInterrupt != INVALID_INTERRUPT_NUM;
}
// Watchdog Timer interrupt service routine. This routine is required
@@ -38,14 +58,13 @@ ISR (WDT_vect)
{
}
-void hwPowerDown(period_t period) {
-
+void hwPowerDown(period_t period)
+{
// disable ADC for power saving
ADCSRA &= ~(1 << ADEN);
// save WDT settings
uint8_t WDTsave = WDTCSR;
- if (period != SLEEP_FOREVER)
- {
+ if (period != SLEEP_FOREVER) {
wdt_enable(period);
// enable WDT interrupt before system reset
WDTCSR |= (1 << WDCE) | (1 << WDIE);
@@ -59,10 +78,11 @@ void hwPowerDown(period_t period) {
#if defined __AVR_ATmega328P__
sleep_bod_disable();
#endif
+ // Enable interrupts & sleep until WDT or ext. interrupt
sei();
- // sleep until WDT or ext. interrupt
+ // Directly sleep CPU, to prevent race conditions! (see chapter 7.7 of ATMega328P datasheet)
sleep_cpu();
- sleep_disable();
+ sleep_disable();
// restore previous WDT settings
cli();
wdt_reset();
@@ -75,88 +95,136 @@ void hwPowerDown(period_t period) {
ADCSRA |= (1 << ADEN);
}
-void hwInternalSleep(unsigned long ms) {
+void hwInternalSleep(unsigned long ms)
+{
// Let serial prints finish (debug, log etc)
- #ifndef MY_DISABLED_SERIAL
- MY_SERIALDEVICE.flush();
- #endif
- // reset interrupt trigger var
- pinIntTrigger = 0;
- while (!pinIntTrigger && ms >= 8000) { hwPowerDown(SLEEP_8S); ms -= 8000; }
- if (!pinIntTrigger && ms >= 4000) { hwPowerDown(SLEEP_4S); ms -= 4000; }
- if (!pinIntTrigger && ms >= 2000) { hwPowerDown(SLEEP_2S); ms -= 2000; }
- if (!pinIntTrigger && ms >= 1000) { hwPowerDown(SLEEP_1S); ms -= 1000; }
- if (!pinIntTrigger && ms >= 500) { hwPowerDown(SLEEP_500MS); ms -= 500; }
- if (!pinIntTrigger && ms >= 250) { hwPowerDown(SLEEP_250MS); ms -= 250; }
- if (!pinIntTrigger && ms >= 125) { hwPowerDown(SLEEP_120MS); ms -= 120; }
- if (!pinIntTrigger && ms >= 64) { hwPowerDown(SLEEP_60MS); ms -= 60; }
- if (!pinIntTrigger && ms >= 32) { hwPowerDown(SLEEP_30MS); ms -= 30; }
- if (!pinIntTrigger && ms >= 16) { hwPowerDown(SLEEP_15MS); ms -= 15; }
+#ifndef MY_DISABLED_SERIAL
+ MY_SERIALDEVICE.flush();
+#endif
+ while (!interruptWakeUp() && ms >= 8000) {
+ hwPowerDown(SLEEP_8S);
+ ms -= 8000;
+ }
+ if (!interruptWakeUp() && ms >= 4000) {
+ hwPowerDown(SLEEP_4S);
+ ms -= 4000;
+ }
+ if (!interruptWakeUp() && ms >= 2000) {
+ hwPowerDown(SLEEP_2S);
+ ms -= 2000;
+ }
+ if (!interruptWakeUp() && ms >= 1000) {
+ hwPowerDown(SLEEP_1S);
+ ms -= 1000;
+ }
+ if (!interruptWakeUp() && ms >= 500) {
+ hwPowerDown(SLEEP_500MS);
+ ms -= 500;
+ }
+ if (!interruptWakeUp() && ms >= 250) {
+ hwPowerDown(SLEEP_250MS);
+ ms -= 250;
+ }
+ if (!interruptWakeUp() && ms >= 125) {
+ hwPowerDown(SLEEP_120MS);
+ ms -= 120;
+ }
+ if (!interruptWakeUp() && ms >= 64) {
+ hwPowerDown(SLEEP_60MS);
+ ms -= 60;
+ }
+ if (!interruptWakeUp() && ms >= 32) {
+ hwPowerDown(SLEEP_30MS);
+ ms -= 30;
+ }
+ if (!interruptWakeUp() && ms >= 16) {
+ hwPowerDown(SLEEP_15MS);
+ ms -= 15;
+ }
}
-int8_t hwSleep(unsigned long ms) {
+int8_t hwSleep(unsigned long ms)
+{
hwInternalSleep(ms);
- return -1;
+ return MY_WAKE_UP_BY_TIMER;
}
-int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
- return hwSleep(interrupt,mode,0xFF,0x00,ms);
+int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms)
+{
+ return hwSleep(interrupt,mode,INVALID_INTERRUPT_NUM,0u,ms);
}
-int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
- // reset interrupt trigger var
- pinIntTrigger = 0;
- // attach interrupts
- attachInterrupt(interrupt1, wakeUp, mode1);
- if (interrupt2!=0xFF) attachInterrupt(interrupt2, wakeUp2, mode2);
-
+int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2,
+ unsigned long ms)
+{
+ // Disable interrupts until going to sleep, otherwise interrupts occurring between attachInterrupt()
+ // and sleep might cause the ATMega to not wakeup from sleep as interrupt has already be handled!
+ cli();
+ // attach interrupts
+ _wakeUp1Interrupt = interrupt1;
+ _wakeUp2Interrupt = interrupt2;
+ if (interrupt1 != INVALID_INTERRUPT_NUM) {
+ attachInterrupt(interrupt1, wakeUp1, mode1);
+ }
+ if (interrupt2 != INVALID_INTERRUPT_NUM) {
+ attachInterrupt(interrupt2, wakeUp2, mode2);
+ }
+
if (ms>0) {
// sleep for defined time
hwInternalSleep(ms);
} else {
// sleep until ext interrupt triggered
- hwPowerDown(SLEEP_FOREVER);
+ hwPowerDown(SLEEP_FOREVER);
+ }
+
+ // Assure any interrupts attached, will get detached when they did not occur.
+ if (interrupt1 != INVALID_INTERRUPT_NUM) {
+ detachInterrupt(interrupt1);
+ }
+ if (interrupt2 != INVALID_INTERRUPT_NUM) {
+ detachInterrupt(interrupt2);
}
-
- detachInterrupt(interrupt1);
- if (interrupt2!=0xFF) detachInterrupt(interrupt2);
-
- // default: no interrupt triggered, timer wake up
- int8_t retVal = -1;
- if (pinIntTrigger == 1) {
- retVal = (int8_t)interrupt1;
- } else if (pinIntTrigger == 2) {
- retVal = (int8_t)interrupt2;
+ // Return what woke the mcu.
+ int8_t ret = MY_WAKE_UP_BY_TIMER; // default: no interrupt triggered, timer wake up
+ if (interruptWakeUp()) {
+ ret = static_cast(_wokeUpByInterrupt);
}
- return retVal;
+ // Clear woke-up-by-interrupt flag, so next sleeps won't return immediately.
+ _wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
+
+ return ret;
}
-uint16_t hwCPUVoltage() {
- // Measure Vcc against 1.1V Vref
- #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
- ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
- #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
- ADMUX = (_BV(MUX5) | _BV(MUX0));
- #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
- ADMUX = (_BV(MUX3) | _BV(MUX2));
- #else
- ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
- #endif
+#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
+uint16_t hwCPUVoltage()
+{
+ // Measure Vcc against 1.1V Vref
+#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+ ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
+#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
+ ADMUX = (_BV(MUX5) | _BV(MUX0));
+#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
+ ADMUX = (_BV(MUX3) | _BV(MUX2));
+#else
+ ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
+#endif
// Vref settle
- delay(70);
+ delay(70);
// Do conversion
ADCSRA |= _BV(ADSC);
- while (bit_is_set(ADCSRA,ADSC));
+ while (bit_is_set(ADCSRA,ADSC)) {};
// return Vcc in mV
return (1125300UL) / ADC;
}
-uint16_t hwCPUFrequency() {
- noInterrupts();
+uint16_t hwCPUFrequency()
+{
+ cli();
// setup timer1
- TIFR1 = 0xFF;
- TCNT1 = 0;
+ TIFR1 = 0xFF;
+ TCNT1 = 0;
TCCR1A = 0;
TCCR1C = 0;
// save WDT settings
@@ -166,47 +234,51 @@ uint16_t hwCPUFrequency() {
WDTCSR |= (1 << WDIE);
wdt_reset();
// start timer1 with 1024 prescaling
- TCCR1B = _BV(CS12) | _BV(CS10);
+ TCCR1B = _BV(CS12) | _BV(CS10);
// wait until wdt interrupt
- while (bit_is_clear(WDTCSR,WDIF));
+ while (bit_is_clear(WDTCSR,WDIF)) {};
// stop timer
TCCR1B = 0;
// restore WDT settings
wdt_reset();
WDTCSR |= (1 << WDCE) | (1 << WDE);
WDTCSR = WDTsave;
- interrupts();
+ sei();
// return frequency in 1/10MHz (accuracy +- 10%)
return TCNT1 * 2048UL / 100000UL;
}
-uint16_t hwFreeMem() {
- extern int __heap_start, *__brkval;
- int v;
- return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
+uint16_t hwFreeMem()
+{
+ extern int __heap_start, *__brkval;
+ int v;
+ return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
-
-
+#endif
#ifdef MY_DEBUG
-void hwDebugPrint(const char *fmt, ... ) {
- char fmtBuffer[300];
- #ifdef MY_GATEWAY_FEATURE
- // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
- snprintf_P(fmtBuffer, 299, PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
- MY_SERIALDEVICE.print(fmtBuffer);
- #endif
+void hwDebugPrint(const char *fmt, ... )
+{
+ char fmtBuffer[MY_SERIAL_OUTPUT_SIZE];
+#ifdef MY_GATEWAY_FEATURE
+ // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
+ snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
+ MY_SERIALDEVICE.print(fmtBuffer);
+#else
+ // prepend timestamp (AVR nodes)
+ MY_SERIALDEVICE.print(hwMillis());
+ MY_SERIALDEVICE.print(" ");
+#endif
va_list args;
va_start (args, fmt );
- va_end (args);
- #ifdef MY_GATEWAY_FEATURE
- // Truncate message if this is gateway node
- vsnprintf_P(fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, fmt, args);
- fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH-1] = '\n';
- fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH] = '\0';
- #else
- vsnprintf_P(fmtBuffer, 299, fmt, args);
- #endif
+#ifdef MY_GATEWAY_FEATURE
+ // Truncate message if this is gateway node
+ vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args);
+ fmtBuffer[sizeof(fmtBuffer) - 2] = '\n';
+ fmtBuffer[sizeof(fmtBuffer) - 1] = '\0';
+#else
+ vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args);
+#endif
va_end (args);
MY_SERIALDEVICE.print(fmtBuffer);
MY_SERIALDEVICE.flush();
diff --git a/core/MyHwATMega328.h b/core/MyHwATMega328.h
index 8eb0060b1..7f120319b 100644
--- a/core/MyHwATMega328.h
+++ b/core/MyHwATMega328.h
@@ -27,56 +27,61 @@
#include
#include
#include
-
+#include
#ifdef __cplusplus
#include
#endif
+#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial
+#endif
+
#if defined __AVR_ATmega328P__
#ifndef sleep_bod_disable
#define sleep_bod_disable() \
-do { \
- unsigned char tempreg; \
- __asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \
- "ori %[tempreg], %[bods_bodse]" "\n\t" \
- "out %[mcucr], %[tempreg]" "\n\t" \
- "andi %[tempreg], %[not_bodse]" "\n\t" \
- "out %[mcucr], %[tempreg]" \
- : [tempreg] "=&d" (tempreg) \
- : [mcucr] "I" _SFR_IO_ADDR(MCUCR), \
- [bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \
- [not_bodse] "i" (~_BV(BODSE))); \
-} while (0)
+ do { \
+ unsigned char tempreg; \
+ __asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \
+ "ori %[tempreg], %[bods_bodse]" "\n\t" \
+ "out %[mcucr], %[tempreg]" "\n\t" \
+ "andi %[tempreg], %[not_bodse]" "\n\t" \
+ "out %[mcucr], %[tempreg]" \
+ : [tempreg] "=&d" (tempreg) \
+ : [mcucr] "I" _SFR_IO_ADDR(MCUCR), \
+ [bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \
+ [not_bodse] "i" (~_BV(BODSE))); \
+ } while (0)
#endif
#endif
// Define these as macros to save valuable space
-#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value))
-#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE)
-#define hwWatchdogReset() wdt_reset()
-#define hwReboot() wdt_enable(WDTO_15MS); while (1)
-#define hwMillis() millis()
-#define hwReadConfig(__pos) (eeprom_read_byte((uint8_t*)(__pos)))
+#define hwDigitalWrite(__pin, __value) digitalWriteFast(__pin, __value)
+#define hwDigitalRead(__pin) digitalReadFast(__pin)
+#define hwPinMode(__pin, __value) pinModeFast(__pin, __value)
-#ifndef eeprom_update_byte
- #define hwWriteConfig(loc, val) if((uint8_t)(val) != eeprom_read_byte((uint8_t*)(loc))) { eeprom_write_byte((uint8_t*)(loc), (val)); }
+
+#if defined(MY_DISABLED_SERIAL)
+#define hwInit()
#else
- #define hwWriteConfig(__pos, __value) (eeprom_update_byte((uint8_t*)(__pos), (__value)))
+#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE)
#endif
-//
-#define hwReadConfigBlock(__buf, __pos, __length) (eeprom_read_block((__buf), (void*)(__pos), (__length)))
-#define hwWriteConfigBlock(__pos, __buf, __length) (eeprom_write_block((void*)(__pos), (void*)__buf, (__length)))
+#define hwWatchdogReset() wdt_reset()
+#define hwReboot() wdt_enable(WDTO_15MS); while (1)
+#define hwMillis() millis()
+#define hwRandomNumberInit() randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN))
+#define hwReadConfig(__pos) eeprom_read_byte((uint8_t*)(__pos))
+#define hwWriteConfig(__pos, __val) eeprom_update_byte((uint8_t*)(__pos), (__val))
+#define hwReadConfigBlock(__buf, __pos, __length) eeprom_read_block((void*)(__buf), (void*)(__pos), (__length))
+#define hwWriteConfigBlock(__buf, __pos, __length) eeprom_update_block((void*)(__buf), (void*)(__pos), (__length))
-enum period_t
-{
+enum period_t {
SLEEP_15MS,
SLEEP_30MS,
SLEEP_60MS,
@@ -92,4 +97,8 @@ enum period_t
void hwInternalSleep(unsigned long ms);
+#ifndef DOXYGEN
+#define MY_CRITICAL_SECTION ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
+#endif /* DOXYGEN */
+
#endif
diff --git a/core/MyHwESP8266.cpp b/core/MyHwESP8266.cpp
index 1e501565f..9cc852abe 100644
--- a/core/MyHwESP8266.cpp
+++ b/core/MyHwESP8266.cpp
@@ -20,135 +20,128 @@
#include "MyHwESP8266.h"
#include
-/*
-int8_t pinIntTrigger = 0;
-void wakeUp() //place to send the interrupts
+void hwInit(void)
{
- pinIntTrigger = 1;
-}
-void wakeUp2() //place to send the second interrupts
-{
- pinIntTrigger = 2;
+#if !defined(MY_DISABLED_SERIAL)
+ MY_SERIALDEVICE.begin(MY_BAUD_RATE, SERIAL_8N1, MY_ESP8266_SERIAL_MODE, 1);
+ MY_SERIALDEVICE.setDebugOutput(true);
+#endif
+ EEPROM.begin(EEPROM_size);
}
-// Watchdog Timer interrupt service routine. This routine is required
-// to allow automatic WDIF and WDIE bit clearance in hardware.
-ISR (WDT_vect)
+void hwReadConfigBlock(void* buf, void* addr, size_t length)
{
- // WDIE & WDIF is cleared in hardware upon entering this ISR
- wdt_disable();
+ uint8_t* dst = static_cast(buf);
+ int pos = reinterpret_cast(addr);
+ while (length-- > 0) {
+ *dst++ = EEPROM.read(pos++);
+ }
}
-*/
-static void hwInitConfigBlock( size_t length = 1024 /*ATMega328 has 1024 bytes*/ )
+void hwWriteConfigBlock(void* buf, void* addr, size_t length)
{
- static bool initDone = false;
- if (!initDone)
- {
- EEPROM.begin(length);
- initDone = true;
- }
+ uint8_t* src = static_cast(buf);
+ int pos = reinterpret_cast(addr);
+ while (length-- > 0) {
+ EEPROM.write(pos++, *src++);
+ }
+ // see implementation, commit only executed if diff
+ EEPROM.commit();
}
-void hwReadConfigBlock(void* buf, void* adr, size_t length)
+uint8_t hwReadConfig(const int addr)
{
- hwInitConfigBlock();
- uint8_t* dst = static_cast(buf);
- int offs = reinterpret_cast(adr);
- while (length-- > 0)
- {
- *dst++ = EEPROM.read(offs++);
- }
+ uint8_t value;
+ hwReadConfigBlock(&value, reinterpret_cast(addr), 1);
+ return value;
}
-void hwWriteConfigBlock(void* buf, void* adr, size_t length)
+void hwWriteConfig(const int addr, uint8_t value)
{
- hwInitConfigBlock();
- uint8_t* src = static_cast(buf);
- int offs = reinterpret_cast(adr);
- while (length-- > 0)
- {
- EEPROM.write(offs++, *src++);
- }
- EEPROM.commit();
+ hwWriteConfigBlock(&value, reinterpret_cast(addr), 1);
}
-uint8_t hwReadConfig(int adr)
-{
- uint8_t value;
- hwReadConfigBlock(&value, reinterpret_cast(adr), 1);
- return value;
-}
-void hwWriteConfig(int adr, uint8_t value)
+int8_t hwSleep(unsigned long ms)
{
- uint8_t curr = hwReadConfig(adr);
- if (curr != value)
- {
- hwWriteConfigBlock(&value, reinterpret_cast(adr), 1);
- }
-}
-
-
-int8_t hwSleep(unsigned long ms) {
// TODO: Not supported!
(void)ms;
- return -2;
+ return MY_SLEEP_NOT_POSSIBLE;
}
-int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
+int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms)
+{
// TODO: Not supported!
(void)interrupt;
(void)mode;
(void)ms;
- return -2;
+ return MY_SLEEP_NOT_POSSIBLE;
}
-int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
+int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2,
+ unsigned long ms)
+{
// TODO: Not supported!
(void)interrupt1;
(void)mode1;
(void)interrupt2;
(void)mode2;
(void)ms;
- return -2;
+ return MY_SLEEP_NOT_POSSIBLE;
}
+#if defined(MY_SPECIAL_DEBUG)
+// settings for getVcc()
ADC_MODE(ADC_VCC);
+#else
+// [default] settings for analogRead(A0)
+ADC_MODE(ADC_TOUT);
+#endif
-uint16_t hwCPUVoltage() {
- // in mV
+#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
+
+uint16_t hwCPUVoltage()
+{
+#if defined(MY_SPECIAL_DEBUG)
+ // in mV, requires ADC_VCC set
return ESP.getVcc();
+#else
+ // not possible
+ return 0;
+#endif
}
-uint16_t hwCPUFrequency() {
+uint16_t hwCPUFrequency()
+{
// in 1/10Mhz
return ESP.getCpuFreqMHz()*10;
}
-uint16_t hwFreeMem() {
+uint16_t hwFreeMem()
+{
return ESP.getFreeHeap();
}
+#endif
#ifdef MY_DEBUG
-void hwDebugPrint(const char *fmt, ... ) {
- char fmtBuffer[300];
- #ifdef MY_GATEWAY_FEATURE
- // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
- snprintf_P(fmtBuffer, 299, PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
- MY_SERIALDEVICE.print(fmtBuffer);
- #endif
+void hwDebugPrint(const char *fmt, ... )
+{
+ char fmtBuffer[MY_SERIAL_OUTPUT_SIZE];
+#ifdef MY_GATEWAY_FEATURE
+ // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
+ snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
+ MY_SERIALDEVICE.print(fmtBuffer);
+#endif
va_list args;
va_start (args, fmt );
- va_end (args);
- #ifdef MY_GATEWAY_FEATURE
- // Truncate message if this is gateway node
- vsnprintf_P(fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, fmt, args);
- fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH-1] = '\n';
- fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH] = '\0';
- #else
- vsnprintf_P(fmtBuffer, 299, fmt, args);
- #endif
+#ifdef MY_GATEWAY_FEATURE
+ // Truncate message if this is gateway node
+ vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args);
+ fmtBuffer[sizeof(fmtBuffer) - 2] = '\n';
+ fmtBuffer[sizeof(fmtBuffer) - 1] = '\0';
+#else
+ vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args);
+#endif
va_end (args);
MY_SERIALDEVICE.print(fmtBuffer);
MY_SERIALDEVICE.flush();
diff --git a/core/MyHwESP8266.h b/core/MyHwESP8266.h
index a9693ff5b..bfb7e7cb0 100644
--- a/core/MyHwESP8266.h
+++ b/core/MyHwESP8266.h
@@ -25,23 +25,41 @@
#include
#endif
+#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial
+#endif
+
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
+#define EEPROM_size (1024)
// Define these as macros to save valuable space
-
-#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value))
-#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE); MY_SERIALDEVICE.setDebugOutput(true)
+#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value)
+#define hwDigitalRead(__pin) digitalRead(__pin)
+#define hwPinMode(__pin, __value) pinMode(__pin, __value)
#define hwWatchdogReset() wdt_reset()
-#define hwReboot() ESP.restart();
+#define hwReboot() ESP.restart()
#define hwMillis() millis()
+#define hwRandomNumberInit() randomSeed(RANDOM_REG32)
+void hwInit(void);
void hwReadConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfigBlock(void* buf, void* adr, size_t length);
-void hwWriteConfig(int adr, uint8_t value);
-uint8_t hwReadConfig(int adr);
+void hwWriteConfig(const int addr, uint8_t value);
+uint8_t hwReadConfig(const int addr);
+
+/**
+ * Restore interrupt state.
+ * Helper function for MY_CRITICAL_SECTION.
+ */
+static __inline__ void __psRestore(const uint32_t *__s)
+{
+ xt_wsr_ps( *__s );
+}
+#ifndef DOXYGEN
+#define MY_CRITICAL_SECTION for ( uint32_t __psSaved __attribute__((__cleanup__(__psRestore))) = xt_rsil(15), __ToDo = 1; __ToDo ; __ToDo = 0 )
+#endif /* DOXYGEN */
#endif // #ifdef ARDUINO_ARCH_ESP8266
diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp
new file mode 100644
index 000000000..e90b596a1
--- /dev/null
+++ b/core/MyHwLinuxGeneric.cpp
@@ -0,0 +1,132 @@
+/**
+ * The MySensors Arduino library handles the wireless radio link and protocol
+ * between your home built sensors/actuators and HA controller of choice.
+ * The sensors forms a self healing radio network with optional repeaters. Each
+ * repeater and gateway builds a routing tables in EEPROM which keeps track of the
+ * network topology allowing messages to be routed to nodes.
+ *
+ * Created by Henrik Ekblad
+ * Copyright (C) 2013-2016 Sensnology AB
+ * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
+ *
+ * Documentation: http://www.mysensors.org
+ * Support Forum: http://forum.mysensors.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include "MyHwLinuxGeneric.h"
+
+#include
+#include
+#include "SoftEeprom.h"
+#include "log.h"
+
+static SoftEeprom eeprom = SoftEeprom(MY_LINUX_CONFIG_FILE, 1024); // ATMega328 has 1024 bytes
+
+void hwInit()
+{
+#ifdef MY_GATEWAY_SERIAL
+ MY_SERIALDEVICE.begin(MY_BAUD_RATE);
+#ifdef MY_LINUX_SERIAL_GROUPNAME
+ if (!MY_SERIALDEVICE.setGroupPerm(MY_LINUX_SERIAL_GROUPNAME)) {
+ logError("Unable to change permission for serial port device.\n");
+ exit(1);
+ }
+#endif
+#endif
+}
+
+void hwReadConfigBlock(void* buf, void* addr, size_t length)
+{
+ eeprom.readBlock(buf, addr, length);
+}
+
+void hwWriteConfigBlock(void* buf, void* addr, size_t length)
+{
+ eeprom.writeBlock(buf, addr, length);
+}
+
+uint8_t hwReadConfig(int addr)
+{
+ return eeprom.readByte(addr);
+}
+
+void hwWriteConfig(int addr, uint8_t value)
+{
+ eeprom.writeByte(addr, value);
+}
+
+void hwRandomNumberInit()
+{
+ randomSeed(time(NULL));
+}
+
+unsigned long hwMillis()
+{
+ return millis();
+}
+
+// Not supported!
+int8_t hwSleep(unsigned long ms)
+{
+ (void)ms;
+
+ return MY_SLEEP_NOT_POSSIBLE;
+}
+
+// Not supported!
+int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms)
+{
+ (void)interrupt;
+ (void)mode;
+ (void)ms;
+
+ return MY_SLEEP_NOT_POSSIBLE;
+}
+
+// Not supported!
+int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2,
+ unsigned long ms)
+{
+ (void)interrupt1;
+ (void)mode1;
+ (void)interrupt2;
+ (void)mode2;
+ (void)ms;
+
+ return MY_SLEEP_NOT_POSSIBLE;
+}
+
+#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
+uint16_t hwCPUVoltage()
+{
+ // TODO: Not supported!
+ return 0;
+}
+
+uint16_t hwCPUFrequency()
+{
+ // TODO: Not supported!
+ return 0;
+}
+
+uint16_t hwFreeMem()
+{
+ // TODO: Not supported!
+ return 0;
+}
+#endif
+
+#ifdef MY_DEBUG
+void hwDebugPrint(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vlogDebug(fmt, args);
+ va_end(args);
+}
+#endif
diff --git a/core/MyHwLinuxGeneric.h b/core/MyHwLinuxGeneric.h
new file mode 100644
index 000000000..234156f78
--- /dev/null
+++ b/core/MyHwLinuxGeneric.h
@@ -0,0 +1,91 @@
+/**
+ * The MySensors Arduino library handles the wireless radio link and protocol
+ * between your home built sensors/actuators and HA controller of choice.
+ * The sensors forms a self healing radio network with optional repeaters. Each
+ * repeater and gateway builds a routing tables in EEPROM which keeps track of the
+ * network topology allowing messages to be routed to nodes.
+ *
+ * Created by Henrik Ekblad
+ * Copyright (C) 2013-2016 Sensnology AB
+ * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
+ *
+ * Documentation: http://www.mysensors.org
+ * Support Forum: http://forum.mysensors.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef MyHwLinuxGeneric_h
+#define MyHwLinuxGeneric_h
+
+#include
+#include
+#include "MyHw.h"
+#include "SerialPort.h"
+
+#ifdef MY_IS_SERIAL_PTY
+SerialPort Serial = SerialPort(MY_LINUX_SERIAL_PTY, true);
+#else
+SerialPort Serial = SerialPort(MY_LINUX_SERIAL_PORT);
+#endif
+
+#ifndef MY_SERIALDEVICE
+#define MY_SERIALDEVICE Serial
+#endif
+
+// Define these as macros (do nothing)
+#define hwWatchdogReset()
+#define hwReboot()
+
+#define hwDigitalWrite(__pin, __value) _Pragma("GCC error \"Not supported on linux-generic\"")
+#define hwDigitalRead(__pin) _Pragma("GCC error \"Not supported on linux-generic\"")
+#define hwPinMode(__pin, __value) _Pragma("GCC error \"Not supported on linux-generic\"")
+
+void hwInit();
+inline void hwReadConfigBlock(void* buf, void* addr, size_t length);
+inline void hwWriteConfigBlock(void* buf, void* addr, size_t length);
+inline uint8_t hwReadConfig(int addr);
+inline void hwWriteConfig(int addr, uint8_t value);
+inline void hwRandomNumberInit();
+inline unsigned long hwMillis();
+
+#ifdef MY_RF24_IRQ_PIN
+static pthread_mutex_t hw_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static __inline__ void __hwUnlock(const uint8_t *__s)
+{
+ pthread_mutex_unlock(&hw_mutex);
+ (void)__s;
+}
+
+static __inline__ void __hwLock()
+{
+ pthread_mutex_lock(&hw_mutex);
+}
+#endif
+
+#if defined(DOXYGEN)
+#define ATOMIC_BLOCK_CLEANUP
+#elif defined(MY_RF24_IRQ_PIN)
+#define ATOMIC_BLOCK_CLEANUP uint8_t __atomic_loop \
+ __attribute__((__cleanup__( __hwUnlock ))) = 1
+#else
+#define ATOMIC_BLOCK_CLEANUP
+#endif /* DOXYGEN */
+
+#if defined(DOXYGEN)
+#define ATOMIC_BLOCK
+#elif defined(MY_RF24_IRQ_PIN)
+#define ATOMIC_BLOCK for ( ATOMIC_BLOCK_CLEANUP, __hwLock(); \
+ __atomic_loop ; __atomic_loop = 0 )
+#else
+#define ATOMIC_BLOCK
+#endif /* DOXYGEN */
+
+#ifndef DOXYGEN
+#define MY_CRITICAL_SECTION ATOMIC_BLOCK
+#endif /* DOXYGEN */
+
+#endif
diff --git a/core/MyHwRPi.cpp b/core/MyHwRPi.cpp
new file mode 100644
index 000000000..989af5fa9
--- /dev/null
+++ b/core/MyHwRPi.cpp
@@ -0,0 +1,146 @@
+/**
+ * The MySensors Arduino library handles the wireless radio link and protocol
+ * between your home built sensors/actuators and HA controller of choice.
+ * The sensors forms a self healing radio network with optional repeaters. Each
+ * repeater and gateway builds a routing tables in EEPROM which keeps track of the
+ * network topology allowing messages to be routed to nodes.
+ *
+ * Created by Henrik Ekblad
+ * Copyright (C) 2013-2016 Sensnology AB
+ * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
+ *
+ * Documentation: http://www.mysensors.org
+ * Support Forum: http://forum.mysensors.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include "MyHwRPi.h"
+
+#include
+#include
+#include "SoftEeprom.h"
+
+static SoftEeprom eeprom = SoftEeprom(MY_LINUX_CONFIG_FILE, 1024); // ATMega328 has 1024 bytes
+
+void hwInit()
+{
+#ifdef MY_GATEWAY_SERIAL
+ MY_SERIALDEVICE.begin(MY_BAUD_RATE);
+#ifdef MY_LINUX_SERIAL_GROUPNAME
+ if (!MY_SERIALDEVICE.setGroupPerm(MY_LINUX_SERIAL_GROUPNAME)) {
+ logError("Unable to change permission for serial port device.\n");
+ exit(1);
+ }
+#endif
+#endif
+}
+
+void hwReadConfigBlock(void* buf, void* adr, size_t length)
+{
+ eeprom.readBlock(buf, adr, length);
+}
+
+void hwWriteConfigBlock(void* buf, void* adr, size_t length)
+{
+ eeprom.writeBlock(buf, adr, length);
+}
+
+uint8_t hwReadConfig(int adr)
+{
+ return eeprom.readByte(adr);
+}
+
+void hwWriteConfig(int adr, uint8_t value)
+{
+ eeprom.writeByte(adr, value);
+}
+
+void hwRandomNumberInit()
+{
+ randomSeed(time(NULL));
+}
+
+unsigned long hwMillis()
+{
+ return millis();
+}
+
+// Not supported!
+int8_t hwSleep(unsigned long ms)
+{
+ (void)ms;
+
+ return MY_SLEEP_NOT_POSSIBLE;
+}
+
+// Not supported!
+int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms)
+{
+ (void)interrupt;
+ (void)mode;
+ (void)ms;
+
+ return MY_SLEEP_NOT_POSSIBLE;
+}
+
+// Not supported!
+int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2,
+ unsigned long ms)
+{
+ (void)interrupt1;
+ (void)mode1;
+ (void)interrupt2;
+ (void)mode2;
+ (void)ms;
+
+ return MY_SLEEP_NOT_POSSIBLE;
+}
+
+#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
+uint16_t hwCPUVoltage()
+{
+ // TODO: Not supported!
+ return 0;
+}
+
+uint16_t hwCPUFrequency()
+{
+ // TODO: Not supported!
+ return 0;
+}
+
+uint16_t hwFreeMem()
+{
+ // TODO: Not supported!
+ return 0;
+}
+#endif
+
+#ifdef MY_DEBUG
+void hwDebugPrint(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vlogDebug(fmt, args);
+ va_end(args);
+}
+#endif
+
+void hwDigitalWrite(uint8_t pin, uint8_t value)
+{
+ digitalWrite(pin, value);
+}
+
+int hwDigitalRead(uint8_t pin)
+{
+ return digitalRead(pin);
+}
+
+void hwPinMode(uint8_t pin, uint8_t mode)
+{
+ pinMode(pin, mode);
+}
diff --git a/core/MyHwRPi.h b/core/MyHwRPi.h
new file mode 100644
index 000000000..2e39d136a
--- /dev/null
+++ b/core/MyHwRPi.h
@@ -0,0 +1,55 @@
+/**
+ * The MySensors Arduino library handles the wireless radio link and protocol
+ * between your home built sensors/actuators and HA controller of choice.
+ * The sensors forms a self healing radio network with optional repeaters. Each
+ * repeater and gateway builds a routing tables in EEPROM which keeps track of the
+ * network topology allowing messages to be routed to nodes.
+ *
+ * Created by Henrik Ekblad
+ * Copyright (C) 2013-2016 Sensnology AB
+ * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
+ *
+ * Documentation: http://www.mysensors.org
+ * Support Forum: http://forum.mysensors.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef MyHwRPi_h
+#define MyHwRPi_h
+
+#include
+#include
+#include "MyHwLinuxGeneric.h"
+#include "log.h"
+#include "bcm2835.h"
+
+class Bcm2835Init
+{
+public:
+ Bcm2835Init()
+ {
+ if (!bcm2835_init()) {
+ logError("Failed to initialized bcm2835.\n");
+ exit(1);
+ }
+ }
+ ~Bcm2835Init()
+ {
+ bcm2835_close();
+ }
+};
+Bcm2835Init bcm2835Init;
+
+#undef hwDigitalWrite
+inline void hwDigitalWrite(uint8_t, uint8_t);
+
+#undef hwDigitalRead
+inline int hwDigitalRead(uint8_t);
+
+#undef hwPinMode
+inline void hwPinMode(uint8_t, uint8_t);
+
+#endif
diff --git a/core/MyHwSAMD.cpp b/core/MyHwSAMD.cpp
index 115400a28..b5c220757 100644
--- a/core/MyHwSAMD.cpp
+++ b/core/MyHwSAMD.cpp
@@ -42,142 +42,187 @@ ISR (WDT_vect)
-void i2c_eeprom_write_byte(unsigned int eeaddress, byte data ) {
- int rdata = data;
- Wire.beginTransmission(I2C_EEP_ADDRESS);
- Wire.write((int)(eeaddress >> 8)); // MSB
- Wire.write((int)(eeaddress & 0xFF)); // LSB
- Wire.write(rdata);
- Wire.endTransmission();
-}
-
-byte i2c_eeprom_read_byte(unsigned int eeaddress ) {
- byte rdata = 0xFF;
- Wire.beginTransmission(I2C_EEP_ADDRESS);
- Wire.write((int)(eeaddress >> 8)); // MSB
- Wire.write((int)(eeaddress & 0xFF)); // LSB
- Wire.endTransmission();
- Wire.requestFrom(I2C_EEP_ADDRESS,1);
- if (Wire.available()) rdata = Wire.read();
- return rdata;
+void i2c_eeprom_write_byte(unsigned int eeaddress, byte data )
+{
+ int rdata = data;
+ Wire.beginTransmission(I2C_EEP_ADDRESS);
+ Wire.write((int)(eeaddress >> 8)); // MSB
+ Wire.write((int)(eeaddress & 0xFF)); // LSB
+ Wire.write(rdata);
+ Wire.endTransmission();
+}
+
+byte i2c_eeprom_read_byte(unsigned int eeaddress )
+{
+ byte rdata = 0xFF;
+ Wire.beginTransmission(I2C_EEP_ADDRESS);
+ Wire.write((int)(eeaddress >> 8)); // MSB
+ Wire.write((int)(eeaddress & 0xFF)); // LSB
+ Wire.endTransmission();
+ Wire.requestFrom(I2C_EEP_ADDRESS,1);
+ if (Wire.available()) {
+ rdata = Wire.read();
+ }
+ return rdata;
}
void hwReadConfigBlock(void* buf, void* adr, size_t length)
{
- uint8_t* dst = static_cast(buf);
- int offs = reinterpret_cast(adr);
- while (length-- > 0)
- {
- *dst++ = i2c_eeprom_read_byte(offs++);
- }
+ uint8_t* dst = static_cast(buf);
+ int offs = reinterpret_cast(adr);
+ while (length-- > 0) {
+ *dst++ = i2c_eeprom_read_byte(offs++);
+ }
}
void hwWriteConfigBlock(void* buf, void* adr, size_t length)
{
- uint8_t* src = static_cast(buf);
- int offs = reinterpret_cast(adr);
- while (length-- > 0)
- {
- i2c_eeprom_write_byte(offs++, *src++);
- }
+ uint8_t* src = static_cast(buf);
+ int offs = reinterpret_cast(adr);
+ while (length-- > 0) {
+ i2c_eeprom_write_byte(offs++, *src++);
+ }
}
uint8_t hwReadConfig(int adr)
{
- uint8_t value;
- hwReadConfigBlock(&value, reinterpret_cast(adr), 1);
- return value;
+ uint8_t value;
+ hwReadConfigBlock(&value, reinterpret_cast(adr), 1);
+ return value;
}
void hwWriteConfig(int adr, uint8_t value)
{
- uint8_t curr = hwReadConfig(adr);
- if (curr != value)
- {
- hwWriteConfigBlock(&value, reinterpret_cast(adr), 1);
- }
+ uint8_t curr = hwReadConfig(adr);
+ if (curr != value) {
+ hwWriteConfigBlock(&value, reinterpret_cast(adr), 1);
+ }
}
-void hwInit() {
- MY_SERIALDEVICE.begin(MY_BAUD_RATE);
- #ifndef MY_GATEWAY_W5100
- while (!MY_SERIALDEVICE) {}
- #endif
- Wire.begin();
-}
-
-void hwWatchdogReset() {
- // TODO: Not supported!
+void hwInit()
+{
+ MY_SERIALDEVICE.begin(MY_BAUD_RATE);
+#if defined(MY_GATEWAY_SERIAL)
+ while (!MY_SERIALDEVICE) {}
+#endif
+ Wire.begin();
}
-void hwReboot() {
- // TODO: Not supported!
+void hwWatchdogReset()
+{
+ // TODO: Not supported!
}
-int8_t hwSleep(unsigned long ms) {
- // TODO: Not supported!
- (void)ms;
- return -2;
+void hwReboot()
+{
+ NVIC_SystemReset();
+ while (true);
}
-int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
- // TODO: Not supported!
- (void)interrupt;
- (void)mode;
- (void)ms;
- return -2;
+int8_t hwSleep(unsigned long ms)
+{
+ // TODO: Not supported!
+ (void)ms;
+ return MY_SLEEP_NOT_POSSIBLE;
}
-int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
- // TODO: Not supported!
- (void)interrupt1;
- (void)mode1;
- (void)interrupt2;
- (void)mode2;
- (void)ms;
- return -2;
+int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms)
+{
+ // TODO: Not supported!
+ (void)interrupt;
+ (void)mode;
+ (void)ms;
+ return MY_SLEEP_NOT_POSSIBLE;
}
-uint16_t hwCPUVoltage() {
+int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2,
+ unsigned long ms)
+{
// TODO: Not supported!
- return 0;
+ (void)interrupt1;
+ (void)mode1;
+ (void)interrupt2;
+ (void)mode2;
+ (void)ms;
+ return MY_SLEEP_NOT_POSSIBLE;
}
-
-uint16_t hwCPUFrequency() {
- // TODO: Not supported!
- return 0;
+
+#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
+uint16_t hwCPUVoltage()
+{
+
+ // disable ADC
+ while (ADC->STATUS.bit.SYNCBUSY);
+ ADC->CTRLA.bit.ENABLE = 0x00;
+
+ // internal 1V reference (default)
+ analogReference(AR_INTERNAL1V0);
+ // 12 bit resolution (default)
+ analogWriteResolution(12);
+ // MUXp 0x1B = SCALEDIOVCC/4 => connected to Vcc
+ ADC->INPUTCTRL.bit.MUXPOS = 0x1B ;
+
+ // enable ADC
+ while (ADC->STATUS.bit.SYNCBUSY);
+ ADC->CTRLA.bit.ENABLE = 0x01;
+ // start conversion
+ while (ADC->STATUS.bit.SYNCBUSY);
+ ADC->SWTRIG.bit.START = 1;
+ // clear the Data Ready flag
+ ADC->INTFLAG.bit.RESRDY = 1;
+ // start conversion again, since The first conversion after the reference is changed must not be used.
+ while (ADC->STATUS.bit.SYNCBUSY);
+ ADC->SWTRIG.bit.START = 1;
+
+ // waiting for conversion to complete
+ while (!ADC->INTFLAG.bit.RESRDY);
+ const uint32_t valueRead = ADC->RESULT.reg;
+
+ // disable ADC
+ while (ADC->STATUS.bit.SYNCBUSY);
+ ADC->CTRLA.bit.ENABLE = 0x00;
+
+ return valueRead * 4;
+}
+
+uint16_t hwCPUFrequency()
+{
+ // TODO: currently reporting compile time frequency (in 1/10MHz)
+ return F_CPU / 100000UL;
}
-
-uint16_t hwFreeMem() {
+
+uint16_t hwFreeMem()
+{
// TODO: Not supported!
return 0;
}
+#endif
#ifdef MY_DEBUG
-void hwDebugPrint(const char *fmt, ... ) {
- if (MY_SERIALDEVICE) {
- char fmtBuffer[300];
- #ifdef MY_GATEWAY_FEATURE
+void hwDebugPrint(const char *fmt, ... )
+{
+ if (MY_SERIALDEVICE) {
+ char fmtBuffer[MY_SERIAL_OUTPUT_SIZE];
+#ifdef MY_GATEWAY_FEATURE
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
- snprintf(fmtBuffer, 299, PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
+ snprintf(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
MY_SERIALDEVICE.print(fmtBuffer);
- #endif
- va_list args;
- va_start (args, fmt );
- va_end (args);
- #ifdef MY_GATEWAY_FEATURE
+#endif
+ va_list args;
+ va_start (args, fmt );
+#ifdef MY_GATEWAY_FEATURE
// Truncate message if this is gateway node
- vsnprintf(fmtBuffer, 60, fmt, args);
- fmtBuffer[59] = '\n';
- fmtBuffer[60] = '\0';
- #else
- vsnprintf(fmtBuffer, 299, fmt, args);
- #endif
- va_end (args);
- MY_SERIALDEVICE.print(fmtBuffer);
-// MY_SERIALDEVICE.flush();
-
- //MY_SERIALDEVICE.write(freeRam());
- }
+ vsnprintf(fmtBuffer, sizeof(fmtBuffer), fmt, args);
+ fmtBuffer[sizeof(fmtBuffer) - 2] = '\n';
+ fmtBuffer[sizeof(fmtBuffer) - 1] = '\0';
+#else
+ vsnprintf(fmtBuffer, sizeof(fmtBuffer), fmt, args);
+#endif
+ va_end (args);
+ MY_SERIALDEVICE.print(fmtBuffer);
+ // MY_SERIALDEVICE.flush();
+
+ //MY_SERIALDEVICE.write(freeRam());
+ }
}
#endif
diff --git a/core/MyHwSAMD.h b/core/MyHwSAMD.h
index fcc5a9d79..647de3805 100644
--- a/core/MyHwSAMD.h
+++ b/core/MyHwSAMD.h
@@ -34,28 +34,48 @@
#define max(a,b) ((a)>(b)?(a):(b))
#define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__)
+uint8_t configBlock[1024];
// Define these as macros to save valuable space
+#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value)
+#define hwDigitalRead(__pin) digitalRead(__pin)
+#define hwPinMode(__pin, __value) pinMode(__pin, __value)
+#define hwMillis() millis()
+#define hwRandomNumberInit() randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN))
-uint8_t configBlock[1024];
-#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value))
void hwInit();
void hwWatchdogReset();
void hwReboot();
-#define hwMillis() millis()
-
void hwReadConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfig(int adr, uint8_t value);
uint8_t hwReadConfig(int adr);
+#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE SerialUSB
+#endif
+
+/**
+ * Disable all interrupts.
+ * Helper function for MY_CRITICAL_SECTION.
+ */
+static __inline__ uint8_t __disableIntsRetVal(void)
+{
+ __disable_irq();
+ return 1;
+}
+
+/**
+ * Restore priority mask register.
+ * Helper function for MY_CRITICAL_SECTION.
+ */
+static __inline__ void __priMaskRestore(const uint32_t *priMask)
+{
+ __set_PRIMASK(*priMask);
+}
+#ifndef DOXYGEN
+#define MY_CRITICAL_SECTION for ( uint32_t __savePriMask __attribute__((__cleanup__(__priMaskRestore))) = __get_PRIMASK(), __ToDo = __disableIntsRetVal(); __ToDo ; __ToDo = 0 )
+#endif /* DOXYGEN */
-/*
-#define hwReadConfigBlock(__buf, __adr, __length) ( __length = __length)
-#define hwWriteConfigBlock(__buf, __adr, __length) ( __length = __length)
-#define hwWriteConfig(__adr, __value) ( __value = __value)
-#define hwReadConfig(__adr) (0)
-*/
#endif // #ifdef ARDUINO_ARCH_SAMD
diff --git a/core/MyInclusionMode.cpp b/core/MyInclusionMode.cpp
index e55612f3b..a3a2319f8 100644
--- a/core/MyInclusionMode.cpp
+++ b/core/MyInclusionMode.cpp
@@ -19,38 +19,44 @@
#include "MyInclusionMode.h"
+// global variables
+extern MyMessage _msgTmp;
+
unsigned long _inclusionStartTime;
bool _inclusionMode;
-inline void inclusionInit() {
+inline void inclusionInit()
+{
_inclusionMode = false;
- #if defined(MY_INCLUSION_BUTTON_FEATURE)
- // Setup digital in that triggers inclusion mode
- pinMode(MY_INCLUSION_MODE_BUTTON_PIN, INPUT);
- digitalWrite(MY_INCLUSION_MODE_BUTTON_PIN, HIGH);
- #endif
+#if defined(MY_INCLUSION_BUTTON_FEATURE)
+ // Setup digital in that triggers inclusion mode
+ hwPinMode(MY_INCLUSION_MODE_BUTTON_PIN, INPUT);
+ hwDigitalWrite(MY_INCLUSION_MODE_BUTTON_PIN, HIGH);
+#endif
}
-void inclusionModeSet(bool newMode) {
- if (newMode != _inclusionMode) {
- _inclusionMode = newMode;
- // Send back mode change to controller
- gatewayTransportSend(buildGw(_msg, I_INCLUSION_MODE).set((uint8_t)(_inclusionMode?1:0)));
- if (_inclusionMode) {
- _inclusionStartTime = hwMillis();
- }
- }
+void inclusionModeSet(bool newMode)
+{
+ if (newMode != _inclusionMode) {
+ _inclusionMode = newMode;
+ // Send back mode change to controller
+ gatewayTransportSend(buildGw(_msgTmp, I_INCLUSION_MODE).set((uint8_t)(_inclusionMode?1:0)));
+ if (_inclusionMode) {
+ _inclusionStartTime = hwMillis();
+ }
+ }
}
-inline void inclusionProcess() {
- #ifdef MY_INCLUSION_BUTTON_FEATURE
- if (!_inclusionMode && digitalRead(MY_INCLUSION_MODE_BUTTON_PIN) == MY_INCLUSION_BUTTON_PRESSED) {
+inline void inclusionProcess()
+{
+#ifdef MY_INCLUSION_BUTTON_FEATURE
+ if (!_inclusionMode && hwDigitalRead(MY_INCLUSION_MODE_BUTTON_PIN) == MY_INCLUSION_BUTTON_PRESSED) {
// Start inclusion mode
inclusionModeSet(true);
}
- #endif
+#endif
if (_inclusionMode && hwMillis()-_inclusionStartTime>MY_INCLUSION_MODE_DURATION*1000L) {
// inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode
diff --git a/core/MyIndication.cpp b/core/MyIndication.cpp
index 8e5e65b86..72faa2285 100644
--- a/core/MyIndication.cpp
+++ b/core/MyIndication.cpp
@@ -18,25 +18,29 @@
*/
#include "MyIndication.h"
-#ifdef MY_LEDS_BLINKING_FEATURE
+#if defined(MY_DEFAULT_TX_LED_PIN)|| defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_ERR_LED_PIN)
#include "MyLeds.h"
#endif
void setIndication( const indication_t ind )
{
-#ifdef MY_LEDS_BLINKING_FEATURE
- if ((INDICATION_TX == ind) || (INDICATION_GW_TX == ind))
- {
- ledsBlinkTx(1);
- } else if ((INDICATION_RX == ind) || (INDICATION_GW_RX == ind))
- {
- ledsBlinkRx(1);
- } else if (ind > INDICATION_ERR_START)
- {
- // Number of blinks indicates which error occurred.
- ledsBlinkErr(ind-INDICATION_ERR_START);
- }
+#if defined(MY_DEFAULT_TX_LED_PIN)
+ if ((INDICATION_TX == ind) || (INDICATION_GW_TX == ind)) {
+ ledsBlinkTx(1);
+ } else
#endif
- if (indication)
- indication(ind);
+#if defined(MY_DEFAULT_RX_LED_PIN)
+ if ((INDICATION_RX == ind) || (INDICATION_GW_RX == ind)) {
+ ledsBlinkRx(1);
+ } else
+#endif
+#if defined(MY_DEFAULT_ERR_LED_PIN)
+ if (ind > INDICATION_ERR_START) {
+ // Number of blinks indicates which error occurred.
+ ledsBlinkErr(ind-INDICATION_ERR_START);
+ }
+#endif
+ if (indication) {
+ indication(ind);
+ }
}
diff --git a/core/MyIndication.h b/core/MyIndication.h
index ab2bbbc02..0e2f8ea2b 100644
--- a/core/MyIndication.h
+++ b/core/MyIndication.h
@@ -24,41 +24,44 @@
* Indication type
*/
typedef enum {
- INDICATION_TX = 0, //!< Sent a message.
- INDICATION_RX, //!< Received a message.
-
- INDICATION_GW_TX, //!< Gateway transmit message.
- INDICATION_GW_RX, //!< Gateway receive message.
-
- INDICATION_FIND_PARENT, //!< Start finding parent node.
- INDICATION_GOT_PARENT, //!< Found parent node.
- INDICATION_REQ_NODEID, //!< Request node ID.
- INDICATION_GOT_NODEID, //!< Got a node ID.
- INDICATION_REQ_REGISTRATION, //!< Request node registration.
- INDICATION_GOT_REGISTRATION, //!< Got registration reponse.
- INDICATION_REBOOT, //!< Rebooting node.
- INDICATION_PRESENT, //!< Presenting node to gateway.
- INDICATION_CLEAR_ROUTING, //!< Clear rrouting table requested.
- INDICATION_SLEEP, //!< Node goes to sleep.
- INDICATION_WAKEUP, //!< Node just woke from sleep.
- INDICATION_FW_UPDATE_START, //!< Start of OTA firmware update process.
- INDICATION_FW_UPDATE_RX, //!< Received a piece of firmware data.
+ INDICATION_TX = 0, //!< Sent a message.
+ INDICATION_RX, //!< Received a message.
- INDICATION_ERR_START = 100,
- INDICATION_ERR_TX, //!< Failed to transmit message.
+ INDICATION_GW_TX, //!< Gateway transmit message.
+ INDICATION_GW_RX, //!< Gateway receive message.
+
+ INDICATION_FIND_PARENT, //!< Start finding parent node.
+ INDICATION_GOT_PARENT, //!< Found parent node.
+ INDICATION_REQ_NODEID, //!< Request node ID.
+ INDICATION_GOT_NODEID, //!< Got a node ID.
+ INDICATION_CHECK_UPLINK, //!< Check uplink
+ INDICATION_REQ_REGISTRATION, //!< Request node registration.
+ INDICATION_GOT_REGISTRATION, //!< Got registration reponse.
+ INDICATION_REBOOT, //!< Rebooting node.
+ INDICATION_PRESENT, //!< Presenting node to gateway.
+ INDICATION_CLEAR_ROUTING, //!< Clear routing table requested.
+ INDICATION_SLEEP, //!< Node goes to sleep.
+ INDICATION_WAKEUP, //!< Node just woke from sleep.
+ INDICATION_FW_UPDATE_START, //!< Start of OTA firmware update process.
+ INDICATION_FW_UPDATE_RX, //!< Received a piece of firmware data.
+
+ INDICATION_ERR_START = 100,
+ INDICATION_ERR_TX, //!< Failed to transmit message.
INDICATION_ERR_TRANSPORT_FAILURE, //!< Transport failure.
- INDICATION_ERR_INIT_TRANSPORT, //!< MySensors transport hardware (radio) init failure.
- INDICATION_ERR_FIND_PARENT, //!< Failed to find parent node.
- INDICATION_ERR_GET_NODEID, //!< Failed to receive node ID.
- INDICATION_ERR_SIGN, //!< Error signing.
- INDICATION_ERR_VERSION, //!< Protocol version mismatch.
- INDICATION_ERR_NET_FULL, //!< Network full. All node ID's are taken.
- INDICATION_ERR_INIT_GWTRANSPORT, //!< Gateway transport hardware init failure.
- INDICATION_ERR_LOCKED, //!< Node is locked.
- INDICATION_ERR_FW_FLASH_INIT, //!< Firmware update flash initialisation failure.
- INDICATION_ERR_FW_TIMEOUT, //!< Firmware update timeout.
- INDICATION_ERR_FW_CHECKSUM, //!< Firmware update checksum mismatch.
- INDICATION_ERR_END
+ INDICATION_ERR_INIT_TRANSPORT, //!< MySensors transport hardware (radio) init failure.
+ INDICATION_ERR_FIND_PARENT, //!< Failed to find parent node.
+ INDICATION_ERR_GET_NODEID, //!< Failed to receive node ID.
+ INDICATION_ERR_CHECK_UPLINK, //!< Failed to check uplink
+ INDICATION_ERR_SIGN, //!< Error signing.
+ INDICATION_ERR_LENGTH, //!< Invalid message length.
+ INDICATION_ERR_VERSION, //!< Protocol version mismatch.
+ INDICATION_ERR_NET_FULL, //!< Network full. All node ID's are taken.
+ INDICATION_ERR_INIT_GWTRANSPORT, //!< Gateway transport hardware init failure.
+ INDICATION_ERR_LOCKED, //!< Node is locked.
+ INDICATION_ERR_FW_FLASH_INIT, //!< Firmware update flash initialisation failure.
+ INDICATION_ERR_FW_TIMEOUT, //!< Firmware update timeout.
+ INDICATION_ERR_FW_CHECKSUM, //!< Firmware update checksum mismatch.
+ INDICATION_ERR_END
} indication_t;
/**
diff --git a/core/MyLeds.cpp b/core/MyLeds.cpp
index 2974c183f..f27f71857 100644
--- a/core/MyLeds.cpp
+++ b/core/MyLeds.cpp
@@ -26,8 +26,7 @@
static uint8_t countRx;
static uint8_t countTx;
static uint8_t countErr;
-static unsigned long prevTime = hwMillis() - LED_PROCESS_INTERVAL_MS; // Substract some, to make sure leds gets updated on first run.
-
+static unsigned long prevTime;
inline void ledsInit()
{
@@ -37,46 +36,82 @@ inline void ledsInit()
countErr = 0;
// Setup led pins
- pinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT);
- pinMode(MY_DEFAULT_TX_LED_PIN, OUTPUT);
- pinMode(MY_DEFAULT_ERR_LED_PIN, OUTPUT);
-
- ledsProcess();
+#if defined(MY_DEFAULT_RX_LED_PIN)
+ hwPinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT);
+#endif
+#if defined(MY_DEFAULT_TX_LED_PIN)
+ hwPinMode(MY_DEFAULT_TX_LED_PIN, OUTPUT);
+#endif
+#if defined(MY_DEFAULT_ERR_LED_PIN)
+ hwPinMode(MY_DEFAULT_ERR_LED_PIN, OUTPUT);
+#endif
+ prevTime = hwMillis() -
+ LED_PROCESS_INTERVAL_MS; // Substract some, to make sure leds gets updated on first run.
+ ledsProcess();
}
-void ledsProcess() {
+void ledsProcess()
+{
// Just return if it is not the time...
- if ((hwMillis() - prevTime) < LED_PROCESS_INTERVAL_MS)
+ if ((hwMillis() - prevTime) < LED_PROCESS_INTERVAL_MS) {
return;
-
- prevTime += LED_PROCESS_INTERVAL_MS;
-
- uint8_t state;
-
- // For an On/Off ratio of 4, the pattern repeated will be [on, on, on, off]
- // until the counter becomes 0.
- state = (countRx & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
- hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, state);
- if (countRx) --countRx;
-
- state = (countTx & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
- hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, state);
- if (countTx) --countTx;
-
- state = (countErr & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
- hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, state);
- if (countErr) --countErr;
+ }
+ prevTime = hwMillis();
+
+ uint8_t state;
+
+ // For an On/Off ratio of 4, the pattern repeated will be [on, on, on, off]
+ // until the counter becomes 0.
+#if defined(MY_DEFAULT_RX_LED_PIN)
+ if (countRx) {
+ --countRx;
+ }
+ state = (countRx & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
+ hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, state);
+#endif
+
+#if defined(MY_DEFAULT_TX_LED_PIN)
+ if (countTx) {
+ --countTx;
+ }
+ state = (countTx & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
+ hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, state);
+#endif
+
+#if defined(MY_DEFAULT_ERR_LED_PIN)
+ if (countErr) {
+ --countErr;
+ }
+ state = (countErr & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
+ hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, state);
+#endif
}
-void ledsBlinkRx(uint8_t cnt) {
- if (!countRx) { countRx = cnt*LED_ON_OFF_RATIO; }
+void ledsBlinkRx(uint8_t cnt)
+{
+ if (!countRx) {
+ countRx = cnt*LED_ON_OFF_RATIO;
+ }
+ ledsProcess();
}
-void ledsBlinkTx(uint8_t cnt) {
- if(!countTx) { countTx = cnt*LED_ON_OFF_RATIO; }
+void ledsBlinkTx(uint8_t cnt)
+{
+ if(!countTx) {
+ countTx = cnt*LED_ON_OFF_RATIO;
+ }
+ ledsProcess();
}
-void ledsBlinkErr(uint8_t cnt) {
- if(!countErr) { countErr = cnt*LED_ON_OFF_RATIO; }
+void ledsBlinkErr(uint8_t cnt)
+{
+ if(!countErr) {
+ countErr = cnt*LED_ON_OFF_RATIO;
+ }
+ ledsProcess();
}
+bool ledsBlinking()
+{
+ return countRx || countTx || countErr;
+}
diff --git a/core/MyLeds.h b/core/MyLeds.h
index 18e138d17..ab78ab2e6 100644
--- a/core/MyLeds.h
+++ b/core/MyLeds.h
@@ -29,26 +29,31 @@
#define LED_OFF 0x1
#endif
-#ifdef MY_LEDS_BLINKING_FEATURE
- #define ledBlinkTx(x,...) ledsBlinkTx(x)
- #define ledBlinkRx(x,...) ledsBlinkRx(x)
- #define ledBlinkErr(x,...) ledsBlinkErr(x)
+#if defined(MY_DEFAULT_TX_LED_PIN) || defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_ERR_LED_PIN)
+#define ledBlinkTx(x,...) ledsBlinkTx(x)
+#define ledBlinkRx(x,...) ledsBlinkRx(x)
+#define ledBlinkErr(x,...) ledsBlinkErr(x)
- /**
- * Blink with LEDs
- * @param cnt how many blink cycles to keep the LED on. Default cycle is 300ms
- */
- void ledsInit();
- void ledsBlinkRx(uint8_t cnt);
- void ledsBlinkTx(uint8_t cnt);
- void ledsBlinkErr(uint8_t cnt);
- void ledsProcess(); // do the actual blinking
+/**
+ * Blink with LEDs
+ * @param cnt how many blink cycles to keep the LED on. Default cycle is 300ms
+ */
+void ledsInit();
+void ledsBlinkRx(uint8_t cnt);
+void ledsBlinkTx(uint8_t cnt);
+void ledsBlinkErr(uint8_t cnt);
+void ledsProcess(); // do the actual blinking
+/**
+ * Test if any LED is currently blinking.
+ * @return true when one or more LEDs are blinking, false otherwise.
+ */
+bool ledsBlinking();
#else
- // Remove led functions if feature is disabled
- #define ledBlinkTx(x,...)
- #define ledBlinkRx(x,...)
- #define ledBlinkErr(x,...)
+// Remove led functions if feature is disabled
+#define ledBlinkTx(x,...)
+#define ledBlinkRx(x,...)
+#define ledBlinkErr(x,...)
#endif
#endif
diff --git a/core/MyMainDefault.cpp b/core/MyMainDefault.cpp
index b1934b94e..0e006fd5a 100644
--- a/core/MyMainDefault.cpp
+++ b/core/MyMainDefault.cpp
@@ -1,19 +1,46 @@
+/**
+ * The MySensors Arduino library handles the wireless radio link and protocol
+ * between your home built sensors/actuators and HA controller of choice.
+ * The sensors forms a self healing radio network with optional repeaters. Each
+ * repeater and gateway builds a routing tables in EEPROM which keeps track of the
+ * network topology allowing messages to be routed to nodes.
+ *
+ * Created by Henrik Ekblad
+ * Copyright (C) 2013-2015 Sensnology AB
+ * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
+ *
+ * Documentation: http://www.mysensors.org
+ * Support Forum: http://forum.mysensors.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
// Initialize library and handle sketch functions like we want to
-int main(void) {
+extern "C" void __libc_init_array(void);
+
+int main(void)
+{
init();
- #if defined(USBCON)
- #if defined(ARDUINO_ARCH_SAMD)
- USBDevice.init();
- #endif
- USBDevice.attach();
- #endif
+#if defined(USBCON)
+#if defined(ARDUINO_ARCH_SAMD)
+ __libc_init_array();
+ USBDevice.init();
+#endif
+ USBDevice.attach();
+#endif
_begin(); // Startup MySensors library
for(;;) {
_process(); // Process incoming data
- if (loop) loop(); // Call sketch loop
- if (serialEventRun) serialEventRun();
+ if (loop) {
+ loop(); // Call sketch loop
+ }
+ if (serialEventRun) {
+ serialEventRun();
+ }
}
return 0;
}
diff --git a/core/MyMainESP8266.cpp b/core/MyMainESP8266.cpp
index ee800a0a7..608a60f02 100644
--- a/core/MyMainESP8266.cpp
+++ b/core/MyMainESP8266.cpp
@@ -1,3 +1,21 @@
+/**
+ * The MySensors Arduino library handles the wireless radio link and protocol
+ * between your home built sensors/actuators and HA controller of choice.
+ * The sensors forms a self healing radio network with optional repeaters. Each
+ * repeater and gateway builds a routing tables in EEPROM which keeps track of the
+ * network topology allowing messages to be routed to nodes.
+ *
+ * Created by Henrik Ekblad
+ * Copyright (C) 2013-2015 Sensnology AB
+ * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
+ *
+ * Documentation: http://www.mysensors.org
+ * Support Forum: http://forum.mysensors.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
//This may be used to change user task stack size:
//#define CONT_STACKSIZE 4096
@@ -20,27 +38,31 @@ extern "C" {
struct rst_info resetInfo;
extern "C" {
- extern const uint32_t __attribute__((section(".ver_number"))) core_version = ARDUINO_ESP8266_GIT_VER;
+ extern const uint32_t __attribute__((section(".ver_number"))) core_version =
+ ARDUINO_ESP8266_GIT_VER;
const char* core_release =
#ifdef ARDUINO_ESP8266_RELEASE
- ARDUINO_ESP8266_RELEASE;
+ ARDUINO_ESP8266_RELEASE;
#else
- NULL;
+ NULL;
#endif
} // extern "C"
-int atexit(void(*func)()) {
+int atexit(void(*func)())
+{
(void)func;
return 0;
}
extern "C" void ets_update_cpu_frequency(int freqmhz);
void initVariant() __attribute__((weak));
-void initVariant() {
+void initVariant()
+{
}
void preloop_update_frequency() __attribute__((weak));
-void preloop_update_frequency() {
+void preloop_update_frequency()
+{
#if defined(F_CPU) && (F_CPU == 160000000L)
REG_SET_BIT(0x3ff00014, BIT(0));
ets_update_cpu_frequency(160);
@@ -55,37 +77,40 @@ static os_event_t g_loop_queue[LOOP_QUEUE_SIZE];
static uint32_t g_micros_at_task_start;
-extern "C" void esp_yield() {
+extern "C" void esp_yield()
+{
if (cont_can_yield(&g_cont)) {
cont_yield(&g_cont);
}
}
-extern "C" void esp_schedule() {
+extern "C" void esp_schedule()
+{
ets_post(LOOP_TASK_PRIORITY, 0, 0);
}
-extern "C" void __yield() {
+extern "C" void __yield()
+{
if (cont_can_yield(&g_cont)) {
esp_schedule();
esp_yield();
- }
- else {
+ } else {
panic();
}
}
extern "C" void yield(void) __attribute__((weak, alias("__yield")));
-extern "C" void optimistic_yield(uint32_t interval_us) {
+extern "C" void optimistic_yield(uint32_t interval_us)
+{
if (cont_can_yield(&g_cont) &&
- (system_get_time() - g_micros_at_task_start) > interval_us)
- {
+ (system_get_time() - g_micros_at_task_start) > interval_us) {
yield();
}
}
-static void loop_wrapper() {
+static void loop_wrapper()
+{
static bool setup_done = false;
preloop_update_frequency();
if (!setup_done) {
@@ -98,7 +123,8 @@ static void loop_wrapper() {
esp_schedule();
}
-static void loop_task(os_event_t *events) {
+static void loop_task(os_event_t *events)
+{
(void)events;
g_micros_at_task_start = system_get_time();
cont_run(&g_cont, &loop_wrapper);
@@ -107,10 +133,12 @@ static void loop_task(os_event_t *events) {
}
}
-static void do_global_ctors(void) {
+static void do_global_ctors(void)
+{
void(**p)(void) = &__init_array_end;
- while (p != &__init_array_start)
+ while (p != &__init_array_start) {
(*--p)();
+ }
}
extern "C" void __gdb_init() {}
@@ -119,7 +147,8 @@ extern "C" void gdb_init(void) __attribute__((weak, alias("__gdb_init")));
extern "C" void __gdb_do_break() {}
extern "C" void gdb_do_break(void) __attribute__((weak, alias("__gdb_do_break")));
-void init_done() {
+void init_done()
+{
system_set_os_print(1);
gdb_init();
do_global_ctors();
@@ -127,7 +156,8 @@ void init_done() {
}
-extern "C" void user_init(void) {
+extern "C" void user_init(void)
+{
struct rst_info *rtc_info_ptr = system_get_rst_info();
memcpy((void *)&resetInfo, (void *)rtc_info_ptr, sizeof(resetInfo));
@@ -140,8 +170,8 @@ extern "C" void user_init(void) {
cont_init(&g_cont);
ets_task(loop_task,
- LOOP_TASK_PRIORITY, g_loop_queue,
- LOOP_QUEUE_SIZE);
+ LOOP_TASK_PRIORITY, g_loop_queue,
+ LOOP_QUEUE_SIZE);
system_init_done_cb(&init_done);
-}
\ No newline at end of file
+}
diff --git a/core/MyMainLinux.cpp b/core/MyMainLinux.cpp
new file mode 100644
index 000000000..5e875ecea
--- /dev/null
+++ b/core/MyMainLinux.cpp
@@ -0,0 +1,450 @@
+/**
+ * The MySensors Arduino library handles the wireless radio link and protocol
+ * between your home built sensors/actuators and HA controller of choice.
+ * The sensors forms a self healing radio network with optional repeaters. Each
+ * repeater and gateway builds a routing tables in EEPROM which keeps track of the
+ * network topology allowing messages to be routed to nodes.
+ *
+ * Created by Henrik Ekblad
+ * Copyright (C) 2013-2015 Sensnology AB
+ * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
+ *
+ * Documentation: http://www.mysensors.org
+ * Support Forum: http://forum.mysensors.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+// Initialize library and handle sketch functions like we want to
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "log.h"
+#include "MySensorsCore.h"
+
+void handle_sigint(int sig)
+{
+ if (sig == SIGINT) {
+ logNotice("Received SIGINT\n\n");
+ } else if (sig == SIGTERM) {
+ logNotice("Received SIGTERM\n\n");
+ } else {
+ return;
+ }
+
+#ifdef MY_RF24_IRQ_PIN
+ detachInterrupt(MY_RF24_IRQ_PIN);
+#endif
+
+#if defined(MY_GATEWAY_SERIAL)
+ MY_SERIALDEVICE.end();
+#endif
+
+ closelog();
+
+ exit(0);
+}
+
+static int daemonize(void)
+{
+ pid_t pid, sid;
+
+ /* Fork off the parent process */
+ pid = fork();
+ if (pid < 0) {
+ logError("fork: %s\n", strerror(errno));
+ return -1;
+ }
+ /* If we got a good PID, then we can exit the parent process. */
+ if (pid > 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ /* At this point we are executing as the child process */
+
+ /* Change the file mode mask */
+ umask(0);
+
+ /* Create a new SID for the child process */
+ sid = setsid();
+ if (sid < 0) {
+ logError("setsid: %s\n", strerror(errno));
+ return -1;
+ }
+
+ /* Change the current working directory. This prevents the current
+ directory from being locked; hence not being able to remove it. */
+ if ((chdir("/")) < 0) {
+ logError("chdir(\"/\"): %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (freopen( "/dev/null", "r", stdin) == NULL) {
+ logError("freopen: %s\n", strerror(errno));
+ }
+ if (freopen( "/dev/null", "r", stdout) == NULL) {
+ logError("freopen: %s\n", strerror(errno));
+ }
+ if (freopen( "/dev/null", "r", stderr) == NULL) {
+ logError("freopen: %s\n", strerror(errno));
+ }
+
+ return 0;
+}
+
+void print_usage()
+{
+ printf("Usage: mysgw [options]\n\n" \
+ "Options:\n" \
+ " -h, --help Display a short summary of all program options.\n" \
+ " -d, --debug Enable debug.\n" \
+ " -b, --background Run as a background process.\n"
+ " --gen-soft-hmac-key Generate and print a soft hmac key.\n"
+ " --gen-soft-serial-key Generate and print a soft serial key.\n"
+ " --gen-aes-key Generate and print an aes encryption key.\n"
+ " --print-soft-hmac-key Print the soft hmac key from the config file.\n"
+ " --print-soft-serial-key Print the soft serial key from the config file.\n"
+ " --print-aes-key Print the aes encryption key from the config file.\n"
+ " --set-soft-hmac-key Write a soft hmac key to the config file.\n"
+ " --set-soft-serial-key Write a soft serial key to the config file.\n"
+ " --set-aes-key Write an aes encryption key to the config file.\n");
+}
+
+void print_soft_sign_hmac_key(uint8_t *key_ptr = NULL)
+{
+ uint8_t key[32];
+
+ if (key_ptr == NULL) {
+ hwReadConfigBlock(&key, reinterpret_castEEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
+ key_ptr = key;
+ }
+
+ printf("SOFT_HMAC_KEY | ");
+ for (int i = 0; i < 32; i++) {
+ printf("%02X", key_ptr[i]);
+ }
+ printf("\n\n");
+
+ printf("The next line is intended to be used in SecurityPersonalizer.ino:\n");
+ printf("#define MY_SOFT_HMAC_KEY ");
+ for (int i=0; i<32; i++) {
+ printf("%#02X", key_ptr[i]);
+ if (i < 31) {
+ printf(",");
+ }
+ }
+ printf("\n\n");
+}
+
+void generate_soft_sign_hmac_key()
+{
+ uint8_t key[32];
+
+ for (int i = 0; i < 32; i++) {
+ key[i] = random(256) ^ micros();
+ unsigned long enter = hwMillis();
+ while (hwMillis() - enter < (unsigned long)2);
+ }
+
+ print_soft_sign_hmac_key(key);
+
+ printf("To use this key, run mysgw with:\n"
+ " --set-soft-hmac-key=");
+ for (int i = 0; i < 32; i++) {
+ printf("%02X", key[i]);
+ }
+ printf("\n");
+}
+
+void set_soft_sign_hmac_key(char *key_str)
+{
+ uint8_t key[32];
+ int n;
+
+ if (strlen(key_str) != 64) {
+ printf("invalid key!\n");
+ } else {
+ for (int i = 0; i < 64; ++i) {
+ char c = key_str[i];
+ if (c <= '9') {
+ n = c - '0';
+ } else if (c >= 'a') {
+ n = c - 'a' + 10;
+ } else {
+ n = c - 'A' + 10;
+ }
+
+ if ((i & 0x1) == 0) {
+ key[i/2] = n * 16;
+ } else {
+ key[i/2] += n;
+ }
+ }
+ hwWriteConfigBlock(&key, reinterpret_castEEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
+ print_soft_sign_hmac_key();
+ }
+}
+
+void print_soft_sign_serial_key(uint8_t *key_ptr = NULL)
+{
+ uint8_t key[9];
+
+ if (key_ptr == NULL) {
+ hwReadConfigBlock(&key, reinterpret_castEEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
+ key_ptr = key;
+ }
+
+ printf("SOFT_SERIAL | ");
+ for (int i = 0; i < 9; i++) {
+ printf("%02X", key_ptr[i]);
+ }
+ printf("\n\n");
+
+ printf("The next line is intended to be used in SecurityPersonalizer.ino:\n");
+ printf("#define MY_SOFT_SERIAL ");
+ for (int i=0; i<9; i++) {
+ printf("%#02X", key_ptr[i]);
+ if (i < 8) {
+ printf(",");
+ }
+ }
+ printf("\n\n");
+}
+
+void generate_soft_sign_serial_key()
+{
+ uint8_t key[9];
+
+ for (int i = 0; i < 9; i++) {
+ key[i] = random(256) ^ micros();
+ unsigned long enter = hwMillis();
+ while (hwMillis() - enter < (unsigned long)2);
+ }
+
+ print_soft_sign_serial_key(key);
+
+ printf("To use this key, run mysgw with:\n"
+ " --set-soft-serial-key=");
+ for (int i = 0; i < 9; i++) {
+ printf("%02X", key[i]);
+ }
+ printf("\n");
+}
+
+void set_soft_sign_serial_key(char *key_str)
+{
+ uint8_t key[9];
+ int n;
+
+ if (strlen(key_str) != 18) {
+ printf("invalid key!\n");
+ } else {
+ for (int i = 0; i < 18; ++i) {
+ char c = key_str[i];
+ if (c <= '9') {
+ n = c - '0';
+ } else if (c >= 'a') {
+ n = c - 'a' + 10;
+ } else {
+ n = c - 'A' + 10;
+ }
+
+ if ((i & 0x1) == 0) {
+ key[i/2] = n * 16;
+ } else {
+ key[i/2] += n;
+ }
+ }
+ hwWriteConfigBlock(&key, reinterpret_castEEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
+ print_soft_sign_serial_key();
+ }
+}
+
+void print_aes_key(uint8_t *key_ptr = NULL)
+{
+ uint8_t key[16];
+
+ if (key_ptr == NULL) {
+ hwReadConfigBlock(&key, reinterpret_castEEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
+ key_ptr = key;
+ }
+
+ printf("AES_KEY | ");
+ for (int i = 0; i < 16; i++) {
+ printf("%02X", key_ptr[i]);
+ }
+ printf("\n\n");
+
+ printf("The next line is intended to be used in SecurityPersonalizer.ino:\n");
+ printf("#define MY_AES_KEY ");
+ for (int i=0; i<16; i++) {
+ printf("%#02X", key_ptr[i]);
+ if (i < 15) {
+ printf(",");
+ }
+ }
+ printf("\n\n");
+}
+
+void generate_aes_key()
+{
+ uint8_t key[16];
+
+ for (int i = 0; i < 16; i++) {
+ key[i] = random(256) ^ micros();
+ unsigned long enter = hwMillis();
+ while (hwMillis() - enter < (unsigned long)2);
+ }
+
+ print_aes_key(key);
+
+ printf("To use this key, run mysgw with:\n"
+ " --set-aes-key=");
+ for (int i = 0; i < 16; i++) {
+ printf("%02X", key[i]);
+ }
+ printf("\n");
+}
+
+void set_aes_key(char *key_str)
+{
+ uint8_t key[16];
+ int n;
+
+ if (strlen(key_str) != 32) {
+ printf("invalid key!\n");
+ } else {
+ for (int i = 0; i < 32; ++i) {
+ char c = key_str[i];
+ if (c <= '9') {
+ n = c - '0';
+ } else if (c >= 'a') {
+ n = c - 'a' + 10;
+ } else {
+ n = c - 'A' + 10;
+ }
+
+ if ((i & 0x1) == 0) {
+ key[i/2] = n * 16;
+ } else {
+ key[i/2] += n;
+ }
+ }
+ hwWriteConfigBlock(&key, reinterpret_castEEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
+ print_aes_key();
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int opt, log_opts, debug = 0, foreground = 1;
+ char *key = NULL;
+
+ /* register the signal handler */
+ signal(SIGINT, handle_sigint);
+ signal(SIGTERM, handle_sigint);
+
+ hwRandomNumberInit();
+
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"debug", no_argument, 0, 'd'},
+ {"background", no_argument, 0, 'b'},
+ {"gen-soft-hmac-key", no_argument, 0, 'A'},
+ {"gen-soft-serial-key", no_argument, 0, 'B'},
+ {"gen-aes-key", no_argument, 0, 'C'},
+ {"print-soft-hmac-key", no_argument, 0, 'D'},
+ {"print-soft-serial-key", no_argument, 0, 'E'},
+ {"print-aes-key", no_argument, 0, 'F'},
+ {"set-soft-hmac-key", required_argument, 0, 'G'},
+ {"set-soft-serial-key", required_argument, 0, 'H'},
+ {"set-aes-key", required_argument, 0, 'I'},
+ {0, 0, 0, 0}
+ };
+
+ int long_index = 0;
+ while ((opt = getopt_long(argc, argv,"hdbABCDEFGHI", long_options, &long_index )) != -1) {
+ switch (opt) {
+ case 'h':
+ print_usage();
+ exit(0);
+ case 'd':
+ debug = 1;
+ break;
+ case 'b':
+ foreground = 0;
+ break;
+ case 'A':
+ generate_soft_sign_hmac_key();
+ exit(0);
+ case 'B':
+ generate_soft_sign_serial_key();
+ exit(0);
+ case 'C':
+ generate_aes_key();
+ exit(0);
+ case 'D':
+ print_soft_sign_hmac_key();
+ exit(0);
+ case 'E':
+ print_soft_sign_serial_key();
+ exit(0);
+ case 'F':
+ print_aes_key();
+ exit(0);
+ case 'G':
+ key = strdup(optarg);
+ set_soft_sign_hmac_key(key);
+ exit(0);
+ case 'H':
+ key = strdup(optarg);
+ set_soft_sign_serial_key(key);
+ exit(0);
+ case 'I':
+ key = strdup(optarg);
+ set_aes_key(key);
+ exit(0);
+ default:
+ print_usage();
+ exit(0);
+ }
+ }
+
+ log_opts = LOG_CONS;
+ if (foreground && isatty(STDIN_FILENO)) {
+ // Also print syslog to stderror
+ log_opts |= LOG_PERROR;
+ }
+ if (!debug) {
+ // Ignore debug type messages
+ setlogmask(LOG_UPTO (LOG_INFO));
+ }
+ logOpen(log_opts, LOG_USER);
+
+ if (!foreground && !debug) {
+ if (daemonize() != 0) {
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ logInfo("Starting gateway...\n");
+ logInfo("Protocol version - %s\n", MYSENSORS_LIBRARY_VERSION);
+
+ _begin(); // Startup MySensors library
+
+ for (;;) {
+ _process(); // Process incoming data
+ if (loop) {
+ loop(); // Call sketch loop
+ }
+ }
+ return 0;
+}
diff --git a/core/MyMessage.cpp b/core/MyMessage.cpp
index 117dfe34b..c5baf5177 100644
--- a/core/MyMessage.cpp
+++ b/core/MyMessage.cpp
@@ -21,32 +21,53 @@
#include "MyMessage.h"
#include
#include
+#include
-
-MyMessage::MyMessage() {
- destination = 0; // Gateway is default destination
+MyMessage::MyMessage()
+{
+ clear();
}
-MyMessage::MyMessage(uint8_t _sensor, uint8_t _type) {
- destination = 0; // Gateway is default destination
+MyMessage::MyMessage(uint8_t _sensor, uint8_t _type)
+{
+ clear();
sensor = _sensor;
- type = _type;
+ type = _type;
+}
+
+void MyMessage::clear()
+{
+ last = 0u;
+ sender = 0u;
+ destination = 0u; // Gateway is default destination
+ version_length = 0u;
+ command_ack_payload = 0u;
+ type = 0u;
+ sensor = 0u;
+ (void)memset(data, 0u, sizeof(data));
+
+ // set message protocol version
+ miSetVersion(PROTOCOL_VERSION);
}
-bool MyMessage::isAck() const {
+bool MyMessage::isAck() const
+{
return miGetAck();
}
-uint8_t MyMessage::getCommand() const {
+uint8_t MyMessage::getCommand() const
+{
return miGetCommand();
}
/* Getters for payload converted to desired form */
-void* MyMessage::getCustom() const {
+void* MyMessage::getCustom() const
+{
return (void *)data;
}
-const char* MyMessage::getString() const {
+const char* MyMessage::getString() const
+{
uint8_t payloadType = miGetPayloadType();
if (payloadType == P_STRING) {
return data;
@@ -56,17 +77,19 @@ const char* MyMessage::getString() const {
}
// handles single character hex (0 - 15)
-char MyMessage::i2h(uint8_t i) const {
+char MyMessage::i2h(uint8_t i) const
+{
uint8_t k = i & 0x0F;
- if (k <= 9)
+ if (k <= 9) {
return '0' + k;
- else
+ } else {
return 'A' + k - 10;
+ }
}
-char* MyMessage::getCustomString(char *buffer) const {
- for (uint8_t i = 0; i < miGetLength(); i++)
- {
+char* MyMessage::getCustomString(char *buffer) const
+{
+ for (uint8_t i = 0; i < miGetLength(); i++) {
buffer[i * 2] = i2h(data[i] >> 4);
buffer[(i * 2) + 1] = i2h(data[i]);
}
@@ -74,7 +97,8 @@ char* MyMessage::getCustomString(char *buffer) const {
return buffer;
}
-char* MyMessage::getStream(char *buffer) const {
+char* MyMessage::getStream(char *buffer) const
+{
uint8_t cmd = miGetCommand();
if ((cmd == C_STREAM) && (buffer != NULL)) {
return getCustomString(buffer);
@@ -83,7 +107,8 @@ char* MyMessage::getStream(char *buffer) const {
}
}
-char* MyMessage::getString(char *buffer) const {
+char* MyMessage::getString(char *buffer) const
+{
uint8_t payloadType = miGetPayloadType();
if (buffer != NULL) {
if (payloadType == P_STRING) {
@@ -100,7 +125,7 @@ char* MyMessage::getString(char *buffer) const {
} else if (payloadType == P_ULONG32) {
ultoa(ulValue, buffer, 10);
} else if (payloadType == P_FLOAT32) {
- dtostrf(fValue,2,min(fPrecision, 8),buffer);
+ dtostrf(fValue,2,min(fPrecision, (uint8_t)8),buffer);
} else if (payloadType == P_CUSTOM) {
return getCustomString(buffer);
}
@@ -110,11 +135,13 @@ char* MyMessage::getString(char *buffer) const {
}
}
-bool MyMessage::getBool() const {
+bool MyMessage::getBool() const
+{
return getByte();
}
-uint8_t MyMessage::getByte() const {
+uint8_t MyMessage::getByte() const
+{
if (miGetPayloadType() == P_BYTE) {
return data[0];
} else if (miGetPayloadType() == P_STRING) {
@@ -125,7 +152,8 @@ uint8_t MyMessage::getByte() const {
}
-float MyMessage::getFloat() const {
+float MyMessage::getFloat() const
+{
if (miGetPayloadType() == P_FLOAT32) {
return fValue;
} else if (miGetPayloadType() == P_STRING) {
@@ -135,7 +163,8 @@ float MyMessage::getFloat() const {
}
}
-int32_t MyMessage::getLong() const {
+int32_t MyMessage::getLong() const
+{
if (miGetPayloadType() == P_LONG32) {
return lValue;
} else if (miGetPayloadType() == P_STRING) {
@@ -145,7 +174,8 @@ int32_t MyMessage::getLong() const {
}
}
-uint32_t MyMessage::getULong() const {
+uint32_t MyMessage::getULong() const
+{
if (miGetPayloadType() == P_ULONG32) {
return ulValue;
} else if (miGetPayloadType() == P_STRING) {
@@ -155,8 +185,9 @@ uint32_t MyMessage::getULong() const {
}
}
-int16_t MyMessage::getInt() const {
- if (miGetPayloadType() == P_INT16) {
+int16_t MyMessage::getInt() const
+{
+ if (miGetPayloadType() == P_INT16) {
return iValue;
} else if (miGetPayloadType() == P_STRING) {
return atoi(data);
@@ -165,8 +196,9 @@ int16_t MyMessage::getInt() const {
}
}
-uint16_t MyMessage::getUInt() const {
- if (miGetPayloadType() == P_UINT16) {
+uint16_t MyMessage::getUInt() const
+{
+ if (miGetPayloadType() == P_UINT16) {
return uiValue;
} else if (miGetPayloadType() == P_STRING) {
return atoi(data);
@@ -176,34 +208,40 @@ uint16_t MyMessage::getUInt() const {
}
-MyMessage& MyMessage::setType(uint8_t _type) {
+MyMessage& MyMessage::setType(uint8_t _type)
+{
type = _type;
return *this;
}
-MyMessage& MyMessage::setSensor(uint8_t _sensor) {
+MyMessage& MyMessage::setSensor(uint8_t _sensor)
+{
sensor = _sensor;
return *this;
}
-MyMessage& MyMessage::setDestination(uint8_t _destination) {
+MyMessage& MyMessage::setDestination(uint8_t _destination)
+{
destination = _destination;
return *this;
}
// Set payload
-MyMessage& MyMessage::set(void* value, uint8_t length) {
+MyMessage& MyMessage::set(void* value, uint8_t length)
+{
+ uint8_t payloadLength = value == NULL ? 0 : min(length, (uint8_t)MAX_PAYLOAD);
+ miSetLength(payloadLength);
miSetPayloadType(P_CUSTOM);
- miSetLength(length);
- memcpy(data, value, min(length, MAX_PAYLOAD));
+ memcpy(data, value, payloadLength);
return *this;
}
-MyMessage& MyMessage::set(const char* value) {
- uint8_t length = value == NULL ? 0 : min(strlen(value), MAX_PAYLOAD);
+MyMessage& MyMessage::set(const char* value)
+{
+ uint8_t length = value == NULL ? 0 : min(strlen(value), (size_t)MAX_PAYLOAD);
miSetLength(length);
miSetPayloadType(P_STRING);
- if (length) {
+ if (length) {
strncpy(data, value, length);
}
// null terminate string
@@ -211,21 +249,24 @@ MyMessage& MyMessage::set(const char* value) {
return *this;
}
-MyMessage& MyMessage::set(bool value) {
+MyMessage& MyMessage::set(bool value)
+{
miSetLength(1);
miSetPayloadType(P_BYTE);
data[0] = value;
return *this;
}
-MyMessage& MyMessage::set(uint8_t value) {
+MyMessage& MyMessage::set(uint8_t value)
+{
miSetLength(1);
miSetPayloadType(P_BYTE);
data[0] = value;
return *this;
}
-MyMessage& MyMessage::set(float value, uint8_t decimals) {
+MyMessage& MyMessage::set(float value, uint8_t decimals)
+{
miSetLength(5); // 32 bit float + persi
miSetPayloadType(P_FLOAT32);
fValue=value;
@@ -233,28 +274,32 @@ MyMessage& MyMessage::set(float value, uint8_t decimals) {
return *this;
}
-MyMessage& MyMessage::set(uint32_t value) {
+MyMessage& MyMessage::set(uint32_t value)
+{
miSetPayloadType(P_ULONG32);
miSetLength(4);
ulValue = value;
return *this;
}
-MyMessage& MyMessage::set(int32_t value) {
+MyMessage& MyMessage::set(int32_t value)
+{
miSetPayloadType(P_LONG32);
miSetLength(4);
lValue = value;
return *this;
}
-MyMessage& MyMessage::set(uint16_t value) {
+MyMessage& MyMessage::set(uint16_t value)
+{
miSetPayloadType(P_UINT16);
miSetLength(2);
uiValue = value;
return *this;
}
-MyMessage& MyMessage::set(int16_t value) {
+MyMessage& MyMessage::set(int16_t value)
+{
miSetPayloadType(P_INT16);
miSetLength(2);
iValue = value;
diff --git a/core/MyMessage.h b/core/MyMessage.h
index 2d37f78d6..822dc19df 100644
--- a/core/MyMessage.h
+++ b/core/MyMessage.h
@@ -32,13 +32,12 @@
#ifdef __cplusplus
#include
-#include
#include
#endif
-#define PROTOCOL_VERSION 2 //!< The version of the protocol
-#define MAX_MESSAGE_LENGTH 32 //!< The maximum size of a message (including header)
-#define HEADER_SIZE 7 //!< The size of the header
+#define PROTOCOL_VERSION (2u) //!< The version of the protocol
+#define MAX_MESSAGE_LENGTH (32u) //!< The maximum size of a message (including header)
+#define HEADER_SIZE (7u) //!< The size of the header
#define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE) //!< The maximum size of a payload depends on #MAX_MESSAGE_LENGTH and #HEADER_SIZE
/// @brief The command field (message-type) defines the overall properties of a message
@@ -53,11 +52,11 @@ typedef enum {
/// @brief Type of sensor (used when presenting sensors)
typedef enum {
S_DOOR = 0, //!< Door sensor, V_TRIPPED, V_ARMED
- S_MOTION = 1, //!< Motion sensor, V_TRIPPED, V_ARMED
+ S_MOTION = 1, //!< Motion sensor, V_TRIPPED, V_ARMED
S_SMOKE = 2, //!< Smoke sensor, V_TRIPPED, V_ARMED
S_BINARY = 3, //!< Binary light or relay, V_STATUS, V_WATT
S_LIGHT = 3, //!< \deprecated Same as S_BINARY, **** DEPRECATED, DO NOT USE ****
- S_DIMMER = 4, //!< Dimmable light or fan device, V_STATUS (on/off), V_PERCENTAGE (dimmer level 0-100), V_WATT
+ S_DIMMER = 4, //!< Dimmable light or fan device, V_STATUS (on/off), V_PERCENTAGE (dimmer level 0-100), V_WATT
S_COVER = 5, //!< Blinds or window cover, V_UP, V_DOWN, V_STOP, V_PERCENTAGE (open/close to a percentage)
S_TEMP = 6, //!< Temperature sensor, V_TEMP
S_HUM = 7, //!< Humidity sensor, V_HUM
@@ -71,38 +70,38 @@ typedef enum {
S_DISTANCE = 15, //!< Distance sensor, V_DISTANCE
S_LIGHT_LEVEL = 16, //!< Light level sensor, V_LIGHT_LEVEL (uncalibrated in percentage), V_LEVEL (light level in lux)
S_ARDUINO_NODE = 17, //!< Used (internally) for presenting a non-repeating Arduino node
- S_ARDUINO_REPEATER_NODE = 18, //!< Used (internally) for presenting a repeating Arduino node
+ S_ARDUINO_REPEATER_NODE = 18, //!< Used (internally) for presenting a repeating Arduino node
S_LOCK = 19, //!< Lock device, V_LOCK_STATUS
S_IR = 20, //!< IR device, V_IR_SEND, V_IR_RECEIVE
S_WATER = 21, //!< Water meter, V_FLOW, V_VOLUME
S_AIR_QUALITY = 22, //!< Air quality sensor, V_LEVEL
- S_CUSTOM = 23, //!< Custom sensor
+ S_CUSTOM = 23, //!< Custom sensor
S_DUST = 24, //!< Dust sensor, V_LEVEL
- S_SCENE_CONTROLLER = 25, //!< Scene controller device, V_SCENE_ON, V_SCENE_OFF.
- S_RGB_LIGHT = 26, //!< RGB light. Send color component data using V_RGB. Also supports V_WATT
+ S_SCENE_CONTROLLER = 25, //!< Scene controller device, V_SCENE_ON, V_SCENE_OFF.
+ S_RGB_LIGHT = 26, //!< RGB light. Send color component data using V_RGB. Also supports V_WATT
S_RGBW_LIGHT = 27, //!< RGB light with an additional White component. Send data using V_RGBW. Also supports V_WATT
S_COLOR_SENSOR = 28, //!< Color sensor, send color information using V_RGB
S_HVAC = 29, //!< Thermostat/HVAC device. V_HVAC_SETPOINT_HEAT, V_HVAC_SETPOINT_COLD, V_HVAC_FLOW_STATE, V_HVAC_FLOW_MODE, V_TEMP
- S_MULTIMETER = 30, //!< Multimeter device, V_VOLTAGE, V_CURRENT, V_IMPEDANCE
+ S_MULTIMETER = 30, //!< Multimeter device, V_VOLTAGE, V_CURRENT, V_IMPEDANCE
S_SPRINKLER = 31, //!< Sprinkler, V_STATUS (turn on/off), V_TRIPPED (if fire detecting device)
S_WATER_LEAK = 32, //!< Water leak sensor, V_TRIPPED, V_ARMED
S_SOUND = 33, //!< Sound sensor, V_TRIPPED, V_ARMED, V_LEVEL (sound level in dB)
S_VIBRATION = 34, //!< Vibration sensor, V_TRIPPED, V_ARMED, V_LEVEL (vibration in Hz)
- S_MOISTURE = 35, //!< Moisture sensor, V_TRIPPED, V_ARMED, V_LEVEL (water content or moisture in percentage?)
+ S_MOISTURE = 35, //!< Moisture sensor, V_TRIPPED, V_ARMED, V_LEVEL (water content or moisture in percentage?)
S_INFO = 36, //!< LCD text device / Simple information device on controller, V_TEXT
S_GAS = 37, //!< Gas meter, V_FLOW, V_VOLUME
S_GPS = 38, //!< GPS Sensor, V_POSITION
- S_WATER_QUALITY = 39 //!< V_TEMP, V_PH, V_ORP, V_EC, V_STATUS
+ S_WATER_QUALITY = 39 //!< V_TEMP, V_PH, V_ORP, V_EC, V_STATUS
} mysensor_sensor;
/// @brief Type of sensor data (for set/req/ack messages)
typedef enum {
V_TEMP = 0, //!< S_TEMP. Temperature S_TEMP, S_HEATER, S_HVAC
V_HUM = 1, //!< S_HUM. Humidity
- V_STATUS = 2, //!< S_BINARY, S_DIMMER, S_SPRINKLER, S_HVAC, S_HEATER. Used for setting/reporting binary (on/off) status. 1=on, 0=off
+ V_STATUS = 2, //!< S_BINARY, S_DIMMER, S_SPRINKLER, S_HVAC, S_HEATER. Used for setting/reporting binary (on/off) status. 1=on, 0=off
V_LIGHT = 2, //!< \deprecated Same as V_STATUS, **** DEPRECATED, DO NOT USE ****
- V_PERCENTAGE = 3, //!< S_DIMMER. Used for sending a percentage value 0-100 (%).
- V_DIMMER = 3, //!< \deprecated Same as V_PERCENTAGE, **** DEPRECATED, DO NOT USE ****
+ V_PERCENTAGE = 3, //!< S_DIMMER. Used for sending a percentage value 0-100 (%).
+ V_DIMMER = 3, //!< \deprecated Same as V_PERCENTAGE, **** DEPRECATED, DO NOT USE ****
V_PRESSURE = 4, //!< S_BARO. Atmospheric Pressure
V_FORECAST = 5, //!< S_BARO. Whether forecast. string of "stable", "sunny", "cloudy", "unstable", "thunderstorm" or "unknown"
V_RAIN = 6, //!< S_RAIN. Amount of rain
@@ -120,12 +119,12 @@ typedef enum {
V_KWH = 18, //!< S_POWER. Accumulated number of KWH for a power meter
V_SCENE_ON = 19, //!< S_SCENE_CONTROLLER. Turn on a scene
V_SCENE_OFF = 20, //!< S_SCENE_CONTROLLER. Turn of a scene
- V_HVAC_FLOW_STATE = 21, //!< S_HEATER, S_HVAC. HVAC flow state ("Off", "HeatOn", "CoolOn", or "AutoChangeOver")
+ V_HVAC_FLOW_STATE = 21, //!< S_HEATER, S_HVAC. HVAC flow state ("Off", "HeatOn", "CoolOn", or "AutoChangeOver")
V_HEATER = 21, //!< \deprecated Same as V_HVAC_FLOW_STATE, **** DEPRECATED, DO NOT USE ****
- V_HVAC_SPEED = 22, //!< S_HVAC, S_HEATER. HVAC/Heater fan speed ("Min", "Normal", "Max", "Auto")
+ V_HVAC_SPEED = 22, //!< S_HVAC, S_HEATER. HVAC/Heater fan speed ("Min", "Normal", "Max", "Auto")
V_LIGHT_LEVEL = 23, //!< S_LIGHT_LEVEL. Uncalibrated light level. 0-100%. Use V_LEVEL for light level in lux
- V_VAR1 = 24, //!< VAR1
- V_VAR2 = 25, //!< VAR2
+ V_VAR1 = 24, //!< VAR1
+ V_VAR2 = 25, //!< VAR2
V_VAR3 = 26, //!< VAR3
V_VAR4 = 27, //!< VAR4
V_VAR5 = 28, //!< VAR5
@@ -138,11 +137,11 @@ typedef enum {
V_VOLUME = 35, //!< S_WATER. Water volume
V_LOCK_STATUS = 36, //!< S_LOCK. Set or get lock status. 1=Locked, 0=Unlocked
V_LEVEL = 37, //!< S_DUST, S_AIR_QUALITY, S_SOUND (dB), S_VIBRATION (hz), S_LIGHT_LEVEL (lux)
- V_VOLTAGE = 38, //!< S_MULTIMETER
+ V_VOLTAGE = 38, //!< S_MULTIMETER
V_CURRENT = 39, //!< S_MULTIMETER
V_RGB = 40, //!< S_RGB_LIGHT, S_COLOR_SENSOR. Sent as ASCII hex: RRGGBB (RR=red, GG=green, BB=blue component)
V_RGBW = 41, //!< S_RGBW_LIGHT. Sent as ASCII hex: RRGGBBWW (WW=white component)
- V_ID = 42, //!< Used for sending in sensors hardware ids (i.e. OneWire DS1820b).
+ V_ID = 42, //!< Used for sending in sensors hardware ids (i.e. OneWire DS1820b).
V_UNIT_PREFIX = 43, //!< Allows sensors to send in a string representing the unit prefix to be displayed in GUI, not parsed by controller! E.g. cm, m, km, inch.
V_HVAC_SETPOINT_COOL = 44, //!< S_HVAC. HVAC cool setpoint (Integer between 0-100)
V_HVAC_SETPOINT_HEAT = 45, //!< S_HEATER, S_HVAC. HVAC/Heater setpoint (Integer between 0-100)
@@ -152,24 +151,24 @@ typedef enum {
V_POSITION = 49, //!< GPS position and altitude. Payload: latitude;longitude;altitude(m). E.g. "55.722526;13.017972;18"
V_IR_RECORD = 50, //!< Record IR codes S_IR for playback
V_PH = 51, //!< S_WATER_QUALITY, water PH
- V_ORP = 52, //!< S_WATER_QUALITY, water ORP : redox potential in mV
- V_EC = 53, //!< S_WATER_QUALITY, water electric conductivity μS/cm (microSiemens/cm)
- V_VAR = 54, //!< S_POWER, Reactive power: volt-ampere reactive (var)
- V_VA = 55, //!< S_POWER, Apparent power: volt-ampere (VA)
- V_POWER_FACTOR = 56, //!< S_POWER, Ratio of real power to apparent power: floating point value in the range [-1,..,1]
+ V_ORP = 52, //!< S_WATER_QUALITY, water ORP : redox potential in mV
+ V_EC = 53, //!< S_WATER_QUALITY, water electric conductivity μS/cm (microSiemens/cm)
+ V_VAR = 54, //!< S_POWER, Reactive power: volt-ampere reactive (var)
+ V_VA = 55, //!< S_POWER, Apparent power: volt-ampere (VA)
+ V_POWER_FACTOR = 56, //!< S_POWER, Ratio of real power to apparent power: floating point value in the range [-1,..,1]
} mysensor_data;
/// @brief Type of internal messages (for internal messages)
typedef enum {
I_BATTERY_LEVEL = 0, //!< Battery level
- I_TIME = 1, //!< Time
+ I_TIME = 1, //!< Time (request/response)
I_VERSION = 2, //!< Version
I_ID_REQUEST = 3, //!< ID request
I_ID_RESPONSE = 4, //!< ID response
I_INCLUSION_MODE = 5, //!< Inclusion mode
- I_CONFIG = 6, //!< Config
- I_FIND_PARENT = 7, //!< Find parent
+ I_CONFIG = 6, //!< Config (request/response)
+ I_FIND_PARENT_REQUEST = 7, //!< Find parent
I_FIND_PARENT_RESPONSE = 8, //!< Find parent response
I_LOG_MESSAGE = 9, //!< Log message
I_CHILDREN = 10, //!< Children
@@ -180,9 +179,9 @@ typedef enum {
I_SIGNING_PRESENTATION = 15, //!< Provides signing related preferences (first byte is preference version)
I_NONCE_REQUEST = 16, //!< Request for a nonce
I_NONCE_RESPONSE = 17, //!< Payload is nonce data
- I_HEARTBEAT = 18, //!< Heartbeat request
+ I_HEARTBEAT_REQUEST = 18, //!< Heartbeat request
I_PRESENTATION = 19, //!< Presentation message
- I_DISCOVER = 20, //!< Discover request
+ I_DISCOVER_REQUEST = 20, //!< Discover request
I_DISCOVER_RESPONSE = 21, //!< Discover response
I_HEARTBEAT_RESPONSE = 22, //!< Heartbeat response
I_LOCKED = 23, //!< Node is locked (reason in string-payload)
@@ -229,26 +228,26 @@ typedef enum {
#define BF_SET(y, x, start, len) ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) ) //!< Insert a new bitfield value 'x' into 'y'
// Getters/setters for special bit fields in header
-#define mSetVersion(_msg,_version) BF_SET(_msg.version_length, _version, 0, 2) //!< Set version field
-#define mGetVersion(_msg) ((uint8_t)BF_GET(_msg.version_length, 0, 2)) //!< Get version field
+#define mSetVersion(_message,_version) BF_SET(_message.version_length, _version, 0, 2) //!< Set version field
+#define mGetVersion(_message) ((uint8_t)BF_GET(_message.version_length, 0, 2)) //!< Get version field
-#define mSetSigned(_msg,_signed) BF_SET(_msg.version_length, _signed, 2, 1) //!< Set signed field
-#define mGetSigned(_msg) ((bool)BF_GET(_msg.version_length, 2, 1)) //!< Get versignedsion field
+#define mSetSigned(_message,_signed) BF_SET(_message.version_length, _signed, 2, 1) //!< Set signed field
+#define mGetSigned(_message) ((bool)BF_GET(_message.version_length, 2, 1)) //!< Get versignedsion field
-#define mSetLength(_msg,_length) BF_SET(_msg.version_length, _length, 3, 5) //!< Set length field
-#define mGetLength(_msg) ((uint8_t)BF_GET(_msg.version_length, 3, 5)) //!< Get length field
+#define mSetLength(_message,_length) BF_SET(_message.version_length, _length, 3, 5) //!< Set length field
+#define mGetLength(_message) ((uint8_t)BF_GET(_message.version_length, 3, 5)) //!< Get length field
-#define mSetCommand(_msg,_command) BF_SET(_msg.command_ack_payload, _command, 0, 3) //!< Set command field
-#define mGetCommand(_msg) ((uint8_t)BF_GET(_msg.command_ack_payload, 0, 3)) //!< Get command field
+#define mSetCommand(_message,_command) BF_SET(_message.command_ack_payload, _command, 0, 3) //!< Set command field
+#define mGetCommand(_message) ((uint8_t)BF_GET(_message.command_ack_payload, 0, 3)) //!< Get command field
-#define mSetRequestAck(_msg,_rack) BF_SET(_msg.command_ack_payload, _rack, 3, 1) //!< Set ack-request field
-#define mGetRequestAck(_msg) ((bool)BF_GET(_msg.command_ack_payload, 3, 1)) //!< Get ack-request field
+#define mSetRequestAck(_message,_rack) BF_SET(_message.command_ack_payload, _rack, 3, 1) //!< Set ack-request field
+#define mGetRequestAck(_message) ((bool)BF_GET(_message.command_ack_payload, 3, 1)) //!< Get ack-request field
-#define mSetAck(_msg,_ackMsg) BF_SET(_msg.command_ack_payload, _ackMsg, 4, 1) //!< Set ack field
-#define mGetAck(_msg) ((bool)BF_GET(_msg.command_ack_payload, 4, 1)) //!< Get ack field
+#define mSetAck(_message,_ackMsg) BF_SET(_message.command_ack_payload, _ackMsg, 4, 1) //!< Set ack field
+#define mGetAck(_message) ((bool)BF_GET(_message.command_ack_payload, 4, 1)) //!< Get ack field
-#define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_ack_payload, _pt, 5, 3) //!< Set payload type field
-#define mGetPayloadType(_msg) ((uint8_t)BF_GET(_msg.command_ack_payload, 5, 3)) //!< Get payload type field
+#define mSetPayloadType(_message, _pt) BF_SET(_message.command_ack_payload, _pt, 5, 3) //!< Set payload type field
+#define mGetPayloadType(_message) ((uint8_t)BF_GET(_message.command_ack_payload, 5, 3)) //!< Get payload type field
// internal access for special fields
@@ -257,6 +256,9 @@ typedef enum {
#define miSetLength(_length) BF_SET(version_length, _length, 3, 5) //!< Internal setter for length field
#define miGetLength() ((uint8_t)BF_GET(version_length, 3, 5)) //!< Internal getter for length field
+#define miSetVersion(_version) BF_SET(version_length, _version, 0, 2) //!< Internal setter for version field
+#define miGetVersion() ((uint8_t)BF_GET(version_length, 0, 2)) //!< Internal getter for version field
+
#define miSetRequestAck(_rack) BF_SET(command_ack_payload, _rack, 3, 1) //!< Internal setter for ack-request field
#define miGetRequestAck() ((bool)BF_GET(command_ack_payload, 3, 1)) //!< Internal getter for ack-request field
@@ -282,6 +284,11 @@ class MyMessage
char i2h(uint8_t i) const;
+ /**
+ * Clear message contents.
+ */
+ void clear();
+
/**
* If payload is something else than P_STRING you can have the payload value converted
* into string representation by supplying a buffer with the minimum size of
@@ -324,8 +331,7 @@ class MyMessage
#else
typedef union {
-struct
-{
+ struct {
#endif
uint8_t last; // 8 bit - Id of last node this message passed
@@ -333,12 +339,12 @@ struct
uint8_t destination; // 8 bit - Id of destination node
uint8_t version_length; // 2 bit - Protocol version
- // 1 bit - Signed flag
- // 5 bit - Length of payload
+ // 1 bit - Signed flag
+ // 5 bit - Length of payload
uint8_t command_ack_payload; // 3 bit - Command type
- // 1 bit - Request an ack - Indicator that receiver should send an ack back.
- // 1 bit - Is ack messsage - Indicator that this is the actual ack message.
- // 3 bit - Payload data type
+ // 1 bit - Request an ack - Indicator that receiver should send an ack back.
+ // 1 bit - Is ack messsage - Indicator that this is the actual ack message.
+ // 3 bit - Payload data type
uint8_t type; // 8 bit - Type varies depending on command
uint8_t sensor; // 8 bit - Id of sensor that this message concerns.
@@ -357,7 +363,7 @@ struct
};
struct { // Presentation messages
uint8_t version; // Library version
- uint8_t sensorType; // Sensor type hint for controller, see table above
+ uint8_t sensorType; // Sensor type hint for controller, see table above
};
char data[MAX_PAYLOAD + 1];
} __attribute__((packed));
@@ -365,7 +371,7 @@ struct
} __attribute__((packed));
#else
};
-uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1];
+uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1];
} __attribute__((packed)) MyMessage;
#endif
#endif
diff --git a/core/MyOTAFirmwareUpdate.cpp b/core/MyOTAFirmwareUpdate.cpp
index 73f7488b3..1757c6b8f 100644
--- a/core/MyOTAFirmwareUpdate.cpp
+++ b/core/MyOTAFirmwareUpdate.cpp
@@ -16,134 +16,160 @@
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
-
+
#include "MyOTAFirmwareUpdate.h"
+// global variables
+extern MyMessage _msg;
+extern MyMessage _msgTmp;
+
+// local variables
SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID);
-NodeFirmwareConfig _fc;
-bool _fwUpdateOngoing;
-unsigned long _fwLastRequestTime;
-uint16_t _fwBlock;
-uint8_t _fwRetry;
+nodeFirmwareConfig_t _nodeFirmwareConfig;
+bool _firmwareUpdateOngoing;
+uint32_t _firmwareLastRequest;
+uint16_t _firmwareBlock;
+uint8_t _firmwareRetry;
-inline void readFirmwareSettings() {
- hwReadConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
+void readFirmwareSettings(void)
+{
+ hwReadConfigBlock((void*)&_nodeFirmwareConfig, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,
+ sizeof(nodeFirmwareConfig_t));
}
-inline void firmwareOTAUpdateRequest() {
- unsigned long enter = hwMillis();
- if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) {
- if (!_fwRetry) {
- setIndication(INDICATION_ERR_FW_TIMEOUT);
- debug(PSTR("fw upd fail\n"));
+void firmwareOTAUpdateRequest(void)
+{
+ const uint32_t enterMS = hwMillis();
+ if (_firmwareUpdateOngoing && (enterMS - _firmwareLastRequest > MY_OTA_RETRY_DELAY)) {
+ if (!_firmwareRetry) {
+ setIndication(INDICATION_ERR_FW_TIMEOUT);
+ OTA_DEBUG(PSTR("!OTA:FRQ:FW UPD FAIL\n")); // fw update failed
// Give up. We have requested MY_OTA_RETRY times without any packet in return.
- _fwUpdateOngoing = false;
+ _firmwareUpdateOngoing = false;
return;
}
- _fwRetry--;
- _fwLastRequestTime = enter;
+ _firmwareRetry--;
+ _firmwareLastRequest = enterMS;
// Time to (re-)request firmware block from controller
- RequestFWBlock firmwareRequest;
- firmwareRequest.type = _fc.type;
- firmwareRequest.version = _fc.version;
- firmwareRequest.block = (_fwBlock - 1);
- debug(PSTR("req FW: T=%02X, V=%02X, B=%04X\n"),_fc.type,_fc.version,_fwBlock - 1);
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false).set(&firmwareRequest,sizeof(RequestFWBlock)));
+ requestFirmwareBlock_t firmwareRequest;
+ firmwareRequest.type = _nodeFirmwareConfig.type;
+ firmwareRequest.version = _nodeFirmwareConfig.version;
+ firmwareRequest.block = (_firmwareBlock - 1);
+ OTA_DEBUG(PSTR("OTA:FRQ:FW REQ,T=%04X,V=%04X,B=%04X\n"), _nodeFirmwareConfig.type,
+ _nodeFirmwareConfig.version, _firmwareBlock - 1); // request FW update block
+ (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST,
+ false).set(&firmwareRequest, sizeof(requestFirmwareBlock_t)));
}
}
-inline bool firmwareOTAUpdateProcess() {
+bool firmwareOTAUpdateProcess(void)
+{
if (_msg.type == ST_FIRMWARE_CONFIG_RESPONSE) {
- NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)_msg.data;
- // compare with current node configuration, if they differ, start fw fetch process
- if (memcmp(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) {
- setIndication(INDICATION_FW_UPDATE_START);
- debug(PSTR("fw update\n"));
+ nodeFirmwareConfig_t *firmwareConfigResponse = (nodeFirmwareConfig_t *)_msg.data;
+ // compare with current node configuration, if they differ, start FW fetch process
+ if (memcmp(&_nodeFirmwareConfig, firmwareConfigResponse, sizeof(nodeFirmwareConfig_t))) {
+ setIndication(INDICATION_FW_UPDATE_START);
+ OTA_DEBUG(PSTR("OTA:FWP:UPDATE\n")); // FW update initiated
// copy new FW config
- memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig));
+ (void)memcpy(&_nodeFirmwareConfig, firmwareConfigResponse, sizeof(nodeFirmwareConfig_t));
// Init flash
if (!_flash.initialize()) {
- setIndication(INDICATION_ERR_FW_FLASH_INIT);
- debug(PSTR("flash init fail\n"));
- _fwUpdateOngoing = false;
+ setIndication(INDICATION_ERR_FW_FLASH_INIT);
+ OTA_DEBUG(PSTR("!OTA:FWP:FLASH INIT FAIL\n")); // failed to initialise flash
+ _firmwareUpdateOngoing = false;
} else {
// erase lower 32K -> max flash size for ATMEGA328
_flash.blockErase32K(0);
// wait until flash erased
- while ( _flash.busy() );
- _fwBlock = _fc.blocks;
- _fwUpdateOngoing = true;
+ while ( _flash.busy() ) {}
+ _firmwareBlock = _nodeFirmwareConfig.blocks;
+ _firmwareUpdateOngoing = true;
// reset flags
- _fwRetry = MY_OTA_RETRY+1;
- _fwLastRequestTime = 0;
+ _firmwareRetry = MY_OTA_RETRY + 1;
+ _firmwareLastRequest = 0;
}
return true;
}
- debug(PSTR("fw update skipped\n"));
+ OTA_DEBUG(PSTR("OTA:FWP:UPDATE SKIPPED\n")); // FW update skipped, no newer version available
} else if (_msg.type == ST_FIRMWARE_RESPONSE) {
- if (_fwUpdateOngoing) {
+ if (_firmwareUpdateOngoing) {
// Save block to flash
- setIndication(INDICATION_FW_UPDATE_RX);
- debug(PSTR("fw block %d\n"), _fwBlock);
+ setIndication(INDICATION_FW_UPDATE_RX);
+ OTA_DEBUG(PSTR("OTA:FWP:RECV B=%04X\n"), _firmwareBlock); // received FW block
// extract FW block
- ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)_msg.data;
+ replyFirmwareBlock_t *firmwareResponse = (replyFirmwareBlock_t *)_msg.data;
// write to flash
- _flash.writeBytes( ((_fwBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
+ _flash.writeBytes( ((_firmwareBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET,
+ firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
// wait until flash written
- while ( _flash.busy() );
- _fwBlock--;
- if (!_fwBlock) {
+ while (_flash.busy()) {}
+ _firmwareBlock--;
+ if (!_firmwareBlock) {
// We're finished! Do a checksum and reboot.
- _fwUpdateOngoing = false;
+ OTA_DEBUG(PSTR("OTA:FWP:FW END\n")); // received FW block
+ _firmwareUpdateOngoing = false;
if (transportIsValidFirmware()) {
- debug(PSTR("fw checksum ok\n"));
- // All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it)
- uint16_t fwsize = FIRMWARE_BLOCK_SIZE * _fc.blocks;
- uint8_t OTAbuffer[10] = {'F','L','X','I','M','G',':',(uint8_t)(fwsize >> 8),(uint8_t)(fwsize & 0xff),':'};
- _flash.writeBytes(0, OTAbuffer, 10);
+ OTA_DEBUG(PSTR("OTA:FWP:CRC OK\n")); // FW checksum ok
// Write the new firmware config to eeprom
- hwWriteConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
+ hwWriteConfigBlock((void*)&_nodeFirmwareConfig, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,
+ sizeof(nodeFirmwareConfig_t));
+ // All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it)
+ const uint16_t firmwareSize = FIRMWARE_BLOCK_SIZE * _nodeFirmwareConfig.blocks;
+ const uint8_t OTAbuffer[FIRMWARE_START_OFFSET] = {'F','L','X','I','M','G',':', (uint8_t)(firmwareSize >> 8), (uint8_t)(firmwareSize & 0xff),':'};
+ _flash.writeBytes(0, OTAbuffer, FIRMWARE_START_OFFSET);
+ // wait until flash ready
+ while (_flash.busy()) {}
hwReboot();
} else {
- setIndication(INDICATION_ERR_FW_CHECKSUM);
- debug(PSTR("fw checksum fail\n"));
+ setIndication(INDICATION_ERR_FW_CHECKSUM);
+ OTA_DEBUG(PSTR("!OTA:FWP:CRC FAIL\n"));
}
}
// reset flags
- _fwRetry = MY_OTA_RETRY+1;
- _fwLastRequestTime = 0;
+ _firmwareRetry = MY_OTA_RETRY + 1;
+ _firmwareLastRequest = 0;
} else {
- debug(PSTR("No fw update ongoing\n"));
+ OTA_DEBUG(PSTR("!OTA:FWP:NO UPDATE\n"));
}
return true;
}
return false;
}
-inline void presentBootloaderInformation(){
- RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)_msgTmp.data;
- mSetLength(_msgTmp, sizeof(RequestFirmwareConfig));
+void presentBootloaderInformation(void)
+{
+ requestFirmwareConfig_t *requestFirmwareConfig = (requestFirmwareConfig_t *)_msgTmp.data;
+ mSetLength(_msgTmp, sizeof(requestFirmwareConfig_t));
mSetCommand(_msgTmp, C_STREAM);
- mSetPayloadType(_msgTmp,P_CUSTOM);
+ mSetPayloadType(_msgTmp, P_CUSTOM);
// copy node settings to reqFWConfig
- memcpy(reqFWConfig,&_fc,sizeof(NodeFirmwareConfig));
+ (void)memcpy(requestFirmwareConfig, &_nodeFirmwareConfig, sizeof(nodeFirmwareConfig_t));
// add bootloader information
- reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION;
- _fwUpdateOngoing = false;
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false));
+ requestFirmwareConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION;
+ _firmwareUpdateOngoing = false;
+ (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM,
+ ST_FIRMWARE_CONFIG_REQUEST, false));
+}
+bool isFirmwareUpdateOngoing(void)
+{
+ return _firmwareUpdateOngoing;
}
// do a crc16 on the whole received firmware
-inline bool transportIsValidFirmware() {
+bool transportIsValidFirmware(void)
+{
// init crc
uint16_t crc = ~0;
- for (uint16_t i = 0; i < _fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) {
+ for (uint16_t i = 0; i < _nodeFirmwareConfig.blocks * FIRMWARE_BLOCK_SIZE; ++i) {
crc ^= _flash.readByte(i + FIRMWARE_START_OFFSET);
- for (int8_t j = 0; j < 8; ++j) {
- if (crc & 1)
- crc = (crc >> 1) ^ 0xA001;
- else
- crc = (crc >> 1);
- }
+ for (int8_t j = 0; j < 8; ++j) {
+ if (crc & 1) {
+ crc = (crc >> 1) ^ 0xA001;
+ } else {
+ crc = (crc >> 1);
+ }
+ }
}
- return crc == _fc.crc;
+ OTA_DEBUG(PSTR("OTA:CRC:B=%04X,C=%04X,F=%04X\n"), _nodeFirmwareConfig.blocks,crc,
+ _nodeFirmwareConfig.crc);
+ return crc == _nodeFirmwareConfig.crc;
}
diff --git a/core/MyOTAFirmwareUpdate.h b/core/MyOTAFirmwareUpdate.h
index 3c3921d44..9372fd2c9 100644
--- a/core/MyOTAFirmwareUpdate.h
+++ b/core/MyOTAFirmwareUpdate.h
@@ -17,58 +17,100 @@
* version 2 as published by the Free Software Foundation.
*/
+/**
+* @file MyOTAFirmwareUpdate.h
+*
+* @defgroup MyOTAFirmwaregrp MyOTAFirmwareUpdate
+* @ingroup internals
+* @{
+*
+* MyOTAFirmwareUpdate-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE
+* - [!] Exclamation mark is prepended in case of error or warning
+* - SYSTEM:
+* - OTA messages emitted by MyOTAFirmwareUpdate
+* - SUB SYSTEMS:
+* - OTA:FRQ from @ref firmwareOTAUpdateRequest()
+* - OTA:FWP from @ref firmwareOTAUpdateProcess()
+*
+* MyOTAFirmwareUpdate debug log messages:
+*
+* |E| SYS | SUB | Message | Comment
+* |-|------|-------|-------------------------------------------|----------------------------------------------------------------------------
+* | | OTA | FWP | UPDATE | FW update initiated
+* |!| OTA | FWP | FLASH INIT FAIL | Failed to initialise flash
+* | | OTA | FWP | UPDATE SKIPPED | FW update skipped, no newer version available
+* | | OTA | FWP | RECV B=%04X | Received FW block (B)
+* | | OTA | FWP | FW END | FW received, proceed to CRC verification
+* | | OTA | FWP | CRC OK | FW CRC verification OK
+* |!| OTA | FWP | CRC FAIL | FW CRC verification failed
+* | | OTA | FRQ | FW REQ,T=%04X,V=%04X,B=%04X | Request FW update, FW type (T), version (V), block (B)
+* |!| OTA | FRQ | FW UPD FAIL | FW update failed
+* | | OTA | CRC | B=%04X,C=%04X,F=%04X | FW CRC verification. FW blocks (B), calculated CRC (C), FW CRC (F)
+*
+*
+* @brief API declaration for MyOTAFirmwareUpdate
+*/
+
+
#ifndef MyOTAFirmwareUpdate_h
#define MyOTAFirmwareUpdate_h
#include "MySensorsCore.h"
-// Size of each firmware block
-#define FIRMWARE_BLOCK_SIZE 16
-// Number of times a firmware block should be requested before giving up
-#define FIRMWARE_MAX_REQUESTS 5
-// Number of times to request a fw block before giving up
-#define MY_OTA_RETRY 5
-// Number of millisecons before re-request a fw block
-#define MY_OTA_RETRY_DELAY 500
-// Start offset for firmware in flash (DualOptiboot wants to keeps a signature first)
-#define FIRMWARE_START_OFFSET 10
-// Bootloader version
-#define MY_OTA_BOOTLOADER_MAJOR_VERSION 3
-#define MY_OTA_BOOTLOADER_MINOR_VERSION 0
-#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION)
+#define FIRMWARE_BLOCK_SIZE (16u) //!< Size of each firmware block
+#define FIRMWARE_MAX_REQUESTS (5u) //!< Number of times a firmware block should be requested before giving up
+#define MY_OTA_RETRY (5u) //!< Number of times to request a fw block before giving up
+#define MY_OTA_RETRY_DELAY (500u) //!< Number of milliseconds before re-requesting a FW block
+#define FIRMWARE_START_OFFSET (10u) //!< Start offset for firmware in flash (DualOptiboot wants to keeps a signature first)
+#define MY_OTA_BOOTLOADER_MAJOR_VERSION (3u) //!< Bootloader version major
+#define MY_OTA_BOOTLOADER_MINOR_VERSION (0u) //!< Bootloader version minor
+#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION) //!< Bootloader version
-/// @brief FW config structure, stored in eeprom
+#if defined(MY_DEBUG)
+#define OTA_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
+#else
+#define OTA_DEBUG(x,...) //!< debug NULL
+#endif
+/**
+* @brief FW config structure, stored in eeprom
+*/
typedef struct {
- uint16_t type; //!< Type of config
- uint16_t version; //!< Version of config
- uint16_t blocks; //!< Number of blocks
- uint16_t crc; //!< CRC of block data
-} __attribute__((packed)) NodeFirmwareConfig;
+ uint16_t type; //!< Type of config
+ uint16_t version; //!< Version of config
+ uint16_t blocks; //!< Number of blocks
+ uint16_t crc; //!< CRC of block data
+} __attribute__((packed)) nodeFirmwareConfig_t;
-/// @brief FW config request structure
+/**
+* @brief FW config request structure
+*/
typedef struct {
- uint16_t type; //!< Type of config
- uint16_t version; //!< Version of config
- uint16_t blocks; //!< Number of blocks
- uint16_t crc; //!< CRC of block data
- uint16_t BLVersion; //!< Bootloader version
-} __attribute__((packed)) RequestFirmwareConfig;
+ uint16_t type; //!< Type of config
+ uint16_t version; //!< Version of config
+ uint16_t blocks; //!< Number of blocks
+ uint16_t crc; //!< CRC of block data
+ uint16_t BLVersion; //!< Bootloader version
+} __attribute__((packed)) requestFirmwareConfig_t;
-/// @brief FW block request structure
+/**
+* @brief FW block request structure
+*/
typedef struct {
- uint16_t type; //!< Type of config
- uint16_t version; //!< Version of config
- uint16_t block; //!< Block index
-} __attribute__((packed)) RequestFWBlock;
+ uint16_t type; //!< Type of config
+ uint16_t version; //!< Version of config
+ uint16_t block; //!< Block index
+} __attribute__((packed)) requestFirmwareBlock_t;
-/// @brief FW block reply structure
+/**
+* @brief FW block reply structure
+*/
typedef struct {
- uint16_t type; //!< Type of config
- uint16_t version; //!< Version of config
- uint16_t block; //!< Block index
- uint8_t data[FIRMWARE_BLOCK_SIZE]; //!< Block data
-} __attribute__((packed)) ReplyFWBlock;
+ uint16_t type; //!< Type of config
+ uint16_t version; //!< Version of config
+ uint16_t block; //!< Block index
+ uint8_t data[FIRMWARE_BLOCK_SIZE]; //!< Block data
+} __attribute__((packed)) replyFirmwareBlock_t;
/**
@@ -76,26 +118,28 @@ typedef struct {
*
* Current firmware settings (type, version, crc, blocks) are read into _fc
*/
-void readFirmwareSettings();
+void readFirmwareSettings(void);
/**
* @brief Handle OTA FW update requests
*/
- void firmwareOTAUpdateRequest();
+void firmwareOTAUpdateRequest(void);
/**
* @brief Handle OTA FW update responses
*
* This function handles incoming OTA FW packets and stores them to external flash (Sensebender)
*/
-bool firmwareOTAUpdateProcess();
+bool firmwareOTAUpdateProcess(void);
/**
* @brief Validate uploaded FW CRC
*
* This function verifies if uploaded FW CRC is valid
*/
-bool transportIsValidFirmware();
+bool transportIsValidFirmware(void);
/**
- * @brief Present bootloader/FW information upon startup
+ * @brief Present bootloader/FW information upon startup
*/
-void presentBootloaderInformation();
+void presentBootloaderInformation(void);
+
+#endif
-#endif
\ No newline at end of file
+/** @}*/
diff --git a/core/MyProtocolMySensors.cpp b/core/MyProtocolMySensors.cpp
old mode 100644
new mode 100755
index 347b9d2b5..0c7308560
--- a/core/MyProtocolMySensors.cpp
+++ b/core/MyProtocolMySensors.cpp
@@ -20,95 +20,190 @@
#include "MyConfig.h"
#include "MyTransport.h"
#include "MyProtocol.h"
+#include
uint8_t protocolH2i(char c);
char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH];
char _convBuffer[MAX_PAYLOAD*2+1];
-bool protocolParse(MyMessage &message, char *inputString) {
+bool protocolParse(MyMessage &message, char *inputString)
+{
char *str, *p, *value=NULL;
uint8_t bvalue[MAX_PAYLOAD];
uint8_t blen = 0;
int i = 0;
uint8_t command = 0;
- uint8_t ack = 0;
// Extract command data coming on serial line
for (str = strtok_r(inputString, ";", &p); // split using semicolon
- str && i < 6; // loop while str is not null an max 5 times
- str = strtok_r(NULL, ";", &p) // get subsequent tokens
- ) {
+ str && i < 6; // loop while str is not null an max 5 times
+ str = strtok_r(NULL, ";", &p) // get subsequent tokens
+ ) {
switch (i) {
- case 0: // Radioid (destination)
- message.destination = atoi(str);
- break;
- case 1: // Childid
- message.sensor = atoi(str);
- break;
- case 2: // Message type
- command = atoi(str);
- mSetCommand(message, command);
- break;
- case 3: // Should we request ack from destination?
- ack = atoi(str);
- break;
- case 4: // Data type
- message.type = atoi(str);
- break;
- case 5: // Variable value
- if (command == C_STREAM) {
- blen = 0;
+ case 0: // Radioid (destination)
+ message.destination = atoi(str);
+ break;
+ case 1: // Childid
+ message.sensor = atoi(str);
+ break;
+ case 2: // Message type
+ command = atoi(str);
+ mSetCommand(message, command);
+ break;
+ case 3: // Should we request ack from destination?
+ mSetRequestAck(message, atoi(str)?1:0);
+ break;
+ case 4: // Data type
+ message.type = atoi(str);
+ break;
+ case 5: // Variable value
+ if (command == C_STREAM) {
+ blen = 0;
+ while (*str) {
uint8_t val;
- while (*str) {
- val = protocolH2i(*str++) << 4;
- val += protocolH2i(*str++);
- bvalue[blen] = val;
- blen++;
- }
- } else {
- value = str;
- // Remove trailing carriage return and newline character (if it exists)
- uint8_t lastCharacter = strlen(value)-1;
- if (value[lastCharacter] == '\r')
- value[lastCharacter] = 0;
- if (value[lastCharacter] == '\n')
- value[lastCharacter] = 0;
+ val = protocolH2i(*str++) << 4;
+ val += protocolH2i(*str++);
+ bvalue[blen] = val;
+ blen++;
}
- break;
+ } else {
+ value = str;
+ // Remove trailing carriage return and newline character (if it exists)
+ uint8_t lastCharacter = strlen(value)-1;
+ if (value[lastCharacter] == '\r') {
+ value[lastCharacter] = 0;
+ }
+ if (value[lastCharacter] == '\n') {
+ value[lastCharacter] = 0;
+ }
+ }
+ break;
}
i++;
}
//debug(PSTR("Received %d"), i);
// Check for invalid input
- if (i < 5)
+ if (i < 5) {
return false;
-
+ }
message.sender = GATEWAY_ADDRESS;
message.last = GATEWAY_ADDRESS;
- mSetRequestAck(message, ack?1:0);
- mSetAck(message, false);
- if (command == C_STREAM)
+ mSetAck(message, false);
+ if (command == C_STREAM) {
message.set(bvalue, blen);
- else
+ } else {
message.set(value);
+ }
return true;
}
-char * protocolFormat(MyMessage &message) {
- snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%d;%d;%d;%d;%d;%s\n"), message.sender, message.sensor, (uint8_t)mGetCommand(message), (uint8_t)mGetAck(message), message.type, message.getString(_convBuffer));
+char * protocolFormat(MyMessage &message)
+{
+ snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%d;%d;%d;%d;%d;%s\n"), message.sender,
+ message.sensor, (uint8_t)mGetCommand(message), (uint8_t)mGetAck(message), message.type,
+ message.getString(_convBuffer));
+ return _fmtBuffer;
+}
+
+char * protocolFormatMQTTTopic(const char* prefix, MyMessage &message)
+{
+ snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%s/%d/%d/%d/%d/%d"), prefix,
+ message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type);
+ return _fmtBuffer;
+}
+
+char * protocolFormatMQTTSubscribe(const char* prefix)
+{
+ snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%s/+/+/+/+/+"), prefix);
return _fmtBuffer;
}
-uint8_t protocolH2i(char c) {
+#ifdef MY_GATEWAY_MQTT_CLIENT
+bool protocolMQTTParse(MyMessage &message, char* topic, uint8_t* payload, unsigned int length)
+{
+ char *str, *p;
uint8_t i = 0;
- if (c <= '9')
+ uint8_t bvalue[MAX_PAYLOAD];
+ uint8_t blen = 0;
+ uint8_t command = 0;
+ if (topic != strstr(topic, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX)) {
+ // Prefix doesn't match incoming topic
+ return false;
+ }
+ for (str = strtok_r(topic + strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1, "/", &p); str && i <= 5;
+ str = strtok_r(NULL, "/", &p)) {
+ switch (i) {
+ case 0: {
+ // Node id
+ message.destination = atoi(str);
+ break;
+ }
+ case 1: {
+ // Sensor id
+ message.sensor = atoi(str);
+ break;
+ }
+ case 2: {
+ // Command type
+ command = atoi(str);
+ mSetCommand(message, command);
+ break;
+ }
+ case 3: {
+ // Ack flag
+ mSetRequestAck(message, atoi(str)?1:0);
+ break;
+ }
+ case 4: {
+ // Sub type
+ message.type = atoi(str);
+ break;
+ }
+ }
+ i++;
+ }
+
+ if (i != 5) {
+ return false;
+ }
+
+ message.sender = GATEWAY_ADDRESS;
+ message.last = GATEWAY_ADDRESS;
+ mSetAck(message, false);
+
+ // Add payload
+ if (command == C_STREAM) {
+ blen = 0;
+ uint8_t val;
+ while (*payload) {
+ val = protocolH2i(*payload++) << 4;
+ val += protocolH2i(*payload++);
+ bvalue[blen] = val;
+ blen++;
+ }
+ message.set(bvalue, blen);
+ } else {
+ char* ca;
+ ca = (char *) payload;
+ ca += length;
+ *ca = '\0';
+ message.set((const char*) payload);
+ }
+
+ return true;
+}
+#endif
+
+uint8_t protocolH2i(char c)
+{
+ uint8_t i = 0;
+ if (c <= '9') {
i += c - '0';
- else if (c >= 'a')
+ } else if (c >= 'a') {
i += c - 'a' + 10;
- else
+ } else {
i += c - 'A' + 10;
+ }
return i;
}
-
-
diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp
index 7ab8b20b6..3dfed8739 100644
--- a/core/MySensorsCore.cpp
+++ b/core/MySensorsCore.cpp
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad
- * Copyright (C) 2013-2015 Sensnology AB
+ * Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
@@ -19,544 +19,671 @@
#include "MySensorsCore.h"
-ControllerConfig _cc; // Configuration coming from controller
-NodeConfig _nc; // Essential settings for node to work
-MyMessage _msg; // Buffer for incoming messages.
-MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others).
+#if defined(__linux__)
+#include
+#include
+#endif
+
+// message buffers
+MyMessage _msg; // Buffer for incoming messages
+MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others)
-bool _nodeRegistered = false;
+// core configuration
+static coreConfig_t _coreConfig;
#if defined(MY_DEBUG)
- char _convBuf[MAX_PAYLOAD*2+1];
+char _convBuf[MAX_PAYLOAD*2+1];
#endif
-void (*_timeCallback)(unsigned long); // Callback for requested time messages
-
-void _process() {
- hwWatchdogReset();
+// Callback for transport=ok transition
+void _callbackTransportReady(void)
+{
+ if (!_coreConfig.presentationSent) {
+#if !defined(MY_GATEWAY_FEATURE) // GW calls presentNode() when client connected
+ presentNode();
+#endif
+ _registerNode();
+ _coreConfig.presentationSent = true;
+ }
+}
- #if defined (MY_LEDS_BLINKING_FEATURE)
- ledsProcess();
- #endif
+void _process(void)
+{
+ doYield();
- #if defined(MY_INCLUSION_MODE_FEATURE)
- inclusionProcess();
- #endif
+#if defined(MY_INCLUSION_MODE_FEATURE)
+ inclusionProcess();
+#endif
- #if defined(MY_GATEWAY_FEATURE)
- gatewayTransportProcess();
- #endif
+#if defined(MY_GATEWAY_FEATURE)
+ gatewayTransportProcess();
+#endif
- #if defined(MY_RADIO_FEATURE)
- transportProcess();
- #endif
+#if defined(MY_SENSOR_NETWORK)
+ transportProcess();
+#endif
+#if defined(__linux__)
+ // To avoid high cpu usage
+ usleep(10000); // 10ms
+#endif
}
-void _infiniteLoop() {
+void _infiniteLoop(void)
+{
while(1) {
- #if defined(ARDUINO_ARCH_ESP8266)
- yield();
- #endif
- #if defined (MY_LEDS_BLINKING_FEATURE)
- ledsProcess();
- #endif
+ doYield();
+#if defined(__linux__)
+ exit(1);
+#endif
}
}
-void _begin() {
- #if !defined(MY_DISABLED_SERIAL)
- hwInit();
- #endif
+void _begin(void)
+{
+ // reset wdt
+ hwWatchdogReset();
- // Call before() in sketch (if it exists)
- if (before)
- before();
+ if (preHwInit) {
+ preHwInit();
+ }
+
+ hwInit();
+
+ CORE_DEBUG(PSTR("MCO:BGN:INIT " MY_NODE_TYPE ",CP=" MY_CAPABILITIES ",VER="
+ MYSENSORS_LIBRARY_VERSION "\n"));
+
+ // set defaults
+ _coreConfig.presentationSent = false;
- debug(PSTR("Starting " MY_NODE_TYPE " (" MY_CAPABILITIES ", " MYSENSORS_LIBRARY_VERSION ")\n"));
+ // Call sketch before() (if defined)
+ if (before) {
+ CORE_DEBUG(PSTR("MCO:BGN:BFR\n")); // before callback
+ before();
+ }
- #if defined(MY_LEDS_BLINKING_FEATURE)
- ledsInit();
- #endif
+#if defined(MY_DEFAULT_TX_LED_PIN) || defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_ERR_LED_PIN)
+ ledsInit();
+#endif
signerInit();
// Read latest received controller configuration from EEPROM
- hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
- // isMetric is bool, hence empty EEPROM (=0xFF) evaluates to true
-
- #if defined(MY_OTA_FIRMWARE_FEATURE)
- // Read firmware config from EEPROM, i.e. type, version, CRC, blocks
- readFirmwareSettings();
- #endif
-
- #if defined(MY_RADIO_FEATURE)
- // Save static parent id in eeprom (used by bootloader)
- hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, MY_PARENT_NODE_ID);
- transportInitialize();
- while (!isTransportOK()) {
- hwWatchdogReset();
- transportProcess();
- #if defined(ARDUINO_ARCH_ESP8266)
- yield();
- #endif
- }
- #endif
-
-
-
- #ifdef MY_NODE_LOCK_FEATURE
- // Check if node has been locked down
- if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0) {
- // Node is locked, check if unlock pin is asserted, else hang the node
- pinMode(MY_NODE_UNLOCK_PIN, INPUT_PULLUP);
- // Make a short delay so we are sure any large external nets are fully pulled
- unsigned long enter = hwMillis();
- while (hwMillis() - enter < 2);
- if (digitalRead(MY_NODE_UNLOCK_PIN) == 0) {
- // Pin is grounded, reset lock counter
- hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
- // Disable pullup
- pinMode(MY_NODE_UNLOCK_PIN, INPUT);
- setIndication(INDICATION_ERR_LOCKED);
- debug(PSTR("Node is unlocked.\n"));
- } else {
- // Disable pullup
- pinMode(MY_NODE_UNLOCK_PIN, INPUT);
- nodeLock("LDB"); //Locked during boot
- }
- } else if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0xFF) {
- // Reset walue
- hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
- }
- #endif
-
- #if defined(MY_GATEWAY_FEATURE)
- #if defined(MY_INCLUSION_BUTTON_FEATURE)
- inclusionInit();
- #endif
-
- // initialize the transport driver
- if (!gatewayTransportInit()) {
- setIndication(INDICATION_ERR_INIT_GWTRANSPORT);
- debug(PSTR("Transport driver init fail\n"));
- // Nothing more we can do
- _infiniteLoop();
- }
- #endif
-
- // Call sketch setup
- if (setup)
- setup();
+ // Note: _coreConfig.isMetric is bool, hence empty EEPROM (=0xFF) evaluates to true (default)
+ hwReadConfigBlock((void*)&_coreConfig.controllerConfig, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS,
+ sizeof(controllerConfig_t));
- #if defined(MY_RADIO_FEATURE)
- presentNode();
- #endif
-
- // register node
- _registerNode();
+#if defined(MY_OTA_FIRMWARE_FEATURE)
+ // Read firmware config from EEPROM, i.e. type, version, CRC, blocks
+ readFirmwareSettings();
+#endif
- debug(PSTR("Init complete, id=%d, parent=%d, distance=%d, registration=%d\n"), _nc.nodeId, _nc.parentNodeId, _nc.distance, _nodeRegistered);
-}
+#if defined(MY_SENSOR_NETWORK)
+ // Save static parent ID in eeprom (used by bootloader)
+ hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, MY_PARENT_NODE_ID);
+ // Initialise transport layer
+ transportInitialise();
+ // Register transport=ready callback
+ transportRegisterReadyCallback(_callbackTransportReady);
+ // wait until transport is ready
+ (void)transportWaitUntilReady(MY_TRANSPORT_WAIT_READY_MS);
+#endif
+
+ _checkNodeLock();
+
+#if defined(MY_GATEWAY_FEATURE)
+#if defined(MY_INCLUSION_BUTTON_FEATURE)
+ inclusionInit();
+#endif
+
+ // initialise the transport driver
+ if (!gatewayTransportInit()) {
+ setIndication(INDICATION_ERR_INIT_GWTRANSPORT);
+ CORE_DEBUG(PSTR("!MCO:BGN:TSP FAIL\n"));
+ // Nothing more we can do
+ _infiniteLoop();
+ }
+#endif
+ // Call sketch setup() (if defined)
+ if (setup) {
+ CORE_DEBUG(PSTR("MCO:BGN:STP\n")); // setup callback
+ setup();
+ }
+#if defined(MY_SENSOR_NETWORK)
+ CORE_DEBUG(PSTR("MCO:BGN:INIT OK,TSP=%d\n"), isTransportReady());
+#else
+ // no sensor network defined, call presentation & registration
+ _callbackTransportReady();
+ CORE_DEBUG(PSTR("MCO:BGN:INIT OK,TSP=NA\n"));
+#endif
+ // reset wdt before handing over to loop
+ hwWatchdogReset();
+}
-void _registerNode() {
- #if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
- debug(PSTR("Request registration...\n")); // registration request
- setIndication(INDICATION_REQ_REGISTRATION);
- _nodeRegistered = MY_REGISTRATION_DEFAULT;
- uint8_t counter = MY_REGISTRATION_RETRIES;
- // only proceed if register response received or retries exceeded
- do {
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REGISTRATION_REQUEST, false).set(MY_CORE_VERSION));
- } while (!wait(2000, C_INTERNAL, I_REGISTRATION_RESPONSE) && counter--);
- #else
- _nodeRegistered = true;
- debug(PSTR("No registration required\n"));
- #endif
+void _registerNode(void)
+{
+#if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
+ CORE_DEBUG(PSTR("MCO:REG:REQ\n")); // registration request
+ setIndication(INDICATION_REQ_REGISTRATION);
+ _coreConfig.nodeRegistered = MY_REGISTRATION_DEFAULT;
+ uint8_t counter = MY_REGISTRATION_RETRIES;
+ // only proceed if register response received or retries exceeded
+ do {
+ (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
+ I_REGISTRATION_REQUEST).set(MY_CORE_VERSION));
+ } while (!wait(2000, C_INTERNAL, I_REGISTRATION_RESPONSE) && counter--);
+#else
+ _coreConfig.nodeRegistered = true;
+ CORE_DEBUG(PSTR("MCO:REG:NOT NEEDED\n"));
+#endif
}
-void presentNode() {
+void presentNode(void)
+{
setIndication(INDICATION_PRESENT);
// Present node and request config
- #if defined(MY_GATEWAY_FEATURE)
- // Send presentation for this gateway device
- #if defined(MY_REPEATER_FEATURE)
- present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
- #else
- present(NODE_SENSOR_ID, S_ARDUINO_NODE);
- #endif
- #else
-
- #if defined(MY_OTA_FIRMWARE_FEATURE)
- presentBootloaderInformation();
- #endif
-
- // Send signing preferences for this node to the GW
- signerPresentation(_msgTmp, GATEWAY_ADDRESS);
-
- // Send presentation for this radio node
- #if defined(MY_REPEATER_FEATURE)
- present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
- #else
- present(NODE_SENSOR_ID, S_ARDUINO_NODE);
- #endif
-
- // Send a configuration exchange request to controller
- // Node sends parent node. Controller answers with latest node configuration
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(_nc.parentNodeId));
-
- // Wait configuration reply.
- wait(2000, C_INTERNAL, I_CONFIG);
-
- #endif
-
- if (presentation)
- presentation();
+#if defined(MY_GATEWAY_FEATURE)
+ // Send presentation for this gateway device
+#if defined(MY_REPEATER_FEATURE)
+ (void)present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
+#else
+ (void)present(NODE_SENSOR_ID, S_ARDUINO_NODE);
+#endif
+#else
+
+#if defined(MY_OTA_FIRMWARE_FEATURE)
+ presentBootloaderInformation();
+#endif
+ // Send signing preferences for this node to the GW
+ signerPresentation(_msgTmp, GATEWAY_ADDRESS);
+
+ // Send presentation for this radio node
+#if defined(MY_REPEATER_FEATURE)
+ (void)present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
+#else
+ (void)present(NODE_SENSOR_ID, S_ARDUINO_NODE);
+#endif
+
+ // Send a configuration exchange request to controller
+ // Node sends parent node. Controller answers with latest node configuration
+ (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
+ I_CONFIG).set(getParentNodeId()));
+
+ // Wait configuration reply.
+ (void)wait(2000, C_INTERNAL, I_CONFIG);
+
+#endif
+
+ if (presentation) {
+ presentation();
+ }
}
-uint8_t getNodeId() {
- return _nc.nodeId;
+uint8_t getNodeId(void)
+{
+ uint8_t result;
+#if defined(MY_GATEWAY_FEATURE)
+ result = GATEWAY_ADDRESS;
+#elif defined(MY_SENSOR_NETWORK)
+ result = transportGetNodeId();
+#else
+ result = VALUE_NOT_DEFINED;
+#endif
+ return result;
}
-uint8_t getParentNodeId() {
- return _nc.parentNodeId;
+uint8_t getParentNodeId(void)
+{
+ uint8_t result;
+#if defined(MY_GATEWAY_FEATURE)
+ result = VALUE_NOT_DEFINED; // GW doesn't have a parent
+#elif defined(MY_SENSOR_NETWORK)
+ result = transportGetParentNodeId();
+#else
+ result = VALUE_NOT_DEFINED;
+#endif
+ return result;
}
-ControllerConfig getConfig() {
- return _cc;
+uint8_t getDistanceGW(void)
+{
+ uint8_t result;
+#if defined(MY_GATEWAY_FEATURE)
+ result = 0;
+#elif defined(MY_SENSOR_NETWORK)
+ result = transportGetDistanceGW();
+#else
+ result = VALUE_NOT_DEFINED;
+#endif
+ return result;
}
+controllerConfig_t getConfig(void)
+{
+ return _coreConfig.controllerConfig;
+}
-bool _sendRoute(MyMessage &message) {
- #if defined(MY_CORE_ONLY)
- (void)message;
- #endif
- #if defined(MY_GATEWAY_FEATURE)
- if (message.destination == _nc.nodeId) {
- // This is a message sent from a sensor attached on the gateway node.
- // Pass it directly to the gateway transport layer.
- return gatewayTransportSend(message);
- }
- #endif
- #if defined(MY_RADIO_FEATURE)
- return transportSendRoute(message);
- #else
- return false;
- #endif
+
+bool _sendRoute(MyMessage &message)
+{
+#if defined(MY_CORE_ONLY)
+ (void)message;
+#endif
+#if defined(MY_GATEWAY_FEATURE)
+ if (message.destination == getNodeId()) {
+ // This is a message sent from a sensor attached on the gateway node.
+ // Pass it directly to the gateway transport layer.
+ return gatewayTransportSend(message);
+ }
+#endif
+#if defined(MY_SENSOR_NETWORK)
+ return transportSendRoute(message);
+#else
+ return false;
+#endif
}
-bool send(MyMessage &message, bool enableAck) {
- message.sender = _nc.nodeId;
+bool send(MyMessage &message, const bool enableAck)
+{
+ message.sender = getNodeId();
mSetCommand(message, C_SET);
mSetRequestAck(message, enableAck);
- #if defined(MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
- if (_nodeRegistered) {
- return _sendRoute(message);
- }
- else {
- debug(PSTR("NODE:!REG\n"));
- return false;
- }
- #else
+#if defined(MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
+ if (_coreConfig.nodeRegistered) {
return _sendRoute(message);
- #endif
+ } else {
+ CORE_DEBUG(PSTR("!MCO:SND:NODE NOT REG\n")); // node not registered
+ return false;
}
+#else
+ return _sendRoute(message);
+#endif
+}
-void sendBatteryLevel(uint8_t value, bool enableAck) {
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value));
+bool sendBatteryLevel(const uint8_t value, const bool ack)
+{
+ return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL,
+ ack).set(value));
}
-void sendHeartbeat(void) {
- #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485)
- uint32_t heartbeat = transportGetHeartbeat();
- #else
- uint32_t heartbeat = hwMillis();
- #endif
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT_RESPONSE, false).set(heartbeat));
+bool sendHeartbeat(const bool ack)
+{
+#if defined(MY_SENSOR_NETWORK)
+ const uint32_t heartbeat = transportGetHeartbeat();
+ return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT_RESPONSE,
+ ack).set(heartbeat));
+#else
+ (void)ack;
+ return false;
+#endif
}
-void present(uint8_t childSensorId, uint8_t sensorType, const char *description, bool enableAck) {
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?MYSENSORS_LIBRARY_VERSION:description));
+bool present(const uint8_t childSensorId, const uint8_t sensorType, const char *description,
+ const bool ack)
+{
+ return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType,
+ ack).set(childSensorId==NODE_SENSOR_ID?MYSENSORS_LIBRARY_VERSION:description));
}
-void sendSketchInfo(const char *name, const char *version, bool enableAck) {
- if (name) _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name));
- if (version) _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version));
+bool sendSketchInfo(const char *name, const char *version, const bool ack)
+{
+ bool result = true;
+ if (name) {
+ result &= _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME,
+ ack).set(name));
+ }
+ if (version) {
+ result &= _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION,
+ ack).set(version));
+ }
+ return result;
}
-void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) {
- _sendRoute(build(_msgTmp, _nc.nodeId, destination, childSensorId, C_REQ, variableType, false).set(""));
+bool request(const uint8_t childSensorId, const uint8_t variableType, const uint8_t destination)
+{
+ return _sendRoute(build(_msgTmp, destination, childSensorId, C_REQ, variableType).set(""));
}
-void requestTime() {
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set(""));
+bool requestTime(const bool ack)
+{
+ return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, ack).set(""));
}
// Message delivered through _msg
-bool _processInternalMessages() {
- uint8_t type = _msg.type;
+bool _processInternalMessages(void)
+{
+ const uint8_t type = _msg.type;
if (_msg.sender == GATEWAY_ADDRESS) {
if (type == I_REBOOT) {
- #if !defined(MY_DISABLE_REMOTE_RESET)
- // Requires MySensors or other bootloader with watchdogs enabled
- setIndication(INDICATION_REBOOT);
- hwReboot();
- #endif
- }
- else if (type == I_REGISTRATION_RESPONSE) {
- #if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
- _nodeRegistered = _msg.getBool();
- setIndication(INDICATION_GOT_REGISTRATION);
- debug(PSTR("Node registration=%d\n"), _nodeRegistered);
- #endif
- }
- else if (type == I_CONFIG) {
+#if !defined(MY_DISABLE_REMOTE_RESET)
+ // Requires MySensors or other bootloader with watchdogs enabled
+ setIndication(INDICATION_REBOOT);
+ hwReboot();
+#endif
+ } else if (type == I_REGISTRATION_RESPONSE) {
+#if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
+ _coreConfig.nodeRegistered = _msg.getBool();
+ setIndication(INDICATION_GOT_REGISTRATION);
+ CORE_DEBUG(PSTR("MCO:PIM:NODE REG=%d\n"), _coreConfig.nodeRegistered); // node registration
+#endif
+ } else if (type == I_CONFIG) {
// Pick up configuration from controller (currently only metric/imperial) and store it in eeprom if changed
- _cc.isMetric = _msg.data[0] == 0x00 || _msg.data[0] == 'M'; // metric if null terminated or M
- hwWriteConfig(EEPROM_CONTROLLER_CONFIG_ADDRESS, _cc.isMetric);
- }
- else if (type == I_PRESENTATION) {
+ _coreConfig.controllerConfig.isMetric = _msg.data[0] == 0x00 ||
+ _msg.data[0] == 'M'; // metric if null terminated or M
+ hwWriteConfigBlock((void*)&_coreConfig.controllerConfig, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS,
+ sizeof(controllerConfig_t));
+ } else if (type == I_PRESENTATION) {
// Re-send node presentation to controller
- #if defined(MY_RADIO_FEATURE)
- presentNode();
- #endif
- }
- else if (type == I_HEARTBEAT) {
- sendHeartbeat();
- }
- else if (type == I_TIME) {
+ presentNode();
+ } else if (type == I_HEARTBEAT_REQUEST) {
+ (void)sendHeartbeat();
+ } else if (type == I_TIME) {
// Deliver time to callback
- if (receiveTime)
+ if (receiveTime) {
receiveTime(_msg.getULong());
- }
- else if (type == I_CHILDREN) {
- #if defined(MY_REPEATER_FEATURE)
- if (_msg.data[0] == 'C') {
- // Clears child relay data for this node
- setIndication(INDICATION_CLEAR_ROUTING);
- transportClearRoutingTable();
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN, false).set("ok"));
- }
- #endif
- }
- else if (type == I_DEBUG) {
- #if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
- char debug_msg = _msg.data[0];
- if (debug_msg == 'R') { // routing table
- #if defined(MY_REPEATER_FEATURE)
- for (uint8_t cnt = 0; cnt != 255; cnt++) {
- uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS + cnt);
- if (route != BROADCAST_ADDRESS) {
- debug(PSTR("ID: %d via %d\n"), cnt, route);
- uint8_t OutBuf[2] = { cnt,route };
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(OutBuf, 2));
- wait(200);
- }
+ }
+ } else if (type == I_CHILDREN) {
+#if defined(MY_REPEATER_FEATURE)
+ if (_msg.data[0] == 'C') {
+ // Clears child relay data for this node
+ setIndication(INDICATION_CLEAR_ROUTING);
+ transportClearRoutingTable();
+ (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN).set("OK"));
+ }
+#endif
+ } else if (type == I_DEBUG) {
+#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
+ const char debug_msg = _msg.data[0];
+ if (debug_msg == 'R') { // routing table
+#if defined(MY_REPEATER_FEATURE)
+ for (uint16_t cnt = 0; cnt < SIZE_ROUTES; cnt++) {
+ const uint8_t route = transportGetRoute(cnt);
+ if (route != BROADCAST_ADDRESS) {
+ CORE_DEBUG(PSTR("MCO:PIM:ROUTE N=%d,R=%d\n"), cnt, route);
+ uint8_t outBuf[2] = { (uint8_t)cnt,route };
+ (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG).set(outBuf,
+ 2));
+ wait(200);
}
- #endif
- }
- else if (debug_msg == 'V') { // CPU voltage
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(hwCPUVoltage()));
- }
- else if (debug_msg == 'F') { // CPU frequency in 1/10Mhz
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(hwCPUFrequency()));
- }
- else if (debug_msg == 'M') { // free memory
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(hwFreeMem()));
}
- else if (debug_msg == 'E') { // clear MySensors eeprom area and reboot
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set("ok"));
- for (int i = EEPROM_START; i= MY_CORE_MIN_VERSION);
- #endif
- _sendRoute(build(_msgTmp, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REGISTRATION_RESPONSE, false).set(approveRegistration));
- #else
- return false; // processing of this request via controller
- #endif
- #endif
+#if defined(MY_GATEWAY_FEATURE)
+ // registeration requests are exclusively handled by GW/Controller
+#if !defined(MY_REGISTRATION_CONTROLLER)
+ bool approveRegistration;
+
+#if defined(MY_CORE_COMPATIBILITY_CHECK)
+ approveRegistration = (_msg.getByte() >= MY_CORE_MIN_VERSION);
+#else
+ // auto registration if version compatible
+ approveRegistration = true;
+#endif
+
+#if (F_CPU>16000000)
+ // delay for fast GW and slow nodes
+ delay(5);
+#endif
+ (void)_sendRoute(build(_msgTmp, _msg.sender, NODE_SENSOR_ID, C_INTERNAL,
+ I_REGISTRATION_RESPONSE).set(approveRegistration));
+#else
+ return false; // processing of this request via controller
+#endif
+#endif
+ } else {
+ return false;
}
- else return false;
}
return true;
}
-void saveState(uint8_t pos, uint8_t value) {
+void saveState(const uint8_t pos, const uint8_t value)
+{
hwWriteConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos, value);
}
-uint8_t loadState(uint8_t pos) {
+uint8_t loadState(const uint8_t pos)
+{
return hwReadConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos);
}
-void wait(unsigned long ms) {
- unsigned long enter = hwMillis();
- while (hwMillis() - enter < ms) {
+void wait(const uint32_t waitingMS)
+{
+ const uint32_t enteringMS = hwMillis();
+ while (hwMillis() - enteringMS < waitingMS) {
_process();
- #if defined(ARDUINO_ARCH_ESP8266)
- yield();
- #endif
}
}
-bool wait(unsigned long ms, uint8_t cmd, uint8_t msgtype) {
- unsigned long enter = hwMillis();
+bool wait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgType)
+{
+ const uint32_t enteringMS = hwMillis();
// invalidate msg type
- _msg.type = !msgtype;
+ _msg.type = !msgType;
bool expectedResponse = false;
- while ( (hwMillis() - enter < ms) && !expectedResponse ) {
+ while ( (hwMillis() - enteringMS < waitingMS) && !expectedResponse ) {
_process();
- #if defined(ARDUINO_ARCH_ESP8266)
- yield();
- #endif
- expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgtype);
+ expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgType);
}
return expectedResponse;
}
+void doYield(void)
+{
+ hwWatchdogReset();
-int8_t sleep(unsigned long ms) {
- #if defined(MY_OTA_FIRMWARE_FEATURE)
- if (_fwUpdateOngoing) {
- // Do not sleep node while fw update is ongoing
- wait(ms);
- return -1;
+ yield();
+
+#if defined (MY_DEFAULT_TX_LED_PIN) || defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_ERR_LED_PIN)
+ ledsProcess();
+#endif
+}
+
+int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t interrupt1,
+ const uint8_t mode1, const uint8_t interrupt2, const uint8_t mode2)
+{
+ CORE_DEBUG(PSTR("MCO:SLP:MS=%lu,SMS=%d,I1=%d,M1=%d,I2=%d,M2=%d\n"), sleepingMS, smartSleep,
+ interrupt1, mode1, interrupt2, mode2);
+ // OTA FW feature: do not sleep if FW update ongoing
+#if defined(MY_OTA_FIRMWARE_FEATURE)
+ if (isFirmwareUpdateOngoing()) {
+ debug(PSTR("!MCO:SLP:FWUPD\n")); // sleeping not possible, FW update ongoing
+ wait(sleepingMS);
+ return MY_SLEEP_NOT_POSSIBLE;
}
- #endif
- // if repeater, do not sleep
- #if defined(MY_REPEATER_FEATURE)
- wait(ms);
- return -1;
- #else
- #if defined(MY_RADIO_FEATURE)
- transportPowerDown();
- #endif
- setIndication(INDICATION_SLEEP);
- const int8_t res = hwSleep(ms);
- setIndication(INDICATION_WAKEUP);
- return res;
- #endif
+#endif
+ // repeater feature: sleeping not possible
+#if defined(MY_REPEATER_FEATURE)
+ (void)smartSleep;
+ (void)interrupt1;
+ (void)mode1;
+ (void)interrupt2;
+ (void)mode2;
+
+ CORE_DEBUG(PSTR("!MCO:SLP:REP\n")); // sleeping not possible, repeater feature enabled
+ wait(sleepingMS);
+ return MY_SLEEP_NOT_POSSIBLE;
+#else
+ uint32_t sleepingTimeMS = sleepingMS;
+#if defined(MY_SENSOR_NETWORK)
+ // Do not sleep if transport not ready
+ if (!isTransportReady()) {
+ CORE_DEBUG(PSTR("!MCO:SLP:TNR\n")); // sleeping not possible, transport not ready
+ const uint32_t sleepEnterMS = hwMillis();
+ uint32_t sleepDeltaMS = 0;
+ while (!isTransportReady() && (sleepDeltaMS < sleepingTimeMS) &&
+ (sleepDeltaMS < MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS)) {
+ _process();
+ sleepDeltaMS = hwMillis() - sleepEnterMS;
+ }
+ // sleep remainder
+ if (sleepDeltaMS < sleepingTimeMS) {
+ sleepingTimeMS -= sleepDeltaMS; // calculate remaining sleeping time
+ CORE_DEBUG(PSTR("MCO:SLP:MS=%lu\n"), sleepingTimeMS);
+ } else {
+ // no sleeping time left
+ return MY_SLEEP_NOT_POSSIBLE;
+ }
+ }
+#endif
+
+ if (smartSleep) {
+ // notify controller about going to sleep
+ (void)sendHeartbeat();
+ wait(MY_SMART_SLEEP_WAIT_DURATION_MS); // listen for incoming messages
+ }
+
+#if defined(MY_SENSOR_NETWORK)
+ CORE_DEBUG(PSTR("MCO:SLP:TPD\n")); // sleep, power down transport
+ transportPowerDown();
+#endif
+
+#if defined (MY_DEFAULT_TX_LED_PIN) || defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_ERR_LED_PIN)
+ // Wait until leds finish their blinking pattern
+ while (ledsBlinking()) {
+ doYield();
+ }
+#endif
+
+ setIndication(INDICATION_SLEEP);
+
+ int8_t result = MY_SLEEP_NOT_POSSIBLE; // default
+
+ if (interrupt1 != INTERRUPT_NOT_DEFINED && interrupt2 != INTERRUPT_NOT_DEFINED) {
+ // both IRQs
+ result = hwSleep(interrupt1, mode1, interrupt2, mode2, sleepingTimeMS);
+ } else if (interrupt1 != INTERRUPT_NOT_DEFINED && interrupt2 == INTERRUPT_NOT_DEFINED) {
+ // one IRQ
+ result = hwSleep(interrupt1, mode1, sleepingTimeMS);
+ } else if (interrupt1 == INTERRUPT_NOT_DEFINED && interrupt2 == INTERRUPT_NOT_DEFINED) {
+ // no IRQ
+ result = hwSleep(sleepingTimeMS);
+ }
+
+ setIndication(INDICATION_WAKEUP);
+ CORE_DEBUG(PSTR("MCO:SLP:WUP=%d\n"), result); // sleep wake-up
+ return result;
+#endif
}
-int8_t smartSleep(unsigned long ms) {
- int8_t ret = sleep(ms);
- // notifiy controller about wake up
- sendHeartbeat();
- // listen for incoming messages
- wait(MY_SMART_SLEEP_WAIT_DURATION);
- return ret;
+// sleep functions
+int8_t sleep(const uint32_t sleepingMS, const bool smartSleep)
+{
+ return _sleep(sleepingMS, smartSleep);
}
-int8_t sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
- #if defined(MY_OTA_FIRMWARE_FEATURE)
- if (_fwUpdateOngoing) {
- // not supported
- return -2;
- }
- #endif
- #if defined(MY_REPEATER_FEATURE)
- // not supported
- (void)interrupt;
- (void)mode;
- (void)ms;
- return -2;
- #else
- #if defined(MY_RADIO_FEATURE)
- transportPowerDown();
- #endif
- setIndication(INDICATION_SLEEP);
- const int8_t res = hwSleep(interrupt, mode, ms);
- setIndication(INDICATION_WAKEUP);
- return res;
- #endif
+int8_t sleep(const uint8_t interrupt, const uint8_t mode, const uint32_t sleepingMS,
+ const bool smartSleep)
+{
+ return _sleep(sleepingMS, smartSleep, interrupt, mode);
}
-int8_t smartSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
- int8_t ret = sleep(interrupt, mode, ms);
- // notifiy controller about wake up
- sendHeartbeat();
- // listen for incoming messages
- wait(MY_SMART_SLEEP_WAIT_DURATION);
- return ret;
+int8_t sleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
+ const uint8_t mode2, const uint32_t sleepingMS, const bool smartSleep)
+{
+ return _sleep(sleepingMS, smartSleep, interrupt1, mode1, interrupt2, mode2);
}
-int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
- #if defined(MY_OTA_FIRMWARE_FEATURE)
- if (_fwUpdateOngoing) {
- // not supported
- return -2;
- }
- #endif
- #if defined(MY_REPEATER_FEATURE)
- // not supported
- (void)interrupt1;
- (void)mode1;
- (void)interrupt2;
- (void)mode2;
- (void)ms;
- return -2;
- #else
- #if defined(MY_RADIO_FEATURE)
- transportPowerDown();
- #endif
- setIndication(INDICATION_SLEEP);
- const int8_t res = hwSleep(interrupt1, mode1, interrupt2, mode2, ms);
- setIndication(INDICATION_WAKEUP);
- return res;
- #endif
+// deprecated smartSleep() functions
+int8_t smartSleep(const uint32_t sleepingMS)
+{
+ // compatibility
+ return _sleep(sleepingMS, true);
}
-int8_t smartSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
- int8_t ret = sleep(interrupt1, mode1, interrupt2, mode2, ms);
- // notifiy controller about wake up
- sendHeartbeat();
- // listen for incoming messages
- wait(MY_SMART_SLEEP_WAIT_DURATION);
- return ret;
+int8_t smartSleep(const uint8_t interrupt, const uint8_t mode, const uint32_t sleepingMS)
+{
+ // compatibility
+ return _sleep(sleepingMS, true, interrupt, mode);
}
+int8_t smartSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
+ const uint8_t mode2, const uint32_t sleepingMS)
+{
+ // compatibility
+ return _sleep(sleepingMS, true, interrupt1, mode1, interrupt2, mode2);
+}
+
+
+
+void _nodeLock(const char* str)
+{
#ifdef MY_NODE_LOCK_FEATURE
-void nodeLock(const char* str) {
// Make sure EEPROM is updated to locked status
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, 0);
while (1) {
setIndication(INDICATION_ERR_LOCKED);
- debug(PSTR("Node is locked. Ground pin %d and reset to unlock.\n"), MY_NODE_UNLOCK_PIN);
- #if defined(ARDUINO_ARCH_ESP8266)
- yield();
- #endif
- _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID,C_INTERNAL, I_LOCKED, false).set(str));
- #if defined(MY_RADIO_FEATURE)
- transportPowerDown();
- #endif
+ CORE_DEBUG(PSTR("MCO:NLK:NODE LOCKED. TO UNLOCK, GND PIN %d AND RESET\n"), MY_NODE_UNLOCK_PIN);
+ doYield();
+ (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID,C_INTERNAL, I_LOCKED).set(str));
+#if defined(MY_SENSOR_NETWORK)
+ transportPowerDown();
+ CORE_DEBUG(PSTR("MCO:NLK:TPD\n")); // power down transport
+#endif
setIndication(INDICATION_SLEEP);
(void)hwSleep((unsigned long)1000*60*30); // Sleep for 30 min before resending LOCKED message
setIndication(INDICATION_WAKEUP);
}
-}
+#else
+ (void)str;
#endif
+}
+void _checkNodeLock(void)
+{
+#ifdef MY_NODE_LOCK_FEATURE
+ // Check if node has been locked down
+ if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0) {
+ // Node is locked, check if unlock pin is asserted, else hang the node
+ hwPinMode(MY_NODE_UNLOCK_PIN, INPUT_PULLUP);
+ // Make a short delay so we are sure any large external nets are fully pulled
+ unsigned long enter = hwMillis();
+ while (hwMillis() - enter < 2) {}
+ if (hwDigitalRead(MY_NODE_UNLOCK_PIN) == 0) {
+ // Pin is grounded, reset lock counter
+ hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
+ // Disable pullup
+ hwPinMode(MY_NODE_UNLOCK_PIN, INPUT);
+ setIndication(INDICATION_ERR_LOCKED);
+ CORE_DEBUG(PSTR("MCO:BGN:NODE UNLOCKED\n"));
+ } else {
+ // Disable pullup
+ hwPinMode(MY_NODE_UNLOCK_PIN, INPUT);
+ _nodeLock("LDB"); //Locked during boot
+ }
+ } else if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0xFF) {
+ // Reset walue
+ hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
+ }
+#endif
+}
diff --git a/core/MySensorsCore.h b/core/MySensorsCore.h
index cde376566..f0234dcc7 100644
--- a/core/MySensorsCore.h
+++ b/core/MySensorsCore.h
@@ -17,6 +17,52 @@
* version 2 as published by the Free Software Foundation.
*/
+/**
+* @file MySensorsCore.h
+*
+* @defgroup MySensorsCoregrp MySensorsCore
+* @ingroup internals
+* @{
+*
+* MySensorsCore-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE
+* - [!] Exclamation mark is prepended in case of error or warning
+* - SYSTEM:
+* - MCO messages emitted by MySensorsCore
+* - SUB SYSTEMS:
+* - MCO:BGN from @ref _begin()
+* - MCO:REG from @ref _registerNode()
+* - MCO:SND from @ref send()
+* - MCO:PIM from @ref _processInternalMessages()
+* - MCO:NLK from nodeLock()
+*
+* MySensorsCore debug log messages:
+*
+* |E| SYS | SUB | Message | Comment
+* |-|------|-------|-----------------------------------------------|----------------------------------------------------------------------------
+* | | MCO | BGN | INIT %%s,CP=%%s,LIB=%%s | Core initialization, capabilities (CP), library version (VER)
+* | | MCO | BGN | BFR | Callback before()
+* | | MCO | BGN | MTR | MY_TRANSPORT_RELAXED enabled
+* | | MCO | BGN | STP | Callback setup()
+* | | MCO | BGN | INIT OK,TSP=%%d | Core initialised, transport status (TSP), 1=initialised, 0=not initialised, NA=not available
+* | | MCO | BGN | NODE UNLOCKED | Node successfully unlocked (see signing chapter)
+* |!| MCO | BGN | TSP FAIL | Transport initialization failed
+* | | MCO | REG | REQ | Registration request
+* | | MCO | REG | NOT NEEDED | No registration needed (i.e. GW)
+* |!| MCO | SND | NODE NOT REG | Node is not registered, cannot send message
+* | | MCO | PIM | NODE REG=%%d | Registration response received, registration status (REG)
+* | | MCO | PIM | ROUTE N=%%d,R=%%d | Routing table, messages to node (N) are routed via node (R)
+* | | MCO | SLP | MS=%%lu,SMS=%%d,I1=%%d,M1=%%d,I2=%%d,M2=%%d | Sleep node, time (MS), smartSleep (SMS), Int1/M1, Int2/M2
+* | | MCO | SLP | TPD | Sleep node, powerdown transport
+* | | MCO | SLP | WUP=%%d | Node woke-up, reason/IRQ (WUP)
+* |!| MCO | SLP | FWUPD | Sleeping not possible, FW update ongoing
+* |!| MCO | SLP | REP | Sleeping not possible, repeater feature enabled
+* | | MCO | NLK | NODE LOCKED. UNLOCK: GND PIN %%d AND RESET | Node locked during booting, see signing chapter for additional information
+* | | MCO | NLK | TPD | Powerdown transport
+*
+*
+* @brief API declaration for MySensorsCore
+*/
+
#ifndef MySensorsCore_h
#define MySensorsCore_h
@@ -28,54 +74,64 @@
#include
#include
+#define GATEWAY_ADDRESS ((uint8_t)0) //!< Node ID for GW sketch
+#define NODE_SENSOR_ID ((uint8_t)255) //!< Node child is always created/presented when a node is started
+#define MY_CORE_VERSION ((uint8_t)2) //!< core version
+#define MY_CORE_MIN_VERSION ((uint8_t)2) //!< min core version required for compatibility
+
+#define MY_WAKE_UP_BY_TIMER ((int8_t)-1) //!< Sleeping wake up by timer
+#define MY_SLEEP_NOT_POSSIBLE ((int8_t)-2) //!< Sleeping not possible
+#define INTERRUPT_NOT_DEFINED ((uint8_t)255) //!< _sleep() param: no interrupt defined
+#define MODE_NOT_DEFINED ((uint8_t)255) //!< _sleep() param: no mode defined
+#define VALUE_NOT_DEFINED ((uint8_t)255) //!< Value not defined
+
+
#ifdef MY_DEBUG
- #define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__)
+#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug, to be removed (follow-up PR)
+#define CORE_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
#else
- #define debug(x,...)
+#define debug(x,...) //!< debug NULL, to be removed (follow-up PR)
+#define CORE_DEBUG(x,...) //!< debug NULL
#endif
-#define GATEWAY_ADDRESS ((uint8_t)0) //!< Node ID for GW sketch
-#define NODE_SENSOR_ID 0xFF //!< Node child is always created/presented when a node is started
-#define MY_CORE_VERSION ((uint8_t)2) //!< core version
-#define MY_CORE_MIN_VERSION ((uint8_t)2) //!< min core version required for compatibility
-
/**
- * @brief Node configuration
+ * @brief Controller configuration
*
- * This structure stores node-related configurations
+ * This structure stores controller-related configurations
*/
-struct NodeConfig {
- uint8_t nodeId; //!< Current node id
- uint8_t parentNodeId; //!< Where this node sends its messages
- uint8_t distance; //!< This nodes distance to sensor net gateway (number of hops)
-};
+typedef struct {
+ uint8_t isMetric; //!< Flag indicating if metric or imperial measurements are used
+} controllerConfig_t;
/**
- * @brief Controller configuration
- *
- * This structure stores controllerrelated configurations
- */
-struct ControllerConfig {
- uint8_t isMetric; //!< Flag indicating if metric or imperial measurements are used
-};
+* @brief Node core configuration
+*/
+typedef struct {
+ controllerConfig_t controllerConfig; //!< Controller config
+ // 8 bit
+ bool nodeRegistered : 1; //!< Flag node registered
+ bool presentationSent : 1; //!< Flag presentation sent
+ uint8_t reserved : 6; //!< reserved
+} coreConfig_t;
+// **** public functions ********
/**
* Return this nodes id.
*/
-uint8_t getNodeId();
+uint8_t getNodeId(void);
/**
* Return the parent node id.
*/
-uint8_t getParentNodeId();
+uint8_t getParentNodeId(void);
/**
* Sends node information to the gateway.
*/
-void presentNode();
+void presentNode(void);
/**
* Each node must present all attached sensors before any values can be handled correctly by the controller.
@@ -86,17 +142,19 @@ void presentNode();
* @param description A textual description of the sensor.
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
* @param description A textual description of the sensor.
+* @return true Returns true if message reached the first stop on its way to destination.
*/
-void present(uint8_t sensorId, uint8_t sensorType, const char *description="", bool ack=false);
+bool present(const uint8_t sensorId, const uint8_t sensorType, const char *description="",
+ const bool ack = false);
/**
* Sends sketch meta information to the gateway. Not mandatory but a nice thing to do.
* @param name String containing a short Sketch name or NULL if not applicable
* @param version String containing a short Sketch version or NULL if not applicable
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
- *
+ * @return true Returns true if message reached the first stop on its way to destination.
*/
-void sendSketchInfo(const char *name, const char *version, bool ack=false);
+bool sendSketchInfo(const char *name, const char *version, const bool ack = false);
/**
* Sends a message to gateway or one of the other nodes in the radio network
@@ -105,22 +163,24 @@ void sendSketchInfo(const char *name, const char *version, bool ack=false);
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
* @return true Returns true if message reached the first stop on its way to destination.
*/
-bool send(MyMessage &msg, bool ack=false);
+bool send(MyMessage &msg, const bool ack = false);
/**
* Send this nodes battery level to gateway.
* @param level Level between 0-100(%)
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
- *
+ * @return true Returns true if message reached the first stop on its way to destination.
*/
-void sendBatteryLevel(uint8_t level, bool ack=false);
+bool sendBatteryLevel(const uint8_t level, const bool ack = false);
/**
* Send a heartbeat message (I'm alive!) to the gateway/controller.
* The payload will be an incremental 16 bit integer value starting at 1 when sensor is powered on.
+ * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
+ * @return true Returns true if message reached the first stop on its way to destination.
*/
-void sendHeartbeat(void);
+bool sendHeartbeat(const bool ack = false);
/**
* Requests a value from gateway or some other sensor in the radio network.
@@ -129,21 +189,22 @@ void sendHeartbeat(void);
* @param childSensorId The unique child id for the different sensors connected to this Arduino. 0-254.
* @param variableType The variableType to fetch
* @param destination The nodeId of other node in radio network. Default is gateway
+* @return true Returns true if message reached the first stop on its way to destination.
*/
-void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS);
+bool request(const uint8_t childSensorId, const uint8_t variableType,
+ const uint8_t destination = GATEWAY_ADDRESS);
/**
* Requests time from controller. Answer will be delivered to receiveTime function in sketch.
- *
+ * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
+ * @return true Returns true if message reached the first stop on its way to destination.
*/
-void requestTime();
-
-
+bool requestTime(const bool ack = false);
/**
* Returns the most recent node configuration received from controller
*/
-ControllerConfig getConfig();
+controllerConfig_t getControllerConfig(void);
/**
* Save a state (in local EEPROM). Good for actuators to "remember" state between
@@ -155,7 +216,7 @@ ControllerConfig getConfig();
* @param pos The position to store value in (0-255)
* @param value to store in position
*/
-void saveState(uint8_t pos, uint8_t value);
+void saveState(const uint8_t pos, const uint8_t value);
/**
* Load a state (from local EEPROM).
@@ -163,36 +224,43 @@ void saveState(uint8_t pos, uint8_t value);
* @param pos The position to fetch value from (0-255)
* @return Value to store in position
*/
-uint8_t loadState(uint8_t pos);
+uint8_t loadState(const uint8_t pos);
/**
* Wait for a specified amount of time to pass. Keeps process()ing.
* This does not power-down the radio nor the Arduino.
* Because this calls process() in a loop, it is a good way to wait
* in your loop() on a repeater node or sensor that listens to messages.
- * @param ms Number of milliseconds to sleep.
+ * @param waitingMS Number of milliseconds to wait.
*/
-void wait(unsigned long ms);
+void wait(const uint32_t waitingMS);
/**
* Wait for a specified amount of time to pass or until specified message received. Keeps process()ing.
* This does not power-down the radio nor the Arduino.
* Because this calls process() in a loop, it is a good way to wait
* in your loop() on a repeater node or sensor that listens to messages.
- * @param ms Number of milliseconds to sleep.
+ * @param waitingMS Number of milliseconds to wait.
* @param cmd Command of incoming message.
* @param msgtype Message type.
* @return True if specified message received
*/
-bool wait(unsigned long ms, uint8_t cmd, uint8_t msgtype);
+bool wait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgtype);
+
+/**
+ * Function to allow scheduler to do some work.
+ * @remark Internally it will call yield, kick the watchdog and update led states.
+ */
+void doYield(void);
+
/**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer.
- * @param ms Number of milliseconds to sleep.
- * @return -1 if timer woke it up, -2 if not possible (e.g. ongoing FW update)
+ * @param sleepingMS Number of milliseconds to sleep.
+ * @param smartSleep Set True if sending heartbeat and process incoming messages before going to sleep.
+ * @return @ref MY_WAKE_UP_BY_TIMER if timer woke it up, @ref MY_SLEEP_NOT_POSSIBLE if not possible (e.g. ongoing FW update)
*/
-int8_t sleep(unsigned long ms);
-int8_t smartSleep(unsigned long ms);
+int8_t sleep(const uint32_t sleepingMS, const bool smartSleep = false);
/**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change.
@@ -200,11 +268,12 @@ int8_t smartSleep(unsigned long ms);
* is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3
* @param interrupt Interrupt that should trigger the wakeup
* @param mode RISING, FALLING, CHANGE
- * @param ms Number of milliseconds to sleep or 0 to sleep forever
- * @return Interrupt number wake up was triggered by pin change, -1 if timer woke it up, -2 if not possible (e.g. ongoing FW update)
+ * @param sleepingMS Number of milliseconds to sleep or 0 to sleep forever
+ * @param smartSleep Set True if sending heartbeat and process incoming messages before going to sleep
+ * @return Interrupt number if wake up was triggered by pin change, @ref MY_WAKE_UP_BY_TIMER if wake up was triggered by timer, @ref MY_SLEEP_NOT_POSSIBLE if sleep was not possible (e.g. ongoing FW update)
*/
-int8_t sleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0);
-int8_t smartSleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0);
+int8_t sleep(const uint8_t interrupt, const uint8_t mode, const uint32_t sleepingMS = 0,
+ const bool smartSleep = false);
/**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change for two separate interrupts.
@@ -214,71 +283,161 @@ int8_t smartSleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0);
* @param mode1 Mode for first interrupt (RISING, FALLING, CHANGE)
* @param interrupt2 Second interrupt that should trigger the wakeup
* @param mode2 Mode for second interrupt (RISING, FALLING, CHANGE)
- * @param ms Number of milliseconds to sleep or 0 to sleep forever
- * @return Interrupt number wake up was triggered by pin change, -1 if timer woke it up, -2 if not possible (e.g. ongoing FW update)
+ * @param sleepingMS Number of milliseconds to sleep or 0 to sleep forever
+ * @param smartSleep Set True if sending heartbeat and process incoming messages before going to sleep.
+ * @return Interrupt number if wake up was triggered by pin change, @ref MY_WAKE_UP_BY_TIMER if wake up was triggered by timer, @ref MY_SLEEP_NOT_POSSIBLE if sleep was not possible (e.g. ongoing FW update)
*/
-int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0);
-int8_t smartSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0);
+int8_t sleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
+ const uint8_t mode2, const uint32_t sleepingMS = 0, const bool smartSleep = false);
+
+/**
+* \deprecated Use sleep(ms, true) instead
+* Same as sleep(), send heartbeat and process incoming messages before going to sleep.
+* Specify the time to wait for incoming messages by defining MY_SMART_SLEEP_WAIT_DURATION to a time (ms).
+* @param sleepingMS Number of milliseconds to sleep.
+* @return @ref MY_WAKE_UP_BY_TIMER if timer woke it up, @ref MY_SLEEP_NOT_POSSIBLE if not possible (e.g. ongoing FW update)
+*/
+int8_t smartSleep(const uint32_t sleepingMS);
+
+/**
+* \deprecated Use sleep(interrupt, mode, ms, true) instead
+* Same as sleep(), send heartbeat and process incoming messages before going to sleep.
+* Specify the time to wait for incoming messages by defining MY_SMART_SLEEP_WAIT_DURATION to a time (ms).
+* @param interrupt Interrupt that should trigger the wakeup
+* @param mode RISING, FALLING, CHANGE
+* @param sleepingMS Number of milliseconds to sleep or 0 to sleep forever
+* @return Interrupt number if wake up was triggered by pin change, @ref MY_WAKE_UP_BY_TIMER if wake up was triggered by timer, @ref MY_SLEEP_NOT_POSSIBLE if sleep was not possible (e.g. ongoing FW update)
+*/
+int8_t smartSleep(const uint8_t interrupt, const uint8_t mode, const uint32_t sleepingMS = 0);
+
+/**
+* \deprecated Use sleep(interrupt1, mode1, interrupt2, mode2, ms, true) instead
+* Same as sleep(), send heartbeat and process incoming messages before going to sleep.
+* Specify the time to wait for incoming messages by defining MY_SMART_SLEEP_WAIT_DURATION to a time (ms).
+* @param interrupt1 First interrupt that should trigger the wakeup
+* @param mode1 Mode for first interrupt (RISING, FALLING, CHANGE)
+* @param interrupt2 Second interrupt that should trigger the wakeup
+* @param mode2 Mode for second interrupt (RISING, FALLING, CHANGE)
+* @param sleepingMS Number of milliseconds to sleep or 0 to sleep forever
+* @return Interrupt number if wake up was triggered by pin change, @ref MY_WAKE_UP_BY_TIMER if wake up was triggered by timer, @ref MY_SLEEP_NOT_POSSIBLE if sleep was not possible (e.g. ongoing FW update)
+*/
+int8_t smartSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
+ const uint8_t mode2, const uint32_t sleepingMS = 0);
+
+/**
+* Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change for two separate interrupts.
+* See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin
+* is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3
+* @param sleepingMS Number of milliseconds to sleep or 0 to sleep forever
+* @param interrupt1 (optional) First interrupt that should trigger the wakeup
+* @param mode1 (optional) Mode for first interrupt (RISING, FALLING, CHANGE)
+* @param interrupt2 (optional) Second interrupt that should trigger the wakeup
+* @param mode2 (optional) Mode for second interrupt (RISING, FALLING, CHANGE)
+* @param smartSleep (optional) Set True if sending heartbeat and process incoming messages before going to sleep.
+* @return Interrupt number if wake up was triggered by pin change, @ref MY_WAKE_UP_BY_TIMER if wake up was triggered by timer, @ref MY_SLEEP_NOT_POSSIBLE if sleep was not possible (e.g. ongoing FW update)
+*/
+int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep = false,
+ const uint8_t interrupt1 = INTERRUPT_NOT_DEFINED, const uint8_t mode1 = MODE_NOT_DEFINED,
+ const uint8_t interrupt2 = INTERRUPT_NOT_DEFINED, const uint8_t mode2 = MODE_NOT_DEFINED);
+
-#ifdef MY_NODE_LOCK_FEATURE
/**
* @ingroup MyLockgrp
- * @ingroup internals
* @brief Lock a node and transmit provided message with 30m intervals
*
* This function is called if suspicious activity has exceeded the threshold (see
- * @ref ATTACK_COUNTER_MAX). Unlocking with a normal Arduino bootloader require erasing the EEPROM
- * while unlocking with a custom bootloader require holding @ref UNLOCK_PIN low during power on/reset.
+ * @ref MY_NODE_LOCK_COUNTER_MAX). Unlocking with a normal Arduino bootloader require erasing the EEPROM
+ * while unlocking with a custom bootloader require holding @ref MY_NODE_UNLOCK_PIN low during power on/reset.
*
* @param str The string to transmit.
*/
-void nodeLock(const char* str);
-#endif
+void _nodeLock(const char* str);
-/****** PRIVATE ********/
+/**
+ * @brief Check node lock status and prevent node execution if locked.
+ */
+void _checkNodeLock(void);
-void _begin();
+// **** private functions ********
+/**
+* @brief Node initialisation
+*/
+void _begin(void);
+/**
+* @brief Main framework process
+*/
void _process(void);
-
-bool _processInternalMessages();
-
-void _infiniteLoop();
-
-void _registerNode();
-
+/**
+* @brief Processes internal messages
+* @return True if received message requires further processing
+*/
+bool _processInternalMessages(void);
+/**
+* @brief Puts node to a infinite loop if unrecoverable situation detected
+*/
+void _infiniteLoop(void);
+/**
+* @brief Handles registration request
+*/
+void _registerNode(void);
+/**
+* @brief Sends message according to routing table
+* @param message
+* @return true Returns true if message reached the first stop on its way to destination.
+*/
bool _sendRoute(MyMessage &message);
-extern NodeConfig _nc;
-extern MyMessage _msg; // Buffer for incoming messages.
-extern MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others).
-#ifdef MY_DEBUG
- extern char _convBuf[MAX_PAYLOAD*2+1];
-#endif
+/**
+* @brief Callback for incoming messages
+* @param message
+*/
void receive(const MyMessage &message) __attribute__((weak));
+/**
+* @brief Callback for incoming time messages
+*/
void receiveTime(unsigned long) __attribute__((weak));
-void presentation() __attribute__((weak));
-void before() __attribute__((weak));
-void setup() __attribute__((weak));
-void loop() __attribute__((weak));
+/**
+* @brief Node presenation
+*/
+void presentation(void) __attribute__((weak));
+/**
+* @brief Called before node initialises
+*/
+void before(void) __attribute__((weak));
+/**
+* @brief Called before any hwInitialization is done
+*/
+void preHwInit(void) __attribute__((weak));
+/**
+* @brief Called after node initialises but before main loop
+*/
+void setup(void) __attribute__((weak));
+/**
+* @brief Main loop
+*/
+void loop(void) __attribute__((weak));
// Inline function and macros
-static inline MyMessage& build(MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) {
- msg.sender = sender;
+static inline MyMessage& build(MyMessage &msg, const uint8_t destination, const uint8_t sensor,
+ const uint8_t command, const uint8_t type, const bool ack = false)
+{
+ msg.sender = getNodeId();
msg.destination = destination;
msg.sensor = sensor;
msg.type = type;
mSetCommand(msg,command);
- mSetRequestAck(msg,enableAck);
+ mSetRequestAck(msg,ack);
mSetAck(msg,false);
return msg;
}
-static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) {
+static inline MyMessage& buildGw(MyMessage &msg, const uint8_t type)
+{
msg.sender = GATEWAY_ADDRESS;
msg.destination = GATEWAY_ADDRESS;
- msg.sensor = 255;
+ msg.sensor = NODE_SENSOR_ID;
msg.type = type;
mSetCommand(msg, C_INTERNAL);
mSetRequestAck(msg, false);
@@ -288,3 +447,5 @@ static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) {
#endif
+
+/** @}*/
diff --git a/core/MySigning.cpp b/core/MySigning.cpp
index a1081a435..35c2ff550 100644
--- a/core/MySigning.cpp
+++ b/core/MySigning.cpp
@@ -33,6 +33,12 @@
#if defined(MY_SIGNING_REQUEST_SIGNATURES) && (!defined(MY_SIGNING_ATSHA204) && !defined(MY_SIGNING_SOFT))
#error You have to pick either MY_SIGNING_ATSHA204 or MY_SIGNING_SOFT in order to require signatures!
#endif
+#if defined(MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL) && !defined(MY_SIGNING_REQUEST_SIGNATURES)
+#error You have to require signatures if you want to require signatures from all (also enable MY_SIGNING_REQUEST_SIGNATURES in your gateway)
+#endif
+#if defined(MY_SIGNING_SOFT) && defined(MY_SIGNING_ATSHA204)
+#error You have to pick one and only one signing backend
+#endif
#ifdef MY_SIGNING_FEATURE
uint8_t _doSign[32]; // Bitfield indicating which sensors require signed communication
uint8_t _doWhitelist[32]; // Bitfield indicating which sensors require serial salted signatures
@@ -44,23 +50,17 @@ static uint8_t nof_nonce_requests = 0;
static uint8_t nof_failed_verifications = 0;
#endif
-// Status when waiting for signing nonce in signerProcessInternal
+// Status when waiting for signing nonce in signerSignMsg
enum { SIGN_WAITING_FOR_NONCE = 0, SIGN_OK = 1 };
-// Macros for manipulating signing requirement table
+// Macros for manipulating signing requirement tables
#define DO_SIGN(node) (~_doSign[node>>3]&(1<>3]&=~(1<>3]|=(1<>3]&(1<>3]&=~(1<>3]|=(1<= MY_NODE_LOCK_COUNTER_MAX) {
- nodeLock("TMNR"); //Too many nonces requested
- }
-#endif
-#if defined(MY_SIGNING_SOFT)
- if (signerAtsha204SoftGetNonce(msg)) {
-#endif
-#if defined(MY_SIGNING_ATSHA204)
- if (signerAtsha204GetNonce(msg)) {
-#endif
- if (!_sendRoute(build(msg, _nc.nodeId, msg.sender, NODE_SENSOR_ID,
- C_INTERNAL, I_NONCE_RESPONSE, false))) {
- SIGN_DEBUG(PSTR("Failed to transmit nonce!\n"));
- } else {
- SIGN_DEBUG(PSTR("Transmitted nonce\n"));
- }
- } else {
- SIGN_DEBUG(PSTR("Failed to generate nonce!\n"));
- }
- return true; // No need to further process I_NONCE_REQUEST
- } else if (msg.type == I_SIGNING_PRESENTATION) {
- if (msg.data[0] != SIGNING_PRESENTATION_VERSION_1) {
- SIGN_DEBUG(PSTR("Unsupported signing presentation version (%d)!\n"), msg.data[0]);
- return true; // Just drop this presentation message
- }
-
- // We only handle version 1 here...
- if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) {
- // We received an indicator that the sender require us to sign all messages we send to it
- SIGN_DEBUG(PSTR("Mark node %d as one that require signed messages\n"), msg.sender);
- SET_SIGN(msg.sender);
- } else {
- // We received an indicator that the sender does not require us to sign all messages we send to it
- SIGN_DEBUG(PSTR("Mark node %d as one that do not require signed messages\n"), msg.sender);
- CLEAR_SIGN(msg.sender);
- }
-
- if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) {
- // We received an indicator that the sender require us to salt signatures with serial
- SIGN_DEBUG(PSTR("Mark node %d as one that require whitelisting\n"), msg.sender);
- SET_WHITELIST(msg.sender);
- } else {
- // We received an indicator that the sender does not require us to sign all messages we send to it
- SIGN_DEBUG(PSTR("Mark node %d as one that do not require whitelisting\n"), msg.sender);
- CLEAR_WHITELIST(msg.sender);
- }
-
- // Save updated tables
- hwWriteConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS,
- sizeof(_doSign));
- hwWriteConfigBlock((void*)_doWhitelist, (void*)EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS,
- sizeof(_doWhitelist));
-
- // Inform sender about our preference if we are a gateway, but only require signing if the sender
- // required signing
- // We do not want a gateway to require signing from all nodes in a network just because it wants one node
- // to sign it's messages
-#if defined(MY_GATEWAY_FEATURE)
- prepareSigningPresentation(msg, sender);
-#if defined(MY_SIGNING_REQUEST_SIGNATURES)
- if (DO_SIGN(sender)) {
- msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
- }
-#endif
-#if defined(MY_SIGNING_NODE_WHITELISTING)
- if (DO_WHITELIST(sender)) {
- msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING;
- }
-#endif
- if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) {
- SIGN_DEBUG(PSTR("Informing node %d that we require signatures\n"), sender);
- } else {
- SIGN_DEBUG(PSTR("Informing node %d that we do not require signatures\n"), sender);
- }
- if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) {
- SIGN_DEBUG(PSTR("Informing node %d that we require whitelisting\n"), sender);
- } else {
- SIGN_DEBUG(PSTR("Informing node %d that we do not require whitelisting\n"), sender);
- }
- if (!_sendRoute(msg)) {
- SIGN_DEBUG(PSTR("Failed to transmit signing presentation!"));
- }
-#endif // MY_GATEWAY_FEATURE
- return true; // No need to further process I_SIGNING_PRESENTATION
- } else if (msg.type == I_NONCE_RESPONSE) {
- // Proceed with signing if nonce has been received
- SIGN_DEBUG(PSTR("Nonce received from %d. Proceeding with signing...\n"), sender);
- if (sender != _msgSign.destination) {
- SIGN_DEBUG(PSTR("Nonce did not come from the destination (%d) of the message to be signed! "
- "It came from %d.\n"), _msgSign.destination, sender);
- SIGN_DEBUG(PSTR("Silently discarding this nonce\n"));
- return true; // No need to further process I_NONCE_RESPONSE
- }
-#if defined(MY_SIGNING_SOFT)
- signerAtsha204SoftPutNonce(msg);
-#endif
-#if defined(MY_SIGNING_ATSHA204)
- signerAtsha204PutNonce(msg);
-#endif
-#if defined(MY_SIGNING_SOFT)
- if (!signerAtsha204SoftSignMsg(_msgSign)) {
-#endif
-#if defined(MY_SIGNING_ATSHA204)
- if (!signerAtsha204SignMsg(_msgSign)) {
-#endif
- SIGN_DEBUG(PSTR("Failed to sign message!\n"));
- } else {
- SIGN_DEBUG(PSTR("Message signed\n"));
- _signingNonceStatus = SIGN_OK; // _msgSign now contains the signed message pending transmission
- }
- return true; // No need to further process I_NONCE_RESPONSE
- }
-#endif // MY_SIGNING_FEATURE
+bool signerProcessInternal(MyMessage &msg)
+{
+ bool ret;
+ switch (msg.type) {
+ case I_SIGNING_PRESENTATION:
+ ret = signerInternalProcessPresentation(msg);
+ break;
+ case I_NONCE_REQUEST:
+ ret = signerInternalProcessNonceRequest(msg);
+ break;
+ case I_NONCE_RESPONSE:
+ ret = signerInternalProcessNonceResponse(msg);
+ break;
+ default:
+ ret = false; // Let the transport process this message further as it is not related to signing
+ break;
}
- return false;
+ return ret;
}
-bool signerCheckTimer(void) {
-#if defined(MY_SIGNING_SOFT)
- return signerAtsha204SoftCheckTimer();
-#elif defined(MY_SIGNING_ATSHA204)
- return signerAtsha204CheckTimer();
-#else
- return true; // Without a configured backend, we always give "positive" results
-#endif
+bool signerCheckTimer(void)
+{
+ return signerBackendCheckTimer();
}
-bool signerSignMsg(MyMessage &msg) {
+bool signerSignMsg(MyMessage &msg)
+{
+ bool ret;
#if defined(MY_SIGNING_FEATURE)
// If destination is known to require signed messages and we are the sender,
- // sign this message unless it is a handshake message
- if (DO_SIGN(msg.destination) && msg.sender == _nc.nodeId) {
+ // sign this message unless it is identified as an exception
+ if (DO_SIGN(msg.destination) && msg.sender == getNodeId()) {
if (skipSign(msg)) {
- return true;
+ ret = true;
} else {
// Send nonce-request
_signingNonceStatus=SIGN_WAITING_FOR_NONCE;
- if (!_sendRoute(build(_msgSign, _nc.nodeId, msg.destination, msg.sensor,
- C_INTERNAL, I_NONCE_REQUEST, false).set(""))) {
+ if (!_sendRoute(build(_msgSign, msg.destination, msg.sensor, C_INTERNAL,
+ I_NONCE_REQUEST).set(""))) {
SIGN_DEBUG(PSTR("Failed to transmit nonce request!\n"));
- return false;
- }
- SIGN_DEBUG(PSTR("Nonce requested from %d. Waiting...\n"), msg.destination);
- // We have to wait for the nonce to arrive before we can sign our original message
- // Other messages could come in-between. We trust _process() takes care of them
- unsigned long enter = hwMillis();
- _msgSign = msg; // Copy the message to sign since message buffer might be touched in _process()
- while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS && _signingNonceStatus==SIGN_WAITING_FOR_NONCE) {
- _process();
- }
- if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) {
- SIGN_DEBUG(PSTR("Timeout waiting for nonce!\n"));
- return false;
- }
- if (_signingNonceStatus == SIGN_OK) {
- // process() received a nonce and signerProcessInternal successfully signed the message
- msg = _msgSign; // Write the signed message back
- SIGN_DEBUG(PSTR("Message to send has been signed\n"));
+ ret = false;
} else {
- SIGN_DEBUG(PSTR("Message to send could not be signed!\n"));
- return false;
+ SIGN_DEBUG(PSTR("Nonce requested from %d. Waiting...\n"), msg.destination);
+ // We have to wait for the nonce to arrive before we can sign our original message
+ // Other messages could come in-between. We trust _process() takes care of them
+ unsigned long enter = hwMillis();
+ _msgSign = msg; // Copy the message to sign since message buffer might be touched in _process()
+ while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS &&
+ _signingNonceStatus==SIGN_WAITING_FOR_NONCE) {
+ _process();
+ }
+ if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) {
+ SIGN_DEBUG(PSTR("Timeout waiting for nonce!\n"));
+ ret = false;
+ } else {
+ if (_signingNonceStatus == SIGN_OK) {
+ // process() received a nonce and signerProcessInternal successfully signed the message
+ msg = _msgSign; // Write the signed message back
+ SIGN_DEBUG(PSTR("Message to send has been signed\n"));
+ ret = true;
+ // After this point, only the 'last' member of the message structure is allowed to be altered if the
+ // message has been signed, or signature will become invalid and the message rejected by the receiver
+ } else {
+ SIGN_DEBUG(PSTR("Message to send could not be signed!\n"));
+ ret = false;
+ }
+ }
}
- // After this point, only the 'last' member of the message structure is allowed to be altered if the
- // message has been signed, or signature will become invalid and the message rejected by the receiver
}
- } else if (_nc.nodeId == msg.sender) {
+ } else if (getNodeId() == msg.sender) {
mSetSigned(msg, 0); // Message is not supposed to be signed, make sure it is marked unsigned
+ SIGN_DEBUG(PSTR("Will not sign message for destination %d as it does not require it\n"),
+ msg.destination);
+ ret = true;
+ } else {
+ SIGN_DEBUG(PSTR("Will not sign message since it was from %d and we are %d\n"), msg.sender,
+ getNodeId());
+ ret = true;
}
#else
(void)msg;
+ ret = true;
#endif // MY_SIGNING_FEATURE
- return true;
+ return ret;
}
-bool signerVerifyMsg(MyMessage &msg) {
+bool signerVerifyMsg(MyMessage &msg)
+{
bool verificationResult = true;
// Before processing message, reject unsigned messages if signing is required and check signature
// (if it is signed and addressed to us)
// Note that we do not care at all about any signature found if we do not require signing
#if defined(MY_SIGNING_FEATURE) && defined(MY_SIGNING_REQUEST_SIGNATURES)
- // If we are a node, or we are a gateway and the sender require signatures
+ // If we are a node, or we are a gateway and the sender require signatures (or just a strict gw)
// and we are the destination...
- if ((!MY_IS_GATEWAY || DO_SIGN(msg.sender)) && msg.destination == _nc.nodeId) {
+#if defined(MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL)
+ if (msg.destination == getNodeId()) {
+#else
+ if ((!MY_IS_GATEWAY || DO_SIGN(msg.sender)) && msg.destination == getNodeId()) {
+#endif
// Internal messages of certain types are not verified
if (skipSign(msg)) {
verificationResult = true;
- }
- else if (!mGetSigned(msg)) {
+ } else if (!mGetSigned(msg)) {
// Got unsigned message that should have been signed
SIGN_DEBUG(PSTR("Message is not signed, but it should have been!\n"));
verificationResult = false;
} else {
-#if defined(MY_SIGNING_SOFT)
- verificationResult = signerAtsha204SoftVerifyMsg(msg);
-#endif
-#if defined(MY_SIGNING_ATSHA204)
- verificationResult = signerAtsha204VerifyMsg(msg);
-#endif
- if (!verificationResult) {
+ if (!signerBackendVerifyMsg(msg)) {
SIGN_DEBUG(PSTR("Signature verification failed!\n"));
+ verificationResult = false;
}
#if defined(MY_NODE_LOCK_FEATURE)
if (verificationResult) {
@@ -386,9 +254,10 @@ bool signerVerifyMsg(MyMessage &msg) {
nof_failed_verifications = 0;
} else {
nof_failed_verifications++;
- SIGN_DEBUG(PSTR("Failed verification attempts left until lockdown: %d\n"), MY_NODE_LOCK_COUNTER_MAX-nof_failed_verifications);
+ SIGN_DEBUG(PSTR("Failed verification attempts left until lockdown: %d\n"),
+ MY_NODE_LOCK_COUNTER_MAX-nof_failed_verifications);
if (nof_failed_verifications >= MY_NODE_LOCK_COUNTER_MAX) {
- nodeLock("TMFV"); //Too many failed verifications
+ _nodeLock("TMFV"); //Too many failed verifications
}
}
#endif
@@ -404,23 +273,27 @@ bool signerVerifyMsg(MyMessage &msg) {
static uint8_t sha256_hash[32];
Sha256Class _soft_sha256;
-void signerSha256Init(void) {
+void signerSha256Init(void)
+{
memset(sha256_hash, 0, 32);
_soft_sha256.init();
}
-void signerSha256Update(const uint8_t* data, size_t sz) {
+void signerSha256Update(const uint8_t* data, size_t sz)
+{
for (size_t i = 0; i < sz; i++) {
_soft_sha256.write(data[i]);
}
}
-uint8_t* signerSha256Final(void) {
+uint8_t* signerSha256Final(void)
+{
memcpy(sha256_hash, _soft_sha256.result(), 32);
return sha256_hash;
}
-int signerMemcmp(const void* a, const void* b, size_t sz) {
+int signerMemcmp(const void* a, const void* b, size_t sz)
+{
int retVal;
size_t i;
int done = 0;
@@ -448,3 +321,190 @@ int signerMemcmp(const void* a, const void* b, size_t sz) {
}
return retVal;
}
+
+#if defined(MY_SIGNING_FEATURE)
+// Helper function to centralize signing/verification exceptions
+static bool skipSign(MyMessage &msg)
+{
+ bool ret;
+ if (mGetAck(msg)) {
+ SIGN_DEBUG(PSTR("Skipping security for ACK on command %d type %d\n"), mGetCommand(msg), msg.type);
+ ret = true;
+ } else if (mGetCommand(msg) == C_INTERNAL &&
+ (msg.type == I_NONCE_REQUEST || msg.type == I_NONCE_RESPONSE ||
+ msg.type == I_SIGNING_PRESENTATION ||
+ msg.type == I_ID_REQUEST || msg.type == I_ID_RESPONSE ||
+ msg.type == I_FIND_PARENT_REQUEST || msg.type == I_FIND_PARENT_RESPONSE ||
+ msg.type == I_HEARTBEAT_REQUEST || msg.type == I_HEARTBEAT_RESPONSE ||
+ msg.type == I_PING || msg.type == I_PONG ||
+ msg.type == I_REGISTRATION_REQUEST )) {
+ SIGN_DEBUG(PSTR("Skipping security for command %d type %d\n"), mGetCommand(msg), msg.type);
+ ret = true;
+ } else if (mGetCommand(msg) == C_STREAM &&
+ (msg.type == ST_FIRMWARE_REQUEST || msg.type == ST_FIRMWARE_RESPONSE ||
+ msg.type == ST_SOUND || msg.type == ST_IMAGE)) {
+ SIGN_DEBUG(PSTR("Skipping security for command %d type %d\n"), mGetCommand(msg), msg.type);
+ ret = true;
+ } else {
+ ret = false;
+ }
+ return ret;
+}
+#endif
+
+// Helper to prepare a signing presentation message
+static void prepareSigningPresentation(MyMessage &msg, uint8_t destination)
+{
+ // Only supports version 1 for now
+ (void)build(msg, destination, NODE_SENSOR_ID, C_INTERNAL, I_SIGNING_PRESENTATION).set("");
+ mSetLength(msg, 2);
+ mSetPayloadType(msg, P_CUSTOM); // displayed as hex
+ msg.data[0] = SIGNING_PRESENTATION_VERSION_1;
+ msg.data[1] = 0;
+}
+
+// Helper to process presentation mesages
+static bool signerInternalProcessPresentation(MyMessage &msg)
+{
+#if defined(MY_SIGNING_FEATURE)
+ if (msg.data[0] != SIGNING_PRESENTATION_VERSION_1) {
+ SIGN_DEBUG(PSTR("Unsupported signing presentation version (%d)!\n"), msg.data[0]);
+ return true; // Just drop this presentation message
+ }
+ // We only handle version 1 here...
+ if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) {
+ // We received an indicator that the sender require us to sign all messages we send to it
+ SIGN_DEBUG(PSTR("Mark node %d as one that require signed messages\n"), msg.sender);
+ SET_SIGN(msg.sender);
+ } else {
+ // We received an indicator that the sender does not require us to sign all messages we send to it
+ SIGN_DEBUG(PSTR("Mark node %d as one that do not require signed messages\n"), msg.sender);
+ CLEAR_SIGN(msg.sender);
+ }
+ if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) {
+ // We received an indicator that the sender require us to salt signatures with serial
+ SIGN_DEBUG(PSTR("Mark node %d as one that require whitelisting\n"), msg.sender);
+ SET_WHITELIST(msg.sender);
+ } else {
+ // We received an indicator that the sender does not require us to sign all messages we send to it
+ SIGN_DEBUG(PSTR("Mark node %d as one that do not require whitelisting\n"), msg.sender);
+ CLEAR_WHITELIST(msg.sender);
+ }
+
+ // Save updated tables
+ hwWriteConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS,
+ sizeof(_doSign));
+ hwWriteConfigBlock((void*)_doWhitelist, (void*)EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS,
+ sizeof(_doWhitelist));
+
+ // Inform sender about our preference if we are a gateway, but only require signing if the sender
+ // required signing unless we explicitly configure it to
+#if defined(MY_GATEWAY_FEATURE)
+ prepareSigningPresentation(msg, msg.sender);
+#if defined(MY_SIGNING_REQUEST_SIGNATURES)
+#if defined(MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL)
+ msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
+#else
+ if (DO_SIGN(msg.sender)) {
+ msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
+ }
+#endif
+#endif // MY_SIGNING_REQUEST_SIGNATURES
+#if defined(MY_SIGNING_NODE_WHITELISTING)
+#if defined(MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL)
+ msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING;
+#else
+ if (DO_WHITELIST(msg.sender)) {
+ msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING;
+ }
+#endif
+#endif // MY_SIGNING_NODE_WHITELISTING
+ if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) {
+ SIGN_DEBUG(PSTR("Informing node %d that we require signatures\n"), msg.sender);
+ } else {
+ SIGN_DEBUG(PSTR("Informing node %d that we do not require signatures\n"), msg.sender);
+ }
+ if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) {
+ SIGN_DEBUG(PSTR("Informing node %d that we require whitelisting\n"), msg.sender);
+ } else {
+ SIGN_DEBUG(PSTR("Informing node %d that we do not require whitelisting\n"), msg.sender);
+ }
+ if (!_sendRoute(msg)) {
+ SIGN_DEBUG(PSTR("Failed to transmit signing presentation!\n"));
+ }
+#endif // MY_GATEWAY_FEATURE
+#else // not MY_SIGNING_FEATURE
+#if defined(MY_GATEWAY_FEATURE)
+ // If we act as gateway and do not have the signing feature and receive a signing request we still
+ // need to do make sure the requester does not believe the gateway still require signatures
+ prepareSigningPresentation(msg, msg.sender);
+ SIGN_DEBUG(
+ PSTR("Informing node %d that we do not require signatures because we do not support it\n"),
+ msg.sender);
+ if (!_sendRoute(msg)) {
+ SIGN_DEBUG(PSTR("Failed to transmit signing presentation!\n"));
+ }
+#else // not MY_GATEWAY_FEATURE
+ // If we act as a node and do not have the signing feature then we just silently drop any signing
+ // presentation messages received
+ (void)msg;
+ SIGN_DEBUG(PSTR("Received signing presentation, but signing is not supported (message ignored)\n"));
+#endif // not MY_GATEWAY_FEATURE
+#endif // not MY_SIGNING_FEATURE
+ return true; // No need to further process I_SIGNING_PRESENTATION
+}
+
+// Helper to process nonce request mesages
+static bool signerInternalProcessNonceRequest(MyMessage &msg)
+{
+#if defined(MY_SIGNING_FEATURE)
+#if defined(MY_NODE_LOCK_FEATURE)
+ nof_nonce_requests++;
+ SIGN_DEBUG(PSTR("Nonce requests left until lockdown: %d\n"),
+ MY_NODE_LOCK_COUNTER_MAX-nof_nonce_requests);
+ if (nof_nonce_requests >= MY_NODE_LOCK_COUNTER_MAX) {
+ _nodeLock("TMNR"); //Too many nonces requested
+ }
+#endif // MY_NODE_LOCK_FEATURE
+ if (signerBackendGetNonce(msg)) {
+ if (!_sendRoute(build(msg, msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_NONCE_RESPONSE))) {
+ SIGN_DEBUG(PSTR("Failed to transmit nonce!\n"));
+ } else {
+ SIGN_DEBUG(PSTR("Transmitted nonce\n"));
+ }
+ } else {
+ SIGN_DEBUG(PSTR("Failed to generate nonce!\n"));
+ }
+#else // not MY_SIGNING_FEATURE
+ (void)msg;
+ SIGN_DEBUG(PSTR("Received nonce request, but signing is not supported (message ignored)\n"));
+#endif // MY_SIGNING_FEATURE
+ return true; // No need to further process I_NONCE_REQUEST
+}
+
+// Helper to process nonce response mesages
+static bool signerInternalProcessNonceResponse(MyMessage &msg)
+{
+#if defined(MY_SIGNING_FEATURE)
+ // Proceed with signing if nonce has been received
+ SIGN_DEBUG(PSTR("Nonce received from %d.\n"), msg.sender);
+ if (msg.sender != _msgSign.destination) {
+ SIGN_DEBUG(PSTR("Nonce did not come from the destination (%d) of the message to be signed! "
+ "It came from %d.\n"), _msgSign.destination, msg.sender);
+ SIGN_DEBUG(PSTR("Silently discarding this nonce\n"));
+ } else {
+ SIGN_DEBUG(PSTR("Proceeding with signing...\n"));
+ signerBackendPutNonce(msg);
+ if (!signerBackendSignMsg(_msgSign)) {
+ SIGN_DEBUG(PSTR("Failed to sign message!\n"));
+ } else {
+ SIGN_DEBUG(PSTR("Message signed\n"));
+ _signingNonceStatus = SIGN_OK; // _msgSign now contains the signed message pending transmission
+ }
+ }
+#else
+ (void)msg;
+ SIGN_DEBUG(PSTR("Received nonce response, but signing is not supported (message ignored)\n"));
+#endif
+ return true; // No need to further process I_NONCE_RESPONSE
+}
diff --git a/core/MySigning.h b/core/MySigning.h
index 135335322..386d60386 100644
--- a/core/MySigning.h
+++ b/core/MySigning.h
@@ -99,7 +99,7 @@
* scramble into “garbage” when transmitted over the air and then reassembled by a receiving node before being fed in “the clear” up the stack
* at the receiving end.
*
- * There are methods and possibilities to provide encryption also in software, but if this is done, it is my recommendation that this is done
+ * There are methods and possibilities to provide encryption also in software, but if this is done, it is my recommendation that this is done
* after integrity- and authentication information has been provided to the message (if this is desired). Integrity and authentication is of
* course not mandatory and some might be happy with only having encryption to cover their need for security. I, however, have only focused on
* integrity and authenticity while at the same time keeping the current message routing mechanisms intact and therefore leave
@@ -156,7 +156,8 @@
* This has to be set by at least one of the node in a "pair" or nobody will actually start calculating a signature for a message.
* Just set the flag @ref MY_SIGNING_REQUEST_SIGNATURES and the node will inform the gateway that it expects the gateway to sign all
* messages sent to the node. If this is set in a gateway, it will @b NOT force all nodes to sign messages to it. It will only require
- * signatures from nodes that in turn require signatures.
+ * signatures from nodes that in turn require signatures. If it is desired that the gateway should require signatures from all nodes,
+ * @ref MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL can be set in the gateway sketch.
* If you want to have two nodes communicate securely directly with each other, the nodes that require signatures must send a presentation
* message to all nodes it expect signed messages from (only the gateway is informed automatically). See @ref signerPresentation().
* A node can have three "states" with respect to signing:
@@ -267,14 +268,15 @@
*
* If a node does require signing, any unsigned message sent to the node will be rejected.
* This also applies to the gateway. However, the difference is that the gateway will only require signed messages from nodes it knows in turn
- * require signed messages.
+ * require signed messages (unless @ref MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL is set).
* A node can also inform a different node that it expects to receive signed messages from it. This is done by transmitting an internal message
* of type @ref I_SIGNING_PRESENTATION and provide flags as payload that inform the receiver of the signing preferences of the sender.
* All nodes and gateways in a network maintain a table where the signing preferences of all nodes are stored. This is also stored in EEPROM so
* if the gateway reboots, the nodes does not have to retransmit a signing presentation to the gateway for the gateway to realize that the node
* expect signed messages.
* Also, the nodes that do not require signed messages will also inform gateway of this, so if you reprogram a node to stop require signing,
- * the gateway will adhere to this as soon as the new node has presented itself to the gateway.
+ * the gateway will adhere to this as soon as the new node has presented itself to the gateway. Note however, that if the gateway sets
+ * @ref MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL a node that does not support signing will be unable to send any data to the gateway.
*
* The following sequence diagram illustrate how messages are passed in a MySensors network with respect to signing:
* @image html MySigning/signingsequence.png
@@ -311,7 +313,7 @@
* The whitelist is stored on the node that require signatures. When a received message is verified, the serial of the sender is looked up in a
* list stored on the receiving node, and the corresponding serial stored in the list for that sender is then included in the signature verification
* process. The list is stored as the value of the flag that enables whitelisting, @ref MY_SIGNING_NODE_WHITELISTING.
- *
+ *
* Whitelisting is achieved by 'salting' the signature with some node-unique information known to the receiver. In the case of ATSHA204A this is the
* unique serial number programmed into the circuit. This unique number is never transmitted over the air in clear text, so Eve will not be able to
* figure out a "trusted" serial by snooping on the traffic.
@@ -429,7 +431,7 @@
* ...
* @endcode
*
- * The gateway needs to configured with a whitelist (and it have to have an entry for all nodes that send and/or require signed messages):
+ * The gateway needs to be configured with a whitelist (and it has to have an entry for all nodes that send and/or require signed messages):
* @code{.cpp}
* #define MY_SIGNING_SOFT
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
@@ -488,14 +490,6 @@ typedef struct {
/** @brief Helper macro to determine the number of elements in a array */
#define NUM_OF(x) (sizeof(x)/sizeof(x[0]))
-/** @brief Helper macro to determine if node require serial salted signatures */
-#define DO_WHITELIST(node) (~_doWhitelist[node>>3]&(1<>3]&=~(1<>3]|=(1<> 4);
printBuffer[(i * 2) + 1] = i2h(buf[i]);
}
@@ -76,8 +80,7 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf,
printBuffer[MY_GATEWAY_MAX_SEND_LENGTH-1-strlen_P((const char*)str)] = '\0';
#endif
MY_SERIALDEVICE.print(str);
- if (sz > 0)
- {
+ if (sz > 0) {
MY_SERIALDEVICE.print(printBuffer);
}
MY_SERIALDEVICE.println("");
@@ -86,32 +89,36 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf,
#define DEBUG_SIGNING_PRINTBUF(str, buf, sz)
#endif
-void signerAtsha204Init(void) {
+void signerAtsha204Init(void)
+{
atsha204_init(MY_SIGNING_ATSHA204_PIN);
}
-bool signerAtsha204CheckTimer(void) {
+bool signerAtsha204CheckTimer(void)
+{
if (_signing_verification_ongoing) {
- if (hwMillis() < _signing_timestamp || hwMillis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
+ if (hwMillis() < _signing_timestamp ||
+ hwMillis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0);
// Purge nonce
memset(_signing_signing_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
memset(_signing_verifying_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
_signing_verification_ongoing = false;
- return false;
+ return false;
}
}
return true;
}
-bool signerAtsha204GetNonce(MyMessage &msg) {
+bool signerAtsha204GetNonce(MyMessage &msg)
+{
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0);
// Generate random number for use as nonce
// We used a basic whitening technique that XORs each byte in a 32byte random value with current hwMillis() counter
// This 32-byte random value is then hashed (SHA256) to produce the resulting nonce
(void)atsha204_wakeup(_signing_temp_message);
if (atsha204_execute(SHA204_RANDOM, RANDOM_SEED_UPDATE, 0, 0, NULL,
- RANDOM_COUNT, _signing_tx_buffer, RANDOM_RSP_SIZE, _signing_rx_buffer) != SHA204_SUCCESS) {
+ RANDOM_COUNT, _signing_tx_buffer, RANDOM_RSP_SIZE, _signing_rx_buffer) != SHA204_SUCCESS) {
DEBUG_SIGNING_PRINTBUF(F("Failed to generate nonce"), NULL, 0);
return false;
}
@@ -132,11 +139,14 @@ bool signerAtsha204GetNonce(MyMessage &msg) {
// Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover)
// Note that if message is "too" quick, and arrives before turnover, it will be rejected
// but this is consider such a rare case that it is accepted and rejects are 'safe'
- if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < hwMillis()) _signing_timestamp = 0;
+ if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < hwMillis()) {
+ _signing_timestamp = 0;
+ }
return true;
}
-void signerAtsha204PutNonce(MyMessage &msg) {
+void signerAtsha204PutNonce(MyMessage &msg)
+{
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0);
memcpy(_signing_signing_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
@@ -144,11 +154,12 @@ void signerAtsha204PutNonce(MyMessage &msg) {
memset(&_signing_signing_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_signing_nonce)-MAX_PAYLOAD);
}
-bool signerAtsha204SignMsg(MyMessage &msg) {
+bool signerAtsha204SignMsg(MyMessage &msg)
+{
// If we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) {
DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0);
- return false;
+ return false;
}
// Calculate signature of message
@@ -157,10 +168,12 @@ bool signerAtsha204SignMsg(MyMessage &msg) {
if (DO_WHITELIST(msg.destination)) {
// Salt the signature with the senders nodeId and the unique serial of the ATSHA device
- memcpy(_signing_signing_nonce, &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
+ memcpy(_signing_signing_nonce, &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
+ 32); // We can reuse the nonce buffer now since it is no longer needed
_signing_signing_nonce[32] = msg.sender;
atsha204_getSerialNumber(&_signing_signing_nonce[33]);
- (void)signerSha256(_signing_signing_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
+ (void)signerSha256(_signing_signing_nonce,
+ 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0);
}
@@ -171,30 +184,34 @@ bool signerAtsha204SignMsg(MyMessage &msg) {
_signing_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
// Transfer as much signature data as the remaining space in the message permits
- memcpy(&msg.data[mGetLength(msg)], &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg));
- DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg));
+ memcpy(&msg.data[mGetLength(msg)], &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
+ MAX_PAYLOAD-mGetLength(msg));
+ DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)],
+ MAX_PAYLOAD-mGetLength(msg));
return true;
}
-bool signerAtsha204VerifyMsg(MyMessage &msg) {
+bool signerAtsha204VerifyMsg(MyMessage &msg)
+{
if (!_signing_verification_ongoing) {
DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0);
- return false;
+ return false;
} else {
// Make sure we have not expired
if (!signerCheckTimer()) {
- return false;
+ return false;
}
_signing_verification_ongoing = false;
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTBUF(F("Incorrect signing identifier"), NULL, 0);
- return false;
+ return false;
}
- DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg));
+ DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)],
+ MAX_PAYLOAD-mGetLength(msg));
signerCalculateSignature(msg, false); // Get signature of message
#ifdef MY_SIGNING_NODE_WHITELISTING
@@ -203,10 +220,12 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) {
for (j=0; j < NUM_OF(_signing_whitelist); j++) {
if (_signing_whitelist[j].nodeId == msg.sender) {
DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0);
- memcpy(_signing_verifying_nonce, &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
+ memcpy(_signing_verifying_nonce, &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
+ 32); // We can reuse the nonce buffer now since it is no longer needed
_signing_verifying_nonce[32] = msg.sender;
memcpy(&_signing_verifying_nonce[33], _signing_whitelist[j].serial, SHA204_SERIAL_SZ);
- (void)signerSha256(_signing_verifying_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
+ (void)signerSha256(_signing_verifying_nonce,
+ 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
break;
}
}
@@ -225,12 +244,14 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) {
_signing_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
// Compare the caluclated signature with the provided signature
- if (signerMemcmp(&msg.data[mGetLength(msg)], &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) {
- DEBUG_SIGNING_PRINTBUF(F("Signature bad: "), &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg));
+ if (signerMemcmp(&msg.data[mGetLength(msg)], &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
+ MAX_PAYLOAD-mGetLength(msg))) {
+ DEBUG_SIGNING_PRINTBUF(F("Signature bad: "), &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
+ MAX_PAYLOAD-mGetLength(msg));
#ifdef MY_SIGNING_NODE_WHITELISTING
DEBUG_SIGNING_PRINTBUF(F("Is the sender whitelisted and serial correct?"), NULL, 0);
#endif
- return false;
+ return false;
} else {
DEBUG_SIGNING_PRINTBUF(F("Signature OK"), NULL, 0);
return true;
@@ -239,42 +260,49 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) {
}
// Helper to calculate signature of msg (returned in _signing_rx_buffer[SHA204_BUFFER_POS_DATA])
-static void signerCalculateSignature(MyMessage &msg, bool signing) {
+static void signerCalculateSignature(MyMessage &msg, bool signing)
+{
(void)atsha204_wakeup(_signing_temp_message);
memset(_signing_temp_message, 0, 32);
- memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
+ memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE],
+ MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
// Program the data to sign into the ATSHA204
- DEBUG_SIGNING_PRINTBUF(F("Message to process: "), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
- DEBUG_SIGNING_PRINTBUF(F("Current nonce: "), signing ? _signing_signing_nonce : _signing_verifying_nonce, 32);
- (void)atsha204_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, _signing_temp_message,
- WRITE_COUNT_LONG, _signing_tx_buffer, WRITE_RSP_SIZE, _signing_rx_buffer);
+ DEBUG_SIGNING_PRINTBUF(F("Message to process: "), (uint8_t*)&msg.data[1-HEADER_SIZE],
+ MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
+ DEBUG_SIGNING_PRINTBUF(F("Current nonce: "),
+ signing ? _signing_signing_nonce : _signing_verifying_nonce, 32);
+ (void)atsha204_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32,
+ _signing_temp_message,
+ WRITE_COUNT_LONG, _signing_tx_buffer, WRITE_RSP_SIZE, _signing_rx_buffer);
// Program the nonce to use for the signature (has to be done just before GENDIG due to chip limitations)
(void)atsha204_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH,
- signing ? _signing_signing_nonce : _signing_verifying_nonce,
- NONCE_COUNT_LONG, _signing_tx_buffer, NONCE_RSP_SIZE_SHORT, _signing_rx_buffer);
+ signing ? _signing_signing_nonce : _signing_verifying_nonce,
+ NONCE_COUNT_LONG, _signing_tx_buffer, NONCE_RSP_SIZE_SHORT, _signing_rx_buffer);
// Purge nonce when used
- memset(signing ? _signing_signing_nonce : _signing_verifying_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
+ memset(signing ? _signing_signing_nonce : _signing_verifying_nonce, 0x00,
+ NONCE_NUMIN_SIZE_PASSTHROUGH);
// Generate digest of data and nonce
(void)atsha204_execute(SHA204_GENDIG, GENDIG_ZONE_DATA, 8, 0, NULL,
- GENDIG_COUNT_DATA, _signing_tx_buffer, GENDIG_RSP_SIZE, _signing_rx_buffer);
+ GENDIG_COUNT_DATA, _signing_tx_buffer, GENDIG_RSP_SIZE, _signing_rx_buffer);
// Calculate HMAC of message+nonce digest and secret key
(void)atsha204_execute(SHA204_HMAC, HMAC_MODE_SOURCE_FLAG_MATCH, 0, 0, NULL,
- HMAC_COUNT, _signing_tx_buffer, HMAC_RSP_SIZE, _signing_rx_buffer);
+ HMAC_COUNT, _signing_tx_buffer, HMAC_RSP_SIZE, _signing_rx_buffer);
DEBUG_SIGNING_PRINTBUF(F("HMAC: "), &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], 32);
}
// Helper to calculate a generic SHA256 digest of provided buffer (only supports one block)
// The pointer to the hash is returned, but the hash is also stored in _signing_rx_buffer[SHA204_BUFFER_POS_DATA])
-static uint8_t* signerSha256(const uint8_t* data, size_t sz) {
+static uint8_t* signerSha256(const uint8_t* data, size_t sz)
+{
// Initiate SHA256 calculator
(void)atsha204_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL,
- SHA_COUNT_SHORT, _signing_tx_buffer, SHA_RSP_SIZE_SHORT, _signing_rx_buffer);
+ SHA_COUNT_SHORT, _signing_tx_buffer, SHA_RSP_SIZE_SHORT, _signing_rx_buffer);
// Calculate a hash
memset(_signing_temp_message, 0x00, SHA_MSG_SIZE);
@@ -284,7 +312,7 @@ static uint8_t* signerSha256(const uint8_t* data, size_t sz) {
_signing_temp_message[SHA_MSG_SIZE-2] = (sz >> 5);
_signing_temp_message[SHA_MSG_SIZE-1] = (sz << 3);
(void)atsha204_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, _signing_temp_message,
- SHA_COUNT_LONG, _signing_tx_buffer, SHA_RSP_SIZE_LONG, _signing_rx_buffer);
+ SHA_COUNT_LONG, _signing_tx_buffer, SHA_RSP_SIZE_LONG, _signing_rx_buffer);
DEBUG_SIGNING_PRINTBUF(F("SHA256: "), &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], 32);
return &_signing_rx_buffer[SHA204_BUFFER_POS_DATA];
diff --git a/core/MySigningAtsha204Soft.cpp b/core/MySigningAtsha204Soft.cpp
index 400895f2f..62490f0ca 100644
--- a/core/MySigningAtsha204Soft.cpp
+++ b/core/MySigningAtsha204Soft.cpp
@@ -47,75 +47,89 @@ extern uint8_t _doWhitelist[32];
static uint8_t _signing_node_serial_info[9];
#ifdef MY_SIGNING_NODE_WHITELISTING
- const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
+const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
#endif
static void signerCalculateSignature(MyMessage &msg, bool signing);
#ifdef MY_DEBUG_VERBOSE_SIGNING
static char i2h(uint8_t i)
- {
+{
uint8_t k = i & 0x0F;
- if (k <= 9)
+ if (k <= 9) {
return '0' + k;
- else
+ } else {
return 'A' + k - 10;
+ }
}
-static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) {
+#ifdef __linux__
+#define __FlashStringHelper char
+#define MY_SERIALDEVICE.print debug
+#endif
+
+static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz)
+{
static char printBuffer[300];
-#ifdef MY_GATEWAY_FEATURE
+ if (NULL == buf) {
+ return;
+ }
+#if defined(MY_GATEWAY_FEATURE) && !defined(__linux__)
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
snprintf_P(printBuffer, 299, PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
MY_SERIALDEVICE.print(printBuffer);
#endif
- for (int i=0; i> 4);
printBuffer[(i * 2) + 1] = i2h(buf[i]);
}
printBuffer[sz * 2] = '\0';
-#ifdef MY_GATEWAY_FEATURE
+#if defined(MY_GATEWAY_FEATURE) && !defined(__linux__)
// Truncate message if this is gateway node
printBuffer[MY_GATEWAY_MAX_SEND_LENGTH-1-strlen_P((const char*)str)] = '\0';
#endif
MY_SERIALDEVICE.print(str);
- if (sz > 0)
- {
+ if (sz > 0) {
MY_SERIALDEVICE.print(printBuffer);
}
+#if !defined(__linux__)
MY_SERIALDEVICE.println("");
+#endif
}
#else
#define DEBUG_SIGNING_PRINTBUF(str, buf, sz)
#endif
-void signerAtsha204SoftInit(void) {
+void signerAtsha204SoftInit(void)
+{
// initialize pseudo-RNG
- randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN));
+ hwRandomNumberInit();
// Set secrets
hwReadConfigBlock((void*)_signing_hmac_key, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
hwReadConfigBlock((void*)_signing_node_serial_info, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
}
-bool signerAtsha204SoftCheckTimer(void) {
+bool signerAtsha204SoftCheckTimer(void)
+{
if (_signing_verification_ongoing) {
- if (hwMillis() < _signing_timestamp || hwMillis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
+ if (hwMillis() < _signing_timestamp ||
+ hwMillis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0);
// Purge nonce
memset(_signing_signing_nonce, 0xAA, 32);
memset(_signing_verifying_nonce, 0xAA, 32);
_signing_verification_ongoing = false;
- return false;
+ return false;
}
}
return true;
}
-bool signerAtsha204SoftGetNonce(MyMessage &msg) {
+bool signerAtsha204SoftGetNonce(MyMessage &msg)
+{
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0);
- // We used a basic whitening technique that XORs a random byte with the current hwMillis() counter and then the byte is
+ // We used a basic whitening technique that XORs a random byte with the current hwMillis() counter and then the byte is
// hashed (SHA256) to produce the resulting nonce
_signing_sha256.init();
for (int i = 0; i < 32; i++) {
@@ -134,11 +148,14 @@ bool signerAtsha204SoftGetNonce(MyMessage &msg) {
// Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover)
// Note that if message is "too" quick, and arrives before turnover, it will be rejected
// but this is consider such a rare case that it is accepted and rejects are 'safe'
- if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < hwMillis()) _signing_timestamp = 0;
+ if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < hwMillis()) {
+ _signing_timestamp = 0;
+ }
return true;
}
-void signerAtsha204SoftPutNonce(MyMessage &msg) {
+void signerAtsha204SoftPutNonce(MyMessage &msg)
+{
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0);
memcpy(_signing_signing_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
@@ -146,11 +163,12 @@ void signerAtsha204SoftPutNonce(MyMessage &msg) {
memset(&_signing_signing_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_signing_nonce)-MAX_PAYLOAD);
}
-bool signerAtsha204SoftSignMsg(MyMessage &msg) {
+bool signerAtsha204SoftSignMsg(MyMessage &msg)
+{
// If we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) {
DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0);
- return false;
+ return false;
}
// Calculate signature of message
@@ -160,9 +178,13 @@ bool signerAtsha204SoftSignMsg(MyMessage &msg) {
if (DO_WHITELIST(msg.destination)) {
// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided
_signing_sha256.init();
- for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]);
+ for (int i=0; i<32; i++) {
+ _signing_sha256.write(_signing_hmac[i]);
+ }
_signing_sha256.write(msg.sender);
- for (int i=0; i
- * Copyright (C) 2013-2015 Sensnology AB
+ * Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
@@ -17,452 +17,648 @@
* version 2 as published by the Free Software Foundation.
*/
-
#include "MyTransport.h"
-static uint32_t _transport_lastUplinkCheck; //!< last uplink check, to prevent GW flooding
+// SM: transitions and update states
+static transportState_t stInit = { stInitTransition, stInitUpdate };
+static transportState_t stParent = { stParentTransition, stParentUpdate };
+static transportState_t stID = { stIDTransition, stIDUpdate };
+static transportState_t stUplink = { stUplinkTransition, stUplinkUpdate };
+static transportState_t stReady = { stReadyTransition, stReadyUpdate };
+static transportState_t stFailure = { stFailureTransition, stFailureUpdate };
+
+// transport SM variables
+static transportSM_t _transportSM;
+
+// transport configuration
+static transportConfig_t _transportConfig;
+
+// callback transportOk
+transportCallback_t transportReady_cb = NULL;
+
+// global variables
+extern MyMessage _msg; // incoming message
+extern MyMessage _msgTmp; // outgoing message
-// repeaters (this includes GW) should perform regular sanity checks for network reliability
-#if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_REPEATER_FEATURE)
- uint32_t _transport_lastSanityCheck; //!< last sanity check
+#if defined(MY_RAM_ROUTING_TABLE_ENABLED)
+static routingTable_t _transportRoutingTable; //!< routing table
+static uint32_t _lastRoutingTableSave; //!< last routing table dump
#endif
-// SM: transitions and update states
-static State stInit = { stInitTransition, NULL };
-static State stParent = { stParentTransition, stParentUpdate };
-static State stID = { stIDTransition, stIDUpdate };
-static State stUplink = { stUplinkTransition, NULL };
-static State stOK = { stOKTransition, stOKUpdate };
-static State stFailure = { stFailureTransition, stFailureUpdate };
-
-static transportSM _transportSM;
-
-// stInit: initialize transport HW
-void stInitTransition() {
- debug(PSTR("TSM:INIT\n"));
- // initialize status variables
- _transportSM.failedUplinkTransmissions = 0;
+// regular sanity check, activated by default on GW and repeater nodes
+#if defined(MY_TRANSPORT_SANITY_CHECK)
+static uint32_t _lastSanityCheck; //!< last sanity check
+#endif
+
+// regular network discovery, sends I_DISCOVER_REQUESTS to update routing table
+// sufficient to have GW triggering requests to also update repeater nodes
+#if defined(MY_GATEWAY_FEATURE)
+static uint32_t _lastNetworkDiscovery; //! last network discovery
+#endif
+
+// stInit: initialise transport HW
+void stInitTransition(void)
+{
+ TRANSPORT_DEBUG(PSTR("TSM:INIT\n"));
+ // initialise status variables
_transportSM.pingActive = false;
_transportSM.transportActive = false;
- #if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_REPEATER_FEATURE)
- _transport_lastSanityCheck = hwMillis();
- #endif
- _transport_lastUplinkCheck = 0;
- // Read node settings (ID, parentId, GW distance) from EEPROM
- hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
-
- // initialize radio
+ _transportSM.lastUplinkCheck = 0ul;
+
+#if defined(MY_TRANSPORT_SANITY_CHECK)
+ _lastSanityCheck = hwMillis();
+#endif
+#if defined(MY_GATEWAY_FEATURE)
+ _lastNetworkDiscovery = 0ul;
+#endif
+#if defined(MY_RAM_ROUTING_TABLE_ENABLED)
+ _lastRoutingTableSave = hwMillis();
+#endif
+
+ // Read node settings (ID, parent ID, GW distance) from EEPROM
+ hwReadConfigBlock((void*)&_transportConfig, (void*)EEPROM_NODE_ID_ADDRESS,
+ sizeof(transportConfig_t));
+}
+
+void stInitUpdate(void)
+{
+ // initialise radio
if (!transportInit()) {
- debug(PSTR("!TSM:RADIO:FAIL\n"));
+ TRANSPORT_DEBUG(PSTR("!TSM:INIT:TSP FAIL\n"));
setIndication(INDICATION_ERR_INIT_TRANSPORT);
transportSwitchSM(stFailure);
- }
- else {
- debug(PSTR("TSM:RADIO:OK\n"));
+ } else {
+ TRANSPORT_DEBUG(PSTR("TSM:INIT:TSP OK\n"));
_transportSM.transportActive = true;
- #if defined(MY_GATEWAY_FEATURE)
- // Set configuration for gateway
- debug(PSTR("TSM:GW MODE\n"));
- _nc.parentNodeId = GATEWAY_ADDRESS;
- _nc.distance = 0;
- _nc.nodeId = GATEWAY_ADDRESS;
- transportSetAddress(GATEWAY_ADDRESS);
- transportSwitchSM(stOK);
- #else
- if (MY_NODE_ID != AUTO) {
- // Set static id
- _nc.nodeId = MY_NODE_ID;
- // Save static id in eeprom
- hwWriteConfig(EEPROM_NODE_ID_ADDRESS, MY_NODE_ID);
- }
- // set ID if static or set in EEPROM
- if(_nc.nodeId!=AUTO) transportAssignNodeID(_nc.nodeId);
+#if defined(MY_GATEWAY_FEATURE)
+ // Set configuration for gateway
+ TRANSPORT_DEBUG(PSTR("TSM:INIT:GW MODE\n"));
+ _transportConfig.parentNodeId = GATEWAY_ADDRESS;
+ _transportConfig.distanceGW = 0u;
+ _transportConfig.nodeId = GATEWAY_ADDRESS;
+ transportSetAddress(GATEWAY_ADDRESS);
+ // GW mode: skip FPAR,ID,UPL states
+ transportSwitchSM(stReady);
+#else
+ if (MY_NODE_ID != AUTO) {
+ TRANSPORT_DEBUG(PSTR("TSM:INIT:STATID=%d\n"),(uint8_t)MY_NODE_ID);
+ // Set static ID
+ _transportConfig.nodeId = (uint8_t)MY_NODE_ID;
+ // Save static ID to eeprom (for bootloader)
+ hwWriteConfig(EEPROM_NODE_ID_ADDRESS, (uint8_t)MY_NODE_ID);
+ }
+ // assign ID if set
+ if (_transportConfig.nodeId == AUTO || transportAssignNodeID(_transportConfig.nodeId)) {
+ // if node ID valid (>0 and <255), proceed to next state
transportSwitchSM(stParent);
- #endif
+ } else {
+ // ID invalid (0 or 255)
+ transportSwitchSM(stFailure);
+ }
+#endif
}
}
// stParent: find parent
-void stParentTransition() {
- debug(PSTR("TSM:FPAR\n")); // find parent
+void stParentTransition(void)
+{
+ TRANSPORT_DEBUG(PSTR("TSM:FPAR\n")); // find parent
+ setIndication(INDICATION_FIND_PARENT);
+ _transportSM.uplinkOk = false;
_transportSM.preferredParentFound = false;
+#if defined(MY_PARENT_NODE_IS_STATIC)
+ TRANSPORT_DEBUG(PSTR("TSM:FPAR:STATP=%d\n"), (uint8_t)MY_PARENT_NODE_ID); // static parent
+ _transportSM.findingParentNode = false;
+ _transportConfig.distanceGW = 1u; // assumption, CHKUPL:GWDC will update this variable
+ _transportConfig.parentNodeId = (uint8_t)MY_PARENT_NODE_ID;
+ // save parent ID to eeprom (for bootloader)
+ hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, (uint8_t)MY_PARENT_NODE_ID);
+#else
_transportSM.findingParentNode = true;
- _transportSM.failedUplinkTransmissions = 0;
- _transportSM.uplinkOk = false;
- // Set distance to max and invalidate parent node id
- _nc.distance = DISTANCE_INVALID;
- _nc.parentNodeId = AUTO;
+ _transportConfig.distanceGW = DISTANCE_INVALID; // Set distance to max and invalidate parent node ID
+ _transportConfig.parentNodeId = AUTO;
// Broadcast find parent request
- setIndication(INDICATION_FIND_PARENT);
- transportRouteMessage(build(_msgTmp, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set(""));
+ (void)transportRouteMessage(build(_msgTmp, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
+ I_FIND_PARENT_REQUEST).set(""));
+#endif
}
// stParentUpdate
-void stParentUpdate() {
- if (transportTimeInState() > STATE_TIMEOUT || _transportSM.preferredParentFound) {
+void stParentUpdate(void)
+{
+#if defined(MY_PARENT_NODE_IS_STATIC)
+ // skipping find parent
+ setIndication(INDICATION_GOT_PARENT);
+ transportSwitchSM(stID);
+#else
+ if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS || _transportSM.preferredParentFound) {
// timeout or preferred parent found
- if (_nc.parentNodeId != AUTO) {
- debug(PSTR("TSM:FPAR:OK\n")); // find parent ok
+ if (_transportConfig.parentNodeId != AUTO) {
+ // parent assigned
+ TRANSPORT_DEBUG(PSTR("TSM:FPAR:OK\n")); // find parent ok
_transportSM.findingParentNode = false;
setIndication(INDICATION_GOT_PARENT);
+ // go to next state
transportSwitchSM(stID);
- }
- else if (transportTimeInState() > STATE_TIMEOUT) {
- if (_transportSM.retries < STATE_RETRIES) {
- // reenter if timeout and retries left
+ } else {
+ // timeout w/o reply or valid parent
+ if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) {
+ // retries left
+ TRANSPORT_DEBUG(PSTR("!TSM:FPAR:NO REPLY\n")); // find parent, no reply
+ // reenter state
transportSwitchSM(stParent);
- }
- else {
- debug(PSTR("!TSM:FPAR:FAIL\n")); // find parent fail
+ } else {
+ // no retries left, finding parent failed
+ TRANSPORT_DEBUG(PSTR("!TSM:FPAR:FAIL\n"));
setIndication(INDICATION_ERR_FIND_PARENT);
transportSwitchSM(stFailure);
}
}
}
+#endif
}
// stID: verify and request ID if necessary
-void stIDTransition() {
- debug(PSTR("TSM:ID\n"));
- if (_nc.nodeId == AUTO) {
+void stIDTransition(void)
+{
+ TRANSPORT_DEBUG(PSTR("TSM:ID\n")); // verify/request node ID
+ if (_transportConfig.nodeId == AUTO) {
// send ID request
setIndication(INDICATION_REQ_NODEID);
- transportRouteMessage(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set(""));
+ TRANSPORT_DEBUG(PSTR("TSM:ID:REQ\n")); // request node ID
+ (void)transportRouteMessage(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
+ I_ID_REQUEST).set(""));
}
}
-void stIDUpdate() {
- if (_nc.nodeId != AUTO) {
- // current node ID is valid, proceed to uplink check
- debug(PSTR("TSM:CHKID:OK (ID=%d)\n"), _nc.nodeId);
+void stIDUpdate(void)
+{
+ if (_transportConfig.nodeId != AUTO) {
+ // current node ID is valid
+ TRANSPORT_DEBUG(PSTR("TSM:ID:OK\n"));
setIndication(INDICATION_GOT_NODEID);
- // check uplink
- transportSwitchSM(stUplink);
- }
- else if (transportTimeInState() > STATE_TIMEOUT) {
- if (_transportSM.retries < STATE_RETRIES) {
- // re-enter if retries left
+ // proceed to next state
+ transportSwitchSM(stUplink);
+ } else if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS) {
+ // timeout
+ if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) {
+ // retries left: reenter state
transportSwitchSM(stID);
- }
- else {
- // fail
- debug(PSTR("!TSM:CHKID:FAIL (ID=%d)\n"), _nc.nodeId);
+ } else {
+ // no retries left
+ TRANSPORT_DEBUG(PSTR("!TSM:ID:FAIL\n"));
setIndication(INDICATION_ERR_GET_NODEID);
transportSwitchSM(stFailure);
}
}
}
-void stUplinkTransition() {
- debug(PSTR("TSM:UPL\n"));
- if(transportCheckUplink(true)) {
- debug(PSTR("TSM:UPL:OK\n"));
- transportSwitchSM(stOK);
- }
- else {
- debug(PSTR("!TSM:UPL:FAIL\n"));
- transportSwitchSM(stParent);
+void stUplinkTransition(void)
+{
+#if !defined(MY_TRANSPORT_UPLINK_CHECK_DISABLED)
+ TRANSPORT_DEBUG(PSTR("TSM:UPL\n"));
+ setIndication(INDICATION_CHECK_UPLINK);
+ _transportSM.pingResponse = INVALID_HOPS;
+ _transportSM.pingActive = true;
+ (void)transportRouteMessage(build(_msgTmp,GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
+ I_PING).set((uint8_t)0x01));
+#endif
+}
+
+void stUplinkUpdate(void)
+{
+#if !defined(MY_TRANSPORT_UPLINK_CHECK_DISABLED)
+ if (_transportSM.pingResponse != INVALID_HOPS) {
+ _transportSM.lastUplinkCheck = hwMillis();
+ // uplink ok, i.e. GW replied
+ TRANSPORT_DEBUG(PSTR("TSM:UPL:OK\n")); // uplink ok
+ if (_transportSM.pingResponse != _transportConfig.distanceGW) {
+ TRANSPORT_DEBUG(PSTR("TSM:UPL:DGWC,O=%d,N=%d\n"), _transportConfig.distanceGW,
+ _transportSM.pingResponse); // distance to GW changed
+ _transportConfig.distanceGW = _transportSM.pingResponse;
+ }
+ transportSwitchSM(stReady); // proceed to next state
+ } else if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS) {
+ // timeout
+ if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) {
+ // retries left: reenter state
+ transportSwitchSM(stUplink);
+ } else {
+ // no retries left
+ TRANSPORT_DEBUG(PSTR("!TSM:UPL:FAIL\n")); // uplink check failed
+ _transportSM.pingActive = false;
+ setIndication(INDICATION_ERR_CHECK_UPLINK);
+ transportSwitchSM(stParent); // go back to stParent
+ }
}
+#else
+ TRANSPORT_DEBUG(PSTR("TSM:UPL:DISABLED\n")); // uplink check disabled
+ transportSwitchSM(stReady);
+#endif
}
-void stOKTransition() {
- debug(PSTR("TSM:READY\n")); // transport is ready
+void stReadyTransition(void)
+{
+ // transport is ready and fully operational
+ TRANSPORT_DEBUG(PSTR("TSM:READY:ID=%d,PAR=%d,DIS=%d\n"), _transportConfig.nodeId,
+ _transportConfig.parentNodeId, _transportConfig.distanceGW);
_transportSM.uplinkOk = true;
+ _transportSM.failureCounter = 0u; // reset failure counter
+ _transportSM.failedUplinkTransmissions = 0u; // reset failed uplink TX counter
+ // callback
+ if (transportReady_cb) {
+ transportReady_cb();
+ }
}
-// stOK update: monitors uplink failures
-void stOKUpdate() {
- #if !defined(MY_GATEWAY_FEATURE)
- if (_transportSM.failedUplinkTransmissions > TRANSMISSION_FAILURES) {
- // too many uplink transmissions failed, find new parent
- #if !defined(MY_PARENT_NODE_IS_STATIC)
- debug(PSTR("!TSM:UPL FAIL, SNP\n"));
- transportSwitchSM(stParent);
- #else
- debug(PSTR("!TSM:UPL FAIL, STATP\n"));
- _transportSM.failedUplinkTransmissions = 0;
- #endif
- }
- #endif
+// stReadyUpdate: monitors link
+void stReadyUpdate(void)
+{
+#if defined(MY_GATEWAY_FEATURE)
+ if (hwMillis() - _lastNetworkDiscovery > MY_TRANSPORT_DISCOVERY_INTERVAL_MS) {
+ _lastNetworkDiscovery = hwMillis();
+ TRANSPORT_DEBUG(PSTR("TSM:READY:NWD REQ\n")); // send transport network discovery
+ (void)transportRouteMessage(build(_msgTmp, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
+ I_DISCOVER_REQUEST).set(""));
+ }
+#else
+ if (_transportSM.failedUplinkTransmissions > MY_TRANSPORT_MAX_TX_FAILURES) {
+ // too many uplink transmissions failed, find new parent (if non-static)
+#if !defined(MY_PARENT_NODE_IS_STATIC)
+ TRANSPORT_DEBUG(PSTR("!TSM:READY:UPL FAIL,SNP\n")); // uplink failed, search new parent
+ transportSwitchSM(stParent);
+#else
+ TRANSPORT_DEBUG(PSTR("!TSM:READY:UPL FAIL,STATP\n")); // uplink failed, static parent
+ // reset counter
+ _transportSM.failedUplinkTransmissions = 0u;
+#endif
+ }
+#endif
+
+#if defined(MY_RAM_ROUTING_TABLE_ENABLED)
+ if (hwMillis() - _lastRoutingTableSave > MY_ROUTING_TABLE_SAVE_INTERVAL_MS) {
+ _lastRoutingTableSave = hwMillis();
+ transportSaveRoutingTable();
+ }
+#endif
}
// stFailure: entered upon HW init failure or max retries exceeded
-void stFailureTransition() {
- debug(PSTR("!TSM:FAILURE\n"));
- _transportSM.uplinkOk = false;
- _transportSM.transportActive = false;
+void stFailureTransition(void)
+{
+ if (_transportSM.failureCounter < MY_TRANSPORT_MAX_TSM_FAILURES) {
+ _transportSM.failureCounter++; // increment consecutive TSM failure counter
+ }
+ TRANSPORT_DEBUG(PSTR("TSM:FAIL:CNT=%d\n"),_transportSM.failureCounter);
+ _transportSM.uplinkOk = false; // uplink nok
+ _transportSM.transportActive = false; // transport inactive
setIndication(INDICATION_ERR_INIT_TRANSPORT);
- // power down transport, no need until re-init
- debug(PSTR("TSM:PDT\n"));
+#if defined(MY_SENSOR_NETWORK)
+ TRANSPORT_DEBUG(PSTR("TSM:FAIL:PDT\n")); // power down transport, no need until re-init
transportPowerDown();
+#endif
}
-void stFailureUpdate() {
- if (transportTimeInState()> TIMEOUT_FAILURE_STATE) {
+void stFailureUpdate(void)
+{
+ if (transportTimeInState() > ( isTransportExtendedFailure()? MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE:
+ MY_TRANSPORT_TIMEOUT_FAILURE_STATE) ) {
+ TRANSPORT_DEBUG(PSTR("TSM:FAIL:RE-INIT\n")); // attempt to re-initialise transport
transportSwitchSM(stInit);
}
}
-void transportSwitchSM(State& newState) {
+void transportSwitchSM(transportState_t& newState)
+{
if (_transportSM.currentState != &newState) {
- // state change, reset retry counter
- _transportSM.retries = 0;
- // change state
- _transportSM.currentState = &newState;
+ _transportSM.stateRetries = 0u; // state change, reset retry counter
+ _transportSM.currentState = &newState; // change state
+ } else {
+ _transportSM.stateRetries++; // increment retries
}
- else {
- _transportSM.retries++; // increment retries
+ if (_transportSM.currentState->Transition) {
+ _transportSM.currentState->Transition(); // State transition
}
- // Transition event
- if (_transportSM.currentState->Transition) _transportSM.currentState->Transition();
- // save time
- _transportSM.stateEnter = hwMillis();
+ _transportSM.stateEnter = hwMillis(); // save time
}
-uint32_t transportTimeInState() {
+uint32_t transportTimeInState(void)
+{
return hwMillis() - _transportSM.stateEnter;
}
-void transportUpdateSM(){
- if (_transportSM.currentState->Update) _transportSM.currentState->Update();
+void transportUpdateSM(void)
+{
+ if (_transportSM.currentState->Update) {
+ _transportSM.currentState->Update();
+ }
+}
+
+bool isTransportReady(void)
+{
+ return _transportSM.uplinkOk;
}
-bool isTransportOK() {
- return (_transportSM.uplinkOk);
+bool isTransportExtendedFailure(void)
+{
+ return _transportSM.failureCounter == MY_TRANSPORT_MAX_TSM_FAILURES;
}
+bool isTransportSearchingParent(void)
+{
+ return _transportSM.findingParentNode;
+}
-void transportInitialize() {
+bool isMessageReceived(void)
+{
+ return _transportSM.msgReceived;
+}
+
+void resetMessageReceived(void)
+{
+ _transportSM.msgReceived = false;
+}
+
+
+void transportInitialise(void)
+{
+ _transportSM.failureCounter = 0u; // reset failure counter
+ transportLoadRoutingTable(); // load routing table to RAM (if feature enabled)
// intial state
- _transportSM.currentState = &stFailure;
+ _transportSM.currentState = NULL;
transportSwitchSM(stInit);
}
+bool transportWaitUntilReady(const uint32_t waitingMS)
+{
+ // check if transport ready
+ TRANSPORT_DEBUG(PSTR("TSF:WUR:MS=%lu\n"), waitingMS); // timeout
+ uint32_t enterMS = hwMillis();
+ bool result = false;
+ while (!result && ( hwMillis() - enterMS < waitingMS || !waitingMS)) {
+ transportProcess();
+ result = isTransportReady();
+ doYield();
+ }
+ return result;
+}
+
// update TSM and process incoming messages
-void transportProcess() {
+void transportProcess(void)
+{
// update state machine
- transportUpdateSM();
+ transportUpdateSM();
// process transport FIFO
transportProcessFIFO();
}
-bool transportCheckUplink(bool force) {
- if (!force && (hwMillis() - _transport_lastUplinkCheck) < CHKUPL_INTERVAL) {
- debug(PSTR("TSP:CHKUPL:OK (FLDCTRL)\n")); // flood control
+bool transportCheckUplink(const bool force)
+{
+ if (!force && (hwMillis() - _transportSM.lastUplinkCheck) < MY_TRANSPORT_CHKUPL_INTERVAL_MS) {
+ TRANSPORT_DEBUG(PSTR("TSF:CKU:OK,FCTRL\n")); // flood control
return true;
}
// ping GW
- uint8_t hopsCount = transportPingNode(GATEWAY_ADDRESS);
+ const uint8_t hopsCount = transportPingNode(GATEWAY_ADDRESS);
// verify hops
if (hopsCount != INVALID_HOPS) {
// update
- _transport_lastUplinkCheck = hwMillis();
- debug(PSTR("TSP:CHKUPL:OK\n"));
- // did distance to GW change upstream, i.e. re-routing of uplink nodes?
- if (hopsCount != _nc.distance) {
- debug(PSTR("TSP:CHKUPL:DGWC (old=%d,new=%d)"), _nc.distance, hopsCount); // distance to GW changed
- _nc.distance = hopsCount;
+ _transportSM.lastUplinkCheck = hwMillis();
+ TRANSPORT_DEBUG(PSTR("TSF:CKU:OK\n"));
+ // did distance to GW change upstream, eg. re-routing of uplink nodes
+ if (hopsCount != _transportConfig.distanceGW) {
+ TRANSPORT_DEBUG(PSTR("TSF:CKU:DGWC,O=%d,N=%d\n"), _transportConfig.distanceGW,
+ hopsCount); // distance to GW changed
+ _transportConfig.distanceGW = hopsCount;
}
return true;
- }
- else {
- debug(PSTR("TSP:CHKUPL:FAIL (hops=%d)\n"), hopsCount);
+ } else {
+ TRANSPORT_DEBUG(PSTR("TSF:CKU:FAIL\n"));
return false;
}
}
-void transportAssignNodeID(uint8_t newNodeId) {
+bool transportAssignNodeID(const uint8_t newNodeId)
+{
// verify if ID valid
if (newNodeId != GATEWAY_ADDRESS && newNodeId != AUTO) {
- _nc.nodeId = newNodeId;
+ _transportConfig.nodeId = newNodeId;
transportSetAddress(newNodeId);
// Write ID to EEPROM
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, newNodeId);
- debug(PSTR("TSP:ASSIGNID:OK (ID=%d)\n"),newNodeId);
- }
- else {
- debug(PSTR("!TSP:ASSIGNID:FAIL (ID=%d)\n"),newNodeId);
+ TRANSPORT_DEBUG(PSTR("TSF:SID:OK,ID=%d\n"),newNodeId); // Node ID assigned
+ return true;
+ } else {
+ TRANSPORT_DEBUG(PSTR("!TSF:SID:FAIL,ID=%d\n"),newNodeId); // ID is invalid, cannot assign ID
setIndication(INDICATION_ERR_NET_FULL);
- // Nothing else we can do...
- transportSwitchSM(stFailure);
+ _transportConfig.nodeId = AUTO;
+ return false;
}
}
-bool transportRouteMessage(MyMessage &message) {
- uint8_t destination = message.destination;
- uint8_t route;
-
+bool transportRouteMessage(MyMessage &message)
+{
+ const uint8_t destination = message.destination;
+ uint8_t route = _transportConfig.parentNodeId; // by default, all traffic is routed via parent node
+
if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) {
- debug(PSTR("!TSP:FPAR:ACTIVE (msg not send)\n"));
+ TRANSPORT_DEBUG(PSTR("!TSF:RTE:FPAR ACTIVE\n")); // find parent active, message not sent
// request to send a non-BC message while finding parent active, abort
return false;
}
if (destination == GATEWAY_ADDRESS) {
- route = _nc.parentNodeId; // message to GW always routes via parent
- }
- else if (destination == BROADCAST_ADDRESS) {
+ route = _transportConfig.parentNodeId; // message to GW always routes via parent
+ } else if (destination == BROADCAST_ADDRESS) {
route = BROADCAST_ADDRESS; // message to BC does not require routing
- }
- else {
- #if defined(MY_REPEATER_FEATURE)
- // destination not GW & not BC, get route
- route = hwReadConfig(EEPROM_ROUTES_ADDRESS + destination);
- if (route == AUTO) {
- // route unknown
- if (message.last != _nc.parentNodeId) {
- // message not from parent, i.e. child node - route it to parent
- debug(PSTR("!TSP:ROUTING:DEST UNKNOWN (dest=%d, STP=%d)\n"), destination, _nc.parentNodeId);
- route = _nc.parentNodeId;
- }
- else {
- // route unknown and msg received from parent, send it to destination assuming in rx radius
- route = destination;
- }
+ } else {
+#if defined(MY_REPEATER_FEATURE)
+ // destination not GW & not BC, get route
+ route = transportGetRoute(destination);
+ if (route == AUTO) {
+ TRANSPORT_DEBUG(PSTR("!TSF:RTE:%d UNKNOWN\n"), destination); // route unknown
+#if !defined(MY_GATEWAY_FEATURE)
+ if (message.last != _transportConfig.parentNodeId) {
+ // message not from parent, i.e. child node - route it to parent
+ route = _transportConfig.parentNodeId;
+ } else {
+ // route unknown and msg received from parent, send it to destination assuming in rx radius
+ route = destination;
}
- #else
- route = _nc.parentNodeId; // not a repeater, all traffic routed via parent
- #endif
+#else
+ // if GW, all unknown destinations are directly addressed
+ route = destination;
+#endif
+ }
+#else
+ route = _transportConfig.parentNodeId; // not a repeater, all traffic routed via parent
+#endif
}
// send message
- bool ok = transportSendWrite(route, message);
- #if !defined(MY_GATEWAY_FEATURE)
- // update counter
- if (route == _nc.parentNodeId) {
- if (!ok) {
- setIndication(INDICATION_ERR_TX);
- _transportSM.failedUplinkTransmissions++;
- }
- else _transportSM.failedUplinkTransmissions = 0;
+ const bool result = transportSendWrite(route, message);
+#if !defined(MY_GATEWAY_FEATURE)
+ // update counter
+ if (route == _transportConfig.parentNodeId) {
+ if (!result) {
+ setIndication(INDICATION_ERR_TX);
+ _transportSM.failedUplinkTransmissions++;
+ } else {
+ _transportSM.failedUplinkTransmissions = 0u;
}
- #else
- if(!ok) setIndication(INDICATION_ERR_TX);
-
- #endif
+ }
+#else
+ if(!result) {
+ setIndication(INDICATION_ERR_TX);
+ }
+#endif
- return ok;
+ return result;
}
-bool transportSendRoute(MyMessage &message) {
- if (isTransportOK()) {
- return transportRouteMessage(message);
- }
- else {
+bool transportSendRoute(MyMessage &message)
+{
+ bool result = false;
+ if (isTransportReady()) {
+ result = transportRouteMessage(message);
+ } else {
// TNR: transport not ready
- debug(PSTR("!TSP:SEND:TNR\n"));
- return false;
+ TRANSPORT_DEBUG(PSTR("!TSF:SND:TNR\n"));
}
+ return result;
}
// only be used inside transport
-bool transportWait(uint32_t ms, uint8_t cmd, uint8_t msgtype){
- uint32_t enter = hwMillis();
+bool transportWait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgType)
+{
+ const uint32_t enterMS = hwMillis();
// invalidate msg type
- _msg.type = !msgtype;
+ _msg.type = !msgType;
bool expectedResponse = false;
- while ((hwMillis() - enter < ms) && !expectedResponse) {
+ while ((hwMillis() - enterMS < waitingMS) && !expectedResponse) {
// process incoming messages
transportProcessFIFO();
- #if defined(ARDUINO_ARCH_ESP8266)
- yield();
- #endif
- expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgtype);
+ doYield();
+ expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgType);
}
return expectedResponse;
}
-uint8_t transportPingNode(uint8_t targetId) {
- if(!_transportSM.pingActive){
- if(targetId == _nc.nodeId) {
- // ping to ourself, pingActive remains false
- return 0;
+uint8_t transportPingNode(const uint8_t targetId)
+{
+ if(!_transportSM.pingActive) {
+ TRANSPORT_DEBUG(PSTR("TSF:PNG:SEND,TO=%d\n"), targetId);
+ if(targetId == _transportConfig.nodeId) {
+ // pinging self
+ _transportSM.pingResponse = 0u;
+ } else {
+ _transportSM.pingActive = true;
+ _transportSM.pingResponse = INVALID_HOPS;
+ (void)transportRouteMessage(build(_msgTmp, targetId, NODE_SENSOR_ID, C_INTERNAL,
+ I_PING).set((uint8_t)0x01));
+ // Wait for ping reply or timeout
+ (void)transportWait(2000, C_INTERNAL, I_PONG);
}
- _transportSM.pingActive = true;
- _transportSM.pingResponse = INVALID_HOPS;
- debug(PSTR("TSP:PING:SEND (dest=%d)\n"), targetId);
- transportRouteMessage(build(_msgTmp, _nc.nodeId, targetId, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set((uint8_t)0x01));
- // Wait for ping reply or timeout
- transportWait(2000, C_INTERNAL, I_PONG);
// make sure missing I_PONG msg does not block pinging function by leaving pignActive=true
_transportSM.pingActive = false;
return _transportSM.pingResponse;
- }
- else {
+ } else {
+ TRANSPORT_DEBUG(PSTR("!TSF:PNG:ACTIVE\n")); // ping active, cannot start new ping
return INVALID_HOPS;
}
}
-void transportClearRoutingTable() {
- for (uint8_t i = 0; i != 255; i++) {
- hwWriteConfig(EEPROM_ROUTES_ADDRESS + i, BROADCAST_ADDRESS);
- }
- debug(PSTR("TSP:CRT:OK\n")); // clear routing table
-}
-
-uint32_t transportGetHeartbeat() {
+uint32_t transportGetHeartbeat(void)
+{
return transportTimeInState();
}
-void transportProcessMessage() {
- (void)signerCheckTimer(); // Manage signing timeout
-
- uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
- (void)payloadLength; // currently not used, but good to test for CRC-ok but corrupt msgs
-
+void transportProcessMessage(void)
+{
+ // Manage signing timeout
+ (void)signerCheckTimer();
+ // receive message
setIndication(INDICATION_RX);
+ uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
+ // get message length and limit size
+
+ const uint8_t msgLength = min(mGetLength(_msg), (uint8_t)MAX_PAYLOAD);
+ // calculate expected length
+ const uint8_t expectedMessageLength = HEADER_SIZE + (mGetSigned(_msg) ? MAX_PAYLOAD : msgLength);
+#if defined(MY_RF24_ENABLE_ENCRYPTION)
+ // payload length = a multiple of blocksize length for decrypted messages, i.e. cannot be used for payload length check
+ payloadLength = expectedMessageLength;
+#endif
+ const uint8_t command = mGetCommand(_msg);
+ const uint8_t type = _msg.type;
+ const uint8_t sender = _msg.sender;
+ const uint8_t last = _msg.last;
+ const uint8_t destination = _msg.destination;
+
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:READ,%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
+ sender, last, destination, _msg.sensor, command, type, mGetPayloadType(_msg), msgLength,
+ mGetSigned(_msg), _msg.getString(_convBuf));
+
+ // Reject payloads with incorrect length
+ if (payloadLength != expectedMessageLength) {
+ setIndication(INDICATION_ERR_LENGTH);
+ TRANSPORT_DEBUG(PSTR("!TSF:MSG:LEN,%d!=%d\n"), payloadLength,
+ expectedMessageLength); // invalid payload length
+ return;
+ }
- uint8_t command = mGetCommand(_msg);
- uint8_t type = _msg.type;
- uint8_t sender = _msg.sender;
- uint8_t last = _msg.last;
- uint8_t destination = _msg.destination;
-
- debug(PSTR("TSP:MSG:READ %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
- sender, last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf));
-
- // verify protocol version
- if(mGetVersion(_msg) != PROTOCOL_VERSION) {
+ // Reject messages with incorrect protocol version
+ if (mGetVersion(_msg) != PROTOCOL_VERSION) {
setIndication(INDICATION_ERR_VERSION);
- debug(PSTR("!TSP:MSG:PVER mismatch\n")); // protocol version
+ TRANSPORT_DEBUG(PSTR("!TSF:MSG:PVER,%d=%d\n"), mGetVersion(_msg),
+ PROTOCOL_VERSION); // protocol version mismatch
return;
}
-
- // Reject massages that do not pass verification
+
+ // Reject messages that do not pass verification
if (!signerVerifyMsg(_msg)) {
setIndication(INDICATION_ERR_SIGN);
- debug(PSTR("!TSP:MSG:SIGN verify fail\n"));
- return;
+ TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN VERIFY FAIL\n"));
+ return;
}
-
- if (destination == _nc.nodeId) {
- // This message is addressed to this node
+ // update routing table if msg not from parent
+#if defined(MY_REPEATER_FEATURE)
+#if !defined(MY_GATEWAY_FEATURE)
+ if (last != _transportConfig.parentNodeId) {
+#else
+ // GW doesn't have parent
+ {
+#endif
+ // Message is from one of the child nodes and not sent from this node. Add it to routing table.
+ if (sender != _transportConfig.nodeId)
+ {
+ transportSetRoute(sender, last);
+ }
+ }
+#endif // MY_REPEATER_FEATURE
+
+ // set message received flag
+ _transportSM.msgReceived = true;
+
+ // Is message addressed to this node?
+ if (destination == _transportConfig.nodeId) {
// prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes)
- mSetLength(_msg, min(mGetLength(_msg),MAX_PAYLOAD));
+ mSetLength(_msg, min(mGetLength(_msg),(uint8_t)MAX_PAYLOAD));
// null terminate data
- _msg.data[mGetLength(_msg)] = 0x00;
-
- // update routing table if msg not from parent
- #if defined(MY_REPEATER_FEATURE)
- if (last != _nc.parentNodeId) {
- // Message is from one of the child nodes. Add it to routing table.
- hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
- }
- #endif
-
+ _msg.data[msgLength] = 0u;
// Check if sender requests an ack back.
if (mGetRequestAck(_msg)) {
- _msgTmp = _msg; // Copy message
- mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop)
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK REQ\n")); // ACK requested
+ _msgTmp = _msg; // Copy message
+ mSetRequestAck(_msgTmp,
+ false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(_msgTmp, true); // set ACK flag
- _msgTmp.sender = _nc.nodeId;
+ _msgTmp.sender = _transportConfig.nodeId;
_msgTmp.destination = sender;
- // send ACK
- debug(PSTR("TSP:MSG:ACK msg\n"));
- // use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply
- transportSendRoute(_msgTmp);
- }
+ // send ACK, use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply
+ (void)transportSendRoute(_msgTmp);
+ }
if(!mGetAck(_msg)) {
// only process if not ACK
if (command == C_INTERNAL) {
@@ -470,211 +666,303 @@ void transportProcessMessage() {
if (signerProcessInternal(_msg)) {
return; // Signer processing indicated no further action needed
}
- #if !defined(MY_GATEWAY_FEATURE)
- if (type == I_ID_RESPONSE) {
- #if (MY_NODE_ID == AUTO)
- // only active if node ID dynamic
- transportAssignNodeID(_msg.getByte());
- #endif
+#if !defined(MY_GATEWAY_FEATURE)
+ if (type == I_ID_RESPONSE) {
+#if (MY_NODE_ID == AUTO)
+ // only active if node ID dynamic
+ (void)transportAssignNodeID(_msg.getByte());
+#endif
return; // no further processing required
- }
- if (type == I_FIND_PARENT_RESPONSE) {
- #if !defined(MY_GATEWAY_FEATURE)
- // Reply to a I_FIND_PARENT message. Check if the distance is shorter than we already have.
- uint8_t distance = _msg.getByte();
- debug(PSTR("TSP:MSG:FPAR RES (ID=%d, dist=%d)\n"), sender, distance);
- if (isValidDistance(distance)) {
- // Distance to gateway is one more for us w.r.t. parent
- distance++;
- // update settings if distance shorter or preferred parent found
- if (((isValidDistance(distance) && distance < _nc.distance) || (!_autoFindParent && sender == MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) {
- // Found a neighbor closer to GW than previously found
- if (!_autoFindParent && sender == MY_PARENT_NODE_ID) {
- _transportSM.preferredParentFound = true;
- debug(PSTR("TSP:MSG:FPAR (PPAR FOUND)\n")); // preferred parent found
- }
- _nc.distance = distance;
- _nc.parentNodeId = sender;
- debug(PSTR("TSP:MSG:PAR OK (ID=%d, dist=%d)\n"), _nc.parentNodeId, _nc.distance);
+ }
+ if (type == I_FIND_PARENT_RESPONSE) {
+#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_PARENT_NODE_IS_STATIC)
+ if (_transportSM.findingParentNode) { // only process if find parent active
+ // Reply to a I_FIND_PARENT_REQUEST message. Check if the distance is shorter than we already have.
+ uint8_t distance = _msg.getByte();
+ if (isValidDistance(distance)) {
+ distance++; // Distance to gateway is one more for us w.r.t. parent
+ // update settings if distance shorter or preferred parent found
+ if (((isValidDistance(distance) && distance < _transportConfig.distanceGW) || (!_autoFindParent &&
+ sender == (uint8_t)MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) {
+ // Found a neighbor closer to GW than previously found
+ if (!_autoFindParent && sender == (uint8_t)MY_PARENT_NODE_ID) {
+ _transportSM.preferredParentFound = true;
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR PREF\n")); // find parent, preferred parent found
}
+ _transportConfig.distanceGW = distance;
+ _transportConfig.parentNodeId = sender;
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR OK,ID=%d,D=%d\n"), _transportConfig.parentNodeId,
+ _transportConfig.distanceGW);
}
- return;
- #endif
+ }
+ } else {
+ TRANSPORT_DEBUG(PSTR("!TSF:MSG:FPAR INACTIVE\n")); // find parent response received, but inactive
}
- #endif
+ return;
+#endif
+ }
+#endif
// general
if (type == I_PING) {
- debug(PSTR("TSP:MSG:PINGED (ID=%d, hops=%d)\n"), sender, _msg.getByte());
- transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PONG, false).set((uint8_t)0x01));
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:PINGED,ID=%d,HP=%d\n"), sender, _msg.getByte()); // node pinged
+#if defined(MY_GATEWAY_FEATURE) && (F_CPU>16000000)
+ // delay for fast GW and slow nodes
+ delay(5);
+#endif
+ (void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
+ I_PONG).set((uint8_t)1));
return; // no further processing required
}
if (type == I_PONG) {
if (_transportSM.pingActive) {
_transportSM.pingActive = false;
_transportSM.pingResponse = _msg.getByte();
- debug(PSTR("TSP:MSG:PONG RECV (hops=%d)\n"), _transportSM.pingResponse);
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:PONG RECV,HP=%d\n"), _transportSM.pingResponse); // pong received
+ } else {
+ TRANSPORT_DEBUG(PSTR("!TSF:MSG:PONG RECV,INACTIVE\n")); // pong received, but !pingActive
}
return; // no further processing required
}
-
if (_processInternalMessages()) {
return; // no further processing required
}
} else if (command == C_STREAM) {
- #if defined(MY_OTA_FIRMWARE_FEATURE)
- if(firmwareOTAUpdateProcess()){
- return; // OTA FW update processing indicated no further action needed
- }
- #endif
+#if defined(MY_OTA_FIRMWARE_FEATURE)
+ if(firmwareOTAUpdateProcess()) {
+ return; // OTA FW update processing indicated no further action needed
+ }
+#endif
}
+ } else {
+ TRANSPORT_DEBUG(
+ PSTR("TSF:MSG:ACK\n")); // received message is ACK, no internal processing, handover to msg callback
}
- #if defined(MY_GATEWAY_FEATURE)
- // Hand over message to controller
- gatewayTransportSend(_msg);
- #else
- // Call incoming message callback if available
- if (receive) {
- receive(_msg);
- }
- #endif
- return;
- }
- else if (destination == BROADCAST_ADDRESS) {
- // broadcast
- debug(PSTR("TSP:MSG:BC\n"));
+#if defined(MY_GATEWAY_FEATURE)
+ // Hand over message to controller
+ (void)gatewayTransportSend(_msg);
+#endif
+ // Call incoming message callback if available
+ if (receive) {
+ receive(_msg);
+ }
+ } else if (destination == BROADCAST_ADDRESS) {
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:BC\n")); // broadcast msg
if (command == C_INTERNAL) {
- if (isTransportOK()) {
+ if (isTransportReady()) {
// only reply if node is fully operational
- if (type == I_FIND_PARENT) {
- #if defined(MY_REPEATER_FEATURE)
- if (sender != _nc.parentNodeId) { // no circular reference
- debug(PSTR("TSP:MSG:FPAR REQ (sender=%d)\n"), sender); // FPR: find parent request
- // node is in our range, update routing table - important if node has new repeater as parent
- hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, sender);
- // check if uplink functional - node can only be parent node if link to GW functional
- // this also prevents circular references in case GW ooo
- if(transportCheckUplink(false)){
- #if defined(MY_REPEATER_FEATURE)
- _transport_lastUplinkCheck = hwMillis();
- #endif
- debug(PSTR("TSP:MSG:GWL OK\n")); // GW uplink ok
- // delay minimizes collisions
- delay(hwMillis() & 0x3ff);
- transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance));
- }
+ if (type == I_FIND_PARENT_REQUEST) {
+#if defined(MY_REPEATER_FEATURE)
+ if (sender != _transportConfig.parentNodeId) { // no circular reference
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR REQ,ID=%d\n"), sender); // FPAR: find parent request
+ // check if uplink functional - node can only be parent node if link to GW functional
+ // this also prevents circular references in case GW ooo
+ if (transportCheckUplink()) {
+ _transportSM.lastUplinkCheck = hwMillis();
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok
+ // random delay minimizes collisions
+ delay(hwMillis() & 0x3ff);
+ (void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
+ I_FIND_PARENT_RESPONSE).set(_transportConfig.distanceGW));
+ } else {
+ TRANSPORT_DEBUG(PSTR("!TSF:MSG:GWL FAIL\n")); // GW uplink fail, do not respond to parent request
}
-
- #endif
- return; // no further processing required
+ }
+#endif
+ return; // no further processing required, do not forward
}
+ } // isTransportReady
+ if (type == I_FIND_PARENT_RESPONSE) {
+ return; // no further processing required, do not forward
}
- if (type == I_DISCOVER) {
- if (last == _nc.parentNodeId) {
+#if !defined(MY_GATEWAY_FEATURE)
+ if (type == I_DISCOVER_REQUEST) {
+ if (last == _transportConfig.parentNodeId) {
// random wait to minimize collisions
delay(hwMillis() & 0x3ff);
- transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_RESPONSE, false).set(_nc.parentNodeId));
+ (void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
+ I_DISCOVER_RESPONSE).set(_transportConfig.parentNodeId));
// no return here (for fwd if repeater)
}
}
+#endif
}
// controlled BC relay
- #if defined(MY_REPEATER_FEATURE)
- // controlled BC repeating: forward only if message received from parent and sender not self to prevent circular fwds
- if(last == _nc.parentNodeId && sender != _nc.nodeId && isTransportOK()){
- debug(PSTR("TSP:MSG:FWD BC MSG\n")); // forward BC msg
- transportRouteMessage(_msg);
- }
- #endif
-
- // Call incoming message callback if available, but only if message received from parent
- if (command != C_INTERNAL && last == _nc.parentNodeId && receive) {
- receive(_msg);
+#if defined(MY_REPEATER_FEATURE)
+ // controlled BC repeating: forward only if message received from parent and sender not self to prevent circular fwds
+ if(last == _transportConfig.parentNodeId && sender != _transportConfig.nodeId &&
+ isTransportReady()) {
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:FWD BC MSG\n")); // controlled broadcast msg forwarding
+ (void)transportRouteMessage(_msg);
}
-
- }
- else {
- // msg not no us and not BC, relay msg
- #if defined(MY_REPEATER_FEATURE)
- if (isTransportOK()) {
- debug(PSTR("TSP:MSG:REL MSG\n")); // relay msg
- // update routing table if message not received from parent
- if (last != _nc.parentNodeId) {
- hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, last);
+#endif
+
+ // Callback for BC, only for non-internal messages
+ if (command != C_INTERNAL) {
+#if !defined(MY_GATEWAY_FEATURE)
+ // only proceed if message received from parent
+ if (last != _transportConfig.parentNodeId) {
+ return;
+ }
+#endif
+#if defined(MY_GATEWAY_FEATURE)
+ // Hand over message to controller
+ (void)gatewayTransportSend(_msg);
+#endif
+ if (receive) {
+ receive(_msg);
}
+ }
+
+ } else {
+ // msg not to us and not BC, relay msg
+#if defined(MY_REPEATER_FEATURE)
+ if (isTransportReady()) {
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:REL MSG\n")); // relay msg
if (command == C_INTERNAL) {
if (type == I_PING || type == I_PONG) {
uint8_t hopsCnt = _msg.getByte();
if (hopsCnt != MAX_HOPS) {
- debug(PSTR("TSP:MSG:REL PxNG (hops=%d)\n"), hopsCnt);
- _msg.set((uint8_t)(hopsCnt + 1));
+ TRANSPORT_DEBUG(PSTR("TSF:MSG:REL PxNG,HP=%d\n"), hopsCnt);
+ hopsCnt++;
+ _msg.set(hopsCnt);
}
}
}
// Relay this message to another node
- transportRouteMessage(_msg);
+ (void)transportRouteMessage(_msg);
}
- #else
- debug(PSTR("!TSM:MSG:REL MSG, but not a repeater\n"));
- #endif
+#else
+ TRANSPORT_DEBUG(PSTR("!TSF:MSG:REL MSG,NREP\n")); // message relaying request, but not a repeater
+#endif
}
}
-inline void transportProcessFIFO() {
- if (_transportSM.transportActive) {
- #if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_REPEATER_FEATURE)
- if (hwMillis() > (_transport_lastSanityCheck + MY_TRANSPORT_SANITY_CHECK_INTERVAL)) {
- _transport_lastSanityCheck = hwMillis();
- if (!transportSanityCheck()) {
- debug(PSTR("!TSP:SANCHK:FAIL\n")); // sanity check fail
- transportSwitchSM(stFailure);
- return;
- }
- else {
- debug(PSTR("TSP:SANCHK:OK\n")); // sanity check ok
- }
- }
- #endif
+void transportInvokeSanityCheck(void)
+{
+ if (!transportSanityCheck()) {
+ TRANSPORT_DEBUG(PSTR("!TSF:SAN:FAIL\n")); // sanity check fail
+ transportSwitchSM(stFailure);
+ } else {
+ TRANSPORT_DEBUG(PSTR("TSF:SAN:OK\n")); // sanity check ok
}
- else {
- // transport not active, nothing to be done
+}
+
+void transportProcessFIFO(void)
+{
+ if (!_transportSM.transportActive) {
+ // transport not active, no further processing required
return;
}
+
+#if defined(MY_TRANSPORT_SANITY_CHECK)
+ if (hwMillis() - _lastSanityCheck > MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS) {
+ _lastSanityCheck = hwMillis();
+ transportInvokeSanityCheck();
+ }
+#endif
+
uint8_t _processedMessages = MAX_SUBSEQ_MSGS;
// process all msgs in FIFO or counter exit
while (transportAvailable() && _processedMessages--) {
transportProcessMessage();
}
- #if defined(MY_OTA_FIRMWARE_FEATURE)
- if (isTransportOK()) {
- // only process if transport ok
- firmwareOTAUpdateRequest();
- }
- #endif
+#if defined(MY_OTA_FIRMWARE_FEATURE)
+ if (isTransportReady()) {
+ // only process if transport ok
+ firmwareOTAUpdateRequest();
+ }
+#endif
}
-bool transportSendWrite(uint8_t to, MyMessage &message) {
- // set protocol version and update last
- mSetVersion(message, PROTOCOL_VERSION);
- message.last = _nc.nodeId;
-
+bool transportSendWrite(const uint8_t to, MyMessage &message)
+{
+ message.last = _transportConfig.nodeId; // Update last
// sign message if required
if (!signerSignMsg(message)) {
- debug(PSTR("!TSP:MSG:SIGN fail\n"));
+ TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN FAIL\n"));
setIndication(INDICATION_ERR_SIGN);
return false;
}
-
+
// msg length changes if signed
- uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message);
-
+ const uint8_t totalMsgLength = HEADER_SIZE + ( mGetSigned(message) ? MAX_PAYLOAD : mGetLength(
+ message) );
+
// send
setIndication(INDICATION_TX);
- bool ok = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length));
-
- debug(PSTR("%sTSP:MSG:SEND %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,ft=%d,st=%s:%s\n"),
- (ok || to == BROADCAST_ADDRESS ? "" : "!"),message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type,
- mGetPayloadType(message), mGetLength(message), mGetSigned(message), _transportSM.failedUplinkTransmissions, to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(_convBuf));
-
- return (ok || to==BROADCAST_ADDRESS);
+ bool result = transportSend(to, &message, min((uint8_t)MAX_MESSAGE_LENGTH, totalMsgLength));
+ // broadcasting (workaround counterfeits)
+ result |= (to == BROADCAST_ADDRESS);
+
+ TRANSPORT_DEBUG(PSTR("%sTSF:MSG:SEND,%d-%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,ft=%d,st=%s:%s\n"),
+ (result ? "" : "!"), message.sender, message.last, to, message.destination, message.sensor,
+ mGetCommand(message), message.type,
+ mGetPayloadType(message), mGetLength(message), mGetSigned(message),
+ _transportSM.failedUplinkTransmissions, (result ? "OK" : "NACK"), message.getString(_convBuf));
+
+ return result;
+}
+
+void transportRegisterReadyCallback(transportCallback_t cb)
+{
+ transportReady_cb = cb;
+}
+
+uint8_t transportGetNodeId(void)
+{
+ return _transportConfig.nodeId;
+}
+uint8_t transportGetParentNodeId(void)
+{
+ return _transportConfig.parentNodeId;
+}
+uint8_t transportGetDistanceGW(void)
+{
+ return _transportConfig.distanceGW;
+}
+
+
+void transportClearRoutingTable(void)
+{
+ for (uint16_t i = 0; i < SIZE_ROUTES; i++) {
+ transportSetRoute((uint8_t)i, BROADCAST_ADDRESS);
+ }
+ transportSaveRoutingTable(); // save cleared routing table to EEPROM (if feature enabled)
+ TRANSPORT_DEBUG(PSTR("TSF:CRT:OK\n")); // clear routing table
}
+void transportLoadRoutingTable(void)
+{
+#if defined(MY_RAM_ROUTING_TABLE_ENABLED)
+ hwReadConfigBlock((void*)&_transportRoutingTable.route, (void*)EEPROM_ROUTES_ADDRESS, SIZE_ROUTES);
+ TRANSPORT_DEBUG(PSTR("TSF:LRT:OK\n")); // load routing table
+#endif
+}
+
+void transportSaveRoutingTable(void)
+{
+#if defined(MY_RAM_ROUTING_TABLE_ENABLED)
+ hwWriteConfigBlock((void*)&_transportRoutingTable.route, (void*)EEPROM_ROUTES_ADDRESS, SIZE_ROUTES);
+ TRANSPORT_DEBUG(PSTR("TSF:SRT:OK\n")); // save routing table
+#endif
+}
+
+void transportSetRoute(const uint8_t node, const uint8_t route)
+{
+#if defined(MY_RAM_ROUTING_TABLE_ENABLED)
+ _transportRoutingTable.route[node] = route;
+#else
+ hwWriteConfig(EEPROM_ROUTES_ADDRESS + node, route);
+#endif
+}
+
+uint8_t transportGetRoute(const uint8_t node)
+{
+ uint8_t result;
+#if defined(MY_RAM_ROUTING_TABLE_ENABLED)
+ result = _transportRoutingTable.route[node];
+#else
+ result = hwReadConfig(EEPROM_ROUTES_ADDRESS + node);
+#endif
+ return result;
+}
diff --git a/core/MyTransport.h b/core/MyTransport.h
index c4800e9c1..02a3f0eca 100644
--- a/core/MyTransport.h
+++ b/core/MyTransport.h
@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad
- * Copyright (C) 2013-2015 Sensnology AB
+ * Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
@@ -17,154 +17,355 @@
* version 2 as published by the Free Software Foundation.
*/
- /**
- * @file MyTransport.h
- *
- * @brief API declaration for MyTransport
- */
+/**
+* @file MyTransport.h
+*
+* @defgroup MyTransportgrp MyTransport
+* @ingroup internals
+* @{
+*
+* Transport-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE
+* - [!] Exclamation mark is prepended in case of error
+* - SYSTEM:
+* - TSM: messages emitted by the transport state machine
+* - TSF: messages emitted by transport support functions
+* - SUB SYSTEMS:
+* - Transport state machine (TSM)
+* - TSM:INIT from stInit Initialize transport and radio
+* - TSM:FPAR from stParent Find parent
+* - TSM:ID from stID Check/request node ID, if dynamic node ID set
+* - TSM:UPL from stUplink Verify uplink connection by pinging GW
+* - TSM:READY from stReady Transport is ready and fully operational
+* - TSM:FAIL from stFailure Failure in transport link or transport HW
+* - Transport support function (TSF)
+* - TSF:CKU from @ref transportCheckUplink(), checks connection to GW
+* - TSF:SID from @ref transportAssignNodeID(), assigns node ID
+* - TSF:PNG from @ref transportPingNode(), pings a node
+* - TSF:WUR from @ref transportWaitUntilReady(), waits until transport is ready
+* - TSF:CRT from @ref transportClearRoutingTable(), clears routing table stored in EEPROM
+* - TSF:LRT from @ref transportLoadRoutingTable(), loads RAM routing table from EEPROM (only GW/repeaters)
+* - TSF:SRT from @ref transportSaveRoutingTable(), saves RAM routing table to EEPROM (only GW/repeaters)
+* - TSF:MSG from @ref transportProcessMessage(), processes incoming message
+* - TSF:SAN from @ref transportInvokeSanityCheck(), calls transport-specific sanity check
+* - TSF:RTE from @ref transportRouteMessage(), sends message
+* - TSF:SND from @ref transportSendRoute(), sends message if transport is ready (exposed)
+
+*
+* Transport debug log messages:
+*
+* |E| SYS | SUB | Message | Comment
+* |-|------|-----------|-----------------------|---------------------------------------------------------------------
+* | | TSM | INIT | | Transition to stInit state
+* | | TSM | INIT | STATID=%%d | Node ID is static
+* | | TSM | INIT | TSP OK | Transport device configured and fully operational
+* | | TSM | INIT | GW MODE | Node is set up as GW, thus omitting ID and findParent states
+* |!| TSM | INIT | TSP FAIL | Transport device initialization failed
+* | | TSM | FPAR | | Transition to stParent state
+* | | TSM | FPAR | STATP=%%d | Static parent set, skip finding parent
+* | | TSM | FPAR | OK | Parent node identified
+* |!| TSM | FPAR | NO REPLY | No potential parents replied to find parent request
+* |!| TSM | FPAR | FAIL | Finding parent failed
+* | | TSM | ID | | Transition to stID state
+* | | TSM | ID | OK,ID=%%d | Node ID is valid
+* | | TSM | ID | REQ | Request node ID from controller
+* |!| TSM | ID | FAIL,ID=%%d | ID verification failed, ID invalid
+* | | TSM | UPL | | Transition to stUplink state
+* | | TSM | UPL | OK | Uplink OK, GW returned ping
+* | | TSF | UPL | DGWC,O=%%d,N=%%d | Uplink check revealed changed network topology, old distance (O), new distance (N)
+* |!| TSM | UPL | FAIL | Uplink check failed, i.e. GW could not be pinged
+* | | TSM | READY | SRT | Save routing table
+* | | TSM | READY | ID=%%d,PAR=%%d,DIS=%%d| Transition to stReady Transport ready, node ID (ID), parent node ID (PAR), distance to GW (DIS)
+* |!| TSM | READY | UPL FAIL,SNP | Too many failed uplink transmissions, search new parent
+* |!| TSM | READY | FAIL,STATP | Too many failed uplink transmissions, static parent enforced
+* | | TSM | FAIL | CNT=%%d | Transition to stFailure state, consecutive failure counter (CNT)
+* | | TSM | FAIL | PDT | Power-down transport
+* | | TSM | FAIL | RE-INIT | Attempt to re-initialize transport
+* | | TSF | CKU | OK | Uplink OK
+* | | TSF | CKU | OK,FCTRL | Uplink OK, flood control prevents pinging GW in too short intervals
+* | | TSF | CKU | DGWC,O=%%d,N=%%d | Uplink check revealed changed network topology, old distance (O), new distance (N)
+* | | TSF | CKU | FAIL | No reply received when checking uplink
+* | | TSF | SID | OK,ID=%%d | Node ID assigned
+* |!| TSF | SID | FAIL,ID=%%d | Assigned ID is invalid
+* | | TSF | PNG | SEND,TO=%%d | Send ping to destination (TO)
+* | | TSF | WUR | MS=%%lu | Wait until transport ready, timeout (MS)
+* | | TSF | MSG | ACK REQ | ACK message requested
+* | | TSF | MSG | ACK | ACK message, do not proceed but forward to callback
+* | | TSF | MSG | FPAR RES,ID=%%d,D=%%d | Response to find parent received from node (ID) with distance (D) to GW
+* | | TSF | MSG | FPAR PREF FOUND | Preferred parent found, i.e. parent defined via MY_PARENT_NODE_ID
+* | | TSF | MSG | FPAR OK,ID=%%d,D=%%d | Find parent response from node (ID) is valid, distance (D) to GW
+* | | TSF | MSG | FPAR INACTIVE | Find parent response received, but no find parent request active, skip response
+* | | TSF | MSG | FPAR REQ,ID=%%d | Find parent request from node (ID)
+* | | TSF | MSG | PINGED,ID=%%d,HP=%%d | Node pinged by node (ID) with (HP) hops
+* | | TSF | MSG | PONG RECV,HP=%%d | Pinged node replied with (HP) hops
+* | | TSF | MSG | BC | Broadcast message received
+* | | TSF | MSG | GWL OK | Link to GW ok
+* | | TSF | MSG | FWD BC MSG | Controlled broadcast message forwarding
+* | | TSF | MSG | REL MSG | Relay message
+* | | TSF | MSG | REL PxNG,HP=%%d | Relay PING/PONG message, increment hop counter (HP)
+* |!| TSF | MSG | LEN,%%d!=%%d | Invalid message length, (actual!=expected)
+* |!| TSF | MSG | PVER,%%d!=%%d | Message protocol version mismatch (actual!=expected)
+* |!| TSF | MSG | SIGN VERIFY FAIL | Signing verification failed
+* |!| TSF | MSG | REL MSG,NORP | Node received a message for relaying, but node is not a repeater, message skipped
+* |!| TSF | MSG | SIGN FAIL | Signing message failed
+* |!| TSF | MSG | GWL FAIL | GW uplink failed
+* | | TSF | SAN | OK | Sanity check passed
+* |!| TSF | SAN | FAIL | Sanity check failed, attempt to re-initialize radio
+* | | TSF | CRT | OK | Clearing routing table successful
+* | | TSF | LRT | OK | Loading routing table successful
+* | | TSF | SRT | OK | Saving routing table successful
+* |!| TSF | RTE | FPAR ACTIVE | Finding parent active, message not sent
+* |!| TSF | RTE | DST %%d UNKNOWN | Routing for destination (DST) unknown, send message to parent
+* |!| TSF | SND | TNR | Transport not ready, message cannot be sent
+*
+* Incoming / outgoing messages:
+*
+* See here for more detail on the format and definitons.
+*
+* Receiving a message
+* - TSF:MSG:READ,sender-last-destination,s=%%d,c=%%d,t=%%d,pt=%%d,l=%%d,sg=%%d:%%s
+*
+* Sending a message
+* - [!]TSF:MSG:SEND,sender-last-next-destination,s=%%d,c=%%d,t=%%d,pt=%%d,l=%%d,sg=%%d,ft=%%d,st=%%s:%%s
+*
+* Message fields:
+* - s=sensor ID
+* - c=command
+* - t=msg type
+* - pt=payload type
+* - l=length
+* - sg=signing flag
+* - ft=failed uplink transmission counter
+* - st=send status, OK=success, NACK=no radio ACK received
+*
+* @brief API declaration for MyTransport
+*
+*/
+
#ifndef MyTransport_h
#define MyTransport_h
#include "MySensorsCore.h"
+// debug
+#if defined(MY_DEBUG)
+#define TRANSPORT_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
+extern char _convBuf[MAX_PAYLOAD * 2 + 1];
+#else
+#define TRANSPORT_DEBUG(x,...) //!< debug NULL
+#endif
+
+
#if defined(MY_REPEATER_FEATURE)
- #define TRANSMISSION_FAILURES 10 //!< search for a new parent node after this many transmission failures, higher threshold for repeating nodes
+#define MY_TRANSPORT_MAX_TX_FAILURES (10u) //!< search for a new parent node after this many transmission failures, higher threshold for repeating nodes
#else
- #define TRANSMISSION_FAILURES 5 //!< search for a new parent node after this many transmission failures, lower threshold for non-repeating nodes
+#define MY_TRANSPORT_MAX_TX_FAILURES (5u) //!< search for a new parent node after this many transmission failures, lower threshold for non-repeating nodes
+#endif
+
+#define MY_TRANSPORT_MAX_TSM_FAILURES (7u) //!< Max. number of consecutive TSM failure state entries (3bits)
+
+#ifndef MY_TRANSPORT_TIMEOUT_FAILURE_STATE
+#define MY_TRANSPORT_TIMEOUT_FAILURE_STATE (10*1000ul) //!< Duration failure state (in ms)
+#endif
+#ifndef MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE
+#define MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE (60*1000ul) //!< Duration extended failure state (in ms)
+#endif
+#ifndef MY_TRANSPORT_STATE_TIMEOUT_MS
+#define MY_TRANSPORT_STATE_TIMEOUT_MS (2*1000ul) //!< general state timeout (in ms)
+#endif
+#ifndef MY_TRANSPORT_CHKUPL_INTERVAL_MS
+#define MY_TRANSPORT_CHKUPL_INTERVAL_MS (10*1000ul) //!< Interval to re-check uplink (in ms)
+#endif
+#ifndef MY_TRANSPORT_STATE_RETRIES
+#define MY_TRANSPORT_STATE_RETRIES (3u) //!< retries before switching to FAILURE
+#endif
+
+#define AUTO (255u) //!< ID 255 is reserved
+#define BROADCAST_ADDRESS (255u) //!< broadcasts are addressed to ID 255
+#define DISTANCE_INVALID (255u) //!< invalid distance when searching for parent
+#define MAX_HOPS (254u) //!< maximal mumber of hops for ping/pong
+#define INVALID_HOPS (255u) //!< invalid hops
+#define MAX_SUBSEQ_MSGS (5u) //!< Maximum number of subsequentially processed messages in FIFO (to prevent transport deadlock if HW issue)
+
+// parent node check
+#if defined(MY_PARENT_NODE_IS_STATIC) && !defined(MY_PARENT_NODE_ID)
+#error MY_PARENT_NODE_IS_STATIC but no MY_PARENT_NODE_ID defined!
#endif
-#define TIMEOUT_FAILURE_STATE 10000 //!< duration failure state
-#define STATE_TIMEOUT 2000 //!< general state timeout
-#define STATE_RETRIES 3 //!< retries before switching to FAILURE
-#define AUTO 255 //!< ID 255 is reserved for auto initialization of nodeId.
-#define BROADCAST_ADDRESS ((uint8_t)255) //!< broadcasts are addressed to ID 255
-#define DISTANCE_INVALID ((uint8_t)255) //!< invalid distance when searching for parent
-#define MAX_HOPS ((uint8_t)254) //!< maximal mumber of hops for ping/pong
-#define INVALID_HOPS ((uint8_t)255) //!< invalid hops
-#define MAX_SUBSEQ_MSGS 5 //!< Maximum number of subsequentially processed messages in FIFO (to prevent transport deadlock if HW issue)
-#define CHKUPL_INTERVAL ((uint32_t)10000) //!< Minimum time interval to re-check uplink
#define _autoFindParent (bool)(MY_PARENT_NODE_ID == AUTO) //!< returns true if static parent id is undefined
-#define isValidDistance(distance) (bool)(distance!=DISTANCE_INVALID) //!< returns true if distance is valid
-#define isValidParent(parent) (bool)(parent != AUTO) //!< returns true if parent is valid
+#define isValidDistance(_distance) (bool)(_distance!=DISTANCE_INVALID) //!< returns true if distance is valid
+#define isValidParent(_parent) (bool)(_parent != AUTO) //!< returns true if parent is valid
+
+// RX queue
+#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
+#if defined(MY_RADIO_RFM69)
+#error Receive message buffering not supported for RFM69!
+#endif
+#if defined(MY_RS485)
+#error Receive message buffering not supported for RS485!
+#endif
+#elif !defined(MY_RX_MESSAGE_BUFFER_FEATURE) && defined(MY_RX_MESSAGE_BUFFER_SIZE)
+#error Receive message buffering requires message buffering feature enabled!
+#endif
+
+/**
+ * @brief Callback type
+ */
+typedef void(*transportCallback_t)(void);
+
+/**
+ * @brief Node configuration
+ *
+ * This structure stores node-related configurations
+ */
+typedef struct {
+ uint8_t nodeId; //!< Current node id
+ uint8_t parentNodeId; //!< Where this node sends its messages
+ uint8_t distanceGW; //!< This nodes distance to sensor net gateway (number of hops)
+} transportConfig_t;
- /**
+/**
* @brief SM state
*
* This structure stores SM state definitions
*/
-struct State {
- void(*Transition)(); //!< state transition function
- void(*Update)(); //!< state update function
-};
+typedef struct {
+ void(*Transition)(void); //!< state transition function
+ void(*Update)(void); //!< state update function
+} transportState_t;
/**
-* @brief Status variables and SM state
-*
-* This structure stores transport status and SM variables
-*/
+ * @brief Status variables and SM state
+ *
+ * This structure stores transport status and SM variables
+ */
typedef struct {
- State* currentState; //!< pointer to current fsm state
+ // SM variables
+ transportState_t* currentState; //!< pointer to current fsm state
uint32_t stateEnter; //!< state enter timepoint
+ // general transport variables
+ uint32_t lastUplinkCheck; //!< last uplink check, required to prevent GW flooding
+ // 8 bits
bool findingParentNode : 1; //!< flag finding parent node is active
bool preferredParentFound : 1; //!< flag preferred parent found
bool uplinkOk : 1; //!< flag uplink ok
bool pingActive : 1; //!< flag ping active
bool transportActive : 1; //!< flag transport active
- uint8_t reserved : 3; //!< reserved
- uint8_t retries : 4; //!< retries / state re-enter
- uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions
- uint8_t pingResponse; //!< stores hops received in I_PONG
-} __attribute__((packed)) transportSM;
+ uint8_t stateRetries : 3; //!< retries / state re-enter (max 7)
+ // 8 bits
+ uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions (max 15)
+ uint8_t failureCounter : 3; //!< counter for TSM failures (max 7)
+ bool msgReceived : 1; //!< flag message received
+ // 8 bits
+ uint8_t pingResponse; //!< stores I_PONG hops
+} transportSM_t;
+/**
+* @brief RAM routing table
+*/
+typedef struct {
+ uint8_t route[SIZE_ROUTES]; //!< route for node
+} routingTable_t;
// PRIVATE functions
/**
* @brief Initialize SM variables and transport HW
*/
-void stInitTransition();
+void stInitTransition(void);
+/**
+* @brief Initialize transport
+*/
+void stInitUpdate(void);
/**
* @brief Find parent
*/
-void stParentTransition();
+void stParentTransition(void);
/**
* @brief Verify find parent responses
*/
-void stParentUpdate();
+void stParentUpdate(void);
/**
* @brief Send ID request
*/
-void stIDTransition();
+void stIDTransition(void);
/**
* @brief Verify ID response and GW link
*/
-void stIDUpdate();
+void stIDUpdate(void);
/**
* @brief Send uplink ping request
*/
-void stUplinkTransition();
+void stUplinkTransition(void);
+/**
+* @brief Verify uplink response
+*/
+void stUplinkUpdate(void);
/**
* @brief Set transport OK
*/
-void stOKTransition();
+void stReadyTransition(void);
/**
* @brief Monitor transport link
*/
-void stOKUpdate();
+void stReadyUpdate(void);
/**
-* @brief Transport failure and power down radio
+* @brief Transport failure and power down radio
*/
-void stFailureTransition();
+void stFailureTransition(void);
/**
* @brief Re-initialize transport after timeout
*/
-void stFailureUpdate();
+void stFailureUpdate(void);
/**
* @brief Switch SM state
* @param newState New state to switch SM to
*/
-void transportSwitchSM(State& newState);
+void transportSwitchSM(transportState_t& newState);
/**
* @brief Update SM state
*/
-void transportUpdateSM();
+void transportUpdateSM(void);
/**
* @brief Request time in current SM state
* @return ms in current state
*/
-uint32_t transportTimeInState();
-
+uint32_t transportTimeInState(void);
+/**
+* @brief Call transport driver sanity check
+*/
+void transportInvokeSanityCheck(void);
/**
* @brief Process all pending messages in RX FIFO
*/
-void transportProcessFIFO();
+void transportProcessFIFO(void);
/**
* @brief Receive message from RX FIFO and process
*/
-void transportProcessMessage();
+void transportProcessMessage(void);
/**
* @brief Assign node ID
* @param newNodeId New node ID
+* @return true if node ID is valid and successfully assigned
*/
-void transportAssignNodeID(uint8_t newNodeId);
+bool transportAssignNodeID(const uint8_t newNodeId);
/**
* @brief Wait and process messages for a defined amount of time until specified message received
-* @param ms Time to wait and process incoming messages in ms
+* @param waitingMS Time to wait and process incoming messages in ms
* @param cmd Specific command
-* @param msgtype Specific message type
+* @param msgType Specific message type
* @return true if specified command received within waiting time
*/
-bool transportWait(uint32_t ms, uint8_t cmd, uint8_t msgtype);
+bool transportWait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgType);
/**
* @brief Ping node
* @param targetId Node to be pinged
* @return hops from pinged node or 255 if no answer received within 2000ms
*/
-uint8_t transportPingNode(uint8_t targetId);
+uint8_t transportPingNode(const uint8_t targetId);
/**
* @brief Send and route message according to destination
-*
-* This function is used in MyTransport and omits the transport state check, i.e. message can be sent even if transport is !OK
+*
+* This function is used in MyTransport and omits the transport state check, i.e. message can be sent even if transport is not ready
*
* @param message
* @return true if message sent successfully
@@ -180,55 +381,118 @@ bool transportSendRoute(MyMessage &message);
* @brief Send message to recipient
* @param to Recipient of message
* @param message
-* @return true if message sent successfully
+* @return true if message sent successfully
*/
-bool transportSendWrite(uint8_t to, MyMessage &message);
+bool transportSendWrite(const uint8_t to, MyMessage &message);
/**
* @brief Check uplink to GW, includes flooding control
* @param force to override flood control timer
* @return true if uplink ok
*/
-bool transportCheckUplink(bool force);
+bool transportCheckUplink(const bool force = false);
// PUBLIC functions
+/**
+* @brief Wait until transport is ready
+* @param waitingMS timeout in MS, set 0 (default) for no timeout, i.e. wait indefinitely. For a node in standalone mode (optional network connection) set >0 to allow a node entering the main loop() function.
+* @return true if transport is ready
+*/
+bool transportWaitUntilReady(const uint32_t waitingMS = 0);
/**
* @brief Initialize transport and SM
*/
-void transportInitialize();
+void transportInitialize(void);
/**
* @brief Process FIFO msg and update SM
*/
-void transportProcess();
+void transportProcess(void);
+/**
+* @brief Flag transport ready
+* @return true if transport is initialized and ready
+*/
+bool isTransportReady(void);
/**
-* @brief Verify transport status
-* @return true if transport is initialize and ready
+* @brief Flag searching parent ongoing
+* @return true if transport is searching for parent
*/
-bool isTransportOK();
+bool isTransportSearchingParent(void);
+/**
+* @brief Flag TSM extended failure
+* @return true if TSM had too many consecutive failure state entries
+*/
+bool isTransportExtendedFailure(void);
+/**
+* @brief Flag valid message received
+* @return true if valid message received, needs to be reset if used
+*/
+bool isMessageReceived(void);
+/**
+* @brief Reset message received flag
+*/
+void resetMessageReceived(void);
/**
* @brief Clear routing table
*/
-void transportClearRoutingTable();
+void transportClearRoutingTable(void);
/**
-* @brief Return heart beat, i.e. ms in current state
+* @brief Return heart beat
+* @return MS in current state
*/
-uint32_t transportGetHeartbeat();
+uint32_t transportGetHeartbeat(void);
+/**
+* @brief Load routing table from EEPROM to RAM.
+* Only for GW devices with enough RAM, i.e. ESP8266, RPI Sensebender GW, etc.
+* Atmega328 has only limited amount of RAM
+*/
+void transportLoadRoutingTable(void);
+/**
+* @brief Save routing table to EEPROM.
+*/
+void transportSaveRoutingTable(void);
+/**
+* @brief Update routing table
+* @param node
+* @param route
+*/
+void transportSetRoute(const uint8_t node, const uint8_t route);
+/**
+* @brief Load route to node
+* @param node
+* @return route to node
+*/
+uint8_t transportGetRoute(const uint8_t node);
+/**
+* @brief Get node ID
+* @return node ID
+*/
+uint8_t transportGetNodeId(void);
+/**
+* @brief Get parent node ID
+* @return parent node ID
+*/
+uint8_t transportGetParentNodeId(void);
+/**
+* @brief Get distance to GW
+* @return distance (=hops) to GW
+*/
+uint8_t transportGetDistanceGW(void);
// interface functions for radio driver
/**
* @brief Initialize transport HW
-* @return true if initalization successful
+* @return true if initialization successful
*/
-bool transportInit();
+bool transportInit(void);
/**
* @brief Set node address
*/
-void transportSetAddress(uint8_t address);
+void transportSetAddress(const uint8_t address);
/**
* @brief Retrieve node address
*/
-uint8_t transportGetAddress();
+uint8_t transportGetAddress(void);
/**
* @brief Send message
* @param to recipient
@@ -236,27 +500,26 @@ uint8_t transportGetAddress();
* @param len length of message (header + payload)
* @return true if message sent successfully
*/
-bool transportSend(uint8_t to, const void* data, uint8_t len);
+bool transportSend(const uint8_t to, const void* data, const uint8_t len);
/**
* @brief Verify if RX FIFO has pending messages
* @return true if message available in RX FIFO
*/
-bool transportAvailable();
+bool transportAvailable(void);
/**
* @brief Sanity check for transport: is transport still responsive?
-* @return true transport ok
+* @return true if transport HW is ok
*/
-bool transportSanityCheck();
+bool transportSanityCheck(void);
/**
* @brief Receive message from FIFO
* @return length of recevied message (header + payload)
*/
-uint8_t transportReceive(void* data);
+uint8_t transportReceive(void* data);
/**
* @brief Power down transport HW
*/
-void transportPowerDown();
-
+void transportPowerDown(void);
#endif // MyTransport_h
/** @}*/
diff --git a/core/MyTransportNRF24.cpp b/core/MyTransportNRF24.cpp
index e38ecf4ca..14e8d257b 100644
--- a/core/MyTransportNRF24.cpp
+++ b/core/MyTransportNRF24.cpp
@@ -19,77 +19,137 @@
#include "MyConfig.h"
#include "MyTransport.h"
+
#include "drivers/RF24/RF24.h"
+#include "drivers/CircularBuffer/CircularBuffer.h"
+
+#if defined(MY_RF24_ENABLE_ENCRYPTION)
+#include "drivers/AES/AES.h"
+#endif
+
+#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
+typedef struct _transportQueuedMessage {
+ uint8_t m_len; // Length of the data
+ uint8_t m_data[MAX_MESSAGE_LENGTH]; // The raw data
+} transportQueuedMessage;
+
+/** Buffer to store queued messages in. */
+static transportQueuedMessage transportRxQueueStorage[MY_RX_MESSAGE_BUFFER_SIZE];
+/** Circular buffer, which uses the transportRxQueueStorage and administers stored messages. */
+static CircularBuffer transportRxQueue(transportRxQueueStorage,
+ MY_RX_MESSAGE_BUFFER_SIZE);
+
+static volatile uint8_t transportLostMessageCount = 0;
+
+static void transportRxCallback(void)
+{
+ // Called for each message received by radio, from interrupt context.
+ // This function _must_ call RF24_readMessage() to de-assert interrupt line!
+ if (!transportRxQueue.full()) {
+ transportQueuedMessage* msg = transportRxQueue.getFront();
+ msg->m_len = RF24_readMessage(msg->m_data); // Read payload & clear RX_DR
+ (void)transportRxQueue.pushFront(msg);
+ } else {
+ // Queue is full. Discard message.
+ (void)RF24_readMessage(NULL); // Read payload & clear RX_DR
+ // Keep track of messages lost. Max 255, prevent wrapping.
+ if (transportLostMessageCount < 255) {
+ ++transportLostMessageCount;
+ }
+ }
+}
+#endif
+
#if defined(MY_RF24_ENABLE_ENCRYPTION)
- #include "drivers/AES/AES.h"
+AES _aes;
+uint8_t _dataenc[32] = {0};
+uint8_t _psk[16];
#endif
+bool transportInit(void)
+{
#if defined(MY_RF24_ENABLE_ENCRYPTION)
- AES _aes;
- uint8_t _dataenc[32] = {0};
- uint8_t _psk[16];
+ hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
+ //set up AES-key
+ _aes.set_key(_psk, 16);
+ // Make sure it is purged from memory when set
+ memset(_psk, 0, 16);
#endif
-bool transportInit() {
-
- #if defined(MY_RF24_ENABLE_ENCRYPTION)
- hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
- //set up AES-key
- _aes.set_key(_psk, 16);
- // Make sure it is purged from memory when set
- memset(_psk, 0, 16);
- #endif
-
+#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
+ RF24_registerReceiveCallback( transportRxCallback );
+#endif
return RF24_initialize();
}
-void transportSetAddress(uint8_t address) {
+void transportSetAddress(const uint8_t address)
+{
RF24_setNodeAddress(address);
RF24_startListening();
}
-uint8_t transportGetAddress() {
+uint8_t transportGetAddress(void)
+{
return RF24_getNodeID();
}
-bool transportSend(uint8_t recipient, const void* data, uint8_t len) {
- #if defined(MY_RF24_ENABLE_ENCRYPTION)
- // copy input data because it is read-only
- memcpy(_dataenc,data,len);
- // has to be adjusted, WIP!
- _aes.set_IV(0);
- len = len > 16 ? 32 : 16;
- //encrypt data
- _aes.cbc_encrypt(_dataenc, _dataenc, len/16);
- bool status = RF24_sendMessage( recipient, _dataenc, len );
- #else
- bool status = RF24_sendMessage( recipient, data, len );
- #endif
-
- return status;
+bool transportSend(const uint8_t to, const void* data, const uint8_t len)
+{
+#if defined(MY_RF24_ENABLE_ENCRYPTION)
+ // copy input data because it is read-only
+ (void)memcpy(_dataenc,data,len);
+ // has to be adjusted, WIP!
+ _aes.set_IV(0);
+ const uint8_t finalLength = len > 16 ? 32 : 16;
+ //encrypt data
+ _aes.cbc_encrypt(_dataenc, _dataenc, finalLength /16);
+ return RF24_sendMessage(to, _dataenc, finalLength);
+#else
+ return RF24_sendMessage(to, data, len);
+#endif
}
-bool transportAvailable() {
- bool avail = RF24_isDataAvailable();
- return avail;
+bool transportAvailable(void)
+{
+#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
+ (void)RF24_isDataAvailable; // Prevent 'defined but not used' warning
+ return !transportRxQueue.empty();
+#else
+ return RF24_isDataAvailable();
+#endif
}
-bool transportSanityCheck() {
+bool transportSanityCheck(void)
+{
return RF24_sanityCheck();
}
-uint8_t transportReceive(void* data) {
- uint8_t len = RF24_readMessage(data);
- #if defined(MY_RF24_ENABLE_ENCRYPTION)
- // has to be adjusted, WIP!
- _aes.set_IV(0);
- // decrypt data
- _aes.cbc_decrypt((byte*)(data), (byte*)(data), len>16?2:1);
- #endif
+uint8_t transportReceive(void* data)
+{
+ uint8_t len = 0;
+#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
+ transportQueuedMessage* msg = transportRxQueue.getBack();
+ if (msg) {
+ len = msg->m_len;
+ (void)memcpy(data, msg->m_data, len);
+ (void)transportRxQueue.popBack();
+ }
+#else
+ len = RF24_readMessage(data);
+#endif
+#if defined(MY_RF24_ENABLE_ENCRYPTION)
+ // has to be adjusted, WIP!
+ _aes.set_IV(0);
+ // decrypt data
+ if (_aes.cbc_decrypt((uint8_t*)(data), (uint8_t*)(data), len > 16 ? 2 : 1) != AES_SUCCESS) {
+ len = 0;
+ }
+#endif
return len;
}
-void transportPowerDown() {
+void transportPowerDown(void)
+{
RF24_powerDown();
}
diff --git a/core/MyTransportRFM69.cpp b/core/MyTransportRFM69.cpp
index b5aec3325..905ee8b1a 100644
--- a/core/MyTransportRFM69.cpp
+++ b/core/MyTransportRFM69.cpp
@@ -26,51 +26,61 @@ RFM69 _radio(MY_RF69_SPI_CS, MY_RF69_IRQ_PIN, MY_RFM69HW, MY_RF69_IRQ_NUM);
uint8_t _address;
-bool transportInit() {
+bool transportInit(void)
+{
// Start up the radio library (_address will be set later by the MySensors library)
if (_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID)) {
- #ifdef MY_RFM69_ENABLE_ENCRYPTION
- uint8_t _psk[16];
- hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
- _radio.encrypt((const char*)_psk);
- memset(_psk, 0, 16); // Make sure it is purged from memory when set
- #endif
+#ifdef MY_RFM69_ENABLE_ENCRYPTION
+ uint8_t _psk[16];
+ hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
+ _radio.encrypt((const char*)_psk);
+ memset(_psk, 0, 16); // Make sure it is purged from memory when set
+#endif
return true;
}
return false;
}
-void transportSetAddress(uint8_t address) {
+void transportSetAddress(const uint8_t address)
+{
_address = address;
_radio.setAddress(address);
}
-uint8_t transportGetAddress() {
+uint8_t transportGetAddress(void)
+{
return _address;
}
-bool transportSend(uint8_t to, const void* data, uint8_t len) {
+bool transportSend(const uint8_t to, const void* data, const uint8_t len)
+{
return _radio.sendWithRetry(to,data,len);
}
-bool transportAvailable() {
+bool transportAvailable(void)
+{
return _radio.receiveDone();
}
-bool transportSanityCheck() {
+bool transportSanityCheck(void)
+{
// not implemented yet
return true;
}
-uint8_t transportReceive(void* data) {
+uint8_t transportReceive(void* data)
+{
memcpy(data,(const void *)_radio.DATA, _radio.DATALEN);
+ // save payload length
+ const uint8_t dataLen = _radio.DATALEN;
// Send ack back if this message wasn't a broadcast
- if (_radio.TARGETID != RF69_BROADCAST_ADDR)
- _radio.ACKRequested();
- _radio.sendACK();
- return _radio.DATALEN;
-}
+ if(_radio.ACKRequested()) {
+ _radio.sendACK();
+ }
+ return dataLen;
+}
-void transportPowerDown() {
+void transportPowerDown(void)
+{
_radio.sleep();
}
diff --git a/core/MyTransportRFM95.cpp b/core/MyTransportRFM95.cpp
new file mode 100644
index 000000000..3b88994e9
--- /dev/null
+++ b/core/MyTransportRFM95.cpp
@@ -0,0 +1,92 @@
+/*
+ * The MySensors Arduino library handles the wireless radio link and protocol
+ * between your home built sensors/actuators and HA controller of choice.
+ * The sensors forms a self healing radio network with optional repeaters. Each
+ * repeater and gateway builds a routing tables in EEPROM which keeps track of the
+ * network topology allowing messages to be routed to nodes.
+ *
+ * Created by Henrik Ekblad
+ * Copyright (C) 2013-2016 Sensnology AB
+ * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
+ *
+ * Documentation: http://www.mysensors.org
+ * Support Forum: http://forum.mysensors.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ */
+
+#include "MyConfig.h"
+#include "MyTransport.h"
+#include "drivers/RFM95/RFM95.h"
+
+bool transportInit(void)
+{
+ const bool result = RFM95_initialise(MY_RFM95_FREQUENCY);
+#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RFM95_ATC_MODE_DISABLED)
+ // only enable ATC mode nodes
+ RFM95_ATCmode(true, MY_RFM95_ATC_TARGET_RSSI);
+#endif
+ return result;
+}
+
+void transportSetAddress(const uint8_t address)
+{
+ RFM95_setAddress(address);
+}
+
+uint8_t transportGetAddress(void)
+{
+ return RFM95_getAddress();
+}
+
+bool transportSend(const uint8_t to, const void* data, const uint8_t len)
+{
+ return RFM95_sendWithRetry(to, data, len);
+}
+
+bool transportAvailable(void)
+{
+ return RFM95_available();
+}
+
+bool transportSanityCheck(void)
+{
+ return RFM95_sanityCheck();
+}
+
+uint8_t transportReceive(void* data)
+{
+ return RFM95_recv((uint8_t*)data);
+}
+
+void transportPowerDown(void)
+{
+ (void)RFM95_sleep();
+}
+
+// experimental
+// **********************************************
+int16_t transportGetReceivingSignalStrength(void)
+{
+ return RFM95_getReceivingRSSI();
+}
+int16_t transportGetSendingSignalStrength(void)
+{
+ return RFM95_getSendingRSSI();
+}
+int8_t transportGetReceivingSNR(void)
+{
+ return RFM95_getReceivingSNR();
+}
+int8_t transportGetSendingSNR(void)
+{
+ return RFM95_getSendingSNR();
+}
+uint8_t transportGetTxPower(void)
+{
+ return RFM95_getTxPowerPercent();
+}
+// **********************************************
diff --git a/core/MyTransportRS485.cpp b/core/MyTransportRS485.cpp
index 814f721fd..976bc867c 100644
--- a/core/MyTransportRS485.cpp
+++ b/core/MyTransportRS485.cpp
@@ -57,23 +57,21 @@
#include "MyTransport.h"
#include
-
-#if defined(ARDUINO) && ARDUINO >= 100
#include
-#else
-#include
-#endif
#include "MyTransport.h"
+#ifdef __linux__
+#include "SerialPort.h"
+#endif
#if defined(MY_RS485_DE_PIN)
- #define assertDE() digitalWrite(MY_RS485_DE_PIN, HIGH); delayMicroseconds(5)
- #define deassertDE() digitalWrite(MY_RS485_DE_PIN, LOW)
+#define assertDE() hwDigitalWrite(MY_RS485_DE_PIN, HIGH); delayMicroseconds(5)
+#define deassertDE() hwDigitalWrite(MY_RS485_DE_PIN, LOW)
#else
- #define assertDE()
- #define deassertDE()
+#define assertDE()
+#define deassertDE()
#endif
// We only use SYS_PACK in this application
@@ -92,8 +90,14 @@ unsigned char _recSender;
unsigned char _recCS;
unsigned char _recCalcCS;
-AltSoftSerial _dev;
+#if defined(__linux__)
+SerialPort _dev = SerialPort(MY_RS485_HWSERIAL);
+#elif defined(MY_RS485_HWSERIAL)
+HardwareSerial& _dev = MY_RS485_HWSERIAL;
+#else
+AltSoftSerial _dev;
+#endif
unsigned char _nodeId;
char _data[MY_RS485_MAX_MESSAGE_LENGTH];
@@ -110,13 +114,14 @@ bool _packet_received;
//Reset the state machine and release the data pointer
-void _serialReset(){
- _recPhase = 0;
- _recPos = 0;
- _recLen = 0;
- _recCommand = 0;
- _recCS = 0;
- _recCalcCS = 0;
+void _serialReset()
+{
+ _recPhase = 0;
+ _recPos = 0;
+ _recLen = 0;
+ _recCommand = 0;
+ _recCS = 0;
+ _recCalcCS = 0;
}
// This is the main reception state machine. Progress through the states
@@ -128,120 +133,118 @@ void _serialReset(){
// function.
bool _serialProcess()
{
- char inch;
- unsigned char i;
- if (!_dev.available()) return false;
-
- while(_dev.available()) {
- inch = _dev.read();
-
- switch(_recPhase) {
-
- // Case 0 looks for the header. Bytes arrive in the serial interface and get
- // shifted through a header buffer. When the start and end characters in
- // the buffer match the SOH/STX pair, and the destination station ID matches
- // our ID, save the header information and progress to the next state.
- case 0:
- memcpy(&_header[0],&_header[1],5);
- _header[5] = inch;
- if ((_header[0] == SOH) && (_header[5] == STX) && (_header[1] != _header[2])) {
- _recCalcCS = 0;
- _recStation = _header[1];
- _recSender = _header[2];
- _recCommand = _header[3];
- _recLen = _header[4];
-
- for (i=1; i<=4; i++) {
- _recCalcCS += _header[i];
- }
- _recPhase = 1;
- _recPos = 0;
-
- //Check if we should process this message
- //We reject the message if we are the sender
- //We reject if we are not the receiver and message is not a broadcast
- if ((_recSender == _nodeId) ||
- (_recStation != _nodeId &&
- _recStation != BROADCAST_ADDRESS)) {
- _dev.print(" wrongid: ");
- _dev.print(_recStation);
- _dev.print(" - ");
- _dev.println(_nodeId);
- _serialReset();
- break;
- }
-
- if (_recLen == 0) {
- _recPhase = 2;
- }
-
- }
- break;
-
- // Case 1 receives the data portion of the packet. Read in "_recLen" number
- // of bytes and store them in the _data array.
- case 1:
- _data[_recPos++] = inch;
- _recCalcCS += inch;
- if (_recPos == _recLen) {
- _recPhase = 2;
- }
- break;
-
- // After the data comes a single ETX character. Do we have it? If not,
- // reset the state machine to default and start looking for a new header.
- case 2:
- // Packet properly terminated?
- if (inch == ETX) {
- _recPhase = 3;
- } else {
- _serialReset();
- }
- break;
-
- // Next comes the checksum. We have already calculated it from the incoming
- // data, so just store the incoming checksum byte for later.
- case 3:
- _recCS = inch;
- _recPhase = 4;
- break;
-
- // The final state - check the last character is EOT and that the checksum matches.
- // If that test passes, then look for a valid command callback to execute.
- // Execute it if found.
- case 4:
- if (inch == EOT) {
- if (_recCS == _recCalcCS) {
- // First, check for system level commands. It is possible
- // to register your own callback as well for system level
- // commands which will be called after the system default
- // hook.
-
- switch (_recCommand) {
- case ICSC_SYS_PACK:
- _packet_from = _recSender;
- _packet_len = _recLen;
- _packet_received = true;
- break;
- }
- }
- }
- //Clear the data
- _serialReset();
- //Return true, we have processed one command
- return true;
- break;
- }
- }
- return true;
+ char inch;
+ unsigned char i;
+ if (!_dev.available()) {
+ return false;
+ }
+
+ while(_dev.available()) {
+ inch = _dev.read();
+
+ switch(_recPhase) {
+
+ // Case 0 looks for the header. Bytes arrive in the serial interface and get
+ // shifted through a header buffer. When the start and end characters in
+ // the buffer match the SOH/STX pair, and the destination station ID matches
+ // our ID, save the header information and progress to the next state.
+ case 0:
+ memcpy(&_header[0],&_header[1],5);
+ _header[5] = inch;
+ if ((_header[0] == SOH) && (_header[5] == STX) && (_header[1] != _header[2])) {
+ _recCalcCS = 0;
+ _recStation = _header[1];
+ _recSender = _header[2];
+ _recCommand = _header[3];
+ _recLen = _header[4];
+
+ for (i=1; i<=4; i++) {
+ _recCalcCS += _header[i];
+ }
+ _recPhase = 1;
+ _recPos = 0;
+
+ //Check if we should process this message
+ //We reject the message if we are the sender
+ //We reject if we are not the receiver and message is not a broadcast
+ if ((_recSender == _nodeId) ||
+ (_recStation != _nodeId &&
+ _recStation != BROADCAST_ADDRESS)) {
+ _serialReset();
+ break;
+ }
+
+ if (_recLen == 0) {
+ _recPhase = 2;
+ }
+
+ }
+ break;
+
+ // Case 1 receives the data portion of the packet. Read in "_recLen" number
+ // of bytes and store them in the _data array.
+ case 1:
+ _data[_recPos++] = inch;
+ _recCalcCS += inch;
+ if (_recPos == _recLen) {
+ _recPhase = 2;
+ }
+ break;
+
+ // After the data comes a single ETX character. Do we have it? If not,
+ // reset the state machine to default and start looking for a new header.
+ case 2:
+ // Packet properly terminated?
+ if (inch == ETX) {
+ _recPhase = 3;
+ } else {
+ _serialReset();
+ }
+ break;
+
+ // Next comes the checksum. We have already calculated it from the incoming
+ // data, so just store the incoming checksum byte for later.
+ case 3:
+ _recCS = inch;
+ _recPhase = 4;
+ break;
+
+ // The final state - check the last character is EOT and that the checksum matches.
+ // If that test passes, then look for a valid command callback to execute.
+ // Execute it if found.
+ case 4:
+ if (inch == EOT) {
+ if (_recCS == _recCalcCS) {
+ // First, check for system level commands. It is possible
+ // to register your own callback as well for system level
+ // commands which will be called after the system default
+ // hook.
+
+ switch (_recCommand) {
+ case ICSC_SYS_PACK:
+ _packet_from = _recSender;
+ _packet_len = _recLen;
+ _packet_received = true;
+ break;
+ }
+ }
+ }
+ //Clear the data
+ _serialReset();
+ //Return true, we have processed one command
+ return true;
+ break;
+ }
+ }
+ return true;
}
-bool transportSend(uint8_t to, const void* data, uint8_t len)
+bool transportSend(const uint8_t to, const void* data, const uint8_t len)
{
const char *datap = static_cast(data);
- unsigned char i;
- unsigned char cs = 0;
- unsigned char del;
+ unsigned char i;
+ unsigned char cs = 0;
+ unsigned char del;
// This is how many times to try and transmit before failing.
unsigned char timeout = 10;
@@ -262,99 +265,107 @@ bool transportSend(uint8_t to, const void* data, uint8_t len)
}
}
- #if defined(MY_RS485_DE_PIN)
- digitalWrite(MY_RS485_DE_PIN, HIGH);
- delayMicroseconds(5);
- #endif
-
- // Start of header by writing multiple SOH
- for(byte w=0;w<1;w++) _dev.write(SOH);
- _dev.write(to); // Destination address
- cs += to;
- _dev.write(_nodeId); // Source address
- cs += _nodeId;
- _dev.write(ICSC_SYS_PACK); // Command code
- cs += ICSC_SYS_PACK;
- _dev.write(len); // Length of text
- cs += len;
- _dev.write(STX); // Start of text
- for(i=0; i= 100
- #if ARDUINO >= 104
- // Arduino 1.0.4 and upwards does it right
- _dev.flush();
- #else
- // Between 1.0.0 and 1.0.3 it almost does it - need to compensate
- // for the hardware buffer. Delay for 2 bytes worth of transmission.
- _dev.flush();
- delayMicroseconds((20000000UL/9600)+1);
- #endif
- #endif
- #endif
- digitalWrite(MY_RS485_DE_PIN, LOW);
- #endif
- return true;
+#if defined(MY_RS485_DE_PIN)
+ hwDigitalWrite(MY_RS485_DE_PIN, HIGH);
+ delayMicroseconds(5);
+#endif
+
+ // Start of header by writing multiple SOH
+ for(byte w=0; w<1; w++) {
+ _dev.write(SOH);
+ }
+ _dev.write(to); // Destination address
+ cs += to;
+ _dev.write(_nodeId); // Source address
+ cs += _nodeId;
+ _dev.write(ICSC_SYS_PACK); // Command code
+ cs += ICSC_SYS_PACK;
+ _dev.write(len); // Length of text
+ cs += len;
+ _dev.write(STX); // Start of text
+ for(i=0; i= 100
+#if ARDUINO >= 104
+ // Arduino 1.0.4 and upwards does it right
+ _dev.flush();
+#else
+ // Between 1.0.0 and 1.0.3 it almost does it - need to compensate
+ // for the hardware buffer. Delay for 2 bytes worth of transmission.
+ _dev.flush();
+ delayMicroseconds((20000000UL/9600)+1);
+#endif
+#elif defined(__linux__)
+ _dev.flush();
+#endif
+#endif
+ hwDigitalWrite(MY_RS485_DE_PIN, LOW);
+#endif
+ return true;
}
-bool transportInit() {
- // Reset the state machine
+bool transportInit(void)
+{
+ // Reset the state machine
_dev.begin(MY_RS485_BAUD_RATE);
- _serialReset();
- #if defined(MY_RS485_DE_PIN)
- pinMode(MY_RS485_DE_PIN, OUTPUT);
- digitalWrite(MY_RS485_DE_PIN, LOW);
- #endif
- return true;
+ _serialReset();
+#if defined(MY_RS485_DE_PIN)
+ hwPinMode(MY_RS485_DE_PIN, OUTPUT);
+ hwDigitalWrite(MY_RS485_DE_PIN, LOW);
+#endif
+ return true;
}
-void transportSetAddress(uint8_t address) {
+void transportSetAddress(const uint8_t address)
+{
_nodeId = address;
}
-uint8_t transportGetAddress() {
+uint8_t transportGetAddress(void)
+{
return _nodeId;
}
-bool transportAvailable() {
+bool transportAvailable(void)
+{
_serialProcess();
return _packet_received;
}
-bool transportSanityCheck() {
+bool transportSanityCheck(void)
+{
// not implemented yet
return true;
}
-uint8_t transportReceive(void* data) {
+uint8_t transportReceive(void* data)
+{
if (_packet_received) {
memcpy(data,_data,_packet_len);
_packet_received = false;
return _packet_len;
- }
- else {
+ } else {
return (0);
}
}
-void transportPowerDown() {
+void transportPowerDown(void)
+{
// Nothing to shut down here
}
-
-
diff --git a/core/Version.h b/core/Version.h
index e80c508ab..5f365ff64 100644
--- a/core/Version.h
+++ b/core/Version.h
@@ -1,3 +1,21 @@
+/**
+ * The MySensors Arduino library handles the wireless radio link and protocol
+ * between your home built sensors/actuators and HA controller of choice.
+ * The sensors forms a self healing radio network with optional repeaters. Each
+ * repeater and gateway builds a routing tables in EEPROM which keeps track of the
+ * network topology allowing messages to be routed to nodes.
+ *
+ * Created by Henrik Ekblad
+ * Copyright (C) 2013-2015 Sensnology AB
+ * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
+ *
+ * Documentation: http://www.mysensors.org
+ * Support Forum: http://forum.mysensors.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
/***
* This file defines the Sensor library version number
* Normally, contributors should not modify this directly
@@ -6,6 +24,6 @@
#ifndef Version_h
#define Version_h
-#define MYSENSORS_LIBRARY_VERSION "2.0.0"
+#define MYSENSORS_LIBRARY_VERSION "2.1.0"
#endif
diff --git a/drivers/AES/AES.cpp b/drivers/AES/AES.cpp
index cd5d3067f..d62589f69 100644
--- a/drivers/AES/AES.cpp
+++ b/drivers/AES/AES.cpp
@@ -36,13 +36,13 @@
/* This version derived by Mark Tillotson 2012-01-23, tidied up, slimmed down
and tailored to 8-bit microcontroller abilities and Arduino datatypes.
- The s-box and inverse s-box were retained as tables (0.5kB PROGMEM) but all
- the other transformations are coded to save table space. Many efficiency
+ The s-box and inverse s-box were retained as tables (0.5kB PROGMEM) but all
+ the other transformations are coded to save table space. Many efficiency
improvments to the routines mix_sub_columns() and inv_mix_sub_columns()
(mainly common sub-expression elimination).
Only the routines with precalculated subkey schedule are retained (together
- with set_key() - this does however mean each AES object takes 240 bytes of
+ with set_key() - this does however mean each AES object takes 240 bytes of
RAM, alas)
The CBC routines side-effect the iv argument (so that successive calls work
@@ -65,84 +65,80 @@
#define WPOLY 0x011B
#define DPOLY 0x008D
-const static byte s_fwd [0x100] PROGMEM =
-{
- 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
- 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
- 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
- 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
- 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
- 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
- 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
- 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
- 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
- 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
- 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
- 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
- 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
- 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
- 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
- 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
+const static byte s_fwd [0x100] PROGMEM = {
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
} ;
-const static byte s_inv [0x100] PROGMEM =
-{
- 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
- 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
- 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
- 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
- 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
- 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
- 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
- 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
- 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
- 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
- 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
- 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
- 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
- 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
- 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
- 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
+const static byte s_inv [0x100] PROGMEM = {
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
} ;
// times 2 in the GF(2^8)
-#define f2(x) ((x) & 0x80 ? (x << 1) ^ WPOLY : x << 1)
+#define f2(x) (((x) & 0x80) ? (x << 1) ^ WPOLY : x << 1)
#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0))
static byte s_box (byte x)
{
- // return fwd_affine (pgm_read_byte (&inv [x])) ;
- return pgm_read_byte (& s_fwd [x]) ;
+ // return fwd_affine (pgm_read_byte (&inv [x])) ;
+ return pgm_read_byte (& s_fwd [x]) ;
}
// Inverse Sbox
static byte is_box (byte x)
{
- // return pgm_read_byte (&inv [inv_affine (x)]) ;
- return pgm_read_byte (& s_inv [x]) ;
+ // return pgm_read_byte (&inv [inv_affine (x)]) ;
+ return pgm_read_byte (& s_inv [x]) ;
}
static void xor_block (byte * d, byte * s)
{
- for (byte i = 0 ; i < N_BLOCK ; i += 4)
- {
- *d++ ^= *s++ ; // some unrolling
- *d++ ^= *s++ ;
- *d++ ^= *s++ ;
- *d++ ^= *s++ ;
- }
+ for (byte i = 0 ; i < N_BLOCK ; i += 4) {
+ *d++ ^= *s++ ; // some unrolling
+ *d++ ^= *s++ ;
+ *d++ ^= *s++ ;
+ *d++ ^= *s++ ;
+ }
}
static void copy_and_key (byte * d, byte * s, byte * k)
{
- for (byte i = 0 ; i < N_BLOCK ; i += 4)
- {
- *d++ = *s++ ^ *k++ ; // some unrolling
- *d++ = *s++ ^ *k++ ;
- *d++ = *s++ ^ *k++ ;
- *d++ = *s++ ^ *k++ ;
- }
+ for (byte i = 0 ; i < N_BLOCK ; i += 4) {
+ *d++ = *s++ ^ *k++ ; // some unrolling
+ *d++ = *s++ ^ *k++ ;
+ *d++ = *s++ ^ *k++ ;
+ *d++ = *s++ ^ *k++ ;
+ }
}
// #define add_round_key(d, k) xor_block (d, k)
@@ -151,88 +147,115 @@ static void copy_and_key (byte * d, byte * s, byte * k)
static void shift_sub_rows (byte st [N_BLOCK])
{
- st [0] = s_box (st [0]) ; st [4] = s_box (st [4]) ;
- st [8] = s_box (st [8]) ; st [12] = s_box (st [12]) ;
-
- byte tt = st [1] ;
- st [1] = s_box (st [5]) ; st [5] = s_box (st [9]) ;
- st [9] = s_box (st [13]) ; st [13] = s_box (tt) ;
-
- tt = st[2] ; st [2] = s_box (st [10]) ; st [10] = s_box (tt) ;
- tt = st[6] ; st [6] = s_box (st [14]) ; st [14] = s_box (tt) ;
-
- tt = st[15] ;
- st [15] = s_box (st [11]) ; st [11] = s_box (st [7]) ;
- st [7] = s_box (st [3]) ; st [3] = s_box (tt) ;
+ st [0] = s_box (st [0]) ;
+ st [4] = s_box (st [4]) ;
+ st [8] = s_box (st [8]) ;
+ st [12] = s_box (st [12]) ;
+
+ byte tt = st [1] ;
+ st [1] = s_box (st [5]) ;
+ st [5] = s_box (st [9]) ;
+ st [9] = s_box (st [13]) ;
+ st [13] = s_box (tt) ;
+
+ tt = st[2] ;
+ st [2] = s_box (st [10]) ;
+ st [10] = s_box (tt) ;
+ tt = st[6] ;
+ st [6] = s_box (st [14]) ;
+ st [14] = s_box (tt) ;
+
+ tt = st[15] ;
+ st [15] = s_box (st [11]) ;
+ st [11] = s_box (st [7]) ;
+ st [7] = s_box (st [3]) ;
+ st [3] = s_box (tt) ;
}
static void inv_shift_sub_rows (byte st[N_BLOCK])
{
- st [0] = is_box (st[0]) ; st [4] = is_box (st [4]);
- st [8] = is_box (st[8]) ; st [12] = is_box (st [12]);
-
- byte tt = st[13] ;
- st [13] = is_box (st [9]) ; st [9] = is_box (st [5]) ;
- st [5] = is_box (st [1]) ; st [1] = is_box (tt) ;
-
- tt = st [2] ; st [2] = is_box (st [10]) ; st [10] = is_box (tt) ;
- tt = st [6] ; st [6] = is_box (st [14]) ; st [14] = is_box (tt) ;
-
- tt = st [3] ;
- st [3] = is_box (st [7]) ; st [7] = is_box (st [11]) ;
- st [11] = is_box (st [15]) ; st [15] = is_box (tt) ;
+ st [0] = is_box (st[0]) ;
+ st [4] = is_box (st [4]);
+ st [8] = is_box (st[8]) ;
+ st [12] = is_box (st [12]);
+
+ byte tt = st[13] ;
+ st [13] = is_box (st [9]) ;
+ st [9] = is_box (st [5]) ;
+ st [5] = is_box (st [1]) ;
+ st [1] = is_box (tt) ;
+
+ tt = st [2] ;
+ st [2] = is_box (st [10]) ;
+ st [10] = is_box (tt) ;
+ tt = st [6] ;
+ st [6] = is_box (st [14]) ;
+ st [14] = is_box (tt) ;
+
+ tt = st [3] ;
+ st [3] = is_box (st [7]) ;
+ st [7] = is_box (st [11]) ;
+ st [11] = is_box (st [15]) ;
+ st [15] = is_box (tt) ;
}
/* SUB COLUMNS PHASE */
static void mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK])
{
- byte j = 5 ;
- byte k = 10 ;
- byte l = 15 ;
- for (byte i = 0 ; i < N_BLOCK ; i += N_COL)
- {
- byte a = st [i] ;
- byte b = st [j] ; j = (j+N_COL) & 15 ;
- byte c = st [k] ; k = (k+N_COL) & 15 ;
- byte d = st [l] ; l = (l+N_COL) & 15 ;
- byte a1 = s_box (a), b1 = s_box (b), c1 = s_box (c), d1 = s_box (d) ;
- byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ;
- dt[i] = a2 ^ b2^b1 ^ c1 ^ d1 ;
- dt[i+1] = a1 ^ b2 ^ c2^c1 ^ d1 ;
- dt[i+2] = a1 ^ b1 ^ c2 ^ d2^d1 ;
- dt[i+3] = a2^a1 ^ b1 ^ c1 ^ d2 ;
- }
+ byte j = 5 ;
+ byte k = 10 ;
+ byte l = 15 ;
+ for (byte i = 0 ; i < N_BLOCK ; i += N_COL) {
+ byte a = st [i] ;
+ byte b = st [j] ;
+ j = (j+N_COL) & 15 ;
+ byte c = st [k] ;
+ k = (k+N_COL) & 15 ;
+ byte d = st [l] ;
+ l = (l+N_COL) & 15 ;
+ byte a1 = s_box (a), b1 = s_box (b), c1 = s_box (c), d1 = s_box (d) ;
+ byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ;
+ dt[i] = a2 ^ b2^b1 ^ c1 ^ d1 ;
+ dt[i+1] = a1 ^ b2 ^ c2^c1 ^ d1 ;
+ dt[i+2] = a1 ^ b1 ^ c2 ^ d2^d1 ;
+ dt[i+3] = a2^a1 ^ b1 ^ c1 ^ d2 ;
+ }
}
static void inv_mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK])
{
- for (byte i = 0 ; i < N_BLOCK ; i += N_COL)
- {
- byte a1 = st [i] ;
- byte b1 = st [i+1] ;
- byte c1 = st [i+2] ;
- byte d1 = st [i+3] ;
- byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ;
- byte a4 = f2(a2), b4 = f2(b2), c4 = f2(c2), d4 = f2(d2) ;
- byte a8 = f2(a4), b8 = f2(b4), c8 = f2(c4), d8 = f2(d4) ;
- byte a9 = a8 ^ a1,b9 = b8 ^ b1,c9 = c8 ^ c1,d9 = d8 ^ d1 ;
- byte ac = a8 ^ a4,bc = b8 ^ b4,cc = c8 ^ c4,dc = d8 ^ d4 ;
-
- dt[i] = is_box (ac^a2 ^ b9^b2 ^ cc^c1 ^ d9) ;
- dt[(i+5)&15] = is_box (a9 ^ bc^b2 ^ c9^c2 ^ dc^d1) ;
- dt[(i+10)&15] = is_box (ac^a1 ^ b9 ^ cc^c2 ^ d9^d2) ;
- dt[(i+15)&15] = is_box (a9^a2 ^ bc^b1 ^ c9 ^ dc^d2) ;
- }
+ for (byte i = 0 ; i < N_BLOCK ; i += N_COL) {
+ byte a1 = st [i] ;
+ byte b1 = st [i+1] ;
+ byte c1 = st [i+2] ;
+ byte d1 = st [i+3] ;
+ byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ;
+ byte a4 = f2(a2), b4 = f2(b2), c4 = f2(c2), d4 = f2(d2) ;
+ byte a8 = f2(a4), b8 = f2(b4), c8 = f2(c4), d8 = f2(d4) ;
+ byte a9 = a8 ^ a1,b9 = b8 ^ b1,c9 = c8 ^ c1,d9 = d8 ^ d1 ;
+ byte ac = a8 ^ a4,bc = b8 ^ b4,cc = c8 ^ c4,dc = d8 ^ d4 ;
+
+ dt[i] = is_box (ac^a2 ^ b9^b2 ^ cc^c1 ^ d9) ;
+ dt[(i+5)&15] = is_box (a9 ^ bc^b2 ^ c9^c2 ^ dc^d1) ;
+ dt[(i+10)&15] = is_box (ac^a1 ^ b9 ^ cc^c2 ^ d9^d2) ;
+ dt[(i+15)&15] = is_box (a9^a2 ^ bc^b1 ^ c9 ^ dc^d2) ;
+ }
}
/******************************************************************************/
-AES::AES(){
+AES::AES()
+{
byte ar_iv[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 };
+ IVC = 0x01;
+ round = 0;
+ pad = 0;
+ size = 0;
+ memset(key_sched, 0, KEY_SCHEDULE_BYTES);
memcpy(iv,ar_iv,8);
memcpy(iv+8,ar_iv,8);
- arr_pad[0] = 0x01;
+ arr_pad[0] = 0x01;
arr_pad[1] = 0x02;
arr_pad[2] = 0x03;
arr_pad[3] = 0x04;
@@ -253,200 +276,196 @@ AES::AES(){
byte AES::set_key (byte key [], int keylen)
{
- byte hi ;
- switch (keylen)
- {
- case 16:
- case 128:
- keylen = 16; // 10 rounds
- round = 10 ;
- break;
- case 24:
- case 192:
- keylen = 24; // 12 rounds
- round = 12 ;
- break;
- case 32:
- case 256:
- keylen = 32; // 14 rounds
- round = 14 ;
- break;
- default:
- round = 0;
- return AES_FAILURE;
- }
- hi = (round + 1) << 4 ;
- copy_n_bytes (key_sched, key, keylen) ;
- byte t[4] ;
- byte next = keylen ;
- for (byte cc = keylen, rc = 1 ; cc < hi ; cc += N_COL)
- {
- for (byte i = 0 ; i < N_COL ; i++)
- t[i] = key_sched [cc-4+i] ;
- if (cc == next)
- {
- next += keylen ;
- byte ttt = t[0] ;
- t[0] = s_box (t[1]) ^ rc ;
- t[1] = s_box (t[2]) ;
- t[2] = s_box (t[3]) ;
- t[3] = s_box (ttt) ;
- rc = f2 (rc) ;
- }
- else if (keylen == 32 && (cc & 31) == 16)
- {
- for (byte i = 0 ; i < 4 ; i++)
- t[i] = s_box (t[i]) ;
- }
- byte tt = cc - keylen ;
- for (byte i = 0 ; i < N_COL ; i++)
- key_sched [cc + i] = key_sched [tt + i] ^ t[i] ;
- }
- return AES_SUCCESS ;
+ byte hi ;
+ switch (keylen) {
+ case 16:
+ case 128:
+ keylen = 16; // 10 rounds
+ round = 10 ;
+ break;
+ case 24:
+ case 192:
+ keylen = 24; // 12 rounds
+ round = 12 ;
+ break;
+ case 32:
+ case 256:
+ keylen = 32; // 14 rounds
+ round = 14 ;
+ break;
+ default:
+ round = 0;
+ return AES_FAILURE;
+ }
+ hi = (round + 1) << 4 ;
+ copy_n_bytes (key_sched, key, keylen) ;
+ byte t[4] ;
+ byte next = keylen ;
+ for (byte cc = keylen, rc = 1 ; cc < hi ; cc += N_COL) {
+ for (byte i = 0 ; i < N_COL ; i++) {
+ t[i] = key_sched [cc-4+i] ;
+ }
+ if (cc == next) {
+ next += keylen ;
+ byte ttt = t[0] ;
+ t[0] = s_box (t[1]) ^ rc ;
+ t[1] = s_box (t[2]) ;
+ t[2] = s_box (t[3]) ;
+ t[3] = s_box (ttt) ;
+ rc = f2 (rc) ;
+ } else if (keylen == 32 && (cc & 31) == 16) {
+ for (byte i = 0 ; i < 4 ; i++) {
+ t[i] = s_box (t[i]) ;
+ }
+ }
+ byte tt = cc - keylen ;
+ for (byte i = 0 ; i < N_COL ; i++) {
+ key_sched [cc + i] = key_sched [tt + i] ^ t[i] ;
+ }
+ }
+ return AES_SUCCESS ;
}
/******************************************************************************/
void AES::clean ()
{
- for (byte i = 0 ; i < KEY_SCHEDULE_BYTES ; i++)
- key_sched [i] = 0 ;
- round = 0 ;
+ for (byte i = 0 ; i < KEY_SCHEDULE_BYTES ; i++) {
+ key_sched [i] = 0 ;
+ }
+ round = 0 ;
}
/******************************************************************************/
void AES::copy_n_bytes (byte * d, byte * s, byte nn)
{
- while (nn >= 4)
- {
- *d++ = *s++ ; // some unrolling
- *d++ = *s++ ;
- *d++ = *s++ ;
- *d++ = *s++ ;
- nn -= 4 ;
- }
- while (nn--)
- *d++ = *s++ ;
+ while (nn >= 4) {
+ *d++ = *s++ ; // some unrolling
+ *d++ = *s++ ;
+ *d++ = *s++ ;
+ *d++ = *s++ ;
+ nn -= 4 ;
+ }
+ while (nn--) {
+ *d++ = *s++ ;
+ }
}
/******************************************************************************/
byte AES::encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK])
{
- if (round)
- {
- byte s1 [N_BLOCK], r ;
- copy_and_key (s1, plain, (byte*) (key_sched)) ;
-
- for (r = 1 ; r < round ; r++)
- {
- byte s2 [N_BLOCK] ;
- mix_sub_columns (s2, s1) ;
- copy_and_key (s1, s2, (byte*) (key_sched + r * N_BLOCK)) ;
- }
- shift_sub_rows (s1) ;
- copy_and_key (cipher, s1, (byte*) (key_sched + r * N_BLOCK)) ;
- }
- else
- return AES_FAILURE ;
- return AES_SUCCESS ;
+ if (round) {
+ byte s1 [N_BLOCK], r ;
+ copy_and_key (s1, plain, (byte*) (key_sched)) ;
+
+ for (r = 1 ; r < round ; r++) {
+ byte s2 [N_BLOCK] ;
+ mix_sub_columns (s2, s1) ;
+ copy_and_key (s1, s2, (byte*) (key_sched + r * N_BLOCK)) ;
+ }
+ shift_sub_rows (s1) ;
+ copy_and_key (cipher, s1, (byte*) (key_sched + r * N_BLOCK)) ;
+ } else {
+ return AES_FAILURE ;
+ }
+ return AES_SUCCESS ;
}
/******************************************************************************/
byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block, byte iv [N_BLOCK])
{
- while (n_block--)
- {
- xor_block (iv, plain) ;
- if (encrypt (iv, iv) != AES_SUCCESS)
- return AES_FAILURE ;
- copy_n_bytes (cipher, iv, N_BLOCK) ;
- plain += N_BLOCK ;
- cipher += N_BLOCK ;
- }
- return AES_SUCCESS ;
+ while (n_block--) {
+ xor_block (iv, plain) ;
+ if (encrypt (iv, iv) != AES_SUCCESS) {
+ return AES_FAILURE ;
+ }
+ copy_n_bytes (cipher, iv, N_BLOCK) ;
+ plain += N_BLOCK ;
+ cipher += N_BLOCK ;
+ }
+ return AES_SUCCESS ;
}
/******************************************************************************/
byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block)
{
- while (n_block--)
- {
- xor_block (iv, plain) ;
- if (encrypt (iv, iv) != AES_SUCCESS)
- return AES_FAILURE ;
- copy_n_bytes (cipher, iv, N_BLOCK) ;
- plain += N_BLOCK ;
- cipher += N_BLOCK ;
- }
- return AES_SUCCESS ;
+ while (n_block--) {
+ xor_block (iv, plain) ;
+ if (encrypt (iv, iv) != AES_SUCCESS) {
+ return AES_FAILURE ;
+ }
+ copy_n_bytes (cipher, iv, N_BLOCK) ;
+ plain += N_BLOCK ;
+ cipher += N_BLOCK ;
+ }
+ return AES_SUCCESS ;
}
/******************************************************************************/
byte AES::decrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK])
{
- if (round)
- {
- byte s1 [N_BLOCK] ;
- copy_and_key (s1, plain, (byte*) (key_sched + round * N_BLOCK)) ;
- inv_shift_sub_rows (s1) ;
-
- for (byte r = round ; --r ; )
- {
- byte s2 [N_BLOCK] ;
- copy_and_key (s2, s1, (byte*) (key_sched + r * N_BLOCK)) ;
- inv_mix_sub_columns (s1, s2) ;
- }
- copy_and_key (cipher, s1, (byte*) (key_sched)) ;
- }
- else
- return AES_FAILURE ;
- return AES_SUCCESS ;
+ if (round) {
+ byte s1 [N_BLOCK] ;
+ copy_and_key (s1, plain, (byte*) (key_sched + round * N_BLOCK)) ;
+ inv_shift_sub_rows (s1) ;
+
+ for (byte r = round ; --r ; ) {
+ byte s2 [N_BLOCK] ;
+ copy_and_key (s2, s1, (byte*) (key_sched + r * N_BLOCK)) ;
+ inv_mix_sub_columns (s1, s2) ;
+ }
+ copy_and_key (cipher, s1, (byte*) (key_sched)) ;
+ } else {
+ return AES_FAILURE ;
+ }
+ return AES_SUCCESS ;
}
/******************************************************************************/
byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block, byte iv [N_BLOCK])
-{
- while (n_block--)
- {
- byte tmp [N_BLOCK] ;
- copy_n_bytes (tmp, cipher, N_BLOCK) ;
- if (decrypt (cipher, plain) != AES_SUCCESS)
- return AES_FAILURE ;
- xor_block (plain, iv) ;
- copy_n_bytes (iv, tmp, N_BLOCK) ;
- plain += N_BLOCK ;
- cipher += N_BLOCK;
- }
- return AES_SUCCESS ;
+{
+ while (n_block--) {
+ byte tmp [N_BLOCK] ;
+ copy_n_bytes (tmp, cipher, N_BLOCK) ;
+ if (decrypt (cipher, plain) != AES_SUCCESS) {
+ return AES_FAILURE ;
+ }
+ xor_block (plain, iv) ;
+ copy_n_bytes (iv, tmp, N_BLOCK) ;
+ plain += N_BLOCK ;
+ cipher += N_BLOCK;
+ }
+ return AES_SUCCESS ;
}
/******************************************************************************/
byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block)
-{
- while (n_block--)
- {
- byte tmp [N_BLOCK] ;
- copy_n_bytes (tmp, cipher, N_BLOCK) ;
- if (decrypt (cipher, plain) != AES_SUCCESS)
- return AES_FAILURE ;
- xor_block (plain, iv) ;
- copy_n_bytes (iv, tmp, N_BLOCK) ;
- plain += N_BLOCK ;
- cipher += N_BLOCK;
- }
- return AES_SUCCESS ;
+{
+ while (n_block--) {
+ byte tmp [N_BLOCK] ;
+ copy_n_bytes (tmp, cipher, N_BLOCK) ;
+ if (decrypt (cipher, plain) != AES_SUCCESS) {
+ return AES_FAILURE ;
+ }
+ xor_block (plain, iv) ;
+ copy_n_bytes (iv, tmp, N_BLOCK) ;
+ plain += N_BLOCK ;
+ cipher += N_BLOCK;
+ }
+ return AES_SUCCESS ;
}
/*****************************************************************************/
-void AES::set_IV(unsigned long long int IVCl){
+void AES::set_IV(unsigned long long int IVCl)
+{
memcpy(iv,&IVCl,8);
memcpy(iv+8,&IVCl,8);
IVC = IVCl;
@@ -454,7 +473,8 @@ void AES::set_IV(unsigned long long int IVCl){
/******************************************************************************/
-void AES::iv_inc(){
+void AES::iv_inc()
+{
IVC += 1;
memcpy(iv,&IVC,8);
memcpy(iv+8,&IVC,8);
@@ -462,31 +482,35 @@ void AES::iv_inc(){
/******************************************************************************/
-int AES::get_size(){
+int AES::get_size()
+{
return size;
}
/******************************************************************************/
-void AES::set_size(int sizel){
+void AES::set_size(int sizel)
+{
size = sizel;
}
/******************************************************************************/
-void AES::get_IV(byte *out){
+void AES::get_IV(byte *out)
+{
memcpy(out,&IVC,8);
memcpy(out+8,&IVC,8);
}
/******************************************************************************/
-void AES::calc_size_n_pad(int p_size){
+void AES::calc_size_n_pad(int p_size)
+{
int s_of_p = p_size - 1;
- if ( s_of_p % N_BLOCK == 0){
- size = s_of_p;
- }else{
+ if ( s_of_p % N_BLOCK == 0) {
+ size = s_of_p;
+ } else {
size = s_of_p + (N_BLOCK-(s_of_p % N_BLOCK));
}
pad = size - s_of_p;
@@ -497,59 +521,63 @@ void AES::calc_size_n_pad(int p_size){
void AES::padPlaintext(void* in,byte* out)
{
memcpy(out,in,size);
- for (int i = size-pad; i < size; i++){;
+ for (int i = size-pad; i < size; i++) {
+ ;
out[i] = arr_pad[pad - 1];
}
}
/******************************************************************************/
-bool AES::CheckPad(byte* in,int lsize){
- if (in[lsize-1] <= 0x0f){
+bool AES::CheckPad(byte* in,int lsize)
+{
+ if (in[lsize-1] <= 0x0f) {
int lpad = (int)in[lsize-1];
- for (int i = lsize - 1; i >= lsize-lpad; i--){
- if (arr_pad[lpad - 1] != in[i]){
+ for (int i = lsize - 1; i >= lsize-lpad; i--) {
+ if (arr_pad[lpad - 1] != in[i]) {
return false;
}
}
- }else{
+ } else {
return true;
}
-return true;
+ return true;
}
/******************************************************************************/
void AES::printArray(byte output[],bool p_pad)
{
-uint8_t i,j;
-uint8_t loops = size/N_BLOCK;
-uint8_t outp = N_BLOCK;
-for (j = 0; j < loops; j += 1){
- if (p_pad && (j == (loops - 1)) ) { outp = N_BLOCK - pad; }
- for (i = 0; i < outp; i++)
- {
- printf_P(PSTR("%c"),output[j*N_BLOCK + i]);
- }
-}
- printf_P(PSTR("\n"));
+ uint8_t i,j;
+ uint8_t loops = size/N_BLOCK;
+ uint8_t outp = N_BLOCK;
+ for (j = 0; j < loops; j += 1) {
+ if (p_pad && (j == (loops - 1)) ) {
+ outp = N_BLOCK - pad;
+ }
+ for (i = 0; i < outp; i++) {
+ printf_P(PSTR("%c"),output[j*N_BLOCK + i]);
+ }
+ }
+ printf_P(PSTR("\n"));
}
/******************************************************************************/
void AES::printArray(byte output[],int sizel)
{
- for (int i = 0; i < sizel; i++)
- {
- printf_P(PSTR("%x"),output[i]);
- }
- printf_P(PSTR("\n"));
+ for (int i = 0; i < sizel; i++) {
+ printf_P(PSTR("%x"),output[i]);
+ }
+ printf_P(PSTR("\n"));
}
/******************************************************************************/
-void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits, byte ivl [N_BLOCK]){
+void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits,
+ byte ivl [N_BLOCK])
+{
calc_size_n_pad(size_p);
byte plain_p[get_size()];
padPlaintext(plain,plain_p);
@@ -560,7 +588,8 @@ void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits
/******************************************************************************/
-void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits){
+void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits)
+{
calc_size_n_pad(size_p);
byte plain_p[get_size()];
padPlaintext(plain,plain_p);
@@ -571,7 +600,9 @@ void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits
/******************************************************************************/
-void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits, byte ivl [N_BLOCK]){
+void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits,
+ byte ivl [N_BLOCK])
+{
set_size(size_c);
int blocks = size_c / N_BLOCK;
set_key (key, bits);
@@ -580,7 +611,8 @@ void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits
/******************************************************************************/
-void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits){
+void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits)
+{
set_size(size_c);
int blocks = size_c / N_BLOCK;
set_key (key, bits);
@@ -591,7 +623,8 @@ void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits
/******************************************************************************/
#if defined(AES_LINUX)
-double AES::millis(){
+double AES::millis()
+{
gettimeofday(&tv, NULL);
return (tv.tv_sec + 0.000001 * tv.tv_usec);
}
diff --git a/drivers/AES/AES.h b/drivers/AES/AES.h
index 1d92c8c68..3c49f4e0e 100644
--- a/drivers/AES/AES.h
+++ b/drivers/AES/AES.h
@@ -31,44 +31,44 @@
This is an AES implementation that uses only 8-bit byte operations on the
cipher state.
*/
-
- /* code was modified by george spanos
- * 16/12/14
- */
+
+/* code was modified by george spanos
+* 16/12/14
+*/
/** AES class */
class AES
{
- public:
+public:
-/* The following calls are for a precomputed key schedule
+ /* The following calls are for a precomputed key schedule
- NOTE: If the length_type used for the key length is an
- unsigned 8-bit character, a key length of 256 bits must
- be entered as a length in bytes (valid inputs are hence
- 128, 192, 16, 24 and 32).
-*/
+ NOTE: If the length_type used for the key length is an
+ unsigned 8-bit character, a key length of 256 bits must
+ be entered as a length in bytes (valid inputs are hence
+ 128, 192, 16, 24 and 32).
+ */
/** \fn AES()
* \brief AES constructor
- *
+ *
* This function initialized an instance of AES.
*/
AES();
-
- /** Set the cipher key for the pre-keyed version.
+
+ /** Set the cipher key for the pre-keyed version.
* @param key[] pointer to the key string.
* @param keylen Integer that indicates the length of the key.
- * @note NOTE: If the length_type used for the key length is an unsigned 8-bit character,
- * a key length of 256 bits must be entered as a length in bytes
+ * @note NOTE: If the length_type used for the key length is an unsigned 8-bit character,
+ * a key length of 256 bits must be entered as a length in bytes
* (valid inputs are hence 128, 192, 16, 24 and 32).
*
*/
byte set_key (byte key[], int keylen) ;
-
+
/** clean up subkeys after use.
- *
- */
+ *
+ */
void clean () ; // delete key schedule after use
-
+
/** copying and xoring utilities.
* @param *AESt byte pointer of the AEStination array.
* @param *src byte pointer of the source array.
@@ -92,9 +92,9 @@ class AES
*
*/
byte encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK]) ;
-
+
/** CBC encrypt a number of blocks (input and return an IV).
- *
+ *
* @param *plain Pointer, points to the plaintex.
* @param *cipher Pointer, points to the ciphertext that will be created.
* @param n_block integer, indicated the number of blocks to be ciphered.
@@ -103,9 +103,9 @@ class AES
*
*/
byte cbc_encrypt (byte * plain, byte * cipher, int n_block, byte iv [N_BLOCK]) ;
-
+
/** CBC encrypt a number of blocks (input and return an IV).
- *
+ *
* @param *plain Pointer, points to the plaintex.
* @param *cipher Pointer, points to the ciphertext that will be created.
* @param n_block integer, indicated the number of blocks to be ciphered.
@@ -115,7 +115,7 @@ class AES
byte cbc_encrypt (byte * plain, byte * cipher, int n_block) ;
- /** Decrypt a single block of 16 bytes
+ /** Decrypt a single block of 16 bytes
* @param cipher Array of the ciphertext.
* @param plain Array of the plaintext.
* @note The N_BLOCK is defined in AES_config.h as,
@@ -128,9 +128,9 @@ class AES
*
*/
byte decrypt (byte cipher [N_BLOCK], byte plain [N_BLOCK]) ;
-
- /** CBC decrypt a number of blocks (input and return an IV)
- *
+
+ /** CBC decrypt a number of blocks (input and return an IV)
+ *
* @param *cipher Pointer, points to the ciphertext that will be created.
* @param *plain Pointer, points to the plaintex.
* @param n_block integer, indicated the number of blocks to be ciphered.
@@ -139,9 +139,9 @@ class AES
*
*/
byte cbc_decrypt (byte * cipher, byte * plain, int n_block, byte iv [N_BLOCK]) ;
-
- /** CBC decrypt a number of blocks (input and return an IV)
- *
+
+ /** CBC decrypt a number of blocks (input and return an IV)
+ *
* @param *cipher Pointer, points to the ciphertext that will be created.
* @param *plain Pointer, points to the plaintex.
* @param n_block integer, indicated the number of blocks to be ciphered.
@@ -149,7 +149,7 @@ class AES
*
*/
byte cbc_decrypt (byte * cipher, byte * plain, int n_block) ;
-
+
/** Sets IV (initialization vector) and IVC (IV counter).
* This function changes the ivc and iv variables needed for AES.
*
@@ -158,62 +158,62 @@ class AES
* @code unsigned long long int my_iv = 01234567; @endcode
*/
void set_IV(unsigned long long int IVCl);
-
+
/** increase the iv (initialization vector) and IVC (IV counter) by 1
- *
+ *
* This function increased the VI by one step in order to have a different IV each time
- *
+ *
*/
void iv_inc();
-
+
/** Getter method for size
- *
+ *
* This function return the size
* @return an integer, that is the size of the of the padded plaintext,
* thus, the size of the ciphertext.
*/
int get_size();
-
+
/** Setter method for size
*
* This function sets the size of the plaintext+pad
- *
+ *
*/
void set_size(int sizel);
-
+
/** Getter method for IV
- *
+ *
* This function return the IV
* @param out byte pointer that gets the IV.
* @return none, the IV is writed to the out pointer.
*/
void get_IV(byte *out);
-
+
/** Calculates the size of the plaintext and the padding.
- *
+ *
* Calculates the size of theplaintext with the padding
* and the size of the padding needed. Moreover it stores them in their class variables.
- *
+ *
* @param p_size the size of the byte array ex sizeof(plaintext)
*/
void calc_size_n_pad(int p_size);
-
+
/** Pads the plaintext
- *
- * This function pads the plaintext and returns an char array with the
- * plaintext and the padding in order for the plaintext to be compatible with
+ *
+ * This function pads the plaintext and returns an char array with the
+ * plaintext and the padding in order for the plaintext to be compatible with
* 16bit size blocks required by AES
- *
+ *
* @param in the string of the plaintext in a byte array
* @param out The string of the out array.
* @return no return, The padded plaintext is stored in the out pointer.
*/
void padPlaintext(void* in,byte* out);
-
+
/** Check the if the padding is correct.
- *
+ *
* This functions checks the padding of the plaintext.
- *
+ *
* @param in the string of the plaintext in a byte array
* @param size the size of the string
* @return true if correct / false if not
@@ -221,26 +221,26 @@ class AES
bool CheckPad(byte* in,int size);
/** Prints the array given.
- *
- * This function prints the given array and pad,
+ *
+ * This function prints the given array and pad,
* It is mainlly used for debugging purpuses or to output the string.
- *
+ *
* @param output[] the string of the text in a byte array
* @param p_pad optional, used to print with out the padding characters
*/
void printArray(byte output[],bool p_pad = true);
-
+
/** Prints the array given.
- *
+ *
* This function prints the given array in Hexadecimal.
- *
+ *
* @param output[] the string of the text in a byte array
* @param sizel the size of the array.
*/
void printArray(byte output[],int sizel);
-
+
/** User friendly implementation of AES-CBC encryption.
- *
+ *
* @param *plain pointer to the plaintext
* @param size_p size of the plaintext
* @param *cipher pointer to the ciphertext
@@ -250,9 +250,9 @@ class AES
* @note The key will be stored in class variable.
*/
void do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits, byte ivl [N_BLOCK]);
-
+
/** User friendly implementation of AES-CBC encryption.
- *
+ *
* @param *plain pointer to the plaintext
* @param size_p size of the plaintext
* @param *cipher pointer to the ciphertext
@@ -261,9 +261,9 @@ class AES
* @note The key will be stored in class variable.
*/
void do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits);
-
+
/** User friendly implementation of AES-CBC decryption.
- *
+ *
* @param *cipher pointer to the ciphertext
* @param size_c size of the ciphertext
* @param *plain pointer to the plaintext
@@ -273,9 +273,9 @@ class AES
* @note The key will be stored in class variable.
*/
void do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits, byte ivl [N_BLOCK]);
-
+
/** User friendly implementation of AES-CBC decryption.
- *
+ *
* @param *cipher pointer to the ciphertext
* @param size_c size of the ciphertext
* @param *plain pointer to the plaintext
@@ -285,68 +285,33 @@ class AES
*/
void do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits);
- #if defined(AES_LINUX)
- /**
- * used in linux in order to retrieve the time in milliseconds.
- *
- * @return returns the milliseconds in a double format.
- */
- double millis();
- #endif
- private:
- int round ;/**< holds the number of rounds to be used. */
- byte key_sched [KEY_SCHEDULE_BYTES] ;/**< holds the pre-computed key for the encryption/decrpytion. */
- unsigned long long int IVC;/**< holds the initialization vector counter in numerical format. */
- byte iv[16];/**< holds the initialization vector that will be used in the cipher. */
- int pad;/**< holds the size of the padding. */
- int size;/**< hold the size of the plaintext to be ciphered */
- #if defined(AES_LINUX)
+#if defined(AES_LINUX)
+ /**
+ * used in linux in order to retrieve the time in milliseconds.
+ *
+ * @return returns the milliseconds in a double format.
+ */
+ double millis();
+#endif
+private:
+ int round ;/**< holds the number of rounds to be used. */
+ byte key_sched [KEY_SCHEDULE_BYTES]
+ ;/**< holds the pre-computed key for the encryption/decrpytion. */
+ unsigned long long int IVC;/**< holds the initialization vector counter in numerical format. */
+ byte iv[16];/**< holds the initialization vector that will be used in the cipher. */
+ int pad;/**< holds the size of the padding. */
+ int size;/**< hold the size of the plaintext to be ciphered */
+#if defined(AES_LINUX)
timeval tv;/**< holds the time value on linux */
byte arr_pad[15];/**< holds the hexadecimal padding values on linux */
- #else
+#else
byte arr_pad[15];// = { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f };/**< holds the hexadecimal padding values */
- #endif
+#endif
} ;
#endif
-/**
- * @example aes.pde
- * For Arduino
- * Updated: spaniakos 2015
- *
- * This is an example of how to use AES in CBC mode easily.
- * The text and keys can be either in HEX or String format.
- */
-
- /**
- * @example aes.cpp
- * For Rasberry pi
- * Updated: spaniakos 2015
- *
- * This is an example of how to use AES in CBC mode easily.
- * The text and keys can be either in HEX or String format.
- */
-
- /**
- * @example test_vectors.pde
- * For Arduino
- * Updated: spaniakos 2015
- *
- * This is an example of monte carlo test vectors, in order to justify the effectiveness of the algorithm.
- * plus is a classical approach to the AES encryption library with out the easy to use add-on modifications.
- */
-
- /**
- * @example test_vectors.cpp
- * For Rasberry pi
- * Updated: spaniakos 2015
- *
- * This is an example of monte carlo test vectors, in order to justify the effectiveness of the algorithm.
- * plus is a classical approach to the AES encryption library with out the easy to use add-on modifications.
- */
-
/**
* @defgroup aeslib AES library for Arduino and Raspberry pi
* @ingroup internals
@@ -362,21 +327,21 @@ class AES
*
* @section Acknowledgements Acknowledgements
* This is an AES library for the Arduino, based on tzikis's AES library, which you can find here:.
- * Tzikis library was based on scottmac`s library, which you can find here:
- *
+ * Tzikis library was based on scottmac`s library, which you can find here:
+ *
* @section Installation Installation
*
Arduino
- * Create a folder named _AES_ in the _libraries_ folder inside your Arduino sketch folder. If the
+ * Create a folder named _AES_ in the _libraries_ folder inside your Arduino sketch folder. If the
* libraries folder doesn't exist, create it. Then copy everything inside. (re)launch the Arduino IDE.
* You're done. Time for a mojito
- *
+ *
*
Raspberry pi
* install
- *
+ *
* sudo make install
* cd examples_Rpi
* make
- *
+ *
* What to do after changes to the library
* sudo make clean
* sudo make install
@@ -392,7 +357,7 @@ class AES
* How to start a sketch
* cd examples_Rpi
* sudo ./\
- *
+ *
* @section AesNews News
*
* If issues are discovered with the documentation, please report them here
@@ -411,6 +376,6 @@ class AES
* - Arduino
* - Intel Galileo support
* - Raspberry Pi Support
- *
+ *
* - The library has not been tested to other boards, but it should suppport ATMega 328 based boards,Mega Boards,Arduino Due,ATTiny board
*/
diff --git a/drivers/AES/AES_config.h b/drivers/AES/AES_config.h
index d0ae8282f..ab3848960 100644
--- a/drivers/AES/AES_config.h
+++ b/drivers/AES/AES_config.h
@@ -7,34 +7,34 @@
#if (defined(__linux) || defined(linux)) && !defined(__ARDUINO_X86__)
- #define AES_LINUX
-
- #include
- #include
- #include
- #include
- #include
- #include
+#define AES_LINUX
+
+#include
+#include
+#include
+#include
+#include
+#include
#else
- #include
+#include
#endif
#include
#include
#if defined(__ARDUINO_X86__) || (defined (__linux) || defined (linux))
- #undef PROGMEM
- #define PROGMEM __attribute__(( section(".progmem.data") ))
- #define pgm_read_byte(p) (*(p))
- typedef unsigned char byte;
- #define printf_P printf
- #define PSTR(x) (x)
+#undef PROGMEM
+#define PROGMEM __attribute__(( section(".progmem.data") ))
+#define pgm_read_byte(p) (*(p))
+typedef unsigned char byte;
+#define printf_P printf
+#define PSTR(x) (x)
#elif defined(ARDUINO_ARCH_ESP8266)
- #include
+#include
#elif defined(ARDUINO_ARCH_SAMD)
- #define printf_P printf
+#define printf_P printf
#else
- #include
+#include
#endif
#define N_ROW 4
diff --git a/drivers/AES/Doxyfile b/drivers/AES/Doxyfile
deleted file mode 100644
index 79cf3b7dc..000000000
--- a/drivers/AES/Doxyfile
+++ /dev/null
@@ -1,2372 +0,0 @@
-# Doxyfile 1.8.6
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project.
-#
-# All text after a double hash (##) is considered a comment and is placed in
-# front of the TAG it is preceding.
-#
-# All text after a single hash (#) is considered a comment and will be ignored.
-# The format is:
-# TAG = value [value, ...]
-# For lists, items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (\" \").
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all text
-# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
-# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
-# for the list of possible encodings.
-# The default value is: UTF-8.
-
-DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
-# double-quotes, unless you are using Doxywizard) that should identify the
-# project for which the documentation is generated. This name is used in the
-# title of most generated pages and in a few other places.
-# The default value is: My Project.
-
-PROJECT_NAME = "AES Encryption Library for Arduino and Raspberry Pi"
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
-# could be handy for archiving the generated documentation or if some version
-# control system is used.
-
-PROJECT_NUMBER =
-
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer a
-# quick idea about the purpose of the project. Keep the description short.
-
-PROJECT_BRIEF = "Spaniakos - AES Encryption Library for Arduino and Raspberry Pi"
-
-# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
-# the documentation. The maximum height of the logo should not exceed 55 pixels
-# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
-# to the output directory.
-
-PROJECT_LOGO =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
-# into which the generated documentation will be written. If a relative path is
-# entered, it will be relative to the location where doxygen was started. If
-# left blank the current directory will be used.
-
-OUTPUT_DIRECTORY = "../Documentations/AES"
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
-# directories (in 2 levels) under the output directory of each output format and
-# will distribute the generated files over these directories. Enabling this
-# option can be useful when feeding doxygen a huge amount of source files, where
-# putting all generated files in the same directory would otherwise causes
-# performance problems for the file system.
-# The default value is: NO.
-
-CREATE_SUBDIRS = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
-# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
-# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
-# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
-# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
-# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
-# Ukrainian and Vietnamese.
-# The default value is: English.
-
-OUTPUT_LANGUAGE = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
-# descriptions after the members that are listed in the file and class
-# documentation (similar to Javadoc). Set to NO to disable this.
-# The default value is: YES.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
-# description of a member or function before the detailed description
-#
-# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-# The default value is: YES.
-
-REPEAT_BRIEF = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator that is
-# used to form the text in various listings. Each string in this list, if found
-# as the leading text of the brief description, will be stripped from the text
-# and the result, after processing the whole list, is used as the annotated
-# text. Otherwise, the brief description is used as-is. If left blank, the
-# following values are used ($name is automatically replaced with the name of
-# the entity):The $name class, The $name widget, The $name file, is, provides,
-# specifies, contains, represents, a, an and the.
-
-ABBREVIATE_BRIEF = "The $name class" \
- "The $name widget" \
- "The $name file" \
- is \
- provides \
- specifies \
- contains \
- represents \
- a \
- an \
- the
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# doxygen will generate a detailed section even if there is only a brief
-# description.
-# The default value is: NO.
-
-ALWAYS_DETAILED_SEC = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-# The default value is: NO.
-
-INLINE_INHERITED_MEMB = NO
-
-# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
-# before files name in the file list and in the header files. If set to NO the
-# shortest path that makes the file name unique will be used
-# The default value is: YES.
-
-FULL_PATH_NAMES = YES
-
-# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
-# Stripping is only done if one of the specified strings matches the left-hand
-# part of the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the path to
-# strip.
-#
-# Note that you can specify absolute paths here, but also relative paths, which
-# will be relative from the directory where doxygen is started.
-# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-
-STRIP_FROM_PATH =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
-# path mentioned in the documentation of a class, which tells the reader which
-# header file to include in order to use a class. If left blank only the name of
-# the header file containing the class definition is used. Otherwise one should
-# specify the list of include paths that are normally passed to the compiler
-# using the -I flag.
-
-STRIP_FROM_INC_PATH =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
-# less readable) file names. This can be useful is your file systems doesn't
-# support long names like on DOS, Mac, or CD-ROM.
-# The default value is: NO.
-
-SHORT_NAMES = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
-# first line (until the first dot) of a Javadoc-style comment as the brief
-# description. If set to NO, the Javadoc-style will behave just like regular Qt-
-# style comments (thus requiring an explicit @brief command for a brief
-# description.)
-# The default value is: NO.
-
-JAVADOC_AUTOBRIEF = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
-# line (until the first dot) of a Qt-style comment as the brief description. If
-# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
-# requiring an explicit \brief command for a brief description.)
-# The default value is: NO.
-
-QT_AUTOBRIEF = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
-# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
-# a brief description. This used to be the default behavior. The new default is
-# to treat a multi-line C++ comment block as a detailed description. Set this
-# tag to YES if you prefer the old behavior instead.
-#
-# Note that setting this tag to YES also means that rational rose comments are
-# not recognized any more.
-# The default value is: NO.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
-# documentation from any documented member that it re-implements.
-# The default value is: YES.
-
-INHERIT_DOCS = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
-# new page for each member. If set to NO, the documentation of a member will be
-# part of the file/class/namespace that contains it.
-# The default value is: NO.
-
-SEPARATE_MEMBER_PAGES = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
-# uses this value to replace tabs by spaces in code fragments.
-# Minimum value: 1, maximum value: 16, default value: 4.
-
-TAB_SIZE = 4
-
-# This tag can be used to specify a number of aliases that act as commands in
-# the documentation. An alias has the form:
-# name=value
-# For example adding
-# "sideeffect=@par Side Effects:\n"
-# will allow you to put the command \sideeffect (or @sideeffect) in the
-# documentation, which will result in a user-defined paragraph with heading
-# "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines.
-
-ALIASES =
-
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
-# only. Doxygen will then generate output that is more tailored for C. For
-# instance, some of the names that are used will be different. The list of all
-# members will be omitted, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_FOR_C = NO
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
-# Python sources only. Doxygen will then generate output that is more tailored
-# for that language. For instance, namespaces will be presented as packages,
-# qualified scopes will look different, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_JAVA = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources. Doxygen will then generate output that is tailored for Fortran.
-# The default value is: NO.
-
-OPTIMIZE_FOR_FORTRAN = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for VHDL.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_VHDL = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given
-# extension. Doxygen has a built-in mapping, but you can override or extend it
-# using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C.
-#
-# Note For files without extension you can use no_extension as a placeholder.
-#
-# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
-# the files are not read by doxygen.
-
-EXTENSION_MAPPING = ino=c \
- pde=c
-
-# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
-# according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you can
-# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
-# case of backward compatibilities issues.
-# The default value is: YES.
-
-MARKDOWN_SUPPORT = YES
-
-# When enabled doxygen tries to link words that correspond to documented
-# classes, or namespaces to their corresponding documentation. Such a link can
-# be prevented in individual cases by by putting a % sign in front of the word
-# or globally by setting AUTOLINK_SUPPORT to NO.
-# The default value is: YES.
-
-AUTOLINK_SUPPORT = YES
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should set this
-# tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string);
-# versus func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-# The default value is: NO.
-
-BUILTIN_STL_SUPPORT = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-# The default value is: NO.
-
-CPP_CLI_SUPPORT = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
-# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
-# will parse them like normal C++ but will assume all classes use public instead
-# of private inheritance when no explicit protection keyword is present.
-# The default value is: NO.
-
-SIP_SUPPORT = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate
-# getter and setter methods for a property. Setting this option to YES will make
-# doxygen to replace the get and set methods by a property in the documentation.
-# This will only work if the methods are indeed getting or setting a simple
-# type. If this is not the case, or you want to show the methods anyway, you
-# should set this option to NO.
-# The default value is: YES.
-
-IDL_PROPERTY_SUPPORT = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-# The default value is: NO.
-
-DISTRIBUTE_GROUP_DOC = NO
-
-# Set the SUBGROUPING tag to YES to allow class member groups of the same type
-# (for instance a group of public functions) to be put as a subgroup of that
-# type (e.g. under the Public Functions section). Set it to NO to prevent
-# subgrouping. Alternatively, this can be done per class using the
-# \nosubgrouping command.
-# The default value is: YES.
-
-SUBGROUPING = YES
-
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
-# are shown inside the group in which they are included (e.g. using \ingroup)
-# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
-# and RTF).
-#
-# Note that this feature does not work in combination with
-# SEPARATE_MEMBER_PAGES.
-# The default value is: NO.
-
-INLINE_GROUPED_CLASSES = NO
-
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
-# with only public data fields or simple typedef fields will be shown inline in
-# the documentation of the scope in which they are defined (i.e. file,
-# namespace, or group documentation), provided this scope is documented. If set
-# to NO, structs, classes, and unions are shown on a separate page (for HTML and
-# Man pages) or section (for LaTeX and RTF).
-# The default value is: NO.
-
-INLINE_SIMPLE_STRUCTS = NO
-
-# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
-# enum is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically be
-# useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-# The default value is: NO.
-
-TYPEDEF_HIDES_STRUCT = NO
-
-# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
-# cache is used to resolve symbols given their name and scope. Since this can be
-# an expensive process and often the same symbol appears multiple times in the
-# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
-# doxygen will become slower. If the cache is too large, memory is wasted. The
-# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
-# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
-# symbols. At the end of a run doxygen will report the cache usage and suggest
-# the optimal cache size from a speed point of view.
-# Minimum value: 0, maximum value: 9, default value: 0.
-
-LOOKUP_CACHE_SIZE = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available. Private
-# class members and static file members will be hidden unless the
-# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
-# Note: This will also disable the warnings about undocumented members that are
-# normally produced when WARNINGS is set to YES.
-# The default value is: NO.
-
-EXTRACT_ALL = YES
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
-# be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PRIVATE = YES
-
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
-# scope will be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PACKAGE = YES
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
-# included in the documentation.
-# The default value is: NO.
-
-EXTRACT_STATIC = YES
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
-# locally in source files will be included in the documentation. If set to NO
-# only classes defined in header files are included. Does not have any effect
-# for Java sources.
-# The default value is: YES.
-
-EXTRACT_LOCAL_CLASSES = YES
-
-# This flag is only useful for Objective-C code. When set to YES local methods,
-# which are defined in the implementation section but not in the interface are
-# included in the documentation. If set to NO only methods in the interface are
-# included.
-# The default value is: NO.
-
-EXTRACT_LOCAL_METHODS = YES
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base name of
-# the file that contains the anonymous namespace. By default anonymous namespace
-# are hidden.
-# The default value is: NO.
-
-EXTRACT_ANON_NSPACES = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
-# undocumented members inside documented classes or files. If set to NO these
-# members will be included in the various overviews, but no documentation
-# section is generated. This option has no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy. If set
-# to NO these classes will be included in the various overviews. This option has
-# no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-HIDE_UNDOC_CLASSES = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# (class|struct|union) declarations. If set to NO these declarations will be
-# included in the documentation.
-# The default value is: NO.
-
-HIDE_FRIEND_COMPOUNDS = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
-# documentation blocks found inside the body of a function. If set to NO these
-# blocks will be appended to the function's detailed documentation block.
-# The default value is: NO.
-
-HIDE_IN_BODY_DOCS = NO
-
-# The INTERNAL_DOCS tag determines if documentation that is typed after a
-# \internal command is included. If the tag is set to NO then the documentation
-# will be excluded. Set it to YES to include the internal documentation.
-# The default value is: NO.
-
-INTERNAL_DOCS = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
-# names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-# The default value is: system dependent.
-
-CASE_SENSE_NAMES = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
-# their full class and namespace scopes in the documentation. If set to YES the
-# scope will be hidden.
-# The default value is: NO.
-
-HIDE_SCOPE_NAMES = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
-# the files that are included by a file in the documentation of that file.
-# The default value is: YES.
-
-SHOW_INCLUDE_FILES = YES
-
-
-SHOW_GROUPED_MEMB_INC = NO
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
-# files with double quotes in the documentation rather than with sharp brackets.
-# The default value is: NO.
-
-FORCE_LOCAL_INCLUDES = NO
-
-# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
-# documentation for inline members.
-# The default value is: YES.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
-# (detailed) documentation of file and class members alphabetically by member
-# name. If set to NO the members will appear in declaration order.
-# The default value is: YES.
-
-SORT_MEMBER_DOCS = NO
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
-# descriptions of file, namespace and class members alphabetically by member
-# name. If set to NO the members will appear in declaration order. Note that
-# this will also influence the order of the classes in the class list.
-# The default value is: NO.
-
-SORT_BRIEF_DOCS = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
-# (brief and detailed) documentation of class members so that constructors and
-# destructors are listed first. If set to NO the constructors will appear in the
-# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
-# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
-# member documentation.
-# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
-# detailed member documentation.
-# The default value is: NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
-# of group names into alphabetical order. If set to NO the group names will
-# appear in their defined order.
-# The default value is: NO.
-
-SORT_GROUP_NAMES = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
-# fully-qualified names, including namespaces. If set to NO, the class list will
-# be sorted only by class name, not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the alphabetical
-# list.
-# The default value is: NO.
-
-SORT_BY_SCOPE_NAME = YES
-
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
-# type resolution of all parameters of a function it will reject a match between
-# the prototype and the implementation of a member function even if there is
-# only one candidate or it is obvious which candidate to choose by doing a
-# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
-# accept a match between prototype and implementation in such cases.
-# The default value is: NO.
-
-STRICT_PROTO_MATCHING = NO
-
-# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
-# todo list. This list is created by putting \todo commands in the
-# documentation.
-# The default value is: YES.
-
-GENERATE_TODOLIST = NO
-
-# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
-# test list. This list is created by putting \test commands in the
-# documentation.
-# The default value is: YES.
-
-GENERATE_TESTLIST = YES
-
-# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
-# list. This list is created by putting \bug commands in the documentation.
-# The default value is: YES.
-
-GENERATE_BUGLIST = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
-# the deprecated list. This list is created by putting \deprecated commands in
-# the documentation.
-# The default value is: YES.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional documentation
-# sections, marked by \if ... \endif and \cond
-# ... \endcond blocks.
-
-ENABLED_SECTIONS =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
-# initial value of a variable or macro / define can have for it to appear in the
-# documentation. If the initializer consists of more lines than specified here
-# it will be hidden. Use a value of 0 to hide initializers completely. The
-# appearance of the value of individual variables and macros / defines can be
-# controlled using \showinitializer or \hideinitializer command in the
-# documentation regardless of this setting.
-# Minimum value: 0, maximum value: 10000, default value: 30.
-
-MAX_INITIALIZER_LINES = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
-# the bottom of the documentation of classes and structs. If set to YES the list
-# will mention the files that were used to generate the documentation.
-# The default value is: YES.
-
-SHOW_USED_FILES = NO
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
-# will remove the Files entry from the Quick Index and from the Folder Tree View
-# (if specified).
-# The default value is: YES.
-
-SHOW_FILES = NO
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
-# page. This will remove the Namespaces entry from the Quick Index and from the
-# Folder Tree View (if specified).
-# The default value is: YES.
-
-SHOW_NAMESPACES = NO
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command command input-file, where command is the value of the
-# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
-# by doxygen. Whatever the program writes to standard output is used as the file
-# version. For an example see the documentation.
-
-FILE_VERSION_FILTER =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option. You can
-# optionally specify a file name after the option, if omitted DoxygenLayout.xml
-# will be used as the name of the layout file.
-#
-# Note that if you run doxygen from a directory containing a file called
-# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
-# tag is left empty.
-
-LAYOUT_FILE =
-
-# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
-# the reference definitions. This must be a list of .bib files. The .bib
-# extension is automatically appended if omitted. This requires the bibtex tool
-# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
-# For LaTeX the style of the bibliography can be controlled using
-# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
-# search path. Do not use file names with spaces, bibtex cannot handle them. See
-# also \cite for info how to create references.
-
-CITE_BIB_FILES =
-
-#---------------------------------------------------------------------------
-# Configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated to
-# standard output by doxygen. If QUIET is set to YES this implies that the
-# messages are off.
-# The default value is: NO.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
-# this implies that the warnings are on.
-#
-# Tip: Turn warnings on while writing the documentation.
-# The default value is: YES.
-
-WARNINGS = YES
-
-# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
-# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
-# will automatically be disabled.
-# The default value is: YES.
-
-WARN_IF_UNDOCUMENTED = YES
-
-# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some parameters
-# in a documented function, or documenting parameters that don't exist or using
-# markup commands wrongly.
-# The default value is: YES.
-
-WARN_IF_DOC_ERROR = YES
-
-# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
-# are documented, but have no documentation for their parameters or return
-# value. If set to NO doxygen will only warn about wrong or incomplete parameter
-# documentation, but not about the absence of documentation.
-# The default value is: NO.
-
-WARN_NO_PARAMDOC = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that doxygen
-# can produce. The string should contain the $file, $line, and $text tags, which
-# will be replaced by the file and line number from which the warning originated
-# and the warning text. Optionally the format may contain $version, which will
-# be replaced by the version of the file (if it could be obtained via
-# FILE_VERSION_FILTER)
-# The default value is: $file:$line: $text.
-
-WARN_FORMAT = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning and error
-# messages should be written. If left blank the output is written to standard
-# error (stderr).
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag is used to specify the files and/or directories that contain
-# documented source files. You may enter file names like myfile.cpp or
-# directories like /usr/src/myproject. Separate the files or directories with
-# spaces.
-# Note: If this tag is empty the current directory is searched.
-
-INPUT = ./
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
-# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: http://www.gnu.org/software/libiconv) for the list of
-# possible encodings.
-# The default value is: UTF-8.
-
-INPUT_ENCODING = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
-# *.h) to filter out the source-files in the directories. If left blank the
-# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
-# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
-# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
-# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
-# *.qsf, *.as and *.js.
-
-FILE_PATTERNS = *.c \
- *.cc \
- *.cxx \
- *.cpp \
- *.c++ \
- *.java \
- *.ii \
- *.ixx \
- *.ipp \
- *.i++ \
- *.inl \
- *.idl \
- *.ddl \
- *.odl \
- *.h \
- *.hh \
- *.hxx \
- *.hpp \
- *.h++ \
- *.cs \
- *.d \
- *.php \
- *.php4 \
- *.php5 \
- *.phtml \
- *.inc \
- *.m \
- *.markdown \
- *.md \
- *.mm \
- *.dox \
- *.py \
- *.f90 \
- *.f \
- *.for \
- *.tcl \
- *.vhd \
- *.vhdl \
- *.ucf \
- *.qsf \
- *.as \
- *.js
-
-# The RECURSIVE tag can be used to specify whether or not subdirectories should
-# be searched for input files as well.
-# The default value is: NO.
-
-RECURSIVE = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-#
-# Note that relative paths are relative to the directory from which doxygen is
-# run.
-
-EXCLUDE =
-
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
-# from the input.
-# The default value is: NO.
-
-EXCLUDE_SYMLINKS = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories.
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories for example use the pattern */test/*
-
-EXCLUDE_PATTERNS =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories use the pattern */test/*
-
-EXCLUDE_SYMBOLS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or directories
-# that contain example code fragments that are included (see the \include
-# command).
-
-EXAMPLE_PATH = examples \
- examples_RPi \
- examples_Rpi
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
-# *.h) to filter out the source-files in the directories. If left blank all
-# files are included.
-
-EXAMPLE_PATTERNS =
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude commands
-# irrespective of the value of the RECURSIVE tag.
-# The default value is: NO.
-
-EXAMPLE_RECURSIVE = YES
-
-# The IMAGE_PATH tag can be used to specify one or more files or directories
-# that contain images that are to be included in the documentation (see the
-# \image command).
-
-IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command:
-#
-#
-#
-# where is the value of the INPUT_FILTER tag, and is the
-# name of an input file. Doxygen will then use the output that the filter
-# program writes to standard output. If FILTER_PATTERNS is specified, this tag
-# will be ignored.
-#
-# Note that the filter must not add or remove lines; it is applied before the
-# code is scanned, but not when the output code is generated. If lines are added
-# or removed, the anchors will not be placed correctly.
-
-INPUT_FILTER =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis. Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match. The filters are a list of the form: pattern=filter
-# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
-# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
-# patterns match the file name, INPUT_FILTER is applied.
-
-FILTER_PATTERNS =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER ) will also be used to filter the input files that are used for
-# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
-# The default value is: NO.
-
-FILTER_SOURCE_FILES = NO
-
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
-# it is also possible to disable source filtering for a specific pattern using
-# *.ext= (so without naming a filter).
-# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
-
-FILTER_SOURCE_PATTERNS =
-
-# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
-# is part of the input, its contents will be placed on the main page
-# (index.html). This can be useful if you have a project on for instance GitHub
-# and want to reuse the introduction page also for the doxygen output.
-
-USE_MDFILE_AS_MAINPAGE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
-# generated. Documented entities will be cross-referenced with these sources.
-#
-# Note: To get rid of all source code in the generated output, make sure that
-# also VERBATIM_HEADERS is set to NO.
-# The default value is: NO.
-
-SOURCE_BROWSER = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body of functions,
-# classes and enums directly into the documentation.
-# The default value is: NO.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
-# special comment blocks from generated source code fragments. Normal C, C++ and
-# Fortran comments will always remain visible.
-# The default value is: YES.
-
-STRIP_CODE_COMMENTS = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
-# function all documented functions referencing it will be listed.
-# The default value is: NO.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES then for each documented function
-# all documented entities called/used by that function will be listed.
-# The default value is: NO.
-
-REFERENCES_RELATION = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
-# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
-# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
-# link to the documentation.
-# The default value is: YES.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
-# source code will show a tooltip with additional information such as prototype,
-# brief description and links to the definition and documentation. Since this
-# will make the HTML file larger and loading of large files a bit slower, you
-# can opt to disable this feature.
-# The default value is: YES.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-SOURCE_TOOLTIPS = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code will
-# point to the HTML generated by the htags(1) tool instead of doxygen built-in
-# source browser. The htags tool is part of GNU's global source tagging system
-# (see http://www.gnu.org/software/global/global.html). You will need version
-# 4.8.6 or higher.
-#
-# To use it do the following:
-# - Install the latest version of global
-# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
-# - Make sure the INPUT points to the root of the source tree
-# - Run doxygen as normal
-#
-# Doxygen will invoke htags (and that will in turn invoke gtags), so these
-# tools must be available from the command line (i.e. in the search path).
-#
-# The result: instead of the source browser generated by doxygen, the links to
-# source code will now point to the output of htags.
-# The default value is: NO.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
-# verbatim copy of the header file for each class for which an include is
-# specified. Set to NO to disable this.
-# See also: Section \class.
-# The default value is: YES.
-
-VERBATIM_HEADERS = YES
-
-# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
-# clang parser (see: http://clang.llvm.org/) for more acurate parsing at the
-# cost of reduced performance. This can be particularly helpful with template
-# rich C++ code for which doxygen's built-in parser lacks the necessary type
-# information.
-# Note: The availability of this option depends on whether or not doxygen was
-# compiled with the --with-libclang option.
-# The default value is: NO.
-
-CLANG_ASSISTED_PARSING = NO
-
-# If clang assisted parsing is enabled you can provide the compiler with command
-# line options that you would normally use when invoking the compiler. Note that
-# the include paths will already be set by doxygen for the files and directories
-# specified with INPUT and INCLUDE_PATH.
-# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
-
-CLANG_OPTIONS =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
-# compounds will be generated. Enable this if the project contains a lot of
-# classes, structs, unions or interfaces.
-# The default value is: YES.
-
-ALPHABETICAL_INDEX = YES
-
-# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
-# which the alphabetical index list will be split.
-# Minimum value: 1, maximum value: 20, default value: 5.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-COLS_IN_ALPHA_INDEX = 10
-
-# In case all classes in a project start with a common prefix, all classes will
-# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
-# can be used to specify a prefix (or a list of prefixes) that should be ignored
-# while generating the index headers.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
-# The default value is: YES.
-
-GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
-# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
-# it.
-# The default directory is: html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_OUTPUT = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
-# generated HTML page (for example: .htm, .php, .asp).
-# The default value is: .html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
-# each generated HTML page. If the tag is left blank doxygen will generate a
-# standard header.
-#
-# To get valid HTML the header file that includes any scripts and style sheets
-# that doxygen needs, which is dependent on the configuration options used (e.g.
-# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
-# default header using
-# doxygen -w html new_header.html new_footer.html new_stylesheet.css
-# YourConfigFile
-# and then modify the file new_header.html. See also section "Doxygen usage"
-# for information on how to generate the default header that doxygen normally
-# uses.
-# Note: The header is subject to change so you typically have to regenerate the
-# default header when upgrading to a newer version of doxygen. For a description
-# of the possible markers and block names see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
-# generated HTML page. If the tag is left blank doxygen will generate a standard
-# footer. See HTML_HEADER for more information on how to generate a default
-# footer and what special commands can be used inside the footer. See also
-# section "Doxygen usage" for information on how to generate the default footer
-# that doxygen normally uses.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FOOTER =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
-# sheet that is used by each HTML page. It can be used to fine-tune the look of
-# the HTML output. If left blank doxygen will generate a default style sheet.
-# See also section "Doxygen usage" for information on how to generate the style
-# sheet that doxygen normally uses.
-# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
-# it is more robust and this tag (HTML_STYLESHEET) will in the future become
-# obsolete.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_STYLESHEET =
-
-# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
-# defined cascading style sheet that is included after the standard style sheets
-# created by doxygen. Using this option one can overrule certain style aspects.
-# This is preferred over using HTML_STYLESHEET since it does not replace the
-# standard style sheet and is therefor more robust against future updates.
-# Doxygen will copy the style sheet file to the output directory. For an example
-# see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_STYLESHEET = doxygen-custom.css
-
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
-# files will be copied as-is; there are no commands or markers available.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_FILES =
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
-# will adjust the colors in the stylesheet and background images according to
-# this color. Hue is specified as an angle on a colorwheel, see
-# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
-# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
-# purple, and 360 is red again.
-# Minimum value: 0, maximum value: 359, default value: 220.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_HUE = 220
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
-# in the HTML output. For a value of 0 the output will use grayscales only. A
-# value of 255 will produce the most vivid colors.
-# Minimum value: 0, maximum value: 255, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_SAT = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
-# luminance component of the colors in the HTML output. Values below 100
-# gradually make the output lighter, whereas values above 100 make the output
-# darker. The value divided by 100 is the actual gamma applied, so 80 represents
-# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
-# change the gamma.
-# Minimum value: 40, maximum value: 240, default value: 80.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_COLORSTYLE_GAMMA = 80
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting this
-# to NO can help when comparing the output of multiple runs.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_TIMESTAMP = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_DYNAMIC_SECTIONS = YES
-
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
-# shown in the various tree structured indices initially; the user can expand
-# and collapse entries dynamically later on. Doxygen will expand the tree to
-# such a level that at most the specified number of entries are visible (unless
-# a fully collapsed tree already exceeds this amount). So setting the number of
-# entries 1 will produce a full collapsed tree by default. 0 is a special value
-# representing an infinite number of entries and will result in a full expanded
-# tree by default.
-# Minimum value: 0, maximum value: 9999, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_INDEX_NUM_ENTRIES = 100
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files will be
-# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: http://developer.apple.com/tools/xcode/), introduced with
-# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
-# Makefile in the HTML output directory. Running make will produce the docset in
-# that directory and running make install will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
-# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_DOCSET = NO
-
-# This tag determines the name of the docset feed. A documentation feed provides
-# an umbrella under which multiple documentation sets from a single provider
-# (such as a company or product suite) can be grouped.
-# The default value is: Doxygen generated docs.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_FEEDNAME = "Doxygen generated docs"
-
-# This tag specifies a string that should uniquely identify the documentation
-# set bundle. This should be a reverse domain-name style string, e.g.
-# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_BUNDLE_ID = org.doxygen.Project
-
-# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
-# The default value is: org.doxygen.Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-
-# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
-# The default value is: Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_NAME = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
-# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
-# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
-# Windows.
-#
-# The HTML Help Workshop contains a compiler that can convert all HTML output
-# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
-# files are now used as the Windows 98 help format, and will replace the old
-# Windows help format (.hlp) on all Windows platforms in the future. Compressed
-# HTML files also contain an index, a table of contents, and you can search for
-# words in the documentation. The HTML workshop also contains a viewer for
-# compressed HTML files.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_HTMLHELP = NO
-
-# The CHM_FILE tag can be used to specify the file name of the resulting .chm
-# file. You can add a path in front of the file if the result should not be
-# written to the html output directory.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_FILE =
-
-# The HHC_LOCATION tag can be used to specify the location (absolute path
-# including file name) of the HTML help compiler ( hhc.exe). If non-empty
-# doxygen will try to run the HTML help compiler on the generated index.hhp.
-# The file has to be specified with full path.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-HHC_LOCATION =
-
-# The GENERATE_CHI flag controls if a separate .chi index file is generated (
-# YES) or that it should be included in the master .chm file ( NO).
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-GENERATE_CHI = NO
-
-# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
-# and project file content.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_INDEX_ENCODING =
-
-# The BINARY_TOC flag controls whether a binary table of contents is generated (
-# YES) or a normal table of contents ( NO) in the .chm file.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members to
-# the table of contents of the HTML help documentation and to the tree view.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-TOC_EXPAND = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
-# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
-# (.qch) of the generated HTML documentation.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_QHP = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
-# the file name of the resulting .qch file. The path specified is relative to
-# the HTML output folder.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QCH_FILE =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
-# Project output. For more information please see Qt Help Project / Namespace
-# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_NAMESPACE = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
-# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
-# folders).
-# The default value is: doc.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_VIRTUAL_FOLDER = doc
-
-# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
-# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_NAME =
-
-# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_ATTRS =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's filter section matches. Qt Help Project / Filter Attributes (see:
-# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_SECT_FILTER_ATTRS =
-
-# The QHG_LOCATION tag can be used to specify the location of Qt's
-# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
-# generated .qhp file.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHG_LOCATION =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
-# generated, together with the HTML files, they form an Eclipse help plugin. To
-# install this plugin and make it available under the help contents menu in
-# Eclipse, the contents of the directory containing the HTML and XML files needs
-# to be copied into the plugins directory of eclipse. The name of the directory
-# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
-# After copying Eclipse needs to be restarted before the help appears.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_ECLIPSEHELP = NO
-
-# A unique identifier for the Eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have this
-# name. Each documentation set should have its own identifier.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
-
-ECLIPSE_DOC_ID = org.doxygen.Project
-
-# If you want full control over the layout of the generated HTML pages it might
-# be necessary to disable the index and replace it with your own. The
-# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
-# of each HTML page. A value of NO enables the index and the value YES disables
-# it. Since the tabs in the index contain the same information as the navigation
-# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-DISABLE_INDEX = NO
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information. If the tag
-# value is set to YES, a side panel will be generated containing a tree-like
-# index structure (just like the one that is generated for HTML Help). For this
-# to work a browser that supports JavaScript, DHTML, CSS and frames is required
-# (i.e. any modern browser). Windows users are probably better off using the
-# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
-# further fine-tune the look of the index. As an example, the default style
-# sheet generated by doxygen has an example that shows how to put an image at
-# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
-# the same information as the tab index, you could consider setting
-# DISABLE_INDEX to YES when enabling this option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_TREEVIEW = NO
-
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
-# doxygen will group on one line in the generated HTML documentation.
-#
-# Note that a value of 0 will completely suppress the enum values from appearing
-# in the overview section.
-# Minimum value: 0, maximum value: 20, default value: 4.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-ENUM_VALUES_PER_LINE = 4
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
-# to set the initial width (in pixels) of the frame in which the tree is shown.
-# Minimum value: 0, maximum value: 1500, default value: 250.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-TREEVIEW_WIDTH = 250
-
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
-# external symbols imported via tag files in a separate window.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-EXT_LINKS_IN_WINDOW = NO
-
-# Use this tag to change the font size of LaTeX formulas included as images in
-# the HTML documentation. When you change the font size after a successful
-# doxygen run you need to manually remove any form_*.png images from the HTML
-# output directory to force them to be regenerated.
-# Minimum value: 8, maximum value: 50, default value: 10.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_FONTSIZE = 10
-
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are not
-# supported properly for IE 6.0, but are supported on all modern browsers.
-#
-# Note that when changing this option you need to delete any form_*.png files in
-# the HTML output directory before the changes have effect.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-FORMULA_TRANSPARENT = YES
-
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# http://www.mathjax.org) which uses client side Javascript for the rendering
-# instead of using prerendered bitmaps. Use this if you do not have LaTeX
-# installed or if you want to formulas look prettier in the HTML output. When
-# enabled you may also need to install MathJax separately and configure the path
-# to it using the MATHJAX_RELPATH option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-USE_MATHJAX = NO
-
-# When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/latest/output.html) for more details.
-# Possible values are: HTML-CSS (which is slower, but has the best
-# compatibility), NativeMML (i.e. MathML) and SVG.
-# The default value is: HTML-CSS.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_FORMAT = HTML-CSS
-
-# When MathJax is enabled you need to specify the location relative to the HTML
-# output directory using the MATHJAX_RELPATH option. The destination directory
-# should contain the MathJax.js script. For instance, if the mathjax directory
-# is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
-# Content Delivery Network so you can quickly see the result without installing
-# MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from http://www.mathjax.org before deployment.
-# The default value is: http://cdn.mathjax.org/mathjax/latest.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
-
-# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
-# extension names that should be enabled during MathJax rendering. For example
-# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_EXTENSIONS =
-
-# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
-# of code that will be used on startup of the MathJax code. See the MathJax site
-# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
-# example see the documentation.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_CODEFILE =
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
-# the HTML output. The underlying search engine uses javascript and DHTML and
-# should work on any modern browser. Note that when using HTML help
-# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
-# there is already a search function so this one should typically be disabled.
-# For large projects the javascript based search engine can be slow, then
-# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
-# search using the keyboard; to jump to the search box use + S
-# (what the is depends on the OS and browser, but it is typically
-# , /